Files
fusion/packages/core/src/task-lineage.ts
gsxdsm 47e7b4a150 FN-6704: capture merge LOC for productivity analytics
Capture merge-time diff stats so Command Center Productivity can report real Lines changed when available.

- Add nullable additions/deletions columns to task commit associations with migration and storage normalization.
- Persist git shortstat counts from merge association paths without blocking merges when stats are unavailable.
- Aggregate Productivity LOC from recorded commit stats while preserving the unavailable sentinel for historical unknowns.
- Cover migration, store upsert, analytics, and merger association stats behavior with tests and documentation.

Files changed:
 .changeset/fn-6704-command-center-loc.md           |  5 ++
 docs/architecture.md                               |  4 +-
 docs/storage.md                                    |  3 +
 packages/core/src/__tests__/db-migrate.test.ts     | 32 ++++----
 packages/core/src/__tests__/db.test.ts             | 90 ++++++++++++++++------
 .../src/__tests__/productivity-analytics.test.ts   | 47 +++++++++--
 packages/core/src/__tests__/store-upsert.test.ts   | 40 ++++++++++
 packages/core/src/db.ts                            | 14 +++-
 packages/core/src/productivity-analytics.ts        | 56 +++++++++-----
 packages/core/src/store.ts                         | 17 +++-
 packages/core/src/task-lineage.ts                  |  2 +
 packages/core/src/types.ts                         |  2 +
 .../merger-commit-association-stats.test.ts        | 90 ++++++++++++++++++++++
 packages/engine/src/merger-ai.ts                   |  2 +
 packages/engine/src/merger.ts                      | 17 +++-
 15 files changed, 349 insertions(+), 72 deletions(-)

Fusion-Task-Id: FN-6704

Fusion-Task-Lineage: f079fecb-eade-4318-bc55-23aa48097b4a
2026-06-19 06:51:57 -07:00

50 lines
1.5 KiB
TypeScript

import { randomUUID } from "node:crypto";
import type {
TaskCommitAssociation,
TaskCommitAssociationConfidence,
TaskCommitAssociationMatchSource,
} from "./types.js";
export const FUSION_TASK_LINEAGE_TRAILER_KEY = "Fusion-Task-Lineage";
export function generateTaskLineageId(): string {
return randomUUID();
}
export function buildTaskLineageTrailer(lineageId: string): string {
return `${FUSION_TASK_LINEAGE_TRAILER_KEY}: ${lineageId}`;
}
export function parseTaskLineageTrailer(message: string): string | undefined {
const lines = message.split(/\r?\n/);
const prefix = `${FUSION_TASK_LINEAGE_TRAILER_KEY}:`;
for (let i = lines.length - 1; i >= 0; i -= 1) {
const line = lines[i]?.trim();
if (line?.startsWith(prefix)) {
const value = line.slice(prefix.length).trim();
if (value) return value;
}
}
return undefined;
}
export function classifyTaskCommitAssociationConfidence(
matchedBy: TaskCommitAssociationMatchSource,
): TaskCommitAssociationConfidence {
if (matchedBy === "canonical-lineage-trailer") return "canonical";
if (matchedBy === "manual-reconciliation") return "ambiguous";
return "legacy";
}
export function normalizeTaskCommitAssociation(
row: TaskCommitAssociation,
): TaskCommitAssociation {
return {
...row,
note: row.note?.trim() || undefined,
additions: row.additions ?? undefined,
deletions: row.deletions ?? undefined,
confidence: row.confidence ?? classifyTaskCommitAssociationConfidence(row.matchedBy),
};
}