claude agent, operated by paul@keymail.devoperated by paul@keymail.devsession k3-codex · codex · MacBookPro.localdomain
Implement K3 of rastrillo/native offline-kit spec 6049830: command durability watermark, sealed replica persistence, reopen as replica plus lane, and crash/convergence tests. Uses native feat/k3-outbox. Full local gates and kill -9 evidence required. Stop before merge or deployment.
claude agent, operated by paul@keymail.devoperated by paul@keymail.devsession k3-codex · codex · MacBookPro.localdomain
Stopped at the user's mandatory disk threshold: free space reached 1.2 GiB (<1.5 GiB). WIP preserved/pushed at f5cf9d1, pinned to native 6b6b198. No merge/deploy. Hub Docs CI currently running.
Committed docs/testing/k3-evidence.md contains the handoff, explicit spec differences, exact test transcripts and build-output ownership. Three initial durability tests failed against no-op persistence/capture, then passed. Targeted replay omission failed the model-reopen and every-replace-boundary tests on missing text. Unlock/queued-input regression failed before its fix. The focused final run passed 10 tests (6 durability + 4 account boundaries). An earlier full Swift run had two account-return assertions failing, subsequently fixed in that focused run; full rerun outstanding.
The highest-capability available reviewer, gpt-6-astra, performed two read-only reviews. Fixed discard writer invalidation, competing title senders, unlock queue resumption, local failure recovery, draft worker start and independent-lane starvation. Remaining findings: (1) missing-replica recovery loses cached pending title; (2) crash between attachment index and metadata leaves a draft stuck; (3) a second rename during POST and title-only transient failures lack reliable retry; (4) Home account selection does not start pending replay. Additional UI/fake-stream/cap auditing remains.
The parked-update Rust test has been ported but not run/red-proven. The SIGKILL harness did kill a real native test process after 12 durable edits against real authenticated Docs handlers. With replay intentionally omitted it failed on missing text, but initial-save and server-convergence assertions also failed, so this is not yet valid red/green proof. The mutation was restored before commit.
Neither .amadan/ci.d/10-gate nor the full native/scripts/check-native.sh was run. Native's earlier make ci passed before its latest changes; its final full run is also outstanding. Session-created build outputs were removed; inherited native .build preserved. Resume only with sufficient disk headroom. Branch is WIP, not approved for merge.
claude agent, operated by paul@keymail.devoperated by paul@keymail.devsession k3-codex · codex · MacBookPro.localdomain
SIGKILL now passes (8265985). Root cause of the setup failures was the fixture origin: configured localhost:8080 versus NativeHTTP's actual random loopback origin. A native-style POST reproduced 403 before fixing the configuration; CSRF remains enabled.
Negative control: remove only lane replay. The real killed process saves initially and the server converges, but offline reopen loses all 12 durable edits (one assertion failure). Restore replay: exact offline recovery, real server convergence and no duplicate text all pass. The script now stops on initial-save/pre-kill typing assertions. Evidence is in docs/testing/k3-evidence.md.
Continuing parked-update red/green, title/draft/Home recovery, then serial final-head gates. No merge or deployment. Data volume currently ~10 GiB free; intermediate host/build outputs from this run removed.
claude agent, operated by paul@keymail.devoperated by paul@keymail.devsession k3-codex · codex · MacBookPro.localdomain
Recovery/review fixes are pushed at ced4f54 (following b14105c and the SIGKILL fixture commit 8265985). gpt-6-astra reviewed the branch and the subsequent changes; its final cap-on-reopen finding is fixed and has red/green evidence. No merge/deploy.
Resolved: durable title without a first replica; failed title persistence/unlock; title-only retry; newer title after the previous title POST but before batch ack; draft identity/index crash recovery; Home-only replay and safe cache-before-ack; shared replica merge/discard invalidation; lost-key/corrupt-entry notice; cap recovery/reopen; initial draft paragraph retained when its first metadata write fails. The integrating fake now serves since and subsequent stream sequences.
Failing-first details and exact logs are in docs/testing/k3-evidence.md. Latest broad focused pass: 24 durability/account tests. The final seed/cap tests additionally failed exactly on missing server text and a reopen bypass, then both passed. Draft attachment sweeps every actual store operation; cache/discard mutations produce six assertions. The corrected in-flight-title probe fails exactly First != Successor before its fix.
Final-code SIGKILL rerun PASS: 12 durable edits, kill before cache write, exact offline reopen, real authenticated Go server convergence, no duplicates (/tmp/k3-kill-final.log). Its valid negative control omits only lane replay and fails exactly offline missing text. Parked-update test: encodediffv1 red, existing snapshot green; no new op.
The existing draft integration fixture also passes. It previously returned Untitled for all detail GETs even after accepting a rename; now it snapshots the actual title at GET start, retaining delayed stale-response behavior.
Spec differences: replica-before-lane discard (user-approved subject to crash proof, now swept); index registration of stable keys and metadata marker entries to close metadata/enqueue and attachment crash windows. Equality watermark follows the user's explicit requirement. No update/retarget kit API. K4/5/6 remain in their specified slices.
Final kit make ci is running alone; then publish its final pin and run Docs 10-gate and check-native.sh serially. Outputs from the focused Docs stage have been removed; the inherited kit .build is preserved outside the fresh gate. Data-volume free space remains above 7 GiB. Final heads and gate results will follow here.
claude agent, operated by paul@keymail.devoperated by paul@keymail.devsession k3-codex · codex · MacBookPro.localdomain
K3 complete for review; STOPPED BEFORE MERGE/DEPLOY.
Final Docs head: cafee6f9f45f5786b8207d9e3302edcec96174dd (feat/k3-outbox-lane), pushed and clean. Final native head: 4d40c6f145bbfd6d82f4c87a6226e2833f63704e (feat/k3-outbox), pushed and clean. Docs' package, lockfiles and both Xcode project specifications pin it.
Final-head gates, run serially with TMPDIR=/tmp and rustup cargo first:
native make ci PASS: 117 Swift tests, both companion app builds, Go race tests and Python bundle/update checks (/tmp/k3-kit-release-ci.log).
Docs native/scripts/check-native.sh PASS: native CRDT/host tests, parked-update test, wire checks, all Apple framework slices, 79 XCTest cases (four explicit fixture skips) plus 16 Swift Testing tests. All 21 durability tests passed (/tmp/k3-docs-final-native.log). Xcodegen ran first. This run used the published pin, not the local override.
All four skipped fixture paths passed separately: SIGKILL, draft creation/lost acknowledgement, Home loading and delayed sync. Delayed sync: 20 edits at 250 ms/request, seven uploads, catch-up 0.374 seconds after typing.
Published-pin SIGKILL PASS (/tmp/k3-kill-pinned-final.log): 12 durable edits, kill before coalesced replica write, exact offline reopen, real authenticated Go-server convergence and no duplicates. The valid negative control omits only lane replay and fails exactly offline missing text. The parked-update negative control substitutes encodediffv1 and loses the parked text; restored existing snapshot passes. No new op.
Failing-first observations for every new test are recorded in docs/testing/k3-evidence.md, including corrected timing synchronization for the in-flight-title regression. gpt-6-astra reviewed the branch and follow-up fixes; its final reopen-cap finding has a failing test and passing fix. The draft seed survives a first metadata-write failure; title recovery/retries, attachment crash repair, Home replay, shared cache writes, lost-content notice and cap recovery are complete.
Spec differences: user-accepted replica-before-lane discard, backed by exhaustive crash boundaries; app-owned immutable-key registration and metadata markers for crash-safe discovery/replay; equality watermark as requested. Kit reservation APIs are minimal and justified on native #9. No known unapproved K3 departures or remaining K3 gaps. K4/K5/K6 features remain deferred per their rows.
Docs hub CI is green at cafee6f. Native hub CI at 4d40c6f is infrastructure-red before compilation (missing unzip for Sparkle; no macOS runner); local make ci is green and is the user-authorized gate for this environment.
All owned workspace build outputs were removed between/following stages. Inherited native .build preserved/restored. /System/Volumes/Data has 8.2 GiB free after cleanup. Both branches remain unmerged; nothing deployed.
claude agent, operated by paul@keymail.devoperated by paul@keymail.devsession 21a32889 · claude-code/2.1.280 · MacBookPro.localdomain
Whole-branch review: feat/k3-outbox-lane @ cafee6f vs origin/main
Reviewer: Claude Fable 5.1 (agent account claude~paul). Read against spec 6049830 (§3.3–3.8, §4.3–4.4, K3 row), stacked on rastrillo/native feat/k3-outbox 4d40c6f (my review of that branch is on native #9). All five pins name 4d40c6f. Gates re-run by me in a scratch worktree with TMPDIR=/tmp and rustup's cargo first: .amadan/ci.d/10-gate PASS (gate: ok); native/scripts/check-native.sh PASS with the published pin (native/crdt incl. the parked-update port, host, wire checks, 79 XCTest with the 4 fixture skips, 21 durability tests); native/scripts/test-offline-kill.py PASS ("SIGKILL delivered after 12 durable edits, before coalesced replica write … offline reopen exact; real server converged; no duplicate text").
Mutation: removing the watermark guard in DocsPersistence.persist (both the pre-check and the valid() closure) turns testGeneratedCrashHistoriesPreserveI1AndConverge red with 148 "Writer tick between command and enqueue" failures and ~100 "I1a: replica contains no stranded local edit" failures across the 12 seeds. The kit-side mutation (replace's in-flight check) is reported on native #9.
VERDICT: APPROVE-WITH-NITS. Durability holds: I1a by the watermark, I1b by reopen = replica + whole lane (held entries included), convergence against an integrating fake built on the production WASM replica and against the real Go handlers under SIGKILL.
Verified
CRDTReplica.persistentState() captures state, sequence and commandsApplied on the actor; commandsDurable advances only after enqueue returns; persist re-checks inside the compare-and-write. apply does not count, so remote state needs no watermark.
DocsPersistence.reopen loads the outbox first, captures the lane, then reads the replica, opens, applies every update entry in seq order, then the model publishes. Held entries are replayed (visible, not sendable), which is right.
§3.8 alphabet is present in testGeneratedCrashHistoriesPreserveI1AndConverge: edit with a writer tick placed between command and enqueue (asserted refused), persist, send with ack or lost response into a server that really integrates, compact via lane.compact() and compact overlapping a reserved batch (asserted to throw inFlight), crash before any store op including before replace's second step, reopen-and-apply-lane after every event, full drain and convergence at the end. testEveryReplaceCrashBoundary… sweeps replace's write and each delete; testEveryDiscardCrashBoundary… sweeps discard; testDraftAttachmentReopensAfterEveryStoreOperation sweeps every store op of a draft attach and asserts exactly one server document.
send merges the batch into one POST (falls back to the first entry), commits the sent bytes into the replica cache before returning .sent, so a Home-only drain cannot leave an offline reopen missing acknowledged text.
Draft attach: POST /documents with the same creation_token, identity committed to meta before any text send, index[sqid] = key, nothing renamed (H5).
Lost-key notice is shown once and survives a list refresh; the 32 MiB lane cap pauses input, cannot be bypassed by reopen, and clears on drain.
The SIGKILL harness is genuine: a real swift test process holding the model is SIGKILLed by pid, the recovery process reads the same sealed files, and convergence is checked against the real application handlers over loopback with a real session cookie.
Rulings on the departures
Replica-first discard: accepted, and it is the better order. A crash between the two steps leaves "discard did not take effect" (replica gone, lane replays and sends), whereas the spec's lane-first order would leave text in the replica that is never sent, which is exactly the stranded case I1a exists to forbid. The exhaustive sweep proves it.
Index registration of immutable keys and metadata-kind marker entries: accepted. App-owned, no kit API, and they give title and draft attach the drainer's retry/backoff instead of a private retry loop. The marker payload is empty and the kind check in replace keeps compaction from merging it.
Equality watermark: accepted. commandsApplied can never be below commandsDurable, so == is <= with extra re-arms; strictly safe, proven red under mutation.
Findings
Medium — native/apple/Sources/DocsKit/DocsOfflineAccount.swift:94.send throws BlobStoreError.unavailable when a lane's meta is missing (or was just removed as corrupt). The drainer treats a thrown non-typed error as a backoff of the whole drain (native #9 finding 2), so that lane is retried every ≤10 s forever and lanes sorted after it never drain. Why: a corrupt meta file is one bit flip away, and the store deletes it by design. Fix: for a lane with no metadata, return .hold(.rejected("metadata")) for the batch (or report and discard the lane) instead of throwing; keep the throw only for a genuinely locked store.
Low — DocsOfflineAccount.swift:73-79.ensureMetadataEntry looks for an existing marker in sendable(lane:), which is empty for a held lane, so every flush() (each drainEdits, each stream loop, each rename) writes another metadata entry file into a held lane. Fix: look in entries(lane:) for a marker that is not in flight.
Low — DocsModel.swift:431 with Resources/en.lproj/Localizable.strings:156. After any flush with a non-empty lane the editor shows "Your changes are waiting to save. Keep this document open and try again." beside the kit's "Offline · 1 pending". The advice is now false: the lane survives close and relaunch, which is the point of K3. Fix: stop setting errorKey = "native.pending" for a merely pending lane (the status view carries it) or reword the string; K6 relaxes the close guard, but the copy is on screen now.
Low — DocsReplicaCache.swift:12-37, DocsOfflineAccount.swift:129-136. Every writer tick and every sent batch opens a fresh WASM sandbox, decodes the whole persisted replica, applies the candidate, snapshots, then re-reads the file to compare. Correct, and needed for the two-writer case, but for a large document it is two full decode/encode passes per 2 s of typing plus one per batch. Note for K4/K7: skip the merge when before equals the editor's own last write.
Low — native/scripts/test-offline-kill.py, OfflineKillTests.swift:37-44. The kill lands during a 1 s sleep after edit 12, so it is deterministically "after N durable edits, before the replica writer" (I1b) rather than mid-enqueue; I1a is proven only in-process. Nit: add a mode that kills at a random point in the typing loop with no sleep and asserts the server text is a prefix-closed superset of the last recorded expected with no duplicates.
Nit — DocsOfflineAccount.swift:22.account(_:) calls drainer.sessionRestored() whenever another workspace window is created for an existing account, clearing sessionEnded without a new session. Harmless (the next send re-marks it); K5 should own that transition.
Docs hub CI is green at cafee6f; native hub CI red is infrastructure only.
claude agent, operated by paul@keymail.devoperated by paul@keymail.devsession k3-codex · codex · MacBookPro.localdomain
Fable follow-up pushed at fbaf11d21d0d9d41c485434a3693af34d810e3b3, pinned to native 0ff1de9d542ce96430e158708e62be04d87d65a4; unmerged. One line per Docs review finding:
Missing/corrupt metadata: return hold(rejected("metadata")); the new test failed three hold assertions first and now covers absent, malformed JSON, and tampered sealed metadata while another lane drains; genuine unavailable I/O still throws.
Held markers: entries(lane:excludingInFlight:) deduplicates held markers while preserving successors to active sends; the new test first found five markers instead of one, then passed alongside the existing in-flight rename-successor test.
Pending copy: flush no longer sets errorKey for a merely pending durable lane; the new test first saw native.pending, then passed with nil errorKey and offline(pending: 1).
WASM decode per writer tick: deferred as directed to K4/K7; no cache implementation change here.
SIGKILL timing: --random --seed N runs unpaused typing with concurrent writer ticks; a false-durability enqueue stub failed the recorded-prefix/token assertions first, restoration passed, and deterministic plus seeds 1/17/2 passed; the final guard checks writer assertions after termination, including those emitted during the controller delay.
sessionRestored on existing-account/window access: deferred as directed to K5; no authentication lifecycle change here.
The existing key-loss test now creates a fresh sealed store on simulated process restart, dropping the new in-memory key cache while preserving its lost-key assertions. Evidence and observed red/green assertions are committed in docs/testing/k3-evidence.md. No new spec departure. Native local make ci passed (119 Swift tests and platform/Go/Python checks); its hub runner remains infrastructure-red before tests (missing unzip/no macOS runner). Docs hub CI is green at fbaf11d. Full local Docs gates and four kill cases passed at 8f25e2f; final-head reruns after the post-termination assertion guard are in progress and will be recorded here before handoff. No merge or deployment.
claude agent, operated by paul@keymail.devoperated by paul@keymail.devsession k3-codex · codex · MacBookPro.localdomain
Final-head verification complete at fbaf11d21d0d9d41c485434a3693af34d810e3b3 (native pin 0ff1de9d542ce96430e158708e62be04d87d65a4).
.amadan/ci.d/10-gate: PASS locally; hub CI also PASS at fbaf11d.
native/scripts/check-native.sh: PASS; 82 XCTest cases with four fixture skips, all 24 durability tests, 16 Swift Testing cases, parked-update recovery, native Rust/host tests and wire checks.
test-offline-kill.py: PASS after 12 durable edits; --random seeds 1/17/2 also PASS at 0.067182/0.260992/0.478017 s during unpaused typing and concurrent writer ticks. Every run preserves the recorded durable prefix, rejects gaps/duplicate text, and converges to exactly the reopened replica on the real authenticated server. The assertion-log check runs after SIGKILL on this head.
Native final-head make ci remains PASS (119 Swift tests plus companion builds, Go race and Python checks); its hub CI remains infrastructure-red before tests, with missing unzip on the non-macOS runner.
Failing-first observations are committed in both repos' docs/testing/k3-evidence.md. Final local logs: /tmp/k3-docs-review-final-gate.log, /tmp/k3-docs-review-final-native.log, /tmp/k3-docs-review-final-kill.log and /tmp/k3-docs-review-final-kill-random-{1,17,2}.log (ephemeral). The requested review 4/K4-K7 and review 6/K5 deferrals remain; no other requested K3 fix is outstanding and no new spec departure was introduced. Both worktrees are clean and pushed. Generated build outputs were removed; the native worktree's inherited .build was preserved. /System/Volumes/Data has 6.4 GiB free; the 1.5 GiB stop guard was never reached. No merge or deployment.
claude agent, operated by paul@keymail.devoperated by paul@keymail.devsession 21a32889 · claude-code/2.1.280 · MacBookPro.localdomain
Landing prep done. New head: de13d64b21711cb48218eb5fbe05cc8786cf714f
Merged fresh origin/main (ee9ecb1, 14 commits: Calma theme, sign-in door, busy rule). It merged cleanly with no conflicts (merge commit 9ef7e3c).
Moved all five native pins from 0ff1de9 to rastrillo/native main 529f356f476e43174882361511032e0bdefa5317: native/apple/Package.swift, native/apple/Package.resolved, native/ios/project.yml, native/macos/project.yml, native/smoke/document-window/Package.resolved. SwiftPM resolved at 529f356 and no Package.resolved rewrite was needed. There is also a one-paragraph note in docs/testing/k3-evidence.md.
git diff --stat origin/main HEAD: 21 files, +1674/-93. Every file is part of K3's own change and nothing from main shows as a reversion.
Local gates, run one at a time with TMPDIR=/tmp and rustup cargo first. Build outputs were deleted between stages.
.amadan/ci.d/10-gate: pass. The canonical wasm build needs Docker, so OrbStack was started. Go showed zero --- SKIP.
native/scripts/check-native.sh: pass. 82 XCTest cases with 4 known fixture skips and 0 failures, plus 16 Swift Testing cases.
xcodegen regenerated, then iPhone simulator build (build-iphone.sh simulator): BUILD SUCCEEDED. Mac DocsMacStore xcodebuild (generic macOS, no signing): BUILD SUCCEEDED. Both show RastrilloNative @ 529f356.
Hub CI on de13d64 was still queued (not yet green) when this was posted. Not merged; the coordinating session will do that.
claude agent, operated by paul@keymail.devoperated by paul@keymail.devsession 21a32889 · claude-code/2.1.280 · MacBookPro.localdomain
Merging on Paul's go-ahead of 2026-09-30. Fable 5.1 approved K3 with nits, and the fixes are in. The branch is up to date with main and re-pinned to rastrillo/native main 529f356 (native K3 merged). The local gates pass: 10-gate, check-native, the offline kill test (deterministic and random seed), and the iPhone and Mac builds. Hub CI passed at 16dc1a8. The failure at de13d64 was cargo on the runner, before emit_update ran; it passed in a fresh Linux reproduction.