cleargate 0.8.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/CHANGELOG.md +210 -0
  2. package/README.md +22 -1
  3. package/dist/MANIFEST.json +276 -31
  4. package/dist/chunk-HZPJ5QX4.js +459 -0
  5. package/dist/chunk-HZPJ5QX4.js.map +1 -0
  6. package/dist/{chunk-OM4FAEA7.js → chunk-Q3BTSXCK.js} +69 -3
  7. package/dist/chunk-Q3BTSXCK.js.map +1 -0
  8. package/dist/cli.cjs +2888 -598
  9. package/dist/cli.cjs.map +1 -1
  10. package/dist/cli.js +2481 -619
  11. package/dist/cli.js.map +1 -1
  12. package/dist/lib/ledger.cjs +120 -0
  13. package/dist/lib/ledger.cjs.map +1 -0
  14. package/dist/lib/ledger.d.cts +64 -0
  15. package/dist/lib/ledger.d.ts +64 -0
  16. package/dist/lib/ledger.js +96 -0
  17. package/dist/lib/ledger.js.map +1 -0
  18. package/dist/lib/lifecycle-reconcile.cjs +497 -0
  19. package/dist/lib/lifecycle-reconcile.cjs.map +1 -0
  20. package/dist/lib/lifecycle-reconcile.d.cts +136 -0
  21. package/dist/lib/lifecycle-reconcile.d.ts +136 -0
  22. package/dist/lib/lifecycle-reconcile.js +20 -0
  23. package/dist/lib/lifecycle-reconcile.js.map +1 -0
  24. package/dist/templates/cleargate-planning/.claude/agents/architect.md +65 -10
  25. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +108 -0
  26. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +49 -3
  27. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +6 -1
  28. package/dist/templates/cleargate-planning/.claude/agents/developer.md +51 -2
  29. package/dist/templates/cleargate-planning/.claude/agents/devops.md +249 -0
  30. package/dist/templates/cleargate-planning/.claude/agents/qa.md +91 -1
  31. package/dist/templates/cleargate-planning/.claude/agents/reporter.md +72 -14
  32. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +21 -0
  33. package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +148 -0
  34. package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +6 -0
  35. package/dist/templates/cleargate-planning/.claude/hooks/stamp-and-gate.sh +12 -1
  36. package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +334 -96
  37. package/dist/templates/cleargate-planning/.claude/settings.json +4 -0
  38. package/dist/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +644 -0
  39. package/dist/templates/cleargate-planning/.cleargate/config.example.yml +19 -0
  40. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +542 -0
  41. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +102 -428
  42. package/dist/templates/cleargate-planning/.cleargate/knowledge/mid-sprint-triage-rubric.md +160 -0
  43. package/dist/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +72 -9
  44. package/dist/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +71 -0
  45. package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +24 -2
  46. package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +471 -29
  47. package/dist/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +219 -0
  48. package/dist/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +3 -3
  49. package/dist/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +86 -10
  50. package/dist/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +54 -0
  51. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +378 -0
  52. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +888 -0
  53. package/dist/templates/cleargate-planning/.cleargate/scripts/run_script.sh +173 -87
  54. package/dist/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +71 -0
  55. package/dist/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +483 -13
  56. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +482 -0
  57. package/dist/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +32 -8
  58. package/dist/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +136 -0
  59. package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +27 -1
  60. package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +35 -1
  61. package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +48 -14
  62. package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +40 -3
  63. package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +53 -0
  64. package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +98 -29
  65. package/dist/templates/cleargate-planning/.cleargate/templates/proposal.md +17 -4
  66. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_context.md +8 -0
  67. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +23 -4
  68. package/dist/templates/cleargate-planning/.cleargate/templates/story.md +58 -3
  69. package/dist/templates/cleargate-planning/CLAUDE.md +30 -10
  70. package/dist/templates/cleargate-planning/MANIFEST.json +276 -31
  71. package/dist/{whoami-CX7CXJD5.js → whoami-W4U6DPVG.js} +17 -17
  72. package/dist/whoami-W4U6DPVG.js.map +1 -0
  73. package/package.json +20 -6
  74. package/templates/cleargate-planning/.claude/agents/architect.md +65 -10
  75. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +108 -0
  76. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +49 -3
  77. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +6 -1
  78. package/templates/cleargate-planning/.claude/agents/developer.md +51 -2
  79. package/templates/cleargate-planning/.claude/agents/devops.md +249 -0
  80. package/templates/cleargate-planning/.claude/agents/qa.md +91 -1
  81. package/templates/cleargate-planning/.claude/agents/reporter.md +72 -14
  82. package/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +21 -0
  83. package/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +148 -0
  84. package/templates/cleargate-planning/.claude/hooks/session-start.sh +6 -0
  85. package/templates/cleargate-planning/.claude/hooks/stamp-and-gate.sh +12 -1
  86. package/templates/cleargate-planning/.claude/hooks/token-ledger.sh +334 -96
  87. package/templates/cleargate-planning/.claude/settings.json +4 -0
  88. package/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +644 -0
  89. package/templates/cleargate-planning/.cleargate/config.example.yml +19 -0
  90. package/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +542 -0
  91. package/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +102 -428
  92. package/templates/cleargate-planning/.cleargate/knowledge/mid-sprint-triage-rubric.md +160 -0
  93. package/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +72 -9
  94. package/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +71 -0
  95. package/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +24 -2
  96. package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +471 -29
  97. package/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +219 -0
  98. package/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +3 -3
  99. package/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +86 -10
  100. package/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +54 -0
  101. package/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +378 -0
  102. package/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +888 -0
  103. package/templates/cleargate-planning/.cleargate/scripts/run_script.sh +173 -87
  104. package/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +71 -0
  105. package/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +483 -13
  106. package/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +482 -0
  107. package/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +32 -8
  108. package/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +136 -0
  109. package/templates/cleargate-planning/.cleargate/templates/Bug.md +27 -1
  110. package/templates/cleargate-planning/.cleargate/templates/CR.md +35 -1
  111. package/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +48 -14
  112. package/templates/cleargate-planning/.cleargate/templates/epic.md +40 -3
  113. package/templates/cleargate-planning/.cleargate/templates/hotfix.md +53 -0
  114. package/templates/cleargate-planning/.cleargate/templates/initiative.md +98 -29
  115. package/templates/cleargate-planning/.cleargate/templates/sprint_context.md +8 -0
  116. package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +23 -4
  117. package/templates/cleargate-planning/.cleargate/templates/story.md +58 -3
  118. package/templates/cleargate-planning/CLAUDE.md +30 -10
  119. package/templates/cleargate-planning/MANIFEST.json +276 -31
  120. package/dist/chunk-OM4FAEA7.js.map +0 -1
  121. package/dist/whoami-CX7CXJD5.js.map +0 -1
  122. package/templates/cleargate-planning/.cleargate/templates/proposal.md +0 -61
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/lib/ledger.ts
22
+ var ledger_exports = {};
23
+ __export(ledger_exports, {
24
+ sumDeltas: () => sumDeltas
25
+ });
26
+ module.exports = __toCommonJS(ledger_exports);
27
+ function isTokenCounts(v) {
28
+ if (typeof v !== "object" || v === null) return false;
29
+ const obj = v;
30
+ return typeof obj["input"] === "number" && typeof obj["output"] === "number" && typeof obj["cache_creation"] === "number" && typeof obj["cache_read"] === "number";
31
+ }
32
+ function hasDelta(row) {
33
+ return isTokenCounts(row["delta"]);
34
+ }
35
+ function zeroCounts() {
36
+ return { input: 0, output: 0, cache_creation: 0, cache_read: 0 };
37
+ }
38
+ function addCounts(a, b) {
39
+ return {
40
+ input: a.input + b.input,
41
+ output: a.output + b.output,
42
+ cache_creation: a.cache_creation + b.cache_creation,
43
+ cache_read: a.cache_read + b.cache_read
44
+ };
45
+ }
46
+ function lastRowTrickForFlatRows(rows) {
47
+ const sessionMap = /* @__PURE__ */ new Map();
48
+ for (const row of rows) {
49
+ const sessionId = typeof row["session_id"] === "string" ? row["session_id"] : "(unknown)";
50
+ const existing = sessionMap.get(sessionId);
51
+ if (!existing) {
52
+ sessionMap.set(sessionId, row);
53
+ } else {
54
+ const existingTs = typeof existing["ts"] === "string" ? existing["ts"] : "";
55
+ const rowTs = typeof row["ts"] === "string" ? row["ts"] : "";
56
+ if (rowTs > existingTs) {
57
+ sessionMap.set(sessionId, row);
58
+ }
59
+ }
60
+ }
61
+ let totals = zeroCounts();
62
+ for (const row of sessionMap.values()) {
63
+ totals = addCounts(totals, {
64
+ input: typeof row["input"] === "number" ? row["input"] : 0,
65
+ output: typeof row["output"] === "number" ? row["output"] : 0,
66
+ cache_creation: typeof row["cache_creation"] === "number" ? row["cache_creation"] : 0,
67
+ cache_read: typeof row["cache_read"] === "number" ? row["cache_read"] : 0
68
+ });
69
+ }
70
+ return totals;
71
+ }
72
+ function sumDeltas(rows) {
73
+ const validRows = [];
74
+ for (const raw of rows) {
75
+ if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
76
+ validRows.push(raw);
77
+ } else {
78
+ console.warn("[ledger.ts] sumDeltas: skipping malformed row (not an object):", raw);
79
+ }
80
+ }
81
+ if (validRows.length === 0) {
82
+ return { totals: zeroCounts(), format: "delta" };
83
+ }
84
+ const deltaRows = validRows.filter(hasDelta);
85
+ const flatRows = validRows.filter((r) => !hasDelta(r));
86
+ if (flatRows.length === 0) {
87
+ let totals2 = zeroCounts();
88
+ for (const row of deltaRows) {
89
+ const d = row["delta"];
90
+ totals2 = addCounts(totals2, d);
91
+ }
92
+ return { totals: totals2, format: "delta" };
93
+ }
94
+ if (deltaRows.length === 0) {
95
+ const totals2 = lastRowTrickForFlatRows(flatRows);
96
+ return {
97
+ totals: totals2,
98
+ format: "pre-0.9.0",
99
+ pre_v2_caveat: "**Ledger format note:** This sprint's token-ledger.jsonl uses pre-0.9.0 flat-field rows; cost is computed via the last-row-per-session trick (reconciliation accuracy \xB1N \xD7 real-cost where N = SubagentStop fires per session)."
100
+ };
101
+ }
102
+ let totals = zeroCounts();
103
+ for (const row of deltaRows) {
104
+ const d = row["delta"];
105
+ totals = addCounts(totals, d);
106
+ }
107
+ const flatTotals = lastRowTrickForFlatRows(flatRows);
108
+ totals = addCounts(totals, flatTotals);
109
+ const caveat = `Mixed format ledger: ${deltaRows.length} delta rows + ${flatRows.length} pre-0.9.0 rows; flat segment uses last-row trick.`;
110
+ return {
111
+ totals,
112
+ format: "mixed",
113
+ pre_v2_caveat: caveat
114
+ };
115
+ }
116
+ // Annotate the CommonJS export names for ESM import in node:
117
+ 0 && (module.exports = {
118
+ sumDeltas
119
+ });
120
+ //# sourceMappingURL=ledger.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/ledger.ts"],"sourcesContent":["/**\n * ledger.ts — CR-018\n *\n * Sprint-wide cost math: parses token-ledger.jsonl rows and sums delta tokens\n * for the Reporter agent. Distinct from ledger-reader.ts (per-work-item\n * attribution lookup); this file provides sumDeltas() for sprint-total cost.\n *\n * Format detection handles three cases:\n * 'delta' — all rows carry delta.* blocks (post-0.9.0)\n * 'pre-0.9.0' — all rows use flat input/output/cache_* fields (pre-0.9.0)\n * 'mixed' — some rows have delta, others don't (SPRINT-15 cutover window)\n */\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\n/** Token counts block (used in both delta and session_total). */\nexport interface TokenCounts {\n input: number;\n output: number;\n cache_creation: number;\n cache_read: number;\n}\n\n/** Full post-0.9.0 ledger row shape. */\nexport interface LedgerRowV2 {\n ts: string;\n sprint_id: string;\n story_id: string;\n work_item_id: string;\n agent_type: string;\n session_id: string;\n transcript?: string;\n sentinel_started_at?: string;\n delta_from_turn?: number;\n delta: TokenCounts;\n session_total: TokenCounts;\n model: string;\n turns: number;\n}\n\n/** Result returned by sumDeltas(). */\nexport interface SumResult {\n /** Aggregated token totals across all rows (using appropriate format). */\n totals: TokenCounts;\n /** How the totals were computed. */\n format: 'delta' | 'pre-0.9.0' | 'mixed';\n /**\n * Present when format is 'pre-0.9.0' or 'mixed'.\n * Reporter should paste this verbatim into REPORT.md §3.\n */\n pre_v2_caveat?: string;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\nfunction isTokenCounts(v: unknown): v is TokenCounts {\n if (typeof v !== 'object' || v === null) return false;\n const obj = v as Record<string, unknown>;\n return (\n typeof obj['input'] === 'number' &&\n typeof obj['output'] === 'number' &&\n typeof obj['cache_creation'] === 'number' &&\n typeof obj['cache_read'] === 'number'\n );\n}\n\nfunction hasDelta(row: Record<string, unknown>): boolean {\n return isTokenCounts(row['delta']);\n}\n\nfunction zeroCounts(): TokenCounts {\n return { input: 0, output: 0, cache_creation: 0, cache_read: 0 };\n}\n\nfunction addCounts(a: TokenCounts, b: TokenCounts): TokenCounts {\n return {\n input: a.input + b.input,\n output: a.output + b.output,\n cache_creation: a.cache_creation + b.cache_creation,\n cache_read: a.cache_read + b.cache_read,\n };\n}\n\n/**\n * Last-row-per-session trick for pre-0.9.0 flat-field rows.\n * Groups rows by session_id, takes the row with max ts per group,\n * and sums the flat input/output/cache_* fields across those last rows.\n */\nfunction lastRowTrickForFlatRows(rows: Record<string, unknown>[]): TokenCounts {\n const sessionMap = new Map<string, Record<string, unknown>>();\n\n for (const row of rows) {\n const sessionId = typeof row['session_id'] === 'string' ? row['session_id'] : '(unknown)';\n const existing = sessionMap.get(sessionId);\n if (!existing) {\n sessionMap.set(sessionId, row);\n } else {\n const existingTs = typeof existing['ts'] === 'string' ? existing['ts'] : '';\n const rowTs = typeof row['ts'] === 'string' ? row['ts'] : '';\n if (rowTs > existingTs) {\n sessionMap.set(sessionId, row);\n }\n }\n }\n\n let totals = zeroCounts();\n for (const row of sessionMap.values()) {\n totals = addCounts(totals, {\n input: typeof row['input'] === 'number' ? row['input'] : 0,\n output: typeof row['output'] === 'number' ? row['output'] : 0,\n cache_creation: typeof row['cache_creation'] === 'number' ? row['cache_creation'] : 0,\n cache_read: typeof row['cache_read'] === 'number' ? row['cache_read'] : 0,\n });\n }\n return totals;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Compute sprint-wide token totals from an array of raw ledger rows (parsed JSONL).\n *\n * Format-detection algorithm (mandatory):\n * 1. Classify each row: has `delta` object with 4 numeric fields → v2; else → flat/legacy.\n * 2. All v2 → format='delta', sum delta.* directly.\n * 3. All flat → format='pre-0.9.0', group by session_id, last-row-per-session trick.\n * 4. Mixed → format='mixed', delta rows contribute delta.*; flat rows within\n * each session use last-row trick scoped to flat rows only.\n *\n * Malformed or unrecognisable rows are skipped with a console.warn.\n *\n * @param rows Array of parsed JSON objects (unknown type for defensive parsing).\n */\nexport function sumDeltas(rows: unknown[]): SumResult {\n // Filter to recognisable objects; skip malformed rows.\n const validRows: Record<string, unknown>[] = [];\n for (const raw of rows) {\n if (typeof raw === 'object' && raw !== null && !Array.isArray(raw)) {\n validRows.push(raw as Record<string, unknown>);\n } else {\n console.warn('[ledger.ts] sumDeltas: skipping malformed row (not an object):', raw);\n }\n }\n\n if (validRows.length === 0) {\n return { totals: zeroCounts(), format: 'delta' };\n }\n\n const deltaRows = validRows.filter(hasDelta);\n const flatRows = validRows.filter((r) => !hasDelta(r));\n\n // All rows are v2 (delta) format\n if (flatRows.length === 0) {\n let totals = zeroCounts();\n for (const row of deltaRows) {\n const d = row['delta'] as TokenCounts;\n totals = addCounts(totals, d);\n }\n return { totals, format: 'delta' };\n }\n\n // All rows are pre-0.9.0 flat format\n if (deltaRows.length === 0) {\n const totals = lastRowTrickForFlatRows(flatRows);\n return {\n totals,\n format: 'pre-0.9.0',\n pre_v2_caveat:\n '**Ledger format note:** This sprint\\'s token-ledger.jsonl uses pre-0.9.0 flat-field rows; ' +\n 'cost is computed via the last-row-per-session trick ' +\n '(reconciliation accuracy ±N × real-cost where N = SubagentStop fires per session).',\n };\n }\n\n // Mixed format: both delta rows and flat rows in same ledger (e.g. SPRINT-15 cutover)\n // Delta rows → sum delta.*\n // Flat rows → group by session_id, last-row trick (scoped to flat rows only)\n let totals = zeroCounts();\n\n // Sum delta rows directly\n for (const row of deltaRows) {\n const d = row['delta'] as TokenCounts;\n totals = addCounts(totals, d);\n }\n\n // Apply last-row trick to flat rows\n const flatTotals = lastRowTrickForFlatRows(flatRows);\n totals = addCounts(totals, flatTotals);\n\n const caveat =\n `Mixed format ledger: ${deltaRows.length} delta rows + ${flatRows.length} pre-0.9.0 rows; ` +\n `flat segment uses last-row trick.`;\n\n return {\n totals,\n format: 'mixed',\n pre_v2_caveat: caveat,\n };\n}\n"],"mappings":"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","names":["totals"]}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * ledger.ts — CR-018
3
+ *
4
+ * Sprint-wide cost math: parses token-ledger.jsonl rows and sums delta tokens
5
+ * for the Reporter agent. Distinct from ledger-reader.ts (per-work-item
6
+ * attribution lookup); this file provides sumDeltas() for sprint-total cost.
7
+ *
8
+ * Format detection handles three cases:
9
+ * 'delta' — all rows carry delta.* blocks (post-0.9.0)
10
+ * 'pre-0.9.0' — all rows use flat input/output/cache_* fields (pre-0.9.0)
11
+ * 'mixed' — some rows have delta, others don't (SPRINT-15 cutover window)
12
+ */
13
+ /** Token counts block (used in both delta and session_total). */
14
+ interface TokenCounts {
15
+ input: number;
16
+ output: number;
17
+ cache_creation: number;
18
+ cache_read: number;
19
+ }
20
+ /** Full post-0.9.0 ledger row shape. */
21
+ interface LedgerRowV2 {
22
+ ts: string;
23
+ sprint_id: string;
24
+ story_id: string;
25
+ work_item_id: string;
26
+ agent_type: string;
27
+ session_id: string;
28
+ transcript?: string;
29
+ sentinel_started_at?: string;
30
+ delta_from_turn?: number;
31
+ delta: TokenCounts;
32
+ session_total: TokenCounts;
33
+ model: string;
34
+ turns: number;
35
+ }
36
+ /** Result returned by sumDeltas(). */
37
+ interface SumResult {
38
+ /** Aggregated token totals across all rows (using appropriate format). */
39
+ totals: TokenCounts;
40
+ /** How the totals were computed. */
41
+ format: 'delta' | 'pre-0.9.0' | 'mixed';
42
+ /**
43
+ * Present when format is 'pre-0.9.0' or 'mixed'.
44
+ * Reporter should paste this verbatim into REPORT.md §3.
45
+ */
46
+ pre_v2_caveat?: string;
47
+ }
48
+ /**
49
+ * Compute sprint-wide token totals from an array of raw ledger rows (parsed JSONL).
50
+ *
51
+ * Format-detection algorithm (mandatory):
52
+ * 1. Classify each row: has `delta` object with 4 numeric fields → v2; else → flat/legacy.
53
+ * 2. All v2 → format='delta', sum delta.* directly.
54
+ * 3. All flat → format='pre-0.9.0', group by session_id, last-row-per-session trick.
55
+ * 4. Mixed → format='mixed', delta rows contribute delta.*; flat rows within
56
+ * each session use last-row trick scoped to flat rows only.
57
+ *
58
+ * Malformed or unrecognisable rows are skipped with a console.warn.
59
+ *
60
+ * @param rows Array of parsed JSON objects (unknown type for defensive parsing).
61
+ */
62
+ declare function sumDeltas(rows: unknown[]): SumResult;
63
+
64
+ export { type LedgerRowV2, type SumResult, type TokenCounts, sumDeltas };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * ledger.ts — CR-018
3
+ *
4
+ * Sprint-wide cost math: parses token-ledger.jsonl rows and sums delta tokens
5
+ * for the Reporter agent. Distinct from ledger-reader.ts (per-work-item
6
+ * attribution lookup); this file provides sumDeltas() for sprint-total cost.
7
+ *
8
+ * Format detection handles three cases:
9
+ * 'delta' — all rows carry delta.* blocks (post-0.9.0)
10
+ * 'pre-0.9.0' — all rows use flat input/output/cache_* fields (pre-0.9.0)
11
+ * 'mixed' — some rows have delta, others don't (SPRINT-15 cutover window)
12
+ */
13
+ /** Token counts block (used in both delta and session_total). */
14
+ interface TokenCounts {
15
+ input: number;
16
+ output: number;
17
+ cache_creation: number;
18
+ cache_read: number;
19
+ }
20
+ /** Full post-0.9.0 ledger row shape. */
21
+ interface LedgerRowV2 {
22
+ ts: string;
23
+ sprint_id: string;
24
+ story_id: string;
25
+ work_item_id: string;
26
+ agent_type: string;
27
+ session_id: string;
28
+ transcript?: string;
29
+ sentinel_started_at?: string;
30
+ delta_from_turn?: number;
31
+ delta: TokenCounts;
32
+ session_total: TokenCounts;
33
+ model: string;
34
+ turns: number;
35
+ }
36
+ /** Result returned by sumDeltas(). */
37
+ interface SumResult {
38
+ /** Aggregated token totals across all rows (using appropriate format). */
39
+ totals: TokenCounts;
40
+ /** How the totals were computed. */
41
+ format: 'delta' | 'pre-0.9.0' | 'mixed';
42
+ /**
43
+ * Present when format is 'pre-0.9.0' or 'mixed'.
44
+ * Reporter should paste this verbatim into REPORT.md §3.
45
+ */
46
+ pre_v2_caveat?: string;
47
+ }
48
+ /**
49
+ * Compute sprint-wide token totals from an array of raw ledger rows (parsed JSONL).
50
+ *
51
+ * Format-detection algorithm (mandatory):
52
+ * 1. Classify each row: has `delta` object with 4 numeric fields → v2; else → flat/legacy.
53
+ * 2. All v2 → format='delta', sum delta.* directly.
54
+ * 3. All flat → format='pre-0.9.0', group by session_id, last-row-per-session trick.
55
+ * 4. Mixed → format='mixed', delta rows contribute delta.*; flat rows within
56
+ * each session use last-row trick scoped to flat rows only.
57
+ *
58
+ * Malformed or unrecognisable rows are skipped with a console.warn.
59
+ *
60
+ * @param rows Array of parsed JSON objects (unknown type for defensive parsing).
61
+ */
62
+ declare function sumDeltas(rows: unknown[]): SumResult;
63
+
64
+ export { type LedgerRowV2, type SumResult, type TokenCounts, sumDeltas };
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/ledger.ts
4
+ function isTokenCounts(v) {
5
+ if (typeof v !== "object" || v === null) return false;
6
+ const obj = v;
7
+ return typeof obj["input"] === "number" && typeof obj["output"] === "number" && typeof obj["cache_creation"] === "number" && typeof obj["cache_read"] === "number";
8
+ }
9
+ function hasDelta(row) {
10
+ return isTokenCounts(row["delta"]);
11
+ }
12
+ function zeroCounts() {
13
+ return { input: 0, output: 0, cache_creation: 0, cache_read: 0 };
14
+ }
15
+ function addCounts(a, b) {
16
+ return {
17
+ input: a.input + b.input,
18
+ output: a.output + b.output,
19
+ cache_creation: a.cache_creation + b.cache_creation,
20
+ cache_read: a.cache_read + b.cache_read
21
+ };
22
+ }
23
+ function lastRowTrickForFlatRows(rows) {
24
+ const sessionMap = /* @__PURE__ */ new Map();
25
+ for (const row of rows) {
26
+ const sessionId = typeof row["session_id"] === "string" ? row["session_id"] : "(unknown)";
27
+ const existing = sessionMap.get(sessionId);
28
+ if (!existing) {
29
+ sessionMap.set(sessionId, row);
30
+ } else {
31
+ const existingTs = typeof existing["ts"] === "string" ? existing["ts"] : "";
32
+ const rowTs = typeof row["ts"] === "string" ? row["ts"] : "";
33
+ if (rowTs > existingTs) {
34
+ sessionMap.set(sessionId, row);
35
+ }
36
+ }
37
+ }
38
+ let totals = zeroCounts();
39
+ for (const row of sessionMap.values()) {
40
+ totals = addCounts(totals, {
41
+ input: typeof row["input"] === "number" ? row["input"] : 0,
42
+ output: typeof row["output"] === "number" ? row["output"] : 0,
43
+ cache_creation: typeof row["cache_creation"] === "number" ? row["cache_creation"] : 0,
44
+ cache_read: typeof row["cache_read"] === "number" ? row["cache_read"] : 0
45
+ });
46
+ }
47
+ return totals;
48
+ }
49
+ function sumDeltas(rows) {
50
+ const validRows = [];
51
+ for (const raw of rows) {
52
+ if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
53
+ validRows.push(raw);
54
+ } else {
55
+ console.warn("[ledger.ts] sumDeltas: skipping malformed row (not an object):", raw);
56
+ }
57
+ }
58
+ if (validRows.length === 0) {
59
+ return { totals: zeroCounts(), format: "delta" };
60
+ }
61
+ const deltaRows = validRows.filter(hasDelta);
62
+ const flatRows = validRows.filter((r) => !hasDelta(r));
63
+ if (flatRows.length === 0) {
64
+ let totals2 = zeroCounts();
65
+ for (const row of deltaRows) {
66
+ const d = row["delta"];
67
+ totals2 = addCounts(totals2, d);
68
+ }
69
+ return { totals: totals2, format: "delta" };
70
+ }
71
+ if (deltaRows.length === 0) {
72
+ const totals2 = lastRowTrickForFlatRows(flatRows);
73
+ return {
74
+ totals: totals2,
75
+ format: "pre-0.9.0",
76
+ pre_v2_caveat: "**Ledger format note:** This sprint's token-ledger.jsonl uses pre-0.9.0 flat-field rows; cost is computed via the last-row-per-session trick (reconciliation accuracy \xB1N \xD7 real-cost where N = SubagentStop fires per session)."
77
+ };
78
+ }
79
+ let totals = zeroCounts();
80
+ for (const row of deltaRows) {
81
+ const d = row["delta"];
82
+ totals = addCounts(totals, d);
83
+ }
84
+ const flatTotals = lastRowTrickForFlatRows(flatRows);
85
+ totals = addCounts(totals, flatTotals);
86
+ const caveat = `Mixed format ledger: ${deltaRows.length} delta rows + ${flatRows.length} pre-0.9.0 rows; flat segment uses last-row trick.`;
87
+ return {
88
+ totals,
89
+ format: "mixed",
90
+ pre_v2_caveat: caveat
91
+ };
92
+ }
93
+ export {
94
+ sumDeltas
95
+ };
96
+ //# sourceMappingURL=ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/ledger.ts"],"sourcesContent":["/**\n * ledger.ts — CR-018\n *\n * Sprint-wide cost math: parses token-ledger.jsonl rows and sums delta tokens\n * for the Reporter agent. Distinct from ledger-reader.ts (per-work-item\n * attribution lookup); this file provides sumDeltas() for sprint-total cost.\n *\n * Format detection handles three cases:\n * 'delta' — all rows carry delta.* blocks (post-0.9.0)\n * 'pre-0.9.0' — all rows use flat input/output/cache_* fields (pre-0.9.0)\n * 'mixed' — some rows have delta, others don't (SPRINT-15 cutover window)\n */\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\n/** Token counts block (used in both delta and session_total). */\nexport interface TokenCounts {\n input: number;\n output: number;\n cache_creation: number;\n cache_read: number;\n}\n\n/** Full post-0.9.0 ledger row shape. */\nexport interface LedgerRowV2 {\n ts: string;\n sprint_id: string;\n story_id: string;\n work_item_id: string;\n agent_type: string;\n session_id: string;\n transcript?: string;\n sentinel_started_at?: string;\n delta_from_turn?: number;\n delta: TokenCounts;\n session_total: TokenCounts;\n model: string;\n turns: number;\n}\n\n/** Result returned by sumDeltas(). */\nexport interface SumResult {\n /** Aggregated token totals across all rows (using appropriate format). */\n totals: TokenCounts;\n /** How the totals were computed. */\n format: 'delta' | 'pre-0.9.0' | 'mixed';\n /**\n * Present when format is 'pre-0.9.0' or 'mixed'.\n * Reporter should paste this verbatim into REPORT.md §3.\n */\n pre_v2_caveat?: string;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\nfunction isTokenCounts(v: unknown): v is TokenCounts {\n if (typeof v !== 'object' || v === null) return false;\n const obj = v as Record<string, unknown>;\n return (\n typeof obj['input'] === 'number' &&\n typeof obj['output'] === 'number' &&\n typeof obj['cache_creation'] === 'number' &&\n typeof obj['cache_read'] === 'number'\n );\n}\n\nfunction hasDelta(row: Record<string, unknown>): boolean {\n return isTokenCounts(row['delta']);\n}\n\nfunction zeroCounts(): TokenCounts {\n return { input: 0, output: 0, cache_creation: 0, cache_read: 0 };\n}\n\nfunction addCounts(a: TokenCounts, b: TokenCounts): TokenCounts {\n return {\n input: a.input + b.input,\n output: a.output + b.output,\n cache_creation: a.cache_creation + b.cache_creation,\n cache_read: a.cache_read + b.cache_read,\n };\n}\n\n/**\n * Last-row-per-session trick for pre-0.9.0 flat-field rows.\n * Groups rows by session_id, takes the row with max ts per group,\n * and sums the flat input/output/cache_* fields across those last rows.\n */\nfunction lastRowTrickForFlatRows(rows: Record<string, unknown>[]): TokenCounts {\n const sessionMap = new Map<string, Record<string, unknown>>();\n\n for (const row of rows) {\n const sessionId = typeof row['session_id'] === 'string' ? row['session_id'] : '(unknown)';\n const existing = sessionMap.get(sessionId);\n if (!existing) {\n sessionMap.set(sessionId, row);\n } else {\n const existingTs = typeof existing['ts'] === 'string' ? existing['ts'] : '';\n const rowTs = typeof row['ts'] === 'string' ? row['ts'] : '';\n if (rowTs > existingTs) {\n sessionMap.set(sessionId, row);\n }\n }\n }\n\n let totals = zeroCounts();\n for (const row of sessionMap.values()) {\n totals = addCounts(totals, {\n input: typeof row['input'] === 'number' ? row['input'] : 0,\n output: typeof row['output'] === 'number' ? row['output'] : 0,\n cache_creation: typeof row['cache_creation'] === 'number' ? row['cache_creation'] : 0,\n cache_read: typeof row['cache_read'] === 'number' ? row['cache_read'] : 0,\n });\n }\n return totals;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Compute sprint-wide token totals from an array of raw ledger rows (parsed JSONL).\n *\n * Format-detection algorithm (mandatory):\n * 1. Classify each row: has `delta` object with 4 numeric fields → v2; else → flat/legacy.\n * 2. All v2 → format='delta', sum delta.* directly.\n * 3. All flat → format='pre-0.9.0', group by session_id, last-row-per-session trick.\n * 4. Mixed → format='mixed', delta rows contribute delta.*; flat rows within\n * each session use last-row trick scoped to flat rows only.\n *\n * Malformed or unrecognisable rows are skipped with a console.warn.\n *\n * @param rows Array of parsed JSON objects (unknown type for defensive parsing).\n */\nexport function sumDeltas(rows: unknown[]): SumResult {\n // Filter to recognisable objects; skip malformed rows.\n const validRows: Record<string, unknown>[] = [];\n for (const raw of rows) {\n if (typeof raw === 'object' && raw !== null && !Array.isArray(raw)) {\n validRows.push(raw as Record<string, unknown>);\n } else {\n console.warn('[ledger.ts] sumDeltas: skipping malformed row (not an object):', raw);\n }\n }\n\n if (validRows.length === 0) {\n return { totals: zeroCounts(), format: 'delta' };\n }\n\n const deltaRows = validRows.filter(hasDelta);\n const flatRows = validRows.filter((r) => !hasDelta(r));\n\n // All rows are v2 (delta) format\n if (flatRows.length === 0) {\n let totals = zeroCounts();\n for (const row of deltaRows) {\n const d = row['delta'] as TokenCounts;\n totals = addCounts(totals, d);\n }\n return { totals, format: 'delta' };\n }\n\n // All rows are pre-0.9.0 flat format\n if (deltaRows.length === 0) {\n const totals = lastRowTrickForFlatRows(flatRows);\n return {\n totals,\n format: 'pre-0.9.0',\n pre_v2_caveat:\n '**Ledger format note:** This sprint\\'s token-ledger.jsonl uses pre-0.9.0 flat-field rows; ' +\n 'cost is computed via the last-row-per-session trick ' +\n '(reconciliation accuracy ±N × real-cost where N = SubagentStop fires per session).',\n };\n }\n\n // Mixed format: both delta rows and flat rows in same ledger (e.g. SPRINT-15 cutover)\n // Delta rows → sum delta.*\n // Flat rows → group by session_id, last-row trick (scoped to flat rows only)\n let totals = zeroCounts();\n\n // Sum delta rows directly\n for (const row of deltaRows) {\n const d = row['delta'] as TokenCounts;\n totals = addCounts(totals, d);\n }\n\n // Apply last-row trick to flat rows\n const flatTotals = lastRowTrickForFlatRows(flatRows);\n totals = addCounts(totals, flatTotals);\n\n const caveat =\n `Mixed format ledger: ${deltaRows.length} delta rows + ${flatRows.length} pre-0.9.0 rows; ` +\n `flat segment uses last-row trick.`;\n\n return {\n totals,\n format: 'mixed',\n pre_v2_caveat: caveat,\n };\n}\n"],"mappings":"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","names":["totals"]}