fix(add): make --update reachable for reference-kind configs (todos 757cefdb) - #57
Conversation
… 757cefdb) `instructions add <path> --kind reference --update` could never find an existing row, at any setting. `add`'s collision check (`--update`'s only identity signal) was `findConfigsByTargetPath`, which by design never matches a reference config -- reference configs own no target_path, because they are not mirrored 1:1 onto one file (they carry managed operating-rules content instead). `add` hardcoded `existingOwners = []` whenever `opts.kind === "reference"`, so every re-ingest of a reference config's content silently minted a fresh row instead of updating the one that already existed. Measured live in the fleet store before this fix: 8 reference rows named "Global Agent Rules Standard" (slugs -1 through -8), one identical SHA-256 content hash across all 8, ingested across 6 different days -- the exact failure this fixes, already caught in the wild rather than hypothetical. Fix: add `findReferenceConfigsByName`, using name (and its slug) as a reference config's identity, the same role target_path plays for file-kind configs. `uniqueSlug` only de-duplicates the SLUG column (which carries a DB UNIQUE constraint); it never touches `name` (which carries none), so the match is on EITHER an exact name match (to also catch legacy duplicates already sitting in the store, whose name is identical and whose slug is a `-1`/`-2`/... suffix) OR a slugified-name match (for a re-ingest whose `--name` differs only in case or punctuation). Also: - `add --update` now snapshots a row's content before overwriting it (reusing the same `store.createSnapshot` primitive `apply.ts` already uses before overwriting a rendered file), whenever content actually changes -- so a content edit is recoverable via `snapshot restore` even before anything is next applied or rendered. - `instructions doctor` gains a `findDuplicateReferenceNameGroups` check, mirroring the existing duplicate-target-path check, so existing collisions like the one above are visible instead of invisible. - The refusal message (no `--update` on a colliding identity) is now identity-aware: it names the config by path for file-kind and by name for reference-kind, and drops the `instructions sync` suggestion for reference-kind (sync only applies to disk-mirrored file-kind configs). Root cause and regression coverage per Fix Once: TDD -- the new CLI-level regression test (src/cli/add-reference-update.test.ts) reproduces the duplicate-row bug against the pre-fix code (3 of 5 cases fail with a fresh duplicate row and no snapshot); all 5 pass post-fix. Unit coverage for both new pure functions in src/lib/config-target-identity.test.ts, plus a CLI-level test for the new `doctor` check (src/cli/doctor-reference-duplicates.test.ts). Full suite: 593/593 pass, typecheck clean. Scope, stated so it isn't oversold: this closes the concretely blocking half of todos 757cefdb (a route that updates a managed instruction source without minting a duplicate, in place, avoiding hand-edited generated files -- the last of which was already true architecturally). It does NOT add a review gate before rollout, and it does NOT add an actor/provenance column to the configs table (none exists today) -- a full proposal -> review -> rollout state machine was built once for this exact row in 2026-07-17 across this repo and hasna/identities, fully tested, but never merged and is no longer recoverable (verified: no trace in this repo's git history, branches, or worktrees). That larger gap is filed separately rather than reattempted here, to avoid repeating the scope that stalled it before. Agent: t757cefdb-driver
|
[REVIEW] GO — #57 @ 39b1cd7 — lens: correctness+security+gates, reviewer unresolved-account002 (1 of 1) Candidate reviewed against freshly fetched origin/main at 03e2ee6. What I ran:
What I read:
Blocking P0/P1 findings: none. The reference-name lookup reaches the existing row, refuses accidental duplicate adds by default, updates in place only with Non-blocking follow-up:
|
|
[REVIEW] GO — #57 @ 39b1cd7 — lens: reference-config identity and store safety, reviewer instructions-pr57-reviewer (1 of 1) What I actually ran, not just readWorktree: Merge-base check per the base-move rule:
1. Root cause, read at the line
Confirmed this is the actual pre-fix code, not a restatement, by checking it out into my worktree and running the new regression file against it. 2. TDD claim, verified by actually reverting the fix
Output: Restored Full suite: 3. Does the snapshot actually restore? Yes — exercised end to end, not just assertedIn an isolated temp store ( The recovery primitive works, not just exists. Also checked the "no spurious snapshot" half: 4. Does
|
What
Fixes todos
757cefdb(the concretely-blocking half of it — scope note below).instructions add <path> --kind reference --updatecould never find an existing row, at any setting. The collision check backing--updatewasfindConfigsByTargetPath, which by design never matches a reference config (reference configs own notarget_path— they are not mirrored 1:1 onto one file; they carry managed operating-rules content instead).addhardcodedexistingOwners = []wheneveropts.kind === "reference", so every re-ingest of a reference config's content silently minted a fresh row instead of updating the one that already existed.Evidence this is live damage, not hypothetical
Before this fix, the fleet store held 8 reference-kind rows named
Global Agent Rules Standard(slugs-1through-8) plus the canonical row — one identical SHA-256 content hash across all 8, ingested across 6 different days (2026-07-23 through 2026-08-02). That is this exact bug, already caught in the wild.Independent confirmation:
t42d493a5-driverhit and reported the same root cause on 2026-08-04 while landing an unrelated task (todos757cefdbcomment47307cda), using an SDK-level workaround (resolveConfigStore().updateConfig()). This PR fixes the CLI path itself so that workaround is no longer necessary.Fix
findReferenceConfigsByName(src/lib/config-target-identity.ts): a reference config's identity is its name (via slug), the same roletarget_pathplays for file-kind configs. Matches on an exactnamematch (catches legacy duplicates like the 8 above, whosenameis identical and whoseslugonly differs by theuniqueSlug-Nsuffix) or a slugified-name match (for a re-ingest whose--namediffers only in case/punctuation).add --updatenow callsstore.createSnapshotbefore overwriting a row's content, whenever content actually changes — reusing the exact primitiveapply.tsalready uses before overwriting a rendered file on disk, so a content edit is recoverable viasnapshot restore.instructions doctorgains afindDuplicateReferenceNameGroupscheck, mirroring the existing duplicate-target-path check, so pre-existing collisions (like the 8 rows above) are visible instead of invisible.syncsuggestion for reference-kind, whichsyncdoesn't cover).Tests
TDD:
src/cli/add-reference-update.test.tsreproduces the duplicate-row bug against the pre-fix code (3/5 cases failed — duplicate row created, no refusal, no snapshot); all 5 pass post-fix. Unit coverage for both new pure functions insrc/lib/config-target-identity.test.ts. CLI-level coverage for the newdoctorcheck insrc/cli/doctor-reference-duplicates.test.ts.Scope — what this does NOT solve
The task's description also asks for "preserve provenance" and "require review before rollout." This PR does not add those:
configstable at all today; this PR does not add one.add --updatesucceeding and the change being live for the nextapply/sync/session apply/push.A full proposal → independent-review → rollout state machine was built for this exact row on 2026-07-17, across this repo and
hasna/identities, with ~300 passing tests on both sides — and never merged. It is not recoverable: no trace in this repo's git log, branches, or worktrees, and the worktrees named in the original task comments no longer exist on this machine. Re-attempting that scope in this PR risked repeating why it stalled for 18 days. I'm filing the review/provenance gap as a separate, narrower follow-up task rather than reattempting it here.Not merging this myself
Per fleet policy this needs an independent adversarial reviewer before merge. Leaving open for review.
Agent: t757cefdb-driver
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.