3.4 KiB
3.4 KiB
M0.6 — Claude transcript adapter
| Field | Value |
|---|---|
| Phase | M0 — Read-only spine |
| Size | S — under 1 day |
| Status | ⬜ Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | M0.5 |
Goal
The second RecordSource, which is the one that proves the trait is real.
Facts (inlined — no spec read needed)
~/.claude/projects/-Users-rockliang-workplace-Poimen/<uuid>.jsonl
└─ cwd, / replaced by -, no wrapping dashes (differs from pi)
15 MB across 7 transcripts on this machine. Record types observed:
attachment 150 queue-operation 138 assistant 134 user 81
file-history-snapshot 69 system 69 ai-title 21 last-prompt 21 mode 21
Fields, read from any line: sessionId, cwd, gitBranch.
Typed records dispatch on type; the ones that matter here:
user/assistant— content undermessage.contentsystemwithsubtype == "api_error"— a failed turn, worth keepingsummary— carriessummary- everything else — ignore
The filename stem is a UUID. The encoding differs from pi (no wrapping --),
which is exactly why the project key must come from the cwd field, as in
M0.5, and not from the directory name.
Steps
- Implement
ClaudeTranscriptSourceinmem-ingest, implementingRecordSource. - Take
cwdfrom the first line carrying it; error if no line does. - Emit records for
user,assistant, andsystem+api_error. - Flatten
message.contentacross its shapes, as in M0.5. Provenance=claude:<uuid>plus line offset.- Reuse the content-flattening and malformed-line handling from M0.5 — extract them into a shared helper rather than copying, since divergence between two flatteners is a bug that only shows up on one source.
Acceptance
- Both sources satisfy
RecordSourcewith no changes tomem-chunk. - Project keys from pi and claude for the same directory resolve to the same
ProjectId. api_errorsystem records survive into the stream.
Verify
Harness: a committed transcript fixture plus a real one, secrets scrubbed.
Integration test — tests/it_claude_source.rs:
a1_project_from_cwd_field— assert the key comes fromcwd, not the directory name, using a fixture where they would differ.a2_same_project_across_sources— a pi session and a claude transcript for the same directory yield an equalProjectId. This is the assertion that makes cross-source memory possible at all.a3_role_mapping— user/assistant/api_error appear;attachment,queue-operation,file-history-snapshotdo not.a4_shared_flattener— the same content-block fixture flattens identically through both sources (call both, compare strings).
Command: cargo test -p mem-ingest claude_source
False pass:
- Testing each source in isolation. The point of this task is that they agree; assertions 2 and 4 are the only ones that check it, and both are cross-source.
Traps
- Copying the flattener instead of sharing it. The two will drift, and the resulting bug looks like "claude transcripts lose tool output" rather than "there are two flatteners".
- Assuming the pi encoding. Claude has no wrapping
--, so a decoder written against pi silently produces a different project key for the same directory — and cross-source memory quietly splits in two.
Background: DESIGN.md — Context