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
|
@@ -13,7 +13,7 @@ import path from 'node:path';
|
|
|
13
13
|
* @param {string} rootDir
|
|
14
14
|
* @returns {string[]} absolute paths
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
function walkSkillFiles(rootDir) {
|
|
17
17
|
const out = [];
|
|
18
18
|
if (!fs.existsSync(rootDir)) return out;
|
|
19
19
|
const stack = [rootDir];
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// .agents/scripts/lib/source-text/strip-js-comments.js
|
|
2
|
+
/**
|
|
3
|
+
* The one string-literal-aware JavaScript comment stripper.
|
|
4
|
+
*
|
|
5
|
+
* Several guards in this repository grep source text for a pattern that must
|
|
6
|
+
* appear in real code rather than in a docblock — a CLI citation, an import
|
|
7
|
+
* specifier, a schema path. Each grew its own stripper, and the four
|
|
8
|
+
* implementations disagreed on what a stripped block comment leaves behind:
|
|
9
|
+
* one preserved newlines only, one deleted the comment outright (losing line
|
|
10
|
+
* numbers), one blanked it to whitespace, and one used a regex that did not
|
|
11
|
+
* honour string literals at all. A fix to any of them silently missed the
|
|
12
|
+
* others, and a guard whose own rationale mentions the pattern it greps for
|
|
13
|
+
* can satisfy itself if the stripping is wrong.
|
|
14
|
+
*
|
|
15
|
+
* **Semantics: comment bodies become equivalent whitespace.** Every character
|
|
16
|
+
* of a comment is replaced by a space except newlines, which survive. That is
|
|
17
|
+
* the most information-preserving of the four behaviours and the only one that
|
|
18
|
+
* is safe for all of them:
|
|
19
|
+
*
|
|
20
|
+
* - line and column positions are unchanged, so a guard can report a
|
|
21
|
+
* `file:line` that matches the original source;
|
|
22
|
+
* - a regex looking for code cannot match comment text, because none
|
|
23
|
+
* survives;
|
|
24
|
+
* - the output is the same length as the input, so byte offsets hold.
|
|
25
|
+
*
|
|
26
|
+
* String and template literals are copied through verbatim, so a `//` inside a
|
|
27
|
+
* URL or a `/*` inside a message is not mistaken for a comment opener. Escape
|
|
28
|
+
* sequences are honoured, so an escaped quote does not end the literal early.
|
|
29
|
+
*
|
|
30
|
+
* An unterminated comment or literal runs to end of input rather than
|
|
31
|
+
* throwing: this is a lint helper reading files that may be mid-edit, and a
|
|
32
|
+
* crash there would be a worse failure than a slightly over-stripped tail.
|
|
33
|
+
*
|
|
34
|
+
* Builtins only — no dependency, so the guards that run before a consumer's
|
|
35
|
+
* install can use it.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Replace every non-newline character of `text` with a space.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} text
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
function blank(text) {
|
|
45
|
+
return text.replace(/[^\n]/g, ' ');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Index just past the string or template literal opening at `start`.
|
|
50
|
+
* Runs to end of input when the literal is never closed.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} text
|
|
53
|
+
* @param {number} start - index of the opening quote
|
|
54
|
+
* @returns {number}
|
|
55
|
+
*/
|
|
56
|
+
function endOfLiteral(text, start) {
|
|
57
|
+
const quote = text[start];
|
|
58
|
+
let i = start + 1;
|
|
59
|
+
while (i < text.length) {
|
|
60
|
+
const ch = text[i];
|
|
61
|
+
if (ch === '\\') {
|
|
62
|
+
i += 2;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (ch === quote) return i + 1;
|
|
66
|
+
i += 1;
|
|
67
|
+
}
|
|
68
|
+
return text.length;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Strip JavaScript comments, replacing their bodies with equivalent
|
|
73
|
+
* whitespace so line numbers, columns and byte offsets all survive.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} source - JavaScript source text. Nullish is treated as empty.
|
|
76
|
+
* @returns {string} the source with every comment body blanked to whitespace
|
|
77
|
+
*/
|
|
78
|
+
export function stripJsComments(source) {
|
|
79
|
+
const text = String(source ?? '');
|
|
80
|
+
let out = '';
|
|
81
|
+
let i = 0;
|
|
82
|
+
|
|
83
|
+
while (i < text.length) {
|
|
84
|
+
const two = text.slice(i, i + 2);
|
|
85
|
+
|
|
86
|
+
if (two === '//') {
|
|
87
|
+
const newline = text.indexOf('\n', i);
|
|
88
|
+
const stop = newline === -1 ? text.length : newline;
|
|
89
|
+
out += blank(text.slice(i, stop));
|
|
90
|
+
i = stop;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (two === '/*') {
|
|
95
|
+
const close = text.indexOf('*/', i + 2);
|
|
96
|
+
const stop = close === -1 ? text.length : close + 2;
|
|
97
|
+
out += blank(text.slice(i, stop));
|
|
98
|
+
i = stop;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const ch = text[i];
|
|
103
|
+
if (ch === "'" || ch === '"' || ch === '`') {
|
|
104
|
+
const stop = endOfLiteral(text, i);
|
|
105
|
+
out += text.slice(i, stop);
|
|
106
|
+
i = stop;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
out += ch;
|
|
111
|
+
i += 1;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/story-adjacency.js — the single story-level adjacency builder.
|
|
3
3
|
*
|
|
4
|
-
* Both pre-v2 Epic wave wrappers and the v2 `/deliver` ready-set path bottom out in the shared
|
|
4
|
+
* Both pre-v2 Epic wave wrappers and the v2 `/mandrel-deliver` ready-set path bottom out in the shared
|
|
5
5
|
* `lib/Graph.js` kernel (`detectCycle` / `assignLayers` / `computeWaves`),
|
|
6
6
|
* but each historically re-implemented the step that turns a list of Story
|
|
7
7
|
* records into the `Map<storyId, number[]>` adjacency the kernel consumes.
|
|
@@ -43,7 +43,7 @@ import { parseBlockedBy } from './dependency-parser.js';
|
|
|
43
43
|
* DAG nodes).
|
|
44
44
|
* @param {object} [opts]
|
|
45
45
|
* @param {boolean} [opts.dropForeign=false] When `false` (the v2 default,
|
|
46
|
-
* matching the `/deliver` path — `stories-wave-tick.js` and the
|
|
46
|
+
* matching the `/mandrel-deliver` path — `stories-wave-tick.js` and the
|
|
47
47
|
* `planReadySet` core), the operator-DAG contract is preserved: a
|
|
48
48
|
* dependency on an id absent from the input is treated as not-yet-done
|
|
49
49
|
* and withholds the dependent until it completes. When `true` (the
|
|
@@ -21,7 +21,7 @@ import { BODY_FORMAT_LINTS } from '../story-body/body-format-lints.js';
|
|
|
21
21
|
*
|
|
22
22
|
* **Single source of the prompt body (Story #4162).** This module is the sole
|
|
23
23
|
* carrier of the full decomposer system-prompt body, delivered to the host
|
|
24
|
-
* LLM in the `systemPrompts.decompose` field of the `/plan` context envelope
|
|
24
|
+
* LLM in the `systemPrompts.decompose` field of the `/mandrel-plan` context envelope
|
|
25
25
|
* (via `lib/orchestration/planning/decomposer-context.js`), so no second
|
|
26
26
|
* verbatim copy can drift.
|
|
27
27
|
*/
|
|
@@ -58,7 +58,7 @@ const NODE_TEST_BASE = [
|
|
|
58
58
|
* @param {typeof spawn} [opts.spawnFn]
|
|
59
59
|
* @returns {Promise<IsolatedResult>}
|
|
60
60
|
*/
|
|
61
|
-
|
|
61
|
+
function runFileIsolated({
|
|
62
62
|
repoRoot,
|
|
63
63
|
file,
|
|
64
64
|
envOutDir,
|
|
@@ -195,7 +195,7 @@ function sanitizeEnv(source) {
|
|
|
195
195
|
* @param {typeof spawn} [opts.spawnFn]
|
|
196
196
|
* @returns {Promise<{ results: SuiteResult[], exitCode: number, stdout: string, stderr: string }>}
|
|
197
197
|
*/
|
|
198
|
-
|
|
198
|
+
function runSuite({
|
|
199
199
|
repoRoot,
|
|
200
200
|
files,
|
|
201
201
|
concurrency = 8,
|
|
@@ -266,7 +266,7 @@ export function runSuite({
|
|
|
266
266
|
* @param {(r: IsolatedResult) => void} [opts.onResult]
|
|
267
267
|
* @returns {Promise<IsolatedResult[]>}
|
|
268
268
|
*/
|
|
269
|
-
|
|
269
|
+
async function runIsolatedPool({
|
|
270
270
|
repoRoot,
|
|
271
271
|
files,
|
|
272
272
|
envOutDir,
|
|
@@ -90,13 +90,20 @@ export const TEST_RUNNER_FLAGS = Object.freeze([
|
|
|
90
90
|
* the spawns `runTierPreflight` actually issues — which is the thing that
|
|
91
91
|
* has to be true.
|
|
92
92
|
*/
|
|
93
|
+
const STATE_PROBE_ONLY = Object.freeze(['.agents/scripts/test-wrapper.js']);
|
|
94
|
+
|
|
93
95
|
const TIER_PREFLIGHT_SCRIPTS = Object.freeze({
|
|
94
96
|
full: Object.freeze([
|
|
95
|
-
|
|
97
|
+
...STATE_PROBE_ONLY,
|
|
96
98
|
'.agents/scripts/validate-skills.js',
|
|
97
99
|
]),
|
|
98
|
-
quick:
|
|
99
|
-
integration:
|
|
100
|
+
quick: STATE_PROBE_ONLY,
|
|
101
|
+
integration: STATE_PROBE_ONLY,
|
|
102
|
+
// Story #5111: the `e2e` tier gets the state probe like every other
|
|
103
|
+
// non-full tier. An absent entry is not a neutral default — the lookup
|
|
104
|
+
// below falls through to `[]`, so a tier missing from this map runs *no*
|
|
105
|
+
// preflight at all and the omission is invisible in a green run.
|
|
106
|
+
e2e: STATE_PROBE_ONLY,
|
|
100
107
|
});
|
|
101
108
|
|
|
102
109
|
/**
|
|
@@ -108,7 +115,7 @@ const TIER_PREFLIGHT_SCRIPTS = Object.freeze({
|
|
|
108
115
|
* executes, which the npm hook does not under `ignore-scripts=true`.
|
|
109
116
|
*
|
|
110
117
|
* @param {object} [opts]
|
|
111
|
-
* @param {'full' | 'quick' | 'integration'} [opts.tier]
|
|
118
|
+
* @param {'full' | 'quick' | 'integration' | 'e2e'} [opts.tier]
|
|
112
119
|
* @param {string} [opts.repoRoot] Absolute repository root.
|
|
113
120
|
* @param {typeof spawnSync} [opts.spawn] Injected in tests.
|
|
114
121
|
* @param {string} [opts.execPath] Node binary to spawn; injected in tests.
|
|
@@ -127,8 +134,9 @@ export function runTierPreflight({
|
|
|
127
134
|
stdio: 'inherit',
|
|
128
135
|
});
|
|
129
136
|
if (run.error) throw run.error;
|
|
130
|
-
|
|
131
|
-
|
|
137
|
+
// A signal-killed child reports `status: null`; treat that as the
|
|
138
|
+
// reserved failure code rather than letting it read as "not zero, no code".
|
|
139
|
+
if (run.status !== 0) return run.status ?? 1;
|
|
132
140
|
}
|
|
133
141
|
return 0;
|
|
134
142
|
}
|
|
@@ -23,6 +23,46 @@ export const INTEGRATION_INCLUDE = [
|
|
|
23
23
|
|
|
24
24
|
const matchesIntegration = picomatch(INTEGRATION_INCLUDE, { dot: true });
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Globs for the `e2e` tier — real-binary suites under `tests/e2e/` that pack
|
|
28
|
+
* this repository, install it into a temp consumer and drive the shipped
|
|
29
|
+
* `mandrel` binary end to end.
|
|
30
|
+
*
|
|
31
|
+
* They are the most expensive files the repository owns by an order of
|
|
32
|
+
* magnitude: one `npm pack` plus real `npm install` spawns per file, measured
|
|
33
|
+
* (Story #5111) at ~7 s of system time and ~17 s of summed install wall clock
|
|
34
|
+
* for `update-chain.integration.test.js` alone. Every one of those seconds was
|
|
35
|
+
* charged to `npm test` — i.e. to every pre-push hook and every local
|
|
36
|
+
* iteration — for a signal that only changes when the release-shaped install
|
|
37
|
+
* path changes.
|
|
38
|
+
*
|
|
39
|
+
* So they get their own tier and leave every other one: `full` (`npm test`),
|
|
40
|
+
* `quick` and `integration` all exclude them, and `npm run test:e2e` is how
|
|
41
|
+
* they run. CI runs that tier as its own job on every PR, so the
|
|
42
|
+
* release-shaped path keeps its per-PR signal.
|
|
43
|
+
*
|
|
44
|
+
* Deliberately not exported: a second reader of this list is a second place
|
|
45
|
+
* for the tier definition to drift. `listTestFilesForTier('e2e', root)` is the
|
|
46
|
+
* public answer to "which files are e2e", and the tests assert through it.
|
|
47
|
+
*/
|
|
48
|
+
const E2E_INCLUDE = ['tests/e2e/**/*.test.js'];
|
|
49
|
+
|
|
50
|
+
const matchesE2E = picomatch(E2E_INCLUDE, { dot: true });
|
|
51
|
+
|
|
52
|
+
/** Tier names `parseTierArgv` accepts, in the order `--help` lists them. */
|
|
53
|
+
const TIERS = ['full', 'quick', 'integration', 'e2e'];
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* `node --test` flags the runner forwards verbatim to the child.
|
|
57
|
+
*
|
|
58
|
+
* Everything else that looks like a flag is a mistake — a typo, a retired
|
|
59
|
+
* option, or a flag meant for `npm` that landed after the `--` separator —
|
|
60
|
+
* and `parseTierArgv` rejects it rather than passing it to `node --test`,
|
|
61
|
+
* which reads an unknown `--flag` as a *file pattern* and silently runs a
|
|
62
|
+
* suite that matches nothing while exiting 0.
|
|
63
|
+
*/
|
|
64
|
+
const PASSTHROUGH_FLAGS = ['--test-name-pattern', '--test-only'];
|
|
65
|
+
|
|
26
66
|
/**
|
|
27
67
|
* Repo-relative roots the tier walker scans for test files (names ending in
|
|
28
68
|
* `.test.js`).
|
|
@@ -47,12 +87,22 @@ const TEST_WALK_ROOTS = ['tests', 'lib', '.agents/scripts'];
|
|
|
47
87
|
* globs are scoped to `__tests__` subtrees so they only match colocated
|
|
48
88
|
* tests, never the shipped source modules themselves.
|
|
49
89
|
*
|
|
50
|
-
* Exported because
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* `
|
|
54
|
-
*
|
|
55
|
-
*
|
|
90
|
+
* Exported because it is the **measured surface**: `run-coverage.js` — the
|
|
91
|
+
* required CI job, and the run every coverage / CRAP baseline is scored from —
|
|
92
|
+
* consumes it directly. Story #4922: the coverage runner used to restate
|
|
93
|
+
* `tests/**` on its own, so the 47 colocated `__tests__` files ran under
|
|
94
|
+
* `npm test` but were absent from the measured surface, leaving the coverage
|
|
95
|
+
* and CRAP numbers computed over code the measuring run never executed.
|
|
96
|
+
* Consume this constant; never restate a glob.
|
|
97
|
+
*
|
|
98
|
+
* Story #5111 made this a strict **superset** of the `full` runner tier: the
|
|
99
|
+
* measured surface still includes `tests/e2e/**`, while `npm test` no longer
|
|
100
|
+
* runs it. That asymmetry is deliberate and load-bearing. The e2e suites drive
|
|
101
|
+
* the shipped binary through `bin/mandrel.js` and `lib/cli/update.js` in real
|
|
102
|
+
* child processes, and c8's `NODE_V8_COVERAGE` is inherited by those children —
|
|
103
|
+
* so dropping them from the measured run would deflate exactly the CLI files
|
|
104
|
+
* they exist to exercise and red the coverage ratchet on code nobody touched.
|
|
105
|
+
* Cheapening the pre-push loop must not cost the measurement its subject.
|
|
56
106
|
*/
|
|
57
107
|
export const FULL_TIER_GLOBS = [
|
|
58
108
|
'tests/**/*.test.js',
|
|
@@ -81,10 +131,47 @@ function walkTestFiles(dir, prefix, fsLike) {
|
|
|
81
131
|
return out;
|
|
82
132
|
}
|
|
83
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Split the walked set into the `e2e` tier and the remainder every other tier
|
|
136
|
+
* is drawn from, so an e2e file belongs to exactly one tier and `npm test`
|
|
137
|
+
* never pays for it.
|
|
138
|
+
*
|
|
139
|
+
* @param {string[]} all
|
|
140
|
+
* @returns {{ e2e: string[], rest: string[] }}
|
|
141
|
+
*/
|
|
142
|
+
function partitionE2E(all) {
|
|
143
|
+
const e2e = all.filter((file) => matchesE2E(file));
|
|
144
|
+
const e2eSet = new Set(e2e);
|
|
145
|
+
return { e2e, rest: all.filter((file) => !e2eSet.has(file)) };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Split the non-e2e remainder into the slow `integration` tier and the `quick`
|
|
150
|
+
* complement — the historical partition, unchanged.
|
|
151
|
+
*
|
|
152
|
+
* @param {string[]} rest
|
|
153
|
+
* @param {'quick' | 'integration'} tier
|
|
154
|
+
* @returns {string[]}
|
|
155
|
+
*/
|
|
156
|
+
function splitBySpeed(rest, tier) {
|
|
157
|
+
const integration = rest.filter((file) => matchesIntegration(file));
|
|
158
|
+
if (tier === 'integration') {
|
|
159
|
+
return integration;
|
|
160
|
+
}
|
|
161
|
+
const integrationSet = new Set(integration);
|
|
162
|
+
return rest.filter((file) => !integrationSet.has(file));
|
|
163
|
+
}
|
|
164
|
+
|
|
84
165
|
/**
|
|
85
166
|
* List repo-relative test file paths for a tier.
|
|
86
167
|
*
|
|
87
|
-
*
|
|
168
|
+
* `full` used to return {@link FULL_TIER_GLOBS} verbatim and let `node --test`
|
|
169
|
+
* expand them. It enumerates files instead since Story #5111, because the one
|
|
170
|
+
* thing a glob list cannot express is an exclusion: `node --test` has no
|
|
171
|
+
* negative pattern, so "everything except `tests/e2e/**`" is only sayable as
|
|
172
|
+
* a file set. The measured surface keeps the globs (see `FULL_TIER_GLOBS`).
|
|
173
|
+
*
|
|
174
|
+
* @param {'full' | 'quick' | 'integration' | 'e2e'} tier
|
|
88
175
|
* @param {string} repoRoot
|
|
89
176
|
* @param {typeof fs} [fsLike]
|
|
90
177
|
* @returns {string[]}
|
|
@@ -93,34 +180,64 @@ export function listTestFilesForTier(tier, repoRoot, fsLike = fs) {
|
|
|
93
180
|
const all = TEST_WALK_ROOTS.flatMap((root) =>
|
|
94
181
|
walkTestFiles(path.join(repoRoot, root), root, fsLike),
|
|
95
182
|
).sort();
|
|
96
|
-
|
|
97
|
-
|
|
183
|
+
const { e2e, rest } = partitionE2E(all);
|
|
184
|
+
if (tier === 'e2e') {
|
|
185
|
+
return e2e;
|
|
98
186
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return integration;
|
|
187
|
+
if (tier === 'full') {
|
|
188
|
+
return rest;
|
|
102
189
|
}
|
|
103
|
-
|
|
104
|
-
|
|
190
|
+
return splitBySpeed(rest, tier);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Reject argv tokens that look like flags but are neither `--tier` (already
|
|
195
|
+
* consumed) nor a sanctioned `node --test` pass-through.
|
|
196
|
+
*
|
|
197
|
+
* Silence was the old behaviour and the reason this exists: the runner
|
|
198
|
+
* forwarded every unrecognized token verbatim, and `node --test` treats an
|
|
199
|
+
* unknown `--flag` as another **file pattern**. A typo therefore produced a
|
|
200
|
+
* run that matched nothing, printed a plausible-looking summary and exited 0 —
|
|
201
|
+
* a green that proved nothing.
|
|
202
|
+
*
|
|
203
|
+
* @param {string[]} rest
|
|
204
|
+
* @throws {Error} naming both the accepted tiers and the accepted flags.
|
|
205
|
+
*/
|
|
206
|
+
function assertKnownFlags(rest) {
|
|
207
|
+
const unknown = rest.filter(
|
|
208
|
+
(arg) =>
|
|
209
|
+
arg.startsWith('--') &&
|
|
210
|
+
!PASSTHROUGH_FLAGS.includes(arg) &&
|
|
211
|
+
!PASSTHROUGH_FLAGS.some((flag) => arg.startsWith(`${flag}=`)),
|
|
212
|
+
);
|
|
213
|
+
if (unknown.length === 0) return;
|
|
214
|
+
throw new Error(
|
|
215
|
+
`[run-tests] unrecognized argument(s): ${unknown.join(', ')}. ` +
|
|
216
|
+
`Accepted: --tier <${TIERS.join('|')}>, ${PASSTHROUGH_FLAGS.join(', ')}, --help. ` +
|
|
217
|
+
'Unrecognized flags are not forwarded: `node --test` would read them as ' +
|
|
218
|
+
'file patterns and exit 0 having run nothing.',
|
|
219
|
+
);
|
|
105
220
|
}
|
|
106
221
|
|
|
107
222
|
/**
|
|
108
|
-
* Parse `--tier <name>` from argv. Unknown tiers throw.
|
|
223
|
+
* Parse `--tier <name>` from argv. Unknown tiers and unknown flags throw.
|
|
109
224
|
*
|
|
110
225
|
* @param {string[]} argv
|
|
111
|
-
* @returns {{ tier: 'full' | 'quick' | 'integration', rest: string[] }}
|
|
226
|
+
* @returns {{ tier: 'full' | 'quick' | 'integration' | 'e2e', rest: string[] }}
|
|
112
227
|
*/
|
|
113
228
|
export function parseTierArgv(argv) {
|
|
114
229
|
const tierIdx = argv.indexOf('--tier');
|
|
115
230
|
if (tierIdx === -1) {
|
|
231
|
+
assertKnownFlags(argv);
|
|
116
232
|
return { tier: 'full', rest: argv };
|
|
117
233
|
}
|
|
118
234
|
const tier = argv[tierIdx + 1];
|
|
119
|
-
if (!tier || !
|
|
235
|
+
if (!tier || !TIERS.includes(tier)) {
|
|
120
236
|
throw new Error(
|
|
121
|
-
`[run-tests] --tier requires one of:
|
|
237
|
+
`[run-tests] --tier requires one of: ${TIERS.join(', ')} (got ${JSON.stringify(tier)})`,
|
|
122
238
|
);
|
|
123
239
|
}
|
|
124
240
|
const rest = argv.filter((_, i) => i !== tierIdx && i !== tierIdx + 1);
|
|
241
|
+
assertKnownFlags(rest);
|
|
125
242
|
return { tier, rest };
|
|
126
243
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Section-scoped writes are the load-bearing contract (extending the
|
|
13
13
|
* single-writer discipline #4303 established for the body trailer):
|
|
14
14
|
*
|
|
15
|
-
* - The `/plan` persist path upserts the `techSpec` and `acceptanceTable`
|
|
15
|
+
* - The `/mandrel-plan` persist path upserts the `techSpec` and `acceptanceTable`
|
|
16
16
|
* regions.
|
|
17
17
|
* - The close-time acceptance reconciler
|
|
18
18
|
* (`acceptance-spec-reconciler.js`) reads and rewrites the
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { createRequire, SourceMap } from 'node:module';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { resolveDependencyVersion } from './dependency-version.js';
|
|
4
5
|
import { Logger } from './Logger.js';
|
|
5
6
|
|
|
6
7
|
const require = createRequire(import.meta.url);
|
|
@@ -22,18 +23,30 @@ function loadTypeScript() {
|
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
let _tsVersion = null;
|
|
27
|
+
|
|
25
28
|
/**
|
|
26
29
|
* Resolve the `typescript` package version, used to stamp baselines so
|
|
27
30
|
* consumers can detect transpiler drift. Returns `'0.0.0'` when the
|
|
28
31
|
* dependency is unresolvable — callers treat that sentinel as "unknown
|
|
29
32
|
* environment" and may refuse to persist a baseline that includes TS rows.
|
|
30
33
|
*
|
|
34
|
+
* **The version is read from the package manifest, never by evaluating the
|
|
35
|
+
* compiler** (Story #5109) — see
|
|
36
|
+
* [`dependency-version.js`](dependency-version.js) for how, and for what it
|
|
37
|
+
* cost to do it the other way. The manifest returns the identical string, so
|
|
38
|
+
* the stamp (and therefore every committed baseline envelope) is
|
|
39
|
+
* byte-identical either way. The compiler itself is still loaded — lazily, by
|
|
40
|
+
* `transpileIfNeeded` — the first time a `.ts`/`.tsx` input is actually
|
|
41
|
+
* transpiled.
|
|
42
|
+
*
|
|
31
43
|
* @returns {string}
|
|
32
44
|
*/
|
|
33
45
|
export function resolveTsTranspilerVersion() {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
if (_tsVersion === null) {
|
|
47
|
+
_tsVersion = resolveDependencyVersion('typescript', require) ?? '0.0.0';
|
|
48
|
+
}
|
|
49
|
+
return _tsVersion;
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
function isTypeScriptPath(filePath) {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
/**
|
|
17
17
|
* The one bound every independent-write fan-out over the GitHub API uses
|
|
18
18
|
* (Story #4952 raised those loops off serial; Story #4961 made this the single
|
|
19
|
-
* owner of the number they share). Imported by the `/plan` context gathers,
|
|
19
|
+
* owner of the number they share). Imported by the `/mandrel-plan` context gathers,
|
|
20
20
|
* the persist checkpoint fan-out, the `agent::ready` flips and the supersede
|
|
21
21
|
* close loop, so re-tuning the policy is one edit rather than six.
|
|
22
22
|
*
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* parse-id-list — expand a Story-id list that may contain dash ranges.
|
|
3
3
|
*
|
|
4
4
|
* Operators name a contiguous span of Stories the way they read one — as a
|
|
5
|
-
* range: `/deliver 4922 - 4926`. Enumerating it by hand is the kind of
|
|
5
|
+
* range: `/mandrel-deliver 4922 - 4926`. Enumerating it by hand is the kind of
|
|
6
6
|
* transcription step that silently drops or invents an id, so the range is a
|
|
7
7
|
* first-class shape of every delivery id list rather than something the host
|
|
8
8
|
* expands from prose.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* in-flight count, and it decides. Until now the only adapter was the
|
|
8
8
|
* flag-driven one (`stories-wave-tick.js --dag/--done/--in-flight`), which
|
|
9
9
|
* pushed the *gathering* of those inputs onto the caller — in practice onto
|
|
10
|
-
* the host LLM following `/deliver`'s prose, re-seeding `--done` and counting
|
|
10
|
+
* the host LLM following `/mandrel-deliver`'s prose, re-seeding `--done` and counting
|
|
11
11
|
* `--in-flight` by hand every beat. That is hand-maintained accounting on the
|
|
12
12
|
* one correctness-critical path where a mistake silently wedges a run (a
|
|
13
13
|
* dropped foreign blocker) or double-dispatches a Story (a miscounted slot).
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
* The graph resolution is **not** reimplemented here — it reuses
|
|
48
48
|
* `resolve-stories.js`'s machinery wholesale (body `depends_on` ∪ native
|
|
49
49
|
* `blocked_by` edges, foreign-blocker resolution, `files[]` footprints), so
|
|
50
|
-
* the probe and `/deliver`'s step-1 resolution cannot disagree about what
|
|
50
|
+
* the probe and `/mandrel-deliver`'s step-1 resolution cannot disagree about what
|
|
51
51
|
* depends on what.
|
|
52
52
|
*
|
|
53
53
|
* @module lib/wave-runner/live-probe
|
|
@@ -127,7 +127,7 @@ function deriveInFlightIds(storyRecords, dispatched = []) {
|
|
|
127
127
|
* in-flight, so `detectWedge` dropped it (its "undone work with no unmet
|
|
128
128
|
* blockers would have been dispatched" invariant is precisely what probe mode
|
|
129
129
|
* broke) and the beat reported exit 0 / `ready: []` / `wedged: null` forever.
|
|
130
|
-
* `/deliver` reads that as "waiting", so the `agent::blocked` HITL pause — the
|
|
130
|
+
* `/mandrel-deliver` reads that as "waiting", so the `agent::blocked` HITL pause — the
|
|
131
131
|
* one runtime gate in the protocol — was never surfaced to the operator.
|
|
132
132
|
*
|
|
133
133
|
* @param {Array<{id?: number, number?: number, labels?: string[], state?: string}>} storyRecords
|
|
@@ -193,7 +193,7 @@ function deriveForeignHeld(storyRecords, self, warn) {
|
|
|
193
193
|
* Resolve the provider + repo coordinates the probe reads through.
|
|
194
194
|
*
|
|
195
195
|
* Shares `resolve-stories.js`'s provider seam, so probe mode authenticates and
|
|
196
|
-
* targets exactly the same repo `/deliver`'s resolution step does. Tests
|
|
196
|
+
* targets exactly the same repo `/mandrel-deliver`'s resolution step does. Tests
|
|
197
197
|
* inject a stub provider instead of calling this.
|
|
198
198
|
*
|
|
199
199
|
* @param {object} [deps]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* lib/wave-runner/ready-set.js — the path-agnostic ready-set scheduling
|
|
3
3
|
* core.
|
|
4
4
|
*
|
|
5
|
-
* This module is the scheduling kernel the v2 `/deliver` multi-Story path
|
|
5
|
+
* This module is the scheduling kernel the v2 `/mandrel-deliver` multi-Story path
|
|
6
6
|
* dispatches through (`stories-wave-tick.js`). It replaces wave-*batch* selection
|
|
7
7
|
* (group N must fully drain before group N+1 opens) with *continuous*,
|
|
8
8
|
* dependency-driven selection: a Story becomes dispatchable the instant
|
|
@@ -345,7 +345,7 @@ function loadDocs(root, fs) {
|
|
|
345
345
|
* (`helpers/deliver-story.md` → `# /deliver-story …`). The self-naming test is
|
|
346
346
|
* what separates an invocable helper from an appendix that merely titles itself
|
|
347
347
|
* after the command it documents (`helpers/deliver-reference.md` →
|
|
348
|
-
* `# /deliver — reference appendix`), which is read on demand, never invoked.
|
|
348
|
+
* `# /mandrel-deliver — reference appendix`), which is read on demand, never invoked.
|
|
349
349
|
*
|
|
350
350
|
* @param {WorkflowDoc} doc
|
|
351
351
|
* @returns {boolean}
|