@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ai-content-space/loopx",
3
3
  "type": "module",
4
- "version": "0.1.1",
4
+ "version": "0.1.2",
5
5
  "description": "Skill-first loopx workflow product for Codex",
6
6
  "repository": {
7
7
  "type": "git",
@@ -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
+ }
@@ -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 docs blockers.',
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