gsd-pi 2.79.0 → 2.80.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.
- package/README.md +94 -47
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/contracts.js +1 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +146 -0
- package/dist/resources/extensions/gsd/auto/phases.js +61 -7
- package/dist/resources/extensions/gsd/auto/session.js +8 -0
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +2 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +2 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +52 -29
- package/dist/resources/extensions/gsd/auto-recovery.js +63 -55
- package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -0
- package/dist/resources/extensions/gsd/auto-start.js +3 -2
- package/dist/resources/extensions/gsd/auto.js +159 -2
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +41 -45
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +8 -8
- package/dist/resources/extensions/gsd/commands/context.js +1 -1
- package/dist/resources/extensions/gsd/gsd-db.js +34 -1
- package/dist/resources/extensions/gsd/guided-flow.js +40 -0
- package/dist/resources/extensions/gsd/paths.js +5 -1
- package/dist/resources/extensions/gsd/post-execution-checks.js +25 -6
- package/dist/resources/extensions/gsd/preferences-types.js +20 -2
- package/dist/resources/extensions/gsd/preferences-validation.js +3 -3
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +82 -2
- package/dist/resources/extensions/gsd/unit-context-composer.js +32 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +21 -0
- package/dist/resources/extensions/gsd/uok/audit.js +23 -9
- package/dist/resources/extensions/gsd/uok/contracts.js +69 -1
- package/dist/resources/extensions/gsd/uok/dispatch-envelope.js +3 -0
- package/dist/resources/extensions/gsd/uok/loop-adapter.js +48 -33
- package/dist/resources/extensions/gsd/uok/timeline.js +125 -0
- package/dist/resources/extensions/shared/gsd-phase-state.js +45 -3
- package/dist/resources/extensions/shared/interview-ui.js +15 -4
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- 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 +9 -9
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/workflow-tools.d.ts +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +53 -0
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/workflow-tools.test.ts +129 -2
- package/packages/mcp-server/src/workflow-tools.ts +81 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/contracts.ts +87 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +10 -3
- package/src/resources/extensions/gsd/auto/orchestrator.ts +161 -0
- package/src/resources/extensions/gsd/auto/phases.ts +88 -9
- package/src/resources/extensions/gsd/auto/session.ts +11 -0
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +2 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +1 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +106 -28
- package/src/resources/extensions/gsd/auto-recovery.ts +59 -53
- package/src/resources/extensions/gsd/auto-runtime-state.ts +7 -0
- package/src/resources/extensions/gsd/auto-start.ts +3 -2
- package/src/resources/extensions/gsd/auto.ts +167 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +14 -1
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +49 -46
- package/src/resources/extensions/gsd/bootstrap/tests/write-gate-shouldblock-basepath.test.ts +97 -0
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +8 -4
- package/src/resources/extensions/gsd/commands/context.ts +1 -1
- package/src/resources/extensions/gsd/gsd-db.ts +35 -1
- package/src/resources/extensions/gsd/guided-flow.ts +47 -0
- package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
- package/src/resources/extensions/gsd/paths.ts +6 -1
- package/src/resources/extensions/gsd/post-execution-checks.ts +31 -6
- package/src/resources/extensions/gsd/preferences-types.ts +23 -4
- package/src/resources/extensions/gsd/preferences-validation.ts +3 -3
- package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +353 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +108 -1
- package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/check-auto-start-pending-gate.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +148 -0
- package/src/resources/extensions/gsd/tests/current-directory-root-homedir-fallback.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +63 -2
- package/src/resources/extensions/gsd/tests/execute-summary-save-empty-project.test.ts +109 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +134 -0
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/paused-session-via-db.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +109 -1
- package/src/resources/extensions/gsd/tests/uok-loop-adapter-writer.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +132 -3
- package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +3 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +84 -1
- package/src/resources/extensions/gsd/unit-context-composer.ts +49 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +34 -0
- package/src/resources/extensions/gsd/uok/audit.ts +25 -9
- package/src/resources/extensions/gsd/uok/contracts.ts +105 -0
- package/src/resources/extensions/gsd/uok/dispatch-envelope.ts +4 -0
- package/src/resources/extensions/gsd/uok/loop-adapter.ts +60 -45
- package/src/resources/extensions/gsd/uok/timeline.ts +158 -0
- package/src/resources/extensions/shared/gsd-phase-state.ts +56 -3
- package/src/resources/extensions/shared/interview-ui.ts +18 -5
- package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +43 -1
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +41 -0
- /package/dist/web/standalone/.next/static/{J-CU-p_sp45CJHT3R9TJS → V-3Ehy4B24f9FCGiLPWIM}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{J-CU-p_sp45CJHT3R9TJS → V-3Ehy4B24f9FCGiLPWIM}/_ssgManifest.js +0 -0
|
@@ -96,12 +96,25 @@ export function resolveImportPath(importPath, sourceFile, basePath) {
|
|
|
96
96
|
if (existsSync(directPath)) {
|
|
97
97
|
return { exists: true, resolvedPath: directPath };
|
|
98
98
|
}
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
// Known concrete extensions that should NOT fall through to code-shadow
|
|
100
|
+
// probing when missing. This preserves the "missing.css must stay missing"
|
|
101
|
+
// guarantee while still allowing dotted module stems like ./route.server
|
|
102
|
+
// to resolve as ./route.server.ts.
|
|
103
|
+
const nonFallbackExtensions = new Set([
|
|
104
|
+
".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
|
|
105
|
+
".json", ".css", ".scss", ".sass", ".less", ".styl",
|
|
106
|
+
".svg", ".png", ".jpg", ".jpeg", ".gif", ".webp", ".avif", ".ico", ".bmp",
|
|
107
|
+
".woff", ".woff2", ".ttf", ".otf", ".eot",
|
|
108
|
+
]);
|
|
109
|
+
const runtimeFallbackExtensions = new Set([".js", ".jsx", ".mjs", ".cjs"]);
|
|
110
|
+
const dottedStemFallbackExtensions = new Set([".server", ".client", ".webhook"]);
|
|
111
|
+
if (explicitExt !== "" &&
|
|
112
|
+
!runtimeFallbackExtensions.has(explicitExt) &&
|
|
113
|
+
!nonFallbackExtensions.has(explicitExt) &&
|
|
114
|
+
!dottedStemFallbackExtensions.has(explicitExt)) {
|
|
115
|
+
return { exists: false, resolvedPath: null };
|
|
116
|
+
}
|
|
117
|
+
if (nonFallbackExtensions.has(explicitExt) && !runtimeFallbackExtensions.has(explicitExt)) {
|
|
105
118
|
return { exists: false, resolvedPath: null };
|
|
106
119
|
}
|
|
107
120
|
}
|
|
@@ -163,6 +176,12 @@ export function checkImportResolution(taskRow, _priorTasks, basePath) {
|
|
|
163
176
|
}
|
|
164
177
|
const imports = extractRelativeImports(source);
|
|
165
178
|
for (const { importPath, lineNum } of imports) {
|
|
179
|
+
// React Router generated +types modules may not exist on disk during
|
|
180
|
+
// post-exec checks (generated during framework build). Don't block task
|
|
181
|
+
// completion on these imports.
|
|
182
|
+
if (/^\.{1,2}\/\+types\//.test(importPath)) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
166
185
|
const resolution = resolveImportPath(importPath, file, basePath);
|
|
167
186
|
if (!resolution.exists) {
|
|
168
187
|
results.push({
|
|
@@ -100,8 +100,26 @@ export const KNOWN_PREFERENCE_KEYS = new Set([
|
|
|
100
100
|
"context_mode",
|
|
101
101
|
"planning_depth",
|
|
102
102
|
]);
|
|
103
|
-
/**
|
|
104
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Broad union of every recognized unit-type *label* used across the codebase.
|
|
105
|
+
*
|
|
106
|
+
* This intentionally covers more than the manifest-tracked dispatch units in
|
|
107
|
+
* `unit-context-manifest.ts:KNOWN_UNIT_TYPES`. Examples that live here but not
|
|
108
|
+
* in the manifest:
|
|
109
|
+
* - `discuss-slice` — dispatched by `guided-flow.ts` rather than auto-mode;
|
|
110
|
+
* composer falls through to default behavior via `resolveManifest()` null path.
|
|
111
|
+
* - `worktree-merge` — used as a model-routing case, prompt-template name, and
|
|
112
|
+
* commit-message label, not as an LLM-dispatched unit.
|
|
113
|
+
*
|
|
114
|
+
* Used by `preferences-validation.ts` to validate user-provided unit-type
|
|
115
|
+
* references in preferences (model overrides, skill rules, etc.) — preferences
|
|
116
|
+
* may legitimately reference any label, including non-dispatched ones.
|
|
117
|
+
*
|
|
118
|
+
* The manifest-strict subset lives in `unit-context-manifest.ts:KNOWN_UNIT_TYPES`
|
|
119
|
+
* and is enforced 1:1 against `UNIT_MANIFESTS` by the parity test in
|
|
120
|
+
* `tests/unit-context-manifest.test.ts`.
|
|
121
|
+
*/
|
|
122
|
+
export const KNOWN_UNIT_LABELS = [
|
|
105
123
|
"research-milestone", "plan-milestone", "research-slice", "plan-slice", "refine-slice",
|
|
106
124
|
"execute-task", "reactive-execute", "gate-evaluate", "complete-slice", "replan-slice", "reassess-roadmap",
|
|
107
125
|
"run-uat", "complete-milestone", "validate-milestone", "rewrite-docs",
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { VALID_BRANCH_NAME } from "./git-service.js";
|
|
9
9
|
import { normalizeStringArray } from "../shared/format-utils.js";
|
|
10
|
-
import { KNOWN_PREFERENCE_KEYS,
|
|
10
|
+
import { KNOWN_PREFERENCE_KEYS, KNOWN_UNIT_LABELS, SKILL_ACTIONS, } from "./preferences-types.js";
|
|
11
11
|
const VALID_TOKEN_PROFILES = new Set(["budget", "balanced", "quality", "burn-max"]);
|
|
12
12
|
const VALID_UOK_TURN_ACTIONS = new Set([
|
|
13
13
|
"commit",
|
|
@@ -432,7 +432,7 @@ export function validatePreferences(preferences) {
|
|
|
432
432
|
if (preferences.post_unit_hooks && Array.isArray(preferences.post_unit_hooks)) {
|
|
433
433
|
const validHooks = [];
|
|
434
434
|
const seenNames = new Set();
|
|
435
|
-
const knownUnitTypes = new Set(
|
|
435
|
+
const knownUnitTypes = new Set(KNOWN_UNIT_LABELS);
|
|
436
436
|
for (const hook of preferences.post_unit_hooks) {
|
|
437
437
|
if (!hook || typeof hook !== "object") {
|
|
438
438
|
errors.push("post_unit_hooks entry must be an object");
|
|
@@ -493,7 +493,7 @@ export function validatePreferences(preferences) {
|
|
|
493
493
|
if (preferences.pre_dispatch_hooks && Array.isArray(preferences.pre_dispatch_hooks)) {
|
|
494
494
|
const validPreHooks = [];
|
|
495
495
|
const seenPreNames = new Set();
|
|
496
|
-
const knownUnitTypes = new Set(
|
|
496
|
+
const knownUnitTypes = new Set(KNOWN_UNIT_LABELS);
|
|
497
497
|
const validActions = new Set(["modify", "skip", "replace"]);
|
|
498
498
|
for (const hook of preferences.pre_dispatch_hooks) {
|
|
499
499
|
if (!hook || typeof hook !== "object") {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
|
|
2
2
|
import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
|
|
3
3
|
import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, shouldBlockRootArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
|
|
4
|
-
import { getActiveRequirements, getMilestone, getSliceStatusSummary, getSliceTaskCounts, readTransaction, saveGateResult, } from "../gsd-db.js";
|
|
4
|
+
import { getActiveRequirements, insertMilestone, getMilestone, getSliceStatusSummary, getSliceTaskCounts, readTransaction, saveGateResult, } from "../gsd-db.js";
|
|
5
5
|
import { GATE_REGISTRY } from "../gate-registry.js";
|
|
6
6
|
import { generateRequirementsMd, saveArtifactToDb } from "../db-writer.js";
|
|
7
7
|
import { resolveMilestoneFile, resolveSliceFile } from "../paths.js";
|
|
@@ -17,6 +17,7 @@ import { handleValidateMilestone } from "./validate-milestone.js";
|
|
|
17
17
|
import { logError, logWarning } from "../workflow-logger.js";
|
|
18
18
|
import { invalidateStateCache } from "../state.js";
|
|
19
19
|
import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|
20
|
+
import { parseProject } from "../schemas/parsers.js";
|
|
20
21
|
export const SUPPORTED_SUMMARY_ARTIFACT_TYPES = [
|
|
21
22
|
"SUMMARY",
|
|
22
23
|
"RESEARCH",
|
|
@@ -37,6 +38,19 @@ function isRootSummaryArtifactType(artifactType) {
|
|
|
37
38
|
artifactType === "REQUIREMENTS" ||
|
|
38
39
|
artifactType === "REQUIREMENTS-DRAFT";
|
|
39
40
|
}
|
|
41
|
+
function registerProjectMilestoneSequence(content) {
|
|
42
|
+
const parsed = parseProject(content);
|
|
43
|
+
const registered = [];
|
|
44
|
+
for (const milestone of parsed.milestones) {
|
|
45
|
+
insertMilestone({
|
|
46
|
+
id: milestone.id,
|
|
47
|
+
title: milestone.title,
|
|
48
|
+
status: milestone.done ? "complete" : "queued",
|
|
49
|
+
});
|
|
50
|
+
registered.push(milestone.id);
|
|
51
|
+
}
|
|
52
|
+
return registered;
|
|
53
|
+
}
|
|
40
54
|
export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
41
55
|
const dbAvailable = await ensureDbOpen(basePath);
|
|
42
56
|
if (!dbAvailable) {
|
|
@@ -126,6 +140,66 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
126
140
|
slice_id: isRootArtifact ? undefined : params.slice_id,
|
|
127
141
|
task_id: isRootArtifact ? undefined : params.task_id,
|
|
128
142
|
}, basePath);
|
|
143
|
+
let registeredMilestones = [];
|
|
144
|
+
if (params.artifact_type === "PROJECT") {
|
|
145
|
+
try {
|
|
146
|
+
registeredMilestones = registerProjectMilestoneSequence(contentToSave);
|
|
147
|
+
if (registeredMilestones.length > 0)
|
|
148
|
+
invalidateStateCache();
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
152
|
+
logError("tool", `gsd_summary_save: PROJECT artifact persisted but milestone registration threw: ${msg}`, {
|
|
153
|
+
tool: "gsd_summary_save",
|
|
154
|
+
error: String(err),
|
|
155
|
+
stack: err instanceof Error ? err.stack ?? "" : "",
|
|
156
|
+
});
|
|
157
|
+
// PROJECT.md was persisted by saveArtifactToDb above; the artifacts row
|
|
158
|
+
// changed even though no milestones registered. Invalidate so subsequent
|
|
159
|
+
// /gsd reads see the persisted artifact instead of the pre-save cache.
|
|
160
|
+
invalidateStateCache();
|
|
161
|
+
return {
|
|
162
|
+
content: [{
|
|
163
|
+
type: "text",
|
|
164
|
+
text: `Error: PROJECT.md was saved to ${relativePath} but milestone registration failed: ${msg}. ` +
|
|
165
|
+
`The DB has no milestone rows for this project, so /gsd will report "No Active Milestone". ` +
|
|
166
|
+
`Re-call gsd_summary_save(PROJECT) once the underlying error is resolved — INSERT OR IGNORE makes registration idempotent.`,
|
|
167
|
+
}],
|
|
168
|
+
details: {
|
|
169
|
+
operation: "save_summary",
|
|
170
|
+
path: relativePath,
|
|
171
|
+
artifact_type: params.artifact_type,
|
|
172
|
+
error: "milestone_registration_threw",
|
|
173
|
+
registration_error: msg,
|
|
174
|
+
},
|
|
175
|
+
isError: true,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (registeredMilestones.length === 0) {
|
|
179
|
+
logError("tool", `gsd_summary_save: PROJECT.md saved to ${relativePath} but parsed zero milestones — registration produced no DB rows`, {
|
|
180
|
+
tool: "gsd_summary_save",
|
|
181
|
+
});
|
|
182
|
+
// PROJECT.md was persisted; invalidate so subsequent reads see the new
|
|
183
|
+
// artifacts row even though no milestones registered.
|
|
184
|
+
invalidateStateCache();
|
|
185
|
+
return {
|
|
186
|
+
content: [{
|
|
187
|
+
type: "text",
|
|
188
|
+
text: `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
|
|
189
|
+
`so no milestones were registered in the DB. /gsd will report "No Active Milestone". ` +
|
|
190
|
+
`Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
|
|
191
|
+
`\`- [ ] M001: <Title> — <One-liner>\` (em-dash, double-dash \`--\`, or single-dash \`-\` separator), then re-call gsd_summary_save(PROJECT).`,
|
|
192
|
+
}],
|
|
193
|
+
details: {
|
|
194
|
+
operation: "save_summary",
|
|
195
|
+
path: relativePath,
|
|
196
|
+
artifact_type: params.artifact_type,
|
|
197
|
+
error: "milestone_registration_empty_parse",
|
|
198
|
+
},
|
|
199
|
+
isError: true,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
129
203
|
if (params.artifact_type === "CONTEXT" && !params.task_id) {
|
|
130
204
|
try {
|
|
131
205
|
const draftFile = params.slice_id
|
|
@@ -140,7 +214,13 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
140
214
|
}
|
|
141
215
|
return {
|
|
142
216
|
content: [{ type: "text", text: `Saved ${params.artifact_type} artifact to ${relativePath}` }],
|
|
143
|
-
details: {
|
|
217
|
+
details: {
|
|
218
|
+
operation: "save_summary",
|
|
219
|
+
path: relativePath,
|
|
220
|
+
artifact_type: params.artifact_type,
|
|
221
|
+
content_source: contentSource,
|
|
222
|
+
...(registeredMilestones.length > 0 ? { registeredMilestones } : {}),
|
|
223
|
+
},
|
|
144
224
|
};
|
|
145
225
|
}
|
|
146
226
|
catch (err) {
|
|
@@ -71,6 +71,38 @@ export function manifestBudgetChars(unitType) {
|
|
|
71
71
|
const manifest = resolveManifest(unitType);
|
|
72
72
|
return manifest ? manifest.maxSystemPromptChars : null;
|
|
73
73
|
}
|
|
74
|
+
const CONTEXT_MODE_LANE_LABELS = {
|
|
75
|
+
interview: "interview",
|
|
76
|
+
research: "research",
|
|
77
|
+
planning: "planning",
|
|
78
|
+
execution: "execution",
|
|
79
|
+
verification: "verification",
|
|
80
|
+
orchestration: "orchestration",
|
|
81
|
+
docs: "documentation",
|
|
82
|
+
};
|
|
83
|
+
const CONTEXT_MODE_GUIDANCE = "Use `gsd_exec` for noisy scans, builds, and tests so full output stays out of prompt context; call `gsd_exec_search` before repeating prior runs; call `gsd_resume` after compaction or resume to recover stored execution context.";
|
|
84
|
+
/**
|
|
85
|
+
* Render the Context Mode instruction lane for a unit type. Unknown unit
|
|
86
|
+
* types, disabled config, and explicit `contextMode: "none"` all omit the
|
|
87
|
+
* block so callers can prefix this safely without extra branching.
|
|
88
|
+
*/
|
|
89
|
+
export function composeContextModeInstructions(unitType, opts) {
|
|
90
|
+
if (!opts.enabled)
|
|
91
|
+
return "";
|
|
92
|
+
const manifest = resolveManifest(unitType);
|
|
93
|
+
if (!manifest || manifest.contextMode === "none")
|
|
94
|
+
return "";
|
|
95
|
+
const lane = CONTEXT_MODE_LANE_LABELS[manifest.contextMode];
|
|
96
|
+
if (opts.renderMode === "nested") {
|
|
97
|
+
return `Context Mode (${lane} lane): ${CONTEXT_MODE_GUIDANCE}`;
|
|
98
|
+
}
|
|
99
|
+
return [
|
|
100
|
+
"## Context Mode",
|
|
101
|
+
"",
|
|
102
|
+
`Lane: **${lane} lane**.`,
|
|
103
|
+
CONTEXT_MODE_GUIDANCE,
|
|
104
|
+
].join("\n");
|
|
105
|
+
}
|
|
74
106
|
const SECTION_SEPARATOR = "\n\n---\n\n";
|
|
75
107
|
/**
|
|
76
108
|
* Compose all manifest-declared context for a unit type using the v2
|
|
@@ -137,6 +137,7 @@ export const UNIT_MANIFESTS = {
|
|
|
137
137
|
memory: "prompt-relevant",
|
|
138
138
|
codebaseMap: true,
|
|
139
139
|
preferences: "active-only",
|
|
140
|
+
contextMode: "research",
|
|
140
141
|
tools: TOOLS_PLANNING,
|
|
141
142
|
artifacts: {
|
|
142
143
|
// Phase 3 migration (#4782): matches today's actual
|
|
@@ -153,6 +154,7 @@ export const UNIT_MANIFESTS = {
|
|
|
153
154
|
memory: "prompt-relevant",
|
|
154
155
|
codebaseMap: true,
|
|
155
156
|
preferences: "active-only",
|
|
157
|
+
contextMode: "planning",
|
|
156
158
|
tools: TOOLS_PLANNING,
|
|
157
159
|
artifacts: {
|
|
158
160
|
inline: ["project", "requirements", "decisions", "milestone-research", "templates"],
|
|
@@ -167,6 +169,7 @@ export const UNIT_MANIFESTS = {
|
|
|
167
169
|
memory: "prompt-relevant",
|
|
168
170
|
codebaseMap: true,
|
|
169
171
|
preferences: "active-only",
|
|
172
|
+
contextMode: "interview",
|
|
170
173
|
tools: TOOLS_PLANNING,
|
|
171
174
|
artifacts: {
|
|
172
175
|
inline: ["project", "requirements", "decisions", "milestone-context", "templates"],
|
|
@@ -181,6 +184,7 @@ export const UNIT_MANIFESTS = {
|
|
|
181
184
|
memory: "prompt-relevant",
|
|
182
185
|
codebaseMap: false,
|
|
183
186
|
preferences: "active-only",
|
|
187
|
+
contextMode: "verification",
|
|
184
188
|
// planning-dispatch: validation is a verification-fan-out unit. It reads
|
|
185
189
|
// the milestone surface and dispatches reviewer/security/tester subagents
|
|
186
190
|
// to report findings without touching user source. Mirrors
|
|
@@ -199,6 +203,7 @@ export const UNIT_MANIFESTS = {
|
|
|
199
203
|
memory: "prompt-relevant",
|
|
200
204
|
codebaseMap: false,
|
|
201
205
|
preferences: "active-only",
|
|
206
|
+
contextMode: "verification",
|
|
202
207
|
// planning-dispatch: completion is a high-leverage place to fan out to
|
|
203
208
|
// reviewer / security / tester subagents. They read the diff and report
|
|
204
209
|
// findings; they do not write user source. Write isolation to .gsd/ is
|
|
@@ -221,6 +226,7 @@ export const UNIT_MANIFESTS = {
|
|
|
221
226
|
memory: "prompt-relevant",
|
|
222
227
|
codebaseMap: true,
|
|
223
228
|
preferences: "active-only",
|
|
229
|
+
contextMode: "research",
|
|
224
230
|
tools: TOOLS_PLANNING,
|
|
225
231
|
artifacts: {
|
|
226
232
|
inline: ["roadmap", "milestone-research", "dependency-summaries", "templates"],
|
|
@@ -235,6 +241,7 @@ export const UNIT_MANIFESTS = {
|
|
|
235
241
|
memory: "prompt-relevant",
|
|
236
242
|
codebaseMap: true,
|
|
237
243
|
preferences: "active-only",
|
|
244
|
+
contextMode: "planning",
|
|
238
245
|
// planning-dispatch: allows subagent dispatch so the planner can fan out
|
|
239
246
|
// to scout for codebase recon and to planner/decompose-style specialists
|
|
240
247
|
// for sub-decomposition. Write-isolation to .gsd/ is preserved.
|
|
@@ -252,6 +259,7 @@ export const UNIT_MANIFESTS = {
|
|
|
252
259
|
memory: "prompt-relevant",
|
|
253
260
|
codebaseMap: true,
|
|
254
261
|
preferences: "active-only",
|
|
262
|
+
contextMode: "planning",
|
|
255
263
|
// See plan-slice — same rationale: dispatch to scout/planner-style
|
|
256
264
|
// specialists during refinement is materially better than re-doing recon
|
|
257
265
|
// inline.
|
|
@@ -269,6 +277,7 @@ export const UNIT_MANIFESTS = {
|
|
|
269
277
|
memory: "prompt-relevant",
|
|
270
278
|
codebaseMap: true,
|
|
271
279
|
preferences: "active-only",
|
|
280
|
+
contextMode: "planning",
|
|
272
281
|
tools: TOOLS_PLANNING,
|
|
273
282
|
artifacts: {
|
|
274
283
|
inline: ["slice-plan", "slice-research", "dependency-summaries", "prior-task-summaries", "templates"],
|
|
@@ -283,6 +292,7 @@ export const UNIT_MANIFESTS = {
|
|
|
283
292
|
memory: "prompt-relevant",
|
|
284
293
|
codebaseMap: false,
|
|
285
294
|
preferences: "active-only",
|
|
295
|
+
contextMode: "verification",
|
|
286
296
|
// See complete-milestone — same rationale: dispatch to reviewer / security /
|
|
287
297
|
// tester subagents to fan out review work without bloating this unit's
|
|
288
298
|
// context.
|
|
@@ -304,6 +314,7 @@ export const UNIT_MANIFESTS = {
|
|
|
304
314
|
memory: "critical-only",
|
|
305
315
|
codebaseMap: false,
|
|
306
316
|
preferences: "none",
|
|
317
|
+
contextMode: "planning",
|
|
307
318
|
tools: TOOLS_PLANNING,
|
|
308
319
|
artifacts: {
|
|
309
320
|
// Phase 2 pilot (#4782): manifest now matches today's actual
|
|
@@ -322,6 +333,7 @@ export const UNIT_MANIFESTS = {
|
|
|
322
333
|
memory: "prompt-relevant",
|
|
323
334
|
codebaseMap: true,
|
|
324
335
|
preferences: "active-only",
|
|
336
|
+
contextMode: "execution",
|
|
325
337
|
tools: TOOLS_ALL,
|
|
326
338
|
artifacts: {
|
|
327
339
|
inline: ["task-plan", "slice-plan", "prior-task-summaries", "templates"],
|
|
@@ -336,6 +348,7 @@ export const UNIT_MANIFESTS = {
|
|
|
336
348
|
memory: "prompt-relevant",
|
|
337
349
|
codebaseMap: true,
|
|
338
350
|
preferences: "active-only",
|
|
351
|
+
contextMode: "execution",
|
|
339
352
|
tools: TOOLS_ALL,
|
|
340
353
|
artifacts: {
|
|
341
354
|
inline: ["slice-plan", "prior-task-summaries", "templates"],
|
|
@@ -351,6 +364,7 @@ export const UNIT_MANIFESTS = {
|
|
|
351
364
|
memory: "critical-only",
|
|
352
365
|
codebaseMap: false,
|
|
353
366
|
preferences: "active-only",
|
|
367
|
+
contextMode: "verification",
|
|
354
368
|
tools: TOOLS_PLANNING,
|
|
355
369
|
artifacts: {
|
|
356
370
|
// Phase 3 migration (#4782): manifest matches today's actual
|
|
@@ -369,6 +383,7 @@ export const UNIT_MANIFESTS = {
|
|
|
369
383
|
memory: "critical-only",
|
|
370
384
|
codebaseMap: false,
|
|
371
385
|
preferences: "active-only",
|
|
386
|
+
contextMode: "verification",
|
|
372
387
|
tools: TOOLS_PLANNING,
|
|
373
388
|
artifacts: {
|
|
374
389
|
inline: ["slice-plan", "prior-task-summaries"],
|
|
@@ -383,6 +398,7 @@ export const UNIT_MANIFESTS = {
|
|
|
383
398
|
memory: "prompt-relevant",
|
|
384
399
|
codebaseMap: true,
|
|
385
400
|
preferences: "active-only",
|
|
401
|
+
contextMode: "docs",
|
|
386
402
|
tools: TOOLS_DOCS,
|
|
387
403
|
artifacts: {
|
|
388
404
|
inline: ["project", "requirements", "decisions", "templates"],
|
|
@@ -401,6 +417,7 @@ export const UNIT_MANIFESTS = {
|
|
|
401
417
|
memory: "none",
|
|
402
418
|
codebaseMap: false,
|
|
403
419
|
preferences: "none",
|
|
420
|
+
contextMode: "none",
|
|
404
421
|
tools: TOOLS_PLANNING,
|
|
405
422
|
artifacts: {
|
|
406
423
|
inline: [],
|
|
@@ -418,6 +435,7 @@ export const UNIT_MANIFESTS = {
|
|
|
418
435
|
memory: "prompt-relevant",
|
|
419
436
|
codebaseMap: true,
|
|
420
437
|
preferences: "active-only",
|
|
438
|
+
contextMode: "interview",
|
|
421
439
|
tools: TOOLS_PLANNING,
|
|
422
440
|
artifacts: {
|
|
423
441
|
inline: ["templates"],
|
|
@@ -434,6 +452,7 @@ export const UNIT_MANIFESTS = {
|
|
|
434
452
|
memory: "prompt-relevant",
|
|
435
453
|
codebaseMap: true,
|
|
436
454
|
preferences: "active-only",
|
|
455
|
+
contextMode: "interview",
|
|
437
456
|
tools: TOOLS_PLANNING,
|
|
438
457
|
artifacts: {
|
|
439
458
|
inline: ["project", "templates"],
|
|
@@ -450,6 +469,7 @@ export const UNIT_MANIFESTS = {
|
|
|
450
469
|
memory: "none",
|
|
451
470
|
codebaseMap: false,
|
|
452
471
|
preferences: "none",
|
|
472
|
+
contextMode: "none",
|
|
453
473
|
tools: TOOLS_PLANNING,
|
|
454
474
|
artifacts: {
|
|
455
475
|
inline: [],
|
|
@@ -468,6 +488,7 @@ export const UNIT_MANIFESTS = {
|
|
|
468
488
|
memory: "prompt-relevant",
|
|
469
489
|
codebaseMap: true,
|
|
470
490
|
preferences: "active-only",
|
|
491
|
+
contextMode: "research",
|
|
471
492
|
tools: { mode: "planning-dispatch", allowedSubagents: ["scout"] },
|
|
472
493
|
artifacts: {
|
|
473
494
|
inline: ["project", "requirements", "templates"],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// GSD2 UOK Audit Events and DB-First Projection Writes
|
|
1
2
|
import { appendFileSync, closeSync, existsSync, mkdirSync, openSync } from "node:fs";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -5,6 +6,7 @@ import { isStaleWrite } from "../auto/turn-epoch.js";
|
|
|
5
6
|
import { withFileLockSync } from "../file-lock.js";
|
|
6
7
|
import { gsdRoot } from "../paths.js";
|
|
7
8
|
import { isDbAvailable, insertAuditEvent } from "../gsd-db.js";
|
|
9
|
+
import { CURRENT_UOK_CONTRACT_VERSION, validateAuditEvent } from "./contracts.js";
|
|
8
10
|
function auditLogPath(basePath) {
|
|
9
11
|
return join(gsdRoot(basePath), "audit", "events.jsonl");
|
|
10
12
|
}
|
|
@@ -13,6 +15,7 @@ function ensureAuditDir(basePath) {
|
|
|
13
15
|
}
|
|
14
16
|
export function buildAuditEnvelope(args) {
|
|
15
17
|
return {
|
|
18
|
+
version: CURRENT_UOK_CONTRACT_VERSION,
|
|
16
19
|
eventId: randomUUID(),
|
|
17
20
|
traceId: args.traceId,
|
|
18
21
|
turnId: args.turnId,
|
|
@@ -27,6 +30,25 @@ export function emitUokAuditEvent(basePath, event) {
|
|
|
27
30
|
// Drop writes from a turn superseded by timeout recovery / cancellation.
|
|
28
31
|
if (isStaleWrite("uok-audit"))
|
|
29
32
|
return;
|
|
33
|
+
const validation = validateAuditEvent(event);
|
|
34
|
+
if (!validation.ok) {
|
|
35
|
+
throw new Error(`Invalid UOK audit event: ${validation.issues.map((issue) => `${issue.path}: ${issue.message}`).join("; ")}`);
|
|
36
|
+
}
|
|
37
|
+
const canonical = validation.value;
|
|
38
|
+
if (isDbAvailable()) {
|
|
39
|
+
try {
|
|
40
|
+
insertAuditEvent({
|
|
41
|
+
...canonical,
|
|
42
|
+
payload: {
|
|
43
|
+
...canonical.payload,
|
|
44
|
+
contractVersion: canonical.version ?? CURRENT_UOK_CONTRACT_VERSION,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
throw new Error(`DB authoritative audit write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
30
52
|
try {
|
|
31
53
|
ensureAuditDir(basePath);
|
|
32
54
|
const path = auditLogPath(basePath);
|
|
@@ -39,18 +61,10 @@ export function emitUokAuditEvent(basePath, event) {
|
|
|
39
61
|
// POSIX O_APPEND atomicity still protects small line writes, so skipping
|
|
40
62
|
// the lock rather than stalling orchestration is the correct tradeoff.
|
|
41
63
|
withFileLockSync(path, () => {
|
|
42
|
-
appendFileSync(path, `${JSON.stringify(
|
|
64
|
+
appendFileSync(path, `${JSON.stringify(canonical)}\n`, "utf-8");
|
|
43
65
|
}, { onLocked: "skip" });
|
|
44
66
|
}
|
|
45
67
|
catch {
|
|
46
68
|
// Best-effort: audit writes must never break orchestration.
|
|
47
69
|
}
|
|
48
|
-
if (!isDbAvailable())
|
|
49
|
-
return;
|
|
50
|
-
try {
|
|
51
|
-
insertAuditEvent(event);
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// Projection failures are non-fatal while legacy readers are still active.
|
|
55
|
-
}
|
|
56
70
|
}
|
|
@@ -1 +1,69 @@
|
|
|
1
|
-
|
|
1
|
+
// GSD2 UOK Contract Types and Versioning
|
|
2
|
+
export const CURRENT_UOK_CONTRACT_VERSION = "1";
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
function normalizeVersion(value) {
|
|
7
|
+
return value === CURRENT_UOK_CONTRACT_VERSION ? CURRENT_UOK_CONTRACT_VERSION : "0";
|
|
8
|
+
}
|
|
9
|
+
function requireString(value, key, issues) {
|
|
10
|
+
if (typeof value[key] !== "string" || value[key] === "") {
|
|
11
|
+
issues.push({ path: key, message: `${key} must be a non-empty string` });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function requireRecord(value, key, issues) {
|
|
15
|
+
if (!isRecord(value[key])) {
|
|
16
|
+
issues.push({ path: key, message: `${key} must be an object` });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function normalizeTurnResult(value) {
|
|
20
|
+
return { ...value, version: normalizeVersion(value.version) };
|
|
21
|
+
}
|
|
22
|
+
export function normalizeDispatchEnvelope(value) {
|
|
23
|
+
return { ...value, version: normalizeVersion(value.version) };
|
|
24
|
+
}
|
|
25
|
+
export function normalizeAuditEvent(value) {
|
|
26
|
+
return { ...value, version: normalizeVersion(value.version) };
|
|
27
|
+
}
|
|
28
|
+
export function validateTurnResult(value) {
|
|
29
|
+
const normalized = normalizeTurnResult(value);
|
|
30
|
+
const record = normalized;
|
|
31
|
+
const issues = [];
|
|
32
|
+
requireString(record, "traceId", issues);
|
|
33
|
+
requireString(record, "turnId", issues);
|
|
34
|
+
if (!Number.isInteger(record.iteration)) {
|
|
35
|
+
issues.push({ path: "iteration", message: "iteration must be an integer" });
|
|
36
|
+
}
|
|
37
|
+
requireString(record, "status", issues);
|
|
38
|
+
requireString(record, "failureClass", issues);
|
|
39
|
+
if (!Array.isArray(record.phaseResults)) {
|
|
40
|
+
issues.push({ path: "phaseResults", message: "phaseResults must be an array" });
|
|
41
|
+
}
|
|
42
|
+
requireString(record, "startedAt", issues);
|
|
43
|
+
requireString(record, "finishedAt", issues);
|
|
44
|
+
return { ok: issues.length === 0, value: normalized, issues };
|
|
45
|
+
}
|
|
46
|
+
export function validateDispatchEnvelope(value) {
|
|
47
|
+
const normalized = normalizeDispatchEnvelope(value);
|
|
48
|
+
const record = normalized;
|
|
49
|
+
const issues = [];
|
|
50
|
+
requireString(record, "action", issues);
|
|
51
|
+
requireRecord(record, "reason", issues);
|
|
52
|
+
if (isRecord(record.reason)) {
|
|
53
|
+
requireString(record.reason, "reasonCode", issues);
|
|
54
|
+
requireString(record.reason, "summary", issues);
|
|
55
|
+
}
|
|
56
|
+
return { ok: issues.length === 0, value: normalized, issues };
|
|
57
|
+
}
|
|
58
|
+
export function validateAuditEvent(value) {
|
|
59
|
+
const normalized = normalizeAuditEvent(value);
|
|
60
|
+
const record = normalized;
|
|
61
|
+
const issues = [];
|
|
62
|
+
requireString(record, "eventId", issues);
|
|
63
|
+
requireString(record, "traceId", issues);
|
|
64
|
+
requireString(record, "category", issues);
|
|
65
|
+
requireString(record, "type", issues);
|
|
66
|
+
requireString(record, "ts", issues);
|
|
67
|
+
requireRecord(record, "payload", issues);
|
|
68
|
+
return { ok: issues.length === 0, value: normalized, issues };
|
|
69
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
// GSD2 UOK Dispatch Envelope Builder
|
|
2
|
+
import { CURRENT_UOK_CONTRACT_VERSION } from "./contracts.js";
|
|
1
3
|
export function buildDispatchEnvelope(input) {
|
|
2
4
|
return {
|
|
5
|
+
version: CURRENT_UOK_CONTRACT_VERSION,
|
|
3
6
|
action: input.action,
|
|
4
7
|
nodeKind: input.node?.kind,
|
|
5
8
|
unitType: input.unitType,
|