substrate-ai 0.20.47 → 0.20.49

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,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { FileStateStore, RunManifest, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, parseRuntimeProbes, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorMetrics } from "../health-BV-rzjf7.js";
2
+ import { FileStateStore, RunManifest, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, parseRuntimeProbes, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorMetrics } from "../health-sQ1X_5_6.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, InMemoryDatabaseAdapter, 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-VcMmfo2w.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, runProbeAuthor, runSolutioningPhase, validateStopAfterFromConflict } from "../run-BYMVrlGZ.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, runProbeAuthor, runSolutioningPhase, validateStopAfterFromConflict } from "../run-CFRXRuRO.js";
8
8
  import "../errors-CogpxBUg.js";
9
9
  import "../routing-CcBOCuC9.js";
10
10
  import "../decisions-C0pz9Clx.js";
@@ -3667,7 +3667,7 @@ async function runStatusAction(options) {
3667
3667
  logger$13.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-DQrsmYTt.js");
3670
+ const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-BQ5dj53s.js");
3671
3671
  const substrateDirPath = join(projectRoot, ".substrate");
3672
3672
  const processInfo = inspectProcessTree$1({
3673
3673
  projectRoot,
@@ -5204,7 +5204,7 @@ async function runSupervisorAction(options, deps = {}) {
5204
5204
  await initSchema(expAdapter);
5205
5205
  const { runRunAction: runPipeline } = await import(
5206
5206
  /* @vite-ignore */
5207
- "../run-Dwru4oKj.js"
5207
+ "../run-C6jy25us.js"
5208
5208
  );
5209
5209
  const runStoryFn = async (opts) => {
5210
5210
  const exitCode = await runPipeline({
@@ -1,4 +1,4 @@
1
- import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-BV-rzjf7.js";
1
+ import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-sQ1X_5_6.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./dist-VcMmfo2w.js";
4
4
  import "./decisions-C0pz9Clx.js";
@@ -4135,6 +4135,101 @@ function detectsEventDrivenAC(sourceEpicContent) {
4135
4135
  return false;
4136
4136
  }
4137
4137
  /**
4138
+ * Source-AC keywords that signal a state-integrating implementation. The six
4139
+ * categories — subprocess, filesystem, git, database, network, registry —
4140
+ * mirror the behavioral-signal enumeration in the create-story.md prompt
4141
+ * (Phase 1, v0.20.42, obs_2026-05-01_017 hotfix).
4142
+ *
4143
+ * Code identifiers are case-sensitive (e.g., `execSync(`, `Dolt`).
4144
+ * Natural-language phrases are case-insensitive (e.g., "reads from disk").
4145
+ *
4146
+ * Used by `detectsStateIntegratingAC` to gate probe-author dispatch for
4147
+ * state-integrating stories whose ACs do NOT use event-driven phrasing
4148
+ * (hooks, timers, signals, webhooks). Coexists with `EVENT_DRIVEN_KEYWORDS`:
4149
+ * ACs matching both heuristics trigger a single probe-author dispatch.
4150
+ */
4151
+ const STATE_INTEGRATING_KEYWORDS = [
4152
+ /\bexecSync\(/,
4153
+ /\bspawn\(/,
4154
+ /\bexec\(/,
4155
+ /\bchild_process\b/,
4156
+ /\bspawns\b/i,
4157
+ /\binvokes?\b/i,
4158
+ /\bfs\.read/,
4159
+ /\bfs\.write/,
4160
+ /\breadFile\b/,
4161
+ /\bwriteFile\b/,
4162
+ /\bpath\.join\b/,
4163
+ /\bhomedir\(\)/,
4164
+ /\bos\.homedir\(\)/,
4165
+ /\breads?\s+from\s+disk\b/i,
4166
+ /\bwrites?\s+to\s+disk\b/i,
4167
+ /\bscans?\s+(?:the\s+)?filesystem\b/i,
4168
+ /\bgit\s+log\b/,
4169
+ /\bgit\s+push\b/,
4170
+ /\bgit\s+pull\b/,
4171
+ /\bgit\s+merge\b/,
4172
+ /\bqueries?\s+git\b/i,
4173
+ /\bruns?\s+git\b/i,
4174
+ /\bDolt\b/,
4175
+ /\bmysql\b/i,
4176
+ /\bpg\b/,
4177
+ /\bsqlite\b/i,
4178
+ /\bINSERT\b/,
4179
+ /\bSELECT\b/,
4180
+ /\bqueries?\s+the\s+database\b/i,
4181
+ /\bwrites?\s+to\s+[Dd]olt\b/,
4182
+ /\bfetch\(/,
4183
+ /\baxios\b/i,
4184
+ /\bhttp\.get\(/,
4185
+ /\bhttps\.get\(/,
4186
+ /\bfetches?\b/i,
4187
+ /\bPOSTs?\s+to\b/i,
4188
+ /\bcalls?\s+the\s+API\b/i,
4189
+ /\bqueries?\s+(?:the\s+)?registry\b/i,
4190
+ /\bscans?\s+(?:the\s+)?registry\b/i
4191
+ ];
4192
+ /** Phrases that indicate a keyword match is in a mock/stub context (not real state). */
4193
+ const MOCK_QUALIFIER_PHRASES = [
4194
+ "mocks the",
4195
+ "stubs the",
4196
+ "mock ",
4197
+ "stub "
4198
+ ];
4199
+ /**
4200
+ * Returns true when a line contains a mock/stub qualifier, indicating the
4201
+ * keyword match is in a test-double context rather than production state.
4202
+ */
4203
+ function lineHasMockQualifier(line) {
4204
+ const lower = line.toLowerCase();
4205
+ return MOCK_QUALIFIER_PHRASES.some((phrase) => lower.includes(phrase));
4206
+ }
4207
+ /**
4208
+ * Returns true if the source AC text mentions a state-integrating operation:
4209
+ * subprocess, filesystem, git, database, network, or registry interaction.
4210
+ *
4211
+ * Exported for use by probe-author-integration.ts (Story 65-1) so the
4212
+ * orchestrator can gate probe-author dispatch on the same heuristic.
4213
+ *
4214
+ * Mock guard (AC #4): scans each matching line for mock/stub qualifiers
4215
+ * ("mocks the", "stubs the", "mock ", "stub "). If every match is in a
4216
+ * mock context, returns false — ground truth is whether the production
4217
+ * code path hits real state. If any match is NOT in a mock context,
4218
+ * returns true.
4219
+ *
4220
+ * Coexists with `detectsEventDrivenAC` (Story 60-11): when an AC matches
4221
+ * both heuristics, the orchestrator dispatches probe-author once (dispatch
4222
+ * gate uses `||`). No double-dispatch.
4223
+ */
4224
+ function detectsStateIntegratingAC(sourceContent) {
4225
+ const lines = sourceContent.split("\n");
4226
+ for (const line of lines) {
4227
+ const hasKeyword = STATE_INTEGRATING_KEYWORDS.some((p) => p.test(line));
4228
+ if (hasKeyword && !lineHasMockQualifier(line)) return true;
4229
+ }
4230
+ return false;
4231
+ }
4232
+ /**
4138
4233
  * Returns true if any probe's command line invokes a known production trigger.
4139
4234
  */
4140
4235
  function probesInvokeProductionTrigger(probes) {
@@ -6779,5 +6874,5 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
6779
6874
  }
6780
6875
 
6781
6876
  //#endregion
6782
- export { BMAD_BASELINE_TOKENS_FULL, DEFAULT_STALL_THRESHOLD_SECONDS, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, RuntimeProbeListSchema, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN$1 as STORY_KEY_PATTERN, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, __commonJS, __require, __toESM, aggregateProbeAuthorMetrics, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter$1 as createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, createStateStore, detectCycles, detectsEventDrivenAC, extractTargetFilesFromStoryContent, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, isOrchestratorProcessLine, parseDbTimestampAsUtc, parseRuntimeProbes, registerHealthCommand, renderFindings, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveGraphPath, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorMetrics, runHealthAction, validateStoryKey };
6783
- //# sourceMappingURL=health-BV-rzjf7.js.map
6877
+ export { BMAD_BASELINE_TOKENS_FULL, DEFAULT_STALL_THRESHOLD_SECONDS, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, RuntimeProbeListSchema, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN$1 as STORY_KEY_PATTERN, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, __commonJS, __require, __toESM, aggregateProbeAuthorMetrics, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter$1 as createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, createStateStore, detectCycles, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, isOrchestratorProcessLine, parseDbTimestampAsUtc, parseRuntimeProbes, registerHealthCommand, renderFindings, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveGraphPath, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorMetrics, runHealthAction, validateStoryKey };
6878
+ //# sourceMappingURL=health-sQ1X_5_6.js.map
@@ -1,8 +1,8 @@
1
- import "./health-BV-rzjf7.js";
1
+ import "./health-sQ1X_5_6.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./helpers-CElYrONe.js";
4
4
  import "./dist-VcMmfo2w.js";
5
- import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-BYMVrlGZ.js";
5
+ import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-CFRXRuRO.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, RuntimeProbeListSchema, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, detectsEventDrivenAC, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-BV-rzjf7.js";
1
+ import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, RuntimeProbeListSchema, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-sQ1X_5_6.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-VcMmfo2w.js";
@@ -6082,6 +6082,61 @@ const WORD_TO_NUMBER = {
6082
6082
  nine: 9,
6083
6083
  ten: 10
6084
6084
  };
6085
+ /**
6086
+ * obs_2026-05-03_021: nouns that genuinely end in `-s` in their singular form
6087
+ * — must NOT be lemma-stripped (would yield `proces`, `statu`, `busines`).
6088
+ * The list is the conservative set of common engineering vocabulary; broader
6089
+ * coverage can be added as new false-strips surface.
6090
+ */
6091
+ const LEMMA_STOPLIST = new Set([
6092
+ "process",
6093
+ "status",
6094
+ "class",
6095
+ "access",
6096
+ "success",
6097
+ "address",
6098
+ "business",
6099
+ "analysis",
6100
+ "basis",
6101
+ "crisis",
6102
+ "thesis",
6103
+ "axis",
6104
+ "series",
6105
+ "species"
6106
+ ]);
6107
+ /**
6108
+ * obs_2026-05-03_021: collapse plural ↔ singular noun forms to a shared
6109
+ * lemma so that the source AC's plural ("functions") and the rendered story's
6110
+ * singular code-form (`'function'`) compare on the same key.
6111
+ *
6112
+ * Strata Story 1-11b's failure shape was a canonical TS/JS rendering: source
6113
+ * AC says "X and Y are both **functions**" (prose plural), rendered story
6114
+ * expresses the same constraint as `typeof X === 'function' && typeof Y === 'function'`
6115
+ * (singular literal in backticks). The pre-fix heuristic counted plural
6116
+ * forms only, saw source=2, rendered=0, escalated as drift after 2 retries.
6117
+ *
6118
+ * Rules (priority order):
6119
+ * - Stoplist hit → return as-is (preserves `process`, `status`, etc.)
6120
+ * - `-ies` suffix (≥5 chars) → strip + 'y' (categories → category)
6121
+ * - `-es` suffix (≥5 chars) where stem ends in s/x/z/ch/sh → strip 'es' (classes → class, boxes → box, watches → watch)
6122
+ * - `-s` suffix (≥4 chars) → strip (functions → function, tools → tool)
6123
+ * - otherwise return as-is
6124
+ */
6125
+ function lemmatizeNoun(noun) {
6126
+ const lower = noun.toLowerCase();
6127
+ if (LEMMA_STOPLIST.has(lower)) return lower;
6128
+ if (lower.length >= 5 && lower.endsWith("ies")) return lower.slice(0, -3) + "y";
6129
+ if (lower.length >= 5 && lower.endsWith("ches")) {
6130
+ if (lower.endsWith("tches")) return lower.slice(0, -2);
6131
+ return lower.slice(0, -1);
6132
+ }
6133
+ if (lower.length >= 5 && lower.endsWith("es")) {
6134
+ const stem = lower.slice(0, -2);
6135
+ if (/[sxz]$/.test(stem) || /sh$/.test(stem)) return stem;
6136
+ }
6137
+ if (lower.length >= 4 && lower.endsWith("s")) return lower.slice(0, -1);
6138
+ return lower;
6139
+ }
6085
6140
  function extractBehavioralAssertions(content) {
6086
6141
  if (content.length === 0) return {
6087
6142
  whenClauseCount: 0,
@@ -6113,13 +6168,33 @@ function extractBehavioralAssertions(content) {
6113
6168
  }
6114
6169
  if (noun.length < 3) continue;
6115
6170
  const phrase = `${determiner} ${numStr || (determiner === "both" ? "" : "")} ${noun}`.trim().replace(/\s+/g, " ");
6116
- const dedupKey = `${count}|${noun}`;
6171
+ const lemma = lemmatizeNoun(noun);
6172
+ const dedupKey = `${count}|${lemma}`;
6117
6173
  if (seen.has(dedupKey)) continue;
6118
6174
  seen.add(dedupKey);
6119
6175
  numericQuantifiers.push({
6120
6176
  phrase,
6121
6177
  count,
6122
- noun
6178
+ noun: lemma
6179
+ });
6180
+ }
6181
+ const backtickLiteralPattern = /`[^`]*?'([a-z][a-z_-]+)'[^`]*?`/gi;
6182
+ const backtickCounts = new Map();
6183
+ let blMatch;
6184
+ while ((blMatch = backtickLiteralPattern.exec(content)) !== null) {
6185
+ const rawNoun = blMatch[1]?.toLowerCase() ?? "";
6186
+ if (rawNoun.length < 3) continue;
6187
+ const lemma = lemmatizeNoun(rawNoun);
6188
+ backtickCounts.set(lemma, (backtickCounts.get(lemma) ?? 0) + 1);
6189
+ }
6190
+ for (const [lemma, count] of backtickCounts) {
6191
+ const dedupKey = `${count}|${lemma}`;
6192
+ if (seen.has(dedupKey)) continue;
6193
+ seen.add(dedupKey);
6194
+ numericQuantifiers.push({
6195
+ phrase: `<backtick-literal-occurrences>`,
6196
+ count,
6197
+ noun: lemma
6123
6198
  });
6124
6199
  }
6125
6200
  return {
@@ -6128,6 +6203,14 @@ function extractBehavioralAssertions(content) {
6128
6203
  numericQuantifiers
6129
6204
  };
6130
6205
  }
6206
+ /**
6207
+ * obs_2026-05-03_021: hard-fail threshold for numeric-quantifier drift.
6208
+ * When `renderedCount / sourceCount ≤ 0.5`, the drop is large enough to
6209
+ * indicate genuine clause reduction (strata 1-10 was 4→2 = 0.5; the
6210
+ * boundary is inclusive). Above 0.5, the gap is within plausible
6211
+ * lemma/code-rendering variance and demoted to warn.
6212
+ */
6213
+ const NUMERIC_HARD_FAIL_RATIO = .5;
6131
6214
  function computeClauseFidelity(storyFileContent, sourceContent) {
6132
6215
  const sourceSignals = extractBehavioralAssertions(sourceContent);
6133
6216
  const renderedSignals = extractBehavioralAssertions(storyFileContent);
@@ -6141,13 +6224,19 @@ function computeClauseFidelity(storyFileContent, sourceContent) {
6141
6224
  const numericMismatches = [];
6142
6225
  for (const [noun, sourceCnt] of sourceNounCounts.entries()) {
6143
6226
  const renderedCnt = renderedNounCounts.get(noun) ?? 0;
6144
- if (renderedCnt < sourceCnt) numericMismatches.push({
6145
- noun,
6146
- sourceCount: sourceCnt,
6147
- renderedCount: renderedCnt
6148
- });
6227
+ if (renderedCnt < sourceCnt) {
6228
+ const ratio = sourceCnt === 0 ? 1 : renderedCnt / sourceCnt;
6229
+ const severity = ratio <= NUMERIC_HARD_FAIL_RATIO ? "error" : "warn";
6230
+ numericMismatches.push({
6231
+ noun,
6232
+ sourceCount: sourceCnt,
6233
+ renderedCount: renderedCnt,
6234
+ severity
6235
+ });
6236
+ }
6149
6237
  }
6150
- const numericDriftComponent = numericMismatches.length > 0 ? 1 : 0;
6238
+ const hasNumericHardFail = numericMismatches.some((m) => m.severity === "error");
6239
+ const numericDriftComponent = hasNumericHardFail ? 1 : 0;
6151
6240
  const CLAUSE_RATIO_FLOOR = .7;
6152
6241
  const clauseDriftComponent = clauseRatio >= CLAUSE_RATIO_FLOOR ? 0 : Math.min(1, (CLAUSE_RATIO_FLOOR - clauseRatio) / CLAUSE_RATIO_FLOOR);
6153
6242
  const drift = Math.max(numericDriftComponent, clauseDriftComponent);
@@ -8323,8 +8412,11 @@ const TIMEOUT_RETRY_MULTIPLIER = 1.5;
8323
8412
  /**
8324
8413
  * Execute the probe-author integration phase.
8325
8414
  *
8326
- * Gate 1 — Event-driven AC check: calls `detectsEventDrivenAC(epicContent)`.
8327
- * Skip if the source AC does not describe a hook, timer, signal, or webhook.
8415
+ * Gate 1 — Event-driven or state-integrating AC check: calls
8416
+ * `detectsEventDrivenAC(epicContent)` and `detectsStateIntegratingAC(epicContent)`
8417
+ * (Story 65-1). Skip if the source AC describes neither an event-driven
8418
+ * mechanism (hook, timer, signal, webhook) nor a state-integrating operation
8419
+ * (subprocess, filesystem, git, database, network, registry).
8328
8420
  *
8329
8421
  * Gate 2 — Idempotency check: reads storyFilePath and checks for an existing
8330
8422
  * `## Runtime Probes` section. Skip if present (probes already authored).
@@ -8351,8 +8443,8 @@ async function runProbeAuthor(deps, params) {
8351
8443
  input: 0,
8352
8444
  output: 0
8353
8445
  };
8354
- if (bypassGates !== true && !detectsEventDrivenAC(epicContent)) {
8355
- logger$14.debug({ storyKey }, "probe-author: source AC not event-driven — skipping");
8446
+ if (bypassGates !== true && !detectsEventDrivenAC(epicContent) && !detectsStateIntegratingAC(epicContent)) {
8447
+ logger$14.debug({ storyKey }, "probe-author: source AC neither event-driven nor state-integrating — skipping");
8356
8448
  emitEvent?.("probe-author:skipped", {
8357
8449
  storyKey,
8358
8450
  runId: pipelineRunId,
@@ -13214,7 +13306,7 @@ function createImplementationOrchestrator(deps) {
13214
13306
  renameSync(storyFilePath, stalePath);
13215
13307
  const driftPct = Math.round(overallDrift * 100);
13216
13308
  const pathMissing = pathFidelity?.missing ?? [];
13217
- const numericMismatches = clauseFidelity.numericMismatches;
13309
+ const numericMismatches = clauseFidelity.numericMismatches.filter((m) => m.severity === "error");
13218
13310
  const reasons = [];
13219
13311
  if (pathMissing.length > 0) reasons.push(`${pathMissing.length} named path(s) missing`);
13220
13312
  if (numericMismatches.length > 0) reasons.push(`${numericMismatches.length} numeric quantifier mismatch(es) (e.g., "${numericMismatches[0].noun}" source=${numericMismatches[0].sourceCount} rendered=${numericMismatches[0].renderedCount})`);
@@ -13270,7 +13362,7 @@ function createImplementationOrchestrator(deps) {
13270
13362
  }
13271
13363
  } else {
13272
13364
  const pathMissing = pathFidelity?.missing ?? [];
13273
- const numericMismatches = clauseFidelity.numericMismatches;
13365
+ const numericMismatches = clauseFidelity.numericMismatches.filter((m) => m.severity === "error");
13274
13366
  const reasons = [];
13275
13367
  if (pathMissing.length > 0) reasons.push(`paths missing: ${pathMissing.join(", ")}`);
13276
13368
  if (numericMismatches.length > 0) reasons.push(`numeric mismatches: ${numericMismatches.map((m) => `${m.noun} (source=${m.sourceCount}, rendered=${m.renderedCount})`).join("; ")}`);
@@ -13354,7 +13446,7 @@ function createImplementationOrchestrator(deps) {
13354
13446
  const section = extractStorySection(epicFull, storyKey);
13355
13447
  probeAuthorEpicContent = section ?? epicFull;
13356
13448
  } catch {}
13357
- if (detectsEventDrivenAC(probeAuthorEpicContent)) {
13449
+ if (detectsEventDrivenAC(probeAuthorEpicContent) || detectsStateIntegratingAC(probeAuthorEpicContent)) {
13358
13450
  let artifactHasProbes = false;
13359
13451
  try {
13360
13452
  const artifactContent = readFileSync(storyFilePath, "utf-8");
@@ -45277,4 +45369,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
45277
45369
 
45278
45370
  //#endregion
45279
45371
  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, runProbeAuthor, runRunAction, runSolutioningPhase, validateStopAfterFromConflict, wireNdjsonEmitter };
45280
- //# sourceMappingURL=run-BYMVrlGZ.js.map
45372
+ //# sourceMappingURL=run-CFRXRuRO.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.20.47",
3
+ "version": "0.20.49",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",