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
|
@@ -7,17 +7,33 @@ against AJV schemas at runtime, and consumed through grouped accessors
|
|
|
7
7
|
(`getCommands()`, `getQuality()`, `getPaths()`, etc.).
|
|
8
8
|
|
|
9
9
|
This document is the reader-facing reference for the post-Epic-#1720 grouped
|
|
10
|
-
shape. The authoritative contract is the
|
|
11
|
-
[`.agents/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
shape. The authoritative contract is the runtime AJV schema under
|
|
11
|
+
[`.agents/scripts/lib/`](../scripts/lib/) — `config-settings-schema.js` plus
|
|
12
|
+
its `-delivery` / `-quality` / `config/gates/*` modules. Since Story #5007
|
|
13
|
+
that schema is the **single annotated source** for the whole surface: the
|
|
14
|
+
shipped JSON-Schema mirror at
|
|
15
|
+
[`.agents/schemas/agentrc.schema.json`](../schemas/agentrc.schema.json), the
|
|
16
|
+
defaults inventory at
|
|
17
|
+
[`.agents/docs/agentrc-reference.json`](agentrc-reference.json), and the key
|
|
18
|
+
table below are all emitted from it by
|
|
19
|
+
[`generate-config-docs.js`](../scripts/generate-config-docs.js), and
|
|
20
|
+
`npm run docs:check` (hence `npm run lint`) fails closed on drift. Nothing is
|
|
21
|
+
hand-mirrored, so a key cannot exist in one artifact and not another.
|
|
16
22
|
|
|
17
23
|
> **Editor support.** `.agentrc.json`, `.agents/starter-agentrc.json`, and
|
|
18
24
|
> `.agents/docs/agentrc-reference.json` all declare
|
|
19
25
|
> `"$schema": "./.agents/schemas/agentrc.schema.json"`, so any editor with
|
|
20
26
|
> JSON Schema support gets autocomplete and inline validation.
|
|
27
|
+
>
|
|
28
|
+
> **Upgrading — the shipped mirror no longer has a `$defs` block.** Story
|
|
29
|
+
> #5007 replaced the hand-kept mirror with a serialization of the runtime
|
|
30
|
+
> schema, which inlines every sub-schema at its point of use. Validation is
|
|
31
|
+
> unchanged (2020-12, same keywords, same accept/reject set on every
|
|
32
|
+
> document) and every key survives, but the named definitions and the
|
|
33
|
+
> `#/$defs/<name>` pointers into them are gone. A `$ref` from your own schema
|
|
34
|
+
> into one of them — e.g.
|
|
35
|
+
> `.agents/schemas/agentrc.schema.json#/$defs/crapGate` — no longer resolves;
|
|
36
|
+
> inline the shape you needed, or point at the containing property path.
|
|
21
37
|
|
|
22
38
|
## Top-level shape
|
|
23
39
|
|
|
@@ -27,7 +43,8 @@ with the runtime validators.
|
|
|
27
43
|
"project": { /* paths, commands, baseBranch, docsContextFiles */ },
|
|
28
44
|
"github": { /* owner, repo, branchProtection, mergeMethods, notifications */ },
|
|
29
45
|
"planning": { /* riskHeuristics, conflict gates, complexityGate, navigation */ },
|
|
30
|
-
"delivery": { /* execution, quality, worktreeIsolation, deliverRunner, ... */ }
|
|
46
|
+
"delivery": { /* execution, quality, worktreeIsolation, deliverRunner, ... */ },
|
|
47
|
+
"qa": { /* featureRoot, fixturesManifest, environments, personas */ }
|
|
31
48
|
}
|
|
32
49
|
```
|
|
33
50
|
|
|
@@ -40,265 +57,290 @@ top-level keys are validation errors.
|
|
|
40
57
|
| `github` | No | Ticketing provider config: owner/repo, branch protection, merge methods, notifications. |
|
|
41
58
|
| `planning` | No | `/plan` tuning: conflict advisories, complexity routing, navigability gate. (Story sizing ceilings and the planner-context cap are code-absolute — not agentrc.) |
|
|
42
59
|
| `delivery` | No | `/deliver` tuning: quality gates, worktree isolation, runners, CI watch, code-review providers. |
|
|
60
|
+
| `qa` | No | Agent-driven QA harness contract: feature root, fixtures, environments, personas. |
|
|
43
61
|
| `$schema` | No | JSON Schema pointer for editor tooling. |
|
|
44
62
|
|
|
45
63
|
---
|
|
46
64
|
|
|
47
65
|
<!-- BEGIN GENERATED:agentrc -->
|
|
48
66
|
|
|
49
|
-
> Generated by `node .agents/scripts/generate-config-docs.js` from
|
|
50
|
-
>
|
|
51
|
-
>
|
|
52
|
-
>
|
|
67
|
+
> Generated by `node .agents/scripts/generate-config-docs.js` from the
|
|
68
|
+
> runtime AJV schema in
|
|
69
|
+
> [`.agents/scripts/lib/config-settings-schema.js`](../scripts/lib/config-settings-schema.js).
|
|
70
|
+
> Edit the `description` / `default` annotations on those schema literals
|
|
71
|
+
> and re-run `npm run docs:gen` — do not hand-edit this region, and do not
|
|
72
|
+
> hand-edit `agentrc.schema.json` or `agentrc-reference.json` either: both
|
|
73
|
+
> are emitted by the same generator.
|
|
53
74
|
|
|
54
75
|
### `project` (required)
|
|
55
76
|
|
|
77
|
+
Project identity, filesystem roots, planner docs context, and the commands the close-validation chain spawns.
|
|
78
|
+
|
|
56
79
|
| Key | Required | Type | Default | Description |
|
|
57
80
|
| --- | --- | --- | --- | --- |
|
|
58
|
-
| `baseBranch` | No | `string` |
|
|
59
|
-
| `paths` | Yes | `object` | — |
|
|
60
|
-
| `paths.agentRoot` | Yes | `string` |
|
|
61
|
-
| `paths.docsRoot` | Yes | `string` |
|
|
62
|
-
| `paths.tempRoot` | Yes | `string` |
|
|
63
|
-
| `docsContextFiles` | No | `array<string>` |
|
|
64
|
-
| `commands` | No | `object` | — |
|
|
65
|
-
| `commands.
|
|
66
|
-
| `commands.
|
|
67
|
-
| `commands.
|
|
68
|
-
| `commands.
|
|
69
|
-
| `commands.formatWrite` | No | `string` | — | — |
|
|
81
|
+
| `baseBranch` | No | `string` | `"main"` | Branch every `story-<id>` branch is seeded from and every Story PR targets. |
|
|
82
|
+
| `paths` | Yes | `object` | — | The three required filesystem roots. Every `${dir}Root` the framework needs is derived at runtime as `${agentRoot}/<dir>`, and the audit output dir as `${tempRoot}/audits`. |
|
|
83
|
+
| `paths.agentRoot` | Yes | `string` | `".agents"` | Repo-relative root of the materialized framework tree (`mandrel sync` writes here). |
|
|
84
|
+
| `paths.docsRoot` | Yes | `string` | `"docs"` | Repo-relative root of the project documentation the planner reads for context. |
|
|
85
|
+
| `paths.tempRoot` | Yes | `string` | `"temp"` | Repo-relative gitignored scratch root. Every temporary artifact — gate transcripts, audit reports, plan authoring dirs — lands under it. |
|
|
86
|
+
| `docsContextFiles` | No | `array<string>` | `["architecture.md","data-dictionary.md","decisions.md","patterns.md"]` | Files under `paths.docsRoot` the planner treats as standing context. Read digest-first — the docs digest names the file and the line range, and only the named section is pulled. |
|
|
87
|
+
| `commands` | No | `object` | — | Shell commands the close-validation chain spawns. Each is run from the repo root. |
|
|
88
|
+
| `commands.test` | No | `string` | `"npm test"` | Full test-suite command run by the close-validation chain. |
|
|
89
|
+
| `commands.typecheck` | No | `string` \| `null` | `null` | Static type-check command. `null` disables the gate for projects with no type layer; the empty string is rejected so a typo cannot silently disable it. |
|
|
90
|
+
| `commands.formatCheck` | No | `string` | `"npx biome format ."` | Non-mutating format verification run as a close-validation gate. |
|
|
91
|
+
| `commands.formatWrite` | No | `string` | `"npx biome format --write ."` | Mutating format command the close-time format-autofix step spawns. |
|
|
70
92
|
|
|
71
93
|
### `github` (optional)
|
|
72
94
|
|
|
95
|
+
GitHub provider identity plus the remote stance the bootstrap enforces. `owner`, `repo`, and `operatorHandle` are operator identity — the shipped values are placeholders, not usable defaults.
|
|
96
|
+
|
|
73
97
|
| Key | Required | Type | Default | Description |
|
|
74
98
|
| --- | --- | --- | --- | --- |
|
|
75
|
-
| `owner` | Yes | `string` |
|
|
76
|
-
| `repo` | Yes | `string` |
|
|
77
|
-
| `projectNumber` | No | `integer` \| `null` |
|
|
78
|
-
| `projectOwner` | No | `string` \| `null` |
|
|
79
|
-
| `operatorHandle` | Yes | `string` |
|
|
80
|
-
| `defaultTimeoutMs` | No | `integer` |
|
|
81
|
-
| `branchProtection` | No | `object` | — |
|
|
82
|
-
| `branchProtection.enforce` | No | `boolean` |
|
|
83
|
-
| `branchProtection.requiredChecks[]` | No | `array<
|
|
84
|
-
| `mergeMethods` | No | `object` | — |
|
|
85
|
-
| `mergeMethods.allow_squash_merge` | No | `boolean` |
|
|
86
|
-
| `mergeMethods.allow_rebase_merge` | No | `boolean` |
|
|
87
|
-
| `mergeMethods.allow_merge_commit` | No | `boolean` |
|
|
88
|
-
| `mergeMethods.allow_auto_merge` | No | `boolean` |
|
|
89
|
-
| `mergeMethods.delete_branch_on_merge` | No | `boolean` |
|
|
90
|
-
| `notifications` | No | `object` | — |
|
|
91
|
-
| `notifications.mentionOperator` | No | `boolean` |
|
|
92
|
-
| `notifications.commentEvents` | No | `array<enum>` |
|
|
93
|
-
| `notifications.webhookEvents` | No | `array<enum>` |
|
|
99
|
+
| `owner` | Yes | `string` | `"[OWNER]"` | GitHub owner (user or org) that hosts the repository. |
|
|
100
|
+
| `repo` | Yes | `string` | `"[REPO]"` | Repository name under `owner`. |
|
|
101
|
+
| `projectNumber` | No | `integer` \| `null` | `null` | Projects V2 board number the orchestrator syncs Story status onto. `null` disables board sync. |
|
|
102
|
+
| `projectOwner` | No | `string` \| `null` | `null` | Owner of the Projects V2 board when it lives outside `owner` (an org board fed by a user repo). `null` means the board shares `owner`. |
|
|
103
|
+
| `operatorHandle` | Yes | `string` | `"@[USERNAME]"` | The human the framework escalates to, `@`-prefixed. Used for HITL @-mentions on `agent::blocked`. |
|
|
104
|
+
| `defaultTimeoutMs` | No | `integer` | `60000` | Default `timeoutMs` applied to every `gh` subprocess the provider facade spawns, so a stalled socket or long-poll cannot hang an orchestration indefinitely. A `GhExecTimeoutError` from a hit ceiling is classified `transient` and retried by `withTransientRetry`. Story #2860. |
|
|
105
|
+
| `branchProtection` | No | `object` | — | Branch-protection stance applied to `project.baseBranch` by the GitHub bootstrap, and reproduced locally before every push. |
|
|
106
|
+
| `branchProtection.enforce` | No | `boolean` | `true` | When true, the GitHub bootstrap writes the required-check ruleset. False leaves the remote stance alone. |
|
|
107
|
+
| `branchProtection.requiredChecks[]` | No | `array<object>` | `[{"name":"lint","cmd":["npm","run","lint"]},{"name":"test","cmd":["npm","test"]},{"name":"baselines","cmd":["node",".agents/scripts/check-baselines.js"]}]` | Checks that must pass before a Story PR merges. Each entry carries both the remote context name and the local argv. Each item has: name, cmd. |
|
|
108
|
+
| `mergeMethods` | No | `object` | — | Repository merge-method stance the GitHub bootstrap enforces. The framework ships squash-only with auto-merge on, which is what the one-PR-per-Story model needs for release-please to parse each landed subject. |
|
|
109
|
+
| `mergeMethods.allow_squash_merge` | No | `boolean` | `true` | — |
|
|
110
|
+
| `mergeMethods.allow_rebase_merge` | No | `boolean` | `false` | — |
|
|
111
|
+
| `mergeMethods.allow_merge_commit` | No | `boolean` | `false` | — |
|
|
112
|
+
| `mergeMethods.allow_auto_merge` | No | `boolean` | `true` | — |
|
|
113
|
+
| `mergeMethods.delete_branch_on_merge` | No | `boolean` | `true` | — |
|
|
114
|
+
| `notifications` | No | `object` | — | Allowlist-gated notification channels. An event fires on a channel only when it is named in that channel's array. |
|
|
115
|
+
| `notifications.mentionOperator` | No | `boolean` | `false` | When true, `github.operatorHandle` is @-mentioned in the comments the notifier posts. |
|
|
116
|
+
| `notifications.commentEvents` | No | `array<enum>` | `["state-transition","story-merged","operator-message"]` | Events mirrored onto the Story issue as a comment. Deliberately narrower than `webhookEvents`: only Story-scoped events whose message reads as narrative an operator wants durably on the ticket belong here. |
|
|
117
|
+
| `notifications.webhookEvents` | No | `array<enum>` | `["state-transition","story-merged","story-closing","operator-message","merge.unlanded","merge.flip-failed"]` | Events dispatched to the configured webhook. The vocabulary is the allowlist the webhook channel gates on; `merge.unlanded` and `merge.flip-failed` are allowlistable but reach the run ledger rather than `notify()` today. |
|
|
94
118
|
|
|
95
119
|
### `planning` (optional)
|
|
96
120
|
|
|
121
|
+
Inputs to `/plan`: risk escalation heuristics, ceremony-lite routing, and the cross-Story conflict-finding severity gates.
|
|
122
|
+
|
|
97
123
|
| Key | Required | Type | Default | Description |
|
|
98
124
|
| --- | --- | --- | --- | --- |
|
|
99
|
-
| `riskHeuristics` | No | `string[]` or `{ append?, prepend? }` |
|
|
125
|
+
| `riskHeuristics` | No | `string[]` or `{ append?, prepend? }` | `["Destructive or irreversible data mutations (dropping tables, deleting rows without soft-delete or backup, truncating production state).","Modifications to shared security or auth infrastructure (IAM policies, auth middleware, session or token handling, secret rotation).","Changes to CI/CD, deployment pipelines, or release gating that could disable safety checks or ship unverified code to production.","Monorepo-wide AST or text replacements touching overlapping files in parallel (catastrophic merge-conflict risk across concurrent agents).","Schema migrations that rewrite existing rows or drop columns without a backfill or rollback plan."]` | Prose heuristics the planner escalates a Story against. A plain array replaces the framework list; the `{ append, prepend }` extender form deep-merges with it. |
|
|
100
126
|
| `complexityGate` | No | `object` | — | Shape-derived ceremony-lite complexity routing. A lite claim is validated against the authored Story shape at persist and re-derived from the Story body at dispatch; conservative (full on any doubt). Never relaxes the Story-ticket / PR-to-main / repo-gates / security-baseline non-negotiables. |
|
|
101
127
|
| `complexityGate.enabled` | No | `boolean` | — | Master switch. When false, lite routing is disabled everywhere: persist refuses lite claims and dispatch always takes the sub-agent path. Default true. |
|
|
102
128
|
| `complexityGate.maxArtifacts` | No | `integer` | — | Enumerated-artifact threshold reported by the plan-context complexity signals. An input signal for the planner verdict — carries no routing authority. Default 1. |
|
|
103
|
-
| `failOnSharedEditors` | No | `boolean` |
|
|
104
|
-
| `requireExplicitCrossStoryDeps` | No | `boolean` |
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
129
|
+
| `failOnSharedEditors` | No | `boolean` | `false` | When true, upgrade shared-editor conflict findings to hard errors (default false — advisory soft findings only). |
|
|
130
|
+
| `requireExplicitCrossStoryDeps` | No | `boolean` | `false` | When true, upgrade implicit cross-Story dependency findings to hard errors (default false — advisory soft findings only). |
|
|
131
|
+
| `crossCuttingRegistries` | No | `string[]` or `{ append?, prepend? }` | `["lib/orchestration/lifecycle/listeners/index.js","**/listeners/index.js","**/handlers/index.js"]` | Registry path patterns whose concurrent edits across Stories are flagged as conflicts. Defaults to the framework listener/handler index patterns when omitted. |
|
|
132
|
+
| `failOnRegistryConflicts` | No | `boolean` | `false` | When true, upgrade cross-cutting registry conflict findings to hard errors (default false). |
|
|
133
|
+
| `failOnLargeFanOut` | No | `boolean` | `false` | When true, upgrade fan-out-warning findings (delete blast radius) to hard errors (default false — soft advisory). |
|
|
134
|
+
| `largeFanOutThreshold` | No | `integer` | `10` | Call-site count above which a Story that deletes a module emits a fan-out-warning. Counts base-branch references to the deleted path basename. Soft by default; does not size or reject Stories. Default 10. |
|
|
109
135
|
| `navigation` | No | `object` | — | Opt-in navigability reachability gate. Absent or empty routeGlobs is a silent no-op. |
|
|
110
|
-
| `navigation.routeGlobs` | No | `array<string>` |
|
|
111
|
-
| `navigation.navRegistry` | No | `array<string>` |
|
|
136
|
+
| `navigation.routeGlobs` | No | `array<string>` | `[]` | Glob patterns (e.g. pages/**, app/**/route.ts) marking paths that add a user-facing route. |
|
|
137
|
+
| `navigation.navRegistry` | No | `array<string>` | `[]` | Tokens identifying the nav-registry SSOT a route-adding Story is expected to reference. |
|
|
112
138
|
|
|
113
139
|
### `delivery` (optional)
|
|
114
140
|
|
|
141
|
+
Everything `/deliver` and `single-story-close` consume: execution timeouts, worktree isolation, runner concurrency, docs freshness, signals, quality gates, merge/CI watch, review ceremony, and the feedback loop.
|
|
142
|
+
|
|
115
143
|
| Key | Required | Type | Default | Description |
|
|
116
144
|
| --- | --- | --- | --- | --- |
|
|
117
|
-
| `execution` | No | `object` | — |
|
|
118
|
-
| `execution.timeoutMs` | No | `integer` |
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `docsFreshness` | No | `object` | — | Nested configuration block. |
|
|
122
|
-
| `docsFreshness.paths` | No | `array` | — | — |
|
|
145
|
+
| `execution` | No | `object` | — | Wall-clock bounds on the subprocesses delivery spawns. |
|
|
146
|
+
| `execution.timeoutMs` | No | `integer` | `600000` | Per-command timeout (ms) for the long-running spawns delivery drives — the close-validation chain and the gate CLIs. |
|
|
147
|
+
| `docsFreshness` | No | `object` | — | Documentation-freshness scope: the files a change of consequence is expected to touch. Read by the audit-documentation lens to seed its target set; no delivery gate enforces it. |
|
|
148
|
+
| `docsFreshness.paths` | No | `array<string>` | `["README.md"]` | Repo-relative documentation paths the audit-documentation lens adds to its target set. |
|
|
123
149
|
| `tempRetention` | No | `object` | — | Story #4794. Auto-purge of spent temp artifacts once their Story lands. Classification is an allowlist: only the declared classes below are ever deleted, so operator scratch files under tempRoot are reported with their size and left alone. signals.ndjson is never purged by any path. |
|
|
124
|
-
| `tempRetention.enabled` | No | `boolean` |
|
|
125
|
-
| `tempRetention.staleDays` | No | `integer` |
|
|
150
|
+
| `tempRetention.enabled` | No | `boolean` | `true` | Master switch. Default true — reclaiming a landed Story's gate transcripts and validation evidence is the behaviour, and this knob turns it off. When false every purge path is a reported no-op. |
|
|
151
|
+
| `tempRetention.staleDays` | No | `integer` | `7` | Age floor (days, default 7) for the families no Story id can be recovered from — roster-level audit reports and abandoned plan-<slug>/ dirs. Story-keyed artifacts do not wait for it: they are purged as soon as their merge is confirmed. |
|
|
126
152
|
| `tempRetention.classes` | No | `object` | — | Per-class opt-out. Each defaults to true; set one false to keep that family while the rest are purged. |
|
|
127
|
-
| `tempRetention.classes.orchestrationLogs` | No | `boolean` |
|
|
128
|
-
| `tempRetention.classes.validationEvidence` | No | `boolean` |
|
|
129
|
-
| `tempRetention.classes.auditResults` | No | `boolean` |
|
|
130
|
-
| `tempRetention.classes.planDirs` | No | `boolean` |
|
|
131
|
-
| `deliverRunner` | No | `object` | — |
|
|
132
|
-
| `deliverRunner.concurrencyCap` | No | `integer` |
|
|
133
|
-
| `worktreeIsolation` | No | `object` | — |
|
|
134
|
-
| `worktreeIsolation.enabled` | No | `boolean` |
|
|
135
|
-
| `worktreeIsolation.root` | No | `string` |
|
|
136
|
-
| `worktreeIsolation.nodeModulesStrategy` | No | `"per-worktree"` \| `"clone"` \| `"symlink"` \| `"pnpm-store"` |
|
|
137
|
-
| `worktreeIsolation.primeFromPath` | No | `string` \| `null` |
|
|
138
|
-
| `worktreeIsolation.allowSymlinkOnWindows` | No | `boolean` |
|
|
139
|
-
| `worktreeIsolation.reapOnSuccess` | No | `boolean` |
|
|
140
|
-
| `worktreeIsolation.bootstrapFiles` | No | `array<string>` | `[".env",".mcp.json",".agentrc.local.json",".agents/instructions.local.md"]` |
|
|
141
|
-
| `signals` | No | `object` | — |
|
|
142
|
-
| `signals.rework` | No | `object` | — |
|
|
143
|
-
| `signals.rework.editsPerFile` | No | `integer` |
|
|
144
|
-
| `signals.retry` | No | `object` | — |
|
|
145
|
-
| `signals.retry.repeatCount` | No | `integer` |
|
|
146
|
-
| `quality` | No | `object` | — |
|
|
147
|
-
| `quality.gateScoping` | No | `object` | — |
|
|
148
|
-
| `quality.gateScoping.scope` | No | `"diff"` \| `"full"` |
|
|
149
|
-
| `quality.gateScoping.diffRef` | No | `string` |
|
|
150
|
-
| `quality.gates` | No | `object` | — |
|
|
151
|
-
| `quality.gates.lint` | No | `object` | — |
|
|
152
|
-
| `quality.gates.lint.enabled` | No | `boolean` |
|
|
153
|
-
| `quality.gates.lint.baselinePath` | No | `string` |
|
|
154
|
-
| `quality.gates.lint.tolerance` | No | `object` |
|
|
155
|
-
| `quality.gates.lint.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — |
|
|
156
|
-
| `quality.gates.lint.tolerance.value` | Yes | `number` | — |
|
|
157
|
-
| `quality.gates.lint.floors` | No | `object<map>` |
|
|
158
|
-
| `quality.gates.lint.components` | No | `object<map>` | — | — |
|
|
159
|
-
| `quality.gates.coverage` | No | `object` | — |
|
|
160
|
-
| `quality.gates.coverage.enabled` | No | `boolean` |
|
|
161
|
-
| `quality.gates.coverage.baselinePath` | No | `string` |
|
|
162
|
-
| `quality.gates.coverage.tolerance` | No | `object` |
|
|
163
|
-
| `quality.gates.coverage.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — |
|
|
164
|
-
| `quality.gates.coverage.tolerance.value` | Yes | `number` | — |
|
|
165
|
-
| `quality.gates.coverage.floors` | No | `object<map>` |
|
|
166
|
-
| `quality.gates.coverage.components` | No | `object<map>` | — | — |
|
|
167
|
-
| `quality.gates.coverage.coveragePath` | No | `string` |
|
|
168
|
-
| `quality.gates.coverage.timeoutMs` | No | `integer` |
|
|
169
|
-
| `quality.gates.crap` | No | `object` | — |
|
|
170
|
-
| `quality.gates.crap.enabled` | No | `boolean` |
|
|
171
|
-
| `quality.gates.crap.baselinePath` | No | `string` |
|
|
172
|
-
| `quality.gates.crap.tolerance` | No | `object` |
|
|
173
|
-
| `quality.gates.crap.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — |
|
|
174
|
-
| `quality.gates.crap.tolerance.value` | Yes | `number` | — |
|
|
175
|
-
| `quality.gates.crap.floors` | No | `object<map>` |
|
|
176
|
-
| `quality.gates.crap.components` | No | `object<map>` | — | — |
|
|
177
|
-
| `quality.gates.crap.targetDirs` | No | `string[]` or `{ append?, prepend? }` |
|
|
178
|
-
| `quality.gates.crap.newMethodCeiling` | No | `integer` |
|
|
179
|
-
| `quality.gates.crap.requireCoverage` | No | `boolean` |
|
|
180
|
-
| `quality.gates.crap.minMethodResolutionRate` | No | `number` | — | Fail-closed floor on the per-method coverage JOIN (Story #4775): the fraction of methods that must resolve a coverage entry, counted only over files that HAVE one, before `update-crap-baseline.js` will persist. A broken join is silent by construction — unresolved methods are simply absent — so the updater refuses rather than writing a thin baseline and logging it as success. Not enforced below 25 joinable methods, where a diff-scoped run
|
|
181
|
-
| `quality.gates.crap.friction` | No | `object` |
|
|
182
|
-
| `quality.gates.crap.friction.markerKey` | No | `string` | — |
|
|
183
|
-
| `quality.gates.crap.refreshTag` | No | `string` |
|
|
184
|
-
| `quality.gates.crap.refreshTimeoutMs` | No | `integer` |
|
|
185
|
-
| `quality.gates.crap.ignoreGlobs` | No | `array<string>` |
|
|
186
|
-
| `quality.gates.crap.incrementalCoverage` | No | `object` | — | Story #4981 — opt-in incremental coverage-capture + CRAP-join scoping. Default (key absent) preserves today
|
|
187
|
-
| `quality.gates.crap.incrementalCoverage.enabled` | No | `boolean` | — |
|
|
188
|
-
| `quality.gates.crap.incrementalCoverage.baseRef` | No | `string` | — |
|
|
189
|
-
| `quality.gates.maintainability` | No | `object` | — |
|
|
190
|
-
| `quality.gates.maintainability.enabled` | No | `boolean` |
|
|
191
|
-
| `quality.gates.maintainability.baselinePath` | No | `string` |
|
|
192
|
-
| `quality.gates.maintainability.tolerance` | No | `object` |
|
|
193
|
-
| `quality.gates.maintainability.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — |
|
|
194
|
-
| `quality.gates.maintainability.tolerance.value` | Yes | `number` | — |
|
|
195
|
-
| `quality.gates.maintainability.floors` | No | `object<map>` |
|
|
196
|
-
| `quality.gates.maintainability.components` | No | `object<map>` | — | — |
|
|
197
|
-
| `quality.gates.maintainability.targetDirs` | No | `string[]` or `{ append?, prepend? }` |
|
|
198
|
-
| `quality.gates.maintainability.refreshTag` | No | `string` | — |
|
|
199
|
-
| `quality.gates.maintainability.refreshTimeoutMs` | No | `integer` |
|
|
200
|
-
| `quality.gates.maintainability.ignoreGlobs` | No | `array<string>` |
|
|
201
|
-
| `quality.gates.mutation` | No | `object` | — |
|
|
202
|
-
| `quality.gates.mutation.enabled` | No | `boolean` |
|
|
203
|
-
| `quality.gates.mutation.baselinePath` | No | `string` |
|
|
204
|
-
| `quality.gates.mutation.tolerance` | No | `object` |
|
|
205
|
-
| `quality.gates.mutation.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — |
|
|
206
|
-
| `quality.gates.mutation.tolerance.value` | Yes | `number` | — |
|
|
207
|
-
| `quality.gates.mutation.floors` | No | `object<map>` |
|
|
208
|
-
| `quality.gates.mutation.components` | No | `object<map>` | — | — |
|
|
209
|
-
| `quality.gates.mutation.strykerConfigPath` | No | `string` \| `null` |
|
|
210
|
-
| `quality.gates.lighthouse` | No | `object` | — |
|
|
211
|
-
| `quality.gates.lighthouse.enabled` | No | `boolean` |
|
|
212
|
-
| `quality.gates.lighthouse.baselinePath` | No | `string` |
|
|
213
|
-
| `quality.gates.lighthouse.tolerance` | No | `object` |
|
|
214
|
-
| `quality.gates.lighthouse.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — |
|
|
215
|
-
| `quality.gates.lighthouse.tolerance.value` | Yes | `number` | — |
|
|
216
|
-
| `quality.gates.lighthouse.floors` | No | `object<map>` |
|
|
217
|
-
| `quality.gates.lighthouse.components` | No | `object<map>` | — | — |
|
|
218
|
-
| `quality.gates.lighthouse.baseUrl` | No | `string` \| `null` |
|
|
219
|
-
| `quality.gates.lighthouse.routes[]` | No | `array<
|
|
220
|
-
| `quality.gates.bundle-size` | No | `object` | — |
|
|
221
|
-
| `quality.gates.bundle-size.enabled` | No | `boolean` |
|
|
222
|
-
| `quality.gates.bundle-size.baselinePath` | No | `string` |
|
|
223
|
-
| `quality.gates.bundle-size.tolerance` | No | `object` |
|
|
224
|
-
| `quality.gates.bundle-size.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — |
|
|
225
|
-
| `quality.gates.bundle-size.tolerance.value` | Yes | `number` | — |
|
|
226
|
-
| `quality.gates.bundle-size.floors` | No | `object<map>` |
|
|
227
|
-
| `quality.gates.bundle-size.components` | No | `object<map>` | — | — |
|
|
228
|
-
| `quality.gates.bundle-size.bundles[]` | No | `array<
|
|
229
|
-
| `quality.gates.duplication` | No | `object` | — |
|
|
230
|
-
| `quality.gates.duplication.enabled` | No | `boolean` |
|
|
231
|
-
| `quality.gates.duplication.baselinePath` | No | `string` |
|
|
232
|
-
| `quality.gates.duplication.tolerance` | No | `object` |
|
|
233
|
-
| `quality.gates.duplication.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — |
|
|
234
|
-
| `quality.gates.duplication.tolerance.value` | Yes | `number` | — |
|
|
235
|
-
| `quality.gates.duplication.floors` | No | `object<map>` |
|
|
236
|
-
| `quality.gates.duplication.components` | No | `object<map>` | — | — |
|
|
237
|
-
| `quality.gates.duplication.targetDirs` | No | `string[]` or `{ append?, prepend? }` |
|
|
238
|
-
| `quality.gates.duplication.refreshTimeoutMs` | No | `integer` |
|
|
239
|
-
| `quality.gates.duplication.ignoreGlobs` | No | `array<string>` |
|
|
153
|
+
| `tempRetention.classes.orchestrationLogs` | No | `boolean` | `true` | <tempRoot>/orchestration/*.log — close gate transcripts and terse-result detail dumps. |
|
|
154
|
+
| `tempRetention.classes.validationEvidence` | No | `boolean` | `true` | Per-Story validation-evidence.json, lifecycle.ndjson, and manifest.md under the standalone and per-run story trees. |
|
|
155
|
+
| `tempRetention.classes.auditResults` | No | `boolean` | `true` | <tempRoot>/audits/ — audit lens reports. |
|
|
156
|
+
| `tempRetention.classes.planDirs` | No | `boolean` | `true` | <tempRoot>/plan-<slug>/ — abandoned plan authoring dirs. Age-floored only; the current run is always excluded. |
|
|
157
|
+
| `deliverRunner` | No | `object` | — | Bounded-concurrency knob for the /deliver fan-out. |
|
|
158
|
+
| `deliverRunner.concurrencyCap` | No | `integer` | `3` | Maximum ready Stories dispatched by /deliver at once. Default 3. Moderate by design — keeps host-quota consumption predictable while allowing a small ready-set fan-out. Set 1 for strictly sequential delivery; raise further on hosts with adequate parallel-agent quota. See deliver.md for the sequencing model and throughput tradeoff. |
|
|
159
|
+
| `worktreeIsolation` | No | `object` | — | Per-Story git worktree provisioning. Each Story is implemented in its own checkout so concurrent siblings never share a working tree. |
|
|
160
|
+
| `worktreeIsolation.enabled` | No | `boolean` | `true` | When true, `single-story-init.js` materializes a worktree per Story. False implements every Story in the main checkout, which is only safe for strictly serial delivery. |
|
|
161
|
+
| `worktreeIsolation.root` | No | `string` | `".worktrees"` | Repo-relative directory the per-Story worktrees are created under. Required whenever `enabled` is explicitly true. |
|
|
162
|
+
| `worktreeIsolation.nodeModulesStrategy` | No | `"per-worktree"` \| `"clone"` \| `"symlink"` \| `"pnpm-store"` | `"clone"` | How each worktree gets its dependencies. `clone` copy-on-writes the main checkout tree (fast, cross-platform); `per-worktree` runs a full install; `symlink` links the shared tree (POSIX only unless `allowSymlinkOnWindows`); `pnpm-store` re-links from the pnpm content store. |
|
|
163
|
+
| `worktreeIsolation.primeFromPath` | No | `string` \| `null` | `null` | Absolute path to an existing `node_modules` tree to prime new worktrees from, instead of the main checkout. `null` uses the main checkout. |
|
|
164
|
+
| `worktreeIsolation.allowSymlinkOnWindows` | No | `boolean` | `false` | Permit the `symlink` strategy on win32, where it needs Developer Mode or elevation. Off by default so a Windows consumer fails over to a strategy that works. |
|
|
165
|
+
| `worktreeIsolation.reapOnSuccess` | No | `boolean` | `true` | Remove the Story's worktree once its PR merges. False keeps it for post-mortem inspection. |
|
|
166
|
+
| `worktreeIsolation.bootstrapFiles` | No | `array<string>` | `[".env",".mcp.json",".agentrc.local.json",".agents/instructions.local.md"]` | Gitignored files copied from the main checkout into every new worktree. A worktree checks out tracked files only, so local secrets and overrides would otherwise be missing. |
|
|
167
|
+
| `signals` | No | `object` | — | Detector thresholds for the surviving performance-signal categories. Each block is shallow-merged by the resolver. |
|
|
168
|
+
| `signals.rework` | No | `object` | — | Rework detector — repeated edits to one file in a run. |
|
|
169
|
+
| `signals.rework.editsPerFile` | No | `integer` | `5` | Edits to a single file within one run that trip the rework signal. |
|
|
170
|
+
| `signals.retry` | No | `object` | — | Retry detector — the same command failing repeatedly. |
|
|
171
|
+
| `signals.retry.repeatCount` | No | `integer` | `3` | Repeats of an identical failing command that trip the retry signal. |
|
|
172
|
+
| `quality` | No | `object` | — | Quality-gate configuration. Every gate lives under `gates.<tier>` and shares the same `{ enabled, baselinePath, tolerance, floors, components }` base; shared scoping lives at this block root. |
|
|
173
|
+
| `quality.gateScoping` | No | `object` | — | Shared scope applied to every gate that supports one, unless the gate overrides it. |
|
|
174
|
+
| `quality.gateScoping.scope` | No | `"diff"` \| `"full"` | `"diff"` | Score only the files changed against `diffRef` (`diff`) or every file in the gate target dirs (`full`). |
|
|
175
|
+
| `quality.gateScoping.diffRef` | No | `string` | `"main"` | Git ref the `diff` scope is computed against. |
|
|
176
|
+
| `quality.gates` | No | `object` | — | The eight quality gates, each sharing the `{ enabled, baselinePath, tolerance, floors, components }` base. |
|
|
177
|
+
| `quality.gates.lint` | No | `object` | — | Lint-count ratchet. Floors are absolute error/warning counts; the baseline pins the current count so a regression is visible even while the floor is not yet met. |
|
|
178
|
+
| `quality.gates.lint.enabled` | No | `boolean` | `true` | When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted. |
|
|
179
|
+
| `quality.gates.lint.baselinePath` | No | `string` | `"baselines/lint.json"` | Repo-root-relative path to the gate's committed baseline artifact. |
|
|
180
|
+
| `quality.gates.lint.tolerance` | No | `object` | `{"kind":"absolute","value":0}` | How much a rollup may drift from the committed baseline before the gate reports a regression. |
|
|
181
|
+
| `quality.gates.lint.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — | Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`). |
|
|
182
|
+
| `quality.gates.lint.tolerance.value` | Yes | `number` | — | The tolerance magnitude. 0 means no drift is allowed. |
|
|
183
|
+
| `quality.gates.lint.floors` | No | `object<map>` | `{"*":{"errorCount":0}}` | Workspace-keyed absolute floors: `{ "<workspace>": { "<metric>": number } }`. `"*"` is the project-wide catch-all; the metric keyset is open so per-rollup keys flow through without each gate enumerating them. Floors are absolute — unlike `tolerance`, they are enforced regardless of the baseline. |
|
|
184
|
+
| `quality.gates.lint.components` | No | `object<map>` | — | Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ "*": ["**"] }` at the resolver layer. |
|
|
185
|
+
| `quality.gates.coverage` | No | `object` | — | Line/branch/function coverage ratchet, read from the Istanbul JSON summary the project test run emits. |
|
|
186
|
+
| `quality.gates.coverage.enabled` | No | `boolean` | `true` | When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted. |
|
|
187
|
+
| `quality.gates.coverage.baselinePath` | No | `string` | `"baselines/coverage.json"` | Repo-root-relative path to the gate's committed baseline artifact. |
|
|
188
|
+
| `quality.gates.coverage.tolerance` | No | `object` | `{"kind":"absolute","value":0}` | How much a rollup may drift from the committed baseline before the gate reports a regression. |
|
|
189
|
+
| `quality.gates.coverage.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — | Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`). |
|
|
190
|
+
| `quality.gates.coverage.tolerance.value` | Yes | `number` | — | The tolerance magnitude. 0 means no drift is allowed. |
|
|
191
|
+
| `quality.gates.coverage.floors` | No | `object<map>` | `{"*":{"lines":90,"branches":85,"functions":90}}` | Workspace-keyed absolute floors: `{ "<workspace>": { "<metric>": number } }`. `"*"` is the project-wide catch-all; the metric keyset is open so per-rollup keys flow through without each gate enumerating them. Floors are absolute — unlike `tolerance`, they are enforced regardless of the baseline. |
|
|
192
|
+
| `quality.gates.coverage.components` | No | `object<map>` | — | Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ "*": ["**"] }` at the resolver layer. |
|
|
193
|
+
| `quality.gates.coverage.coveragePath` | No | `string` | `"coverage/coverage-final.json"` | Repo-relative path to the Istanbul `coverage-final.json` the capture step writes and the gate reads. |
|
|
194
|
+
| `quality.gates.coverage.timeoutMs` | No | `integer` | `600000` | Bounded timeout (ms) for the `npm run test:coverage` capture spawn. A SIGKILL at the budget boundary maps to exit 124 so close-validation can tell a hang from a test failure. |
|
|
195
|
+
| `quality.gates.crap` | No | `object` | — | CRAP (Change Risk Anti-Pattern) ratchet — per-method cyclomatic complexity joined against per-method coverage. |
|
|
196
|
+
| `quality.gates.crap.enabled` | No | `boolean` | `true` | When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted. |
|
|
197
|
+
| `quality.gates.crap.baselinePath` | No | `string` | `"baselines/crap.json"` | Repo-root-relative path to the gate's committed baseline artifact. |
|
|
198
|
+
| `quality.gates.crap.tolerance` | No | `object` | `{"kind":"absolute","value":0.05}` | How much a rollup may drift from the committed baseline before the gate reports a regression. |
|
|
199
|
+
| `quality.gates.crap.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — | Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`). |
|
|
200
|
+
| `quality.gates.crap.tolerance.value` | Yes | `number` | — | The tolerance magnitude. 0 means no drift is allowed. |
|
|
201
|
+
| `quality.gates.crap.floors` | No | `object<map>` | `{"*":{"max":30,"p95":20,"methodsAbove20":50}}` | Workspace-keyed absolute floors: `{ "<workspace>": { "<metric>": number } }`. `"*"` is the project-wide catch-all; the metric keyset is open so per-rollup keys flow through without each gate enumerating them. Floors are absolute — unlike `tolerance`, they are enforced regardless of the baseline. |
|
|
202
|
+
| `quality.gates.crap.components` | No | `object<map>` | — | Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ "*": ["**"] }` at the resolver layer. |
|
|
203
|
+
| `quality.gates.crap.targetDirs` | No | `string[]` or `{ append?, prepend? }` | `["src"]` | Directories whose JS sources the CRAP gate scores. Mandrel ships a `src/`-centric default; projects whose executable code lives elsewhere (e.g. this repo's `.agents/scripts/`) override here. The framework default is intentionally not auto-discovered, so an override is the explicit, auditable signal. |
|
|
204
|
+
| `quality.gates.crap.newMethodCeiling` | No | `integer` | `30` | Hard CRAP ceiling applied to a method the diff introduces. A new method above it fails the gate regardless of the baseline. |
|
|
205
|
+
| `quality.gates.crap.requireCoverage` | No | `boolean` | `true` | When true, the gate refuses to score without a coverage artifact rather than silently reporting complexity-only rows. |
|
|
206
|
+
| `quality.gates.crap.minMethodResolutionRate` | No | `number` | — | Fail-closed floor on the per-method coverage JOIN (Story #4775): the fraction of methods that must resolve a coverage entry, counted only over files that HAVE one, before `update-crap-baseline.js` will persist. A broken join is silent by construction — unresolved methods are simply absent — so the updater refuses rather than writing a thin baseline and logging it as success. Not enforced below 25 joinable methods, where a diff-scoped run’s rate is noise. Default 0.75; a healthy repo resolves ~98%. |
|
|
207
|
+
| `quality.gates.crap.friction` | No | `object` | `{"markerKey":"crap-baseline-regression"}` | Friction-signal wiring for a CRAP baseline regression, so a recurring one can reach the actionable threshold. |
|
|
208
|
+
| `quality.gates.crap.friction.markerKey` | No | `string` | — | Signal marker key the regression is recorded under. |
|
|
209
|
+
| `quality.gates.crap.refreshTag` | No | `string` | `"baseline-refresh:"` | Commit-subject substring that acknowledges a deliberate CRAP baseline refresh in the compared range. A range commit carrying it that also touches the baseline file demotes head-vs-base regressions; floors stay enforced. |
|
|
210
|
+
| `quality.gates.crap.refreshTimeoutMs` | No | `integer` | `60000` | Bounded timeout (ms) for `npm run crap:update` spawned by the baseline-attribution refresh path. Mirrors `coverage.timeoutMs`: a SIGKILL fired at the budget boundary maps to exit 124 so the close orchestrator can flip the Story to `agent::blocked`. Default 60000 (Story #2165). |
|
|
211
|
+
| `quality.gates.crap.ignoreGlobs` | No | `array<string>` | `[]` | Minimatch glob patterns matched against the canonicalised repo-relative path of each discovered file. Files matching any pattern are excluded from CRAP discovery before scoring. Orthogonal to `components` (grouping) — a file excluded here never appears in any component bucket. Absent or empty preserves the existing IGNORED_DIRS-only behaviour (Story #3217). |
|
|
212
|
+
| `quality.gates.crap.incrementalCoverage` | No | `object` | — | Story #4981 — opt-in incremental coverage-capture + CRAP-join scoping. Default (key absent) preserves today’s full-repo behaviour byte-for-byte. When `enabled: true`, `coverage-capture.js` scopes `npm run test:coverage` to the files changed against `baseRef` (default: the gate’s own `--ref` / `main`), and the CRAP join treats a method in a file the diff did not touch as resolved by its committed baseline row instead of requiring fresh coverage for it. |
|
|
213
|
+
| `quality.gates.crap.incrementalCoverage.enabled` | No | `boolean` | — | Master switch for incremental capture + join scoping. |
|
|
214
|
+
| `quality.gates.crap.incrementalCoverage.baseRef` | No | `string` | — | Git ref the changed-file set is computed against. Omitted falls back to the gate’s own `--ref` (`main`). |
|
|
215
|
+
| `quality.gates.maintainability` | No | `object` | — | Maintainability-index ratchet. Scores per file as the average over its methods, so deleting a small high-MI method can legitimately lower a file’s score. |
|
|
216
|
+
| `quality.gates.maintainability.enabled` | No | `boolean` | `true` | When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted. |
|
|
217
|
+
| `quality.gates.maintainability.baselinePath` | No | `string` | `"baselines/maintainability.json"` | Repo-root-relative path to the gate's committed baseline artifact. |
|
|
218
|
+
| `quality.gates.maintainability.tolerance` | No | `object` | `{"kind":"absolute","value":0.5}` | How much a rollup may drift from the committed baseline before the gate reports a regression. |
|
|
219
|
+
| `quality.gates.maintainability.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — | Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`). |
|
|
220
|
+
| `quality.gates.maintainability.tolerance.value` | Yes | `number` | — | The tolerance magnitude. 0 means no drift is allowed. |
|
|
221
|
+
| `quality.gates.maintainability.floors` | No | `object<map>` | `{"*":{"min":70}}` | Workspace-keyed absolute floors: `{ "<workspace>": { "<metric>": number } }`. `"*"` is the project-wide catch-all; the metric keyset is open so per-rollup keys flow through without each gate enumerating them. Floors are absolute — unlike `tolerance`, they are enforced regardless of the baseline. |
|
|
222
|
+
| `quality.gates.maintainability.components` | No | `object<map>` | — | Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ "*": ["**"] }` at the resolver layer. |
|
|
223
|
+
| `quality.gates.maintainability.targetDirs` | No | `string[]` or `{ append?, prepend? }` | `[]` | Directories whose JS sources the maintainability gate scores. Mandrel ships a `src/`-centric default; projects whose executable code lives elsewhere (e.g. this repo's `.agents/scripts/` plus `tests/`) override here. The framework default is intentionally not auto-discovered, so an override is the explicit, auditable signal. |
|
|
224
|
+
| `quality.gates.maintainability.refreshTag` | No | `string` | — | Commit-subject substring that acknowledges a deliberate maintainability baseline refresh in the compared range. Mirrors the CRAP gate's `refreshTag`; a range commit carrying it that touches the baseline file demotes head-vs-base regressions (floors still enforced). |
|
|
225
|
+
| `quality.gates.maintainability.refreshTimeoutMs` | No | `integer` | `60000` | Bounded timeout (ms) for `npm run maintainability:update` spawned by the baseline-attribution refresh path. Mirrors `coverage.timeoutMs`: a SIGKILL fired at the budget boundary maps to exit 124 so the close orchestrator can flip the Story to `agent::blocked`. Default 60000 (Story #2165). |
|
|
226
|
+
| `quality.gates.maintainability.ignoreGlobs` | No | `array<string>` | `[]` | Minimatch glob patterns matched against the canonicalised repo-relative path of each discovered file. Files matching any pattern are excluded from MI discovery before scoring. Orthogonal to `components` (grouping) — a file excluded here never appears in any component bucket. Absent or empty preserves the existing IGNORED_DIRS-only behaviour (Story #3217). |
|
|
227
|
+
| `quality.gates.mutation` | No | `object` | — | Stryker mutation-score ratchet. Off in practice for most consumers — the baseline kind is registered but no framework path runs Stryker. |
|
|
228
|
+
| `quality.gates.mutation.enabled` | No | `boolean` | `true` | When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted. |
|
|
229
|
+
| `quality.gates.mutation.baselinePath` | No | `string` | `"baselines/mutation.json"` | Repo-root-relative path to the gate's committed baseline artifact. |
|
|
230
|
+
| `quality.gates.mutation.tolerance` | No | `object` | `{"kind":"percent","value":0}` | How much a rollup may drift from the committed baseline before the gate reports a regression. |
|
|
231
|
+
| `quality.gates.mutation.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — | Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`). |
|
|
232
|
+
| `quality.gates.mutation.tolerance.value` | Yes | `number` | — | The tolerance magnitude. 0 means no drift is allowed. |
|
|
233
|
+
| `quality.gates.mutation.floors` | No | `object<map>` | `{"*":{"score":60}}` | Workspace-keyed absolute floors: `{ "<workspace>": { "<metric>": number } }`. `"*"` is the project-wide catch-all; the metric keyset is open so per-rollup keys flow through without each gate enumerating them. Floors are absolute — unlike `tolerance`, they are enforced regardless of the baseline. |
|
|
234
|
+
| `quality.gates.mutation.components` | No | `object<map>` | — | Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ "*": ["**"] }` at the resolver layer. |
|
|
235
|
+
| `quality.gates.mutation.strykerConfigPath` | No | `string` \| `null` | `null` | Repo-relative path to the Stryker config. `null` lets Stryker resolve its own default. |
|
|
236
|
+
| `quality.gates.lighthouse` | No | `object` | — | Lighthouse category-score ratchet for web targets. Off by default — it needs a running deployment. |
|
|
237
|
+
| `quality.gates.lighthouse.enabled` | No | `boolean` | `false` | When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted. |
|
|
238
|
+
| `quality.gates.lighthouse.baselinePath` | No | `string` | `"baselines/lighthouse.json"` | Repo-root-relative path to the gate's committed baseline artifact. |
|
|
239
|
+
| `quality.gates.lighthouse.tolerance` | No | `object` | `{"kind":"absolute","value":0}` | How much a rollup may drift from the committed baseline before the gate reports a regression. |
|
|
240
|
+
| `quality.gates.lighthouse.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — | Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`). |
|
|
241
|
+
| `quality.gates.lighthouse.tolerance.value` | Yes | `number` | — | The tolerance magnitude. 0 means no drift is allowed. |
|
|
242
|
+
| `quality.gates.lighthouse.floors` | No | `object<map>` | `{"*":{"performance":0,"accessibility":0,"bestPractices":0,"seo":0}}` | Workspace-keyed absolute floors: `{ "<workspace>": { "<metric>": number } }`. `"*"` is the project-wide catch-all; the metric keyset is open so per-rollup keys flow through without each gate enumerating them. Floors are absolute — unlike `tolerance`, they are enforced regardless of the baseline. |
|
|
243
|
+
| `quality.gates.lighthouse.components` | No | `object<map>` | — | Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ "*": ["**"] }` at the resolver layer. |
|
|
244
|
+
| `quality.gates.lighthouse.baseUrl` | No | `string` \| `null` | `null` | Origin the routes are resolved against. `null` leaves the gate unusable, which is why it ships disabled. |
|
|
245
|
+
| `quality.gates.lighthouse.routes[]` | No | `array<object>` | `[]` | Routes to score on each run. Each item has: path, formFactor. |
|
|
246
|
+
| `quality.gates.bundle-size` | No | `object` | — | Built-artifact size ratchet for web targets. Off by default — it needs declared bundles. |
|
|
247
|
+
| `quality.gates.bundle-size.enabled` | No | `boolean` | `false` | When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted. |
|
|
248
|
+
| `quality.gates.bundle-size.baselinePath` | No | `string` | `"baselines/bundle-size.json"` | Repo-root-relative path to the gate's committed baseline artifact. |
|
|
249
|
+
| `quality.gates.bundle-size.tolerance` | No | `object` | `{"kind":"percent","value":0}` | How much a rollup may drift from the committed baseline before the gate reports a regression. |
|
|
250
|
+
| `quality.gates.bundle-size.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — | Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`). |
|
|
251
|
+
| `quality.gates.bundle-size.tolerance.value` | Yes | `number` | — | The tolerance magnitude. 0 means no drift is allowed. |
|
|
252
|
+
| `quality.gates.bundle-size.floors` | No | `object<map>` | `{"*":{}}` | Workspace-keyed absolute floors: `{ "<workspace>": { "<metric>": number } }`. `"*"` is the project-wide catch-all; the metric keyset is open so per-rollup keys flow through without each gate enumerating them. Floors are absolute — unlike `tolerance`, they are enforced regardless of the baseline. |
|
|
253
|
+
| `quality.gates.bundle-size.components` | No | `object<map>` | — | Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ "*": ["**"] }` at the resolver layer. |
|
|
254
|
+
| `quality.gates.bundle-size.bundles[]` | No | `array<object>` | `[]` | The artifacts to weigh. Empty means the gate has nothing to do. Each item has: name, path, limit. |
|
|
255
|
+
| `quality.gates.duplication` | No | `object` | — | Code-duplication (DRY) gate (Story #3664). Shares the gate base and adds the scan-scope extras: the `targetDirs` the duplication scanner walks, a bounded refresh timeout mirroring crap/MI, and `ignoreGlobs` to exclude files from the scan. |
|
|
256
|
+
| `quality.gates.duplication.enabled` | No | `boolean` | `false` | When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted. |
|
|
257
|
+
| `quality.gates.duplication.baselinePath` | No | `string` | `"baselines/duplication.json"` | Repo-root-relative path to the gate's committed baseline artifact. |
|
|
258
|
+
| `quality.gates.duplication.tolerance` | No | `object` | `{"kind":"absolute","value":1}` | How much a rollup may drift from the committed baseline before the gate reports a regression. |
|
|
259
|
+
| `quality.gates.duplication.tolerance.kind` | Yes | `"absolute"` \| `"percent"` | — | Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`). |
|
|
260
|
+
| `quality.gates.duplication.tolerance.value` | Yes | `number` | — | The tolerance magnitude. 0 means no drift is allowed. |
|
|
261
|
+
| `quality.gates.duplication.floors` | No | `object<map>` | `{"*":{"percentage":25}}` | Workspace-keyed absolute floors: `{ "<workspace>": { "<metric>": number } }`. `"*"` is the project-wide catch-all; the metric keyset is open so per-rollup keys flow through without each gate enumerating them. Floors are absolute — unlike `tolerance`, they are enforced regardless of the baseline. |
|
|
262
|
+
| `quality.gates.duplication.components` | No | `object<map>` | — | Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ "*": ["**"] }` at the resolver layer. |
|
|
263
|
+
| `quality.gates.duplication.targetDirs` | No | `string[]` or `{ append?, prepend? }` | `["src"]` | Directories whose JS sources the duplication (DRY) gate scans for copy-paste clones. Mandrel ships a `src/`-centric default; projects whose executable code lives elsewhere (e.g. this repo's `.agents/scripts/`) override here. The framework default is intentionally not auto-discovered, so an override is the explicit, auditable signal (Story #3664). |
|
|
264
|
+
| `quality.gates.duplication.refreshTimeoutMs` | No | `integer` | `60000` | Bounded timeout (ms) for `npm run duplication:update` spawned by the baseline-attribution refresh path. Mirrors `crap.refreshTimeoutMs` / `coverage.timeoutMs`: a SIGKILL fired at the budget boundary maps to exit 124. Default 60000 (Story #3664). |
|
|
265
|
+
| `quality.gates.duplication.ignoreGlobs` | No | `array<string>` | `[]` | Minimatch glob patterns matched against the canonicalised repo-relative path of each discovered file. Files matching any pattern are excluded from duplication discovery before scanning. Orthogonal to `components` (grouping). Absent or empty preserves the existing behaviour (Story #3664). |
|
|
240
266
|
| `quality.formatAutofix` | No | `object` | — | Bounded-timeout knob for the close-time `npx biome format --write` spawn (Story #2165). A SIGKILL fired at the budget boundary maps to exit 124 so the close orchestrator can flip the Story to `agent::blocked` with a friction comment. |
|
|
241
|
-
| `quality.formatAutofix.timeoutMs` | No | `integer` |
|
|
242
|
-
| `quality.codingGuardrails` | No | `object` | — |
|
|
243
|
-
| `quality.codingGuardrails.cyclomaticFlag` | No | `integer` |
|
|
244
|
-
| `quality.codingGuardrails.cyclomaticMustFix` | No | `integer` |
|
|
245
|
-
| `quality.codingGuardrails.requireSiblingTest` | No | `boolean` |
|
|
246
|
-
| `quality.autoRefresh` | No | `object` | — |
|
|
247
|
-
| `quality.autoRefresh.enabled` | No | `boolean` |
|
|
248
|
-
| `quality.autoRefresh.crapJumpCap` | No | `number` |
|
|
249
|
-
| `quality.autoRefresh.scope` | No | `"diff"` \| `"full"` |
|
|
267
|
+
| `quality.formatAutofix.timeoutMs` | No | `integer` | `60000` | Timeout (ms) for the format-write spawn. |
|
|
268
|
+
| `quality.codingGuardrails` | No | `object` | — | Authoring-time cyclomatic-complexity advisories surfaced by the quality-preview pre-commit gate. |
|
|
269
|
+
| `quality.codingGuardrails.cyclomaticFlag` | No | `integer` | `8` | Cyclomatic complexity at which a new or changed method is flagged for a refactor look. |
|
|
270
|
+
| `quality.codingGuardrails.cyclomaticMustFix` | No | `integer` | `12` | Cyclomatic complexity at which a new or changed method must be decomposed before the diff closes. |
|
|
271
|
+
| `quality.codingGuardrails.requireSiblingTest` | No | `boolean` | `false` | When true, a new source file with no colocated sibling test is reported by the guardrails pass. |
|
|
272
|
+
| `quality.autoRefresh` | No | `object` | — | Baseline-attribution auto-refresh: when a gate can prove a regression is a legitimate consequence of the diff, it rewrites the baseline instead of blocking. |
|
|
273
|
+
| `quality.autoRefresh.enabled` | No | `boolean` | `true` | Master switch for the auto-refresh path. When false, every baseline refresh is a deliberate operator action. |
|
|
274
|
+
| `quality.autoRefresh.crapJumpCap` | No | `number` | `5` | Largest single-row CRAP jump the auto-refresh will absorb. A larger jump is reported as a regression rather than rewritten. |
|
|
275
|
+
| `quality.autoRefresh.scope` | No | `"diff"` \| `"full"` | `"diff"` | Whether an auto-refresh rescores only the changed files (`diff`) or every file in the target dirs (`full`). |
|
|
250
276
|
| `quality.baselineEpsilon` | No | `object` | — | Per-kind epsilon for s-stability-epsilon (Story #1964). Sub-epsilon row deltas resolve to prior bytes so env variance does not rewrite the on-disk baseline. |
|
|
251
|
-
| `quality.baselineEpsilon.maintainability` | No | `number` |
|
|
252
|
-
| `quality.baselineEpsilon.crap` | No | `number` |
|
|
253
|
-
| `quality.baselineEpsilon.coverage` | No | `number` |
|
|
254
|
-
| `quality.baselineEpsilon.mutation` | No | `number` |
|
|
255
|
-
| `quality.baselineEpsilon.lint` | No | `number` |
|
|
256
|
-
| `quality.baselineEpsilon.lighthouse` | No | `number` |
|
|
257
|
-
| `quality.baselineEpsilon.bundle-size` | No | `number` |
|
|
258
|
-
| `quality.baselineEpsilon.duplication` | No | `number` |
|
|
259
|
-
| `quality.requireBaselines` | No | `boolean` |
|
|
277
|
+
| `quality.baselineEpsilon.maintainability` | No | `number` | `0.5` | — |
|
|
278
|
+
| `quality.baselineEpsilon.crap` | No | `number` | `0.5` | — |
|
|
279
|
+
| `quality.baselineEpsilon.coverage` | No | `number` | `0.1` | — |
|
|
280
|
+
| `quality.baselineEpsilon.mutation` | No | `number` | `0.5` | — |
|
|
281
|
+
| `quality.baselineEpsilon.lint` | No | `number` | `0` | — |
|
|
282
|
+
| `quality.baselineEpsilon.lighthouse` | No | `number` | `1` | — |
|
|
283
|
+
| `quality.baselineEpsilon.bundle-size` | No | `number` | `1024` | — |
|
|
284
|
+
| `quality.baselineEpsilon.duplication` | No | `number` | `0.5` | — |
|
|
285
|
+
| `quality.requireBaselines` | No | `boolean` | `false` | Story #4495. Fail-closed baseline-enforcement policy for the unified check-baselines close-validation gate. When false (default), a consumer that enables baseline gates (crap/maintainability/…) but has not committed the corresponding baseline artifacts under baselines/ gets a clean skip-with-reason instead of a deterministic first-try close failure. Set true to keep the gate registered so an absent baseline artifact fails close-validation with a preflight hint naming the fix (the fail-closed posture). |
|
|
260
286
|
| `quality.navigability` | No | `object` | — | Navigability lens + journey-suite config (Epic #4131, F2/F3/F1/F4). Read by audit-suite/selector.js (route globs) and /deliver's per-Story ceremony (journey suite). Opt-in: absent or empty routeGlobs degrades to a silent no-op. |
|
|
261
|
-
| `quality.navigability.routeGlobs` | No | `array<string>` |
|
|
262
|
-
| `quality.navigability.navRegistry` | No | `array<string>` |
|
|
287
|
+
| `quality.navigability.routeGlobs` | No | `array<string>` | `[]` | Glob patterns (pages/**, app/**/route.ts) marking paths that add a user-facing route — the route-tree SSOT the navigability lens enumerates and the route-added routing predicate matches against. |
|
|
288
|
+
| `quality.navigability.navRegistry` | No | `array<string>` | `[]` | Tokens identifying the nav-registry SSOT the navigability lens checks every route resolves a nav door against. |
|
|
263
289
|
| `quality.navigability.journeySuite` | No | `string` | — | Path or command for the per-persona journey suite /deliver's per-Story ceremony runs. |
|
|
264
290
|
| `mergeWatch` | No | `object` | — | Knobs consumed by the close-and-land merge wait (Story #4543; defaults in `lib/orchestration/merge-poll.js`). `mode` (Story #4698) selects the close-time merge posture. `intervalSeconds` is the poll cadence between `gh pr view` probes after the arm. `maxWaitSeconds` bounds ONE invocation of the merge wait and its expiry returns a resumable `pending` terminal with no label mutation; `maxBudgetSeconds` bounds the CUMULATIVE wait across resumes (anchored at the PR's createdAt, so a resume does not restart the clock) and exhausting it is the genuine give-up that classifies and blocks. `updateAttempts` caps the bounded update of a behind-the-base PR. |
|
|
265
|
-
| `mergeWatch.mode` | No | `"sync"` \| `"async"` |
|
|
291
|
+
| `mergeWatch.mode` | No | `"sync"` \| `"async"` | — | Close-time merge-wait posture (Story #4698). `sync` (default) keeps the in-close foreground merge wait unchanged. `async` caps the per-invocation wait to a short ~60s probe window — long enough to catch an instant merge and, via the head-anchored required-check predicate, an instantly-red required check — then returns the resumable `pending` terminal (exit 3) with a `nextCommand`. Opt in when slow CI makes the foreground wait routinely expire: the worker launches `nextCommand` in the background instead of burning the host tool slot polling. `maxBudgetSeconds` (the cumulative give-up) is unchanged. |
|
|
266
292
|
| `mergeWatch.intervalSeconds` | No | `integer` | `30` | Seconds between merge-wait polls. Default 30. |
|
|
267
|
-
| `mergeWatch.maxWaitSeconds` | No | `integer` |
|
|
293
|
+
| `mergeWatch.maxWaitSeconds` | No | `integer` | — | Per-invocation merge-wait bound (seconds). Default 300 (5 minutes) — chosen to fit inside a single host tool invocation (~10 min ceiling) alongside the close gates that precede the wait. Expiry yields `pending` (exit 3), never a block. Headless callers with no host ceiling raise this to land in one block. |
|
|
268
294
|
| `mergeWatch.maxBudgetSeconds` | No | `integer` | `3600` | Cumulative wall-clock budget (seconds) across merge-wait resumes, anchored at the PR's createdAt. Default 3600 (60 minutes). Exhausting this classifies the block and transitions the Story to agent::blocked. |
|
|
269
|
-
| `mergeWatch.updateAttempts` | No | `integer` |
|
|
270
|
-
| `codeReview` | No | `object` | — |
|
|
271
|
-
| `codeReview.providers[]` | No | `array<object>` |
|
|
295
|
+
| `mergeWatch.updateAttempts` | No | `integer` | — | Maximum times the merge wait will bring a behind-the-base PR up to date before giving up on the branch. Default 3. Set 0 to disable the update. |
|
|
296
|
+
| `codeReview` | No | `object` | — | Review-provider chain plus bounded-retry knobs for the /deliver code-review ceremony. |
|
|
297
|
+
| `codeReview.providers[]` | No | `array<object>` | `[{"name":"native"},{"name":"security-review","scopes":["story"],"optional":true},{"name":"ultrareview","scopes":["story"],"manualPrompt":true,"when":{"label":"risk::high"}}]` | Review-provider chain (Story #2871). When unset or empty, defaults to [{ name: "native" }]. The orchestrator iterates inline entries in declaration order and merges their Finding[] before posting one structured comment; manual-prompt entries (e.g. ultrareview) contribute a trailing 'Manual review suggestions' section. Selecting an adapter whose probe fails hard-fails at factory construction unless declared `optional: true` in the chain. Each item has: name, scopes, optional, manualPrompt, when. |
|
|
272
298
|
| `codeReview.providerConfig` | No | `object` | — | Optional escape hatch for adapter-specific configuration. No documented keys in Epic #2815; reserved so future adapters can be configured without another schema migration. |
|
|
273
|
-
| `codeReview.maxFixAttempts` | No | `integer` |
|
|
274
|
-
| `codeReview.maxFixScopeFiles` | No | `integer` |
|
|
299
|
+
| `codeReview.maxFixAttempts` | No | `integer` | `3` | Maximum auto-fix retry attempts per finding in /deliver Phase 5 (code-review). 0 disables auto-fix. Default 3. |
|
|
300
|
+
| `codeReview.maxFixScopeFiles` | No | `integer` | `5` | Maximum file count a single auto-fix may modify before escalating to agent::blocked. Default 5. |
|
|
275
301
|
| `codeReview.autoFixSeverity` | No | `"high"` \| `"medium"` | `"medium"` | Severity threshold for on-branch remediation in /deliver Phase 5 (code-review). `medium` (default) routes 🔴/🟠/🟡 findings into the host-LLM focused-fix routing (Mediums batched per lens: one commit per lens, a single validation + rescan at the end) while 🟢 suggestions still graduate to follow-up issues; `high` reproduces the pre-4399 Critical/High-only routing. Hard cutover — no back-compat flag. |
|
|
276
302
|
| `review` | No | `object` | — | Close-scope review tuning (Story #4699). Governs the Story-scope local-lens pass that runs inside the close subprocess; the maker-blind code-review pass and all hard gates are unaffected. |
|
|
277
|
-
| `review.lensDiffFloor` | No | `integer` |
|
|
303
|
+
| `review.lensDiffFloor` | No | `integer` | — | Changed-line floor for the close-scope lens walk (Story #4699). A diff strictly below this many changed lines (additions + deletions) with zero sensitive-path hits skips lens materialization and records the skip in the findings-yield ledger. Default 40; 0 disables the skip. Hard gates and the maker-blind code-review pass are unaffected. |
|
|
278
304
|
| `refactorStage` | No | `object` | — | Opt-in, config-gated post-green refactor checkpoint wired into story-deliver (Story #3430, Epic #3418). Strictly additive and default-OFF: when disabled, story-deliver behaves exactly as before. Advisory only — never changes existing close-validation gate semantics. |
|
|
279
305
|
| `refactorStage.enabled` | No | `boolean` | `false` | When true, story-deliver runs an advisory post-green refactor stage (core/code-review-and-quality skill, Post-Green Refactor Pass) after the suite is green. Default false — when unset the stage is skipped and close-validation gate semantics are unchanged. |
|
|
280
306
|
| `acceptanceEval` | No | `object` | — | Story #3819. Bounded per-Story acceptance self-eval loop. After the implementation commits land and before the Story-implementation phase flips to `closing`, an independent (fresh-context) critic pass scores the caller-injected change set against each inline `acceptance[]` item, redrafts the unmet items, and re-evaluates — capped at `maxRounds` redraft rounds, then escalates to `agent::blocked` when criteria remain unmet. There is no `enabled` flag: the loop is a hard cutover (always on). |
|
|
281
|
-
| `acceptanceEval.maxRounds` | No | `integer` |
|
|
282
|
-
| `
|
|
283
|
-
| `ci` | No | `object` | — | Nested configuration block. |
|
|
284
|
-
| `ci.watch` | No | `object` | — | Story #4356 (Epic #4355). Poll-loop tuning for the merge/CI watch. pollIntervalMs is the cadence between check probes; maxPolls caps total probes before the watcher gives up; maxResumes caps how many times the watcher may resume after a transient stall; attachWindowMs bounds the wait for a required context to attach at all. |
|
|
285
|
-
| `ci.watch.pollIntervalMs` | No | `integer` | — | — |
|
|
286
|
-
| `ci.watch.maxPolls` | No | `integer` | — | — |
|
|
287
|
-
| `ci.watch.maxResumes` | No | `integer` | — | — |
|
|
288
|
-
| `ci.watch.attachWindowMs` | No | `integer` | — | Story #4890. How long (ms) the watch keeps re-resolving an EMPTY `gh pr checks --required` set before it stops waiting for a required context to attach. A ruleset attaches its contexts asynchronously and the arrival latency is set by the slowest one, so a required context that is an aggregator job gated on every other tier is the last to appear — measured at 16m52s on this repository. Default 1200000 (20 minutes). Raise it for a repository whose contexts arrive later still; exhausting the window is never reported as a red check (the watch exits 2, not-yet-started). |
|
|
289
|
-
| `ci.autoMerge` | No | `"trust-ci"` \| `"strict"` | — | Story #4356 (Epic #4355). Merge posture. 'trust-ci' (default) merges once required checks pass; 'strict' additionally requires a clean review gate. |
|
|
290
|
-
| `routing` | No | `object` | — | v2 delivery-spawn routing: role-scoped boot contexts and maker-checker sampling. The v1 singleDelivery epic-route kill-switch was removed in Stage 6. |
|
|
291
|
-
| `routing.roleScopedAgents` | No | `boolean` | — | Epic #4478 (M7-B). Kill-switch for the role-scoped boot contexts. When true (default), a converted delivery spawn (`story-worker`, `acceptance-critic`) boots on its own `.claude/agents/<role>.md` system prompt instead of re-paying the full CLAUDE.md @-import closure (≈50KB → ≈8KB per spawn — the payoff of the context diet). When false, every converted spawn falls back to `subagent_type: general-purpose` — the instant, code-rollback-free per-consumer revert, and the universal escape for hosts that ignore `.claude/agents/`. The fallback is the full-closure agent that ran before M7-B, so flipping it off never drops a gate. |
|
|
292
|
-
| `routing.freshCriticSampleRate` | No | `number` | — | Epic #4478 (M7-B, Part 2). Maker-checker sampling floor. Under the standard profile, a change set touching no sensitive path routes its acceptance clusters down the contract-identical inline critic path, but this fraction of them is still forced through a fresh-context critic so a low derived level never means zero independent checking. Clamped to [0, 1]; 0 disables the floor, 1 forces every cluster fresh. Default 0.2. Consumed by resolveCeremonyForRisk (lib/orchestration/ceremony-routing.js). |
|
|
293
|
-
| `routing.ceremonyProfile` | No | `"minimal"` \| `"standard"` \| `"strict"` | — | Acceptance-ceremony depth. minimal = always inline critic; strict = always fresh-context critic; standard (default) = routed off the change level derived from the Story diff, with the maker-checker sampling floor. |
|
|
294
|
-
| `routing.closeAndLand` | No | `boolean` | — | When true (default), single-story-close lands through merge in one close. Opt out per-run with --no-wait-merge. |
|
|
295
|
-
| `feedbackLoop` | No | `object` | — | Nested configuration block. |
|
|
307
|
+
| `acceptanceEval.maxRounds` | No | `integer` | `2` | Maximum number of redraft rounds before escalation. Default 2; clamped into [1, hard ceiling] by lib/config/acceptance-eval.js so the cap can never be disabled (maxRounds: 0 clamps up to 1). |
|
|
308
|
+
| `feedbackLoop` | No | `object` | — | Opt-out toggles for the close-time auto-file graduators. All default to auto-filing on. |
|
|
296
309
|
| `feedbackLoop.auditResultsAutoFile` | No | `boolean` | `true` | When true (default), the close-time audit-results graduator auto-files non-blocking audit-results findings as follow-up issues routed by source classification. Set to false to suppress auto-filing; findings remain accessible in the structured comments on the Story. |
|
|
297
310
|
| `feedbackLoop.retroProposals` | No | `boolean` | `true` | When true (default), the retro auto-files its actionable routed proposals as meta::<framework-gap\|consumer-improvement> + friction::<category> issues via the graduator pre-parsed-findings seam, and the rendered retro sections list the filed issue numbers instead of paste-ready gh command stanzas. Set to false to fall back to the command stanzas. |
|
|
298
|
-
| `feedbackLoop.frictionWindowDays` | No | `integer` |
|
|
299
|
-
| `auditToStories` | No | `object` | — |
|
|
300
|
-
| `auditToStories.severityFloor` | No | `"critical"` \| `"high"` \| `"medium"` \| `"low"` \| `"all"` |
|
|
301
|
-
| `auditToStories.autoComment` | No | `boolean` |
|
|
311
|
+
| `feedbackLoop.frictionWindowDays` | No | `integer` | — | How many days back the run-scope friction recurrence window reaches (Story #4850). The window spans every surviving per-Story signal stream rather than the triggering run's own Stories, so that a defect firing once per Story can reach the actionable threshold; this bounds it by age so a defect fixed weeks ago stops re-routing. Rows older than the bound — and rows carrying no readable timestamp — are excluded and counted on the roll-up step result. Default 30. |
|
|
312
|
+
| `auditToStories` | No | `object` | — | Knobs for the `/audit-to-stories` unattended (`--auto`) sweep (Story #4626). |
|
|
313
|
+
| `auditToStories.severityFloor` | No | `"critical"` \| `"high"` \| `"medium"` \| `"low"` \| `"all"` | — | Minimum severity a finding must meet to be proposed as a Story on an unattended `/audit-to-stories --auto` sweep (Story #4626). Default high. |
|
|
314
|
+
| `auditToStories.autoComment` | No | `boolean` | — | When true (default), `/audit-to-stories --auto` posts a re-detected comment on an already-open matched Issue instead of silently skipping it. |
|
|
315
|
+
| `ci` | No | `object` | — | CI-aware delivery namespace (Story #4356, Epic #4355): the merge/CI watch poll loop and the merge posture. |
|
|
316
|
+
| `ci.watch` | No | `object` | — | Story #4356 (Epic #4355). Poll-loop tuning for the merge/CI watch. pollIntervalMs is the cadence between check probes; maxPolls caps total probes before the watcher gives up; maxResumes caps how many times the watcher may resume after a transient stall; attachWindowMs bounds the wait for a required context to attach at all. |
|
|
317
|
+
| `ci.watch.pollIntervalMs` | No | `integer` | `10000` | Milliseconds between check probes. |
|
|
318
|
+
| `ci.watch.maxPolls` | No | `integer` | `180` | Total probes before the watcher gives up on one invocation. |
|
|
319
|
+
| `ci.watch.maxResumes` | No | `integer` | `3` | How many times the watcher may resume after a transient stall. 0 disables resuming. |
|
|
320
|
+
| `ci.watch.attachWindowMs` | No | `integer` | `1200000` | Story #4890. How long (ms) the watch keeps re-resolving an EMPTY `gh pr checks --required` set before it stops waiting for a required context to attach. A ruleset attaches its contexts asynchronously and the arrival latency is set by the slowest one, so a required context that is an aggregator job gated on every other tier is the last to appear — measured at 16m52s on this repository. Default 1200000 (20 minutes). Raise it for a repository whose contexts arrive later still; exhausting the window is never reported as a red check (the watch exits 2, not-yet-started). |
|
|
321
|
+
| `ci.autoMerge` | No | `"trust-ci"` \| `"strict"` | `"trust-ci"` | Story #4356 (Epic #4355). Merge posture. 'trust-ci' (default) merges once required checks pass; 'strict' additionally requires a clean review gate. |
|
|
322
|
+
| `routing` | No | `object` | — | v2 delivery-spawn routing: role-scoped boot contexts and maker-checker sampling. The v1 singleDelivery epic-route kill-switch was removed in Stage 6. |
|
|
323
|
+
| `routing.roleScopedAgents` | No | `boolean` | `true` | Epic #4478 (M7-B). Kill-switch for the role-scoped boot contexts. When true (default), a converted delivery spawn (`story-worker`, `acceptance-critic`) boots on its own `.claude/agents/<role>.md` system prompt instead of re-paying the full CLAUDE.md @-import closure. When false, every converted spawn falls back to `subagent_type: general-purpose` — the instant, code-rollback-free per-consumer revert, and the universal escape for hosts that ignore `.claude/agents/`. The fallback is the full-closure agent that ran before M7-B, so flipping it off never drops a gate. |
|
|
324
|
+
| `routing.freshCriticSampleRate` | No | `number` | `0.2` | Epic #4478 (M7-B, Part 2). Maker-checker sampling floor. Under the standard profile, a change set touching no sensitive path routes its acceptance clusters down the contract-identical inline critic path, but this fraction of them is still forced through a fresh-context critic so a low derived level never means zero independent checking. Clamped to [0, 1]; 0 disables the floor, 1 forces every cluster fresh. Consumed by resolveCeremonyForRisk (lib/orchestration/ceremony-routing.js). |
|
|
325
|
+
| `routing.ceremonyProfile` | No | `"minimal"` \| `"standard"` \| `"strict"` | `"standard"` | Acceptance-ceremony depth. minimal = always inline critic; strict = always fresh-context critic; standard (default) = routed off the change level derived from the Story diff, with the maker-checker sampling floor. |
|
|
326
|
+
| `routing.closeAndLand` | No | `boolean` | `true` | When true (default), single-story-close lands through merge in one close. Opt out per-run with --no-wait-merge. |
|
|
327
|
+
|
|
328
|
+
### `qa` (optional)
|
|
329
|
+
|
|
330
|
+
Agent-driven QA harness contract (Epic #3214; environment-keyed by Epic #4326). Optional top-level block. All filesystem-pointer fields (featureRoot, fixturesManifest, designTokens) carry safeString guards rejecting shell-injection metacharacters. environments is a map of named deployment targets (each with a baseUrl, a per-environment url-template/skill sign-in seam, and an optional allowWrites gate); personas resolve to a stored credential reference or a sign-in skill, never an inline secret.
|
|
331
|
+
|
|
332
|
+
| Key | Required | Type | Default | Description |
|
|
333
|
+
| --- | --- | --- | --- | --- |
|
|
334
|
+
| `featureRoot` | No | `string` | `"tests/features"` | Directory holding the Gherkin feature files the QA sweep drives. |
|
|
335
|
+
| `fixturesManifest` | No | `string` | `"tests/fixtures/personas.json"` | Path to the persona/fixture manifest the harness seeds from. |
|
|
336
|
+
| `environments` | No | `object<map>` | `{"local":{"baseUrl":"http://localhost:3000","signInSeam":{"urlTemplate":"/dev/sign-in-as/{persona}"}},"staging":{"baseUrl":"https://staging.example.test","signInSeam":{"skill":"stack/qa/sign-in"},"allowWrites":false}}` | Deployment targets the QA harness can run against (Epic #4326). A map keyed by environment name (e.g. `local`, `staging`), each carrying its own `baseUrl`, its own per-environment sign-in seam (the same url-template/skill union as the top-level seam), and an optional `allowWrites` gate. resolveQaEnvironment selects one environment per invocation by name or by raw-URL origin match against `baseUrl`; `allowWrites` defaults to true only for the `local` environment. Replaces the retired top-level single `signInSeam`. |
|
|
337
|
+
| `personas` | No | one of: `array`, `object` | `{"admin":{"credentialRef":"QA_ADMIN_CREDENTIAL"},"member":{"signInSkill":"stack/qa/sign-in-member"}}` | Personas the QA-harness sign-in seam accepts. Two accepted shapes: (1) a plain array of persona names — the honest shape for a `urlTemplate` dev-impersonation seam, where the persona name is the sole input the workflow consumes; (2) the object-map form keyed by persona name, where each entry carries per-persona auth material (`credentialRef` or `signInSkill`) consulted only under a skill-based or credential-based seam. |
|
|
338
|
+
| `gherkinLint` | No | `object` | `{"scopes":{"web":{"featureRoots":["apps/web/tests/features"],"stepRoots":["apps/web/tests/steps"]}},"exemptionTags":["@skip"],"stepWaivers":[]}` | Static Gherkin corpus gate (Story #5013). Optional; the gate runs only when this block is present, so an upgrade never reddens the lint of a consumer that never asked the framework to police its `.feature` files. Inside the opt-in it fails closed: an unresolvable `@cucumber/gherkin` parser, or a scope resolving zero step definitions, exits 1 rather than reporting a clean run. |
|
|
339
|
+
| `gherkinLint.scopes` | Yes | `object<map>` | — | Binding scopes, keyed by name. Each scope resolves its own features against its own step definitions only — pooling every step root into one matcher list is what makes a cross-app false bind possible, where a step defined solely in app B silently vouches for app A. The scope name appears verbatim in every unbound finding. |
|
|
340
|
+
| `gherkinLint.exemptionTags` | No | `array<string>` | `["@skip"]` | Tags marking a scenario as intentionally non-binding, so must-bind skips it. Never an escape from must-compile: a parse error in the file still fails the run. Default: ["@skip"]. |
|
|
341
|
+
| `gherkinLint.stepWaivers` | No | `array<string>` | `[]` | Exact step texts must-bind never reports as unbound. The step index is a source scan and therefore heuristic while the parser is exact, so a false unbound must always have an escape that does not require switching the gate off. Default: []. |
|
|
342
|
+
| `consoleAllowlist` | No | `array<string>` | `["Download the React DevTools","[HMR]"]` | Console-message substrings the QA run tolerates instead of reporting as a finding (framework dev-mode chatter). |
|
|
343
|
+
| `designTokens` | No | `string` | `"src/styles/tokens.css"` | Path to the design-token SSOT the UX/UI lens checks rendered styles against. |
|
|
302
344
|
|
|
303
345
|
<!-- END GENERATED:agentrc -->
|
|
304
346
|
|
|
@@ -476,7 +518,6 @@ number of keys.
|
|
|
476
518
|
|
|
477
519
|
| Key | Root dogfood | Distributed template (`agentrc-reference.json`) | Why they differ |
|
|
478
520
|
| ---------------------------------------------------- | ------------------------------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
479
|
-
| `project.commands.lintBaseline` | `npm run lint` | `npx eslint . --format json` | Root piggybacks on the repo's existing lint script; consumer template assumes a generic ESLint setup with structured output. |
|
|
480
521
|
| `delivery.quality.gates.maintainability.targetDirs` | `[".agents/scripts", "tests"]` | `["src"]` | Root scans the framework's own source tree; consumer template scans the conventional `src/`. |
|
|
481
522
|
| `delivery.quality.gates.crap.targetDirs` | `[".agents/scripts"]` | `["src"]` | Same reason as maintainability above. |
|
|
482
523
|
| `github.owner` / `.repo` / `.projectNumber` | Populated for `dsj1984/mandrel` | `[OWNER]` / `[REPO]` / `null` | Shared repo identifiers; placeholders in the template are replaced by `node .agents/scripts/bootstrap.js` (or by hand). |
|
|
@@ -510,7 +551,7 @@ the lint ratchet, and the CRAP/MI gates.
|
|
|
510
551
|
|
|
511
552
|
| File | Owner | Refresh |
|
|
512
553
|
| --------------------------------- | ------------------------------------ | ---------------------------------------------------------------------- |
|
|
513
|
-
| `baselines/lint.json` |
|
|
554
|
+
| `baselines/lint.json` | the consumer's own linter | No framework CLI — see [Lint baseline ratchet](quality-gates.md#lint-baseline-ratchet) |
|
|
514
555
|
| `baselines/crap.json` | `update-crap-baseline.js` | `npm run crap:update` |
|
|
515
556
|
| `baselines/maintainability.json` | `update-maintainability-baseline.js` | `npm run maintainability:update` |
|
|
516
557
|
| `baselines/bundle-size.json` | consumer's own build/measure step | Commit the build's measured sizes; for an intentional growth, run the check with `BUNDLE_SIZE_REFRESH=1` (see [Bundle-size ratchet](quality-gates.md#bundle-size-ratchet--one-shot-refreshacknowledge-story-151)) |
|
|
@@ -606,14 +647,20 @@ and stay committed with real values.
|
|
|
606
647
|
This is a framework-level change, not a project-level one. The path is:
|
|
607
648
|
|
|
608
649
|
1. Add the AJV schema in the relevant module under
|
|
609
|
-
`.agents/scripts/lib
|
|
610
|
-
|
|
611
|
-
|
|
650
|
+
`.agents/scripts/lib/`, carrying a `description` annotation — that string
|
|
651
|
+
is the operator-facing gloss in the shipped mirror and in the key table
|
|
652
|
+
above. Add a `default` annotation only when the key belongs in the
|
|
653
|
+
defaults inventory (see the annotation contract at the top of
|
|
654
|
+
`config-settings-schema.js`), importing the matching runtime `*_DEFAULTS`
|
|
655
|
+
constant rather than restating a literal.
|
|
656
|
+
2. Add a resolver getter in
|
|
612
657
|
[`config-resolver.js`](../scripts/lib/config-resolver.js).
|
|
658
|
+
3. Run `npm run docs:gen`. The mirror, the defaults inventory, and this
|
|
659
|
+
file's key table are regenerated — never hand-edited.
|
|
613
660
|
4. Add tests under `tests/lib/config-*.test.js` and confirm
|
|
614
|
-
`tests/config-schema-mirror-drift.test.js` passes.
|
|
615
|
-
5.
|
|
616
|
-
the
|
|
661
|
+
`tests/config-schema-mirror-drift.test.js` (generator fidelity) passes.
|
|
662
|
+
5. Update `mandrel-sync-config.md` only if the merge semantics differ from
|
|
663
|
+
the default (project-wins) rule.
|
|
617
664
|
|
|
618
665
|
---
|
|
619
666
|
|