Skip to content

One module for how a Run's ending shows and how a child Run's is read back - #258

Merged
JacobStephens2 merged 6 commits into
mainfrom
issue-253
Oct 1, 2026
Merged

JacobStephens2 merged 6 commits into
mainfrom
issue-253

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

One module, src/run_ending.rs, now owns a Run's ending both ways: how it shows, and how a Run reads back the ending of a child Run it started. What a Run prints does not change.

 src/
-├── main.rs         # shows the ending: run_outcome, report, print_url
+├── main.rs         # calls run_ending::show / show_failure / show_architect
+├── run_ending.rs   # show: lines on stderr, URLs on stdout, exit code
+│                   # read: Reader fed each relayed line, then stdout + exit
+│                   # private: "session log: " and "Base fix: " prefixes, line order
-├── child_run.rs    # start, relay, interrupt, and reads the last two lines
+├── child_run.rs    # start, relay, interrupt; reads the ending through run_ending::Reader
-├── failed_run.rs   # pub const SESSION_LOG
+├── failed_run.rs
-├── base_fix.rs     # pub const REPORT; Advice::is_line
+├── base_fix.rs     # Advice::is_line
-└── progress.rs     # relay(label, line) -> message
+└── progress.rs     # relay(number, line) -> ChildLine: Own(message) | Relayed | Continuation

How a child Run's ending is read:

 for each line of the child's stderr
   relay it under #<number>
-  if it starts "Base fix: "     -> base fix
-  if it is advice               -> skip it
-  keep it as one of the last two lines
+  if it is not one of the child's own progress lines
+    (it continues the line before, or the child relayed it from its own child)
+    skip it
+  if it starts "Base fix: "     -> base fix
+  else if "session log: "       -> session log
+  else if it is not advice      -> cause, and forget any earlier session log
 when the child exits
   exit 0   -> reached: last line of stdout, base fix
-  else     -> cause = the last line before the session log
+  else     -> cause = first line of the cause it showed, session log, base fix
   no cause -> "the Run <exit status>"

The branch merges main, which since added the advice a Run gives when Inherited failures fail it (between its cause and its session log) and the skipped Architect run (no URL, or several, on stdout). Both now show through the module, and the reader passes over advice as main's did.

Seams under test: the Run ending module's interface (show an ending as values, feed its lines to Reader, compare what comes back), and the progress module's relay (relayed, ChildLine::of).

Evidence

  • Before: the reader ported as it was into the new module, with the round-trip tests written first:

    test run_ending::tests::a_cause_of_several_lines_reads_back_as_its_first_line ... FAILED
      left: ChildFailure { cause: Some("hint: fetch first"), log: Some(".../widgets-21-implement.jsonl") }
     right: ChildFailure { cause: Some("git push failed: exit status: 1"), log: Some(".../widgets-21-implement.jsonl") }
    

    After: cargo test on the merge with main: all 29 suites pass, the end-to-end tests unedited (this branch changes nothing under tests/). The 119 unit tests include 10 round trips in run_ending: ready for review; merged; reached after a Base fix; failed with and without a session log; failed with a pull request left open; a cause of several lines; failed with advice; failed after a Base fix that did not merge; an ending shown after lines relayed from a child of its own; a child that showed nothing. The progress module's tests cover which relayed lines are a child's own. cargo clippy --all-targets and cargo fmt --check are clean.

Unaddressed findings

