Building Log

A running log of what I'm building with Claude Code: the problems, what I pushed back on, what got corrected, and the learnings along the way.

  1. In ProgressFront Desk

    Moved from design to build on the support desk, with five parallel AI builders each in their own workspace, while other agents summarised years of closed tickets and we chose the AI model for live tickets with a blind comparison.

    The problem
    A large, interlocking feature set, a fixed pilot date, and tens of thousands of old support conversations to clean, summarise and import, without breaking the rules about where customer data may be processed.
    What I pushed back on
    I drew a hard line on data: historic tickets are processed only within our Claude subscription, never on our cloud provider's AI service, which is reserved for new tickets. I asked for summaries to run oldest to newest so they capture how the product changed, for a screen where I could judge summary quality myself, and for cheaper non-Anthropic models to be compared. When the weekly limit ran out, I capped usage.
    What Claude pushed back on
    Claude swapped a planned grading step that would have sent more history to the cloud service for a blind local grader, and admitted the test tickets had already gone there before my rule was stated. It explained that 'local in Claude Code' still means Anthropic processes the text, put the trade-off of a strict rule to me rather than deciding quietly, and fixed its own run, which kept starting batches after hitting the usage limit.
    Learning
    Parallel AI builds work when each stream has its own workspace, branch and database, an owner for each shared file, and a fixed merge order. A blind comparison on real tasks changed our model choice: a cheaper model invented less, at about a ninth of the cost. And a data-residency rule needs restating at every new step.
    #agents #privacy #ai-model-selection
  2. In ProgressFront Desk

    Designed how our support desk decides who sees which tickets and how tickets get shared out fairly, planned bringing years of support history across from our previous tool, and scoped an AI assistant that drafts a plan and reply for each new ticket.

    The problem
    Anyone with access saw every ticket for every customer they could reach, the round robin didn't work, and the desk was missing from roles and permissions altogether. Meanwhile four years of history sat in another tool, tagged inconsistently, with customers identified only by email domains and free-text tags.
    What I pushed back on
    Claude's first round-robin idea, give the next ticket to whoever has the fewest open, would have punished efficient people, because fast closers always look free. We switched to counting tickets assigned per shift. I added shifts, pointed out that Claude couldn't know our customers because they only live in production, asked for a small local tool to clean the data before anything is uploaded, and insisted every imported ticket keeps a link back to its original.
    What Claude pushed back on
    Claude checked the code first and found the desk was already in the permissions catalogue, so its absence was a data problem, plus real gaps such as assignments allowed to people without access to that customer. It refused to take secrets in chat, and when its safety check blocked it from fetching database credentials, it gave me a read-only export to run myself. The export showed no customer had approved email domains configured, so a live verification check had probably been failing quietly.
    Learning
    Before migrating a support history, count the real data on both sides; the surprises only appear then. Fairness depends on what you count: open tickets reward slowness, tickets assigned per shift don't. And reviewing messy matches by brand or domain, not ticket by ticket, turns thousands of decisions into about a hundred.
    #product #data-migration #fairness
    Read the full write-up
  3. TestingNineTales

    Built three NineTales features in parallel overnight, the biggest being a bulk run that turns one ad strategy document into a set of finished videos, unattended.

    The problem
    My ad playbooks hold twenty or more short pieces: hooks, content angles and calls to action. Making each one separately meant five to seven approval stops per video. I wanted to drop in the document, pick a style and come back to finished videos.
    What I pushed back on
    When Claude proposed joining pieces into complete ads, I said no: I only want each piece made and named, and joining them is my job. I asked for no approval stops at all. When the cost estimate jumped, I asked whether it was running on my subscription, which reframed the 'dollars' as usage against my plan. I also set the length for each piece type.
    What Claude pushed back on
    A reviewer found that Claude had left a costly step out of its own estimate, and Claude came back and said so rather than keeping the lower figure. It checked how the engine actually worked before designing, ran the split on my real playbook first (exactly twenty videos), and reviews caught the bugs that matter when nobody is watching: a run that could stall forever, a step that could keep spending, and automation that would build over my own edit.
    Learning
    Removing a human from a pipeline is mostly about answering, ahead of time, every question the pipeline used to ask. Write those answers down and the automation becomes reviewable. And unattended work changes what 'safe' means: the worst bugs aren't crashes, they're things that quietly spend, stall or overwrite.
    #automation #ai #video
    Read the full write-up
  4. TestingNineTales

    Built the browsing side of a style library for the ad-making tool in NineTales, my personal Mac app, including an importer that turns my existing notes into structured files.

    The problem
    My visual styles, creative directions and colour palettes lived in loose notes. The tool needs them as structured data, and the import writes into a folder I care about: it must never edit my notes, never run twice by accident, and never leave things half-written.
    What I pushed back on
    I had Claude build from my own designs rather than inventing a look. I changed my mind about where app connections live and moved them into each workspace's settings. I made the scope calls on what to import and how, and held to a standing rule: anything not built yet stays hidden, with no 'coming soon' labels.
    What Claude pushed back on
    Each task was built by one agent and checked by a separate reviewer, which kept finding real problems: an interrupted import that could reuse an id, a double-click starting two imports, one damaged file blanking a whole page. Claude never touched my real library while building; every check was a preview, with checksums before and after to prove nothing changed. At the end it found links between items that didn't match from both sides, and asked me rather than guessing.
    Learning
    When code writes to something you care about, give the reviewer the worst cases to try: crash it halfway, change a file underneath it, double-click, close the window early. Each found a separate bug here that happy-path tests missed. And a preview with before-and-after checksums turns 'trust me' into proof.
    #testing #macos #data-safety
  5. TestingWedding Save the Date

    Built a cinematic save-the-date website: a short AI-generated film walks the guest through a candlelit garden to an invitation, which then becomes a real, scrollable web page.

    The problem
    The film ends on a photographed invitation, but the site needed a long, editable one with a countdown, photos and venue details. The hard part was making the handoff from video to web page feel like one continuous shot, on a phone, opened from a WhatsApp link.
    What I pushed back on
    I pushed back several times, and each one improved it. The first transition ghosted, so I asked to zoom into the invitation itself instead. I spotted a seam where photographs met a recreated section and asked for the whole thing to be rebuilt. I asked for portrait framing everywhere, a zoom driven by scrolling, and photos that all stay visible. I chose AWS over another host to keep everything in one account, and caught a date in the wrong year.
    What Claude pushed back on
    Claude asked before changing the year rather than assuming. It wouldn't buy the domain itself and kept the cloud keys out of the chat. It found that the 'abrupt transition' I'd reported was a layering bug: the paused video sat on top of the page, so the dissolve had never been visible at all.
    Learning
    An invisible cut comes from changing one thing at a time: freeze the film on its last frame, dissolve the page over it, and only then add depth as the guest scrolls. And checking each step with real screenshots caught three bugs that 'the code looks right' would have missed.
    #web #animation #ai-video
  6. ActiveOlympus

    Two weeks of hardening the Olympus admin portal after a new documentation area went live, plus a redesign of the bug tracker so it can replace a spreadsheet.

    The problem
    Real users kept finding bugs that testing on one machine never would: a permission change that reported success and saved nothing, replies that failed only on production, draft content that vanished when a second person opened the editor, and invitation emails that silently never reached one customer's inbox.
    What I pushed back on
    I interrupted a deploy chain twice to change priorities, and both times something important was found in between. I rejected a side drawer for bug details in favour of a large centred window. I stopped Claude mid-command to ask why it needed a new email configuration rather than reusing one; the answer justified it, but the question forced the reasoning into the open. And no user should ever read 'Access denied to this tenant'.
    What Claude pushed back on
    Claude traced the lost permission changes to a failed save being treated as a deliberate reset, and the vanished drafts to stale data plus autosave, fixed by having the server reject writes from a session that had fallen behind. It proved from logs that the missing emails were accepted and never bounced, then found that delivery feedback had never been connected at all. It also refused link tracking on review emails, because rewritten links look like phishing to the very filters we were fighting.
    Learning
    Anything that treats a failed save as success will eventually eat someone's data; return the error. Environments drift quietly: the same table had different column names in each. And an email system without delivery feedback looks exactly like one that works, until a customer asks where their invitation went.
    #production #reliability #email
  7. In ProgressIntegration Framework

    Started turning years of one-off customer integrations into a single shared framework, and built the first working slice: a scheduled file import that runs per customer, with a full record of every run.

    The problem
    Every enterprise customisation, such as file imports or HR system syncs, had been built as customer-specific code, leaving dozens of separate versions to maintain. The goal was one codebase where a new customer integration is a small extension plus configuration, never a copy, and where account managers can see what runs, when, and how it went.
    What I pushed back on
    I rejected Claude's first design, which described each customer's mapping entirely as configuration, in favour of small per-customer code classes: code for logic, configuration only for parameters. I questioned the credential storage twice until we landed on simply encrypting the column, and pointed the new API at an existing authentication pattern instead of a new one. And I kept correcting the business rules from domain knowledge: multiple folders with different naming, 'latest' meaning the date in the filename, active records only, and only genuinely changed assignments going through.
    What Claude pushed back on
    Claude explored before designing and found half the framework already existed in the codebase, so the design became reuse rather than invention. It caught that an existing upload helper silently drops rows it doesn't recognise, and that 'per-customer packages' risked becoming copies in disguise. Its best catch came from testing on the real app: the encryption settings were being silently ignored because of the order the app loads its configuration.
    Learning
    Draw the line between code and configuration on purpose: customer logic in small code extensions, customer parameters in data. And build a thin working slice quickly, because the most valuable corrections were my domain knowledge, and they only arrived once there was something real to react to.
    #architecture #integrations #reuse
    Read the full write-up
  8. In ProgressLearning Campaigns

    Built the new AI marking pipeline for learner submissions, then spent the rest of a marathon session hardening it against seven separate failures once it met the real environment.

    The problem
    Submissions marked by AI stayed stuck at 'pending' with no score. Every failure looked the same from the outside, and each had a different cause. Separately, a baseline quiz from a real run had four questions that were really the same question in disguise.
    What I pushed back on
    I gave Claude the facts it was missing instead of letting it hunt: the right field name, and how the security keys pick an environment. That steered it towards an existing convention rather than a new workaround. I asked for reference files to stay hidden from learners on mobile, and for internal cost figures to show only on our internal site.
    What Claude pushed back on
    Claude caught a naming clash that would have crashed the app on start-up. It refused to rewrite code blind and instead gave me step-by-step checks for each layer, which surfaced the issues one by one. Its sharpest find: two 'successful' deploys had silently rolled back, so we had been reading the logs of a twelve-hour-old version.
    Learning
    'Deployment completed' can mean the new version is running, or that it failed and quietly rolled back. The tell is when the running version actually started. And for repeated quiz questions, the fix was cheap: show the AI the questions it had already written for that day.
    #ai #deployment #debugging
  9. TestingAI Roleplay

    Gave an embedded results panel more room, then fixed a small but irritating bug where the 'latest' tag sat on a learner's oldest attempt.

    The problem
    A learner's attempt list showed the first attempt tagged as the latest, with four newer ones underneath. The screen simply trusts the first item it is given, so the list was arriving in the wrong order.
    What Claude pushed back on
    Claude traced it to a quirk in how the framework combines sorting rules: asking for newest-first added a second sort on top of an existing oldest-first one, instead of replacing it. A comment elsewhere in the code already warned about exactly this. Claude had read it that morning and still missed it on a new endpoint, so it fixed both places and copied the warning alongside.
    Learning
    A symptom can look nothing like its cause. 'The latest tag is on the wrong row' points you at the screen, but the bug was one layer down, in what the server sent. When ordering looks wrong, check the actual order coming off the wire first.
    #debugging #data
  10. TestingAI Roleplay

    Built the redesigned view page for a published scenario, then spent most of the day untangling why a multi-language scenario could not be republished even after a clean re-translation.

    The problem
    The view page was a simple read-only canvas; the new design added tabs and an attempts table. The hard part was that once a scenario had a translation and the author edited and re-synced, republishing kept failing. That turned out to be three separate bugs stacked on top of each other.
    What I pushed back on
    I guessed auto-save was corrupting the source text; Claude showed auto-save was actually fine and the leak was in four other save points, so we patched only those. I also caught a real security gap: a learner listing was not being filtered by who the viewer is allowed to see.
    What Claude pushed back on
    Claude worked through the three stacked failures one at a time (each only visible once the one in front was fixed), and found that a wholesale 'delete and recreate the rows' save was quietly wiping the translation state on every edit. It fixed it by snapshotting that state and restoring it after each save.
    Learning
    Recreating rows wholesale without preserving their metadata is a quiet data-loss footgun when something saves constantly in the background. And access-control checks belong on every listing endpoint, not just the obvious one.
    #debugging #security #data
  11. In ProgressLearning Campaigns

    Designed and built the backend that publishes a learning campaign and assigns it to individual learners, with progress tracking and a hard guarantee that nobody is ever double-assigned.

    The problem
    The product could build an AI learning plan but had no way to reach actual learners: no per-person enrolment, no assignment flow, no progress tracking, no link from a generated assessment to a real attempt.
    What I pushed back on
    My first exploration was against a stale copy of the code. I told Claude to pull the actual live branch, which turned out to be 98 commits ahead and already had a huge amount built, so my first plan had been written against the wrong reality and had to be redone. I also fixed the data model so the heavy per-learner objects are created only when first needed, and insisted there must never be a duplicate assignment.
    What Claude pushed back on
    Claude found that some existing tables could not be reused without loosening constraints on heavily-used shared tables, surfaced that as an explicit decision rather than guessing, and noticed one model was already designed for exactly this so it could be reused directly. It also added database-level guards to make double-assignment impossible.
    Learning
    When a codebase is mid-evolution, verify which branch is actually live before you plan. A plan built on a stale tree can be almost entirely wrong, and you will not feel it until you are deep in. Five minutes of checking saves hours.
    #planning #architecture #data-model
    Read the full write-up
  12. TestingLearning Campaigns

    What began as a quick 'why is image generation stuck on this campaign' question turned into a six-hour debugging marathon that uncovered three hidden bugs and a performance trap in my own earlier code.

    The problem
    A published campaign sat at 0 percent imaging for forty-five minutes with no movement, and the logs showed no image requests at all. So the question was not 'did it crash', it was 'why did it never start'. The trail kept going further back.
    What I pushed back on
    I corrected a migration-naming shortcut (again, the second time), kept the failure reason off the user-facing screen and server-side only, and worked through a long list of UX cleanups.
    What Claude pushed back on
    Claude found a one-line bug that had been quietly breaking every image job since it shipped, hidden by a catch-all error handler that logged nothing useful. It also traced a frozen page to my own earlier code: a screen recomputing heavy data on every refresh tick, which pegged the browser to 95 percent CPU.
    Learning
    Silent error handlers are debt that compounds. Catching an error and returning cleanly looks tidy, but when someone finds a stuck record hours later, the only clue is a log line that may already have rolled off. Leave a breadcrumb on the record itself: the error message, where it came from, and when, so debugging is one lookup away.
    #debugging #observability #performance
  13. TestingAI Roleplay

    Tidied up loose ends after the dashboard and translation work: collapsed three near-duplicate data calls into one, rolled translation spend into the cost shown to authors, added a Duplicate option, and fixed a clutch of small fit-and-finish bugs.

    The problem
    The dashboard had landed but was full of small 'almost right' details that add up to a janky feel. Underneath the polish were two real wins: three sibling data endpoints that were really one call, and translation spend that was tracked for billing but never shown on the per-scenario cost badge, so authors saw a smaller number than they were charged.
    What I pushed back on
    I pushed to retire the three redundant endpoints in favour of the single combined one, to hide a large block of internal config from the author's view while still sending it, and flagged that the cost badge was missing translation spend.
    What Claude pushed back on
    Claude made a greyed-out control that looked broken read as intentionally disabled with a hint, rather than a dead button.
    Learning
    A control that looks enabled but does nothing is a worse failure than a missing one: people read it as broken. A visible disabled state plus a one-line reason is a five-minute fix that prevents a support ticket.
    #ui #cost #cleanup
  14. TestingAI Roleplay

    Built a new analytics dashboard from designs, wired it to real data, and chased a bug where two translations running at once kept clobbering each other's progress.

    The problem
    A big mixed session: build the dashboard pixel for pixel, connect it to real numbers, and fix a pile of small bookkeeping issues, while running two language translations at once produced impossible-looking progress (one bar at 73 percent, the other stuck then jumping backwards).
    What I pushed back on
    I kept pulling the work back to the platform's existing look: use the same icons, fonts, and dropdown styles the rest of the product uses rather than hand-made ones. I also tightened the publish rules (you cannot publish until every enabled language has finished) and pointed at the concurrent-translation race.
    What Claude pushed back on
    Claude rewrote each shared-progress update as a single database statement so two updates on different language slices no longer overwrite each other, and caught a start-up crash caused by giving a new service the same name as an existing one.
    Learning
    When several writers update different parts of the same shared record, doing the read, change, and write in separate steps is a race waiting to happen. Letting the database do the merge in one statement makes it take turns safely. And publish rules belong on both the front end and the server: front-end only is a polite suggestion, server-only is invisible.
    #reliability #ui #data
  15. TestingLearning Campaigns

    A long session on the campaign image pipeline: added retries and proper error visibility, fixed reference images being silently dropped, and tracked an end-to-end failure to a surprisingly mundane cause.

    The problem
    Trainers were getting image failures with zero diagnostic information. Reference images meant to guide the look were being silently thrown away with just a warning and a leftover 'fix this later' note that had been there since launch. And per-item failures were invisible because only a single overall error was recorded.
    What I pushed back on
    When image generation was failing in production and Claude started speculating about API compatibility, I just told it the likely answer: sometimes you have simply run out of credit. Which was exactly right, and saved a round of guessing.
    What Claude pushed back on
    Claude discovered that the catch-all error handler was storing the real error message but never logging it, so the logs only ever showed a generic 'upstream error'. Once it logged the actual message, the next attempt revealed the true cause: the account's billing limit had been reached. An operational problem, not a code one.
    Learning
    Opaque errors from an outside service usually have boring causes. Before speculating about version support or formats, look at the actual error message. Every handler that catches an error should log the message, even if it also stores it: the cost is tiny, and the cost of not having it is paid exactly when you can least afford it.
    #debugging #observability #ai
    Read the full write-up
  16. In ProgressAI Roleplay

    Hunted a data-loss bug where freshly generated dialogue kept reverting to placeholder text, and found the page's auto-save was overwriting the background job's work in real time.

    The problem
    Generating a scenario produced a mix of good dialogue and turns that reverted to placeholder text. It looked like a generation bug, but the generation was clean every time; something was wiping the saved result right after it was written.
    What I pushed back on
    I rejected a hand-written list of 'narration words' as the way to tell dialogue from notes, and asked why we were not using the actual AI for a judgement that is about meaning. I also kept asking Claude to prove each theory with evidence, which mattered because the first three theories were all wrong.
    What Claude pushed back on
    Claude eventually checked the web server's own access logs, not just the application logs, and found 150 save calls in 60 seconds during a single generation. A database query then showed hundreds of rows deleted inside that same minute, which made the cause undeniable.
    Learning
    Application logs lie by omission. When a request seems to be missing, the web server's access log and the database's own row history are the ground truth. And for a judgement about meaning, ask a small AI model rather than hand-coding a brittle list of words.
    #debugging #ai #reliability
    Read the full write-up
  17. In ProgressAI Roleplay

    Made a reused screen behave correctly depending on who opens it, an author previewing, a learner taking the scenario, or a manager reviewing a result, instead of always assuming the author.

    The problem
    One screen now serves three different people, but its header, navigation, and buttons were all hardcoded for the author. And when a scenario is set to hide results, the learner must see only that they finished, with no score or pass or fail.
    What I pushed back on
    I insisted the reuse be thorough, not superficial: the exact back-button chain, a breadcrumb modelled on how the other pages already do it, the side navigation removed, and results hidden from the learner but still visible to managers.
    What Claude pushed back on
    Claude found that the page it needed to go back to could already rebuild itself from the web address, so what looked like fiddly navigation work became a one-line link. It also flagged that hiding the score has to cover every place it leaks, not just the headline number.
    Learning
    Treat a privacy setting as covering the whole surface, not just the obvious value: list every place the hidden data could leak (side panels, per-step breakdowns, trend strips) and gate each one.
    #ux #privacy #code-reuse
  18. TestingLearning Campaigns

    Debugged why no images were being generated for any learning campaign, working through a stack of independent bugs across several services before real images finally appeared.

    The problem
    'Image generation is broken' hid a stack of separate bugs across several services. Each layer was right about what it did, and each was missing a piece of information the layer before it had quietly dropped.
    What I pushed back on
    I stopped Claude from reusing an internal-only security token to let one service talk to another, because that token belongs to a different trust boundary and the service should authenticate with its own credentials. I also pushed to use the cloud logs more aggressively rather than guess, and to add a real error trace instead of instrumenting blindly.
    What Claude pushed back on
    Claude found the same setting was being silently dropped at four or five separate hops, and that fixing one was not enough; every hop had to pass it before the AI ever saw it.
    Learning
    In a system with strict input filtering at every boundary, a field quietly dropped at any one hop looks exactly like a field that was never sent. The fix is to check every place that touches the field across the whole system, not to debug the symptom at one layer. And the trust boundary, not whichever credential is lying around, should decide how a service authenticates.
    #debugging #security #architecture
  19. In ProgressAI Roleplay

    Got the roleplay working inside another feature end to end, then spent a long session fixing a fiddly read-only preview.

    The problem
    Embedding the existing roleplay editor read-only inside a different page surfaced a string of layout bugs: an empty canvas, an expand button that did nothing, panels that felt glued to the canvas, and editable-looking controls in a read-only view.
    What I pushed back on
    The biggest correction: Claude had built the settings panels inside the editor component; I said they belong to the host page instead, and that one call explained a whole cluster of symptoms. When it kept making confident styling tweaks that did not work, I told it to stop and reassess rather than keep guessing.
    What Claude pushed back on
    Once I told it to stop guessing, Claude found the real cause of the empty canvas: a height that collapsed to zero when the component was embedded in a different layout. The fix let height flow naturally instead of relying on percentages.
    Learning
    Decide which component owns a piece of UI before worrying about its layout: a panel that belongs to the host page should live in the host, or you inherit all of the reused child's coupling. And stop guessing, pause, reassess is usually faster than another confident tweak.
    #frontend #debugging #css
  20. In ProgressAI Roleplay

    Rebuilt how roleplay scenarios are generated, moving from one slow all-at-once AI call to a step-by-step process with live progress and a stop button.

    The problem
    Generating a whole branching scenario in one AI call timed out after four minutes and left a useless failed state, and meanwhile I just stared at a blank overlay for a minute or two with no sign anything was happening.
    What I pushed back on
    I turned down a quick 'raise the timeout' band-aid because it would not have shown any progress, and went straight to the proper rebuild. I also insisted on showing the scenario fill in piece by piece, because that is what I had actually asked for.
    Learning
    A timeout failure and a show me progress request often point at the same fix. Doing the work in smaller pieces both removes the timeout and gives you natural checkpoints to show progress. When two different problems point at the same change, that change is probably worth doing.
    #ai #ux #architecture
  21. ActiveLearning Campaigns

    A long session spent mostly rescuing the previous day's work, where a batch of changes shipped quickly turned out to be full of small bugs.

    The problem
    A batch of features Claude had shipped passed its code checks but had never actually been clicked through in the browser. Each click surfaced a different small bug: wrong addresses, buttons that looked like they did nothing, mismatched colours, broken styling.
    What I pushed back on
    I told Claude bluntly that the previous day's batch was half-baked, too many things rushed at once to do any of them properly. That was accurate. It had been treating a clean code check as a green light, when a feature is not done until you have actually clicked it in the real screen.
    Learning
    Shipping fast is not the same as shipping more. The work I rushed all had bugs that surfaced the moment anyone used it, and the cleanups took longer than careful first-pass work would have. A passing build is not a finished feature.
    #ai-assisted-dev #quality #process
    Read the full write-up
  22. ActiveOlympus

    Tore down the previous day's image feature in Olympus and rebuilt it as a simpler, clearer version.

    The problem
    The first version assumed admins already knew the exact wording to type. They do not; they have example images and want the system to work out the style for them. The feature had also taken on work that belonged in a separate shared service, and a few settings were in the wrong place.
    What I pushed back on
    I kept cutting the feature back to the one job it should do, and pushed the work it should not own onto the shared service. When the part that actually makes the images was not built yet, I asked for a clear 'not ready yet' message rather than faking it with dummy results. And I redesigned the size picker around shapes people recognise ('Square', 'Portrait') instead of platform names.
    What Claude pushed back on
    Claude pointed out a real limit on the image sizes the model can produce, and added a required field to a table that already had data using a safe, step-by-step approach, so none of the existing rows broke.
    Learning
    Name things the way people use them, not the way they are built: a picker of shapes beats one of platform names, because the same image ends up everywhere. And when part of a system is not built yet, a clear 'not ready' message is more honest than faking it with dummy data, which tends to hide real problems and stick around too long.
    #product #design #ai
    Read the full write-up
  23. In ProgressAI Roleplay

    A rapid polish pass on the roleplay builder, comparing the live screen side by side with the design and fixing what did not match.

    The problem
    The code built cleanly and matched the spec, but in the browser it looked visibly less polished than the design: fuzzy text, a slider stuck at halfway, a menu cropped on one side, connector dots in the wrong place.
    What I pushed back on
    I treated the broken slider as a real bug rather than a minor visual nit, and kept sending close-up screenshots until each thing actually matched, instead of accepting close enough.
    What Claude pushed back on
    Claude traced each problem to its real cause rather than papering over it, for example measuring the actual page layout instead of guessing pixel positions, and degrading gracefully when data was missing.
    Learning
    A green build is not evidence that the screen looks right. Every real bug here was correct in code but visibly broken in the browser. Ship to a real URL early, screenshot it, and compare side by side.
    #ux #debugging #design
    Read the full write-up
  24. ActiveLearning Campaigns

    Shipped a batch of platform-hardening work, including tightening how an internal service was exposed to the public internet.

    The problem
    An internal service was reachable from the public internet and being probed by automated scanners, there was still no per-customer cost record for billing, and trainers had no way to fix one bad day in a plan short of redoing the whole thing.
    What I pushed back on
    When Claude claimed the scanner noise had dropped to zero, I asked sharper questions; it widened the time window and admitted the quick fix had not actually worked for that scanner. It had also been reading the wrong service's logs until I questioned it.
    What Claude pushed back on
    Claude argued against putting a heavy web firewall on the new internal-only service. Once you remove the untrusted public surface, that firewall is spend for negligible value, and the right defence at that layer is network rules instead.
    Learning
    Any public hostname that resolves to your infrastructure ends up on automated target lists within hours. Quick fixes that only block crude scanners do not stop ones that look up your hostname directly. The durable fix is taking the hostname off the public internet.
    #security #infrastructure #architecture
  25. ActiveLearning Campaigns

    Made long training-campaign generation reliable, after a big campaign kept failing partway through.

    The problem
    Generating a 45-day campaign from a 90-page document kept failing. The database ran out of connections, a timeout had no retry, the time estimate was wildly optimistic, and the AI was writing questions about the table of contents instead of the material.
    What I pushed back on
    Claude proposed a database setting I flagged as risky on shared infrastructure, so I had it audit the real usage data first, and we shipped a far more conservative number. I also corrected its over-optimistic time estimate to a simple rule of thumb that matched reality.
    What Claude pushed back on
    Claude spotted a fully built cost-tracking service that was never actually being called, meaning there was no per-customer spend record, and flagged it as a launch blocker.
    Learning
    Changes to shared infrastructure need an audit step before the change, not after. Conservative numbers backed by real usage data beat round numbers picked on intuition every time.
    #reliability #ai #cost
  26. ActiveEvent Management Platform

    Built a participant feedback form, then chased a mobile video-upload bug where uploads kept silently failing on weak connections.

    The problem
    Feedback videos were sent through a long chain of servers before reaching storage, giving the upload four places to fail on a flaky mobile network, and when it failed the screen just silently reset so people had no idea what happened.
    What I pushed back on
    When the proposed fix was to tune one server's settings, I checked whether that file was even the one used in production. It was not, which killed a whole branch of false confidence. I also pushed for a visible progress bar and a clear confirmation rather than a silent state change.
    What Claude pushed back on
    Claude argued the real fix was structural: upload directly from the browser to storage, removing every middle server as a possible failure point, rather than tuning any single one of them.
    Learning
    A chain of servers is a chain of places to fail. Sometimes the right debugging move is to delete the chain rather than work out which link is breaking. Once that frame clicked, the fix wrote itself.
    #debugging #mobile #architecture
    Read the full write-up
  27. In ProgressAI Roleplay

    Redesigned the roleplay scenario builder around a single clear list of outcomes per turn, replacing a confusing two-part form.

    The problem
    The old builder split each turn into two overlapping sections, so it was unclear which one meant a wrong answer, and the whole scenario sprawled open at once and became unreadable.
    What I pushed back on
    I split the work into smaller stages rather than shipping it all in one go, and sent design screenshots even though the written spec looked complete, which caught several missing details before they were built.
    What Claude pushed back on
    Claude found the cause of a bug that was silently flipping AI-generated scenarios back to manual whenever the author touched anything, and quietly repaired rows that had already been corrupted.
    Learning
    Shipping in small, independently deployable stages is worth the extra friction, because any one piece can be rolled back on its own. And sending screenshots at planning time costs minutes but saves hours of rework.
    #product #ux #ai
  28. ActiveOlympus

    Built a shared library of reusable image styles for Olympus, the admin tool behind Frontlyne, that the apps can draw from, and spent most of the session cutting the plan down to what we actually needed.

    The problem
    There was no shared set of visual styles for the apps to reuse, so each was on its own. We wanted one catalogue everything could pull from. The first plan tried to do far more than that.
    What I pushed back on
    My main job turned out to be saying 'no, smaller'. I kept cutting the plan back to the one thing that mattered, and insisted that unfinished work stay hidden until it's deliberately published.
    What Claude pushed back on
    Claude caught a few things I'd have missed, including a warning, right after we shipped, that one button wouldn't work yet because a setting hadn't been switched on in the live system.
    Learning
    The most useful thing I did all session wasn't writing code. It was deciding what not to build. A good tool will build whatever you let it; the real skill is restraint.
    #product #ai #scope
    Read the full write-up