mandrel 2.24.0 → 2.26.0
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/.agents/agents/acceptance-critic.md +19 -21
- package/.agents/agents/auditor.md +16 -19
- package/.agents/agents/story-worker.md +41 -54
- package/.agents/audit-checklists/architecture.md +5 -1
- package/.agents/audit-checklists/baselines.md +21 -0
- package/.agents/audit-checklists/quality.md +3 -0
- package/.agents/docs/agentrc-reference.json +2 -1
- package/.agents/docs/configuration.md +2 -1
- package/.agents/docs/quality-gates.md +80 -18
- package/.agents/docs/workflows.md +4 -2
- package/.agents/instructions.md +1 -1
- package/.agents/rules/ci-remediation.md +68 -3
- package/.agents/schemas/agentrc.schema.json +6 -1
- package/.agents/schemas/audit-rules.json +15 -0
- package/.agents/schemas/baselines/audit-baselines-envelope.schema.json +242 -0
- package/.agents/schemas/baselines/baseline-envelope.schema.json +8 -0
- package/.agents/schemas/baselines/crap.schema.json +13 -1
- package/.agents/schemas/crap-report.schema.json +37 -0
- package/.agents/schemas/model-attribution.schema.json +4 -0
- package/.agents/scripts/acceptance-eval.js +124 -15
- package/.agents/scripts/audit-baselines.js +136 -0
- package/.agents/scripts/audit-labels-bootstrap.js +16 -95
- package/.agents/scripts/audit-to-stories.js +33 -3
- package/.agents/scripts/check-arch-cycles.js +12 -93
- package/.agents/scripts/check-baseline-drift.js +16 -3
- package/.agents/scripts/check-baselines.js +19 -3
- package/.agents/scripts/check-context-budget.js +95 -14
- package/.agents/scripts/check-cyclomatic.js +214 -0
- package/.agents/scripts/check-schema-references.js +392 -0
- package/.agents/scripts/check-test-temp-hygiene.js +121 -1
- package/.agents/scripts/check-workflow-timeouts.js +291 -0
- package/.agents/scripts/cleanup-repo-test-temp.js +7 -5
- package/.agents/scripts/deliver-light.js +3 -4
- package/.agents/scripts/deliver-recover.js +13 -0
- package/.agents/scripts/diagnose-friction.js +85 -19
- package/.agents/scripts/lib/audit-baselines/engine.js +177 -0
- package/.agents/scripts/lib/audit-baselines/gate-surface.js +63 -0
- package/.agents/scripts/lib/audit-baselines/headroom.js +72 -0
- package/.agents/scripts/lib/audit-baselines/hotspots.js +69 -0
- package/.agents/scripts/lib/audit-baselines/kinds.js +313 -0
- package/.agents/scripts/lib/audit-baselines/outliers.js +100 -0
- package/.agents/scripts/lib/audit-baselines/read.js +87 -0
- package/.agents/scripts/lib/audit-baselines/staleness.js +123 -0
- package/.agents/scripts/lib/audit-baselines/surface-entry.js +106 -0
- package/.agents/scripts/lib/audit-baselines/trend.js +125 -0
- package/.agents/scripts/lib/audit-baselines/weights.js +193 -0
- package/.agents/scripts/lib/audit-suite/index.js +0 -5
- package/.agents/scripts/lib/audit-suite/selector.js +9 -62
- package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +177 -0
- package/.agents/scripts/lib/audit-to-stories/audit-lenses.js +1 -0
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +35 -3
- package/.agents/scripts/lib/audit-to-stories/group-findings.js +20 -11
- package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +23 -9
- package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +9 -2
- package/.agents/scripts/lib/baseline-loader.js +0 -0
- package/.agents/scripts/lib/baseline-schema-registry.js +13 -1
- package/.agents/scripts/lib/baselines/diff-scope-cli.js +22 -160
- package/.agents/scripts/lib/baselines/duplication-scanner.js +27 -0
- package/.agents/scripts/lib/baselines/git-base.js +0 -0
- package/.agents/scripts/lib/baselines/kinds/crap.js +499 -57
- package/.agents/scripts/lib/baselines/preview-gates.js +58 -15
- package/.agents/scripts/lib/baselines/reader.js +10 -0
- package/.agents/scripts/lib/baselines/refresh-service.js +69 -11
- package/.agents/scripts/lib/baselines/scope.js +39 -90
- package/.agents/scripts/lib/baselines/writer.js +16 -7
- package/.agents/scripts/lib/changed-files.js +8 -1
- package/.agents/scripts/lib/cli-args.js +115 -1
- package/.agents/scripts/lib/close-validation/runner.js +70 -25
- package/.agents/scripts/lib/config-settings-schema-delivery.js +5 -0
- package/.agents/scripts/lib/crap-engine.js +124 -27
- package/.agents/scripts/lib/crap-method-identity.js +153 -0
- package/.agents/scripts/lib/crap-utils.js +86 -13
- package/.agents/scripts/lib/cyclomatic-ceiling.js +265 -0
- package/.agents/scripts/lib/env-loader.js +46 -16
- package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -2
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +0 -2
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +0 -2
- package/.agents/scripts/lib/findings/route-finding.js +123 -17
- package/.agents/scripts/lib/findings/severity.js +80 -2
- package/.agents/scripts/lib/git/cached-fetch.js +0 -0
- package/.agents/scripts/lib/git-utils.js +136 -80
- package/.agents/scripts/lib/import-graph.js +156 -0
- package/.agents/scripts/lib/label-constants.js +17 -0
- package/.agents/scripts/lib/observability/runtime-friction.js +17 -2
- package/.agents/scripts/lib/observability/source-classifier.js +175 -2
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +110 -17
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +17 -12
- package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +36 -6
- package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +5 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +12 -1
- package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +8 -1
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +548 -0
- package/.agents/scripts/lib/orchestration/deliver-recover.js +2 -2
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +10 -5
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +39 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +71 -20
- package/.agents/scripts/lib/orchestration/light-backstop.js +20 -3
- package/.agents/scripts/lib/orchestration/light-escalation.js +82 -3
- package/.agents/scripts/lib/orchestration/light-suitability.js +103 -5
- package/.agents/scripts/lib/orchestration/plan-context.js +119 -66
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +31 -5
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +216 -109
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +61 -13
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +79 -22
- package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +51 -20
- package/.agents/scripts/lib/orchestration/planning/authoring-context.js +70 -74
- package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +231 -0
- package/.agents/scripts/lib/orchestration/resolve-stories.js +18 -17
- package/.agents/scripts/lib/orchestration/retro-proposals.js +0 -0
- package/.agents/scripts/lib/orchestration/run-epilogue.js +12 -0
- package/.agents/scripts/lib/orchestration/run-scoped-config.js +276 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +41 -10
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +125 -37
- package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +6 -6
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +42 -38
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +129 -21
- package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +6 -1
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +267 -125
- package/.agents/scripts/lib/orchestration/spec-budget.js +16 -5
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +182 -95
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +22 -0
- package/.agents/scripts/lib/orchestration/ticket-validator.js +5 -11
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +4 -4
- package/.agents/scripts/lib/reserved-test-ids.js +77 -0
- package/.agents/scripts/lib/story-adjacency.js +3 -3
- package/.agents/scripts/lib/story-plan.js +137 -42
- package/.agents/scripts/lib/templates/decomposer-prompts.js +4 -4
- package/.agents/scripts/lib/test-runner-contract.js +134 -0
- package/.agents/scripts/lib/test-tiers.js +11 -2
- package/.agents/scripts/lib/ticket-body-sections.js +1 -1
- package/.agents/scripts/lib/util/concurrent-map.js +17 -0
- package/.agents/scripts/lib/util/parse-id-list.js +103 -0
- package/.agents/scripts/lib/util/poll-loop.js +18 -2
- package/.agents/scripts/lib/wave-runner/live-probe.js +29 -13
- package/.agents/scripts/lib/wave-runner/ready-set.js +254 -43
- package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +10 -13
- package/.agents/scripts/lib/workers/crap-worker.js +8 -13
- package/.agents/scripts/lib/workers/maintainability-report-worker.js +4 -10
- package/.agents/scripts/lib/workers/maintainability-worker.js +4 -10
- package/.agents/scripts/lib/workers/serve-worker-messages.js +35 -0
- package/.agents/scripts/lib/worktree/git-hooks.js +206 -0
- package/.agents/scripts/lib/worktree/lifecycle/creation.js +6 -0
- package/.agents/scripts/lib/worktree-manager.js +14 -0
- package/.agents/scripts/plan-run-epilogue.js +17 -5
- package/.agents/scripts/pr-watch-with-update.js +568 -213
- package/.agents/scripts/providers/github/tickets.js +33 -10
- package/.agents/scripts/provision-git-hooks.js +85 -0
- package/.agents/scripts/quality-preview.js +137 -28
- package/.agents/scripts/resolve-stories.js +4 -1
- package/.agents/scripts/run-coverage.js +86 -35
- package/.agents/scripts/run-lint.js +20 -0
- package/.agents/scripts/run-tests.js +36 -36
- package/.agents/scripts/single-story-close.js +34 -2
- package/.agents/scripts/single-story-confirm-merge.js +22 -6
- package/.agents/scripts/single-story-init.js +7 -0
- package/.agents/scripts/stories-wave-tick.js +308 -47
- package/.agents/scripts/story-plan.js +65 -9
- package/.agents/scripts/update-coverage-baseline.js +34 -4
- package/.agents/scripts/update-crap-baseline.js +42 -4
- package/.agents/scripts/update-duplication-baseline.js +209 -83
- package/.agents/scripts/update-maintainability-baseline.js +31 -4
- package/.agents/scripts/validate-docs-freshness.js +1 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +4 -1
- package/.agents/skills/core/gates-and-baselines/SKILL.md +17 -11
- package/.agents/skills/skills.index.json +2 -2
- package/.agents/templates/single-story-body.md +16 -8
- package/.agents/workflows/audit-architecture.md +69 -13
- package/.agents/workflows/audit-baselines.md +289 -0
- package/.agents/workflows/audit-navigability.md +5 -4
- package/.agents/workflows/audit-quality.md +26 -0
- package/.agents/workflows/audit-to-stories.md +30 -4
- package/.agents/workflows/deliver.md +92 -97
- package/.agents/workflows/helpers/acceptance-self-eval.md +47 -10
- package/.agents/workflows/helpers/audit-lens-core.md +25 -4
- package/.agents/workflows/helpers/code-quality-guardrails.md +9 -2
- package/.agents/workflows/helpers/deliver-digest.md +41 -21
- package/.agents/workflows/helpers/deliver-reference.md +95 -4
- package/.agents/workflows/helpers/deliver-story-reference.md +174 -52
- package/.agents/workflows/helpers/deliver-story.md +104 -119
- package/.agents/workflows/helpers/plan-reference.md +49 -5
- package/.agents/workflows/memory-consolidate.md +116 -0
- package/.agents/workflows/plan.md +62 -75
- package/README.md +13 -6
- package/docs/CHANGELOG.md +93 -0
- package/lib/cli/update.js +14 -11
- package/lib/cli/version-check.js +9 -1
- package/package.json +9 -4
- package/.agents/schemas/friction-event.schema.json +0 -56
- package/.agents/scripts/lib/feedback-loop/memory-freshness.js +0 -707
|
@@ -29,52 +29,34 @@
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
import { spawnSync } from 'node:child_process';
|
|
32
|
-
import os from 'node:os';
|
|
33
32
|
import path from 'node:path';
|
|
34
33
|
import { fileURLToPath } from 'node:url';
|
|
34
|
+
import { assertNoReservedIdStreams } from './check-test-temp-hygiene.js';
|
|
35
35
|
import { cleanupRepoTestTempArtifacts } from './cleanup-repo-test-temp.js';
|
|
36
36
|
import { runAsCli } from './lib/cli-utils.js';
|
|
37
37
|
import { buildWebhookSafeTestEnv } from './lib/test-env.js';
|
|
38
|
+
import {
|
|
39
|
+
resolveTestConcurrency,
|
|
40
|
+
runTierPreflight,
|
|
41
|
+
TEST_CONCURRENCY_MAX,
|
|
42
|
+
TEST_CONCURRENCY_MIN,
|
|
43
|
+
TEST_RUNNER_FLAGS,
|
|
44
|
+
} from './lib/test-runner-contract.js';
|
|
38
45
|
import { listTestFilesForTier, parseTierArgv } from './lib/test-tiers.js';
|
|
39
46
|
|
|
40
47
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
41
48
|
const ROOT = path.resolve(__dirname, '..', '..');
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* without touching the OS.
|
|
54
|
-
*
|
|
55
|
-
* @param {number} [parallelism] - defaults to `os.availableParallelism()`
|
|
56
|
-
* @returns {number}
|
|
57
|
-
*/
|
|
58
|
-
export function resolveTestConcurrency(
|
|
59
|
-
parallelism = os.availableParallelism(),
|
|
60
|
-
) {
|
|
61
|
-
return Math.min(
|
|
62
|
-
TEST_CONCURRENCY_MAX,
|
|
63
|
-
Math.max(TEST_CONCURRENCY_MIN, parallelism),
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Fixed `node --test` flags applied to every spawn (every chunk).
|
|
69
|
-
* `--test-concurrency` is derived at startup from the host's available
|
|
70
|
-
* parallelism so the value is appropriate for the machine running the suite
|
|
71
|
-
* rather than being pinned to the historical constant of 8.
|
|
72
|
-
*/
|
|
73
|
-
export const TEST_RUNNER_FLAGS = Object.freeze([
|
|
74
|
-
'--experimental-test-module-mocks',
|
|
75
|
-
'--test',
|
|
76
|
-
`--test-concurrency=${resolveTestConcurrency()}`,
|
|
77
|
-
]);
|
|
50
|
+
// The runner contract (flag set, concurrency clamp, tier preflight) is
|
|
51
|
+
// declared once in `lib/test-runner-contract.js` so `run-coverage.js` shares
|
|
52
|
+
// it by construction. Re-exported here because this module is the runner's
|
|
53
|
+
// public face for its own unit tests.
|
|
54
|
+
export {
|
|
55
|
+
resolveTestConcurrency,
|
|
56
|
+
TEST_CONCURRENCY_MAX,
|
|
57
|
+
TEST_CONCURRENCY_MIN,
|
|
58
|
+
TEST_RUNNER_FLAGS,
|
|
59
|
+
};
|
|
78
60
|
|
|
79
61
|
/**
|
|
80
62
|
* Per-spawn character budget for the joined *targets* portion of the argv.
|
|
@@ -165,8 +147,18 @@ export function runTestSuite({
|
|
|
165
147
|
cleanup = cleanupRepoTestTempArtifacts,
|
|
166
148
|
listTargets = listTestFilesForTier,
|
|
167
149
|
maxTargetChars = resolveMaxTargetChars(),
|
|
150
|
+
fixtureStreamGuard = assertNoReservedIdStreams,
|
|
151
|
+
preflight = runTierPreflight,
|
|
168
152
|
} = {}) {
|
|
169
153
|
const { tier, rest } = parseTierArgv(argv);
|
|
154
|
+
|
|
155
|
+
// Preflight first, and abort on refusal — the tier's checks exist to stop
|
|
156
|
+
// the suite starting from a known-bad state, so running them after the
|
|
157
|
+
// spawn would be pointless. Nothing has been created yet, so there is
|
|
158
|
+
// nothing to clean up on this path.
|
|
159
|
+
const preflightStatus = preflight({ tier, repoRoot: cwd });
|
|
160
|
+
if (preflightStatus !== 0) return preflightStatus;
|
|
161
|
+
|
|
170
162
|
const targets = listTargets(tier, cwd);
|
|
171
163
|
const chunks = chunkTestTargets(targets, maxTargetChars);
|
|
172
164
|
|
|
@@ -192,6 +184,14 @@ export function runTestSuite({
|
|
|
192
184
|
|
|
193
185
|
cleanup({ repoRoot: cwd });
|
|
194
186
|
|
|
187
|
+
// A test that spawns a real CLI at the repository root inherits the real
|
|
188
|
+
// state directory, so its fixture telemetry can land in the operator's live
|
|
189
|
+
// signals tree — where the retro graduator counts it as recurrence evidence
|
|
190
|
+
// and files a ticket citing a fixture Story id. Fail the run that caused it
|
|
191
|
+
// rather than discovering it from that ticket.
|
|
192
|
+
const polluted = fixtureStreamGuard({ cwd });
|
|
193
|
+
if (polluted !== 0 && status === 0) status = polluted;
|
|
194
|
+
|
|
195
195
|
if (spawnError) {
|
|
196
196
|
throw spawnError;
|
|
197
197
|
}
|
|
@@ -43,6 +43,16 @@
|
|
|
43
43
|
* [--skip-validation] [--skip-sync]
|
|
44
44
|
* [--no-auto-merge]
|
|
45
45
|
* [--wait-merge | --no-wait-merge]
|
|
46
|
+
* [--merge-watch-mode <sync|async>]
|
|
47
|
+
*
|
|
48
|
+
* `--merge-watch-mode` (Story #4949) overrides `delivery.mergeWatch.mode` for
|
|
49
|
+
* one invocation, on the same explicit-wins-over-config precedence
|
|
50
|
+
* `--max-wait-seconds` uses, and the two compose. It exists because run
|
|
51
|
+
* topology is invisible from inside close: a solo delivery is cheapest waiting
|
|
52
|
+
* in the foreground, while the Nth close of a wave pays that wait as
|
|
53
|
+
* serialized dead time. The config default therefore stays `sync` and the
|
|
54
|
+
* orchestrator — the only party that knows N — passes `async` per close. An
|
|
55
|
+
* unrecognized value fails during option parsing, before any phase runs.
|
|
46
56
|
*
|
|
47
57
|
* Close-and-land is the DEFAULT for every run (Story #4428 introduced it as
|
|
48
58
|
* `--wait-merge`; `delivery.routing.closeAndLand` — default `true` — made it
|
|
@@ -85,11 +95,12 @@
|
|
|
85
95
|
* @see .agents/schemas/story-deliver-terminal.schema.json
|
|
86
96
|
*/
|
|
87
97
|
|
|
88
|
-
import {
|
|
98
|
+
import { parseSprintArgsTolerant } from './lib/cli-args.js';
|
|
89
99
|
import { runAsCli } from './lib/cli-utils.js';
|
|
90
100
|
import { formatCliError } from './lib/error-redactor.js';
|
|
91
101
|
import { Logger } from './lib/Logger.js';
|
|
92
102
|
import { emitTerminalFriction } from './lib/observability/runtime-friction.js';
|
|
103
|
+
import { resolveRunScopedConfig } from './lib/orchestration/run-scoped-config.js';
|
|
93
104
|
import {
|
|
94
105
|
failedTerminalFor,
|
|
95
106
|
gatesForFailedPhase,
|
|
@@ -123,12 +134,17 @@ export const enableAutoMerge = enableAutoMergeWith;
|
|
|
123
134
|
// `gatesForFailedPhase` now lives beside the envelope it feeds
|
|
124
135
|
// (`single-story-close/failed-terminal.js`); it is re-exported here so the
|
|
125
136
|
// CLI's public surface is unchanged by that move.
|
|
137
|
+
// `resolveRunScopedConfig` (Story #4891) is the run-scoped config pin the
|
|
138
|
+
// pipeline reads before its first phase; it is part of this CLI's surface for
|
|
139
|
+
// the same reason the sync helpers are — the runner reaches it only through a
|
|
140
|
+
// dynamic import, so this file is where it is statically visible.
|
|
126
141
|
export {
|
|
127
142
|
buildStoryReviewCrossRefBody,
|
|
128
143
|
buildSyncFailureCommentBody,
|
|
129
144
|
gatesForFailedPhase,
|
|
130
145
|
handleSyncFailure,
|
|
131
146
|
parsePrNumber,
|
|
147
|
+
resolveRunScopedConfig,
|
|
132
148
|
runStoryScopeReview,
|
|
133
149
|
};
|
|
134
150
|
|
|
@@ -145,13 +161,25 @@ export async function runSingleStoryClose(opts) {
|
|
|
145
161
|
* status rather than from a thrown/not-thrown distinction, so `pending`
|
|
146
162
|
* (resumable) is distinguishable from `blocked` (come look) without parsing
|
|
147
163
|
* stdout.
|
|
164
|
+
*
|
|
165
|
+
* The catch parses argv through the **non-throwing** wrapper (Story #4959).
|
|
166
|
+
* It used to call `parseSprintArgs()` — re-invoking the very parser that had
|
|
167
|
+
* just thrown, since `parseMergeWatchMode` made argv parsing fallible. The
|
|
168
|
+
* second throw escaped the handler, so an unparseable argv produced a bare
|
|
169
|
+
* stack trace with no envelope and no friction signal, on the surface whose
|
|
170
|
+
* whole contract is that every invocation emits exactly one envelope. An
|
|
171
|
+
* error handler may not depend on an operation already known to fail.
|
|
172
|
+
*
|
|
173
|
+
* A parse rejection carries no `closePhase`, so the envelope reports `init` —
|
|
174
|
+
* accurate: the runner rejected the flag before any phase ran, and nothing
|
|
175
|
+
* was mutated.
|
|
148
176
|
*/
|
|
149
177
|
async function main() {
|
|
150
178
|
try {
|
|
151
179
|
const outcome = await runSingleStoryClose();
|
|
152
180
|
return exitCodeForTerminal(outcome?.terminal ?? { status: 'failed' });
|
|
153
181
|
} catch (err) {
|
|
154
|
-
const terminal = failedTerminalFor(err,
|
|
182
|
+
const terminal = failedTerminalFor(err, parseSprintArgsTolerant().args);
|
|
155
183
|
if (!terminal) throw err;
|
|
156
184
|
// Mirror runAsCli's default error line (which this catch pre-empts) so the
|
|
157
185
|
// human-facing failure text is unchanged, then emit the envelope.
|
|
@@ -185,6 +213,10 @@ runAsCli(import.meta.url, main, {
|
|
|
185
213
|
['--wait-merge', 'Force the in-close merge wait.'],
|
|
186
214
|
['--no-wait-merge', 'Return as soon as the PR is open; do not wait.'],
|
|
187
215
|
['--max-wait-seconds <n>', 'Per-invocation merge-wait bound.'],
|
|
216
|
+
[
|
|
217
|
+
'--merge-watch-mode <sync|async>',
|
|
218
|
+
'Override delivery.mergeWatch.mode for this invocation only. `async` caps the merge wait to a short probe window and returns the resumable `pending` terminal instead of holding the foreground slot — pass it on every close of a multi-Story run. An invalid value exits non-zero before any phase runs.',
|
|
219
|
+
],
|
|
188
220
|
['--no-evidence', 'Do not reuse or write gate evidence stamps.'],
|
|
189
221
|
['--dry-run', 'Report the plan; mutate nothing.'],
|
|
190
222
|
],
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
import { parseArgs } from 'node:util';
|
|
40
|
-
import {
|
|
40
|
+
import { parseSprintArgsTolerant } from './lib/cli-args.js';
|
|
41
41
|
import { runAsCli } from './lib/cli-utils.js';
|
|
42
42
|
import { resolveConfig } from './lib/config-resolver.js';
|
|
43
43
|
import { formatCliError } from './lib/error-redactor.js';
|
|
@@ -66,6 +66,15 @@ const progress = Logger.createProgress('single-story-confirm-merge', {
|
|
|
66
66
|
stderr: true,
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* This CLI's own surface name — the `runAsCli` source, and the `emitter.tool`
|
|
71
|
+
* every friction record it emits carries. Single-homed so the two cannot
|
|
72
|
+
* drift: `emitTerminalFriction` defaults to the CLOSE CLI's name, so a record
|
|
73
|
+
* emitted from here without it is attributed to a CLI that never ran, and the
|
|
74
|
+
* retro roll-up sends the follow-up to the wrong surface.
|
|
75
|
+
*/
|
|
76
|
+
const CLI_SOURCE = 'single-story-confirm-merge';
|
|
77
|
+
|
|
69
78
|
/**
|
|
70
79
|
* Default `gh` facade for this CLI, bound to the merge wait's spawn-level
|
|
71
80
|
* timeout (Story #4710). This CLI is the resume surface async mode hands the
|
|
@@ -204,7 +213,7 @@ async function logConfirmResult(result, terminal, config) {
|
|
|
204
213
|
},
|
|
205
214
|
});
|
|
206
215
|
emitTerminalEnvelope(terminal, { config });
|
|
207
|
-
await emitTerminalFriction({ envelope: terminal, config });
|
|
216
|
+
await emitTerminalFriction({ envelope: terminal, tool: CLI_SOURCE, config });
|
|
208
217
|
return { success: terminal.status !== 'failed', result, terminal };
|
|
209
218
|
}
|
|
210
219
|
|
|
@@ -512,14 +521,21 @@ async function main() {
|
|
|
512
521
|
const outcome = await runConfirmMerge();
|
|
513
522
|
return exitCodeForTerminal(outcome?.terminal ?? { status: 'failed' });
|
|
514
523
|
} catch (err) {
|
|
515
|
-
|
|
524
|
+
// Non-throwing by construction (Story #4959): this used to call
|
|
525
|
+
// `parseSprintArgs()`, so a rejected `--merge-watch-mode` threw a second
|
|
526
|
+
// time here and escaped the handler — no envelope, no friction. Close
|
|
527
|
+
// carries the identical shape; both landing surfaces behave the same.
|
|
528
|
+
const { args, error: argvError } = parseSprintArgsTolerant();
|
|
529
|
+
const storyId = Number(args.storyId);
|
|
516
530
|
// No story id → a usage error; there is nothing to report an envelope
|
|
517
531
|
// about, so let runAsCli surface it as a plain fatal.
|
|
518
532
|
if (!Number.isInteger(storyId) || storyId <= 0) throw err;
|
|
519
533
|
const terminal = buildTerminalEnvelope({
|
|
520
534
|
storyId,
|
|
521
535
|
status: 'failed',
|
|
522
|
-
phase
|
|
536
|
+
// An argv rejection happens before any phase runs, so it reports at
|
|
537
|
+
// `init` rather than claiming a confirm-merge attempt that never began.
|
|
538
|
+
phase: argvError ? 'init' : 'confirm-merge',
|
|
523
539
|
failure: { reason: String(err?.message ?? err) },
|
|
524
540
|
nextCommand: NEXT_COMMANDS.recover(storyId),
|
|
525
541
|
elapsedSeconds: 0,
|
|
@@ -528,13 +544,13 @@ async function main() {
|
|
|
528
544
|
`[single-story-confirm-merge] Fatal error: ${formatCliError(err)}`,
|
|
529
545
|
);
|
|
530
546
|
emitTerminalEnvelope(terminal);
|
|
531
|
-
await emitTerminalFriction({ envelope: terminal });
|
|
547
|
+
await emitTerminalFriction({ envelope: terminal, tool: CLI_SOURCE });
|
|
532
548
|
return exitCodeForTerminal(terminal);
|
|
533
549
|
}
|
|
534
550
|
}
|
|
535
551
|
|
|
536
552
|
runAsCli(import.meta.url, main, {
|
|
537
|
-
source:
|
|
553
|
+
source: CLI_SOURCE,
|
|
538
554
|
propagateExitCode: true,
|
|
539
555
|
usage: {
|
|
540
556
|
invocation:
|
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
planFastForward,
|
|
62
62
|
} from './lib/orchestration/git-cleanup/phases/fast-forward.js';
|
|
63
63
|
import { verifyRemote } from './lib/orchestration/remote-verifier.js';
|
|
64
|
+
import { pinRunScopedConfig } from './lib/orchestration/run-scoped-config.js';
|
|
64
65
|
import {
|
|
65
66
|
acquireStoryLease,
|
|
66
67
|
releaseStoryLease,
|
|
@@ -735,6 +736,11 @@ export async function runSingleStoryInit({
|
|
|
735
736
|
standalone: true,
|
|
736
737
|
storyBranch,
|
|
737
738
|
baseBranch,
|
|
739
|
+
// The write half of the run-scoped config pin. `baseBranch` above is the
|
|
740
|
+
// legacy field close still reads as a fallback; this block is the
|
|
741
|
+
// registry-driven form a second run-scoped key joins without a second
|
|
742
|
+
// mechanism (`lib/orchestration/run-scoped-config.js`).
|
|
743
|
+
runScopedConfig: pinRunScopedConfig(config),
|
|
738
744
|
storyTitle: story.title,
|
|
739
745
|
worktreeEnabled: runtime.worktreeEnabled,
|
|
740
746
|
workCwd,
|
|
@@ -805,6 +811,7 @@ export function renderSingleStoryInitComment(result) {
|
|
|
805
811
|
standalone: true,
|
|
806
812
|
storyBranch: result.storyBranch,
|
|
807
813
|
baseBranch: result.baseBranch,
|
|
814
|
+
runScopedConfig: result.runScopedConfig,
|
|
808
815
|
worktreeEnabled: result.worktreeEnabled,
|
|
809
816
|
workCwd: result.workCwd,
|
|
810
817
|
worktreeCreated: result.worktreeCreated,
|