mandrel 2.54.0 → 2.56.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/story-worker.md +24 -23
- package/.agents/audit-checklists/accessibility.md +0 -3
- package/.agents/audit-checklists/mobile.md +0 -4
- package/.agents/docs/agentrc-reference.json +8 -2
- package/.agents/docs/configuration.md +5 -0
- package/.agents/rules/ci-remediation.md +39 -21
- package/.agents/schemas/agentrc.schema.json +34 -1
- package/.agents/schemas/lifecycle/merge.unlanded.schema.json +2 -1
- package/.agents/schemas/story-deliver-terminal.schema.json +1 -0
- package/.agents/scripts/audit-to-stories.js +374 -76
- package/.agents/scripts/check-audit-attribution.js +119 -62
- package/.agents/scripts/check-test-portability.js +512 -0
- package/.agents/scripts/coverage-capture.js +17 -10
- package/.agents/scripts/evidence-gate.js +31 -4
- package/.agents/scripts/file-ci-gap.js +306 -0
- package/.agents/scripts/generate-workflows-doc.js +65 -14
- package/.agents/scripts/git-cleanup.js +4 -0
- package/.agents/scripts/lib/ITicketingProvider.js +78 -0
- package/.agents/scripts/lib/audit-advisories.js +195 -0
- package/.agents/scripts/lib/audit-attribution.js +22 -0
- package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +25 -1
- package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +80 -29
- package/.agents/scripts/lib/audit-to-stories/finding-adapter.js +5 -1
- package/.agents/scripts/lib/audit-to-stories/issue-corpus.js +162 -0
- package/.agents/scripts/lib/audit-to-stories/issue-index.js +83 -0
- package/.agents/scripts/lib/audit-to-stories/issues-file.js +121 -0
- package/.agents/scripts/lib/audit-to-stories/ledger-commit.js +61 -115
- package/.agents/scripts/lib/audit-to-stories/ledger-pr.js +347 -0
- package/.agents/scripts/lib/audit-to-stories/ledger-record.js +126 -0
- package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +169 -44
- package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +11 -0
- package/.agents/scripts/lib/baselines/merge-envelopes.js +298 -32
- package/.agents/scripts/lib/bootstrap/baseline-merge-driver.js +180 -14
- package/.agents/scripts/lib/cli-args.js +26 -0
- package/.agents/scripts/lib/close-validation/gates.js +113 -7
- package/.agents/scripts/lib/close-validation/process.js +7 -3
- package/.agents/scripts/lib/close-validation/runner.js +62 -11
- package/.agents/scripts/lib/config/ci.js +28 -9
- package/.agents/scripts/lib/config-settings-schema-delivery.js +7 -0
- package/.agents/scripts/lib/config-settings-schema.js +52 -1
- package/.agents/scripts/lib/coverage-capture-fullscope.js +23 -11
- package/.agents/scripts/lib/coverage-capture-incremental.js +22 -16
- package/.agents/scripts/lib/coverage-capture-usage.js +5 -1
- package/.agents/scripts/lib/coverage-capture.js +77 -3
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +53 -13
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +71 -25
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +18 -25
- package/.agents/scripts/lib/{audit-to-stories/ledger.js → findings/audit-ledger.js} +131 -24
- package/.agents/scripts/lib/findings/route-finding.js +42 -2
- package/.agents/scripts/lib/full-suite-lock.js +232 -6
- package/.agents/scripts/lib/generated/agentrc-validator.js +1 -1
- package/.agents/scripts/lib/git/sync-from-base.js +130 -13
- package/.agents/scripts/lib/github/framework-repo.js +148 -2
- package/.agents/scripts/lib/label-constants.js +6 -1
- package/.agents/scripts/lib/observability/source-classifier.js +2 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +10 -2
- package/.agents/scripts/lib/orchestration/check-baselines/phases/refresh-ack.js +75 -15
- package/.agents/scripts/lib/orchestration/ci-gap-intake.js +605 -0
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +13 -8
- package/.agents/scripts/lib/orchestration/deliver-recover.js +82 -43
- package/.agents/scripts/lib/orchestration/dependency-candidates.js +8 -4
- package/.agents/scripts/lib/orchestration/epic-candidates.js +9 -4
- package/.agents/scripts/lib/orchestration/epic-container.js +66 -4
- package/.agents/scripts/lib/orchestration/epic-rollup.js +233 -84
- package/.agents/scripts/lib/orchestration/file-assumptions.js +218 -16
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +93 -7
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +22 -6
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/parse-args.js +26 -5
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +13 -2
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +35 -5
- package/.agents/scripts/lib/orchestration/merge-block-class.js +18 -3
- package/.agents/scripts/lib/orchestration/merge-poll.js +284 -40
- package/.agents/scripts/lib/orchestration/plan-persist/audit-provenance.js +197 -0
- package/.agents/scripts/lib/orchestration/plan-persist/epic-adoption.js +49 -2
- package/.agents/scripts/lib/orchestration/plan-persist/epic-ops.js +43 -7
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +39 -3
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +5 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +3 -0
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +63 -0
- package/.agents/scripts/lib/orchestration/plan-persist/wave-serialisation.js +110 -0
- package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +130 -40
- package/.agents/scripts/lib/orchestration/resolve-stories.js +44 -1
- package/.agents/scripts/lib/orchestration/review-providers/native.js +31 -11
- package/.agents/scripts/lib/orchestration/review-providers/scoped-lint.js +27 -24
- package/.agents/scripts/lib/orchestration/run-epilogue.js +63 -42
- package/.agents/scripts/lib/orchestration/single-story-close/close-note.js +81 -0
- package/.agents/scripts/lib/orchestration/single-story-close/failed-terminal.js +40 -51
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +10 -2
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +101 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +351 -28
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +27 -6
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +117 -22
- package/.agents/scripts/lib/orchestration/story-close/baseline-upward-writeback.js +94 -12
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +6 -1
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +32 -20
- package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -14
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +30 -0
- package/.agents/scripts/lib/orchestration/verify-credit.js +37 -0
- package/.agents/scripts/lib/pinned-override-notes.js +41 -53
- package/.agents/scripts/lib/pinned-override-resolve.js +212 -0
- package/.agents/scripts/lib/qa/resolve-qa-contract.js +18 -0
- package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +173 -9
- package/.agents/scripts/lib/skills/walk-skill-files.js +24 -7
- package/.agents/scripts/lib/test-temp.js +167 -30
- package/.agents/scripts/lib/validation-evidence.js +37 -0
- package/.agents/scripts/lib/wave-runner/footprint.js +167 -14
- package/.agents/scripts/lib/wave-runner/live-probe.js +7 -1
- package/.agents/scripts/lib/wave-runner/ready-set.js +1 -1
- package/.agents/scripts/merge-baseline.js +175 -21
- package/.agents/scripts/pr-watch-with-update.js +3 -2
- package/.agents/scripts/providers/github/errors.js +22 -1
- package/.agents/scripts/providers/github/issues.js +106 -1
- package/.agents/scripts/providers/github/sub-issue-add.js +18 -1
- package/.agents/scripts/providers/github.js +6 -0
- package/.agents/scripts/resolve-stories.js +44 -34
- package/.agents/scripts/single-story-close.js +5 -0
- package/.agents/scripts/stories-wave-tick.js +37 -13
- package/.agents/templates/docs/audit-sweep-runbook.md +41 -7
- package/.agents/workflows/audit-accessibility.md +16 -31
- package/.agents/workflows/audit-mobile.md +20 -37
- package/.agents/workflows/audit-to-stories.md +63 -27
- package/.agents/workflows/git-cleanup.md +17 -3
- package/.agents/workflows/helpers/audit-lens-core.md +45 -0
- package/.agents/workflows/helpers/deliver-digest.md +7 -6
- package/.agents/workflows/helpers/deliver-reference.md +35 -14
- package/.agents/workflows/helpers/deliver-story-reference.md +26 -8
- package/.agents/workflows/helpers/deliver-story.md +15 -12
- package/.agents/workflows/helpers/plan-reference.md +30 -0
- package/.agents/workflows/mandrel-plan.md +10 -13
- package/.agents/workflows/memory-consolidate.md +14 -9
- package/docs/CHANGELOG.md +37 -0
- package/lib/cli/registry.js +64 -21
- package/lib/cli/sync.js +27 -2
- package/package.json +7 -4
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
* [--no-auto-merge]
|
|
45
45
|
* [--wait-merge | --no-wait-merge]
|
|
46
46
|
* [--merge-watch-mode <sync|async>]
|
|
47
|
+
* [--rerun-advisory <n>]
|
|
47
48
|
* [--override-review-block <reason>]
|
|
48
49
|
*
|
|
49
50
|
* `--override-review-block <reason>` is the one sanctioned way
|
|
@@ -225,6 +226,10 @@ runAsCli(import.meta.url, main, {
|
|
|
225
226
|
['--wait-merge', 'Force the in-close merge wait.'],
|
|
226
227
|
['--no-wait-merge', 'Return as soon as the PR is open; do not wait.'],
|
|
227
228
|
['--max-wait-seconds <n>', 'Per-invocation merge-wait bound.'],
|
|
229
|
+
[
|
|
230
|
+
'--rerun-advisory <n>',
|
|
231
|
+
'How many times this close may re-run a failed ADVISORY (non-required) workflow run before blocking on it. Overrides `delivery.ci.rerunAdvisory` for one invocation; BOTH default to 0, so close spends no CI minutes and issues no GitHub mutation on an advisory red unless you ask. At n > 0 the failed run(s) are re-run within that allowance and the merge wait keeps polling inside its existing budget, landing or blocking on the re-run verdict.',
|
|
232
|
+
],
|
|
228
233
|
[
|
|
229
234
|
'--merge-watch-mode <sync|async>',
|
|
230
235
|
'Override delivery.mergeWatch.mode for this invocation only. `async` caps the merge wait to a short probe window and returns the resumable `pending` terminal instead of holding the foreground slot — pass it on every close of a multi-Story run. An invalid value exits non-zero before any phase runs.',
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
* inFlight: number,
|
|
56
56
|
* cycleError: string | null,
|
|
57
57
|
* wedged: { reason, stories: [{ id, unmetBlockers }] } | null,
|
|
58
|
-
* inFlightReservation: { available, withheld: [{ id, blockedBy, reason, source, paths }], note },
|
|
59
|
-
* footprintGuard: { mode, withheld: [{ id, blockedBy, scope, source, paths }], advisory, note }
|
|
58
|
+
* inFlightReservation: { available, withheld: [{ id, blockedBy, reason, source, paths, attribution }], note },
|
|
59
|
+
* footprintGuard: { mode, withheld: [{ id, blockedBy, scope, source, paths, attribution }], advisory, note }
|
|
60
60
|
* }
|
|
61
61
|
*
|
|
62
62
|
* `inFlightReservation` reports the cross-beat half of the co-dispatch guard
|
|
@@ -130,7 +130,10 @@ import { AGENT_LABELS } from './lib/label-constants.js';
|
|
|
130
130
|
import { parseIds } from './lib/orchestration/resolve-stories.js';
|
|
131
131
|
import { buildStoryAdjacency } from './lib/story-adjacency.js';
|
|
132
132
|
import { expandIdList } from './lib/util/parse-id-list.js';
|
|
133
|
-
import {
|
|
133
|
+
import {
|
|
134
|
+
OVERLAP_SOURCES,
|
|
135
|
+
renderScrapeAttribution,
|
|
136
|
+
} from './lib/wave-runner/footprint.js';
|
|
134
137
|
import {
|
|
135
138
|
createProbeContext,
|
|
136
139
|
probeLiveState,
|
|
@@ -237,7 +240,10 @@ Output envelope:
|
|
|
237
240
|
"blockedBy": 4949,
|
|
238
241
|
"reason": "in-flight-earlier-beat",
|
|
239
242
|
"source": "declared-overlap",
|
|
240
|
-
"paths": ["lib/shared.js"]
|
|
243
|
+
"paths": ["lib/shared.js"],
|
|
244
|
+
"attribution": [
|
|
245
|
+
{ "path": "lib/shared.js", "declared": true, "fields": [] }
|
|
246
|
+
]
|
|
241
247
|
}
|
|
242
248
|
],
|
|
243
249
|
"note": "..."
|
|
@@ -250,7 +256,14 @@ Output envelope:
|
|
|
250
256
|
"blockedBy": 4951,
|
|
251
257
|
"scope": "beat",
|
|
252
258
|
"source": "scraped-overlap",
|
|
253
|
-
"paths": ["lib/other.js"]
|
|
259
|
+
"paths": ["lib/other.js"],
|
|
260
|
+
"attribution": [
|
|
261
|
+
{
|
|
262
|
+
"path": "lib/other.js",
|
|
263
|
+
"declared": false,
|
|
264
|
+
"fields": ["body:Verify"]
|
|
265
|
+
}
|
|
266
|
+
]
|
|
254
267
|
}
|
|
255
268
|
],
|
|
256
269
|
"advisory": [],
|
|
@@ -268,7 +281,10 @@ footprintGuard names each Story withheld from THIS beat by a peer already
|
|
|
268
281
|
admitted on it — the half that used to be an unreported skip — and every
|
|
269
282
|
entry in either report carries the colliding paths plus a source tag
|
|
270
283
|
(declared-overlap when both changes[] declarations named the path, else
|
|
271
|
-
scraped-overlap from the text evidence)
|
|
284
|
+
scraped-overlap from the text evidence) and an "attribution" list naming, per
|
|
285
|
+
path, the field the scrape read it from ("title", "spec", or "body:<section>"
|
|
286
|
+
— so a path that reached the comparison only because every Story RUNS it in
|
|
287
|
+
"## Verify" says so). Its "mode" echoes
|
|
272
288
|
delivery.deliverRunner.footprintGuard: under "advisory" the collisions are
|
|
273
289
|
detected and listed in "advisory" but never withhold, and dispatch follows the
|
|
274
290
|
declared depends_on edges alone.
|
|
@@ -359,7 +375,7 @@ const RESERVATION_REASONS = Object.freeze({
|
|
|
359
375
|
* @param {object[]|null|undefined} inFlightRecords
|
|
360
376
|
* @param {Array<{id: number, blockedBy: number, source?: string, paths?: string[]}>} withheld
|
|
361
377
|
* @param {Iterable<number>} [foreignHeldIds] Ids held by a foreign lease.
|
|
362
|
-
* @returns {{ available: boolean, withheld: Array<{id: number, blockedBy: number, reason: string, source: string, paths: string[]}>, note: string|null }}
|
|
378
|
+
* @returns {{ available: boolean, withheld: Array<{id: number, blockedBy: number, reason: string, source: string, paths: string[], attribution: object[]}>, note: string|null }}
|
|
363
379
|
*/
|
|
364
380
|
export function buildReservationReport(
|
|
365
381
|
inFlightRecords,
|
|
@@ -390,6 +406,7 @@ export function buildReservationReport(
|
|
|
390
406
|
: RESERVATION_REASONS.EARLIER_BEAT,
|
|
391
407
|
source: w.source ?? OVERLAP_SOURCES.DECLARED,
|
|
392
408
|
paths: w.paths ?? [],
|
|
409
|
+
attribution: w.attribution ?? [],
|
|
393
410
|
}));
|
|
394
411
|
return {
|
|
395
412
|
available: true,
|
|
@@ -421,12 +438,16 @@ export function buildReservationReport(
|
|
|
421
438
|
*/
|
|
422
439
|
export function buildFootprintGuardReport(footprintWithholds, mode) {
|
|
423
440
|
const ledger = Array.isArray(footprintWithholds) ? footprintWithholds : [];
|
|
424
|
-
const project = ({ id, blockedBy, scope, source, paths }) => ({
|
|
441
|
+
const project = ({ id, blockedBy, scope, source, paths, attribution }) => ({
|
|
425
442
|
id,
|
|
426
443
|
blockedBy,
|
|
427
444
|
scope,
|
|
428
445
|
source,
|
|
429
446
|
paths,
|
|
447
|
+
// Story #5265: the per-path field attribution rides the entry itself, so
|
|
448
|
+
// a consumer reading the envelope never has to re-derive where a scraped
|
|
449
|
+
// path came from (and cannot get a different answer than the note did).
|
|
450
|
+
attribution: attribution ?? [],
|
|
430
451
|
});
|
|
431
452
|
const beat = ledger
|
|
432
453
|
.filter((w) => w.scope === WITHHOLD_SCOPES.BEAT && w.enforced)
|
|
@@ -453,10 +474,11 @@ export function buildFootprintGuardReport(footprintWithholds, mode) {
|
|
|
453
474
|
function footprintGuardNote(beat, advisory, mode) {
|
|
454
475
|
const detail = (entries) =>
|
|
455
476
|
entries
|
|
456
|
-
.map(
|
|
457
|
-
(w)
|
|
458
|
-
|
|
459
|
-
|
|
477
|
+
.map((w) => {
|
|
478
|
+
const scraped = renderScrapeAttribution(w.attribution);
|
|
479
|
+
const provenance = scraped ? `, scraped from ${scraped}` : '';
|
|
480
|
+
return `#${w.id} ← #${w.blockedBy} on ${w.paths.join(', ')} (${w.source}${provenance})`;
|
|
481
|
+
})
|
|
460
482
|
.join('; ');
|
|
461
483
|
if (beat.length > 0) {
|
|
462
484
|
return (
|
|
@@ -465,7 +487,9 @@ function footprintGuardNote(beat, advisory, mode) {
|
|
|
465
487
|
`Each is still eligible and re-admits on a later beat once its peer ` +
|
|
466
488
|
`lands. A ${OVERLAP_SOURCES.SCRAPED} source means the collision came ` +
|
|
467
489
|
`from path evidence in the Story text rather than from either ` +
|
|
468
|
-
`changes[] declaration
|
|
490
|
+
`changes[] declaration — the 'scraped from' clause names the field ` +
|
|
491
|
+
`each such path was read out of, so a path only cited in '## Verify' ` +
|
|
492
|
+
`is distinguishable from an unpredicted edit target.`
|
|
469
493
|
);
|
|
470
494
|
}
|
|
471
495
|
if (advisory.length > 0) {
|
|
@@ -51,6 +51,17 @@ to that declared tally. A mismatch — or a missing tally line — means the rep
|
|
|
51
51
|
is not trustworthy: a finding was malformed, a severity did not resolve onto the
|
|
52
52
|
canonical scale, or the lens truncated its own output.
|
|
53
53
|
|
|
54
|
+
The line is read **from the Executive Summary**, and a report declaring it more
|
|
55
|
+
than once fails as `duplicate-tally` naming both lines rather than adopting
|
|
56
|
+
whichever the scan reached first. Prose elsewhere in the report that quotes the
|
|
57
|
+
tally format is a second declaration as far as the check is concerned; move it
|
|
58
|
+
or reword it.
|
|
59
|
+
|
|
60
|
+
A `###` heading with no severity axis and no field bullets is read as a
|
|
61
|
+
**grouping header**, not as a finding — so a lens that emits `### Robust` with
|
|
62
|
+
`_No findings._` under it contributes zero findings and still cross-checks
|
|
63
|
+
against a zero tally.
|
|
64
|
+
|
|
54
65
|
`--auto` **fails closed** on any such failure. It exits non-zero having opened
|
|
55
66
|
no Issue and written no ledger, and names the offending report in
|
|
56
67
|
`summary.reportFailures[]`. `--allow-missing-tally` is a `--scan` affordance
|
|
@@ -70,6 +81,10 @@ stop surprising you:
|
|
|
70
81
|
node .agents/scripts/audit-to-stories.js --auto --dry-run
|
|
71
82
|
```
|
|
72
83
|
|
|
84
|
+
`--severity` is validated against the canonical scale: a typo (`--severity Hgh`)
|
|
85
|
+
exits non-zero naming the accepted levels rather than silently widening the run
|
|
86
|
+
to every finding.
|
|
87
|
+
|
|
73
88
|
`--dry-run` performs zero GitHub writes and skips the ledger write, printing
|
|
74
89
|
only the run summary. Read `totals.create` before you let the sweep file
|
|
75
90
|
anything: a first full-scope run over an un-audited repository can propose more
|
|
@@ -94,11 +109,24 @@ rejected.
|
|
|
94
109
|
`--ledger-commit` closes that loop. After the run summary has printed, and only
|
|
95
110
|
when the ledger actually changed, it:
|
|
96
111
|
|
|
97
|
-
1.
|
|
98
|
-
|
|
112
|
+
1. refuses, naming its step, if HEAD is not the base branch or there is no
|
|
113
|
+
`origin` — before writing anything,
|
|
114
|
+
2. creates `chore/audit-ledger-<YYYY-MM-DD>-<shortsha>` from `origin/<base>`,
|
|
115
|
+
3. commits **only** the ledger file, subject
|
|
99
116
|
`chore(audit): reconcile audit ledger <date>`,
|
|
100
|
-
|
|
101
|
-
|
|
117
|
+
4. pushes the branch,
|
|
118
|
+
5. opens a PR against your base branch, and
|
|
119
|
+
6. puts the checkout back on the branch it started from.
|
|
120
|
+
|
|
121
|
+
It prints one line on stderr saying what happened: the branch and the PR URL on
|
|
122
|
+
success, or the skip reason otherwise.
|
|
123
|
+
|
|
124
|
+
**It is re-runnable.** The `<shortsha>` is the base commit, so a retry on the
|
|
125
|
+
same day does not collide with the branch a failed run left behind — it
|
|
126
|
+
recognises it. A ledger already committed on an **unpushed** ledger branch
|
|
127
|
+
resumes at the push rather than reporting `ledger-unchanged` (the ledger file is
|
|
128
|
+
clean: it is committed, just not pushed). Across a failed push and its retry you
|
|
129
|
+
get exactly one PR.
|
|
102
130
|
|
|
103
131
|
**Auto-merge is never requested.** The ledger records machine-derived lifecycle
|
|
104
132
|
state, so a human glance before it lands is the point — nominate that reviewer
|
|
@@ -123,8 +151,10 @@ Those bodies are **audit prose**, not delivery-ready Specs: they describe a
|
|
|
123
151
|
symptom and a recommendation, not a scoped change with acceptance criteria a
|
|
124
152
|
worker can verify against.
|
|
125
153
|
|
|
126
|
-
Do not point `/mandrel-deliver` at a freshly-filed audit Story
|
|
127
|
-
|
|
154
|
+
Do not point `/mandrel-deliver` at a freshly-filed audit Story — and as of the
|
|
155
|
+
label guard below, you cannot: `resolve-stories.js` refuses a Story carrying no
|
|
156
|
+
`agent::*` label, naming this step, unless `--allow-unlabelled` is passed. Route
|
|
157
|
+
it through `/mandrel-plan` first — the planning pass is where the finding becomes a
|
|
128
158
|
capability slice with a `## Spec`, real `acceptance[]` items and runnable
|
|
129
159
|
`verify[]` lines. Planning is deliberately not automated here: deciding what a
|
|
130
160
|
finding is worth, and how far the fix should reach, is the judgement the sweep
|
|
@@ -172,6 +202,10 @@ to mint labels no taxonomy defines.
|
|
|
172
202
|
| --- | --- | --- |
|
|
173
203
|
| Non-zero exit, `summary.reportFailures[]` populated | A lens report's tally is missing or disagrees with the parse | Re-run that lens; never downgrade with `--allow-missing-tally` |
|
|
174
204
|
| `ledger.unpersisted: true` in the summary | No `origin`, or HEAD off the base branch | Re-run with `--ledger-commit`, or commit the ledger by hand |
|
|
175
|
-
| `--ledger-commit failed at step "..."` | git or `gh` failed at the named step | Fix the
|
|
205
|
+
| `--ledger-commit failed at step "..."` | git or `gh` failed at the named step | Fix the cause and re-run; a retry resumes rather than duplicating, and the summary above it is still valid |
|
|
206
|
+
| `--ledger-commit failed at step "verify-base-branch"` | HEAD is on a feature branch | Check out the base branch and re-run; nothing was committed |
|
|
207
|
+
| `[resolve-stories] ... carries no "agent::*" label` | An unenriched audit Story was named for delivery | Plan it (Step 5), or pass `--allow-unlabelled` deliberately |
|
|
208
|
+
| `[audit-to-stories] --severity "..." is not a severity` | A typo in the floor | Use one of the canonical levels |
|
|
209
|
+
| `duplicate-tally` in `summary.reportFailures[]` | The report declares the tally twice | Reword the prose copy; the Executive Summary line is the declaration |
|
|
176
210
|
| Same findings re-proposed every cycle | The ledger is not being committed | Adopt Step 4 |
|
|
177
211
|
| `totals.create` far larger than the team can absorb | Severity floor too low for a first full-scope run | Raise `--severity` and re-dry-run |
|
|
@@ -82,9 +82,9 @@ the project is configured.** Before any detection:
|
|
|
82
82
|
- **Design tokens:** the colour tokens (`tailwind.config.*`, CSS custom
|
|
83
83
|
properties, a theme object) whose literal values you need to compute contrast
|
|
84
84
|
ratios statically.
|
|
85
|
-
- **Runtime target (optional):** the
|
|
86
|
-
[
|
|
87
|
-
|
|
85
|
+
- **Runtime target (optional):** whatever the shared runtime scaffold in
|
|
86
|
+
[`helpers/audit-lens-core.md`](helpers/audit-lens-core.md#runtime-pass)
|
|
87
|
+
resolves. Note only whether one exists; the scaffold owns how it is found.
|
|
88
88
|
|
|
89
89
|
Record what exists. Every finding downstream is measured against _this
|
|
90
90
|
discovered surface and config_, not a generic ideal. If **no** frontend surface
|
|
@@ -135,31 +135,16 @@ Cover every static WCAG dimension:
|
|
|
135
135
|
|
|
136
136
|
## Step 2: Runtime verification mode (optional corroboration)
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
step** and note in the report that runtime corroboration was unavailable —
|
|
149
|
-
do not invent a URL and do not start an arbitrary dev server.
|
|
150
|
-
2. **Sample routes from the navigability SSOT.** Draw the routes to exercise
|
|
151
|
-
from the consumer's route/nav registry (`planning.navigation.navRegistry` /
|
|
152
|
-
`routeGlobs` — the same SSOT [`/audit-navigability`](audit-navigability.md)
|
|
153
|
-
reads), sampling a representative set (key personas' landing routes plus any
|
|
154
|
-
route in the change-set scope) rather than a single hardcoded page.
|
|
155
|
-
3. **Run an accessibility engine per sampled route.** Use the
|
|
156
|
-
`mcp__chrome-devtools__lighthouse_audit` tool's **Accessibility category**,
|
|
157
|
-
or run **axe** via the browser tooling, against each sampled `baseUrl`-rooted
|
|
158
|
-
route. Prefer a production-mode build.
|
|
159
|
-
4. **Median-of-3 or provisional.** Any runtime score or metric is subject to
|
|
160
|
-
run-to-run variance: capture a **median-of-3** (three runs per route, report
|
|
161
|
-
the median) before treating a number as authoritative. A single-run value is
|
|
162
|
-
reported **provisional** and never drives a Critical/High verdict on its own.
|
|
138
|
+
Run the shared runtime scaffold in
|
|
139
|
+
[`helpers/audit-lens-core.md`](helpers/audit-lens-core.md#runtime-pass) — it
|
|
140
|
+
owns target resolution, route sampling, median-of-3, and the two skip reasons
|
|
141
|
+
(no configured target; browser tooling unavailable). This lens's probe is its
|
|
142
|
+
step 3:
|
|
143
|
+
|
|
144
|
+
- **Run an accessibility engine per sampled route.** Use the
|
|
145
|
+
`mcp__chrome-devtools__lighthouse_audit` tool's **Accessibility category**, or
|
|
146
|
+
run **axe** via the browser tooling, against each sampled `baseUrl`-rooted
|
|
147
|
+
route. Prefer a production-mode build.
|
|
163
148
|
|
|
164
149
|
Corroborate static findings against the runtime results (a statically-flagged
|
|
165
150
|
contrast defect confirmed by the engine graduates from provisional to
|
|
@@ -168,8 +153,8 @@ confirmed), and surface runtime-only violations the static pass could not see.
|
|
|
168
153
|
## Report additions
|
|
169
154
|
|
|
170
155
|
Beyond the shared skeleton, the Executive Summary states the runtime mode's
|
|
171
|
-
status (ran against `<env
|
|
156
|
+
status (ran against `<env>`, or the scaffold's skip reason), and the report
|
|
172
157
|
ends with a **Runtime Verification** section: per-route median-of-3
|
|
173
|
-
accessibility scores when the runtime mode ran, or
|
|
174
|
-
|
|
158
|
+
accessibility scores when the runtime mode ran, or that skip reason verbatim.
|
|
159
|
+
Drop every claimed
|
|
175
160
|
violation that names no concrete element and no specific WCAG success criterion.
|
|
@@ -88,9 +88,9 @@ what they declare:
|
|
|
88
88
|
explicit `viewport`, a Cypress `viewportWidth`/`viewportHeight`, a
|
|
89
89
|
visual-regression viewport list. This is the project's own statement of which
|
|
90
90
|
form factors it holds itself to.
|
|
91
|
-
- **Runtime target (optional):** the
|
|
92
|
-
[
|
|
93
|
-
|
|
91
|
+
- **Runtime target (optional):** whatever the shared runtime scaffold in
|
|
92
|
+
[`helpers/audit-lens-core.md`](helpers/audit-lens-core.md#runtime-pass)
|
|
93
|
+
resolves. Note only whether one exists; the scaffold owns how it is found.
|
|
94
94
|
|
|
95
95
|
Record the breakpoints, the viewport contract, and the device matrix. Every
|
|
96
96
|
finding downstream is measured against *this discovered baseline*. If the
|
|
@@ -192,37 +192,20 @@ fix is a new test, name the viewport and the assertion it should make, not just
|
|
|
192
192
|
|
|
193
193
|
## Step 3: Runtime viewport pass (optional corroboration)
|
|
194
194
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
`routeGlobs` — the same SSOT [`/audit-navigability`](audit-navigability.md)
|
|
210
|
-
reads), sampling a representative set (key personas' landing routes plus any
|
|
211
|
-
route in the change-set scope) rather than a single hardcoded page.
|
|
212
|
-
3. **Drive two form factors per route.** Emulate a **phone** and a **tablet**
|
|
213
|
-
viewport — `mcp__chrome-devtools__emulate` for a device profile, or
|
|
214
|
-
`resize_page` for an explicit width/height — then, per route and viewport:
|
|
215
|
-
take a screenshot, and evaluate the two observables static analysis cannot
|
|
216
|
-
resolve — whether `document.scrollingElement.scrollWidth` exceeds the
|
|
217
|
-
viewport width (horizontal overflow), and the rendered box of the
|
|
218
|
-
interactive controls Step 1 flagged as candidates. Reload after switching
|
|
219
|
-
form factor so load-time device gates re-run.
|
|
220
|
-
4. **Median-of-3 or provisional.** Any runtime measurement is subject to
|
|
221
|
-
run-to-run variance: capture a **median-of-3** (three runs per route, report
|
|
222
|
-
the median) before treating a number as authoritative. A single-run value is
|
|
223
|
-
reported **provisional** and never drives a Critical/High verdict on its own.
|
|
224
|
-
5. **Leave the viewport as you found it.** Reset the emulation before finishing
|
|
225
|
-
so a following lens or QA run does not inherit a phone viewport.
|
|
195
|
+
Run the shared runtime scaffold in
|
|
196
|
+
[`helpers/audit-lens-core.md`](helpers/audit-lens-core.md#runtime-pass) — it
|
|
197
|
+
owns target resolution, route sampling, median-of-3, and the two skip reasons
|
|
198
|
+
(no configured target; browser tooling unavailable). This lens's probe is its
|
|
199
|
+
step 3:
|
|
200
|
+
|
|
201
|
+
- **Drive two form factors per route.** Emulate a **phone** and a **tablet**
|
|
202
|
+
viewport — `mcp__chrome-devtools__emulate` for a device profile, or
|
|
203
|
+
`resize_page` for an explicit width/height — then, per route and viewport:
|
|
204
|
+
take a screenshot, and evaluate the two observables static analysis cannot
|
|
205
|
+
resolve — whether `document.scrollingElement.scrollWidth` exceeds the viewport
|
|
206
|
+
width (horizontal overflow), and the rendered box of the interactive controls
|
|
207
|
+
Step 1 flagged as candidates. Reload after switching form factor so load-time
|
|
208
|
+
device gates re-run.
|
|
226
209
|
|
|
227
210
|
Corroborate static findings against the runtime observations (a statically
|
|
228
211
|
flagged fixed width confirmed by a real horizontal overflow graduates from
|
|
@@ -233,10 +216,10 @@ that opens off-screen.
|
|
|
233
216
|
## Report additions
|
|
234
217
|
|
|
235
218
|
Beyond the shared skeleton, the Executive Summary states the runtime mode's
|
|
236
|
-
status (ran against `<env
|
|
219
|
+
status (ran against `<env>`, or the scaffold's skip reason) and names the
|
|
237
220
|
narrowest breakpoint the project declares, so a reader can tell what "mobile"
|
|
238
221
|
meant for this run. The report ends with a **Runtime Viewport Pass** section:
|
|
239
|
-
per-route, per-form-factor observations when the runtime mode ran, or
|
|
240
|
-
|
|
222
|
+
per-route, per-form-factor observations when the runtime mode ran, or the
|
|
223
|
+
scaffold's skip reason verbatim.
|
|
241
224
|
Drop every claimed finding that names no concrete element, style rule, or test
|
|
242
225
|
file.
|
|
@@ -181,6 +181,14 @@ persists, via `carryProvenanceFooters`
|
|
|
181
181
|
The carry is additive, union-preserving and idempotent, so a resumed persist
|
|
182
182
|
cannot stack footers and a hand-authored fingerprint is never dropped.
|
|
183
183
|
|
|
184
|
+
**Persist records the ledger and the labels too.** It stamps the seed's
|
|
185
|
+
`audit::<dimension>` labels — the dedup corpus is listed by them, and an indexed
|
|
186
|
+
run answers lookups from that pool without reaching the provider, so a footer
|
|
187
|
+
alone leaves a plan-path Story invisible — and records each Story's
|
|
188
|
+
**attributed** identities. Union-only identities are not recorded: every sibling
|
|
189
|
+
carries every fingerprint, so an owner would be a coin flip; persist says so on
|
|
190
|
+
stderr. Author per-Story `provenance` to record them.
|
|
191
|
+
|
|
184
192
|
This is deliberately not an authoring step. It used to be: the footers reached
|
|
185
193
|
the seed and stopped there, leaving the authoring agent to notice HTML comments
|
|
186
194
|
in a one-pager and copy them forward — a remembered step, which is to say no
|
|
@@ -243,6 +251,14 @@ every Story that has a resolvable blocker with a canonical
|
|
|
243
251
|
GitHub `blocked_by` relations. An edge whose target was never opened (deduped,
|
|
244
252
|
ledger-suppressed) drops rather than becoming a `blocked by #undefined`.
|
|
245
253
|
|
|
254
|
+
**This pass also records the ledger.** The `--ids` map is the only artifact
|
|
255
|
+
carrying the numbers just opened, so `--wire-edges` folds in the cross-run
|
|
256
|
+
record: each mapped group's findings are written `filed` against its Issue
|
|
257
|
+
(`--ledger <path>`, default `baselines/audit-ledger.json`; `--dry-run`
|
|
258
|
+
suppresses the write). The record runs **before** the provider loads, so a host
|
|
259
|
+
with no `gh` still remembers what it filed. Without it nothing ever writes
|
|
260
|
+
`filed` and the ledger suppresses nothing.
|
|
261
|
+
|
|
246
262
|
**Do not skip this.** `/mandrel-deliver` has no other source for this cohort's order:
|
|
247
263
|
its footprint guard ignores the shared provenance footers, so an unwired cohort
|
|
248
264
|
is genuinely unordered and `/mandrel-deliver` will co-dispatch Stories the edges say
|
|
@@ -277,31 +293,45 @@ the `classifications` array:
|
|
|
277
293
|
is skipped by default; flag in the Phase 7 summary so the operator
|
|
278
294
|
can decide whether to reopen.
|
|
279
295
|
|
|
280
|
-
`routeFinding`
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
Dedup runs in **two stages** when a provider resolves: a
|
|
290
|
-
meaning-first **semantic candidate** pass (`searchCandidates`, wired to
|
|
291
|
-
[`lib/findings/semantic-issue-search.js`](../scripts/lib/findings/semantic-issue-search.js))
|
|
292
|
-
runs FIRST and widens the net across open + closed issues; the exact
|
|
293
|
-
**fingerprint / semantic-key** confirmation runs SECOND. A finding whose title
|
|
294
|
-
was reworded but whose *location* is unchanged still confirms against the Issue
|
|
295
|
-
that already tracks that location, because the audit filers stamp a
|
|
296
|
-
location-based `audit-semantic-keys` footer alongside the `audit-fingerprints`
|
|
297
|
-
footer. Filings from the
|
|
296
|
+
`routeFinding` reads open + closed issues through two ports, which **both run
|
|
297
|
+
and union their pools** — the exact `searchIssues` lookup, and the meaning-first
|
|
298
|
+
`searchCandidates` pass wired to
|
|
299
|
+
[`lib/findings/semantic-issue-search.js`](../scripts/lib/findings/semantic-issue-search.js).
|
|
300
|
+
Confirmation then filters that union by footer, dropping hits whose body
|
|
301
|
+
mentions a sha in prose without the canonical marker. A finding reworded but
|
|
302
|
+
unmoved still confirms, because the filers stamp a location-based
|
|
303
|
+
`audit-semantic-keys` footer
|
|
304
|
+
beside `audit-fingerprints`; so do
|
|
298
305
|
[`retro-proposals-graduator`](../scripts/lib/feedback-loop/retro-proposals-graduator.js)
|
|
299
|
-
|
|
300
|
-
|
|
306
|
+
filings, which a sweep therefore never re-files.
|
|
307
|
+
|
|
308
|
+
Where the corpus is pre-fetched — either off the list endpoint or from
|
|
309
|
+
`--issues-file` — the exact lookup is answered from that local index and the
|
|
310
|
+
search API is spent only on findings with no exact hit. The workflow owns **no**
|
|
311
|
+
parallel dedup or footer-parsing code: fingerprint, footer round-trip, and
|
|
312
|
+
routing all live in that one shared module.
|
|
313
|
+
|
|
314
|
+
### When there is no `gh` CLI
|
|
315
|
+
|
|
316
|
+
**No GitHub access** (air-gapped): pass `--no-provider` to `--scan`. Every
|
|
317
|
+
group is classified `create` and the operator is told dedupe was skipped — a
|
|
318
|
+
re-run opens duplicates.
|
|
319
|
+
|
|
320
|
+
**Reachable, but not through `gh`** (a cloud sandbox: no `gh`, no direct API,
|
|
321
|
+
MCP fine): fetch the corpus yourself. List every issue labelled `audit::*` at
|
|
322
|
+
state `all` — `mcp__github__list_issues` or any other path — write the raw
|
|
323
|
+
result as a JSON array, and pass it:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
node .agents/scripts/audit-to-stories.js --scan --no-provider \
|
|
327
|
+
--issues-file temp/audits/issues.json --glob "temp/audits/audit-*-results.md"
|
|
328
|
+
```
|
|
301
329
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
330
|
+
Real `skip-open` / `skip-reoccurring` classifications come back. The corpus is
|
|
331
|
+
normalised on load, so a raw list result works as-is; only `number` and `body`
|
|
332
|
+
are read. An unreadable file is a hard error, never a silent fall-back to an
|
|
333
|
+
unchecked run; an **empty** array — a valid first sweep — is reported with its
|
|
334
|
+
count so it cannot pass for a failed fetch.
|
|
305
335
|
|
|
306
336
|
### Cross-run ledger
|
|
307
337
|
|
|
@@ -314,8 +344,12 @@ shape). Each entry is keyed by the finding's fingerprint plus a location-based
|
|
|
314
344
|
(`new | filed | fixed | accepted-risk | regressed`). A finding whose tracking
|
|
315
345
|
Issue was closed as `not_planned` becomes `accepted-risk` and is **suppressed**
|
|
316
346
|
on every later scan; a `fixed` finding that re-appears becomes `regressed`. The
|
|
317
|
-
ledger is written by the unattended `--auto` sweep
|
|
318
|
-
run
|
|
347
|
+
ledger is written by the unattended `--auto` sweep, by any `--scan --ledger`
|
|
348
|
+
run, by the Phase 5c `--wire-edges` pass, and by `plan-persist` on the Phase 5a
|
|
349
|
+
chained path — the two filing paths both record what they filed; the
|
|
350
|
+
plain `--scan` path leaves it untouched. A finding whose resolved Issue is open
|
|
351
|
+
is recorded `filed` and is known on re-detection; a closed Issue still outranks
|
|
352
|
+
that.
|
|
319
353
|
|
|
320
354
|
## Phase 7 — Summary & cleanup
|
|
321
355
|
|
|
@@ -391,8 +425,10 @@ The routine shape is **lenses full-scope → dry-run → live with a ledger PR**
|
|
|
391
425
|
from `delivery.auditToStories.severityFloor` (default `high`, overridable with
|
|
392
426
|
`--severity`), applies the two-stage dedup, reconciles the cross-run ledger,
|
|
393
427
|
and prints a run-summary JSON (create / skip-open / skip-reoccurring /
|
|
394
|
-
suppressed-by-ledger tallies,
|
|
395
|
-
|
|
428
|
+
suppressed-by-ledger tallies, the `create`-classified group keys the `--ids`
|
|
429
|
+
map is built from, plus the re-detected open Issue numbers an operator may want
|
|
430
|
+
a "re-detected" comment on). It opens no Issues itself, so run Phase 5c after
|
|
431
|
+
filing or the sweep's memory stays empty. `--dry-run` performs zero GitHub
|
|
396
432
|
writes and skips the ledger write, emitting only the summary.
|
|
397
433
|
|
|
398
434
|
`--auto` **fails closed on any `summary.reportFailures[]` entry** (Phase 1): an
|
|
@@ -5,7 +5,7 @@ description: >-
|
|
|
5
5
|
`git stash` entries — each step gated by operator confirmation.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# /git-cleanup [--fast-forward-main] [--prune-remotes] [--branches] [--stashes] [--execute] [--remote] [--yes] [--drop-stashes <ref>] [--exclude <pattern>] [--json]
|
|
8
|
+
# /git-cleanup [--fast-forward-main] [--prune-remotes] [--branches] [--stashes] [--execute] [--remote] [--yes] [--include-content-merged] [--drop-stashes <ref>] [--exclude <pattern>] [--json]
|
|
9
9
|
|
|
10
10
|
`/git-cleanup` folds the four cleanup steps operators routinely run by hand
|
|
11
11
|
after a busy session into a single pipeline with per-step confirmation. It is a
|
|
@@ -36,7 +36,7 @@ flags: `node .agents/scripts/git-cleanup.js --help`.
|
|
|
36
36
|
| --- | --- | --- |
|
|
37
37
|
| **fast-forward-main** | `git fetch origin <base>` then `git merge --ff-only origin/<base>`. | Skipped silently on a dirty tree or a non-fast-forward; otherwise prompts `Fast-forward main by N commit(s)?`. Checks out `<base>` first when HEAD is elsewhere and does **not** restore the prior branch. |
|
|
38
38
|
| **prune-remotes** | `git fetch --prune origin` to drop `refs/remotes/origin/*` GitHub already deleted. | Prompts before pruning. Runs as its own phase regardless of `--remote`. |
|
|
39
|
-
| **branches** | Reaps merged local branches (squash-aware: merged-PR, git-ancestry, and content-equivalence signals), removing an attached worktree first. Also enumerates **remote-only** merged branches. | Prints the candidate list, then prompts `Reap N merged branch(es)?`. `--remote` is required **on top of** `--execute` to delete any `origin/<branch>`. `content-merged` candidates carry a weaker-signal warning. |
|
|
39
|
+
| **branches** | Reaps merged local branches (squash-aware: merged-PR, git-ancestry, and content-equivalence signals), removing an attached worktree first. Also enumerates **remote-only** merged branches. | Prints the candidate list, then prompts `Reap N merged branch(es)?`. `--remote` is required **on top of** `--execute` to delete any `origin/<branch>`. `content-merged` candidates carry a weaker-signal warning, and under `--yes` their **remote** ref is withheld unless `--include-content-merged` is passed (their local ref still goes — it is recoverable from the remote). |
|
|
40
40
|
| **stashes** | Lists every stash and triages it. | Interactive: `drop / keep / quit` per entry (default `keep`). Under `--yes` / `--json`, drops require an explicit `--drop-stashes <ref>` allowlist (repeatable) — there is no "drop all". |
|
|
41
41
|
|
|
42
42
|
## Constraint
|
|
@@ -49,6 +49,14 @@ Two consequences the flag list alone does not carry: `--remote` deletions cannot
|
|
|
49
49
|
be undone without re-pushing, and `--exclude '<pattern>'` is the **only** way to
|
|
50
50
|
protect an in-scope merged-PR branch you want to keep.
|
|
51
51
|
|
|
52
|
+
That irreversibility is why `--yes` and `content-merged` do not combine on their
|
|
53
|
+
own. Content-equivalence says "applying this branch to the base changes nothing"
|
|
54
|
+
— which is true of a squash-merged branch and equally true of one whose every
|
|
55
|
+
change was reverted. An operator answering the prompt sees the weaker-signal
|
|
56
|
+
note and decides; an unattended run has nobody to decide, so it withholds the
|
|
57
|
+
remote delete and says so, and `--include-content-merged` is the decision made
|
|
58
|
+
in advance.
|
|
59
|
+
|
|
52
60
|
Do **not** run with `--execute` if there is unmerged work that needs saving. The
|
|
53
61
|
fast-forward phase skips on a dirty tree (safe), but the branches phase reaps any
|
|
54
62
|
merged-PR branch in scope unless `--exclude`d.
|
|
@@ -59,9 +67,15 @@ merged-PR branch in scope unless `--exclude`d.
|
|
|
59
67
|
# Preview all four phases (no mutation).
|
|
60
68
|
node .agents/scripts/git-cleanup.js
|
|
61
69
|
|
|
62
|
-
# Run everything non-interactively, including origin refs.
|
|
70
|
+
# Run everything non-interactively, including origin refs. Branches detected
|
|
71
|
+
# only by content-equivalence keep their origin ref — see the note below.
|
|
63
72
|
node .agents/scripts/git-cleanup.js --execute --remote --yes
|
|
64
73
|
|
|
74
|
+
# Same, but also delete the origin refs of content-merged branches. Nobody is
|
|
75
|
+
# watching, so opting in is the whole confirmation this delete ever gets.
|
|
76
|
+
node .agents/scripts/git-cleanup.js --execute --remote --yes \
|
|
77
|
+
--include-content-merged
|
|
78
|
+
|
|
65
79
|
# Only fast-forward main.
|
|
66
80
|
node .agents/scripts/git-cleanup.js --fast-forward-main --execute
|
|
67
81
|
|
|
@@ -208,6 +208,51 @@ extracted and names any disagreement as a **report failure**
|
|
|
208
208
|
report whose line is missing or wrong. A parse that silently drops findings is
|
|
209
209
|
otherwise indistinguishable from a clean audit.
|
|
210
210
|
|
|
211
|
+
## Runtime pass scaffold {#runtime-pass}
|
|
212
|
+
|
|
213
|
+
Some lenses corroborate their static findings against a live target. Static
|
|
214
|
+
detection is the default and always runs; the runtime pass is **conditional** —
|
|
215
|
+
it runs only when a live target and the browser tooling are both available, and
|
|
216
|
+
its absence never blocks the static report. The steps below are shared by every
|
|
217
|
+
lens that has one, so a lens documents only its own probe.
|
|
218
|
+
|
|
219
|
+
1. **Resolve the target from config — never a hardcoded URL.** Resolve through
|
|
220
|
+
the consumer's `qa.environments.<env>.baseUrl` (via
|
|
221
|
+
[`resolveQaEnvironment`](../../scripts/lib/qa/resolve-qa-contract.js), the
|
|
222
|
+
same resolver `/qa-run` uses): an `<env>` argument resolves by exact name or
|
|
223
|
+
origin match; with no argument, enumerate `name → baseUrl` and let the
|
|
224
|
+
operator pick.
|
|
225
|
+
2. **Sample routes from the navigability SSOT.** Draw the routes to exercise
|
|
226
|
+
from the consumer's route/nav registry (`planning.navigation.navRegistry` /
|
|
227
|
+
`routeGlobs` — the same SSOT
|
|
228
|
+
[`/audit-navigability`](../audit-navigability.md) reads), sampling a
|
|
229
|
+
representative set (key personas' landing routes plus any route in the
|
|
230
|
+
change-set scope) rather than a single hardcoded page.
|
|
231
|
+
3. **Run the lens's own probe** against each sampled route. That step, and only
|
|
232
|
+
that step, is the lens's to document.
|
|
233
|
+
4. **Median-of-3 or provisional.** Any runtime measurement is subject to
|
|
234
|
+
run-to-run variance: capture a **median-of-3** (three runs per route, report
|
|
235
|
+
the median) before treating a number as authoritative. A single-run value is
|
|
236
|
+
reported **provisional** and never drives a Critical/High verdict on its own.
|
|
237
|
+
5. **Leave the environment as you found it.** Reset any emulation — viewport,
|
|
238
|
+
device profile, colour scheme — before finishing, so a following lens or QA
|
|
239
|
+
run does not inherit it.
|
|
240
|
+
|
|
241
|
+
**Skip reasons, and how to report them.** The pass is skipped, never faked, for
|
|
242
|
+
either of two reasons, and the Executive Summary says which:
|
|
243
|
+
|
|
244
|
+
| Condition | Reported as |
|
|
245
|
+
| --- | --- |
|
|
246
|
+
| No `qa.environments` target is configured | `skipped — no target configured` |
|
|
247
|
+
| The browser tooling is unavailable in this runtime | `skipped — browser tooling unavailable` |
|
|
248
|
+
|
|
249
|
+
The second is the one an unattended sweep meets: a scheduled run has no browser
|
|
250
|
+
MCP server attached, so a lens that treated "cannot drive" as "nothing found"
|
|
251
|
+
would report a clean runtime section it never ran. Do not invent a URL, do not
|
|
252
|
+
start an arbitrary dev server, and do not fall back to a static-only claim
|
|
253
|
+
dressed as a runtime one — name the skip and let the static findings stand on
|
|
254
|
+
their own.
|
|
255
|
+
|
|
211
256
|
## Execution strategy {#execution-strategy}
|
|
212
257
|
|
|
213
258
|
A lens is a self-contained, read-only unit of work — exactly the shape a
|
|
@@ -107,11 +107,12 @@ Per-round mechanics: [`acceptance-self-eval.md`](acceptance-self-eval.md).
|
|
|
107
107
|
|
|
108
108
|
## 5. The one creditable full-suite run
|
|
109
109
|
|
|
110
|
-
**After the self-eval loop's last fix commit, immediately
|
|
111
|
-
the credit is keyed on the tree,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
**After the self-eval loop's last fix commit, immediately after the push** —
|
|
111
|
+
the credit is keyed on the tree, not push state: a later commit voids it, a
|
|
112
|
+
push does not, and the backgrounded capture (below) ends the turn. Redraft
|
|
113
|
+
rounds run scoped tests; only this run needs credit, and a bare `npm test` /
|
|
114
|
+
`pnpm run test` deposits **none**, so close re-runs it. Shape it by what
|
|
115
|
+
`close-validation/gates.js` runs:
|
|
115
116
|
|
|
116
117
|
```bash
|
|
117
118
|
# CRAP gate on (default) + a `test:coverage` script — writes close's stamp:
|
|
@@ -127,7 +128,7 @@ Dispatch it in the **background**: it outruns the host's sync Bash ceiling, and
|
|
|
127
128
|
Rule 2).
|
|
128
129
|
|
|
129
130
|
Read the **output**, not the exit code: capture skips — no test run, no
|
|
130
|
-
credit — when nothing changed under
|
|
131
|
+
credit — when nothing changed under CRAP `targetDirs`. Run the scoped
|
|
131
132
|
projects for the roots you changed plus `verify[]`, not the whole suite.
|
|
132
133
|
|
|
133
134
|
`verify[]` is scoped entries **plus** this one run: an entry that is itself a
|