Symptom
ox session regenerate <name> (default mode) regenerates markdown artifacts from a session's raw.jsonl. session.md gets regenerated unconditionally, but summary.md is only written if summary.json already exists in the session directory. If summary.json is missing, summary.md is silently skipped — no warning, no fallback template.
Evidence
Reproduced during #519 investigation. Recreated a session in cache with only raw.jsonl, ran ox session regenerate <name>. Output claimed success:
✓ Regenerated artifacts for 2026-04-19T05-59-galexy-OxBkup
But on disk:
-rw-r--r-- raw.jsonl 250496 bytes
-rw-r--r-- session.md 100428 bytes ← regenerated
← no summary.md
No warning that summary.md wasn't written. Only session.md was produced.
Root cause
cmd/ox/session_regenerate.go:691-706:
// summary.md — regenerate from summary.json if available
summaryJSONPath := filepath.Join(sessionPath, "summary.json")
if data, err := os.ReadFile(summaryJSONPath); err == nil {
var summaryResp session.SummarizeResponse
if json.Unmarshal(data, &summaryResp) == nil {
summaryView := session.SummarizeResponseToSummaryView(&summaryResp)
summaryMdGen := session.NewSummaryMarkdownGenerator()
summaryMdBytes, err := summaryMdGen.Generate(rawSession.Meta, summaryView, rawSession.Entries)
if err == nil {
summaryMdPath := filepath.Join(sessionPath, ledgerFileSummaryMD)
if writeErr := os.WriteFile(summaryMdPath, summaryMdBytes, 0644); writeErr != nil {
errs = append(errs, ...)
}
}
}
}
Three silent-skip points:
summary.json doesn't exist → outer if skips. No fallback.
summary.json fails to unmarshal → inner if skips. Error not collected.
summaryMdGen.Generate fails → skip. Error not collected.
Only write errors get added to errs and surfaced to the user.
Impact
- Users rebuilding a session from
raw.jsonl (e.g., recovering from data loss, importing prior history) get session.md but no summary.md. They don't know this unless they check.
- To get
summary.md, users must either have an existing summary.json or run ox session regenerate --summary — which invokes claude as a subprocess (requires the CLI, uses LLM tokens, may take minutes).
- There's no template-based fallback for
summary.md even though session.md has a perfectly good template-based generation path (NewMarkdownGenerator).
- Contrast with
ox agent session import (cmd/ox/agent_session_plan_history.go:148-157) which DOES generate a template-based summary.md without an LLM:
summaryMDGen := session.NewSummaryMarkdownGenerator()
summaryBytes, summaryErr := summaryMDGen.Generate(stored.Meta, nil, stored.Entries)
// ^^^ nil SummaryView works
The generator accepts a nil SummaryView and produces a structural summary.md from meta + entries alone. regenerate gates on a non-existent summary.json for no real reason.
Fix direction
In regenerateArtifacts, always generate summary.md:
- If
summary.json exists and parses → generate with the rich summary view.
- Otherwise → generate with
nil SummaryView (structural fallback). Same behavior as session import.
Also surface the three silent-skip points as warnings at minimum — users should know when a file they expect didn't get written.
Acceptance
- Running
ox session regenerate <name> on a session with no summary.json produces a non-empty summary.md.
- The output of regenerate either reports which artifacts were written or warns which were skipped — no silent partials.
- Test fixture covers: session dir with only
raw.jsonl → both session.md and summary.md produced.
Related
Symptom
ox session regenerate <name>(default mode) regenerates markdown artifacts from a session'sraw.jsonl.session.mdgets regenerated unconditionally, butsummary.mdis only written ifsummary.jsonalready exists in the session directory. Ifsummary.jsonis missing,summary.mdis silently skipped — no warning, no fallback template.Evidence
Reproduced during #519 investigation. Recreated a session in cache with only
raw.jsonl, ranox session regenerate <name>. Output claimed success:But on disk:
No warning that
summary.mdwasn't written. Onlysession.mdwas produced.Root cause
cmd/ox/session_regenerate.go:691-706:Three silent-skip points:
summary.jsondoesn't exist → outerifskips. No fallback.summary.jsonfails to unmarshal → innerifskips. Error not collected.summaryMdGen.Generatefails → skip. Error not collected.Only write errors get added to
errsand surfaced to the user.Impact
raw.jsonl(e.g., recovering from data loss, importing prior history) getsession.mdbut nosummary.md. They don't know this unless they check.summary.md, users must either have an existingsummary.jsonor runox session regenerate --summary— which invokesclaudeas a subprocess (requires the CLI, uses LLM tokens, may take minutes).summary.mdeven thoughsession.mdhas a perfectly good template-based generation path (NewMarkdownGenerator).ox agent session import(cmd/ox/agent_session_plan_history.go:148-157) which DOES generate a template-basedsummary.mdwithout an LLM:The generator accepts a nil
SummaryViewand produces a structuralsummary.mdfrom meta + entries alone.regenerategates on a non-existentsummary.jsonfor no real reason.Fix direction
In
regenerateArtifacts, always generatesummary.md:summary.jsonexists and parses → generate with the rich summary view.nilSummaryView (structural fallback). Same behavior assession import.Also surface the three silent-skip points as warnings at minimum — users should know when a file they expect didn't get written.
Acceptance
ox session regenerate <name>on a session with nosummary.jsonproduces a non-emptysummary.md.raw.jsonl→ bothsession.mdandsummary.mdproduced.Related