feat(runtime): publish a labelled instruction line beneath the session title - #223
Merged
Conversation
Contributor
CoverageThreshold: |
…n title
A title answers "which session is this". "What is it working on now" is a
different question, and two harnesses were answering it wrongly in opposite
directions.
Codex published nothing. Its title came from a bounded tail read of the
`event_msg`/`user_message` record, and current builds put the prompt elsewhere:
of the 276 local rollouts holding a genuine prompt, 171 (62.0%) have the newest
one outside `tail_bytes`, because `reasoning` records flood the tail with
encrypted blobs. Claude published something stale and looked healthy doing it.
Its `ai-title` is generated once and never refreshed, so on a long session it
names work that finished hours ago.
So line 1 keeps its job and a second, labelled line carries the current
instruction:
line 1 the session's identity, unchanged in meaning
line 2 the newest genuine prompt where it states work -> "asked, 4m: ..."
a bare continuation instead -> the agent's own
turn-start intent,
above an explicit
task_started floor,
"agent, 4m: ..."
-> else the newest
older prompt that
states work, and
only with no
compaction between,
"earlier, 2h: ..."
none of those -> nothing at all
Nothing rather than a guess is load-bearing: the page resolves a title through a
`||` chain, so a wrong value masks the project name for the life of the row.
Both record shapes are read on both records, because the Codex turn-start
preamble moved at CLI 0.149: `event_msg`/`agent_message` with
`phase: "commentary"` covers 295 of the 306 local rollouts on 0.142.5-0.146.1 and
0 of the 88 on 0.149.1, while `event_msg`/`item_completed` with an `AgentMessage`
item covers 78 of those 88 and none of the older files.
Measured on the shipped readers: Codex publishes a title on 280 of 457 rollouts
(61.3%) and a second line on 58 (48 "agent", 10 "earlier"); Claude publishes a
second line on 1,906 of 3,771 transcripts (1,733 "asked", 173 "earlier"). The
walk costs 2.6 ms median and 93 ms worst case on Codex, 0.3 ms median on Claude.
Implements DRC-4264 and DRC-4266 (Linear).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
The duplicate-line rule was a plain prefix test in both directions, so a short
generated title that happens to be the first few words of a genuinely newer
instruction ("Fix the tests" above "Fix the tests failing on Windows") was
read as a repeat and dropped. That is exactly the case this feature was added
for.
The rule is now equality, plus the one case beyond it: line 1 clips at 80
characters and line 2 at 140, so a single prompt reaches them as two strings
with the shorter one ellipsed. Nothing else is a duplicate.
Byte pins recomputed from the assets: next-boot.js 6,930 -> 7,200, and the
assembled next page 227,761 -> 228,031. test_page.py's pins are unmoved,
verified rather than assumed: load_page() still recomputes to 321,499.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
…ntrols Five findings from the adversarial review of #223, plus one the arbiter merged from four reviewers. `_codex_walk` published a previous turn's statement of intent as the current one's. `reached_floor` was read as proof that THIS turn opened, and it only proves some turn did: a turn that has not written `task_started` yet lets the walk run past the newest prompt, overwrite the preamble with the previous turn's commentary, and then set the floor on that turn. The preamble is now bounded structurally instead, assigned only while no prompt has been seen, which makes it strictly newer than the newest genuine prompt and so this turn's by construction. Gating the RETURN on a "this turn wrote no floor" flag was tried first and is worse: the two differ only when a turn has commentary but no floor of its own, and there the flag suppresses a preamble that is genuinely current. The frozen corpus measures 0 incidence, which is why this needs a fixture; the mutation that drops the new clause republishes the previous turn's intent. A bare harness-control slash command published as the instruction on 202 of 1,906 local Claude lines (`/login` 74, `/clear` 68, `/plugin` 23, `/mcp` 12), indistinguishable in the labelled slot from something the operator asked for. #222 fixed this class for the observer's goal slot two hours before this branch rebased onto it, so the list is not written a second time: `_is_harness_control` and its measured names move to `records.harness_control` and both surfaces call it. Two primitives disagreeing about whether `/clear` is an objective is the bug #222 was eliminating. The carve-out it measured survives, with a test: a bare SKILL invocation (`/create-pr`, `/cargento:cargento`) IS the operator's intent and keeps publishing, and so does an argument-carrying command. After: 0 of 1,820. `instruction_line` scrubbed at the cap while `clip` appends its ellipsis AFTER cutting to it, so the marker was trimmed off 29 of 1,906 lines and a mid-token cut read as a truncated sentence. The bound is now the cap plus one, the same `+ 1` and the same reasoning line 1 already carries. After: 4, and all four are texts naturally exactly at the cap, which `clip` never cut. The two instruction rules in the next stylesheet set no overflow guard. 50 of 1,789 published lines carry an unbreakable token over 80 characters (longest 113), pasted URLs that `shorten_paths` deliberately leaves whole; under `max-width:760px` the detail padding drops to 0 and one of those gives the whole page a horizontal scrollbar. Every comparable surface in the file already guards it. Byte pins recomputed from the assets, not resolved textually: next `styles.css` 24,551 -> 24,597 bytes, the assembled next page 228,031 -> 228,077, both digests replaced. All twelve JS part digests are unchanged, and the +46 is exactly two copies of the declaration. Finally, `states_work` counted words on `prompt_title`'s output, which is line 1 only. The rendering has to decide the SHAPE, because that is where a slash command becomes recognisable, but the count belongs on the tag-stripped body: counting the rendering called 97 of 2,066 local newest prompts a bare continuation when the operator had written an instruction, and quoted an older prompt in place of the one they had just sent. Two shapes, both measured: a short opener over a multi-line body, and a one-line prompt whose first 140 characters are mostly a pasted URL, which counts as a single word. All 97 move the same way and none moves the other. The shipped skill body was wrong in three ways this branch introduced, and it is a validated artifact. The Session-title bullet claimed the fallback is the session's newest instruction: that is true only on Codex, and the fallback is the OPENING prompt on Claude (`claude_data.session_title`) and on Pi (`collectors/pi.py`), which is the great majority of titled rows. The new bullet claimed Codex suppresses line 2 when its title is the newest instruction "word for word", and 167 of 280 local Codex titles are ellipsed truncations, 144 of which suppress the line while holding back a median 56 further characters. And the line renders only on the opt-in `?next=true` UI, the convention the file already uses twice, since the default bundle's APP_PARTS read no `instruction` field at all. Refs DRC-4264. Signed-off-by: Jared Scott <jared.scott@variable.team> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Jared Scott <jared.scott@variable.team>
Four claims the review fixes invalidated. The module map said `states_work` renders first and counts words second. It reads two different things off one prompt now: the rendering decides the shape, the tag-stripped body decides the count, and the row says why counting the rendering was wrong on 97 of 2,066 local newest prompts. The same row, and the rejected-alternatives list in the session-identity design, both described the turn floor as the thing that bounds the preamble. It is not sufficient on its own, and the design doc keeps the floor bullet while naming what reaching one does not prove. `records.harness_control` is new to the `records.py` row, with the reason it lives there rather than in either caller, and so is `instruction_line`'s cap plus one. The observer row no longer implies it owns the control list. `COMPATIBILITY.md`'s sync marker is deliberately untouched: this is a feature branch, and parallel branches cannot vouch for one another's work. Refs DRC-4264. Signed-off-by: Jared Scott <jared.scott@variable.team> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Jared Scott <jared.scott@variable.team>
gcko
force-pushed
the
feat/session-instruction-line
branch
from
August 27, 2026 07:02
d19a917 to
cabbc5c
Compare
This was referenced Aug 27, 2026
gcko
added a commit
that referenced
this pull request
Aug 27, 2026
AGENTS.md's Parallel Work section says to leave this marker alone per branch and stamp it once from main after the merges, naming what the range actually covers. Three parallel branches just landed, so this is that stamp. 11730b5..0f9d086 is eleven commits: the v0.16.0 release, the five PRs that built the instruction line and the redaction filter (#220, #222, #223, #225, #226), the review-calibration and CI-detector change (#227), and the three DRC-4269 follow-ups that cleared the deferred-findings backlog (#228, #229, #230). Each of those PRs carried its own doc updates, and the three DRC-4269 branches deliberately did not touch this line so they could not collide on it. What this stamp vouches for is that range as a whole: the design docs, SECURITY.md and the runtime architecture module map were reconciled against the code inside the PRs that changed it, and several figures in them were re-derived rather than carried forward. Signed-off-by: Jared Scott <jared.scott@variable.team>
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.
Implements DRC-4264 (Codex sessions carry no instruction line) and DRC-4266 (Claude session titles are the session's opening instruction, not its current work). They ship together because they are one UI change: the same second line, on the same cards, from the same payload fields.
The problem
A title answers "which session is this". "What is it working on now" is a different question, and two harnesses were answering it wrongly in opposite directions.
Codex published nothing. Its title came from a bounded tail read of the
event_msg/user_messagerecord. Of the 276 local rollouts holding a genuine prompt, 171 (62.0%) have the newest one outsidetail_bytes, becausereasoningrecords flood the tail with encrypted blobs, and CLI 0.149 writes the prompt under a different record shape entirely.Claude published something stale and looked healthy doing it. Its
ai-titleis generated once and never refreshed: of the 425 transcripts carrying two or more of those records, 424 carry a single distinct value. On sessions with four or more real prompts the title shares no content word with the newest one in 112 of 152 decidable cases (73.7%).The design
Line 1 keeps its job. A second, labelled line carries the current instruction.
Codex publishes no
askedline, because there line 1 already is that prompt verbatim.Two clauses are load-bearing:
||chain, so a wrong value does not mislead once, it masks the project name for the life of the row. On the local corpus 177 of 457 rollouts (38.7%) reach that branch, closely matching DRC-4264's predicted 39.5%. That is the design working: 143 of them have<recommended_plugins>as their only user record.records.instruction_linerefuses an unlabelled reading outright.Measured coverage of the shipped readers
Run through the shipped functions, not a reimplementation, so the measurement and the filter cannot diverge.
agent, 10earlierai-title)asked, 173earlierWalk cost: Codex median 2.6 ms, p95 11.9 ms, max 84.7 ms. Claude median 0.3 ms, p95 2.3 ms.
Both preamble shapes are read, and the per-version table is why. The Codex turn-start record moved at CLI 0.149; a single-shape reader finds nothing on the build the operator actually runs.
event_msg/agent_messageitem_completed/AgentMessageBoth user-record shapes plus 0.149's third (
item_completed/UserMessage) are read for the same reason, each with its own test.Recomputed byte pins
Recomputed from the assets, never patched textually. Old values are this branch's merge base (
4f0be9b), which is ahead of the figures quoted in the issues.next-boot.jssizenext-boot.jssha2564b801dc5…d5f40852…next-sessions.jssizenext-sessions.jssha2566e2fd028…32d5c413…next-session.jssizenext-session.jssha2567d827a70…b444364d…styles.csssizestyles.csssha2562ae8e188…4a519ef9…load_next_page()sizeload_next_page()sha256443c083e…d74f335e…What moved them: the shared
nextInstructionLinerenderer and its label vocabulary innext-boot.js, the line rendered into the detail header innext-session.jsand into the identity cell innext-sessions.js, and three rules in nextstyles.css. The assembled page moves because every part feeds it.test_page.py's pins did not move, and that was verified rather than assumed.load_page()recomputes to 321,499 and appstyles.cssto 60,265, both identical to the pins onmain. NoAPP_PARTSfile is touched by this branch.Two defects found and fixed while building
/burndown DRC-4266 and the boardwas classified a bare continuation and buried. Fixed bytranscripts.states_work: render first, count second, with slash commands exempt because/releaseis two words rendered and a whole instruction meant. This lifted Claude line-2 coverage from 42.8% to 50.5%.claude_data.session_titlereturned the rawaiTitlevendor string with nosafe_textand no cap, the one untrusted vendor string in the runtime reaching the payload unbounded. DRC-4266 asks that any change editingsession_titleclose it rather than preserve it. Measured harmless on this corpus (max 69 characters, no C0 or bidi), so it is hardening, not a live bug.Decisions recorded
instructionkey, not a widerlast_prompt. That field is read at ten render sites and two of them are not the session card (next-projects.jsputs it first in its own chain,calm.jsrenders it standalone), so a labelled line packed into it would leak onto both with no label to explain it.records.safe_textcollapses newlines, so two lines cannot share one string field anyway.config.pythreshold.config.pyis a documented conflict hotspot. The caps (PROMPT_TITLE_CAP_CHARS80,LAST_PROMPT_CAP_CHARS140,INSTRUCTION_CAP_CHARS140) and the six-word continuation rule live inrecords.py, which is where the bounding happens. DRC-4264 asked that the 80 and the 140 stop living in two files; they now live in one.turns.scan_turns.test_contracts.pypins its imports and excludestranscripts, soprompt_titleis unreachable from it. The readers useio.reverse_lineswith a(mtime_ns, size)cache, the patternclaude_data.session_titlealready established. The two new cache slots are documented as never hitting for a live session; they pay for the idle and?all=1rows.<command-message>/<command-name>/<command-args>stay out of the rejection lists, and both readers route throughtranscripts.prompt_titlerather than growing new tag-stripping code.Tests
42 new test methods across five modules, three of them mutation-checked against deliberate regressions (removing the turn floor, failing to freeze the preamble at the floor, dropping the 0.149 preamble shape) and each caught by exactly the intended test.
test_records.py(continuation rule, unlabelled refusal, bounding, unusable stamps),test_transcripts.py(14 cases: all three user shapes, both preamble shapes, first-of-turn versus last, the floor, the publish-nothing branch, both compaction shapes, caching, malformed records),test_claude.py(anchor A, continuation fallback, six injected shapes, compaction in both directions, slash commands, a prompt behind the tail,aiTitlebounding),test_codex.py(four end-to-end row assertions including sanitization),test_next_session.py(7 render cases: label and age, the closed label vocabulary, publish-nothing, the no-duplicate rule and its deliberate non-duplicate counterpart, unstamped, escaping).Docs
sync-docsreconciled:SKILL.md(the title note and the Codex data-source line),docs/design-runtime-architecture.md(four module rows),docs/design-next-ui.md(the header), and a new D-6 indocs/design-session-identity.mdrecording nine rejected alternatives with their measurements, including the character-length rule, the positional rules, the silent walk-back, and the single-shape read. Thedocs-synced-throughmarker is deliberately untouched per the parallel-work rule.Follow-up commit
d19a917corrects the duplicate-line rule. It was a plain prefix test in both directions, which suppressed a line whose title merely opens it ("Fix the tests" above "Fix the tests failing on Windows") — the case the feature exists for. It is now equality plus the one ellipsis case that the two caps genuinely produce, with a test pinning each side.🤖 Generated with Claude Code