substrate-ai 0.20.27 → 0.20.28

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_FINDING_COUNTS, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts } from "../health-0_axmI2t.js";
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-Dx9hm9x1.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-DQcG05Ar.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-BmRu588B.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-CrEdV2B3.js");
3670
+ const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-C6x1jDlX.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-DQ29oNG2.js"
5201
+ "../run-CV3EAUZK.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-0_axmI2t.js";
1
+ import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-Dx9hm9x1.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./dist-CqtWS9wF.js";
4
4
  import "./decisions-C0pz9Clx.js";
@@ -3744,6 +3744,70 @@ const CATEGORY_TIMEOUT = "runtime-probe-timeout";
3744
3744
  * from "tool errored loudly".
3745
3745
  */
3746
3746
  const CATEGORY_ASSERTION_FAIL = "runtime-probe-assertion-fail";
3747
+ /**
3748
+ * Story 60-11: source AC describes an event-driven mechanism (hook, timer,
3749
+ * signal, webhook) but no probe's command invokes a known production-trigger
3750
+ * pattern. Strata Run 13 (Story 1-12, 2026-04-26): vault conflict hook
3751
+ * shipped SHIP_IT non-functional because the dev's probe ran the hook script
3752
+ * directly with `bash .git/hooks/post-merge` — git only fires post-merge on
3753
+ * a SUCCESSFUL merge, so under conflict (the hook's actual use case) the
3754
+ * production trigger never fires. Direct-invocation probe missed it; only
3755
+ * e2e smoke caught it. Sibling to obs_012's success-shape gap.
3756
+ *
3757
+ * Severity is warn (advisory, non-blocking) until the heuristic is
3758
+ * calibrated against several runs. Flip to error once false-positive rate
3759
+ * is verified low.
3760
+ */
3761
+ const CATEGORY_MISSING_TRIGGER = "runtime-probe-missing-production-trigger";
3762
+ /**
3763
+ * Source-AC keywords that signal an event-driven implementation. Word-boundary
3764
+ * matched, case-insensitive. When any of these appears in source AC text AND
3765
+ * no probe's command invokes a known production trigger, the check emits a
3766
+ * `runtime-probe-missing-production-trigger` warn finding.
3767
+ *
3768
+ * Each keyword is paired with the trigger patterns that satisfy it:
3769
+ * - `git hook` / `post-merge` / etc. → satisfied by `git merge|pull|push|commit|rebase`
3770
+ * - `systemd` / `timer` / `unit` → satisfied by `systemctl ... start|enable|trigger`
3771
+ * - `cron` / `crontab` / `schedule` → satisfied by `crontab|run-parts|schedule`
3772
+ * - `signal` / `SIGHUP|SIGTERM|SIGUSR` → satisfied by `kill -<signal>`
3773
+ * - `webhook` / `HTTP POST` / `endpoint` → satisfied by `curl ... -X POST` or wget
3774
+ * - `inotify` / `path watch` → satisfied by `touch|mkdir|rm` (filesystem mutation)
3775
+ */
3776
+ const EVENT_DRIVEN_KEYWORDS = [
3777
+ /\b(?:git\s+hook|post-merge|post-commit|post-rewrite|pre-push|pre-commit|pre-merge-commit)\b/i,
3778
+ /\b(?:systemd\s+(?:unit|service|timer|path)|systemctl|\.timer\b|\.service\b)\b/i,
3779
+ /\b(?:cron\s*(?:job|tab|expression)?|crontab|scheduled\s+task)\b/i,
3780
+ /\b(?:signal\s+handler|SIG(?:HUP|TERM|INT|USR1|USR2|KILL))\b/,
3781
+ /\b(?:webhook|HTTP\s+(?:POST|GET)\s+endpoint|REST\s+endpoint)\b/i,
3782
+ /\b(?:inotify|path\s+watch|file\s+watcher)\b/i
3783
+ ];
3784
+ /**
3785
+ * Production-trigger command patterns. If ANY probe's command matches one of
3786
+ * these, the heuristic considers the trigger covered. Word-boundary matched.
3787
+ */
3788
+ const TRIGGER_COMMAND_PATTERNS = [
3789
+ /\bgit\s+(?:merge|pull|push|commit|rebase|cherry-pick)\b/,
3790
+ /\bsystemctl\b/,
3791
+ /\bcrontab\b|\brun-parts\b/,
3792
+ /\bkill\s+-/,
3793
+ /\bcurl\s+(?:[^|]*\s)?-X\s+(?:POST|GET|PUT|DELETE)/i,
3794
+ /\bwget\s+--method=(?:POST|GET|PUT|DELETE)/i,
3795
+ /\b(?:touch|mkdir|rm)\s+/
3796
+ ];
3797
+ /**
3798
+ * Returns true if the source AC text mentions an event-driven mechanism.
3799
+ */
3800
+ function detectsEventDrivenAC(sourceEpicContent) {
3801
+ for (const pattern of EVENT_DRIVEN_KEYWORDS) if (pattern.test(sourceEpicContent)) return true;
3802
+ return false;
3803
+ }
3804
+ /**
3805
+ * Returns true if any probe's command line invokes a known production trigger.
3806
+ */
3807
+ function probesInvokeProductionTrigger(probes) {
3808
+ for (const probe of probes) for (const pattern of TRIGGER_COMMAND_PATTERNS) if (pattern.test(probe.command)) return true;
3809
+ return false;
3810
+ }
3747
3811
  const defaultExecutors = { host: (probe) => executeProbeOnHost(probe, { cwd: process.cwd() }) };
