From 526aef857a0bd7724278eb864a2520a9ffcdbaf4 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 09:08:25 +0900 Subject: [PATCH 1/4] docs(devlog): record the WP5 outcome for #1849 and #1049 #1849 was two defects wearing one issue number. #1877 stopped the service wrapper hammering a missing install and shipped in v2.24.0; nothing has been done about how the install went missing, which is npm install -g removing the existing package before the replacement is verified. That half is now #1942 and the umbrella stays open so the original evidence stays attached to it. #1049 I assessed and did not start. The design is complete in the write-substrate contract and none of it exists: adoption-pending appears 37 times in the devlog and zero times in src. What is missing is not an adoption branch but the whole publication protocol - a complete-database temp publisher, atomic no-clobber publication, fsync ordering, and the positive-authority gate. That is crash-safety machinery for durable state in the user's Codex home, where a subtly wrong publication corrupts an install rather than failing a test. It is implemented completely with its fixture matrix or not at all, and squeezing a partial version into a wave beside unrelated fixes is the worst of the options. --- .../050_1849_1049_durability.md | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md b/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md index 4e93d66425..97ebf0aa41 100644 --- a/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md +++ b/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md @@ -64,3 +64,38 @@ stops at `ambiguous` rather than guessing. #1849 stays OPEN as the transactional-updater issue with A recorded as shipped. #1049 closes only on landed adoption + crash-recovery evidence; otherwise it is reported as a real terminal outcome, not silently dropped. +## Outcome (executed) + +**#1849 — split, umbrella kept open.** The report carried two defects with different fixes +and different closure conditions: + +| Half | Defect | Status | +|---|---|---| +| A | Service wrapper restart-loops against a missing install | Fixed by #1877, released in v2.24.0 (`git tag --contains 01b212579`) | +| B | `ocx update` deletes the working install with no rollback | **#1942**, open | + +#1849 stays open as the umbrella so the original evidence — 107 directories / zero files, every +launcher gone from `%APPDATA%\npm`, 1,009 restarts over 89 minutes — stays attached to the +story instead of being split away from it. #1942 carries the transactional shape, the ordering +constraint (never delete the existing install before the replacement is verified), and the +kill-at-every-boundary matrix. + +**#1049 — assessed, not started, and the reason is in the issue.** The gap is exactly as +described: `decideCoordination` returns `legacy-uncoordinated` for routed homes without a +coordinator row, routing around a refusal that is itself correct. + +What made me stop is the size of what is missing. `grep -r 'adoption-pending' src/` returns +**zero** matches against **37** in `devlog/_fin/260804_codex_write_substrate/`. The design is +complete and none of it is built: the complete-database temp publisher, atomic no-clobber +publication (exclusive hard link or rename-without-replace, with an ordinary replacing rename +forbidden), fsync before publication and parent-directory fsync after, `adoption-pending` +accepted as a ready state by ordinary openers, and the positive-authority gate. + +This is crash-safety machinery for durable state in the user's Codex home. A subtly wrong +publication corrupts an install rather than failing a test, and the contract's crash boundary +is the kind of property that is implemented completely with its fixture matrix or not at all. +Landing a partial version inside a wave next to unrelated fixes would be the worst option +available. + +Terminal outcome for #1049: **NEEDS_HUMAN on scheduling** — well-specified, unstarted, and +deserving its own work-phase. From 2d3de036aea6a0a18b835491647921fb10d1b923 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 09:14:13 +0900 Subject: [PATCH 2/4] docs(devlog): fold the WP5 audit corrections Three factual fixes and one scoping fix, all from the review of this phase. The symbol name was wrong - codexWriteCoordinationEligibility, not decideCoordination, which greps to nothing. In a note written specifically so the next attempt does not have to rediscover the shape, that is the error that actually costs someone time. #1942 claimed nothing verifies the post-install tree can boot. Verification does exist; it is detect-only and runs after the old install is already gone, which is a narrower claim and a worse defect. Corrected on the issue. The #1049 gap is bigger than I recorded: the contract forbids opening a missing final path with create:true, and transition-state.ts does exactly that, so the publisher work rewrites the create path every clean install uses rather than a legacy branch. That widens the blast radius and makes the defer more clearly right. The defer itself held, but the monolith framing did not. The temp-database publisher for the ordinary clean row is a prerequisite that stands on its own and removes the zero-byte first-create window for everyone, so the remaining work is two reviewable phases rather than one. --- .../050_1849_1049_durability.md | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md b/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md index 97ebf0aa41..c6e3df1ad6 100644 --- a/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md +++ b/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md @@ -2,7 +2,7 @@ ## #1849 — split, do not auto-close -v2.24.2 shipped #1877, which stops the service restart loop when the install is +v2.24.0 shipped #1877 (merge `01b212579`), which stops the service restart loop when the install is missing. That is the mitigation half. The root defect is the non-atomic update transaction: download → delete existing files → copy new files, which leaves a file-less package skeleton when it fails midway. @@ -99,3 +99,27 @@ available. Terminal outcome for #1049: **NEEDS_HUMAN on scheduling** — well-specified, unstarted, and deserving its own work-phase. +### Corrections from the WP5 audit + +Three, all folded: + +1. **Wrong symbol name.** I cited `decideCoordination`; it greps to nothing. The real export + is `codexWriteCoordinationEligibility` (`src/codex/inject-coordination.ts`). The described + behaviour was right — only the name was wrong, which is the one error that costs the next + reader time in a note written to save them the rediscovery. +2. **#1942 overstated the gap.** It said nothing verifies the post-install tree can boot. + Verification exists — `checkUpdatePackageIntegrity` fails closed before the proxy stops, + `isRealBunBinary` rejects the postinstall stub, and `awaitRestartedProxyHealthy` runs an + identity-checked probe. The accurate claim is narrower and worse: it is **detect-only and + runs after the old install is gone**, so a failed probe has nothing left to restore. +3. **The #1049 gap is larger than recorded, which strengthens the defer.** The contract forbids + opening a missing final path with SQLite `create:true`; `src/codex/transition-state.ts` does + exactly that today. So the temp-publisher work rewrites the create path **every clean + install** uses — the blast radius is the whole installed base, not the legacy subset. + +And one scoping correction worth carrying forward: the deferral was right, but the monolith +framing was not. A prerequisite subset stands alone — the complete-temp-database publisher plus +atomic no-clobber publication, **ordinary clean row only**, with no `adoption-pending`, no +authority gate, no callback plumbing. It closes nothing by itself, but it removes the visible +zero-byte window on first create for every user and splits the remaining work into two +reviewable phases instead of one. From 66905cca2746dded8f8a7e2f7dbd468a728d9c7b Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 09:16:19 +0900 Subject: [PATCH 3/4] docs(devlog): fix the symbol name inline, not just in the errata The errata section recorded the correction, but the narrative still said decideCoordination - so grepping the name a future reader would try still hit the wrong line first, with the fix seventy lines further down. Correcting a name only in an appendix is the failure mode the errata was written to prevent. --- devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md b/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md index c6e3df1ad6..0e9d900305 100644 --- a/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md +++ b/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md @@ -81,7 +81,7 @@ constraint (never delete the existing install before the replacement is verified kill-at-every-boundary matrix. **#1049 — assessed, not started, and the reason is in the issue.** The gap is exactly as -described: `decideCoordination` returns `legacy-uncoordinated` for routed homes without a +described: `codexWriteCoordinationEligibility` returns `legacy-uncoordinated` for routed homes without a coordinator row, routing around a refusal that is itself correct. What made me stop is the size of what is missing. `grep -r 'adoption-pending' src/` returns From 3dcac90ae056d4467b6d076ee11c246c0e81e157 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 09:18:19 +0900 Subject: [PATCH 4/4] docs(devlog): map where the split work now lives The WP5 outcome named four trackers across three sections; this puts them in one table so the next person does not have to reassemble the split from prose. Worth stating explicitly: the two open items are unstarted for different reasons. #1942 is ordinary work nobody has done. #1049 is unstarted because doing it right means rewriting the coordinator create path every clean install already uses - a blast-radius decision rather than a backlog position, and the kind of distinction that gets lost when an issue just sits there. --- .../050_1849_1049_durability.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md b/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md index 0e9d900305..e810c51d73 100644 --- a/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md +++ b/devlog/_plan/260817_wave5_execution/050_1849_1049_durability.md @@ -123,3 +123,17 @@ atomic no-clobber publication, **ordinary clean row only**, with no `adoption-pe authority gate, no callback plumbing. It closes nothing by itself, but it removes the visible zero-byte window on first create for every user and splits the remaining work into two reviewable phases instead of one. +### Where this work now lives + +| Tracker | Scope | State | +|---------|-------|-------| +| #1849 | Umbrella; holds the original evidence (107 dirs / 0 files, missing launchers, 1,009 restarts) | open | +| #1877 | Service wrapper stops restart-looping a missing install | merged `01b212579`, released v2.24.0 | +| #1942 | Transactional updater: stage → verify → snapshot → atomic switch → probe → restore | open, unstarted | +| #1049 | Coordinator adoption for pre-substrate homes | open, unstarted, needs its own phase | + +The two open items are unstarted for different reasons, and the distinction matters when +someone picks them up. #1942 is unstarted because it is ordinary work nobody has done yet. +#1049 is unstarted because doing it correctly means rewriting the coordinator create path +that every clean install already uses — a decision about blast radius, not a backlog +position.