# Queue as data — adversarial review, round 2 VERDICT: revise Rocko for Sage, 2026-09-26. Verified the requested plan SHA-256 **before reading**: `94922cc5319f03d9f4d11c3a3a8935022440ff2386c9dd160c4bce1daaa27cc5`. Review target is section 7 plus the revised body of `agents/filbert/work/queue-as-data-plan-2026-09-26.md`. Prior findings are the unchanged R1 report, SHA-256 `df433f932f6e8687fdb2ad6d707d133b633a245ac39293362ce6bde6942cbf5c`. The dispositions make substantive improvements. They do not yet supply a consistent implementable protocol: the lock/recovery design has unresolved failure schedules, claims rely on a variable the launchers explicitly unset, and the review-post retry still overpromises. These are design fixes for the lead/planner, not nine more questions for Jason. “Closed” below means closed at the planning level, subject to implementation and its tests. No implementation, fault injection, live state change, or credential access was performed. This report is the only write for round 2. ## Disposition of all original findings | Original | Round-2 status | Does the proposed change close the scenario? | |---|---|---| | R1 transaction/recovery | Open | Journal-first ordering helps, but partial append, stale-lock reclamation, drift/recovery discrimination and retry identity remain unspecified or contradictory. See S1–S4. | | R2 auto-commit ownership | Partly closed | Removing the commit feature closes the automatic sweep. Manual path commits still need exact-content freezing, header ownership and coordinated integration; “after tests” alone has a race. See S5. | | R3 provenance | Partly closed | Replay, tombstones and an honest cooperative boundary close several scenarios. Worktrees/forks/genesis and automatic repair still undermine the proposed guarantees. See S2, S3, S6. | | R4 authority | Conditional, not yet closed | The claimed-actor matrix and J1/J2 are concrete, bounded improvements. Protected field creation, parked immutability and exception precedence need correction; owner acceptance of cooperative trust remains pending. See S7. | | R5 Gate G | Partly closed | Auditing every user-role entry and requiring a work action close the classifier/no-work holes. Incarnation evidence is unavailable, USER is not a committed input, and helper/file-based coaching remains outside the stated check. See S8. | | R6 dependencies/claims | Open | Transition predicates and review actions help. The required dependency prohibition rejects row 9's own prerequisite; absent identities and “resume another incarnation” leave duplicate work possible. See S7/S8. | | R7 remote request | Open | Durable intent and a marker improve recovery but lookup-then-retry cannot establish at-most-once delivery. Outcome state and replay also need definition. See S9. | | R8 issue evidence | Closed at design level, budget pending | Unknown-by-default, positive typed issue lookup, bounded fetches and not-run reporting close the original false-closure scenarios. Keep those rules even if J8 is declined. | | R9 liveness | Partly closed | Coverage and explicit exemptions close the false-green policy. The plan cannot compare process start identity against a field never recorded; unknown PID/start evidence needs a defined result. See S8. | | R10 age/history | Partly closed | Stable requiredSince and journal history address note-reset and overwritten attribution. Genesis dates and proof of remediation still need rules. See S6/S10. | | R11 owner changes | Mostly closed as triage | Changes are now explicitly held rather than silently implemented. J3/J7/J9 need not all go to Jason; closure mapping still needs per-issue semantics for multi-issue rows. See S7 and the J table. | | R12 candidates/receipts | Partly closed | Digests, review rounds, multiple reviewers and reviewIssue help. A local manifest path or unreferenced commit is not durable candidate content. See S10. | | R13 brief/migration | Partly closed | Exact legacy rows, anchor checks and a migration map are real fixes. Index membership does not prove committed content, and replay cannot revalidate historical briefs against current files. See S6/S10. | | R14 wiring/acceptance | Closed at design level | Relative imports, no loader move, named suites, C/D/E receipts and B context inspection resolve the original wiring gaps. Consolidate stale body instructions before implementation (S11). | ## Remaining mechanism findings ### S1 — High: mkdir plus PID/start time is not a complete lock protocol **Failure schedules:** - A successfully creates the lock directory and dies before creating its owner file. A paused live A looks identical to a dead A at this point. There is no PID/start time to prove stale. A partial owner file has the same problem. - B and C both inspect dead A's lock. B removes it and acquires a new lock. C removes the path using its earlier observation of A. B and C can now both enter. A check immediately before removal still has a race unless the removal/reclamation protocol itself is serialized. - PID and start ticks identify a process within a boot/PID namespace, not across reboot or another host mounting the checkout. Permission errors reading `/proc` are not proof of death. **Required fix:** either use a kernel lock for *all* entry points, including direct CLI invocations, or fully specify atomic owner publication, exclusive reclamation, owner-checked release and incomplete-owner handling. Kernel locking need not be limited to the shell wrapper: the CLI can invoke a locked child path; implementation choice belongs to the lead. If retaining mkdir, refusing incomplete owner records for explicit recovery is honest; pretending PID checks automatically recover them is not. Include boot/host scope or explicitly refuse unsupported environments. Do not delete a lock based on age or an old inspection. **Tests:** kill between mkdir and owner publication; pause there; partial owner record; two simultaneous reclaimers; reboot identity mismatch; unreadable `/proc`; delayed old owner's cleanup after replacement. ### S2 — High: partial journal appends have no append-only recovery rule **Scenario:** append writes only the first half of a JSON object before an I/O error or kill. Replay now cannot parse the tail. Appending the next JSON object does not repair the malformed line. Truncating the tail would alter the new append-only journal, contrary to the proposed invariant. “Anything else: refuse with recovery instruction” is safe refusal, but the recovery instruction itself has not been designed. **Required fix:** define complete-entry framing, entry checksum/hash chain, format version, short-write handling and the durable commit point. Choose one bounded torn-tail procedure that preserves prior bytes/evidence: for example a precisely recognized incomplete frame plus an explicit recovery record, or a new journal segment linked to preserved damaged evidence. Alternatively retain refusal and require a separately specified recovery operation; do not promise automatic recovery in its absence. Arbitrary interior corruption must never be skipped as an incomplete tail. The durability prose is also wrong: an entry fsynced before stdout **can survive without acknowledgement** and will be replayed. Say acknowledged operations are durable; unacknowledged operations may have committed and must be queried/retried by identity. The Markdown write lacks explicit file and directory fsync. It may be a rebuildable cache, but then do not promise that the entire acknowledged three-file set survives a host crash. Fsync new journal/genesis directory entries as well as file data. **Tests:** short write and ENOSPC during append, crash before/after fsync, missing newline, invalid interior entry, crash on initial journal creation, and recovery without rewriting already-persisted evidence. ### S3 — High: “stale view: re-render” also erases hand edits **Scenario:** a user changes the Markdown table or stamps it with an older revision. Under R1 it is stale and automatically regenerated; under R3 it is unexplained drift and must refuse. A JSON hand edit that happens to equal an old parent similarly resembles incomplete materialization. Current instructions do not distinguish these cases or decide whether verification precedes recovery or recovery precedes byte equality. **Required fix:** define recovery only for exact known materializations of the prior/current revision, with expected content hashes and transaction state. Preserve and reject anything else. Revision markers alone are not content proof. Define separately non-mutating `verify`/`render --check`, normal reads, and an explicit repair path. Running a check must not silently fix the drift that the check was intended to expose. If `list`/`next` may repair caches, label that effect and never repair unexplained content. **Tests:** stale genuine view versus edited view with the same old marker; current marker with changed body; missing/duplicate marker; edited header during rendering; verify/check on an interrupted materialization. Body and hand-maintained header/footer must retain their respective ownership. ### S4 — High: equality of rows/states is not idempotency **Scenario:** A starts review, its result is lost, B requests changes and moves back to in-progress, then A retries. Comparing only current state permits a second review round. An add retry after reassignment/completion no longer matches the same non-terminal tuple and can allocate a new id. An unrelated writer can produce the same state and A is incorrectly told its own earlier operation succeeded. Notes and other mutations are omitted. R1 says moving to the current state succeeds; R6 says in-progress→in-progress is refused. Both cannot be the retry contract. **Required fix:** stable caller operation id plus expected parent/row revision and canonical payload, persisted for every mutation. Retrying the same id returns its recorded result even after later state changes; reusing it with a different payload refuses. A *new* operation attempting in-progress→in-progress may refuse. Specify how the caller retains the id before sending so a killed CLI does not generate a new id on every retry. Otherwise withdraw the at-most-once retry claim and expose uncertainty. ### S5 — Medium: manual commits still need an integration boundary **Scenario:** test-queue.sh passes revision 12, another CLI writes revision 13, and the lead commits by path. Or QUEUE.md's header contains another author's unreviewed change. Removing auto-commit does not make either file content safe to include automatically. **Required fix:** Sage can decide a cooperative integration procedure: freeze/pin all three queue files and hand-maintained changes, prevent queue mutations between exact-content verification and staging/commit, and check the prospective tree and HEAD. Coordinate other commit writers. Do not hold a non-reentrant lock and run a test suite whose verify subcommand waits on the same lock; define the maintenance/integration path explicitly. No automatic git operation needs to be added to A. The tests/code receipt must match what is actually committed. Lead status alone still grants no commit or push authority beyond the established plan. ### S6 — High: common-dir serialization does not establish one queue history **Scenario:** two linked worktrees share the real common-dir lock, but each has its own queue files. They write sequentially under the lock from the same parent and create divergent histories anyway. A later merge resolving the journal by selecting only one side leaves no visible fork for replay to detect. An appended “reconciliation” entry also cannot make an earlier fork satisfy a strictly single-parent replay rule without special semantics. **Required fix:** for this canonical-checkout assignment, simplest is to refuse mutations in other worktrees/clones and pin the canonical root, branch and queue identity. The symlink to that root is fine. Resolve a relative git-common-dir result against the command's actual cwd before canonicalization. Define integration checks against the last accepted tail and relevant parents; acknowledge that discarded branches are not detectable from the remaining journal alone. Prefer refusing forks for a reviewed reconciliation procedure over inventing an unspecified append that bypasses validation. No distributed writer support is needed here. **Genesis:** it must be a single, reviewed, versioned migration seed, not a normal sequence of transitions (legacy rows start done/parked and lack old claims/receipts). Permit this exception exactly once. Keep original rows, the reviewed normalized mapping, retired IDs/high-water mark, and provenance of legacy dates/acceptance gaps. Do not invent historical approvals. Set requiredSince from evidence or explicitly unknown; migration time must not reset old required rows' age. A second genesis/reset must refuse. Historical replay must use versioned operation semantics and recorded brief identities, not today's mutable brief files, owner roster or transition implementation. Check *current* actionable briefs separately. Otherwise editing or removing a once-valid brief makes all future replay impossible. Specify deterministic JSON serialization for the byte-equality requirement. ### S7 — High: the revised matrix rejects its own prerequisite and has gaps **Scenario:** R4 forbids a required row depending on a non-required row. Row 9 is required and depends on row 6, which is not marked required in the current queue. Genesis/current validation cannot enforce that blanket rule while preserving the approved start condition. Real prerequisites are not the same thing as gratuitously lowering required work's priority. **Required fix:** preserve explicit owner-approved prerequisites, including row 6, and restrict who may change them; do not ban their data shape. Define what happens after `settled` allowed a child to start and the parent later leaves blocked: do not inadvertently strand review/completion by rechecking start prerequisites on every transition. Permission checks need field-level precedence on `add` as well as subsequent edits (an ordinary owner must not choose a privileged gate/required field to bypass the matrix). Parked-row note/assign edits must not escape terminal immutability unless J4 explicitly permits them. Specify the closure mapping per issue: one `closesIssue` boolean on a row with `issues: [1511,1512]` does not explain which issue it governs. Multiple rows for #1508 also need an explicit closure rule. Review receipt writes need a permitted actor and exact-candidate check, not just storage fields. ### S8 — High: claim and process identity are not provided by current code **Observed:** `scripts/agent-host-dev.sh:131` and `agents/rocko/launch.sh:84` explicitly `unset MOSAIC_LAUNCH_INCARNATION`. No producer for that variable was found in the scoped launcher/seat code. The registration whitelist in `packages/seat/src/seat.mjs:130` contains PID and an ISO `startedAt`, but no captured `/proc` start ticks or boot id. The latter timestamp is recorded at registration creation; it is not a saved process-start identity suitable for the comparison R9 proposes. **Consequences:** Gate G's required claim cannot be satisfied as written. R6's “when present” falls back to indistinguishable claims for multiple same-seat sessions. A different incarnation offered `resume` can still resume the work; a claimed-by warning is not refusal. Liveness cannot prove that a present PID was reused by comparing against evidence never recorded. **Required fix:** choose an existing verifiable session/launch receipt identity, or explicitly plan the narrowly scoped identity producer and its ownership/review dependencies. Do not silently change launchers or registration schema as A/E implementation detail. Refuse missing claim identity where exclusivity is required; report another claimant as wait/ conflict, with explicit handover/reclaim semantics. Treat unavailable process-start proof as unknown/incomplete, not live or stale by invention. Gate G must use actual launch inputs: USER defaults to `/user/USER.md` in agent-host-dev.sh:66, not a committed repo file. Hash/audit it privately without copying private content into git. Bound the no-coaching audit to all actual context sources, including loaded skills and post-launch task-specific file changes; transcript messages alone do not cover them. An incarnation string remains a cooperative claim; bind acceptance to the observed test-session command/action trace and Jason's observation, rather than claiming it proves no helper spoofed it. ### S9 — High: marker lookup does not guarantee at-most-one POST **Scenario:** Gitea receives a POST, the client times out, and the first request is still being processed. A subsequent successful GET shows no marker yet; retry creates a second comment, and the first request then completes. Incomplete pagination or a removed comment also looks absent. Changing queue revision while an intent is unresolved can generate a new operation id for the same logical review if identity is recomputed. **Required fix:** on uncertainty, reconcile a found marker; otherwise remain unresolved and do not re-POST merely because a GET found nothing. Retry only when non-acceptance is positively known, or use server-side idempotency if separately established. This bounds availability honestly. Define complete comment lookup, pagination bounds and ambiguous duplicates. Persist the operation id and candidate before send and reuse them unchanged. Specify journal events for intent, confirmed request, definite failure and unresolved outcome, including their materialized snapshot/review states. If the lock is released during I/O, finalization must compare the expected row/round and cannot overwrite a newer candidate. If held, set network deadlines and state the effect on all reads sharing the 10-second lock wait. A replay/check must never send the POST. Removing “exactly once” but keeping an unsupported “at most one” claim does not close R7. ### S10 — Medium: hashes and tracking are necessary, not sufficient, evidence **Candidates:** a repository-contained manifest may still be untracked or overwritten; its digest cannot recover absent source bytes. A present commit may be unreachable and later garbage-collected. Require a retained commit reference or durable, committed manifest *and retrievable pinned source*, with readback verification. Keep all prior review rounds. **Briefs:** `git ls-files --error-unmatch` proves index membership. A staged new brief has no committed copy; a tracked modified brief can have an anchor only in the dirty version. Resolve brief content/anchor against the actual prospective committed tree and bind that blob identity to the queue evidence. **Remediation:** a same-day journal timestamp proves a row changed, not that the recorded violation was addressed. Link the specific violation/issue and follow-up result or explicit accepted disposition. A note-only edit must not erase an invalid-registration finding. RequiredSince for legacy rows must retain uncertainty rather than invent a recent date (S6). ### S11 — Medium: contradictory executable instructions remain in the body Section 7 contains the better decisions, but 2.E still says absent issues count as closed; 3.E still exempts every missing registration; 3.A says anchors are not verified; 5.6 still recommends optional briefs. 2.A still says missing shell wrappers mean invariant 8 does not cover package tests. Some sections explicitly mark supersession; these do not consistently do so. **Fix:** consolidate the active specification and tests, retaining the old reasoning only as clearly marked history. Publish one state/permission matrix, one schema (including the new snapshot envelope replacing the old array), one journal/recovery table and one acceptance checklist. Otherwise builders can implement opposite rules while following the same plan. ## Are the four modifications honest and bounded? | Modification | Assessment | |---|---| | R1: mkdir/PID-start lock instead of flock | Legitimate implementation choice, but “provable stale-owner detection” is not yet earned. Accept only after S1–S4 specify the failure boundaries. Node lacking built-in flock is not proof a kernel lock cannot cover direct CLI calls. | | R3: cooperative writer, fork refusal | Honest and appropriate with J2 accepted. Bound to one canonical writable checkout and say forks are detected only when their evidence is retained. Do not promise an undefined reconciliation entry repairs arbitrary merged history. | | R6: conditional dependencies and optional incarnation | `settled`, action roles and the no-active-priority limitation are reasonable. Optional nonexistent incarnation and resume of another claim are not a bounded substitute for exclusivity. Correct S7/S8; the row-6 condition need not be asked of Jason again if Sage is applying the already explicit brief. | | R7: at-most-one request with reconciliation | Honest intention, currently false guarantee. At most one attempted POST after ambiguity, with unresolved state and no automatic resend, is bounded and implementable. Successful empty lookup alone is not safe retry authority. | ## J1–J9: who should decide? | Item | Disposition | Recommendation | |---|---|---| | J1 required-row authority | Real owner boundary | Resolve the ambiguous “only Jason moves” against delegated routine progression. Recommend owners may progress approved required work; only Jason changes its mandated priority/removes required status. Do not misstate required→in-progress as inherently contradicting Jason-only authority: it is a legal edge, not an actor rule. | | J2 cooperative trust model | Real owner boundary | Jason must accept the limitation relative to “iron-clad,” or fund a different boundary. Journal replay is not authenticated human authorization. | | J3 queued wording | Lead should resolve | Existing mandatory brief rule is clear. Define queued as an existing planning brief not yet accepted, keep `add --brief`, correct the descriptive queue header and record the interpretation. No need for a separate owner question unless Jason previously mandated brief-less records. | | J4 unpark | Real conflict if implementing | The brief says never change, the queue says Jason reopens. Keep terminal refusal until settled; if reopening is wanted, ask once, preserving Jason-only reopening. | | J5 review→done | Real owner gate change | Jason decides any relaxation of mandatory waiting-on-jason. A non-Jason gateOwner must come from approved gate policy, not a row author's convenience. | | J6 issue closure model | Real acceptance-rule change | Jason approves replacing literal E equivalence; Sage supplies a concrete per-issue closure mapping and test cases first. Do not ask Jason to design the schema. | | J7 broader review-file ban | Lead should drop the optional expansion | The assigned brief already chooses issue receipts and bans docs/plans/reviews. Implement that. There is no present need to ask Jason whether to prohibit every private working report as well. If Sage later wants that broader governance change, it is a separate proposal. | | J8 fetch budget | Conditional owner boundary | Keep #1506's existing weekly metric query unchanged. Sage can choose E's bounded technical fetch design; if the one-call restriction governs the entire combined command, Jason must approve exceeding it. Resolve scope from the original contract before asking, and present one concrete amended budget (base/open-list/per-issue/identity calls distinguished), not “allow 20 or permanently incomplete.” | | J9 row-8 brief | Lead should supply the conservative stub | A factual parked brief can preserve “no fleet migration without Jason,” ownership unassigned, and current boundaries without authorizing work. Keep historical id 8; do not remove the row merely to avoid documenting it. Jason is needed to activate/change its scope, not to record the existing prohibition. | The explicitly held reduced-liveness acceptance and an E-before-D delivery exception remain real owner decisions if the team wants them. They need not block source planning that preserves the original gates. No new Jason decisions are required to fix torn writes, locking, replay determinism or test coverage. ## Recommended next round Sage should choose the canonical-checkout-only cooperative design and a concrete lock/recovery protocol, then have Filbert consolidate the plan with S1–S11 resolved. Prefer refusal with a documented recovery path over claims of automatic repair/idempotency that have not been specified. Carry only the actual owner boundaries above to Jason. Keep implementation held until the protocol and the missing identity evidence are reconciled. Source verification for the key runtime findings used the current shared files (no edits): agent-host-dev.sh SHA-256 `706f8e02fe0d8c18887d2e030f64f447a7db05badb3df3a20800c68ed5313687`, Rocko launch.sh `b23341bc66dbf4594e4aabe641c53e71efbc4ad33a3c91004eb66a1a76447670`, seat.mjs `eec970961a667c9e614d1474d33c31a019b2cf1b0bf931a51458d874bca13a15`. Failure schedules above are adversarial design analyses, not claims of executed fault-injection tests against queue code that does not exist yet.