3748
3812
  var RuntimeProbeCheck = class {
3749
3813
  name = "runtime-probes";
@@ -3797,6 +3861,13 @@ var RuntimeProbeCheck = class {
3797
3861
  findings: []
3798
3862
  };
3799
3863
  const findings = [];
3864
+ if (context.sourceEpicContent !== void 0) {
3865
+ if (detectsEventDrivenAC(context.sourceEpicContent) && !probesInvokeProductionTrigger(parsed.probes)) findings.push({
3866
+ category: CATEGORY_MISSING_TRIGGER,
3867
+ severity: "warn",
3868
+ message: "source AC describes an event-driven mechanism (hook / timer / signal / webhook) but no probe's command invokes a known production trigger (git merge/pull/push, systemctl, crontab, kill -<sig>, curl -X POST, etc.). Probes that call the implementation directly skip the wiring layer the AC's user-facing event would exercise — see strata Run 13 / Story 1-12 for the canonical case (post-merge hook never fires under git's conflict semantic). Authoring guidance: probes/event-driven section of create-story.md."
3869
+ });
3870
+ }
3800
3871
  for (const probe of parsed.probes) {
3801
3872
  if (probe.sandbox === "twin") {
3802
3873
  findings.push({
@@ -5996,4 +6067,4 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
5996
6067
 
5997
6068
  //#endregion
5998
6069
  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 };
5999
- //# sourceMappingURL=health-0_axmI2t.js.map
6070
+ //# sourceMappingURL=health-Dx9hm9x1.js.map
@@ -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-0_axmI2t.js";
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-Dx9hm9x1.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";
@@ -44488,4 +44488,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
44488
44488
 
44489
44489
  //#endregion
44490
44490
  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 };
44491
- //# sourceMappingURL=run-DQcG05Ar.js.map
44491
+ //# sourceMappingURL=run-BmRu588B.js.map
@@ -1,8 +1,8 @@
1
- import "./health-0_axmI2t.js";
1
+ import "./health-Dx9hm9x1.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-DQcG05Ar.js";
5
+ import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-BmRu588B.js";
6
6
  import "./routing-CcBOCuC9.js";
7
7
  import "./decisions-C0pz9Clx.js";
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.20.27",
3
+ "version": "0.20.28",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -158,6 +158,54 @@ Exit-code success is necessary but **not sufficient** for probes calling tools t
158
158
 
159
159
  Patterns are JavaScript regex (`new RegExp`). Evaluated only when exit code is 0; non-zero exits emit `runtime-probe-fail` and assertions are skipped to avoid redundant findings.
160
160
 
161
+ ### Probes for event-driven mechanisms must invoke the production trigger
162
+
163
+ When the source AC describes a hook, timer, signal, webhook, or other event-driven mechanism, the probe MUST invoke the **production trigger** that fires the implementation in real usage — NOT call the implementation script directly. Calling the implementation directly verifies it produces correct outputs given synthetic inputs; it does NOT verify the implementation is wired to the right trigger and will actually fire when the AC's user-facing event occurs.
164
+
165
+ Strata Run 13 (Story 1-12, post-merge git hook) shipped SHIP_IT after the dev's probe ran the resolver script directly with conflict-marker fixtures. The resolver was correct; the wiring was not. `git`'s `post-merge` hook is **not executed when a merge fails due to conflicts** (per `githooks(5)`) — and the AC's whole point was conflict resolution. The hook never fired in production. Direct invocation hid this entirely.
166
+
167
+ **Rule**: if the AC describes "when X happens, Y runs", the probe must MAKE X HAPPEN and assert Y ran. Synthesized inputs to Y skip the wiring layer.
168
+
169
+ | AC describes | Production trigger to invoke | Common wrong shape (DO NOT use) |
170
+ |---|---|---|
171
+ | `post-merge` / `post-commit` / `post-rewrite` git hook | `git merge <branch>` (with the conflict scenario the AC describes) | `bash .git/hooks/post-merge` |
172
+ | `pre-push` git hook | `git push` against a local fixture remote | `bash .git/hooks/pre-push` |
173
+ | systemd unit / timer | `systemctl --user start <unit>` or `systemctl --user start <timer>.timer` then assert `<unit>.service` ran | direct call to the binary the unit invokes |
174
+ | systemd path / inotify trigger | touch / create / modify the watched path; assert the unit fires within N seconds | direct call to the script |
175
+ | cron job | invoke `crontab` to install + run-once via `run-parts` OR shorten the schedule to `* * * * *` and wait | direct call to the script |
176
+ | Signal handler | `kill -<SIGNAL> <pid>` against the running process | direct call to the handler function |
177
+ | Webhook receiver | `curl -X POST <endpoint>` with the actual payload shape | direct call to the handler with synthetic payload |
178
+
179
+ **Example: post-merge hook probe (the strata 1-12 case, fixed)**
180
+
181
+ ```yaml
182
+ - name: post-merge-hook-fires-and-resolves-conflict
183
+ sandbox: twin
184
+ command: |
185
+ set -e
186
+ REPO=$(mktemp -d)
187
+ cd "$REPO" && git init -q
188
+ git config user.email t@example.com && git config user.name test
189
+ bash <REPO_ROOT>/hooks/install-vault-hooks.sh "$REPO"
190
+ echo "human content" > note.md && git add . && git commit -qm initial
191
+ git checkout -qb branch-jarvis
192
+ GIT_AUTHOR_NAME=jarvis-bot GIT_AUTHOR_EMAIL=jarvis@bot \
193
+ bash -c 'echo "jarvis content" > note.md && git commit -aqm "jarvis edit"'
194
+ git checkout -q main
195
+ echo "human content edit" > note.md && git commit -aqm "human edit"
196
+ git merge --no-edit branch-jarvis || true # produces conflict
197
+ # If post-merge fired correctly via the production trigger, the conflict is resolved.
198
+ # If it did NOT fire (because it can't, by design — see githooks(5)), the working
199
+ # tree still has conflict markers and this assertion catches it.
200
+ expect_stdout_no_regex:
201
+ - '<{7}|>{7}' # conflict markers must NOT remain in tree after resolution
202
+ expect_stdout_regex:
203
+ - 'human content' # human side preserved per "Jarvis yields to human" rule
204
+ description: real git merge fires (or fails to fire) post-merge — assertion catches both
205
+ ```
206
+
207
+ Note this example, taken to production, would have caught the strata 1-12 bug at runtime-probe phase rather than only at e2e smoke pass. That's the standard 60-10 sets.
208
+
161
209
  ### Examples by artifact class
162
210
 
163
211
  **Systemd unit:**