Skip to content

Wardens: cwd/worktree drift causes silent gate abstains and co-gate bucket mismatches #1025

Description

@liam-cyberpro

Problem

Warden/gate invocations (GPT codex_warden, edit-gate, verification-gate) resolve their target (diff source, verdict-store bucket) from the invoking process's cwd. When an agent's mental model of "which worktree am I in" drifts across a cd/EnterWorktree boundary mid-session, this produces two silent failure modes rather than a loud error:

  1. Empty-change abstain - running the GPT warden from the main-repo cwd instead of the target worktree reads an empty diff and abstains, which looks identical to "nothing to review."
  2. Co-gate bucket mismatch - a cd moves the hook event's cwd mid-session without the agent noticing, so a SHIP verdict gets written to the wrong bucket (flat vs. per-worktree), and the gate that reads the other bucket reports "not run yet" even though it was.

Both are a correctly-written fail-closed gate silently no-op'ing - the one case where silence reads as "protection is off," not as an error.

Where it shows up

Surfaced repeatedly across a multi-day session retrospective (3 separate incidents in one window: two wrong-cwd GPT warden runs, one mid-session bucket split). Root cause is the same in all three: nothing asserts/echoes the invocation context (worktree identity + non-empty diff) before the gate call fires.

Fix applied downstream (fork)

Landed as a doc-only preflight habit in the fork (liam-cyberpro/Deus#31, commit 6714c85): before invoking any warden/gate command, run git rev-parse --show-toplevel + git status --short and eyeball both, so drift is caught before the call instead of after a confusing abstain/mismatch. Added as a new "Gate Invocation Preflight" section in .claude/rules/orchestration-rules.md, cross-referencing the existing "Warden Co-Gate Verdict Marking" section.

This is intentionally the cheap fix, not the complete one - it's a habit, not an enforcement. A more durable version would be mechanical: codex_warden refusing to run on an empty diff instead of silently abstaining, or the gate/hook itself printing the resolved toplevel + bucket at invocation time so a mismatch is visible immediately rather than discovered later via the existing "co-gate bucket mismatch" diagnostic message. Flagging both here for visibility - not opening a PR, since fork/upstream sync here is issues-only by convention on this end.

Suggested next step

Worth deciding whether:
(a) pull the doc-only preflight line into upstream orchestration-rules.md (or wherever the canonical version lives) as-is, or
(b) skip straight to the mechanical fix (empty-diff hard-refuse in codex_warden, or an invocation-time context print in the gate hook) and treat the doc line as superseded.

Happy to provide the exact diff from the fork commit if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions