mandrel 2.30.0 → 2.32.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 +4 -4
- package/.agents/docs/agentrc-reference.json +61 -57
- package/.agents/docs/configuration.md +274 -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/ci-remediation.md +44 -1
- 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 +0 -1
- package/.agents/schemas/agentrc.schema.json +1939 -1400
- 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 +7 -29
- package/.agents/scripts/apply-quality-bootstrap.js +27 -34
- 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 +136 -0
- package/.agents/scripts/check-lifecycle-lint.js +36 -112
- 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 +0 -1
- 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/temp-paths.js +6 -5
- package/.agents/scripts/lib/config-settings-schema-delivery.js +237 -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/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/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-entry-sync.js +469 -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 +6 -0
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +0 -1
- package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
- package/.agents/scripts/lib/orchestration/resolve-stories.js +5 -15
- 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/push.js +17 -2
- 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 +65 -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-sizing.js +1 -1
- 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/story-body.js +12 -59
- 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/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/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 +0 -2
- 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/update-dead-exports-baseline.js +321 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +2 -2
- package/.agents/skills/skills.index.json +2 -12
- package/.agents/skills/stack/qa/playwright/SKILL.md +48 -0
- package/.agents/workflows/audit-documentation.md +5 -6
- package/.agents/workflows/audit-to-stories.md +2 -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 +2 -1
- package/.agents/workflows/helpers/deliver-story-reference.md +27 -16
- 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 +49 -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 +12 -3
- 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
|
@@ -11,11 +11,11 @@ links here from the sections that used to inline this content.
|
|
|
11
11
|
|
|
12
12
|
## Friction telemetry
|
|
13
13
|
|
|
14
|
-
Reference mechanics behind the friction-telemetry
|
|
15
|
-
[`instructions.md` § 1.H](../instructions.md).
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
validated.
|
|
14
|
+
Reference mechanics behind the optional friction-telemetry tool pointed at
|
|
15
|
+
from [`instructions.md` § 1.H](../instructions.md). Capture is a **tool, not a
|
|
16
|
+
mandate** — reach for `diagnose-friction.js` when a wrapped command's failure
|
|
17
|
+
shape is worth attributing; the detail below is consulted only when reasoning
|
|
18
|
+
about **where** a friction record lands and **how** it is validated.
|
|
19
19
|
|
|
20
20
|
- **Canonical record + schema validation**: `diagnose-friction.js` appends one
|
|
21
21
|
`kind: friction` record, validated write-time against
|
|
@@ -62,10 +62,10 @@ Unrecognized `AGENT_LOG_LEVEL` values fall back to `info`. There is no
|
|
|
62
62
|
`debug` level alias.
|
|
63
63
|
|
|
64
64
|
This is a diagnostic knob: set it when you need quieter script embedding
|
|
65
|
-
(`silent`) or a deeper trace (`verbose`).
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
schema mechanics are in [§ Friction telemetry](#friction-telemetry) above.
|
|
65
|
+
(`silent`) or a deeper trace (`verbose`). This table is the SSOT for the
|
|
66
|
+
levels; the optional friction-capture tool it sits beside is pointed at from
|
|
67
|
+
[`instructions.md` § 1.H](../instructions.md), and that tool's record-landing
|
|
68
|
+
and schema mechanics are in [§ Friction telemetry](#friction-telemetry) above.
|
|
69
69
|
|
|
70
70
|
---
|
|
71
71
|
|
|
@@ -85,11 +85,10 @@ over-ceiling envelope or an over-budget Story count.
|
|
|
85
85
|
> before shipping the raw seed anyway. The schema now **rejects**
|
|
86
86
|
> `planning.context`, so a config carrying it fails loudly rather than silently
|
|
87
87
|
> capping nothing. The ceiling below is the replacement and the only live bound
|
|
88
|
-
> on planner-context size. Separately, `
|
|
89
|
-
>
|
|
90
|
-
>
|
|
91
|
-
>
|
|
92
|
-
> from that module is live.
|
|
88
|
+
> on planner-context size. Separately, the `ContextEnvelope` SDK this section
|
|
89
|
+
> used to credit with limiting hydrated prompt size had no production caller
|
|
90
|
+
> and was deleted in Story #5005; only its `estimateTokens` helper survived,
|
|
91
|
+
> re-homed in `lib/orchestration/spec-spill.js`.
|
|
93
92
|
|
|
94
93
|
### Planner-context envelope (`/plan`)
|
|
95
94
|
|
|
@@ -172,15 +172,20 @@ baseline still trips the gate.
|
|
|
172
172
|
run on push; use `npm run verify` locally before a PR. CI enforces the
|
|
173
173
|
authoritative full gate set on every PR.
|
|
174
174
|
- **CI** (`.github/workflows/ci.yml`): the `validate` job runs
|
|
175
|
-
**Lint and Format** (`npm run lint`)
|
|
176
|
-
(`npm run maintainability:check` → `check-baselines.js --gate
|
|
177
|
-
maintainability`, diff-scoped on PRs via
|
|
178
|
-
`delivery.quality.gateScoping`, full scope on push-to-main via
|
|
179
|
-
`BASELINE_SCOPE=full`), and **Run Tests with Coverage**
|
|
175
|
+
**Lint and Format** (`npm run lint`) and **Run Tests with Coverage**
|
|
180
176
|
(`npm run test:coverage`), uploading the `test-results` and
|
|
181
177
|
`coverage-final` artifacts. A separate required **baselines** job runs
|
|
182
178
|
the unified `node .agents/scripts/check-baselines.js --format text`,
|
|
183
|
-
which enforces floors across every configured gate
|
|
179
|
+
which enforces floors across every configured gate and is the only
|
|
180
|
+
baseline gate on the per-change path. (Story #5004 removed a
|
|
181
|
+
`Maintainability Check` step from `validate` that re-ran
|
|
182
|
+
`check-baselines.js --gate maintainability` at the same scope; a later
|
|
183
|
+
correction pass revisited its record of what the step's
|
|
184
|
+
`BASELINE_SCOPE=full` branch did — see `docs/ci-contract.md`.)
|
|
185
|
+
- **Nightly** (`.github/workflows/baseline-drift.yml`): the only
|
|
186
|
+
automated **full-scope re-score**. See
|
|
187
|
+
[`check-baseline-drift.js`](#check-baseline-driftjs--the-scheduled-full-scope-re-score)
|
|
188
|
+
below.
|
|
184
189
|
|
|
185
190
|
### Opt-out
|
|
186
191
|
|
|
@@ -248,16 +253,27 @@ the `delivery.acceptanceEval` field reference is in
|
|
|
248
253
|
> Baseline envelope, axes, and component model: see the
|
|
249
254
|
> [Baseline reference](#baseline-reference) section below.
|
|
250
255
|
|
|
251
|
-
The lint baseline
|
|
252
|
-
delivery.
|
|
253
|
-
baseline
|
|
256
|
+
The `lint` baseline kind enforces zero-deterioration during Story
|
|
257
|
+
delivery: `check-baselines.js --gate lint` fails if new lint warnings are
|
|
258
|
+
introduced, and the baseline tightens when the codebase improves.
|
|
254
259
|
|
|
255
260
|
The canonical baseline file lives at `baselines/lint.json` (override via
|
|
256
|
-
`delivery.quality.gates.lint.baselinePath`).
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
+
`delivery.quality.gates.lint.baselinePath`).
|
|
262
|
+
|
|
263
|
+
**There is no framework capture CLI.** Story #5004 retired the
|
|
264
|
+
`lint-baseline.js` shell that used to write this file: it spawned a
|
|
265
|
+
configured lint command and parsed the linter's JSON, a shape only
|
|
266
|
+
ESLint-style output satisfies, and this repo's own `npm run lint`
|
|
267
|
+
(Biome + markdownlint fan-out) never produced it, so the gate was
|
|
268
|
+
configured-but-unfed. A consumer that wants the kind writes
|
|
269
|
+
`baselines/lint.json` from its own linter in the envelope shape documented
|
|
270
|
+
under [Baseline reference](#baseline-reference); a consumer that does not is
|
|
271
|
+
unaffected, because an absent baseline leaves the gate unconfigured.
|
|
272
|
+
|
|
273
|
+
> **Upgrading?** The `project.commands.lintBaseline` key that fed the retired
|
|
274
|
+
> shell is gone from the config schema, which is `additionalProperties: false`
|
|
275
|
+
> — a `.agentrc.json` still carrying it now **fails validation** rather than
|
|
276
|
+
> being silently ignored. Delete the key.
|
|
261
277
|
|
|
262
278
|
Refresh commits should use a `baseline-refresh:` subject + non-empty body so
|
|
263
279
|
the operator can spot baseline edits in review — same convention as the CRAP
|
|
@@ -320,6 +336,58 @@ its `new-method count over c=<flag>` column.
|
|
|
320
336
|
|
|
321
337
|
---
|
|
322
338
|
|
|
339
|
+
## Gherkin corpus gate (opt-in)
|
|
340
|
+
|
|
341
|
+
`check-gherkin-corpus.js` is a static gate over a project's `.feature` corpus.
|
|
342
|
+
It runs inside `npm run lint` — the same required check as the arch-cycle
|
|
343
|
+
ratchet — and it enforces two things:
|
|
344
|
+
|
|
345
|
+
- **must-compile.** Every in-scope `.feature` is parsed with the real
|
|
346
|
+
`@cucumber/gherkin` parser and a failure is reported at `file:line:column`.
|
|
347
|
+
Re-implementing acceptance is the defect the gate exists to prevent: a
|
|
348
|
+
hand-rolled reader skips what it does not recognise, so a corpus that cannot
|
|
349
|
+
generate reads clean.
|
|
350
|
+
- **must-bind.** Every active scenario's steps are resolved against the step
|
|
351
|
+
definitions of **its own scope only**. A file that fails must-compile is
|
|
352
|
+
excluded from must-bind — a broken file parses as an arbitrary subset of
|
|
353
|
+
itself, and linting the remainder buries the one actionable finding.
|
|
354
|
+
|
|
355
|
+
The gate is **opt-in**: with no `qa.gherkinLint` block in `.agentrc.json` it
|
|
356
|
+
reports that it is not configured and exits 0, even when `.feature` files
|
|
357
|
+
exist on disk. An upgrade must never redden the lint of a corpus the consumer
|
|
358
|
+
never asked the framework to police. This repository does not configure it.
|
|
359
|
+
|
|
360
|
+
```jsonc
|
|
361
|
+
"qa": {
|
|
362
|
+
"gherkinLint": {
|
|
363
|
+
"scopes": {
|
|
364
|
+
"web": {
|
|
365
|
+
"featureRoots": ["apps/web/tests/features"],
|
|
366
|
+
"stepRoots": ["apps/web/tests/steps"]
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
"exemptionTags": ["@skip"],
|
|
370
|
+
"stepWaivers": []
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Inside the opt-in the gate fails **closed**. An unresolvable
|
|
376
|
+
`@cucumber/gherkin`, or a scope resolving zero step definitions, exits 1
|
|
377
|
+
naming the cause and the remedy — reporting every step as unbound would be the
|
|
378
|
+
same blackout in a different costume. The parser is an optional peer
|
|
379
|
+
dependency resolved from the consumer project's own module chain, so a
|
|
380
|
+
consumer with no BDD tier gains nothing; install it with
|
|
381
|
+
`npm install --save-dev @cucumber/gherkin` when enabling the gate.
|
|
382
|
+
|
|
383
|
+
Two escapes exist because the step index is a source scan (heuristic) while
|
|
384
|
+
the parser is exact: `exemptionTags` (default `["@skip"]`) drops a scenario
|
|
385
|
+
from must-bind, and `stepWaivers` drops one exact step text. Neither is an
|
|
386
|
+
escape from must-compile — a parse error in an exempt scenario's file still
|
|
387
|
+
fails the run.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
323
391
|
## CRAP gate — Consumer onboarding
|
|
324
392
|
|
|
325
393
|
> Baseline envelope, axes, and component model: see the
|
|
@@ -556,9 +624,110 @@ tolerance, **in either direction**. A row that silently improved is equally
|
|
|
556
624
|
strong evidence the baseline no longer describes the tree.
|
|
557
625
|
|
|
558
626
|
Exit codes: `0` no drift (or every kind skipped), `1` drift detected, `2` the
|
|
559
|
-
check could not run.
|
|
560
|
-
|
|
561
|
-
|
|
627
|
+
check could not run.
|
|
628
|
+
|
|
629
|
+
**`--require-scored`.** "Every kind skipped" mapping to `0` is a
|
|
630
|
+
fail-open trap for the scheduled use this CLI was built for. Measured: with no
|
|
631
|
+
`coverage/coverage-final.json` on disk, `check-baseline-drift.js --gate crap`
|
|
632
|
+
prints `✅ No baseline drift detected` and exits `0` — a nightly job wired that
|
|
633
|
+
way is green and inert. Pass `--require-scored` and any skipped kind exits `2`
|
|
634
|
+
instead, naming the kind and the skip reason. Use it in every scheduled
|
|
635
|
+
invocation.
|
|
636
|
+
|
|
637
|
+
This repository schedules the maintainability kind in
|
|
638
|
+
`.github/workflows/baseline-drift.yml` (framework repo only — that path is not
|
|
639
|
+
part of the materialized `.agents/` payload) — nightly at 05:43 UTC plus
|
|
640
|
+
`workflow_dispatch`; it files or updates one
|
|
641
|
+
`meta::baseline-drift` issue with the report, closes it when the tree comes
|
|
642
|
+
back clean, and fails the run. A consumer materializing `.agents/` still owns
|
|
643
|
+
its own schedule.
|
|
644
|
+
|
|
645
|
+
`crap` is deliberately **not** in that job. Its drift identity is
|
|
646
|
+
`path::method@startLine`, so anything that shifts a method's line re-keys its
|
|
647
|
+
row: measured on this tree with a real coverage artifact, 82 rows drifted but
|
|
648
|
+
1438 were reported added and 898 removed — and 853 of those removals are the
|
|
649
|
+
same `path::method` reappearing at a different line. The added/removed axis is
|
|
650
|
+
re-keying churn, not drift, and the remedy the report prints
|
|
651
|
+
(`npm run crap:update -- --full-scope`) additionally re-measures, pulling in
|
|
652
|
+
near-empty coverage entries minted by CLI-spawning tests. Fixing the identity
|
|
653
|
+
is a prerequisite to scheduling the kind.
|
|
654
|
+
|
|
655
|
+
### `check-baseline-scope.js` — is this baseline still measuring the tree?
|
|
656
|
+
|
|
657
|
+
Drift detection assumes the row set is right and asks whether its numbers
|
|
658
|
+
moved. The prior question went unasked: **does this baseline still describe
|
|
659
|
+
the tree at all?** A ratchet is perfectly capable of being green while
|
|
660
|
+
measuring almost nothing — a row can point at a file deleted months ago, and
|
|
661
|
+
an in-scope file can carry no row whatsoever, and every gate above stays
|
|
662
|
+
green.
|
|
663
|
+
|
|
664
|
+
The scope gate asserts the row set in **both directions**, recomputing each
|
|
665
|
+
kind's in-scope file set from the gate's own configuration —
|
|
666
|
+
`.c8rc.cjs` `include`/`exclude` for coverage,
|
|
667
|
+
`delivery.quality.gates.<kind>.{targetDirs,ignoreGlobs}` for the rest —
|
|
668
|
+
through the same helpers the refresh scorers use, so the gate and the
|
|
669
|
+
producers cannot disagree about scope:
|
|
670
|
+
|
|
671
|
+
```bash
|
|
672
|
+
npm run baselines:scope # every kind
|
|
673
|
+
node .agents/scripts/check-baseline-scope.js --kind coverage --json
|
|
674
|
+
node .agents/scripts/check-baseline-scope.js --strict # skip attribution
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
Two design constraints are worth knowing before reading a report:
|
|
678
|
+
|
|
679
|
+
- **Only dense kinds assert `missing`.** `coverage` and `maintainability`
|
|
680
|
+
emit one row per in-scope file, so a file with no row is a real hole. `crap`
|
|
681
|
+
(per-method, coverage-gated), `duplication` (rows only where clones exist),
|
|
682
|
+
`lint` and `mutation` are sparse by construction — asserting `missing`
|
|
683
|
+
against them yields hundreds of phantom findings on a healthy tree, so they
|
|
684
|
+
assert `extra` only. `lighthouse` (`route`) and `bundle-size` (`bundle`) are
|
|
685
|
+
not file-keyed and are excluded from both.
|
|
686
|
+
- **A PR is blocked only for divergence it created.** Whole-tree equality
|
|
687
|
+
would red every open PR the moment anyone lands an in-scope file, so the
|
|
688
|
+
gate blocks on divergence attributable to `merge-base(base, HEAD)..HEAD` and
|
|
689
|
+
warns about the inherited remainder. It fails towards **strict** — every
|
|
690
|
+
finding fatal — when no base resolves, when HEAD is not ahead of it, or when
|
|
691
|
+
the change set edits a baseline or the config defining its scope.
|
|
692
|
+
|
|
693
|
+
Exit codes: `0` no fatal divergence, `1` fatal divergence, `2` the check could
|
|
694
|
+
not run. It runs in the required `baselines` CI job.
|
|
695
|
+
|
|
696
|
+
### `prune-baseline-orphans.js` — the cheap remedy that makes the gate fair
|
|
697
|
+
|
|
698
|
+
A hard gate is only defensible while clearing it costs a command. Re-deriving
|
|
699
|
+
a whole baseline to express a *deletion* spends a coverage run or a full-tree
|
|
700
|
+
MI pass, which is exactly why stale rows accumulate. The pruner is that
|
|
701
|
+
deletion, done as arithmetic:
|
|
702
|
+
|
|
703
|
+
```bash
|
|
704
|
+
npm run baselines:prune # write the prune
|
|
705
|
+
node .agents/scripts/prune-baseline-orphans.js --check # report only, exit 1
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
It removes exactly two provably-inert row classes across every file-keyed
|
|
709
|
+
baseline — a row whose file is **absent** from disk, and a row for a file now
|
|
710
|
+
**out-of-scope** under the gate's own `targetDirs`/`ignoreGlobs` — and it is
|
|
711
|
+
**measurement-free by contract**: it never adds a row, never restamps
|
|
712
|
+
`generatedAt` (a fresh stamp over rows nobody re-measured is the precise
|
|
713
|
+
failure an age check exists to catch), and recomputes `rollup` through the
|
|
714
|
+
kind's own arithmetic so the pruned envelope still validates against its
|
|
715
|
+
schema. An unreadable scope config degrades to orphan-only pruning rather than
|
|
716
|
+
reading unknown scope as empty scope, which would hand it the whole baseline.
|
|
717
|
+
|
|
718
|
+
A **missing** row is the one thing the pruner will not fix: a file added
|
|
719
|
+
without being measured needs its producer (`npm run coverage:update`,
|
|
720
|
+
`npm run maintainability:update`), because inventing a row would be claiming a
|
|
721
|
+
measurement nobody took.
|
|
722
|
+
|
|
723
|
+
**CI does not run the pruner in either mode.** `--check` exits 1 on any stale
|
|
724
|
+
row without asking which change set introduced it, so pairing it with
|
|
725
|
+
`check-baseline-scope.js` in the required job cancelled that gate's merge-base
|
|
726
|
+
attribution: a row inherited from `main` — say one PR deletes a file while a
|
|
727
|
+
second, branched earlier, re-adds its row through a baseline refresh — reds
|
|
728
|
+
every open PR on divergence its author did not create and cannot fix from
|
|
729
|
+
their branch. The scope gate reports that row as an inherited warning; the
|
|
730
|
+
pruner is the remedy an operator (or agent) runs with the branch in hand.
|
|
562
731
|
|
|
563
732
|
---
|
|
564
733
|
|
|
@@ -651,10 +820,13 @@ Cross-references:
|
|
|
651
820
|
configuration surface that backs the gates.
|
|
652
821
|
- [`.agents/README.md`](../README.md) — consumer onboarding.
|
|
653
822
|
|
|
654
|
-
>
|
|
655
|
-
>
|
|
656
|
-
>
|
|
657
|
-
>
|
|
823
|
+
> `mutation` is a **registered baseline kind with no shipped runner**. The
|
|
824
|
+
> envelope, schema, and floor config below describe a `baselines/mutation.json`
|
|
825
|
+
> the framework can read and ratchet, but nothing in Mandrel invokes Stryker or
|
|
826
|
+
> writes that file: the `update-mutation-baseline.js` refresh CLI was retired
|
|
827
|
+
> in #4482 and the `lib/mutation/` snapshot machinery in #5008. Activating the
|
|
828
|
+
> gate means shipping a runner first — treat the kind as a reserved slot, not a
|
|
829
|
+
> dormant feature.
|
|
658
830
|
|
|
659
831
|
### Envelope
|
|
660
832
|
|
|
@@ -911,8 +1083,8 @@ Refresh paths:
|
|
|
911
1083
|
`baselines/crap.json`.
|
|
912
1084
|
- `node .agents/scripts/update-maintainability-baseline.js` — rewrites
|
|
913
1085
|
`baselines/maintainability.json`.
|
|
914
|
-
- `
|
|
915
|
-
|
|
1086
|
+
- `baselines/lint.json` has no framework refresh CLI — see
|
|
1087
|
+
[Lint baseline ratchet](#lint-baseline-ratchet).
|
|
916
1088
|
|
|
917
1089
|
After a kernel bump, regenerate every baseline whose `kernelVersion`
|
|
918
1090
|
drifted, then commit the refreshed files. The writer guarantees
|
package/.agents/instructions.md
CHANGED
|
@@ -77,11 +77,8 @@ present, else the **Tech Stack** section of `docs/architecture.md`.
|
|
|
77
77
|
|
|
78
78
|
### H. Observability & Friction Telemetry
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
`node .agents/scripts/diagnose-friction.js --story [STORY_ID] --cmd <cmd> <args...>`
|
|
83
|
-
— a **local NDJSON signal** (not posted to the ticket). Schema and the
|
|
84
|
-
`AGENT_LOG_LEVEL` table:
|
|
80
|
+
Optional: `diagnose-friction.js` wraps a command and records its failure
|
|
81
|
+
shape as a local NDJSON signal —
|
|
85
82
|
[`docs/execution-reference.md`](docs/execution-reference.md#friction-telemetry).
|
|
86
83
|
|
|
87
84
|
### I. Anti-Thrashing Protocol
|
|
@@ -51,6 +51,7 @@ the two options above. Name the verdict you reached in the `friction` comment.
|
|
|
51
51
|
| **defect-in-diff** | The failure reproduces on the branch and not on an unmodified `main` | Option 1 — fix at source |
|
|
52
52
|
| **pre-existing** | The same check fails on an unmodified `main` too | Option 2 — file `meta::framework-gap`; remediate here only if it blocks this delivery |
|
|
53
53
|
| **capacity** | Proven exhaustion of a runner resource, not a property of the diff (see below) | Option 2 — file `meta::framework-gap` **and** escalate to the operator |
|
|
54
|
+
| **unreproducible-tier** | The tier cannot be exercised in this sandbox at all, proven by an attempted attach (see below) | Option 2 — file `meta::framework-gap` **and** escalate on first encounter |
|
|
54
55
|
|
|
55
56
|
### The `capacity` verdict
|
|
56
57
|
|
|
@@ -82,10 +83,46 @@ the operator, who owns the runner pool. Do not sit in a retry loop waiting for
|
|
|
82
83
|
capacity to return.
|
|
83
84
|
|
|
84
85
|
**Rerunning a failed job to reach green stays forbidden under every verdict,
|
|
85
|
-
`capacity` included.** The verdict changes who owns the fix and where it is
|
|
86
|
+
`capacity` and `unreproducible-tier` included.** The verdict changes who owns the fix and where it is
|
|
86
87
|
filed; it never licenses a re-run, and it is not a route to a green bar. A
|
|
87
88
|
capacity-blocked delivery ends `agent::blocked` — not merged.
|
|
88
89
|
|
|
90
|
+
### The `unreproducible-tier` verdict
|
|
91
|
+
|
|
92
|
+
A check can fail on a tier the sandbox cannot run at all — most often a
|
|
93
|
+
browser suite whose Playwright `webServer` block supervises a dev server the
|
|
94
|
+
local process manager daemonizes, which aborts the run with
|
|
95
|
+
`Process from config.webServer exited early` before any test executes. The
|
|
96
|
+
failure is a property of the sandbox's ability to *host* the suite, not of the
|
|
97
|
+
diff.
|
|
98
|
+
|
|
99
|
+
This is the same structural hole the `capacity` verdict was added to fill, one
|
|
100
|
+
step earlier in the loop. Without it the honest reading is `flaky`, which routes
|
|
101
|
+
to Option 1 — and fix-at-source requires reproducing the failure, which is the
|
|
102
|
+
one thing that cannot be done. The agent then spends the full timebox
|
|
103
|
+
rediscovering that before escalating anyway, and any fix it does author is
|
|
104
|
+
written blind against a tier it never ran.
|
|
105
|
+
|
|
106
|
+
**Unreproducible must be proven, not inferred.** "The suite did not run for me"
|
|
107
|
+
is not the verdict — it is the symptom every misconfiguration produces. Cite
|
|
108
|
+
both:
|
|
109
|
+
|
|
110
|
+
- **The attempted attach.** Work the attach-don't-boot seam in the
|
|
111
|
+
[`playwright`](../skills/stack/qa/playwright/SKILL.md) skill — boot the server
|
|
112
|
+
out-of-band, point the suite at the running origin, set `reuseExistingServer`
|
|
113
|
+
— and name which step failed and how. A tier that runs once attached was never
|
|
114
|
+
unreproducible.
|
|
115
|
+
- **The observed signature.** The verbatim line the runner aborted on, so a
|
|
116
|
+
later reader can tell a lifetime-ownership mismatch from a genuine boot
|
|
117
|
+
failure in the app under test.
|
|
118
|
+
|
|
119
|
+
Absent both readings the verdict is unavailable and the failure routes as it did
|
|
120
|
+
before. On the verdict: file the `meta::framework-gap` issue with the run link,
|
|
121
|
+
the failure signature, and the attach attempt; flip the Story to
|
|
122
|
+
`agent::blocked` with a `friction` comment naming the verdict; and hand back to
|
|
123
|
+
the operator, who owns the sandbox. Do not author a fix for a tier you could not
|
|
124
|
+
run — a blind fix to a suite nobody exercised is how the gap compounds.
|
|
125
|
+
|
|
89
126
|
## Verifier
|
|
90
127
|
|
|
91
128
|
The check is resolved only when it is **green with zero reruns of the failed
|
|
@@ -131,3 +168,9 @@ operator under **any** of:
|
|
|
131
168
|
signature) and escalate on the first encounter rather than burning iterations
|
|
132
169
|
trying to code around it. A proven-capacity failure is this case: reach the
|
|
133
170
|
`capacity` verdict above and escalate on the first encounter.
|
|
171
|
+
- **Unrunnable tier → escalate immediately.** A tier the sandbox cannot host at
|
|
172
|
+
all is this case too: work the attach seam once, reach the
|
|
173
|
+
`unreproducible-tier` verdict above with its two readings, and escalate on the
|
|
174
|
+
**first encounter**. The 30-minute timebox is a ceiling here, never a budget
|
|
175
|
+
to spend — every minute past the failed attach buys nothing, because no
|
|
176
|
+
iteration can make an unhostable suite run.
|
|
@@ -43,6 +43,33 @@ Practical guidance when authoring a contract change:
|
|
|
43
43
|
can detect "I cannot read this artifact"); they are **not** an
|
|
44
44
|
invitation to keep multiple readers alive in the same release.
|
|
45
45
|
|
|
46
|
+
### Declaring the break so consumers see it
|
|
47
|
+
|
|
48
|
+
A hard cutover is only honest if the consumer can find out about it. Prose in
|
|
49
|
+
a commit body does not qualify: release-please recognizes exactly two signals,
|
|
50
|
+
a `!` before the colon in the subject and a `BREAKING CHANGE:` footer, and
|
|
51
|
+
sees nothing else. Story #5004 removed `project.commands.lintBaseline` from a
|
|
52
|
+
schema block that is `additionalProperties: false` — a config that used to be
|
|
53
|
+
silently ignored now fails validation — described it in three body paragraphs,
|
|
54
|
+
and shipped with neither signal. It reached `main` as `docs:` and would have
|
|
55
|
+
been absent from the release notes entirely.
|
|
56
|
+
|
|
57
|
+
So a contract change MUST declare itself in one of two places, and close
|
|
58
|
+
(`normalize-pr-title.js`) propagates either to the squash subject and PR body:
|
|
59
|
+
|
|
60
|
+
1. **A commit footer** — a `BREAKING CHANGE:` (or `BREAKING-CHANGE:`) line in
|
|
61
|
+
the body of whichever commit does the breaking. This is the default; write
|
|
62
|
+
it as you write the commit. The keyword is uppercase and starts its own
|
|
63
|
+
line, per Conventional Commits.
|
|
64
|
+
2. **A Story-body declaration** — the same footer as its own line in the Story
|
|
65
|
+
issue, naturally at the end of `## Spec`. Use this when the break is known
|
|
66
|
+
at plan time, or when it spans several commits and no single one owns it.
|
|
67
|
+
|
|
68
|
+
Close reads both, marks the PR title `<type>!: …`, and appends the collected
|
|
69
|
+
note to the PR body as a footer. Consumers whose on-disk state needs changing
|
|
70
|
+
should also get a step in `lib/migrations/` — see that directory's README;
|
|
71
|
+
`2.32.0-retire-lint-baseline-command.js` is the step #5004 should have shipped.
|
|
72
|
+
|
|
46
73
|
## Push Validation — the known false-negative signature
|
|
47
74
|
|
|
48
75
|
The core rule is: **never bypass hooks** (`--no-verify`, `--no-gpg-sign`, or
|
|
@@ -125,33 +152,6 @@ different hazards:
|
|
|
125
152
|
(`git -C <main-repo> checkout -- <files>`), then re-run `/deliver <storyId>`.
|
|
126
153
|
Never `git reset --hard` or `git checkout --force` to clear the way.
|
|
127
154
|
|
|
128
|
-
## Documentation Freshness Gate
|
|
129
|
-
|
|
130
|
-
The `validate-docs-freshness.js` gate (run during `/deliver`) asks a
|
|
131
|
-
falsifiable question of every doc in `delivery.docsFreshness.paths` +
|
|
132
|
-
`project.docsContextFiles`: **was this doc actually updated for the Epic?**
|
|
133
|
-
A doc passes on either of two conditions, but they are not
|
|
134
|
-
interchangeable:
|
|
135
|
-
|
|
136
|
-
- **Living docs are satisfied by being rewritten, not annotated.** For
|
|
137
|
-
any non-changelog doc (architecture, decisions, README, guides, …) the
|
|
138
|
-
gate passes **only** when an Epic-referencing commit touched the file —
|
|
139
|
-
a commit whose message references `#<epicId>` and changes the doc.
|
|
140
|
-
Rewrite the doc as part of the Epic's work; do not sprinkle `#<epicId>`
|
|
141
|
-
into its prose to satisfy the check. An appended `#<epicId>` annotation
|
|
142
|
-
alone **fails** the gate for these files, and the failure message names
|
|
143
|
-
the file and the rewrite-not-append contract.
|
|
144
|
-
- **`#<epicId>` body annotations pass only for changelog files.** A
|
|
145
|
-
changelog-class file (basename matches `/changelog/i`, e.g.
|
|
146
|
-
`docs/CHANGELOG.md`) may pass on a body annotation, because an appended
|
|
147
|
-
release note keyed to the Epic is the legitimate, expected update there.
|
|
148
|
-
This is the single sanctioned annotation path; every other doc must use
|
|
149
|
-
the rewrite path above.
|
|
150
|
-
|
|
151
|
-
This restriction exists to remove the perverse incentive by which the
|
|
152
|
-
gate would otherwise reward manufacturing fake provenance — appending
|
|
153
|
-
Epic-ID history into living docs purely to clear the check.
|
|
154
|
-
|
|
155
155
|
## Meta Labels (Retrospective Signal Routing)
|
|
156
156
|
|
|
157
157
|
Two `meta::*` labels route retrospective signals into durable substrates so
|
|
@@ -5,8 +5,8 @@ core**: branch shapes, the Conventional-Commit subject contract, issue
|
|
|
5
5
|
references, and the non-negotiable push/hygiene MUSTs. Deeper mechanics —
|
|
6
6
|
the hard-cutover contract policy, the full push-validation procedure and
|
|
7
7
|
the push-hook false-negative signature, checkout-hygiene scope rules,
|
|
8
|
-
shared-checkout merge contention, the
|
|
9
|
-
|
|
8
|
+
shared-checkout merge contention, and the `meta::*` labels — live in the
|
|
9
|
+
on-demand
|
|
10
10
|
[`git-conventions-reference.md`](git-conventions-reference.md); read it
|
|
11
11
|
**before** doing the matching work.
|
|
12
12
|
|
|
@@ -32,6 +32,8 @@ subject referencing the Story via `(refs #<storyId>)` — see
|
|
|
32
32
|
It does **not** run on squash-merge titles edited in the GitHub UI —
|
|
33
33
|
author the PR title in conventional form so the squash commit parses
|
|
34
34
|
for release-please.
|
|
35
|
+
- Consumer-breaking changes MUST carry a `BREAKING CHANGE:` footer
|
|
36
|
+
(breaking commit or Story body); prose never reaches release-please.
|
|
35
37
|
|
|
36
38
|
## Push Validation & Reliability (MUSTs)
|
|
37
39
|
|
|
@@ -23,13 +23,14 @@ delivery cycle.
|
|
|
23
23
|
|
|
24
24
|
## 1. `npm run lint` prints `Summary: 0 error(s)` and can still exit 1
|
|
25
25
|
|
|
26
|
-
**Behavior.** `npm run lint` is `run-lint.js`, which spawns
|
|
27
|
-
concurrently with inherited stdio and exits with the first non-zero code
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
**Behavior.** `npm run lint` is `run-lint.js`, which spawns eight tools
|
|
27
|
+
concurrently with inherited stdio and exits with the first non-zero code —
|
|
28
|
+
Biome, markdownlint-cli2, the lifecycle lint, the workflow-CLI lint, the
|
|
29
|
+
label-vocabulary lint, the workflow-timeout gate, the arch-cycle ratchet, and
|
|
30
|
+
the Gherkin corpus gate. `Summary: 0 error(s)` is **markdownlint-cli2's own
|
|
31
|
+
verdict**, not the aggregate — it is printed whether or not any of the other
|
|
32
|
+
seven failed. Because the tools run in parallel, that line can land anywhere
|
|
33
|
+
in the output, including last, so the tail of a failing run reads green.
|
|
33
34
|
Biome's format diagnostics are `error`-severity, so a file that only needs
|
|
34
35
|
reformatting fails the check while emitting no lint rule name at all.
|
|
35
36
|
|
|
@@ -55,34 +56,53 @@ with `npm run format`; never reach for `--no-verify`.
|
|
|
55
56
|
`node .agents/scripts/check-baselines.js`, but that script only runs the
|
|
56
57
|
gates configured under `delivery.quality.gates` — currently **crap,
|
|
57
58
|
maintainability, and duplication**. CI's job named `baselines` in
|
|
58
|
-
`.github/workflows/ci.yml` runs that script **and then
|
|
59
|
-
|
|
59
|
+
`.github/workflows/ci.yml` runs that script **and then nine standalone
|
|
60
|
+
commands** the script knows nothing about, so a locally green
|
|
60
61
|
`check-baselines.js` is not evidence that the `baselines` check will pass.
|
|
61
62
|
|
|
62
|
-
|
|
|
63
|
+
| Command CI's `baselines` job runs | Covered by `check-baselines.js` | Covered by `npm run lint` | Covered by `npm run verify` |
|
|
63
64
|
| --- | --- | --- | --- |
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
70
|
-
`check-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
| `check-arch-cycles.js` | no | **yes** | via `lint` |
|
|
66
|
+
| `check-dead-exports.js` | no | no | **yes** |
|
|
67
|
+
| `check-dead-exports.js --production` | no | no | **yes** |
|
|
68
|
+
| `check-context-budget.js` | no | no | **yes** |
|
|
69
|
+
| `check-workflow-citations.js` | no | no | **no** |
|
|
70
|
+
| `check-cyclomatic.js` | no | no | **yes** |
|
|
71
|
+
| `check-schema-references.js` | no | no | **yes** |
|
|
72
|
+
| `check-knip-entries.js` | no | no | **yes** |
|
|
73
|
+
| `check-baseline-scope.js` | no | no | **no** |
|
|
74
|
+
|
|
75
|
+
Two are still in **no** local aggregate command —
|
|
76
|
+
`check-workflow-citations.js` and `check-baseline-scope.js`. Each is reachable
|
|
77
|
+
only as its own npm script (`check:workflow-citations`, `baselines:scope`) or a
|
|
78
|
+
direct invocation.
|
|
79
|
+
|
|
80
|
+
`prune-baseline-orphans.js --check` left this table in v2.32.0: it no longer
|
|
81
|
+
runs in CI in any mode. It reports the same absent / out-of-scope rows as
|
|
82
|
+
`check-baseline-scope.js` but without merge-base attribution, so holding it in
|
|
83
|
+
the required job made the scope gate's inherited-divergence warning
|
|
84
|
+
unreachable — a stale row on `main` red every open PR regardless of who landed
|
|
85
|
+
it. It stays the operator's remedy, via `npm run baselines:prune`.
|
|
86
|
+
|
|
87
|
+
`check-context-budget.js` additionally runs in `.husky/pre-push`. It is
|
|
88
|
+
zero-tolerance in **both** directions — a change that *shrinks* the
|
|
89
|
+
always-loaded doc closure reds it exactly as growth does, and the remedy is a
|
|
90
|
+
committed baseline refresh, not a smaller diff.
|
|
73
91
|
|
|
74
92
|
**Reproduce.**
|
|
75
93
|
|
|
76
94
|
```bash
|
|
77
95
|
node .agents/scripts/check-baselines.js --format text # names the 3 gates it ran
|
|
78
|
-
sed -n '/name: baselines/,/windows-smoke/p' .github/workflows/ci.yml | grep '
|
|
79
|
-
grep "label: '" .agents/scripts/run-verify.js # the
|
|
96
|
+
sed -n '/name: baselines/,/windows-smoke/p' .github/workflows/ci.yml | grep 'js'
|
|
97
|
+
grep "label: '" .agents/scripts/run-verify.js # the 9 steps verify covers
|
|
80
98
|
```
|
|
81
99
|
|
|
82
|
-
**Safe move.** `npm run verify` is the closest local mirror
|
|
83
|
-
`node .agents/scripts/check-workflow-citations.js` alongside it when the
|
|
84
|
-
|
|
85
|
-
|
|
100
|
+
**Safe move.** `npm run verify` is the closest local mirror. Run
|
|
101
|
+
`node .agents/scripts/check-workflow-citations.js` alongside it when the change
|
|
102
|
+
touches workflow prose under `.agents/workflows/`, and
|
|
103
|
+
`npm run baselines:scope && npm run baselines:prune -- --check` when it adds,
|
|
104
|
+
deletes or moves files inside a scored `targetDirs` root. Reproducing only the
|
|
105
|
+
`.agentrc.json` command before a push is a false green.
|
|
86
106
|
|
|
87
107
|
## 3. The two dead-export passes disagree, and the production pass is silent without `!`
|
|
88
108
|
|
|
@@ -90,10 +110,21 @@ the `.agentrc.json` command before a push is a false green.
|
|
|
90
110
|
baselines. The default pass treats `tests/**` as knip entry points, so an
|
|
91
111
|
export whose only importer is a test still reads as *used*; the
|
|
92
112
|
`--production` pass discounts test importers and therefore sees a much
|
|
93
|
-
larger surface — `baselines/dead-exports.json` carries
|
|
94
|
-
`baselines/dead-exports-production.json`'s
|
|
113
|
+
larger surface — `baselines/dead-exports.json` carries 130 rows against
|
|
114
|
+
`baselines/dead-exports-production.json`'s 695. A new export that is only
|
|
95
115
|
imported by its test passes the default pass and fails the production one.
|
|
96
116
|
|
|
117
|
+
Rows come in two shapes. `{ file, symbol: '<name>' }` is one unused export;
|
|
118
|
+
`{ file, symbol: '*' }` is **whole-file death** — a module nothing imports.
|
|
119
|
+
Knip reports such a module once, under its `files` category, and suppresses
|
|
120
|
+
that module's per-export rows, so a file losing its last importer *shrinks*
|
|
121
|
+
the export row set. Reading only `exports` therefore scored losing a whole
|
|
122
|
+
module as an improvement; the `*` rows exist so it reads as the regression
|
|
123
|
+
it is. `knip.json` lists the `.agents/scripts` CLIs as explicit entry paths
|
|
124
|
+
for the same reason: a blanket `.agents/scripts/*.js!` glob declared every
|
|
125
|
+
top-level CLI reachable by construction, so no uninvoked one could ever
|
|
126
|
+
surface. Adding a CLI means adding its entry line.
|
|
127
|
+
|
|
97
128
|
The production pass depends entirely on the `!` suffix on the `entry` and
|
|
98
129
|
`project` patterns in `knip.json`: `!` marks a pattern as
|
|
99
130
|
production-relevant. Strip the suffixes and `knip --production` reports
|
|
@@ -109,6 +140,11 @@ grep -c '!"' knip.json # the production markers
|
|
|
109
140
|
```
|
|
110
141
|
|
|
111
142
|
**Safe move.** Run both passes before pushing. When an export is genuinely
|
|
112
|
-
test-only, keep it and refresh
|
|
113
|
-
`.agents/
|
|
114
|
-
|
|
143
|
+
test-only, keep it and refresh both baselines deliberately with `npm run
|
|
144
|
+
dead-exports:update` — `.agents/scripts/update-dead-exports-baseline.js`,
|
|
145
|
+
which rewrites the rows knip currently reports and fails closed rather than
|
|
146
|
+
persisting an empty snapshot from a knip run that never worked. Never
|
|
147
|
+
hand-edit `baselines/dead-exports*.json`: a hand-written row set is the one
|
|
148
|
+
input no gate re-derives. `.agents/rules/test-seams.md` governs which seams
|
|
149
|
+
are sanctioned. Never remove the `!` suffixes from `knip.json` to quieten the
|
|
150
|
+
production pass.
|