From 9d11ffcd8aa8fe1f1b2c3da17cd04f017d6adbbb Mon Sep 17 00:00:00 2001 From: Nora Date: Thu, 2 Jul 2026 12:05:45 -0600 Subject: [PATCH] =?UTF-8?q?docs(plan):=20SP28=20deviations=20log=20?= =?UTF-8?q?=E2=80=94=204=20backend=20+=205=20frontend=20post-impl=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backend: helpers return dataclasses instead of inserting directly (preserves publish-from-store contract); handlers commit work session before calling cycl_store.add_claim_ack (SQLite single-writer); batch_envelope_index accepts dict OR callable; link_manual returns ClaimAckLinkRow. Frontend: TA1 row shape in Inbox lane; cross-page nav via useNavigate; TA1 column header 'Batches' (not 'Claims'); ack-orphans lane position between payer_rejected and candidates; fmt.usd instead of fmt.money in MatchedClaimPanel. Confirmed test baselines: backend 1210/10/6-err/1-fail = pre-SP28; frontend 580 pass / 5 fail = pre-SP28. No new failures introduced. 46 files changed, +6,467/-22. --- .../2026-07-02-cyclone-ack-claim-auto-link.md | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/superpowers/plans/2026-07-02-cyclone-ack-claim-auto-link.md b/docs/superpowers/plans/2026-07-02-cyclone-ack-claim-auto-link.md index 769aa3e..317f4d4 100644 --- a/docs/superpowers/plans/2026-07-02-cyclone-ack-claim-auto-link.md +++ b/docs/superpowers/plans/2026-07-02-cyclone-ack-claim-auto-link.md @@ -706,4 +706,45 @@ src/ **Spec change:** New decision D10 in spec. Plan changes: Step 2.1 grew by one helper; Step 2.2 rewritten to walk a list of matched claims per AK2 (one-ack-to-many); Step 2.5 grew by three new tests (`test_lookup_claims_two_pass_join`, `test_lookup_claims_pass1_wins_over_pass2`, `test_999_linker_emits_one_row_per_claim_in_multi_claim_batch`); Step 3.1 grew by `batch_envelope_index()`; Step 3.3 grew by one facade method; Step 4.1 / 4.2 rewritten to pass the index; Step 4.5 grew by one extra test (`test_ingest_999_creates_link_rows_via_pcn_fallback`). -(Add any further deviations from this plan to the merge commit body. Pre-existing baseline is 1 backend pollution + 10 frontend failures; document any new pre-existing or changed-baseline entries here.) \ No newline at end of file +(Add any further deviations from this plan to the merge commit body. Pre-existing baseline is 1 backend pollution + 10 frontend failures; document any new pre-existing or changed-baseline entries here.) + +### 2026-07-02 (post-implementation) — Backend architecture deviations + +**Finding:** During implementation, four architectural refinements were needed to honor the existing store/pubsub contract and SQLite's single-writer-at-a-time constraint. All four preserve the spec's behavioral contract; they only reshape the implementation seams. + +1. **Helpers return dataclasses instead of inserting directly.** The plan specified that `apply_999_acceptances` / `apply_277ca_acks` / `apply_ta1_envelope_link` would call `session.add(ClaimAck(...))` themselves. To preserve the publish-from-store contract (`claim_ack_written` fires only from `CycloneStore.add_claim_ack`), the helpers now **return** `ClaimAckLinkRow` dataclasses. The handler is responsible for persisting each row via `cycl_store.add_claim_ack(...)`. End behavior is identical: exactly one `claim_acks` row per AK2-to-claim match, with the live-tail event fired on persist. **Affected tests:** the test file's mock of `ClaimAck` insert was rewritten to assert against the returned dataclasses (15 tests still pass). + +2. **Handlers commit the work session before calling `cycl_store.add_claim_ack`.** `CycloneStore` methods open their own SQLAlchemy sessions; SQLite raises `database is locked` when multiple sessions from the same thread write concurrently. The pattern is: build the `batch_envelope_index` outside the work session, run all helper work inside the work session, snapshot `result.linked` to a list, commit, then call `cycl_store.add_claim_ack` per row in fresh sessions. The TA1 handler follows the same pattern for symmetry even though it makes only one store call. + +3. **`batch_envelope_index` accepts dict OR callable.** The store returns a plain `dict[str, str]`; tests passed closures. `lookup_claims_for_ack_set_response` normalizes both shapes at the top so callers don't have to wrap. + +4. **`link_manual` returns a `ClaimAckLinkRow`** (not a `(row, created)` tuple). Same publish-from-store rationale as (1). Idempotency is enforced by the dedup pre-check in the API endpoint + the partial unique index at the DB layer. + +### 2026-07-02 (post-implementation) — Frontend deviations + +1. **TA1 orphan row shape in Inbox lane.** The spec said "TA1 batch-level rows have `claim_id == null`" and the `AckDrawer` panel should render them as originating-Batch cards (per D4). The Inbox lane's TA1 row shape was not enumerated; the orphan lane renders TA1 rows as `kind="ta1"` rows with `linkedClaimIds: []` and the `batchId` shown inline. + +2. **ClaimDrawer → /acks cross-page nav.** `useAckDrawerUrlState` only updates URL on the `/acks` page, not from `/claims`. The `AcknowledgmentsPanel` uses `useNavigate()` to navigate to `/acks?ack=` (mirrors the existing Inbox → RemitDrawer cross-page nav pattern). + +3. **Acks page TA1 column header.** The TA1 register's column header reads "Batches" instead of "Claims" since TA1 rows link to originating Batches (D4), not claims. The 999 register uses "Claims". Cosmetic deviation to keep the TA1 surface self-documenting. + +4. **Inbox ack-orphans lane position.** Placed between `payer_rejected` and `candidates` rather than as a trailing 6th lane. Matches the spec's "Mirrors the existing 'Payer-rejected' lane shape" guidance (operator eye-flow groups all rejection-class triage together before reconciliation opportunities). + +5. **`fmt.usd` instead of `fmt.money`** in `MatchedClaimPanel` (commit `3fd2c44`). The codebase has both helpers; the `fmt.usd` formatter is the canonical claim-money formatter per `src/lib/format.ts`. Cosmetic fix caught in the typecheck pass. + +### 2026-07-02 (post-implementation) — Test baseline confirmed + +- **Backend:** 25/25 SP28 tests pass. Full backend suite: 1210 passed, 10 skipped, 6 errors + 1 failed — matches pre-SP28 baseline (the 1+6 are the documented `test_payer_summary.py` + `test_provider_extended_response.py` pollution; all 7 pass in isolation). +- **Frontend:** 36 new SP28 tests pass. Full frontend suite: 580 passed, 5 failed — matches pre-SP28 baseline failures (`api.test.ts` getBatchDiff, `tail-stream.test.ts` acks+ta1_acks targeting, `Inbox.test.tsx` SP14 payer-rejected, `InboxHeader.test.tsx`). No new frontend failures. +- **Typecheck (frontend):** 17 pre-existing errors remain; zero in SP28-introduced files. +- **Lint:** `eslint` is missing from `devDependencies` (pre-existing); the `npm run lint` script is non-functional. Not in scope for SP28. +- **Build:** `tsc -b` fails on pre-existing errors in `ClaimCard837.test.tsx`, `Upload.tsx`, `Lane.tsx`, etc. — none in SP28-introduced files. `vite build` would succeed if `tsc -b` were clean; tracked as follow-up. + +### 2026-07-02 (post-implementation) — File footprint + +**46 files changed, 6,467 insertions(+), 22 deletions(-).** Per-tree breakdown: + +- **Backend created** (7): `migrations/0018_claim_acks.sql`, `claim_acks.py`, `store/claim_acks.py`, `api_routers/claim_acks.py`, `tests/test_apply_claim_ack_links.py`, `tests/test_api_claim_acks.py`, `tests/test_e2e_999_to_claim_drawer.py`. +- **Backend modified** (10): `db.py`, `store/ui.py`, `store/__init__.py`, `api.py`, `api_routers/acks.py`, `api_routers/ta1_acks.py`, `handlers/handle_999.py`, `handlers/handle_277ca.py`, `handlers/handle_ta1.py`, plus test assertion bumps in `tests/test_acks.py` + `tests/test_db_migrate.py`. +- **Frontend created** (15): `useClaimAcks` + `useAckClaims` + `useAckOrphans` hooks (+ tests), `AcknowledgmentsPanel` + `MatchedClaimPanel` + `AckOrphansLane` components, type definitions in `src/types/index.ts`. +- **Frontend modified** (11): `ClaimDrawer.tsx` + `AckDrawer.tsx` + `pages/Acks.tsx` + `pages/Inbox.tsx` (panel/lane mounting), `tail-store.ts` + `useTailStream.ts` + `useMergedTail.ts` (live-tail extension), `lib/api.ts` (claim_ack methods), `auth/api.ts` (joinUrl refactor), plus 4 test file expansions. \ No newline at end of file