A re-run of an executed plan is unguarded and overwrites its own audit record #195
Labels
No labels
already-shipped
bug
documentation
duplicate
enhancement
external-review
good first issue
help wanted
in progress
invalid
needs-decision
proposal
question
security
wontfix
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: crenshawdev/cadence-archived#195
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The defect
Re-running a plan destroys the record of the previous run, and nothing stops the
re-run from happening by accident.
Two halves, one fix site each.
1.
locatedoes not refuse an already-executed phasecadence-core/workflows/execute.md'slocatestep stops onunplannedand onmissing plan files, and on nothing else. A phase whose cursor status is
executedis dispatched again exactly like a fresh one: a new executor starts attask 1 against a plan whose tasks are already committed.
The within-run protections do not cover this. The
partialandtimeoutarmsread
reports/plan-<k>.mdfor completed task numbers specifically so acontinuation cannot repeat a finished task, and that reasoning applies just as
well across runs. It is simply not applied there.
2. The report file is overwritten in place
skills/cad-executor-contract/SKILL.md:74has the executor rewrite<plandir>/reports/plan-<k>.mdafter every task commit, and:197fixes thepath at
plan-<k>.mdwith no run component. So the second run's first taskcommit overwrites the first run's report before anything has read it.
That report is the only per-task record of what was run and what it printed. The
SUMMARY manifest survives (it is a separate file), but the evidence behind each
row does not, which is precisely the artifact you would want when comparing two
runs of the same plan.
Why it matters
The audit trail is otherwise sufficient to reconstruct a run: SUMMARY maps task
to hash, the report carries the verify command and its output per commit, the
risk-checkrecord is keyed to the run, and the trace brackets carry the tokenfigure. Every one of those is run-scoped or append-only EXCEPT the report, which
is the most detailed of them.
The correlation id already exists and is already unique per run - it derives from
PHASE_START (
2-392ae60for the run that closed phase 2), so the scoping key isin hand and costs nothing to adopt.
The fix
locaterefuses a phase whose cursor status isexecuted, naming thesupported path (
/cad-undo <N>then/cad-execute <N>) rather than silentlyre-executing. An explicit override flag is a reasonable escape hatch; a silent
re-run is not.
reports/<corr>/plan-<k>.mdorreports/plan-<k>.<corr>.md, with the readers that already derive the path(the
summarystep, the continuation branches,/cad-undo's manifestfallback) deriving the same one.
Blast radius
The path is derived in
cadence-core/workflows/execute.mdat three read sites anddeclared in
skills/cad-executor-contract/SKILL.mdat two. Any change has to moveall five together, and the executor contract is a preloaded surface under a weight
budget, so the shorter spelling is the cheaper one.
Watched failure before the fix
Execute a phase, note the report contents, execute the same phase again, and show
the report replaced with a run that re-did committed work. That is a two-command
reproduction and it should be the failing case committed first.