Repository navigation
fix(sleep): harvest pasted briefs instead of dropping them as host noise - #321
Ansu (SU-AN-coder) wants to merge 2 commits into
Conversation
A user message that arrives wrapped in angle brackets was classified as system noise, so a brief pasted into the Claude Code desktop app (`<pasted_content id="…">…</pasted_content id="…">`) never became a task: the session was mined from a short follow-up instead, or dropped entirely when the brief was its only user turn. - unwrap `<pasted_content …>` and keep the inner text as the user's - drop host-injected `<system-reminder>` blocks from the intent rather than copying them into it - keep the "message is nothing but one host tag block" filter, but only for a closed list of host tags (command / local-command / system-reminder / bash / IDE / Copilot `environment_info`), so a wrapper this list does not know about loses its tags rather than the session's request - treat a session whose user turns were all filtered as engine-generated only when it has no assistant work Fixes microsoft#318
|
@microsoft-github-policy-service agree |
|
Reviewed
Please retain the working pasted-brief fix while limiting host stripping so quoted user content survives, and keep known no-intent records from consuming the task cap. The passing full suite does not cover these failures. #318 stays open pending these narrow boundary corrections and retesting. |
…ds out
Two boundary corrections from review.
* Host stripping no longer rewrites text the user wrote. `_strip_host_wrappers`
removed every `<system-reminder>…</system-reminder>` block in the message, so
a prompt that quoted one — inline code, a fenced XML example — lost it, and
both the harvested request and its mined intent changed. Removal is now
anchored: the block is dropped only while it leads the message, which is the
form the host prepends. A quoted sample anywhere after the user's own text
survives verbatim. The tag match now also tolerates attributes.
* A transcript with no user turns is a replay again, whatever assistant work it
holds. The zero-user/assistant-present exception admitted host-only isMeta
records before the session cap; such a record mines nothing, so under
`--max-sessions` it displaced a real session and left the night with no
tasks. Reverted to the base behaviour. The pasted-brief fix does not need the
exception: unwrapping now yields a user turn, which is what the new tests
assert.
Measured with the new tests against the pre-review head `d97592c`: 3 failed,
136 passed, 1 skipped —
test_inline_code_system_reminder_example_is_user_text
['How ... escape `` inside my transcript fixture?'] != [the prompt]
test_fenced_system_reminder_example_is_user_text
the reminder block was deleted from inside the fence
test_host_only_transcript_does_not_take_a_session_slot
['host-note'] != ['real-task'] (limit=1, host-only record newer)
After: 139 passed, 1 skipped, 12 subtests. Full suite: 1,809 passed, 11
skipped, 359 subtests.
|
Both boundaries were regressions this PR introduced, not gaps on Host stripping is anchored to the leading block. Two tests pin it, and their three-commit history is the proof of direction:
A no-intent record no longer consumes the cap. The zero-user/assistant-present exception is reverted: The #318 case does not need that exception any more, which is why reverting it costs nothing: unwrapping a pasted brief now yields a real user turn, so the pasted-only session is admitted on its own merit ( Verification on
The PR body now carries the same three-commit table so the direction is visible without reading this thread. I have not rebased onto |
Summary
_is_meta_prompt()treated any message that starts with<and ends with>as system noise, so a brief pasted into the Claude Code desktop app — delivered as<pasted_content id="…">…</pasted_content id="…">— was dropped. The session was then mined from a short follow-up (heuristic_mine()takes the first surviving prompt), and when the brief was its only user turn the session disappeared entirely, because_is_headless_replay()reads "zero harvested user turns" as "engine replay". A user message that starts with a<system-reminder>block followed by the real request was kept, with the reminder copied verbatim into the intent.Four changes, all in
skillopt_sleep/harvest.py:_strip_host_wrappers()unwraps<pasted_content …>/</pasted_content …>and keeps the inner text as the user's;digest_transcript()runs it before the meta test, so the intent is what the user pasted rather than the follow-up.<system-reminder>block is dropped only while it leads the message, which is the form the host prepends; the tag match also tolerates attributes. If the user quotes one — inline code, a fenced XML sample — it stays, because that is text they wrote.command-name,command-message,command-args,local-command-stdout,local-command-caveat,system-reminder,bash-input,bash-stdout,bash-stderr,ide_opened_file,ide_selection, and Copilot Chat'senvironment_info. A wrapper the list does not know about now keeps its content instead of the session's request.--max-sessionsand displace a real session.The closed list was picked, not guessed
The repo's own tests and fixtures pin which tags must stay out.
tests/fixtures/copilot/session-v3.jsonlstores<environment_info>private machine context</environment_info>as a request's message text, andtest_digest_extracts_visible_content_and_enforces_privacy_boundaryrequires it to stay out of the digest.harvest_copilot._sanitize_text()calls the same_is_meta_prompt(), so narrowing the rule without listing that tag fails that test (AssertionError: 2 != 1onn_user_turns, andharvest_copilot()returns 0 sessions instead of 1) — I hit exactly that while building this.<system-reminder>,<command-name>and<command-message>are pinned bytests/test_sleep_engine.py;bash-*andide_*are the remaining whole-message host tags of the same shape.Review corrections
Both boundary failures the review named were introduced by this PR, not present at base, and both are now fixed:
<system-reminder>…</system-reminder>occurrence in the message, so a prompt that quoted one in inline code or a fenced XML block lost it and the harvested request and mined intent changed. Removal is now anchored to the leading position;test_inline_code_system_reminder_example_is_user_textandtest_fenced_system_reminder_example_is_user_textpin it. At baseae2f766both pass (main preserves the text); at the reviewed headd97592cboth fail; on this head both pass.isMetatranscript before the cap; under--max-sessions 1it displaced the real session and the night mined nothing. Reverted to the base behaviour, and the pasted-brief case does not need the exception: unwrapping now yields a user turn.test_host_only_transcript_does_not_take_a_session_slotbuilds exactly that pair (['host-note'] != ['real-task']on the reviewed head).Validation
Focused on the flow
_is_meta_prompt()→strip → digest_transcript()→harvest()→heuristic_mine().8 new tests in
tests/test_sleep_engine.py::TestHarvest: the brief is kept and unwrapped; only known host tags are filtered (an unknown wrapper keeps its text); the pasted brief becomes the mined intent with the follow-up as context; a pasted-brief-only session survivesharvest(); a leading reminder does not ride into the intent; a quoted reminder in inline code / a fenced block survives; a host-only transcript does not take a session slot.Three commits, three regimes, same test file:
The three failures on
d97592care the review's two points:The issue's own reproduction script, unchanged, base vs. branch (no model calls, no network):
s2-pasted-only(the pasted brief as its only user turn) is now harvested, and both pasted sessions are mined from the brief rather than from the follow-up. The reminder-prefix session's intent is still the request, unchanged by the narrowed stripping..github/workflows/ci.yml:1801 passed, 11 skipped, 359 subtests passedat baseae2f766;1809 passed, 11 skipped, 359 subtests passedon this head (the eight new tests).ruff check:tests/test_sleep_engine.py— "All checks passed!";skillopt_sleep/harvest.pyreports only the pre-existingI001that is also present at base (a blank line after the import block, untouched here).python -m mkdocs build --strictpassed; no documentation is touched.Not in this PR
entrypointquestion in Mining counts headlessclaude -pcalls and sub-agent fan-out as user tasks, inflating recurring-task signal #62 are separate.<cross-session-message>wrapper intests/test_sleep_engine.pycarriesisMeta: true, so it never reaches this filter; I left it out of the closed list rather than list a tag no path exercises.bash-*double as user input in shell mode and were dropped as noise before this change too; I kept them filtered to avoid changing behaviour outside the reported shape.I read every line of the diff myself. The branch is
SU-AN-coder:fix/harvest-pasted-content-318atf5f1f9a, and the owner has it.Fixes #318