fix(analytics): always populate user email/period start/period end in report metadata - #444
Open
kostiantyn-pshenychnyi1 wants to merge 7 commits into
Conversation
…load Track min(startTime)/max(startTime+duration) across included ReportSessionRecord items during the flatten pass; use those as fallback in meta when the caller did not stamp explicit periodStart/periodEnd via PayloadContext. buildPayload stays a pure function — the fallback uses only data already in its arguments. Guards: sessions with startTime<=0 are skipped from the min/max; duration<=0 is treated as endTime=startTime; when no valid session exists, the fields are omitted (no synthetic generatedAt fallback). Also updates ReportMeta JSDoc to reflect the new contract: fields are always present when the report contains any sessions. Ref EPMCDME-13643
parseFilterOptions now stamps filter.toDate=new Date() alongside fromDate for --last <duration>, matching the user's implied 'from N ago to now' contract. This wires periodEnd through to buildPayload's ctx for --last runs and lets downstream aggregators/session-source filters see the upper bound. Adds CLI-metadata regression tests for the four formerly-broken invocation shapes (--last, --session, --project + --branch, bare). The --session/--project/ --branch/bare tests document that the CLI intentionally leaves periodStart/End undefined in the context — the end-user contract is fulfilled by the buildPayload fallback added in the previous commit. Ref EPMCDME-13643
…report maybeWriteSessionReport() now attempts ConfigLoader.loadMultiProviderConfig() when CODEMIE_PROFILE_CONFIG is absent, malformed, or lacks userEmail. The env var stays the fast path; the config-file fallback closes the reported gap where agent-exit session reports had no meta.userEmail even though ~/.codemie config held one. Both lookups remain non-fatal — any failure omits email silently. ConfigLoader is dynamically imported to stay off the hot path when the env var already carries the email. Ref EPMCDME-13643
…nity (cr-001) Wrap the min/max tracking in Number.isFinite checks so a single malformed session (NaN duration from a truncated timestamp event, Infinity startTime from a corrupted log) cannot propagate into new Date(...).toISOString() and throw RangeError, which would crash report generation for every user. - startTime must be finite AND > 0 to contribute to minStartMs/maxEndMs - duration is coerced to 0 when non-finite (still safely contributes startTime) Adds a regression test with mixed NaN duration, Infinity startTime, and a good session, asserting the derived period is stable and no exception thrown. Addresses CR-001 from code-review-final.json. Ref EPMCDME-13643
Adds the SDLC task run directory for EPMCDME-13643 (analytics report always populates userEmail/periodStart/periodEnd): - technical-analysis.md — tech-analyst research - complexity-assessment.json — S (12/36), routing writing-plans - spec.md — approved design (HITL gate) - plan.md — 5-task TDD plan (approved gate) - code-review-final.json — full-lens review verdict (request-changes CR-001) - code-review-check.json — check-round verdict (approve, CR-001 resolved) - code-review.diff / code-review-check.diff — materialized review diffs - qa-report.md / gate-plan.json — guide-first qa-gates run - decisions.jsonl / events.jsonl — HITL gate audit log - .state.json — sdlc-task state (phase: main) Ref EPMCDME-13643
Stage 8 actual-mode complexity assessment: total 15/36 (M), initial was 12/36 (S). Delta +3 driven almost entirely by file-change-estimate dimension inflating to XXL because diffstat counts the 12 SDLC planning artifacts alongside the 7 production/test files. Real implementation effort was ~41 net production LOC — comfortably within the S band. Ref EPMCDME-13643
…ion test fixtures - payload-builder.ts: remove the Number.isFinite explanation comment — the guard is self-explanatory at the call site. - payload-builder.test.ts: introduce shared timestamp constants (T0, T_PLUS_5M, T_PLUS_10M, HALF_MIN, ONE_MIN) plus pricedCost(id) / pricedIndex(...ids) / singleBranchRoot(sessions) helpers, then rewrite the six new period-derivation cases against them. No behavior change; all 20 payload-builder cases still green. Ref EPMCDME-13643
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes EPMCDME-13643 — the follow-up to #433 which partially wired
meta.userEmail,meta.periodStart,meta.periodEndinto analytics reports.Before this PR only
codemie analytics --from A --to B --report --report-format jsonpopulated all three fields. Every other invocation shape leaked partial or missing metadata:--report(bare)--last 7d --report--session <id> --report--project X --branch Y --report--from A --to B --reportEvery shape now populates all three. Verified end-to-end on the linked local build.
Changes
buildPayload()fallback derivation (src/cli/commands/analytics/report/payload-builder.ts) — tracksmin(startTime)/max(startTime + duration)across the flattenedReportSessionRecord[]in the same pass that already computes totals. When the caller doesn't stamp explicitperiodStart/periodEnd, meta falls back to those derived values.buildPayloadstays a pure function (no I/O, no config reads, no env access). Also covers the OTEL and agent-exit callers for free.Number.isFiniteon bothstartTimeandduration, so a single malformed session doesn't crash report generation vianew Date(NaN).toISOString()RangeError. (Added in response to code-review CR-001.)parseFilterOptions()--lastsetstoDate(src/cli/commands/analytics/index.ts) — one-line fix. The user's implied "from N ago to now" contract is now honored at the caller layer.BaseAgentAdapter.maybeWriteSessionReport()ConfigLoader fallback (src/agents/core/BaseAgentAdapter.ts) — whenCODEMIE_PROFILE_CONFIGis absent, malformed, or lacksuserEmail, fall back toConfigLoader.loadMultiProviderConfig().userEmail. Both lookups remain non-fatal.payload-builder.test.ts(min derivation, max derivation, regression guard,duration=0edge, zero-sessions omission,startTime≤0skip), 1 fix-up case for NaN/Infinity; 4 new cases inanalytics-cli-metadata.test.tscovering--last,--session,--project+--branch, bare wiring; 4 new cases inBaseAgentAdapter-session-report.test.tsfor the ConfigLoader fallback.ReportMeta.periodStart/periodEnd— from "absent for unfiltered reports" to "always present when the report contains any sessions".session-report.tsis unchanged (its per-session derivation was already correct and is the reference pattern this PR mirrors).Impact
Before — bare
codemie analytics --report --report-format json:{ "meta": { "userEmail": "u@example.com" } }After — same command:
{ "meta": { "userEmail": "u@example.com", "periodStart": "2026-01-13T09:17:11.262Z", "periodEnd": "2026-07-27T09:49:08.872Z" } }No CLI surface changes, no schema breaks (fields stay TypeScript-optional), no new dependencies.
Checklist
docs/superpowers/tasks/2026-07-27-analytics-report-always-populate-fields/code-review-final.json, one round of fixes recorded incode-review-check.json)ReportMeta; full SDLC artifacts underdocs/superpowers/tasks/2026-07-27-analytics-report-always-populate-fields/)Test plan
npm run test:unit— 2397 pass / 1 skippednpm run typecheck— cleannpm run build— cleannpm run license-check— cleannpx commitlint --from origin/main --to HEAD— clean--last 7d,--session <id>,--project X --branch Y,--from A --to B— all populate the three fields;--from/--tovalues preservedmain(unrelated, pre-existing — seeqa-report.md):npm run lintfails on 5 files this PR does not touch;npm run test:integrationfails on 6 suites due to missingnode-ptylocally and Windows subprocess crashes inskills.test.tsRef EPMCDME-13643