mandrel 2.31.0 → 2.33.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/README.md +13 -17
- package/.agents/agents/acceptance-critic.md +1 -2
- package/.agents/docs/SDLC.md +12 -9
- package/.agents/docs/agentrc-reference.json +63 -58
- package/.agents/docs/configuration.md +275 -227
- package/.agents/docs/execution-reference.md +13 -14
- package/.agents/docs/quality-gates.md +195 -23
- package/.agents/instructions.md +2 -5
- package/.agents/rules/git-conventions-reference.md +27 -27
- package/.agents/rules/git-conventions.md +4 -2
- package/.agents/rules/known-tooling-behavior.md +66 -30
- package/.agents/rules/testing-standards.md +35 -71
- package/.agents/runtime-deps.json +2 -2
- package/.agents/schemas/agentrc.schema.json +1955 -1410
- package/.agents/schemas/lifecycle/README.md +21 -14
- package/.agents/schemas/lifecycle/ledger-record.schema.json +76 -22
- package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
- package/.agents/scripts/README.md +16 -29
- package/.agents/scripts/apply-quality-bootstrap.js +27 -34
- package/.agents/scripts/audit-to-stories.js +160 -41
- package/.agents/scripts/bootstrap.js +28 -26
- package/.agents/scripts/check-baseline-drift.js +73 -13
- package/.agents/scripts/check-baseline-scope.js +362 -0
- package/.agents/scripts/check-dead-exports.js +9 -1
- package/.agents/scripts/check-gherkin-corpus.js +508 -0
- package/.agents/scripts/check-knip-entries.js +159 -0
- package/.agents/scripts/check-lifecycle-lint.js +108 -124
- package/.agents/scripts/check-schema-references.js +1 -1
- package/.agents/scripts/diagnose-friction.js +7 -4
- package/.agents/scripts/generate-config-docs.js +263 -171
- package/.agents/scripts/install-matrix-assert.js +0 -1
- package/.agents/scripts/lib/ITicketingProvider.js +0 -58
- package/.agents/scripts/lib/audit-baselines/staleness.js +6 -6
- package/.agents/scripts/lib/audit-baselines/trend.js +7 -8
- package/.agents/scripts/lib/audit-baselines/weights.js +4 -5
- package/.agents/scripts/lib/audit-suite/checklist-threading.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +81 -35
- package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +185 -0
- package/.agents/scripts/lib/baselines/envelope.js +41 -60
- package/.agents/scripts/lib/baselines/git-base.js +30 -37
- package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +103 -0
- package/.agents/scripts/lib/baselines/kinds/_crap-read.js +150 -0
- package/.agents/scripts/lib/baselines/kinds/crap.js +25 -65
- package/.agents/scripts/lib/baselines/orphan-pruner.js +233 -0
- package/.agents/scripts/lib/baselines/refresh-service.js +6 -8
- package/.agents/scripts/lib/baselines/scope-assert.js +223 -0
- package/.agents/scripts/lib/baselines/scope-inventory.js +314 -0
- package/.agents/scripts/lib/bdd-step-index.js +326 -0
- package/.agents/scripts/lib/bootstrap/install-ledger.js +5 -3
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +4 -6
- package/.agents/scripts/lib/bootstrap/manifest.js +17 -40
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +12 -59
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +62 -2
- package/.agents/scripts/lib/checks/loop-health.js +9 -37
- package/.agents/scripts/lib/child-exec.js +193 -0
- package/.agents/scripts/lib/cli/standard-args.js +1 -1
- package/.agents/scripts/lib/cli-args.js +64 -0
- package/.agents/scripts/lib/close-validation/gates.js +2 -2
- package/.agents/scripts/lib/close-validation/runner.js +3 -3
- package/.agents/scripts/lib/config/acceptance-eval.js +5 -52
- package/.agents/scripts/lib/config/commands.js +3 -5
- package/.agents/scripts/lib/config/explain.js +5 -7
- package/.agents/scripts/lib/config/gates/bundle-size.schema.js +32 -6
- package/.agents/scripts/lib/config/gates/coverage.schema.js +25 -5
- package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +12 -2
- package/.agents/scripts/lib/config/gates/crap.schema.js +68 -23
- package/.agents/scripts/lib/config/gates/duplication.schema.js +29 -17
- package/.agents/scripts/lib/config/gates/index.js +5 -2
- package/.agents/scripts/lib/config/gates/lighthouse.schema.js +34 -6
- package/.agents/scripts/lib/config/gates/lint.schema.js +11 -2
- package/.agents/scripts/lib/config/gates/maintainability.schema.js +37 -15
- package/.agents/scripts/lib/config/gates/mutation.schema.js +15 -3
- package/.agents/scripts/lib/config/gates/shared.js +58 -9
- package/.agents/scripts/lib/config/github.js +0 -1
- package/.agents/scripts/lib/config/limits.js +3 -48
- package/.agents/scripts/lib/config/qa.js +105 -0
- package/.agents/scripts/lib/config/runners.js +38 -16
- package/.agents/scripts/lib/config/temp-paths.js +6 -5
- package/.agents/scripts/lib/config-settings-schema-delivery.js +245 -56
- package/.agents/scripts/lib/config-settings-schema-quality.js +209 -29
- package/.agents/scripts/lib/config-settings-schema.js +386 -39
- package/.agents/scripts/lib/crap-baseline-join.js +126 -9
- package/.agents/scripts/lib/crap-utils.js +84 -520
- package/.agents/scripts/lib/dead-exports-knip.js +79 -10
- package/.agents/scripts/lib/degraded-mode.js +2 -2
- package/.agents/scripts/lib/dependency-parser.js +20 -7
- package/.agents/scripts/lib/doc-tiers.js +3 -3
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +46 -104
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +10 -8
- package/.agents/scripts/lib/findings/provenance-field.js +135 -0
- package/.agents/scripts/lib/findings/route-finding.js +57 -8
- package/.agents/scripts/lib/fs-walk.js +52 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
- package/.agents/scripts/lib/git-utils.js +16 -36
- package/.agents/scripts/lib/knip-config-resolver.js +181 -0
- package/.agents/scripts/lib/knip-entry-sync.js +508 -0
- package/.agents/scripts/lib/observability/metrics-ledger.js +1 -1
- package/.agents/scripts/lib/observability/runtime-friction.js +10 -0
- package/.agents/scripts/lib/observability/signal-validator.js +5 -85
- package/.agents/scripts/lib/observability/signals-writer.js +19 -62
- package/.agents/scripts/lib/observability/source-classifier.js +5 -7
- package/.agents/scripts/lib/observability/terse-result.js +3 -3
- package/.agents/scripts/lib/orchestration/behind-recovery.js +114 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +7 -8
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +34 -33
- package/.agents/scripts/lib/orchestration/code-review.js +2 -2
- package/.agents/scripts/lib/orchestration/complexity-gate.js +43 -161
- package/.agents/scripts/lib/orchestration/diff-magnitude.js +4 -4
- package/.agents/scripts/lib/orchestration/label-transitions.js +3 -2
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +12 -38
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +5 -6
- package/.agents/scripts/lib/orchestration/plan-metrics.js +2 -3
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +7 -26
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +69 -5
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +69 -13
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +49 -0
- package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
- package/.agents/scripts/lib/orchestration/resolve-stories.js +77 -50
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +130 -0
- package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +37 -26
- package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +376 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +161 -151
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +15 -3
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +10 -15
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +5 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +157 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +0 -14
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +59 -25
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +20 -31
- package/.agents/scripts/lib/orchestration/spec-spill.js +17 -3
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +7 -6
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +2 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +4 -1
- package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -127
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +116 -1
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-validator.js +38 -0
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +5 -5
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -4
- package/.agents/scripts/lib/orchestration/verify-tier-repair.js +107 -0
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -87
- package/.agents/scripts/lib/signals/detectors/common.js +1 -1
- package/.agents/scripts/lib/signals/index.js +8 -6
- package/.agents/scripts/lib/signals/schema.js +20 -25
- package/.agents/scripts/lib/signals/write.js +8 -8
- package/.agents/scripts/lib/story-body/footer-block.js +97 -0
- package/.agents/scripts/lib/story-body/story-body.js +18 -81
- package/.agents/scripts/lib/temp-retention.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +16 -14
- package/.agents/scripts/lib/ticket-body-sections.js +4 -5
- package/.agents/scripts/lib/wave-runner/footprint.js +306 -0
- package/.agents/scripts/lib/wave-runner/ready-set.js +198 -181
- package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +13 -45
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +4 -5
- package/.agents/scripts/lib/worktree-manager.js +2 -3
- package/.agents/scripts/lint-label-vocabulary.js +2 -24
- package/.agents/scripts/pr-watch-with-update.js +7 -5
- package/.agents/scripts/providers/github/blocked-by-add.js +25 -10
- package/.agents/scripts/providers/github/cache.js +2 -2
- package/.agents/scripts/providers/github/comments.js +6 -28
- package/.agents/scripts/providers/github/compose.js +0 -15
- package/.agents/scripts/providers/github/errors.js +10 -27
- package/.agents/scripts/providers/github/request-helpers.js +1 -2
- package/.agents/scripts/providers/github/sub-issues.js +10 -218
- package/.agents/scripts/providers/github.js +4 -7
- package/.agents/scripts/prune-baseline-orphans.js +181 -0
- package/.agents/scripts/resolve-stories.js +21 -7
- package/.agents/scripts/run-lint.js +61 -61
- package/.agents/scripts/run-test-profile.js +6 -6
- package/.agents/scripts/run-verify.js +48 -30
- package/.agents/scripts/single-story-close.js +20 -0
- package/.agents/scripts/single-story-init.js +12 -35
- package/.agents/scripts/stories-wave-tick.js +192 -9
- package/.agents/scripts/update-dead-exports-baseline.js +321 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +2 -2
- package/.agents/skills/skills.index.json +1 -11
- package/.agents/workflows/audit-documentation.md +5 -6
- package/.agents/workflows/audit-to-stories.md +28 -2
- package/.agents/workflows/helpers/audit-lens-core.md +11 -12
- package/.agents/workflows/helpers/code-quality-guardrails.md +15 -14
- package/.agents/workflows/helpers/code-review.md +3 -8
- package/.agents/workflows/helpers/deliver-reference.md +30 -2
- package/.agents/workflows/helpers/deliver-story-reference.md +84 -16
- package/.agents/workflows/helpers/plan-reference.md +76 -0
- package/.agents/workflows/helpers/worktree-lifecycle.md +1 -2
- package/.agents/workflows/mandrel-update.md +10 -10
- package/.agents/workflows/qa-assist.md +15 -20
- package/.agents/workflows/qa-explore.md +9 -8
- package/README.md +1 -1
- package/docs/CHANGELOG.md +58 -0
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +127 -0
- package/package.json +14 -5
- package/.agents/schemas/lifecycle/checkpoint.written.schema.json +0 -13
- package/.agents/schemas/lifecycle/close-validate.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/close-validate.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/code-review.end.schema.json +0 -30
- package/.agents/schemas/lifecycle/code-review.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/intervention.recorded.schema.json +0 -15
- package/.agents/schemas/lifecycle/loop.tick.schema.json +0 -20
- package/.agents/schemas/lifecycle/notification.emitted.schema.json +0 -18
- package/.agents/schemas/lifecycle/pr.created.schema.json +0 -14
- package/.agents/schemas/lifecycle/retro.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/retro.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/story.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +0 -17
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +0 -15
- package/.agents/schemas/lifecycle/story.merged.schema.json +0 -13
- package/.agents/scripts/check-gherkin-placeholders.js +0 -663
- package/.agents/scripts/check-lifecycle-doc-drift.js +0 -411
- package/.agents/scripts/lib/audit-suite/cli.js +0 -64
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +0 -202
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +0 -212
- package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +0 -104
- package/.agents/scripts/lib/checks/push-hook-parity.js +0 -106
- package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +0 -92
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +0 -81
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +0 -55
- package/.agents/scripts/lib/crap-baseline-index.js +0 -46
- package/.agents/scripts/lib/crap-utils-incremental.js +0 -113
- package/.agents/scripts/lib/dynamic-workflow/capability.js +0 -396
- package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -335
- package/.agents/scripts/lib/mutation/baseline-snapshot.js +0 -239
- package/.agents/scripts/lib/mutation/config-detector.js +0 -119
- package/.agents/scripts/lib/mutation/stryker-runner.js +0 -306
- package/.agents/scripts/lib/mutation/survivor-report.js +0 -160
- package/.agents/scripts/lib/observability/active-story-env.js +0 -170
- package/.agents/scripts/lib/observability/tool-trace-hook.js +0 -456
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +0 -111
- package/.agents/scripts/lib/orchestration/context-envelope.js +0 -277
- package/.agents/scripts/lib/orchestration/detectors-phase.js +0 -194
- package/.agents/scripts/lib/orchestration/lifecycle/bus.js +0 -309
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +0 -181
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +0 -229
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +0 -54
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +0 -344
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +0 -114
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +0 -37
- package/.agents/scripts/lib/orchestration/resolves-token.js +0 -127
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +0 -130
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +0 -55
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +0 -211
- package/.agents/scripts/lib/planning-corpus.js +0 -37
- package/.agents/scripts/lib/qa/coverage-report.js +0 -181
- package/.agents/scripts/lib/qa/propose-missing-test.js +0 -95
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +0 -217
- package/.agents/scripts/lib/signals/detectors/index.js +0 -14
- package/.agents/scripts/lib/signals/detectors/retry.js +0 -253
- package/.agents/scripts/lib/signals/detectors/rework.js +0 -167
- package/.agents/scripts/lib/signals/read.js +0 -268
- package/.agents/scripts/lib/signals/span-tree.js +0 -291
- package/.agents/scripts/lib/story-lifecycle.js +0 -194
- package/.agents/scripts/lib/story-plan.js +0 -379
- package/.agents/scripts/lib/util/phase-timer-state.js +0 -72
- package/.agents/scripts/lib/util/phase-timer.js +0 -163
- package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +0 -169
- package/.agents/scripts/lint-baseline.js +0 -507
- package/.agents/scripts/providers/github/prs.js +0 -103
- package/.agents/scripts/signals-view.js +0 -309
- package/.agents/scripts/story-plan.js +0 -370
- package/.agents/scripts/sync-branch-from-base.js +0 -149
- package/.agents/scripts/validate-docs-freshness.js +0 -314
- package/.agents/skills/core/diagnose-friction/SKILL.md +0 -78
- package/.agents/workflows/helpers/signals.md +0 -112
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* update-dead-exports-baseline.js — the producer behind the dead-export
|
|
5
|
+
* ratchet (Story #5011).
|
|
6
|
+
*
|
|
7
|
+
* `check-dead-exports.js` has shipped since Story #1852 with no way to write
|
|
8
|
+
* the baseline it ratchets against. Every seed and every refresh was therefore
|
|
9
|
+
* a hand-edit: run the checker with `--json`, copy `currentRows` out of the
|
|
10
|
+
* report, re-sort them, and preserve `$schema` / `kernelVersion` / `mode` by
|
|
11
|
+
* hand. That is the gap this CLI closes — dead-exports now sits beside its four
|
|
12
|
+
* `update-*-baseline.js` siblings instead of being the one baseline an operator
|
|
13
|
+
* had to author in a text editor.
|
|
14
|
+
*
|
|
15
|
+
* **Fail closed, unlike the checker.** `check-dead-exports.js` treats a knip
|
|
16
|
+
* spawn or parse failure as advisory (exit 0 + a stderr warning) because it
|
|
17
|
+
* still holds a committed snapshot to compare against — a broken knip install
|
|
18
|
+
* must not redden CI on its own. The producer has no such fallback: the file it
|
|
19
|
+
* is about to write *is* the snapshot. An empty row set persisted from a failed
|
|
20
|
+
* run would silently grandfather every dead export in the repository and blind
|
|
21
|
+
* the ratchet permanently. So this CLI exits non-zero and writes **nothing**
|
|
22
|
+
* whenever knip cannot run, its report cannot be parsed, or the knip version
|
|
23
|
+
* cannot be resolved. That asymmetry with the checker is deliberate.
|
|
24
|
+
*
|
|
25
|
+
* **Envelope, not the shared writer.** Dead-exports does not route through
|
|
26
|
+
* `lib/baselines/writer.js`: that writer admits only the kinds registered in
|
|
27
|
+
* `lib/baselines/envelope.js` and requires a `*` rollup row, while dead-exports
|
|
28
|
+
* is an out-of-band ratchet kind (`lib/audit-baselines/kinds.js`) that carries
|
|
29
|
+
* no rollup. This CLI writes exactly the shape the checker already reads and
|
|
30
|
+
* both committed baselines already carry:
|
|
31
|
+
*
|
|
32
|
+
* { $schema, kernelVersion, generatedAt, [mode], rows: [{ file, symbol }] }
|
|
33
|
+
*
|
|
34
|
+
* `kernelVersion` is knip's own installed version — knip is the scorer, so a
|
|
35
|
+
* knip upgrade is what invalidates the rows. `mode` is stamped on the
|
|
36
|
+
* `--production` pass only, matching the committed production baseline.
|
|
37
|
+
*
|
|
38
|
+
* Rows are de-duplicated and sorted by `(file, symbol)` with the same
|
|
39
|
+
* comparator `check-dead-exports.js` uses for its diff output, so a re-run
|
|
40
|
+
* against an unchanged tree differs only in `generatedAt` and review sees real
|
|
41
|
+
* movement rather than reordering noise.
|
|
42
|
+
*
|
|
43
|
+
* The pass is selected with `--production`, and the baseline/label/mode triple
|
|
44
|
+
* comes from `lib/dead-exports-mode.js` — the same resolver the checker uses.
|
|
45
|
+
* Resolving that pairing independently here is precisely how a producer would
|
|
46
|
+
* end up writing production rows over the default baseline.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
import fs from 'node:fs';
|
|
50
|
+
import path from 'node:path';
|
|
51
|
+
import process from 'node:process';
|
|
52
|
+
import { runAsCli } from './lib/cli-utils.js';
|
|
53
|
+
import {
|
|
54
|
+
extractRowsFromKnip,
|
|
55
|
+
readKnipOutput,
|
|
56
|
+
runKnip,
|
|
57
|
+
} from './lib/dead-exports-knip.js';
|
|
58
|
+
import { resolveDeadExportsMode } from './lib/dead-exports-mode.js';
|
|
59
|
+
|
|
60
|
+
/** `$schema` ref stamped into every dead-export baseline envelope. */
|
|
61
|
+
export const DEAD_EXPORTS_SCHEMA_REF =
|
|
62
|
+
'https://mandrel.dev/baselines/dead-exports.schema.json';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Parse argv for `--production`, `--baseline <path>` and `--knip-output
|
|
66
|
+
* <path>`. `--knip-output` is the test seam: it feeds a pre-captured knip JSON
|
|
67
|
+
* report instead of spawning knip, and mirrors the checker's flag of the same
|
|
68
|
+
* name so a captured report drives both sides of the ratchet.
|
|
69
|
+
*
|
|
70
|
+
* @param {string[]} argv
|
|
71
|
+
* @returns {{ baselinePath: string | null, knipOutputPath: string | null, production: boolean }}
|
|
72
|
+
*/
|
|
73
|
+
export function parseArgv(argv = []) {
|
|
74
|
+
const out = { baselinePath: null, knipOutputPath: null, production: false };
|
|
75
|
+
const valueFlags = {
|
|
76
|
+
'--baseline': 'baselinePath',
|
|
77
|
+
'--knip-output': 'knipOutputPath',
|
|
78
|
+
};
|
|
79
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
80
|
+
const arg = argv[i];
|
|
81
|
+
if (arg === '--production') {
|
|
82
|
+
out.production = true;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const field = valueFlags[arg];
|
|
86
|
+
const next = argv[i + 1];
|
|
87
|
+
if (field && next && !next.startsWith('--')) {
|
|
88
|
+
out[field] = next;
|
|
89
|
+
i += 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Resolve the knip version to stamp as `kernelVersion`, read from the
|
|
97
|
+
* installed package rather than recalled. Returns `null` when knip is not
|
|
98
|
+
* installed or its manifest is unreadable — the caller treats that as a
|
|
99
|
+
* fail-closed condition, because an unstamped baseline cannot signal to a
|
|
100
|
+
* later run that the scorer moved underneath it.
|
|
101
|
+
*
|
|
102
|
+
* @param {{ cwd?: string, readFileImpl?: typeof fs.readFileSync }} [opts]
|
|
103
|
+
* @returns {string | null}
|
|
104
|
+
*/
|
|
105
|
+
export function resolveKnipKernelVersion({
|
|
106
|
+
cwd = process.cwd(),
|
|
107
|
+
readFileImpl = fs.readFileSync,
|
|
108
|
+
} = {}) {
|
|
109
|
+
try {
|
|
110
|
+
const manifest = path.resolve(cwd, 'node_modules', 'knip', 'package.json');
|
|
111
|
+
const version = JSON.parse(readFileImpl(manifest, 'utf-8'))?.version;
|
|
112
|
+
return typeof version === 'string' && version.length > 0 ? version : null;
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* De-duplicate and sort `{ file, symbol }` rows into the committed order.
|
|
120
|
+
*
|
|
121
|
+
* Identity is `(file, symbol)` — the same identity `diffRows` in
|
|
122
|
+
* `check-dead-exports.js` uses — so two knip issue records naming the same
|
|
123
|
+
* dead file collapse to one row. The comparator is that checker's comparator,
|
|
124
|
+
* which is why re-sorting an already-committed baseline is a no-op.
|
|
125
|
+
*
|
|
126
|
+
* @param {Array<{ file?: unknown, symbol?: unknown }>} rows
|
|
127
|
+
* @returns {Array<{ file: string, symbol: string }>}
|
|
128
|
+
*/
|
|
129
|
+
export function normalizeRows(rows) {
|
|
130
|
+
const seen = new Set();
|
|
131
|
+
const out = [];
|
|
132
|
+
for (const row of rows ?? []) {
|
|
133
|
+
if (typeof row?.file !== 'string' || typeof row?.symbol !== 'string')
|
|
134
|
+
continue;
|
|
135
|
+
const key = `${row.file}\0${row.symbol}`;
|
|
136
|
+
if (seen.has(key)) continue;
|
|
137
|
+
seen.add(key);
|
|
138
|
+
out.push({ file: row.file, symbol: row.symbol });
|
|
139
|
+
}
|
|
140
|
+
return out.sort(
|
|
141
|
+
(a, b) => a.file.localeCompare(b.file) || a.symbol.localeCompare(b.symbol),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Assemble the baseline envelope. `mode` is stamped on the production pass
|
|
147
|
+
* only: the default baseline has never carried the key, and adding it would
|
|
148
|
+
* churn the committed file for no signal.
|
|
149
|
+
*
|
|
150
|
+
* @param {{ kernelVersion: string, mode: string, rows: Array<object>, generatedAt: string }} args
|
|
151
|
+
* @returns {object}
|
|
152
|
+
*/
|
|
153
|
+
export function buildEnvelope({ kernelVersion, mode, rows, generatedAt }) {
|
|
154
|
+
const envelope = {
|
|
155
|
+
$schema: DEAD_EXPORTS_SCHEMA_REF,
|
|
156
|
+
kernelVersion,
|
|
157
|
+
generatedAt,
|
|
158
|
+
};
|
|
159
|
+
if (mode === 'production') envelope.mode = mode;
|
|
160
|
+
envelope.rows = rows;
|
|
161
|
+
return envelope;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Obtain a knip report, either from the `--knip-output` seam or by spawning
|
|
166
|
+
* knip for the requested pass. Never throws; a failure is reported as
|
|
167
|
+
* `{ ok: false, error }` so the caller can fail closed without a try/catch.
|
|
168
|
+
*
|
|
169
|
+
* @param {{
|
|
170
|
+
* cwd: string,
|
|
171
|
+
* production: boolean,
|
|
172
|
+
* knipOutputPath: string | null,
|
|
173
|
+
* runKnipImpl: typeof runKnip,
|
|
174
|
+
* readKnipOutputImpl: typeof readKnipOutput,
|
|
175
|
+
* }} args
|
|
176
|
+
* @returns {{ ok: true, envelope: unknown } | { ok: false, error: string }}
|
|
177
|
+
*/
|
|
178
|
+
export function collectKnipReport({
|
|
179
|
+
cwd,
|
|
180
|
+
production,
|
|
181
|
+
knipOutputPath,
|
|
182
|
+
runKnipImpl,
|
|
183
|
+
readKnipOutputImpl,
|
|
184
|
+
}) {
|
|
185
|
+
if (knipOutputPath) {
|
|
186
|
+
const envelope = readKnipOutputImpl(path.resolve(cwd, knipOutputPath));
|
|
187
|
+
return envelope == null
|
|
188
|
+
? { ok: false, error: `could not read knip report at ${knipOutputPath}` }
|
|
189
|
+
: { ok: true, envelope };
|
|
190
|
+
}
|
|
191
|
+
return runKnipImpl({ cwd, production });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Reject a report the extractor cannot read. `extractRowsFromKnip` is total —
|
|
196
|
+
* it answers `[]` for any shape it does not recognise — which is the right
|
|
197
|
+
* posture for an advisory checker and the wrong one for a producer: an
|
|
198
|
+
* unrecognised report and a genuinely clean repository would persist the same
|
|
199
|
+
* empty row set. Returns an error string, or `null` when the report is usable.
|
|
200
|
+
*
|
|
201
|
+
* @param {unknown} envelope
|
|
202
|
+
* @returns {string | null}
|
|
203
|
+
*/
|
|
204
|
+
export function describeUnusableReport(envelope) {
|
|
205
|
+
if (!envelope || typeof envelope !== 'object')
|
|
206
|
+
return 'knip report is not a JSON object';
|
|
207
|
+
if (!Array.isArray(envelope.issues))
|
|
208
|
+
return 'knip report carries no `issues` array';
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Top-level CLI entry, exported so tests drive the whole pipeline through
|
|
214
|
+
* injected hooks without spawning knip or touching a committed baseline.
|
|
215
|
+
*
|
|
216
|
+
* @param {{
|
|
217
|
+
* argv?: string[],
|
|
218
|
+
* cwd?: string,
|
|
219
|
+
* stdout?: { write: (s: string) => void },
|
|
220
|
+
* stderr?: { write: (s: string) => void },
|
|
221
|
+
* runKnipImpl?: typeof runKnip,
|
|
222
|
+
* readKnipOutputImpl?: typeof readKnipOutput,
|
|
223
|
+
* readFileImpl?: typeof fs.readFileSync,
|
|
224
|
+
* writeFileImpl?: typeof fs.writeFileSync,
|
|
225
|
+
* renameImpl?: typeof fs.renameSync,
|
|
226
|
+
* now?: () => string,
|
|
227
|
+
* }} [opts]
|
|
228
|
+
* @returns {Promise<number>} 0 on a written baseline; 1 on any fail-closed path.
|
|
229
|
+
*/
|
|
230
|
+
export async function runCli({
|
|
231
|
+
argv = process.argv.slice(2),
|
|
232
|
+
cwd = process.cwd(),
|
|
233
|
+
stdout = process.stdout,
|
|
234
|
+
stderr = process.stderr,
|
|
235
|
+
runKnipImpl = runKnip,
|
|
236
|
+
readKnipOutputImpl = readKnipOutput,
|
|
237
|
+
readFileImpl = fs.readFileSync,
|
|
238
|
+
writeFileImpl = fs.writeFileSync,
|
|
239
|
+
renameImpl = fs.renameSync,
|
|
240
|
+
now = () => new Date().toISOString(),
|
|
241
|
+
} = {}) {
|
|
242
|
+
const { baselinePath, knipOutputPath, production } = parseArgv(argv);
|
|
243
|
+
const { mode, label, baseline } = resolveDeadExportsMode(production);
|
|
244
|
+
const target = path.resolve(cwd, baselinePath ?? baseline);
|
|
245
|
+
|
|
246
|
+
const kernelVersion = resolveKnipKernelVersion({ cwd, readFileImpl });
|
|
247
|
+
if (!kernelVersion) {
|
|
248
|
+
stderr.write(
|
|
249
|
+
`[${label}] ❌ cannot resolve knip's version from node_modules/knip/package.json — refusing to write ${target}\n`,
|
|
250
|
+
);
|
|
251
|
+
return 1;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const report = collectKnipReport({
|
|
255
|
+
cwd,
|
|
256
|
+
production,
|
|
257
|
+
knipOutputPath,
|
|
258
|
+
runKnipImpl,
|
|
259
|
+
readKnipOutputImpl,
|
|
260
|
+
});
|
|
261
|
+
const failure = report.ok
|
|
262
|
+
? describeUnusableReport(report.envelope)
|
|
263
|
+
: report.error;
|
|
264
|
+
if (failure) {
|
|
265
|
+
stderr.write(
|
|
266
|
+
`[${label}] ❌ ${failure} — refusing to write ${target} (an empty baseline would grandfather every dead export)\n`,
|
|
267
|
+
);
|
|
268
|
+
return 1;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const rows = normalizeRows(extractRowsFromKnip(report.envelope));
|
|
272
|
+
const envelope = buildEnvelope({
|
|
273
|
+
kernelVersion,
|
|
274
|
+
mode,
|
|
275
|
+
rows,
|
|
276
|
+
generatedAt: now(),
|
|
277
|
+
});
|
|
278
|
+
// Write-then-rename, matching `lib/baselines/writer.js`: a crash or a full
|
|
279
|
+
// disk mid-write must not leave a truncated envelope behind. An unparseable
|
|
280
|
+
// baseline reads as empty to `check-dead-exports.js`, which would report
|
|
281
|
+
// every pre-existing row as newly added.
|
|
282
|
+
const tmpTarget = `${target}.tmp`;
|
|
283
|
+
writeFileImpl(tmpTarget, `${JSON.stringify(envelope, null, 2)}\n`, 'utf-8');
|
|
284
|
+
renameImpl(tmpTarget, target);
|
|
285
|
+
stdout.write(
|
|
286
|
+
`[${label}] ✅ wrote ${rows.length} row(s) to ${target} (kernelVersion=${kernelVersion}).\n`,
|
|
287
|
+
);
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function main() {
|
|
292
|
+
return runCli();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
runAsCli(import.meta.url, main, {
|
|
296
|
+
source: 'dead-exports-baseline',
|
|
297
|
+
propagateExitCode: true,
|
|
298
|
+
errorPrefix: '[dead-exports-baseline] ❌ Fatal error',
|
|
299
|
+
usage: {
|
|
300
|
+
invocation:
|
|
301
|
+
'node .agents/scripts/update-dead-exports-baseline.js [--production] [--baseline <path>] [--knip-output <path>]',
|
|
302
|
+
summary:
|
|
303
|
+
'Scan → write one dead-export baseline. Regenerates the rows `check-dead-exports.js` ratchets against, so the snapshot is produced rather than hand-edited.',
|
|
304
|
+
flags: [
|
|
305
|
+
[
|
|
306
|
+
'--production',
|
|
307
|
+
'Write the production-pass baseline (baselines/dead-exports-production.json) instead of the default one.',
|
|
308
|
+
],
|
|
309
|
+
['--baseline <path>', 'Write to this path instead of the mode default.'],
|
|
310
|
+
[
|
|
311
|
+
'--knip-output <path>',
|
|
312
|
+
'Read a saved knip JSON report instead of running knip.',
|
|
313
|
+
],
|
|
314
|
+
],
|
|
315
|
+
notes: [
|
|
316
|
+
'Fails closed: when knip cannot run or its report cannot be parsed, the CLI exits 1 and writes nothing — the checker is advisory on that failure, the producer must not be.',
|
|
317
|
+
'There is no scope flag: knip scores the whole graph, so a diff-scoped refresh is not expressible. Run `npm run dead-exports:update` to refresh both passes.',
|
|
318
|
+
'Exit codes:\n 0 baseline written\n 1 knip unavailable, unparseable, or version unresolvable',
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
});
|
|
@@ -18,7 +18,7 @@ allowed_tools:
|
|
|
18
18
|
- **No gate may be skipped.** Failing lint means fix lint, not disable the rule; a failing test means fix the code, not `.skip` or delete the test. Gates are ordered shift-left so cheap checks fail first, and CI failure output is fed back verbatim with the directive to reproduce and fix locally before re-pushing.
|
|
19
19
|
- **Introducing a gate that asserts on pre-existing state** (doc-drift, lint-vocabulary, dependency-cycle, missing-coverage) MUST land green at merge: either advisory-first (report-only until the backlog is burned down) or with the populated baseline committed in the same change that turns the gate on. Never wire a gate into `requiredChecks` that lands red on latent findings nobody authored.
|
|
20
20
|
- **Refresh a baseline only when the change is deliberate** — a rename/move, an operator-approved complexity bump, a signed-off perf delta, an intentional API-surface change. Never refresh to paper over an unintentional regression; fix the regression instead.
|
|
21
|
-
- **Pick the refresh that matches why you are refreshing** — the two are different scopes, and the wrong one silently no-ops. `npm run <kind>:update` is **diff-scoped**: it re-scores only files changed in `origin/main..HEAD` and preserves every other row verbatim. That is what you want after changing code. `npm run <kind>:reanchor` is the same script with `--full-scope`: it re-scores every file in every target dir. That is what you want when the baseline is **stale** — a scorer-version bump, a coverage-shape change, accumulated drift — because a diff-scoped run would leave the stale rows exactly as they were and the staleness finding would re-fire. Kinds: `coverage`, `crap`, `maintainability`, `duplication
|
|
21
|
+
- **Pick the refresh that matches why you are refreshing** — the two are different scopes, and the wrong one silently no-ops. `npm run <kind>:update` is **diff-scoped**: it re-scores only files changed in `origin/main..HEAD` and preserves every other row verbatim. That is what you want after changing code. `npm run <kind>:reanchor` is the same script with `--full-scope`: it re-scores every file in every target dir. That is what you want when the baseline is **stale** — a scorer-version bump, a coverage-shape change, accumulated drift — because a diff-scoped run would leave the stale rows exactly as they were and the staleness finding would re-fire. Kinds with both scopes: `coverage`, `crap`, `maintainability`, `duplication`. Dead-exports has a producer but no scope axis — knip scores the whole graph, so `npm run dead-exports:update` (both passes, via `.agents/scripts/update-dead-exports-baseline.js`) is the only sanctioned way to write `baselines/dead-exports.json` / `baselines/dead-exports-production.json`; never hand-edit those files. Lighthouse still has no producer — edit `baselines/lighthouse.json` directly. Run any of them on the **Story branch**, not on `main`.
|
|
22
22
|
- Verify the refresh diff is scoped to the relevant `baselines/<kind>.json` (plus cosmetic `package-lock.json` churn only). If unrelated files appear, STOP — the refresh is contaminated. Stage baseline files **explicitly** (`git add baselines/<kind>.json`); never `git add -A` in a refresh commit.
|
|
23
23
|
- Commit-subject contract: a **Conventional-Commits** subject `chore(baselines): refresh <kind> snapshot for <reason>` — never an ad-hoc leading token like `baseline-refresh:` (commitlint and the planner validator reject it). The body is **mandatory** and non-empty: what changed, why the new floor is correct, and the Story that triggered it.
|
|
24
24
|
- Add the machine-readable trailer `baseline-refresh: true` (git-trailer `Key: value` style) and `Story: #<storyId>` to the body whenever observability classification matters. Never pass `--no-verify`; the `commit-msg` hook (commitlint) MUST run and pass.
|
|
@@ -101,7 +101,7 @@ own as the parseable marker for any future reader.)
|
|
|
101
101
|
| Maintainability | `npm run maintainability:update` | `npm run maintainability:reanchor` |
|
|
102
102
|
| Coverage | `npm run coverage:update` | `npm run coverage:reanchor` |
|
|
103
103
|
| Duplication | `npm run duplication:update` | `npm run duplication:reanchor` |
|
|
104
|
-
| Dead-exports |
|
|
104
|
+
| Dead-exports | `npm run dead-exports:update` (regenerates both passes; never hand-edit the JSON) | same — no scope flag applies |
|
|
105
105
|
| Lighthouse | edit `baselines/lighthouse.json` | same — no scope flag applies |
|
|
106
106
|
|
|
107
107
|
1. **Run the matching command for your reason** — left column after a code
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-08-
|
|
2
|
+
"generatedAt": "2026-08-06T10:26:18.702Z",
|
|
3
3
|
"generator": "generate-skills-index.js@1",
|
|
4
4
|
"skills": [
|
|
5
5
|
{
|
|
@@ -42,16 +42,6 @@
|
|
|
42
42
|
"allowedTools": null,
|
|
43
43
|
"vendor": null
|
|
44
44
|
},
|
|
45
|
-
{
|
|
46
|
-
"name": "diagnose-friction",
|
|
47
|
-
"tier": "core",
|
|
48
|
-
"category": "core",
|
|
49
|
-
"path": ".agents/skills/core/diagnose-friction/SKILL.md",
|
|
50
|
-
"description": "Wrap a shell command with diagnostic capture. On failure, print static suggestions and append a structured `friction` record to the per-Story signals.ndjson stream. Use whenever a script in the orchestration loop invokes a tool whose failure shape we want the analyzer to attribute.",
|
|
51
|
-
"policyCapsuleBullets": 9,
|
|
52
|
-
"allowedTools": ["Bash", "Read"],
|
|
53
|
-
"vendor": null
|
|
54
|
-
},
|
|
55
45
|
{
|
|
56
46
|
"name": "documentation-and-adrs",
|
|
57
47
|
"tier": "core",
|
|
@@ -7,10 +7,10 @@ description: Audit the repository's main documentation for staleness, semantic d
|
|
|
7
7
|
You are a Staff Engineer & Documentation Steward verifying the repository's prose
|
|
8
8
|
documentation is **up to date and complete**. Prose rots silently: commands get
|
|
9
9
|
renamed, scripts move, workflows change shape, version/topology claims go stale.
|
|
10
|
-
The deterministic gates (`check-doc-links.js
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
The deterministic gates (`check-doc-links.js` and the generators' `--check`
|
|
11
|
+
mode) catch broken links and generator drift — they cannot tell whether the
|
|
12
|
+
prose still describes how the code actually behaves. That semantic
|
|
13
|
+
verification is this lens's job. The
|
|
14
14
|
shared lens machinery — read-only constraint, scope interpretation, report
|
|
15
15
|
envelope + finding-block skeleton, severity scale, self-cross-check, and
|
|
16
16
|
execution strategy — lives in
|
|
@@ -78,7 +78,6 @@ are cheap, exact, and de-duplicate the easy findings:
|
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
80
|
node .agents/scripts/check-doc-links.js
|
|
81
|
-
node .agents/scripts/check-lifecycle-doc-drift.js
|
|
82
81
|
node .agents/scripts/generate-config-docs.js --check
|
|
83
82
|
node .agents/scripts/generate-lifecycle-docs.js --check
|
|
84
83
|
node .agents/scripts/generate-workflows-doc.js --check
|
|
@@ -87,7 +86,7 @@ node .agents/scripts/resolve-doc-tiers.js --json
|
|
|
87
86
|
|
|
88
87
|
Fold the results in as findings:
|
|
89
88
|
|
|
90
|
-
- **Checker failures** (broken links,
|
|
89
|
+
- **Checker failures** (broken links, generator drift) become individual
|
|
91
90
|
findings with `Category: Link Integrity` (or `Generator Drift` for the
|
|
92
91
|
lifecycle gate), citing the checker output verbatim.
|
|
93
92
|
- **Generator dirtiness** (any `--check` reporting stale output, including
|
|
@@ -189,6 +189,32 @@ Labels applied:
|
|
|
189
189
|
(cross-audit groups carry multiple).
|
|
190
190
|
- `risk::high` — added when any finding in the group is Critical.
|
|
191
191
|
|
|
192
|
+
### Phase 5c — Wire the cohort's declared ordering (**required**)
|
|
193
|
+
|
|
194
|
+
Creating the Issues is only the first pass. `groupFindings` detects `edges[]`
|
|
195
|
+
between groups, but at emit time no group has an issue number, so each body
|
|
196
|
+
ships with an empty `depends_on` and the cohort has **no declared ordering
|
|
197
|
+
at all**. Replay the numbers you just opened:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
node .agents/scripts/audit-to-stories.js --wire-edges \
|
|
201
|
+
--plan temp/audits/audit-to-stories-plan.json \
|
|
202
|
+
--ids '{"<groupKey>": <issueNumber>, ...}' \
|
|
203
|
+
--out temp/audits/audit-to-stories-wired.json
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Each entry in the emitted `--json` payload carries its own `groupKey` and
|
|
207
|
+
`dependsOn`, so the map is a lookup, not a reconstruction. The pass re-renders
|
|
208
|
+
every Story that has a resolvable blocker with a canonical
|
|
209
|
+
`---` / `blocked by #N` footer **and** mirrors the same edges as native
|
|
210
|
+
GitHub `blocked_by` relations. An edge whose target was never opened (deduped,
|
|
211
|
+
ledger-suppressed) drops rather than becoming a `blocked by #undefined`.
|
|
212
|
+
|
|
213
|
+
**Do not skip this.** `/deliver` has no other source for this cohort's order:
|
|
214
|
+
its footprint guard ignores the shared provenance footers, so an unwired cohort
|
|
215
|
+
is genuinely unordered and `/deliver` will co-dispatch Stories the edges say
|
|
216
|
+
must follow one another.
|
|
217
|
+
|
|
192
218
|
## Phase 6 — Idempotency (folded into Phase 1 scan)
|
|
193
219
|
|
|
194
220
|
The `--scan` step routes each group's findings through the shared
|
|
@@ -227,8 +253,8 @@ runs FIRST and widens the net across open + closed issues; the exact
|
|
|
227
253
|
was reworded but whose *location* is unchanged still confirms against the Issue
|
|
228
254
|
that already tracks that location, because the audit filers stamp a
|
|
229
255
|
location-based `audit-semantic-keys` footer alongside the `audit-fingerprints`
|
|
230
|
-
footer.
|
|
231
|
-
[`
|
|
256
|
+
footer. Filings from the
|
|
257
|
+
[`retro-proposals-graduator`](../scripts/lib/feedback-loop/retro-proposals-graduator.js)
|
|
232
258
|
carry the same canonical `audit-fingerprints` footer, so a sweep recognizes a
|
|
233
259
|
graduator-filed issue and never re-files it.
|
|
234
260
|
|
|
@@ -229,18 +229,17 @@ which path produced it.
|
|
|
229
229
|
> stage before synthesising the report. It derives its per-dimension prompts
|
|
230
230
|
> from the *lens* markdown at run time — the lens stays the single source of
|
|
231
231
|
> truth. This is a performance optimization over path 1, **not** a separate
|
|
232
|
-
> contract
|
|
233
|
-
> [`../../
|
|
234
|
-
>
|
|
235
|
-
>
|
|
236
|
-
>
|
|
237
|
-
>
|
|
238
|
-
>
|
|
239
|
-
>
|
|
240
|
-
>
|
|
241
|
-
>
|
|
242
|
-
>
|
|
243
|
-
> the final report artifact.
|
|
232
|
+
> contract, and it is not covered by the No-Shim / hard-cutover rule in
|
|
233
|
+
> [`../../rules/git-conventions.md`](../../rules/git-conventions.md) because
|
|
234
|
+
> there is one report contract and only the execution strategy varies — the
|
|
235
|
+
> same capability-degradation pattern the protocol endorses for live-docs
|
|
236
|
+
> fallback. **The host owns the choice.** Mandrel ships no in-repo strategy
|
|
237
|
+
> selector and no force-override env var: Claude Code launches the saved
|
|
238
|
+
> workflow when it can, and you get path 1 or 2 above when it cannot.
|
|
239
|
+
> Suppress the orchestrated path with `CLAUDE_CODE_DISABLE_WORKFLOWS=1`
|
|
240
|
+
> or `disableWorkflows: true` in `.claude/settings.json`. On the orchestrated
|
|
241
|
+
> path the analysis subagents are granted only read/search tools (`Read`,
|
|
242
|
+
> `Grep`, `Glob`) — the single write is the final report artifact.
|
|
244
243
|
|
|
245
244
|
## Parallel tooling {#parallel-tooling}
|
|
246
245
|
|
|
@@ -95,17 +95,18 @@ suppress the noise.
|
|
|
95
95
|
|
|
96
96
|
## When a number changes
|
|
97
97
|
|
|
98
|
-
Update
|
|
99
|
-
|
|
100
|
-
1. `
|
|
101
|
-
[`
|
|
102
|
-
|
|
103
|
-
[`
|
|
104
|
-
|
|
105
|
-
[`
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
98
|
+
Update two places **in the same commit**:
|
|
99
|
+
|
|
100
|
+
1. `CODING_GUARDRAILS_DEFAULTS` in
|
|
101
|
+
[`scripts/lib/config/quality.js`](../../scripts/lib/config/quality.js).
|
|
102
|
+
The schema literal in
|
|
103
|
+
[`config-settings-schema-quality.js`](../../scripts/lib/config-settings-schema-quality.js)
|
|
104
|
+
imports it, and `npm run docs:gen` propagates the number into
|
|
105
|
+
[`agentrc-reference.json`](../../docs/agentrc-reference.json), the shipped
|
|
106
|
+
JSON-Schema mirror, and the `configuration.md` key table.
|
|
107
|
+
2. The threshold cell or sentence in this helper.
|
|
108
|
+
|
|
109
|
+
The generator-fidelity test (`tests/config-schema-mirror-drift.test.js`)
|
|
110
|
+
catches a stale generated artifact; the helper-prose drift is caught by
|
|
111
|
+
`audit-clean-code` and `agent-protocol` linking back here rather than
|
|
112
|
+
restating the numbers.
|
|
@@ -293,14 +293,9 @@ For every finding, provide:
|
|
|
293
293
|
|
|
294
294
|
Findings that Step 4.5 remediated on `[HEAD_REF]` MUST be rendered under a
|
|
295
295
|
dedicated **`## Fixed on-branch`** heading, **not** in the severity groups
|
|
296
|
-
above. This
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
(the sole canonical reader of the unified comment)
|
|
300
|
-
skips every entry inside this section (both because a fixed entry is
|
|
301
|
-
rendered with a **✅ prefix** — so it carries no leading severity emoji the
|
|
302
|
-
parser would match — and because the parser has an explicit
|
|
303
|
-
Fixed-on-branch section guard).
|
|
296
|
+
above. This keeps a remediated finding legible as remediated to every reader
|
|
297
|
+
of the unified comment — human or downstream — rather than reading as an
|
|
298
|
+
outstanding severity-grouped finding.
|
|
304
299
|
|
|
305
300
|
Render each fixed finding as a `✅`-prefixed line naming its original
|
|
306
301
|
severity, the file path in backticks, and the remediating commit SHA, e.g.:
|
|
@@ -94,7 +94,7 @@ probe logs a warning and leans on init's lease refusal alone.
|
|
|
94
94
|
**Overlapping footprints are reserved across beats, not just within one.** A
|
|
95
95
|
Story sharing a **concrete** path with a still-implementing Story is withheld
|
|
96
96
|
and named in `inFlightReservation: { available, withheld: [{ id, blockedBy,
|
|
97
|
-
reason }], note }`, where `reason` is `in-flight-earlier-beat` or
|
|
97
|
+
reason, source, paths }], note }`, where `reason` is `in-flight-earlier-beat` or
|
|
98
98
|
`foreign-lease`. Like `foreignHeld` this is neither a failure nor a wedge — the
|
|
99
99
|
Story re-admits automatically once its blocker leaves the in-flight set — and
|
|
100
100
|
it exists so an unfilled slot is explained rather than mysterious. A **glob**
|
|
@@ -103,6 +103,33 @@ across beats; it still serializes its own beat. Reservation needs the in-flight
|
|
|
103
103
|
Stories' footprints, so it is a `--probe-live` capability: under `--dag` the
|
|
104
104
|
report is `available: false` and selection de-conflicts within the beat only.
|
|
105
105
|
|
|
106
|
+
**Beat-local skips are reported too**, in `footprintGuard: { mode, withheld,
|
|
107
|
+
advisory, note }`. They used to be an unreported skip, so a Story simply
|
|
108
|
+
vanished from `ready[]` and an unfilled slot read exactly like a cap that was
|
|
109
|
+
never reached. Every entry in **either** report carries the colliding `paths`
|
|
110
|
+
and a `source` tag:
|
|
111
|
+
|
|
112
|
+
- `declared-overlap` — both Stories' `changes[]` named the path (or a declared
|
|
113
|
+
glob). Intended serialization; two Stories rewriting the same generated
|
|
114
|
+
baseline must not co-dispatch.
|
|
115
|
+
- `scraped-overlap` — only the text evidence produced it. Real signal — a
|
|
116
|
+
declaration is only a lower bound — but the class where a false positive is
|
|
117
|
+
possible.
|
|
118
|
+
|
|
119
|
+
**The evidence scrape excludes exactly three token sources**, each structurally
|
|
120
|
+
incapable of naming an edit target: `audit-fingerprints` /
|
|
121
|
+
`audit-semantic-keys` provenance footers, paths under `project.paths.tempRoot`,
|
|
122
|
+
and markdown-link URL interiors. Nothing else is stripped — a
|
|
123
|
+
`<!-- DECOMPOSITION -->` block's paths are genuine intent
|
|
124
|
+
([`instructions.md` § 7](../../instructions.md)) and still count.
|
|
125
|
+
|
|
126
|
+
**`delivery.deliverRunner.footprintGuard`** selects what a collision does:
|
|
127
|
+
|
|
128
|
+
| Mode | Effect |
|
|
129
|
+
| --- | --- |
|
|
130
|
+
| `enforce` (default) | A collision withholds the Story. Keep this unless you have a reason — the guard encodes delivery-time-only knowledge (open implementation windows, foreign leases, ground moved since planning) no `depends_on` edge can carry. |
|
|
131
|
+
| `advisory` | Collisions are still **detected** and listed in `footprintGuard.advisory`, but never withhold; dispatch follows the declared `depends_on` edges alone. A throughput trade for a run whose ordering is fully declared. |
|
|
132
|
+
|
|
106
133
|
## Dispatch mechanics (role-scoped by default)
|
|
107
134
|
|
|
108
135
|
**A single-Story run executes inline.** Sub-agent isolation is
|
|
@@ -118,7 +145,8 @@ engine runs, never what runs — gates, PR, and terminal envelope are identical.
|
|
|
118
145
|
**Read the mode; never infer it from shape.** Before spawning
|
|
119
146
|
anything, read the Story's `dispatchMode` from the resolver envelope
|
|
120
147
|
(`stories[].dispatchMode`, produced by `resolveStoryDispatchMode` in
|
|
121
|
-
`lib/orchestration/complexity-gate.js
|
|
148
|
+
`lib/orchestration/complexity-gate.js`, which decides on the resolved set size
|
|
149
|
+
alone — it does not read the Story body). A Story with `dispatchMode: "inline"`
|
|
122
150
|
executes [`deliver-story.md`](deliver-story.md) **inline in this session** — no
|
|
123
151
|
`story-worker` sub-agent boot and no fresh acceptance-critic sub-agents
|
|
124
152
|
(sub-agent boots are the dominant deliver-phase token cost at trivial scope) —
|