feat(stella-cli): the Claude Code transcript adapter — local-only, option (b), honest about what the corpus cannot supply (#2304) - #2367
Conversation
…tion (b), and honest about what the corpus cannot supply
Implements PR 5 of `doc:trace-replay-learning-harness` §9, and records what was
actually built as §13.
Claude Code transcripts contain no Stella reflection JSON, so the adapter must
either derive lessons from transcript text or decline to. Deriving them
fabricates the exact signal under test. This implements the spec's recommended
option (b): shell history, session boundaries and timing only.
That needed a fourth `ScriptedReflection` arm. Every existing spelling of "the
source carried no reflection" is a claim the source does not support —
`Lessons { lessons: [] }` asserts the model had nothing to say, `Unreadable`
asserts it said something unparseable, and the second would fabricate
starvation and corrupt assertion 7's own metric. `NotRecorded` is counted
separately and skips the model boundary entirely.
The privacy gate lands where the risk actually is. Under option (b) no
statements are derived, so there is no proposal to quarantine; the highest-risk
field the adapter touches is the shell command. Every command is redacted, and
a command whose redaction fired is dropped rather than kept with a hole in it.
Measured against the real corpus: 496 project directories, a 20-project sample
adapting to 16,664 turns and 17,343 commands, every trace passing the loader's
contract.
Refs #2304
There was a problem hiding this comment.
Sorry @macanderson, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Reviewer's GuideImplements the Claude Code transcript adapter for the trace replay learning harness using option (b): adapt shell history, session boundaries, and timing only, introduce a Sequence diagram for replaying turns with NotRecorded reflectionssequenceDiagram
participant Replayer
participant Turn
participant ScriptedReflection
participant Memory
Replayer->>Turn: get reflection
Turn->>ScriptedReflection: reaches_the_model()
alt reflection reaches model
ScriptedReflection-->>Replayer: true
Replayer->>Memory: reflect_and_record(provider, "replay", transcript, true, succeeded, None)
Memory-->>Replayer: ReflectionReport
else reflection NotRecorded
ScriptedReflection-->>Replayer: false
Replayer->>Replayer: ReflectionReport::default()
end
Replayer->>Replayer: run foundry and summary over accumulated history
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Heads-up on a spec collision with #2371. Both PRs edit
Different subsections, so a textual conflict is likely but the resolution is a straight union — take both. Whichever lands second should rebase and confirm §13 still reads 13.1 → 13.2 → 13.3 → 13.4 with no duplicate heading. Flagging it rather than racing, because a same-day rewrite of one file by two PRs is how additions get silently dropped in this repo. No code overlap: #2371 touches Also worth saying plainly: #2358 came out of this harness, and #2371 shipping a measured fix within hours is the loop working — the corpus in #2371 can now be worded naturally, which is exactly the workaround §13.1 said should not outlive the bug. |
Refs #2304. Implements PR 5 of
doc:trace-replay-learning-harness§9 — the Claude Code transcript adapter — and completes spec §13 with the adapter's half (§13.3), which #2350 left open.Based on
main, which now carries the harness — #2350 merged while this was in flight.(Replaces #2357, which GitHub auto-closed when #2350 merged and its base branch
feat/2304-trace-replay-harnesswas auto-deleted. Same content, rebased onto the merged harness.)The adapter's honest limit is the design
Claude Code transcripts contain no Stella reflection JSON. There is nothing in them to script a lessons array from, so §7.2 puts the choice plainly: derive lessons from the transcript, or decline to. Deriving them means the harness measures the adapter's lesson-invention heuristic instead of Stella's learning — it fabricates the exact signal under test.
This implements option (b), which the spec recommends: shell history, session and turn boundaries, and timing only. It lights up the tool foundry against thousands of real commands and launders nothing.
That needed a fourth
ScriptedReflectionarmExpressing "the source carried no reflection" with the three existing arms is impossible without asserting something the source does not support:
Lessons { lessons: [] }Unreadable { .. }ModelError { .. }The second is the dangerous one: it would fabricate starvation, and assertion 7 (
an_unreadable_corpus_builds_nothing_and_says_why) counts exactly those turns. An adapter that emittedUnreadablefor 16,000 turns would corrupt the metric its sibling test depends on.So
NotRecordedis its own arm. The replayer skips the model boundary entirely for it, and the summary countsturns_not_recordedseparately from empty reflections — a metric that folded them together would report the learner as idle when it was never asked.Provenance::Derivedalready exists on theLessonsarm from #2350, andevery_lesson_is_labelledasserts nothing reaches a trace unlabelled — so if option (a) is ever built, it cannot ship un-stamped quietly.The privacy gate (§7.1), applied where the risk actually is
The spec asks for "a secret-shaped string is quarantined rather than stored". Under option (b) the adapter derives no statements, so there is no proposal to run through
gate_proposal/quarantine_for. The highest-risk field it does touch is the shell command —export ANTHROPIC_API_KEY=sk-ant-…andcurl -H "Authorization: Bearer …"are ordinary things to have typed.So the gate runs there, and it is stricter than quarantine:
redact_secretsruns on every command, and a command whose redaction fired is dropped, not kept with a[redacted]hole in it. Two reasons:Beyond that: local-only and opt-in (
STELLA_REPLAY_CC_CORPUS, reachable only from a test — no shipped command, and a plaincargo testnever touches the corpus); nothing derived is committed (the CI corpus stays synthetic, permanently, andno-scratchfails the gate on a committed derivative on its own); and the transcript stub is left empty, because nothing reads it under option (b) and carrying user text into a trace we did not need would be gratuitous retention.Measured against the real corpus
Every adapted trace was round-tripped through
Trace::parse— the loader's own contract, applied to a real uncontrolled source rather than to a synthetic fixture the adapter was written against. That is the assertion that catches a transcript shape the adapter mishandles.A correction to §7's measurement, recorded in §13.3. The spec measured 485 project directories on 2026-08-08; it is 496 today. Consistent with the rolling window the spec already flags, and the reason a derived trace can never be a committed fixture.
The spec now records what was built (§13)
The document said
status: proposed. Rather than leave a plan describing something that now exists, §13 records the divergences and — more usefully — the four things replaying the real loop measured, none of which is visible from reading a single module: the shared dedup/clustering threshold, the foundry's value-like-argument rule, the workspace-derived lineage id, and lexicalstarts_with. A future reader planning fixtures needs all four.Verification
cargo test -p stella-cli— 1531 passed, 0 failed (12 new), on top of the merged harnessSTELLA_REPLAY_CC_CORPUS=1 cargo test … the_real_corpus_adapts— output abovecargo clippy -p stella-cli --all-targets -- -D warnings— cleanmake guards-fast— clean (main is red: shellcheck fails on scripts/test-arena-scripts.sh, reddening every open PR #2355's shellcheck failure is fixed onmainby fix(arenabench): emit the launch stub with a quoted heredoc, so shellcheck and the gate are green again (#2355) #2363)No tests deleted.
One guard caught a real mistake, on the first run
paths::tests::nothing_else_in_this_crate_reads_a_home_out_of_the_environmentfailed on mystd::env::var_os("HOME"). Fixed tocrate::paths::home(), which is what lets a test redirect the anchor without mutating process-global state (#1139). Worth naming because it is exactly the guard working as designed.Not in this PR
Summary by Sourcery
Add a local-only, opt-in Claude Code transcript adapter that feeds real shell history into the trace replay harness without inventing reflections, and extend the harness to account for turns where the source provided no reflection.
New Features:
ScriptedReflection::NotRecordedarm and corresponding replay summary metric to represent turns where the source carried no reflection JSON.Enhancements:
Documentation:
Tests: