Repository navigation
fix(setup): keep a Cordis operation's own fields when uninstall empties it - #2730
Conversation
…es it Uninstall dropped every patch operation whose insert list became empty after removing the rea-mcp row. A hand-edited operation that also patches a group (`id` with `config`) lost those fields and its comment, and the .rea.backup from first setup may not hold that edit. Remove only the emptied `insert` key and keep the operation when it holds a field besides its `id`/`name` selector. Still drop it when only the selector is left, or when it targets rea-mcp itself, since that would turn it into a direct patch of the removed registration.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
…cond REA row Setup refuses a REA row inside a group patch, so it reaches the keep rule only through an operation without a selector. Cover that path: the caller's field and comment stay, one REA row remains, and the registration reads back as aligned.
|
Thanks for merging, and for bringing it up to date with main. |
|
Thank you for catching this uninstall data-loss case, @JayOfTheKeyboard. #2730 now removes REA's row while preserving a Cordis operation's own fields, comments, and group configuration, with the important check that an emptied REA operation still gets removed. Great, concrete fix. |
Summary
rea uninstalldeletes a whole Cordis patch operation in~/.dsh/cordis.patch.ymlwhen REA'srea-mcprow was the only row in itsinsertlist. If that operation also carries the user's own fields, such as a group patch withidandconfig, those fields and the comment above them are lost. Uninstall still reportsremoved. No issue is linked: I found this while reviewing the DeepSeek Harness support from #1929, and the fix is small enough to explain here.Problem and expected behavior
Setup writes REA's row as its own operation,
- insert: [rea-mcp], and uninstall correctly drops that operation once the row is gone. A user can move the row into an operation that does more than insert, though, for example one that also patches a group:DshCordisDocument.serializedrops any operation whoseinsertlist ends up empty, so uninstall removesid: tools, itsconfigand the comment with it. REA's own parser reads a no-insert{ id: rea-mcp, config }operation as a patch of REA's row (the direct patch branch in the constructor), so by the same shape{ id: tools, config }is the user's patch oftools..rea.backupis written on first setup and kept after that, so it may not hold the user's later edit. For setup,docs/client-configuration.mdpromises to keep "unrelated patches and comments", and uninstall should keep them too.Measured with the built CLI under a scratch
HOMEonmainat0043d9b1(src/application/Dsh*and the test file are unchanged on currentmain,4276aaf1):rea setup --client dsh --yes, then the hand edit above, thenrea uninstall. Uninstall reportsdsh: removed, and the file is left with only theuser-aoperation. The backup holds only the originaluser-aoperation as well.Expected: remove REA's row and keep the rest of the operation, dropping the operation only when REA's row was everything in it.
Change and scope
In
DshCordisDocument.serialize(src/application/DshCordisPatch.ts):insertlist, delete only theinsertkey.idornameselector. The kept fields keep their comments and tags, since the operation is the same detached copy as before.- insert: [rea-mcp], and- id: toolswith only REA's row are still removed whole.idisrea-mcp. Kept withoutinsert, it would read back as a direct patch of REA's row, and uninstall's readback check would then reportfailed.Not changed, and yours to decide:
insert:key itself, is removed with it, as it was before..rea.backupis still written once, on first setup. Refreshing it on each uninstall would cover other hand edits too, but it changes the backup rule for every client.id,name) and REA's own id directly. Reusing the constructor'sdirectclassification on the kept operation would be the more general check if more key kinds turn up.<<: *anchor) are not read by the parser, so a REA row reached only through one is left in place on uninstall. That was already true before this change.Contract and boundary impact
DshCordisDocument.serialize, used byrea uninstall(and by setup when it collapses duplicate REA rows).docs/public/product-catalog.json), package, or installation impact: none.Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.
Evidence and regression coverage
tests/boundary/filesystem/dshClientConfiguration.test.ts, each throughsystemUninstallHost(home).removeClient:id,config, a comment above it) whose only row was REA's keepsid,configand the comment, and losesinsert;id: rea-mcpis dropped and uninstall reportsremoved;id,name, or both) is dropped;disabled: false) and a comment, keeps both, leaves one REA row and reads back as aligned. Setup refuses a REA row inside a group patch, so this is the only way setup reaches the rule. It fails onmain, which drops the field and the comment (added after review).mainat4276aaf1the group patch case fails withexpected [ { insert: [ { …(2) } ] } ] to deeply equal [ { insert: [ { …(2) } ] }, …(1) ]. The other 5 pass there too. They pin the cases where the operation must still go, which a plain "keep it if anything is left" rule gets wrong. Each part of the fix turns a test red when removed: keeping the emptyinsertkey fails 10 tests, dropping therea-mcpcheck fails 1, counting selectors as caller fields fails 3, leavingidornameout of the selector keys fails 2 each, and the old drop rule fails the group patch case.HOME.rea setup --client dsh --yeson a file holding oneuser-aoperation, then the hand edit from above, thenrea uninstall.mainat0043d9b1dshremovedremoveduser-aoperation# caller group patchid: toolswithconfig.sandbox: strictinsertcordis.patch.yml.rea.backupuser-aoperationuser-aoperation{ id, config }operation with noinsert, or an operation left with no selector at all (- insert: [rea-mcp]plusdisabled: falsebecomes- disabled: false). Both keep what the user wrote rather than guess.For evidence-bearing changes:
Validation performed
npx vitest runover the 20 test files whose path names setup, uninstall,dshorcordis: 227 tests passed.npm run check:fast: passed.npm run verify:test-discovery: "Discovered all 961 test files exactly once."npx oxfmt --checkon the 2 changed files: passed.Compatibility, safety, and release
Before merge
type(scope): outcome.