mandrel 1.58.0 → 1.59.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 (129) hide show
  1. package/.agents/README.md +89 -87
  2. package/.agents/docs/SDLC.md +11 -7
  3. package/.agents/docs/workflows.md +2 -1
  4. package/.agents/schemas/audit-rules.json +20 -0
  5. package/.agents/scripts/acceptance-eval.js +20 -3
  6. package/.agents/scripts/assert-branch.js +1 -3
  7. package/.agents/scripts/bootstrap.js +1 -1
  8. package/.agents/scripts/check-arch-cycles.js +360 -0
  9. package/.agents/scripts/coverage-capture.js +24 -3
  10. package/.agents/scripts/epic-deliver-preflight.js +5 -3
  11. package/.agents/scripts/epic-deliver-prepare.js +12 -4
  12. package/.agents/scripts/epic-execute-record-wave.js +1 -1
  13. package/.agents/scripts/evidence-gate.js +1 -1
  14. package/.agents/scripts/git-rebase-and-resolve.js +1 -1
  15. package/.agents/scripts/hierarchy-gate.js +34 -14
  16. package/.agents/scripts/lib/baselines/kinds/coverage.js +33 -149
  17. package/.agents/scripts/lib/baselines/kinds/duplication.js +27 -116
  18. package/.agents/scripts/lib/baselines/kinds/kind-factory.js +192 -0
  19. package/.agents/scripts/lib/baselines/kinds/lighthouse.js +34 -133
  20. package/.agents/scripts/lib/baselines/kinds/maintainability.js +31 -124
  21. package/.agents/scripts/lib/baselines/kinds/mutation.js +25 -111
  22. package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +59 -0
  23. package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +37 -0
  24. package/.agents/scripts/lib/baselines/writer.js +1 -1
  25. package/.agents/scripts/lib/close-validation/commands.js +188 -0
  26. package/.agents/scripts/lib/close-validation/gates.js +235 -0
  27. package/.agents/scripts/lib/close-validation/process.js +101 -0
  28. package/.agents/scripts/lib/close-validation/projections/maintainability.js +1 -1
  29. package/.agents/scripts/lib/close-validation/runner.js +325 -0
  30. package/.agents/scripts/lib/close-validation/telemetry.js +70 -0
  31. package/.agents/scripts/lib/config/quality.js +6 -6
  32. package/.agents/scripts/lib/config-resolver.js +2 -5
  33. package/.agents/scripts/lib/coverage-capture.js +147 -4
  34. package/.agents/scripts/lib/cpu-pool.js +14 -0
  35. package/.agents/scripts/lib/crap-utils.js +6 -11
  36. package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +87 -0
  37. package/.agents/scripts/lib/git-utils.js +24 -22
  38. package/.agents/scripts/lib/maintainability-engine.js +1 -1
  39. package/.agents/scripts/lib/maintainability-utils.js +4 -187
  40. package/.agents/scripts/lib/observability/perf-report-readers.js +32 -23
  41. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +80 -6
  42. package/.agents/scripts/lib/orchestration/code-review.js +90 -77
  43. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +5 -12
  44. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +14 -14
  45. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +2 -2
  46. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +184 -49
  47. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +1 -1
  48. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +26 -2
  49. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +26 -6
  50. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +7 -20
  51. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +1 -2
  52. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -6
  53. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +103 -0
  54. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +22 -64
  55. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +38 -76
  56. package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +2 -2
  57. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +4 -16
  58. package/.agents/scripts/lib/orchestration/file-assumptions.js +4 -3
  59. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +144 -0
  60. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
  61. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +7 -7
  62. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +3 -3
  63. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +7 -7
  64. package/.agents/scripts/lib/orchestration/preflight-cache.js +35 -12
  65. package/.agents/scripts/lib/orchestration/review-providers/codex.js +5 -60
  66. package/.agents/scripts/lib/orchestration/review-providers/native.js +7 -6
  67. package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +105 -0
  68. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +7 -59
  69. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +2 -4
  70. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +1 -1
  71. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -4
  72. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +32 -35
  73. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +1 -2
  74. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +451 -503
  75. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +8 -2
  76. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +47 -2
  77. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +2 -2
  78. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +358 -54
  79. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +1 -1
  80. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +3 -2
  81. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +30 -3
  82. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +5 -18
  83. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +3 -3
  84. package/.agents/scripts/lib/orchestration/story-close-recovery.js +33 -16
  85. package/.agents/scripts/lib/orchestration/story-reachability.js +47 -0
  86. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +2 -33
  87. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +42 -64
  88. package/.agents/scripts/lib/orchestration/ticketing/reads.js +9 -0
  89. package/.agents/scripts/lib/orchestration/ticketing/state.js +50 -436
  90. package/.agents/scripts/lib/orchestration/ticketing/transition.js +471 -0
  91. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  92. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +1 -1
  93. package/.agents/scripts/lib/orchestration/wave-record-projection.js +1 -7
  94. package/.agents/scripts/lib/project-root.js +17 -0
  95. package/.agents/scripts/lib/story-adjacency.js +76 -0
  96. package/.agents/scripts/lib/story-lifecycle.js +1 -1
  97. package/.agents/scripts/lib/transpile.js +93 -0
  98. package/.agents/scripts/lib/wave-runner/tick.js +4 -153
  99. package/.agents/scripts/lib/workers/crap-worker.js +1 -1
  100. package/.agents/scripts/lib/workers/maintainability-report-worker.js +1 -1
  101. package/.agents/scripts/lib/worktree/lifecycle/creation.js +20 -2
  102. package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +90 -0
  103. package/.agents/scripts/lib/worktree/lifecycle/reap.js +26 -8
  104. package/.agents/scripts/lib/worktree/node-modules-strategy.js +74 -0
  105. package/.agents/scripts/providers/github/tickets.js +110 -6
  106. package/.agents/scripts/run-lint.js +9 -0
  107. package/.agents/scripts/run-tests.js +24 -4
  108. package/.agents/scripts/stories-wave-tick.js +8 -5
  109. package/.agents/scripts/story-init.js +149 -10
  110. package/.agents/scripts/sync-branch-from-base.js +1 -1
  111. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +1 -1
  112. package/.agents/workflows/audit-documentation.md +226 -0
  113. package/.agents/workflows/epic-deliver.md +16 -23
  114. package/.agents/workflows/epic-plan.md +1 -1
  115. package/.agents/workflows/helpers/epic-deliver-story.md +17 -28
  116. package/.agents/workflows/helpers/single-story-deliver.md +2 -1
  117. package/.agents/workflows/onboard.md +4 -3
  118. package/.agents/workflows/story-deliver.md +1 -1
  119. package/README.md +13 -8
  120. package/lib/cli/init.js +336 -0
  121. package/package.json +2 -1
  122. package/.agents/scripts/lib/auto-refresh-baselines.js +0 -308
  123. package/.agents/scripts/lib/close-validation.js +0 -897
  124. package/.agents/scripts/lib/orchestration/cascade-grouping.js +0 -275
  125. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +0 -69
  126. package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +0 -221
  127. package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +0 -123
  128. package/.agents/scripts/lib/task-utils.js +0 -26
  129. package/.agents/scripts/story-deliver-prepare.js +0 -267
