Skip to content

story-084: A rerun keeps the output that explains it - #82

Merged
jerodw merged 1 commit into
mainfrom
story/story-084
Aug 29, 2026
Merged

story-084: A rerun keeps the output that explains it#82
jerodw merged 1 commit into
mainfrom
story/story-084

Conversation

@jerodw

@jerodw jerodw commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Story

A rerun keeps the output that explains it.

A self-route caused by a red suite cited suite-run-result.json and its output file. The rerun it triggered then wrote both over — so by the time a human read the record, the artifact it named held a different run, usually the green one that ended the story, and nothing said so. The coordinator owning the suite run is what makes the exit code trustworthy; the cost was that it kept one slot for an unbounded number of runs.

Change

suite_run_check writes two pairs instead of one.

  • The canonical pair at the run root keeps its present meaning exactly — suite-run-result.json and the output beside it hold the most recent run — so every reader that already knows those names, the verifier's rendered context among them, reads what it read before.
  • Beside it, a pair keyed by stage, attempt and try through retained_suite_result_file, in the shape self_route_result_file and prompt_file already establish. The retained output name is derived by passing that result name back through suite_output_file, so the output filename is shaped once rather than twice.

The try number is the invocation's own self_route_count — the same number the prompt file for the invocation this run judges is keyed by — so the prompt a stage was given and the suite run judging that turn share a key by construction rather than by two derivations that happen to agree.

One record, written twice, with each copy's output_path repointed at the file beside it — the pointer-rewriting shape _archive_companion_output already uses — so following either record's own pointer reaches that record's run. suite_run_check returns the retained pointer, so the self-route's artifacts array and the statement composed from it name the pair that survives. The reason text is untouched: still the truncated summary, since what needed widening was what it points at, not the sentence.

attempt and try_number are required and keyword-only for the reason stage_name is — a defaulted argument would let a call site write an unkeyed run by omission, which is the overwrite the keying exists to remove.

Retention is unconditional: a story with a single suite run writes the retained pair too, and the OSError path writes both records with no output_path on either. The only path writing neither is the one that wrote neither before — a target configuring no test_command.

Bound, and what reads these

Retention is unbounded and the bound is structural. Nothing prunes or expires a retained file and no configured retention key was added; the ceiling is arithmetic on budgets the workflow already declares — one pair per invocation, invocations per attempt no more than max_self_routes plus one, attempts no more than the retry budget. The files are small and run directories are gitignored.

Nothing routes on a retained file. Every occurrence of the identifiers under orchestration/ is a write position except two discovery calls that move names and decide nothing: retained_suite_artifacts is written beside self_route_artifacts and built the same way, globbing the run directory because the try is live state a resume has already zeroed, taking the artifact off suite_run_declaration and the names off the writing functions with the try wildcarded — so discovery cannot drift from writing and no artifact name is spelled in orchestration/. interrupted_attempt_artifacts calls it so a resume archives an interrupted attempt's retained pairs before the resumed stage's first suite run lands on those names; entry_artifacts calls it with the attempt wildcarded too.

Evidence

  • Suite on the working tree: 3799 passed (suite-run-result.json, exit 0)
  • Suite in a fresh clone with the story committed: 3799 passed (clean-clone-result.json, exit 0)
  • Verification: passed, no blocking issues (verification-result.json)
  • Revert-check nomination: tests/test_coordinator_runs_the_suite.py::test_the_statement_names_both_the_record_and_the_path_to_the_output — exit 1 reverted, exit 0 applied
  • 25 tests written; new module tests/test_retained_suite_runs.py
  • 0 retries; one self-route, on the tester's own new module (test_the_fixture_really_makes_two_suite_runs), repaired in place

Note on the run

This run's own directory holds no retained pair, and that is expected rather than a finding: the coordinator process driving it imported story_coordinator.py before the change existed, so it ran the pre-story code. It is the recurring shape of any story moving work into orchestration. The retention is verified instead through tests/test_retained_suite_runs.py, which drives run_story against temporary targets with a faked agent runner — evidence that does not depend on the running process having loaded the code under test. The first run to demonstrate it on itself is the next one.

story-083's scope field appears in all three of this run's records, which is its own first live confirmation.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HkujNrgMcsRBFWNEKWYnmA

Implemented by the l5 harness story workflow.
@jerodw
jerodw merged commit 37aa86c into main Aug 29, 2026
3 checks passed
@jerodw
jerodw deleted the story/story-084 branch August 29, 2026 01:43
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.

1 participant