@workflow-cannon/workspace-kit 0.18.0 → 0.24.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 +23 -9
- package/dist/cli/doctor-planning-issues.js +3 -22
- package/dist/cli/run-command.js +22 -38
- package/dist/cli.js +95 -4
- package/dist/contracts/command-manifest.d.ts +17 -0
- package/dist/contracts/command-manifest.js +1 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/module-contract.d.ts +12 -11
- package/dist/core/agent-instruction-surface.d.ts +33 -0
- package/dist/core/agent-instruction-surface.js +46 -0
- package/dist/core/config-cli.js +13 -17
- package/dist/core/config-metadata.js +61 -2
- package/dist/core/index.d.ts +4 -1
- package/dist/core/index.js +3 -0
- package/dist/core/module-command-router.js +19 -1
- package/dist/core/module-registry-resolve.d.ts +27 -0
- package/dist/core/module-registry-resolve.js +91 -0
- package/dist/core/module-registry.d.ts +14 -0
- package/dist/core/module-registry.js +57 -0
- package/dist/core/planning/build-plan-session-file.d.ts +29 -0
- package/dist/core/planning/build-plan-session-file.js +58 -0
- package/dist/core/planning/index.d.ts +17 -0
- package/dist/core/planning/index.js +15 -0
- package/dist/core/policy.js +18 -8
- package/dist/core/state/unified-state-db.d.ts +21 -0
- package/dist/core/state/unified-state-db.js +80 -0
- package/dist/core/workspace-kit-config.js +8 -0
- package/dist/modules/agent-behavior/builtins.d.ts +3 -0
- package/dist/modules/agent-behavior/builtins.js +71 -0
- package/dist/modules/agent-behavior/explain.d.ts +6 -0
- package/dist/modules/agent-behavior/explain.js +46 -0
- package/dist/modules/agent-behavior/index.d.ts +4 -0
- package/dist/modules/agent-behavior/index.js +461 -0
- package/dist/modules/agent-behavior/interview-session-file.d.ts +9 -0
- package/dist/modules/agent-behavior/interview-session-file.js +43 -0
- package/dist/modules/agent-behavior/interview.d.ts +13 -0
- package/dist/modules/agent-behavior/interview.js +88 -0
- package/dist/modules/agent-behavior/persistence.d.ts +6 -0
- package/dist/modules/agent-behavior/persistence.js +89 -0
- package/dist/modules/agent-behavior/store.d.ts +34 -0
- package/dist/modules/agent-behavior/store.js +119 -0
- package/dist/modules/agent-behavior/types.d.ts +28 -0
- package/dist/modules/agent-behavior/types.js +1 -0
- package/dist/modules/agent-behavior/validate.d.ts +11 -0
- package/dist/modules/agent-behavior/validate.js +123 -0
- package/dist/modules/approvals/index.js +54 -51
- package/dist/modules/approvals/policy-sensitive-commands.d.ts +4 -0
- package/dist/modules/approvals/policy-sensitive-commands.js +4 -0
- package/dist/modules/approvals/review-runtime.js +1 -2
- package/dist/modules/documentation/index.js +47 -45
- package/dist/modules/documentation/normalizer.d.ts +3 -0
- package/dist/modules/documentation/normalizer.js +171 -0
- package/dist/modules/documentation/parser.d.ts +7 -0
- package/dist/modules/documentation/parser.js +39 -0
- package/dist/modules/documentation/policy-sensitive-commands.d.ts +5 -0
- package/dist/modules/documentation/policy-sensitive-commands.js +8 -0
- package/dist/modules/documentation/renderer.d.ts +23 -0
- package/dist/modules/documentation/renderer.js +105 -0
- package/dist/modules/documentation/runtime-batch.d.ts +10 -0
- package/dist/modules/documentation/runtime-batch.js +67 -0
- package/dist/modules/documentation/runtime-config.d.ts +11 -0
- package/dist/modules/documentation/runtime-config.js +54 -0
- package/dist/modules/documentation/runtime-render-support.d.ts +8 -0
- package/dist/modules/documentation/runtime-render-support.js +36 -0
- package/dist/modules/documentation/runtime.js +22 -510
- package/dist/modules/documentation/types.d.ts +182 -0
- package/dist/modules/documentation/validator.d.ts +8 -0
- package/dist/modules/documentation/validator.js +234 -0
- package/dist/modules/documentation/view-models.d.ts +3 -0
- package/dist/modules/documentation/view-models.js +124 -0
- package/dist/modules/improvement/generate-recommendations-runtime.js +3 -3
- package/dist/modules/improvement/improvement-state.d.ts +2 -2
- package/dist/modules/improvement/improvement-state.js +52 -23
- package/dist/modules/improvement/index.js +140 -138
- package/dist/modules/improvement/ingest.d.ts +1 -1
- package/dist/modules/improvement/policy-sensitive-commands.d.ts +4 -0
- package/dist/modules/improvement/policy-sensitive-commands.js +7 -0
- package/dist/modules/index.d.ts +6 -0
- package/dist/modules/index.js +17 -0
- package/dist/modules/planning/index.js +384 -50
- package/dist/modules/planning/question-engine.d.ts +2 -0
- package/dist/modules/planning/question-engine.js +8 -1
- package/dist/modules/task-engine/doctor-planning-persistence.js +21 -13
- package/dist/modules/task-engine/index.d.ts +1 -2
- package/dist/modules/task-engine/index.js +1 -1143
- package/dist/modules/task-engine/migrate-task-persistence-runtime.js +31 -4
- package/dist/modules/task-engine/migrate-wishlist-intake-runtime.d.ts +2 -0
- package/dist/modules/task-engine/migrate-wishlist-intake-runtime.js +146 -0
- package/dist/modules/task-engine/planning-open.d.ts +2 -9
- package/dist/modules/task-engine/planning-open.js +4 -15
- package/dist/modules/task-engine/policy-sensitive-commands.d.ts +5 -0
- package/dist/modules/task-engine/policy-sensitive-commands.js +5 -0
- package/dist/modules/task-engine/sqlite-dual-planning.d.ts +11 -2
- package/dist/modules/task-engine/sqlite-dual-planning.js +134 -28
- package/dist/modules/task-engine/strict-task-validation.js +3 -0
- package/dist/modules/task-engine/suggestions.js +2 -1
- package/dist/modules/task-engine/task-engine-internal.d.ts +2 -0
- package/dist/modules/task-engine/task-engine-internal.js +1304 -0
- package/dist/modules/task-engine/task-type-validation.js +40 -0
- package/dist/modules/task-engine/wishlist-intake.d.ts +22 -0
- package/dist/modules/task-engine/wishlist-intake.js +180 -0
- package/dist/modules/task-engine/wishlist-validation.d.ts +4 -0
- package/dist/modules/task-engine/wishlist-validation.js +19 -0
- package/dist/modules/workspace-config/index.js +9 -11
- package/package.json +2 -2
- package/schemas/agent-behavior-profile.schema.json +52 -0
- package/schemas/task-engine-run-contracts.schema.json +80 -5
- package/src/modules/documentation/README.md +16 -25
- package/src/modules/documentation/RULES.md +9 -9
- package/src/modules/documentation/index.ts +54 -49
- package/src/modules/documentation/instructions/document-project.md +6 -6
- package/src/modules/documentation/instructions/generate-document.md +4 -4
- package/src/modules/documentation/normalizer.ts +187 -0
- package/src/modules/documentation/parser.ts +41 -0
- package/src/modules/documentation/policy-sensitive-commands.ts +8 -0
- package/src/modules/documentation/renderer.ts +121 -0
- package/src/modules/documentation/runtime-batch.ts +74 -0
- package/src/modules/documentation/runtime-config.ts +68 -0
- package/src/modules/documentation/runtime-render-support.ts +39 -0
- package/src/modules/documentation/runtime.ts +28 -600
- package/src/modules/documentation/schemas/documentation-schema.md +37 -54
- package/src/modules/documentation/types.ts +228 -0
- package/src/modules/documentation/validator.ts +247 -0
- package/src/modules/documentation/view-models.ts +132 -0
- package/src/modules/documentation/views/agents.view.yaml +18 -0
- package/src/modules/documentation/views/architecture.view.yaml +18 -0
- package/src/modules/documentation/views/principles.view.yaml +18 -0
- package/src/modules/documentation/views/readme.view.yaml +18 -0
- package/src/modules/documentation/views/releasing.view.yaml +18 -0
- package/src/modules/documentation/views/roadmap.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-consumer-cadence.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-parity-validation-flow.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-release-channels.view.yaml +18 -0
- package/src/modules/documentation/views/security.view.yaml +18 -0
- package/src/modules/documentation/views/support.view.yaml +18 -0
- package/src/modules/documentation/views/terms.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-phase2-config-policy-workbook.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-task-engine-workbook.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-transcript-automation-baseline.view.yaml +18 -0
- package/src/modules/documentation/state.md +0 -8
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
import { PLANNING_WORKFLOW_DESCRIPTORS, PLANNING_WORKFLOW_TYPES } from "./types.js";
|
|
2
2
|
import { nextPlanningQuestions, resolvePlanningConfig, resolvePlanningRulePack } from "./question-engine.js";
|
|
3
3
|
import { composePlanningWishlistArtifact } from "./artifact.js";
|
|
4
|
-
import { openPlanningStores } from "
|
|
5
|
-
import {
|
|
6
|
-
|
|
4
|
+
import { openPlanningStores, validateKnownTaskTypeRequirements, validateWishlistContentFields, allocateNextTaskNumericId, taskEntityFromNewIntake, clearBuildPlanSession, persistBuildPlanSession } from "../../core/planning/index.js";
|
|
5
|
+
import { planningStrictValidationEnabled } from "../task-engine/planning-config.js";
|
|
6
|
+
import { validateTaskSetForStrictMode } from "../task-engine/strict-task-validation.js";
|
|
7
|
+
function resolveOutputMode(args) {
|
|
8
|
+
const raw = typeof args.outputMode === "string" ? args.outputMode.trim() : "";
|
|
9
|
+
if (raw === "") {
|
|
10
|
+
return { ok: true, mode: "wishlist" };
|
|
11
|
+
}
|
|
12
|
+
if (raw === "wishlist" || raw === "tasks" || raw === "response") {
|
|
13
|
+
return { ok: true, mode: raw };
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
ok: false,
|
|
17
|
+
message: "build-plan outputMode must be one of: wishlist, tasks, response"
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function nextTaskId(tasks) {
|
|
7
21
|
let max = 0;
|
|
8
|
-
for (const
|
|
9
|
-
const match = /^
|
|
22
|
+
for (const task of tasks) {
|
|
23
|
+
const match = /^T(\d+)$/.exec(task.id);
|
|
10
24
|
if (!match)
|
|
11
25
|
continue;
|
|
12
26
|
const parsed = Number(match[1]);
|
|
@@ -14,10 +28,75 @@ function nextWishlistId(items) {
|
|
|
14
28
|
max = Math.max(max, parsed);
|
|
15
29
|
}
|
|
16
30
|
}
|
|
17
|
-
return `
|
|
31
|
+
return `T${max + 1}`;
|
|
32
|
+
}
|
|
33
|
+
function findMissingAnsweredQuestions(questions, answers) {
|
|
34
|
+
return questions.filter((q) => {
|
|
35
|
+
const value = answers[q.id];
|
|
36
|
+
return !(typeof value === "string" && value.trim().length > 0);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function toNormalizedText(value) {
|
|
40
|
+
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
41
|
+
}
|
|
42
|
+
function buildScoringHints(args) {
|
|
43
|
+
const { planningType, answers, unresolvedCriticalCount, unresolvedAdaptiveCount } = args;
|
|
44
|
+
const signals = [
|
|
45
|
+
toNormalizedText(answers.complexity),
|
|
46
|
+
toNormalizedText(answers.riskPriority),
|
|
47
|
+
toNormalizedText(answers.timeline),
|
|
48
|
+
toNormalizedText(answers.compatibilityRisk)
|
|
49
|
+
];
|
|
50
|
+
const hasSignal = signals.some((s) => s.length > 0) || unresolvedCriticalCount > 0 || unresolvedAdaptiveCount > 0;
|
|
51
|
+
if (!hasSignal) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
let riskScore = Math.min(100, unresolvedCriticalCount * 20 + unresolvedAdaptiveCount * 10);
|
|
55
|
+
if (signals.some((s) => s.includes("high") || s.includes("critical"))) {
|
|
56
|
+
riskScore = Math.min(100, riskScore + 20);
|
|
57
|
+
}
|
|
58
|
+
const effortScore = Math.min(100, 30 +
|
|
59
|
+
unresolvedCriticalCount * 10 +
|
|
60
|
+
unresolvedAdaptiveCount * 5 +
|
|
61
|
+
(signals.some((s) => s.includes("high")) ? 15 : 0));
|
|
62
|
+
const orderingScore = Math.min(100, 40 + unresolvedCriticalCount * 8 + (planningType === "task-ordering" ? 15 : 0) + (planningType === "sprint-phase" ? 10 : 0));
|
|
63
|
+
const classify = (score) => {
|
|
64
|
+
if (score >= 70)
|
|
65
|
+
return "high";
|
|
66
|
+
if (score >= 40)
|
|
67
|
+
return "medium";
|
|
68
|
+
return "low";
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
schemaVersion: 1,
|
|
72
|
+
effort: { score: effortScore, level: classify(effortScore) },
|
|
73
|
+
risk: { score: riskScore, level: classify(riskScore) },
|
|
74
|
+
ordering: {
|
|
75
|
+
score: orderingScore,
|
|
76
|
+
level: classify(orderingScore),
|
|
77
|
+
recommendedStrategy: riskScore >= 70 ? "risk-first" : orderingScore >= 60 ? "dependency-first" : "balanced"
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function persistInterviewSnapshot(workspacePath, args) {
|
|
82
|
+
const cg = args.cliGuidance;
|
|
83
|
+
const completionPct = typeof cg.completionPct === "number" ? cg.completionPct : 0;
|
|
84
|
+
const answeredCritical = typeof cg.answeredCritical === "number" ? cg.answeredCritical : 0;
|
|
85
|
+
const totalCritical = typeof cg.totalCritical === "number" ? cg.totalCritical : 0;
|
|
86
|
+
const resumeCli = typeof cg.suggestedNextCommand === "string" ? cg.suggestedNextCommand : "";
|
|
87
|
+
await persistBuildPlanSession(workspacePath, {
|
|
88
|
+
planningType: args.planningType,
|
|
89
|
+
outputMode: args.outputMode,
|
|
90
|
+
status: args.status,
|
|
91
|
+
completionPct,
|
|
92
|
+
answeredCritical,
|
|
93
|
+
totalCritical,
|
|
94
|
+
answers: args.answers,
|
|
95
|
+
resumeCli
|
|
96
|
+
});
|
|
18
97
|
}
|
|
19
98
|
function toCliGuidance(args) {
|
|
20
|
-
const { planningType, answers, unresolvedCriticalCount, totalCriticalCount, finalize } = args;
|
|
99
|
+
const { planningType, answers, unresolvedCriticalCount, totalCriticalCount, finalize, outputMode } = args;
|
|
21
100
|
const answeredCritical = Math.max(0, totalCriticalCount - unresolvedCriticalCount);
|
|
22
101
|
const completionPct = totalCriticalCount > 0 ? Math.round((answeredCritical / totalCriticalCount) * 100) : 100;
|
|
23
102
|
return {
|
|
@@ -27,28 +106,26 @@ function toCliGuidance(args) {
|
|
|
27
106
|
suggestedNextCommand: `workspace-kit run build-plan '${JSON.stringify({
|
|
28
107
|
planningType,
|
|
29
108
|
answers,
|
|
30
|
-
finalize: finalize === true
|
|
109
|
+
finalize: finalize === true,
|
|
110
|
+
outputMode: outputMode ?? "wishlist"
|
|
31
111
|
})}'`
|
|
32
112
|
};
|
|
33
113
|
}
|
|
34
114
|
export const planningModule = {
|
|
35
115
|
registration: {
|
|
36
116
|
id: "planning",
|
|
37
|
-
version: "0.
|
|
117
|
+
version: "0.2.0",
|
|
38
118
|
contractVersion: "1",
|
|
119
|
+
stateSchema: 1,
|
|
39
120
|
capabilities: ["planning"],
|
|
40
121
|
dependsOn: ["task-engine"],
|
|
122
|
+
optionalPeers: [],
|
|
41
123
|
enabledByDefault: true,
|
|
42
124
|
config: {
|
|
43
125
|
path: "src/modules/planning/config.md",
|
|
44
126
|
format: "md",
|
|
45
127
|
description: "Planning module configuration contract."
|
|
46
128
|
},
|
|
47
|
-
state: {
|
|
48
|
-
path: "src/modules/planning/state.md",
|
|
49
|
-
format: "md",
|
|
50
|
-
description: "Planning module runtime state contract."
|
|
51
|
-
},
|
|
52
129
|
instructions: {
|
|
53
130
|
directory: "src/modules/planning/instructions",
|
|
54
131
|
entries: [
|
|
@@ -77,12 +154,22 @@ export const planningModule = {
|
|
|
77
154
|
code: "planning-types-listed",
|
|
78
155
|
message: `Found ${PLANNING_WORKFLOW_DESCRIPTORS.length} planning workflow types`,
|
|
79
156
|
data: {
|
|
157
|
+
responseSchemaVersion: 1,
|
|
80
158
|
planningTypes: PLANNING_WORKFLOW_DESCRIPTORS
|
|
81
159
|
}
|
|
82
160
|
};
|
|
83
161
|
}
|
|
84
162
|
if (command.name === "build-plan") {
|
|
85
163
|
const args = command.args ?? {};
|
|
164
|
+
const outputModeResolved = resolveOutputMode(args);
|
|
165
|
+
if (!outputModeResolved.ok) {
|
|
166
|
+
return {
|
|
167
|
+
ok: false,
|
|
168
|
+
code: "invalid-planning-output-mode",
|
|
169
|
+
message: outputModeResolved.message
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const outputMode = outputModeResolved.mode;
|
|
86
173
|
const planningType = typeof args.planningType === "string" ? args.planningType.trim() : "";
|
|
87
174
|
if (!PLANNING_WORKFLOW_TYPES.includes(planningType)) {
|
|
88
175
|
return {
|
|
@@ -101,61 +188,141 @@ export const planningModule = {
|
|
|
101
188
|
const finalize = args.finalize === true;
|
|
102
189
|
const { missingCritical, adaptiveFollowups } = nextPlanningQuestions(planningType, answers, ctx.effectiveConfig);
|
|
103
190
|
const config = resolvePlanningConfig(ctx.effectiveConfig);
|
|
191
|
+
const unresolvedAdaptive = findMissingAnsweredQuestions(adaptiveFollowups, answers);
|
|
192
|
+
const scoringHints = buildScoringHints({
|
|
193
|
+
planningType,
|
|
194
|
+
answers,
|
|
195
|
+
unresolvedCriticalCount: missingCritical.length,
|
|
196
|
+
unresolvedAdaptiveCount: unresolvedAdaptive.length
|
|
197
|
+
});
|
|
104
198
|
if (finalize && missingCritical.length > 0) {
|
|
105
199
|
if (!config.hardBlockCriticalUnknowns) {
|
|
200
|
+
const cliGuidance = toCliGuidance({
|
|
201
|
+
planningType,
|
|
202
|
+
answers,
|
|
203
|
+
unresolvedCriticalCount: missingCritical.length,
|
|
204
|
+
totalCriticalCount,
|
|
205
|
+
outputMode
|
|
206
|
+
});
|
|
207
|
+
await persistInterviewSnapshot(ctx.workspacePath, {
|
|
208
|
+
planningType,
|
|
209
|
+
outputMode,
|
|
210
|
+
status: "ready-with-warnings",
|
|
211
|
+
answers,
|
|
212
|
+
cliGuidance
|
|
213
|
+
});
|
|
106
214
|
return {
|
|
107
215
|
ok: true,
|
|
108
216
|
code: "planning-ready-with-warnings",
|
|
109
217
|
message: `Finalize allowed with unresolved critical questions because planning.hardBlockCriticalUnknowns=false`,
|
|
110
218
|
data: {
|
|
219
|
+
responseSchemaVersion: 1,
|
|
111
220
|
planningType,
|
|
221
|
+
outputMode,
|
|
112
222
|
status: "ready-with-warnings",
|
|
113
223
|
unresolvedCritical: missingCritical,
|
|
114
224
|
nextQuestions: [...missingCritical, ...adaptiveFollowups],
|
|
225
|
+
scoringHints,
|
|
115
226
|
capturedAnswers: answers,
|
|
116
|
-
cliGuidance
|
|
117
|
-
planningType,
|
|
118
|
-
answers,
|
|
119
|
-
unresolvedCriticalCount: missingCritical.length,
|
|
120
|
-
totalCriticalCount
|
|
121
|
-
})
|
|
227
|
+
cliGuidance
|
|
122
228
|
}
|
|
123
229
|
};
|
|
124
230
|
}
|
|
231
|
+
const cliGuidanceBlocked = toCliGuidance({
|
|
232
|
+
planningType,
|
|
233
|
+
answers,
|
|
234
|
+
unresolvedCriticalCount: missingCritical.length,
|
|
235
|
+
totalCriticalCount,
|
|
236
|
+
finalize: true,
|
|
237
|
+
outputMode
|
|
238
|
+
});
|
|
239
|
+
await persistInterviewSnapshot(ctx.workspacePath, {
|
|
240
|
+
planningType,
|
|
241
|
+
outputMode,
|
|
242
|
+
status: "blocked-critical-unknowns",
|
|
243
|
+
answers,
|
|
244
|
+
cliGuidance: cliGuidanceBlocked
|
|
245
|
+
});
|
|
125
246
|
return {
|
|
126
247
|
ok: false,
|
|
127
248
|
code: "planning-critical-unknowns",
|
|
128
249
|
message: `Cannot finalize ${planningType}: unresolved critical questions (${missingCritical.map((q) => q.id).join(", ")})`,
|
|
129
250
|
data: {
|
|
251
|
+
responseSchemaVersion: 1,
|
|
130
252
|
planningType,
|
|
253
|
+
outputMode,
|
|
131
254
|
unresolvedCritical: missingCritical,
|
|
132
255
|
nextQuestions: [...missingCritical, ...adaptiveFollowups],
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
answers,
|
|
136
|
-
unresolvedCriticalCount: missingCritical.length,
|
|
137
|
-
totalCriticalCount,
|
|
138
|
-
finalize: true
|
|
139
|
-
})
|
|
256
|
+
scoringHints,
|
|
257
|
+
cliGuidance: cliGuidanceBlocked
|
|
140
258
|
}
|
|
141
259
|
};
|
|
142
260
|
}
|
|
261
|
+
if (finalize && unresolvedAdaptive.length > 0 && config.adaptiveFinalizePolicy === "block") {
|
|
262
|
+
const cliGuidanceAdaptive = toCliGuidance({
|
|
263
|
+
planningType,
|
|
264
|
+
answers,
|
|
265
|
+
unresolvedCriticalCount: 0,
|
|
266
|
+
totalCriticalCount,
|
|
267
|
+
finalize: true,
|
|
268
|
+
outputMode
|
|
269
|
+
});
|
|
270
|
+
await persistInterviewSnapshot(ctx.workspacePath, {
|
|
271
|
+
planningType,
|
|
272
|
+
outputMode,
|
|
273
|
+
status: "blocked-adaptive-unknowns",
|
|
274
|
+
answers,
|
|
275
|
+
cliGuidance: cliGuidanceAdaptive
|
|
276
|
+
});
|
|
277
|
+
return {
|
|
278
|
+
ok: false,
|
|
279
|
+
code: "planning-adaptive-unknowns",
|
|
280
|
+
message: `Cannot finalize ${planningType}: unresolved adaptive follow-ups (${unresolvedAdaptive
|
|
281
|
+
.map((q) => q.id)
|
|
282
|
+
.join(", ")})`,
|
|
283
|
+
data: {
|
|
284
|
+
responseSchemaVersion: 1,
|
|
285
|
+
planningType,
|
|
286
|
+
outputMode,
|
|
287
|
+
unresolvedAdaptive,
|
|
288
|
+
unresolvedCritical: [],
|
|
289
|
+
nextQuestions: unresolvedAdaptive,
|
|
290
|
+
scoringHints,
|
|
291
|
+
cliGuidance: cliGuidanceAdaptive
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
const adaptiveWarnings = finalize && unresolvedAdaptive.length > 0 && config.adaptiveFinalizePolicy === "warn"
|
|
296
|
+
? unresolvedAdaptive
|
|
297
|
+
: [];
|
|
143
298
|
if (missingCritical.length > 0) {
|
|
299
|
+
const cliGuidanceQuestions = toCliGuidance({
|
|
300
|
+
planningType,
|
|
301
|
+
answers,
|
|
302
|
+
unresolvedCriticalCount: missingCritical.length,
|
|
303
|
+
totalCriticalCount,
|
|
304
|
+
outputMode
|
|
305
|
+
});
|
|
306
|
+
await persistInterviewSnapshot(ctx.workspacePath, {
|
|
307
|
+
planningType,
|
|
308
|
+
outputMode,
|
|
309
|
+
status: "needs-input",
|
|
310
|
+
answers,
|
|
311
|
+
cliGuidance: cliGuidanceQuestions
|
|
312
|
+
});
|
|
144
313
|
return {
|
|
145
314
|
ok: true,
|
|
146
315
|
code: "planning-questions",
|
|
147
316
|
message: `${missingCritical.length} critical planning questions require answers before finalize`,
|
|
148
317
|
data: {
|
|
318
|
+
responseSchemaVersion: 1,
|
|
149
319
|
planningType,
|
|
320
|
+
outputMode,
|
|
150
321
|
status: "needs-input",
|
|
151
322
|
unresolvedCritical: missingCritical,
|
|
152
323
|
nextQuestions: [...missingCritical, ...adaptiveFollowups],
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
answers,
|
|
156
|
-
unresolvedCriticalCount: missingCritical.length,
|
|
157
|
-
totalCriticalCount
|
|
158
|
-
})
|
|
324
|
+
scoringHints,
|
|
325
|
+
cliGuidance: cliGuidanceQuestions
|
|
159
326
|
}
|
|
160
327
|
};
|
|
161
328
|
}
|
|
@@ -165,18 +332,156 @@ export const planningModule = {
|
|
|
165
332
|
answers,
|
|
166
333
|
unresolvedCriticalQuestionIds: unresolvedIds
|
|
167
334
|
});
|
|
335
|
+
if (outputMode === "response") {
|
|
336
|
+
await clearBuildPlanSession(ctx.workspacePath);
|
|
337
|
+
return {
|
|
338
|
+
ok: true,
|
|
339
|
+
code: "planning-response-ready",
|
|
340
|
+
message: `Planning interview complete for ${planningType}; returning response-only artifact`,
|
|
341
|
+
data: {
|
|
342
|
+
responseSchemaVersion: 1,
|
|
343
|
+
planningType,
|
|
344
|
+
descriptor,
|
|
345
|
+
outputMode,
|
|
346
|
+
scaffoldVersion: 3,
|
|
347
|
+
status: "ready-for-response",
|
|
348
|
+
unresolvedCritical: [],
|
|
349
|
+
adaptiveWarnings,
|
|
350
|
+
adaptiveFollowups,
|
|
351
|
+
scoringHints,
|
|
352
|
+
capturedAnswers: answers,
|
|
353
|
+
artifact,
|
|
354
|
+
cliGuidance: toCliGuidance({
|
|
355
|
+
planningType,
|
|
356
|
+
answers,
|
|
357
|
+
unresolvedCriticalCount: 0,
|
|
358
|
+
totalCriticalCount,
|
|
359
|
+
finalize,
|
|
360
|
+
outputMode
|
|
361
|
+
})
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
if (outputMode === "tasks") {
|
|
366
|
+
const persistTasks = args.persistTasks === true;
|
|
367
|
+
const taskType = typeof args.taskType === "string" && args.taskType.trim().length > 0
|
|
368
|
+
? args.taskType.trim()
|
|
369
|
+
: "task";
|
|
370
|
+
const taskPriority = typeof args.taskPriority === "string" && ["P1", "P2", "P3"].includes(args.taskPriority)
|
|
371
|
+
? args.taskPriority
|
|
372
|
+
: undefined;
|
|
373
|
+
const stores = await openPlanningStores(ctx);
|
|
374
|
+
const store = stores.taskStore;
|
|
375
|
+
const plannedTaskId = nextTaskId(store.getAllTasks());
|
|
376
|
+
const planRef = `planning:${planningType}:${new Date().toISOString()}`;
|
|
377
|
+
const scopeFromArtifact = artifact.candidateFeaturesOrChanges.length > 0
|
|
378
|
+
? artifact.candidateFeaturesOrChanges
|
|
379
|
+
: artifact.goals;
|
|
380
|
+
const criteriaFromSignals = typeof answers.successSignals === "string" && answers.successSignals.trim().length > 0
|
|
381
|
+
? [answers.successSignals.trim()]
|
|
382
|
+
: [];
|
|
383
|
+
const task = {
|
|
384
|
+
id: plannedTaskId,
|
|
385
|
+
title: artifact.goals[0] && artifact.goals[0].trim().length > 0
|
|
386
|
+
? artifact.goals[0].trim()
|
|
387
|
+
: `${descriptor?.title ?? planningType} task output`,
|
|
388
|
+
type: taskType,
|
|
389
|
+
status: "proposed",
|
|
390
|
+
createdAt: new Date().toISOString(),
|
|
391
|
+
updatedAt: new Date().toISOString(),
|
|
392
|
+
priority: taskPriority,
|
|
393
|
+
phase: typeof args.taskPhase === "string" ? args.taskPhase : undefined,
|
|
394
|
+
approach: typeof answers.approach === "string" && answers.approach.trim().length > 0
|
|
395
|
+
? answers.approach
|
|
396
|
+
: artifact.approach,
|
|
397
|
+
technicalScope: scopeFromArtifact.length > 0 ? scopeFromArtifact : undefined,
|
|
398
|
+
acceptanceCriteria: criteriaFromSignals.length > 0
|
|
399
|
+
? criteriaFromSignals
|
|
400
|
+
: ["Task output reviewed and refined from planning artifact."],
|
|
401
|
+
metadata: {
|
|
402
|
+
planRef,
|
|
403
|
+
planningProvenance: {
|
|
404
|
+
planningType,
|
|
405
|
+
outputMode,
|
|
406
|
+
capturedAnswerKeys: Object.keys(artifact.sourceAnswers).sort()
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
const knownTypeValidationError = validateKnownTaskTypeRequirements(task);
|
|
411
|
+
if (knownTypeValidationError) {
|
|
412
|
+
return {
|
|
413
|
+
ok: false,
|
|
414
|
+
code: knownTypeValidationError.code,
|
|
415
|
+
message: knownTypeValidationError.message
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
if (persistTasks) {
|
|
419
|
+
if (store.getTask(task.id)) {
|
|
420
|
+
return {
|
|
421
|
+
ok: false,
|
|
422
|
+
code: "duplicate-task-id",
|
|
423
|
+
message: `Task '${task.id}' already exists`
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
store.addTask(task);
|
|
427
|
+
await store.save();
|
|
428
|
+
}
|
|
429
|
+
await clearBuildPlanSession(ctx.workspacePath);
|
|
430
|
+
return {
|
|
431
|
+
ok: true,
|
|
432
|
+
code: persistTasks ? "planning-task-output-created" : "planning-task-output-preview",
|
|
433
|
+
message: persistTasks
|
|
434
|
+
? `Planning task output persisted as '${task.id}'`
|
|
435
|
+
: "Planning task output prepared (preview only; set persistTasks=true to write)",
|
|
436
|
+
data: {
|
|
437
|
+
responseSchemaVersion: 1,
|
|
438
|
+
planningType,
|
|
439
|
+
descriptor,
|
|
440
|
+
outputMode,
|
|
441
|
+
persistTasks,
|
|
442
|
+
scaffoldVersion: 3,
|
|
443
|
+
status: persistTasks ? "task-output-created" : "task-output-preview",
|
|
444
|
+
unresolvedCritical: [],
|
|
445
|
+
adaptiveWarnings,
|
|
446
|
+
adaptiveFollowups,
|
|
447
|
+
scoringHints,
|
|
448
|
+
capturedAnswers: answers,
|
|
449
|
+
artifact,
|
|
450
|
+
taskOutputs: [task],
|
|
451
|
+
provenance: {
|
|
452
|
+
planRef,
|
|
453
|
+
outputMode,
|
|
454
|
+
persistedTaskIds: persistTasks ? [task.id] : [],
|
|
455
|
+
suggestedTaskIds: [task.id]
|
|
456
|
+
},
|
|
457
|
+
cliGuidance: toCliGuidance({
|
|
458
|
+
planningType,
|
|
459
|
+
answers,
|
|
460
|
+
unresolvedCriticalCount: 0,
|
|
461
|
+
totalCriticalCount,
|
|
462
|
+
finalize: true,
|
|
463
|
+
outputMode
|
|
464
|
+
})
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
}
|
|
168
468
|
if (!finalize || !createWishlist) {
|
|
469
|
+
await clearBuildPlanSession(ctx.workspacePath);
|
|
169
470
|
return {
|
|
170
471
|
ok: true,
|
|
171
|
-
code: "planning-ready",
|
|
172
|
-
message: `Planning interview complete for ${planningType}; artifact ready`,
|
|
472
|
+
code: "planning-wishlist-ready",
|
|
473
|
+
message: `Planning interview complete for ${planningType}; wishlist artifact ready`,
|
|
173
474
|
data: {
|
|
475
|
+
responseSchemaVersion: 1,
|
|
174
476
|
planningType,
|
|
175
477
|
descriptor,
|
|
478
|
+
outputMode,
|
|
176
479
|
scaffoldVersion: 3,
|
|
177
|
-
status: "ready-for-
|
|
480
|
+
status: "ready-for-wishlist",
|
|
178
481
|
unresolvedCritical: [],
|
|
482
|
+
adaptiveWarnings,
|
|
179
483
|
adaptiveFollowups,
|
|
484
|
+
scoringHints,
|
|
180
485
|
capturedAnswers: answers,
|
|
181
486
|
artifact,
|
|
182
487
|
cliGuidance: toCliGuidance({
|
|
@@ -184,17 +489,15 @@ export const planningModule = {
|
|
|
184
489
|
answers,
|
|
185
490
|
unresolvedCriticalCount: 0,
|
|
186
491
|
totalCriticalCount,
|
|
187
|
-
finalize: createWishlist
|
|
492
|
+
finalize: createWishlist,
|
|
493
|
+
outputMode
|
|
188
494
|
})
|
|
189
495
|
}
|
|
190
496
|
};
|
|
191
497
|
}
|
|
192
498
|
const stores = await openPlanningStores(ctx);
|
|
193
|
-
const wishlist = await stores.openWishlist();
|
|
194
|
-
const wishlistId = nextWishlistId(wishlist.getAllItems());
|
|
195
499
|
const now = new Date().toISOString();
|
|
196
500
|
const intake = {
|
|
197
|
-
id: wishlistId,
|
|
198
501
|
title: typeof args.title === "string" && args.title.trim().length > 0
|
|
199
502
|
? args.title.trim()
|
|
200
503
|
: `${descriptor?.title ?? planningType} plan artifact`,
|
|
@@ -220,7 +523,7 @@ export const planningModule = {
|
|
|
220
523
|
? args.evidenceRef.trim()
|
|
221
524
|
: `planning:${planningType}:${now}`
|
|
222
525
|
};
|
|
223
|
-
const valid =
|
|
526
|
+
const valid = validateWishlistContentFields(intake);
|
|
224
527
|
if (!valid.ok) {
|
|
225
528
|
return {
|
|
226
529
|
ok: false,
|
|
@@ -228,35 +531,64 @@ export const planningModule = {
|
|
|
228
531
|
message: valid.errors.join("; ")
|
|
229
532
|
};
|
|
230
533
|
}
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
item.metadata = {
|
|
534
|
+
const taskId = allocateNextTaskNumericId(stores.taskStore.getAllTasks());
|
|
535
|
+
const task = taskEntityFromNewIntake(intake, taskId, now, {
|
|
234
536
|
planningType,
|
|
235
537
|
artifactSchemaVersion: artifact.schemaVersion,
|
|
236
538
|
artifact
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
|
|
539
|
+
});
|
|
540
|
+
const typeErr = validateKnownTaskTypeRequirements(task);
|
|
541
|
+
if (typeErr) {
|
|
542
|
+
return { ok: false, code: typeErr.code, message: typeErr.message };
|
|
543
|
+
}
|
|
544
|
+
if (planningStrictValidationEnabled({ effectiveConfig: ctx.effectiveConfig })) {
|
|
545
|
+
const strictIssue = validateTaskSetForStrictMode([...stores.taskStore.getAllTasks(), task]);
|
|
546
|
+
if (strictIssue) {
|
|
547
|
+
return { ok: false, code: "strict-task-validation-failed", message: strictIssue };
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
try {
|
|
551
|
+
if (stores.kind === "sqlite") {
|
|
552
|
+
stores.sqliteDual.withTransaction(() => {
|
|
553
|
+
stores.taskStore.addTask(task);
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
stores.taskStore.addTask(task);
|
|
558
|
+
await stores.taskStore.save();
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
catch (e) {
|
|
562
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
563
|
+
return { ok: false, code: "invalid-planning-artifact", message: msg };
|
|
564
|
+
}
|
|
565
|
+
await clearBuildPlanSession(ctx.workspacePath);
|
|
240
566
|
return {
|
|
241
567
|
ok: true,
|
|
242
568
|
code: "planning-artifact-created",
|
|
243
|
-
message: `Planning artifact created as wishlist
|
|
569
|
+
message: `Planning artifact created as wishlist intake task ${taskId}`,
|
|
244
570
|
data: {
|
|
571
|
+
responseSchemaVersion: 1,
|
|
245
572
|
planningType,
|
|
246
573
|
descriptor,
|
|
574
|
+
outputMode,
|
|
247
575
|
scaffoldVersion: 3,
|
|
248
576
|
status: "artifact-created",
|
|
249
|
-
wishlistId,
|
|
577
|
+
wishlistId: taskId,
|
|
578
|
+
taskId,
|
|
579
|
+
adaptiveWarnings,
|
|
250
580
|
artifact,
|
|
251
581
|
unresolvedCritical: [],
|
|
252
582
|
adaptiveFollowups,
|
|
583
|
+
scoringHints,
|
|
253
584
|
capturedAnswers: answers,
|
|
254
585
|
cliGuidance: toCliGuidance({
|
|
255
586
|
planningType,
|
|
256
587
|
answers,
|
|
257
588
|
unresolvedCriticalCount: 0,
|
|
258
589
|
totalCriticalCount,
|
|
259
|
-
finalize: true
|
|
590
|
+
finalize: true,
|
|
591
|
+
outputMode
|
|
260
592
|
})
|
|
261
593
|
}
|
|
262
594
|
};
|
|
@@ -278,9 +610,11 @@ export const planningModule = {
|
|
|
278
610
|
code: "planning-rules-explained",
|
|
279
611
|
message: `Effective planning rules for ${planningType}`,
|
|
280
612
|
data: {
|
|
613
|
+
responseSchemaVersion: 1,
|
|
281
614
|
planningType,
|
|
282
615
|
defaultQuestionDepth: config.depth,
|
|
283
616
|
hardBlockCriticalUnknowns: config.hardBlockCriticalUnknowns,
|
|
617
|
+
adaptiveFinalizePolicy: config.adaptiveFinalizePolicy,
|
|
284
618
|
baseQuestions: rulePack.baseQuestions,
|
|
285
619
|
adaptiveQuestions: rulePack.adaptiveQuestions
|
|
286
620
|
}
|
|
@@ -7,6 +7,7 @@ export type PlanningQuestion = {
|
|
|
7
7
|
critical: boolean;
|
|
8
8
|
};
|
|
9
9
|
export type PlanningQuestionDepth = "minimal" | "guided" | "adaptive";
|
|
10
|
+
export type AdaptiveFinalizePolicy = "off" | "warn" | "block";
|
|
10
11
|
export type PlanningRulePack = {
|
|
11
12
|
baseQuestions: PlanningQuestion[];
|
|
12
13
|
adaptiveQuestions: PlanningQuestion[];
|
|
@@ -14,6 +15,7 @@ export type PlanningRulePack = {
|
|
|
14
15
|
export declare function resolvePlanningConfig(config: Record<string, unknown> | undefined): {
|
|
15
16
|
depth: PlanningQuestionDepth;
|
|
16
17
|
hardBlockCriticalUnknowns: boolean;
|
|
18
|
+
adaptiveFinalizePolicy: AdaptiveFinalizePolicy;
|
|
17
19
|
rulePacks: Partial<Record<PlanningWorkflowType, PlanningRulePack>>;
|
|
18
20
|
};
|
|
19
21
|
export declare function resolvePlanningRulePack(planningType: PlanningWorkflowType, config: Record<string, unknown> | undefined): PlanningRulePack;
|
|
@@ -206,10 +206,17 @@ function parseDepth(value) {
|
|
|
206
206
|
}
|
|
207
207
|
return "adaptive";
|
|
208
208
|
}
|
|
209
|
+
function parseAdaptiveFinalizePolicy(value) {
|
|
210
|
+
if (value === "off" || value === "warn" || value === "block") {
|
|
211
|
+
return value;
|
|
212
|
+
}
|
|
213
|
+
return "off";
|
|
214
|
+
}
|
|
209
215
|
export function resolvePlanningConfig(config) {
|
|
210
216
|
const planning = asRecord(config?.planning);
|
|
211
217
|
const depth = parseDepth(planning?.defaultQuestionDepth);
|
|
212
218
|
const hardBlockCriticalUnknowns = planning?.hardBlockCriticalUnknowns !== false;
|
|
219
|
+
const adaptiveFinalizePolicy = parseAdaptiveFinalizePolicy(planning?.adaptiveFinalizePolicy);
|
|
213
220
|
const rulesRoot = asRecord(planning?.rulePacks);
|
|
214
221
|
const rulePacks = {};
|
|
215
222
|
if (rulesRoot) {
|
|
@@ -227,7 +234,7 @@ export function resolvePlanningConfig(config) {
|
|
|
227
234
|
};
|
|
228
235
|
}
|
|
229
236
|
}
|
|
230
|
-
return { depth, hardBlockCriticalUnknowns, rulePacks };
|
|
237
|
+
return { depth, hardBlockCriticalUnknowns, adaptiveFinalizePolicy, rulePacks };
|
|
231
238
|
}
|
|
232
239
|
export function resolvePlanningRulePack(planningType, config) {
|
|
233
240
|
const resolved = resolvePlanningConfig(config);
|