@@ -32,6 +32,7 @@
32
32
  */
33
33
 
34
34
  import { spawnSync } from 'node:child_process';
35
+ import { parseProviderFindings } from './parse-findings.js';
35
36
  import { renderDepthDirective } from './review-depth.js';
36
37
 
37
38
  /**
@@ -138,65 +139,12 @@ export function mapSecurityReviewSeverity(raw) {
138
139
  * @throws {Error} when stdout is not parseable JSON.
139
140
  */
140
141
  export function parseSecurityReviewFindings(rawStdout) {
141
- const text = (rawStdout ?? '').trim();
142
- if (text.length === 0) return [];
143
-
144
- let parsed;
145
- try {
146
- parsed = JSON.parse(text);
147
- } catch (err) {
148
- throw new Error(
149
- `[security-review] Failed to parse /security-review stdout as JSON: ${
150
- err?.message ?? err
151
- }`,
152
- );
153
- }
154
-
155
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
156
- if (Array.isArray(parsed.findings)) parsed = parsed.findings;
157
- else if (parsed.result !== undefined) parsed = parsed.result;
158
- else if (parsed.data !== undefined) parsed = parsed.data;
159
- }
160
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
161
- if (Array.isArray(parsed.findings)) parsed = parsed.findings;
162
- }
163
-
164
- if (!Array.isArray(parsed)) return [];
165
-
166
- /** @type {Finding[]} */
167
- const findings = [];
168
- for (const entry of parsed) {
169
- if (!entry || typeof entry !== 'object') continue;
170
- const title =
171
- typeof entry.title === 'string' && entry.title.trim().length > 0
172
- ? entry.title.trim()
173
- : null;
174
- const body =
175
- typeof entry.body === 'string' && entry.body.trim().length > 0
176
- ? entry.body
177
- : typeof entry.message === 'string' && entry.message.trim().length > 0
178
- ? entry.message
179
- : null;
180
- if (!title || !body) continue;
181
- /** @type {Finding} */
182
- const finding = {
183
- severity: mapSecurityReviewSeverity(entry.severity),
184
- title,
185
- body,
186
- category:
187
- typeof entry.category === 'string' && entry.category.length > 0
188
- ? entry.category
189
- : 'security',
190
- };
191
- if (typeof entry.file === 'string' && entry.file.length > 0) {
192
- finding.file = entry.file;
193
- }
194
- if (Number.isInteger(entry.line) && entry.line > 0) {
195
- finding.line = entry.line;
196
- }
197
- findings.push(finding);
198
- }
199
- return findings;
142
+ return parseProviderFindings(rawStdout, {
143
+ errorPrefix:
144
+ '[security-review] Failed to parse /security-review stdout as JSON',
145
+ mapSeverity: mapSecurityReviewSeverity,
146
+ defaultCategory: 'security',
147
+ });
200
148
  }
201
149
 
202
150
  /**
@@ -22,10 +22,8 @@
22
22
  * that mock the upstream module URLs.
23
23
  */
24
24
 
25
- import {
26
- buildDefaultGates as defaultBuildDefaultGates,
27
- runCloseValidation as defaultRunCloseValidation,
28
- } from '../../../close-validation.js';
25
+ import { buildDefaultGates as defaultBuildDefaultGates } from '../../../close-validation/gates.js';
26
+ import { runCloseValidation as defaultRunCloseValidation } from '../../../close-validation/runner.js';
29
27
  import { Logger } from '../../../Logger.js';
30
28
 
31
29
  /**
@@ -10,7 +10,7 @@
10
10
 
11
11
  import path from 'node:path';
12
12
  import { parseSprintArgs } from '../../../cli-args.js';
13
- import { PROJECT_ROOT } from '../../../config-resolver.js';
13
+ import { PROJECT_ROOT } from '../../../project-root.js';
14
14
 
15
15
  /**
16
16
  * Resolve a flag value from an explicit override, a parsed CLI arg, or a
@@ -1,9 +1,7 @@
1
1
  import nodeFs from 'node:fs';
2
2
  import path from 'node:path';
3
- import {
4
- buildDefaultGates,
5
- runCloseValidation,
6
- } from '../../close-validation.js';
3
+ import { buildDefaultGates } from '../../close-validation/gates.js';
4
+ import { runCloseValidation } from '../../close-validation/runner.js';
7
5
  import { resolveConfig } from '../../config-resolver.js';
8
6
  import { getStoryBranch, gitSync } from '../../git-utils.js';
9
7
  import { Logger } from '../../Logger.js';
@@ -39,16 +39,20 @@
39
39
  */
40
40
 
41
41
  import {
42
- acquireLease,
43
- normalizeOperatorHandle,
44
- releaseLease,
45
- } from './ticket-lease.js';
42
+ acquireLeaseFailClosed,
43
+ resolveOperatorFromCandidates,
44
+ } from './lease-guard-shared.js';
45
+ import { releaseLease } from './ticket-lease.js';
46
46
 
47
47
  /**
48
48
  * Resolve the operator handle used as the lease owner from resolved config.
49
- * Routes through the shared `normalizeOperatorHandle` so a leading `@` is
49
+ * Routes through the shared lease-guard kernel
50
+ * (`lease-guard-shared.resolveOperatorFromCandidates`) so a leading `@` is
50
51
  * stripped (the assignees API expects bare logins, not `@`-prefixed mentions)
51
- * and the self-held-claim comparison matches.
52
+ * and the self-held-claim comparison matches. The standalone surface's
53
+ * missing-handle policy is `'throw'` (intentional divergence from the plan
54
+ * path's `'null'`): init has no best-effort leg that can degrade, so an
55
+ * unowned lease must refuse immediately.
52
56
  *
53
57
  * @param {object} config Resolved `.agentrc.json` config.
54
58
  * @returns {string} Bare operator handle.
@@ -58,17 +62,16 @@ import {
58
62
  * path cannot safely serialise concurrent runs.
59
63
  */
