codebook-v2-messages.html, which this page extends rather than repeats.AutoCodeToast.tsx paints today. The sidebar column uses the vocabulary already shipped for a
pipeline run: verb-led text, · separator, a bare n of m count, and the 16pt
determinate ring in the subtitle-right slot with its hover-× cancel.A is what the sibling page already drew; B is what this flow argues for. The run is not finished
when the job completes — nothing has been applied to a single quote yet. Thirty-six proposals sit in
ProposedTag with status="pending" until someone opens the review and moves the
thresholds. A row that says 36 tags on 72 quotes describes a completed act; the researcher's actual
state is a decision is waiting. B says that, and is shorter.
Neither row is a button, and neither should become one. The sidebar is attention, not affordance — the standing rule for this surface. So on the Mac the door to the review lives where the codebook lives: the framework card in the codebook lens, in the WKWebView, which is the same React surface the browser shows. The toast's Report link is the browser's door because the browser has no sidebar; it is not a second door on the Mac, it is the only door on the other channel.
ThresholdReviewModal is React inside the same web view, so
there is nothing to fork. Drawn once, on the shipped threshold-* classes.Drag the thresholds to control how many auto-tags are applied as tentative or accepted.
The three zones are where the researcher's judgement actually goes, and only one of them is a
decision. Above the upper threshold is accepted, below the lower is denied, and the band between is
left pending — handleApply calls acceptAllProposals(upper) then
denyAllProposals(lower) and touches nothing in the middle. Tentative is not a third verdict, it
is the absence of one, which is why its counter is the dashed border rather than a filled state.
Apply is the only irreversible act in the whole flow, and it is two sequential network calls with no
transaction around them. If the second fails, the accepts have already landed and the denials have not;
autocode.review.applyFailed says "your per-row decisions are saved", which is true and
does not describe that particular half-applied state.
The tokens here are correct, unlike the toast's. threshold-review.css names
--bn-colour-positive and --bn-colour-warning, both of which the palette defines in
default and Edo. It is autocode-toast.css that reaches for
--bn-colour-success and --bn-colour-danger, which the palette does not define at
all. So the review renders correctly today and the toast's tick does not — and if progress moves to the
sidebar, that bug stays behind in the browser channel rather than being fixed by the move.
1. There is no SubtitleVariant case for tagging. The enum
(ProjectSubtitle.swift) carries running, queued, copying,
importingBatch, addingInterviews and nine more — every long job the app knows
about. AutoCode is not among them, so the row above cannot be produced by the shipping code. Adding a case is
the smallest change that makes any of this drawable, and it is genuinely new — flagged here rather than
slipped in.
2. The ring is driven from PipelineState, and AutoCode is not one.
ProjectRowActivityIndicator.Kind.from switches exhaustively over PipelineState with
no default: — deliberately, so a new state forces a decision at compile time. AutoCode
runs in serve mode and has its own AutoCodeJob table; it never becomes a
PipelineState, so it cannot reach that switch at all. Either the indicator grows a second input
or autocode jobs get projected into the state the sidebar already reads.
3. Nothing under bristlenose/server/ writes to the events log. That log is the channel
the Mac glyph, subtitle and popover are fed from, and the sibling page recorded the same gap for the failure
strings. AutoCode is entirely a serve-mode feature — server/autocode.py,
server/routes/autocode.py, polled over HTTP by the SPA every 2s. Every sidebar row on this page
is therefore a drawing of a surface that exists, fed by a wire that does not. This is the one blocking
dependency; items 1 and 2 are small in front of it.
4. The completed toast never dismisses itself, and that is load-bearing.
AutoCodeToast.tsx renders the close button only when the job is not completed, and
auto-dismisses on failure alone. So today the toast is the persistence layer for "a decision is waiting" —
badly, since it dies with the tab. If the Mac takes that state into the row, the browser still needs somewhere
durable for it, and the framework card in the lens is the obvious candidate on both channels.
The sidebar row needs no new vocabulary, and that is the argument for it. Every element above already ships for a pipeline run:
chrome.pipeline.sessionsCountis literally"{{complete}} of {{total}}", the·separator isSidebarSubtitleText.activityText's, and the ring isSidebarActivityRingat its shipped 16pt with the hover-× cancel. A tagging run is a long job against a project; the row already knows how to say that.The Mac drops the elapsed timer and the percentage on purpose. The toast can afford
1:12because it is a transient thing you are looking at. The row is glanced at from across a window that is otherwise about something else, andcloud-import-sidebar-progress.htmlalready settled the question for the import phase: a count is the question a closed window leaves — not how fast, not which batch, but how many are still to wait for. Same argument, same answer.22 characters is a real ceiling, not a guess.
SidebarSubtitleText.swift:34-44records the run-failed string being cut back to"Run failed"because a 36-character sentence truncated mid-phrase in English before any locale swell. 58 of 72 quotes tagged is exactly 22 and therefore already at the line in the shortest language we ship. It wants shortening before it is translated, not after.