mandrel 2.36.0 → 2.38.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/README.md +16 -16
- package/.agents/agents/plan-critic.md +1 -1
- package/.agents/docs/SDLC.md +40 -40
- package/.agents/docs/configuration.md +41 -16
- package/.agents/docs/execution-reference.md +3 -3
- package/.agents/docs/quality-gates.md +2 -2
- package/.agents/docs/workflows.md +6 -6
- package/.agents/instructions.md +5 -5
- package/.agents/rules/changelog-style.md +3 -3
- package/.agents/rules/ci-remediation.md +1 -1
- package/.agents/rules/git-conventions-reference.md +4 -4
- package/.agents/rules/testing-standards.md +56 -0
- package/.agents/schemas/agentrc.schema.json +9 -9
- package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +1 -1
- package/.agents/scripts/apply-quality-bootstrap.js +1 -1
- package/.agents/scripts/audit-to-stories.js +2 -2
- package/.agents/scripts/boot-sweep.js +7 -1
- package/.agents/scripts/check-context-budget.js +195 -103
- package/.agents/scripts/check-cyclomatic.js +112 -42
- package/.agents/scripts/check-doc-links.js +32 -4
- package/.agents/scripts/check-generated-validator.js +202 -0
- package/.agents/scripts/check-lifecycle-lint.js +2 -68
- package/.agents/scripts/check-schema-references.js +2 -28
- package/.agents/scripts/check-test-temp-hygiene.js +1 -1
- package/.agents/scripts/check-workflow-cli-lint.js +1 -1
- package/.agents/scripts/deliver-light.js +10 -10
- package/.agents/scripts/deliver-recover.js +1 -1
- package/.agents/scripts/drain-pending-cleanup.js +19 -2
- package/.agents/scripts/evidence-gate.js +1 -1
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +5 -5
- package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +1 -1
- package/.agents/scripts/lib/baselines/components.js +32 -2
- package/.agents/scripts/lib/baselines/env-overrides.js +1 -1
- package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
- package/.agents/scripts/lib/bdd-scenario-budget.js +1 -1
- package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
- package/.agents/scripts/lib/bdd-step-index.js +1 -1
- package/.agents/scripts/lib/bootstrap/branch-protection.js +2 -2
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/bootstrap/install-ledger.js +1 -1
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +1 -1
- package/.agents/scripts/lib/bootstrap/prompt.js +1 -1
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +2 -2
- package/.agents/scripts/lib/checks/core-bare-clean.js +1 -1
- package/.agents/scripts/lib/checks/loop-health.js +1 -1
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +1 -1
- package/.agents/scripts/lib/cli-args.js +1 -3
- package/.agents/scripts/lib/close-validation/gates.js +1 -1
- package/.agents/scripts/lib/close-validation/process.js +1 -1
- package/.agents/scripts/lib/command-header.js +1 -1
- package/.agents/scripts/lib/config/delivery-routing.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/sync-agentrc.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-delivery.js +3 -3
- package/.agents/scripts/lib/config-settings-schema-quality.js +6 -6
- package/.agents/scripts/lib/config-settings-schema.js +49 -9
- package/.agents/scripts/lib/coverage-baseline.js +2 -2
- package/.agents/scripts/lib/cpu-pool.js +90 -10
- package/.agents/scripts/lib/crap-utils.js +6 -2
- package/.agents/scripts/lib/cyclomatic-ceiling.js +28 -6
- package/.agents/scripts/lib/cyclomatic-scope.js +144 -0
- package/.agents/scripts/lib/dependency-version.js +86 -0
- package/.agents/scripts/lib/duplicate-search.js +2 -2
- package/.agents/scripts/lib/errors/index.js +1 -1
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +2 -2
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +5 -5
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +2 -2
- package/.agents/scripts/lib/findings/classify-finding.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +12 -12
- package/.agents/scripts/lib/findings/route-finding.js +2 -2
- package/.agents/scripts/lib/generated/agentrc-validator.js +16 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
- package/.agents/scripts/lib/install-cmd-parser.js +1 -1
- package/.agents/scripts/lib/knip-entry-sync.js +1 -57
- package/.agents/scripts/lib/label-constants.js +2 -2
- package/.agents/scripts/lib/label-taxonomy.js +1 -1
- package/.agents/scripts/lib/maintainability-utils.js +108 -10
- package/.agents/scripts/lib/observability/source-classifier.js +1 -0
- package/.agents/scripts/lib/onboard/init-tail.js +4 -4
- package/.agents/scripts/lib/onboard/scaffold-docs.js +2 -2
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
- package/.agents/scripts/lib/orchestration/code-review.js +4 -4
- package/.agents/scripts/lib/orchestration/complexity-gate.js +5 -5
- package/.agents/scripts/lib/orchestration/deliver-recover.js +3 -3
- package/.agents/scripts/lib/orchestration/docs-digest.js +3 -3
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +2 -2
- package/.agents/scripts/lib/orchestration/light-backstop.js +1 -1
- package/.agents/scripts/lib/orchestration/light-escalation.js +6 -6
- package/.agents/scripts/lib/orchestration/light-suitability.js +19 -19
- package/.agents/scripts/lib/orchestration/plan-context.js +4 -4
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +2 -2
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +2 -2
- package/.agents/scripts/lib/orchestration/plan-metrics.js +1 -1
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +1 -1
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +3 -3
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +6 -6
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +86 -25
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +3 -3
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +6 -6
- package/.agents/scripts/lib/orchestration/planning/authoring-context.js +7 -7
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +1 -1
- package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +3 -3
- package/.agents/scripts/lib/orchestration/pr-watch.js +1 -30
- package/.agents/scripts/lib/orchestration/reassert-status-column.js +2 -2
- package/.agents/scripts/lib/orchestration/remote-verifier.js +2 -2
- package/.agents/scripts/lib/orchestration/resolve-stories.js +4 -4
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +3 -3
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/native.js +9 -2
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +2 -2
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +3 -3
- package/.agents/scripts/lib/orchestration/run-epilogue.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +5 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +52 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +3 -3
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +2 -5
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +14 -12
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-lease.js +34 -9
- package/.agents/scripts/lib/orchestration/ticket-validator.js +1 -1
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +6 -4
- package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
- package/.agents/scripts/lib/preflight-runner.js +3 -3
- package/.agents/scripts/lib/qa/qa-session.js +1 -1
- package/.agents/scripts/lib/runtime-deps/manifest.js +1 -7
- package/.agents/scripts/lib/runtime-deps/scan-imports.js +3 -58
- package/.agents/scripts/lib/signals/schema.js +1 -1
- package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +284 -25
- package/.agents/scripts/lib/skills/walk-skill-files.js +1 -1
- package/.agents/scripts/lib/source-text/strip-js-comments.js +115 -0
- package/.agents/scripts/lib/story-adjacency.js +2 -2
- package/.agents/scripts/lib/templates/decomposer-prompts.js +1 -1
- package/.agents/scripts/lib/test-isolate/runner.js +3 -3
- package/.agents/scripts/lib/test-runner-contract.js +14 -6
- package/.agents/scripts/lib/test-tiers.js +135 -18
- package/.agents/scripts/lib/ticket-body-sections.js +1 -1
- package/.agents/scripts/lib/transpile.js +16 -3
- package/.agents/scripts/lib/util/concurrent-map.js +1 -1
- package/.agents/scripts/lib/util/parse-id-list.js +1 -1
- package/.agents/scripts/lib/wave-runner/live-probe.js +4 -4
- package/.agents/scripts/lib/wave-runner/ready-set.js +1 -1
- package/.agents/scripts/lib/workflow-closure.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +170 -32
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +2 -5
- package/.agents/scripts/lint-issue-body.js +3 -3
- package/.agents/scripts/plan-context.js +4 -4
- package/.agents/scripts/plan-critics.js +2 -2
- package/.agents/scripts/plan-persist.js +2 -2
- package/.agents/scripts/plan-run-epilogue.js +2 -2
- package/.agents/scripts/pr-watch-with-update.js +305 -137
- package/.agents/scripts/providers/github/board-add.js +1 -1
- package/.agents/scripts/providers/github/errors.js +1 -1
- package/.agents/scripts/providers/github/tickets.js +114 -21
- package/.agents/scripts/quality-preview.js +162 -70
- package/.agents/scripts/resolve-stories.js +3 -3
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/run-lint.js +1 -1
- package/.agents/scripts/run-test-profile.js +8 -5
- package/.agents/scripts/run-tests.js +79 -14
- package/.agents/scripts/single-story-close.js +2 -4
- package/.agents/scripts/single-story-init.js +10 -6
- package/.agents/scripts/stories-wave-tick.js +8 -8
- package/.agents/skills/core/idea-refinement/SKILL.md +5 -5
- package/.agents/skills/core/scope-triage/SKILL.md +3 -3
- package/.agents/skills/skills.index.json +1 -1
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -1
- package/.agents/workflows/audit-data-model.md +1 -1
- package/.agents/workflows/audit-documentation.md +2 -2
- package/.agents/workflows/audit-to-stories.md +11 -11
- package/.agents/workflows/git-deliver.md +4 -4
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/audit-lens-core.md +1 -1
- package/.agents/workflows/helpers/code-review.md +4 -4
- package/.agents/workflows/helpers/deliver-light.md +26 -26
- package/.agents/workflows/helpers/deliver-reference.md +15 -7
- package/.agents/workflows/helpers/deliver-story-reference.md +9 -4
- package/.agents/workflows/helpers/deliver-story.md +6 -7
- package/.agents/workflows/helpers/diagnose.md +2 -2
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/helpers/plan-reference.md +9 -9
- package/.agents/workflows/helpers/qa-core.md +8 -8
- package/.agents/workflows/helpers/worktree-lifecycle.md +6 -6
- package/.agents/workflows/{deliver.md → mandrel-deliver.md} +15 -19
- package/.agents/workflows/{plan.md → mandrel-plan.md} +9 -9
- package/.agents/workflows/memory-consolidate.md +4 -4
- package/.agents/workflows/prototype.md +3 -3
- package/.agents/workflows/qa-assist.md +12 -12
- package/.agents/workflows/qa-explore.md +6 -6
- package/.agents/workflows/qa-run.md +1 -1
- package/README.md +7 -7
- package/docs/CHANGELOG.md +43 -0
- package/lib/cli/doctor.js +1 -1
- package/lib/cli/init.js +1 -1
- package/lib/cli/migrate.js +100 -94
- package/lib/migrations/helpers/retire-agentrc-key.js +200 -0
- package/lib/migrations/index.js +32 -33
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +21 -80
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +15 -87
- package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +13 -71
- package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +1 -1
- package/lib/migrations/steps/2.20.0-retire-codebase-snapshot.js +13 -101
- package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +13 -100
- package/package.json +7 -3
- package/.agents/scripts/lib/audit-suite/frontmatter-lint.js +0 -32
- package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +0 -37
- package/.agents/scripts/lib/cli/parse-numeric.js +0 -60
- package/.agents/scripts/lib/close-validation/telemetry.js +0 -79
- package/.agents/scripts/lib/orchestration/label-transitions.js +0 -44
- package/.agents/scripts/lib/orchestration/parked-follow-ons.js +0 -147
- package/.agents/scripts/lib/orchestration/phase-runner.js +0 -88
- package/.agents/scripts/lib/orchestration/recut.js +0 -56
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* phases/authoring-context.js — emit-context phase.
|
|
3
3
|
*
|
|
4
4
|
* Builds the authoring context the host LLM (or the
|
|
5
|
-
* `/plan` author step) needs to write the Tech Spec.
|
|
5
|
+
* `/mandrel-plan` author step) needs to write the Tech Spec.
|
|
6
6
|
* Returns a plain JSON-serialisable object; never hits the network beyond
|
|
7
7
|
* the provider call needed to load the Epic.
|
|
8
8
|
*/
|
|
@@ -31,12 +31,12 @@ import { buildMemoryPoolAdvisory } from './memory-pool-advisory.js';
|
|
|
31
31
|
* pull-on-demand, mirroring the Story #4324 delivery-children cutover).
|
|
32
32
|
*
|
|
33
33
|
* Ensures a docs digest exists at the **same** per-Epic temp path the
|
|
34
|
-
* `/deliver` story sub-agents already consume
|
|
34
|
+
* `/mandrel-deliver` story sub-agents already consume
|
|
35
35
|
* (`<tempRoot>/epic-<seedIssueId>/docs-digest.md`) via the shared
|
|
36
36
|
* `ensureDocsDigest` export in `docs-digest.js` — one generator, one file,
|
|
37
37
|
* reused across the planning and delivery surfaces for the same Epic. The
|
|
38
38
|
* envelope carries only the digest path, not embedded doc content; the
|
|
39
|
-
* planner (host LLM driving the `/plan` author step) reads the digest
|
|
39
|
+
* planner (host LLM driving the `/mandrel-plan` author step) reads the digest
|
|
40
40
|
* and pulls a full file/section on demand when it bears on the decision.
|
|
41
41
|
*
|
|
42
42
|
* Returns `null` — a silent no-op — when `project.docsContextFiles` is not
|
|
@@ -80,7 +80,7 @@ async function buildPlanningDocsContext({ seedIssueId, settings, cwd }) {
|
|
|
80
80
|
* Story #4977 — the scan itself stays uncapped (a faithful `.feature`
|
|
81
81
|
* index), but the envelope-bound result is truncated to
|
|
82
82
|
* `BDD_SCENARIOS_BYTE_BUDGET` via `capBddScenarios` so a mature Gherkin
|
|
83
|
-
* corpus cannot alone consume the `/plan` context-envelope ceiling. Callers
|
|
83
|
+
* corpus cannot alone consume the `/mandrel-plan` context-envelope ceiling. Callers
|
|
84
84
|
* needing the raw count read `totalScenarios` vs `includedScenarios`.
|
|
85
85
|
*
|
|
86
86
|
* @returns {ReturnType<typeof capBddScenarios>}
|
|
@@ -97,7 +97,7 @@ function scanBddScenariosBestEffort() {
|
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
99
|
* Build the authoring context the host LLM (or the
|
|
100
|
-
* `/plan` author step) needs to write the Tech Spec.
|
|
100
|
+
* `/mandrel-plan` author step) needs to write the Tech Spec.
|
|
101
101
|
*
|
|
102
102
|
* `docsContext` is digest-first (Story #4433): a pointer at the per-Epic
|
|
103
103
|
* docs digest rather than embedded doc content, `null` when
|
|
@@ -133,7 +133,7 @@ export async function buildAuthoringContext(
|
|
|
133
133
|
const githubCfg = opts.github ?? null;
|
|
134
134
|
|
|
135
135
|
// Story #4952 — these five gathers share no data, so they run under bounded
|
|
136
|
-
// concurrency instead of five sequential awaits on the interactive `/plan`
|
|
136
|
+
// concurrency instead of five sequential awaits on the interactive `/mandrel-plan`
|
|
137
137
|
// path. `concurrentMap` preserves input order, so the destructuring is
|
|
138
138
|
// positional and the produced context is identical to the serial build:
|
|
139
139
|
//
|
|
@@ -149,7 +149,7 @@ export async function buildAuthoringContext(
|
|
|
149
149
|
// memory corpus is delivery retrospectives whose subject IS a delivered
|
|
150
150
|
// Story — and its directory (`~/.claude/projects/<repo>/memory/`) never
|
|
151
151
|
// resolved, because harness project dirs are cwd-slugs. This renders no
|
|
152
|
-
// per-entry verdict at all: it stats and counts, and the `/plan` spine
|
|
152
|
+
// per-entry verdict at all: it stats and counts, and the `/mandrel-plan` spine
|
|
153
153
|
// surfaces `recommend` at Gate #1. Filesystem-only and total;
|
|
154
154
|
// 5. Story #2554 — open meta feedback issues, so retro signals are routed
|
|
155
155
|
// into durable substrates rather than lost in chat. Best-effort:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* memory-pool-advisory.js — the `/plan` Phase 0 memory-hygiene advisory.
|
|
2
|
+
* memory-pool-advisory.js — the `/mandrel-plan` Phase 0 memory-hygiene advisory.
|
|
3
3
|
*
|
|
4
4
|
* Replaces the retired memory-freshness pre-flight (Story #2557 / #4414) in
|
|
5
5
|
* the same slot, fixing both of that design's defects:
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* replaced by `-` — so the old path never resolved in any consumer and
|
|
11
11
|
* the scan was a silent no-op everywhere.
|
|
12
12
|
* 2. **A named consumer.** The retired scanner emitted a per-entry staleness
|
|
13
|
-
* verdict nothing read. This emits one advisory the `/plan` spine
|
|
13
|
+
* verdict nothing read. This emits one advisory the `/mandrel-plan` spine
|
|
14
14
|
* surfaces at Gate #1, recommending `/memory-consolidate`.
|
|
15
15
|
*
|
|
16
16
|
* It also drops the semantic that made the old scanner unfixable: it renders
|
|
@@ -131,7 +131,7 @@ function countEntries({ poolDir, fsImpl }) {
|
|
|
131
131
|
* Build the `memoryPoolAdvisory` envelope field.
|
|
132
132
|
*
|
|
133
133
|
* Advisory only — it carries **no routing authority**, mirroring
|
|
134
|
-
* `deliverLightSuggestion`. The `/plan` spine surfaces `recommend` at Gate #1;
|
|
134
|
+
* `deliverLightSuggestion`. The `/mandrel-plan` spine surfaces `recommend` at Gate #1;
|
|
135
135
|
* nothing auto-runs, and nothing here mutates the operator's memory store.
|
|
136
136
|
*
|
|
137
137
|
* @param {object} [opts]
|
|
@@ -50,35 +50,6 @@ import { applyBehindUpdate } from './behind-recovery.js';
|
|
|
50
50
|
* values collapse to `'skipped'` so any future GitHub state we haven't
|
|
51
51
|
* enumerated still maps into the vocabulary.
|
|
52
52
|
*/
|
|
53
|
-
/**
|
|
54
|
-
* The raw check-state tokens `normalizeCheckState` recognizes (lowercased).
|
|
55
|
-
* A token absent from this set is one we have NOT enumerated — the watch
|
|
56
|
-
* path collapses it to `'skipped'` (validate-anything), but a fail-closed
|
|
57
|
-
* consumer (the auto-merge arming probe) must treat it as unknown-therefore-
|
|
58
|
-
* blocking rather than trust the `'skipped'` collapse. Exported so that
|
|
59
|
-
* stricter consumer lives here as the single vocabulary owner.
|
|
60
|
-
*/
|
|
61
|
-
export const RECOGNIZED_CHECK_STATES = Object.freeze(
|
|
62
|
-
new Set([
|
|
63
|
-
'',
|
|
64
|
-
'pending',
|
|
65
|
-
'queued',
|
|
66
|
-
'in_progress',
|
|
67
|
-
'requested',
|
|
68
|
-
'waiting',
|
|
69
|
-
'success',
|
|
70
|
-
'completed',
|
|
71
|
-
'failure',
|
|
72
|
-
'startup_failure',
|
|
73
|
-
'neutral',
|
|
74
|
-
'cancelled',
|
|
75
|
-
'timed_out',
|
|
76
|
-
'action_required',
|
|
77
|
-
'stale',
|
|
78
|
-
'skipped',
|
|
79
|
-
]),
|
|
80
|
-
);
|
|
81
|
-
|
|
82
53
|
export function normalizeCheckState(raw) {
|
|
83
54
|
const v = String(raw ?? '')
|
|
84
55
|
.trim()
|
|
@@ -325,7 +296,7 @@ export function allTerminal(outcomes) {
|
|
|
325
296
|
* red check): a `'still-running'` map means "re-arm the watch / hand off
|
|
326
297
|
* to `/loop`," never "the change is broken."
|
|
327
298
|
*/
|
|
328
|
-
|
|
299
|
+
const STILL_RUNNING = 'still-running';
|
|
329
300
|
|
|
330
301
|
/**
|
|
331
302
|
* Promote any leftover `'pending'` outcomes to the schema-valid
|
|
@@ -38,14 +38,14 @@ import { ColumnSync, columnForLabels } from './column-sync.js';
|
|
|
38
38
|
* long enough to outlast the typical GitHub built-in workflow fire
|
|
39
39
|
* timing without ballooning the close-path wall clock.
|
|
40
40
|
*/
|
|
41
|
-
|
|
41
|
+
const DEFAULT_POLL_ATTEMPTS = 4;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Default delay between drift checks (ms). Five seconds is a sweet
|
|
45
45
|
* spot: short enough that the operator doesn't perceive the close
|
|
46
46
|
* path as hung, long enough that one re-fire usually wins the race.
|
|
47
47
|
*/
|
|
48
|
-
|
|
48
|
+
const DEFAULT_POLL_DELAY_MS = 5000;
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Production sleep function. Tests inject `sleepFn: () => Promise.resolve()`
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* remote-verifier.js — deterministic "is there a live, pushable remote?"
|
|
4
4
|
* evidence for the delivery entry seams. Issue #4483.
|
|
5
5
|
*
|
|
6
|
-
* `/deliver` could silently shortcut the entire orchestration — building
|
|
6
|
+
* `/mandrel-deliver` could silently shortcut the entire orchestration — building
|
|
7
7
|
* the delivery inline and committing to local `main` without pushing —
|
|
8
8
|
* when the driving agent *perceived* the environment had no live GitHub
|
|
9
9
|
* remote. The judgment was vibes, not fact. This module gives the entry
|
|
10
|
-
* seams (`single-story-init.js` for v2 `/deliver`) a verified
|
|
10
|
+
* seams (`single-story-init.js` for v2 `/mandrel-deliver`) a verified
|
|
11
11
|
* probe result to record in their envelopes so the workflow can branch on
|
|
12
12
|
* `remoteVerified: true|false` deterministically: use the remote, or
|
|
13
13
|
* transition to `agent::blocked` quoting the probe output — never a
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* lib/orchestration/resolve-stories.js — resolve a set of Story ids into the
|
|
3
3
|
* `{ stories, dag, done }` envelope the delivery scheduler consumes.
|
|
4
4
|
*
|
|
5
|
-
* This is the ONE resolution step for `/deliver`. It generalizes the
|
|
5
|
+
* This is the ONE resolution step for `/mandrel-deliver`. It generalizes the
|
|
6
6
|
* envelope shaping proven by the retired `resolve-plan-run.js` and fixes the
|
|
7
7
|
* two defects that shipped with it:
|
|
8
8
|
*
|
|
@@ -77,7 +77,7 @@ export function toStoryRecord(issue, requestedId) {
|
|
|
77
77
|
if (!labels.includes(TYPE_LABELS.STORY)) {
|
|
78
78
|
throw new Error(
|
|
79
79
|
`[resolve-stories] Issue #${id} is not a Story (labels: ${labels.join(', ') || 'none'}). ` +
|
|
80
|
-
`/deliver accepts ${TYPE_LABELS.STORY} tickets only — close it or re-plan it as a v2 Story.`,
|
|
80
|
+
`/mandrel-deliver accepts ${TYPE_LABELS.STORY} tickets only — close it or re-plan it as a v2 Story.`,
|
|
81
81
|
);
|
|
82
82
|
}
|
|
83
83
|
const body = String(issue?.body ?? '');
|
|
@@ -351,7 +351,7 @@ export function buildStoriesEnvelope({
|
|
|
351
351
|
return {
|
|
352
352
|
kind: 'stories',
|
|
353
353
|
// `dispatchMode` (Story #4722, #4736, #4829): the resolver reports the
|
|
354
|
-
// per-Story execution mode so `/deliver` reads one field — `inline` (run
|
|
354
|
+
// per-Story execution mode so `/mandrel-deliver` reads one field — `inline` (run
|
|
355
355
|
// deliver-story in the router's own session: no story-worker /
|
|
356
356
|
// acceptance-critic sub-agent boots) or `subagent` (the conservative
|
|
357
357
|
// default). Model-side fan-out only; close gates are untouched.
|
|
@@ -385,7 +385,7 @@ export function buildStoriesEnvelope({
|
|
|
385
385
|
/**
|
|
386
386
|
* Parse and validate the `--ids` list, expanding any `A-B` dash range.
|
|
387
387
|
*
|
|
388
|
-
* A contiguous span is how an operator names a plan run — `/deliver 4922 -
|
|
388
|
+
* A contiguous span is how an operator names a plan run — `/mandrel-deliver 4922 -
|
|
389
389
|
* 4926` — so the range is expanded here rather than transcribed by the host.
|
|
390
390
|
* `stories-wave-tick.js --stories` reads through this same function, which is
|
|
391
391
|
* what keeps the sequencing set identical to the resolved one.
|
|
@@ -57,7 +57,7 @@ export const CODEX_REMEDIATIONS = Object.freeze({
|
|
|
57
57
|
*
|
|
58
58
|
* Exported so tests can extend the list without monkey-patching `os`.
|
|
59
59
|
*/
|
|
60
|
-
|
|
60
|
+
const DEFAULT_PLUGIN_MARKERS = Object.freeze([
|
|
61
61
|
path.join(os.homedir(), '.claude', 'plugins', 'codex-plugin-cc'),
|
|
62
62
|
path.join(os.homedir(), '.claude', 'plugins', 'openai', 'codex-plugin-cc'),
|
|
63
63
|
]);
|
|
@@ -194,7 +194,7 @@ export function parseCodexFindings(rawStdout) {
|
|
|
194
194
|
* @param {{ baseRef: string, headRef: string, depth?: import('./types.js').ReviewDepth }} args
|
|
195
195
|
* @returns {string}
|
|
196
196
|
*/
|
|
197
|
-
|
|
197
|
+
function buildCodexReviewPrompt({ baseRef, headRef, depth }) {
|
|
198
198
|
return (
|
|
199
199
|
`/codex:review --base ${baseRef} --head ${headRef} --wait ` +
|
|
200
200
|
`${renderDepthDirective(depth)}`
|
|
@@ -214,7 +214,7 @@ export function buildCodexReviewPrompt({ baseRef, headRef, depth }) {
|
|
|
214
214
|
* @param {{ baseRef: string, headRef: string, depth?: import('./types.js').ReviewDepth }} args
|
|
215
215
|
* @returns {{ status: number, stdout: string, stderr: string }}
|
|
216
216
|
*/
|
|
217
|
-
|
|
217
|
+
function defaultInvokeCodexReview({ baseRef, headRef, depth }) {
|
|
218
218
|
const cliArgs = [
|
|
219
219
|
'--print',
|
|
220
220
|
buildCodexReviewPrompt({ baseRef, headRef, depth }),
|
|
@@ -102,7 +102,7 @@ export function renderFinding(finding) {
|
|
|
102
102
|
* @param {ReadonlyArray<string>} messages
|
|
103
103
|
* @returns {string[]} lines to append (empty when no messages)
|
|
104
104
|
*/
|
|
105
|
-
|
|
105
|
+
function renderManualPromptsSection(messages) {
|
|
106
106
|
const filtered = Array.isArray(messages)
|
|
107
107
|
? messages.filter((m) => typeof m === 'string' && m.trim().length > 0)
|
|
108
108
|
: [];
|
|
@@ -267,8 +267,14 @@ function isJsMaintainabilityFile(relPath) {
|
|
|
267
267
|
* `reportFn` forces the serial path (the injected scorer cannot cross the
|
|
268
268
|
* worker boundary).
|
|
269
269
|
*
|
|
270
|
+
* `serialThreshold` overrides the pool-vs-serial cutover for one call. It is
|
|
271
|
+
* the seam the parity test uses to drive the pooled branch: Story #5109
|
|
272
|
+
* retuned `POOL_SERIAL_THRESHOLD` from 8 to 256 against measured data, and a
|
|
273
|
+
* fixture sized to clear the old number would otherwise have gone quietly
|
|
274
|
+
* serial while still claiming to cover the pool.
|
|
275
|
+
*
|
|
270
276
|
* @param {string[]} changedFiles
|
|
271
|
-
* @param {{ reportFn?: Function, classifier?: Function, runOnPoolFn?: typeof runOnPool, headRef?: string|null, gitSpawnFn?: typeof gitSpawn, readHeadSourceFn?: typeof readHeadSource }} [deps]
|
|
277
|
+
* @param {{ reportFn?: Function, classifier?: Function, runOnPoolFn?: typeof runOnPool, headRef?: string|null, gitSpawnFn?: typeof gitSpawn, readHeadSourceFn?: typeof readHeadSource, serialThreshold?: number }} [deps]
|
|
272
278
|
* @returns {Promise<{ totalFiles: number, jsFiles: number, maintainability: object[], criticalFindings: Finding[], mediumFindings: Finding[] }>}
|
|
273
279
|
*/
|
|
274
280
|
export async function analyzeChangedFiles(
|
|
@@ -280,6 +286,7 @@ export async function analyzeChangedFiles(
|
|
|
280
286
|
headRef = null,
|
|
281
287
|
gitSpawnFn = gitSpawn,
|
|
282
288
|
readHeadSourceFn = readHeadSource,
|
|
289
|
+
serialThreshold = SERIAL_THRESHOLD,
|
|
283
290
|
} = {},
|
|
284
291
|
) {
|
|
285
292
|
const results = {
|
|
@@ -308,7 +315,7 @@ export async function analyzeChangedFiles(
|
|
|
308
315
|
const customReportFn = reportFn != null;
|
|
309
316
|
|
|
310
317
|
// Serial path: small batches, or whenever a caller injects its own scorer.
|
|
311
|
-
if (jsFiles.length <
|
|
318
|
+
if (jsFiles.length < serialThreshold || customReportFn) {
|
|
312
319
|
for (let i = 0; i < jsFiles.length; i += 1) {
|
|
313
320
|
const relPath = jsFiles[i];
|
|
314
321
|
const source = sources[i];
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
*
|
|
29
29
|
* @type {Readonly<Record<ReviewDepth, string>>}
|
|
30
30
|
*/
|
|
31
|
-
|
|
31
|
+
const DEPTH_DIRECTIVES = Object.freeze({
|
|
32
32
|
light:
|
|
33
33
|
'Review depth: LIGHT. Run a single pass focused on spec adherence over the ' +
|
|
34
34
|
'changed surface — confirm the change matches its stated intent. Reduce the ' +
|
|
@@ -54,7 +54,7 @@ export const DEPTH_DIRECTIVES = Object.freeze({
|
|
|
54
54
|
* @param {unknown} depth
|
|
55
55
|
* @returns {ReviewDepth}
|
|
56
56
|
*/
|
|
57
|
-
|
|
57
|
+
function normalizeDepth(depth) {
|
|
58
58
|
return depth === 'light' || depth === 'deep' ? depth : 'standard';
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -59,7 +59,7 @@ export const SECURITY_REVIEW_REMEDIATIONS = Object.freeze({
|
|
|
59
59
|
* @param {{ spawnFn?: typeof spawnSync }} [opts]
|
|
60
60
|
* @returns {boolean}
|
|
61
61
|
*/
|
|
62
|
-
|
|
62
|
+
function defaultProbeClaudeCli(opts = {}) {
|
|
63
63
|
const spawnFn = opts.spawnFn ?? spawnSync;
|
|
64
64
|
try {
|
|
65
65
|
const result = spawnFn('claude', ['--version'], {
|
|
@@ -162,7 +162,7 @@ export function parseSecurityReviewFindings(rawStdout) {
|
|
|
162
162
|
* (Story #3937) so a high-risk Epic instructs the model toward a deeper
|
|
163
163
|
* second-pass review while a low-risk one keeps it light.
|
|
164
164
|
*/
|
|
165
|
-
|
|
165
|
+
const SECURITY_REVIEW_INVOKE_PROMPT =
|
|
166
166
|
'Run /security-review against the diff `{baseRef}`...`{headRef}` ' +
|
|
167
167
|
'for {scopeLabel} #{ticketId}. {depthDirective} After the review, emit ' +
|
|
168
168
|
'ONLY a JSON array of findings on stdout with this exact shape:\n\n' +
|
|
@@ -208,7 +208,7 @@ export function buildSecurityReviewPrompt(input) {
|
|
|
208
208
|
* @param {ReviewInput} input
|
|
209
209
|
* @returns {{ status: number, stdout: string, stderr: string }}
|
|
210
210
|
*/
|
|
211
|
-
|
|
211
|
+
function defaultInvokeSecurityReview(input) {
|
|
212
212
|
const prompt = buildSecurityReviewPrompt(input);
|
|
213
213
|
const result = spawnSync('claude', ['--print', prompt], {
|
|
214
214
|
encoding: 'utf-8',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* run-epilogue.js — real per-run closeout for `/deliver --run`.
|
|
2
|
+
* run-epilogue.js — real per-run closeout for `/mandrel-deliver --run`.
|
|
3
3
|
*
|
|
4
4
|
* After the last Story in a multi-Story plan-run lands, this module:
|
|
5
5
|
* 1. Selects the cross-Story audit lens roster over the combined landed
|
|
@@ -100,7 +100,7 @@ export function planRunEpilogue({ planRunId, stories } = {}) {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
// Positional `/deliver 101 102` has no plan-run label. Synthesize a
|
|
103
|
+
// Positional `/mandrel-deliver 101 102` has no plan-run label. Synthesize a
|
|
104
104
|
// stable adhoc id from the sorted Story set so the epilogue still
|
|
105
105
|
// anchors comments / audit roster without requiring `--run`.
|
|
106
106
|
const effectiveRunId =
|
|
@@ -83,7 +83,7 @@ export async function runBaseSyncPhase({
|
|
|
83
83
|
: syncResult.stderr
|
|
84
84
|
? `: ${syncResult.stderr.slice(0, 200)}`
|
|
85
85
|
: '') +
|
|
86
|
-
`. Story transitioned to ${AGENT_LABELS.BLOCKED}; resolve in ${syncCwd} and re-run \`/deliver ${storyId}\`.`,
|
|
86
|
+
`. Story transitioned to ${AGENT_LABELS.BLOCKED}; resolve in ${syncCwd} and re-run \`/mandrel-deliver ${storyId}\`.`,
|
|
87
87
|
);
|
|
88
88
|
}
|
|
89
89
|
progress('SYNC', `✅ Synced from origin/${baseBranch} (${syncResult.kind}).`);
|
|
@@ -182,7 +182,7 @@ export function buildSyncFailureCommentBody({
|
|
|
182
182
|
const lines = [
|
|
183
183
|
`### ${heading}`,
|
|
184
184
|
'',
|
|
185
|
-
'`/deliver` close-validation passed, but the pre-push',
|
|
185
|
+
'`/mandrel-deliver` close-validation passed, but the pre-push',
|
|
186
186
|
`sync against \`origin/${baseBranch}\` could not complete. The Story has`,
|
|
187
187
|
`been transitioned to \`agent::blocked\`. To resume:`,
|
|
188
188
|
'',
|
|
@@ -343,7 +343,7 @@ function scanForBreaking(text, readHeaderBang) {
|
|
|
343
343
|
* writing the commit that does the breaking.
|
|
344
344
|
* - **The Story body** — the same footer, written as its own line anywhere
|
|
345
345
|
* in the Story issue (the `## Spec` block is the natural home). This is
|
|
346
|
-
* the declarative path: `/plan` can state the break up front and close
|
|
346
|
+
* the declarative path: `/mandrel-plan` can state the break up front and close
|
|
347
347
|
* propagates it even when no individual commit remembered the footer.
|
|
348
348
|
* Only the footer form counts; prose describing a break does not, because
|
|
349
349
|
* a keyword-free sentence is exactly what release-please cannot parse.
|
|
@@ -191,7 +191,11 @@ function synthesizeSubject({ description, storyId, commitMessages }) {
|
|
|
191
191
|
* @returns {string}
|
|
192
192
|
*/
|
|
193
193
|
function buildPrBody({ storyId, breakingNotes }) {
|
|
194
|
-
const lines = [
|
|
194
|
+
const lines = [
|
|
195
|
+
`Closes #${storyId}`,
|
|
196
|
+
'',
|
|
197
|
+
'_Auto-opened by `/mandrel-deliver`._',
|
|
198
|
+
];
|
|
195
199
|
if (breakingNotes.length > 0) {
|
|
196
200
|
lines.push('', `BREAKING CHANGE: ${breakingNotes.join(' ')}`);
|
|
197
201
|
}
|
|
@@ -87,6 +87,50 @@ export function resolveWaitForMerge({
|
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Flags this CLI advertised in its `--help` descriptor and never implemented
|
|
92
|
+
* (Story #5100). `--dry-run` was transcribed into the usage block by the
|
|
93
|
+
* #4750 self-describing sweep and read by nothing in this pipeline —
|
|
94
|
+
* `git log -S dryRun` over it is empty in every revision — so an operator who
|
|
95
|
+
* trusted it got a real base-sync merge, an `agent::blocked` Story, and a
|
|
96
|
+
* terminal envelope that then captured `deliver-recover`'s routing.
|
|
97
|
+
* `--no-evidence` names a working flag on the gate wrappers
|
|
98
|
+
* (`evidence-gate.js`), but the slot this CLI parsed had no reader at all.
|
|
99
|
+
*
|
|
100
|
+
* Deleting them from the descriptor is NOT enough. `parseSprintArgs` runs
|
|
101
|
+
* `parseArgs` with `strict: false`, so an unknown flag is silently ignored and
|
|
102
|
+
* the close proceeds for real — the identical injury, now with no help text to
|
|
103
|
+
* explain it. Fail closed instead, exactly as `parseMergeWatchMode` does for an
|
|
104
|
+
* unrecognized mode: throw during option parsing, before the first phase, so
|
|
105
|
+
* the CLI boundary reports `failed` at phase `init` and nothing is mutated.
|
|
106
|
+
*/
|
|
107
|
+
const RETIRED_FLAGS = Object.freeze({
|
|
108
|
+
'--dry-run':
|
|
109
|
+
'this pipeline has never had a dry-run mode; it was advertised in error.',
|
|
110
|
+
'--no-evidence':
|
|
111
|
+
'per-close evidence control was never wired here; the working flag of that name belongs to the gate wrappers.',
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Reject a retired flag before any phase runs.
|
|
116
|
+
*
|
|
117
|
+
* Matches `--flag` and `--flag=value`; a bare `--` terminator or a positional
|
|
118
|
+
* that merely contains the text is not a flag and does not trip it.
|
|
119
|
+
*
|
|
120
|
+
* @param {string[]} argv argv tail (`process.argv.slice(2)`)
|
|
121
|
+
* @throws {Error} naming the flag, why it is gone, and that nothing was mutated
|
|
122
|
+
*/
|
|
123
|
+
function assertNoRetiredFlags(argv) {
|
|
124
|
+
for (const [flag, why] of Object.entries(RETIRED_FLAGS)) {
|
|
125
|
+
const present = argv.some((a) => a === flag || a.startsWith(`${flag}=`));
|
|
126
|
+
if (!present) continue;
|
|
127
|
+
throw new Error(
|
|
128
|
+
`${flag} was retired: ${why} Nothing was mutated — no branch, label, ` +
|
|
129
|
+
`comment, or PR was touched. Re-run without it to close for real.`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
90
134
|
/**
|
|
91
135
|
* Parse and resolve all CLI / injection options for `runSingleStoryClose`.
|
|
92
136
|
*
|
|
@@ -116,7 +160,14 @@ export function parseCloseOptions({
|
|
|
116
160
|
// which is precisely what `resolveFlag` already does below, preferring the
|
|
117
161
|
// param over the parsed slot. One expression per flag now serves both
|
|
118
162
|
// callers, so a new flag is added in one place instead of two that can drift.
|
|
119
|
-
|
|
163
|
+
// The guard runs on the argv door only: an injecting caller supplies
|
|
164
|
+
// `storyIdParam` and never reads argv, so the host process's flags (a test
|
|
165
|
+
// runner's, say) are none of its business.
|
|
166
|
+
let parsed = {};
|
|
167
|
+
if (storyIdParam === undefined) {
|
|
168
|
+
assertNoRetiredFlags(process.argv.slice(2));
|
|
169
|
+
parsed = parseSprintArgs();
|
|
170
|
+
}
|
|
120
171
|
// Preserve undefined so resolveWaitForMerge can apply the closeAndLand
|
|
121
172
|
// config default when neither flag was supplied.
|
|
122
173
|
const waitForMergeExplicit = resolveFlag(
|
|
@@ -19,7 +19,7 @@ export async function handleCriticalReviewBlock({
|
|
|
19
19
|
'### Code review blocked delivery',
|
|
20
20
|
'',
|
|
21
21
|
`The Story-scope review reported **${criticalCount} critical blocker(s)** on ${prUrl}.`,
|
|
22
|
-
'Remediate the posted findings, then re-run `/deliver`.',
|
|
22
|
+
'Remediate the posted findings, then re-run `/mandrel-deliver`.',
|
|
23
23
|
'',
|
|
24
24
|
'If you have reviewed a finding and judged it wrong, re-run close with',
|
|
25
25
|
'`--override-review-block "<reason>"` rather than merging by hand — see',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* phases/wrong-tree-guard.js — detect worktree/main-checkout edit divergence.
|
|
3
3
|
*
|
|
4
|
-
* Story #3364 — `/single-story-deliver` (and `/deliver`) materializes a
|
|
4
|
+
* Story #3364 — `/single-story-deliver` (and `/mandrel-deliver`) materializes a
|
|
5
5
|
* per-Story worktree and instructs the agent to `cd` into it before editing.
|
|
6
6
|
* On Windows that guidance is silently insufficient: `cd <workCwd>` steers the
|
|
7
7
|
* Bash tool's working directory, but the path-based Edit/Write tools operate on
|
|
@@ -232,7 +232,7 @@ export function formatWrongTreeFinding({ storyId, strayFiles, worktreePath }) {
|
|
|
232
232
|
`**Recovery:** relocate these edits into the worktree (re-apply them under ` +
|
|
233
233
|
`\`${worktreePath}\`), restore the main checkout ` +
|
|
234
234
|
`(\`git -C <main-repo> checkout -- <files>\`), then re-run ` +
|
|
235
|
-
`\`/deliver ${storyId}\`.`
|
|
235
|
+
`\`/mandrel-deliver ${storyId}\`.`
|
|
236
236
|
);
|
|
237
237
|
}
|
|
238
238
|
|
|
@@ -307,7 +307,7 @@ async function abortWrongTree({
|
|
|
307
307
|
`diff-path set while the worktree (${worktreePath}) is the active work ` +
|
|
308
308
|
`tree. Close aborted to avoid an empty-diff PR. Stray files: ` +
|
|
309
309
|
`${strayFiles.join(', ')}. Relocate the edits into the worktree, ` +
|
|
310
|
-
`restore the main checkout, then re-run /deliver ${storyId}.`,
|
|
310
|
+
`restore the main checkout, then re-run /mandrel-deliver ${storyId}.`,
|
|
311
311
|
);
|
|
312
312
|
}
|
|
313
313
|
|
|
@@ -84,7 +84,7 @@ async function emitTerminal({ terminal, result, config }) {
|
|
|
84
84
|
* issue as `completed` (the `Closes #<id>` footer firing on merge — the work
|
|
85
85
|
* IS on the base branch) or as `not_planned` (superseded by a re-plan,
|
|
86
86
|
* abandoned — nothing ever merged). Reporting the second as `landed` told
|
|
87
|
-
* `/deliver` that work had reached `main` when no PR ever merged, which would
|
|
87
|
+
* `/mandrel-deliver` that work had reached `main` when no PR ever merged, which would
|
|
88
88
|
* also satisfy any dependent Story waiting on it. Fail loudly instead: being
|
|
89
89
|
* handed an abandoned Story is an input error only the operator can resolve.
|
|
90
90
|
*
|
|
@@ -283,7 +283,7 @@ async function openAndReviewPr({
|
|
|
283
283
|
});
|
|
284
284
|
throw new Error(
|
|
285
285
|
`[single-story-close] Story-scope review reported ${criticalCount} critical blocker(s) on PR ${prUrl}. ` +
|
|
286
|
-
'Auto-merge was not enabled. Remediate the findings posted to the PR and re-run `/deliver`. ' +
|
|
286
|
+
'Auto-merge was not enabled. Remediate the findings posted to the PR and re-run `/mandrel-deliver`. ' +
|
|
287
287
|
'If you have reviewed a finding and judged it wrong, re-run with ' +
|
|
288
288
|
'`--override-review-block "<reason>"` rather than merging by hand.',
|
|
289
289
|
);
|
|
@@ -105,7 +105,7 @@ export async function acquireStoryLease({
|
|
|
105
105
|
steal,
|
|
106
106
|
renderRefusal: (result) =>
|
|
107
107
|
`single-story lease: Story #${storyId} is currently held by @${result.owner}. ` +
|
|
108
|
-
'Another /deliver run owns this Story. Coordinate with that ' +
|
|
108
|
+
'Another /mandrel-deliver run owns this Story. Coordinate with that ' +
|
|
109
109
|
'operator, or re-run with --steal to forcibly transfer the claim once you ' +
|
|
110
110
|
'have confirmed the other run is dead. (A foreign assignee always ' +
|
|
111
111
|
'blocks unless stolen.)',
|
|
@@ -63,7 +63,7 @@ export const FORMAT_AUTOFIX_TIMEOUT_EXIT_CODE = 124;
|
|
|
63
63
|
* @param {(args: string[], opts: object) => string} git
|
|
64
64
|
* @returns {string[]}
|
|
65
65
|
*/
|
|
66
|
-
|
|
66
|
+
function listDirtyPaths(cwd, git) {
|
|
67
67
|
const out = git(['status', '--porcelain'], {
|
|
68
68
|
cwd,
|
|
69
69
|
encoding: 'utf8',
|
|
@@ -91,10 +91,7 @@ export function listDirtyPaths(cwd, git) {
|
|
|
91
91
|
* }} [opts]
|
|
92
92
|
* @returns {{ writeCmdString: string, writeCmd: string, writeArgs: string[] }}
|
|
93
93
|
*/
|
|
94
|
-
|
|
95
|
-
commands,
|
|
96
|
-
dropTrailingDot = false,
|
|
97
|
-
} = {}) {
|
|
94
|
+
function resolveFormatterCmd({ commands, dropTrailingDot = false } = {}) {
|
|
98
95
|
// `resolveFormatWriteCommand` reads `config.project.commands`; wrap the
|
|
99
96
|
// caller-supplied `commands` map into that canonical shape.
|
|
100
97
|
const writeCmdString = resolveFormatWriteCommand({ project: { commands } });
|
|
@@ -47,7 +47,7 @@ export const TERMINAL_ENVELOPE_KIND = 'story-deliver-terminal';
|
|
|
47
47
|
* `pending` is **3**, not 0 and not 1. Zero would tell a headless caller the
|
|
48
48
|
* Story landed when it did not; one would conflate a resumable slow-CI wait
|
|
49
49
|
* with a hard block and send the operator to diagnose branch protection that
|
|
50
|
-
* is working fine. A distinct code is what lets `/deliver` resume without
|
|
50
|
+
* is working fine. A distinct code is what lets `/mandrel-deliver` resume without
|
|
51
51
|
* classifying.
|
|
52
52
|
*/
|
|
53
53
|
export const TERMINAL_EXIT_CODES = Object.freeze({
|
|
@@ -61,7 +61,7 @@ export const TERMINAL_EXIT_CODES = Object.freeze({
|
|
|
61
61
|
* terminal, not a new one, so giving it a fresh code would fork a vocabulary
|
|
62
62
|
* callers already branch on. It stays distinct from `landed` (nothing was
|
|
63
63
|
* delivered) and from `blocked`/`failed` (nothing is wrong — the work simply
|
|
64
|
-
* belongs to `/plan`).
|
|
64
|
+
* belongs to `/mandrel-plan`).
|
|
65
65
|
*/
|
|
66
66
|
escalated: 2,
|
|
67
67
|
});
|
|
@@ -74,12 +74,12 @@ export const TERMINAL_STATUSES = Object.freeze([
|
|
|
74
74
|
'escalated',
|
|
75
75
|
]);
|
|
76
76
|
|
|
77
|
-
/** Cap on the prompt echoed into an escalation's `/plan` next command. */
|
|
77
|
+
/** Cap on the prompt echoed into an escalation's `/mandrel-plan` next command. */
|
|
78
78
|
const PLAN_PROMPT_MAX = 200;
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Render an operator prompt safe to sit inside the double quotes of the
|
|
82
|
-
* `/plan "<prompt>"` next command: collapse newlines (a next command is one
|
|
82
|
+
* `/mandrel-plan "<prompt>"` next command: collapse newlines (a next command is one
|
|
83
83
|
* line by contract), escape backslashes and double quotes so the quoting
|
|
84
84
|
* cannot be broken out of, and cap the length so a long prompt does not turn
|
|
85
85
|
* the envelope into a transcript. Total — a non-string yields the empty
|
|
@@ -133,7 +133,7 @@ export const NEXT_COMMANDS = Object.freeze({
|
|
|
133
133
|
recover: (storyId) =>
|
|
134
134
|
`node .agents/scripts/deliver-recover.js --story ${storyId}`,
|
|
135
135
|
/**
|
|
136
|
-
* Hand over-scope work to `/plan` — the next command of an `escalated`
|
|
136
|
+
* Hand over-scope work to `/mandrel-plan` — the next command of an `escalated`
|
|
137
137
|
* terminal (Story #4746).
|
|
138
138
|
*
|
|
139
139
|
* The only entry here that is a slash command rather than a script, and
|
|
@@ -143,7 +143,7 @@ export const NEXT_COMMANDS = Object.freeze({
|
|
|
143
143
|
* the workflow's escalation section for why running it in the escalating
|
|
144
144
|
* session is forbidden.
|
|
145
145
|
*/
|
|
146
|
-
escalateToPlan: (prompt) => `/plan "${quoteForPlan(prompt)}"`,
|
|
146
|
+
escalateToPlan: (prompt) => `/mandrel-plan "${quoteForPlan(prompt)}"`,
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
/**
|
|
@@ -260,15 +260,15 @@ export function buildTerminalEnvelope({
|
|
|
260
260
|
* `/deliver-light`'s suitability gate already decided correctly when it
|
|
261
261
|
* overrode a `lite` self-verdict on shape; what it lacked was an outcome a
|
|
262
262
|
* session could not walk past. A mandrel-bench 2.13.0 light-arm run did
|
|
263
|
-
* exactly that — it read the gate's `escalate-plan`, then invoked `/plan`
|
|
263
|
+
* exactly that — it read the gate's `escalate-plan`, then invoked `/mandrel-plan`
|
|
264
264
|
* in the same session and delivered. The continuation was not harmless:
|
|
265
265
|
* planning inside a session already framed as small work authored ONE Story
|
|
266
|
-
* against the scenario's 3-5 contract, where a fresh `/plan` session on the
|
|
266
|
+
* against the scenario's 3-5 contract, where a fresh `/mandrel-plan` session on the
|
|
267
267
|
* identical seed authored four. Escalation silently produced the very
|
|
268
268
|
* under-decomposition the guard exists to prevent.
|
|
269
269
|
*
|
|
270
270
|
* So the outcome is a validated envelope with its own exit code, naming the
|
|
271
|
-
* `/plan` command that owns the work, and asserting per artifact that nothing
|
|
271
|
+
* `/mandrel-plan` command that owns the work, and asserting per artifact that nothing
|
|
272
272
|
* was started. Every guarantee here is enforced by the schema rather than by
|
|
273
273
|
* prose: `storyId` must be null, `escalation.created.*` are pinned `false`.
|
|
274
274
|
*
|
|
@@ -288,10 +288,10 @@ export function buildEscalationTerminal({
|
|
|
288
288
|
}) {
|
|
289
289
|
const quoted = quoteForPlan(prompt);
|
|
290
290
|
if (quoted === '') {
|
|
291
|
-
// An escalation whose next command is `/plan ""` hands the operator
|
|
291
|
+
// An escalation whose next command is `/mandrel-plan ""` hands the operator
|
|
292
292
|
// nothing — the same walk-past-able non-outcome in envelope clothing.
|
|
293
293
|
throw new TypeError(
|
|
294
|
-
'buildEscalationTerminal: a non-empty prompt is required — the escalated terminal exists to name the /plan invocation that owns the work',
|
|
294
|
+
'buildEscalationTerminal: a non-empty prompt is required — the escalated terminal exists to name the /mandrel-plan invocation that owns the work',
|
|
295
295
|
);
|
|
296
296
|
}
|
|
297
297
|
const recorded = (Array.isArray(reasons) ? reasons : []).filter(
|
|
@@ -305,7 +305,9 @@ export function buildEscalationTerminal({
|
|
|
305
305
|
reasons:
|
|
306
306
|
recorded.length > 0
|
|
307
307
|
? recorded
|
|
308
|
-
: [
|
|
308
|
+
: [
|
|
309
|
+
'predicted scope exceeds the light ceilings — escalate to /mandrel-plan',
|
|
310
|
+
],
|
|
309
311
|
// Not computed from anything: the escalation path returns before the
|
|
310
312
|
// receipt/init call sites, so these are the assertion that it did.
|
|
311
313
|
created: { receiptStory: false, storyBranch: false, worktree: false },
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from './retro-proposals.js';
|
|
25
25
|
import { upsertStructuredComment } from './ticketing.js';
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
const FOLLOW_UPS_COMMENT_TYPE = 'follow-ups';
|
|
28
28
|
|
|
29
29
|
/** Milliseconds in one day — the unit `frictionWindowDays` is expressed in. */
|
|
30
30
|
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|