@workflow-cannon/workspace-kit 0.17.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.
Files changed (144) hide show
  1. package/README.md +23 -9
  2. package/dist/cli/doctor-planning-issues.js +3 -22
  3. package/dist/cli/run-command.js +22 -38
  4. package/dist/cli.js +95 -4
  5. package/dist/contracts/command-manifest.d.ts +17 -0
  6. package/dist/contracts/command-manifest.js +1 -0
  7. package/dist/contracts/index.d.ts +1 -1
  8. package/dist/contracts/module-contract.d.ts +12 -11
  9. package/dist/core/agent-instruction-surface.d.ts +33 -0
  10. package/dist/core/agent-instruction-surface.js +46 -0
  11. package/dist/core/config-cli.js +13 -17
  12. package/dist/core/config-metadata.js +101 -2
  13. package/dist/core/index.d.ts +4 -1
  14. package/dist/core/index.js +3 -0
  15. package/dist/core/module-command-router.js +19 -1
  16. package/dist/core/module-registry-resolve.d.ts +27 -0
  17. package/dist/core/module-registry-resolve.js +91 -0
  18. package/dist/core/module-registry.d.ts +14 -0
  19. package/dist/core/module-registry.js +57 -0
  20. package/dist/core/planning/build-plan-session-file.d.ts +29 -0
  21. package/dist/core/planning/build-plan-session-file.js +58 -0
  22. package/dist/core/planning/index.d.ts +17 -0
  23. package/dist/core/planning/index.js +15 -0
  24. package/dist/core/policy.js +18 -8
  25. package/dist/core/state/unified-state-db.d.ts +21 -0
  26. package/dist/core/state/unified-state-db.js +80 -0
  27. package/dist/core/workspace-kit-config.js +13 -1
  28. package/dist/modules/agent-behavior/builtins.d.ts +3 -0
  29. package/dist/modules/agent-behavior/builtins.js +71 -0
  30. package/dist/modules/agent-behavior/explain.d.ts +6 -0
  31. package/dist/modules/agent-behavior/explain.js +46 -0
  32. package/dist/modules/agent-behavior/index.d.ts +4 -0
  33. package/dist/modules/agent-behavior/index.js +461 -0
  34. package/dist/modules/agent-behavior/interview-session-file.d.ts +9 -0
  35. package/dist/modules/agent-behavior/interview-session-file.js +43 -0
  36. package/dist/modules/agent-behavior/interview.d.ts +13 -0
  37. package/dist/modules/agent-behavior/interview.js +88 -0
  38. package/dist/modules/agent-behavior/persistence.d.ts +6 -0
  39. package/dist/modules/agent-behavior/persistence.js +89 -0
  40. package/dist/modules/agent-behavior/store.d.ts +34 -0
  41. package/dist/modules/agent-behavior/store.js +119 -0
  42. package/dist/modules/agent-behavior/types.d.ts +28 -0
  43. package/dist/modules/agent-behavior/types.js +1 -0
  44. package/dist/modules/agent-behavior/validate.d.ts +11 -0
  45. package/dist/modules/agent-behavior/validate.js +123 -0
  46. package/dist/modules/approvals/index.js +54 -51
  47. package/dist/modules/approvals/policy-sensitive-commands.d.ts +4 -0
  48. package/dist/modules/approvals/policy-sensitive-commands.js +4 -0
  49. package/dist/modules/approvals/review-runtime.js +1 -2
  50. package/dist/modules/documentation/index.js +47 -45
  51. package/dist/modules/documentation/normalizer.d.ts +3 -0
  52. package/dist/modules/documentation/normalizer.js +171 -0
  53. package/dist/modules/documentation/parser.d.ts +7 -0
  54. package/dist/modules/documentation/parser.js +39 -0
  55. package/dist/modules/documentation/policy-sensitive-commands.d.ts +5 -0
  56. package/dist/modules/documentation/policy-sensitive-commands.js +8 -0
  57. package/dist/modules/documentation/renderer.d.ts +23 -0
  58. package/dist/modules/documentation/renderer.js +105 -0
  59. package/dist/modules/documentation/runtime-batch.d.ts +10 -0
  60. package/dist/modules/documentation/runtime-batch.js +67 -0
  61. package/dist/modules/documentation/runtime-config.d.ts +11 -0
  62. package/dist/modules/documentation/runtime-config.js +54 -0
  63. package/dist/modules/documentation/runtime-render-support.d.ts +8 -0
  64. package/dist/modules/documentation/runtime-render-support.js +36 -0
  65. package/dist/modules/documentation/runtime.js +22 -510
  66. package/dist/modules/documentation/types.d.ts +182 -0
  67. package/dist/modules/documentation/validator.d.ts +8 -0
  68. package/dist/modules/documentation/validator.js +234 -0
  69. package/dist/modules/documentation/view-models.d.ts +3 -0
  70. package/dist/modules/documentation/view-models.js +124 -0
  71. package/dist/modules/improvement/generate-recommendations-runtime.js +3 -3
  72. package/dist/modules/improvement/improvement-state.d.ts +2 -2
  73. package/dist/modules/improvement/improvement-state.js +52 -23
  74. package/dist/modules/improvement/index.js +140 -138
  75. package/dist/modules/improvement/ingest.d.ts +1 -1
  76. package/dist/modules/improvement/policy-sensitive-commands.d.ts +4 -0
  77. package/dist/modules/improvement/policy-sensitive-commands.js +7 -0
  78. package/dist/modules/index.d.ts +6 -0
  79. package/dist/modules/index.js +17 -0
  80. package/dist/modules/planning/artifact.d.ts +19 -0
  81. package/dist/modules/planning/artifact.js +72 -0
  82. package/dist/modules/planning/index.js +605 -6
  83. package/dist/modules/planning/question-engine.d.ts +25 -0
  84. package/dist/modules/planning/question-engine.js +284 -0
  85. package/dist/modules/planning/types.d.ts +9 -0
  86. package/dist/modules/planning/types.js +39 -0
  87. package/dist/modules/task-engine/doctor-planning-persistence.js +21 -13
  88. package/dist/modules/task-engine/index.d.ts +1 -2
  89. package/dist/modules/task-engine/index.js +1 -1143
  90. package/dist/modules/task-engine/migrate-task-persistence-runtime.js +31 -4
  91. package/dist/modules/task-engine/migrate-wishlist-intake-runtime.d.ts +2 -0
  92. package/dist/modules/task-engine/migrate-wishlist-intake-runtime.js +146 -0
  93. package/dist/modules/task-engine/planning-open.d.ts +2 -9
  94. package/dist/modules/task-engine/planning-open.js +4 -15
  95. package/dist/modules/task-engine/policy-sensitive-commands.d.ts +5 -0
  96. package/dist/modules/task-engine/policy-sensitive-commands.js +5 -0
  97. package/dist/modules/task-engine/sqlite-dual-planning.d.ts +11 -2
  98. package/dist/modules/task-engine/sqlite-dual-planning.js +134 -28
  99. package/dist/modules/task-engine/strict-task-validation.js +3 -0
  100. package/dist/modules/task-engine/suggestions.js +2 -1
  101. package/dist/modules/task-engine/task-engine-internal.d.ts +2 -0
  102. package/dist/modules/task-engine/task-engine-internal.js +1304 -0
  103. package/dist/modules/task-engine/task-type-validation.js +40 -0
  104. package/dist/modules/task-engine/wishlist-intake.d.ts +22 -0
  105. package/dist/modules/task-engine/wishlist-intake.js +180 -0
  106. package/dist/modules/task-engine/wishlist-validation.d.ts +4 -0
  107. package/dist/modules/task-engine/wishlist-validation.js +19 -0
  108. package/dist/modules/workspace-config/index.js +9 -11
  109. package/package.json +2 -2
  110. package/schemas/agent-behavior-profile.schema.json +52 -0
  111. package/schemas/task-engine-run-contracts.schema.json +80 -5
  112. package/src/modules/documentation/README.md +16 -25
  113. package/src/modules/documentation/RULES.md +9 -9
  114. package/src/modules/documentation/index.ts +54 -49
  115. package/src/modules/documentation/instructions/document-project.md +6 -6
  116. package/src/modules/documentation/instructions/generate-document.md +4 -4
  117. package/src/modules/documentation/normalizer.ts +187 -0
  118. package/src/modules/documentation/parser.ts +41 -0
  119. package/src/modules/documentation/policy-sensitive-commands.ts +8 -0
  120. package/src/modules/documentation/renderer.ts +121 -0
  121. package/src/modules/documentation/runtime-batch.ts +74 -0
  122. package/src/modules/documentation/runtime-config.ts +68 -0
  123. package/src/modules/documentation/runtime-render-support.ts +39 -0
  124. package/src/modules/documentation/runtime.ts +28 -600
  125. package/src/modules/documentation/schemas/documentation-schema.md +37 -54
  126. package/src/modules/documentation/types.ts +228 -0
  127. package/src/modules/documentation/validator.ts +247 -0
  128. package/src/modules/documentation/view-models.ts +132 -0
  129. package/src/modules/documentation/views/agents.view.yaml +18 -0
  130. package/src/modules/documentation/views/architecture.view.yaml +18 -0
  131. package/src/modules/documentation/views/principles.view.yaml +18 -0
  132. package/src/modules/documentation/views/readme.view.yaml +18 -0
  133. package/src/modules/documentation/views/releasing.view.yaml +18 -0
  134. package/src/modules/documentation/views/roadmap.view.yaml +18 -0
  135. package/src/modules/documentation/views/runbooks-consumer-cadence.view.yaml +18 -0
  136. package/src/modules/documentation/views/runbooks-parity-validation-flow.view.yaml +18 -0
  137. package/src/modules/documentation/views/runbooks-release-channels.view.yaml +18 -0
  138. package/src/modules/documentation/views/security.view.yaml +18 -0
  139. package/src/modules/documentation/views/support.view.yaml +18 -0
  140. package/src/modules/documentation/views/terms.view.yaml +18 -0
  141. package/src/modules/documentation/views/workbooks-phase2-config-policy-workbook.view.yaml +18 -0
  142. package/src/modules/documentation/views/workbooks-task-engine-workbook.view.yaml +18 -0
  143. package/src/modules/documentation/views/workbooks-transcript-automation-baseline.view.yaml +18 -0
  144. package/src/modules/documentation/state.md +0 -8
