substrate-ai 0.2.23 → 0.2.24

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/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-BaAws8IQ.js";
2
+ import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-CT8B9gG9.js";
3
3
  import { createLogger, deepMask } from "../logger-D2fS2ccL.js";
4
4
  import { AdapterRegistry, ConfigError, ConfigIncompatibleFormatError } from "../errors-CswS7Mzg.js";
5
5
  import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator } from "../version-manager-impl-CZ6KF1Ds.js";
@@ -2842,7 +2842,7 @@ async function runSupervisorAction(options, deps = {}) {
2842
2842
  const expDb = expDbWrapper.db;
2843
2843
  const { runRunAction: runPipeline } = await import(
2844
2844
  /* @vite-ignore */
2845
- "../run-BenC8JuM.js"
2845
+ "../run-DoxsPIlD.js"
2846
2846
  );
2847
2847
  const runStoryFn = async (opts) => {
2848
2848
  const exitCode = await runPipeline({
@@ -676,7 +676,8 @@ const PackManifestSchema = z.object({
676
676
  phases: z.array(PhaseDefinitionSchema),
677
677
  prompts: z.record(z.string(), z.string()),
678
678
  constraints: z.record(z.string(), z.string()),
679
- templates: z.record(z.string(), z.string())
679
+ templates: z.record(z.string(), z.string()),
680
+ conflictGroups: z.record(z.string(), z.string()).optional()
680
681
  });
681
682
  const ConstraintSeveritySchema = z.enum([
682
683
  "required",
@@ -5806,33 +5807,6 @@ function makeBatch(batchIndex, tasks) {
5806
5807
  //#endregion
5807
5808
  //#region src/modules/implementation-orchestrator/conflict-detector.ts
5808
5809
  /**
5809
- * Maps story key prefix patterns to module directory names.
5810
- *
5811
- * The heuristic: stories whose numeric prefix belongs to the same epic
5812
- * (same first digit) and whose known mapping points to the same module are
5813
- * considered conflicting. Unknown prefixes default to the story key itself
5814
- * (each unknown story is in its own group).
5815
- *
5816
- * Format: prefix (e.g. "10-") → module name
5817
- */
5818
- const STORY_PREFIX_TO_MODULE = {
5819
- "1-": "core",
5820
- "2-": "core",
5821
- "3-": "core",
5822
- "4-": "core",
5823
- "5-": "core",
5824
- "6-": "task-graph",
5825
- "7-": "worker-pool",
5826
- "8-": "monitor",
5827
- "9-": "bmad-context-engine",
5828
- "10-1": "compiled-workflows",
5829
- "10-2": "compiled-workflows",
5830
- "10-3": "compiled-workflows",
5831
- "10-4": "implementation-orchestrator",
5832
- "10-5": "cli",
5833
- "11-": "pipeline-phases"
5834
- };
5835
- /**
5836
5810
  * Determine the module prefix for a story key.
5837
5811
  *
5838
5812
  * Checks most-specific prefix first (e.g., "10-1" before "10-"), then falls
@@ -5840,10 +5814,11 @@ const STORY_PREFIX_TO_MODULE = {
5840
5814
  * itself for unknown stories so each gets its own group.
5841
5815
  *
5842
5816
  * @param storyKey - e.g. "10-1", "10-2-dev-story", "5-3-something"
5843
- * @param effectiveMap - The resolved prefix-to-module map (built-in + any extras)
5817
+ * @param effectiveMap - The resolved prefix-to-module map
5844
5818
  * @returns module name string used for conflict grouping
5845
5819
  */
5846
5820
  function resolveModulePrefix(storyKey, effectiveMap) {
5821
+ if (Object.keys(effectiveMap).length === 0) return storyKey;
5847
5822
  const sortedKeys = Object.keys(effectiveMap).sort((a, b) => b.length - a.length);
5848
5823
  for (const prefix of sortedKeys) if (storyKey.startsWith(prefix)) return effectiveMap[prefix];
5849
5824
  return storyKey;
@@ -5855,26 +5830,33 @@ function resolveModulePrefix(storyKey, effectiveMap) {
5855
5830
  * group and will be serialized. Stories with different prefixes can run in
5856
5831
  * parallel.
5857
5832
  *
5833
+ * When no `moduleMap` is configured, every story key is placed in its own
5834
+ * conflict group (maximum parallelism). This is the default for cross-project
5835
+ * runs where the story key prefixes are not known in advance.
5836
+ *
5858
5837
  * @param storyKeys - Array of story key strings
5859
- * @param config - Optional configuration; supply `moduleMap` to extend the
5860
- * built-in prefix-to-module mappings (additional entries are
5861
- * merged on top of the defaults, allowing overrides).
5838
+ * @param config - Optional configuration; supply `moduleMap` to define
5839
+ * prefix-to-module mappings. Without this config, each story
5840
+ * gets its own group (maximum parallelism).
5862
5841
  * @returns Array of conflict groups; each inner array is a list of story keys
5863
5842
  * that must be processed sequentially
5864
5843
  *
5865
5844
  * @example
5866
- * detectConflictGroups(['10-1', '10-2', '10-4', '10-5'])
5867
- * // => [['10-1', '10-2'], ['10-4'], ['10-5']]
5845
+ * // Without a moduleMap, all stories run in parallel
5846
+ * detectConflictGroups(['4-1', '4-2', '4-3'])
5847
+ * // => [['4-1'], ['4-2'], ['4-3']]
5848
+ *
5849
+ * @example
5850
+ * // With a moduleMap, matching stories are serialized
5851
+ * detectConflictGroups(['10-1', '10-2', '10-4'], { moduleMap: { '10-1': 'compiled-workflows', '10-2': 'compiled-workflows', '10-4': 'implementation-orchestrator' } })
5852
+ * // => [['10-1', '10-2'], ['10-4']]
5868
5853
  *
5869
5854
  * @example
5870
5855
  * detectConflictGroups(['12-1', '12-2'], { moduleMap: { '12-': 'my-module' } })
5871
5856
  * // => [['12-1', '12-2']]
5872
5857
  */
5873
5858
  function detectConflictGroups(storyKeys, config) {
5874
- const effectiveMap = {
5875
- ...STORY_PREFIX_TO_MODULE,
5876
- ...config?.moduleMap ?? {}
5877
- };
5859
+ const effectiveMap = { ...config?.moduleMap ?? {} };
5878
5860
  const moduleToStories = new Map();
5879
5861
  for (const key of storyKeys) {
5880
5862
  const module = resolveModulePrefix(key, effectiveMap);
@@ -7148,31 +7130,61 @@ function createImplementationOrchestrator(deps) {
7148
7130
  const fixModel = taskType === "major-rework" ? "claude-opus-4-6" : void 0;
7149
7131
  try {
7150
7132
  let fixPrompt;
7133
+ const isMajorRework = taskType === "major-rework";
7134
+ const templateName = isMajorRework ? "rework-story" : "fix-story";
7151
7135
  try {
7152
- const fixTemplate = await pack.getPrompt("fix-story");
7136
+ const fixTemplate = await pack.getPrompt(templateName);
7153
7137
  const storyContent = await readFile$1(storyFilePath ?? "", "utf-8");
7154
7138
  let reviewFeedback;
7155
- if (issueList.length === 0) reviewFeedback = [
7139
+ if (issueList.length === 0) reviewFeedback = isMajorRework ? [
7140
+ `Verdict: ${verdict}`,
7141
+ "Issues: The reviewer flagged fundamental issues but did not provide specifics.",
7142
+ "Instructions: Re-read the story file carefully, re-implement from scratch addressing all acceptance criteria."
7143
+ ].join("\n") : [
7156
7144
  `Verdict: ${verdict}`,
7157
7145
  "Issues: The reviewer flagged this as needing work but did not provide specific issues.",
7158
7146
  "Instructions: Re-read the story file carefully, compare each acceptance criterion against the current implementation, and fix any gaps you find.",
7159
7147
  "Focus on: unimplemented ACs, missing tests, incorrect behavior, and incomplete task checkboxes."
7160
7148
  ].join("\n");
7161
- else reviewFeedback = [
7162
- `Verdict: ${verdict}`,
7163
- `Issues (${issueList.length}):`,
7164
- ...issueList.map((issue, i) => {
7165
- const iss = issue;
7166
- return ` ${i + 1}. [${iss.severity ?? "unknown"}] ${iss.description ?? "no description"}${iss.file ? ` (${iss.file}${iss.line ? `:${iss.line}` : ""})` : ""}`;
7167
- })
7168
- ].join("\n");
7149
+ else {
7150
+ const issueHeader = isMajorRework ? "Issues from previous review that MUST be addressed" : "Issues";
7151
+ reviewFeedback = [
7152
+ `Verdict: ${verdict}`,
7153
+ `${issueHeader} (${issueList.length}):`,
7154
+ ...issueList.map((issue, i) => {
7155
+ const iss = issue;
7156
+ return ` ${i + 1}. [${iss.severity ?? "unknown"}] ${iss.description ?? "no description"}${iss.file ? ` (${iss.file}${iss.line ? `:${iss.line}` : ""})` : ""}`;
7157
+ })
7158
+ ].join("\n");
7159
+ }
7169
7160
  let archConstraints = "";
7170
7161
  try {
7171
7162
  const decisions = getDecisionsByPhase(db, "solutioning");
7172
7163
  const constraints = decisions.filter((d) => d.category === "architecture");
7173
7164
  archConstraints = constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
7174
7165
  } catch {}
7175
- const sections = [
7166
+ const sections = isMajorRework ? [
7167
+ {
7168
+ name: "story_content",
7169
+ content: storyContent,
7170
+ priority: "required"
7171
+ },
7172
+ {
7173
+ name: "review_findings",
7174
+ content: reviewFeedback,
7175
+ priority: "required"
7176
+ },
7177
+ {
7178
+ name: "arch_constraints",
7179
+ content: archConstraints,
7180
+ priority: "optional"
7181
+ },
7182
+ {
7183
+ name: "git_diff",
7184
+ content: "",
7185
+ priority: "optional"
7186
+ }
7187
+ ] : [
7176
7188
  {
7177
7189
  name: "story_content",
7178
7190
  content: storyContent,
@@ -7199,12 +7211,19 @@ function createImplementationOrchestrator(deps) {
7199
7211
  });
7200
7212
  }
7201
7213
  incrementDispatches(storyKey);
7202
- const handle = dispatcher.dispatch({
7214
+ const handle = isMajorRework ? dispatcher.dispatch({
7215
+ prompt: fixPrompt,
7216
+ agent: "claude-code",
7217
+ taskType,
7218
+ ...fixModel !== void 0 ? { model: fixModel } : {},
7219
+ outputSchema: DevStoryResultSchema,
7220
+ ...projectRoot !== void 0 ? { workingDirectory: projectRoot } : {}
7221
+ }) : dispatcher.dispatch({
7203
7222
  prompt: fixPrompt,
7204
7223
  agent: "claude-code",
7205
7224
  taskType,
7206
7225
  ...fixModel !== void 0 ? { model: fixModel } : {},
7207
- workingDirectory: projectRoot
7226
+ ...projectRoot !== void 0 ? { workingDirectory: projectRoot } : {}
7208
7227
  });
7209
7228
  const fixResult = await handle.result;
7210
7229
  eventBus.emit("orchestrator:story-phase-complete", {
@@ -7236,11 +7255,34 @@ function createImplementationOrchestrator(deps) {
7236
7255
  persistState();
7237
7256
  return;
7238
7257
  }
7239
- if (fixResult.status === "failed") logger$20.warn("Fix dispatch failed", {
7240
- storyKey,
7241
- taskType,
7242
- exitCode: fixResult.exitCode
7243
- });
7258
+ if (fixResult.status === "failed") {
7259
+ if (isMajorRework) {
7260
+ logger$20.warn("Major rework dispatch failed — escalating story", {
7261
+ storyKey,
7262
+ exitCode: fixResult.exitCode
7263
+ });
7264
+ endPhase(storyKey, "code-review");
7265
+ updateStory(storyKey, {
7266
+ phase: "ESCALATED",
7267
+ error: "major-rework-dispatch-failed",
7268
+ completedAt: new Date().toISOString()
7269
+ });
7270
+ writeStoryMetricsBestEffort(storyKey, "escalated", reviewCycles + 1);
7271
+ emitEscalation({
7272
+ storyKey,
7273
+ lastVerdict: verdict,
7274
+ reviewCycles: reviewCycles + 1,
7275
+ issues: issueList
7276
+ });
7277
+ persistState();
7278
+ return;
7279
+ }
7280
+ logger$20.warn("Fix dispatch failed", {
7281
+ storyKey,
7282
+ taskType,
7283
+ exitCode: fixResult.exitCode
7284
+ });
7285
+ }
7244
7286
  } catch (err) {
7245
7287
  logger$20.warn("Fix dispatch failed, continuing to next review", {
7246
7288
  storyKey,
@@ -7323,7 +7365,7 @@ function createImplementationOrchestrator(deps) {
7323
7365
  skippedCategories: seedResult.skippedCategories
7324
7366
  }, "Methodology context seeded from planning artifacts");
7325
7367
  }
7326
- const groups = detectConflictGroups(storyKeys);
7368
+ const groups = detectConflictGroups(storyKeys, { moduleMap: pack.manifest.conflictGroups });
7327
7369
  logger$20.info("Orchestrator starting", {
7328
7370
  storyCount: storyKeys.length,
7329
7371
  groupCount: groups.length,
@@ -12525,4 +12567,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
12525
12567
 
12526
12568
  //#endregion
12527
12569
  export { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
12528
- //# sourceMappingURL=run-BaAws8IQ.js.map
12570
+ //# sourceMappingURL=run-CT8B9gG9.js.map
@@ -1,4 +1,4 @@
1
- import { registerRunCommand, runRunAction } from "./run-BaAws8IQ.js";
1
+ import { registerRunCommand, runRunAction } from "./run-CT8B9gG9.js";
2
2
  import "./logger-D2fS2ccL.js";
3
3
  import "./event-bus-CAvDMst7.js";
4
4
  import "./decisions-Dq4cAA2L.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.2.23",
3
+ "version": "0.2.24",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -191,6 +191,7 @@ prompts:
191
191
  dev-story: prompts/dev-story.md
192
192
  code-review: prompts/code-review.md
193
193
  fix-story: prompts/fix-story.md
194
+ rework-story: prompts/rework-story.md
194
195
  # Multi-step phase decomposition prompts
195
196
  analysis-step-1-vision: prompts/analysis-step-1-vision.md
196
197
  analysis-step-2-scope: prompts/analysis-step-2-scope.md
@@ -228,3 +229,25 @@ constraints:
228
229
 
229
230
  templates:
230
231
  story: templates/story.md
232
+
233
+ # Conflict group mappings for the implementation orchestrator.
234
+ # Stories whose prefixes map to the same module name are serialized within
235
+ # a conflict group. This map covers substrate's own epic numbering so that
236
+ # self-pipeline runs preserve correct serialization (e.g., epics 1-5 share
237
+ # 'core', stories 10-1/10-2/10-3 share 'compiled-workflows').
238
+ conflictGroups:
239
+ '1-': core
240
+ '2-': core
241
+ '3-': core
242
+ '4-': core
243
+ '5-': core
244
+ '6-': task-graph
245
+ '7-': worker-pool
246
+ '8-': monitor
247
+ '9-': bmad-context-engine
248
+ '10-1': compiled-workflows
249
+ '10-2': compiled-workflows
250
+ '10-3': compiled-workflows
251
+ '10-4': implementation-orchestrator
252
+ '10-5': cli
253
+ '11-': pipeline-phases
@@ -0,0 +1,93 @@
1
+ # BMAD Compiled Rework-Story Agent
2
+
3
+ ## Context (pre-assembled by pipeline)
4
+
5
+ ### Story File Content
6
+ {{story_content}}
7
+
8
+ ### Issues from Previous Review that MUST be Addressed
9
+ {{review_findings}}
10
+
11
+ ### Architecture Constraints
12
+ {{arch_constraints}}
13
+
14
+ ### Previous Implementation Diff
15
+ {{git_diff}}
16
+
17
+ ---
18
+
19
+ ## Mission
20
+
21
+ This is a FULL re-implementation. The previous implementation had fundamental issues identified in a code review. You must address ALL review findings above while implementing the story from scratch.
22
+
23
+ Do NOT patch the existing implementation. Re-implement the story completely, using the review findings as guidance on what went wrong previously.
24
+
25
+ ## Instructions
26
+
27
+ 1. **Parse the review findings** to understand:
28
+ - Each issue's severity (blocker, major, minor)
29
+ - Each issue's description, file, and line number (if provided)
30
+ - The root causes that led to the previous implementation's failure
31
+
32
+ 2. **Re-read the story file** to understand:
33
+ - Acceptance Criteria (AC1, AC2, etc.)
34
+ - Tasks/Subtasks (ordered list with `[ ]` checkboxes)
35
+ - Dev Notes (file paths, import patterns, test requirements)
36
+
37
+ 3. **Re-implement each task in order** (Red-Green-Refactor):
38
+ - Write failing tests first
39
+ - Make tests pass with minimal code
40
+ - Refactor while keeping tests green
41
+ - Ensure each review finding is addressed
42
+
43
+ 4. **After each task**:
44
+ - Verify tests pass
45
+ - Run the full test suite to check for regressions
46
+ - Mark the task `[x]` in the story file
47
+ - Update the story File List with all new/modified files
48
+
49
+ 5. **After all tasks complete**:
50
+ - Run the full test suite one final time
51
+ - Verify ALL review findings have been addressed
52
+ - Update story Status to `review`
53
+
54
+ ## CRITICAL: Output Contract Emission
55
+
56
+ **You MUST emit the YAML output block (see Output Contract below) as the very last thing you produce.** The downstream pipeline depends on `files_modified` to generate scoped code-review diffs. If you exhaust your turns without emitting the YAML block, the pipeline cannot review your work properly.
57
+
58
+ - If you are running low on turns, **stop implementation and emit the YAML block immediately** with whatever progress you have made. A partial `files_modified` list is far more valuable than none at all.
59
+ - The YAML block must be the final output — no summary text, no explanation after it.
60
+
61
+ ## HALT Conditions (stop and report as failed)
62
+
63
+ - New dependency required beyond story spec
64
+ - 3 consecutive implementation failures with no progress
65
+ - Story requirements are ambiguous with no way to resolve
66
+ - Review findings contradict the story requirements
67
+
68
+ ## Output Contract
69
+
70
+ After completing all tasks (or hitting a HALT condition), emit ONLY this YAML block — no other text:
71
+
72
+ ```yaml
73
+ result: success
74
+ ac_met:
75
+ - AC1
76
+ - AC2
77
+ ac_failures: []
78
+ files_modified:
79
+ - <absolute path to modified file>
80
+ tests: pass
81
+ ```
82
+
83
+ If a HALT condition was hit:
84
+
85
+ ```yaml
86
+ result: failed
87
+ ac_met: []
88
+ ac_failures:
89
+ - <which AC could not be met>
90
+ files_modified: []
91
+ tests: fail
92
+ notes: <reason for failure>
93
+ ```