Repository navigation
Mark decisions proposed during a replay and refuse to approve them - #55
Merged
Merged
Conversation
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
Bot
force-pushed
the
feat/36-replay-decisions-marked
branch
from
October 9, 2026 14:49
1ba4dd5 to
8b5fa13
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
agent investigate --replay <recording>saved the decisions the model proposed into--decisionslike 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 decisionsand the default--decisions decisions, the replay also wrote new records into the recording it was playing. This implements D-2:replayfield naming the recording. The value is the path as--replaygave it (POSIX form, relative to the repository when given relative), the same value the investigation record already keeps underreplay. Older records have no field and read as livedecisions listshows the field (it is part of the record);cloudfall whyadds a sentence after the check: the output was played back from the recording, not run on the hosts, so it cannot be approveddecisions approverefuses such a record withPRECONDITION(context codedecision_replayed), before the person is asked for the decision id and before anything runs or the record changes.decision.approveitself refuses it too (first check), so any other caller of the library is covered.operator approveworks on operator proposals, not decision records, anddecisions approveis not an MCP tool (mcp=False,requires_person), so there is no other approve pathagent investigate --replayrefuses withARG_ERROR, before any model call or record, a--decisionsdirectory that resolves (resolved paths, sorecorded,./recordedand the absolute path match) to the recording directoryoperation-decision.schema.json: optionalreplaystring, additive#26 (PR #53) changes the same schema and
decision.py; whichever merges second needs a rebase. On that rebase,decisions approveshould 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 listandwhyshow it; approving it at a terminal, typing its id, is refused withPRECONDITION/decision_replayedand leaves the record byte-for-byte unchanged with no run log;--decisionsequal to the recording (recorded,./recorded, absolute) is refused withARG_ERRORbefore the model is asked, leaving no investigation and nothing in the recordingsdk/tests/test_decision.py:approve()refuses a replayed decision without running anything; a record withoutreplayreads as liveuv run ruff check .,uv run mypy,uv run pytest(full suite),uv run cloudfall config validate --project config/examplespass locally. The Ansible syntax-check and ansible-lint CI steps were not run locally (the session could not run them); this change touches no playbookFixes #36
🤖 Generated with Claude Code