Skip to content

Write-scope guard (#134): draft_writer_agent Bucket A multi-phase static union — refine at Slice 3 (low priority) #330

@Imbad0202

Description

@Imbad0202

Summary

Post-ship review of PR #294 (#134 Slice 1 scoped-write guard, surfaced via #295) found the guard to be strongly engineered with no live fail-open on main. codex raised 1 P1 + 1 P2: the P1 is an empirically-disproven false-positive (recorded below), the P2 is a valid refinement of a documented Slice-1 limitation — filed here at low priority, not a ship-blocking defect.

Independent review: codex gpt-5.5 xhigh + adversarial first-party read + direct invocation of evaluate_decision.


Refinement (P2, low priority) — draft_writer_agent is the one Bucket A agent given a multi-phase static union

scripts/ars_phase_scope_manifest.json classifies draft_writer_agent as Bucket A phase: "4" but grants it allowed_write_globs: ["phase4_*/**", "phase6_*/**"]. Because the PreToolUse payload carries only agent_type (not the invocation phase), the guard cannot distinguish a Phase 4 draft invocation from a Phase 6 revision invocation, so a Phase 4 draft call can write Phase 6 dirs — a static-union phase-inflation.

This is within the documented Slice-1 limitation, not an oversight. Spec §3.6 (line 123) states explicitly: "A static union of a multi-phase agent's scopes is weak (it would permit writes to any phase the agent ever touches, defeating the point). Real enforcement... needs per-invocation scope grants — a later slice" — and Slice 3 (§4 item 3) specifies exactly that (per-invocation grants from the dispatching layer, under a designed authenticated-grant trust boundary).

The refinement question this finding sharpens (worth deciding when Slice 3 is scoped): draft_writer_agent is in Bucket A (fenced), unlike the 4 Bucket B multi-phase agents that the spec deliberately leaves exempt precisely because a static union half-defeats the fence. Giving a Bucket A agent a Bucket-B-style two-phase union weakens its fence to the same degree the spec uses to justify exempting Bucket B. Options to weigh at Slice 3:

  • Narrow draft_writer_agent to phase4_*/** in Slice 1 (its declared phase: "4"), and route its Phase 6 writes through the Slice-3 per-invocation grant once that lands; or
  • Reclassify it alongside the Bucket B multi-phase agents (exempt until per-invocation grants exist); or
  • Keep the static union and accept the documented inflation until Slice 3.

No urgency — the inflation is bounded to one agent and to the phase4↔phase6 pair, and the real fix is already on the Slice-3 roadmap. Tracking so the decision isn't lost.


Not a bug — codex P1 false-positive (workspace-escape gating), empirically disproven

codex flagged ars_write_scope_guard.py:273-278 as [P1]: "the unconditional escape denial runs before the not is_bucket_a allow gate, so the hook blocks main-session / Bucket B/C/D cross-worktree edits."

The escape branch (lines 285-305, NOT 273-278 which is the extraction helper) already gates by is_bucket_a: only a Bucket A agent is denied on escape; a non-Bucket-A actor returns _allow_unconstrained. Verified empirically by invoking evaluate_decision:

main-session escape  -> allow   (codex claimed wrongly denied)
Bucket B/C/D escape  -> allow
Bucket A escape      -> deny     (the intended #302 fence)

This is exactly the behavior codex asks for; it was the #302 fix and is documented in the code comment at lines 287-298 and pinned by the spec §3.5 test ("main-session / non-Bucket-A escape allowed"). codex cited the wrong line range and didn't trace to the escape logic. No action.

Ref #134, #295.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions