Skip to content

fix(capture-recovery): align attempt/capture-id types with canonical domain (post-squash adaptation) - #35

Merged
obvious-autobuild[bot] merged 1 commit into
masterfrom
fix/capture-recovery-domain-adaptation
Sep 17, 2026
Merged

obvious-autobuild[bot] merged 1 commit into
masterfrom
fix/capture-recovery-domain-adaptation

Conversation

@obvious-autobuild

Copy link
Copy Markdown
Contributor

Acceptance criteria

  1. @journal/capture-recovery typechecks, tests, and builds against current master's canonical domain (post v0.3-fold ExtractionAttempt→record rename / AttemptNumber counter / branded CaptureId), which the squash merge of PR feat(capture-recovery): interrupted-capture recovery machine + truthful one-handed UI projection #15 (9ea7e73) silently violated — breaking turbo test and turbo build on master.
  2. Zero behavioral change to the recovery machine: the six fixture scenarios, their expectations, truthfulness invariants (saved only via SubmitAccepted, idempotent retry, stale-result discard, discard receipt), and discard-receipt semantics are unchanged.
  3. The trust boundary stays fail-closed: fixture captureId strings are decoded through the canonical CaptureId schema at the replay boundary and at test call sites — no casts, and the pure reducer gains only a type-only domain import.
  4. Full local verification table green at this head (below), and the standalone suites (security, evaluation + negative control) pass — required by the packages/ diff.

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: ExtractionAttempt moved from a numeric counter (now AttemptNumber, adaptation A5) to the attempt RECORD, and CaptureId gained 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's turbo test/build red. This repair is mechanical adaptation only: rename the counter fields to AttemptNumber, take the branded CaptureId at createCapture, 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 cached
  • bun test ./security: 29 tests / 0 fail (49 expect() calls)
  • cd evaluation && bun src/run.ts: 6/6 fixtures PASS
  • negative control --expect-failure: failed as expected
  • focused capture-recovery suite: 20 pass / 0 fail / 107 expect() calls

Disposition notes

  • Authored by the serialized merge owner for the parent-home recovery lane while master is red (blocks every open PR's merge-ref CI). Reviewer of record is the merge owner; the mechanical scope (6 files, 23+/15−, no behavior change) is fully visible in the diff, and the verification gate + CI enforce the mechanical checks.
  • packages/domain untouched; adaptation is consumer-side, as the domain's own A5 comment prescribes.
  • Unlocks: PR feat(catch-up): since-last-seen caregiver catch-up — contract delta v0.1 + deterministic acceptance cases #33 rebase merge (its merge-ref CI needs green master), composite prototype PR, and every other open PR's CI.

…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.
@obvious-autobuild

Copy link
Copy Markdown
Contributor Author

Run log — repair verification (merge-owner)

Tested head: 5f4edf79011bf47c2dbe15308ff4d18f29442beb (branch fix/capture-recovery-domain-adaptation)

Master breakage being repaired (reproduced on pure master 9ea7e73)

  • pnpm exec turbo run test → FAILED: @journal/capture-recovery#test, 0 pass / 1 fail — ReferenceError: ExtractionAttempt is not defined at src/state.ts:34:12 (Effect fixture decode at steps[2].event.attempt).
  • Mirror-lane independent reproduction: typecheck exit 2 with 14 TS errors on pure master; build fails at src/reducer.ts(34,3) — Type 'string' is not assignable to type 'string & Brand<"CaptureId">'.
  • Root cause: PR feat(capture-recovery): interrupted-capture recovery machine + truthful one-handed UI projection #15 merged textually clean but was written against the pre-A5/pre-brand domain; ExtractionAttempt is now the attempt record (numeric counter = AttemptNumber) and CaptureId is branded. Master's turbo test/build red → CI merge gate red for every open PR.

Green at 5f4edf7 (clean detached worktree, repair applied)

  • pnpm exec turbo run typecheck test build --force → 21/21 tasks, 0 cached
  • bun test ./security → 29 tests / 0 fail (49 expect() calls)
  • cd evaluation && bun src/run.ts → 6/6 fixtures PASS
  • bun src/run.ts --adapter=./src/example/broken-adapter.ts --expect-failure → failed as expected
  • Focused capture-recovery suite → 20 pass / 0 fail / 107 expect() calls

CI on this head

Scope: 6 files, +23/−15, packages/capture-recovery only; no behavioral change to the six fixture scenarios or the truthfulness invariants.

@obvious-autobuild

Copy link
Copy Markdown
Contributor Author
{
  "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)."
}

@obvious-autobuild

Copy link
Copy Markdown
Contributor Author

Independent review verdict — PASS (tested head 5f4edf79011bf47c2dbe15308ff4d18f29442beb)

Independent read-only review per .obvious/obvious.md (reviewer lane, todo_5nAlqQda; merge ownership stays with the recovery lane — this reviewer does not merge).

Scope (minimal-repair claim): confirmed. Diff vs master 9ea7e73 is exactly 6 files, +23/−15, all under packages/capture-recovery/**. No domain, backend, apps, evaluation, security, verification, or .github changes.

Master breakage — reproduced on pure 9ea7e73 (isolated uncached worktree, reviewer-run): focused test fails at the fixture decode — error: Expected object at ["steps"][2]["event"]["attempt"]; pnpm run typecheck exits 2 with TS2769 (plain string vs Brand<"CaptureId">); turbo test/build cascade red. Zero PR-35 files exist at that SHA. Master is red without this PR.

Fix verified at 5f4edf7 (reviewer-run, 0 cached):

  • pnpm exec turbo run typecheck test build --force → 21/21 tasks, 0 cached
  • bun test ./security → 29 pass / 0 fail
  • cd evaluation && bun src/run.ts → 6/6 PASS; --expect-failure negative control → failed as expected
  • pnpm --filter @journal/month-history journeys → 28/28 checks
  • focused capture-recovery suite → 20 pass / 0 fail / 107 expect()

Acceptance criteria: (1) ✓ aligns with canonical domain — AttemptNumber = Schema.Int counter and ExtractionAttempt = Schema.Struct record + branded CaptureId confirmed in packages/domain (A5 comment in extraction.ts prescribes exactly this consumer-side adaptation); (2) ✓ mechanical type adaptation only — event/state shapes structurally identical at runtime, six fixture scenarios and truthfulness invariants untouched; (3) ✓ decode via Schema.decodeUnknownSync(CaptureId) at the replay boundary and all test call sites, no casts, the pure reducer gains only a type-only import; (4) ✓ full verification table green (above).

Manifest gap — closed. The earlier coordination probe reported manifest_missing; a verification-manifest:v1 block was posted at 20:28:32Z (comment 5720789835). bun verification/src/cli.ts validate-pr --pr=35 validates it against live head 5f4edf7 → decision merge.

CI: both workflows completed/success on this exact head (runs 35270508818, 35270508921). Comments: both issue comments are owning-lane status artifacts (run log + manifest); zero review feedback outstanding.

Verdict: pass. The recovery lane (merge owner) can proceed to merge per the verification gate.

@obvious-autobuild
obvious-autobuild Bot merged commit 5949812 into master Sep 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants