mandrel 2.35.0 → 2.37.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.
Files changed (108) hide show
  1. package/.agents/docs/agentrc-reference.json +3 -1
  2. package/.agents/docs/configuration.md +27 -0
  3. package/.agents/rules/testing-standards.md +56 -0
  4. package/.agents/schemas/agentrc.schema.json +11 -0
  5. package/.agents/schemas/lifecycle/merge.unlanded.schema.json +2 -1
  6. package/.agents/schemas/story-deliver-terminal.schema.json +1 -0
  7. package/.agents/scripts/boot-sweep.js +7 -1
  8. package/.agents/scripts/check-context-budget.js +195 -103
  9. package/.agents/scripts/check-cyclomatic.js +112 -42
  10. package/.agents/scripts/check-doc-links.js +23 -2
  11. package/.agents/scripts/check-generated-validator.js +202 -0
  12. package/.agents/scripts/check-lifecycle-lint.js +2 -68
  13. package/.agents/scripts/check-schema-references.js +2 -28
  14. package/.agents/scripts/drain-pending-cleanup.js +18 -1
  15. package/.agents/scripts/git-cleanup.js +2 -0
  16. package/.agents/scripts/lib/baselines/components.js +32 -2
  17. package/.agents/scripts/lib/baselines/env-overrides.js +1 -1
  18. package/.agents/scripts/lib/bootstrap/branch-protection.js +1 -1
  19. package/.agents/scripts/lib/bootstrap/install-ledger.js +1 -1
  20. package/.agents/scripts/lib/bootstrap/prompt.js +1 -1
  21. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +2 -2
  22. package/.agents/scripts/lib/checks/loop-health.js +1 -1
  23. package/.agents/scripts/lib/cli-args.js +0 -2
  24. package/.agents/scripts/lib/close-validation/gates.js +1 -1
  25. package/.agents/scripts/lib/close-validation/process.js +1 -1
  26. package/.agents/scripts/lib/config/ci.js +18 -0
  27. package/.agents/scripts/lib/config/sync-agentrc.js +1 -1
  28. package/.agents/scripts/lib/config-settings-schema-delivery.js +13 -0
  29. package/.agents/scripts/lib/config-settings-schema.js +46 -6
  30. package/.agents/scripts/lib/coverage-baseline.js +2 -2
  31. package/.agents/scripts/lib/cpu-pool.js +90 -10
  32. package/.agents/scripts/lib/crap-utils.js +6 -2
  33. package/.agents/scripts/lib/cyclomatic-ceiling.js +28 -6
  34. package/.agents/scripts/lib/cyclomatic-scope.js +144 -0
  35. package/.agents/scripts/lib/dependency-version.js +86 -0
  36. package/.agents/scripts/lib/duplicate-search.js +1 -1
  37. package/.agents/scripts/lib/errors/index.js +1 -1
  38. package/.agents/scripts/lib/feedback-loop/graduator-core.js +2 -2
  39. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +1 -1
  40. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  41. package/.agents/scripts/lib/generated/agentrc-validator.js +16 -0
  42. package/.agents/scripts/lib/install-cmd-parser.js +1 -1
  43. package/.agents/scripts/lib/knip-entry-sync.js +1 -57
  44. package/.agents/scripts/lib/maintainability-utils.js +108 -10
  45. package/.agents/scripts/lib/observability/source-classifier.js +1 -1
  46. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +22 -7
  47. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +22 -14
  48. package/.agents/scripts/lib/orchestration/git-cleanup/phases/merged-tip.js +132 -0
  49. package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +56 -11
  50. package/.agents/scripts/lib/orchestration/merge-block-class.js +10 -1
  51. package/.agents/scripts/lib/orchestration/merge-poll.js +164 -0
  52. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +70 -9
  53. package/.agents/scripts/lib/orchestration/pr-watch.js +1 -30
  54. package/.agents/scripts/lib/orchestration/reassert-status-column.js +2 -2
  55. package/.agents/scripts/lib/orchestration/review-providers/codex.js +3 -3
  56. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +1 -1
  57. package/.agents/scripts/lib/orchestration/review-providers/native.js +9 -2
  58. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +2 -2
  59. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +3 -3
  60. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +145 -0
  61. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +96 -5
  62. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +52 -1
  63. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +9 -1
  64. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +2 -5
  65. package/.agents/scripts/lib/orchestration/story-follow-ups.js +1 -1
  66. package/.agents/scripts/lib/orchestration/ticket-lease.js +34 -9
  67. package/.agents/scripts/lib/orchestration/ticketing/reads.js +3 -1
  68. package/.agents/scripts/lib/preflight-runner.js +3 -3
  69. package/.agents/scripts/lib/qa/qa-session.js +1 -1
  70. package/.agents/scripts/lib/runtime-deps/manifest.js +1 -7
  71. package/.agents/scripts/lib/runtime-deps/scan-imports.js +3 -58
  72. package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +284 -25
  73. package/.agents/scripts/lib/skills/walk-skill-files.js +1 -1
  74. package/.agents/scripts/lib/source-text/strip-js-comments.js +115 -0
  75. package/.agents/scripts/lib/test-isolate/runner.js +3 -3
  76. package/.agents/scripts/lib/test-runner-contract.js +14 -6
  77. package/.agents/scripts/lib/test-tiers.js +135 -18
  78. package/.agents/scripts/lib/transpile.js +16 -3
  79. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +169 -31
  80. package/.agents/scripts/lib/worktree/node-modules-strategy.js +2 -5
  81. package/.agents/scripts/notify.js +4 -10
  82. package/.agents/scripts/pr-watch-with-update.js +305 -137
  83. package/.agents/scripts/providers/github/tickets.js +109 -16
  84. package/.agents/scripts/quality-preview.js +162 -70
  85. package/.agents/scripts/run-test-profile.js +8 -5
  86. package/.agents/scripts/run-tests.js +79 -14
  87. package/.agents/scripts/single-story-close.js +0 -2
  88. package/.agents/scripts/single-story-init.js +5 -1
  89. package/.agents/workflows/audit-documentation.md +5 -6
  90. package/docs/CHANGELOG.md +50 -0
  91. package/lib/cli/migrate.js +100 -94
  92. package/lib/migrations/helpers/retire-agentrc-key.js +200 -0
  93. package/lib/migrations/index.js +32 -33
  94. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +21 -80
  95. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +15 -87
  96. package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +13 -71
  97. package/lib/migrations/steps/2.20.0-retire-codebase-snapshot.js +13 -101
  98. package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +13 -100
  99. package/package.json +9 -5
  100. package/.agents/scripts/generate-lifecycle-docs.js +0 -237
  101. package/.agents/scripts/lib/audit-suite/frontmatter-lint.js +0 -32
  102. package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +0 -37
  103. package/.agents/scripts/lib/cli/parse-numeric.js +0 -60
  104. package/.agents/scripts/lib/close-validation/telemetry.js +0 -79
  105. package/.agents/scripts/lib/orchestration/label-transitions.js +0 -44
  106. package/.agents/scripts/lib/orchestration/parked-follow-ons.js +0 -147
  107. package/.agents/scripts/lib/orchestration/phase-runner.js +0 -88
  108. package/.agents/scripts/lib/orchestration/recut.js +0 -56
