@opengsd/gsd-pi 1.1.1-dev.9bb7453 → 1.1.1-dev.a5a2de8
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/gsd/auto-dispatch.js +11 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +4 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +3 -4
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +18 -66
- package/dist/resources/extensions/gsd/auto-worktree.js +18 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +16 -10
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +19 -8
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +18 -29
- package/dist/resources/extensions/gsd/closeout-consistency-gate.js +61 -0
- package/dist/resources/extensions/gsd/guided-flow.js +89 -107
- package/dist/resources/extensions/gsd/milestone-closeout.js +3 -1
- package/dist/resources/extensions/gsd/pending-auto-start.js +0 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +3 -17
- package/dist/resources/extensions/gsd/recovery-classification.js +20 -0
- package/dist/resources/extensions/gsd/tool-contract.js +5 -0
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +17 -7
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +81 -4
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +169 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +3 -75
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
- 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 +10 -10
- 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 +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- 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/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +4 -3
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.js +3 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/types.d.ts +1 -0
- package/packages/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/types.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +3 -1
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +6 -6
- package/packages/pi-ai/dist/models.generated.js +6 -6
- 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/extensions/extension-upstream-types.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +2 -2
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +3 -2
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/render-utils.js +6 -0
- package/packages/pi-coding-agent/dist/core/tools/render-utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +3 -2
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- 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/gsd/auto-dispatch.ts +14 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +4 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +3 -3
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +43 -74
- package/src/resources/extensions/gsd/auto-worktree.ts +23 -5
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +16 -10
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +23 -8
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +50 -54
- package/src/resources/extensions/gsd/closeout-consistency-gate.ts +137 -0
- package/src/resources/extensions/gsd/guided-flow.ts +124 -134
- package/src/resources/extensions/gsd/milestone-closeout.ts +3 -1
- package/src/resources/extensions/gsd/pending-auto-start.ts +0 -2
- package/src/resources/extensions/gsd/prompts/run-uat.md +3 -17
- package/src/resources/extensions/gsd/recovery-classification.ts +20 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +10 -2
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/check-auto-start-pending-gate.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +26 -16
- package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +40 -1
- package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +31 -79
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +40 -4
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +9 -1
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +23 -5
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +221 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
- package/src/resources/extensions/gsd/tool-contract.ts +6 -0
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +38 -8
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +100 -5
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +186 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -75
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +0 -246
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +0 -218
- /package/dist/web/standalone/.next/static/{jBtwT9v1u2lUA3UEOy_ZH → 9y3LeeR2uGr2yRj9RjY3D}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{jBtwT9v1u2lUA3UEOy_ZH → 9y3LeeR2uGr2yRj9RjY3D}/_ssgManifest.js +0 -0
|
@@ -96,6 +96,10 @@ import { probeGitConflictState } from "./git-conflict-state.js";
|
|
|
96
96
|
import { runTurnGitAction } from "./git-service.js";
|
|
97
97
|
import { parseUnitId } from "./unit-id.js";
|
|
98
98
|
import { resolveExpectedArtifactPath } from "./auto-artifact-paths.js";
|
|
99
|
+
import {
|
|
100
|
+
checkCloseoutConsistencyGate,
|
|
101
|
+
formatCloseoutConsistencyBlock,
|
|
102
|
+
} from "./closeout-consistency-gate.js";
|
|
99
103
|
|
|
100
104
|
// ─── Types ────────────────────────────────────────────────────────────────
|
|
101
105
|
|
|
@@ -1635,6 +1639,16 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
1635
1639
|
prompt: await buildCompleteMilestonePrompt(mid, midTitle, basePath),
|
|
1636
1640
|
};
|
|
1637
1641
|
}
|
|
1642
|
+
if (milestone) {
|
|
1643
|
+
const closeoutGate = checkCloseoutConsistencyGate(mid, { refreshFromDisk: true });
|
|
1644
|
+
if (!closeoutGate.ok) {
|
|
1645
|
+
return {
|
|
1646
|
+
action: "stop",
|
|
1647
|
+
reason: formatCloseoutConsistencyBlock(closeoutGate),
|
|
1648
|
+
level: "warning",
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1638
1652
|
}
|
|
1639
1653
|
return {
|
|
1640
1654
|
action: "stop",
|
|
@@ -46,6 +46,7 @@ import { hasBrowserRequiredText } from "./browser-evidence.js";
|
|
|
46
46
|
import { debugLog } from "./debug-logger.js";
|
|
47
47
|
import { buildSkillActivationBlock, buildSkillDiscoveryVars } from "./skill-activation.js";
|
|
48
48
|
import { findMilestoneIds } from "./milestone-ids.js";
|
|
49
|
+
import { buildRunUatResultPresentation, RUN_UAT_TOOL_PRESENTATION_PLAN_ID } from "./tool-presentation-plan.js";
|
|
49
50
|
|
|
50
51
|
export { buildSkillActivationBlock, buildSkillDiscoveryVars };
|
|
51
52
|
|
|
@@ -3384,6 +3385,7 @@ export async function buildRunUatPrompt(
|
|
|
3384
3385
|
|
|
3385
3386
|
const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "ASSESSMENT"));
|
|
3386
3387
|
const uatType = resolveEffectiveUatType(uatContent);
|
|
3388
|
+
const canonicalPresentation = JSON.stringify(buildRunUatResultPresentation(), null, 2);
|
|
3387
3389
|
|
|
3388
3390
|
return loadPrompt("run-uat", {
|
|
3389
3391
|
workingDirectory: base,
|
|
@@ -3392,6 +3394,8 @@ export async function buildRunUatPrompt(
|
|
|
3392
3394
|
uatPath,
|
|
3393
3395
|
uatResultPath,
|
|
3394
3396
|
uatType,
|
|
3397
|
+
toolPresentationPlanId: RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
|
|
3398
|
+
canonicalPresentation,
|
|
3395
3399
|
inlinedContext,
|
|
3396
3400
|
skillActivation: buildSkillActivationBlock({
|
|
3397
3401
|
base,
|
|
@@ -54,6 +54,7 @@ import { isGsdWorktreePath } from "./worktree-root.js";
|
|
|
54
54
|
import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
|
|
55
55
|
import { hasImplementationArtifacts } from "./milestone-implementation-evidence.js";
|
|
56
56
|
import { loadAllCaptures, loadPendingCaptures } from "./captures.js";
|
|
57
|
+
import { checkCloseoutConsistencyGate } from "./closeout-consistency-gate.js";
|
|
57
58
|
|
|
58
59
|
// Re-export so existing consumers of auto-recovery.ts keep working.
|
|
59
60
|
export { resolveExpectedArtifactPath, diagnoseExpectedArtifact };
|
|
@@ -626,9 +627,8 @@ export function verifyExpectedArtifact(
|
|
|
626
627
|
if (summaryOutcome === "failure") return false;
|
|
627
628
|
const { milestone: mid } = parseUnitId(unitId);
|
|
628
629
|
if (mid && isDbAvailable()) {
|
|
629
|
-
const
|
|
630
|
-
if (!
|
|
631
|
-
if (!isClosedStatus(dbMilestone.status) && summaryOutcome !== "success") return false;
|
|
630
|
+
const closeoutGate = checkCloseoutConsistencyGate(mid, { refreshFromDisk: true });
|
|
631
|
+
if (!closeoutGate.ok) return false;
|
|
632
632
|
}
|
|
633
633
|
if (hasImplementationArtifacts(base, mid) === "absent") return false;
|
|
634
634
|
}
|
|
@@ -1,59 +1,13 @@
|
|
|
1
1
|
import { parseUnitId } from "./unit-id.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"browser_click",
|
|
7
|
-
"browser_type",
|
|
8
|
-
"browser_fill_form",
|
|
9
|
-
"browser_click_ref",
|
|
10
|
-
"browser_fill_ref",
|
|
11
|
-
"browser_wait_for",
|
|
12
|
-
"browser_assert",
|
|
13
|
-
"browser_verify",
|
|
14
|
-
"browser_screenshot",
|
|
15
|
-
"browser_snapshot_refs",
|
|
16
|
-
"browser_find",
|
|
17
|
-
"browser_get_console_logs",
|
|
18
|
-
"browser_get_network_logs",
|
|
19
|
-
"browser_evaluate",
|
|
20
|
-
"browser_reload",
|
|
21
|
-
"browser_batch",
|
|
22
|
-
"browser_act",
|
|
23
|
-
] as const;
|
|
2
|
+
import {
|
|
3
|
+
AUTO_UNIT_SCOPED_TOOLS,
|
|
4
|
+
getForbiddenGsdToolReason,
|
|
5
|
+
} from "./unit-tool-contracts.js";
|
|
24
6
|
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"gsd_summary_save",
|
|
30
|
-
"gsd_decision_save",
|
|
31
|
-
"gsd_requirement_save",
|
|
32
|
-
"gsd_requirement_update",
|
|
33
|
-
"gsd_plan_milestone",
|
|
34
|
-
"gsd_milestone_generate_id",
|
|
35
|
-
],
|
|
36
|
-
"discuss-slice": ["gsd_summary_save", "gsd_decision_save"],
|
|
37
|
-
"validate-milestone": ["gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
|
|
38
|
-
"complete-milestone": ["gsd_complete_milestone", "subagent"],
|
|
39
|
-
"research-slice": ["gsd_summary_save", "gsd_decision_save"],
|
|
40
|
-
"plan-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
|
|
41
|
-
"refine-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
|
|
42
|
-
"replan-slice": ["gsd_replan_slice", "gsd_plan_task", "gsd_decision_save"],
|
|
43
|
-
"complete-slice": ["gsd_slice_complete", "gsd_task_reopen", "gsd_replan_slice", "gsd_decision_save", "gsd_requirement_update", "subagent"],
|
|
44
|
-
"reassess-roadmap": ["gsd_reassess_roadmap"],
|
|
45
|
-
"execute-task": ["gsd_task_complete", "gsd_decision_save"],
|
|
46
|
-
"execute-task-simple": ["gsd_task_complete", "gsd_decision_save"],
|
|
47
|
-
"reactive-execute": ["gsd_task_complete", "gsd_decision_save"],
|
|
48
|
-
"run-uat": [...RUN_UAT_WORKFLOW_TOOL_NAMES, "subagent", ...RUN_UAT_BROWSER_TOOL_NAMES],
|
|
49
|
-
"gate-evaluate": ["gsd_save_gate_result"],
|
|
50
|
-
"rewrite-docs": ["gsd_summary_save", "gsd_decision_save"],
|
|
51
|
-
"workflow-preferences": ["gsd_summary_save"],
|
|
52
|
-
"discuss-project": ["gsd_summary_save", "gsd_decision_save", "gsd_requirement_save"],
|
|
53
|
-
"discuss-requirements": ["gsd_requirement_save", "gsd_summary_save"],
|
|
54
|
-
"research-decision": ["gsd_summary_save"],
|
|
55
|
-
"research-project": ["gsd_summary_save", "gsd_decision_save"],
|
|
56
|
-
};
|
|
7
|
+
export {
|
|
8
|
+
AUTO_UNIT_SCOPED_TOOLS,
|
|
9
|
+
RUN_UAT_BROWSER_TOOL_NAMES,
|
|
10
|
+
} from "./unit-tool-contracts.js";
|
|
57
11
|
|
|
58
12
|
const WORKFLOW_TOOL_ALIASES: Record<string, string> = {
|
|
59
13
|
gsd_save_decision: "gsd_decision_save",
|
|
@@ -97,6 +51,14 @@ const SCOPED_GSD_LIFECYCLE_TOOLS = new Set(
|
|
|
97
51
|
.map(canonicalWorkflowToolName),
|
|
98
52
|
);
|
|
99
53
|
|
|
54
|
+
export const GSD_PHASE_SCOPE_DISPLAY_REASON = "This GSD phase only allows its scoped workflow tools.";
|
|
55
|
+
|
|
56
|
+
type AutoUnitToolScopeResult = {
|
|
57
|
+
block: boolean;
|
|
58
|
+
reason?: string;
|
|
59
|
+
displayReason?: string;
|
|
60
|
+
};
|
|
61
|
+
|
|
100
62
|
function stripMcpToolPrefix(toolName: string): string {
|
|
101
63
|
if (!toolName.startsWith("mcp__")) return toolName;
|
|
102
64
|
const toolSeparator = toolName.indexOf("__", "mcp__".length);
|
|
@@ -114,12 +76,20 @@ export function isWorkflowAliasTool(toolName: string): boolean {
|
|
|
114
76
|
|
|
115
77
|
function hardBlockReason(unitType: string, what: string): string {
|
|
116
78
|
return [
|
|
117
|
-
`HARD BLOCK: unit "${unitType}"
|
|
79
|
+
`HARD BLOCK: Tool Contract failure for unit "${unitType}" — ${what}.`,
|
|
118
80
|
"This is a mechanical phase-boundary gate. You MUST NOT proceed, retry the same call,",
|
|
119
81
|
"or route around this block; the orchestrator owns phase transitions.",
|
|
120
82
|
].join(" ");
|
|
121
83
|
}
|
|
122
84
|
|
|
85
|
+
function hardBlock(unitType: string, what: string): AutoUnitToolScopeResult {
|
|
86
|
+
return {
|
|
87
|
+
block: true,
|
|
88
|
+
reason: hardBlockReason(unitType, what),
|
|
89
|
+
displayReason: GSD_PHASE_SCOPE_DISPLAY_REASON,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
123
93
|
function allowedGsdToolsForUnit(unitType: string): string[] {
|
|
124
94
|
return [...new Set(
|
|
125
95
|
(AUTO_UNIT_SCOPED_TOOLS[unitType] ?? [])
|
|
@@ -144,7 +114,7 @@ function shouldBlockTaskCompletionScope(
|
|
|
144
114
|
unitId: string | undefined,
|
|
145
115
|
toolName: string,
|
|
146
116
|
input: unknown,
|
|
147
|
-
):
|
|
117
|
+
): AutoUnitToolScopeResult {
|
|
148
118
|
if (!EXECUTE_TASK_UNIT_TYPES.has(unitType)) return { block: false };
|
|
149
119
|
if (canonicalWorkflowToolName(toolName) !== "gsd_task_complete") return { block: false };
|
|
150
120
|
if (!unitId) return { block: false };
|
|
@@ -165,13 +135,10 @@ function shouldBlockTaskCompletionScope(
|
|
|
165
135
|
return { block: false };
|
|
166
136
|
}
|
|
167
137
|
|
|
168
|
-
return
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`gsd_task_complete may only complete the active task ${expected.milestone}/${expected.slice}/${expected.task}; requested ${actualMilestone}/${actualSlice}/${actualTask}`,
|
|
173
|
-
),
|
|
174
|
-
};
|
|
138
|
+
return hardBlock(
|
|
139
|
+
unitType,
|
|
140
|
+
`gsd_task_complete may only complete the active task ${expected.milestone}/${expected.slice}/${expected.task}; requested ${actualMilestone}/${actualSlice}/${actualTask}`,
|
|
141
|
+
);
|
|
175
142
|
}
|
|
176
143
|
|
|
177
144
|
export function shouldBlockAutoUnitToolCall(
|
|
@@ -179,15 +146,12 @@ export function shouldBlockAutoUnitToolCall(
|
|
|
179
146
|
toolName: string,
|
|
180
147
|
input?: unknown,
|
|
181
148
|
unitId?: string,
|
|
182
|
-
):
|
|
149
|
+
): AutoUnitToolScopeResult {
|
|
183
150
|
const scopedTools = AUTO_UNIT_SCOPED_TOOLS[unitType];
|
|
184
151
|
if (!scopedTools) return { block: false };
|
|
185
152
|
|
|
186
153
|
if (isNativeWorkflowTool(toolName)) {
|
|
187
|
-
return
|
|
188
|
-
block: true,
|
|
189
|
-
reason: hardBlockReason(unitType, "native Workflow is not permitted inside a dispatched GSD auto-mode unit"),
|
|
190
|
-
};
|
|
154
|
+
return hardBlock(unitType, "native Workflow is not permitted inside a dispatched GSD auto-mode unit");
|
|
191
155
|
}
|
|
192
156
|
|
|
193
157
|
const taskScope = shouldBlockTaskCompletionScope(unitType, unitId, toolName, input);
|
|
@@ -199,11 +163,16 @@ export function shouldBlockAutoUnitToolCall(
|
|
|
199
163
|
const allowedTools = allowedGsdToolsForUnit(unitType);
|
|
200
164
|
if (allowedTools.includes(canonicalTool)) return { block: false };
|
|
201
165
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
166
|
+
const forbiddenReason = getForbiddenGsdToolReason(unitType, canonicalTool);
|
|
167
|
+
if (forbiddenReason) {
|
|
168
|
+
return hardBlock(
|
|
205
169
|
unitType,
|
|
206
|
-
`GSD lifecycle tool "${canonicalTool}" is not permitted;
|
|
207
|
-
)
|
|
208
|
-
}
|
|
170
|
+
`GSD lifecycle tool "${canonicalTool}" is not permitted; ${forbiddenReason} Fix unit-tool-contracts.ts or the ${unitType} prompt.`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return hardBlock(
|
|
175
|
+
unitType,
|
|
176
|
+
`GSD lifecycle tool "${canonicalTool}" is not permitted; allowed GSD tools: ${allowedTools.length > 0 ? allowedTools.join(", ") : "(none)"}`,
|
|
177
|
+
);
|
|
209
178
|
}
|
|
@@ -83,6 +83,11 @@ import {
|
|
|
83
83
|
nativeWorktreeList,
|
|
84
84
|
nativeLsFiles,
|
|
85
85
|
} from "./native-git-bridge.js";
|
|
86
|
+
import {
|
|
87
|
+
CLOSEOUT_CONSISTENCY_BLOCKED_REASON,
|
|
88
|
+
checkCloseoutConsistencyGate,
|
|
89
|
+
formatCloseoutConsistencyBlock,
|
|
90
|
+
} from "./closeout-consistency-gate.js";
|
|
86
91
|
import { gsdHome } from "./gsd-home.js";
|
|
87
92
|
import { type MilestoneScope, type GsdWorkspace, createWorkspace } from "./workspace.js";
|
|
88
93
|
import {
|
|
@@ -1771,16 +1776,29 @@ export function mergeMilestoneToMain(
|
|
|
1771
1776
|
// symlink layout) — ATTACHing a WAL-mode file to itself corrupts the
|
|
1772
1777
|
// database (#2823).
|
|
1773
1778
|
if (isDbAvailable()) {
|
|
1779
|
+
const contract = resolveGsdPathContract(worktreeCwd, originalBasePath_);
|
|
1780
|
+
const worktreeDbPath = join(contract.worktreeGsd ?? join(worktreeCwd, ".gsd"), "gsd.db");
|
|
1781
|
+
const mainDbPath = contract.projectDb;
|
|
1774
1782
|
try {
|
|
1775
|
-
const
|
|
1776
|
-
|
|
1777
|
-
|
|
1783
|
+
const activeDbPath = getDbPath();
|
|
1784
|
+
if (activeDbPath && _shouldReconcileWorktreeDb(activeDbPath, mainDbPath)) {
|
|
1785
|
+
closeDatabase();
|
|
1786
|
+
if (!openDatabase(mainDbPath)) {
|
|
1787
|
+
throw new Error(`cannot open project DB at ${mainDbPath}`);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1778
1790
|
if (_shouldReconcileWorktreeDb(worktreeDbPath, mainDbPath)) {
|
|
1779
1791
|
reconcileWorktreeDb(mainDbPath, worktreeDbPath);
|
|
1780
1792
|
}
|
|
1781
1793
|
} catch (err) {
|
|
1782
|
-
|
|
1783
|
-
logError("worktree",
|
|
1794
|
+
const message = `DB reconciliation failed before milestone ${milestoneId} merge: ${err instanceof Error ? err.message : String(err)}`;
|
|
1795
|
+
logError("worktree", message);
|
|
1796
|
+
throw new GSDError(GSD_GIT_ERROR, `${message}. Recovery reason: ${CLOSEOUT_CONSISTENCY_BLOCKED_REASON}.`);
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
const closeoutGate = checkCloseoutConsistencyGate(milestoneId);
|
|
1800
|
+
if (!closeoutGate.ok) {
|
|
1801
|
+
throw new GSDError(GSD_GIT_ERROR, formatCloseoutConsistencyBlock(closeoutGate));
|
|
1784
1802
|
}
|
|
1785
1803
|
}
|
|
1786
1804
|
|
|
@@ -78,6 +78,9 @@ function readDetails(result: any): any {
|
|
|
78
78
|
|
|
79
79
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- result shape varies by tool
|
|
80
80
|
function formatToolErrorText(result: any, details: any): string {
|
|
81
|
+
if (typeof details?.displayReason === "string" && details.displayReason) {
|
|
82
|
+
return details.displayReason;
|
|
83
|
+
}
|
|
81
84
|
const message = details?.error
|
|
82
85
|
?? result?.content?.find((entry: { type?: string; text?: string }) => entry.type === "text")?.text
|
|
83
86
|
?? "unknown";
|
|
@@ -424,6 +427,9 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
424
427
|
kind: StringEnum(["gsd_uat_exec", "gsd_exec", "screenshot", "log", "url", "browser"], { description: "Evidence kind" }),
|
|
425
428
|
ref: Type.String({ description: "Evidence ID, approved .gsd path, or URL" }),
|
|
426
429
|
note: Type.Optional(Type.String({ description: "Short evidence note" })),
|
|
430
|
+
unitType: Type.Optional(Type.String({ description: "Unit that produced the evidence" })),
|
|
431
|
+
tool: Type.Optional(Type.String({ description: "Tool that produced the evidence" })),
|
|
432
|
+
executionId: Type.Optional(Type.String({ description: "Stable execution or artifact id" })),
|
|
427
433
|
});
|
|
428
434
|
|
|
429
435
|
const uatCheck = Type.Object({
|
|
@@ -437,17 +443,17 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
437
443
|
});
|
|
438
444
|
|
|
439
445
|
const toolPresentationBlock = Type.Object({
|
|
440
|
-
surface: StringEnum(["provider-tools", "claude-code-sdk", "mcp", "hybrid"], { description: "Tool presentation surface" }),
|
|
446
|
+
surface: Type.Optional(StringEnum(["provider-tools", "claude-code-sdk", "mcp", "hybrid"], { description: "Tool presentation surface" })),
|
|
441
447
|
model: Type.Optional(Type.Object({
|
|
442
448
|
provider: Type.Optional(Type.String()),
|
|
443
449
|
api: Type.Optional(Type.String()),
|
|
444
450
|
id: Type.Optional(Type.String()),
|
|
445
451
|
})),
|
|
446
|
-
presentedTools: Type.Array(Type.String(), { description: "Tool names actually presented to the model" }),
|
|
447
|
-
blockedTools: Type.Array(Type.Object({
|
|
452
|
+
presentedTools: Type.Optional(Type.Array(Type.String(), { description: "Tool names actually presented to the model" })),
|
|
453
|
+
blockedTools: Type.Optional(Type.Array(Type.Object({
|
|
448
454
|
name: Type.String(),
|
|
449
455
|
reason: Type.String(),
|
|
450
|
-
}), { description: "Tool names blocked from the model with reasons" }),
|
|
456
|
+
}), { description: "Tool names blocked from the model with reasons" })),
|
|
451
457
|
aliases: Type.Optional(Type.Array(Type.Object({
|
|
452
458
|
requested: Type.String(),
|
|
453
459
|
canonical: Type.String(),
|
|
@@ -471,12 +477,12 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
471
477
|
"Do not use raw gsd_summary_save as a substitute for UAT results.",
|
|
472
478
|
],
|
|
473
479
|
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:
|
|
477
|
-
verdict:
|
|
478
|
-
checks: Type.Array(uatCheck, { description: "Structured check results" }),
|
|
479
|
-
presentation: toolPresentationBlock,
|
|
480
|
+
milestoneId: Type.Optional(Type.String({ description: "Milestone ID (e.g. M001)" })),
|
|
481
|
+
sliceId: Type.Optional(Type.String({ description: "Slice ID (e.g. S01)" })),
|
|
482
|
+
uatType: Type.Optional(Type.String({ description: "Declared UAT mode" })),
|
|
483
|
+
verdict: Type.Optional(Type.String({ description: "Overall UAT verdict: PASS, FAIL, or PARTIAL" })),
|
|
484
|
+
checks: Type.Optional(Type.Array(uatCheck, { description: "Structured check results" })),
|
|
485
|
+
presentation: Type.Optional(toolPresentationBlock),
|
|
480
486
|
notes: Type.Optional(Type.String({ description: "Overall verdict rationale" })),
|
|
481
487
|
attempt: Type.Optional(Type.String({ description: "Attempt number or auto" })),
|
|
482
488
|
previousAttemptId: Type.Optional(Type.String({ description: "Prior attempt ID, when retrying" })),
|
|
@@ -38,7 +38,7 @@ import { getGuidedUnitContext } from "../guided-unit-context.js";
|
|
|
38
38
|
import { registerPlanMilestoneSchemaRecovery } from "./plan-milestone-schema-recovery.js";
|
|
39
39
|
import { AUTO_UNIT_SCOPED_TOOLS, RUN_UAT_BROWSER_TOOL_NAMES, isWorkflowAliasTool } from "../auto-unit-tool-scope.js";
|
|
40
40
|
import { filterToolsForProvider } from "../model-router.js";
|
|
41
|
-
import { RUN_UAT_WORKFLOW_TOOL_NAMES } from "../tool-presentation-plan.js";
|
|
41
|
+
import { RUN_UAT_READ_ONLY_TOOL_NAMES, RUN_UAT_WORKFLOW_TOOL_NAMES } from "../tool-presentation-plan.js";
|
|
42
42
|
|
|
43
43
|
let approvalQuestionAbortInFlight = false;
|
|
44
44
|
|
|
@@ -252,7 +252,12 @@ export function buildRunUatGsdToolSet(
|
|
|
252
252
|
): string[] {
|
|
253
253
|
const scoped = resolveScopedToolNames(
|
|
254
254
|
[...activeToolNames, ...registeredToolNames],
|
|
255
|
-
[
|
|
255
|
+
[
|
|
256
|
+
...RUN_UAT_WORKFLOW_TOOL_NAMES,
|
|
257
|
+
...RUN_UAT_READ_ONLY_TOOL_NAMES,
|
|
258
|
+
"subagent",
|
|
259
|
+
...RUN_UAT_BROWSER_TOOL_NAMES,
|
|
260
|
+
],
|
|
256
261
|
);
|
|
257
262
|
return [...new Set(scoped)];
|
|
258
263
|
}
|
|
@@ -480,22 +485,30 @@ function isContextDraftSummarySave(toolName: string, input: unknown): boolean {
|
|
|
480
485
|
return (input as { artifact_type?: unknown }).artifact_type === "CONTEXT-DRAFT";
|
|
481
486
|
}
|
|
482
487
|
|
|
488
|
+
function withDepthGateDisplayReason<T extends { block: boolean; reason?: string }>(
|
|
489
|
+
result: T,
|
|
490
|
+
displayReason = "Depth confirmation is waiting for your answer.",
|
|
491
|
+
): T & { displayReason?: string } {
|
|
492
|
+
if (!result.block) return result;
|
|
493
|
+
return { ...result, displayReason };
|
|
494
|
+
}
|
|
495
|
+
|
|
483
496
|
function shouldBlockDeferredApprovalTool(
|
|
484
497
|
toolName: string,
|
|
485
498
|
input: unknown,
|
|
486
499
|
basePath: string,
|
|
487
|
-
): { block: boolean; reason?: string } {
|
|
500
|
+
): { block: boolean; reason?: string; displayReason?: string } {
|
|
488
501
|
if (deferredApprovalGate?.basePath !== basePath) return { block: false };
|
|
489
502
|
if (toolName === "ask_user_questions") return { block: false };
|
|
490
503
|
if (isContextDraftSummarySave(toolName, input)) return { block: false };
|
|
491
|
-
return {
|
|
504
|
+
return withDepthGateDisplayReason({
|
|
492
505
|
block: true,
|
|
493
506
|
reason: [
|
|
494
507
|
`HARD BLOCK: Approval question "${deferredApprovalGate.gateId}" has been shown to the user.`,
|
|
495
508
|
`Only CONTEXT-DRAFT persistence may finish in this same assistant turn.`,
|
|
496
509
|
`Wait for the user's answer before calling additional tools.`,
|
|
497
510
|
].join(" "),
|
|
498
|
-
};
|
|
511
|
+
});
|
|
499
512
|
}
|
|
500
513
|
|
|
501
514
|
export function resolveNotificationStoreBasePath(basePath: string): string {
|
|
@@ -917,7 +930,7 @@ export function registerHooks(
|
|
|
917
930
|
"Depth confirmation is waiting for your answer — pausing auto-mode.",
|
|
918
931
|
);
|
|
919
932
|
}
|
|
920
|
-
return bashGuard;
|
|
933
|
+
return withDepthGateDisplayReason(bashGuard);
|
|
921
934
|
}
|
|
922
935
|
} else {
|
|
923
936
|
const gateGuard = shouldBlockPendingGate(
|
|
@@ -935,7 +948,7 @@ export function registerHooks(
|
|
|
935
948
|
"Depth confirmation is waiting for your answer — pausing auto-mode.",
|
|
936
949
|
);
|
|
937
950
|
}
|
|
938
|
-
return gateGuard;
|
|
951
|
+
return withDepthGateDisplayReason(gateGuard);
|
|
939
952
|
}
|
|
940
953
|
}
|
|
941
954
|
}
|
|
@@ -1040,7 +1053,9 @@ export function registerHooks(
|
|
|
1040
1053
|
isQueuePhaseActive(discussionBasePath),
|
|
1041
1054
|
discussionBasePath,
|
|
1042
1055
|
);
|
|
1043
|
-
if (result.block)
|
|
1056
|
+
if (result.block) {
|
|
1057
|
+
return withDepthGateDisplayReason(result, "Depth check required before writing milestone context.");
|
|
1058
|
+
}
|
|
1044
1059
|
});
|
|
1045
1060
|
|
|
1046
1061
|
// ── Safety harness: evidence collection + destructive command blocking ──
|
|
@@ -4,7 +4,7 @@ import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
|
4
4
|
|
|
5
5
|
import { minimatch } from "minimatch";
|
|
6
6
|
|
|
7
|
-
import { shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.js";
|
|
7
|
+
import { GSD_PHASE_SCOPE_DISPLAY_REASON, shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.js";
|
|
8
8
|
import { getIsolationMode } from "../preferences.js";
|
|
9
9
|
import { compileSubagentPermissionContract, type ToolsPolicy } from "../unit-context-manifest.js";
|
|
10
10
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -772,6 +772,20 @@ function blockReason(unitType: string, mode: string, what: string): string {
|
|
|
772
772
|
].join(" ");
|
|
773
773
|
}
|
|
774
774
|
|
|
775
|
+
function planningBlock(unitType: string, mode: string, what: string): PlanningUnitBlockResult {
|
|
776
|
+
return {
|
|
777
|
+
block: true,
|
|
778
|
+
reason: blockReason(unitType, mode, what),
|
|
779
|
+
displayReason: GSD_PHASE_SCOPE_DISPLAY_REASON,
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
type PlanningUnitBlockResult = {
|
|
784
|
+
block: boolean;
|
|
785
|
+
reason?: string;
|
|
786
|
+
displayReason?: string;
|
|
787
|
+
};
|
|
788
|
+
|
|
775
789
|
/**
|
|
776
790
|
* Planning-unit tool-policy enforcement. Returns { block } per the policy
|
|
777
791
|
* resolved from the active unit's manifest:
|
|
@@ -812,7 +826,7 @@ export function shouldBlockPlanningUnit(
|
|
|
812
826
|
agentClasses?: readonly string[],
|
|
813
827
|
toolInput?: unknown,
|
|
814
828
|
unitId?: string,
|
|
815
|
-
):
|
|
829
|
+
): PlanningUnitBlockResult {
|
|
816
830
|
const tool = canonicalToolName(toolName);
|
|
817
831
|
const autoScopeGuard = shouldBlockAutoUnitToolCall(unitType, toolName, toolInput, unitId);
|
|
818
832
|
if (autoScopeGuard.block) return autoScopeGuard;
|
|
@@ -825,10 +839,10 @@ export function shouldBlockPlanningUnit(
|
|
|
825
839
|
if (PLANNING_SAFE_TOOLS.has(tool)) return { block: false };
|
|
826
840
|
if (tool.startsWith("gsd_")) return { block: false };
|
|
827
841
|
if (PLANNING_WRITE_TOOLS.has(tool) || tool === "bash" || PLANNING_SUBAGENT_TOOLS.has(tool)) {
|
|
828
|
-
return
|
|
842
|
+
return planningBlock(unitType, policy.mode, `${tool} is not permitted (read-only)`);
|
|
829
843
|
}
|
|
830
844
|
// Unknown tool in read-only mode — block by default.
|
|
831
|
-
return
|
|
845
|
+
return planningBlock(unitType, policy.mode, `tool "${tool}" is not on the read-only allowlist`);
|
|
832
846
|
}
|
|
833
847
|
|
|
834
848
|
// planning / planning-dispatch / docs / verification modes share the same surface for safe tools, bash, and subagent.
|
|
@@ -846,14 +860,11 @@ export function shouldBlockPlanningUnit(
|
|
|
846
860
|
// instead of silently bypassing the gate.
|
|
847
861
|
if (agentClasses === undefined) {
|
|
848
862
|
warnMissingControlledDispatchAgentClasses(unitType, policy.mode, tool);
|
|
849
|
-
return
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
`subagent dispatch blocked: stale caller did not supply agent identities for "${tool}"; update extractSubagentAgentClasses to handle this input shape`,
|
|
855
|
-
),
|
|
856
|
-
};
|
|
863
|
+
return planningBlock(
|
|
864
|
+
unitType,
|
|
865
|
+
policy.mode,
|
|
866
|
+
`subagent dispatch blocked: stale caller did not supply agent identities for "${tool}"; update extractSubagentAgentClasses to handle this input shape`,
|
|
867
|
+
);
|
|
857
868
|
}
|
|
858
869
|
// agentClasses was explicitly provided but resolved to an empty list (for
|
|
859
870
|
// example, a bare tool call with no agent field). Pass through; no agents
|
|
@@ -863,57 +874,45 @@ export function shouldBlockPlanningUnit(
|
|
|
863
874
|
}
|
|
864
875
|
const globallyDisallowed = requested.find(a => !isReadOnlySpecialist(a));
|
|
865
876
|
if (globallyDisallowed) {
|
|
866
|
-
return
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
`subagent dispatch of "${globallyDisallowed}" not permitted; only read-only specialists (${allowedPlanningDispatchAgentsList()}) may be dispatched from ${policy.mode} units`,
|
|
872
|
-
),
|
|
873
|
-
};
|
|
877
|
+
return planningBlock(
|
|
878
|
+
unitType,
|
|
879
|
+
policy.mode,
|
|
880
|
+
`subagent dispatch of "${globallyDisallowed}" not permitted; only read-only specialists (${allowedPlanningDispatchAgentsList()}) may be dispatched from ${policy.mode} units`,
|
|
881
|
+
);
|
|
874
882
|
}
|
|
875
883
|
const disallowedByPolicy = requested.find(a => !allowed.has(a));
|
|
876
884
|
if (disallowedByPolicy) {
|
|
877
|
-
return
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
`subagent dispatch of "${disallowedByPolicy}" not permitted by ToolsPolicy.allowedSubagents; permitted agents for this unit: ${allowedSubagents.join(", ")}`,
|
|
883
|
-
),
|
|
884
|
-
};
|
|
885
|
+
return planningBlock(
|
|
886
|
+
unitType,
|
|
887
|
+
policy.mode,
|
|
888
|
+
`subagent dispatch of "${disallowedByPolicy}" not permitted by ToolsPolicy.allowedSubagents; permitted agents for this unit: ${allowedSubagents.join(", ")}`,
|
|
889
|
+
);
|
|
885
890
|
}
|
|
886
891
|
return { block: false };
|
|
887
892
|
}
|
|
888
|
-
return
|
|
893
|
+
return planningBlock(unitType, policy.mode, "subagent dispatch is not permitted in planning units");
|
|
889
894
|
}
|
|
890
895
|
|
|
891
896
|
if (tool === "bash") {
|
|
892
897
|
if (policy.mode === "verification") {
|
|
893
898
|
if (BASH_VERIFICATION_RE.test(pathOrCommand) || BASH_READ_ONLY_RE.test(pathOrCommand)) return { block: false };
|
|
894
|
-
return
|
|
895
|
-
block: true,
|
|
896
|
-
reason: blockReason(
|
|
897
|
-
unitType,
|
|
898
|
-
policy.mode,
|
|
899
|
-
`bash is restricted to build/test verification commands (npm run build, npm test, etc.); cannot run "${pathOrCommand.slice(0, 80)}${pathOrCommand.length > 80 ? "…" : ""}"`,
|
|
900
|
-
),
|
|
901
|
-
};
|
|
902
|
-
}
|
|
903
|
-
if (BASH_READ_ONLY_RE.test(pathOrCommand)) return { block: false };
|
|
904
|
-
return {
|
|
905
|
-
block: true,
|
|
906
|
-
reason: blockReason(
|
|
899
|
+
return planningBlock(
|
|
907
900
|
unitType,
|
|
908
901
|
policy.mode,
|
|
909
|
-
`bash is restricted to
|
|
910
|
-
)
|
|
911
|
-
}
|
|
902
|
+
`bash is restricted to build/test verification commands (npm run build, npm test, etc.); cannot run "${pathOrCommand.slice(0, 80)}${pathOrCommand.length > 80 ? "…" : ""}"`,
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
if (BASH_READ_ONLY_RE.test(pathOrCommand)) return { block: false };
|
|
906
|
+
return planningBlock(
|
|
907
|
+
unitType,
|
|
908
|
+
policy.mode,
|
|
909
|
+
`bash is restricted to read-only commands (cat/grep/git log/etc); cannot run "${pathOrCommand.slice(0, 80)}${pathOrCommand.length > 80 ? "…" : ""}"`,
|
|
910
|
+
);
|
|
912
911
|
}
|
|
913
912
|
|
|
914
913
|
if (PLANNING_WRITE_TOOLS.has(tool)) {
|
|
915
914
|
if (!pathOrCommand) {
|
|
916
|
-
return
|
|
915
|
+
return planningBlock(unitType, policy.mode, `${tool} called with empty path`);
|
|
917
916
|
}
|
|
918
917
|
const absPath = isAbsolute(pathOrCommand) ? pathOrCommand : resolve(basePath, pathOrCommand);
|
|
919
918
|
|
|
@@ -925,14 +924,11 @@ export function shouldBlockPlanningUnit(
|
|
|
925
924
|
return { block: false };
|
|
926
925
|
}
|
|
927
926
|
|
|
928
|
-
return
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
`cannot ${tool} "${pathOrCommand}" — writes are restricted to .gsd/${policy.mode === "docs" ? " and " + policy.allowedPathGlobs.join(", ") : ""}`,
|
|
934
|
-
),
|
|
935
|
-
};
|
|
927
|
+
return planningBlock(
|
|
928
|
+
unitType,
|
|
929
|
+
policy.mode,
|
|
930
|
+
`cannot ${tool} "${pathOrCommand}" — writes are restricted to .gsd/${policy.mode === "docs" ? " and " + policy.allowedPathGlobs.join(", ") : ""}`,
|
|
931
|
+
);
|
|
936
932
|
}
|
|
937
933
|
|
|
938
934
|
// Unknown tool name — pass through. Other layers (queue, pending-gate,
|