@workflow-cannon/workspace-kit 0.16.1 → 0.18.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/dist/core/config-metadata.js +53 -0
- package/dist/core/workspace-kit-config.js +7 -2
- package/dist/modules/planning/artifact.d.ts +19 -0
- package/dist/modules/planning/artifact.js +72 -0
- package/dist/modules/planning/index.js +265 -0
- package/dist/modules/planning/question-engine.d.ts +23 -0
- package/dist/modules/planning/question-engine.js +277 -0
- package/dist/modules/planning/types.d.ts +9 -0
- package/dist/modules/planning/types.js +39 -0
- package/dist/modules/task-engine/index.js +272 -8
- package/dist/modules/task-engine/planning-config.d.ts +3 -0
- package/dist/modules/task-engine/planning-config.js +7 -0
- package/dist/modules/task-engine/strict-task-validation.d.ts +3 -0
- package/dist/modules/task-engine/strict-task-validation.js +52 -0
- package/dist/modules/task-engine/task-type-validation.d.ts +10 -0
- package/dist/modules/task-engine/task-type-validation.js +26 -0
- package/dist/modules/task-engine/types.d.ts +1 -1
- package/package.json +3 -2
- package/schemas/compatibility-matrix.schema.json +64 -0
- package/schemas/parity-evidence.schema.json +60 -0
- package/schemas/task-engine-run-contracts.schema.json +560 -0
- package/schemas/task-engine-state.schema.json +41 -0
- package/schemas/workspace-kit-profile.schema.json +55 -0
|
@@ -56,6 +56,59 @@ const REGISTRY = {
|
|
|
56
56
|
exposure: "public",
|
|
57
57
|
writableLayers: ["project", "user"]
|
|
58
58
|
},
|
|
59
|
+
"tasks.strictValidation": {
|
|
60
|
+
key: "tasks.strictValidation",
|
|
61
|
+
type: "boolean",
|
|
62
|
+
description: "When true, task mutations validate the full active task set before persistence and fail on invalid task records.",
|
|
63
|
+
default: false,
|
|
64
|
+
domainScope: "project",
|
|
65
|
+
owningModule: "task-engine",
|
|
66
|
+
sensitive: false,
|
|
67
|
+
requiresRestart: false,
|
|
68
|
+
requiresApproval: false,
|
|
69
|
+
exposure: "public",
|
|
70
|
+
writableLayers: ["project", "user"]
|
|
71
|
+
},
|
|
72
|
+
"planning.defaultQuestionDepth": {
|
|
73
|
+
key: "planning.defaultQuestionDepth",
|
|
74
|
+
type: "string",
|
|
75
|
+
description: "Planning interview depth mode: minimal (critical only), guided (critical + static follow-ups), or adaptive (context-driven follow-ups).",
|
|
76
|
+
default: "adaptive",
|
|
77
|
+
allowedValues: ["minimal", "guided", "adaptive"],
|
|
78
|
+
domainScope: "project",
|
|
79
|
+
owningModule: "planning",
|
|
80
|
+
sensitive: false,
|
|
81
|
+
requiresRestart: false,
|
|
82
|
+
requiresApproval: false,
|
|
83
|
+
exposure: "maintainer",
|
|
84
|
+
writableLayers: ["project", "user"]
|
|
85
|
+
},
|
|
86
|
+
"planning.hardBlockCriticalUnknowns": {
|
|
87
|
+
key: "planning.hardBlockCriticalUnknowns",
|
|
88
|
+
type: "boolean",
|
|
89
|
+
description: "When true, planning finalize requests fail until critical unknown questions are answered.",
|
|
90
|
+
default: true,
|
|
91
|
+
domainScope: "project",
|
|
92
|
+
owningModule: "planning",
|
|
93
|
+
sensitive: false,
|
|
94
|
+
requiresRestart: false,
|
|
95
|
+
requiresApproval: false,
|
|
96
|
+
exposure: "maintainer",
|
|
97
|
+
writableLayers: ["project", "user"]
|
|
98
|
+
},
|
|
99
|
+
"planning.rulePacks": {
|
|
100
|
+
key: "planning.rulePacks",
|
|
101
|
+
type: "object",
|
|
102
|
+
description: "Optional object overrides for planning rule packs by workflow type (`baseQuestions` and `adaptiveQuestions`).",
|
|
103
|
+
default: {},
|
|
104
|
+
domainScope: "project",
|
|
105
|
+
owningModule: "planning",
|
|
106
|
+
sensitive: false,
|
|
107
|
+
requiresRestart: false,
|
|
108
|
+
requiresApproval: false,
|
|
109
|
+
exposure: "maintainer",
|
|
110
|
+
writableLayers: ["project", "user"]
|
|
111
|
+
},
|
|
59
112
|
"policy.extraSensitiveModuleCommands": {
|
|
60
113
|
key: "policy.extraSensitiveModuleCommands",
|
|
61
114
|
type: "array",
|
|
@@ -11,7 +11,8 @@ export function getProjectConfigPath(workspacePath) {
|
|
|
11
11
|
export const KIT_CONFIG_DEFAULTS = {
|
|
12
12
|
core: {},
|
|
13
13
|
tasks: {
|
|
14
|
-
storeRelativePath: ".workspace-kit/tasks/state.json"
|
|
14
|
+
storeRelativePath: ".workspace-kit/tasks/state.json",
|
|
15
|
+
strictValidation: false
|
|
15
16
|
},
|
|
16
17
|
documentation: {},
|
|
17
18
|
responseTemplates: {
|
|
@@ -45,7 +46,11 @@ export const KIT_CONFIG_DEFAULTS = {
|
|
|
45
46
|
*/
|
|
46
47
|
export const MODULE_CONFIG_CONTRIBUTIONS = {
|
|
47
48
|
approvals: {},
|
|
48
|
-
planning: {
|
|
49
|
+
planning: {
|
|
50
|
+
defaultQuestionDepth: "adaptive",
|
|
51
|
+
hardBlockCriticalUnknowns: true,
|
|
52
|
+
rulePacks: {}
|
|
53
|
+
}
|
|
49
54
|
};
|
|
50
55
|
export function deepMerge(target, source) {
|
|
51
56
|
const out = { ...target };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PlanningWorkflowType } from "./types.js";
|
|
2
|
+
export type PlanningWishlistArtifact = {
|
|
3
|
+
schemaVersion: 1;
|
|
4
|
+
planningType: PlanningWorkflowType;
|
|
5
|
+
generatedAt: string;
|
|
6
|
+
goals: string[];
|
|
7
|
+
approach: string;
|
|
8
|
+
majorTechnicalDecisions: string[];
|
|
9
|
+
candidateFeaturesOrChanges: string[];
|
|
10
|
+
assumptions: string[];
|
|
11
|
+
openQuestions: string[];
|
|
12
|
+
risksAndConstraints: string[];
|
|
13
|
+
sourceAnswers: Record<string, string>;
|
|
14
|
+
};
|
|
15
|
+
export declare function composePlanningWishlistArtifact(args: {
|
|
16
|
+
planningType: PlanningWorkflowType;
|
|
17
|
+
answers: Record<string, unknown>;
|
|
18
|
+
unresolvedCriticalQuestionIds: string[];
|
|
19
|
+
}): PlanningWishlistArtifact;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
function answer(answers, key, fallback) {
|
|
2
|
+
const value = answers[key];
|
|
3
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
4
|
+
return fallback;
|
|
5
|
+
}
|
|
6
|
+
return value.trim();
|
|
7
|
+
}
|
|
8
|
+
function list(answers, key) {
|
|
9
|
+
const value = answers[key];
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
return value.filter((x) => typeof x === "string" && x.trim().length > 0).map((x) => x.trim());
|
|
12
|
+
}
|
|
13
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
14
|
+
return [value.trim()];
|
|
15
|
+
}
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
export function composePlanningWishlistArtifact(args) {
|
|
19
|
+
const { planningType, answers, unresolvedCriticalQuestionIds } = args;
|
|
20
|
+
const sourceAnswers = {};
|
|
21
|
+
for (const [key, value] of Object.entries(answers)) {
|
|
22
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
23
|
+
sourceAnswers[key] = value.trim();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const goals = list(answers, "goals");
|
|
27
|
+
if (goals.length === 0) {
|
|
28
|
+
const inferredGoal = answer(answers, "goal", "") ||
|
|
29
|
+
answer(answers, "featureGoal", "") ||
|
|
30
|
+
answer(answers, "changeGoal", "");
|
|
31
|
+
if (inferredGoal)
|
|
32
|
+
goals.push(inferredGoal);
|
|
33
|
+
}
|
|
34
|
+
const candidate = list(answers, "candidateFeaturesOrChanges");
|
|
35
|
+
if (candidate.length === 0) {
|
|
36
|
+
const inferred = answer(answers, "scope", "");
|
|
37
|
+
if (inferred)
|
|
38
|
+
candidate.push(inferred);
|
|
39
|
+
}
|
|
40
|
+
const majorTechnicalDecisions = list(answers, "majorTechnicalDecisions");
|
|
41
|
+
const decisionRationale = answer(answers, "decisionRationale", "");
|
|
42
|
+
if (decisionRationale && majorTechnicalDecisions.length === 0) {
|
|
43
|
+
majorTechnicalDecisions.push(decisionRationale);
|
|
44
|
+
}
|
|
45
|
+
const assumptions = list(answers, "assumptions");
|
|
46
|
+
const openQuestions = [
|
|
47
|
+
...list(answers, "openQuestions"),
|
|
48
|
+
...unresolvedCriticalQuestionIds.map((id) => `Unresolved critical question: ${id}`)
|
|
49
|
+
];
|
|
50
|
+
const risksAndConstraints = list(answers, "risksAndConstraints");
|
|
51
|
+
const constraints = answer(answers, "constraints", "");
|
|
52
|
+
if (constraints) {
|
|
53
|
+
risksAndConstraints.push(`Constraint: ${constraints}`);
|
|
54
|
+
}
|
|
55
|
+
const riskPriority = answer(answers, "riskPriority", "");
|
|
56
|
+
if (riskPriority) {
|
|
57
|
+
risksAndConstraints.push(`Risk priority: ${riskPriority}`);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
schemaVersion: 1,
|
|
61
|
+
planningType,
|
|
62
|
+
generatedAt: new Date().toISOString(),
|
|
63
|
+
goals,
|
|
64
|
+
approach: answer(answers, "approach", "Context-driven workflow guided by planning rules."),
|
|
65
|
+
majorTechnicalDecisions,
|
|
66
|
+
candidateFeaturesOrChanges: candidate,
|
|
67
|
+
assumptions,
|
|
68
|
+
openQuestions,
|
|
69
|
+
risksAndConstraints,
|
|
70
|
+
sourceAnswers
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -1,3 +1,36 @@
|
|
|
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 } from "../task-engine/planning-open.js";
|
|
5
|
+
import { buildWishlistItemFromIntake, validateWishlistIntakePayload } from "../task-engine/wishlist-validation.js";
|
|
6
|
+
function nextWishlistId(items) {
|
|
7
|
+
let max = 0;
|
|
8
|
+
for (const item of items) {
|
|
9
|
+
const match = /^W(\d+)$/.exec(item.id);
|
|
10
|
+
if (!match)
|
|
11
|
+
continue;
|
|
12
|
+
const parsed = Number(match[1]);
|
|
13
|
+
if (Number.isFinite(parsed)) {
|
|
14
|
+
max = Math.max(max, parsed);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return `W${max + 1}`;
|
|
18
|
+
}
|
|
19
|
+
function toCliGuidance(args) {
|
|
20
|
+
const { planningType, answers, unresolvedCriticalCount, totalCriticalCount, finalize } = args;
|
|
21
|
+
const answeredCritical = Math.max(0, totalCriticalCount - unresolvedCriticalCount);
|
|
22
|
+
const completionPct = totalCriticalCount > 0 ? Math.round((answeredCritical / totalCriticalCount) * 100) : 100;
|
|
23
|
+
return {
|
|
24
|
+
answeredCritical,
|
|
25
|
+
totalCritical: totalCriticalCount,
|
|
26
|
+
completionPct,
|
|
27
|
+
suggestedNextCommand: `workspace-kit run build-plan '${JSON.stringify({
|
|
28
|
+
planningType,
|
|
29
|
+
answers,
|
|
30
|
+
finalize: finalize === true
|
|
31
|
+
})}'`
|
|
32
|
+
};
|
|
33
|
+
}
|
|
1
34
|
export const planningModule = {
|
|
2
35
|
registration: {
|
|
3
36
|
id: "planning",
|
|
@@ -23,8 +56,240 @@ export const planningModule = {
|
|
|
23
56
|
name: "build-plan",
|
|
24
57
|
file: "build-plan.md",
|
|
25
58
|
description: "Generate a dependency-aware execution plan."
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "list-planning-types",
|
|
62
|
+
file: "list-planning-types.md",
|
|
63
|
+
description: "List supported planning workflow types and their intent."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "explain-planning-rules",
|
|
67
|
+
file: "explain-planning-rules.md",
|
|
68
|
+
description: "Explain effective planning defaults and rule packs for a workflow type."
|
|
26
69
|
}
|
|
27
70
|
]
|
|
28
71
|
}
|
|
72
|
+
},
|
|
73
|
+
async onCommand(command, ctx) {
|
|
74
|
+
if (command.name === "list-planning-types") {
|
|
75
|
+
return {
|
|
76
|
+
ok: true,
|
|
77
|
+
code: "planning-types-listed",
|
|
78
|
+
message: `Found ${PLANNING_WORKFLOW_DESCRIPTORS.length} planning workflow types`,
|
|
79
|
+
data: {
|
|
80
|
+
planningTypes: PLANNING_WORKFLOW_DESCRIPTORS
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (command.name === "build-plan") {
|
|
85
|
+
const args = command.args ?? {};
|
|
86
|
+
const planningType = typeof args.planningType === "string" ? args.planningType.trim() : "";
|
|
87
|
+
if (!PLANNING_WORKFLOW_TYPES.includes(planningType)) {
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
code: "invalid-planning-type",
|
|
91
|
+
message: "build-plan requires planningType of: task-breakdown, sprint-phase, task-ordering, new-feature, change"
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const descriptor = PLANNING_WORKFLOW_DESCRIPTORS.find((x) => x.type === planningType);
|
|
95
|
+
const resolvedRulePack = resolvePlanningRulePack(planningType, ctx.effectiveConfig);
|
|
96
|
+
const totalCriticalCount = resolvedRulePack.baseQuestions.length;
|
|
97
|
+
const answers = typeof args.answers === "object" && args.answers !== null && !Array.isArray(args.answers)
|
|
98
|
+
? args.answers
|
|
99
|
+
: {};
|
|
100
|
+
const createWishlist = args.createWishlist !== false;
|
|
101
|
+
const finalize = args.finalize === true;
|
|
102
|
+
const { missingCritical, adaptiveFollowups } = nextPlanningQuestions(planningType, answers, ctx.effectiveConfig);
|
|
103
|
+
const config = resolvePlanningConfig(ctx.effectiveConfig);
|
|
104
|
+
if (finalize && missingCritical.length > 0) {
|
|
105
|
+
if (!config.hardBlockCriticalUnknowns) {
|
|
106
|
+
return {
|
|
107
|
+
ok: true,
|
|
108
|
+
code: "planning-ready-with-warnings",
|
|
109
|
+
message: `Finalize allowed with unresolved critical questions because planning.hardBlockCriticalUnknowns=false`,
|
|
110
|
+
data: {
|
|
111
|
+
planningType,
|
|
112
|
+
status: "ready-with-warnings",
|
|
113
|
+
unresolvedCritical: missingCritical,
|
|
114
|
+
nextQuestions: [...missingCritical, ...adaptiveFollowups],
|
|
115
|
+
capturedAnswers: answers,
|
|
116
|
+
cliGuidance: toCliGuidance({
|
|
117
|
+
planningType,
|
|
118
|
+
answers,
|
|
119
|
+
unresolvedCriticalCount: missingCritical.length,
|
|
120
|
+
totalCriticalCount
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
ok: false,
|
|
127
|
+
code: "planning-critical-unknowns",
|
|
128
|
+
message: `Cannot finalize ${planningType}: unresolved critical questions (${missingCritical.map((q) => q.id).join(", ")})`,
|
|
129
|
+
data: {
|
|
130
|
+
planningType,
|
|
131
|
+
unresolvedCritical: missingCritical,
|
|
132
|
+
nextQuestions: [...missingCritical, ...adaptiveFollowups],
|
|
133
|
+
cliGuidance: toCliGuidance({
|
|
134
|
+
planningType,
|
|
135
|
+
answers,
|
|
136
|
+
unresolvedCriticalCount: missingCritical.length,
|
|
137
|
+
totalCriticalCount,
|
|
138
|
+
finalize: true
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (missingCritical.length > 0) {
|
|
144
|
+
return {
|
|
145
|
+
ok: true,
|
|
146
|
+
code: "planning-questions",
|
|
147
|
+
message: `${missingCritical.length} critical planning questions require answers before finalize`,
|
|
148
|
+
data: {
|
|
149
|
+
planningType,
|
|
150
|
+
status: "needs-input",
|
|
151
|
+
unresolvedCritical: missingCritical,
|
|
152
|
+
nextQuestions: [...missingCritical, ...adaptiveFollowups],
|
|
153
|
+
cliGuidance: toCliGuidance({
|
|
154
|
+
planningType,
|
|
155
|
+
answers,
|
|
156
|
+
unresolvedCriticalCount: missingCritical.length,
|
|
157
|
+
totalCriticalCount
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const unresolvedIds = missingCritical.map((q) => q.id);
|
|
163
|
+
const artifact = composePlanningWishlistArtifact({
|
|
164
|
+
planningType: planningType,
|
|
165
|
+
answers,
|
|
166
|
+
unresolvedCriticalQuestionIds: unresolvedIds
|
|
167
|
+
});
|
|
168
|
+
if (!finalize || !createWishlist) {
|
|
169
|
+
return {
|
|
170
|
+
ok: true,
|
|
171
|
+
code: "planning-ready",
|
|
172
|
+
message: `Planning interview complete for ${planningType}; artifact ready`,
|
|
173
|
+
data: {
|
|
174
|
+
planningType,
|
|
175
|
+
descriptor,
|
|
176
|
+
scaffoldVersion: 3,
|
|
177
|
+
status: "ready-for-artifact",
|
|
178
|
+
unresolvedCritical: [],
|
|
179
|
+
adaptiveFollowups,
|
|
180
|
+
capturedAnswers: answers,
|
|
181
|
+
artifact,
|
|
182
|
+
cliGuidance: toCliGuidance({
|
|
183
|
+
planningType,
|
|
184
|
+
answers,
|
|
185
|
+
unresolvedCriticalCount: 0,
|
|
186
|
+
totalCriticalCount,
|
|
187
|
+
finalize: createWishlist
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const stores = await openPlanningStores(ctx);
|
|
193
|
+
const wishlist = await stores.openWishlist();
|
|
194
|
+
const wishlistId = nextWishlistId(wishlist.getAllItems());
|
|
195
|
+
const now = new Date().toISOString();
|
|
196
|
+
const intake = {
|
|
197
|
+
id: wishlistId,
|
|
198
|
+
title: typeof args.title === "string" && args.title.trim().length > 0
|
|
199
|
+
? args.title.trim()
|
|
200
|
+
: `${descriptor?.title ?? planningType} plan artifact`,
|
|
201
|
+
problemStatement: typeof answers.problemStatement === "string"
|
|
202
|
+
? answers.problemStatement
|
|
203
|
+
: typeof answers.featureGoal === "string"
|
|
204
|
+
? answers.featureGoal
|
|
205
|
+
: "Planning artifact generated from guided workflow.",
|
|
206
|
+
expectedOutcome: typeof answers.expectedOutcome === "string"
|
|
207
|
+
? answers.expectedOutcome
|
|
208
|
+
: "Clear, reviewable planning artifact for execution decomposition.",
|
|
209
|
+
impact: typeof answers.impact === "string" ? answers.impact : "Improved planning quality and delivery confidence.",
|
|
210
|
+
constraints: typeof answers.constraints === "string"
|
|
211
|
+
? answers.constraints
|
|
212
|
+
: artifact.risksAndConstraints.join("; ") || "None explicitly provided.",
|
|
213
|
+
successSignals: typeof answers.successSignals === "string"
|
|
214
|
+
? answers.successSignals
|
|
215
|
+
: "Critical questions answered and artifact accepted by operators.",
|
|
216
|
+
requestor: typeof args.requestor === "string" && args.requestor.trim().length > 0
|
|
217
|
+
? args.requestor.trim()
|
|
218
|
+
: ctx.resolvedActor ?? "planning-module",
|
|
219
|
+
evidenceRef: typeof args.evidenceRef === "string" && args.evidenceRef.trim().length > 0
|
|
220
|
+
? args.evidenceRef.trim()
|
|
221
|
+
: `planning:${planningType}:${now}`
|
|
222
|
+
};
|
|
223
|
+
const valid = validateWishlistIntakePayload(intake);
|
|
224
|
+
if (!valid.ok) {
|
|
225
|
+
return {
|
|
226
|
+
ok: false,
|
|
227
|
+
code: "invalid-planning-artifact",
|
|
228
|
+
message: valid.errors.join("; ")
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const item = buildWishlistItemFromIntake(intake, now);
|
|
232
|
+
item.updatedAt = now;
|
|
233
|
+
item.metadata = {
|
|
234
|
+
planningType,
|
|
235
|
+
artifactSchemaVersion: artifact.schemaVersion,
|
|
236
|
+
artifact
|
|
237
|
+
};
|
|
238
|
+
wishlist.addItem(item);
|
|
239
|
+
await wishlist.save();
|
|
240
|
+
return {
|
|
241
|
+
ok: true,
|
|
242
|
+
code: "planning-artifact-created",
|
|
243
|
+
message: `Planning artifact created as wishlist item ${wishlistId}`,
|
|
244
|
+
data: {
|
|
245
|
+
planningType,
|
|
246
|
+
descriptor,
|
|
247
|
+
scaffoldVersion: 3,
|
|
248
|
+
status: "artifact-created",
|
|
249
|
+
wishlistId,
|
|
250
|
+
artifact,
|
|
251
|
+
unresolvedCritical: [],
|
|
252
|
+
adaptiveFollowups,
|
|
253
|
+
capturedAnswers: answers,
|
|
254
|
+
cliGuidance: toCliGuidance({
|
|
255
|
+
planningType,
|
|
256
|
+
answers,
|
|
257
|
+
unresolvedCriticalCount: 0,
|
|
258
|
+
totalCriticalCount,
|
|
259
|
+
finalize: true
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (command.name === "explain-planning-rules") {
|
|
265
|
+
const args = command.args ?? {};
|
|
266
|
+
const planningType = typeof args.planningType === "string" ? args.planningType.trim() : "";
|
|
267
|
+
if (!PLANNING_WORKFLOW_TYPES.includes(planningType)) {
|
|
268
|
+
return {
|
|
269
|
+
ok: false,
|
|
270
|
+
code: "invalid-planning-type",
|
|
271
|
+
message: "explain-planning-rules requires planningType of: task-breakdown, sprint-phase, task-ordering, new-feature, change"
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
const config = resolvePlanningConfig(ctx.effectiveConfig);
|
|
275
|
+
const rulePack = resolvePlanningRulePack(planningType, ctx.effectiveConfig);
|
|
276
|
+
return {
|
|
277
|
+
ok: true,
|
|
278
|
+
code: "planning-rules-explained",
|
|
279
|
+
message: `Effective planning rules for ${planningType}`,
|
|
280
|
+
data: {
|
|
281
|
+
planningType,
|
|
282
|
+
defaultQuestionDepth: config.depth,
|
|
283
|
+
hardBlockCriticalUnknowns: config.hardBlockCriticalUnknowns,
|
|
284
|
+
baseQuestions: rulePack.baseQuestions,
|
|
285
|
+
adaptiveQuestions: rulePack.adaptiveQuestions
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
ok: false,
|
|
291
|
+
code: "unsupported-command",
|
|
292
|
+
message: `Planning module does not support command '${command.name}'`
|
|
293
|
+
};
|
|
29
294
|
}
|
|
30
295
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
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 PlanningRulePack = {
|
|
11
|
+
baseQuestions: PlanningQuestion[];
|
|
12
|
+
adaptiveQuestions: PlanningQuestion[];
|
|
13
|
+
};
|
|
14
|
+
export declare function resolvePlanningConfig(config: Record<string, unknown> | undefined): {
|
|
15
|
+
depth: PlanningQuestionDepth;
|
|
16
|
+
hardBlockCriticalUnknowns: boolean;
|
|
17
|
+
rulePacks: Partial<Record<PlanningWorkflowType, PlanningRulePack>>;
|
|
18
|
+
};
|
|
19
|
+
export declare function resolvePlanningRulePack(planningType: PlanningWorkflowType, config: Record<string, unknown> | undefined): PlanningRulePack;
|
|
20
|
+
export declare function nextPlanningQuestions(planningType: PlanningWorkflowType, answers: Record<string, unknown>, config?: Record<string, unknown>): {
|
|
21
|
+
missingCritical: PlanningQuestion[];
|
|
22
|
+
adaptiveFollowups: PlanningQuestion[];
|
|
23
|
+
};
|