@@ -1,18 +1,11 @@
1
1
  // lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js
2
2
  /**
3
- * Story #4545retire `delivery.deliverRunner.verifyConcurrencyCap`.
4
- *
5
- * The knob was schema-validated, defaulted to 4, resolved by `getRunners`,
6
- * and documented as bounding "the per-wave verifyWaveResults loop" a loop
7
- * that does not exist anywhere in the tree. Its only reader was
8
- * `analyze-execution.js`, which echoed the number into a perf report rather
9
- * than bounding anything; that CLI is deleted in the same Story. Setting the
10
- * knob to any value only ever changed a number printed in a report.
11
- *
12
- * `delivery.deliverRunner` declares `additionalProperties: false`, so a
13
- * consumer whose config still carries the key hits a hard AJV validation
14
- * failure on upgrade — every script that resolves config, not just the one
15
- * that read it. This step strips it before that check runs.
3
+ * Story #4531 — `delivery.deliverRunner.verifyConcurrencyCap` was retired
4
+ * when verification stopped being separately capped, but it stayed
5
+ * schema-validated in consumers' configs under a block carrying
6
+ * `additionalProperties: false`, so a config still setting it fails AJV
7
+ * validation on upgrade rather than warning. This step strips it before
8
+ * that check runs.
16
9
  *
17
10
  * Unlike the 2.1.0 mi-drop step, this one also sweeps `.agentrc.local.json`:
18
11
  * the resolver deep-merges the local override over `.agentrc.json` and
@@ -20,82 +13,17 @@
20
13
  * same way a key in the committed file does.
21
14
  */
22
15
 
23
- import nodeFs from 'node:fs';
24
- import path from 'node:path';
25
-
26
- const AGENTRC_FILENAMES = ['.agentrc.json', '.agentrc.local.json'];
27
-
28
- /**
29
- * @param {unknown} ctx
30
- * @param {string} filename
31
- * @returns {string}
32
- */
33
- function resolveAgentrcPath(ctx, filename) {
34
- const projectRoot = ctx?.projectRoot ?? process.cwd();
35
- return path.join(projectRoot, filename);
36
- }
37
-
38
- /**
39
- * @param {unknown} ctx
40
- * @param {string} filename
41
- * @param {typeof nodeFs} fsImpl
42
- * @returns {object | null}
43
- */
44
- function readAgentrcConfig(ctx, filename, fsImpl) {
45
- try {
46
- const raw = fsImpl.readFileSync(resolveAgentrcPath(ctx, filename), 'utf8');
47
- return JSON.parse(raw);
48
- } catch {
49
- return null;
50
- }
51
- }
16
+ import { createRetireAgentrcKeyStep } from '../helpers/retire-agentrc-key.js';
52
17
 
53
- /**
54
- * @param {object | null} config
55
- * @returns {boolean}
56
- */
57
- function hasRetiredKey(config) {
58
- const deliverRunner = config?.delivery?.deliverRunner;
59
- return (
60
- Boolean(deliverRunner) &&
61
- Object.hasOwn(deliverRunner, 'verifyConcurrencyCap')
62
- );
63
- }
64
-
65
- export const retireVerifyConcurrencyCap = {
18
+ export const retireVerifyConcurrencyCap = createRetireAgentrcKeyStep({
66
19
  version: '2.1.0',
67
20
  description:
68
21
  'strip retired delivery.deliverRunner.verifyConcurrencyCap from ' +
69
22
  '.agentrc.json and .agentrc.local.json',
70
- /**
71
- * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
72
- * @returns {boolean}
73
- */
74
- detect(ctx) {
75
- const fsImpl = ctx?.fs ?? nodeFs;
76
- return AGENTRC_FILENAMES.some((filename) =>
77
- hasRetiredKey(readAgentrcConfig(ctx, filename, fsImpl)),
78
- );
79
- },
80
- /**
81
- * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
82
- * @returns {void}
83
- */
84
- apply(ctx) {
85
- const fsImpl = ctx?.fs ?? nodeFs;
86
- for (const filename of AGENTRC_FILENAMES) {
87
- const config = readAgentrcConfig(ctx, filename, fsImpl);
88
- if (!hasRetiredKey(config)) continue;
89
-
90
- delete config.delivery.deliverRunner.verifyConcurrencyCap;
91
- if (Object.keys(config.delivery.deliverRunner).length === 0) {
92
- delete config.delivery.deliverRunner;
93
- }
94
-
95
- fsImpl.writeFileSync(
96
- resolveAgentrcPath(ctx, filename),
97
- `${JSON.stringify(config, null, 2)}\n`,
98
- );
99
- }
100
- },
101
- };
23
+ keys: [
24
+ {
25
+ path: ['delivery', 'deliverRunner', 'verifyConcurrencyCap'],
26
+ pruneDepth: 1,
27
+ },
28
+ ],
29
+ });
@@ -12,81 +12,23 @@
12
12
  * `maxSeedWords` hits a hard validation failure on upgrade, not a
13
13
  * warning. This step strips the key before that check runs — the same
14
14
  * contract-cutover pattern as `2.1.0-retire-mi-drop-knobs.js`.
15
+ *
16
+ * `planning` is an optional block, so an emptied `complexityGate` and an
17
+ * emptied `planning` are both pruned rather than left as orphan objects.
15
18
  */
16
19
 
17
- import nodeFs from 'node:fs';
18
- import path from 'node:path';
19
-
20
- const AGENTRC_FILENAME = '.agentrc.json';
21
-
22
- /**
23
- * @param {unknown} ctx
24
- * @returns {string}
25
- */
26
- function resolveAgentrcPath(ctx) {
27
- const projectRoot = ctx?.projectRoot ?? process.cwd();
28
- return path.join(projectRoot, AGENTRC_FILENAME);
29
- }
30
-
31
- /**
32
- * @param {unknown} ctx
33
- * @param {typeof nodeFs} fsImpl
34
- * @returns {object | null}
35
- */
36
- function readAgentrcConfig(ctx, fsImpl) {
37
- try {
38
- const raw = fsImpl.readFileSync(resolveAgentrcPath(ctx), 'utf8');
39
- return JSON.parse(raw);
40
- } catch {
41
- return null;
42
- }
43
- }
44
-
45
- /**
46
- * @param {object | null} config
47
- * @returns {boolean}
48
- */
49
- function hasRetiredKey(config) {
50
- const gate = config?.planning?.complexityGate;
51
- return Boolean(gate) && Object.hasOwn(gate, 'maxSeedWords');
52
- }
20
+ import {
21
+ AGENTRC_BASE_FILENAME,
22
+ createRetireAgentrcKeyStep,
23
+ } from '../helpers/retire-agentrc-key.js';
53
24
 
54
- export const retireMaxSeedWords = {
25
+ export const retireMaxSeedWords = createRetireAgentrcKeyStep({
55
26
  version: '2.11.0',
56
27
  description:
57
28
  'strip retired planning.complexityGate.maxSeedWords from .agentrc.json ' +
58
29
  '(complexity routes on Story shape, never seed word count — Story #4722)',
59
- /**
60
- * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
61
- * @returns {boolean}
62
- */
63
- detect(ctx) {
64
- const fsImpl = ctx?.fs ?? nodeFs;
65
- return hasRetiredKey(readAgentrcConfig(ctx, fsImpl));
66
- },
67
- /**
68
- * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
69
- * @returns {void}
70
- */
71
- apply(ctx) {
72
- const fsImpl = ctx?.fs ?? nodeFs;
73
- const config = readAgentrcConfig(ctx, fsImpl);
74
- if (!config) return;
75
-
76
- const gate = config.planning?.complexityGate;
77
- if (gate && Object.hasOwn(gate, 'maxSeedWords')) {
78
- delete gate.maxSeedWords;
79
- if (Object.keys(gate).length === 0) {
80
- delete config.planning.complexityGate;
81
- if (Object.keys(config.planning).length === 0) {
82
- delete config.planning;
83
- }
84
- }
85
- }
86
-
87
- fsImpl.writeFileSync(
88
- resolveAgentrcPath(ctx),
89
- `${JSON.stringify(config, null, 2)}\n`,
90
- );
91
- },
92
- };
30
+ filenames: [AGENTRC_BASE_FILENAME],
31
+ keys: [
32
+ { path: ['planning', 'complexityGate', 'maxSeedWords'], pruneDepth: 2 },
33
+ ],
34
+ });
@@ -1,113 +1,25 @@
1
1
  // lib/migrations/steps/2.20.0-retire-codebase-snapshot.js
2
2
  /**
3
3
  * Story #4811 — strip the retired `planning.codebaseSnapshot` block from a
4
- * consumer's `.agentrc.json` / `.agentrc.local.json`.
5
- *
6
- * #4811 hard-cutover-removed the `/plan` codebase snapshot: the pre-computed
7
- * structural view grounded nothing it promised (its default include globs
8
- * missed the standard monorepo layout, and its knobs only re-filtered the same
9
- * matched set), and spec authoring is grounded instead by the author's own
10
- * targeted repo retrieval plus the Phase 8 `validateStoryFileAssumptions`
11
- * gate. The whole block was dropped from the runtime AJV schema and the
12
- * published mirror. `planning` carries `additionalProperties: false`, so a
13
- * consumer whose config still sets `codebaseSnapshot` hits a hard validation
14
- * failure on upgrade, not a warning. This step strips the key before that
15
- * check runs — the same contract-cutover pattern as
16
- * `2.11.0-retire-max-seed-words.js`, widened to the local override file
17
- * because a snapshot narrowed for one checkout is exactly the kind of knob an
4
+ * consumer's `.agentrc.json` and from the `.agentrc.local.json` overlay an
18
5
  * operator pins in `.agentrc.local.json`.
19
- */
20
-
21
- import nodeFs from 'node:fs';
22
- import path from 'node:path';
23
-
24
- /**
25
- * Both config files the resolver deep-merges. A key surviving in either one
26
- * fails validation, so both are stripped.
27
- */
28
- const AGENTRC_FILENAMES = ['.agentrc.json', '.agentrc.local.json'];
29
-
30
- const RETIRED_KEY = 'codebaseSnapshot';
31
-
32
- /**
33
- * @param {unknown} ctx
34
- * @param {string} filename
35
- * @returns {string}
36
- */
37
- function resolveAgentrcPath(ctx, filename) {
38
- const projectRoot = ctx?.projectRoot ?? process.cwd();
39
- return path.join(projectRoot, filename);
40
- }
41
-
42
- /**
43
- * @param {unknown} ctx
44
- * @param {string} filename
45
- * @param {typeof nodeFs} fsImpl
46
- * @returns {object | null}
47
- */
48
- function readAgentrcConfig(ctx, filename, fsImpl) {
49
- try {
50
- const raw = fsImpl.readFileSync(resolveAgentrcPath(ctx, filename), 'utf8');
51
- return JSON.parse(raw);
52
- } catch {
53
- return null;
54
- }
55
- }
56
-
57
- /**
58
- * @param {object | null} config
59
- * @returns {boolean}
60
- */
61
- function hasRetiredKey(config) {
62
- const planning = config?.planning;
63
- return Boolean(planning) && Object.hasOwn(planning, RETIRED_KEY);
64
- }
65
-
66
- /**
67
- * Strip the key and prune the containers it emptied, so a config that carried
68
- * nothing but the snapshot does not keep an orphan `planning: {}`.
69
6
  *
70
- * @param {object} config
71
- * @returns {void}
7
+ * Spec authoring is grounded by targeted retrieval plus the Phase 8
8
+ * file-assumption gate, so the snapshot block has no consumer; the block
9
+ * carries `additionalProperties: false`, so a config still setting it fails
10
+ * validation on upgrade rather than warning.
11
+ *
12
+ * `planning` is optional, so a config that carried nothing but the snapshot
13
+ * does not keep an orphan `planning: {}`.
72
14
  */
73
- function stripRetiredKey(config) {
74
- delete config.planning[RETIRED_KEY];
75
- if (Object.keys(config.planning).length === 0) {
76
- delete config.planning;
77
- }
78
- }
79
15
 
80
- export const retireCodebaseSnapshot = {
16
+ import { createRetireAgentrcKeyStep } from '../helpers/retire-agentrc-key.js';
17
+
18
+ export const retireCodebaseSnapshot = createRetireAgentrcKeyStep({
81
19
  version: '2.20.0',
82
20
  description:
83
21
  'strip retired planning.codebaseSnapshot from .agentrc.json / ' +
84
22
  '.agentrc.local.json (spec authoring is grounded by targeted retrieval ' +
85
23
  'plus the Phase 8 file-assumption gate — Story #4811)',
86
- /**
87
- * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
88
- * @returns {boolean}
89
- */
90
- detect(ctx) {
91
- const fsImpl = ctx?.fs ?? nodeFs;
92
- return AGENTRC_FILENAMES.some((filename) =>
93
- hasRetiredKey(readAgentrcConfig(ctx, filename, fsImpl)),
94
- );
95
- },
96
- /**
97
- * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
98
- * @returns {void}
99
- */
100
- apply(ctx) {
101
- const fsImpl = ctx?.fs ?? nodeFs;
102
- for (const filename of AGENTRC_FILENAMES) {
103
- const config = readAgentrcConfig(ctx, filename, fsImpl);
104
- if (!hasRetiredKey(config)) continue;
105
-
106
- stripRetiredKey(config);
107
- fsImpl.writeFileSync(
108
- resolveAgentrcPath(ctx, filename),
109
- `${JSON.stringify(config, null, 2)}\n`,
110
- );
111
- }
112
- },
113
- };
24
+ keys: [{ path: ['planning', 'codebaseSnapshot'], pruneDepth: 1 }],
25
+ });
@@ -1,19 +1,9 @@
1
1
  // lib/migrations/steps/2.32.0-retire-lint-baseline-command.js
2
2
  /**
3
- * Story #5004 follow-up — strip the retired `project.commands.lintBaseline`
4
- * key from a consumer's `.agentrc.json`.
5
- *
6
- * #5004 (PR #5019) deleted `lint-baseline.js` and
7
- * `lib/orchestration/lint-baseline-service.js`: the CLI spawned the configured
8
- * command and parsed ESLint-shaped JSON, a shape this repo's own `npm run
9
- * lint` (Biome + markdownlint fan-out) never produced, and nothing invoked the
10
- * service. The key left the runtime AJV schema, the published mirror,
11
- * `.agentrc.json` and `agentrc-reference.json` in the same change.
12
- *
13
- * `project.commands` carries `additionalProperties: false`, so a consumer
14
- * whose config still sets `lintBaseline` hits a hard validation failure on
15
- * upgrade, not a warning. This step strips the key before that check runs —
16
- * the same contract-cutover pattern as `2.11.0-retire-max-seed-words.js`.
3
+ * Story #5004 — strip the retired `project.commands.lintBaseline` from a
4
+ * consumer's config. The framework-owned lint-baseline capture CLI is gone,
5
+ * and the `commands` block carries `additionalProperties: false`, so a
6
+ * config still naming the command fails validation on upgrade.
17
7
  *
18
8
  * It sweeps **both** config surfaces. `config-resolver.js` deep-merges
19
9
  * `.agentrc.local.json` over `.agentrc.json` and validates the result, so a
@@ -27,6 +17,11 @@
27
17
  * `baselines/lint.json`; only the framework-owned capture shell is gone, so
28
18
  * nothing here touches the baseline file or the gate config.
29
19
  *
20
+ * An emptied `commands` block is deliberately left in place (`pruneDepth: 0`):
21
+ * unlike `planning.complexityGate`, `project` is a required block and an empty
22
+ * `commands` object is valid against the schema, so pruning it would be a
23
+ * cosmetic edit to a config the consumer owns.
24
+ *
30
25
  * This step is also the carrier for the release note #5004 never emitted: its
31
26
  * commit ships the `BREAKING CHANGE:` footer that the squash subject on `main`
32
27
  * (`227e1af4`) lacks, and which release-please therefore could not surface.
@@ -34,94 +29,12 @@
34
29
  * fix that stops the next one being lost.
35
30
  */
36
31
 
37
- import nodeFs from 'node:fs';
38
- import path from 'node:path';
39
-
40
- /**
41
- * Both config surfaces the resolver reads. `.agentrc.local.json` is deep-merged
42
- * over the base *before* the AJV gate runs
43
- * (`.agents/scripts/lib/config-resolver.js`), so a `lintBaseline` surviving in
44
- * the overlay fails validation exactly as one in the base would — and a step
45
- * that swept only the base would leave that consumer hard-broken with no
46
- * self-service remedy, since re-running `mandrel update` would keep reporting
47
- * clean. The overlay is operator-owned and gitignored, which is why it is easy
48
- * to forget and why the sweep has to name it explicitly.
49
- */
50
- const AGENTRC_FILENAMES = Object.freeze([
51
- '.agentrc.json',
52
- '.agentrc.local.json',
53
- ]);
54
-
55
- /**
56
- * @param {unknown} ctx
57
- * @param {string} filename
58
- * @returns {string}
59
- */
60
- function resolveAgentrcPath(ctx, filename) {
61
- const projectRoot = ctx?.projectRoot ?? process.cwd();
62
- return path.join(projectRoot, filename);
63
- }
32
+ import { createRetireAgentrcKeyStep } from '../helpers/retire-agentrc-key.js';
64
33
 
