Repository navigation
Conversation
TL;DRThanks @gpechieu — this is a correct, well-scoped one-line fix with a test that genuinely earns its place. I'd merge it. What it does: Verdict: no malicious code, no NSFW, no prompt injection, no dependency changes. Typecheck green, 150/150 tests pass, and I independently confirmed the regression is real. One follow-up worth raising separately about backup retention — not a blocker for this PR. What I didChecked out the branch, ran typecheck and the full test suite, then re-introduced the one line you deleted ( The state machine, before and afterThe invariant you've landed on is the right one: What I tested
The test itself is well constructed: it uses a second One follow-up, separate from this PRBecause the guard no longer disarms on refusal, That isn't theoretical. On the machine I'm reviewing from: …all from ordinary Small notes
Questions@gpechieu — one design question, not a change request: the guard is per- @chaitanyagiri — want me to open the backup-retention issue (6,413 files / 153 MB with no pruning), or would you rather fold a cap into this PR? My recommendation is a separate issue: this fix is small and correct, and I'd rather it lands as-is than grows. |
The first-empty-write guard set `wrote = true` when it DECLINED a write, so a renderer reload that re-sent the same empty snapshot got through on the second attempt and flattened a non-empty roster (seen live: declined backup at 20:38:47, file flattened two minutes later). The guard now only disarms when a non-empty write lands — proof the renderer actually holds a roster — so a later empty write is a genuine deletion. Adds a regression test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
b63017d to
ed8bc84
Compare
|
Thanks for the review — and for re-introducing the deleted line to prove the regression test actually bites. That's the check I most wanted a second pair of eyes on. Your design question, verified against the code: the store is a main-process singleton — Branch updated: rebased onto current On backup retention: +1 to a separate issue, and here's a second datapoint for it: my daily-driver harnessHome sits at 579 files / 2.9 MB after ~2 weeks — nowhere near your 6,413 / 153 MB, but the growth curve is the same and it only points one way. A keep-last-N plus max-age prune on |
…ward or 4) RELEASE.md contradicted itself: line 16 said "four merged pull requests" (only tonight's four integration merges), the Thanks said "23 community pull requests landed in this release" (the 0.4.5 number copied forward), and the historical tail correctly says those 23 landed in v0.4.5. Recounted from the actual 0.4.6 changeset (main b9f34e7..release tip, linear so it is exactly this release). Method: a community PR is one authored by someone other than the founder (@chaitanyagiri) whose work landed here, whether merged or re-implemented. That gives 16 community PRs from 13 contributors: #156 #205 #213 #225 #242 #243 #248 #270 #271 #282 #284 #286 #310 #317 #323 #339. Fifteen landed as merges; #213 (Arabic) landed by re-implementation, which is why a merge-only count returns 15 and misses it. Line 16 and the Thanks now both say 16, the Thanks names all 13 contributors inline so the credit matches the count, and the 0.4.5 sections (its intro and its historical table) are left at 23 untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LDbmAwrgtGskFGCRkYZBx4
|
Shipped in v0.4.6. Your commit This closes rather than shows as merged because the commit went in through the 0.4.6 release branch instead of this PR's branch. The work is yours and it is in the build. https://github.com/chaitanyagiri/munder-difflin/releases/tag/v0.4.6 Sorry it sat open this long after landing. Thank you. |
Symptom
A non-empty
roster.jsonwas flattened to an empty one after a renderer reload. Live sequence (2026-08-16): the first empty mirror write of the run was correctly refused ([roster] refused to overwrite a non-empty roster with an empty one,declinedbackup written at 20:38:47) — but a reload re-sent the same empty snapshot and the second write went through, flattening the file two minutes later.Root cause
The empty-guard set
this.wrote = trueon the declined branch, so a refusal disarmed the guard: the very next empty write of the run was treated as a legitimate deletion.Fix
wrotestays false on a refusal and only arms when a NON-empty write lands — i.e. once the renderer has proven it actually holds a roster. After that, an empty write is a real deletion (refusing those would make deletion impossible). The comment block is updated with the real-world case.Validation
npm run typecheckclean;npm run test:focused131/131 pass.Found running the app as a daily driver for a multi-agent office.
🤖 Generated with Claude Code