Keep the landing hero when a startup diagnostic arrives - #415
Merged
Conversation
A skill referenced by three plugins and absent from the search path is one missing skill, not three: the operator installs it once and all three resolve. Counting raw warnings made the summary both wrong and self-contradicting, reading "7 skills missing" above a list of nine names.
Anything the runner says before the first turn lands while the landing still owns the screen, and a transcript row there reaches clearLandingMark and takes the whole composition with it — the mark, the guidance beside it, and the centred prompt box. The previous fix routed the MCP and hook producers away from the transcript one at a time and the plugin producer kept the defect. Routing the runner's own notice helper through the shell's notice path instead fixes every producer at once, including the ones nobody has written yet, and gives the constraint a single owner rather than a rule each call site has to remember. The gutter label goes with it. A system row's text already says what it is, so stamping it "command" only leaked wiring into a column the operator reads.
The sky above the ridgeline was dead space. A sparse field of pixel snow drifts through it on the clock the mark already runs on, so the hero has motion even while the silhouette is held. Density and fall rate stay low deliberately: the mark has to keep reading as a mark, and a storm would turn the one legible thing on the screen into texture. Flakes never land on mountain coverage, and they stop entirely when the mark is held still or is fading out, so the decoration never outlives the thing it drifts over. Absorbed from the standalone snow branch rather than reimplemented; the taste question it was open on is settled by making snow part of the default landing.
The meta column is the operator's: it says what a row is about, and it is read at a glance beside every row in the transcript. A row labelled "palette" says only which part of the code emitted it, which is a fact about us and not about their session — and the three rows carrying it already open with "palette:" in their own text, so the column was repeating a word it sat next to.
The key and its description were joined by a single space, so the two lines started their descriptions on different columns and read as two unrelated notes rather than as the set they are. A fixed key column lines them up. The version moves a row away from them for the same reason: sitting flush under the two keys it read as a third door, when it is only a statement of what is running.
The wrapper module added nothing the shell function did not already do. It introduced no type and narrowed no export, so a producer reaching for appendStreamRow directly was exactly as easy with it as without, and the history it documented reads better on the function itself. The name was also wrong. The path carries unknown commands, unavailable modals and provider failures, none of which happen at startup, and a name that lies to the next reader is how this constraint got lost twice.
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.
The decision worth reviewing: the wiring layer, not the call site
The reported bug is that a startup plugin warning wipes the entire landing hero — mark, guidance, centred composition. The obvious fix is to change the one flush site in
runner.tsto usesurfaceStartupNotice. This PR deliberately does not do that.systemRowturned out to be the onlyappendStreamRowcaller inrunner.ts, feeding all ten of its call sites. So the helper itself is the layer that owns "what happens when the runner says something," and that is where the fix belongs. Routing that one helper through the shell's notice path fixes every producer at once — including the ones nobody has written yet.This is the second time this exact symptom has been fixed. The previous fix routed the MCP and hook producers away from the transcript individually, and the plugin producer kept the defect. A cross-cutting constraint applied at call sites is one forgotten call site away from being false, which is precisely how this bug survived its own fix.
So the constraint gets an owner:
src/tui-opentui/startup-notices.tsexposesflushStartupNotices, and the invariant it holds is a startup diagnostic is never a transcript row. It is small on purpose — the value is that the rule is stated once and named, not that it is complicated.Reviewers should pressure-test exactly this: is the seam load-bearing, or ceremony over a one-line fix? That is the live question, not whether the bug is fixed.
What else is here
No transcript row can be labelled with the wiring that produced it.
meta: "command"(runner.ts,runner-host.ts) and the threemeta: "palette"(shell.ts) painted verbatim into a twelve-column gutter the operator reads. A system row's text already says what it is; the three palette rows literally opened with the wordpalette:beside a column repeating it. Audited acrosssrc/tui/andsrc/tui-opentui/: there is no literalmeta: "overlay"on any rendered row, so that half of the rule was already satisfied.meta: "observe"is kept — observe is a real product mode an operator would name, not plumbing.Each missing plugin skill is counted once.
formatPluginWarningsSummarycounted raw warnings, so three plugins each referencingstyle,philosophyandbrand-identityproduced7 skills missing:above a list of nine names. A skill referenced by three plugins is one missing skill — the operator installs it once and all three resolve. The count is now taken from the deduplicated list, so the number and the names cannot disagree again.Snow over the mark. Absorbs #380 rather than reimplementing it: that branch's implementation and its tests are taken directly. The sky above the ridgeline was dead space; a sparse field of pixel snow drifts through it on the clock the mark already runs on. Density and fall rate stay low deliberately — a storm would turn the one legible thing on the screen into texture. Flakes never land on mountain coverage, and stop entirely when the mark is held still or fading, so the decoration never outlives the thing it drifts over. #380 can be closed on merge.
The landing's two doors read as a pair. Key and description were joined by one space, so the two lines started their descriptions on different columns. A fixed key column sets them. The version moves one row away for the same reason: flush under the two keys it read as a third door, when it only states what is running.
Testing
RED first on both defects. The dedupe test reproduced the operator's exact string before the fix. The landing test asserts a session emitting a startup plugin warning still renders the mark, and a companion asserts no flushed notice ever carries a plumbing gutter label.
Full suite via the repo's mutex-wrapped runner: 4269 pass, 1 fail. The failure is
src/agent/lsp-availability.test.ts— it probes for an installed language-server binary, is environmental, and is untouched by this diff. Typecheck is clean across every file changed here; the onlytscerrors are pre-existing vendor type drift insrc/agent/director.ts, which this diff does not touch.Verified PR #410's animated phase indicator and narrowed side gutter are intact and not reintroduced-over.
Still to come on this branch
Composition work continues as follow-up commits: the changelog swap for fresh installs, and the notice register (notices show only when true, at most two at a time — a permanent stack of four is the failure mode being designed out). The bug fix is not held for it.
Notes
cl-5718-landing-hero-implrather thancl-5718-landing-herobecause the canonical name was locked by another worktree and could not be released. Not a stray branch.docs/plans/became git-ignored on main today.