| name | session-chronicle |
| description | Excavate session logs for provenance tracking. TRIGGERS - who created, document finding, trace origin, session archaeology, provenance, ADR reference. |
| allowed-tools | Read, Grep, Glob, Bash |
Session Chronicle
Excavate Claude Code session logs to capture complete provenance for research findings, ADR decisions, and code contributions. Traces UUID chains across multiple auto-compacted sessions.
S3 Artifact Sharing: Artifacts can be uploaded to S3 for team access. See S3 Sharing ADR.
When to Use This Skill
- User asks "who created this?" or "where did this come from?"
- User says "document this finding" with full session context
- ADR or research finding needs provenance tracking
- Git commit needs session UUID references
- Tracing edits across auto-compacted sessions
Preflight Check
Step 1: Verify Session Storage Location
/usr/bin/env bash << 'PREFLIGHT_EOF'
# Check Claude session storage
PROJECT_DIR="$HOME/.claude/projects"
if [[ ! -d "$PROJECT_DIR" ]]; then
echo "ERROR: Session storage not found at $PROJECT_DIR"
exit 1
fi
# Count project folders
PROJECT_COUNT=$(ls -1d "$PROJECT_DIR"/*/ 2>/dev/null | wc -l)
echo "Found $PROJECT_COUNT project folders in $PROJECT_DIR"
echo "Ready for session archaeology"
PREFLIGHT_EOF
Step 2: Find Current Project Sessions
/usr/bin/env bash << 'FIND_SESSIONS_EOF'
# Encode current working directory path
CWD=$(pwd)
ENCODED_PATH=$(echo "$CWD" | tr '/' '-')
PROJECT_SESSIONS="$HOME/.claude/projects/$ENCODED_PATH"
if [[ -d "$PROJECT_SESSIONS" ]]; then
SESSION_COUNT=$(ls -1 "$PROJECT_SESSIONS"/*.jsonl 2>/dev/null | wc -l)
echo "Found $SESSION_COUNT session files for current project"
echo "Location: $PROJECT_SESSIONS"
# Show largest sessions (likely most relevant)
echo -e "\nLargest sessions (by line count):"
wc -l "$PROJECT_SESSIONS"/*.jsonl 2>/dev/null | sort -rn | head -5
else
echo "No sessions found for current project at: $PROJECT_SESSIONS"
echo "Checking all project folders..."
ls -la "$HOME/.claude/projects/" | head -10
fi
FIND_SESSIONS_EOF
Step 3: Verify Required Tools
/usr/bin/env bash << 'TOOLS_EOF'
# Check for jq (required for JSONL parsing)
command -v jq &>/dev/null || { echo "WARNING: jq not installed (brew install jq)"; }
# Check for brotli (required for compression)
command -v brotli &>/dev/null || { echo "ERROR: brotli not found (brew install brotli)"; exit 1; }
# Check for aws and op (required for S3 upload)
command -v aws &>/dev/null || { echo "WARNING: aws CLI not installed (brew install awscli)"; }
command -v op &>/dev/null || { echo "WARNING: 1Password CLI not installed (brew install 1password-cli)"; }
echo "Required tools available"
TOOLS_EOF
Part 1: AskUserQuestion Flows
Flow A: Identify Target for Provenance
When the skill is triggered, first identify what the user wants to trace:
AskUserQuestion:
question: "What do you want to trace provenance for?"
header: "Target"
multiSelect: false
options:
- label: "Specific code/feature"
description: "Trace who created a specific function, feature, or code block"
- label: "Research finding"
description: "Document a finding with full session context for reproducibility"
- label: "Configuration/decision"
description: "Trace when and why a configuration or architectural decision was made"
- label: "Custom search"
description: "Search session logs for specific keywords or patterns"
Flow B: Specify Search Parameters
Based on target type, gather search parameters:
AskUserQuestion:
question: "What keywords or identifiers should we search for?"
header: "Search"
multiSelect: true
options:
- label: "File path pattern"
description: "Search for edits to specific files (e.g., **/minimal_3*)"
- label: "Function/variable name"
description: "Search for creation of specific identifiers"
- label: "Session UUID"
description: "Start from a known session UUID"
- label: "Date range"
description: "Limit search to sessions from specific dates"
Flow C: Confirm Discovered Sessions
After scanning, confirm with user:
AskUserQuestion:
question: "Found N related sessions. Which should we include?"
header: "Sessions"
multiSelect: true
options:
- label: "Full session chain (Recommended)"
description: "Archive ALL sessions in UUID chain - complete provenance across auto-compacts"
- label: "Add earlier sessions"
description: "Include sessions BEFORE the chain starts (for deeper context)"
- label: "Manual selection"
description: "I'll specify which sessions to include"
- label: "Provide more context"
description: "The search didn't find what I need, let me clarify"
IMPORTANT: Always default to archiving the FULL session chain. Auto-compacting creates arbitrary boundaries - a finding's true origin often spans multiple sessions.
Flow D: Choose Output Format
AskUserQuestion:
question: "What outputs should be generated?"
header: "Outputs"
multiSelect: true
options:
- label: "Full session chain archive (.jsonl.br per session)"
description: "Compress EACH session with Brotli (best compression)"
- label: "provenance.jsonl (append-only log)"
description: "NDJSON record with UUIDs, timestamps, contributors"
- label: "Markdown finding document"
description: "findings/<name>.md with embedded provenance table"
- label: "Git commit with provenance"
description: "Structured commit message with session references"
- label: "Upload to S3 for team sharing"
description: "Upload artifacts to S3 with retrieval command in commit"
Flow E: Link to Existing ADR (for S3 upload)
When S3 upload is selected:
AskUserQuestion:
question: "Link this finding to an existing ADR?"
header: "ADR Link"
multiSelect: false
options:
- label: "No ADR link"
description: "This finding is standalone"
- label: "Specify ADR slug"
description: "Link to an existing ADR (e.g., 2025-12-15-feature-name)"
Part 2: Session Archaeology Process
Step 1: Full Project Scan
Scan all session files in the project folder to build a session index:
/usr/bin/env bash << 'SCAN_EOF'
PROJECT_SESSIONS="$HOME/.claude/projects/$ENCODED_PATH"
# Build session index
echo "Scanning sessions..."
for session in "$PROJECT_SESSIONS"/*.jsonl; do
SESSION_ID=$(basename "$session" .jsonl)
LINE_COUNT=$(wc -l < "$session")
FIRST_TS=$(head -1 "$session" | jq -r '.timestamp // empty')
LAST_TS=$(tail -1 "$session" | jq -r '.timestamp // empty')
echo "$SESSION_ID|$LINE_COUNT|$FIRST_TS|$LAST_TS"
done > /tmp/session_index.txt
echo "Indexed $(wc -l < /tmp/session_index.txt) sessions"
SCAN_EOF
Step 2: Search for Target
Search across all sessions for the target:
/usr/bin/env bash << 'SEARCH_EOF'
# Search for keyword in all sessions
grep -l "SEARCH_TERM" "$PROJECT_SESSIONS"/*.jsonl
# Extract entries with tool_use containing the target
jq -c 'select(.message.content[]?.type == "tool_use") |
select(.message.content[].input | tostring | contains("SEARCH_TERM"))' \
"$PROJECT_SESSIONS"/*.jsonl
SEARCH_EOF
Step 3: Trace UUID Chain
Trace parentUuid chain backwards to find origin:
/usr/bin/env bash << 'TRACE_EOF'
trace_uuid_chain() {
local uuid="$1"
local session_file="$2"
local depth=0
local max_depth=100
echo "Tracing UUID chain from: $uuid"
while [[ -n "$uuid" && $depth -lt $max_depth ]]; do
# Find entry with this UUID
entry=$(jq -c "select(.uuid == \"$uuid\")" "$session_file" 2>/dev/null)
if [[ -n "$entry" ]]; then
parent=$(echo "$entry" | jq -r '.parentUuid // empty')
timestamp=$(echo "$entry" | jq -r '.timestamp // empty')
type=$(echo "$entry" | jq -r '.type // empty')
echo " [$depth] $uuid ($type) @ $timestamp"
echo " -> parent: $parent"
uuid="$parent"
((depth++))
else
# UUID not in this session - search other sessions
echo " UUID $uuid not in current session, searching others..."
found=false
for session in "$PROJECT_SESSIONS"/*.jsonl; do
if grep -q "\"uuid\":\"$uuid\"" "$session" 2>/dev/null; then
session_file="$session"
echo " Found in $(basename "$session")"
found=true
break
fi
done
[[ "$found" == "false" ]] && break
fi
done
echo "Chain depth: $depth"
}
TRACE_EOF
Step 4: Extract Edit Context
Extract the exact tool_use that created/modified the target:
/usr/bin/env bash << 'EXTRACT_EOF'
# Extract tool_use block with context (5 entries before and after)
jq -c 'select(.message.content[]?.type == "tool_use") |
select(.message.content[].name == "Edit" or .message.content[].name == "Write")' \
"$SESSION_FILE" | head -20
EXTRACT_EOF
Part 3: Output Generation
NDJSON Provenance Record Schema
Each provenance record in provenance.jsonl:
{
"id": "uuid-v4",
"type": "finding|decision|contribution",
"target": {
"file": "path/to/file.py",
"identifier": "minimal_3",
"description": "3-feature baseline model"
},
"origin": {
"session_id": "7380c12f-9c92-426f-9fe8-2c08705c81aa",
"edit_uuid": "055fa4fe-b85d-4ff1-b337-f15b99d98eac",
"parent_uuid": "da0ffa7d-7374-414f-8197-7b8bb3d10e52",
"timestamp": "2026-01-01T01:48:27.634Z",
"model": "claude-opus-4-5-20251101",
"contributor": "claude"
},
"chain": {
"sessions_traced": 3,
"total_entries": 22456,
"chain_depth": 15
},
"artifacts": {
"session_context": "findings/provenance/session_context_<id>.jsonl.gz",
"finding_doc": "findings/<name>.md"
},
"created_at": "2026-01-01T12:00:00Z"
}
Compressed Session Context
CRITICAL: Extract the FULL chain across ALL related sessions, not just a fixed window.
The provenance chain typically spans multiple auto-compacted sessions. Each session file represents a context window - the finding may have origins several sessions back.
/usr/bin/env bash << 'COMPRESS_EOF'
# Extract FULL session chain - not limited to fixed entries
# Each session in the chain gets its own compressed file
OUTPUT_DIR="findings/provenance/session_chain_${TARGET_ID}"
mkdir -p "$OUTPUT_DIR"
# 1. Get the UUID chain (traced by uuid_tracer.sh)
CHAIN_FILE="$OUTPUT_DIR/uuid_chain.jsonl"
# 2. For EACH session in the chain, extract and compress the FULL session
for session_id in $(cat "$CHAIN_FILE" | jq -r '.session_id' | sort -u); do
SESSION_PATH="$PROJECT_SESSIONS/${session_id}.jsonl"
if [[ -f "$SESSION_PATH" ]]; then
# Compress entire session (not just a window)
gzip -c "$SESSION_PATH" > "$OUTPUT_DIR/${session_id}.jsonl.gz"
echo "Archived: ${session_id} ($(wc -l < "$SESSION_PATH") entries)"
fi
done
# 3. Create manifest of all archived sessions
cat "$CHAIN_FILE" | jq -s '{
target_id: $TARGET_ID,
sessions_archived: (map(.session_id) | unique),
total_sessions: (map(.session_id) | unique | length),
chain_depth: length,
created_at: now | todate
}' > "$OUTPUT_DIR/manifest.json"
echo "Full session chain archived to: $OUTPUT_DIR"
COMPRESS_EOF
Why full sessions?
- Auto-compacting creates session boundaries at arbitrary points
- A single finding's context may span 3-5+ sessions
- Partial extraction loses critical context about HOW the finding evolved
- Full sessions enable complete reproduction and audit
Markdown Finding Document Template
# Finding: <title>
**Date**: YYYY-MM-DD
**Status**: VALIDATED
**ADR**: [link if applicable]
---
## Summary
<1-2 paragraph description of the finding>
---
## Provenance
| Field | Value |
| ----------- | --------------- |
| Session ID | `<session_id>` |
| Timestamp | `<timestamp>` |
| Model | `<model>` |
| Edit UUID | `<edit_uuid>` |
| Parent UUID | `<parent_uuid>` |
### Session Chain
| Session | Lines | Date Range |
| ----------- | ------- | --------------- |
| <session_1> | <lines> | <start> - <end> |
| <session_2> | <lines> | <start> - <end> |
### Context
<Original Claude message before the edit>
---
## Artifacts
| File | Description |
| ------------------------------------------ | -------------------------- |
| `provenance/session_context_<id>.jsonl.gz` | Compressed session excerpt |
| `provenance/<name>_edit_context.json` | Exact tool_use block |
---
## Reproduction
```bash
# Commands to reproduce or verify the finding
```
Generated by session-chronicle skill
### Git Commit Message Template
feat(finding):
Session-Chronicle Provenance:
session_id:
Artifacts:
- findings/
.md - findings/provenance/sessioncontext
.jsonl.gz - findings/provenance/
_edit_context.json
Co-authored-by: Claude noreply@anthropic.com
---
## Part 4: Iterative Fallback
If initial search fails or is insufficient:
### Fallback A: Expand Search Scope
AskUserQuestion: question: "Search didn't find clear matches. How should we proceed?" header: "Fallback" multiSelect: false options: - label: "Expand to all sessions" description: "Search entire project history (may be slow)" - label: "Different search terms" description: "Let me provide alternative keywords" - label: "Specific session file" description: "I know which session file contains it" - label: "Date range narrowing" description: "I can narrow down when this was created"
### Fallback B: Manual Context
AskUserQuestion: question: "Please provide additional context for the search" header: "Context" multiSelect: true options: - label: "Approximate date" description: "When was this approximately created?" - label: "Related files" description: "What other files were involved?" - label: "Conversation topic" description: "What were we discussing when this was created?" - label: "Session UUID if known" description: "I have a specific session UUID to start from"
---
## Part 5: Workflow Summary
PREFLIGHT ├── Verify session storage location ├── Find current project sessions └── Check required tools (jq, brotli, aws, op)
IDENTIFY TARGET └── AskUserQuestion: What to trace?
FULL PROJECT SCAN ├── Index all session files ├── Search for target across sessions └── Build session timeline
TRACE UUID CHAIN ├── Find target entry ├── Trace parentUuid backwards └── Cross-reference multiple sessions if needed
CONFIRM WITH USER ├── AskUserQuestion: Which sessions to include? └── AskUserQuestion: Link to existing ADR?
GENERATE OUTPUTS ├── Append to provenance.jsonl ├── Create Brotli-compressed session context (.jsonl.br) ├── Generate markdown finding document └── Prepare git commit message
S3 UPLOAD (optional) ├── Upload artifacts to S3 via scripts/s3_upload.sh ├── Uses 1Password for credential injection └── Updates manifest with S3 location
GIT COMMIT ├── Embed S3 URIs in commit message ├── Include retrieval command for coworkers └── Add Session-Chronicle-S3: trailer
FALLBACK (if needed) └── AskUserQuestion: Provide more context
### S3 Upload Scripts
| Script | Purpose |
| ------ | ------- |
| `scripts/s3_upload.sh` | Upload artifacts to S3 with 1Password credentials |
| `scripts/retrieve_artifact.sh` | Download and decompress artifacts for coworkers |
| `scripts/generate_commit_message.sh` | Generate commit message with S3 links |
---
## References
- [S3 Sharing ADR](/docs/adr/2026-01-02-session-chronicle-s3-sharing.md)
- [S3 Retrieval Guide](./references/s3-retrieval-guide.md)
- [Session Storage](/plugins/devops-tools/skills/session-recovery/SKILL.md)
- [NDJSON Specification](https://github.com/ndjson/ndjson-spec)
- [jq Manual](https://jqlang.github.io/jq/manual/)
- [Brotli Compression](https://github.com/google/brotli)
---
## Success Criteria
1. **Complete chain traced** - All UUID links followed to origin
2. **Cross-session tracking** - Auto-compacted sessions handled
3. **Machine-readable output** - NDJSON provenance record created
4. **Human-readable output** - Markdown finding document generated
5. **Git-ready** - Commit message with full provenance prepared
6. **Reproducible** - Compressed context allows verification
7. **Team-accessible** - Artifacts uploaded to S3 with retrieval command