gsd-pi 2.45.0-dev.fdcf73c → 2.46.0-dev.cc9d310
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.
- package/dist/resources/extensions/gsd/auto/phases.js +14 -35
- package/dist/resources/extensions/gsd/auto/session.js +0 -11
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
- package/dist/resources/extensions/gsd/auto-start.js +2 -3
- package/dist/resources/extensions/gsd/auto.js +8 -52
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
- package/dist/resources/extensions/gsd/commands/context.js +0 -4
- package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
- package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
- package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
- package/dist/resources/extensions/gsd/doctor-checks.js +166 -1
- package/dist/resources/extensions/gsd/doctor.js +3 -1
- package/dist/resources/extensions/gsd/gsd-db.js +11 -2
- package/dist/resources/extensions/gsd/guided-flow.js +1 -2
- package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
- package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/dist/resources/extensions/gsd/prompts/rethink.md +7 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/session-lock.js +1 -3
- package/dist/resources/extensions/gsd/state.js +7 -0
- package/dist/resources/extensions/gsd/sync-lock.js +89 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +58 -12
- package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
- package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
- package/dist/resources/extensions/gsd/tools/plan-slice.js +30 -1
- package/dist/resources/extensions/gsd/tools/plan-task.js +27 -1
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +32 -2
- package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
- package/dist/resources/extensions/gsd/workflow-events.js +102 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +56 -1
- package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
- package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
- package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
- package/dist/resources/extensions/gsd/write-intercept.js +84 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
- package/src/resources/extensions/gsd/auto/phases.ts +11 -35
- package/src/resources/extensions/gsd/auto/session.ts +0 -18
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
- package/src/resources/extensions/gsd/auto-start.ts +1 -3
- package/src/resources/extensions/gsd/auto.ts +4 -80
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
- package/src/resources/extensions/gsd/commands/context.ts +0 -5
- package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
- package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
- package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
- package/src/resources/extensions/gsd/doctor-checks.ts +179 -1
- package/src/resources/extensions/gsd/doctor-types.ts +7 -1
- package/src/resources/extensions/gsd/doctor.ts +4 -1
- package/src/resources/extensions/gsd/gsd-db.ts +11 -2
- package/src/resources/extensions/gsd/guided-flow.ts +1 -2
- package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/src/resources/extensions/gsd/prompts/queue.md +2 -2
- package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/src/resources/extensions/gsd/prompts/rethink.md +7 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/session-lock.ts +0 -4
- package/src/resources/extensions/gsd/state.ts +8 -0
- package/src/resources/extensions/gsd/sync-lock.ts +94 -0
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
- package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
- package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
- package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +15 -14
- package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +70 -13
- package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
- package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +38 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +35 -1
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +38 -1
- package/src/resources/extensions/gsd/types.ts +8 -0
- package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
- package/src/resources/extensions/gsd/workflow-events.ts +154 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +51 -1
- package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
- package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
- package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
- package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
- package/src/resources/extensions/gsd/write-intercept.ts +90 -0
- /package/dist/web/standalone/.next/static/{zWYDSwB-terOjfhmWzqk1 → ZIDqryyYDroh_8AnaAOSG}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{zWYDSwB-terOjfhmWzqk1 → ZIDqryyYDroh_8AnaAOSG}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
// GSD Extension — Projection Renderers (DB -> Markdown)
|
|
2
|
+
// Renders PLAN.md, ROADMAP.md, SUMMARY.md, and STATE.md from database rows.
|
|
3
|
+
// Projections are read-only views of engine state (Layer 3 of the architecture).
|
|
4
|
+
import { _getAdapter, isDbAvailable, getMilestone, getMilestoneSlices, getSliceTasks, } from "./gsd-db.js";
|
|
5
|
+
import { atomicWriteSync } from "./atomic-write.js";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { mkdirSync, existsSync } from "node:fs";
|
|
8
|
+
import { logWarning } from "./workflow-logger.js";
|
|
9
|
+
import { deriveState } from "./state.js";
|
|
10
|
+
// ─── PLAN.md Projection ──────────────────────────────────────────────────
|
|
11
|
+
/**
|
|
12
|
+
* Render PLAN.md content from a slice row and its task rows.
|
|
13
|
+
* Pure function — no side effects.
|
|
14
|
+
*/
|
|
15
|
+
export function renderPlanContent(sliceRow, taskRows) {
|
|
16
|
+
const lines = [];
|
|
17
|
+
lines.push(`# ${sliceRow.id}: ${sliceRow.title}`);
|
|
18
|
+
lines.push("");
|
|
19
|
+
lines.push(`**Goal:** ${sliceRow.goal || sliceRow.full_summary_md || "TBD"}`);
|
|
20
|
+
lines.push(`**Demo:** After this: ${sliceRow.demo || sliceRow.full_uat_md || "TBD"}`);
|
|
21
|
+
lines.push("");
|
|
22
|
+
lines.push("## Tasks");
|
|
23
|
+
for (const task of taskRows) {
|
|
24
|
+
const checkbox = task.status === "done" || task.status === "complete" ? "[x]" : "[ ]";
|
|
25
|
+
lines.push(`- ${checkbox} **${task.id}: ${task.title}** \u2014 ${task.description}`);
|
|
26
|
+
// Estimate subline (always present if non-empty)
|
|
27
|
+
if (task.estimate) {
|
|
28
|
+
lines.push(` - Estimate: ${task.estimate}`);
|
|
29
|
+
}
|
|
30
|
+
// Files subline (only if non-empty array)
|
|
31
|
+
if (task.files && task.files.length > 0) {
|
|
32
|
+
lines.push(` - Files: ${task.files.join(", ")}`);
|
|
33
|
+
}
|
|
34
|
+
// Verify subline (only if non-null)
|
|
35
|
+
if (task.verify) {
|
|
36
|
+
lines.push(` - Verify: ${task.verify}`);
|
|
37
|
+
}
|
|
38
|
+
// Duration subline (only if recorded)
|
|
39
|
+
if (task.duration) {
|
|
40
|
+
lines.push(` - Duration: ${task.duration}`);
|
|
41
|
+
}
|
|
42
|
+
// Blocker subline (if discovered)
|
|
43
|
+
if (task.blocker_discovered && task.known_issues) {
|
|
44
|
+
lines.push(` - Blocker: ${task.known_issues}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
lines.push("");
|
|
48
|
+
return lines.join("\n");
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Render PLAN.md projection to disk for a specific slice.
|
|
52
|
+
* Queries DB via helper functions, renders content, writes via atomicWriteSync.
|
|
53
|
+
*/
|
|
54
|
+
export function renderPlanProjection(basePath, milestoneId, sliceId) {
|
|
55
|
+
const sliceRows = getMilestoneSlices(milestoneId);
|
|
56
|
+
const sliceRow = sliceRows.find(s => s.id === sliceId);
|
|
57
|
+
if (!sliceRow)
|
|
58
|
+
return;
|
|
59
|
+
const taskRows = getSliceTasks(milestoneId, sliceId);
|
|
60
|
+
const content = renderPlanContent(sliceRow, taskRows);
|
|
61
|
+
const dir = join(basePath, ".gsd", "milestones", milestoneId, "slices", sliceId);
|
|
62
|
+
mkdirSync(dir, { recursive: true });
|
|
63
|
+
atomicWriteSync(join(dir, `${sliceId}-PLAN.md`), content);
|
|
64
|
+
}
|
|
65
|
+
// ─── ROADMAP.md Projection ───────────────────────────────────────────────
|
|
66
|
+
/**
|
|
67
|
+
* Render ROADMAP.md content from a milestone row and its slice rows.
|
|
68
|
+
* Pure function — no side effects.
|
|
69
|
+
*/
|
|
70
|
+
export function renderRoadmapContent(milestoneRow, sliceRows) {
|
|
71
|
+
const lines = [];
|
|
72
|
+
lines.push(`# ${milestoneRow.id}: ${milestoneRow.title}`);
|
|
73
|
+
lines.push("");
|
|
74
|
+
lines.push("## Vision");
|
|
75
|
+
lines.push(milestoneRow.vision || milestoneRow.title || "TBD");
|
|
76
|
+
lines.push("");
|
|
77
|
+
lines.push("## Slice Overview");
|
|
78
|
+
lines.push("| ID | Slice | Risk | Depends | Done | After this |");
|
|
79
|
+
lines.push("|----|-------|------|---------|------|------------|");
|
|
80
|
+
for (const slice of sliceRows) {
|
|
81
|
+
const done = slice.status === "done" || slice.status === "complete" ? "\u2705" : "\u2B1C";
|
|
82
|
+
// depends is already parsed to string[] by rowToSlice
|
|
83
|
+
let depends = "\u2014";
|
|
84
|
+
if (slice.depends && slice.depends.length > 0) {
|
|
85
|
+
depends = slice.depends.join(", ");
|
|
86
|
+
}
|
|
87
|
+
const risk = (slice.risk || "low").toLowerCase();
|
|
88
|
+
const demo = slice.demo || slice.full_uat_md || "TBD";
|
|
89
|
+
lines.push(`| ${slice.id} | ${slice.title} | ${risk} | ${depends} | ${done} | ${demo} |`);
|
|
90
|
+
}
|
|
91
|
+
lines.push("");
|
|
92
|
+
return lines.join("\n");
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Render ROADMAP.md projection to disk for a specific milestone.
|
|
96
|
+
* Queries DB via helper functions, renders content, writes via atomicWriteSync.
|
|
97
|
+
*/
|
|
98
|
+
export function renderRoadmapProjection(basePath, milestoneId) {
|
|
99
|
+
const milestoneRow = getMilestone(milestoneId);
|
|
100
|
+
if (!milestoneRow)
|
|
101
|
+
return;
|
|
102
|
+
const sliceRows = getMilestoneSlices(milestoneId);
|
|
103
|
+
const content = renderRoadmapContent(milestoneRow, sliceRows);
|
|
104
|
+
const dir = join(basePath, ".gsd", "milestones", milestoneId);
|
|
105
|
+
mkdirSync(dir, { recursive: true });
|
|
106
|
+
atomicWriteSync(join(dir, `${milestoneId}-ROADMAP.md`), content);
|
|
107
|
+
}
|
|
108
|
+
// ─── SUMMARY.md Projection ──────────────────────────────────────────────
|
|
109
|
+
/**
|
|
110
|
+
* Render SUMMARY.md content from a task row.
|
|
111
|
+
* Pure function — no side effects.
|
|
112
|
+
*/
|
|
113
|
+
export function renderSummaryContent(taskRow, sliceId, milestoneId) {
|
|
114
|
+
const lines = [];
|
|
115
|
+
// Frontmatter
|
|
116
|
+
lines.push("---");
|
|
117
|
+
lines.push(`id: ${taskRow.id}`);
|
|
118
|
+
lines.push(`parent: ${sliceId}`);
|
|
119
|
+
lines.push(`milestone: ${milestoneId}`);
|
|
120
|
+
lines.push("provides: []");
|
|
121
|
+
lines.push("requires: []");
|
|
122
|
+
lines.push("affects: []");
|
|
123
|
+
// key_files is already parsed to string[]
|
|
124
|
+
if (taskRow.key_files && taskRow.key_files.length > 0) {
|
|
125
|
+
lines.push(`key_files: [${taskRow.key_files.map(f => `"${f}"`).join(", ")}]`);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
lines.push("key_files: []");
|
|
129
|
+
}
|
|
130
|
+
// key_decisions is already parsed to string[]
|
|
131
|
+
if (taskRow.key_decisions && taskRow.key_decisions.length > 0) {
|
|
132
|
+
lines.push(`key_decisions: [${taskRow.key_decisions.map(d => `"${d}"`).join(", ")}]`);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
lines.push("key_decisions: []");
|
|
136
|
+
}
|
|
137
|
+
lines.push("patterns_established: []");
|
|
138
|
+
lines.push("drill_down_paths: []");
|
|
139
|
+
lines.push("observability_surfaces: []");
|
|
140
|
+
lines.push(`duration: "${taskRow.duration || ""}"`);
|
|
141
|
+
lines.push(`verification_result: "${taskRow.verification_result || ""}"`);
|
|
142
|
+
lines.push(`completed_at: ${taskRow.completed_at || ""}`);
|
|
143
|
+
lines.push(`blocker_discovered: ${taskRow.blocker_discovered ? "true" : "false"}`);
|
|
144
|
+
lines.push("---");
|
|
145
|
+
lines.push("");
|
|
146
|
+
lines.push(`# ${taskRow.id}: ${taskRow.title}`);
|
|
147
|
+
lines.push("");
|
|
148
|
+
// One-liner (if present)
|
|
149
|
+
if (taskRow.one_liner) {
|
|
150
|
+
lines.push(`> ${taskRow.one_liner}`);
|
|
151
|
+
lines.push("");
|
|
152
|
+
}
|
|
153
|
+
lines.push("## What Happened");
|
|
154
|
+
lines.push(taskRow.full_summary_md || taskRow.narrative || "No summary recorded.");
|
|
155
|
+
lines.push("");
|
|
156
|
+
// Deviations (if present)
|
|
157
|
+
if (taskRow.deviations) {
|
|
158
|
+
lines.push("## Deviations");
|
|
159
|
+
lines.push(taskRow.deviations);
|
|
160
|
+
lines.push("");
|
|
161
|
+
}
|
|
162
|
+
// Known issues (if present)
|
|
163
|
+
if (taskRow.known_issues) {
|
|
164
|
+
lines.push("## Known Issues");
|
|
165
|
+
lines.push(taskRow.known_issues);
|
|
166
|
+
lines.push("");
|
|
167
|
+
}
|
|
168
|
+
return lines.join("\n");
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Render SUMMARY.md projection to disk for a specific task.
|
|
172
|
+
* Queries DB via helper functions, renders content, writes via atomicWriteSync.
|
|
173
|
+
*/
|
|
174
|
+
export function renderSummaryProjection(basePath, milestoneId, sliceId, taskId) {
|
|
175
|
+
const taskRows = getSliceTasks(milestoneId, sliceId);
|
|
176
|
+
const taskRow = taskRows.find(t => t.id === taskId);
|
|
177
|
+
if (!taskRow)
|
|
178
|
+
return;
|
|
179
|
+
const content = renderSummaryContent(taskRow, sliceId, milestoneId);
|
|
180
|
+
const dir = join(basePath, ".gsd", "milestones", milestoneId, "slices", sliceId, "tasks");
|
|
181
|
+
mkdirSync(dir, { recursive: true });
|
|
182
|
+
atomicWriteSync(join(dir, `${taskId}-SUMMARY.md`), content);
|
|
183
|
+
}
|
|
184
|
+
// ─── STATE.md Projection ────────────────────────────────────────────────
|
|
185
|
+
/**
|
|
186
|
+
* Render STATE.md content from GSDState.
|
|
187
|
+
* Matches the buildStateMarkdown output format from doctor.ts exactly.
|
|
188
|
+
* Pure function — no side effects.
|
|
189
|
+
*/
|
|
190
|
+
export function renderStateContent(state) {
|
|
191
|
+
const lines = [];
|
|
192
|
+
lines.push("# GSD State", "");
|
|
193
|
+
const activeMilestone = state.activeMilestone
|
|
194
|
+
? `${state.activeMilestone.id}: ${state.activeMilestone.title}`
|
|
195
|
+
: "None";
|
|
196
|
+
const activeSlice = state.activeSlice
|
|
197
|
+
? `${state.activeSlice.id}: ${state.activeSlice.title}`
|
|
198
|
+
: "None";
|
|
199
|
+
lines.push(`**Active Milestone:** ${activeMilestone}`);
|
|
200
|
+
lines.push(`**Active Slice:** ${activeSlice}`);
|
|
201
|
+
lines.push(`**Phase:** ${state.phase}`);
|
|
202
|
+
if (state.requirements) {
|
|
203
|
+
lines.push(`**Requirements Status:** ${state.requirements.active} active \u00b7 ${state.requirements.validated} validated \u00b7 ${state.requirements.deferred} deferred \u00b7 ${state.requirements.outOfScope} out of scope`);
|
|
204
|
+
}
|
|
205
|
+
lines.push("");
|
|
206
|
+
lines.push("## Milestone Registry");
|
|
207
|
+
for (const entry of state.registry) {
|
|
208
|
+
const glyph = entry.status === "complete" ? "\u2705" : entry.status === "active" ? "\uD83D\uDD04" : entry.status === "parked" ? "\u23F8\uFE0F" : "\u2B1C";
|
|
209
|
+
lines.push(`- ${glyph} **${entry.id}:** ${entry.title}`);
|
|
210
|
+
}
|
|
211
|
+
lines.push("");
|
|
212
|
+
lines.push("## Recent Decisions");
|
|
213
|
+
if (state.recentDecisions.length > 0) {
|
|
214
|
+
for (const decision of state.recentDecisions)
|
|
215
|
+
lines.push(`- ${decision}`);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
lines.push("- None recorded");
|
|
219
|
+
}
|
|
220
|
+
lines.push("");
|
|
221
|
+
lines.push("## Blockers");
|
|
222
|
+
if (state.blockers.length > 0) {
|
|
223
|
+
for (const blocker of state.blockers)
|
|
224
|
+
lines.push(`- ${blocker}`);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
lines.push("- None");
|
|
228
|
+
}
|
|
229
|
+
lines.push("");
|
|
230
|
+
lines.push("## Next Action");
|
|
231
|
+
lines.push(state.nextAction || "None");
|
|
232
|
+
lines.push("");
|
|
233
|
+
return lines.join("\n");
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Render STATE.md projection to disk.
|
|
237
|
+
* Derives state from DB, renders content, writes via atomicWriteSync.
|
|
238
|
+
*/
|
|
239
|
+
export async function renderStateProjection(basePath) {
|
|
240
|
+
try {
|
|
241
|
+
if (!isDbAvailable())
|
|
242
|
+
return;
|
|
243
|
+
// Probe DB handle — adapter may be set but underlying handle closed
|
|
244
|
+
const adapter = _getAdapter();
|
|
245
|
+
if (!adapter)
|
|
246
|
+
return;
|
|
247
|
+
try {
|
|
248
|
+
adapter.prepare("SELECT 1").get();
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const state = await deriveState(basePath);
|
|
254
|
+
const content = renderStateContent(state);
|
|
255
|
+
const dir = join(basePath, ".gsd");
|
|
256
|
+
mkdirSync(dir, { recursive: true });
|
|
257
|
+
atomicWriteSync(join(dir, "STATE.md"), content);
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
logWarning("projection", `renderStateProjection failed: ${err.message}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// ─── renderAllProjections ───────────────────────────────────────────────
|
|
264
|
+
/**
|
|
265
|
+
* Regenerate all projection files for a milestone from DB state.
|
|
266
|
+
* All calls are wrapped in try/catch — projection failure is non-fatal per D-02.
|
|
267
|
+
*/
|
|
268
|
+
export async function renderAllProjections(basePath, milestoneId) {
|
|
269
|
+
// Render ROADMAP.md for the milestone
|
|
270
|
+
try {
|
|
271
|
+
renderRoadmapProjection(basePath, milestoneId);
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
logWarning("projection", `renderRoadmapProjection failed for ${milestoneId}: ${err.message}`);
|
|
275
|
+
}
|
|
276
|
+
// Query all slices for this milestone
|
|
277
|
+
const sliceRows = getMilestoneSlices(milestoneId);
|
|
278
|
+
for (const slice of sliceRows) {
|
|
279
|
+
// Render PLAN.md for each slice
|
|
280
|
+
try {
|
|
281
|
+
renderPlanProjection(basePath, milestoneId, slice.id);
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
logWarning("projection", `renderPlanProjection failed for ${milestoneId}/${slice.id}: ${err.message}`);
|
|
285
|
+
}
|
|
286
|
+
// Render SUMMARY.md for each completed task
|
|
287
|
+
const taskRows = getSliceTasks(milestoneId, slice.id);
|
|
288
|
+
const doneTasks = taskRows.filter(t => t.status === "done" || t.status === "complete");
|
|
289
|
+
for (const task of doneTasks) {
|
|
290
|
+
try {
|
|
291
|
+
renderSummaryProjection(basePath, milestoneId, slice.id, task.id);
|
|
292
|
+
}
|
|
293
|
+
catch (err) {
|
|
294
|
+
logWarning("projection", `renderSummaryProjection failed for ${milestoneId}/${slice.id}/${task.id}: ${err.message}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// Render STATE.md
|
|
299
|
+
try {
|
|
300
|
+
await renderStateProjection(basePath);
|
|
301
|
+
}
|
|
302
|
+
catch (err) {
|
|
303
|
+
logWarning("projection", `renderStateProjection failed: ${err.message}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// ─── regenerateIfMissing ────────────────────────────────────────────────
|
|
307
|
+
/**
|
|
308
|
+
* Check if a projection file exists on disk. If missing, regenerate it from DB.
|
|
309
|
+
* Returns true if the file was regenerated, false if it already existed.
|
|
310
|
+
* Satisfies PROJ-05 (corrupted/deleted projections regenerate on demand).
|
|
311
|
+
*/
|
|
312
|
+
export function regenerateIfMissing(basePath, milestoneId, sliceId, fileType) {
|
|
313
|
+
let filePath;
|
|
314
|
+
switch (fileType) {
|
|
315
|
+
case "PLAN":
|
|
316
|
+
filePath = join(basePath, ".gsd", "milestones", milestoneId, "slices", sliceId, `${sliceId}-PLAN.md`);
|
|
317
|
+
break;
|
|
318
|
+
case "ROADMAP":
|
|
319
|
+
filePath = join(basePath, ".gsd", "milestones", milestoneId, `${milestoneId}-ROADMAP.md`);
|
|
320
|
+
break;
|
|
321
|
+
case "SUMMARY":
|
|
322
|
+
// For SUMMARY, we regenerate all task summaries in the slice
|
|
323
|
+
filePath = join(basePath, ".gsd", "milestones", milestoneId, "slices", sliceId, "tasks");
|
|
324
|
+
break;
|
|
325
|
+
case "STATE":
|
|
326
|
+
filePath = join(basePath, ".gsd", "STATE.md");
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
if (fileType === "SUMMARY") {
|
|
330
|
+
// Check each completed task's SUMMARY file individually (not just the directory)
|
|
331
|
+
const taskRows = getSliceTasks(milestoneId, sliceId);
|
|
332
|
+
const doneTasks = taskRows.filter(t => t.status === "done" || t.status === "complete");
|
|
333
|
+
let regenerated = 0;
|
|
334
|
+
for (const task of doneTasks) {
|
|
335
|
+
const summaryPath = join(basePath, ".gsd", "milestones", milestoneId, "slices", sliceId, "tasks", `${task.id}-SUMMARY.md`);
|
|
336
|
+
if (!existsSync(summaryPath)) {
|
|
337
|
+
try {
|
|
338
|
+
renderSummaryProjection(basePath, milestoneId, sliceId, task.id);
|
|
339
|
+
regenerated++;
|
|
340
|
+
}
|
|
341
|
+
catch (err) {
|
|
342
|
+
console.error(`[projections] regenerateIfMissing SUMMARY failed for ${task.id}:`, err);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return regenerated > 0;
|
|
347
|
+
}
|
|
348
|
+
if (existsSync(filePath)) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
// Regenerate the missing file
|
|
352
|
+
try {
|
|
353
|
+
switch (fileType) {
|
|
354
|
+
case "PLAN":
|
|
355
|
+
renderPlanProjection(basePath, milestoneId, sliceId);
|
|
356
|
+
break;
|
|
357
|
+
case "ROADMAP":
|
|
358
|
+
renderRoadmapProjection(basePath, milestoneId);
|
|
359
|
+
break;
|
|
360
|
+
case "STATE":
|
|
361
|
+
// renderStateProjection is async — fire-and-forget.
|
|
362
|
+
// Return false since the file isn't written yet; it will appear
|
|
363
|
+
// on the next post-mutation hook cycle.
|
|
364
|
+
void renderStateProjection(basePath);
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
console.error(`[projections] regenerateIfMissing ${fileType} failed:`, err);
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
}
|