Repository navigation
Record a read run as ran and supersede a repeated proposal - #53
Merged
Merged
Conversation
A risk: read operation has run by the time operations propose records it, yet its record stayed proposed forever, and proposing the same operation again left every older record open: one disk-full incident produced 47 records for one host. Following D-1, a read is now recorded as ran with its run's exit code (ranExitCode), and a new proposal marks any open proposal of the same operation, targets and inputs superseded, naming itself in supersededBy. decisions approve refuses both before asking for the id, and decisions list and why filter on --status (repeatable). The schema change is additive; records already on disk keep their status. Part of #26 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A read record written before #26 still says proposed. The next run of the same read supersedes it with a decision recorded as ran, and approving the old one pointed at that ran decision with "approve that one instead". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
Every
operations proposesaved its record asproposed, so arisk: readoperation, which has already run in check mode by the time it is recorded, stayedproposedforever, and proposing the same operation again piled up open records (47 forcloudfall why --host hz1in one disk-full incident). This implements D-1:proposerecords a read operation with the new statusranand its run's real exit code in a newranExitCodefield (the same value ascheck.exitCode, which is still there)proposerecords an operation, every openproposedrecord with exactly the same operation id, targets (scope and pattern) and inputs becomessuperseded, withsupersededBynaming the new decision and a verdict saying so. Records in any other status (approved, executed, verified, failed, ran) are never toucheddecisions approverefuses aranorsupersededrecord withPRECONDITION/decision_not_proposed(the existing refusal for a status that is notproposed), now before the person is asked to type the id; the superseded message names the newer decisiondecisions list --status Sandcloudfall why --status S(repeatable) keep only those statuses; an unknown status is a usage error (ARG_ERROR). The MCPdecisions.listandwhytools get the same argumentoperation-decision.schema.json:ranandsupersededare added to the status enum;ranExitCodeandsupersededByare optional fields, required byif/thenwhen the status isran/superseded. Additive: records already on disk keep their status and load as beforewhytells both statuses in its last sentence and gives them their own badge tone in the HTML pageoperations proposereads the record before running check mode (to find what it supersedes), so a broken record now stops it withRECORD_INVALIDbefore anything runs;RECORD_INVALIDis added to its declared exit codesstaleis out of scope (postponed to a spec), so the issue stays open.Test notes
sdk/tests/test_decision.py: a read recorded asranwith a non-zero exit code; a mutating proposal stillproposedwith noranExitCode; supersede of the same call; no supersede for another target, another input value or an extra input; an approved record is never superseded; a pre-decisions: read operations stay "proposed" forever, and abandoned proposals are never superseded #26 read record (proposed) still loads and is superseded by a new read run;approverefusesranandsupersededwithout running anything; the issue's repro (read, then the same mutating proposal twice) listsran,superseded,proposed, and--statusfilters it; an unknown--statusis refused; the CLI refuses a superseded record before asking for its idsdk/tests/test_why.py: theranandsupersededsentences,why --status(repeatable), an unknown status refusedexecuted; a read is nowran, so they approve a read recorded the pre-decisions: read operations stay "proposed" forever, and abandoned proposals are never superseded #26 way instead, the only path left toexecuted(every mutating or destructive operation must declare a verify step)uv run ruff check .,uv run mypy,uv run pytest(full suite),uv run cloudfall config validate --project config/examplesandcloudfall-engine inventory renderpass locally. The Ansible syntax-check and ansible-lint CI steps were not run locally (the session could not run them); this change touches no playbookPart of #26
🤖 Generated with Claude Code