60
64
  export function resolveOperator(config) {
61
- const handle = normalizeOperatorHandle(config?.github?.operatorHandle);
62
- if (handle === null) {
63
- throw new Error(
65
+ return resolveOperatorFromCandidates({
66
+ candidates: [config?.github?.operatorHandle],
67
+ missingHandleBehavior: 'throw',
68
+ missingHandleMessage:
64
69
  'single-story lease: no operator identity is configured. ' +
65
- 'github.operatorHandle is unset or still the shipped `@[USERNAME]` ' +
66
- 'placeholder, so the standalone Story lease has no owner. Set your own ' +
67
- 'handle in .agentrc.local.json (e.g. { "github": { "operatorHandle": ' +
68
- '"@your-login" } }) and re-run.',
69
- );
70
- }
71
- return handle;
70
+ 'github.operatorHandle is unset or still the shipped `@[USERNAME]` ' +
71
+ 'placeholder, so the standalone Story lease has no owner. Set your own ' +
72
+ 'handle in .agentrc.local.json (e.g. { "github": { "operatorHandle": ' +
73
+ '"@your-login" } }) and re-run.',
74
+ });
72
75
  }
73
76
 
74
77
  /**
@@ -99,31 +102,25 @@ export async function acquireStoryLease({
99
102
  now,
100
103
  }) {
101
104
  const owner = operator ?? resolveOperator(config);
102
- // Fail closed: with no live-heartbeat source on the standalone path, treat a
103
- // foreign assignee as a live claim. Anchoring `heartbeatAt` to the same
104
- // `now` the primitive evaluates against makes `isClaimLive` return true for
105
- // any foreign owner, so `acquireLease` refuses unless `steal` is set.
106
- const resolvedNow =
107
- typeof now === 'number' && Number.isFinite(now) ? now : Date.now();
108
- const result = await acquireLease({
105
+ // Fail closed: with no live-heartbeat source on the standalone path, the
106
+ // shared kernel anchors `heartbeatAt` to the same `now` the primitive
107
+ // evaluates against, so `isClaimLive` returns true for any foreign owner
108
+ // and `acquireLease` refuses unless `steal` is set.
109
+ return acquireLeaseFailClosed({
109
110
  provider,
110
111
  ticketId: storyId,
111
112
  operator: owner,
112
- heartbeatAt: resolvedNow,
113
113
  steal,
114
114
  config,
115
- now: resolvedNow,
116
- });
117
- if (!result.acquired) {
118
- throw new Error(
115
+ now,
116
+ anchorHeartbeatToNow: true,
117
+ renderRefusal: (result) =>
119
118
  `single-story lease: Story #${storyId} is currently held by @${result.owner}. ` +
120
- 'Another /single-story-deliver run owns this Story. Coordinate with that ' +
121
- 'operator, or re-run with --steal to forcibly transfer the claim once you ' +
122
- 'have confirmed the other run is dead. (The standalone path has no Epic ' +
123
- 'heartbeat ledger, so a foreign assignee always blocks unless stolen.)',
124
- );
125
- }
126
- return result;
119
+ 'Another /single-story-deliver run owns this Story. Coordinate with that ' +
120
+ 'operator, or re-run with --steal to forcibly transfer the claim once you ' +
121
+ 'have confirmed the other run is dead. (The standalone path has no Epic ' +
122
+ 'heartbeat ledger, so a foreign assignee always blocks unless stolen.)',
123
+ });
127
124
  }
128
125
 
129
126
  /**
@@ -12,9 +12,8 @@
12
12
 
13
13
  import fs from 'node:fs';
14
14
  import path from 'node:path';
15
-
16
- import { PROJECT_ROOT } from '../config-resolver.js';
17
15
  import { Logger } from '../Logger.js';
16
+ import { PROJECT_ROOT } from '../project-root.js';
18
17
 
19
18
  const POLICY_HEADING_RE = /^## Policy Capsule\s*$/;
20
19
  const ANY_H2_RE = /^## /;