Skip to content

A bypass session can still silence resume's config-change warning by deleting the out-of-tree baseline #571

Description

@pbean

Follow-up from the codex review of #570, which relocated the resume config-change baseline (trusted_config_digest) out of state.json into the run's out-of-tree state dir per the recorded 2026-08-08 decision on #498.

The residual

#570 closes the incidental silencing path: the pin is no longer a project file, so nothing a session does in the ordinary course of rewriting the tree can collaterally blank it. That is the case the advisory is documented to catch ("it catches the incidental change, not a deliberate one").

It does not close the deliberate path, and the review is right that this deserves stating plainly rather than as a footnote:

  • Every shipped profile launches with permission bypass — claude.toml --permission-mode bypassPermissions, codex.toml --dangerously-bypass-approvals-and-sandbox, gemini.toml --approval-mode=yolo, copilot.toml --allow-all-tools --allow-all-paths, antigravity.toml --dangerously-skip-permissions. This is the default operating mode, not an edge case; it is what an unattended loop is.
  • The session is handed BMAD_LOOP_EVENTS_DIR (engine.py), whose parent is the run's state dir. dirname() yields config-digest.
  • Deleting it makes the reader answer None → the legacy fallback reads state.json's field, which is "" for any run started after fix(resume): move the config-change baseline out of the agent-writable tree (#498) #570bool(pinned) false → no warning. Truncating it answers "" → same.

So a session that goes looking can still silence the warning; it just has to be deliberate about it rather than blanking a field it was already editing.

Why #570 did not fix it

Not an oversight — there is no sound fix at equal privilege, and the half-measures each cost something:

  • Treat absent/empty as tampering. Requires distinguishing "this run should have a baseline" from "genuinely legacy run". Any marker carrying that distinction has to live somewhere the same session cannot reach — and at equal privilege no such place exists. Put it in state.json and the session edits both; put it in the state dir and it is deleted with the file.
  • Warn whenever there is no baseline at all. Fires a false alarm on every genuinely legacy run's first resume, on a warning whose whole value is that it is rare. Training operators to ignore it is worse than the hole.
  • Hide the path. The run id and the state root are both derivable from the events dir the session must be given. A secret in the filename would have to be stored where resume (a fresh process) can find it — i.e. in-tree.

The direction that would actually work

Privilege separation on the state root, not a better hiding place: the control plane owned by a principal the driven session does not run as (separate user/service account, or ACLs that grant the session no write to the state dir while the orchestrator retains it), with the session given only what it must have — write access to events/ and nothing else in the state dir.

That is a real design change with real portability cost (POSIX ownership/ACLs vs Windows ACLs vs the WSL-UNC seam already tracked in #536/#552), and it would also harden the events channel, which today has the identical exposure — a session can delete events/ and stall its own run. Worth deciding as one piece rather than bolting a marker onto the digest.

Meanwhile

#570 states the limit explicitly in runs.config_digest_path_for's docstring and in the CHANGELOG entry, so the claim on record matches what the code delivers.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P4Parked - needs design, blocked upstream/PR, or speculativearea:configBMAD config resolution and the setup skillarea:engineOrchestrator engine and run lifecycleenhancementNew feature or requestneeds-designAwaiting a maintainer design decision before code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions