Skip to content

fix: real timestamps on claude preamble steps; validate asserts schema formats#120

Open
od0 wants to merge 1 commit into
mainfrom
bryan/preamble-timestamps
Open

fix: real timestamps on claude preamble steps; validate asserts schema formats#120
od0 wants to merge 1 commit into
mainfrom
bryan/preamble-timestamps

Conversation

@od0

@od0 od0 commented Jul 7, 2026

Copy link
Copy Markdown

What

Fixes #119.

Claude Code writes several headerless session-log lines with no timestamp field (last-prompt, ai-title, custom-title; a headless claude -p run produces them every time). toolpath-claude stamped the derived claude-preamble-N steps with "timestamp": "", which the schema forbids ($defs/timestamp is format: date-time) — but path p validate accepted anyway, because JSON Schema draft 2020-12 treats format as annotation-only unless the validator opts in. This PR fixes both ends.

  • toolpath-claude (0.12.1): a step derived from a timestamp-less preamble line resolves its timestamp from the nearest real source — the entry the line references (leafUuid, which real last-prompt lines carry, or messageId on file-history-snapshot), else the previous preamble line's resolved time, else the session's first timestamped entry. Only the derived step's identity changes; the source line still rides verbatim in the step's structural change (extra["raw"]), so projecting back to Claude JSONL is unaffected — a test proves the raw lines gain no timestamp on the way out.
  • path-cli (0.15.0): p validate opts into format assertion, so format: date-time on step timestamps and format: uri on meta.kind / path-ref $ref / signature href are enforced instead of ignored. On a pre-fix import this produces exactly the failure from Claude imports emit empty timestamps on preamble steps, and validate doesn't catch them #119 — a oneOf violation at /paths/0. Documents cached by older toolpath-claude releases may now fail validation on their empty preamble timestamps; re-importing the session fixes them.

Tests

  • provider: resolution priority across all four branches (own timestamp > referenced entry > previous preamble line > first entry), plus a projection test proving the timestamp fill doesn't leak into re-emitted JSONL.
  • derive: a fixture session with a last-prompt entry — every derived step parses as RFC 3339, and the last-prompt step carries the leafUuid-referenced entry's time.
  • real fixture: the captured session (3 last-prompt + 4 ai-title lines) derives with all-valid timestamps.
  • schema: an empty step timestamp is rejected, surfacing at /paths/0.
  • integration: end-to-end p import claudep validate on a preamble-bearing fixture — verified red without the provider fix, green with it.

Verification

A fresh headless-session import validates. A real ~300-preamble-line session went from ~300 empty timestamps to zero and validates; a pre-fix import of the same session now fails with the oneOf-at-/paths/0 signature from the issue. All 12 example documents and a live Codex import still validate under format assertion. cargo test --workspace (57 targets), cargo clippy --workspace -- -D warnings, the out-of-workspace toolpath-cli shim build, and the site build (11 pages) are all green.

Scope

  • One logic change in toolpath-claude (conversation_to_view), one in path-cli (schema.rs opts into format assertion), plus tests.
  • toolpath-claude 0.12.0 → 0.12.1 (patch), path-cli 0.14.0 → 0.15.0 (validate behavior change; pre-1.0 minor), toolpath-cli shim tracking 0.15.0 — with the workspace / crates.json / CHANGELOG / CLAUDE.md updates.
  • Known adjacent gap, deliberately not in this PR: toolpath-cursor builds turn timestamps with created_at.unwrap_or_default() and could emit the same class of empty timestamp if a bubble ever lacks createdAt.

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

…a formats

Claude Code writes several headerless session-log lines with no
timestamp field (last-prompt, ai-title, custom-title; a headless
`claude -p` run produces them every time). toolpath-claude stamped the
derived claude-preamble-N steps with `"timestamp": ""`, which the schema
forbids ($defs/timestamp is format: date-time) but `path p validate`
silently accepted, because JSON Schema draft 2020-12 treats format as
annotation-only unless the validator opts in. Fix both ends:

- toolpath-claude (0.12.1): a step derived from a timestamp-less
  preamble line resolves its timestamp from the nearest real source —
  the entry the line references (leafUuid / messageId), else the
  previous preamble line, else the session's first timestamped entry.
  Only the derived step's identity changes; the source line still rides
  verbatim in the step's structural change (extra["raw"]), so projecting
  back to Claude JSONL is unaffected.

- path-cli (0.15.0): `path p validate` opts into format assertion, so
  format: date-time on step timestamps and format: uri on meta.kind /
  path-ref $ref / signature href are enforced instead of ignored.
  Documents imported with an older toolpath-claude may now fail
  validation on their empty preamble timestamps; re-importing the
  session fixes them. (toolpath-cli 0.15.0 tracks the bump.)

Regression tests at every layer: resolution priority + projection
purity (provider), a last-prompt fixture session (derive), the real
captured session (integration), empty-timestamp rejection (schema), and
an end-to-end import→validate round trip that fails without the
provider fix.

Fixes #119
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

🔍 Preview deployed: https://5e25fb26.toolpath.pages.dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claude imports emit empty timestamps on preamble steps, and validate doesn't catch them

1 participant