mandrel 2.64.0 → 2.66.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 (105) hide show
  1. package/.agents/agents/acceptance-critic.md +8 -7
  2. package/.agents/agents/auditor.md +20 -20
  3. package/.agents/agents/plan-critic.md +8 -7
  4. package/.agents/agents/story-worker.md +7 -7
  5. package/.agents/audit-checklists/quality.md +3 -0
  6. package/.agents/docs/agentrc-reference.json +1 -9
  7. package/.agents/docs/configuration.md +8 -7
  8. package/.agents/docs/execution-reference.md +27 -5
  9. package/.agents/instructions.md +10 -12
  10. package/.agents/rules/ci-remediation.md +3 -3
  11. package/.agents/rules/gherkin-standards.md +3 -2
  12. package/.agents/rules/git-conventions-reference.md +12 -3
  13. package/.agents/rules/git-conventions.md +9 -7
  14. package/.agents/rules/testing-standards.md +8 -7
  15. package/.agents/runtime-deps.json +1 -1
  16. package/.agents/schemas/agentrc.schema.json +6 -13
  17. package/.agents/schemas/audit-rules.schema.json +1 -1
  18. package/.agents/schemas/story-deliver-terminal.schema.json +5 -0
  19. package/.agents/scripts/bootstrap.js +102 -91
  20. package/.agents/scripts/check-context-budget.js +1 -1
  21. package/.agents/scripts/lib/ITicketingProvider.js +1 -3
  22. package/.agents/scripts/lib/audit-suite/findings.js +1 -17
  23. package/.agents/scripts/lib/audit-suite/frontmatter.js +0 -28
  24. package/.agents/scripts/lib/audit-suite/index.js +0 -6
  25. package/.agents/scripts/lib/audit-suite/selector.js +0 -31
  26. package/.agents/scripts/lib/baselines/duplication-scanner.js +17 -7
  27. package/.agents/scripts/lib/bootstrap/agents-md-fold.js +156 -0
  28. package/.agents/scripts/lib/bootstrap/commit-push.js +1 -1
  29. package/.agents/scripts/lib/bootstrap/manifest.js +2 -2
  30. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +91 -107
  31. package/.agents/scripts/lib/cli/standard-args.js +60 -76
  32. package/.agents/scripts/lib/cli-args.js +26 -0
  33. package/.agents/scripts/lib/config/gates/shared.js +3 -3
  34. package/.agents/scripts/lib/config/review-chain-default.js +13 -0
  35. package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
  36. package/.agents/scripts/lib/config-settings-schema-quality.js +11 -13
  37. package/.agents/scripts/lib/doc-tiers.js +25 -6
  38. package/.agents/scripts/lib/feedback-loop/graduate-steps.js +205 -0
  39. package/.agents/scripts/lib/feedback-loop/graduator-core.js +47 -782
  40. package/.agents/scripts/lib/feedback-loop/graduator-gh.js +449 -0
  41. package/.agents/scripts/lib/generated/agentrc-validator.js +1 -1
  42. package/.agents/scripts/lib/observability/close-telemetry.js +330 -0
  43. package/.agents/scripts/lib/observability/metrics-ledger.js +0 -72
  44. package/.agents/scripts/lib/observability/runtime-friction.js +2 -0
  45. package/.agents/scripts/lib/observability/signal-validator.js +17 -5
  46. package/.agents/scripts/lib/orchestration/code-review.js +33 -6
  47. package/.agents/scripts/lib/orchestration/epic-rollup.js +29 -12
  48. package/.agents/scripts/lib/orchestration/merge-block-class.js +20 -4
  49. package/.agents/scripts/lib/orchestration/merge-poll.js +41 -22
  50. package/.agents/scripts/lib/orchestration/plan-metrics.js +76 -63
  51. package/.agents/scripts/lib/orchestration/required-checks.js +147 -0
  52. package/.agents/scripts/lib/orchestration/review-providers/code-review.js +203 -0
  53. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +29 -4
  54. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +3 -2
  55. package/.agents/scripts/lib/orchestration/run-epilogue.js +6 -0
  56. package/.agents/scripts/lib/orchestration/single-story-close/failed-terminal.js +1 -0
  57. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +2 -12
  58. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +370 -268
  59. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +21 -7
  60. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +112 -82
  61. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +4 -0
  62. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +393 -313
  63. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +12 -87
  64. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +3 -0
  65. package/.agents/scripts/lib/orchestration/ticket-validator.js +19 -36
  66. package/.agents/scripts/lib/signals/detectors/common.js +63 -51
  67. package/.agents/scripts/lib/templates/decomposer-prompts.js +5 -24
  68. package/.agents/scripts/lib/transpile.js +28 -3
  69. package/.agents/scripts/providers/github/issues.js +14 -23
  70. package/.agents/scripts/single-story-close.js +10 -2
  71. package/.agents/scripts/single-story-confirm-merge.js +267 -238
  72. package/.agents/scripts/sync-claude-agents.js +1 -1
  73. package/.agents/skills/core/idea-refinement/SKILL.md +6 -6
  74. package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -2
  75. package/.agents/workflows/audit-architecture.md +5 -4
  76. package/.agents/workflows/audit-documentation.md +5 -5
  77. package/.agents/workflows/audit-performance.md +10 -10
  78. package/.agents/workflows/audit-quality.md +42 -7
  79. package/.agents/workflows/helpers/acceptance-self-eval.md +9 -9
  80. package/.agents/workflows/helpers/audit-lens-core.md +30 -57
  81. package/.agents/workflows/helpers/code-review.md +15 -38
  82. package/.agents/workflows/helpers/deliver-digest.md +2 -2
  83. package/.agents/workflows/helpers/deliver-reference.md +7 -3
  84. package/.agents/workflows/helpers/deliver-story.md +9 -1
  85. package/.agents/workflows/helpers/parallel-tooling.md +16 -18
  86. package/.agents/workflows/helpers/plan-reference.md +9 -8
  87. package/.agents/workflows/mandrel-deliver.md +3 -2
  88. package/.agents/workflows/mandrel-plan.md +11 -7
  89. package/.agents/workflows/mandrel-update.md +5 -3
  90. package/docs/CHANGELOG.md +57 -0
  91. package/lib/cli/claude-code-version.js +73 -0
  92. package/lib/cli/doctor.js +2 -2
  93. package/lib/cli/guarded-sync.js +87 -0
  94. package/lib/cli/registry.js +9 -0
  95. package/lib/cli/sync-agents.js +9 -92
  96. package/lib/cli/sync-commands.js +9 -101
  97. package/lib/cli/uninstall.js +37 -9
  98. package/lib/migrations/index.js +2 -0
  99. package/lib/migrations/steps/2.65.0-fold-claude-md-into-agents-md.js +38 -0
  100. package/package.json +3 -2
  101. package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +0 -99
  102. package/.agents/scripts/lib/audit-suite/runner.js +0 -205
  103. package/.agents/scripts/lib/audit-suite/substitutions.js +0 -96
  104. package/.agents/scripts/lib/audit-suite/workflow-loader.js +0 -37
  105. package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +0 -234
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Doc-tier resolver: partition the repo's docs into read-tiers with byte
3
- * sizes for the context-budget ratchet (`alwaysLoaded` = CLAUDE.md's
4
- * `@`-import closure; `agentBoot` and `workflowOnDemand` are recorded, not
3
+ * sizes for the context-budget ratchet (`alwaysLoaded` = the host entry
4
+ * doc's `@`-import closure; `agentBoot` and `workflowOnDemand` are recorded, not
5
5
  * gated). A path lives only in its highest tier. Emits paths and byte
6
6
  * counts, never file contents.
7
7
  */
@@ -11,9 +11,27 @@ import path from 'node:path';
11
11
  import { resolveWorkflowClosures } from './workflow-closure.js';
12
12
 
13
13
  /**
14
- * @type {string}
14
+ * Claude Code's project-root entry precedence: it reads `CLAUDE.md` when
15
+ * present and falls back to `AGENTS.md` only when `CLAUDE.md` is absent.
16
+ *
17
+ * @type {string[]}
18
+ */
19
+ const ENTRY_DOC_PRECEDENCE = ['CLAUDE.md', 'AGENTS.md'];
20
+
21
+ /**
22
+ * The root entry doc the host loads, or null when neither exists.
23
+ *
24
+ * @param {string} root
25
+ * @param {{ fs?: FsLike }} [opts]
26
+ * @returns {string | null}
15
27
  */
16
- const ENTRY_DOC = 'CLAUDE.md';
28
+ function resolveEntryDoc(root, { fs = nodeFs } = {}) {
29
+ return (
30
+ ENTRY_DOC_PRECEDENCE.find((name) =>
31
+ fs.existsSync(path.resolve(root, name)),
32
+ ) ?? null
33
+ );
34
+ }
17
35
 
18
36
  /**
19
37
  * @type {string[]}
@@ -89,8 +107,9 @@ function fileEntry(root, rel, fs) {
89
107
  * @returns {Array<{ path: string, bytes: number }>}
90
108
  */
91
109
  export function resolveAlwaysLoadedClosure(root, { fs = nodeFs } = {}) {
92
- const entryAbs = path.resolve(root, ENTRY_DOC);
93
- if (!fs.existsSync(entryAbs)) return [];
110
+ const entry = resolveEntryDoc(root, { fs });
111
+ if (entry === null) return [];
112
+ const entryAbs = path.resolve(root, entry);
94
113
 
95
114
  const visited = new Set();
96
115
  const entries = new Map();
@@ -0,0 +1,205 @@
1
+ /**
2
+ * One finding's walk through the graduator, as route → dedup → file steps.
3
+ * Every step receives the gh context as the single `gh` value.
4
+ */
5
+
6
+ import { routeOwnership } from '../github/framework-repo.js';
7
+ import {
8
+ createFollowUpIssue,
9
+ ensureIssueLabels,
10
+ findExistingFollowUp,
11
+ probePathStatus,
12
+ searchFollowUpByMarker,
13
+ updateFollowUpIssue,
14
+ } from './graduator-gh.js';
15
+
16
+ /** @returns {Promise<string|null>} A skip reason, or `null` to continue. */
17
+ export async function probeFindingPath({ finding, gitRef, gh }) {
18
+ // A path-less finding would misclassify as `file-removed`; skip the probe.
19
+ if (typeof finding.path !== 'string' || finding.path.trim().length === 0) {
20
+ return null;
21
+ }
22
+ const { exists, probeError } = await probePathStatus({
23
+ ref: gitRef,
24
+ path: finding.path,
25
+ ...gh,
26
+ });
27
+ if (probeError) return 'probe-error';
28
+ return exists ? null : 'file-removed';
29
+ }
30
+
31
+ /**
32
+ * Unroutable findings are deferred and named, never re-pointed at the
33
+ * consumer's repo.
34
+ *
35
+ * @returns {{ source: string, routedRepo: object|null, deferred: object|null, skipReason: string|null }}
36
+ */
37
+ export function routeFinding({
38
+ finding,
39
+ classifier,
40
+ repos,
41
+ currentRepo,
42
+ logger,
43
+ spec,
44
+ }) {
45
+ const source = classifier(finding.path, null);
46
+ const routing = routeOwnership({ bucket: source, repos, currentRepo });
47
+ if (!routing.routable) {
48
+ const logLine = `[${spec.fnName}] unroutable ${source} finding (${routing.missingKey} is unset) — not filed: ${finding.title ?? finding.path ?? `finding ${finding.index}`}`;
49
+ logger?.warn?.(logLine);
50
+ const deferred = {
51
+ finding,
52
+ routedRepo: null,
53
+ source,
54
+ logLine,
55
+ missingKey: routing.missingKey,
56
+ };
57
+ return { source, routedRepo: null, deferred, skipReason: 'unroutable' };
58
+ }
59
+ const routedRepo = routing.routedRepo;
60
+ if (routing.crossRepo) {
61
+ const logLine = spec.buildCrossRepoLog({ finding, routedRepo, source });
62
+ logger?.info?.(logLine);
63
+ const deferred = { finding, routedRepo, source, logLine };
64
+ return { source, routedRepo, deferred, skipReason: 'cross-repo-deferred' };
65
+ }
66
+ return { source, routedRepo, deferred: null, skipReason: null };
67
+ }
68
+
69
+ /**
70
+ * Checks the content-hash marker, then the legacy ordinal marker so
71
+ * pre-fingerprint filings are not re-filed.
72
+ *
73
+ * @returns {Promise<{ number: number|null, state: string, url: string }|null>}
74
+ */
75
+ export async function dedupFinding({
76
+ finding,
77
+ epicId,
78
+ routedRepo,
79
+ contentMarker,
80
+ gh,
81
+ spec,
82
+ }) {
83
+ const probe = (marker) =>
84
+ searchFollowUpByMarker({
85
+ marker,
86
+ owner: routedRepo.owner,
87
+ repo: routedRepo.repo,
88
+ ...gh,
89
+ });
90
+ const hit = await probe(contentMarker);
91
+ if (hit) return hit;
92
+ const legacyMarker =
93
+ typeof spec.buildLegacyMarker === 'function'
94
+ ? spec.buildLegacyMarker(epicId, finding.index)
95
+ : null;
96
+ return legacyMarker ? probe(legacyMarker) : null;
97
+ }
98
+
99
+ /**
100
+ * An open, numbered match is refreshed and recorded on `filed` as
101
+ * `action: 'updated'` (a live write, counted like a creation). Anything else
102
+ * is `already-filed` and untouched: a closed follow-up is a human's decision.
103
+ */
104
+ export async function recordRecurrence(walk, existing, body) {
105
+ const { finding, source, routedRepo, envelope, decorate, skip, gh } = walk;
106
+ walk.filedMarkers?.add(walk.contentMarker);
107
+ if (existing.state !== 'open' || existing.number === null) {
108
+ skip('already-filed');
109
+ return;
110
+ }
111
+ const updated = await updateFollowUpIssue({
112
+ owner: routedRepo.owner,
113
+ repo: routedRepo.repo,
114
+ number: existing.number,
115
+ body,
116
+ ...gh,
117
+ });
118
+ if (updated.error) {
119
+ envelope.errors.push(
120
+ `finding ${finding.index} (${finding.path}): ${updated.error}`,
121
+ );
122
+ return;
123
+ }
124
+ envelope.filed.push(
125
+ decorate(
126
+ {
127
+ index: finding.index,
128
+ action: 'updated',
129
+ issueNumber: existing.number,
130
+ severity: finding.severity,
131
+ path: finding.path,
132
+ source,
133
+ repo: `${routedRepo.owner}/${routedRepo.repo}`,
134
+ url: updated.url || existing.url || null,
135
+ },
136
+ finding,
137
+ ),
138
+ );
139
+ }
140
+
141
+ function strongReadExisting(walk, labels) {
142
+ const { epicId, finding, contentMarker, routedRepo, spec } = walk;
143
+ return findExistingFollowUp({
144
+ markers:
145
+ typeof spec.buildMatchTokens === 'function'
146
+ ? spec.buildMatchTokens({ epicId, finding, contentMarker })
147
+ : [contentMarker],
148
+ owner: routedRepo.owner,
149
+ repo: routedRepo.repo,
150
+ labels,
151
+ ...walk.gh,
152
+ });
153
+ }
154
+
155
+ /** Cap → label mint → strong-read confirm → create. */
156
+ export async function fileFinding(walk, { title, body, labels }) {
157
+ const { finding, routedRepo, envelope, decorate, skip, gh } = walk;
158
+ if (envelope.filed.length >= walk.maxFilingsPerRun) {
159
+ return skip('cap-reached');
160
+ }
161
+ // Before the strong read too: `gh issue list --label <absent>` exits 0
162
+ // with `[]`, silently defeating the dedup confirm.
163
+ const ensured = await ensureIssueLabels({
164
+ owner: routedRepo.owner,
165
+ repo: routedRepo.repo,
166
+ labels,
167
+ labelCache: walk.labelCache,
168
+ ...gh,
169
+ });
170
+ envelope.errors.push(...ensured.errors);
171
+ if (ensured.missing.length > 0) return skip('label-ensure-failed');
172
+
173
+ const confirmed = await strongReadExisting(walk, labels);
174
+ if (confirmed) return recordRecurrence(walk, confirmed, body);
175
+
176
+ const created = await createFollowUpIssue({
177
+ owner: routedRepo.owner,
178
+ repo: routedRepo.repo,
179
+ title,
180
+ body,
181
+ labels,
182
+ ...gh,
183
+ });
184
+ if (created.error) {
185
+ envelope.errors.push(
186
+ `finding ${finding.index} (${finding.path}): ${created.error}`,
187
+ );
188
+ return;
189
+ }
190
+ walk.filedMarkers?.add(walk.contentMarker);
191
+ envelope.filed.push(
192
+ decorate(
193
+ {
194
+ index: finding.index,
195
+ action: 'created',
196
+ severity: finding.severity,
197
+ path: finding.path,
198
+ source: walk.source,
199
+ repo: `${routedRepo.owner}/${routedRepo.repo}`,
200
+ url: created.url,
201
+ },
202
+ finding,
203
+ ),
204
+ );
205
+ }