harvest: single-turn headless sessions from other local tooling are harvested as user tasks and fill the session cap
I maintainer di solito rispondono entro 2 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 45/100
Direzione di ricerca
Start with harvest() in skillopt_sleep/harvest.py, where transcripts are sorted newest first and cut off at the session cap (lines 403-433), and with _is_agent_session() and SKILLOPT_SLEEP_AGENT_MARKERS (around lines 186-195). Run the synthetic repro in the issue with and without the env var and check the test suite for harvest. Done means the documentation covers the marker variables, and the cap reports how many candidates were skipped as machine-generated; the default-exclusion question needs a maintainer decision first.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Version examined
microsoft/SkillOptmain@343db229dbd5ddaf9df6b1d5540d8bcdb2604d5c(2026-10-06), packageskillopt_sleep.- Claude Code plugin
skillopt-sleep0.1.0 from the same commit. - Windows 11 Pro (build 26300), Python 3.12.13, plugin runner started from Git Bash 5.3.
- Transcripts written by the Claude Code desktop app (Code tab), version 2.1.271.
Observed behavior
Local tooling outside SkillOpt can run Claude Code headlessly in the same project folder, for example to summarize the previous session every hour. Every such run leaves a single-turn transcript: one machine-written user prompt and one answer.
In one real project folder of the desktop app, 274 of 324 transcripts were of this kind at the first measurement, and 298 of 348 a few hours later: one more arrives every hour. Their only user prompt starts with "Below is a conversation log from a Claude Code coding session. Create a summary …". We counted them with the engine's own digest_transcript(), _is_headless_replay() and _is_agent_session(). We did not identify which tool produces them.
All 348 transcripts in that folder, the headless summary sessions included, carry entrypoint: "claude-desktop" and isSidechain: false; we read only these two fields. In this environment the entrypoint field therefore cannot tell these headless runs apart from interactive sessions.
None of these sessions is filtered out:
_is_headless_replay()(skillopt_sleep/harvest.py, lines 206-236) only catches single-turn sessions that match SkillOpt's own replay markers, or that last under 3 s with a prompt under 200 characters._is_agent_session()only matches the built-in markers (claude-mem and the plugin's own command body, lines 186-195).
Because harvest() sorts all transcripts newest first and stops at the session cap (lines 403-433, --max-sessions), frequent summary sessions crowd real interactive sessions out, and the miner turns the summary prompts into "recurring tasks".
The extension point SKILLOPT_SLEEP_AGENT_MARKERS (line 193) solves it, as verified below. But no .md file in the repository mentions it at this commit: we searched all .md files and found 0 occurrences.
Expected behavior
- Document
SKILLOPT_SLEEP_AGENT_MARKERS, and theSKILLOPT_SLEEP_NEG_FEEDBACK/SKILLOPT_SLEEP_POS_FEEDBACKvariables, in the plugin README and the harvest documentation, with an example for session-summary hooks. - Consider excluding by default, or at least flagging, single-turn sessions with no follow-up user turn whose prompt recurs with an identical prefix across many transcripts: that pattern is machine-generated rather than a user task.
- When the cap is reached, report how many candidate sessions were skipped as machine-generated and how many as real, so the user can see that the cap was filled by noise.
- Do not rely on
entrypointalone, as proposed in #62: in the desktop app the headless sessions above are recorded as"claude-desktop", like interactive ones.
Minimal synthetic reproduction
No model calls and no network. Run from the repository root, or set SKILLOPT_REPO to it. Run it twice: as is, then with SKILLOPT_SLEEP_AGENT_MARKERS="Below is a conversation log".
"""Synthetic repro: single-turn headless summary sessions are harvested as user tasks
and crowd real sessions out of the session cap (no model calls, no network).
Run once as is, then with SKILLOPT_SLEEP_AGENT_MARKERS="Below is a conversation log"."""
import json
import os
import sys
import tempfile
sys.path.insert(0, os.environ.get("SKILLOPT_REPO", "."))
from skillopt_sleep.harvest import harvest # noqa: E402 (markers are read at import time)
from skillopt_sleep.mine import heuristic_mine # noqa: E402
ROOT = tempfile.mkdtemp(prefix="skillopt-d20-")
PROJECT = os.path.join(ROOT, "proj")
TRANSCRIPTS = os.path.join(ROOT, "projects")
os.makedirs(PROJECT)
SUMMARY_PROMPT = ("Below is a conversation log from a coding session. Create a summary to help the next "
"session quickly understand the context.\n## Prioritize\n- decisions\n- open items\n\n"
"LOG:\nuser: please refactor module A\nassistant: done, tests pass\n")
def session(sid, turns, mtime):
folder = os.path.join(TRANSCRIPTS, "proj")
os.makedirs(folder, exist_ok=True)
path = os.path.join(folder, sid + ".jsonl")
with open(path, "w", encoding="utf-8") as f:
for i, (role, text) in enumerate(turns):
f.write(json.dumps({"type": role, "cwd": PROJECT, "sessionId": sid,
"timestamp": f"2026-01-01T10:{i:02d}:00Z",
"message": {"role": role, "content": text}}) + "\n")
os.utime(path, (mtime, mtime))
# two real interactive sessions, older
session("real-1", [("user", "Fix the failing date parser test in module B."), ("assistant", "Fixed."),
("user", "Thanks, now add a regression test."), ("assistant", "Added.")], 1_000_000)
session("real-2", [("user", "Add a --dry-run flag to the export command."), ("assistant", "Added.")], 1_000_100)
# three single-turn summary sessions written by a local hook via a headless CLI, newer
for n in range(3):
session(f"summary-{n}", [("user", SUMMARY_PROMPT), ("assistant", "Summary: ...")], 2_000_000 + n)
print("SKILLOPT_SLEEP_AGENT_MARKERS =", repr(os.environ.get("SKILLOPT_SLEEP_AGENT_MARKERS", "")))
all_digests = harvest(TRANSCRIPTS, scope="invoked", invoked_project=PROJECT)
print("harvested (no cap):", sorted(d.session_id for d in all_digests))
capped = harvest(TRANSCRIPTS, scope="invoked", invoked_project=PROJECT, limit=3)
print("harvested (limit=3):", sorted(d.session_id for d in capped))
for t in heuristic_mine(capped):
print(f"task from {t.source_sessions[0]}: intent starts {t.intent[:45]!r}")
Output at the commit above, run as is:
SKILLOPT_SLEEP_AGENT_MARKERS = ''
harvested (no cap): ['real-1', 'real-2', 'summary-0', 'summary-1', 'summary-2']
harvested (limit=3): ['summary-0', 'summary-1', 'summary-2']
task from summary-2: intent starts 'Below is a conversation log from a coding ses'
task from summary-1: intent starts 'Below is a conversation log from a coding ses'
task from summary-0: intent starts 'Below is a conversation log from a coding ses'
Output with SKILLOPT_SLEEP_AGENT_MARKERS="Below is a conversation log":
SKILLOPT_SLEEP_AGENT_MARKERS = 'Below is a conversation log'
harvested (no cap): ['real-1', 'real-2']
harvested (limit=3): ['real-1', 'real-2']
task from real-2: intent starts 'Add a --dry-run flag to the export command.'
task from real-1: intent starts 'Fix the failing date parser test in module B.'
Related
- #62 (closed) describes the same class of problem, headless
claude -pruns from scripts and hooks mined as user tasks, and proposes filtering onentrypoint == "sdk-cli".- At this commit
harvest.pydoes not readentrypoint. The filter that shipped,_is_headless_replay(), only catches single-turn sessions matching SkillOpt's own replay markers or lasting under 3 s with a short prompt. - In the desktop app the filter proposed in #62 would not catch these sessions either, because they carry
entrypoint: "claude-desktop".
- At this commit
- #99 introduced
_is_agent_session()andSKILLOPT_SLEEP_AGENT_MARKERS. - #286 / #287 (Codex) and #310 (engine replay markers) cover SkillOpt's own replay sessions, not sessions written by other local tooling.
Not verified
- Which local tool writes the summary sessions, and why their transcripts carry
entrypoint: "claude-desktop". We assume the value is inherited from the environment of the desktop session that launched them [INFERENCE]. - The effect with real summaries through the full
mine()path. The repro callsheuristic_mine()directly;mine()also runsdedup_tasks(), which may merge identical synthetic intents. Real summaries differ from each other because they embed different logs [INFERENCE].
- Lingua principale
- Python
- Stelle
- 18.2k
- Fork
- 1.7k
- Merge medio
- 4g 1h
- PR unite (30g)
- 10
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di microsoft/SkillOpt
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 66/100
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
I maintainer di solito rispondono entro 2 giorni
-
Clarification on Rollout Timeouts, Inference Concurrency, and Their Impact on SkillOpt EvaluationAperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 12/100
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 32/100
I maintainer di solito rispondono entro 2 giorni
-
skillopt-sleep: max_tokens_per_night is defaulted and documented but never enforcedForse già presa @Thanatos9404 l’ha presa 1 giorno fa. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 55/100
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di microsoft/SkillOpt
Issue simili
-
Questions crashes on a real SQuAD2 no-answer predictionForse già presa @HuaTNA l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
GST is read as Gulf Standard Time (UTC+4) instead of Guam Standard Time (UTC+10)Forse già presa @yang00333 l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
DOI-USGS/dataretrieval-python#454 ·
I maintainer di solito rispondono entro 4 giorni
-
area:core-platform effort:S priority:P3 status:in-review type:test
Difficoltà 2/5 Meno di un'ora Idoneità per principianti 80/100
randomparity/kdive#3246 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Requantification library converts m/z to mass with the hydrogen-atom mass instead of the proton massApertabug
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
nf-core/metaboigniter#130 ·
I maintainer di solito rispondono entro 1 giorno
-
netbox status: needs owner type: documentation
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 75/100
netbox-community/netbox#23412 · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno