Repository navigation
fix(sleep): mark Claude replay sessions with a producer sentinel - #310
Bogdan (Dan) Baciu (bogdanbaciu21) wants to merge 1 commit into
Conversation
|
Reviewed Please distinguish producer provenance from ordinary quoted prompt content, including custom-template cases, and keep a real harvest regression for both engine sessions and normal lookalikes. Coordinate with #287's Codex producer marker and #272's identity-based exposure tracking; those address related but different boundaries. I have not rewritten or split the feature on your behalf, and the broader issues remain open. |
|
Thank you, Dan, and apologies for how long this has taken. The four evidence-integrity fixes from this PR are now on The fifth change, the harvest marker, is the only open point, and it is the same one from my Oct 17 review. Markers derived from the prompt registry also drop an ordinary user session that discusses or quotes the grader prompt; the same session is kept on
Nothing beyond those. |
With projects="all" the engine's own Claude replay sessions are harvested
and can be mined back as user tasks: the static prompt markers in
_is_headless_replay were written for earlier prompt wording and match none
of the current attempt, judge, reflect, miner or tool-attempt prompts.
Matching current prompt text instead cannot tell who produced a session, so
it also drops an ordinary session that quotes the grader prompt.
Identify the producer, the way the Codex backend does:
- ClaudeCliBackend starts every prompt it hands to the CLI with
CLAUDE_REPLAY_SENTINEL ("[skillopt-sleep:claude-engine:v1]"), after the
template is rendered, so default and custom templates are covered alike.
Cache keys and the token estimate are still computed from the unstamped
prompt
- _is_headless_replay drops a single-turn session whose prompt starts with
the sentinel; a session that quotes an engine prompt or the sentinel later
in a message is kept, and multi-turn sessions are kept as before
- the existing static markers are unchanged for transcripts written before
the sentinel existed
964d9c0 to
f6a0b6f
Compare
|
Thank you for landing the four fixes through #392 and for keeping the commits and authorship intact. This PR is now rebased onto current Against the three criteria:
Things you would otherwise have to find:
Validation on |
|
I updated the description. Proof now comes second and closes the gap I had listed: I ran the real |
What Problem This Solves
With
projects: "all", the engine's own Claude replay sessions are harvested and can be mined back as user tasks._is_headless_replayrecognizes engine sessions by fixed prompt markers written for earlier prompt wording. None of them match the prompts the Claude backend sends today, and the duration fallback only covers prompts under 200 characters.The earlier revision of this PR matched the opening lines of the current prompt registry instead. That cannot tell who produced a session: it also dropped an ordinary user session that quotes the grader prompt, and it missed an engine attempt that uses a task-supplied system prompt.
The other four fixes from the original PR landed through #392. This PR now contains only the harvest change.
Proof
Two kinds of evidence: a run through a real
claudeCLI, and deterministic tests.Live run, real CLI. The installed
claudeCLI (2.1.322) wrote real transcripts into a config directory created for the run, driven by the shippedClaudeCliBackendon two trees:mainat45f65caand this branch atf6a0b6f. Three engine operations per tree: attempt, judge, and tool attempt (the tool attempt really called thesearchshim). No API key was set, so the backend's isolation guard probed the CLI and took--safe-mode, which this CLI advertises. The CLI talked to an Anthropic-compatible endpoint (OpenRouter, modeldeepseek/deepseek-v4.1-flash-1017). A normal user session that quotes the full grader prompt, and mentions the sentinel in the middle of the message, was then added in the CLI's own record format. Each tree's realharvest(..., scope="all")read each directory. The design and the results that would count against this PR were written down before the run.mainbackendmainmainbackendmain[skillopt-sleep:claude-engine:v1]at offset zero, and each transcript has exactly one user message. The CLI did not wrap or split the stdin prompt.main, all three engine sessions are harvested as user sessions. That is the problem this PR fixes, reproduced with the real CLI.Deterministic tests. Only the Claude child process (
subprocess.run) is faked. Engine prompts are captured from the shippedClaudeCliBackendand every session goes through the realharvest(..., scope="all"). Sessions are nine seconds long, so the short-session duration fallback cannot be what drops them.The new file
tests/test_harvest_claude_engine_sentinel.pyhas 16 cases. The second and third columns come from a comparison copy of the file that defines the sentinel string locally and omits the two assertions that the captured prompt is stamped, because those trees do not stamp.f6a0b6f)main(45f65ca)mainplus the earlier registry-marker changeThe last three cases exercise the stamp itself, so their result on a tree that does not stamp says nothing about that tree and is not counted as evidence here.
Totals: 16 passed on this branch. Comparison copy: 10 failed, 6 passed on
main, 5 failed, 11 passed onmainwith the earlier change applied.Why This Change Was Made
The harvester now identifies the producer instead of matching prompt text, the same way #287 does for Codex.
ClaudeCliBackendstarts every prompt it hands to the CLI withCLAUDE_REPLAY_SENTINEL([skillopt-sleep:claude-engine:v1]) and a blank line. The stamp is added after the template is rendered, on both the text path and the tool path, so it does not depend on any template wording, default or custom._is_headless_replaydrops a single-turn session whose prompt starts with the sentinel. A session that quotes an engine prompt, or the sentinel itself, later in a message is kept. Multi-turn sessions are kept, as before.CODEX_REPLAY_SENTINEL: versioned, written by the backend, and matched only at the start of the prompt. The Codex and Cursor harvesters are untouched.main.Project Fit
User Impact
Operators who harvest every project stop seeing the engine's own attempt, judge, reflection, miner, and tool sessions come back as mined tasks. People who discuss or paste SkillOpt prompts in their own sessions keep those sessions.
Academic Support
This is a bug fix, so the support is lineage, not literature.
CODEX_REPLAY_SENTINEL, written by the backend, matched at the start of the prompt). This PR applies the same mechanism to the Claude backend; a test asserts the two markers share one shape. The Cursor backend onmainfollows the same pattern.Testing
Head
f6a0b6fa135a9237eaca103a9f2f9b6be5c66a8a, based onmainat45f65ca04f5f6713bca7b9797b495696ba812751. Five hosted runners, each cloning the branch and asserting the exact commit before testing. The same workflow ran on unmodifiedmainthe same day, shown beside it. These are contributor-run checks, not official CI.main, complete suitemain: the 16 cases intests/test_harvest_claude_engine_sentinel.py.main, and none is in a file this PR touches. macOS, both trees: two tests intests/test_sleep_adopt_skill_subset.py(test_cmd_adopt_refuses_tampered_manifest_rootsandtest_tampered_manifest_roots_cannot_escape_caller_destinations). Windows, both trees: those two, four intests/test_benchmark_task_output_paths.py, one intests/test_harvest_project_scope.py, and twelve intests/test_sleep_config_encoding.py(19 test ids, 24 failures counting subtests). They are reported, not fixed, to keep this PR scoped.git diff --checkclean;ruff checkon the touched modules gives 8 findings, the same 8 as onmain, and the new test file has none.Limitations & Negative Results
main, which do not match the current prompts, so older engine sessions are not caught retroactively.--safe-mode, through one provider. Other CLI versions, the--bareroute, macOS and Windows CLIs were not checked live. If a CLI version wraps the prompt, the session would be harvested as it is onmaintoday, not dropped by mistake.--no-session. The OpenCode and Copilot backends are not changed here.--no-session-persistencewas not added toclaude -p, because older CLI versions reject the flag.maintoday (2 and 24 failures). This PR neither causes nor fixes them.Reproduce It Yourself
Linux or macOS, shell in a fresh working directory:
The live check, with your own authenticated
claudeCLI. Run it once in a checkout ofmainand once in this branch. It writes real sessions into a throwaway config directory and prints how many of them the harvester returns: 2 onmain, 0 here. If you authenticate with an API key instead of a login, the backend takes--bareinstead of--safe-mode.The offline equivalent, with no CLI and no key, is
tests/test_harvest_claude_engine_sentinel.py: the same prompts captured from the shipped backend and run through the real harvester.