fix(capture-recovery): align attempt/capture-id types with canonical domain (post-squash adaptation) - #35
Conversation
…domain Post-squash adaptation for PR #15's merge commit 9ea7e73: the capture-recovery package was written against a pre-v0.3-fold domain where ExtractionAttempt was a numeric attempt counter and CaptureId was an unbranded string. The canonical domain renamed the counter to AttemptNumber (adaptation A5) and now brands CaptureId (Schema.NonEmptyString.pipe(Schema.brand)). - state.ts / events.ts: attempt fields typed AttemptNumber (the counter) - reducer.ts: createCapture input takes the branded CaptureId (type-only import; the pure reducer gains no runtime dependency) - replay.ts: decode the fixture captureId through the canonical CaptureId schema at the boundary (fail-closed, no casts) - test: decode branded CaptureIds at the four direct createCapture sites and in the storage listUnresolved/listReceipts expectations - README: correct the imported-type names No behavioral change to the recovery machine's truthfulness invariants; the six fixture scenarios, their expectations, and the discard-receipt semantics are unchanged.
Run log — repair verification (merge-owner)Tested head: Master breakage being repaired (reproduced on pure master
|
{
"manifestVersion": 1,
"pr": 35,
"prUrl": "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/OCPdev25/obv-hackaton/pull/35",
"testedHeadSha": "5f4edf79011bf47c2dbe15308ff4d18f29442beb",
"classification": "backend-only",
"classificationJustification": "Consumer-side schema adaptation inside packages/capture-recovery (pure TS package, no UI diff): renames the attempt-counter type to canonical AttemptNumber and takes the branded CaptureId at the boundary. packages/domain untouched.",
"review": {
"result": "pass-with-notes",
"reviewer": "merge-owner, parent-home recovery lane (authored the repair; read full diff against acceptance criteria in PR body)",
"reviewedHeadSha": "5f4edf79011bf47c2dbe15308ff4d18f29442beb",
"date": "2026-09-17",
"url": "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/OCPdev25/obv-hackaton/pull/35"
},
"checks": [
{ "name": "Typecheck, test, build", "status": "green", "headSha": "5f4edf79011bf47c2dbe15308ff4d18f29442beb", "url": "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/OCPdev25/obv-hackaton/actions/runs/35270508818" },
{ "name": "suites", "status": "green", "headSha": "5f4edf79011bf47c2dbe15308ff4d18f29442beb", "url": "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/OCPdev25/obv-hackaton/actions/runs/35270508921" }
],
"evidence": [
{ "kind": "test-run", "name": "full forced verification table: turbo typecheck/test/build 21/21 (0 cached) + focused recovery suite 20 pass / 0 fail / 107 assertions", "headSha": "5f4edf79011bf47c2dbe15308ff4d18f29442beb", "result": "pass", "url": "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/OCPdev25/obv-hackaton/pull/35#issuecomment-5720787057" },
{ "kind": "behavior-run", "name": "pure-master breakage reproduced (9ea7e73: runtime decode ReferenceError + build TS2322) vs green at tested head — master is red without this PR", "headSha": "5f4edf79011bf47c2dbe15308ff4d18f29442beb", "result": "pass", "url": "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/OCPdev25/obv-hackaton/pull/35#issuecomment-5720787057" }
],
"suites": {
"security": { "ran": true, "result": "pass", "headSha": "5f4edf79011bf47c2dbe15308ff4d18f29442beb", "url": "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/OCPdev25/obv-hackaton/pull/35#issuecomment-5720787057" },
"evaluation": { "ran": true, "result": "pass", "headSha": "5f4edf79011bf47c2dbe15308ff4d18f29442beb", "negativeControl": "fail-as-expected", "url": "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/OCPdev25/obv-hackaton/pull/35#issuecomment-5720787057" }
},
"notes": "Independent read-only review of this PR has been dispatched by the coordination lane (th_u6EixMrP); verdict pending at manifest-post time. Merge is withheld until that verdict lands; if the verdict moves the head, a fresh manifest will be posted (rule 5)."
} |
Independent review verdict — PASS (tested head
|
Acceptance criteria
@journal/capture-recoverytypechecks, tests, and builds against current master's canonical domain (post v0.3-foldExtractionAttempt→record rename /AttemptNumbercounter / brandedCaptureId), which the squash merge of PR feat(capture-recovery): interrupted-capture recovery machine + truthful one-handed UI projection #15 (9ea7e73) silently violated — breakingturbo testandturbo buildon master.captureIdstrings are decoded through the canonicalCaptureIdschema at the replay boundary and at test call sites — no casts, and the pure reducer gains only a type-only domain import.What changed and why
PR #15 was written against a pre-fold domain and merged cleanly textually, but the same-named types changed meaning on master between its base and the merge commit:
ExtractionAttemptmoved from a numeric counter (nowAttemptNumber, adaptation A5) to the attempt RECORD, andCaptureIdgained a brand. Result: runtime decode failure (Expected object at steps[2].event.attempt) in the fixtures test and a build error (plain string assigned to branded CaptureId) — master'sturbo test/buildred. This repair is mechanical adaptation only: rename the counter fields toAttemptNumber, take the brandedCaptureIdatcreateCapture, decode at boundaries.Verification at this head (5f4edf7) — merge-owner run, clean detached worktree
pnpm exec turbo run typecheck test build --force: 21/21 tasks, 0 cachedbun test ./security: 29 tests / 0 fail (49 expect() calls)cd evaluation && bun src/run.ts: 6/6 fixtures PASS--expect-failure: failed as expectedDisposition notes
packages/domainuntouched; adaptation is consumer-side, as the domain's own A5 comment prescribes.