substrate-ai 0.20.12 → 0.20.14
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 +4 -4
- package/dist/{health-C401hYzi.js → health-C1vOBDbS.js} +1 -1
- package/dist/{health-0jqPFBEL.js → health-Cx8rztu5.js} +62 -4
- package/dist/index.d.ts +14 -0
- package/dist/{run-DEyd1p7U.js → run-Dj5UQs5e.js} +2 -2
- package/dist/{run-B6qbsy-F.js → run-i-5COg2l.js} +141 -22
- package/package.json +1 -1
- package/packs/bmad/prompts/create-story.md +13 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { FileStateStore, RunManifest, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDING_COUNTS, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts } from "../health-
|
|
2
|
+
import { FileStateStore, RunManifest, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDING_COUNTS, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts } from "../health-Cx8rztu5.js";
|
|
3
3
|
import { createLogger } from "../logger-KeHncl-f.js";
|
|
4
4
|
import { createEventBus } from "../helpers-CElYrONe.js";
|
|
5
5
|
import { AdapterRegistry, BudgetConfigSchema, CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, ConfigError, CostTrackerConfigSchema, DEFAULT_CONFIG, DoltClient, DoltNotInstalled, GlobalSettingsSchema, IngestionServer, MonitorDatabaseImpl, OPERATIONAL_FINDING, PartialGlobalSettingsSchema, PartialProviderConfigSchema, ProvidersSchema, RoutingRecommender, STORY_METRICS, TelemetryConfigSchema, addTokenUsage, aggregateTokenUsageForRun, checkDoltInstalled, compareRunMetrics, createAmendmentRun, createConfigSystem, createDecision, createDoltClient, createPipelineRun, getActiveDecisions, getAllCostEntriesFiltered, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initializeDolt, listRunMetrics, loadParentRunDecisions, supersedeDecision, tagRunAsBaseline, updatePipelineRun } from "../dist-CqtWS9wF.js";
|
|
6
6
|
import "../adapter-registry-DXLMTmfD.js";
|
|
7
|
-
import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-
|
|
7
|
+
import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-i-5COg2l.js";
|
|
8
8
|
import "../errors-1uLGqnvr.js";
|
|
9
9
|
import "../routing-CcBOCuC9.js";
|
|
10
10
|
import "../decisions-C0pz9Clx.js";
|
|
@@ -3667,7 +3667,7 @@ async function runStatusAction(options) {
|
|
|
3667
3667
|
logger$12.debug({ err }, "Work graph query failed, continuing without work graph data");
|
|
3668
3668
|
}
|
|
3669
3669
|
if (run === void 0) {
|
|
3670
|
-
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-
|
|
3670
|
+
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-C1vOBDbS.js");
|
|
3671
3671
|
const substrateDirPath = join(projectRoot, ".substrate");
|
|
3672
3672
|
const processInfo = inspectProcessTree$1({
|
|
3673
3673
|
projectRoot,
|
|
@@ -5198,7 +5198,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
5198
5198
|
await initSchema(expAdapter);
|
|
5199
5199
|
const { runRunAction: runPipeline } = await import(
|
|
5200
5200
|
/* @vite-ignore */
|
|
5201
|
-
"../run-
|
|
5201
|
+
"../run-Dj5UQs5e.js"
|
|
5202
5202
|
);
|
|
5203
5203
|
const runStoryFn = async (opts) => {
|
|
5204
5204
|
const exitCode = await runPipeline({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-
|
|
1
|
+
import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-Cx8rztu5.js";
|
|
2
2
|
import "./logger-KeHncl-f.js";
|
|
3
3
|
import "./dist-CqtWS9wF.js";
|
|
4
4
|
import "./decisions-C0pz9Clx.js";
|
|
@@ -3549,9 +3549,10 @@ function parseRuntimeProbes(storyContent) {
|
|
|
3549
3549
|
error: `YAML parse error: ${detail}`
|
|
3550
3550
|
};
|
|
3551
3551
|
}
|
|
3552
|
+
if (!Array.isArray(parsed) && typeof parsed === "object" && parsed !== null && Array.isArray(parsed.probes)) parsed = parsed.probes;
|
|
3552
3553
|
if (!Array.isArray(parsed)) return {
|
|
3553
3554
|
kind: "invalid",
|
|
3554
|
-
error: `probe block root must be a YAML list; got ${typeof parsed}`
|
|
3555
|
+
error: `probe block root must be a YAML list or a \`probes:\` mapping; got ${typeof parsed}`
|
|
3555
3556
|
};
|
|
3556
3557
|
const validation = RuntimeProbeListSchema.safeParse(parsed);
|
|
3557
3558
|
if (!validation.success) {
|
|
@@ -3848,7 +3849,7 @@ var SourceAcFidelityCheck = class {
|
|
|
3848
3849
|
const truncated = clause.text.length > 120 ? clause.text.slice(0, 120) : clause.text;
|
|
3849
3850
|
findings.push({
|
|
3850
3851
|
category: "source-ac-drift",
|
|
3851
|
-
severity: "
|
|
3852
|
+
severity: "warn",
|
|
3852
3853
|
message: `runtime-probes-section: "${truncated}" present in epics source but absent in story artifact`
|
|
3853
3854
|
});
|
|
3854
3855
|
}
|
|
@@ -3856,7 +3857,7 @@ var SourceAcFidelityCheck = class {
|
|
|
3856
3857
|
const truncated = clause.text.length > 120 ? clause.text.slice(0, 120) : clause.text;
|
|
3857
3858
|
findings.push({
|
|
3858
3859
|
category: "source-ac-drift",
|
|
3859
|
-
severity: "
|
|
3860
|
+
severity: "warn",
|
|
3860
3861
|
message: `${clause.type}: "${truncated}" present in epics source but absent in story artifact`
|
|
3861
3862
|
});
|
|
3862
3863
|
}
|
|
@@ -4210,6 +4211,12 @@ const RunManifestSchema = z.object({
|
|
|
4210
4211
|
run_id: z.string(),
|
|
4211
4212
|
cli_flags: CliFlagsSchema.transform((v) => v),
|
|
4212
4213
|
story_scope: z.array(z.string()),
|
|
4214
|
+
run_status: z.enum([
|
|
4215
|
+
"running",
|
|
4216
|
+
"completed",
|
|
4217
|
+
"failed",
|
|
4218
|
+
"stopped"
|
|
4219
|
+
]).optional(),
|
|
4213
4220
|
supervisor_pid: z.number().nullable(),
|
|
4214
4221
|
supervisor_session_id: z.string().nullable(),
|
|
4215
4222
|
per_story_state: z.record(z.string(), PerStoryStateSchema),
|
|
@@ -4219,6 +4226,8 @@ const RunManifestSchema = z.object({
|
|
|
4219
4226
|
run_total: 0
|
|
4220
4227
|
}),
|
|
4221
4228
|
pending_proposals: z.array(ProposalSchema),
|
|
4229
|
+
stopped_reason: z.string().optional(),
|
|
4230
|
+
stopped_at: z.string().optional(),
|
|
4222
4231
|
generation: z.number().int().nonnegative(),
|
|
4223
4232
|
created_at: z.string(),
|
|
4224
4233
|
updated_at: z.string()
|
|
@@ -4560,6 +4569,55 @@ var RunManifest = class RunManifest {
|
|
|
4560
4569
|
/**
|
|
4561
4570
|
* Raw implementation — must only be called from within `_enqueue`.
|
|
4562
4571
|
*/
|
|
4572
|
+
async _patchRunStatusImpl(updates) {
|
|
4573
|
+
let existingData;
|
|
4574
|
+
try {
|
|
4575
|
+
const read = await RunManifest.read(this.runId, this.baseDir, this.doltAdapter);
|
|
4576
|
+
const { generation: _gen, updated_at: _ts,...rest } = read;
|
|
4577
|
+
existingData = rest;
|
|
4578
|
+
} catch {
|
|
4579
|
+
const now = new Date().toISOString();
|
|
4580
|
+
existingData = {
|
|
4581
|
+
run_id: this.runId,
|
|
4582
|
+
cli_flags: {},
|
|
4583
|
+
story_scope: [],
|
|
4584
|
+
supervisor_pid: null,
|
|
4585
|
+
supervisor_session_id: null,
|
|
4586
|
+
per_story_state: {},
|
|
4587
|
+
recovery_history: [],
|
|
4588
|
+
cost_accumulation: {
|
|
4589
|
+
per_story: {},
|
|
4590
|
+
run_total: 0
|
|
4591
|
+
},
|
|
4592
|
+
pending_proposals: [],
|
|
4593
|
+
created_at: now
|
|
4594
|
+
};
|
|
4595
|
+
}
|
|
4596
|
+
const merged = { ...existingData };
|
|
4597
|
+
if (updates.run_status !== void 0) merged.run_status = updates.run_status;
|
|
4598
|
+
if (updates.stopped_reason !== void 0) merged.stopped_reason = updates.stopped_reason;
|
|
4599
|
+
if (updates.stopped_at !== void 0) merged.stopped_at = updates.stopped_at;
|
|
4600
|
+
await this._writeImpl(merged);
|
|
4601
|
+
}
|
|
4602
|
+
/**
|
|
4603
|
+
* Atomically update the run-level status fields in the manifest.
|
|
4604
|
+
*
|
|
4605
|
+
* Reads the current manifest (or creates a minimal default if absent),
|
|
4606
|
+
* merges the provided status updates at the top level, and writes the
|
|
4607
|
+
* result atomically via a single `write()` call.
|
|
4608
|
+
*
|
|
4609
|
+
* Enqueues the operation via `_enqueue` so concurrent calls are serialized
|
|
4610
|
+
* (preserves the single-writer guarantee from Epic 57-1).
|
|
4611
|
+
* Non-fatal: callers MUST wrap in `.catch((err) => logger.warn(...))`.
|
|
4612
|
+
*
|
|
4613
|
+
* @param updates - Top-level status fields to merge (run_status, stopped_reason, stopped_at)
|
|
4614
|
+
*/
|
|
4615
|
+
async patchRunStatus(updates) {
|
|
4616
|
+
return this._enqueue(() => this._patchRunStatusImpl(updates));
|
|
4617
|
+
}
|
|
4618
|
+
/**
|
|
4619
|
+
* Raw implementation — must only be called from within `_enqueue`.
|
|
4620
|
+
*/
|
|
4563
4621
|
async _appendRecoveryEntryImpl(entry) {
|
|
4564
4622
|
let existingData;
|
|
4565
4623
|
try {
|
|
@@ -5477,4 +5535,4 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
5477
5535
|
|
|
5478
5536
|
//#endregion
|
|
5479
5537
|
export { BMAD_BASELINE_TOKENS_FULL, DEFAULT_STALL_THRESHOLD_SECONDS, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN$1 as STORY_KEY_PATTERN, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDING_COUNTS, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter$1 as createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, createStateStore, detectCycles, extractTargetFilesFromStoryContent, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, isOrchestratorProcessLine, parseDbTimestampAsUtc, registerHealthCommand, renderFindings, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveGraphPath, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, runHealthAction, validateStoryKey };
|
|
5480
|
-
//# sourceMappingURL=health-
|
|
5538
|
+
//# sourceMappingURL=health-Cx8rztu5.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -2286,6 +2286,20 @@ interface OrchestratorEvents {
|
|
|
2286
2286
|
/** Retry attempt number (always 2 — first retry after initial timeout) */
|
|
2287
2287
|
attempt: number;
|
|
2288
2288
|
};
|
|
2289
|
+
/**
|
|
2290
|
+
* Emitted when an existing story artifact's stored source-AC hash differs
|
|
2291
|
+
* from the current source epic's AC hash, or when the artifact carries no
|
|
2292
|
+
* hash at all (legacy artifact). Causes the orchestrator to re-run
|
|
2293
|
+
* create-story instead of reusing the stale artifact.
|
|
2294
|
+
*/
|
|
2295
|
+
'story:ac-source-drift': {
|
|
2296
|
+
/** Story key whose artifact is stale */
|
|
2297
|
+
storyKey: string;
|
|
2298
|
+
/** Hash stored in the artifact's HTML comment (null if absent — legacy artifact) */
|
|
2299
|
+
storedHash: string | null;
|
|
2300
|
+
/** Hash computed from the current source epic's AC section */
|
|
2301
|
+
currentHash: string;
|
|
2302
|
+
};
|
|
2289
2303
|
}
|
|
2290
2304
|
|
|
2291
2305
|
//#endregion
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "./health-
|
|
1
|
+
import "./health-Cx8rztu5.js";
|
|
2
2
|
import "./logger-KeHncl-f.js";
|
|
3
3
|
import "./helpers-CElYrONe.js";
|
|
4
4
|
import "./dist-CqtWS9wF.js";
|
|
5
|
-
import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-
|
|
5
|
+
import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-i-5COg2l.js";
|
|
6
6
|
import "./routing-CcBOCuC9.js";
|
|
7
7
|
import "./decisions-C0pz9Clx.js";
|
|
8
8
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-
|
|
1
|
+
import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-Cx8rztu5.js";
|
|
2
2
|
import { createLogger } from "./logger-KeHncl-f.js";
|
|
3
3
|
import { TypedEventBusImpl, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning, sleep } from "./helpers-CElYrONe.js";
|
|
4
4
|
import { ADVISORY_NOTES, Categorizer, ConsumerAnalyzer, DEFAULT_GLOBAL_SETTINGS, DispatcherImpl, DoltClient, ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, EfficiencyScorer, IngestionServer, LogTurnAnalyzer, OPERATIONAL_FINDING, Recommender, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, STORY_METRICS, STORY_OUTCOME, SubstrateConfigSchema, TEST_EXPANSION_FINDING, TEST_PLAN, TelemetryNormalizer, TelemetryPipeline, TurnAnalyzer, addTokenUsage, aggregateTokenUsageForRun, aggregateTokenUsageForStory, callLLM, createConfigSystem, createDatabaseAdapter$1, createDecision, createPipelineRun, createRequirement, detectInterfaceChanges, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunMetrics, getRunningPipelineRuns, getStoryMetricsForRun, getTokenUsageSummary, initSchema, listRequirements, loadModelRoutingConfig, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision, writeRunMetrics, writeStoryMetrics } from "./dist-CqtWS9wF.js";
|
|
@@ -5676,6 +5676,21 @@ function getTokenCeiling(workflowType, tokenCeilings) {
|
|
|
5676
5676
|
//#region src/modules/compiled-workflows/create-story.ts
|
|
5677
5677
|
const logger$18 = createLogger("compiled-workflows:create-story");
|
|
5678
5678
|
/**
|
|
5679
|
+
* Compute a hex SHA-256 of the normalized source AC section text.
|
|
5680
|
+
*
|
|
5681
|
+
* Normalization (minimal — avoids spurious regen from editor whitespace noise):
|
|
5682
|
+
* 1. Split on `\n`
|
|
5683
|
+
* 2. Strip trailing whitespace from each line (`.trimEnd()`)
|
|
5684
|
+
* 3. Rejoin with `\n`
|
|
5685
|
+
* 4. Trim the whole result (`.trim()`)
|
|
5686
|
+
*
|
|
5687
|
+
* Pure function: no I/O, no side effects. Safe to call from tests with zero setup.
|
|
5688
|
+
*/
|
|
5689
|
+
function hashSourceAcSection(section) {
|
|
5690
|
+
const normalized = section.split("\n").map((line) => line.trimEnd()).join("\n").trim();
|
|
5691
|
+
return createHash("sha256").update(normalized, "utf8").digest("hex");
|
|
5692
|
+
}
|
|
5693
|
+
/**
|
|
5679
5694
|
* Execute the compiled create-story workflow.
|
|
5680
5695
|
*
|
|
5681
5696
|
* Steps:
|
|
@@ -5693,7 +5708,7 @@ const logger$18 = createLogger("compiled-workflows:create-story");
|
|
|
5693
5708
|
* @returns Promise resolving to CreateStoryResult
|
|
5694
5709
|
*/
|
|
5695
5710
|
async function runCreateStory(deps, params) {
|
|
5696
|
-
const { epicId, storyKey, pipelineRunId } = params;
|
|
5711
|
+
const { epicId, storyKey, pipelineRunId, source_ac_hash } = params;
|
|
5697
5712
|
logger$18.debug({
|
|
5698
5713
|
epicId,
|
|
5699
5714
|
storyKey,
|
|
@@ -5764,7 +5779,12 @@ async function runCreateStory(deps, params) {
|
|
|
5764
5779
|
name: "story_template",
|
|
5765
5780
|
content: storyTemplateContent,
|
|
5766
5781
|
priority: "important"
|
|
5767
|
-
}
|
|
5782
|
+
},
|
|
5783
|
+
...source_ac_hash !== void 0 ? [{
|
|
5784
|
+
name: "source_ac_hash",
|
|
5785
|
+
content: source_ac_hash,
|
|
5786
|
+
priority: "required"
|
|
5787
|
+
}] : []
|
|
5768
5788
|
], TOKEN_CEILING);
|
|
5769
5789
|
logger$18.debug({
|
|
5770
5790
|
tokenCount,
|
|
@@ -11366,6 +11386,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
11366
11386
|
const _costChecker = new CostGovernanceChecker();
|
|
11367
11387
|
let _costWarningEmitted = false;
|
|
11368
11388
|
let _budgetExhausted = false;
|
|
11389
|
+
let _shutdownRequested = false;
|
|
11390
|
+
let _inFlightCount = 0;
|
|
11391
|
+
let _drainResolve = null;
|
|
11392
|
+
const _drainPromise = new Promise((resolve$6) => {
|
|
11393
|
+
_drainResolve = resolve$6;
|
|
11394
|
+
});
|
|
11369
11395
|
let _otlpEndpoint;
|
|
11370
11396
|
const verificationStore = new VerificationStore();
|
|
11371
11397
|
const verificationPipeline = createDefaultVerificationPipeline(toSdlcEventBus(eventBus));
|
|
@@ -12004,6 +12030,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12004
12030
|
startedAt: new Date().toISOString()
|
|
12005
12031
|
});
|
|
12006
12032
|
let storyFilePath;
|
|
12033
|
+
let sourceAcHash;
|
|
12007
12034
|
const artifactsDir = projectRoot ? join$1(projectRoot, "_bmad-output", "implementation-artifacts") : void 0;
|
|
12008
12035
|
if (artifactsDir && existsSync(artifactsDir)) try {
|
|
12009
12036
|
const files = readdirSync(artifactsDir);
|
|
@@ -12017,22 +12044,52 @@ function createImplementationOrchestrator(deps) {
|
|
|
12017
12044
|
reason: validation.reason
|
|
12018
12045
|
}, `Existing story file for ${storyKey} is invalid (${validation.reason}) — re-creating`);
|
|
12019
12046
|
else {
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
|
|
12024
|
-
|
|
12025
|
-
|
|
12026
|
-
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12047
|
+
let isDrift = false;
|
|
12048
|
+
try {
|
|
12049
|
+
const epicsPath = projectRoot ? findEpicsFile(projectRoot) : void 0;
|
|
12050
|
+
if (epicsPath !== void 0) {
|
|
12051
|
+
const epicContent = readFileSync(epicsPath, "utf-8");
|
|
12052
|
+
const sourceSection = extractStorySection(epicContent, storyKey);
|
|
12053
|
+
if (sourceSection != null) {
|
|
12054
|
+
const currentHash = hashSourceAcSection(sourceSection);
|
|
12055
|
+
sourceAcHash = currentHash;
|
|
12056
|
+
const artifactContent = await readFile$1(candidatePath, "utf-8");
|
|
12057
|
+
const hashMatch = /<!--\s*source-ac-hash:\s*([0-9a-f]{64})\s*-->/.exec(artifactContent);
|
|
12058
|
+
const storedHash = hashMatch?.[1] ?? null;
|
|
12059
|
+
if (storedHash !== currentHash) {
|
|
12060
|
+
isDrift = true;
|
|
12061
|
+
eventBus.emit("story:ac-source-drift", {
|
|
12062
|
+
storyKey,
|
|
12063
|
+
storedHash,
|
|
12064
|
+
currentHash
|
|
12065
|
+
});
|
|
12066
|
+
logger$24.info({
|
|
12067
|
+
storyKey,
|
|
12068
|
+
storedHash,
|
|
12069
|
+
currentHash
|
|
12070
|
+
}, `[orchestrator] story ${storyKey}: source AC hash mismatch, regenerating story artifact`);
|
|
12071
|
+
}
|
|
12072
|
+
}
|
|
12033
12073
|
}
|
|
12034
|
-
}
|
|
12035
|
-
|
|
12074
|
+
} catch {}
|
|
12075
|
+
if (!isDrift) {
|
|
12076
|
+
storyFilePath = candidatePath;
|
|
12077
|
+
logger$24.info({
|
|
12078
|
+
storyKey,
|
|
12079
|
+
storyFilePath
|
|
12080
|
+
}, "Found existing story file — skipping create-story");
|
|
12081
|
+
endPhase(storyKey, "create-story");
|
|
12082
|
+
eventBus.emit("orchestrator:story-phase-complete", {
|
|
12083
|
+
storyKey,
|
|
12084
|
+
phase: "IN_STORY_CREATION",
|
|
12085
|
+
result: {
|
|
12086
|
+
result: "success",
|
|
12087
|
+
story_file: storyFilePath,
|
|
12088
|
+
story_key: storyKey
|
|
12089
|
+
}
|
|
12090
|
+
});
|
|
12091
|
+
await persistState();
|
|
12092
|
+
}
|
|
12036
12093
|
}
|
|
12037
12094
|
}
|
|
12038
12095
|
} catch {}
|
|
@@ -12069,7 +12126,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
12069
12126
|
}, {
|
|
12070
12127
|
epicId: storyKey.split("-")[0] ?? storyKey,
|
|
12071
12128
|
storyKey,
|
|
12072
|
-
pipelineRunId: config.pipelineRunId
|
|
12129
|
+
pipelineRunId: config.pipelineRunId,
|
|
12130
|
+
source_ac_hash: sourceAcHash
|
|
12073
12131
|
});
|
|
12074
12132
|
endPhase(storyKey, "create-story");
|
|
12075
12133
|
eventBus.emit("orchestrator:story-phase-complete", {
|
|
@@ -13898,6 +13956,10 @@ function createImplementationOrchestrator(deps) {
|
|
|
13898
13956
|
async function processConflictGroup(group) {
|
|
13899
13957
|
const completedStoryKeys = [];
|
|
13900
13958
|
for (const storyKey of group) {
|
|
13959
|
+
if (_shutdownRequested) {
|
|
13960
|
+
logger$24.info({ storyKey }, "shutdown requested — skipping dispatch");
|
|
13961
|
+
return;
|
|
13962
|
+
}
|
|
13901
13963
|
if (runManifest !== null && runManifest !== void 0) try {
|
|
13902
13964
|
const manifestData = await runManifest.read();
|
|
13903
13965
|
const ceiling = manifestData.cli_flags.cost_ceiling;
|
|
@@ -13937,7 +13999,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
13937
13999
|
storyKey
|
|
13938
14000
|
}, "Failed to fetch optimization directives — proceeding without");
|
|
13939
14001
|
}
|
|
13940
|
-
|
|
14002
|
+
_inFlightCount++;
|
|
14003
|
+
try {
|
|
14004
|
+
await processStory(storyKey, { optimizationDirectives });
|
|
14005
|
+
} finally {
|
|
14006
|
+
_inFlightCount--;
|
|
14007
|
+
if (_inFlightCount === 0 && _shutdownRequested) _drainResolve?.();
|
|
14008
|
+
}
|
|
13941
14009
|
completedStoryKeys.push(storyKey);
|
|
13942
14010
|
globalThis.gc?.();
|
|
13943
14011
|
const gcPauseMs = config.gcPauseMs ?? 2e3;
|
|
@@ -13956,11 +14024,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
13956
14024
|
const running = new Set();
|
|
13957
14025
|
function enqueue() {
|
|
13958
14026
|
if (_budgetExhausted) return;
|
|
14027
|
+
if (_shutdownRequested) return;
|
|
13959
14028
|
const group = queue.shift();
|
|
13960
14029
|
if (group === void 0) return;
|
|
13961
14030
|
const p = processConflictGroup(group).finally(() => {
|
|
13962
14031
|
running.delete(p);
|
|
13963
|
-
while (running.size < maxConcurrency && queue.length > 0) enqueue();
|
|
14032
|
+
while (running.size < maxConcurrency && queue.length > 0 && !_budgetExhausted && !_shutdownRequested) enqueue();
|
|
13964
14033
|
});
|
|
13965
14034
|
running.add(p);
|
|
13966
14035
|
if (running.size > _maxConcurrentActual) _maxConcurrentActual = running.size;
|
|
@@ -13969,6 +14038,46 @@ function createImplementationOrchestrator(deps) {
|
|
|
13969
14038
|
for (let i = 0; i < initial; i++) enqueue();
|
|
13970
14039
|
while (running.size > 0) await Promise.race(running);
|
|
13971
14040
|
}
|
|
14041
|
+
/**
|
|
14042
|
+
* Graceful shutdown handler for SIGTERM and SIGINT (Story 58-7, AC2).
|
|
14043
|
+
*
|
|
14044
|
+
* Idempotent: subsequent calls during the same run are no-ops.
|
|
14045
|
+
* Sets `_shutdownRequested` to stop new dispatches, waits for in-flight
|
|
14046
|
+
* dispatches to drain (up to `shutdownGracePeriodMs`), persists stopped state
|
|
14047
|
+
* to the run manifest and Dolt (best-effort), then calls process.exit.
|
|
14048
|
+
*/
|
|
14049
|
+
async function shutdownGracefully(reason, signal) {
|
|
14050
|
+
if (_shutdownRequested) return;
|
|
14051
|
+
_shutdownRequested = true;
|
|
14052
|
+
logger$24.info({
|
|
14053
|
+
reason,
|
|
14054
|
+
signal
|
|
14055
|
+
}, "Graceful shutdown initiated — stopping new dispatches");
|
|
14056
|
+
const gracePeriod = config.shutdownGracePeriodMs ?? 5e3;
|
|
14057
|
+
if (_inFlightCount > 0) await Promise.race([_drainPromise, new Promise((r) => setTimeout(r, gracePeriod))]);
|
|
14058
|
+
if (runManifest !== null) await runManifest.patchRunStatus({
|
|
14059
|
+
run_status: "stopped",
|
|
14060
|
+
stopped_reason: reason,
|
|
14061
|
+
stopped_at: new Date().toISOString()
|
|
14062
|
+
}).catch((err) => logger$24.warn({ err }, "patchRunStatus failed during shutdown (best-effort)"));
|
|
14063
|
+
if (config.pipelineRunId !== void 0) await updatePipelineRun(db, config.pipelineRunId, { status: "stopped" }).catch((err) => logger$24.warn({ err }, "updatePipelineRun(stopped) failed during shutdown (best-effort)"));
|
|
14064
|
+
const activePhases = [
|
|
14065
|
+
"PENDING",
|
|
14066
|
+
"IN_STORY_CREATION",
|
|
14067
|
+
"IN_TEST_PLANNING",
|
|
14068
|
+
"IN_DEV",
|
|
14069
|
+
"IN_REVIEW",
|
|
14070
|
+
"NEEDS_FIXES",
|
|
14071
|
+
"CHECKPOINT"
|
|
14072
|
+
];
|
|
14073
|
+
const cancellations = [];
|
|
14074
|
+
for (const [storyKey, state] of _stories.entries()) if (activePhases.includes(state.phase)) cancellations.push(wgRepo.updateStoryStatus(storyKey, "cancelled").catch((err) => logger$24.warn({
|
|
14075
|
+
err,
|
|
14076
|
+
storyKey
|
|
14077
|
+
}, "wg_stories → cancelled failed during shutdown (best-effort)")));
|
|
14078
|
+
if (cancellations.length > 0) await Promise.allSettled(cancellations);
|
|
14079
|
+
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
14080
|
+
}
|
|
13972
14081
|
async function run(storyKeys) {
|
|
13973
14082
|
if (_state === "RUNNING" || _state === "PAUSED") {
|
|
13974
14083
|
logger$24.warn({ state: _state }, "run() called while orchestrator is already running or paused — ignoring");
|
|
@@ -14017,6 +14126,14 @@ function createImplementationOrchestrator(deps) {
|
|
|
14017
14126
|
logger$24.debug({ err }, "Auto-ingest from epics document skipped — work graph may be unavailable");
|
|
14018
14127
|
}
|
|
14019
14128
|
}
|
|
14129
|
+
const sigtermHandler = () => {
|
|
14130
|
+
shutdownGracefully("killed_by_user", "SIGTERM");
|
|
14131
|
+
};
|
|
14132
|
+
const sigintHandler = () => {
|
|
14133
|
+
shutdownGracefully("killed_by_user", "SIGINT");
|
|
14134
|
+
};
|
|
14135
|
+
process.on("SIGTERM", sigtermHandler);
|
|
14136
|
+
process.on("SIGINT", sigintHandler);
|
|
14020
14137
|
try {
|
|
14021
14138
|
if (stateStore !== void 0) {
|
|
14022
14139
|
const stateStoreInitStart = Date.now();
|
|
@@ -14264,6 +14381,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
14264
14381
|
await persistState();
|
|
14265
14382
|
return getStatus();
|
|
14266
14383
|
} finally {
|
|
14384
|
+
process.off("SIGTERM", sigtermHandler);
|
|
14385
|
+
process.off("SIGINT", sigintHandler);
|
|
14267
14386
|
if (stateStore !== void 0) await stateStore.close().catch((err) => logger$24.warn({ err }, "StateStore.close() failed (best-effort)"));
|
|
14268
14387
|
if (ingestionServer !== void 0) await ingestionServer.stop().catch((err) => logger$24.warn({ err }, "IngestionServer.stop() failed (best-effort)"));
|
|
14269
14388
|
}
|
|
@@ -43858,4 +43977,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
43858
43977
|
|
|
43859
43978
|
//#endregion
|
|
43860
43979
|
export { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, normalizeGraphSummaryToStatus, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveMaxReviewCycles, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict, wireNdjsonEmitter };
|
|
43861
|
-
//# sourceMappingURL=run-
|
|
43980
|
+
//# sourceMappingURL=run-i-5COg2l.js.map
|
package/package.json
CHANGED
|
@@ -32,12 +32,24 @@ a different story from the epic scope. The story key, title, and core scope are
|
|
|
32
32
|
## Instructions
|
|
33
33
|
|
|
34
34
|
1. **Use the Story Definition as your primary input** — it specifies exactly what this story builds. The epic scope provides surrounding context only.
|
|
35
|
-
2. **
|
|
35
|
+
2. **Source AC is read-only input; default rendering is a verbatim copy.** Under the rendered `## Acceptance Criteria` heading, begin with an exact copy of the source AC text (all sub-sections, heading hierarchy, file lists, storage choices, probes). Any restructuring or BDD rephrasing goes in a separate `### Create-story reformulation (optional)` subsection BELOW the verbatim copy — never in place of it.
|
|
36
|
+
|
|
37
|
+
Categories that MUST appear verbatim (substring-identical). Never soften, abstract, or paraphrase:
|
|
38
|
+
|
|
39
|
+
- **Directive keywords**: `MUST`, `MUST NOT`, `SHALL`, `SHALL NOT`, `SHOULD NOT` lines with surrounding context.
|
|
40
|
+
- **Named filenames**: backtick-wrapped paths appear as-is. Do not rename (`adjacency-store.ts` → `wikilink-queries.ts`), drop, pluralize, or recase.
|
|
41
|
+
- **Named directories**: same rule. `packages/memory/src/graph/` stays, not `packages/memory/src/wikilink/`.
|
|
42
|
+
- **Explicit technology / storage / data-format choices**: `plain JSON file`, `LanceDB table`, `SQLite database`, `systemd unit`, `Docker Compose`, `Podman Quadlet` etc. appear exactly. A source "JSON adjacency list" MUST NOT become "LanceDB table" — flipping the storage backend is a correctness change, not a clarification.
|
|
43
|
+
- **Named probe identifiers**: probe filenames (`real-jarvis-status-probe.mjs`) and declared probe `name:` fields are part of the contract. Renaming breaks operator dashboards and downstream references.
|
|
44
|
+
- **Full `## Runtime Probes` sections**: transfer entire section verbatim (heading, prose, YAML fence) per Runtime Verification Guidance below.
|
|
45
|
+
|
|
46
|
+
Reshaping "MUST remove X" → "consider deprecating X" silently strips the requirement. Renaming `adjacency-store.ts` silently drops a MUST-exist file. Flipping `plain JSON file` → `LanceDB table` silently swaps the storage architecture. Each of these has shipped code that violated its own spec in recorded substrate sessions — the verbatim-first rule exists because the agent's judgment about "what the author really meant" has been systematically wrong on these dimensions.
|
|
36
47
|
3. **Apply architecture constraints** — every constraint listed above is mandatory (file paths, import style, test framework, etc.)
|
|
37
48
|
4. **Use previous dev notes** as guardrails — don't repeat mistakes, build on patterns that worked
|
|
38
49
|
5. **Fill out the story template** with:
|
|
39
50
|
- A clear user story (As a / I want / So that)
|
|
40
51
|
- Acceptance criteria: preserve the hard-clause text from the Story Definition verbatim. BDD Given/When/Then phrasing is **optional** — permitted for behavior-oriented criteria where it adds clarity, not mandatory. Never let BDD reshape a MUST / MUST NOT / SHALL clause; copy those clauses literally and, if BDD adds clarity, append the Given/When/Then alongside the original clause rather than replacing it. Aim for the same number of ACs as the source — do not condense clauses into fewer items to hit a target count.
|
|
52
|
+
- Immediately after the `## Acceptance Criteria` heading in the rendered story file, emit the line `<!-- source-ac-hash: {{source_ac_hash}} -->` on its own line. When the hash value is empty or blank (the `{{source_ac_hash}}` placeholder resolved to nothing), omit the comment entirely — do not write `<!-- source-ac-hash: -->` or any comment with an empty or missing hash value.
|
|
41
53
|
- Concrete tasks broken into 2–4 hour subtasks, each tied to specific ACs
|
|
42
54
|
- Dev Notes with file paths, import patterns, testing requirements
|
|
43
55
|
6. **Apply the scope cap** — see Scope Cap Guidance below
|