@@ -1,21 +1,131 @@
1
+ import { PLANNING_WORKFLOW_DESCRIPTORS, PLANNING_WORKFLOW_TYPES } from "./types.js";
2
+ import { nextPlanningQuestions, resolvePlanningConfig, resolvePlanningRulePack } from "./question-engine.js";
3
+ import { composePlanningWishlistArtifact } from "./artifact.js";
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) {
21
+ let max = 0;
22
+ for (const task of tasks) {
23
+ const match = /^T(\d+)$/.exec(task.id);
24
+ if (!match)
25
+ continue;
26
+ const parsed = Number(match[1]);
27
+ if (Number.isFinite(parsed)) {
28
+ max = Math.max(max, parsed);
29
+ }
30
+ }
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
+ });
97
+ }
98
+ function toCliGuidance(args) {
99
+ const { planningType, answers, unresolvedCriticalCount, totalCriticalCount, finalize, outputMode } = args;
100
+ const answeredCritical = Math.max(0, totalCriticalCount - unresolvedCriticalCount);
101
+ const completionPct = totalCriticalCount > 0 ? Math.round((answeredCritical / totalCriticalCount) * 100) : 100;
102
+ return {
103
+ answeredCritical,
104
+ totalCritical: totalCriticalCount,
105
+ completionPct,
106
+ suggestedNextCommand: `workspace-kit run build-plan '${JSON.stringify({
107
+ planningType,
108
+ answers,
109
+ finalize: finalize === true,
110
+ outputMode: outputMode ?? "wishlist"
111
+ })}'`
112
+ };
113
+ }
1
114
  export const planningModule = {
2
115
  registration: {
3
116
  id: "planning",
4
- version: "0.1.0",
117
+ version: "0.2.0",
5
118
  contractVersion: "1",
119
+ stateSchema: 1,
6
120
  capabilities: ["planning"],
7
121
  dependsOn: ["task-engine"],
122
+ optionalPeers: [],
8
123
  enabledByDefault: true,
9
124
  config: {
10
125
  path: "src/modules/planning/config.md",
11
126
  format: "md",
12
127
  description: "Planning module configuration contract."
13
128
  },
14
- state: {
15
- path: "src/modules/planning/state.md",
16
- format: "md",
17
- description: "Planning module runtime state contract."
18
- },
19
129
  instructions: {
20
130
  directory: "src/modules/planning/instructions",
21
131
  entries: [
@@ -23,8 +133,497 @@ export const planningModule = {
23
133
  name: "build-plan",
24
134
  file: "build-plan.md",
25
135
  description: "Generate a dependency-aware execution plan."
136
+ },
137
+ {
138
+ name: "list-planning-types",
139
+ file: "list-planning-types.md",
140
+ description: "List supported planning workflow types and their intent."
141
+ },
142
+ {
143
+ name: "explain-planning-rules",
144
+ file: "explain-planning-rules.md",
145
+ description: "Explain effective planning defaults and rule packs for a workflow type."
26
146
  }
27
147
  ]
28
148
  }
149
+ },
150
+ async onCommand(command, ctx) {
151
+ if (command.name === "list-planning-types") {
152
+ return {
153
+ ok: true,
154
+ code: "planning-types-listed",
155
+ message: `Found ${PLANNING_WORKFLOW_DESCRIPTORS.length} planning workflow types`,
156
+ data: {
157
+ responseSchemaVersion: 1,
158
+ planningTypes: PLANNING_WORKFLOW_DESCRIPTORS
159
+ }
160
+ };
161
+ }
162
+ if (command.name === "build-plan") {
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;
173
+ const planningType = typeof args.planningType === "string" ? args.planningType.trim() : "";
174
+ if (!PLANNING_WORKFLOW_TYPES.includes(planningType)) {
175
+ return {
176
+ ok: false,
177
+ code: "invalid-planning-type",
178
+ message: "build-plan requires planningType of: task-breakdown, sprint-phase, task-ordering, new-feature, change"
179
+ };
180
+ }
181
+ const descriptor = PLANNING_WORKFLOW_DESCRIPTORS.find((x) => x.type === planningType);
182
+ const resolvedRulePack = resolvePlanningRulePack(planningType, ctx.effectiveConfig);
183
+ const totalCriticalCount = resolvedRulePack.baseQuestions.length;
184
+ const answers = typeof args.answers === "object" && args.answers !== null && !Array.isArray(args.answers)
185
+ ? args.answers
186
+ : {};
187
+ const createWishlist = args.createWishlist !== false;
188
+ const finalize = args.finalize === true;
189
+ const { missingCritical, adaptiveFollowups } = nextPlanningQuestions(planningType, answers, ctx.effectiveConfig);
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
+ });
198
+ if (finalize && missingCritical.length > 0) {
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
+ });
214
+ return {
215
+ ok: true,
216
+ code: "planning-ready-with-warnings",
217
+ message: `Finalize allowed with unresolved critical questions because planning.hardBlockCriticalUnknowns=false`,
218
+ data: {
219
+ responseSchemaVersion: 1,
220
+ planningType,
221
+ outputMode,
222
+ status: "ready-with-warnings",
223
+ unresolvedCritical: missingCritical,
224
+ nextQuestions: [...missingCritical, ...adaptiveFollowups],
225
+ scoringHints,
226
+ capturedAnswers: answers,
227
+ cliGuidance
228
+ }
229
+ };
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
+ });
246
+ return {
247
+ ok: false,
248
+ code: "planning-critical-unknowns",
249
+ message: `Cannot finalize ${planningType}: unresolved critical questions (${missingCritical.map((q) => q.id).join(", ")})`,
250
+ data: {
251
+ responseSchemaVersion: 1,
252
+ planningType,
253
+ outputMode,
254
+ unresolvedCritical: missingCritical,
255
+ nextQuestions: [...missingCritical, ...adaptiveFollowups],
256
+ scoringHints,
257
+ cliGuidance: cliGuidanceBlocked
258
+ }
259
+ };
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
+ : [];
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
+ });
313
+ return {
314
+ ok: true,
315
+ code: "planning-questions",
316
+ message: `${missingCritical.length} critical planning questions require answers before finalize`,
317
+ data: {
318
+ responseSchemaVersion: 1,
319
+ planningType,
320
+ outputMode,
321
+ status: "needs-input",
322
+ unresolvedCritical: missingCritical,
323
+ nextQuestions: [...missingCritical, ...adaptiveFollowups],
324
+ scoringHints,
325
+ cliGuidance: cliGuidanceQuestions
326
+ }
327
+ };
328
+ }
329
+ const unresolvedIds = missingCritical.map((q) => q.id);
330
+ const artifact = composePlanningWishlistArtifact({
331
+ planningType: planningType,
332
+ answers,
333
+ unresolvedCriticalQuestionIds: unresolvedIds
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
+ }
468
+ if (!finalize || !createWishlist) {
469
+ await clearBuildPlanSession(ctx.workspacePath);
470
+ return {
471
+ ok: true,
472
+ code: "planning-wishlist-ready",
473
+ message: `Planning interview complete for ${planningType}; wishlist artifact ready`,
474
+ data: {
475
+ responseSchemaVersion: 1,
476
+ planningType,
477
+ descriptor,
478
+ outputMode,
479
+ scaffoldVersion: 3,
480
+ status: "ready-for-wishlist",
481
+ unresolvedCritical: [],
482
+ adaptiveWarnings,
483
+ adaptiveFollowups,
484
+ scoringHints,
485
+ capturedAnswers: answers,
486
+ artifact,
487
+ cliGuidance: toCliGuidance({
488
+ planningType,
489
+ answers,
490
+ unresolvedCriticalCount: 0,
491
+ totalCriticalCount,
492
+ finalize: createWishlist,
493
+ outputMode
494
+ })
495
+ }
496
+ };
497
+ }
498
+ const stores = await openPlanningStores(ctx);
499
+ const now = new Date().toISOString();
500
+ const intake = {
501
+ title: typeof args.title === "string" && args.title.trim().length > 0
502
+ ? args.title.trim()
503
+ : `${descriptor?.title ?? planningType} plan artifact`,
504
+ problemStatement: typeof answers.problemStatement === "string"
505
+ ? answers.problemStatement
506
+ : typeof answers.featureGoal === "string"
507
+ ? answers.featureGoal
508
+ : "Planning artifact generated from guided workflow.",
509
+ expectedOutcome: typeof answers.expectedOutcome === "string"
510
+ ? answers.expectedOutcome
511
+ : "Clear, reviewable planning artifact for execution decomposition.",
512
+ impact: typeof answers.impact === "string" ? answers.impact : "Improved planning quality and delivery confidence.",
513
+ constraints: typeof answers.constraints === "string"
514
+ ? answers.constraints
515
+ : artifact.risksAndConstraints.join("; ") || "None explicitly provided.",
516
+ successSignals: typeof answers.successSignals === "string"
517
+ ? answers.successSignals
518
+ : "Critical questions answered and artifact accepted by operators.",
519
+ requestor: typeof args.requestor === "string" && args.requestor.trim().length > 0
520
+ ? args.requestor.trim()
521
+ : ctx.resolvedActor ?? "planning-module",
522
+ evidenceRef: typeof args.evidenceRef === "string" && args.evidenceRef.trim().length > 0
523
+ ? args.evidenceRef.trim()
524
+ : `planning:${planningType}:${now}`
525
+ };
526
+ const valid = validateWishlistContentFields(intake);
527
+ if (!valid.ok) {
528
+ return {
529
+ ok: false,
530
+ code: "invalid-planning-artifact",
531
+ message: valid.errors.join("; ")
532
+ };
533
+ }
534
+ const taskId = allocateNextTaskNumericId(stores.taskStore.getAllTasks());
535
+ const task = taskEntityFromNewIntake(intake, taskId, now, {
536
+ planningType,
537
+ artifactSchemaVersion: artifact.schemaVersion,
538
+ artifact
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);
566
+ return {
567
+ ok: true,
568
+ code: "planning-artifact-created",
569
+ message: `Planning artifact created as wishlist intake task ${taskId}`,
570
+ data: {
571
+ responseSchemaVersion: 1,
572
+ planningType,
573
+ descriptor,
574
+ outputMode,
575
+ scaffoldVersion: 3,
576
+ status: "artifact-created",
577
+ wishlistId: taskId,
578
+ taskId,
579
+ adaptiveWarnings,
580
+ artifact,
581
+ unresolvedCritical: [],
582
+ adaptiveFollowups,
583
+ scoringHints,
584
+ capturedAnswers: answers,
585
+ cliGuidance: toCliGuidance({
586
+ planningType,
587
+ answers,
588
+ unresolvedCriticalCount: 0,
589
+ totalCriticalCount,
590
+ finalize: true,
591
+ outputMode
592
+ })
593
+ }
594
+ };
595
+ }
596
+ if (command.name === "explain-planning-rules") {
597
+ const args = command.args ?? {};
598
+ const planningType = typeof args.planningType === "string" ? args.planningType.trim() : "";
599
+ if (!PLANNING_WORKFLOW_TYPES.includes(planningType)) {
600
+ return {
601
+ ok: false,
602
+ code: "invalid-planning-type",
603
+ message: "explain-planning-rules requires planningType of: task-breakdown, sprint-phase, task-ordering, new-feature, change"
604
+ };
605
+ }
606
+ const config = resolvePlanningConfig(ctx.effectiveConfig);
607
+ const rulePack = resolvePlanningRulePack(planningType, ctx.effectiveConfig);
608
+ return {
609
+ ok: true,
610
+ code: "planning-rules-explained",
611
+ message: `Effective planning rules for ${planningType}`,
612
+ data: {
613
+ responseSchemaVersion: 1,
614
+ planningType,
615
+ defaultQuestionDepth: config.depth,
616
+ hardBlockCriticalUnknowns: config.hardBlockCriticalUnknowns,
617
+ adaptiveFinalizePolicy: config.adaptiveFinalizePolicy,
618
+ baseQuestions: rulePack.baseQuestions,
619
+ adaptiveQuestions: rulePack.adaptiveQuestions
620
+ }
621
+ };
622
+ }
623
+ return {
624
+ ok: false,
625
+ code: "unsupported-command",
626
+ message: `Planning module does not support command '${command.name}'`
627
+ };
29
628
  }
30
629
  };
@@ -0,0 +1,25 @@
1
+ import { type PlanningWorkflowType } from "./types.js";
2
+ export type PlanningQuestion = {
3
+ id: string;
4
+ prompt: string;
5
+ examples: string[];
6
+ whyItMatters: string;
7
+ critical: boolean;
8
+ };
9
+ export type PlanningQuestionDepth = "minimal" | "guided" | "adaptive";
10
+ export type AdaptiveFinalizePolicy = "off" | "warn" | "block";
11
+ export type PlanningRulePack = {
12
+ baseQuestions: PlanningQuestion[];
13
+ adaptiveQuestions: PlanningQuestion[];
14
+ };
15
+ export declare function resolvePlanningConfig(config: Record<string, unknown> | undefined): {
16
+ depth: PlanningQuestionDepth;
17
+ hardBlockCriticalUnknowns: boolean;
18
+ adaptiveFinalizePolicy: AdaptiveFinalizePolicy;
19
+ rulePacks: Partial<Record<PlanningWorkflowType, PlanningRulePack>>;
20
+ };
21
+ export declare function resolvePlanningRulePack(planningType: PlanningWorkflowType, config: Record<string, unknown> | undefined): PlanningRulePack;
22
+ export declare function nextPlanningQuestions(planningType: PlanningWorkflowType, answers: Record<string, unknown>, config?: Record<string, unknown>): {
23
+ missingCritical: PlanningQuestion[];
24
+ adaptiveFollowups: PlanningQuestion[];
25
+ };