Standards

  • progress::stamped, progress::relayed and ChildLine::of are pub with no caller outside progress.rs but the Run ending tests. Kept: the issue asks that the round trip "go through the same formatting and parsing a progress line gets, and start no process", which needs the formatting as a value. They are what step and relay print, not a second path.
  • main::architect picks among show, show_architect and show_failure. Kept: the issue fixes the interface as show, with "showing a failure alone … exposed for the Architect run". show_architect is the line and URLs main already printed for an Architect run that dispatched nothing.
  • show_architect's doc says "dispatched no run" in lower case. Kept: it is the wording main.rs and the help already use for "a Spec run or a Run".
  • relay parses a line twice, once to print it and once to say what it is. Kept: two short passes over one line; sharing them would tie the printing to the classifying for no gain a reader would notice.
  • ChildEnding and ChildFailure nearly mirror child_run::Ended, which wait maps them onto. Kept: the issue leaves the child Run's Ended as it is and keeps interruption and the exit status with the child Run module, so wait has to map one onto the other.
  • ChildEnding.base_fix is read back for a failed child but child_run::wait drops it. Kept: the issue decides it "is read back whichever way the child ended, since it is shown either way".
  • relay, relayed and ChildLine::Relayed share a stem, and run_ending::Reader sits beside BufReader. Partly addressed (the local in ChildLine::of is renamed); the rest kept: relayed is the line relay prints, and the issue names the reading half "read".
  • finish(stdout, exited_zero: bool) and Shown.success: bool are bare booleans. Kept: each has one caller and one meaning, and the issue words the interface as "whether it exited 0".

Spec

  • The advice labels (Base check, Retry with, Or set) stay in the Base fix module, so the Run ending module asks Advice::is_line. Not moved: the advice arrived on main after the issue was written, the labels are what the Base fix module builds its advice with, and the Run notification lays them out too. The acceptance criterion names the session-log and Base fix prefixes, which are private. The module's doc now says what it leaves to the Base fix and progress modules.
  • A child that dies mid-Run without showing an ending reports its last ordinary progress line as the cause, not its exit status. Not changed: it is what main's reader does, and the cause line carries no mark to tell it from a step, so telling them apart would change what a Run prints, which the issue rules out. A child that showed nothing of its own still fails with its exit status.
  • show_architect is a third way in, beyond "show and read", with no unit test. Kept: an Architect run that dispatched nothing must "print what it prints today" through the module, and the end-to-end Architect run tests pin its line and URLs.
  • ChildLine::Relayed would also drop a progress line of the child's own whose message starts #<number>: . Kept: no such line exists outside the relay, and the classification is what keeps a relayed line from being read as the cause.
  • A child's unprefixed stderr, such as a panic message, can no longer be its cause. Kept: the issue asks that "only the child's own progress lines are read as its ending". The text is still relayed.
  • The empty thirdshift: failed run commit 07d073c is on the branch. Left: it is the marker an earlier Run left, and removing it would rewrite pushed history.

Merge Danger

Door: two-way

A refactor inside one binary, with no stored format, flag or published interface changed. Reverting the PR restores the old reader.

Blast Radius: contained

Two behaviours differ from main, both in what a Run reads back from a child Run, never in what it prints. A cause of several lines is now named by its first line in the Tickets checklist, the Spec run's summary, the Run notification and the cause of a Run whose Base fix failed. And only the child's own progress lines can be its cause: not a line it relayed from its own child (one starting #<number>: ), nor one with no progress prefix. A parent and child of different thirdshift versions can't meet: a child Run is the same executable.

Closes #253

🤖 Generated with Claude Code

JacobStephens2 and others added 3 commits October 1, 2026 09:31
… back

The Run ending module shows how a Run, a Spec run or a failed Architect run
ended, and reads a child Run's ending back from the lines it relayed. The
session-log and Base fix prefixes are private to it. A cause of several lines
is read back as its first line, and only the child's own progress lines count
toward its ending: the progress module now says which relayed lines those are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…itect run's ending shows through the Run ending module, clearer names in the relay

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g to fix on the branch)

2026-10-01T09:45:33Z, host tracewake. Uncommitted work at the time of failure is included in this commit.
JacobStephens2 and others added 2 commits October 1, 2026 17:32
…eading past the advice and showing a skipped Architect run's URLs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… fix and progress modules, a clearer name for a line from a child's own child

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 marked this pull request as ready for review October 1, 2026 17:49
@JacobStephens2
JacobStephens2 merged commit 3204f2d into main Oct 1, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-253 branch October 1, 2026 18:03
@JacobStephens2 JacobStephens2 mentioned this pull request Oct 2, 2026
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.

One module for how a Run's ending shows and how a child Run's is read back

1 participant