Commit eef0826
fix(portal): PR #54 review round 2 — factory-alias hole, case-normalize, INV-17 candor
Fix round 2 on the INV-17 write-side idempotence guard (#53), per the gated review:
1. BLOCKER (factory-alias hole): the sync-store keys factory_addresses by STORE
IDENTITY (factory minus id/sourceId; sync-store/index.ts insertChildAddresses
~:459 and getChildAddresses ~:502 both strip { id, sourceId } and upsert/select
the factories row on the remaining value under UNIQUE (factory)). Two config
sources aliasing the same on-chain factory (identical minus id/sourceId) each
read empty then both insert -> duplicate child rows on the FIRST flush, breaking
the PR's own idempotence claim. The guard now canonicalizes the flush by store
identity (storeFactoryKey): ONE read + ONE dedupe + ONE insert per canonical
group. The mock store is RE-KEYED the same way the real store keys (strip
id/sourceId) — its prior factory.id keying is exactly why the hole was invisible.
New regression test 'alias-hole' (two aliased sources -> exactly one store row).
2. Case normalization: getChildAddresses returns stored text verbatim and
min-merges case-sensitively; children are lowercased at discovery. The persisted
lookup is now lowercase-normalized so a checksummed pre-existing row is still
deduped. New regression test 'case-normalization' (checksummed row -> no re-insert).
3. INV-17 doc (perf, candor): the guard is a live uncached getChildAddresses per
canonical pending factory per flush-bearing interval (worst ~O(N^2) over an
accreting backfill) — documented as a known cost with a per-process cache as the
follow-up candidate. Not implemented here (correctness first).
4. INV-17 wording: (a) the ×2-children observation is re-attributed to the chaos
DRIVER's two concurrent live instances (a fixed harness defect), NOT SIGKILL/
resume itself — consistent with the SCOPE section. (b) The single-writer claim is
upgraded from "sequential" to TRANSACTIONAL: read->dedupe->insert->insertIntervals
run in ONE tx-scoped syncStore transaction per interval (runtime/historical.ts
:1310-1352); residual risk is genuinely only the two-transactions/two-processes case.
Both gates green (0.16.6 and 0.15.17: 210/210, exit 0). Both mutations bite named
tests: revert canonicalization to factory.id -> alias-hole fails (expected 2 to be 1);
drop the lowercase-normalize -> case-normalization fails (length +0 but got 1). Biome
error-clean on the changed portal files.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>1 parent 164774a commit eef0826
3 files changed
Lines changed: 232 additions & 16 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
| 35 | + | |
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| |||
0 commit comments