From 65b09170af43c24cd56bd379449db11f4c9181f7 Mon Sep 17 00:00:00 2001 From: Dave Date: Sat, 19 Sep 2026 10:14:51 +0200 Subject: [PATCH] feat(commits): add source and manual_minutes fields Co-Authored-By: Claude Haiku 4.5 --- scripts/setup-remote.ts | 55 +++++++++++++++++++++++++++++++++++++++++ src/lib/pb/types.ts | 5 ++++ 2 files changed, 60 insertions(+) diff --git a/scripts/setup-remote.ts b/scripts/setup-remote.ts index 22fc7b8..55f6da9 100644 --- a/scripts/setup-remote.ts +++ b/scripts/setup-remote.ts @@ -223,6 +223,58 @@ async function ensureCommitSummaryFields(pb: PocketBase): Promise { } } +/** + * Add the `source` and `manual_minutes` fields to commits, for Phase 7's + * logged work entries. + * + * Backfilled to "git" for every existing row — every commit ingested before + * this phase came from the repository, by definition. + * + * Re-runs are safe: fields are not duplicated, and backfill only touches rows + * where source is still unset. + */ +async function ensureManualEntryFields(pb: PocketBase): Promise { + const commits = await pb.collections.getOne("commits"); + const have = new Set(commits.fields.map((f: { name: string }) => f.name)); + + const additions = [ + { + name: "source", + type: "select", + required: false, + maxSelect: 1, + values: ["git", "manual"], + }, + { name: "manual_minutes", type: "number", required: false, onlyInt: true }, + ]; + + const fields = [...(commits.fields as unknown[])]; + let changed = false; + + for (const field of additions) { + if (!have.has(field.name)) { + fields.push({ ...field, presentable: false, hidden: false, system: false }); + changed = true; + } + } + + if (changed) { + await pb.collections.update(commits.id, { fields }); + console.log(" + commits fields: source, manual_minutes"); + } else { + console.log(" = manual entry fields already present"); + } + + const unset = await pb.collection("commits").getFullList({ + filter: 'source = ""', + fields: "id", + }); + for (const commit of unset) { + await pb.collection("commits").update(commit.id, { source: "git" }); + } + console.log(` = commit source backfilled (${unset.length} row(s))`); +} + /** * Add the `narrative`, `narrative_version`, `narrative_shas` fields to cards. * @@ -946,6 +998,9 @@ async function main(): Promise { // Add the per-commit summary fields used by Phase 3b's AI one-liner job. await ensureCommitSummaryFields(pb); + // Add the source/manual_minutes fields for Phase 7's logged work entries. + await ensureManualEntryFields(pb); + // Add the card number field and backfill existing cards. await ensureCardNumbers(pb); diff --git a/src/lib/pb/types.ts b/src/lib/pb/types.ts index b49d09b..f1c9dce 100644 --- a/src/lib/pb/types.ts +++ b/src/lib/pb/types.ts @@ -110,6 +110,7 @@ export interface CommentRecord extends BaseRecord { } export type CommitLinkSource = "explicit" | "scope" | "manual" | "none"; +export type CommitSource = "git" | "manual"; export interface ProjectMetricsRecord extends BaseRecord { project: string; @@ -142,6 +143,10 @@ export interface CommitRecord extends BaseRecord { summary_version: number; /** Consecutive failed attempts. Capped so a poison message stops retrying. */ summary_attempts: number; + /** "git" for every ingested commit, "manual" for a logged entry. */ + source: CommitSource; + /** Only meaningful when source is "manual" — the owner's declared minutes. */ + manual_minutes: number; } export interface ActivityRecord extends Omit {