Repository navigation
Publishing a message no longer rewrites the record's data, expiration, residency or cache-stub state - #3671
Merged
Conversation
kriszyp
marked this pull request as ready for review
October 29, 2026 17:17
A publish rewrote the record from a stale base with fresh metadata: a same-transaction put, patch or create before it was written back to its old value (the index kept the new one), a publish before a put on an unaudited table turned the put into a skipped tie, and a standalone publish cleared the record's expiration, residency id and INVALIDATED/REPLICA flags. _writePublish now joins the same-key write chain (chainsStagedState, reloadCommitBase), rewrites the base priorStagedWrite or the stored entry gives it unchanged, and stages that base. The record keeps its residency through a new recordUpdater recordResidencyId option, while the message's audit entry keeps the message's residency, which replication routes by. A publish-staged base is marked unchanged, so the re-delivery checks do not treat it as an earlier change. Update, invalidate and delete stage the metadata a following publish carries. An older message still rewinds the version, as before (#3637). Dispatch-Task: harper-writepublish-chain-preserve Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The publish now leaves the invalidated stub invalidated, so on RocksDB the fill completes it with the source's value instead of yielding to a stub that only looked complete; LMDB's fill still yields to the earlier same-key write. Dispatch-Task: harper-writepublish-chain-preserve Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dispatch-Task: harper-writepublish-chain-preserve Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A publish now restages its base's localTime, nodeId and refs, so a copy of a delete after a message in the same origin transaction still matches the removal it duplicates and logs once. Dispatch-Task: harper-writepublish-chain-preserve Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The publish copied identity from any null base, including an INVALIDATED stored stub and an unstaged relocate's null row, which a delete's re-delivery check deliberately rejects; a following delete with that identity was then skipped and left the stub invalidated. It now carries what removalBefore returns, as if the publish were absent. Dispatch-Task: harper-writepublish-chain-preserve Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dispatch-Task: harper-writepublish-chain-preserve Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DAm4fA9xir5jYrcQr5QCpo Dispatch-Task: pr-maint-f6e8fed94aa4da790dd2494e0ff02c73
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DAm4fA9xir5jYrcQr5QCpo Dispatch-Task: pr-maint-f6e8fed94aa4da790dd2494e0ff02c73
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DAm4fA9xir5jYrcQr5QCpo Dispatch-Task: pr-maint-f6e8fed94aa4da790dd2494e0ff02c73
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DAm4fA9xir5jYrcQr5QCpo Dispatch-Task: pr-maint-f6e8fed94aa4da790dd2494e0ff02c73
…e relocate and fill gaps Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DAm4fA9xir5jYrcQr5QCpo Dispatch-Task: pr-maint-f6e8fed94aa4da790dd2494e0ff02c73
kriszyp
force-pushed
the
fix/publish-preserves-record-state
branch
from
October 29, 2026 19:38
defa4a5 to
1b3b892
Compare
kriszyp
added a commit
that referenced
this pull request
Oct 30, 2026
main's publish now stages an unchanged entry (#3671), which made priorStaged set after a publish: the committed-list skip no longer ran for a re-delivered [publish, patch], and a patch after it read a staged entry with no list. Skip on priorChange, and let the publish stage and store its base's list. Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
kriszyp
added a commit
that referenced
this pull request
Oct 30, 2026
…md to bring it under its line budget (#3802) #3671 left resources/DESIGN.md at 1003 lines against check:design-docs' 1000-line budget, failing Lint Source Code on main. The note "getFromSource() keeps source versions separate from fill ordering" was the one hard-wrapped section in that part of the file; joining each paragraph onto one line, as the rest of the file does, drops it to 966 lines with the text unchanged. Dispatch-Task: main-red-kriszyp_harper_dede2d3da_5684deb1 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
kriszyp
added a commit
that referenced
this pull request
Oct 30, 2026
main's publish now stages an unchanged entry (#3671), which made priorStaged set after a publish: the committed-list skip no longer ran for a re-delivered [publish, patch], and a patch after it read a staged entry with no list. Skip on priorChange, and let the publish stage and store its base's list. Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
kriszyp
added a commit
that referenced
this pull request
Nov 2, 2026
main's publish now stages an unchanged entry (#3671), which made priorStaged set after a publish: the committed-list skip no longer ran for a re-delivered [publish, patch], and a patch after it read a staged entry with no list. Skip on priorChange, and let the publish stage and store its base's list. Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
kriszyp
added a commit
that referenced
this pull request
Nov 3, 2026
main's publish now stages an unchanged entry (#3671), which made priorStaged set after a publish: the committed-list skip no longer ran for a re-delivered [publish, patch], and a patch after it read a staged entry with no list. Skip on priorChange, and let the publish stage and store its base's list. Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
kriszyp
added a commit
that referenced
this pull request
Nov 3, 2026
main's publish now stages an unchanged entry (#3671), which made priorStaged set after a publish: the committed-list skip no longer ran for a re-delivered [publish, patch], and a patch after it read a staged entry with no list. Skip on priorChange, and let the publish stage and store its base's list. Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
kriszyp
added a commit
that referenced
this pull request
Nov 6, 2026
… newer write (#3780) * Apply a re-delivered increment once on audit:false tables after a newer write An audit:false record's receivedIdentityRefs is the only proof a commutative op was already applied, but only the out-of-order merge path kept it. An in-order patch, a publish, a relocate, and a second same-key write in one LMDB transaction all dropped it, and an in-order add never listed itself, so a re-delivered add applied twice once a newer write landed. Every audit:false patch now keeps the list of the record it applies to (the staged entry on LMDB) and lists its own identity when it applied a commutative op; publish and relocate keep it; a full write still starts over. A listed patch is skipped before any version comparison, since an LMDB merge stores the older version and an applied write can then compare as newer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-noaudit-received-identity-refs-carry * Check a re-delivered audit:false patch against the committed identity list An earlier publish in the same transaction disabled the listed-identity check, so a re-delivered [publish, add] source transaction applied its add again after a newer write. The check now reads the committed record whenever another write of the transaction saved (identityBase), which excludes only identities this transaction listed itself. The carry tests for a commutative op without allocating. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-noaudit-received-identity-refs-carry * Re-apply a sibling patch after a same-transaction full write reset the list The listed-identity check now also requires the body a patch applies to (an earlier staged write of the transaction, if any) to still name the identity: a full write earlier in the transaction started a new list, so its sibling patch builds on that body and must apply. Publish-first source transactions now run on LMDB too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-noaudit-received-identity-refs-carry * Skip a listed audit:false patch only when no earlier write of its transaction staged a body A re-delivered transaction's patches are skipped by the committed list and stage nothing, so an earlier staged write of the same transaction can only be one that replaced the body (a re-delivered put compares as newer after an LMDB merge). Every patch after it now applies on that body; requiring the staged list to name the identity dropped the second increment once the first had listed it again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-noaudit-received-identity-refs-carry * Stage the identity list only on audit:false tables An audited staged entry keeps its old shape; only an audit:false write with a list adds it. Comment and test-message tidying from review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-noaudit-received-identity-refs-carry * Keep the identity list when a 304 revalidation re-stores the cached record getFromSource re-stores the cached body on a 304 but passed no identity list, so an audit:false sourced record forgot which increments it had applied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-noaudit-received-identity-refs-carry * Take a 304 revalidation's identity list from the body it stores The 304 path stores the record read before the source call, so its list must come from that same entry, not the commit base: a RocksDB no-audit merge that commits during the call keeps the version, and its listed increment would then be skipped on re-delivery against a body that lacks it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-noaudit-received-identity-refs-carry * Bound the 304 race test's wait for the source call Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-noaudit-received-identity-refs-carry * Give the 304 race test a deadline Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-noaudit-received-identity-refs-carry * Return the identity list on the entry a 304 revalidation resolves On LMDB a resource that loaded the record through a 304 fill commits its next write against that returned entry, which carried no list, so the write cleared the stored one. The race test now also settles its read on every path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Dispatch-Task: harper-noaudit-received-identity-refs-carry * Observe the 304 race test's read rejection at creation; fix two stale comments Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 * Reword hasCommutativeOp's JSDoc without a null/undefined equivalence Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 * Carry the identity list through a keyed publish's staged entry main's publish now stages an unchanged entry (#3671), which made priorStaged set after a publish: the committed-list skip no longer ran for a re-delivered [publish, patch], and a patch after it read a staged entry with no list. Skip on priorChange, and let the publish stage and store its base's list. Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Read a first attempt's committed list from the effective body; cover a saved-sibling recommit A first attempt has staged only unchanged publishes by the time the skip runs, so the staged-or-reloaded entry already holds the committed list; only a retry needs identityBase's committed-row read. The recommit arm saves two same-key increments, forces a native conflict on another key, and asserts one application. Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Give the native-recommit arm a test deadline Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Record the audit:false identity list in the restructured DESIGN.md Main cut resources/DESIGN.md to invariant notes, so the rebase takes main's file and restates the list's carry, restart and listed-patch skip rules in the audit-reference bullet. The Table.ts skip comment now carries the same-transaction exception itself. Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Co-Authored-By: Gemini <noreply@google.com> * Keep receivedIdentityRefs on a stale-on-error fallback entry Co-Authored-By: Gemini <noreply@google.com> Dispatch-Task: pr-maint-6f13e1c678687230b257b26b0db8f0e1 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Gemini <noreply@google.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
⊙ Problem
Table.publishis documented to leave the record unchanged, but it rewrote it from a stale copy. A put, patch or create earlier in the same transaction was undone in the stored row while the index kept the new value. A standalone publish cleared the record's expiration, residency id and invalidated-stub flag.🤔 Is this the whole fix?
No. An older message still rewinds the record's version (#3637): keeping it would drop the message from RocksDB per-record replay.
💡 Solution
🤔 Accept that
[put K, publish K]in one transaction replays the put, not the message?For now: RocksDB replay finds the first entry at their shared log key. Fix: #3661.
🤔 Accept
publish K; relocate K; patch Kre-storing the full body on an excluded host?For now: a relocate stages nothing, so the patch bases on the publish's full record. Extends a known gap (DESIGN.md).
⚖️ Alternatives
🤔 Should
Cache-Control: max-ageon a publish still set expiration?Main applied it to the record on caching tables; now the record keeps its own expiration.
🔧 Changes
resources/Table.ts_writePublish: joins the same-key write chain, takes its base frompriorStagedWriteor the stored entry, keeps the record's residency and expiration, and stages that base markedunchangedwith the removal identity a later delete would otherwise find. Look hardest here.resources/Table.ts_writeUpdate/_writeInvalidate: a publish-staged base is no earlier change for the tie-break, the re-delivery checks and the invalidation base-copy skip; on unaudited tables a restamp still counts as program order. Update, invalidate and delete stage the metadata a following publish carries. ThefollowsAppliedWriteandidentityBasecomments now say which writes restamp.resources/RecordEncoder.ts:recordResidencyIdsets the record's residency separately from the audit entry's.resources/DatabaseTransaction.ts: theunchangedfield onstagedEntry.resources/DESIGN.md: the publish note in the same-key chain section, publish among the chained writes, and the delete re-delivery note.✅ Verification
publishRecordState.test.json both engines covers same-transaction writes, metadata, local-only flags, residency routing, re-delivery and delete copies; 9 of 14 cases (RocksDB) and 11 of 14 (LMDB) fail on main.relayedHeadTieDuplicate,subscriptionBufferedStateandsourceFillConditionalWrite.test:unit:resources, both engines: only timing tests (pass alone) and old-binding full-text checks fail.PR QA: Do concurrent publishes and writes to the same keys across HTTP workers leave primary rows, indexes and expirations consistent? — queued @ 1b3b892, qa-pr-harper-3671-1b3b892-r2
Publish cost measurements
Single-transaction
Table.publishto a warm key, µs per publish; load average 7–30, turbo on.Uncached
getEntry: 3.3–14 µs vs cached 0.4–1.6 µs (small record), 29–35 µs at 64 KB.Docs: none;
publishnow matches them.🤖 Generated by Anthropic Claude (Opus 5.5, dispatch dev agent); posted via @kriszyp.
Release target: v5.4 — fixes behavior on main; no reported need on an older line
Related PRs: #3453 overlaps (re-delivery dedup checks in _writeUpdate), #3506 overlaps (the unaudited tie rule), #3529 overlaps (changes its source-publish fill expectation), #3527 overlaps (publish carries the shard slot and complete record), 17 others independent
Complexity: complicated
Framing-Verdict: chosen-approach-sound (019fa91eec79)
Deferred findings
f2887ce6cd9751edmajor correctness —resources/Table.ts— older/tied/chained publish sets VERSION_REUSED on the unchanged record, hiding it from full-text results — filed A publish that does not advance the version hides an unchanged record from full-text results #36603d73554ed565a886major performance —resources/Table.ts— keyed publish adds an uncached commit-base reload and decode per message — filed A static publish re-reads its record uncached; large retained records pay ~50% per message #36652b991d7842eb5ffamajor data-integrity —resources/Table.ts— older publish rewinds version so a chained put/delete overwrites a newer record — filed An out-of-order publish rewinds the record's version, letting a stale delete or write through #3637b49828595b981a48minor data-integrity —resources/Table.ts— [publish K, put/patch K] in one transaction: replay omits the later non-message write sharing the log key — filed Per-record replay delivers only the first of a transaction's writes to one record #3661c9fcb19803b01b6eminor correctness —resources/Table.ts— LMDB fill yields to an unchanged publish and leaves the stub invalidated while RocksDB completes it — filed On LMDB a source fill yields to the source's own publish and never caches the key #366229a42bdfed78357cminor correctness —unitTests/resources/sourceFillConditionalWrite.test.js— LMDB fill yields to an unchanged publish and leaves the stub invalidated while RocksDB completes it — NOT FILEDc7d6a0469e0756b7nit correctness —resources/Table.ts— RocksDB skips a chained invalidate that ties with a publish restamp; LMDB applies it — filed A replicated [publish K, invalidate K] skips the invalidation on RocksDB but applies it on LMDB #3666🤔 defer this PR's major correctness finding
f2887ce6cd9751ed(older/tied/chained publish sets VERSION_REUSED on the unchanged record, hiding it from full-text res) to #3660 instead of fixing it here?🤔 defer this PR's major performance finding
3d73554ed565a886(keyed publish adds an uncached commit-base reload and decode per message) to #3665 instead of fixing it here?Late Grok findings
2 surviving (highest major) — Grok reviewed 71e2425
resources/Table.ts:8295— a put/patch/delete/invalidate followed by a publish to the same key in one transaction gives both audit entries the same log key (appendedLogKey === txnTime, so noadditionalAuditRefs). On RocksDB,resolveAuditHead(resources/Table.ts:1707) then callsauditStore.getSync(txnTime, …), and ifresources/Table.ts:8307— an older publish, or a same-version tie (including the RocksDB same-transaction put→publish case, where the reloaded base's version equalstxnTime), writesnewVersion <= existingEntry.version. That setsVERSION_REUSED(resources/RecordEncoder.ts:1002-1003) on a record whose value did notNot standing: none. Grok ran 14m (14m from launch to report); the fold adjudication (
cursor-grok@71e2425b88ea-563a813f, claude) took 2m.Late Grok findings
7 surviving (highest major) — Grok reviewed 71e2425
resources/Table.ts:8287— Aputfollowed by apublishon the same record in one transaction writes both audit entries under the same log key. On RocksDB,getSyncreturns the first entry matching the record at that key (resources/RocksTransactionLogStore.ts:222-225), which is now the put, because the publish chains afresources/Table.ts:8307— An older publish rewrites the record atexistingEntry.version, which satisfiesversionIsReused(resources/RecordEncoder.ts:157-158) and setsVERSION_REUSED(resources/RecordEncoder.ts:1002-1003) on a record whose value did not change.isFullTextSearchEntryCurrent(`resources/Table.ts:194resources/Table.ts:1741—collectMessageBranchesreads only the directrefsand never followspreviousAdditionalAuditRefs. The encoder drops middle log-key refs when they exceed its byte budget (resources/RecordEncoder.ts:416-419,:463). A message whose only remaining link is a later message's `previousAdditionalAuresources/Table.ts:8261— Every keyed publish now takes a commit-base read transaction and reloads the base entry at commit (baseReadTxn,reloadCommitBase: keyed). This is extra read I/O on every publish to a record, and the PR includes no measurement of it.resources/Table.ts:7940— Each RocksDB subscribe with a start time now allocates aMap, builds one string key per replayed entry (:7967), and does one loggetSyncper non-head ref (:1753). The PR includes no measurement of this replay path.resources/Table.ts:8330— The publishstagedEntrydoes not carry the removal identity. With delete → publish → delete on the same record and identity in one transaction, the second delete is logged again. Severity is a nit on the merits; it is graded major only because of the unmeasured hot-path floor. Follow-up issue alreresources/Table.ts:8254— Several added comments restate the code:resources/Table.ts:1739,:8254,:8263,:8288, and the test-description comments inunitTests/resources/publishRecordState.test.jsandunitTests/resources/subscriptionBufferedState.test.js. One existing comment is reported stale near `resources/TabNot standing: 2 separate-issue. Grok ran 13m (13m from launch to report); the fold adjudication (
cursor-grok@71e2425b88ea-8398ace6, claude) took 2m.Origin — the dispatch brief this PR was written from
Table.publish / record.publish (_writePublish in resources/Table.ts) is not a chained write and rewrites metadata. It sets entry: this.#entry without chainsStagedState/reloadCommitBase/priorStagedWrite, passes flags & LOCAL_ONLY only, expiresAt: context?.expiresAt, residencyId: options?.residencyId and newVersion = txnTime, and updateRecord's message path drops HAS_EXPIRATION, residency id and INVALIDATED. Effects: same-transaction put/patch then publish writes a stale base back (primary/index divergence); deferred writes before a publish are dropped; a standalone publish wipes expiresAt, residency id and the INVALIDATED flag (promoting a stub to a full-looking record); an out-of-order publish rewinds the record version so a stale delete passes. Make it chain like _writeInvalidate (#3322): chainsStagedState and reloadCommitBase, base from priorStagedWrite(write)?.stagedEntry ?? existingEntry, publish stagedEntry unchanged, deferred create/update/put applied first; carry the existing expiresAt, residencyId and flags & (LOCAL_ONLY|INVALIDATED|REPLICA); newVersion = Math.max(txnTime, existing version) while the message audit entry keeps the txn timestamp; no updateIndices. Update the resources/DESIGN.md note that publish was left unchained.
Acceptance
Tests on rocksdb and lmdb: put(v2) then publish in one transaction leaves primary store and index on v2; standalone publish on a record with expiration, one with a residency id, and an INVALIDATED stub leaves each unchanged; publish at t1 < t2 keeps version t2 and a stale delete at tMid is rejected. Show each new test failing on origin/main sources (forced rebuild) before the fix.
Dispatch: task
harper-writepublish-chain-preserve· queued by finding-triage/wave-40 · ran by claude/opus/high · worker kzyp-xps-1Late Grok findings
3 surviving (highest major) — Grok reviewed 1b3b892
unitTests/resources/sourceFillConditionalWrite.test.js:284— On RocksDB, a fill commit after a same-transaction publish probably throws the fill away. If so, this assertion fails on the default-engine (RocksDB) CI shard, andresources/DESIGN.md:532documents an engine split that does not exist. Disputed and not executed. Scope: in-scope — Convergence-Class:resources/Table.ts:5600— A replicated transactionpublish K; relocate K; patch Kstores the full record on a host the residency policy excludes. Documented atresources/DESIGN.md:528but not fixed. Scope: in-scope — Convergence-Class: original-changeresources/Table.ts:9498— New publish comments partly repeat the publish section ofresources/DESIGN.md. Scope: in-scope — Convergence-Class: previously-adjudicatedNot standing: 1 merged. Grok ran 8m (8m from launch to report); the fold adjudication (
cursor-grok@1b3b892f5700-4c3fd75e, claude) took 1m.Review-Coverage: authored=claude; ran=cursor-composer,gemini,codex,cursor-grok; late=cursor-grok:adjudicated; adjudicated=domain; declined=muse; rounds=11; full=3 @ 1b3b892
Review-Attention: deep ~25m (critical: DatabaseTransaction.ts, RecordEncoder.ts +1; decisions: publish-ignores-context-expiresat, keyed-publish-joins-chain, document-relocate-gap-not-fix, engine-fork-to-issue, sparse-staged-entry, do-less-alternative, deferred-f2887ce6, deferred-3d73554e) @ 1b3b892