65
- /**
66
- * @param {unknown} ctx
67
- * @param {typeof nodeFs} fsImpl
68
- * @param {string} filename
69
- * @returns {object | null}
70
- */
71
- function readAgentrcConfig(ctx, fsImpl, filename) {
72
- try {
73
- const raw = fsImpl.readFileSync(resolveAgentrcPath(ctx, filename), 'utf8');
74
- return JSON.parse(raw);
75
- } catch {
76
- return null;
77
- }
78
- }
79
-
80
- /**
81
- * @param {object | null} config
82
- * @returns {boolean}
83
- */
84
- function hasRetiredKey(config) {
85
- const commands = config?.project?.commands;
86
- return Boolean(commands) && Object.hasOwn(commands, 'lintBaseline');
87
- }
88
-
89
- export const retireLintBaselineCommand = {
34
+ export const retireLintBaselineCommand = createRetireAgentrcKeyStep({
90
35
  version: '2.32.0',
91
36
  description:
92
37
  'strip retired project.commands.lintBaseline from .agentrc.json ' +
93
38
  '(the framework lint-baseline capture CLI is gone — Story #5004)',
94
- /**
95
- * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
96
- * @returns {boolean}
97
- */
98
- detect(ctx) {
99
- const fsImpl = ctx?.fs ?? nodeFs;
100
- return AGENTRC_FILENAMES.some((filename) =>
101
- hasRetiredKey(readAgentrcConfig(ctx, fsImpl, filename)),
102
- );
103
- },
104
- /**
105
- * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
106
- * @returns {void}
107
- */
108
- apply(ctx) {
109
- const fsImpl = ctx?.fs ?? nodeFs;
110
- for (const filename of AGENTRC_FILENAMES) {
111
- const config = readAgentrcConfig(ctx, fsImpl, filename);
112
- // An absent overlay is the common case, not an error.
113
- if (!config || !hasRetiredKey(config)) continue;
114
-
115
- delete config.project.commands.lintBaseline;
116
- // An emptied `commands` block is left in place: unlike
117
- // `planning.complexityGate`, `project` is a required block and an empty
118
- // `commands` object is valid against the schema, so pruning it would be
119
- // a cosmetic edit to a config the consumer owns.
120
-
121
- fsImpl.writeFileSync(
122
- resolveAgentrcPath(ctx, filename),
123
- `${JSON.stringify(config, null, 2)}\n`,
124
- );
125
- }
126
- },
127
- };
39
+ keys: [{ path: ['project', 'commands', 'lintBaseline'], pruneDepth: 0 }],
40
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mandrel",
3
- "version": "2.35.0",
3
+ "version": "2.37.0",
4
4
  "description": "Claude Code-first opinionated workflow framework: instructions, skills, rules, and SDLC workflows that govern AI coding assistants.",
5
5
  "files": [
6
6
  ".agents/",
@@ -19,7 +19,9 @@
19
19
  "test": "node .agents/scripts/run-tests.js",
20
20
  "test:quick": "node .agents/scripts/run-tests.js --tier quick",
21
21
  "test:integration": "node .agents/scripts/run-tests.js --tier integration",
22
+ "test:e2e": "node .agents/scripts/run-tests.js --tier e2e",
22
23
  "test:profile": "node .agents/scripts/run-test-profile.js",
24
+ "test:census": "SPAWN_CENSUS_OUT=\"${SPAWN_CENSUS_OUT:-temp/census.json}\" NODE_OPTIONS=\"--require $PWD/tests/fixtures/spawn-census.cjs\" node .agents/scripts/run-tests.js",
23
25
  "test:isolate": "node .agents/scripts/test-isolate.js",
24
26
  "verify": "node .agents/scripts/run-verify.js",
25
27
  "typecheck": "node -e \"process.exit(0)\"",
@@ -31,9 +33,9 @@
31
33
  "baselines:scope": "node .agents/scripts/check-baseline-scope.js",
32
34
  "baselines:prune": "node .agents/scripts/prune-baseline-orphans.js",
33
35
  "lint:md": "markdownlint-cli2 \".agents/**/*.md\" \"*.md\" \"!node_modules/**\" \"!.worktrees/**\"",
34
- "lint": "node .agents/scripts/run-lint.js && npm run docs:check",
35
- "docs:gen": "node .agents/scripts/generate-config-docs.js && node .agents/scripts/generate-lifecycle-docs.js && node .agents/scripts/generate-workflows-doc.js && node .agents/scripts/generate-lens-checklists.js",
36
- "docs:check": "node .agents/scripts/generate-config-docs.js --check && node .agents/scripts/generate-lifecycle-docs.js --check && node .agents/scripts/generate-workflows-doc.js --check && node .agents/scripts/generate-lens-checklists.js --check && node .agents/scripts/check-doc-links.js && npm run skills:check",
36
+ "lint": "node .agents/scripts/run-lint.js && node .agents/scripts/check-generated-validator.js --check && npm run docs:check",
37
+ "docs:gen": "node .agents/scripts/generate-config-docs.js && node .agents/scripts/generate-workflows-doc.js && node .agents/scripts/generate-lens-checklists.js",
38
+ "docs:check": "node .agents/scripts/generate-config-docs.js --check && node .agents/scripts/generate-workflows-doc.js --check && node .agents/scripts/generate-lens-checklists.js --check && node .agents/scripts/check-doc-links.js && npm run skills:check",
37
39
  "skills:index": "node .agents/scripts/generate-skills-index.js",
38
40
  "skills:check": "node .agents/scripts/generate-skills-index.js --check",
39
41
  "format": "biome format --write .",
@@ -47,6 +49,8 @@
47
49
  "cyclomatic:update": "node .agents/scripts/check-cyclomatic.js --update",
48
50
  "dead-exports:update": "node .agents/scripts/update-dead-exports-baseline.js && node .agents/scripts/update-dead-exports-baseline.js --production",
49
51
  "check:knip-entries": "node .agents/scripts/check-knip-entries.js",
52
+ "validator:gen": "node .agents/scripts/check-generated-validator.js",
53
+ "validator:check": "node .agents/scripts/check-generated-validator.js --check",
50
54
  "check:schema-refs": "node .agents/scripts/check-schema-references.js",
51
55
  "check:workflow-citations": "node .agents/scripts/check-workflow-citations.js",
52
56
  "crap:check": "node .agents/scripts/check-baselines.js --gate crap",
@@ -101,7 +105,7 @@
101
105
  "markdownlint-cli2": "^0.22.1",
102
106
  "memfs": "^4.57.7",
103
107
  "node-pty": "^1.0.0",
104
- "typescript": ">=5.0.0"
108
+ "typescript": "^6.0.3"
105
109
  },
106
110
  "dependencies": {
107
111
  "ajv": "^8.20.0",