{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "PatchFlow Blog",
  "home_page_url": "https://patchflow.run/blog",
  "feed_url": "https://patchflow.run/blog/feed.json",
  "items": [
    {
      "id": "https://patchflow.run/blog/first-measured-typescript-migration",
      "url": "https://patchflow.run/blog/first-measured-typescript-migration",
      "title": "Our first measured migration: TypeScript 5.9 to 6.0",
      "summary": "What a real private-pilot migration cost, where it failed, and why a red check was the most useful result.",
      "content_text": "The first PatchFlow private-pilot migration did not travel in a straight line. That is precisely why it was useful.\n\nWe selected a repository we own, pinned one transition—TypeScript `5.9.3` to `6.0.2`—and named one customer-owned GitHub Check, `smoke`, as the verification boundary. PatchFlow could inspect the repository, ask a bounded coding agent to make the upgrade, and open a draft pull request. It could not merge.\n\n## The measured result\n\nThe coding-agent attempt completed in roughly three minutes and reported **83.43 cents** of execution cost. The resulting draft pull request changed 16 files. It pinned TypeScript 6 across the workspace, added explicit Node and Vite types, and adapted the configuration for TypeScript 6 defaults.\n\nLocal verification reported:\n\n| Gate | Result |\n| --- | --- |\n| Workspace typecheck | 9 packages passed |\n| Tests | 33 tests passed before the baseline repair |\n| Builds | Web, admin, and marketing passed |\n| Required GitHub Check | Failed |\n\nThat last row mattered more than the first three.\n\n## The failure was real—but not caused by the migration\n\nThe `smoke` workflow failed all three offline agent fixtures with an empty error. We reproduced the same failure on the exact pre-migration commit. The TypeScript change had not caused it; the repository's smoke harness already depended on tenant/database state despite calling itself offline.\n\nIt would have been easy to wave that away. A migration product cannot do that. The named check is the repository owner's boundary, not a suggestion.\n\nWe fixed the harness separately. Offline runs now avoid tenant storage and use deterministic in-memory tools. Its own pull request passed the hosted smoke check, was reviewed, and merged before the TypeScript branch was refreshed.\n\n## A false green we had to remove\n\nThe pilot also exposed a PatchFlow defect: the run was initially recorded as having an open PR before the required check had settled. That state was too optimistic.\n\nWe changed the control plane so a run becomes ready only after PatchFlow:\n\n1. resolves the draft pull request;\n2. records its exact head SHA;\n3. waits for every named check on that SHA;\n4. fails closed on missing, pending, cancelled, or failed checks; and\n5. persists the settled verdict and provider cost.\n\nWhen the base branch later advanced with the smoke repair, the verifier rejected the stale relationship. We added a narrow recheck rule: GitHub must prove that the new base descends from the originally inspected commit **and** that the checked pull-request head contains the new base. The original policy was not replaced with “trust the latest.” It became an ancestry proof.\n\n## What finally merged\n\nThe refreshed TypeScript branch passed the hosted `smoke` check on its new exact head. PatchFlow reconciled the existing run without launching another paid agent. A human reviewed and merged the draft pull request.\n\nThe outcome was one completed migration, one repaired repository check, one hardened control plane, and a real cost measurement.\n\nThat is the standard we want for the pilot: not a perfect demo, but a bounded system that tells the truth when the path is messy.\n\n## What this does not prove\n\nOne repository and one migration do not establish broad compatibility. A passing smoke check proves only the configured check passed on the recorded commit. It does not prove untested behavior is correct.\n\nThe next migrations should deliberately vary the package, repository shape, and check topology. The goal is not a bigger automation claim. It is a larger body of measured evidence.",
      "date_published": "2026-08-01T00:00:00.000Z",
      "authors": [
        {
          "name": "PatchFlow Engineering"
        }
      ],
      "tags": [
        "Field report",
        "TypeScript",
        "CI"
      ]
    },
    {
      "id": "https://patchflow.run/blog/dependency-bots-stop-at-the-hard-part",
      "url": "https://patchflow.run/blog/dependency-bots-stop-at-the-hard-part",
      "title": "Dependency bots stop where the engineering starts",
      "summary": "Version detection is useful. Breaking upgrades still leave teams with evidence gathering, call-site changes, and CI interpretation.",
      "content_text": "A dependency-update alert answers one question: **is a newer version available?**\n\nFor patch releases, that may be most of the work. For a breaking SDK or framework release, it is only the first line of the ticket.\n\nThe remaining work usually looks like this:\n\n- find the release notes that actually match the target version;\n- separate API breaks from unrelated announcements;\n- locate the affected call sites and configuration;\n- update the package manifest and lockfile together;\n- adapt code without drifting into a refactor;\n- run the repository's real setup and checks; and\n- explain the result well enough for a human to review it.\n\nThat is the middle mile PatchFlow is designed to own.\n\n## Detection and migration are different products\n\nDetection is mostly a graph problem: inspect manifests, resolve versions, and apply an update policy. Migration is repository-specific engineering under uncertainty.\n\nA useful migration system must carry more context than “upgrade package X.” It needs the exact before and after versions, the evidence class behind the change, the repository's required checks, and a strict edit boundary. It also needs to preserve uncertainty instead of converting every result into a green badge.\n\nPatchFlow records migration evidence as one of three levels:\n\n| Level | Meaning |\n| --- | --- |\n| Structured playbook | Known before-and-after patterns for the transition |\n| Release notes | Exact upstream release evidence is available |\n| Inferred | The agent must reason from the repository and generic version context |\n\nAn inferred migration can still be useful. It should simply receive more human scrutiny than a transition backed by a precise playbook.\n\n## “Tests passed” needs a subject\n\nCI status is not a free-floating fact. A verdict belongs to a commit.\n\nIf a branch moves after tests start, the old result cannot authorize the new head. If a required check never appears, absence cannot mean success. If an agent can edit the workflow that judges its own patch, the check is not an independent boundary.\n\nPatchFlow therefore treats verification as a tuple:\n\n```text\nrepository + pull request + immutable head SHA + named checks\n```\n\nThe system waits for those checks and records the exact commit they evaluated. Missing or failed checks stop the run. Humans retain merge authority.\n\n## The product is the review boundary\n\nThe interesting promise is not “AI updates dependencies.” Coding agents can already change code.\n\nThe product is a controlled path from a postponed upgrade to a draft pull request with enough evidence, scope control, verification, and cost data for an engineer to make a decision.\n\nThat framing is narrower than autonomous maintenance. It is also far more useful to a team deciding whether to trust the first migration.",
      "date_published": "2026-07-29T00:00:00.000Z",
      "authors": [
        {
          "name": "PatchFlow Engineering"
        }
      ],
      "tags": [
        "Dependency management",
        "Engineering productivity",
        "Product"
      ]
    },
    {
      "id": "https://patchflow.run/blog/commit-bound-ci-verdicts",
      "url": "https://patchflow.run/blog/commit-bound-ci-verdicts",
      "title": "A green CI badge is meaningless without a commit",
      "summary": "Why migration verdicts must be bound to immutable pull-request heads—and what to do when the base branch advances.",
      "content_text": "Imagine a migration branch passes CI. A minute later, another commit lands on that branch. The pull request still displays recent green activity, but the code a reviewer sees is no longer the code that produced the result.\n\nThis is not a theoretical edge case. Any asynchronous automation that edits branches and polls CI must decide what its green verdict actually refers to.\n\n## Bind the verdict to the head SHA\n\nPatchFlow reads the pull request's immutable head SHA before evaluating required GitHub Checks. The stored result includes that SHA. If the branch moves, the old result is stale and cannot make the run ready.\n\nThe core rule is small:\n\n```text\nrequired checks passed on head A ≠ required checks passed on head B\n```\n\nNamed checks matter too. An unpinned system may see a quick lint job finish before a slower integration workflow even registers. Production policies should name the checks that exercise the affected integration. Missing named checks fail closed.\n\n## Base branches move as well\n\nLonger-running pull requests introduce another relationship. The repository's base branch may advance after the migration was inspected.\n\nBlindly accepting the latest base weakens the original authorization boundary. Requiring the base to remain byte-for-byte identical forever makes a safe recheck impossible after normal repository work.\n\nThe narrow solution is ancestry:\n\n1. the current base must descend from the originally inspected commit;\n2. the pull-request head must contain the current base; and\n3. required checks must pass on that exact head.\n\nGitHub's commit comparison API can prove both relationships. A diverged base or stale head is rejected.\n\n## Keep merge authority separate\n\nA passing verification record should never bypass branch protection or human review. PatchFlow opens draft pull requests and stops.\n\nThis separation produces a cleaner security model:\n\n- PatchFlow can propose and verify.\n- Customer-owned GitHub Actions can test.\n- Branch protection and reviewers can authorize merge.\n\nNo single component gets to write the change, define the test, and merge the result.\n\n## What to record\n\nA useful audit record for an automated migration includes:\n\n- repository and pull-request number;\n- original inspected commit;\n- current base and verified head SHA;\n- required check names and conclusions;\n- provider attempt and model;\n- settled execution cost; and\n- the human-controlled merge outcome.\n\nThat record turns “the bot said it passed” into a claim another engineer can inspect.",
      "date_published": "2026-07-25T00:00:00.000Z",
      "authors": [
        {
          "name": "PatchFlow Engineering"
        }
      ],
      "tags": [
        "CI",
        "Security model",
        "GitHub"
      ]
    }
  ]
}
