Skip to content

fix(repo,stella-pipeline,stella-protocol): unbreak main — file-size skew, a dead binding, and a broken doc link - #2003

Merged
macanderson merged 5 commits into
mainfrom
fix/unbreak-file-size-ratchet-skew
Aug 7, 2026
Merged

fix(repo,stella-pipeline,stella-protocol): unbreak main — file-size skew, a dead binding, and a broken doc link#2003
macanderson merged 5 commits into
mainfrom
fix/unbreak-file-size-ratchet-skew

Conversation

@macanderson

@macanderson macanderson commented Aug 7, 2026

Copy link
Copy Markdown
Owner

What & why

main is red on three independent gate steps, so every open PR inherits
failures it did not cause. This PR is the smallest reviewable change that turns
all three green. None of them is reachable from the others, which is why they are
here together rather than in three PRs — landing one still leaves main red.

Step Failure Cause
file size ratchet 2 god files +1 over ceiling baseline skew between #1979 and #1962
cargo clippy -D warnings dead spend binding leftover from the per-iteration reborrow refactor
cargo doc -D warnings unresolved intra-doc link item re-exported at the crate root, link unqualified

1. file size ratchet — a parallel-merge baseline skew

crates/stella-core/src/driver.rs grew to 2572 lines, over its baseline ceiling of 2571 (+1)
crates/stella-pipeline/src/pipeline/tests.rs grew to 2537 lines, over its baseline ceiling of 2536 (+1)

Neither contributing PR did anything wrong. #1979 added three lines to
driver.rs and #1962 added a line to pipeline/tests.rs, and each regenerated
scripts/file-size-baseline.txt on top of a main that did not yet carry the
other's growth. Both were green on their own merge commits; the composition is
red. This is the repository's most common cause of a red main, and it is why
the baseline is generated rather than edited — hand-patching the two visible
numbers fixes today's symptom and leaves the next skew just as invisible.

Regenerated with make file-size-update on top of current main — the whole
file, not the failing lines — so the result is reproducible by re-running the
command rather than a set of numbers someone picked. The diff moves in both
directions, and the tightenings are the larger half:

File Before After Δ
crates/stella-core/src/bus.rs 2126 1891 −235
crates/stella-pipeline/src/pipeline.rs 3451 3181 −270
crates/stella-core/src/driver.rs 2571 2572 +1
crates/stella-pipeline/src/pipeline/tests.rs 2536 2537 +1

The two reductions are #1994's bus/names.rs split and the pipeline.rs
extraction finally reaching the ledger — work the same skew had been hiding. The
ratchet had been holding those two ceilings 505 lines looser than the tree
actually needs
, which is the direction that quietly readmits bloat.

The two raises are +1 apiece against code already on main behind a review
precisely the irreducible case the escape hatch is documented for (AGENTS.md §
"God files"). No new baseline entry, and no file grandfathered that was not
already. No entry dropped below the 1500-line limit either, so the god-file
tables in AGENTS.md and the crate READMEs are untouched.

2. cargo clippy -D warnings — a dead binding

error: variable does not need to be mutable
error: unused variable: `spend`
  --> crates/stella-pipeline/src/pipeline/scope_stage.rs:34:13

let mut spend = Spend { budget, total }; is the binding's only occurrence
in the file. The loop immediately below deliberately constructs a fresh
reborrow on each iteration, and its own comment says why:

Reborrowed per iteration: the loop replans after a rejected scope card, and a
moved Spend could not be handed to the next attempt.

So the outer binding is leftover from that refactor, not a value the loop
shadows. Deleted rather than underscore-prefixed_spend would keep a dead
constructor alive and read as deliberate to the next person.

3. cargo doc -D warnings — an unqualified intra-doc link

error: unresolved link to `CompactionRewrite`
  --> crates/stella-protocol/src/event.rs:545:53

The type is re-exported at the crate root (lib.rs:75) but is not in the event
module's scope. The field two lines below the doc comment already spells it
crate::CompactionRewrite; only the link was unqualified. Qualified it to match,
so the link and the field now name the same item by the same path.

The witness

  • This PR includes a witness test

Not applicable, and deliberately so — this is a build-artifact regeneration plus
two one-line corrections to already-reviewed code, with no behavior change: the
deleted binding had no reader, and a doc link is not code. The gate steps are
the test
, and all three flip fail → pass.

The ratchet flip was verified locally on a branch cut straight from
origin/main with no other change applied:

$ bash scripts/check-file-size.sh          # before
check-file-size: FAILED
  crates/stella-core/src/driver.rs grew to 2572 lines, over its baseline ceiling of 2571 (+1)
  crates/stella-pipeline/src/pipeline/tests.rs grew to 2537 lines, over its baseline ceiling of 2536 (+1)

