@@ -33,7 +33,8 @@ and validate the CLI schema version before opening storage.
3333 components after validation.
3434- Use only conspicuously synthetic names, labels, paths, repositories, and
3535 domains in tests, fixtures, documentation, and commit text.
36- - Do not push, rewrite history, or interact with the pull request.
36+ - Push the new follow-up commit normally after verification. Do not rewrite
37+ history or post pull-request comments.
3738
3839______________________________________________________________________
3940
@@ -637,5 +638,245 @@ docs(export): freeze completed reporting schema v1
637638
638639- [ ] ** Step 8: Confirm the final local state**
639640
640- Run ` git status --short ` and report the commits and verification results. Do not
641- push, rewrite history, or post pull-request comments.
641+ Run ` git status --short ` and record the commits and verification results for
642+ that checkpoint. Task 6 performs the final push without rewriting history or
643+ posting a pull-request comment.
644+
645+ ### Task 5: Match daily-usage survivor ordering
646+
647+ ** Files:**
648+
649+ - Modify: ` internal/activity/activity.go `
650+ - Modify: ` internal/db/activityreport.go `
651+ - Modify: ` internal/db/reporting_export.go `
652+ - Test: ` internal/db/reporting_export_test.go `
653+
654+ ** Interfaces:**
655+
656+ - Extends ` activity.UsageRow ` with ` UsageSource string ` and
657+ ` MessageOrdinal int64 ` , where ` -1 ` represents a missing message ordinal.
658+
659+ - Preserves
660+ ` finalizeReportingUsage(activity.Query, []activity.UsageRow) []activity.UsageRow ` .
661+
662+ - Preserves the current semantic comparators as deterministic tie-breakers after
663+ timestamp, session ID, and ordinal.
664+
665+ - [x] ** Step 1: Write regressions for primary and trailing ordering**
666+
667+ Add a table-driven ` TestFinalizeReportingUsageOrdering ` that constructs real
668+ ` activity.UsageRow ` values with one shared ` UsageDedupKey ` and literal token and
669+ cost values. Cover these cases:
670+
671+ ``` go
672+ {
673+ name: " empty session sorts before linked session" ,
674+ rows: []activity.UsageRow {
675+ {
676+ SessionID: " fixture-session" , MessageOrdinal: -1 ,
677+ UsageSource: " usage_event" , UsageDedupKey: " shared" ,
678+ Timestamp: " 2026-07-28T09:05:00Z" , InputTokens: 41 ,
679+ },
680+ {
681+ SessionID: " " , MessageOrdinal: -1 ,
682+ UsageSource: " cursor" , UsageDedupKey: " shared" ,
683+ Timestamp: " 2026-07-28T09:05:00Z" , InputTokens: 17 ,
684+ },
685+ },
686+ wantInput: 17 ,
687+ },
688+ {
689+ name: " lower ordinal wins before semantic fields" ,
690+ rows: []activity.UsageRow {
691+ {
692+ SessionID: " fixture-session" , MessageOrdinal: 2 ,
693+ UsageSource: " message" , UsageDedupKey: " shared" ,
694+ Timestamp: " 2026-07-28T09:05:00Z" , InputTokens: 11 ,
695+ },
696+ {
697+ SessionID: " fixture-session" , MessageOrdinal: 1 ,
698+ UsageSource: " usage_event" , UsageDedupKey: " shared" ,
699+ Timestamp: " 2026-07-28T09:05:00Z" , InputTokens: 29 ,
700+ },
701+ },
702+ wantInput: 29 ,
703+ },
704+ ```
705+
706+ Add a third case with equal timestamp, session, and ordinal but different
707+ sources and semantic values. Assert exact output for both insertion orders so
708+ the trailing source and semantic comparators remain deterministic.
709+
710+ Extend ` TestReportingExportDeduplicatesMergedUsageInputs ` so the standalone and
711+ session-linked rows have the same occurrence time and stable dedup token but
712+ distinct input tokens, costs, models, and project attribution. Compare the
713+ exported day with ` GetDailyUsage ` and assert the literal winning token and cost
714+ values.
715+
716+ - [x] ** Step 2: Run the ordering regressions and verify RED**
717+
718+ Run:
719+
720+ ``` bash
721+ CGO_ENABLED=1 go test -tags fts5 ./internal/db \
722+ -run ' TestFinalizeReportingUsageOrdering|TestReportingExportDeduplicatesMergedUsageInputs' \
723+ -count=1
724+ ```
725+
726+ Expected: FAIL because reporting puts the non-empty session ID first and has no
727+ ordinal or source metadata after candidate materialization.
728+
729+ - [x] ** Step 3: Carry normalized ordering metadata**
730+
731+ Add these internal fields to ` activity.UsageRow ` :
732+
733+ ``` go
734+ UsageSource string
735+ MessageOrdinal int64
736+ ```
737+
738+ When ` loadActivityReportUsageCandidatesFrom ` maps a scanned row, copy
739+ ` r.usageSource ` and the normalized ordinal already computed for sorting.
740+ Standalone Cursor candidates set ` UsageSource: "cursor" ` and
741+ ` MessageOrdinal: -1 ` . Do not fabricate session or project values.
742+
743+ - [x] ** Step 4: Apply the daily-usage ordering prefix**
744+
745+ Update ` sortReportingUsage ` to compare:
746+
747+ 1 . parsed occurrence instant and raw timestamp fallback;
748+ 1 . ` SessionID ` using ordinary ascending string order, including empty string;
749+ 1 . ` MessageOrdinal ` ascending; and
750+ 1 . ` UsageSource ` followed by the existing deterministic semantic comparators.
751+
752+ Remove the special non-empty-session preference. Keep every existing semantic
753+ comparison after the primary prefix.
754+
755+ - [x] ** Step 5: Run focused tests and verify GREEN**
756+
757+ Run:
758+
759+ ``` bash
760+ CGO_ENABLED=1 go test -tags fts5 ./internal/db \
761+ -run ' TestFinalizeReportingUsageOrdering|TestReportingExportDeduplicatesMergedUsageInputs|TestReportingExportIncludesStandaloneRowsOnlyInUsage|TestReportingExportIsIndependentOfArchiveLayout' \
762+ -count=1
763+ ```
764+
765+ Expected: PASS.
766+
767+ ### Task 6: Prepare reporting fallback pricing
768+
769+ ** Files:**
770+
771+ - Modify: ` cmd/agentsview/export_reporting.go `
772+ - Test: ` cmd/agentsview/export_reporting_test.go `
773+ - Delete:
774+ ` docs/superpowers/specs/2026-07-30-reporting-export-contract-corrections-design.md `
775+
776+ ** Interfaces:**
777+
778+ - Reuses
779+ ` ensureExportSessionsPricing(context.Context, *db.DB, config.Config) error ` .
780+
781+ - Preserves ` openReportingExportDB(*cobra.Command) (*db.DB, func(), error) ` .
782+
783+ - [x] ** Step 1: Write the unseeded-archive regression**
784+
785+ Create ` TestExportReportingFallbackPricingOnUnseededArchive ` . Open a real
786+ temporary archive, leave ` model_pricing ` empty, and seed a synthetic session
787+ with a known exact embedded fallback model:
788+
789+ ``` go
790+ model := exactFallbackPricedModel (t)
791+ ```
792+
793+ Insert a user message at ` 2026-07-28T10:00:00Z ` and an assistant message with
794+ literal usage at ` 2026-07-28T10:05:00Z ` :
795+
796+ ``` json
797+ {"input_tokens" :1000 ,"output_tokens" :500 }
798+ ```
799+
800+ Run ` export hour 2026-07-28-10 ` , decode the reporting hour, and assert:
801+
802+ ``` go
803+ assert.Positive (t, hour.Usage .Totals .Cost .Microdollars )
804+ assert.Positive (t, hour.Activity .Totals .Cost .Microdollars )
805+ ```
806+
807+ The observable nonzero totals catch removal of the fallback setup without
808+ mirroring the pricing implementation.
809+
810+ - [x] ** Step 2: Run the fallback regression and verify RED**
811+
812+ Run:
813+
814+ ``` bash
815+ CGO_ENABLED=1 go test -tags fts5 ./cmd/agentsview \
816+ -run TestExportReportingFallbackPricingOnUnseededArchive -count=1
817+ ```
818+
819+ Expected: FAIL with zero exported cost because reporting has not installed the
820+ empty-catalog in-memory fallback.
821+
822+ - [x] ** Step 3: Prepare pricing during database open**
823+
824+ In ` openReportingExportDB ` , after ` openExportReadOnlyDB ` succeeds, call:
825+
826+ ``` go
827+ if err := ensureExportSessionsPricing (
828+ cmd.Context (), database, appConfig,
829+ ); err != nil {
830+ _ = database.Close ()
831+ return nil , func () {}, err
832+ }
833+ ```
834+
835+ Return the existing cleanup closure only after preparation succeeds. This keeps
836+ the archive read-only, retains stored pricing when present, and prevents a
837+ database leak on setup failure.
838+
839+ - [x] ** Step 4: Run the command regression and neighboring tests**
840+
841+ Run:
842+
843+ ``` bash
844+ CGO_ENABLED=1 go test -tags fts5 ./cmd/agentsview \
845+ -run ' TestExportReportingFallbackPricingOnUnseededArchive|TestExportHour|TestExportDay|TestExportDigest|TestExportReportingSchemaVersion' \
846+ -count=1
847+ ```
848+
849+ Expected: PASS.
850+
851+ - [x] ** Step 5: Remove the temporary design document**
852+
853+ Delete only:
854+
855+ ``` text
856+ docs/superpowers/specs/2026-07-30-reporting-export-contract-corrections-design.md
857+ ```
858+
859+ Do not remove unrelated Superpowers plans or specifications.
860+
861+ - [ ] ** Step 6: Verify, audit, commit, and push**
862+
863+ Run:
864+
865+ ``` bash
866+ go fmt ./...
867+ CGO_ENABLED=1 go test -tags fts5 ./...
868+ go vet ./...
869+ ```
870+
871+ Inspect the complete staged diff and scan added strings for private identities,
872+ hosts, repositories, paths, credentials, or workflow-specific context. Verify
873+ the reporting fixture manifest remains valid. Use the mandatory commit skill
874+ and commit the implementation, regressions, plan update, and requested design
875+ document removal with:
876+
877+ ``` text
878+ fix(export): align reporting usage reconciliation
879+ ```
880+
881+ Push normally to ` origin/agent/hourly-reporting-export ` . Do not force-push,
882+ rewrite history, or post pull-request comments.
0 commit comments