Add export v1 contracts and session summary export - #991
Conversation
Programmatic consumers need stable JSON contracts for report and session-summary exports, including reproducible pricing metadata and project identity that can survive cross-machine aggregation. This commit publishes the final branch state without the private local-path examples that existed only in intermediate local commits. The export surfaces stay additive while gaining shared schema metadata, pricing provenance, persisted project identity, and a content-free daemonless session summary export for headless analytics.
V1 report metadata should distinguish an empty projects map from legacy absence. Usage summary and activity responses now keep the projects field present, with regression coverage at the DB, activity, service, and HTTP response layers.
The v1 activity report golden only needs enough rows to pin the contract surface. The prior full-day default generated hundreds of empty five-minute buckets, making review noisy without adding distinct schema coverage. Use a narrow custom window around the seeded sessions so the fixture still carries pricing, project identity, partial-range metadata, nonzero buckets, summaries, sessions, and intervals while staying small enough to read.
roborev: Combined Review (
|
V1 export metadata is now consumed as join and provenance data, so it must not leak credential-bearing remotes or mutate archives from daemonless read commands. Persist sanitized project remotes and scrub existing observations while preserving remote-derived project keys.\n\nSession-summary export now requires an existing archive database ID and uses the read-only open path, avoiding an uncoordinated SQLite writer beside the daemon.\n\nPostgreSQL and DuckDB now treat explicit pricing rows as the effective pricing table, matching SQLite provenance digests while retaining fallback pricing for empty fresh mirrors. The docs also call out that the new JSON contracts aim for compatibility but may still settle.
roborev: Combined Review (
|
Session-summary export is intentionally read-only, so fresh archives need their database identity created by the normal writable startup path rather than by the export command. This keeps daemonless export from opening a second writer while letting archives initialized by serve or sync export immediately.\n\nDuckDB mirrors also need a repair for rows written before remote sanitization. The mirror schema migration now rewrites credential-bearing project identity remotes to their sanitized storage form and records a repair marker so compatibility checks can detect mirrors that still need the scrub.
roborev: Combined Review (
|
The v1 export surfaces are now join/provenance contracts, so edge cases that previously looked harmless can silently change consumer semantics. This tightens cursor snapshots, project identity normalization, backend mirror parity, pricing provenance, and JSON compatibility so empty, stale, and cross-backend cases remain distinguishable. The sync path also caches project identity discovery and makes identity write failures non-fatal in batch processing, avoiding per-append filesystem work while preserving session ingestion.
Stored project identity keys need to be reproducible across live and persisted observations. Windows drive-letter paths also need the same symlink resolution as live paths, and DuckDB mirror fallback must treat slash-form stored file paths consistently on Windows.
roborev: Combined Review (
|
Live and persisted project identity observations must derive the same root_path-backed key. Windows drive-letter paths now use the same symlink resolution path in both normalizers, so a stored observation cannot diverge from the live observation that created it.
roborev: Combined Review (
|
CLI resume commands cannot repeat query filters because the cursor is meant to carry the query contract. A cursor minted from a filtered first page therefore needs to hydrate the resumed export from the embedded filter instead of comparing it against the default empty CLI filter.\n\nDirect database callers still get the existing conflict check unless they explicitly opt into cursor-owned filtering, so programmatic misuse remains visible while the CLI path can paginate filtered exports.
roborev: Combined Review (
|
Cursor billing rows intentionally do not have a session project label, but the usage export metadata treated every aggregate row project as a join key. That leaked a synthetic empty-string project identity into v1 payloads whenever a report contained only Cursor usage. The totals and breakdown aggregation still preserve those Cursor rows; only the report-level project identity map now skips non-label projects so legacy absence and real project identities stay distinguishable across SQLite, PostgreSQL, and DuckDB. VALID (fixed): #1 -- Cursor-only usage no longer emits a "" project identity in usage report metadata.
roborev: Combined Review (
|
Message token_usage can carry reasoning_tokens, and pricing now charges those tokens at the output rate. Treating message rows as zero made daily usage, session usage, activity report cost, and session-summary export counters undercount message-only reasoning even though usage-event rows were already handled.\n\nKeep SQLite and PostgreSQL cost paths on their tolerant token JSON parsers while projecting message reasoning in the row templates, and add parity coverage across SQLite, PostgreSQL, and DuckDB.
Message token_usage remains raw provider JSON, so PostgreSQL usage SQL must not cast fields before the tolerant Go parser can clamp them. Leave message reasoning projected as zero in SQL and rely on the existing parsed message path for cost calculations, matching the SQLite behavior and avoiding query failures for out-of-range provider values.\n\nVALID (fixed): #27379 -- PostgreSQL message reasoning tokens no longer cast raw token_usage in SQL; pgtest now covers an out-of-range reasoning value.
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
…vice layer) Waypoint C of the staged upstream catch-up (specs/merge-upstream-waypoints.md). Merges u/main up to 725a3d0 (126 commits: export v1 foundation, the export.PricingResolver cost model, a service layer for usage summaries, the kit-ui component library + TopBar/theme, i18n, pairwise usage comparison, new Activity/RecentEdits nav) into our branch. Pricing (thread the fork's 1h cache-write rate through export.PricingResolver without touching the frozen export v1 wire contract): - Add an internal, non-serialized CacheWrite1hPerMTok to export.ModelRates (no canonical_json key, no EffectiveModelRate field -> digest/wire stay byte-identical), populated from db.ModelPricing.CacheCreation1hPerMTok in loadPricingMap/modelPricingRates with the defensive 5m fallback. - Restore the 5m/1h split in dailyUsageAmounts and sessionRowCost via a new parseEphemeralCacheSplit helper (upstream's rewritten token parser skips the nested cache_creation object), computing cost inline so 1h bills at its rate. - Drop the now-orphaned db.modelRates struct; the export resolver is the single cost source. Usage summary: re-home the snapshot exporter's BuildUsageSummary onto the new service layer (service.BuildUsageSummaryResult + usageSummaryResponseFromService), since the fold*/computeCacheStats helpers moved into internal/service. Frontend: adopt kit-ui's TopBar header, filtering the nav `tabs` to hide curation routes in snapshot mode (replaces the old {#if !SNAPSHOT} nav); adopt kit-ui theme getters/setters in ui.svelte.ts; keep the fork's 24-color golden-angle + org-theming projectColor over kit-ui's hashColor; coexist the longestPrompts panel with the new pairwise-comparison panel in the usage store (Endpoint now spans summary|comparison|pairwise|topSessions|longestPrompts). CLI: `usage projects` and `usage longest-prompts` adopt the paired --format/--json convention upstream now enforces. Deferred (unchanged from B): 1h-cache parity for the PostgreSQL and DuckDB cost paths; SetSessionPrivate stays read-only on the PG store. Green: go build/vet/test (except the snapshot test needing a git `origin` remote), svelte-check, 1770 vitest tests, make build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This PR turns report/export JSON into explicit v1 contracts for programmatic consumers. It adds shared schema, pricing provenance, and project identity metadata to usage daily and activity report outputs, and introduces a daemonless
agentsview export sessionssummary export for headless analytics. The session export is content-free, supports JSON/NDJSON, and includes per-session usage, model, cost, project, worktree, branch, machine, timestamp, and classification metadata without transcript content.Pricing provenance is centralized under
internal/export: reports use a resolver-derived block with source/table metadata, RFC 8785-style digest, fallback indicators,cost_source, and a bounded per-model effective rates map. Source-reported costs are marked so consumers know when token-times-rate recomputation is not expected, and reasoning tokens are handled as output-rate billing breakdowns.Project identity now persists raw observations at sync/import time and recomputes stable identities at export time. Remote-backed identities use normalized network remotes with
sha256:keys; path-backed fallbacks remain explicit and machine-local. The identity store is preserved through resync and mirrored through PostgreSQL/DuckDB so CLI and HTTP exports stay aligned across backends.The new session-summary export adds stable watermark/keyset pagination, cursor-reset signaling,
--all, NDJSON meta rows, root/child and automation filtering, and shared pricing/project metadata. Existing usage/activity payloads stay additive: metadata lands as sibling blocks, and daily breakdown arrays are pinned as arrays rather than omitted.Docs now describe the v1 contract rules, pricing digest input, project identity derivation, cursor behavior, session-export limits, and default exclusion caveats. Golden fixtures pin usage daily, usage daily with breakdowns, activity report, and session export JSON/NDJSON shapes. Stale
docs/superpowersdesign notes were removed, and the shared contract package was renamed frominternal/exportcontractstointernal/export.Reviewers should focus on:
internal/exportinternal/db/session_export.goandcmd/agentsview/export.goThe main tradeoff is landing the related export-contract issues together so field names and semantics stay shared across surfaces. This intentionally does not add redaction flags or per-row pricing provenance: raw project paths/remotes are emitted by default, and pricing provenance remains report-level with a bounded per-model map.