$ bash scripts/check-file-size.sh          # after
check-file-size: OK — 1129 Rust/Python/shell files, none over 1500 lines except 30 grandfathered (none grew).

$ bash scripts/check-god-files.sh
check-god-files: OK — 22 god file(s) across 7 crate(s), named identically in AGENTS.md and every crate README.

That same probe branch is how the ratchet failure was isolated from PR #1992's
diff — #1992 is byte-identical to main for both the baseline and both failing
files, so the red was main's, not its.

The clippy and rustdoc fixes were verified in CI, not locally, and that was a
deliberate choice.
Three Terminal-Bench runs are live on this machine right now,
including a Stella-vs-Claude-Code head-to-head. A full workspace clippy + rustdoc
compile would have contended for CPU with a measured benchmark and skewed its
wall-clock numbers. Per CLAUDE.md's "measure honestly" rule, a slower verification
path is the correct trade against corrupting a benchmark this project reports in
public. The cheap guards above compile nothing, which is why they were safe to run.

Ground-rule check

  • No new dependencies; no I/O added to stella-core
  • No new outbound network calls
  • Baseline regenerated, never hand-edited
  • No new baseline entry; no ceiling raised beyond already-merged code
  • No #[allow] used to silence either lint — both were real defects
  • No behavior change, so no serde round-trip or parity-matrix impact

Nothing left behind

