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.
- package/.agents/agents/acceptance-critic.md +8 -7
- package/.agents/agents/auditor.md +20 -20
- package/.agents/agents/plan-critic.md +8 -7
- package/.agents/agents/story-worker.md +7 -7
- package/.agents/audit-checklists/quality.md +3 -0
- package/.agents/docs/agentrc-reference.json +1 -9
- package/.agents/docs/configuration.md +8 -7
- package/.agents/docs/execution-reference.md +27 -5
- package/.agents/instructions.md +10 -12
- package/.agents/rules/ci-remediation.md +3 -3
- package/.agents/rules/gherkin-standards.md +3 -2
- package/.agents/rules/git-conventions-reference.md +12 -3
- package/.agents/rules/git-conventions.md +9 -7
- package/.agents/rules/testing-standards.md +8 -7
- package/.agents/runtime-deps.json +1 -1
- package/.agents/schemas/agentrc.schema.json +6 -13
- package/.agents/schemas/audit-rules.schema.json +1 -1
- package/.agents/schemas/story-deliver-terminal.schema.json +5 -0
- package/.agents/scripts/bootstrap.js +102 -91
- package/.agents/scripts/check-context-budget.js +1 -1
- package/.agents/scripts/lib/ITicketingProvider.js +1 -3
- package/.agents/scripts/lib/audit-suite/findings.js +1 -17
- package/.agents/scripts/lib/audit-suite/frontmatter.js +0 -28
- package/.agents/scripts/lib/audit-suite/index.js +0 -6
- package/.agents/scripts/lib/audit-suite/selector.js +0 -31
- package/.agents/scripts/lib/baselines/duplication-scanner.js +17 -7
- package/.agents/scripts/lib/bootstrap/agents-md-fold.js +156 -0
- package/.agents/scripts/lib/bootstrap/commit-push.js +1 -1
- package/.agents/scripts/lib/bootstrap/manifest.js +2 -2
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +91 -107
- package/.agents/scripts/lib/cli/standard-args.js +60 -76
- package/.agents/scripts/lib/cli-args.js +26 -0
- package/.agents/scripts/lib/config/gates/shared.js +3 -3
- package/.agents/scripts/lib/config/review-chain-default.js +13 -0
- package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-quality.js +11 -13
- package/.agents/scripts/lib/doc-tiers.js +25 -6
- package/.agents/scripts/lib/feedback-loop/graduate-steps.js +205 -0
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +47 -782
- package/.agents/scripts/lib/feedback-loop/graduator-gh.js +449 -0
- package/.agents/scripts/lib/generated/agentrc-validator.js +1 -1
- package/.agents/scripts/lib/observability/close-telemetry.js +330 -0
- package/.agents/scripts/lib/observability/metrics-ledger.js +0 -72
- package/.agents/scripts/lib/observability/runtime-friction.js +2 -0
- package/.agents/scripts/lib/observability/signal-validator.js +17 -5
- package/.agents/scripts/lib/orchestration/code-review.js +33 -6
- package/.agents/scripts/lib/orchestration/epic-rollup.js +29 -12
- package/.agents/scripts/lib/orchestration/merge-block-class.js +20 -4
- package/.agents/scripts/lib/orchestration/merge-poll.js +41 -22
- package/.agents/scripts/lib/orchestration/plan-metrics.js +76 -63
- package/.agents/scripts/lib/orchestration/required-checks.js +147 -0
- package/.agents/scripts/lib/orchestration/review-providers/code-review.js +203 -0
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +29 -4
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +3 -2
- package/.agents/scripts/lib/orchestration/run-epilogue.js +6 -0
- package/.agents/scripts/lib/orchestration/single-story-close/failed-terminal.js +1 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +2 -12
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +370 -268
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +21 -7
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +112 -82
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +4 -0
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +393 -313
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +12 -87
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +3 -0
- package/.agents/scripts/lib/orchestration/ticket-validator.js +19 -36
- package/.agents/scripts/lib/signals/detectors/common.js +63 -51
- package/.agents/scripts/lib/templates/decomposer-prompts.js +5 -24
- package/.agents/scripts/lib/transpile.js +28 -3
- package/.agents/scripts/providers/github/issues.js +14 -23
- package/.agents/scripts/single-story-close.js +10 -2
- package/.agents/scripts/single-story-confirm-merge.js +267 -238
- package/.agents/scripts/sync-claude-agents.js +1 -1
- package/.agents/skills/core/idea-refinement/SKILL.md +6 -6
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -2
- package/.agents/workflows/audit-architecture.md +5 -4
- package/.agents/workflows/audit-documentation.md +5 -5
- package/.agents/workflows/audit-performance.md +10 -10
- package/.agents/workflows/audit-quality.md +42 -7
- package/.agents/workflows/helpers/acceptance-self-eval.md +9 -9
- package/.agents/workflows/helpers/audit-lens-core.md +30 -57
- package/.agents/workflows/helpers/code-review.md +15 -38
- package/.agents/workflows/helpers/deliver-digest.md +2 -2
- package/.agents/workflows/helpers/deliver-reference.md +7 -3
- package/.agents/workflows/helpers/deliver-story.md +9 -1
- package/.agents/workflows/helpers/parallel-tooling.md +16 -18
- package/.agents/workflows/helpers/plan-reference.md +9 -8
- package/.agents/workflows/mandrel-deliver.md +3 -2
- package/.agents/workflows/mandrel-plan.md +11 -7
- package/.agents/workflows/mandrel-update.md +5 -3
- package/docs/CHANGELOG.md +57 -0
- package/lib/cli/claude-code-version.js +73 -0
- package/lib/cli/doctor.js +2 -2
- package/lib/cli/guarded-sync.js +87 -0
- package/lib/cli/registry.js +9 -0
- package/lib/cli/sync-agents.js +9 -92
- package/lib/cli/sync-commands.js +9 -101
- package/lib/cli/uninstall.js +37 -9
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/steps/2.65.0-fold-claude-md-into-agents-md.js +38 -0
- package/package.json +3 -2
- package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +0 -99
- package/.agents/scripts/lib/audit-suite/runner.js +0 -205
- package/.agents/scripts/lib/audit-suite/substitutions.js +0 -96
- package/.agents/scripts/lib/audit-suite/workflow-loader.js +0 -37
- 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` =
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
|
93
|
-
if (
|
|
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
|
+
}
|