@opengsd/gsd-pi 1.1.1-dev.616a1a1 → 1.1.1-dev.74e8dd1
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/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +167 -16
- package/dist/resources/extensions/gsd/auto/phases.js +4 -3
- package/dist/resources/extensions/gsd/auto-dashboard.js +15 -4
- package/dist/resources/extensions/gsd/auto-dispatch.js +39 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +113 -7
- package/dist/resources/extensions/gsd/auto-prompts.js +9 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +4 -4
- package/dist/resources/extensions/gsd/auto-start.js +94 -15
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +2 -1
- package/dist/resources/extensions/gsd/auto.js +22 -4
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +79 -0
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +43 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +30 -9
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -10
- package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +6 -2
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +7 -3
- package/dist/resources/extensions/gsd/commands-maintenance.js +172 -2
- package/dist/resources/extensions/gsd/commands-mcp-status.js +107 -59
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +3 -1
- package/dist/resources/extensions/gsd/commands-verdict.js +1 -1
- package/dist/resources/extensions/gsd/config-overlay.js +2 -1
- package/dist/resources/extensions/gsd/error-classifier.js +2 -1
- package/dist/resources/extensions/gsd/exec-sandbox.js +2 -0
- package/dist/resources/extensions/gsd/gsd-db.js +37 -4
- package/dist/resources/extensions/gsd/guided-flow.js +1 -1
- package/dist/resources/extensions/gsd/mcp-filter.js +3 -0
- package/dist/resources/extensions/gsd/mcp-project-config.js +67 -8
- package/dist/resources/extensions/gsd/migration-auto-check.js +2 -2
- package/dist/resources/extensions/gsd/prompts/run-uat.md +10 -4
- package/dist/resources/extensions/gsd/prompts/system.md +3 -1
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +3 -0
- package/dist/resources/extensions/gsd/skill-activation.js +20 -3
- package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +4 -2
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +1 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +18 -1
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +6 -0
- package/dist/resources/extensions/gsd/state.js +15 -12
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +120 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +109 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -9
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +366 -3
- package/dist/resources/extensions/gsd/unit-context-manifest.js +8 -3
- package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -1
- package/dist/resources/extensions/gsd/workflow-mcp.js +5 -1
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -0
- package/dist/resources/extensions/mcp-client/manager.js +31 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +4 -4
- 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 +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 +4 -4
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- 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/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +2 -2
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +14 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +16 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +72 -31
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +82 -0
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/image-models.generated.d.ts +15 -0
- package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/image-models.generated.js +15 -0
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +338 -17
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +412 -112
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/terminal.d.ts +1 -0
- package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal.js +8 -4
- package/packages/pi-tui/dist/terminal.js.map +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +196 -16
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +239 -63
- package/src/resources/extensions/gsd/auto/phases.ts +5 -3
- package/src/resources/extensions/gsd/auto-dashboard.ts +16 -4
- package/src/resources/extensions/gsd/auto-dispatch.ts +48 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +138 -7
- package/src/resources/extensions/gsd/auto-prompts.ts +9 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +4 -4
- package/src/resources/extensions/gsd/auto-start.ts +112 -17
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +2 -1
- package/src/resources/extensions/gsd/auto.ts +35 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +86 -0
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +51 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +51 -14
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +21 -10
- package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +6 -2
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +7 -3
- package/src/resources/extensions/gsd/commands-maintenance.ts +197 -2
- package/src/resources/extensions/gsd/commands-mcp-status.ts +134 -57
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -1
- package/src/resources/extensions/gsd/commands-verdict.ts +1 -1
- package/src/resources/extensions/gsd/config-overlay.ts +3 -1
- package/src/resources/extensions/gsd/error-classifier.ts +2 -1
- package/src/resources/extensions/gsd/exec-sandbox.ts +4 -0
- package/src/resources/extensions/gsd/gsd-db.ts +41 -6
- package/src/resources/extensions/gsd/guided-flow.ts +1 -1
- package/src/resources/extensions/gsd/mcp-filter.ts +3 -0
- package/src/resources/extensions/gsd/mcp-project-config.ts +92 -10
- package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
- package/src/resources/extensions/gsd/preferences-types.ts +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +10 -4
- package/src/resources/extensions/gsd/prompts/system.md +3 -1
- package/src/resources/extensions/gsd/safety/destructive-guard.ts +3 -0
- package/src/resources/extensions/gsd/skill-activation.ts +20 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +4 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +1 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +20 -0
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +6 -0
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +1 -0
- package/src/resources/extensions/gsd/state.ts +16 -12
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +86 -0
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +143 -2
- package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +24 -2
- package/src/resources/extensions/gsd/tests/commands-dispatcher-validation-block.test.ts +38 -3
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +6 -2
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +50 -13
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/gsd-rebuild.test.ts +199 -0
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +13 -6
- package/src/resources/extensions/gsd/tests/mcp-filter.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +177 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +54 -7
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +39 -1
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +18 -1
- package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +6 -2
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +84 -10
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +29 -6
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +29 -6
- package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +17 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +25 -0
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +167 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +130 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +14 -9
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +440 -2
- package/src/resources/extensions/gsd/unit-context-manifest.ts +14 -5
- package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +5 -1
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +26 -0
- package/src/resources/extensions/mcp-client/manager.ts +33 -1
- package/src/resources/extensions/mcp-client/tests/manager.test.ts +35 -0
- /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → eRWf-RI9bzbrwEurm_3uI}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → eRWf-RI9bzbrwEurm_3uI}/_ssgManifest.js +0 -0
|
@@ -394,6 +394,50 @@ function stripKnownIdPrefix(value: string | undefined | null, id: string): strin
|
|
|
394
394
|
return raw;
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
+
function parseReactiveBatchTaskIds(unitId: string): string[] {
|
|
398
|
+
const { task: batchPart } = parseUnitId(unitId);
|
|
399
|
+
if (!batchPart?.startsWith("reactive+")) return [];
|
|
400
|
+
|
|
401
|
+
const rawIds = batchPart
|
|
402
|
+
.slice("reactive+".length)
|
|
403
|
+
.split(",")
|
|
404
|
+
.map((taskId) => taskId.trim().toUpperCase())
|
|
405
|
+
.filter(Boolean);
|
|
406
|
+
|
|
407
|
+
const unique = new Set<string>();
|
|
408
|
+
for (const taskId of rawIds) {
|
|
409
|
+
unique.add(taskId);
|
|
410
|
+
}
|
|
411
|
+
return [...unique];
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function dedupePaths(values: string[]): string[] {
|
|
415
|
+
const seen = new Set<string>();
|
|
416
|
+
const result: string[] = [];
|
|
417
|
+
for (const value of values) {
|
|
418
|
+
if (!seen.has(value)) {
|
|
419
|
+
seen.add(value);
|
|
420
|
+
result.push(value);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return result;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function getPlannedKeyFiles(tasks: Array<
|
|
427
|
+
{ expected_output?: string[]; files?: string[]; key_files?: string[] }
|
|
428
|
+
>): string[] {
|
|
429
|
+
return dedupePaths(
|
|
430
|
+
tasks.flatMap((taskRow) => [
|
|
431
|
+
...(taskRow.expected_output ?? []),
|
|
432
|
+
...(taskRow.files ?? []),
|
|
433
|
+
...(taskRow.key_files ?? []),
|
|
434
|
+
]),
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export const _parseReactiveBatchTaskIdsForTest = parseReactiveBatchTaskIds;
|
|
439
|
+
export const _getPlannedKeyFilesForTest = getPlannedKeyFiles;
|
|
440
|
+
|
|
397
441
|
function resolveVerificationFailureMarkerPath(
|
|
398
442
|
unitType: string,
|
|
399
443
|
unitId: string,
|
|
@@ -481,6 +525,40 @@ async function buildTaskCommitContextForUnit(
|
|
|
481
525
|
};
|
|
482
526
|
}
|
|
483
527
|
|
|
528
|
+
async function buildReactiveTaskCommitContext(
|
|
529
|
+
_basePath: string,
|
|
530
|
+
unitId: string,
|
|
531
|
+
): Promise<TaskCommitContext | undefined> {
|
|
532
|
+
const { milestone: mid, slice: sid } = parseUnitId(unitId);
|
|
533
|
+
if (!mid || !sid || !isDbAvailable()) return undefined;
|
|
534
|
+
|
|
535
|
+
const batchTaskIds = parseReactiveBatchTaskIds(unitId);
|
|
536
|
+
if (batchTaskIds.length === 0) return undefined;
|
|
537
|
+
|
|
538
|
+
const milestone = getMilestone(mid);
|
|
539
|
+
const slice = getSlice(mid, sid);
|
|
540
|
+
const taskRows = batchTaskIds
|
|
541
|
+
.map((tid) => getTask(mid, sid, tid))
|
|
542
|
+
.filter((taskRow): taskRow is NonNullable<ReturnType<typeof getTask>> => taskRow !== null);
|
|
543
|
+
|
|
544
|
+
const keyFiles = getPlannedKeyFiles(taskRows);
|
|
545
|
+
if (taskRows.length === 0 || keyFiles.length === 0) return undefined;
|
|
546
|
+
|
|
547
|
+
const taskLabel = taskRows.map((row) => row.id).join(",");
|
|
548
|
+
|
|
549
|
+
return {
|
|
550
|
+
taskId: `${sid}/${taskLabel}`,
|
|
551
|
+
taskDisplayId: "reactive-batch",
|
|
552
|
+
taskTitle: `Reactive batch: ${taskLabel}`,
|
|
553
|
+
milestoneId: mid,
|
|
554
|
+
milestoneTitle: stripKnownIdPrefix(milestone?.title, mid),
|
|
555
|
+
sliceId: sid,
|
|
556
|
+
sliceTitle: stripKnownIdPrefix(slice?.title, sid),
|
|
557
|
+
oneLiner: `Reactive execute for ${taskLabel}`,
|
|
558
|
+
keyFiles,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
|
|
484
562
|
async function runPostUnitGitHubSyncIfNeeded(
|
|
485
563
|
basePath: string,
|
|
486
564
|
unit: NonNullable<AutoSession["currentUnit"]>,
|
|
@@ -943,6 +1021,8 @@ export async function autoCommitUnit(
|
|
|
943
1021
|
|
|
944
1022
|
if (unitType === "execute-task") {
|
|
945
1023
|
taskContext = await buildTaskCommitContextForUnit(basePath, unitId);
|
|
1024
|
+
} else if (unitType === "reactive-execute") {
|
|
1025
|
+
taskContext = await buildReactiveTaskCommitContext(basePath, unitId);
|
|
946
1026
|
}
|
|
947
1027
|
|
|
948
1028
|
_resetHasChangesCache();
|
|
@@ -1005,6 +1085,21 @@ async function runCloseoutGitAction(
|
|
|
1005
1085
|
if (mid && sid && tid && isDbAvailable()) {
|
|
1006
1086
|
targetRepositories = getTask(mid, sid, tid)?.target_repositories;
|
|
1007
1087
|
}
|
|
1088
|
+
} else if (turnAction === "commit" && unit.type === "reactive-execute") {
|
|
1089
|
+
taskContext = await buildReactiveTaskCommitContext(s.basePath, unit.id);
|
|
1090
|
+
const { milestone: mid, slice: sid } = parseUnitId(unit.id);
|
|
1091
|
+
if (mid && sid && isDbAvailable()) {
|
|
1092
|
+
const repositories = new Set<string>();
|
|
1093
|
+
for (const tid of parseReactiveBatchTaskIds(unit.id)) {
|
|
1094
|
+
const taskRow = getTask(mid, sid, tid);
|
|
1095
|
+
for (const repoId of taskRow?.target_repositories ?? []) {
|
|
1096
|
+
repositories.add(repoId);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
if (repositories.size > 0) {
|
|
1100
|
+
targetRepositories = [...repositories];
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1008
1103
|
}
|
|
1009
1104
|
|
|
1010
1105
|
// Invalidate the nativeHasChanges cache before auto-commit (#1853).
|
|
@@ -1436,12 +1531,24 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
1436
1531
|
const { milestone: sMid, slice: sSid, task: sTid } = parseUnitId(s.currentUnit.id);
|
|
1437
1532
|
|
|
1438
1533
|
// File change validation (execute-task only, after unit execution)
|
|
1439
|
-
if (safetyConfig.file_change_validation && s.currentUnit.type === "execute-task" && sMid && sSid && sTid
|
|
1534
|
+
if (safetyConfig.file_change_validation && s.currentUnit.type === "execute-task" && sMid && sSid && sTid) {
|
|
1440
1535
|
try {
|
|
1441
|
-
const
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1536
|
+
const sliceTaskRows = isDbAvailable()
|
|
1537
|
+
? getSliceTasks(sMid, sSid).filter((t) => isClosedStatus(t.status) || t.id === sTid)
|
|
1538
|
+
: [];
|
|
1539
|
+
|
|
1540
|
+
if (sliceTaskRows.length > 0) {
|
|
1541
|
+
const expectedOutput = getPlannedKeyFiles(
|
|
1542
|
+
sliceTaskRows.map((taskRow) => ({
|
|
1543
|
+
expected_output: taskRow.expected_output,
|
|
1544
|
+
files: taskRow.files,
|
|
1545
|
+
})),
|
|
1546
|
+
);
|
|
1547
|
+
const plannedFiles = getPlannedKeyFiles(
|
|
1548
|
+
sliceTaskRows.map((taskRow) => ({
|
|
1549
|
+
files: taskRow.files,
|
|
1550
|
+
})),
|
|
1551
|
+
);
|
|
1445
1552
|
const audit = validateFileChanges(s.basePath, expectedOutput, plannedFiles, safetyConfig.file_change_allowlist);
|
|
1446
1553
|
if (audit && audit.violations.length > 0) {
|
|
1447
1554
|
const warnings = audit.violations.filter(v => v.severity === "warning");
|
|
@@ -1455,6 +1562,30 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
1455
1562
|
);
|
|
1456
1563
|
}
|
|
1457
1564
|
}
|
|
1565
|
+
} else {
|
|
1566
|
+
const taskRow = getTask(sMid, sSid, sTid);
|
|
1567
|
+
if (taskRow) {
|
|
1568
|
+
const expectedOutput = taskRow.expected_output ?? [];
|
|
1569
|
+
const plannedFiles = taskRow.files ?? [];
|
|
1570
|
+
const audit = validateFileChanges(
|
|
1571
|
+
s.basePath,
|
|
1572
|
+
expectedOutput,
|
|
1573
|
+
plannedFiles,
|
|
1574
|
+
safetyConfig.file_change_allowlist,
|
|
1575
|
+
);
|
|
1576
|
+
if (audit && audit.violations.length > 0) {
|
|
1577
|
+
const warnings = audit.violations.filter(v => v.severity === "warning");
|
|
1578
|
+
for (const v of warnings) {
|
|
1579
|
+
logWarning("safety", `file-change: ${v.file} — ${v.reason}`);
|
|
1580
|
+
}
|
|
1581
|
+
if (warnings.length > 0) {
|
|
1582
|
+
ctx.ui.notify(
|
|
1583
|
+
`Safety: ${warnings.length} unexpected file change(s) outside task plan`,
|
|
1584
|
+
"warning",
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1458
1589
|
}
|
|
1459
1590
|
} catch (e) {
|
|
1460
1591
|
debugLog("postUnit", { phase: "safety-file-change", error: String(e) });
|
|
@@ -2134,8 +2265,8 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
2134
2265
|
await renderPlanCheckboxes(s.canonicalProjectRoot, mid, sid);
|
|
2135
2266
|
} catch (dbErr) {
|
|
2136
2267
|
// DB unavailable — fail explicitly rather than silently reverting to markdown mutation.
|
|
2137
|
-
// Use 'gsd recover' to
|
|
2138
|
-
logError("engine", `retry state-reset failed (DB unavailable): ${(dbErr as Error).message}. Run 'gsd recover' to
|
|
2268
|
+
// Use 'gsd recover --confirm' to import markdown into the DB if needed.
|
|
2269
|
+
logError("engine", `retry state-reset failed (DB unavailable): ${(dbErr as Error).message}. Run 'gsd recover --confirm' to import markdown into the DB.`);
|
|
2139
2270
|
}
|
|
2140
2271
|
}
|
|
2141
2272
|
|
|
@@ -2711,6 +2711,15 @@ export async function buildCompleteSlicePrompt(
|
|
|
2711
2711
|
sliceSummaryPath,
|
|
2712
2712
|
sliceUatPath,
|
|
2713
2713
|
gatesToClose,
|
|
2714
|
+
skillActivation: buildSkillActivationBlock({
|
|
2715
|
+
base,
|
|
2716
|
+
milestoneId: mid,
|
|
2717
|
+
milestoneTitle: midTitle,
|
|
2718
|
+
sliceId: sid,
|
|
2719
|
+
sliceTitle: sTitle,
|
|
2720
|
+
extraContext: [inlinedContext],
|
|
2721
|
+
unitType: "complete-slice",
|
|
2722
|
+
}),
|
|
2714
2723
|
});
|
|
2715
2724
|
}
|
|
2716
2725
|
|
|
@@ -867,7 +867,7 @@ export function buildLoopRemediationSteps(
|
|
|
867
867
|
return [
|
|
868
868
|
` 1. Run \`gsd undo-task ${mid}/${sid}/${tid}\` to reset the task state`,
|
|
869
869
|
` 2. Resume auto-mode — it will re-execute the task`,
|
|
870
|
-
` 3. If the task keeps failing
|
|
870
|
+
` 3. If the task keeps failing and markdown should repopulate the DB, run \`gsd recover --confirm\``,
|
|
871
871
|
].join("\n");
|
|
872
872
|
}
|
|
873
873
|
case "plan-slice":
|
|
@@ -879,7 +879,7 @@ export function buildLoopRemediationSteps(
|
|
|
879
879
|
: relSliceFile(base, mid, sid, "RESEARCH");
|
|
880
880
|
return [
|
|
881
881
|
` 1. Write ${artifactRel} manually (or with the LLM in interactive mode)`,
|
|
882
|
-
` 2. Run \`gsd recover\` to
|
|
882
|
+
` 2. Run \`gsd recover --confirm\` to import the markdown into the DB`,
|
|
883
883
|
` 3. Resume auto-mode`,
|
|
884
884
|
].join("\n");
|
|
885
885
|
}
|
|
@@ -888,7 +888,7 @@ export function buildLoopRemediationSteps(
|
|
|
888
888
|
return [
|
|
889
889
|
` 1. Run \`gsd reset-slice ${mid}/${sid}\` to reset the slice and all its tasks`,
|
|
890
890
|
` 2. Resume auto-mode — it will re-execute incomplete tasks and re-complete the slice`,
|
|
891
|
-
` 3. If the slice keeps failing
|
|
891
|
+
` 3. If the slice keeps failing and markdown should repopulate the DB, run \`gsd recover --confirm\``,
|
|
892
892
|
].join("\n");
|
|
893
893
|
}
|
|
894
894
|
case "validate-milestone": {
|
|
@@ -896,7 +896,7 @@ export function buildLoopRemediationSteps(
|
|
|
896
896
|
const artifactRel = relMilestoneFile(base, mid, "VALIDATION");
|
|
897
897
|
return [
|
|
898
898
|
` 1. Write ${artifactRel} with verdict: pass`,
|
|
899
|
-
` 2. Run \`gsd recover\` to
|
|
899
|
+
` 2. Run \`gsd recover --confirm\` to import the markdown into the DB`,
|
|
900
900
|
` 3. Resume auto-mode`,
|
|
901
901
|
].join("\n");
|
|
902
902
|
}
|
|
@@ -60,12 +60,13 @@ import { getAutoWorktreePath, isInAutoWorktree, checkoutBranchWithStashGuard } f
|
|
|
60
60
|
import { readResourceVersion, cleanStaleRuntimeUnits } from "./auto-worktree.js";
|
|
61
61
|
import { worktreePath as getWorktreeDir, isInsideWorktreesDir } from "./worktree-manager.js";
|
|
62
62
|
import { emitWorktreeOrphaned } from "./worktree-telemetry.js";
|
|
63
|
+
import { queryJournal } from "./journal.js";
|
|
63
64
|
import { initMetrics } from "./metrics.js";
|
|
64
65
|
import { initRoutingHistory } from "./routing-history.js";
|
|
65
66
|
import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
|
|
66
67
|
import { resetProactiveHealing, setLevelChangeCallback } from "./doctor-proactive.js";
|
|
67
68
|
import { snapshotSkills } from "./skill-discovery.js";
|
|
68
|
-
import { isDbAvailable, getMilestone, getAllMilestones, insertMilestone, openDatabase, getDbStatus } from "./gsd-db.js";
|
|
69
|
+
import { isDbAvailable, getMilestone, getAllMilestones, insertMilestone, openDatabase, getDbStatus, updateMilestoneStatus } from "./gsd-db.js";
|
|
69
70
|
import { isClosedStatus } from "./status-guards.js";
|
|
70
71
|
import { classifyMilestoneSummaryContent } from "./milestone-summary-classifier.js";
|
|
71
72
|
import { extractVerdict } from "./verdict-parser.js";
|
|
@@ -100,6 +101,7 @@ import {
|
|
|
100
101
|
} from "./preferences-models.js";
|
|
101
102
|
import type { WorktreeLifecycle } from "./worktree-lifecycle.js";
|
|
102
103
|
import { getSessionModelOverride } from "./session-model-override.js";
|
|
104
|
+
import { setAutoActiveStatus } from "./auto-dashboard.js";
|
|
103
105
|
|
|
104
106
|
export interface BootstrapDeps {
|
|
105
107
|
shouldUseWorktreeIsolation: (basePath?: string) => boolean;
|
|
@@ -187,6 +189,40 @@ export function reconcileProjectMilestonesFromDisk(basePath: string): number {
|
|
|
187
189
|
}
|
|
188
190
|
}
|
|
189
191
|
|
|
192
|
+
export function reconcileMergedMilestonesFromJournal(basePath: string): number {
|
|
193
|
+
if (!isDbAvailable()) return 0;
|
|
194
|
+
|
|
195
|
+
const mergedAtByMilestone = new Map<string, string>();
|
|
196
|
+
for (const entry of queryJournal(basePath, { eventType: "worktree-merged" })) {
|
|
197
|
+
const data = entry.data ?? {};
|
|
198
|
+
const milestoneId = typeof data.milestoneId === "string" ? data.milestoneId : null;
|
|
199
|
+
if (!milestoneId) continue;
|
|
200
|
+
if (data.conflict === true) continue;
|
|
201
|
+
|
|
202
|
+
const endedAt = typeof data.endedAt === "string" ? data.endedAt : entry.ts;
|
|
203
|
+
const previous = mergedAtByMilestone.get(milestoneId);
|
|
204
|
+
if (!previous || endedAt > previous) mergedAtByMilestone.set(milestoneId, endedAt);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
let closed = 0;
|
|
208
|
+
for (const [milestoneId, completedAt] of mergedAtByMilestone) {
|
|
209
|
+
const existing = getMilestone(milestoneId);
|
|
210
|
+
if (!existing) {
|
|
211
|
+
insertMilestone({ id: milestoneId, title: milestoneId, status: "complete" });
|
|
212
|
+
updateMilestoneStatus(milestoneId, "complete", completedAt);
|
|
213
|
+
closed++;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (!isClosedStatus(existing.status)) {
|
|
217
|
+
updateMilestoneStatus(milestoneId, "complete", completedAt);
|
|
218
|
+
closed++;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (closed > 0) invalidateAllCaches();
|
|
223
|
+
return closed;
|
|
224
|
+
}
|
|
225
|
+
|
|
190
226
|
/**
|
|
191
227
|
* Audit for orphaned milestone branches at bootstrap.
|
|
192
228
|
*
|
|
@@ -258,6 +294,7 @@ export interface OrphanAuditAction {
|
|
|
258
294
|
message: string;
|
|
259
295
|
severity: "info" | "warning";
|
|
260
296
|
branch?: string;
|
|
297
|
+
mainBranch?: string;
|
|
261
298
|
commitsAhead?: number;
|
|
262
299
|
dirtyWorktree?: boolean;
|
|
263
300
|
worktreeDirExists?: boolean;
|
|
@@ -276,6 +313,27 @@ function isBlockingStrandedWorkAction(action: OrphanAuditAction): boolean {
|
|
|
276
313
|
return action.kind === "in-progress-stranded-work" && action.blocksAuto;
|
|
277
314
|
}
|
|
278
315
|
|
|
316
|
+
function strandedWorkEvidence(args: {
|
|
317
|
+
branch?: string;
|
|
318
|
+
commitsAhead: number;
|
|
319
|
+
mainBranch: string;
|
|
320
|
+
dirtyWorktree: boolean;
|
|
321
|
+
}): string[] {
|
|
322
|
+
const evidence: string[] = [];
|
|
323
|
+
if (args.branch && args.commitsAhead > 0) {
|
|
324
|
+
evidence.push(
|
|
325
|
+
`branch ${args.branch} has ${args.commitsAhead} commit(s) ahead of ${args.mainBranch}`,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
if (args.dirtyWorktree) {
|
|
329
|
+
evidence.push("the worktree has uncommitted changes");
|
|
330
|
+
}
|
|
331
|
+
if (evidence.length === 0) {
|
|
332
|
+
evidence.push("physical git evidence exists");
|
|
333
|
+
}
|
|
334
|
+
return evidence;
|
|
335
|
+
}
|
|
336
|
+
|
|
279
337
|
function detectWorktreeEvidence(
|
|
280
338
|
basePath: string,
|
|
281
339
|
milestoneId: string,
|
|
@@ -307,18 +365,7 @@ function strandedWorkMessage(args: {
|
|
|
307
365
|
worktreeDirExists: boolean;
|
|
308
366
|
recoveryMode: StrandedWorkRecoveryMode;
|
|
309
367
|
}): string {
|
|
310
|
-
const evidence
|
|
311
|
-
if (args.branch && args.commitsAhead > 0) {
|
|
312
|
-
evidence.push(
|
|
313
|
-
`branch ${args.branch} has ${args.commitsAhead} commit(s) ahead of ${args.mainBranch}`,
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
if (args.dirtyWorktree) {
|
|
317
|
-
evidence.push("the worktree has uncommitted changes");
|
|
318
|
-
}
|
|
319
|
-
if (evidence.length === 0) {
|
|
320
|
-
evidence.push("physical git evidence exists");
|
|
321
|
-
}
|
|
368
|
+
const evidence = strandedWorkEvidence(args);
|
|
322
369
|
|
|
323
370
|
const wtSuffix = args.worktreeDirExists
|
|
324
371
|
? ` Worktree directory at .gsd/worktrees/${args.milestoneId}/ holds live work.`
|
|
@@ -334,6 +381,45 @@ function strandedWorkMessage(args: {
|
|
|
334
381
|
);
|
|
335
382
|
}
|
|
336
383
|
|
|
384
|
+
function formatStrandedWorkRecoveryMessage(action: OrphanAuditAction): string {
|
|
385
|
+
const recoveryMode = action.recoveryMode === "worktree"
|
|
386
|
+
? "existing worktree"
|
|
387
|
+
: "milestone branch";
|
|
388
|
+
const evidence = strandedWorkEvidence({
|
|
389
|
+
branch: action.branch,
|
|
390
|
+
commitsAhead: action.commitsAhead ?? 0,
|
|
391
|
+
mainBranch: action.mainBranch ?? "main",
|
|
392
|
+
dirtyWorktree: action.dirtyWorktree ?? false,
|
|
393
|
+
});
|
|
394
|
+
const wtSuffix = action.worktreeDirExists
|
|
395
|
+
? ` Worktree directory at .gsd/worktrees/${action.milestoneId}/ holds live work.`
|
|
396
|
+
: "";
|
|
397
|
+
return (
|
|
398
|
+
`Resuming saved milestone work for ${action.milestoneId}: ${evidence.join("; ")}.` +
|
|
399
|
+
wtSuffix +
|
|
400
|
+
` Adopting the ${recoveryMode} before dispatching new units. Park or discard explicitly if abandoning.`
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function formatStrandedWorkBlockerMessage(
|
|
405
|
+
action: OrphanAuditAction,
|
|
406
|
+
activeMilestoneId: string | null,
|
|
407
|
+
): string {
|
|
408
|
+
const target = action.milestoneId;
|
|
409
|
+
const mode = action.recoveryMode === "worktree" ? "existing worktree" : "milestone branch";
|
|
410
|
+
const intro = activeMilestoneId
|
|
411
|
+
? `Stranded work for ${target} blocks auto-mode before ${activeMilestoneId}.`
|
|
412
|
+
: `Stranded work for ${target} blocks auto-mode, but that milestone is not active in project state.`;
|
|
413
|
+
|
|
414
|
+
return [
|
|
415
|
+
intro,
|
|
416
|
+
"Choose one explicit next step:",
|
|
417
|
+
`1. Recover it: run \`/gsd auto ${target}\` to adopt the ${mode}.`,
|
|
418
|
+
`2. Defer it: run \`/gsd park ${target} "reason"\`, then rerun \`/gsd auto\`.`,
|
|
419
|
+
`3. Abandon it: run \`/gsd rethink\` and explicitly discard ${target}.`,
|
|
420
|
+
].join("\n");
|
|
421
|
+
}
|
|
422
|
+
|
|
337
423
|
export function auditOrphanedMilestoneBranches(
|
|
338
424
|
basePath: string,
|
|
339
425
|
_isolationMode: "worktree" | "branch" | "none",
|
|
@@ -435,6 +521,7 @@ export function auditOrphanedMilestoneBranches(
|
|
|
435
521
|
kind: "in-progress-stranded-work",
|
|
436
522
|
milestoneId,
|
|
437
523
|
branch,
|
|
524
|
+
mainBranch,
|
|
438
525
|
commitsAhead,
|
|
439
526
|
dirtyWorktree: worktreeEvidence.dirty,
|
|
440
527
|
worktreeDirExists: worktreeEvidence.dirExists,
|
|
@@ -589,6 +676,7 @@ export function auditOrphanedMilestoneBranches(
|
|
|
589
676
|
pushAction({
|
|
590
677
|
kind: "in-progress-stranded-work",
|
|
591
678
|
milestoneId: m.id,
|
|
679
|
+
mainBranch,
|
|
592
680
|
commitsAhead: 0,
|
|
593
681
|
dirtyWorktree: true,
|
|
594
682
|
worktreeDirExists: worktreeEvidence.dirExists,
|
|
@@ -1066,6 +1154,7 @@ export async function bootstrapAutoSession(
|
|
|
1066
1154
|
await openProjectDbIfPresent(base);
|
|
1067
1155
|
registerAutoWorkerForSession(base);
|
|
1068
1156
|
reconcileProjectMilestonesFromDisk(base);
|
|
1157
|
+
reconcileMergedMilestonesFromJournal(base);
|
|
1069
1158
|
|
|
1070
1159
|
// Clean stale runtime unit files for completed milestones (#887).
|
|
1071
1160
|
// DB-authoritative: when DB is available, require DB status to be closed
|
|
@@ -1103,7 +1192,13 @@ export async function bootstrapAutoSession(
|
|
|
1103
1192
|
for (const msg of auditResult.recovered) {
|
|
1104
1193
|
ctx.ui.notify(`Orphan audit: ${msg}`, "info");
|
|
1105
1194
|
}
|
|
1195
|
+
const deferredStrandedMessages = new Set(
|
|
1196
|
+
auditResult.actions
|
|
1197
|
+
.filter(isBlockingStrandedWorkAction)
|
|
1198
|
+
.map((action) => action.message),
|
|
1199
|
+
);
|
|
1106
1200
|
for (const msg of auditResult.warnings) {
|
|
1201
|
+
if (deferredStrandedMessages.has(msg)) continue;
|
|
1107
1202
|
const prefix = msg.startsWith("Stranded work") ? "" : "Orphan audit: ";
|
|
1108
1203
|
ctx.ui.notify(`${prefix}${msg}`, "warning");
|
|
1109
1204
|
}
|
|
@@ -1177,21 +1272,21 @@ export async function bootstrapAutoSession(
|
|
|
1177
1272
|
if (blockingStrandedRecoveryAction) {
|
|
1178
1273
|
if (!state.activeMilestone) {
|
|
1179
1274
|
ctx.ui.notify(
|
|
1180
|
-
|
|
1275
|
+
formatStrandedWorkBlockerMessage(blockingStrandedRecoveryAction, null),
|
|
1181
1276
|
"error",
|
|
1182
1277
|
);
|
|
1183
1278
|
return releaseLockAndReturn();
|
|
1184
1279
|
}
|
|
1185
1280
|
if (state.activeMilestone.id !== blockingStrandedRecoveryAction.milestoneId) {
|
|
1186
1281
|
ctx.ui.notify(
|
|
1187
|
-
|
|
1282
|
+
formatStrandedWorkBlockerMessage(blockingStrandedRecoveryAction, state.activeMilestone.id),
|
|
1188
1283
|
"error",
|
|
1189
1284
|
);
|
|
1190
1285
|
return releaseLockAndReturn();
|
|
1191
1286
|
}
|
|
1192
1287
|
strandedRecoveryAction = blockingStrandedRecoveryAction;
|
|
1193
1288
|
ctx.ui.notify(
|
|
1194
|
-
|
|
1289
|
+
formatStrandedWorkRecoveryMessage(strandedRecoveryAction),
|
|
1195
1290
|
"info",
|
|
1196
1291
|
);
|
|
1197
1292
|
}
|
|
@@ -1663,7 +1758,7 @@ export async function bootstrapAutoSession(
|
|
|
1663
1758
|
snapshotSkills();
|
|
1664
1759
|
}
|
|
1665
1760
|
|
|
1666
|
-
ctx
|
|
1761
|
+
setAutoActiveStatus(ctx, s.stepMode ? "next" : "auto");
|
|
1667
1762
|
ctx.ui.setWidget("gsd-health", undefined);
|
|
1668
1763
|
const modeLabel = s.stepMode ? "Step-mode" : "Auto-mode";
|
|
1669
1764
|
const pendingCount = (state.registry ?? []).filter(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseUnitId } from "./unit-id.js";
|
|
2
|
+
import { RUN_UAT_WORKFLOW_TOOL_NAMES } from "./tool-presentation-plan.js";
|
|
2
3
|
|
|
3
4
|
export const RUN_UAT_BROWSER_TOOL_NAMES = [
|
|
4
5
|
"browser_navigate",
|
|
@@ -44,7 +45,7 @@ export const AUTO_UNIT_SCOPED_TOOLS: Record<string, readonly string[]> = {
|
|
|
44
45
|
"execute-task": ["gsd_task_complete", "gsd_decision_save"],
|
|
45
46
|
"execute-task-simple": ["gsd_task_complete", "gsd_decision_save"],
|
|
46
47
|
"reactive-execute": ["gsd_task_complete", "gsd_decision_save"],
|
|
47
|
-
"run-uat": ["
|
|
48
|
+
"run-uat": [...RUN_UAT_WORKFLOW_TOOL_NAMES, "subagent", ...RUN_UAT_BROWSER_TOOL_NAMES],
|
|
48
49
|
"gate-evaluate": ["gsd_save_gate_result"],
|
|
49
50
|
"rewrite-docs": ["gsd_summary_save", "gsd_decision_save"],
|
|
50
51
|
"workflow-preferences": ["gsd_summary_save"],
|
|
@@ -205,6 +205,7 @@ import {
|
|
|
205
205
|
updateProgressWidget as _updateProgressWidget,
|
|
206
206
|
setCompletionProgressWidget,
|
|
207
207
|
setAutoOutcomeWidget,
|
|
208
|
+
setAutoActiveStatus,
|
|
208
209
|
updateSliceProgressCache,
|
|
209
210
|
clearSliceProgressCache,
|
|
210
211
|
describeNextUnit as _describeNextUnit,
|
|
@@ -254,7 +255,12 @@ import {
|
|
|
254
255
|
postUnitPreVerification,
|
|
255
256
|
postUnitPostVerification,
|
|
256
257
|
} from "./auto-post-unit.js";
|
|
257
|
-
import {
|
|
258
|
+
import {
|
|
259
|
+
bootstrapAutoSession,
|
|
260
|
+
openProjectDbIfPresent,
|
|
261
|
+
reconcileMergedMilestonesFromJournal,
|
|
262
|
+
type BootstrapDeps,
|
|
263
|
+
} from "./auto-start.js";
|
|
258
264
|
import { initHealthWidget } from "./health-widget.js";
|
|
259
265
|
import { runLegacyAutoLoop, runUokKernelLoop } from "./auto/loop.js";
|
|
260
266
|
import { resolveAgentEnd, resolveAgentEndCancelled, _resetPendingResolve, isSessionSwitchInFlight } from "./auto/resolve.js";
|
|
@@ -2102,6 +2108,28 @@ export function createWiredDispatchAdapter(
|
|
|
2102
2108
|
return null;
|
|
2103
2109
|
}
|
|
2104
2110
|
|
|
2111
|
+
function shouldAdoptActiveMilestone(
|
|
2112
|
+
state: GSDState,
|
|
2113
|
+
activeSession: AutoSession | undefined,
|
|
2114
|
+
activeDispatchBasePath: string,
|
|
2115
|
+
): boolean {
|
|
2116
|
+
const activeMilestoneId = state.activeMilestone?.id;
|
|
2117
|
+
const currentMilestoneId = activeSession?.currentMilestoneId;
|
|
2118
|
+
if (!activeSession || !activeMilestoneId || !currentMilestoneId || activeMilestoneId === currentMilestoneId) {
|
|
2119
|
+
return false;
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
const scopedWorktreeMilestone =
|
|
2123
|
+
(activeSession.basePath ? detectWorktreeName(activeSession.basePath) : null) ??
|
|
2124
|
+
detectWorktreeName(activeDispatchBasePath);
|
|
2125
|
+
if (scopedWorktreeMilestone && scopedWorktreeMilestone !== activeMilestoneId) {
|
|
2126
|
+
return false;
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
const currentMilestone = state.registry.find((milestone) => milestone.id === currentMilestoneId);
|
|
2130
|
+
return !!currentMilestone && isClosedStatus(currentMilestone.status);
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2105
2133
|
return {
|
|
2106
2134
|
async decideNextUnit(input) {
|
|
2107
2135
|
const state = input.stateSnapshot;
|
|
@@ -2110,6 +2138,9 @@ export function createWiredDispatchAdapter(
|
|
|
2110
2138
|
|
|
2111
2139
|
const activeSession = input.session ?? session;
|
|
2112
2140
|
const activeDispatchBasePath = activeSession?.basePath || dispatchBasePath;
|
|
2141
|
+
if (activeSession && shouldAdoptActiveMilestone(state, activeSession, activeDispatchBasePath)) {
|
|
2142
|
+
activeSession.currentMilestoneId = active.id;
|
|
2143
|
+
}
|
|
2113
2144
|
const prefs = loadEffectiveGSDPreferences(activeDispatchBasePath)?.preferences;
|
|
2114
2145
|
|
|
2115
2146
|
// Derive session-derived dispatch inputs the same way phases.ts:runDispatch does
|
|
@@ -2955,6 +2986,7 @@ export async function startAuto(
|
|
|
2955
2986
|
if (!getLedger()) initMetrics(base);
|
|
2956
2987
|
if (s.currentMilestoneId) setActiveMilestoneId(base, s.currentMilestoneId);
|
|
2957
2988
|
await openProjectDbIfPresent(base);
|
|
2989
|
+
reconcileMergedMilestonesFromJournal(base);
|
|
2958
2990
|
registerAutoWorkerForSession(s, base);
|
|
2959
2991
|
|
|
2960
2992
|
// Re-register health level notification callback lost across process restart
|
|
@@ -2991,7 +3023,7 @@ export async function startAuto(
|
|
|
2991
3023
|
ensureOrchestrationModule(ctx, pi, s.basePath || base);
|
|
2992
3024
|
registerSigtermHandler(lockBase());
|
|
2993
3025
|
|
|
2994
|
-
ctx
|
|
3026
|
+
setAutoActiveStatus(ctx, s.stepMode ? "next" : "auto");
|
|
2995
3027
|
ctx.ui.setWidget("gsd-health", undefined);
|
|
2996
3028
|
ctx.ui.notify(
|
|
2997
3029
|
s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.",
|
|
@@ -3320,7 +3352,7 @@ export async function dispatchHookUnit(
|
|
|
3320
3352
|
await pauseAuto(ctx, pi);
|
|
3321
3353
|
}, hookHardTimeoutMs);
|
|
3322
3354
|
|
|
3323
|
-
ctx
|
|
3355
|
+
setAutoActiveStatus(ctx, s.stepMode ? "next" : "auto");
|
|
3324
3356
|
ctx.ui.notify(`Running post-unit hook: ${hookName}`, "info");
|
|
3325
3357
|
|
|
3326
3358
|
debugLog("dispatchHookUnit", {
|
|
@@ -413,6 +413,92 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
413
413
|
pi.registerTool(summarySaveTool);
|
|
414
414
|
registerAlias(pi, summarySaveTool, "gsd_save_summary", "gsd_summary_save");
|
|
415
415
|
|
|
416
|
+
// ─── gsd_uat_result_save ─────────────────────────────────────────────────
|
|
417
|
+
|
|
418
|
+
const uatResultSaveExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
|
|
419
|
+
const { executeUatResultSave } = await loadWorkflowExecutors();
|
|
420
|
+
return executeUatResultSave(params, resolveWorkflowToolBasePath(_ctx, params));
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
const uatEvidenceRef = Type.Object({
|
|
424
|
+
kind: StringEnum(["gsd_uat_exec", "gsd_exec", "screenshot", "log", "url", "browser"], { description: "Evidence kind" }),
|
|
425
|
+
ref: Type.String({ description: "Evidence ID, approved .gsd path, or URL" }),
|
|
426
|
+
note: Type.Optional(Type.String({ description: "Short evidence note" })),
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const uatCheck = Type.Object({
|
|
430
|
+
id: Type.String({ description: "Stable check ID from the UAT spec" }),
|
|
431
|
+
description: Type.String({ description: "Check description" }),
|
|
432
|
+
mode: StringEnum(["artifact", "runtime", "browser", "human-follow-up"], { description: "Evidence mode" }),
|
|
433
|
+
result: StringEnum(["PASS", "FAIL", "NEEDS-HUMAN"], { description: "Check result" }),
|
|
434
|
+
evidence: Type.Optional(Type.Array(uatEvidenceRef, { description: "Objective evidence references" })),
|
|
435
|
+
notes: Type.Optional(Type.String({ description: "Observed result, failure notes, or human instruction" })),
|
|
436
|
+
nonAutomatable: Type.Optional(Type.Boolean({ description: "True when the check is explicitly non-automatable" })),
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
const toolPresentationBlock = Type.Object({
|
|
440
|
+
surface: StringEnum(["provider-tools", "claude-code-sdk", "mcp", "hybrid"], { description: "Tool presentation surface" }),
|
|
441
|
+
model: Type.Optional(Type.Object({
|
|
442
|
+
provider: Type.Optional(Type.String()),
|
|
443
|
+
api: Type.Optional(Type.String()),
|
|
444
|
+
id: Type.Optional(Type.String()),
|
|
445
|
+
})),
|
|
446
|
+
presentedTools: Type.Array(Type.String(), { description: "Tool names actually presented to the model" }),
|
|
447
|
+
blockedTools: Type.Array(Type.Object({
|
|
448
|
+
name: Type.String(),
|
|
449
|
+
reason: Type.String(),
|
|
450
|
+
}), { description: "Tool names blocked from the model with reasons" }),
|
|
451
|
+
aliases: Type.Optional(Type.Array(Type.Object({
|
|
452
|
+
requested: Type.String(),
|
|
453
|
+
canonical: Type.String(),
|
|
454
|
+
}))),
|
|
455
|
+
fallbackToolsUsed: Type.Optional(Type.Array(Type.String())),
|
|
456
|
+
toolPresentationPlanId: Type.Optional(Type.String()),
|
|
457
|
+
notes: Type.Optional(Type.String()),
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const uatResultSaveTool = {
|
|
461
|
+
name: "gsd_uat_result_save",
|
|
462
|
+
label: "Save UAT Result",
|
|
463
|
+
description:
|
|
464
|
+
"Save a structured UAT result for a slice. Validates evidence, writes the ASSESSMENT artifact, " +
|
|
465
|
+
"records attempt history, and saves the aggregate UAT gate result.",
|
|
466
|
+
promptSnippet: "Save structured UAT checks, evidence, verdict, and tool-presentation proof",
|
|
467
|
+
promptGuidelines: [
|
|
468
|
+
"Call gsd_uat_result_save once after all UAT checks have been executed.",
|
|
469
|
+
"Every PASS or FAIL check must cite objective evidence, preferably a gsd_uat_exec evidence ID.",
|
|
470
|
+
"Include the presented and blocked tool set in presentation so tool timing is auditable.",
|
|
471
|
+
"Do not use raw gsd_summary_save as a substitute for UAT results.",
|
|
472
|
+
],
|
|
473
|
+
parameters: Type.Object({
|
|
474
|
+
milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
|
|
475
|
+
sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
|
|
476
|
+
uatType: StringEnum(["artifact-driven", "browser-executable", "runtime-executable", "live-runtime", "mixed", "human-experience"], { description: "Declared UAT mode" }),
|
|
477
|
+
verdict: StringEnum(["PASS", "FAIL", "PARTIAL"], { description: "Overall UAT verdict" }),
|
|
478
|
+
checks: Type.Array(uatCheck, { description: "Structured check results" }),
|
|
479
|
+
presentation: toolPresentationBlock,
|
|
480
|
+
notes: Type.Optional(Type.String({ description: "Overall verdict rationale" })),
|
|
481
|
+
attempt: Type.Optional(Type.String({ description: "Attempt number or auto" })),
|
|
482
|
+
previousAttemptId: Type.Optional(Type.String({ description: "Prior attempt ID, when retrying" })),
|
|
483
|
+
}),
|
|
484
|
+
execute: uatResultSaveExecute,
|
|
485
|
+
renderCall(args: any, theme: any) {
|
|
486
|
+
let text = theme.fg("toolTitle", theme.bold("uat_result_save "));
|
|
487
|
+
text += theme.fg("accent", `${args.milestoneId ?? "?"}/${args.sliceId ?? "?"}`);
|
|
488
|
+
if (args.verdict) text += theme.fg("dim", ` → ${args.verdict}`);
|
|
489
|
+
return new Text(text, 0, 0);
|
|
490
|
+
},
|
|
491
|
+
renderResult(result: any, _options: any, theme: any) {
|
|
492
|
+
const d = readDetails(result);
|
|
493
|
+
if (result.isError || d?.error) {
|
|
494
|
+
return new Text(theme.fg("error", formatToolErrorText(result, d)), 0, 0);
|
|
495
|
+
}
|
|
496
|
+
return new Text(theme.fg("success", `UAT ${d?.sliceId ?? ""}: ${d?.verdict ?? "saved"}`), 0, 0);
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
pi.registerTool(uatResultSaveTool);
|
|
501
|
+
|
|
416
502
|
// ─── gsd_milestone_generate_id (formerly gsd_generate_milestone_id) ────
|
|
417
503
|
|
|
418
504
|
const milestoneGenerateIdExecute = async (_toolCallId: string, _params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
|