mandrel 2.9.0 → 2.11.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/.markdownlint.json +4 -0
- package/.agents/agents/acceptance-critic.md +30 -5
- package/.agents/agents/auditor.md +36 -19
- package/.agents/agents/plan-critic.md +31 -5
- package/.agents/agents/story-worker.md +91 -100
- package/.agents/docs/configuration.md +39 -25
- package/.agents/docs/execution-reference.md +13 -0
- package/.agents/docs/workflows.md +1 -1
- package/.agents/instructions.md +131 -265
- package/.agents/rules/git-conventions.md +47 -83
- package/.agents/rules/orchestration-error-handling.md +28 -0
- package/.agents/schemas/agentrc.schema.json +36 -9
- package/.agents/schemas/validation-evidence.schema.json +3 -1
- package/.agents/scripts/acceptance-eval.js +10 -6
- package/.agents/scripts/apply-quality-bootstrap.js +1 -1
- package/.agents/scripts/check-test-temp-hygiene.js +438 -0
- package/.agents/scripts/deliver-recover.js +23 -6
- package/.agents/scripts/lib/audit-suite/index.js +5 -0
- package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +179 -0
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/baselines/env-overrides.js +33 -0
- package/.agents/scripts/lib/baselines/git-base.js +0 -0
- package/.agents/scripts/lib/baselines/preview-gates.js +5 -0
- package/.agents/scripts/lib/config/gates/maintainability.schema.js +10 -1
- package/.agents/scripts/lib/config/quality.js +13 -0
- package/.agents/scripts/lib/config/temp-paths.js +121 -1
- package/.agents/scripts/lib/config-settings-schema-delivery.js +30 -0
- package/.agents/scripts/lib/config-settings-schema.js +12 -16
- package/.agents/scripts/lib/observability/metrics-ledger.js +217 -0
- package/.agents/scripts/lib/observability/runtime-friction.js +7 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +45 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +97 -4
- package/.agents/scripts/lib/orchestration/check-baselines/phases/parse-args.js +7 -0
- package/.agents/scripts/lib/orchestration/complexity-gate.js +533 -93
- package/.agents/scripts/lib/orchestration/deliver-recover.js +137 -10
- package/.agents/scripts/lib/orchestration/merge-block-class.js +36 -15
- package/.agents/scripts/lib/orchestration/merge-poll.js +213 -0
- package/.agents/scripts/lib/orchestration/plan-context.js +119 -3
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +182 -9
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +29 -2
- package/.agents/scripts/lib/orchestration/plan-metrics.js +31 -82
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +153 -2
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +221 -14
- package/.agents/scripts/lib/orchestration/resolve-stories.js +12 -1
- package/.agents/scripts/lib/orchestration/review-depth.js +9 -4
- package/.agents/scripts/lib/orchestration/review-providers/native.js +34 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -3
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +230 -79
- package/.agents/scripts/lib/orchestration/spec-budget.js +78 -0
- package/.agents/scripts/lib/orchestration/story-body-gate.js +72 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +89 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +73 -0
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +6 -0
- package/.agents/scripts/lib/orchestration/ticket-validator.js +18 -62
- package/.agents/scripts/lib/templates/decomposer-prompts.js +13 -6
- package/.agents/scripts/lib/test-env.js +65 -0
- package/.agents/scripts/plan-context.js +84 -9
- package/.agents/scripts/plan-critics.js +115 -3
- package/.agents/scripts/plan-persist.js +11 -1
- package/.agents/scripts/plan-run-epilogue.js +1 -1
- package/.agents/scripts/resolve-stories.js +2 -0
- package/.agents/scripts/single-story-confirm-merge.js +65 -5
- package/.agents/scripts/stories-wave-tick.js +1 -1
- package/.agents/workflows/deliver.md +88 -230
- package/.agents/workflows/helpers/acceptance-self-eval.md +16 -5
- package/.agents/workflows/helpers/deliver-reference.md +171 -0
- package/.agents/workflows/helpers/deliver-story-reference.md +223 -0
- package/.agents/workflows/helpers/deliver-story.md +115 -432
- package/.agents/workflows/helpers/plan-reference.md +246 -0
- package/.agents/workflows/plan.md +108 -304
- package/docs/CHANGELOG.md +46 -0
- package/lib/cli/registry.js +31 -14
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +92 -0
- package/package.json +1 -1
|
@@ -35,6 +35,19 @@
|
|
|
35
35
|
* sub-agent. This module takes the cluster index as an INPUT and returns a
|
|
36
36
|
* decision for that one cluster; it has no way to add or remove clusters.
|
|
37
37
|
*
|
|
38
|
+
* ## One verdict-owner per cluster (Story #4723)
|
|
39
|
+
*
|
|
40
|
+
* The resolved decision names the cluster's **single verdict owner** via
|
|
41
|
+
* `verdictOwner`: `'fresh-critic'` when the mode is `fresh`,
|
|
42
|
+
* `'inline-self-eval'` when the mode is `inline`. Exactly one pass authors
|
|
43
|
+
* the cluster's verdict — the fresh maker-blind critic OR the
|
|
44
|
+
* contract-identical inline self-eval, never both, and never an additional
|
|
45
|
+
* pre-pass self-assessment before the owner runs. `acceptance-eval.js` is
|
|
46
|
+
* the deterministic SCORER of that one authored verdict (schema validation,
|
|
47
|
+
* round cap, proceed/redraft/block) — it is not a third pass over the
|
|
48
|
+
* criteria. This removes a redundant pass only; it never removes a
|
|
49
|
+
* cluster's verdict (the M4-B floor above holds).
|
|
50
|
+
*
|
|
38
51
|
* ## Tier rules (per cluster, `standard` profile)
|
|
39
52
|
*
|
|
40
53
|
* - `high` level → `fresh` (a sensitive path was touched — a
|
|
@@ -63,10 +76,23 @@
|
|
|
63
76
|
* `undefined` / malformed inputs degrade to `fresh` + `full` ceremony.
|
|
64
77
|
*
|
|
65
78
|
* @typedef {'fresh'|'inline'} CeremonyMode
|
|
79
|
+
* @typedef {'fresh-critic'|'inline-self-eval'} VerdictOwner
|
|
66
80
|
* @typedef {import('./review-depth.js').ChangeLevel} ChangeLevel
|
|
67
81
|
* @typedef {'minimal'|'standard'|'strict'} CeremonyProfile
|
|
68
82
|
*/
|
|
69
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Map a resolved ceremony mode to the cluster's single verdict owner
|
|
86
|
+
* (Story #4723). Total: any non-`fresh` value maps to the inline
|
|
87
|
+
* self-eval owner, mirroring how the mode itself degrades.
|
|
88
|
+
*
|
|
89
|
+
* @param {CeremonyMode} mode
|
|
90
|
+
* @returns {VerdictOwner}
|
|
91
|
+
*/
|
|
92
|
+
export function verdictOwnerForMode(mode) {
|
|
93
|
+
return mode === 'fresh' ? 'fresh-critic' : 'inline-self-eval';
|
|
94
|
+
}
|
|
95
|
+
|
|
70
96
|
/** @type {readonly CeremonyProfile[]} */
|
|
71
97
|
export const CEREMONY_PROFILES = Object.freeze([
|
|
72
98
|
'minimal',
|
|
@@ -133,9 +159,28 @@ export function sampledFresh(clusterIndex, rate) {
|
|
|
133
159
|
* reason: string,
|
|
134
160
|
* sampled: boolean,
|
|
135
161
|
* profile: CeremonyProfile,
|
|
162
|
+
* verdictOwner: VerdictOwner,
|
|
136
163
|
* }}
|
|
137
164
|
*/
|
|
138
165
|
export function resolveCeremonyForRisk(input = {}) {
|
|
166
|
+
const decision = resolveCeremonyDecision(input);
|
|
167
|
+
return { ...decision, verdictOwner: verdictOwnerForMode(decision.mode) };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Internal mode/reason resolution — the tier rules and sampling floor.
|
|
172
|
+
* `resolveCeremonyForRisk` decorates the result with the single
|
|
173
|
+
* `verdictOwner` derived from the mode (Story #4723).
|
|
174
|
+
*
|
|
175
|
+
* @param {Parameters<typeof resolveCeremonyForRisk>[0]} [input]
|
|
176
|
+
* @returns {{
|
|
177
|
+
* mode: CeremonyMode,
|
|
178
|
+
* reason: string,
|
|
179
|
+
* sampled: boolean,
|
|
180
|
+
* profile: CeremonyProfile,
|
|
181
|
+
* }}
|
|
182
|
+
*/
|
|
183
|
+
function resolveCeremonyDecision(input = {}) {
|
|
139
184
|
const derivedLevel =
|
|
140
185
|
input && typeof input === 'object' ? input.derivedLevel : undefined;
|
|
141
186
|
const clusterIndex =
|
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
* @module lib/orchestration/check-baselines/phases/evaluate
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
resolveBundleSizeEnvOverrides,
|
|
12
|
+
resolveMaintainabilityRefreshOverrides,
|
|
13
|
+
} from '../../../baselines/env-overrides.js';
|
|
14
|
+
import { readRangeSubjectsTouchingFile } from '../../../baselines/git-base.js';
|
|
11
15
|
import {
|
|
12
16
|
checkKernelVersion,
|
|
13
17
|
getKindModule,
|
|
@@ -17,6 +21,10 @@ import { Logger } from '../../../Logger.js';
|
|
|
17
21
|
import { isIgnoredByGlobs } from '../../../maintainability-utils.js';
|
|
18
22
|
import { applyTolerance, evaluateCompare, runCompareStage } from './compare.js';
|
|
19
23
|
import { applyFloors, flattenBreaches } from './floors.js';
|
|
24
|
+
import { DEFAULT_BASELINE_PATHS } from './parse-args.js';
|
|
25
|
+
|
|
26
|
+
/** Default refresh-tag substring when the gate omits `refreshTag`. */
|
|
27
|
+
const DEFAULT_REFRESH_TAG = 'baseline-refresh:';
|
|
20
28
|
|
|
21
29
|
/**
|
|
22
30
|
* Defense-in-depth against an `ignoreGlobs`-poisoned baseline (Epic #4326
|
|
@@ -113,6 +121,88 @@ function applyBundleSizeAcknowledgment(kind, compareOutput, env) {
|
|
|
113
121
|
};
|
|
114
122
|
}
|
|
115
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Resolve the maintainability refresh trigger (Story #4731). Two paths, either
|
|
126
|
+
* of which acknowledges — mirroring the bundle-size acknowledge but adding the
|
|
127
|
+
* commit-tagged trigger the breach message already documents:
|
|
128
|
+
*
|
|
129
|
+
* 1. Env parity: `MAINTAINABILITY_REFRESH=1` (the manual override).
|
|
130
|
+
* 2. Commit tag: a commit in the compared range `<baseRef>..HEAD` whose
|
|
131
|
+
* subject contains the configured `refreshTag` AND whose diff touches the
|
|
132
|
+
* maintainability baseline file. One-shot by construction — once merged,
|
|
133
|
+
* the refreshed baseline becomes the base and the tag leaves the range.
|
|
134
|
+
*
|
|
135
|
+
* The tag is matched as a plain substring of a conventional commit subject, so
|
|
136
|
+
* commitlint stays satisfied (e.g. `chore(baselines): baseline-refresh: …`).
|
|
137
|
+
*
|
|
138
|
+
* @returns {{ triggered: boolean, reasons: string[] }}
|
|
139
|
+
*/
|
|
140
|
+
function resolveMaintainabilityRefreshTrigger({ gateBlock, cmp, cwd, env }) {
|
|
141
|
+
const reasons = [];
|
|
142
|
+
const { acknowledged: envAck, overrides } =
|
|
143
|
+
resolveMaintainabilityRefreshOverrides(env);
|
|
144
|
+
if (envAck) reasons.push(...overrides);
|
|
145
|
+
|
|
146
|
+
const baseRef = cmp?.baseRef ?? null;
|
|
147
|
+
if (baseRef) {
|
|
148
|
+
const refreshTag =
|
|
149
|
+
typeof gateBlock?.refreshTag === 'string' && gateBlock.refreshTag.length
|
|
150
|
+
? gateBlock.refreshTag
|
|
151
|
+
: DEFAULT_REFRESH_TAG;
|
|
152
|
+
const baselinePath =
|
|
153
|
+
typeof gateBlock?.baselinePath === 'string' &&
|
|
154
|
+
gateBlock.baselinePath.length
|
|
155
|
+
? gateBlock.baselinePath
|
|
156
|
+
: DEFAULT_BASELINE_PATHS.maintainability;
|
|
157
|
+
const subjects = readRangeSubjectsTouchingFile(baseRef, baselinePath, {
|
|
158
|
+
cwd,
|
|
159
|
+
});
|
|
160
|
+
const match = subjects.find((s) => s.includes(refreshTag));
|
|
161
|
+
if (match) {
|
|
162
|
+
reasons.push(
|
|
163
|
+
`refresh commit "${match}" (subject contains ${JSON.stringify(refreshTag)}, touches ${baselinePath})`,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return { triggered: reasons.length > 0, reasons };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* One-shot maintainability refresh/acknowledge (Story #4731). When triggered
|
|
173
|
+
* (env flag OR a `baseline-refresh:`-tagged range commit touching the baseline),
|
|
174
|
+
* demote every maintainability head-vs-base regression to `unchanged` for this
|
|
175
|
+
* run only — floors still apply, so a row below its `min` floor still breaches.
|
|
176
|
+
* The trigger is read fresh every run and never persisted: post-merge the
|
|
177
|
+
* refreshed baseline is the new base and the tag leaves the range, so the
|
|
178
|
+
* ratchet returns to full strength automatically.
|
|
179
|
+
*
|
|
180
|
+
* No-op for every other kind.
|
|
181
|
+
*/
|
|
182
|
+
function applyMaintainabilityAcknowledgment(kind, compareOutput, ctx) {
|
|
183
|
+
if (kind !== 'maintainability') {
|
|
184
|
+
return { compareOutput, acknowledged: false };
|
|
185
|
+
}
|
|
186
|
+
const { triggered, reasons } = resolveMaintainabilityRefreshTrigger(ctx);
|
|
187
|
+
if (!triggered || compareOutput.regressions.length === 0) {
|
|
188
|
+
return { compareOutput, acknowledged: false };
|
|
189
|
+
}
|
|
190
|
+
Logger.warn(
|
|
191
|
+
`[maintainability] ⚠ ${reasons.join('; ')} — ` +
|
|
192
|
+
`${compareOutput.regressions.length} regression(s) acknowledged for this run only; ` +
|
|
193
|
+
'floors still enforced. This does not persist: once the refresh is the ' +
|
|
194
|
+
'new base the ratchet re-enforces at full strength.',
|
|
195
|
+
);
|
|
196
|
+
return {
|
|
197
|
+
acknowledged: true,
|
|
198
|
+
compareOutput: {
|
|
199
|
+
...compareOutput,
|
|
200
|
+
regressions: [],
|
|
201
|
+
unchanged: [...compareOutput.unchanged, ...compareOutput.regressions],
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
116
206
|
function buildGateReport({
|
|
117
207
|
kind,
|
|
118
208
|
gateBlock,
|
|
@@ -171,11 +261,14 @@ export async function evaluateKind({
|
|
|
171
261
|
rawCompare,
|
|
172
262
|
gateBlock.tolerance ?? null,
|
|
173
263
|
);
|
|
174
|
-
const
|
|
264
|
+
const bundleAck = applyBundleSizeAcknowledgment(kind, toleratedCompare, env);
|
|
265
|
+
const miAck = applyMaintainabilityAcknowledgment(
|
|
175
266
|
kind,
|
|
176
|
-
|
|
177
|
-
env,
|
|
267
|
+
bundleAck.compareOutput,
|
|
268
|
+
{ gateBlock, cmp, cwd, env },
|
|
178
269
|
);
|
|
270
|
+
const compareOutput = miAck.compareOutput;
|
|
271
|
+
const acknowledged = bundleAck.acknowledged || miAck.acknowledged;
|
|
179
272
|
return buildGateReport({
|
|
180
273
|
kind,
|
|
181
274
|
gateBlock,
|
|
@@ -49,6 +49,13 @@ Env vars:
|
|
|
49
49
|
"unchanged" for this run only (floors still
|
|
50
50
|
enforced). Never persisted — the next run without
|
|
51
51
|
this flag re-enforces the ratchet.
|
|
52
|
+
MAINTAINABILITY_REFRESH=1
|
|
53
|
+
One-shot acknowledge for a deliberate maintainability
|
|
54
|
+
baseline refresh: demotes maintainability head-vs-base
|
|
55
|
+
regressions to "unchanged" for this run only (floors
|
|
56
|
+
still enforced). Env-parity override for the
|
|
57
|
+
'baseline-refresh:'-tagged range commit that touches
|
|
58
|
+
baselines/maintainability.json. Never persisted.
|
|
52
59
|
|
|
53
60
|
Exit codes:
|
|
54
61
|
0 every enabled gate passes
|