Skip to content

Commit 180e29d

Browse files
fix(sessions): advance Claude cursor for filtered transcripts (#241)
ClaudeSource scans every ~/.claude/projects slug and filters rows by recorded cwd, but parse_new returned None when every row was filtered out, so the cursor never advanced for transcripts belonging to other projects (or for appended non-message rows). Every sweep re-read and re-git-probed the entire foreign corpus, O(total transcript bytes x projects) instead of one pass. Returning the empty transcript lets ingest_one persist the advanced cursor without upserting a session, the same contract cline/kiro already use. Also fixes issues surfaced while sanity-checking today's merged PRs: - docs: scrub /home/zack absolute paths from the Hermes parity audit, elide the local analytics project id, and note that the skill-catalog consolidation recommended by SKILL-ADOPTION-RESEARCH.md was implemented by #225 (its skill names describe the old catalog) - agents: point code-health-auditor at tracedecay:code-health (the code-health-report skill was retired in the restructure) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent b255633 commit 180e29d

5 files changed

Lines changed: 36 additions & 10 deletions

File tree

docs/HERMES-LOOPS-PARITY-AUDIT.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Hermes Self-Improvement Loops — Parity Audit
22

33
Status: comparison audit, July 2026. Hermes agent codebase examined at
4-
`/home/zack/projects/hermes-agent` (registered tracedecay project
4+
`~/projects/hermes-agent` (registered tracedecay project
55
"hermes-agent"). TraceDecay side examined at HEAD of
66
the current TraceDecay worktree after dashboard automation work landed.
77

@@ -320,7 +320,7 @@ either.
320320

321321
## 5. Source map
322322

323-
Hermes (`/home/zack/projects/hermes-agent`):
323+
Hermes (`~/projects/hermes-agent`):
324324

325325
- `agent/background_review.py` — review fork + prompts
326326
- `agent/turn_context.py:209`, `agent/turn_finalizer.py:375` — trigger logic
@@ -330,7 +330,7 @@ Hermes (`/home/zack/projects/hermes-agent`):
330330
- `agent/curator.py` — weekly consolidation curator
331331
- `cron/jobs.py`, `cron/scheduler.py`, `cron/blueprint_catalog.py`, `cron/suggestions.py` — cron platform
332332

333-
TraceDecay (`/home/zack/projects/tracedecay`):
333+
TraceDecay (`~/projects/tracedecay`):
334334

335335
- `src/automation/{scheduler,lifecycle,run_ledger}.rs` — gates, locks, ledger
336336
- `src/automation/{runner,memory_curator,session_reflector,skill_writer}.rs` — the three tasks

docs/SKILL-ADOPTION-RESEARCH.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ TraceDecay plugins (`~/.cursor/plugins/local/tracedecay/`, `~/.codex/…`), the
66
repo sources (`cursor-plugin/`, `codex-plugin/`, `src/hooks/`,
77
`src/mcp/tools/definitions.rs`), and TraceDecay's own analytics
88
(`http://127.0.0.1:7341/api/plugins/analytics/*`,
9-
`~/.tracedecay/projects/proj_b4a8bbe4953823c4/hook_analytics.jsonl`).*
9+
`~/.tracedecay/projects/proj_<local-id>/hook_analytics.jsonl`).*
10+
11+
> **Status (2026-07-03):** the P1 catalog consolidation recommended in §6 --
12+
> including the new `using-tracedecay` skill — was implemented by
13+
> [PR #225](https://github.com/ScriptedAlchemy/tracedecay/pull/225) the day
14+
> after this snapshot. Skill names, directory counts, and the
15+
> `memorize-subject` duplication described below refer to the
16+
> pre-restructure catalog.
1017
1118
## TL;DR
1219

@@ -168,7 +175,7 @@ Two structural notes:
168175
## 3. The measured usage picture
169176

170177
All numbers from the live dashboard (`/api/plugins/analytics/*`) and
171-
`hook_analytics.jsonl` for project `proj_b4a8bbe4953823c4`, sampled 2026-07-02.
178+
`hook_analytics.jsonl` for project `proj_<local-id>`, sampled 2026-07-02.
172179

173180
### 3.1 MCP tool distribution: TraceDecay is used as a file reader
174181

src/agents/claude_agents/code-health-auditor.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ You are a read-only audit subagent. You score and rank code health and return fi
1515
1. Start with `tracedecay_health` (`details: true`) and let the weak dimensions drive the drill-down.
1616
2. Drill only into weak dimensions or explicit asks: complexity/size -> `tracedecay_complexity`, `tracedecay_gini`, `tracedecay_god_class`, `tracedecay_largest`, `tracedecay_hotspots`; structure -> `tracedecay_coupling`, `tracedecay_dependency_depth`, `tracedecay_dsm`, `tracedecay_circular`, `tracedecay_recursion`; quality -> `tracedecay_redundancy`, `tracedecay_doc_coverage`, `tracedecay_unsafe_patterns`, `tracedecay_test_risk`.
1717
3. Keep expensive scans scoped (`path`, `limit`, `max_pairs`) and stop once the ranked findings are actionable.
18-
4. If the `tracedecay:code-health-report` skill is available, follow its full workflow.
18+
4. If the `tracedecay:code-health` skill is available, follow its full workflow.
1919

2020
## Rules
2121

src/sessions/claude.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,12 @@ impl TranscriptSource for ClaudeSource {
126126
messages.push(message);
127127
}
128128
}
129-
if messages.is_empty() {
130-
return None;
131-
}
129+
// No early return when `messages` is empty: this source scans every
130+
// ~/.claude/projects slug and relies on the per-row cwd filter above,
131+
// so transcripts belonging to other projects legitimately parse to
132+
// zero messages. Returning the (empty) transcript lets `ingest_one`
133+
// persist the advanced cursor; returning `None` would pin the cursor
134+
// at 0 and re-read + re-filter the whole file on every sweep.
132135

133136
let project = project_root.to_string_lossy().to_string();
134137
let draft = SessionDraft {

tests/transcript_ingest_suite/claude.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ async fn claude_transcript_for_other_project_is_skipped() {
228228
let other = tmp.path().join("other-project");
229229
std::fs::create_dir_all(&other).unwrap();
230230
// Transcript records a cwd that is NOT the project we ingest for.
231-
write_claude_transcript(&home, &other, "claude-other");
231+
let path = write_claude_transcript(&home, &other, "claude-other");
232232

233233
let db = open_project_session_db(&project).await.unwrap();
234234
let source = ClaudeSource::with_home(&home);
@@ -238,6 +238,22 @@ async fn claude_transcript_for_other_project_is_skipped() {
238238
stats.messages_upserted, 0,
239239
"a transcript whose cwd is a different project must be skipped"
240240
);
241+
242+
// The cursor must still advance past the filtered-out content, or every
243+
// future sweep re-reads and re-filters the whole foreign transcript.
244+
let file_size = std::fs::metadata(&path).unwrap().len();
245+
let path_str = path.to_string_lossy();
246+
let mut offset = db.get_parse_offset(path_str.as_ref()).await;
247+
if offset.is_none() && cfg!(windows) {
248+
// The scanner stores native separators; the helper built this path
249+
// with embedded forward slashes.
250+
offset = db.get_parse_offset(&path_str.replace('/', "\\")).await;
251+
}
252+
let offset = offset.expect("skipped foreign transcript should persist a parse offset");
253+
assert_eq!(
254+
offset.byte_offset, file_size,
255+
"parse cursor should sit at EOF for a fully filtered transcript"
256+
);
241257
}
242258

243259
#[tokio::test]

0 commit comments

Comments
 (0)