Skip to content

Mark decisions proposed during a replay and refuse to approve them - #55

Merged
shipmill-romamo[bot] merged 2 commits into
mainfrom
feat/36-replay-decisions-marked
Oct 9, 2026
Merged

shipmill-romamo[bot] merged 2 commits into
mainfrom
feat/36-replay-decisions-marked

Conversation

@shipmill-romamo

Copy link
Copy Markdown
Contributor

Summary

agent investigate --replay <recording> saved the decisions the model proposed into --decisions like live ones, and nothing in the decision record said their check output was played back, so an operator could approve one as if its check had run on the host. With --replay decisions and the default --decisions decisions, the replay also wrote new records into the recording it was playing. This implements D-2:

  • a decision proposed during a replay carries a new optional replay field naming the recording. The value is the path as --replay gave it (POSIX form, relative to the repository when given relative), the same value the investigation record already keeps under replay. Older records have no field and read as live
  • decisions list shows the field (it is part of the record); cloudfall why adds a sentence after the check: the output was played back from the recording, not run on the hosts, so it cannot be approved
  • decisions approve refuses such a record with PRECONDITION (context code decision_replayed), before the person is asked for the decision id and before anything runs or the record changes. decision.approve itself refuses it too (first check), so any other caller of the library is covered. operator approve works on operator proposals, not decision records, and decisions approve is not an MCP tool (mcp=False, requires_person), so there is no other approve path
  • agent investigate --replay refuses with ARG_ERROR, before any model call or record, a --decisions directory that resolves (resolved paths, so recorded, ./recorded and the absolute path match) to the recording directory
  • operation-decision.schema.json: optional replay string, additive

#26 (PR #53) changes the same schema and decision.py; whichever merges second needs a rebase. On that rebase, decisions approve should call both refusals before the attestation, replay first.

Test notes

  • sdk/tests/test_investigation.py: a replayed decision names the recording, the live recording record does not, decisions list and why show it; approving it at a terminal, typing its id, is refused with PRECONDITION / decision_replayed and leaves the record byte-for-byte unchanged with no run log; --decisions equal to the recording (recorded, ./recorded, absolute) is refused with ARG_ERROR before the model is asked, leaving no investigation and nothing in the recording
  • sdk/tests/test_decision.py: approve() refuses a replayed decision without running anything; a record without replay reads as live
  • uv run ruff check ., uv run mypy, uv run pytest (full suite), uv run cloudfall config validate --project config/examples pass locally. The Ansible syntax-check and ansible-lint CI steps were not run locally (the session could not run them); this change touches no playbook

Fixes #36

🤖 Generated with Claude Code

shipmill-romamo Bot and others added 2 commits October 9, 2026 17:46
agent investigate --replay saved the decisions the model proposed like
live ones, so an operator could approve one as if its check had run on
the host. Following D-2, such a decision names its recording in a new
optional replay field (the path as --replay gave it, as the investigation
record already does); decisions list shows it, why says so, and decisions
approve refuses it with PRECONDITION before asking for the id or running
anything. --replay also refuses a --decisions directory that resolves to
the recording, which wrote played-back records into the run being played.

Fixes #36

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sked

The investigation and its decisions keep --replay as given, at most 512
characters by their schemas. A longer path crashed agent investigate with
HANDLER_CRASHED on the first proposal's save, after the model was asked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shipmill-romamo
shipmill-romamo Bot force-pushed the feat/36-replay-decisions-marked branch from 1ba4dd5 to 8b5fa13 Compare October 9, 2026 14:49
@shipmill-romamo
shipmill-romamo Bot merged commit bbf1d6c into main Oct 9, 2026
1 check passed
@shipmill-romamo
shipmill-romamo Bot deleted the feat/36-replay-decisions-marked branch October 9, 2026 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decisions proposed during --replay look like live ones

0 participants