@ai-content-space/loopx 0.1.1 → 0.1.2
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/package.json +1 -1
- package/skills/plan/SKILL.md +4 -0
- package/src/cli.mjs +6 -5
- package/src/next-skill.mjs +33 -0
- package/src/plan-runtime.mjs +26 -5
- package/src/workflow.mjs +6 -1
- package/templates/plan.md +6 -0
package/package.json
CHANGED
package/skills/plan/SKILL.md
CHANGED
|
@@ -130,6 +130,7 @@ Critic evaluates:
|
|
|
130
130
|
- clarity of risk mitigation
|
|
131
131
|
- testable acceptance criteria
|
|
132
132
|
- concrete verification steps
|
|
133
|
+
- execution-input completeness for each new or changed ingress / workflow entrypoint
|
|
133
134
|
- explicit non-goals and decision boundaries
|
|
134
135
|
- in deliberate mode: pre-mortem and expanded test plan quality
|
|
135
136
|
|
|
@@ -166,6 +167,7 @@ The final plan must include:
|
|
|
166
167
|
|
|
167
168
|
- ADR: Decision, Drivers, Alternatives considered, Why chosen, Consequences, Follow-ups
|
|
168
169
|
- concrete implementation steps sized to the actual task
|
|
170
|
+
- execution inputs mapped to concrete sources before build starts
|
|
169
171
|
- available execution lanes and recommended lane
|
|
170
172
|
- test and verification commands
|
|
171
173
|
- residual risks and assumptions
|
|
@@ -199,6 +201,7 @@ Without `--interactive`, report the approved plan and recommended next command,
|
|
|
199
201
|
- `plan_package_status`: `missing|partial|complete`
|
|
200
202
|
- `plan_acceptance_criteria_testable`: `true|false`
|
|
201
203
|
- `plan_verification_steps_resolved`: `true|false`
|
|
204
|
+
- `plan_execution_inputs_resolved`: `true|false`
|
|
202
205
|
- `requested_transition`: remains explicit before build/autopilot
|
|
203
206
|
|
|
204
207
|
The plan gate is blocked until:
|
|
@@ -208,6 +211,7 @@ The plan gate is blocked until:
|
|
|
208
211
|
- Critic verdict is `approve`
|
|
209
212
|
- acceptance criteria are testable
|
|
210
213
|
- verification steps are concrete
|
|
214
|
+
- execution inputs are fully mapped to concrete sources
|
|
211
215
|
- user approval exists for any execution transition
|
|
212
216
|
</Runtime_State_Machine>
|
|
213
217
|
|
package/src/cli.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { autopilotStage, approveStage, buildStage, clarifyStage, initWorkspace, planStage, reviewStage, statusSummary } from './workflow.mjs';
|
|
4
4
|
import { installBundledSkills } from './install-discovery.mjs';
|
|
5
|
+
import { withNextSkill } from './next-skill.mjs';
|
|
5
6
|
import { doctorRuntime, migrateLegacyRuntime } from './runtime-maintenance.mjs';
|
|
6
7
|
|
|
7
8
|
function usage() {
|
|
@@ -118,7 +119,7 @@ async function main() {
|
|
|
118
119
|
case 'clarify': {
|
|
119
120
|
const profile = options.get('--deep') ? 'deep' : 'standard';
|
|
120
121
|
const result = await clarifyStage(process.cwd(), positionals[0], { profile });
|
|
121
|
-
console.log(JSON.stringify({ ok: true, command, root: result.root, state: result.state }, null, 2));
|
|
122
|
+
console.log(JSON.stringify(withNextSkill({ ok: true, command, root: result.root, state: result.state }, result.state), null, 2));
|
|
122
123
|
return;
|
|
123
124
|
}
|
|
124
125
|
case 'approve': {
|
|
@@ -126,7 +127,7 @@ async function main() {
|
|
|
126
127
|
from: options.get('--from'),
|
|
127
128
|
to: options.get('--to'),
|
|
128
129
|
});
|
|
129
|
-
console.log(JSON.stringify({ ok: true, command, root: result.root, state: result.state }, null, 2));
|
|
130
|
+
console.log(JSON.stringify(withNextSkill({ ok: true, command, root: result.root, state: result.state }, result.state), null, 2));
|
|
130
131
|
return;
|
|
131
132
|
}
|
|
132
133
|
case 'plan': {
|
|
@@ -135,21 +136,21 @@ async function main() {
|
|
|
135
136
|
interactive: Boolean(options.get('--interactive')),
|
|
136
137
|
deliberate: Boolean(options.get('--deliberate')),
|
|
137
138
|
});
|
|
138
|
-
console.log(JSON.stringify({ ok: true, command, root: result.root, state: result.state }, null, 2));
|
|
139
|
+
console.log(JSON.stringify(withNextSkill({ ok: true, command, root: result.root, state: result.state }, result.state), null, 2));
|
|
139
140
|
return;
|
|
140
141
|
}
|
|
141
142
|
case 'build': {
|
|
142
143
|
const result = await buildStage(process.cwd(), positionals[0], {
|
|
143
144
|
noDeslop: Boolean(options.get('--no-deslop')),
|
|
144
145
|
});
|
|
145
|
-
console.log(JSON.stringify({ ok: true, command, root: result.root, state: result.state }, null, 2));
|
|
146
|
+
console.log(JSON.stringify(withNextSkill({ ok: true, command, root: result.root, state: result.state }, result.state), null, 2));
|
|
146
147
|
return;
|
|
147
148
|
}
|
|
148
149
|
case 'review': {
|
|
149
150
|
const result = await reviewStage(process.cwd(), positionals[0], {
|
|
150
151
|
reviewer: options.get('--reviewer') || 'independent-reviewer',
|
|
151
152
|
});
|
|
152
|
-
console.log(JSON.stringify({ ok: true, command, root: result.root, state: result.state, verdict: result.verdict }, null, 2));
|
|
153
|
+
console.log(JSON.stringify(withNextSkill({ ok: true, command, root: result.root, state: result.state, verdict: result.verdict }, result.state), null, 2));
|
|
153
154
|
return;
|
|
154
155
|
}
|
|
155
156
|
case 'autopilot': {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function nextSkillCommand(state) {
|
|
2
|
+
if (!state || state.stage_status !== 'awaiting-approval' || !state.slug) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
if (state.current_stage === 'clarify') {
|
|
6
|
+
return `$plan ${state.slug}`;
|
|
7
|
+
}
|
|
8
|
+
if (state.current_stage === 'plan' && Array.isArray(state.plan_blockers) && state.plan_blockers.length === 0) {
|
|
9
|
+
return `$build ${state.slug}`;
|
|
10
|
+
}
|
|
11
|
+
if (state.current_stage === 'build' && Array.isArray(state.build_blockers) && state.build_blockers.length === 0) {
|
|
12
|
+
return `$review ${state.slug}`;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function nextSkillHint(state) {
|
|
18
|
+
const command = nextSkillCommand(state);
|
|
19
|
+
if (!command) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return `Next: ${command}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function withNextSkill(payload, state) {
|
|
26
|
+
const nextCommand = nextSkillCommand(state);
|
|
27
|
+
const nextHint = nextSkillHint(state);
|
|
28
|
+
return {
|
|
29
|
+
...payload,
|
|
30
|
+
next_skill_command: nextCommand,
|
|
31
|
+
next_skill_hint: nextHint,
|
|
32
|
+
};
|
|
33
|
+
}
|
package/src/plan-runtime.mjs
CHANGED
|
@@ -65,6 +65,8 @@ function chineseBulletList(items) {
|
|
|
65
65
|
|
|
66
66
|
function plannerDraftFromSource({ slug, sourceText, deliberateMode }) {
|
|
67
67
|
const summary = buildSourceSummary(sourceText);
|
|
68
|
+
const executionInputs = bulletsFromSection(extractSection(sourceText, 'Execution Inputs'), []);
|
|
69
|
+
const executionInputsResolved = executionInputs.length > 0 && executionInputs.every((item) => !/\b(TBD|待定|unknown|later)\b/i.test(item));
|
|
68
70
|
const preMortem = deliberateMode
|
|
69
71
|
? [
|
|
70
72
|
'如果运行时没有独立的规划适配层,真实编排与测试替身会相互污染。',
|
|
@@ -113,10 +115,14 @@ function plannerDraftFromSource({ slug, sourceText, deliberateMode }) {
|
|
|
113
115
|
'## Implementation Steps',
|
|
114
116
|
'',
|
|
115
117
|
'1. Add a plan orchestration adapter for planner, architect, and critic.',
|
|
116
|
-
'2. Record plan iteration, review verdicts, and docs blockers in workflow state.',
|
|
118
|
+
'2. Record plan iteration, review verdicts, execution-input blockers, and docs blockers in workflow state.',
|
|
117
119
|
'3. Generate canonical plan artifacts and Chinese docs outputs from the approved planning source.',
|
|
118
120
|
'4. Expose plan-stage progress in CLI status.',
|
|
119
|
-
'5. Add deterministic regression coverage for happy path, iterate path, and
|
|
121
|
+
'5. Add deterministic regression coverage for happy path, iterate path, docs blockers, and unresolved execution inputs.',
|
|
122
|
+
'',
|
|
123
|
+
'## Execution Inputs',
|
|
124
|
+
'',
|
|
125
|
+
...(executionInputs.length > 0 ? executionInputs.map((item) => `- ${item}`) : ['- TBD: execution inputs not yet mapped to concrete sources.']),
|
|
120
126
|
'',
|
|
121
127
|
'## Risks',
|
|
122
128
|
'',
|
|
@@ -187,10 +193,11 @@ function plannerDraftFromSource({ slug, sourceText, deliberateMode }) {
|
|
|
187
193
|
'- clarify -> plan happy path',
|
|
188
194
|
'- critic iterate then approve path',
|
|
189
195
|
'- docs missing or non-Chinese blocking path',
|
|
196
|
+
'- execution inputs missing or marked TBD blocking path',
|
|
190
197
|
'',
|
|
191
198
|
'## Observability',
|
|
192
199
|
'',
|
|
193
|
-
'- status exposes iteration, architect review status, critic verdict, and docs blockers',
|
|
200
|
+
'- status exposes iteration, architect review status, critic verdict, execution input blockers, and docs blockers',
|
|
194
201
|
].join('\n'),
|
|
195
202
|
docs: {
|
|
196
203
|
architecture: [
|
|
@@ -241,6 +248,7 @@ function plannerDraftFromSource({ slug, sourceText, deliberateMode }) {
|
|
|
241
248
|
optionsReviewed: true,
|
|
242
249
|
acceptanceCriteriaTestable: true,
|
|
243
250
|
verificationStepsResolved: true,
|
|
251
|
+
executionInputsResolved,
|
|
244
252
|
};
|
|
245
253
|
}
|
|
246
254
|
|
|
@@ -284,12 +292,16 @@ function defaultCriticReview({ plannerDraft, iteration }) {
|
|
|
284
292
|
if (!plannerDraft.verificationStepsResolved) {
|
|
285
293
|
findings.push('Verification steps are not concrete.');
|
|
286
294
|
}
|
|
295
|
+
if (!plannerDraft.executionInputsResolved) {
|
|
296
|
+
findings.push('Execution inputs are not fully mapped to concrete sources.');
|
|
297
|
+
}
|
|
287
298
|
if (!containsChinese(plannerDraft.docs.architecture) || !containsChinese(plannerDraft.docs.design) || !containsChinese(plannerDraft.docs.testPlan)) {
|
|
288
299
|
findings.push('Required docs outputs are not Chinese.');
|
|
289
300
|
}
|
|
290
301
|
return reviewArtifact('critic', iteration, findings.length > 0 ? 'iterate' : 'approve', findings, {
|
|
291
302
|
acceptanceCriteriaTestable: plannerDraft.acceptanceCriteriaTestable,
|
|
292
303
|
verificationStepsResolved: plannerDraft.verificationStepsResolved,
|
|
304
|
+
executionInputsResolved: plannerDraft.executionInputsResolved,
|
|
293
305
|
});
|
|
294
306
|
}
|
|
295
307
|
|
|
@@ -302,6 +314,9 @@ function scriptedVerdict(script, index, fallback) {
|
|
|
302
314
|
}
|
|
303
315
|
|
|
304
316
|
function scriptedCriticReview({ plannerDraft, iteration }, script, index) {
|
|
317
|
+
if (!Array.isArray(script) || script.length === 0) {
|
|
318
|
+
return defaultCriticReview({ plannerDraft, iteration });
|
|
319
|
+
}
|
|
305
320
|
const verdict = scriptedVerdict(script, index, 'approve');
|
|
306
321
|
const findings = verdict === 'approve'
|
|
307
322
|
? ['Structured planning outputs satisfy the scripted approval path.']
|
|
@@ -309,6 +324,7 @@ function scriptedCriticReview({ plannerDraft, iteration }, script, index) {
|
|
|
309
324
|
return reviewArtifact('critic', iteration, verdict, findings, {
|
|
310
325
|
acceptanceCriteriaTestable: plannerDraft.acceptanceCriteriaTestable,
|
|
311
326
|
verificationStepsResolved: plannerDraft.verificationStepsResolved,
|
|
327
|
+
executionInputsResolved: plannerDraft.executionInputsResolved,
|
|
312
328
|
});
|
|
313
329
|
}
|
|
314
330
|
|
|
@@ -321,6 +337,9 @@ export function createScriptedPlanAdapter(script = {}) {
|
|
|
321
337
|
},
|
|
322
338
|
async architect(context) {
|
|
323
339
|
const base = defaultArchitectReview(context);
|
|
340
|
+
if (!Array.isArray(script.architect) || script.architect.length === 0) {
|
|
341
|
+
return base;
|
|
342
|
+
}
|
|
324
343
|
const mode = scriptedVerdict(script.architect, architectIndex, 'approve');
|
|
325
344
|
architectIndex += 1;
|
|
326
345
|
return {
|
|
@@ -363,7 +382,8 @@ export function createRealPlanAdapter({ model } = {}) {
|
|
|
363
382
|
' "principlesResolved": boolean,',
|
|
364
383
|
' "optionsReviewed": boolean,',
|
|
365
384
|
' "acceptanceCriteriaTestable": boolean,',
|
|
366
|
-
' "verificationStepsResolved": boolean',
|
|
385
|
+
' "verificationStepsResolved": boolean,',
|
|
386
|
+
' "executionInputsResolved": boolean',
|
|
367
387
|
'}',
|
|
368
388
|
`Deliberate mode: ${Boolean(context.deliberateMode)}`,
|
|
369
389
|
'',
|
|
@@ -433,7 +453,8 @@ export function createRealPlanAdapter({ model } = {}) {
|
|
|
433
453
|
' "verdict": "approve" | "iterate" | "reject",',
|
|
434
454
|
' "findings": string[],',
|
|
435
455
|
' "acceptanceCriteriaTestable": boolean,',
|
|
436
|
-
' "verificationStepsResolved": boolean',
|
|
456
|
+
' "verificationStepsResolved": boolean,',
|
|
457
|
+
' "executionInputsResolved": boolean',
|
|
437
458
|
'}',
|
|
438
459
|
'Do not ask questions. Do not wrap JSON in markdown.',
|
|
439
460
|
'',
|
package/src/workflow.mjs
CHANGED
|
@@ -329,6 +329,7 @@ function createInitialState(slug, profile) {
|
|
|
329
329
|
plan_critic_verdict: 'none',
|
|
330
330
|
plan_acceptance_criteria_testable: false,
|
|
331
331
|
plan_verification_steps_resolved: false,
|
|
332
|
+
plan_execution_inputs_resolved: false,
|
|
332
333
|
plan_docs_status: 'missing',
|
|
333
334
|
plan_docs_artifact_paths: null,
|
|
334
335
|
plan_review_artifact_paths: [],
|
|
@@ -588,6 +589,9 @@ async function readPlanCompletion(cwd, root, slug, state) {
|
|
|
588
589
|
if (!state.plan_verification_steps_resolved) {
|
|
589
590
|
blockers.push('verification_steps_unresolved');
|
|
590
591
|
}
|
|
592
|
+
if (!state.plan_execution_inputs_resolved) {
|
|
593
|
+
blockers.push('execution_inputs_unresolved');
|
|
594
|
+
}
|
|
591
595
|
if (!state.plan_artifact_path || !existsSync(state.plan_artifact_path)) {
|
|
592
596
|
blockers.push('missing_prd');
|
|
593
597
|
}
|
|
@@ -1215,6 +1219,7 @@ export async function planStage(cwd, slug, options = {}) {
|
|
|
1215
1219
|
plan_critic_verdict: criticReview.verdict,
|
|
1216
1220
|
plan_acceptance_criteria_testable: criticReview.acceptanceCriteriaTestable,
|
|
1217
1221
|
plan_verification_steps_resolved: criticReview.verificationStepsResolved,
|
|
1222
|
+
plan_execution_inputs_resolved: criticReview.executionInputsResolved,
|
|
1218
1223
|
plan_package_status: 'complete',
|
|
1219
1224
|
plan_docs_artifact_paths: docPaths,
|
|
1220
1225
|
plan_review_artifact_paths: reviewArtifactPaths,
|
|
@@ -1322,7 +1327,7 @@ export async function buildStage(cwd, slug, options = {}) {
|
|
|
1322
1327
|
build_support_evidence_paths: supportArtifacts,
|
|
1323
1328
|
build_no_deslop: noDeslop,
|
|
1324
1329
|
active_run_id: current?.runId || null,
|
|
1325
|
-
pending_user_decision: TRANSITIONS.NONE,
|
|
1330
|
+
pending_user_decision: finalBlocked ? TRANSITIONS.NONE : TRANSITIONS.BUILD_TO_REVIEW,
|
|
1326
1331
|
requested_transition: TRANSITIONS.NONE,
|
|
1327
1332
|
last_confirmed_transition: TRANSITIONS.PLAN_TO_BUILD,
|
|
1328
1333
|
approval: {
|
package/templates/plan.md
CHANGED
|
@@ -24,6 +24,12 @@ stage_owner: plan
|
|
|
24
24
|
2. keep the loop bounded and deterministic
|
|
25
25
|
3. verify outputs before review
|
|
26
26
|
|
|
27
|
+
## Execution Inputs
|
|
28
|
+
|
|
29
|
+
- list every required build-time input for new or changed entrypoints
|
|
30
|
+
- map each input to a concrete source: config, filename, payload, upstream API, user input, or default
|
|
31
|
+
- no item may remain TBD before plan approval
|
|
32
|
+
|
|
27
33
|
## Risks
|
|
28
34
|
|
|
29
35
|
- skipping approval or verification would break the loopx contract
|