#2004 — the ratchet has no defense against this skew, and this is its third
occurrence
(#1761, #1782, now this). Two PRs can each regenerate the baseline
correctly against different snapshots of main and compose into a red tree;
nothing detects it until the next push pays for it. The proposal is to make the
guard judge the change rather than the tree — a file already over its ceiling
at the merge base must not fail a PR that did not grow it. AGENTS.md already
rejects the shared-cell design for GATE_STEPS counts (#1883) for exactly this
reason; the baseline has the same shape and never got the same treatment.

This PR deliberately does not attempt that fix. main is red right now, and
an unbreak should be the smallest reviewable thing that turns it green.

Related to #1986ci.yml does not run on a push to main, which is why all
three of these survived on main rather than being caught at merge time.

Anything reviewers should know?

This unblocks #1992, the only other open PR, which is MERGEABLE/BLOCKED
solely on these checks. Its own merge conflict is already resolved (origin/main
is an ancestor of its head); GitHub had simply not recomputed the stale
CONFLICTING flag. It will need main merged in after this lands.

…arallel-merge skew

`make gate`'s `file-size` step is red on `main` itself, and has been since
#1991 merged, so every open PR inherits the failure:

    crates/stella-core/src/driver.rs grew to 2572 lines, over its baseline ceiling of 2571 (+1)
    crates/stella-pipeline/src/pipeline/tests.rs grew to 2537 lines, over its baseline ceiling of 2536 (+1)

Neither PR did anything wrong. #1979 added three lines to `driver.rs` and
#1962 added a line to `pipeline/tests.rs`, each regenerating the baseline on
top of a `main` that did not yet carry the other's growth. Both merges are
individually correct; their composition is not. This is the repository's most
common cause of a red `main`, and the reason the baseline is generated rather
than edited: a hand-patch of the two visible numbers fixes the symptom and
leaves the next skew just as invisible.

Regenerated with `make file-size-update` on top of current `main` — the whole
file, not the failing lines — which is what makes the result reproducible by
anyone re-running the command rather than a set of numbers someone chose.

The diff moves in both directions, and the tightenings are the larger half:

  crates/stella-core/src/bus.rs           2126 -> 1891   (-235)
  crates/stella-pipeline/src/pipeline.rs  3451 -> 3181   (-270)
  crates/stella-core/src/driver.rs        2571 -> 2572   (+1)
  crates/stella-pipeline/src/pipeline/tests.rs
                                          2536 -> 2537   (+1)

The two reductions are #1994's `bus/names.rs` split and the `pipeline.rs`
extraction landing in the ledger, which the same skew had been hiding — the
ratchet had been holding two ceilings 505 lines looser than the tree needed.
The two raises are +1 apiece against code that is already on `main` behind a
review, which is exactly the irreducible case the escape hatch documents.

No file dropped below the 1500-line limit, so the god-file tables in AGENTS.md
and the crate READMEs are untouched; `check-god-files.sh` confirms all three
copies still agree.

Verified:
  scripts/check-file-size.sh  OK - 1129 files, 30 grandfathered, none grew
  scripts/check-god-files.sh  OK - 22 god files across 7 crates

The same guard fails on a clean branch off `origin/main` with no other change
applied, which is how this was isolated from PR #1992's own diff.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @macanderson, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stella-cli-docs Ready Ready Preview Aug 7, 2026 3:45am

@sourcery-ai

sourcery-ai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Regenerates the file-size baseline artifact from the current main branch to resolve a skew caused by two parallel merges, tightening some ceilings and slightly raising others so that the check-file-size guard passes again without altering any source code.

File-Level Changes

Change Details Files
Regenerated the file-size baseline from current main to realign size ceilings with actual file lengths and fix the failing file-size gate.
  • Re-ran make file-size-update (aka scripts/check-file-size.sh’s baseline generator) on top of the current origin/main tree.
  • Updated all entries in the file-size baseline artifact to match current line counts, including tightening ceilings for files that recently shrank and raising ceilings by +1 for two files that grew on main.
  • Kept the baseline format, ordering, and set of tracked files unchanged, ensuring no new god-file exemptions or entries were added.
scripts/file-size-baseline.txt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@macanderson

Copy link
Copy Markdown
Owner Author

Verified from a parallel session: this PR is one of four independently-red gates on main at 6c34553, and all four must land. The others are #2000 (rustdoc + clippy) and #2005 (fmt newline).

On a local tree of origin/main + this branch + #2000 + #2005, all four gates come back clean — clippy --workspace --all-targets -D warnings, RUSTDOCFLAGS=-D warnings cargo doc --workspace --no-deps, fmt --check, and check-file-size.sh.

Ordering caution, since this PR owns the baseline. Merging this alongside #2000 leaves scripts/file-size-baseline.txt conflicted — whichever lands second inherits it. The fix is to regenerate on the rebased tree (make file-size-update), never to hand-merge the hunks: a hand-resolved baseline is how the current skew started, and the file is generated precisely so it has one authority. In my verification I had to regenerate after merging, and the regenerated file differs from either side's version.

…ding and an unresolved rustdoc link

`main` fails `cargo clippy -D warnings` and `cargo doc -D warnings`, both
independently of the file-size skew this branch already fixes. Neither is
reachable from the ratchet, so the branch had to carry all three or land green
on a tree that is still red.

`scope_stage.rs:34` built a `Spend` that nothing ever read:

    error: variable does not need to be mutable
    error: unused variable: `spend`
      --> crates/stella-pipeline/src/pipeline/scope_stage.rs:34:13

It is the binding's only occurrence in the file. The loop below it deliberately
constructs a *fresh* reborrow per iteration — its own comment says why ("a moved
`Spend` could not be handed to the next attempt") — so the outer binding is
leftover from that refactor, not a value the loop shadows. Deleted rather than
underscore-prefixed: `_spend` would keep a dead constructor and read as
intentional.

`event.rs:545` linked an item that is not in the `event` module's scope:

    error: unresolved link to `CompactionRewrite`
      --> crates/stella-protocol/src/event.rs:545:53

The type is re-exported at the crate root (`lib.rs:75`) and the field two lines
below already spells it `crate::CompactionRewrite`; only the doc link was
unqualified. Qualified it to match, which keeps the link pointing at the same
item the field names.

Both are one-line changes to already-reviewed code. No behavior changes: the
deleted binding had no reader, and a doc link is not code.
@macanderson macanderson changed the title fix(repo): unbreak main — regenerate the file-size baseline after a parallel-merge skew fix(repo,stella-pipeline,stella-protocol): unbreak main — file-size skew, a dead binding, and a broken doc link Aug 7, 2026
…, not by rewriting the published description

The previous commit fixed `cargo doc -D warnings` by qualifying the link as
[`crate::CompactionRewrite`]. That turned `docs/wire` red instead:

    check-wire-schema: FAIL — docs/wire/agentevent.schema.json is stale.
    check-wire-schema: FAIL — docs/wire/agentevent.d.ts is stale.
    check-wire-schema: FAIL — docs/wire/serveframe.schema.json is stale.
    check-wire-schema: FAIL — docs/wire/serveframe.d.ts is stale.

Doc comments on wire types are not commentary — they are exported into all four
generated artifacts as the field's `description`. The `$ref` never moved, so the
shape was identical and only the prose differed, but that prose ships: a
TypeScript consumer reading `agentevent.d.ts` would have been handed a `crate::`
path, which is Rust syntax that names nothing on their side.

So fix the scope rather than the text. `CompactionRewrite` is publicly
re-exported (`lib.rs:75`) from a public module (`lib.rs:54`), and this file
already imports its siblings exactly this way — `crate::context_event::…`,
`crate::subagent_event::…`, `crate::tool::…`. Importing it puts the item in
scope, so the unqualified link resolves, and the field drops its inline path to
match every other field in the enum.

The doc comment is now byte-identical to `main` — it does not appear in this
change's diff at all — so no generated artifact moves and `docs/wire` needs no
regeneration. The `$ref` is derived from the type's schema name and never from
the use-site path spelling, so the field edit is invisible to the exporters too.
@macanderson

Copy link
Copy Markdown
Owner Author

Deadlock found — see #2015. Verified against origin/main at 6c34553: main is red on five gates, and #2003 and #2005 are each red on precisely the gate the other repairs — #2003 (baseline only) fails wire-schema because main's docs/wire/ is stale; #2005 (wire + park consumers) fails file-size because the baseline skew is #2003's fix. Neither can go green alone, which is why all three unbreak PRs have sat open while main stayed red.

#2015 carries the hunks from this PR plus the others so the set can land in one merge. The substantive work is yours — I collected it rather than re-deriving it, and said so in that PR. If you would rather sequence these individually, the equivalent fix is to add the other half to one of these branches; either route works, but a lone merge of any single one will stay red.

All five gates verified green on the combined tree: workspace clippy -D warnings, the rustdoc gate, fmt --check, check-file-size.sh, and make wire-schema, plus tests on the four affected crates.

…d match arm, and the shell doubles #1971 left orphaned

Three more `main` failures, all of which the earlier lib-level errors were
hiding: `cargo clippy --all-targets` stops at the first crate that fails, so
`stella-pipeline`'s *lib test* target was never checked until the `spend` fix
let the lib compile. Each is in a file this branch had not otherwise touched.

`cargo fmt --check` — `event/tests.rs` lost its trailing newline in #1994's
split:

    Diff in crates/stella-protocol/src/event/tests.rs:1487:
     mod tag_table;
    +

Fixed by `cargo fmt --all`; that one byte is the entire formatting diff.

`unreachable pattern` — `management_prompt/tests.rs` names
`ModelCallRole::Research` twice in the same or-pattern:

    error: unreachable pattern
      --> crates/stella-pipeline/src/management_prompt/tests.rs:98:11
    90 |         | ModelCallRole::Research     <- matches all the relevant values
    98 |         | ModelCallRole::Research     <- no value can reach this

Dropped the trailing one. The surviving arm is the one the comment directly
above the match names ("`Research` (#1778) rides the sub-agent primitive"), and
it sits in the position that comment describes; the copy after `Summarization`
is a merge appending a role that was already there.

`dead_code` ×3 — `SHELL_TOOL`, `shell_call_result` and `PassingShell` in
`verification_hardening.rs` are unreachable, because `flip_halt_arming.rs`
defines its own `PassingShell` and `shell_call_result` and an explicit item
shadows a `use super::*` glob. Nothing errored when #1971 restored the deleted
witness with its own doubles; the parent's pair just went quietly dead.

Deleted the parent's three rather than the child's two, because the child's
module doc is the normative statement and it argues for exactly this layout:

    They were split apart once already, and the parent's next wholesale rewrite
    deleted the configured-command witness and both doubles without failing a
    gate [...] Keeping the cluster in one file is what makes that clobber a
    merge conflict instead of a silent deletion.

The child is already self-contained — its doubles spell the tool name `"bash"`
inline and never read the parent's `SHELL_TOOL` — so removing the parent's copy
takes nothing away and restores the one-cluster-one-file invariant that #1971
and #1997 exist to protect. Retargeted the parent's `mod` doc, which still
claimed the child reaches the shell fakes through this file.
macanderson added a commit that referenced this pull request Aug 7, 2026
…eir fixes (#2015)

## The problem

`main` at 6c34553 fails **five** independent gates. Four fixes already
exist across #2000, #2003 and #2005 — but **two of those PRs are red on
exactly the gate the other one repairs**, so none of them can merge:

- **#2003** regenerates `scripts/file-size-baseline.txt` and touches
nothing else → fails `wire-schema`, because main's `docs/wire/` is stale
against its own types.
- **#2005** regenerates `docs/wire/` and the park consumers → fails
`file-size`, because the baseline skew is #2003's fix.

That is a deadlock, and it is why main has stayed red while three
unbreak PRs sat open. This branch carries both halves plus #2000's
repairs so the set can land in one merge.

## The five gates

| Gate | Break | Fix from |
|---|---|---|
| `lint` | a dead `spend` local | #2000 |
| `doc-warnings` | `[`CompactionRewrite`]` resolves only via the
crate-level re-export | #2000 |
| `format-check` | missing trailing newline in `event/tests.rs` | #2005
|
| `file-size` | baseline skew (`driver.rs` +1, `pipeline/tests.rs` +1) |
#2003 |
| `wire-schema` | `docs/wire/` stale against #1994's
`TurnParked`/`TurnWoken` | #2005 |

The rustdoc one is worth a note: **layered masking**, the shape #1965
records. Rustdoc stops at the first crate that fails to document, so
#1970 had to repair `stella-cli` before `stella-protocol` underneath it
became visible at all. Anyone fixing one layer and re-running would
reasonably have believed they were done.

## Authorship

The substantive hunks are **from #2000, #2003 and #2005** — collected
here, not re-derived, so their authors keep the credit. Close those
three as superseded if this lands, or close this one if they can be
sequenced another way; the point is that they cannot each be green
independently. I also opened #2010 for the rustdoc break before finding
#2000 already covered it, and closed it as a duplicate.

## Verification

Run against this exact tree, each gate with the command the Makefile
uses:

| Check | Result |
|---|---|
| `cargo clippy --workspace --all-targets -- -D warnings` | clean |
| `RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps` | clean |
| `cargo fmt --all -- --check` | clean |
| `scripts/check-file-size.sh` | OK — none grew |
| `make wire-schema` | OK — `docs/wire/` matches the types |
| `cargo test -p stella-protocol -p stella-pipeline -p stella-tui -p
stella-core` | pass |

Each was also confirmed **failing** on `origin/main` beforehand, so this
is a demonstrated repair rather than an assumed one.

One caveat, stated rather than buried: `stella-tui`'s
`run_deck_paints_folds_resizes_and_restores_under_a_real_pty` failed
once in the batch run and passed in isolation (20s, against an 81s
timeout) — a real-PTY timing flake under concurrent build load, not a
regression.

The baseline was regenerated with `make file-size-update`, never
hand-edited — a hand-merged baseline is what produced the current skew.

## Note for reviewers

No witness test: every hunk restores an existing gate to green rather
than changing behavior. The reproduction table above is the evidence,
and each gate flips from fail to pass across this diff.

## Summary by Sourcery

Unstick main by combining previously separate fixes so all gates pass
together, including lint, doc warnings, formatting, file-size checks,
and wire-schema consistency.

Bug Fixes:
- Repair lint break by removing the unused `Spend` local from the
pipeline scope stage.
- Fix rustdoc warnings by correcting the `CompactionRewrite` intra-crate
link to the crate-level re-export and mirroring it in generated wire
docs.
- Restore format-check to green by adding the missing trailing newline
in `event/tests.rs`.
- Update `scripts/file-size-baseline.txt` to reflect current binary
sizes so file-size checks match the regenerated code and tests.
- Bring `docs/wire` schemas back in sync with protocol types and
serveframe definitions, including the `CompactionRewrite` documentation
changes.
- Ensure observatory transcripts correctly capture and render
`turn_parked` and `turn_woken` events so journal gaps and wake reasons
are visible.
- Adjust the management prompt tests to match the current set of model
roles and avoid stale expectations.
- Update the fleet dashboard UI to properly represent parked turns as a
distinct state instead of misclassifying them as blocked.

Enhancements:
- Extend the observatory database query and journal rendering to include
parked and woken turn events, with operator-facing descriptions and
timing details.
- Add UI support in the fleet dashboard for displaying parked turns and
holding their state across park/wake so operators can distinguish
deliberate waits from stalls.
- Clarify the flip-halt arming test module layout and move doubles into
the child module to protect against silent deletion on parent rewrites.

Tests:
- Tidy verification hardening tests around flip-halt arming by
delegating doubles into the child module and simplifying the parent’s
documentation.
- Align management prompt tests with the current role handling to keep
the test suite reflecting real behavior.
- Confirm wire-format tests and tag tables remain unchanged while
restoring formatting and wire-schema consistency.

Chores:
- Regenerate wire schema artifacts and file-size baselines using the
project’s existing tooling so all gates share a consistent view of the
repository state.
macanderson added a commit that referenced this pull request Aug 7, 2026
…ngress (#1787) (#2002)

## Why this PR exists

**#1787's fix is not in `main`.** PR #1982 carried it, but its base was
the topic branch `unbreak-main-pipeline`, whose own PR (#1975) was
**closed, not merged**. #1982 then merged into that dead branch, so the
oracle-trace bound landed nowhere `main` can see, and nothing is
carrying that branch forward.

It also merged in a **broken** state. While the base was being
reconciled with `main`, git's auto-merge of the two
independently-written unbreaks concatenated both sides, leaving:

- `struct PassingShell` and `fn shell_call_result` **defined twice**
- `async fn a_revision_halts_at_the_step_where_the_tracked_test_flips`
defined twice
- a duplicate `ModelCallRole::Research` match arm (unreachable pattern)

None of that compiles. `unbreak-main-pipeline` currently holds it;
`main` is unaffected.

This PR is the clean landing: **`main` plus `evidence.rs`, and nothing
else.**

## What it does (#1787)

Bounds the oracle trace at the verifier-prompt ingress. The trace grows
once per verification round and the repair gate can keep granting rounds
while a measured budget affords them — so unlike the diff, which rides
under a token budget, this channel had **no ceiling at all**.

- `MAX_ORACLE_TRACE_OBSERVATIONS = 24` — sized far above a normal run
(baseline plus a handful of rounds) so the bound only bites a
pathological loop.
- `bounded_oracle_trace` keeps the **newest** observations and states
the drop **in-band** (`…N earlier observation(s) omitted → …`), so the
verifier reads "earlier observations exist" rather than a trace that
silently starts mid-run.
- The **stored snapshot keeps the full trace**; only the prompt ingress
is clipped — the structural-bound rule from #1932.

## Witnesses

- `a_pathological_oracle_trace_is_clipped_with_the_drop_stated` — a
100-observation trace renders clipped to the newest 24 with the omission
counted in-band.
- `an_ordinary_oracle_trace_renders_unchanged` — the bound does not
touch a normal run, so this cannot ship as "always clip".

Observations alternate pass/fail in the fixture so a clipped render is
distinguishable from a repeated one.

## Verification

- `cargo test -p stella-pipeline` — **585 pass**, 0 fail, including both
witnesses above
- `cargo fmt --check -p stella-pipeline` — clean
- Diff vs `main` is exactly one file:
`crates/stella-pipeline/src/pipeline/evidence.rs` (+74/−2)

## CI is red on `main`'s breaks, not this diff

This branch is merged up to current `main`. Every failing step fails in
a file this PR does not touch, and each already has a dedicated unbreak
in flight:

| Failing step | Where | Covered by |
|---|---|---|
| `check-file-size` | `scripts/file-size-baseline.txt` (parallel-merge
skew) | **#2003**, **#2008** |
| `cargo fmt --check` | not this crate's file | **#2005** |
| clippy: unused `spend` / unused `mut` | `pipeline/scope_stage.rs:34` —
a dead local `#1985` left behind | **#2000** |
| rustdoc: unresolved `CompactionRewrite` | `stella-protocol` |
**#2010** |

The clippy one is worth naming precisely, since it is `stella-pipeline`:
`main`'s `scope_stage.rs` binds `let mut spend = Spend { budget, total
};` and then never uses it — the loop constructs a fresh `Spend` inline
per iteration. `spend` occurs exactly once in the file. That is `main`'s
dead local, untouched by this PR.

No competing unbreak is included here on purpose — six are already open
against `main`, and duplicating one is how `main` gets re-broken.

## Note on the dead branch

`unbreak-main-pipeline` still holds the duplicate-definition breakage
and the only copy of #1982's merge. It is not reachable from `main` and
its PR is closed, so nothing needs to be reverted — but it should not be
revived without first taking `main`'s copies of `flip_halt_arming.rs`,
`management_prompt/tests.rs` and `scope_stage.rs`, which is what this PR
does. Filed as #2001.

Closes #1787
macanderson added a commit that referenced this pull request Aug 7, 2026
… restart (#1992)

## What

Adds a `/reload` deck command, and makes a SETTINGS-tab save take effect
in the
running session instead of waiting for a restart.

`Config::reload_from_disk` re-reads the settings scope chain (user +
project,
managed ceiling folded in) and re-applies everything
`load_with_settings`
derives from it — engine posture, tool policy, authority, and the
recap/trace/reward/worktree switches — to the live `Config`.

Provider/model/credential resolution is deliberately **not** re-run: it
needs
the full startup chain (interactive prompt included), and swapping
provider
mid-session is a much larger step than a config refresh. `/model` and
the
SETTINGS tab remain the seam for that.

## The interesting part: a reload cannot happen mid-turn

The first cut threaded `&mut Config` down to the deck's overlay handlers
and
reloaded inline. That does not compile, and the borrow checker was right
on the
substance: the deck's in-turn recv site sits in the same `select!` as
the turn
coroutine, which holds `&Config` and is actively reading the very fields
a
reload rewrites (tool policy, authority, engine posture). Reloading
there tears
config out from under a running turn.

So the handlers no longer reload. They report `stale`, and the caller
re-derives at a safe boundary — the discipline `/budget` already follows
with
`pending_budget`:

- **idle site** — reload immediately; the next prompt sees it.
- **in-turn site** — park it, and apply after the turn ends, right
beside the
  parked `/budget` cap.

The delay is invisible in the UI: `engine_config_inbound` and
`tool_policy_inbound` both re-read the scope chain from disk already, so
the
panels show what the files say regardless. Only *subsequent turns*
depend on
the live `Config`.

Exemplar for the shape: this is the same "park the mutation, apply it at
the
safe boundary" pattern `pending_budget` uses a few lines above, which in
turn
mirrors AGENTS.md invariant #6 ("budget aborts at safe boundaries
only").

## Witness test

`config::tests::reload_from_disk_reapplies_the_settings_scope_chain` —
writes
`{"enable_recap": "on", "tools": {"bash": "off"}}` to the user scope
*after*
the `Config` is built, calls `reload_from_disk`, and asserts both the
recap
toggle and the `bash` switch flipped.

Verified the artisanal way: with `reload_from_disk`'s body replaced by
`Ok(())`, the test fails (`reload must re-derive the recap toggle from
the
scope chain on disk`); with the real body it passes.

It redirects the user scope through the thread-local paths seam
(`paths::test_user_home`, #1139) rather than `$HOME` — no env mutation,
no
`unsafe`, no cross-thread race. Worth noting for anyone writing a
similar test:
`UserPaths::test_default()` keeps the developer's **real** home
(`..Self::from_environment()`), so an earlier draft of this test was
silently
reading my own `~/.stella/settings.json`.

## File-size guard

`command_deck.rs` is a god file closed to growth, so none of this landed
in it.
The SETTINGS overlay handlers and the `/reload` body moved out to
`command_deck/settings_io.rs` (the `skills.rs` / `authoring.rs`
pattern), and
`reload_from_disk` lives in `config/reload.rs` rather than pushing
`config.rs`
(1498 on main) over the ceiling.

Net effect: `command_deck.rs` **shrinks** 4621 → 4566, which is the
single line
the regenerated baseline carries.

## Review feedback: a failed reload was not all-or-nothing

The Vercel review bot caught a real defect, now fixed.

`reload_from_disk` assigned six `self` fields before
`settings.reward_policy()?`
— the only fallible step downstream of the load — could fail. That
falsified an
invariant this PR itself documents on `apply_pending_reload`: *"A failed
reload
leaves the session on its previous (still coherent) values."*

The failure mode is worse than a torn write because it is **silent**.
Both
callers tell the user the reload failed and the previous values were
kept, while
the next turn actually runs under a hybrid posture — tool policy
re-derived from
disk, authority and reward weights from session start — that no scope
chain ever
produced.

The repair is a derive-then-commit split: every fallible call now runs
into a
local before `self` is touched, and the commit block is infallible, so
`?` can
only fire while `self` is still pristine. A phase comment states the
rule, so a
future fallible getter lands above the commit block instead of
rediscovering the
hazard. `apply_pending_reload`'s doc now names where its coherence claim
is
actually guaranteed, rather than assuming it.

**Second witness** —
`config::tests::a_failed_reload_leaves_every_field_untouched` writes a
well-formed `settings.json` whose `verifier_weight: 2.0` outranks the
deterministic weight (`reward_policy()` refuses by name rather than
clamping),
then asserts the recap toggle and the `bash` switch are unmoved. Checked
the
artisanal way: against the old interleaved body it fails on the first
assertion
(`a failed reload must not leave the recap toggle applied`); against the
split
it passes.

Both reload witnesses now share a `reload_fixture` helper, so the
redirected
user home and the all-defaults `Config` are built once.

## Not in this PR

- `main` is red on two gates this branch does not touch, and **four**
unbreak
  PRs are already open for them, so I deliberately did not add a fifth:
- **file-size ratchet** — `stella-core/src/driver.rs` (2572 vs a ceiling
of
2571) and `stella-pipeline/src/pipeline/tests.rs` (2537 vs 2536) are
over
the baseline on `origin/main` itself, the parallel-merge skew. Covered
by
    #2003, #2008, #2009.
  - **clippy** — a dead `spend` local in
    `stella-pipeline/src/pipeline/scope_stage.rs`. Covered by #2000.

Both are inherited: `cargo clippy -p stella-cli --all-targets -- -D
warnings`
reports zero findings in a `stella-cli` file, and `check-file-size`
names only
  the two files above, neither of them this PR's.
- This PR's earlier CI red was a stale base: the run tested a merge
against
  `43402ae4`, where `stella-pipeline`'s tests did not compile
  (`PassingShell`/`shell_call_result` missing, `ModelCallRole::Research`
uncovered). `main` has since repaired all three; the branch is merged up
to
  `6c345532`.
- An open TOOLS panel keeps a stale render after `/reload` (and after
`/model`,
pre-existing) — filed as #1990 with the suggested `DeckCommand`
approach,
  because an accurate row list needs the MCP-inclusive live stack that
  `run_deck_command` does not hold.

## Verification

- `cargo test -p stella-cli` — 1463 + 12 integration targets, all
passed, 0 failed.
- `cargo clippy -p stella-cli --all-targets -- -D warnings` — zero
findings in
`stella-cli`; the only errors are `stella-pipeline`'s pre-existing dead
  `spend` local (#2000).
- `cargo fmt -p stella-cli -- --check` — clean.
- `check-god-files`, `check-left-behind` — OK. `check-file-size` fails
only on
  the two inherited files named above.
- Both reload witnesses re-run against the pre-fix body to confirm each
one
  genuinely flips fail → pass.

Refs #1990

## Summary by Sourcery

Add live settings reload support, including a /reload deck command and
automatic application of SETTINGS tab changes without restarting.

New Features:
- Introduce a /reload deck command that re-reads settings from disk and
reapplies them to the running session.
- Allow SETTINGS tab saves for engine configuration and tool switches to
take effect in the current session via deferred reloads at safe
boundaries.

Enhancements:
- Refactor SETTINGS overlay I/O handlers into a new
command_deck::settings_io module to keep command_deck.rs within size
limits.
- Add Config::reload_from_disk as a focused mutation API for reapplying
the settings scope chain to an existing configuration.

Documentation:
- Document the new /reload command in the chat command reference,
clarifying its effect and relationship to SETTINGS and model changes.

Tests:
- Add a config reload test verifying that post-construction settings
edits are reapplied to enable recap and disable tools as specified on
disk.
macanderson added a commit that referenced this pull request Aug 7, 2026
… the observatory journal and fleet row (#1857 follow-up) (#2005)

Follow-up to #1994 (which merged while this was being written). Two
independent things, the first urgent.

## 1. Unbreak main — `cargo fmt --check` is red

#1994 left `crates/stella-protocol/src/event/tests.rs` without a
trailing newline, so `cargo fmt --check` fails on `main` and **every
open PR inherits the red**. One character.

My break, and worth recording how it escaped: the `mod tag_table;`
declaration was appended by a heredoc *after* that PR's rustfmt pass, so
the local gate had already run over the file in its earlier shape and
reported green.

## 2. Three wildcard consumers where the park silently regressed

Adding `TurnParked`/`TurnWoken` broke five crates one at a time — the
compiler caught those. It could not catch the consumers matching
`AgentEvent` behind a wildcard, and because the park used to arrive as
`Text`, all three silently got **worse** when it became its own variant:

- **`stella-observatory` `execution_journal`** selects an `event_type`
allowlist. `'text'` was on it; `'turn_parked'`/`'turn_woken'` were not —
so the park **disappeared entirely** from the transcript an operator
opens to ask why an execution took so long. It is the one event that
explains a wall-clock gap containing no other events.
- Added to the allowlist, plus `journal_entry` arms carrying the payload
(a row saying a park happened but not what was awaited or for how long
answers nothing), plus a frontend arm in `assets/index.html` — without
one it fell through to the `"answer"` branch and drew a blank row.
- **`stella-tui` fleet dashboard** froze `row.action` on the last tool,
so a worker deliberately waiting read as one stuck mid-tool. New
`LastAction::Parked` rather than reusing `Blocked`: `Blocked` means *a
human must act*, and a park needs nobody — mislabelling it would send an
operator hunting an approval prompt that does not exist. `TurnWoken`
deliberately holds the park rather than clearing to `Idle`, since the
next tool or message repaints a beat later and clearing would only
flicker.

## Deliberately not done

- **`stella-serve`'s `TallyFold`** (`observe/tally.rs`) drops both
variants into `_ => {}`. Not a regression (the park was already dropped
as `Text`), but its own doc says a turn whose stages stop advancing is
*wedged* — and park/wake are exactly the signal distinguishing a
deliberate wait from a hang. Filed rather than bundled, since it means
adding a field to `TurnTally`.
- **`TENDENCY_EVENT_TYPES`** excludes park/wake. Judged correct, not
accidental: every other member is a defect or corrective signal (retry,
loop, budget denial, fallback); a park is normal intended operation.

## Note on the `file-size` gate

`main` is *also* red on `file-size` — `driver.rs` and
`pipeline/tests.rs` are each +1 over ceiling from a parallel-merge skew.
**Neither file is touched here**, and #2003 is already the unbreak for
it, so this PR deliberately does not duplicate that baseline
regeneration.

Refs #1857

## Summary by Sourcery

Unbreak the main branch formatting gate and update observability
surfaces so parked and woken spans are correctly included and
represented in operator-facing views.

Bug Fixes:
- Restore display of parked and woken spans in the observatory execution
journal and fleet dashboard so operators can see and understand
deliberate waits.
- Fix the main branch formatting gate by restoring the trailing newline
in stella-protocol event tests.

Enhancements:
- Extend observatory journal payload handling and frontend rendering to
show detailed context for turn_parked and turn_woken events.
- Add a distinct Parked last-action state in the TUI fleet dashboard to
accurately represent engine-side waits without mislabeling them as
human-blocked.
@macanderson
macanderson merged commit d5ad3bf into main Aug 7, 2026
11 of 13 checks passed
@macanderson
macanderson deleted the fix/unbreak-file-size-ratchet-skew branch August 7, 2026 03:45
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