workflow-fix: codex-* composers leave their own agent-memory writes uncommitted (3x in one session)
Highlight text on any card (body, plan, or an event) to anchor a comment, or leave a whole-task comment here. Mention @claude to summon a reply.
No comments yet.
Overview / Motivation
Observed three times in a single session (#2263 review rounds 4, 5 and 6). Every codex-code-reviewer prompt-composer spawn wrote an agent-memory lesson, deliberately left it uncommitted in the issue worktree, and asked the orchestrator to "sweep it post-merge." Each time the orchestrator had to commit it by hand.
The composers' stated reasoning is consistent and superficially sound: "committing mid-round would enter the very diff under review." It is wrong on the facts, and the disposition it produces is the riskier one.
Goal
Give the codex-* prompt-composers an explicit instruction covering their own agent-memory writes: commit them by explicit path in the producing turn, with the reason the mid-round-contamination worry does not apply.
Workflow gap
Why the composers' reasoning is wrong. Review briefs pin the reviewable diff to explicit commit SHAs (git show <sha>, or an enumerated range), and the orchestrator names every excluded non-deliverable commit in both reviewer briefs. A commit landing after the brief is composed cannot enter a SHA-pinned range. The contamination the composers are avoiding is not reachable.
Why the disposition is actively worse than committing. CLAUDE.md § "Uncommitted TRACKED state at the shared root is unsafe under concurrency" (#2015) makes agent-memory writes the fleet's single largest standing-armer class — 8 of the 14 files in the #2015 standing diff — precisely because they have no natural committer. The rule is that the session whose agent wrote a memory file commits it by explicit path in the SAME turn. "Sweep it post-merge" is the mechanism by which that standing diff accumulates.
Two concrete hazards observed in this very session:
- One composer edited
MEMORY.md(a TRACKED file) and left it modified-uncommitted while the same worktree had live pre-commit stash-race activity — the composer itself observed~/.cache/pre-commit/patch*files bracketing a window in which10-step-6.mdtransiently reverted. A tracked modification sitting in that window is exactly the #2015 permanent-loss shape. - A dirty
.claude/agent-memoryalso marks that family dirty for the Step 5a spec-freshness sync, so main-side agent-memory drift silently stops syncing into the worktree for the rest of the branch's life.
Why it needs a spec fix rather than orchestrator vigilance. The composers are behaving reasonably given their instructions — the shared composer contract (.claude/rules/codex-composer-common.md) says nothing about agent-memory or committing, and .claude/agents/codex-code-reviewer.md has no memory-commit instruction either. So each composer independently invents the same wrong heuristic, and correctness depends on the orchestrator noticing the flag in a long return block. It was noticed three times here; it will not always be.
Proposed change (sketch — refine in planning)
Preferred target is the SHARED contract, since the hazard is identical for every codex-* composer (codex-critic, codex-interpretation-critic, codex-clean-result-critic, codex-follow-up-critic, codex-code-reviewer), not just the code-reviewer twin:
In .claude/rules/codex-composer-common.md:
+ ## Your own agent-memory writes
+
+ A memory lesson you write is a tracked write like any other: commit it by
+ explicit path, in the SAME turn, together with its MEMORY.md index row (one
+ commit, so the tracked index edit never sits uncommitted). Do NOT defer it to
+ a post-merge sweep and do NOT leave it for the orchestrator.
+
+ It cannot contaminate the round you are composing for: review briefs pin the
+ reviewable diff to explicit commit SHAs, so a commit landing after compose is
+ outside the reviewed range by construction. Leaving it uncommitted is the
+ riskier choice — uncommitted agent-memory is the fleet's dominant
+ standing-armer class (CLAUDE.md § "Uncommitted TRACKED state ...", #2015), and
+ a dirty .claude/agent-memory also stops the Step 5a spec-freshness sync for
+ that family for the rest of the branch's life.
+
+ Use the guarded-commit form with LITERAL paths:
+ git -C <absolute worktree path> commit -F <msgfile> -- <literal paths>
+ A variable-expanded pathspec reads as opaque to guard_root_code_commit.sh,
+ which then falls back to the shared staged index and blocks on another
+ session's payload.
Planning should decide whether .claude/agents/codex-code-reviewer.md also needs a pointer, and whether the Claude-side reviewer/critic specs have the same silence (the #2263 Claude code-reviewer DID commit its own memory unprompted at round 3, so the gap may be composer-specific).
Related but distinct, worth checking in the same pass: at #2263 review round 5 the Claude code-reviewer emitted two CONCERN:: rows in its verdict marker and reported them as "persisted", but they never reached concerns.jsonl — the orchestrator's forwarder is wired for the Codex twins only, and the reviewer's own raise-concern never fired. That is a separate persistence gap on the Claude reviewer side; file it separately if planning confirms it is not already covered.
Verified at filing
- #2263
events.jsonl: the round-4, round-5 and round-6 orchestratorepm:progressnotes each record a composer's uncommitted-memory flag and the hand commit that followed (f002e1276e,9a2af9494e,2ba96b4fe3). grep -n -i "commit" .claude/agents/codex-code-reviewer.md— no agent-memory commit instruction (2026-08-22).grep -n -i "agent-memory\|commit" .claude/rules/codex-composer-common.md— zero hits (2026-08-22).- Dedup: a repo-wide scan of
tasks/*/*/body.mdforagent-memoryfound no task covering composer memory-commit duty; the nearest hits are unrelated per-agent memory CONTENT syncs (#1200, #1236, #1261).
Provenance
workflow_fix_target: .claude/rules/codex-composer-common.md
Routed from three observations during #2263's review rounds per .claude/rules/workflow-fix-on-bug.md. Filed by the #2263 orchestrator; task_workflow.is_workflow_fix_session(2263) is False, so the recursion guard does not apply, and the target file is unrelated to #2263's own deliverable.