harvest: single-turn headless sessions from other local tooling are harvested as user tasks and fill the session cap
Maintainer thường phản hồi trong vòng 2 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 45/100
Hướng nghiên cứu
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.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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].
- Ngôn ngữ chính
- Python
- Star
- 18.2k
- Fork
- 1.7k
- Merge trung bình
- 4 ngày 1 giờ
- Pull request đã merge (30 ngày)
- 10
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Không có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của microsoft/SkillOpt
-
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 66/100
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 25/100
Maintainer thường phản hồi trong vòng 2 ngày
-
Clarification on Rollout Timeouts, Inference Concurrency, and Their Impact on SkillOpt EvaluationĐang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 12/100
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 32/100
Maintainer thường phản hồi trong vòng 2 ngày
-
skillopt-sleep: max_tokens_per_night is defaulted and documented but never enforcedCó thể đã có người làm @Thanatos9404 đã nhận 1 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 55/100
Maintainer thường phản hồi trong vòng 2 ngày
Tất cả issue của microsoft/SkillOpt
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
common-workflow-language/cwl-utils#477 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Add dependabotĐang mởfeat
Độ khó 2/5 Dưới một giờ Mức phù hợp với người mới 72/100
open-nudge/opentemplate#204 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
github-stale-ci-pr-closer: KV state is double-wrapped, never persists, and hits the depth-32 limitCó thể đã có người làm @VascoSch92 đã nhận hôm nay. Đang mởpriority:medium ready-for-dev
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
OpenHands/extensions#781 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày
-
curriculum documentation quality
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
githubnext/gh-aw-workshop#4638 ·
Maintainer thường phản hồi trong vòng 1 ngày