How it works

From an upstream change to a pull request you can trust.

Six stages, in a fixed order, recorded as data. Each one hands the next a fact, never a guess, and each one is allowed to stop the line.

01

Ingest. Fetch the upstream source and keep the exact bytes.

Mendward fetches the published specification, SDK release or schema, stores the raw bytes with a SHA-256, and normalises it. The fingerprint is the only identity a fetch has.

  • The raw bytes are stored with their SHA-256 before anything parses them.
  • Same fingerprint, no new run. A provider that republishes identical bytes costs nothing.
  • Every fetch passes an SSRF-hardened client: host allowlist, IPv4 and IPv6 ranges, ports and every redirect hop re-checked.
ledger-api / openapi.jsonObserved
Fetched
2026-10-05 09:14:02 UTC
HTTP
200, 412 KB, ETag "7c1e"
SHA-256
9f3c4a1e...b07de21a
Contract
OpenAPI 3.0, 86 operations, 140 schemas
Previous
41aa09c2...5e1f7730

The fingerprint is the fetch's only identity. Same bytes, no new run.

02

Detect. Diff it, and explain why each change matters.

A semantic diff against the last observation produces typed changes with evidence: what moved, where in the document, how severe, and the reason for that severity.

  • A typed vocabulary of change kinds: renamed, removed, made required, narrowed, widened, and more.
  • Each change carries a severity, a direction (request, response, type or document) and the reason for that severity.
  • Every change points back into the source document, so a reviewer can check it in one click.
Semantic changes2 found
property_renamedBreaking

LedgerEntry.entryRef to LedgerEntry.entry_id

A response property your code may read no longer exists under its old name. Direction: response.

#/components/schemas/LedgerEntry/properties

property_addedAdditive

LedgerEntry.posted_at

New optional field. Nothing to migrate.

03

Analyze. Prove which lines of your code it reaches.

Mendward builds a real program from your repository and follows symbols through imports, aliases, wrappers and re-exports. Every finding carries a file, a line, a column and the evidence for it.

  • A real TypeScript program, not a text search. Symbols are followed through imports, aliases, wrappers, barrels and re-exports.
  • Weak signals are labelled, never hidden. A string that contains a name is reported as LOW and cannot reach HIGH alone.
  • An empty result is a hard failure, so "nothing affected" is never a silent guess.
Impact on acme/payments-service3 findings
src/ledger.ts:42:21HIGH
const ref = row.entryRef;
  • Resolved by the type checker to LedgerEntry
  • Import traced to @ledger/sdk
src/report/export.ts:18:9HIGH
const { entryRef } = entry;
  • Destructured read of the renamed field
scripts/legacy-fmt.ts:7:14LOW
log("entryRef missing");
  • Text match only. A string that contains the name is not proof it is read.

04

Migrate. Write the smallest safe patch.

Deterministic recipes run first. A model is used only for what no recipe claims, and its hunks are labelled. Every changed line must be attributable to a specific edit, or the patch is refused.

  • Deterministic recipes claim what they can prove. A recipe never searches the repository on its own.
  • A model only sees what no recipe claimed, in a delimited data channel, and its hunks are labelled in the pull request.
  • Every changed line must be attributable to a specific edit. Reflowed whitespace is refused outright.
src/ledger.tsrename_property, deterministic
41export function toRow(row: LedgerEntry) {
42  const ref = row.entryRef;42  const ref = row.entry_id;43  return { ref, amount: row.amount };
44}
Every changed line attributed to an edit2 of 2

05

Verify. Run your own checks in a sealed container.

Your typecheck, build and tests, as your repository declares them, in a container with no network and a read-only worktree. A check that did not run says so, forever.

  • The commands come from your repository: its package scripts, its lockfile, its test runner.
  • No network, a read-only worktree, dropped capabilities, and hard limits on memory, processes and file size.
  • A check that could not run is SKIPPED_WITH_REASON, in the database, the interface and the pull request.
Verification, no network, read-only /work
✓pnpm install --frozen-lockfile41.2s
✓pnpm typecheck12.8s
✓pnpm build33.0s
✓pnpm test214 passed
!lintSKIPPED_WITH_REASONNo lint script is declared in package.json. Reported as skipped, never as passed.

06

Pull request. Open a pull request a reviewer can trust.

The diff, the evidence, the verification results, what to doubt and how to undo it. One idempotency key per change set, so a retry can never open a duplicate. You merge. Mendward never does.

  • A body written in reading order: what changed upstream, why this repository, what changed here, what ran, what to doubt, how to undo it.
  • One idempotency key per change set. A retry finds the existing pull request instead of opening a second.
  • Mendward opens pull requests. It never merges, never touches CI configuration and never edits a lockfile it was not asked to.
acme/payments-serviceOpen

Migrate Ledger API: entryRef renamed to entry_id #128

mendward/ledger-entry-id into main. 2 files, +2 −2.

  1. Upstream change
  2. Why this repository
  3. What changed
  4. Verification: 4 ran, 1 skipped with reason
  5. Known uncertainty and manual review
  6. How to undo this
  7. Evidence and confidence: HIGH

Mendward opens it. You merge it.

Deterministic first. A model second. A human when it matters.

Six findings from one run. Each one goes to the most provable path that can handle it, and the path is written on the pull request.

Claimed by a recipe

Deterministic, attributed, repeatable.

  • row.entryRef
  • { entryRef } = entry
  • entry.entryRef ?? null
  • mapRow(r).entryRef

Handed to a model

Only what no recipe claimed. Labelled in the pull request.

  • buildRef(row, "entryRef")

Left for a human

With the reason written down.

  • row[fieldName]

Dynamic property access. The key is not known until runtime.

Anatomy of a pull request.

The product's real output is a document for a reviewer. It is written in reading order, and every line of it is traceable to a recorded run.

acme/payments-service#128

Migrate Ledger API: entryRef renamed to entry_id

Open mendward wants to merge 1 commit into main from mendward/ledger-entry-id

Upstream change

property_renamed on LedgerEntry: entryRef is now entry_id. Severity: breaking, response direction. Source: #/components/schemas/LedgerEntry/properties, fingerprint 9f3c4a1e.

Why this repository

  • src/ledger.ts:42:21 reads row.entryRef, resolved by the type checker to LedgerEntry.
  • src/report/export.ts:18:9 destructures entryRef from an entry.

What changed

-  const ref = row.entryRef;+  const ref = row.entry_id;-  const { entryRef } = entry;+  const { entry_id: entryRef } = entry;

Both hunks by recipe rename_property. No model-authored lines.

Verification

✓pnpm typecheck12.8s
✓pnpm build33.0s
✓pnpm test214 passed
!lintSKIPPED_WITH_REASONNo lint script is declared in package.json.

Known uncertainty and manual review

Not verified: 1 check did not run, listed above. scripts/legacy-fmt.ts:7 mentions the old name inside a string. It was reported as LOW and not edited.

How to undo this

git push origin --delete mendward/ledger-entry-id

2 files, 2 additions, 2 deletions. No configuration, lockfile or CI file is touched.

Evidence and confidence

HIGH This is a level, not a percentage. The signals below are the whole basis for it.

  • src/ledger.ts:42: HIGH, resolved by the type checker.
  • src/report/export.ts:18: HIGH, destructured read of the renamed field.

Generated by Mendward. Every line above is traceable to a recorded run.

Your commands, in a sealed room.

Verification runs the checks your repository already declares, inside a container that cannot reach the network and cannot write to your code. The only writable path is the one Mendward reads results from.

container, --user, --cap-drop ALL/workyour repositoryread-only/outresults and logsthe one writable pathpnpm typecheck, pnpm build, pnpm test, as declarednetwork: none, memory and swap equal, no core dumps

See it run on your own repository.

Mendward is in private beta for teams that integrate with third-party APIs from TypeScript on GitHub.

Request access
0:00 / 1:08
UpDown to moveEnter to runType / for a few more