mandrel 2.31.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/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/review-block.js +5 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +157 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +0 -14
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +59 -25
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +20 -31
- package/.agents/scripts/lib/orchestration/spec-spill.js +17 -3
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +7 -6
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +2 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +4 -1
- package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -127
- package/.agents/scripts/lib/orchestration/ticket-validator-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 +1 -11
- 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 +42 -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
|
@@ -2,338 +2,350 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://github.com/dsj1984/mandrel/blob/main/.agents/schemas/agentrc.schema.json",
|
|
4
4
|
"title": "Mandrel .agentrc",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "GENERATED — do not edit. Emitted by `node .agents/scripts/generate-config-docs.js` from the runtime AJV schema in `.agents/scripts/lib/config-settings-schema.js` (plus its `-delivery` / `-quality` / `config/gates/*` modules), which is the single source of truth for the `.agentrc.json` surface. This file exists for editor tooling and human readers; because it is a serialization of the runtime schema rather than a hand-kept mirror, the two cannot disagree. Edit the annotated schema literals and re-run `npm run docs:gen`.",
|
|
6
6
|
"type": "object",
|
|
7
|
-
"additionalProperties": false,
|
|
8
7
|
"required": ["project"],
|
|
9
8
|
"properties": {
|
|
10
9
|
"$schema": {
|
|
11
|
-
"type": "string"
|
|
12
|
-
},
|
|
13
|
-
"project": {
|
|
14
|
-
"$ref": "#/$defs/project"
|
|
15
|
-
},
|
|
16
|
-
"github": {
|
|
17
|
-
"$ref": "#/$defs/github"
|
|
18
|
-
},
|
|
19
|
-
"planning": {
|
|
20
|
-
"$ref": "#/$defs/planning"
|
|
21
|
-
},
|
|
22
|
-
"delivery": {
|
|
23
|
-
"$ref": "#/$defs/delivery"
|
|
24
|
-
},
|
|
25
|
-
"qa": {
|
|
26
|
-
"$ref": "#/$defs/qa"
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"$defs": {
|
|
30
|
-
"safeString": {
|
|
31
10
|
"type": "string",
|
|
32
|
-
"
|
|
33
|
-
"pattern": "[;&|`$<>]"
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
"nullableSafeString": {
|
|
37
|
-
"type": ["string", "null"],
|
|
38
|
-
"not": {
|
|
39
|
-
"type": "string",
|
|
40
|
-
"pattern": "[;&|`$<>]"
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
"nullableNonEmptySafeString": {
|
|
44
|
-
"type": ["string", "null"],
|
|
45
|
-
"minLength": 1,
|
|
46
|
-
"not": {
|
|
47
|
-
"type": "string",
|
|
48
|
-
"pattern": "[;&|`$<>]"
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
"listOrExtenderOfStrings": {
|
|
52
|
-
"oneOf": [
|
|
53
|
-
{
|
|
54
|
-
"type": "array",
|
|
55
|
-
"items": {
|
|
56
|
-
"type": "string"
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"type": "object",
|
|
61
|
-
"properties": {
|
|
62
|
-
"append": {
|
|
63
|
-
"type": "array",
|
|
64
|
-
"items": {
|
|
65
|
-
"type": "string"
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
"prepend": {
|
|
69
|
-
"type": "array",
|
|
70
|
-
"items": {
|
|
71
|
-
"type": "string"
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
"additionalProperties": false
|
|
76
|
-
}
|
|
77
|
-
]
|
|
78
|
-
},
|
|
79
|
-
"paths": {
|
|
80
|
-
"type": "object",
|
|
81
|
-
"required": ["agentRoot", "docsRoot", "tempRoot"],
|
|
82
|
-
"properties": {
|
|
83
|
-
"agentRoot": {
|
|
84
|
-
"allOf": [
|
|
85
|
-
{
|
|
86
|
-
"$ref": "#/$defs/safeString"
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
"minLength": 1
|
|
90
|
-
}
|
|
91
|
-
]
|
|
92
|
-
},
|
|
93
|
-
"docsRoot": {
|
|
94
|
-
"allOf": [
|
|
95
|
-
{
|
|
96
|
-
"$ref": "#/$defs/safeString"
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
"minLength": 1
|
|
100
|
-
}
|
|
101
|
-
]
|
|
102
|
-
},
|
|
103
|
-
"tempRoot": {
|
|
104
|
-
"allOf": [
|
|
105
|
-
{
|
|
106
|
-
"$ref": "#/$defs/safeString"
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"minLength": 1
|
|
110
|
-
}
|
|
111
|
-
]
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
"additionalProperties": false
|
|
115
|
-
},
|
|
116
|
-
"commands": {
|
|
117
|
-
"type": "object",
|
|
118
|
-
"properties": {
|
|
119
|
-
"lintBaseline": {
|
|
120
|
-
"allOf": [
|
|
121
|
-
{
|
|
122
|
-
"$ref": "#/$defs/safeString"
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
"minLength": 1
|
|
126
|
-
}
|
|
127
|
-
]
|
|
128
|
-
},
|
|
129
|
-
"test": {
|
|
130
|
-
"allOf": [
|
|
131
|
-
{
|
|
132
|
-
"$ref": "#/$defs/safeString"
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
"minLength": 1
|
|
136
|
-
}
|
|
137
|
-
]
|
|
138
|
-
},
|
|
139
|
-
"typecheck": {
|
|
140
|
-
"$ref": "#/$defs/nullableNonEmptySafeString"
|
|
141
|
-
},
|
|
142
|
-
"formatCheck": {
|
|
143
|
-
"allOf": [
|
|
144
|
-
{
|
|
145
|
-
"$ref": "#/$defs/safeString"
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
"minLength": 1
|
|
149
|
-
}
|
|
150
|
-
]
|
|
151
|
-
},
|
|
152
|
-
"formatWrite": {
|
|
153
|
-
"allOf": [
|
|
154
|
-
{
|
|
155
|
-
"$ref": "#/$defs/safeString"
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
"minLength": 1
|
|
159
|
-
}
|
|
160
|
-
]
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
"additionalProperties": false
|
|
11
|
+
"description": "Editor pointer at the shipped JSON-Schema mirror. Not read by the runtime."
|
|
164
12
|
},
|
|
165
13
|
"project": {
|
|
166
14
|
"type": "object",
|
|
15
|
+
"description": "Project identity, filesystem roots, planner docs context, and the commands the close-validation chain spawns.",
|
|
167
16
|
"required": ["paths"],
|
|
168
17
|
"properties": {
|
|
169
18
|
"baseBranch": {
|
|
170
|
-
"
|
|
19
|
+
"type": "string",
|
|
20
|
+
"not": {
|
|
21
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
22
|
+
},
|
|
23
|
+
"description": "Branch every `story-<id>` branch is seeded from and every Story PR targets.",
|
|
24
|
+
"default": "main"
|
|
171
25
|
},
|
|
172
26
|
"paths": {
|
|
173
|
-
"
|
|
27
|
+
"type": "object",
|
|
28
|
+
"description": "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`.",
|
|
29
|
+
"required": ["agentRoot", "docsRoot", "tempRoot"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"agentRoot": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"not": {
|
|
34
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
35
|
+
},
|
|
36
|
+
"minLength": 1,
|
|
37
|
+
"description": "Repo-relative root of the materialized framework tree (`mandrel sync` writes here).",
|
|
38
|
+
"default": ".agents"
|
|
39
|
+
},
|
|
40
|
+
"docsRoot": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"not": {
|
|
43
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
44
|
+
},
|
|
45
|
+
"minLength": 1,
|
|
46
|
+
"description": "Repo-relative root of the project documentation the planner reads for context.",
|
|
47
|
+
"default": "docs"
|
|
48
|
+
},
|
|
49
|
+
"tempRoot": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"not": {
|
|
52
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
53
|
+
},
|
|
54
|
+
"minLength": 1,
|
|
55
|
+
"description": "Repo-relative gitignored scratch root. Every temporary artifact — gate transcripts, audit reports, plan authoring dirs — lands under it.",
|
|
56
|
+
"default": "temp"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"additionalProperties": false
|
|
174
60
|
},
|
|
175
61
|
"docsContextFiles": {
|
|
176
62
|
"type": "array",
|
|
177
63
|
"items": {
|
|
178
64
|
"type": "string"
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
"commands": {
|
|
182
|
-
"$ref": "#/$defs/commands"
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
"additionalProperties": false
|
|
186
|
-
},
|
|
187
|
-
"branchProtectionCheck": {
|
|
188
|
-
"type": "object",
|
|
189
|
-
"required": ["name", "cmd"],
|
|
190
|
-
"properties": {
|
|
191
|
-
"name": {
|
|
192
|
-
"type": "string",
|
|
193
|
-
"minLength": 1
|
|
194
|
-
},
|
|
195
|
-
"cmd": {
|
|
196
|
-
"type": "array",
|
|
197
|
-
"minItems": 1,
|
|
198
|
-
"items": {
|
|
199
|
-
"type": "string",
|
|
200
|
-
"minLength": 1
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
"additionalProperties": false
|
|
205
|
-
},
|
|
206
|
-
"branchProtection": {
|
|
207
|
-
"type": "object",
|
|
208
|
-
"properties": {
|
|
209
|
-
"enforce": {
|
|
210
|
-
"type": "boolean"
|
|
211
|
-
},
|
|
212
|
-
"requiredChecks": {
|
|
213
|
-
"type": "array",
|
|
214
|
-
"items": {
|
|
215
|
-
"$ref": "#/$defs/branchProtectionCheck"
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
},
|
|
219
|
-
"additionalProperties": false
|
|
220
|
-
},
|
|
221
|
-
"mergeMethods": {
|
|
222
|
-
"type": "object",
|
|
223
|
-
"properties": {
|
|
224
|
-
"allow_squash_merge": {
|
|
225
|
-
"type": "boolean"
|
|
226
|
-
},
|
|
227
|
-
"allow_rebase_merge": {
|
|
228
|
-
"type": "boolean"
|
|
229
|
-
},
|
|
230
|
-
"allow_merge_commit": {
|
|
231
|
-
"type": "boolean"
|
|
232
|
-
},
|
|
233
|
-
"allow_auto_merge": {
|
|
234
|
-
"type": "boolean"
|
|
235
|
-
},
|
|
236
|
-
"delete_branch_on_merge": {
|
|
237
|
-
"type": "boolean"
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
"additionalProperties": false
|
|
241
|
-
},
|
|
242
|
-
"notifications": {
|
|
243
|
-
"type": "object",
|
|
244
|
-
"properties": {
|
|
245
|
-
"mentionOperator": {
|
|
246
|
-
"type": "boolean"
|
|
247
|
-
},
|
|
248
|
-
"commentEvents": {
|
|
249
|
-
"type": "array",
|
|
250
|
-
"items": {
|
|
251
|
-
"type": "string",
|
|
252
|
-
"enum": [
|
|
253
|
-
"state-transition",
|
|
254
|
-
"story-merged",
|
|
255
|
-
"story-closing",
|
|
256
|
-
"operator-message"
|
|
257
|
-
]
|
|
258
65
|
},
|
|
259
|
-
"
|
|
66
|
+
"description": "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.",
|
|
67
|
+
"default": [
|
|
68
|
+
"architecture.md",
|
|
69
|
+
"data-dictionary.md",
|
|
70
|
+
"decisions.md",
|
|
71
|
+
"patterns.md"
|
|
72
|
+
]
|
|
260
73
|
},
|
|
261
|
-
"
|
|
262
|
-
"type": "
|
|
263
|
-
"
|
|
264
|
-
|
|
265
|
-
"
|
|
266
|
-
"
|
|
267
|
-
"
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
"
|
|
271
|
-
"
|
|
272
|
-
"
|
|
273
|
-
|
|
74
|
+
"commands": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"description": "Shell commands the close-validation chain spawns. Each is run from the repo root.",
|
|
77
|
+
"properties": {
|
|
78
|
+
"test": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"not": {
|
|
81
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
82
|
+
},
|
|
83
|
+
"minLength": 1,
|
|
84
|
+
"description": "Full test-suite command run by the close-validation chain.",
|
|
85
|
+
"default": "npm test"
|
|
86
|
+
},
|
|
87
|
+
"typecheck": {
|
|
88
|
+
"type": ["string", "null"],
|
|
89
|
+
"minLength": 1,
|
|
90
|
+
"not": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
93
|
+
},
|
|
94
|
+
"description": "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.",
|
|
95
|
+
"default": null
|
|
96
|
+
},
|
|
97
|
+
"formatCheck": {
|
|
98
|
+
"type": "string",
|
|
99
|
+
"not": {
|
|
100
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
101
|
+
},
|
|
102
|
+
"minLength": 1,
|
|
103
|
+
"description": "Non-mutating format verification run as a close-validation gate.",
|
|
104
|
+
"default": "npx biome format ."
|
|
105
|
+
},
|
|
106
|
+
"formatWrite": {
|
|
107
|
+
"type": "string",
|
|
108
|
+
"not": {
|
|
109
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
110
|
+
},
|
|
111
|
+
"minLength": 1,
|
|
112
|
+
"description": "Mutating format command the close-time format-autofix step spawns.",
|
|
113
|
+
"default": "npx biome format --write ."
|
|
114
|
+
}
|
|
274
115
|
},
|
|
275
|
-
"
|
|
116
|
+
"additionalProperties": false
|
|
276
117
|
}
|
|
277
118
|
},
|
|
278
119
|
"additionalProperties": false
|
|
279
120
|
},
|
|
280
121
|
"github": {
|
|
281
122
|
"type": "object",
|
|
123
|
+
"description": "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.",
|
|
282
124
|
"required": ["owner", "repo", "operatorHandle"],
|
|
283
125
|
"properties": {
|
|
284
126
|
"owner": {
|
|
285
127
|
"type": "string",
|
|
286
|
-
"minLength": 1
|
|
128
|
+
"minLength": 1,
|
|
129
|
+
"description": "GitHub owner (user or org) that hosts the repository.",
|
|
130
|
+
"default": "[OWNER]"
|
|
287
131
|
},
|
|
288
132
|
"repo": {
|
|
289
133
|
"type": "string",
|
|
290
|
-
"minLength": 1
|
|
134
|
+
"minLength": 1,
|
|
135
|
+
"description": "Repository name under `owner`.",
|
|
136
|
+
"default": "[REPO]"
|
|
291
137
|
},
|
|
292
138
|
"projectNumber": {
|
|
293
139
|
"type": ["integer", "null"],
|
|
294
|
-
"minimum": 1
|
|
140
|
+
"minimum": 1,
|
|
141
|
+
"description": "Projects V2 board number the orchestrator syncs Story status onto. `null` disables board sync.",
|
|
142
|
+
"default": null
|
|
295
143
|
},
|
|
296
144
|
"projectOwner": {
|
|
297
145
|
"type": ["string", "null"],
|
|
298
|
-
"minLength": 1
|
|
146
|
+
"minLength": 1,
|
|
147
|
+
"description": "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`.",
|
|
148
|
+
"default": null
|
|
299
149
|
},
|
|
300
150
|
"operatorHandle": {
|
|
301
151
|
"type": "string",
|
|
302
|
-
"pattern": "^@.+"
|
|
152
|
+
"pattern": "^@.+",
|
|
153
|
+
"description": "The human the framework escalates to, `@`-prefixed. Used for HITL @-mentions on `agent::blocked`.",
|
|
154
|
+
"default": "@[USERNAME]"
|
|
303
155
|
},
|
|
304
156
|
"defaultTimeoutMs": {
|
|
305
157
|
"type": "integer",
|
|
306
158
|
"minimum": 1000,
|
|
307
|
-
"description": "Default `timeoutMs`
|
|
159
|
+
"description": "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.",
|
|
160
|
+
"default": 60000
|
|
308
161
|
},
|
|
309
162
|
"branchProtection": {
|
|
310
|
-
"
|
|
163
|
+
"type": "object",
|
|
164
|
+
"description": "Branch-protection stance applied to `project.baseBranch` by the GitHub bootstrap, and reproduced locally before every push.",
|
|
165
|
+
"properties": {
|
|
166
|
+
"enforce": {
|
|
167
|
+
"type": "boolean",
|
|
168
|
+
"description": "When true, the GitHub bootstrap writes the required-check ruleset. False leaves the remote stance alone.",
|
|
169
|
+
"default": true
|
|
170
|
+
},
|
|
171
|
+
"requiredChecks": {
|
|
172
|
+
"type": "array",
|
|
173
|
+
"items": {
|
|
174
|
+
"type": "object",
|
|
175
|
+
"description": "One required status check: the context name GitHub gates the merge on, plus the argv the framework runs locally to reproduce it.",
|
|
176
|
+
"required": ["name", "cmd"],
|
|
177
|
+
"properties": {
|
|
178
|
+
"name": {
|
|
179
|
+
"type": "string",
|
|
180
|
+
"minLength": 1,
|
|
181
|
+
"description": "Required status-check context name as GitHub reports it."
|
|
182
|
+
},
|
|
183
|
+
"cmd": {
|
|
184
|
+
"type": "array",
|
|
185
|
+
"minItems": 1,
|
|
186
|
+
"items": {
|
|
187
|
+
"type": "string",
|
|
188
|
+
"minLength": 1
|
|
189
|
+
},
|
|
190
|
+
"description": "argv array (never a shell string) the local pre-push validation runs to reproduce the check."
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
"additionalProperties": false
|
|
194
|
+
},
|
|
195
|
+
"description": "Checks that must pass before a Story PR merges. Each entry carries both the remote context name and the local argv.",
|
|
196
|
+
"default": [
|
|
197
|
+
{
|
|
198
|
+
"name": "lint",
|
|
199
|
+
"cmd": ["npm", "run", "lint"]
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"name": "test",
|
|
203
|
+
"cmd": ["npm", "test"]
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"name": "baselines",
|
|
207
|
+
"cmd": ["node", ".agents/scripts/check-baselines.js"]
|
|
208
|
+
}
|
|
209
|
+
]
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
"additionalProperties": false
|
|
311
213
|
},
|
|
312
214
|
"mergeMethods": {
|
|
313
|
-
"
|
|
215
|
+
"type": "object",
|
|
216
|
+
"description": "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.",
|
|
217
|
+
"properties": {
|
|
218
|
+
"allow_squash_merge": {
|
|
219
|
+
"type": "boolean",
|
|
220
|
+
"default": true
|
|
221
|
+
},
|
|
222
|
+
"allow_rebase_merge": {
|
|
223
|
+
"type": "boolean",
|
|
224
|
+
"default": false
|
|
225
|
+
},
|
|
226
|
+
"allow_merge_commit": {
|
|
227
|
+
"type": "boolean",
|
|
228
|
+
"default": false
|
|
229
|
+
},
|
|
230
|
+
"allow_auto_merge": {
|
|
231
|
+
"type": "boolean",
|
|
232
|
+
"default": true
|
|
233
|
+
},
|
|
234
|
+
"delete_branch_on_merge": {
|
|
235
|
+
"type": "boolean",
|
|
236
|
+
"default": true
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
"additionalProperties": false
|
|
314
240
|
},
|
|
315
241
|
"notifications": {
|
|
316
|
-
"
|
|
242
|
+
"type": "object",
|
|
243
|
+
"description": "Allowlist-gated notification channels. An event fires on a channel only when it is named in that channel's array.",
|
|
244
|
+
"properties": {
|
|
245
|
+
"mentionOperator": {
|
|
246
|
+
"type": "boolean",
|
|
247
|
+
"description": "When true, `github.operatorHandle` is @-mentioned in the comments the notifier posts.",
|
|
248
|
+
"default": false
|
|
249
|
+
},
|
|
250
|
+
"commentEvents": {
|
|
251
|
+
"type": "array",
|
|
252
|
+
"items": {
|
|
253
|
+
"type": "string",
|
|
254
|
+
"enum": [
|
|
255
|
+
"state-transition",
|
|
256
|
+
"story-merged",
|
|
257
|
+
"story-closing",
|
|
258
|
+
"operator-message"
|
|
259
|
+
]
|
|
260
|
+
},
|
|
261
|
+
"uniqueItems": true,
|
|
262
|
+
"description": "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.",
|
|
263
|
+
"default": [
|
|
264
|
+
"state-transition",
|
|
265
|
+
"story-merged",
|
|
266
|
+
"operator-message"
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
"webhookEvents": {
|
|
270
|
+
"type": "array",
|
|
271
|
+
"items": {
|
|
272
|
+
"type": "string",
|
|
273
|
+
"enum": [
|
|
274
|
+
"state-transition",
|
|
275
|
+
"story-merged",
|
|
276
|
+
"story-closing",
|
|
277
|
+
"operator-message",
|
|
278
|
+
"merge.unlanded",
|
|
279
|
+
"merge.flip-failed"
|
|
280
|
+
]
|
|
281
|
+
},
|
|
282
|
+
"uniqueItems": true,
|
|
283
|
+
"description": "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.",
|
|
284
|
+
"default": [
|
|
285
|
+
"state-transition",
|
|
286
|
+
"story-merged",
|
|
287
|
+
"story-closing",
|
|
288
|
+
"operator-message",
|
|
289
|
+
"merge.unlanded",
|
|
290
|
+
"merge.flip-failed"
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
"additionalProperties": false
|
|
317
295
|
}
|
|
318
296
|
},
|
|
319
297
|
"additionalProperties": false
|
|
320
298
|
},
|
|
321
299
|
"planning": {
|
|
322
300
|
"type": "object",
|
|
301
|
+
"description": "Inputs to `/plan`: risk escalation heuristics, ceremony-lite routing, and the cross-Story conflict-finding severity gates.",
|
|
323
302
|
"properties": {
|
|
324
303
|
"riskHeuristics": {
|
|
325
|
-
"
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
"enabled": {
|
|
332
|
-
"type": "boolean",
|
|
333
|
-
"description": "Master switch. When false, lite routing is disabled everywhere: persist refuses lite claims and dispatch always takes the sub-agent path. Default true."
|
|
304
|
+
"oneOf": [
|
|
305
|
+
{
|
|
306
|
+
"type": "array",
|
|
307
|
+
"items": {
|
|
308
|
+
"type": "string"
|
|
309
|
+
}
|
|
334
310
|
},
|
|
335
|
-
|
|
336
|
-
"type": "
|
|
311
|
+
{
|
|
312
|
+
"type": "object",
|
|
313
|
+
"properties": {
|
|
314
|
+
"append": {
|
|
315
|
+
"type": "array",
|
|
316
|
+
"items": {
|
|
317
|
+
"type": "string"
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
"prepend": {
|
|
321
|
+
"type": "array",
|
|
322
|
+
"items": {
|
|
323
|
+
"type": "string"
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
"additionalProperties": false
|
|
328
|
+
}
|
|
329
|
+
],
|
|
330
|
+
"description": "Prose heuristics the planner escalates a Story against. A plain array replaces the framework list; the `{ append, prepend }` extender form deep-merges with it.",
|
|
331
|
+
"default": [
|
|
332
|
+
"Destructive or irreversible data mutations (dropping tables, deleting rows without soft-delete or backup, truncating production state).",
|
|
333
|
+
"Modifications to shared security or auth infrastructure (IAM policies, auth middleware, session or token handling, secret rotation).",
|
|
334
|
+
"Changes to CI/CD, deployment pipelines, or release gating that could disable safety checks or ship unverified code to production.",
|
|
335
|
+
"Monorepo-wide AST or text replacements touching overlapping files in parallel (catastrophic merge-conflict risk across concurrent agents).",
|
|
336
|
+
"Schema migrations that rewrite existing rows or drop columns without a backfill or rollback plan."
|
|
337
|
+
]
|
|
338
|
+
},
|
|
339
|
+
"complexityGate": {
|
|
340
|
+
"type": "object",
|
|
341
|
+
"description": "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.",
|
|
342
|
+
"properties": {
|
|
343
|
+
"enabled": {
|
|
344
|
+
"type": "boolean",
|
|
345
|
+
"description": "Master switch. When false, lite routing is disabled everywhere: persist refuses lite claims and dispatch always takes the sub-agent path. Default true."
|
|
346
|
+
},
|
|
347
|
+
"maxArtifacts": {
|
|
348
|
+
"type": "integer",
|
|
337
349
|
"minimum": 0,
|
|
338
350
|
"description": "Enumerated-artifact threshold reported by the plan-context complexity signals. An input signal for the planner verdict — carries no routing authority. Default 1."
|
|
339
351
|
}
|
|
@@ -342,28 +354,63 @@
|
|
|
342
354
|
},
|
|
343
355
|
"failOnSharedEditors": {
|
|
344
356
|
"type": "boolean",
|
|
345
|
-
"description": "When true, upgrade shared-editor conflict findings to hard errors (default false — advisory soft findings only)."
|
|
357
|
+
"description": "When true, upgrade shared-editor conflict findings to hard errors (default false — advisory soft findings only).",
|
|
358
|
+
"default": false
|
|
346
359
|
},
|
|
347
360
|
"requireExplicitCrossStoryDeps": {
|
|
348
361
|
"type": "boolean",
|
|
349
|
-
"description": "When true, upgrade implicit cross-Story dependency findings to hard errors (default false — advisory soft findings only)."
|
|
362
|
+
"description": "When true, upgrade implicit cross-Story dependency findings to hard errors (default false — advisory soft findings only).",
|
|
363
|
+
"default": false
|
|
364
|
+
},
|
|
365
|
+
"crossCuttingRegistries": {
|
|
366
|
+
"oneOf": [
|
|
367
|
+
{
|
|
368
|
+
"type": "array",
|
|
369
|
+
"items": {
|
|
370
|
+
"type": "string"
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
"type": "object",
|
|
375
|
+
"properties": {
|
|
376
|
+
"append": {
|
|
377
|
+
"type": "array",
|
|
378
|
+
"items": {
|
|
379
|
+
"type": "string"
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
"prepend": {
|
|
383
|
+
"type": "array",
|
|
384
|
+
"items": {
|
|
385
|
+
"type": "string"
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
"additionalProperties": false
|
|
390
|
+
}
|
|
391
|
+
],
|
|
392
|
+
"description": "Registry path patterns whose concurrent edits across Stories are flagged as conflicts. Defaults to the framework listener/handler index patterns when omitted.",
|
|
393
|
+
"default": [
|
|
394
|
+
"lib/orchestration/lifecycle/listeners/index.js",
|
|
395
|
+
"**/listeners/index.js",
|
|
396
|
+
"**/handlers/index.js"
|
|
397
|
+
]
|
|
350
398
|
},
|
|
351
399
|
"failOnRegistryConflicts": {
|
|
352
400
|
"type": "boolean",
|
|
353
|
-
"description": "When true, upgrade cross-cutting registry conflict findings to hard errors (default false)."
|
|
401
|
+
"description": "When true, upgrade cross-cutting registry conflict findings to hard errors (default false).",
|
|
402
|
+
"default": false
|
|
354
403
|
},
|
|
355
404
|
"failOnLargeFanOut": {
|
|
356
405
|
"type": "boolean",
|
|
357
|
-
"description": "When true, upgrade fan-out-warning findings (delete blast radius) to hard errors (default false — soft advisory)."
|
|
406
|
+
"description": "When true, upgrade fan-out-warning findings (delete blast radius) to hard errors (default false — soft advisory).",
|
|
407
|
+
"default": false
|
|
358
408
|
},
|
|
359
409
|
"largeFanOutThreshold": {
|
|
360
410
|
"type": "integer",
|
|
361
411
|
"minimum": 0,
|
|
362
|
-
"description": "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."
|
|
363
|
-
|
|
364
|
-
"crossCuttingRegistries": {
|
|
365
|
-
"$ref": "#/$defs/listOrExtenderOfStrings",
|
|
366
|
-
"description": "Registry path patterns whose concurrent edits across Stories are flagged as conflicts. Defaults to the framework listener/handler index patterns when omitted."
|
|
412
|
+
"description": "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.",
|
|
413
|
+
"default": 10
|
|
367
414
|
},
|
|
368
415
|
"navigation": {
|
|
369
416
|
"type": "object",
|
|
@@ -371,17 +418,19 @@
|
|
|
371
418
|
"properties": {
|
|
372
419
|
"routeGlobs": {
|
|
373
420
|
"type": "array",
|
|
374
|
-
"description": "Glob patterns (e.g. pages/**, app/**/route.ts) marking paths that add a user-facing route.",
|
|
375
421
|
"items": {
|
|
376
422
|
"type": "string"
|
|
377
|
-
}
|
|
423
|
+
},
|
|
424
|
+
"description": "Glob patterns (e.g. pages/**, app/**/route.ts) marking paths that add a user-facing route.",
|
|
425
|
+
"default": []
|
|
378
426
|
},
|
|
379
427
|
"navRegistry": {
|
|
380
428
|
"type": "array",
|
|
381
|
-
"description": "Tokens identifying the nav-registry SSOT a route-adding Story is expected to reference.",
|
|
382
429
|
"items": {
|
|
383
430
|
"type": "string"
|
|
384
|
-
}
|
|
431
|
+
},
|
|
432
|
+
"description": "Tokens identifying the nav-registry SSOT a route-adding Story is expected to reference.",
|
|
433
|
+
"default": []
|
|
385
434
|
}
|
|
386
435
|
},
|
|
387
436
|
"additionalProperties": false
|
|
@@ -389,995 +438,1438 @@
|
|
|
389
438
|
},
|
|
390
439
|
"additionalProperties": false
|
|
391
440
|
},
|
|
392
|
-
"
|
|
393
|
-
"type": "object",
|
|
394
|
-
"properties": {
|
|
395
|
-
"timeoutMs": {
|
|
396
|
-
"type": "integer",
|
|
397
|
-
"minimum": 1
|
|
398
|
-
}
|
|
399
|
-
},
|
|
400
|
-
"additionalProperties": false
|
|
401
|
-
},
|
|
402
|
-
"lease": {
|
|
403
|
-
"type": "object",
|
|
404
|
-
"description": "Story #3480 (Epic #3457). Assignee-as-lease primitive. ttlMs is the staleness window — a ticket claim whose owner's last heartbeat is older than this many milliseconds is reclaimable by another operator. Defaults to 900000 (15 min) in lib/config/limits.js. Note the shipped guards fail closed (there is no live heartbeat source), so a stranded claim is cleared with --steal rather than by TTL expiry.",
|
|
405
|
-
"properties": {
|
|
406
|
-
"ttlMs": {
|
|
407
|
-
"type": "integer",
|
|
408
|
-
"minimum": 1
|
|
409
|
-
}
|
|
410
|
-
},
|
|
411
|
-
"additionalProperties": false
|
|
412
|
-
},
|
|
413
|
-
"tempRetention": {
|
|
441
|
+
"delivery": {
|
|
414
442
|
"type": "object",
|
|
415
|
-
"description": "
|
|
443
|
+
"description": "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.",
|
|
416
444
|
"properties": {
|
|
417
|
-
"
|
|
418
|
-
"type": "boolean",
|
|
419
|
-
"description": "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."
|
|
420
|
-
},
|
|
421
|
-
"staleDays": {
|
|
422
|
-
"type": "integer",
|
|
423
|
-
"minimum": 1,
|
|
424
|
-
"description": "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."
|
|
425
|
-
},
|
|
426
|
-
"classes": {
|
|
445
|
+
"execution": {
|
|
427
446
|
"type": "object",
|
|
428
|
-
"description": "
|
|
447
|
+
"description": "Wall-clock bounds on the subprocesses delivery spawns.",
|
|
429
448
|
"properties": {
|
|
430
|
-
"
|
|
431
|
-
"type": "
|
|
432
|
-
"
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
"type": "boolean",
|
|
436
|
-
"description": "Per-Story validation-evidence.json, lifecycle.ndjson, and manifest.md under the standalone and per-run story trees."
|
|
437
|
-
},
|
|
438
|
-
"auditResults": {
|
|
439
|
-
"type": "boolean",
|
|
440
|
-
"description": "<tempRoot>/audits/ — audit lens reports."
|
|
441
|
-
},
|
|
442
|
-
"planDirs": {
|
|
443
|
-
"type": "boolean",
|
|
444
|
-
"description": "<tempRoot>/plan-<slug>/ — abandoned plan authoring dirs. Age-floored only; the current run is always excluded."
|
|
449
|
+
"timeoutMs": {
|
|
450
|
+
"type": "integer",
|
|
451
|
+
"minimum": 1,
|
|
452
|
+
"description": "Per-command timeout (ms) for the long-running spawns delivery drives — the close-validation chain and the gate CLIs.",
|
|
453
|
+
"default": 600000
|
|
445
454
|
}
|
|
446
455
|
},
|
|
447
456
|
"additionalProperties": false
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
"type": "array",
|
|
457
|
-
"items": {
|
|
458
|
-
"allOf": [
|
|
459
|
-
{
|
|
460
|
-
"$ref": "#/$defs/safeString"
|
|
461
|
-
},
|
|
462
|
-
{
|
|
463
|
-
"minLength": 1
|
|
464
|
-
}
|
|
465
|
-
]
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
},
|
|
469
|
-
"additionalProperties": false
|
|
470
|
-
},
|
|
471
|
-
"deliverRunner": {
|
|
472
|
-
"type": "object",
|
|
473
|
-
"properties": {
|
|
474
|
-
"concurrencyCap": {
|
|
475
|
-
"type": "integer",
|
|
476
|
-
"minimum": 1,
|
|
477
|
-
"description": "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."
|
|
478
|
-
}
|
|
479
|
-
},
|
|
480
|
-
"additionalProperties": false
|
|
481
|
-
},
|
|
482
|
-
"codeReview": {
|
|
483
|
-
"type": "object",
|
|
484
|
-
"properties": {
|
|
485
|
-
"providers": {
|
|
486
|
-
"type": "array",
|
|
487
|
-
"description": "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.",
|
|
488
|
-
"items": {
|
|
489
|
-
"type": "object",
|
|
490
|
-
"required": ["name"],
|
|
491
|
-
"properties": {
|
|
492
|
-
"name": {
|
|
457
|
+
},
|
|
458
|
+
"docsFreshness": {
|
|
459
|
+
"type": "object",
|
|
460
|
+
"description": "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.",
|
|
461
|
+
"properties": {
|
|
462
|
+
"paths": {
|
|
463
|
+
"type": "array",
|
|
464
|
+
"items": {
|
|
493
465
|
"type": "string",
|
|
494
|
-
"
|
|
495
|
-
|
|
496
|
-
},
|
|
497
|
-
"scopes": {
|
|
498
|
-
"type": "array",
|
|
499
|
-
"items": {
|
|
500
|
-
"type": "string",
|
|
501
|
-
"enum": ["story", "epic"]
|
|
466
|
+
"not": {
|
|
467
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
502
468
|
},
|
|
503
|
-
"
|
|
504
|
-
},
|
|
505
|
-
"optional": {
|
|
506
|
-
"type": "boolean",
|
|
507
|
-
"default": false,
|
|
508
|
-
"description": "When true, construction failure (host missing CLI/plugin) is logged and the entry is skipped instead of hard-failing the chain. Use for cross-runtime portability."
|
|
509
|
-
},
|
|
510
|
-
"manualPrompt": {
|
|
511
|
-
"type": "boolean",
|
|
512
|
-
"default": false,
|
|
513
|
-
"description": "When true, the entry is loaded from the manual-prompt registry: contributes a one-line operator suggestion via `renderPrompt()` instead of running a review via `runReview()`. Does not affect severity counts or `halted`."
|
|
469
|
+
"minLength": 1
|
|
514
470
|
},
|
|
515
|
-
"
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
"properties": {
|
|
519
|
-
"label": {
|
|
520
|
-
"type": "string",
|
|
521
|
-
"minLength": 1,
|
|
522
|
-
"description": "Single label that MUST be present on the ticket."
|
|
523
|
-
},
|
|
524
|
-
"labelAny": {
|
|
525
|
-
"type": "array",
|
|
526
|
-
"items": {
|
|
527
|
-
"type": "string",
|
|
528
|
-
"minLength": 1
|
|
529
|
-
},
|
|
530
|
-
"minItems": 1,
|
|
531
|
-
"description": "Run when ANY of the listed labels is present."
|
|
532
|
-
}
|
|
533
|
-
},
|
|
534
|
-
"additionalProperties": false
|
|
535
|
-
}
|
|
536
|
-
},
|
|
537
|
-
"additionalProperties": false
|
|
538
|
-
}
|
|
539
|
-
},
|
|
540
|
-
"providerConfig": {
|
|
541
|
-
"type": "object",
|
|
542
|
-
"additionalProperties": true,
|
|
543
|
-
"description": "Optional escape hatch for adapter-specific configuration. No documented keys in Epic #2815; reserved so future adapters can be configured without another schema migration."
|
|
544
|
-
},
|
|
545
|
-
"maxFixAttempts": {
|
|
546
|
-
"type": "integer",
|
|
547
|
-
"minimum": 0,
|
|
548
|
-
"description": "Maximum auto-fix retry attempts per finding in /deliver Phase 5 (code-review). 0 disables auto-fix. Default 3."
|
|
549
|
-
},
|
|
550
|
-
"maxFixScopeFiles": {
|
|
551
|
-
"type": "integer",
|
|
552
|
-
"minimum": 1,
|
|
553
|
-
"description": "Maximum file count a single auto-fix may modify before escalating to agent::blocked. Default 5."
|
|
554
|
-
},
|
|
555
|
-
"autoFixSeverity": {
|
|
556
|
-
"type": "string",
|
|
557
|
-
"enum": ["high", "medium"],
|
|
558
|
-
"default": "medium",
|
|
559
|
-
"description": "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."
|
|
560
|
-
}
|
|
561
|
-
},
|
|
562
|
-
"additionalProperties": false
|
|
563
|
-
},
|
|
564
|
-
"worktreeIsolation": {
|
|
565
|
-
"type": "object",
|
|
566
|
-
"properties": {
|
|
567
|
-
"enabled": {
|
|
568
|
-
"type": "boolean"
|
|
569
|
-
},
|
|
570
|
-
"root": {
|
|
571
|
-
"type": "string",
|
|
572
|
-
"minLength": 1
|
|
573
|
-
},
|
|
574
|
-
"nodeModulesStrategy": {
|
|
575
|
-
"type": "string",
|
|
576
|
-
"enum": ["per-worktree", "clone", "symlink", "pnpm-store"]
|
|
577
|
-
},
|
|
578
|
-
"primeFromPath": {
|
|
579
|
-
"type": ["string", "null"],
|
|
580
|
-
"minLength": 1
|
|
581
|
-
},
|
|
582
|
-
"allowSymlinkOnWindows": {
|
|
583
|
-
"type": "boolean"
|
|
584
|
-
},
|
|
585
|
-
"reapOnSuccess": {
|
|
586
|
-
"type": "boolean"
|
|
587
|
-
},
|
|
588
|
-
"bootstrapFiles": {
|
|
589
|
-
"type": "array",
|
|
590
|
-
"items": {
|
|
591
|
-
"type": "string",
|
|
592
|
-
"minLength": 1
|
|
593
|
-
},
|
|
594
|
-
"default": [
|
|
595
|
-
".env",
|
|
596
|
-
".mcp.json",
|
|
597
|
-
".agentrc.local.json",
|
|
598
|
-
".agents/instructions.local.md"
|
|
599
|
-
]
|
|
600
|
-
}
|
|
601
|
-
},
|
|
602
|
-
"additionalProperties": false,
|
|
603
|
-
"allOf": [
|
|
604
|
-
{
|
|
605
|
-
"if": {
|
|
606
|
-
"properties": {
|
|
607
|
-
"enabled": {
|
|
608
|
-
"const": true
|
|
609
|
-
}
|
|
610
|
-
},
|
|
611
|
-
"required": ["enabled"]
|
|
471
|
+
"description": "Repo-relative documentation paths the audit-documentation lens adds to its target set.",
|
|
472
|
+
"default": ["README.md"]
|
|
473
|
+
}
|
|
612
474
|
},
|
|
613
|
-
"
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
}
|
|
617
|
-
]
|
|
618
|
-
},
|
|
619
|
-
"signals": {
|
|
620
|
-
"type": "object",
|
|
621
|
-
"properties": {
|
|
622
|
-
"rework": {
|
|
475
|
+
"additionalProperties": false
|
|
476
|
+
},
|
|
477
|
+
"tempRetention": {
|
|
623
478
|
"type": "object",
|
|
479
|
+
"description": "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.",
|
|
624
480
|
"properties": {
|
|
625
|
-
"
|
|
481
|
+
"enabled": {
|
|
482
|
+
"type": "boolean",
|
|
483
|
+
"description": "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.",
|
|
484
|
+
"default": true
|
|
485
|
+
},
|
|
486
|
+
"staleDays": {
|
|
626
487
|
"type": "integer",
|
|
627
|
-
"minimum": 1
|
|
488
|
+
"minimum": 1,
|
|
489
|
+
"description": "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.",
|
|
490
|
+
"default": 7
|
|
491
|
+
},
|
|
492
|
+
"classes": {
|
|
493
|
+
"type": "object",
|
|
494
|
+
"description": "Per-class opt-out. Each defaults to true; set one false to keep that family while the rest are purged.",
|
|
495
|
+
"properties": {
|
|
496
|
+
"orchestrationLogs": {
|
|
497
|
+
"type": "boolean",
|
|
498
|
+
"description": "<tempRoot>/orchestration/*.log — close gate transcripts and terse-result detail dumps.",
|
|
499
|
+
"default": true
|
|
500
|
+
},
|
|
501
|
+
"validationEvidence": {
|
|
502
|
+
"type": "boolean",
|
|
503
|
+
"description": "Per-Story validation-evidence.json, lifecycle.ndjson, and manifest.md under the standalone and per-run story trees.",
|
|
504
|
+
"default": true
|
|
505
|
+
},
|
|
506
|
+
"auditResults": {
|
|
507
|
+
"type": "boolean",
|
|
508
|
+
"description": "<tempRoot>/audits/ — audit lens reports.",
|
|
509
|
+
"default": true
|
|
510
|
+
},
|
|
511
|
+
"planDirs": {
|
|
512
|
+
"type": "boolean",
|
|
513
|
+
"description": "<tempRoot>/plan-<slug>/ — abandoned plan authoring dirs. Age-floored only; the current run is always excluded.",
|
|
514
|
+
"default": true
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
"additionalProperties": false
|
|
628
518
|
}
|
|
629
519
|
},
|
|
630
520
|
"additionalProperties": false
|
|
631
521
|
},
|
|
632
|
-
"
|
|
522
|
+
"deliverRunner": {
|
|
633
523
|
"type": "object",
|
|
524
|
+
"description": "Bounded-concurrency knob for the /deliver fan-out.",
|
|
634
525
|
"properties": {
|
|
635
|
-
"
|
|
526
|
+
"concurrencyCap": {
|
|
636
527
|
"type": "integer",
|
|
637
|
-
"minimum": 1
|
|
528
|
+
"minimum": 1,
|
|
529
|
+
"description": "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.",
|
|
530
|
+
"default": 3
|
|
638
531
|
}
|
|
639
532
|
},
|
|
640
533
|
"additionalProperties": false
|
|
641
|
-
}
|
|
642
|
-
},
|
|
643
|
-
"additionalProperties": false
|
|
644
|
-
},
|
|
645
|
-
"tolerance": {
|
|
646
|
-
"type": "object",
|
|
647
|
-
"required": ["kind", "value"],
|
|
648
|
-
"properties": {
|
|
649
|
-
"kind": {
|
|
650
|
-
"type": "string",
|
|
651
|
-
"enum": ["absolute", "percent"]
|
|
652
|
-
},
|
|
653
|
-
"value": {
|
|
654
|
-
"type": "number",
|
|
655
|
-
"minimum": 0
|
|
656
|
-
}
|
|
657
|
-
},
|
|
658
|
-
"additionalProperties": false
|
|
659
|
-
},
|
|
660
|
-
"floors": {
|
|
661
|
-
"type": "object",
|
|
662
|
-
"additionalProperties": {
|
|
663
|
-
"type": "object",
|
|
664
|
-
"additionalProperties": {
|
|
665
|
-
"type": "number"
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
},
|
|
669
|
-
"components": {
|
|
670
|
-
"type": "object",
|
|
671
|
-
"additionalProperties": {
|
|
672
|
-
"type": "array",
|
|
673
|
-
"items": {
|
|
674
|
-
"type": "string",
|
|
675
|
-
"minLength": 1
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
},
|
|
679
|
-
"lintGate": {
|
|
680
|
-
"type": "object",
|
|
681
|
-
"properties": {
|
|
682
|
-
"enabled": {
|
|
683
|
-
"type": "boolean"
|
|
684
534
|
},
|
|
685
|
-
"
|
|
686
|
-
"
|
|
687
|
-
|
|
688
|
-
|
|
535
|
+
"worktreeIsolation": {
|
|
536
|
+
"type": "object",
|
|
537
|
+
"description": "Per-Story git worktree provisioning. Each Story is implemented in its own checkout so concurrent siblings never share a working tree.",
|
|
538
|
+
"properties": {
|
|
539
|
+
"enabled": {
|
|
540
|
+
"type": "boolean",
|
|
541
|
+
"description": "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.",
|
|
542
|
+
"default": true
|
|
543
|
+
},
|
|
544
|
+
"root": {
|
|
545
|
+
"type": "string",
|
|
546
|
+
"minLength": 1,
|
|
547
|
+
"description": "Repo-relative directory the per-Story worktrees are created under. Required whenever `enabled` is explicitly true.",
|
|
548
|
+
"default": ".worktrees"
|
|
549
|
+
},
|
|
550
|
+
"nodeModulesStrategy": {
|
|
551
|
+
"type": "string",
|
|
552
|
+
"enum": ["per-worktree", "clone", "symlink", "pnpm-store"],
|
|
553
|
+
"description": "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.",
|
|
554
|
+
"default": "clone"
|
|
555
|
+
},
|
|
556
|
+
"primeFromPath": {
|
|
557
|
+
"type": ["string", "null"],
|
|
558
|
+
"minLength": 1,
|
|
559
|
+
"description": "Absolute path to an existing `node_modules` tree to prime new worktrees from, instead of the main checkout. `null` uses the main checkout.",
|
|
560
|
+
"default": null
|
|
561
|
+
},
|
|
562
|
+
"allowSymlinkOnWindows": {
|
|
563
|
+
"type": "boolean",
|
|
564
|
+
"description": "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.",
|
|
565
|
+
"default": false
|
|
566
|
+
},
|
|
567
|
+
"reapOnSuccess": {
|
|
568
|
+
"type": "boolean",
|
|
569
|
+
"description": "Remove the Story's worktree once its PR merges. False keeps it for post-mortem inspection.",
|
|
570
|
+
"default": true
|
|
689
571
|
},
|
|
572
|
+
"bootstrapFiles": {
|
|
573
|
+
"type": "array",
|
|
574
|
+
"items": {
|
|
575
|
+
"type": "string",
|
|
576
|
+
"minLength": 1
|
|
577
|
+
},
|
|
578
|
+
"description": "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.",
|
|
579
|
+
"default": [
|
|
580
|
+
".env",
|
|
581
|
+
".mcp.json",
|
|
582
|
+
".agentrc.local.json",
|
|
583
|
+
".agents/instructions.local.md"
|
|
584
|
+
]
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
"additionalProperties": false,
|
|
588
|
+
"allOf": [
|
|
690
589
|
{
|
|
691
|
-
"
|
|
590
|
+
"if": {
|
|
591
|
+
"properties": {
|
|
592
|
+
"enabled": {
|
|
593
|
+
"const": true
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
"required": ["enabled"]
|
|
597
|
+
},
|
|
598
|
+
"then": {
|
|
599
|
+
"required": ["root"]
|
|
600
|
+
}
|
|
692
601
|
}
|
|
693
602
|
]
|
|
694
603
|
},
|
|
695
|
-
"
|
|
696
|
-
"$ref": "#/$defs/tolerance"
|
|
697
|
-
},
|
|
698
|
-
"floors": {
|
|
699
|
-
"$ref": "#/$defs/floors"
|
|
700
|
-
},
|
|
701
|
-
"components": {
|
|
702
|
-
"$ref": "#/$defs/components"
|
|
703
|
-
}
|
|
704
|
-
},
|
|
705
|
-
"additionalProperties": false
|
|
706
|
-
},
|
|
707
|
-
"coverageGate": {
|
|
708
|
-
"type": "object",
|
|
709
|
-
"properties": {
|
|
710
|
-
"enabled": {
|
|
711
|
-
"type": "boolean"
|
|
712
|
-
},
|
|
713
|
-
"baselinePath": {
|
|
714
|
-
"allOf": [
|
|
715
|
-
{
|
|
716
|
-
"$ref": "#/$defs/safeString"
|
|
717
|
-
},
|
|
718
|
-
{
|
|
719
|
-
"minLength": 1
|
|
720
|
-
}
|
|
721
|
-
]
|
|
722
|
-
},
|
|
723
|
-
"tolerance": {
|
|
724
|
-
"$ref": "#/$defs/tolerance"
|
|
725
|
-
},
|
|
726
|
-
"floors": {
|
|
727
|
-
"$ref": "#/$defs/floors"
|
|
728
|
-
},
|
|
729
|
-
"components": {
|
|
730
|
-
"$ref": "#/$defs/components"
|
|
731
|
-
},
|
|
732
|
-
"coveragePath": {
|
|
733
|
-
"allOf": [
|
|
734
|
-
{
|
|
735
|
-
"$ref": "#/$defs/safeString"
|
|
736
|
-
},
|
|
737
|
-
{
|
|
738
|
-
"minLength": 1
|
|
739
|
-
}
|
|
740
|
-
]
|
|
741
|
-
},
|
|
742
|
-
"timeoutMs": {
|
|
743
|
-
"type": "integer",
|
|
744
|
-
"minimum": 1
|
|
745
|
-
}
|
|
746
|
-
},
|
|
747
|
-
"additionalProperties": false
|
|
748
|
-
},
|
|
749
|
-
"crapGate": {
|
|
750
|
-
"type": "object",
|
|
751
|
-
"properties": {
|
|
752
|
-
"enabled": {
|
|
753
|
-
"type": "boolean"
|
|
754
|
-
},
|
|
755
|
-
"baselinePath": {
|
|
756
|
-
"allOf": [
|
|
757
|
-
{
|
|
758
|
-
"$ref": "#/$defs/safeString"
|
|
759
|
-
},
|
|
760
|
-
{
|
|
761
|
-
"minLength": 1
|
|
762
|
-
}
|
|
763
|
-
]
|
|
764
|
-
},
|
|
765
|
-
"tolerance": {
|
|
766
|
-
"$ref": "#/$defs/tolerance"
|
|
767
|
-
},
|
|
768
|
-
"floors": {
|
|
769
|
-
"$ref": "#/$defs/floors"
|
|
770
|
-
},
|
|
771
|
-
"components": {
|
|
772
|
-
"$ref": "#/$defs/components"
|
|
773
|
-
},
|
|
774
|
-
"targetDirs": {
|
|
775
|
-
"$ref": "#/$defs/listOrExtenderOfStrings",
|
|
776
|
-
"description": "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."
|
|
777
|
-
},
|
|
778
|
-
"newMethodCeiling": {
|
|
779
|
-
"type": "integer",
|
|
780
|
-
"minimum": 1
|
|
781
|
-
},
|
|
782
|
-
"requireCoverage": {
|
|
783
|
-
"type": "boolean"
|
|
784
|
-
},
|
|
785
|
-
"minMethodResolutionRate": {
|
|
786
|
-
"type": "number",
|
|
787
|
-
"minimum": 0,
|
|
788
|
-
"maximum": 1,
|
|
789
|
-
"description": "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%."
|
|
790
|
-
},
|
|
791
|
-
"friction": {
|
|
604
|
+
"signals": {
|
|
792
605
|
"type": "object",
|
|
606
|
+
"description": "Detector thresholds for the surviving performance-signal categories. Each block is shallow-merged by the resolver.",
|
|
793
607
|
"properties": {
|
|
794
|
-
"
|
|
795
|
-
"type": "
|
|
796
|
-
"
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
608
|
+
"rework": {
|
|
609
|
+
"type": "object",
|
|
610
|
+
"description": "Rework detector — repeated edits to one file in a run.",
|
|
611
|
+
"properties": {
|
|
612
|
+
"editsPerFile": {
|
|
613
|
+
"type": "integer",
|
|
614
|
+
"minimum": 1,
|
|
615
|
+
"description": "Edits to a single file within one run that trip the rework signal.",
|
|
616
|
+
"default": 5
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
"additionalProperties": false
|
|
805
620
|
},
|
|
806
|
-
{
|
|
807
|
-
"
|
|
621
|
+
"retry": {
|
|
622
|
+
"type": "object",
|
|
623
|
+
"description": "Retry detector — the same command failing repeatedly.",
|
|
624
|
+
"properties": {
|
|
625
|
+
"repeatCount": {
|
|
626
|
+
"type": "integer",
|
|
627
|
+
"minimum": 1,
|
|
628
|
+
"description": "Repeats of an identical failing command that trip the retry signal.",
|
|
629
|
+
"default": 3
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
"additionalProperties": false
|
|
808
633
|
}
|
|
809
|
-
]
|
|
810
|
-
},
|
|
811
|
-
"refreshTimeoutMs": {
|
|
812
|
-
"type": "integer",
|
|
813
|
-
"minimum": 1,
|
|
814
|
-
"description": "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)."
|
|
815
|
-
},
|
|
816
|
-
"ignoreGlobs": {
|
|
817
|
-
"type": "array",
|
|
818
|
-
"items": {
|
|
819
|
-
"type": "string",
|
|
820
|
-
"minLength": 1
|
|
821
634
|
},
|
|
822
|
-
"
|
|
635
|
+
"additionalProperties": false
|
|
823
636
|
},
|
|
824
|
-
"
|
|
637
|
+
"quality": {
|
|
825
638
|
"type": "object",
|
|
639
|
+
"description": "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.",
|
|
826
640
|
"properties": {
|
|
827
|
-
"
|
|
828
|
-
"type": "
|
|
641
|
+
"gateScoping": {
|
|
642
|
+
"type": "object",
|
|
643
|
+
"description": "Shared scope applied to every gate that supports one, unless the gate overrides it.",
|
|
644
|
+
"properties": {
|
|
645
|
+
"scope": {
|
|
646
|
+
"type": "string",
|
|
647
|
+
"enum": ["diff", "full"],
|
|
648
|
+
"description": "Score only the files changed against `diffRef` (`diff`) or every file in the gate target dirs (`full`).",
|
|
649
|
+
"default": "diff"
|
|
650
|
+
},
|
|
651
|
+
"diffRef": {
|
|
652
|
+
"type": "string",
|
|
653
|
+
"minLength": 1,
|
|
654
|
+
"description": "Git ref the `diff` scope is computed against.",
|
|
655
|
+
"default": "main"
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
"additionalProperties": false
|
|
829
659
|
},
|
|
830
|
-
"
|
|
831
|
-
"type": "
|
|
832
|
-
"
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
660
|
+
"gates": {
|
|
661
|
+
"type": "object",
|
|
662
|
+
"description": "The eight quality gates, each sharing the `{ enabled, baselinePath, tolerance, floors, components }` base.",
|
|
663
|
+
"properties": {
|
|
664
|
+
"lint": {
|
|
665
|
+
"type": "object",
|
|
666
|
+
"description": "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.",
|
|
667
|
+
"properties": {
|
|
668
|
+
"enabled": {
|
|
669
|
+
"type": "boolean",
|
|
670
|
+
"description": "When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted.",
|
|
671
|
+
"default": true
|
|
672
|
+
},
|
|
673
|
+
"baselinePath": {
|
|
674
|
+
"type": "string",
|
|
675
|
+
"not": {
|
|
676
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
677
|
+
},
|
|
678
|
+
"minLength": 1,
|
|
679
|
+
"description": "Repo-root-relative path to the gate's committed baseline artifact.",
|
|
680
|
+
"default": "baselines/lint.json"
|
|
681
|
+
},
|
|
682
|
+
"tolerance": {
|
|
683
|
+
"type": "object",
|
|
684
|
+
"description": "How much a rollup may drift from the committed baseline before the gate reports a regression.",
|
|
685
|
+
"required": ["kind", "value"],
|
|
686
|
+
"properties": {
|
|
687
|
+
"kind": {
|
|
688
|
+
"type": "string",
|
|
689
|
+
"enum": ["absolute", "percent"],
|
|
690
|
+
"description": "Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`)."
|
|
691
|
+
},
|
|
692
|
+
"value": {
|
|
693
|
+
"type": "number",
|
|
694
|
+
"minimum": 0,
|
|
695
|
+
"description": "The tolerance magnitude. 0 means no drift is allowed."
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
"additionalProperties": false,
|
|
699
|
+
"default": {
|
|
700
|
+
"kind": "absolute",
|
|
701
|
+
"value": 0
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
"floors": {
|
|
705
|
+
"type": "object",
|
|
706
|
+
"description": "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.",
|
|
707
|
+
"additionalProperties": {
|
|
708
|
+
"type": "object",
|
|
709
|
+
"additionalProperties": {
|
|
710
|
+
"type": "number"
|
|
711
|
+
}
|
|
712
|
+
},
|
|
713
|
+
"default": {
|
|
714
|
+
"*": {
|
|
715
|
+
"errorCount": 0
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
},
|
|
719
|
+
"components": {
|
|
720
|
+
"type": "object",
|
|
721
|
+
"description": "Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ \"*\": [\"**\"] }` at the resolver layer.",
|
|
722
|
+
"additionalProperties": {
|
|
723
|
+
"type": "array",
|
|
724
|
+
"items": {
|
|
725
|
+
"type": "string",
|
|
726
|
+
"minLength": 1
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
"additionalProperties": false
|
|
732
|
+
},
|
|
733
|
+
"coverage": {
|
|
734
|
+
"type": "object",
|
|
735
|
+
"description": "Line/branch/function coverage ratchet, read from the Istanbul JSON summary the project test run emits.",
|
|
736
|
+
"properties": {
|
|
737
|
+
"enabled": {
|
|
738
|
+
"type": "boolean",
|
|
739
|
+
"description": "When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted.",
|
|
740
|
+
"default": true
|
|
741
|
+
},
|
|
742
|
+
"baselinePath": {
|
|
743
|
+
"type": "string",
|
|
744
|
+
"not": {
|
|
745
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
746
|
+
},
|
|
747
|
+
"minLength": 1,
|
|
748
|
+
"description": "Repo-root-relative path to the gate's committed baseline artifact.",
|
|
749
|
+
"default": "baselines/coverage.json"
|
|
750
|
+
},
|
|
751
|
+
"tolerance": {
|
|
752
|
+
"type": "object",
|
|
753
|
+
"description": "How much a rollup may drift from the committed baseline before the gate reports a regression.",
|
|
754
|
+
"required": ["kind", "value"],
|
|
755
|
+
"properties": {
|
|
756
|
+
"kind": {
|
|
757
|
+
"type": "string",
|
|
758
|
+
"enum": ["absolute", "percent"],
|
|
759
|
+
"description": "Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`)."
|
|
760
|
+
},
|
|
761
|
+
"value": {
|
|
762
|
+
"type": "number",
|
|
763
|
+
"minimum": 0,
|
|
764
|
+
"description": "The tolerance magnitude. 0 means no drift is allowed."
|
|
765
|
+
}
|
|
766
|
+
},
|
|
767
|
+
"additionalProperties": false,
|
|
768
|
+
"default": {
|
|
769
|
+
"kind": "absolute",
|
|
770
|
+
"value": 0
|
|
771
|
+
}
|
|
772
|
+
},
|
|
773
|
+
"floors": {
|
|
774
|
+
"type": "object",
|
|
775
|
+
"description": "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.",
|
|
776
|
+
"additionalProperties": {
|
|
777
|
+
"type": "object",
|
|
778
|
+
"additionalProperties": {
|
|
779
|
+
"type": "number"
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
"default": {
|
|
783
|
+
"*": {
|
|
784
|
+
"lines": 90,
|
|
785
|
+
"branches": 85,
|
|
786
|
+
"functions": 90
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
"components": {
|
|
791
|
+
"type": "object",
|
|
792
|
+
"description": "Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ \"*\": [\"**\"] }` at the resolver layer.",
|
|
793
|
+
"additionalProperties": {
|
|
794
|
+
"type": "array",
|
|
795
|
+
"items": {
|
|
796
|
+
"type": "string",
|
|
797
|
+
"minLength": 1
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
"coveragePath": {
|
|
802
|
+
"type": "string",
|
|
803
|
+
"not": {
|
|
804
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
805
|
+
},
|
|
806
|
+
"minLength": 1,
|
|
807
|
+
"description": "Repo-relative path to the Istanbul `coverage-final.json` the capture step writes and the gate reads.",
|
|
808
|
+
"default": "coverage/coverage-final.json"
|
|
809
|
+
},
|
|
810
|
+
"timeoutMs": {
|
|
811
|
+
"type": "integer",
|
|
812
|
+
"minimum": 1,
|
|
813
|
+
"description": "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.",
|
|
814
|
+
"default": 600000
|
|
815
|
+
}
|
|
816
|
+
},
|
|
817
|
+
"additionalProperties": false
|
|
818
|
+
},
|
|
819
|
+
"crap": {
|
|
820
|
+
"type": "object",
|
|
821
|
+
"description": "CRAP (Change Risk Anti-Pattern) ratchet — per-method cyclomatic complexity joined against per-method coverage.",
|
|
822
|
+
"properties": {
|
|
823
|
+
"enabled": {
|
|
824
|
+
"type": "boolean",
|
|
825
|
+
"description": "When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted.",
|
|
826
|
+
"default": true
|
|
827
|
+
},
|
|
828
|
+
"baselinePath": {
|
|
829
|
+
"type": "string",
|
|
830
|
+
"not": {
|
|
831
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
832
|
+
},
|
|
833
|
+
"minLength": 1,
|
|
834
|
+
"description": "Repo-root-relative path to the gate's committed baseline artifact.",
|
|
835
|
+
"default": "baselines/crap.json"
|
|
836
|
+
},
|
|
837
|
+
"tolerance": {
|
|
838
|
+
"type": "object",
|
|
839
|
+
"description": "How much a rollup may drift from the committed baseline before the gate reports a regression.",
|
|
840
|
+
"required": ["kind", "value"],
|
|
841
|
+
"properties": {
|
|
842
|
+
"kind": {
|
|
843
|
+
"type": "string",
|
|
844
|
+
"enum": ["absolute", "percent"],
|
|
845
|
+
"description": "Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`)."
|
|
846
|
+
},
|
|
847
|
+
"value": {
|
|
848
|
+
"type": "number",
|
|
849
|
+
"minimum": 0,
|
|
850
|
+
"description": "The tolerance magnitude. 0 means no drift is allowed."
|
|
851
|
+
}
|
|
852
|
+
},
|
|
853
|
+
"additionalProperties": false,
|
|
854
|
+
"default": {
|
|
855
|
+
"kind": "absolute",
|
|
856
|
+
"value": 0.05
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
"floors": {
|
|
860
|
+
"type": "object",
|
|
861
|
+
"description": "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.",
|
|
862
|
+
"additionalProperties": {
|
|
863
|
+
"type": "object",
|
|
864
|
+
"additionalProperties": {
|
|
865
|
+
"type": "number"
|
|
866
|
+
}
|
|
867
|
+
},
|
|
868
|
+
"default": {
|
|
869
|
+
"*": {
|
|
870
|
+
"max": 30,
|
|
871
|
+
"p95": 20,
|
|
872
|
+
"methodsAbove20": 50
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
},
|
|
876
|
+
"components": {
|
|
877
|
+
"type": "object",
|
|
878
|
+
"description": "Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ \"*\": [\"**\"] }` at the resolver layer.",
|
|
879
|
+
"additionalProperties": {
|
|
880
|
+
"type": "array",
|
|
881
|
+
"items": {
|
|
882
|
+
"type": "string",
|
|
883
|
+
"minLength": 1
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
},
|
|
887
|
+
"targetDirs": {
|
|
888
|
+
"oneOf": [
|
|
889
|
+
{
|
|
890
|
+
"type": "array",
|
|
891
|
+
"items": {
|
|
892
|
+
"type": "string"
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
"type": "object",
|
|
897
|
+
"properties": {
|
|
898
|
+
"append": {
|
|
899
|
+
"type": "array",
|
|
900
|
+
"items": {
|
|
901
|
+
"type": "string"
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
"prepend": {
|
|
905
|
+
"type": "array",
|
|
906
|
+
"items": {
|
|
907
|
+
"type": "string"
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
},
|
|
911
|
+
"additionalProperties": false
|
|
912
|
+
}
|
|
913
|
+
],
|
|
914
|
+
"description": "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.",
|
|
915
|
+
"default": ["src"]
|
|
916
|
+
},
|
|
917
|
+
"newMethodCeiling": {
|
|
918
|
+
"type": "integer",
|
|
919
|
+
"minimum": 1,
|
|
920
|
+
"description": "Hard CRAP ceiling applied to a method the diff introduces. A new method above it fails the gate regardless of the baseline.",
|
|
921
|
+
"default": 30
|
|
922
|
+
},
|
|
923
|
+
"requireCoverage": {
|
|
924
|
+
"type": "boolean",
|
|
925
|
+
"description": "When true, the gate refuses to score without a coverage artifact rather than silently reporting complexity-only rows.",
|
|
926
|
+
"default": true
|
|
927
|
+
},
|
|
928
|
+
"minMethodResolutionRate": {
|
|
929
|
+
"type": "number",
|
|
930
|
+
"minimum": 0,
|
|
931
|
+
"maximum": 1,
|
|
932
|
+
"description": "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%."
|
|
933
|
+
},
|
|
934
|
+
"friction": {
|
|
935
|
+
"type": "object",
|
|
936
|
+
"description": "Friction-signal wiring for a CRAP baseline regression, so a recurring one can reach the actionable threshold.",
|
|
937
|
+
"properties": {
|
|
938
|
+
"markerKey": {
|
|
939
|
+
"type": "string",
|
|
940
|
+
"minLength": 1,
|
|
941
|
+
"description": "Signal marker key the regression is recorded under."
|
|
942
|
+
}
|
|
943
|
+
},
|
|
944
|
+
"additionalProperties": false,
|
|
945
|
+
"default": {
|
|
946
|
+
"markerKey": "crap-baseline-regression"
|
|
947
|
+
}
|
|
948
|
+
},
|
|
949
|
+
"refreshTag": {
|
|
950
|
+
"type": "string",
|
|
951
|
+
"not": {
|
|
952
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
953
|
+
},
|
|
954
|
+
"minLength": 1,
|
|
955
|
+
"description": "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.",
|
|
956
|
+
"default": "baseline-refresh:"
|
|
957
|
+
},
|
|
958
|
+
"refreshTimeoutMs": {
|
|
959
|
+
"type": "integer",
|
|
960
|
+
"minimum": 1,
|
|
961
|
+
"description": "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).",
|
|
962
|
+
"default": 60000
|
|
963
|
+
},
|
|
964
|
+
"ignoreGlobs": {
|
|
965
|
+
"type": "array",
|
|
966
|
+
"items": {
|
|
967
|
+
"type": "string",
|
|
968
|
+
"minLength": 1
|
|
969
|
+
},
|
|
970
|
+
"description": "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).",
|
|
971
|
+
"default": []
|
|
972
|
+
},
|
|
973
|
+
"incrementalCoverage": {
|
|
974
|
+
"type": "object",
|
|
975
|
+
"description": "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.",
|
|
976
|
+
"properties": {
|
|
977
|
+
"enabled": {
|
|
978
|
+
"type": "boolean",
|
|
979
|
+
"description": "Master switch for incremental capture + join scoping."
|
|
980
|
+
},
|
|
981
|
+
"baseRef": {
|
|
982
|
+
"type": "string",
|
|
983
|
+
"minLength": 1,
|
|
984
|
+
"description": "Git ref the changed-file set is computed against. Omitted falls back to the gate’s own `--ref` (`main`)."
|
|
985
|
+
}
|
|
986
|
+
},
|
|
987
|
+
"additionalProperties": false
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
"additionalProperties": false
|
|
991
|
+
},
|
|
992
|
+
"maintainability": {
|
|
993
|
+
"type": "object",
|
|
994
|
+
"description": "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.",
|
|
995
|
+
"properties": {
|
|
996
|
+
"enabled": {
|
|
997
|
+
"type": "boolean",
|
|
998
|
+
"description": "When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted.",
|
|
999
|
+
"default": true
|
|
1000
|
+
},
|
|
1001
|
+
"baselinePath": {
|
|
1002
|
+
"type": "string",
|
|
1003
|
+
"not": {
|
|
1004
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
1005
|
+
},
|
|
1006
|
+
"minLength": 1,
|
|
1007
|
+
"description": "Repo-root-relative path to the gate's committed baseline artifact.",
|
|
1008
|
+
"default": "baselines/maintainability.json"
|
|
1009
|
+
},
|
|
1010
|
+
"tolerance": {
|
|
1011
|
+
"type": "object",
|
|
1012
|
+
"description": "How much a rollup may drift from the committed baseline before the gate reports a regression.",
|
|
1013
|
+
"required": ["kind", "value"],
|
|
1014
|
+
"properties": {
|
|
1015
|
+
"kind": {
|
|
1016
|
+
"type": "string",
|
|
1017
|
+
"enum": ["absolute", "percent"],
|
|
1018
|
+
"description": "Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`)."
|
|
1019
|
+
},
|
|
1020
|
+
"value": {
|
|
1021
|
+
"type": "number",
|
|
1022
|
+
"minimum": 0,
|
|
1023
|
+
"description": "The tolerance magnitude. 0 means no drift is allowed."
|
|
1024
|
+
}
|
|
1025
|
+
},
|
|
1026
|
+
"additionalProperties": false,
|
|
1027
|
+
"default": {
|
|
1028
|
+
"kind": "absolute",
|
|
1029
|
+
"value": 0.5
|
|
1030
|
+
}
|
|
1031
|
+
},
|
|
1032
|
+
"floors": {
|
|
1033
|
+
"type": "object",
|
|
1034
|
+
"description": "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.",
|
|
1035
|
+
"additionalProperties": {
|
|
1036
|
+
"type": "object",
|
|
1037
|
+
"additionalProperties": {
|
|
1038
|
+
"type": "number"
|
|
1039
|
+
}
|
|
1040
|
+
},
|
|
1041
|
+
"default": {
|
|
1042
|
+
"*": {
|
|
1043
|
+
"min": 70
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
},
|
|
1047
|
+
"components": {
|
|
1048
|
+
"type": "object",
|
|
1049
|
+
"description": "Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ \"*\": [\"**\"] }` at the resolver layer.",
|
|
1050
|
+
"additionalProperties": {
|
|
1051
|
+
"type": "array",
|
|
1052
|
+
"items": {
|
|
1053
|
+
"type": "string",
|
|
1054
|
+
"minLength": 1
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
},
|
|
1058
|
+
"targetDirs": {
|
|
1059
|
+
"oneOf": [
|
|
1060
|
+
{
|
|
1061
|
+
"type": "array",
|
|
1062
|
+
"items": {
|
|
1063
|
+
"type": "string"
|
|
1064
|
+
}
|
|
1065
|
+
},
|
|
1066
|
+
{
|
|
1067
|
+
"type": "object",
|
|
1068
|
+
"properties": {
|
|
1069
|
+
"append": {
|
|
1070
|
+
"type": "array",
|
|
1071
|
+
"items": {
|
|
1072
|
+
"type": "string"
|
|
1073
|
+
}
|
|
1074
|
+
},
|
|
1075
|
+
"prepend": {
|
|
1076
|
+
"type": "array",
|
|
1077
|
+
"items": {
|
|
1078
|
+
"type": "string"
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
},
|
|
1082
|
+
"additionalProperties": false
|
|
1083
|
+
}
|
|
1084
|
+
],
|
|
1085
|
+
"description": "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.",
|
|
1086
|
+
"default": []
|
|
1087
|
+
},
|
|
1088
|
+
"refreshTag": {
|
|
1089
|
+
"type": "string",
|
|
1090
|
+
"not": {
|
|
1091
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
1092
|
+
},
|
|
1093
|
+
"minLength": 1,
|
|
1094
|
+
"description": "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)."
|
|
1095
|
+
},
|
|
1096
|
+
"refreshTimeoutMs": {
|
|
1097
|
+
"type": "integer",
|
|
1098
|
+
"minimum": 1,
|
|
1099
|
+
"description": "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).",
|
|
1100
|
+
"default": 60000
|
|
1101
|
+
},
|
|
1102
|
+
"ignoreGlobs": {
|
|
1103
|
+
"type": "array",
|
|
1104
|
+
"items": {
|
|
1105
|
+
"type": "string",
|
|
1106
|
+
"minLength": 1
|
|
1107
|
+
},
|
|
1108
|
+
"description": "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).",
|
|
1109
|
+
"default": []
|
|
1110
|
+
}
|
|
1111
|
+
},
|
|
1112
|
+
"additionalProperties": false
|
|
1113
|
+
},
|
|
1114
|
+
"mutation": {
|
|
1115
|
+
"type": "object",
|
|
1116
|
+
"description": "Stryker mutation-score ratchet. Off in practice for most consumers — the baseline kind is registered but no framework path runs Stryker.",
|
|
1117
|
+
"properties": {
|
|
1118
|
+
"enabled": {
|
|
1119
|
+
"type": "boolean",
|
|
1120
|
+
"description": "When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted.",
|
|
1121
|
+
"default": true
|
|
1122
|
+
},
|
|
1123
|
+
"baselinePath": {
|
|
1124
|
+
"type": "string",
|
|
1125
|
+
"not": {
|
|
1126
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
1127
|
+
},
|
|
1128
|
+
"minLength": 1,
|
|
1129
|
+
"description": "Repo-root-relative path to the gate's committed baseline artifact.",
|
|
1130
|
+
"default": "baselines/mutation.json"
|
|
1131
|
+
},
|
|
1132
|
+
"tolerance": {
|
|
1133
|
+
"type": "object",
|
|
1134
|
+
"description": "How much a rollup may drift from the committed baseline before the gate reports a regression.",
|
|
1135
|
+
"required": ["kind", "value"],
|
|
1136
|
+
"properties": {
|
|
1137
|
+
"kind": {
|
|
1138
|
+
"type": "string",
|
|
1139
|
+
"enum": ["absolute", "percent"],
|
|
1140
|
+
"description": "Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`)."
|
|
1141
|
+
},
|
|
1142
|
+
"value": {
|
|
1143
|
+
"type": "number",
|
|
1144
|
+
"minimum": 0,
|
|
1145
|
+
"description": "The tolerance magnitude. 0 means no drift is allowed."
|
|
1146
|
+
}
|
|
1147
|
+
},
|
|
1148
|
+
"additionalProperties": false,
|
|
1149
|
+
"default": {
|
|
1150
|
+
"kind": "percent",
|
|
1151
|
+
"value": 0
|
|
1152
|
+
}
|
|
1153
|
+
},
|
|
1154
|
+
"floors": {
|
|
1155
|
+
"type": "object",
|
|
1156
|
+
"description": "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.",
|
|
1157
|
+
"additionalProperties": {
|
|
1158
|
+
"type": "object",
|
|
1159
|
+
"additionalProperties": {
|
|
1160
|
+
"type": "number"
|
|
1161
|
+
}
|
|
1162
|
+
},
|
|
1163
|
+
"default": {
|
|
1164
|
+
"*": {
|
|
1165
|
+
"score": 60
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
"components": {
|
|
1170
|
+
"type": "object",
|
|
1171
|
+
"description": "Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ \"*\": [\"**\"] }` at the resolver layer.",
|
|
1172
|
+
"additionalProperties": {
|
|
1173
|
+
"type": "array",
|
|
1174
|
+
"items": {
|
|
1175
|
+
"type": "string",
|
|
1176
|
+
"minLength": 1
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
"strykerConfigPath": {
|
|
1181
|
+
"type": ["string", "null"],
|
|
1182
|
+
"minLength": 1,
|
|
1183
|
+
"not": {
|
|
1184
|
+
"type": "string",
|
|
1185
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
1186
|
+
},
|
|
1187
|
+
"description": "Repo-relative path to the Stryker config. `null` lets Stryker resolve its own default.",
|
|
1188
|
+
"default": null
|
|
1189
|
+
}
|
|
1190
|
+
},
|
|
1191
|
+
"additionalProperties": false
|
|
1192
|
+
},
|
|
1193
|
+
"lighthouse": {
|
|
1194
|
+
"type": "object",
|
|
1195
|
+
"description": "Lighthouse category-score ratchet for web targets. Off by default — it needs a running deployment.",
|
|
1196
|
+
"properties": {
|
|
1197
|
+
"enabled": {
|
|
1198
|
+
"type": "boolean",
|
|
1199
|
+
"description": "When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted.",
|
|
1200
|
+
"default": false
|
|
1201
|
+
},
|
|
1202
|
+
"baselinePath": {
|
|
1203
|
+
"type": "string",
|
|
1204
|
+
"not": {
|
|
1205
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
1206
|
+
},
|
|
1207
|
+
"minLength": 1,
|
|
1208
|
+
"description": "Repo-root-relative path to the gate's committed baseline artifact.",
|
|
1209
|
+
"default": "baselines/lighthouse.json"
|
|
1210
|
+
},
|
|
1211
|
+
"tolerance": {
|
|
1212
|
+
"type": "object",
|
|
1213
|
+
"description": "How much a rollup may drift from the committed baseline before the gate reports a regression.",
|
|
1214
|
+
"required": ["kind", "value"],
|
|
1215
|
+
"properties": {
|
|
1216
|
+
"kind": {
|
|
1217
|
+
"type": "string",
|
|
1218
|
+
"enum": ["absolute", "percent"],
|
|
1219
|
+
"description": "Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`)."
|
|
1220
|
+
},
|
|
1221
|
+
"value": {
|
|
1222
|
+
"type": "number",
|
|
1223
|
+
"minimum": 0,
|
|
1224
|
+
"description": "The tolerance magnitude. 0 means no drift is allowed."
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
"additionalProperties": false,
|
|
1228
|
+
"default": {
|
|
1229
|
+
"kind": "absolute",
|
|
1230
|
+
"value": 0
|
|
1231
|
+
}
|
|
1232
|
+
},
|
|
1233
|
+
"floors": {
|
|
1234
|
+
"type": "object",
|
|
1235
|
+
"description": "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.",
|
|
1236
|
+
"additionalProperties": {
|
|
1237
|
+
"type": "object",
|
|
1238
|
+
"additionalProperties": {
|
|
1239
|
+
"type": "number"
|
|
1240
|
+
}
|
|
1241
|
+
},
|
|
1242
|
+
"default": {
|
|
1243
|
+
"*": {
|
|
1244
|
+
"performance": 0,
|
|
1245
|
+
"accessibility": 0,
|
|
1246
|
+
"bestPractices": 0,
|
|
1247
|
+
"seo": 0
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
},
|
|
1251
|
+
"components": {
|
|
1252
|
+
"type": "object",
|
|
1253
|
+
"description": "Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ \"*\": [\"**\"] }` at the resolver layer.",
|
|
1254
|
+
"additionalProperties": {
|
|
1255
|
+
"type": "array",
|
|
1256
|
+
"items": {
|
|
1257
|
+
"type": "string",
|
|
1258
|
+
"minLength": 1
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
},
|
|
1262
|
+
"baseUrl": {
|
|
1263
|
+
"type": ["string", "null"],
|
|
1264
|
+
"minLength": 1,
|
|
1265
|
+
"not": {
|
|
1266
|
+
"type": "string",
|
|
1267
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
1268
|
+
},
|
|
1269
|
+
"description": "Origin the routes are resolved against. `null` leaves the gate unusable, which is why it ships disabled.",
|
|
1270
|
+
"default": null
|
|
1271
|
+
},
|
|
1272
|
+
"routes": {
|
|
1273
|
+
"type": "array",
|
|
1274
|
+
"items": {
|
|
1275
|
+
"type": "object",
|
|
1276
|
+
"description": "One route the Lighthouse run scores.",
|
|
1277
|
+
"required": ["path"],
|
|
1278
|
+
"properties": {
|
|
1279
|
+
"path": {
|
|
1280
|
+
"type": "string",
|
|
1281
|
+
"minLength": 1,
|
|
1282
|
+
"description": "Route path appended to `baseUrl`."
|
|
1283
|
+
},
|
|
1284
|
+
"formFactor": {
|
|
1285
|
+
"type": "string",
|
|
1286
|
+
"enum": ["mobile", "desktop"],
|
|
1287
|
+
"description": "Emulated form factor for this route."
|
|
1288
|
+
}
|
|
1289
|
+
},
|
|
1290
|
+
"additionalProperties": false
|
|
1291
|
+
},
|
|
1292
|
+
"description": "Routes to score on each run.",
|
|
1293
|
+
"default": []
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
"additionalProperties": false
|
|
1297
|
+
},
|
|
1298
|
+
"bundle-size": {
|
|
1299
|
+
"type": "object",
|
|
1300
|
+
"description": "Built-artifact size ratchet for web targets. Off by default — it needs declared bundles.",
|
|
1301
|
+
"properties": {
|
|
1302
|
+
"enabled": {
|
|
1303
|
+
"type": "boolean",
|
|
1304
|
+
"description": "When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted.",
|
|
1305
|
+
"default": false
|
|
1306
|
+
},
|
|
1307
|
+
"baselinePath": {
|
|
1308
|
+
"type": "string",
|
|
1309
|
+
"not": {
|
|
1310
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
1311
|
+
},
|
|
1312
|
+
"minLength": 1,
|
|
1313
|
+
"description": "Repo-root-relative path to the gate's committed baseline artifact.",
|
|
1314
|
+
"default": "baselines/bundle-size.json"
|
|
1315
|
+
},
|
|
1316
|
+
"tolerance": {
|
|
1317
|
+
"type": "object",
|
|
1318
|
+
"description": "How much a rollup may drift from the committed baseline before the gate reports a regression.",
|
|
1319
|
+
"required": ["kind", "value"],
|
|
1320
|
+
"properties": {
|
|
1321
|
+
"kind": {
|
|
1322
|
+
"type": "string",
|
|
1323
|
+
"enum": ["absolute", "percent"],
|
|
1324
|
+
"description": "Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`)."
|
|
1325
|
+
},
|
|
1326
|
+
"value": {
|
|
1327
|
+
"type": "number",
|
|
1328
|
+
"minimum": 0,
|
|
1329
|
+
"description": "The tolerance magnitude. 0 means no drift is allowed."
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
"additionalProperties": false,
|
|
1333
|
+
"default": {
|
|
1334
|
+
"kind": "percent",
|
|
1335
|
+
"value": 0
|
|
1336
|
+
}
|
|
1337
|
+
},
|
|
1338
|
+
"floors": {
|
|
1339
|
+
"type": "object",
|
|
1340
|
+
"description": "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.",
|
|
1341
|
+
"additionalProperties": {
|
|
1342
|
+
"type": "object",
|
|
1343
|
+
"additionalProperties": {
|
|
1344
|
+
"type": "number"
|
|
1345
|
+
}
|
|
1346
|
+
},
|
|
1347
|
+
"default": {
|
|
1348
|
+
"*": {}
|
|
1349
|
+
}
|
|
1350
|
+
},
|
|
1351
|
+
"components": {
|
|
1352
|
+
"type": "object",
|
|
1353
|
+
"description": "Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ \"*\": [\"**\"] }` at the resolver layer.",
|
|
1354
|
+
"additionalProperties": {
|
|
1355
|
+
"type": "array",
|
|
1356
|
+
"items": {
|
|
1357
|
+
"type": "string",
|
|
1358
|
+
"minLength": 1
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
},
|
|
1362
|
+
"bundles": {
|
|
1363
|
+
"type": "array",
|
|
1364
|
+
"items": {
|
|
1365
|
+
"type": "object",
|
|
1366
|
+
"description": "One built artifact the bundle-size gate weighs.",
|
|
1367
|
+
"required": ["name", "path", "limit"],
|
|
1368
|
+
"properties": {
|
|
1369
|
+
"name": {
|
|
1370
|
+
"type": "string",
|
|
1371
|
+
"minLength": 1,
|
|
1372
|
+
"description": "Label the rollup row is keyed by."
|
|
1373
|
+
},
|
|
1374
|
+
"path": {
|
|
1375
|
+
"type": "string",
|
|
1376
|
+
"minLength": 1,
|
|
1377
|
+
"description": "Glob or path of the built artifact to measure."
|
|
1378
|
+
},
|
|
1379
|
+
"limit": {
|
|
1380
|
+
"type": "string",
|
|
1381
|
+
"minLength": 1,
|
|
1382
|
+
"description": "Human size ceiling (e.g. `\"180 kB\"`)."
|
|
1383
|
+
}
|
|
1384
|
+
},
|
|
1385
|
+
"additionalProperties": false
|
|
1386
|
+
},
|
|
1387
|
+
"description": "The artifacts to weigh. Empty means the gate has nothing to do.",
|
|
1388
|
+
"default": []
|
|
1389
|
+
}
|
|
1390
|
+
},
|
|
1391
|
+
"additionalProperties": false
|
|
1392
|
+
},
|
|
1393
|
+
"duplication": {
|
|
1394
|
+
"type": "object",
|
|
1395
|
+
"description": "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.",
|
|
1396
|
+
"properties": {
|
|
1397
|
+
"enabled": {
|
|
1398
|
+
"type": "boolean",
|
|
1399
|
+
"description": "When false, the checker exits 0 with a skip line and the gate is reported as `skipped`, never omitted.",
|
|
1400
|
+
"default": false
|
|
1401
|
+
},
|
|
1402
|
+
"baselinePath": {
|
|
1403
|
+
"type": "string",
|
|
1404
|
+
"not": {
|
|
1405
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
1406
|
+
},
|
|
1407
|
+
"minLength": 1,
|
|
1408
|
+
"description": "Repo-root-relative path to the gate's committed baseline artifact.",
|
|
1409
|
+
"default": "baselines/duplication.json"
|
|
1410
|
+
},
|
|
1411
|
+
"tolerance": {
|
|
1412
|
+
"type": "object",
|
|
1413
|
+
"description": "How much a rollup may drift from the committed baseline before the gate reports a regression.",
|
|
1414
|
+
"required": ["kind", "value"],
|
|
1415
|
+
"properties": {
|
|
1416
|
+
"kind": {
|
|
1417
|
+
"type": "string",
|
|
1418
|
+
"enum": ["absolute", "percent"],
|
|
1419
|
+
"description": "Whether `value` is read as raw metric units (`absolute`) or as a percentage of the baseline (`percent`)."
|
|
1420
|
+
},
|
|
1421
|
+
"value": {
|
|
1422
|
+
"type": "number",
|
|
1423
|
+
"minimum": 0,
|
|
1424
|
+
"description": "The tolerance magnitude. 0 means no drift is allowed."
|
|
1425
|
+
}
|
|
1426
|
+
},
|
|
1427
|
+
"additionalProperties": false,
|
|
1428
|
+
"default": {
|
|
1429
|
+
"kind": "absolute",
|
|
1430
|
+
"value": 1
|
|
1431
|
+
}
|
|
1432
|
+
},
|
|
1433
|
+
"floors": {
|
|
1434
|
+
"type": "object",
|
|
1435
|
+
"description": "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.",
|
|
1436
|
+
"additionalProperties": {
|
|
1437
|
+
"type": "object",
|
|
1438
|
+
"additionalProperties": {
|
|
1439
|
+
"type": "number"
|
|
1440
|
+
}
|
|
1441
|
+
},
|
|
1442
|
+
"default": {
|
|
1443
|
+
"*": {
|
|
1444
|
+
"percentage": 25
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
},
|
|
1448
|
+
"components": {
|
|
1449
|
+
"type": "object",
|
|
1450
|
+
"description": "Per-gate component map — component name to the glob list whose files roll up under it. Defaults to `{ \"*\": [\"**\"] }` at the resolver layer.",
|
|
1451
|
+
"additionalProperties": {
|
|
1452
|
+
"type": "array",
|
|
1453
|
+
"items": {
|
|
1454
|
+
"type": "string",
|
|
1455
|
+
"minLength": 1
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
},
|
|
1459
|
+
"targetDirs": {
|
|
1460
|
+
"oneOf": [
|
|
1461
|
+
{
|
|
1462
|
+
"type": "array",
|
|
1463
|
+
"items": {
|
|
1464
|
+
"type": "string"
|
|
1465
|
+
}
|
|
1466
|
+
},
|
|
1467
|
+
{
|
|
1468
|
+
"type": "object",
|
|
1469
|
+
"properties": {
|
|
1470
|
+
"append": {
|
|
1471
|
+
"type": "array",
|
|
1472
|
+
"items": {
|
|
1473
|
+
"type": "string"
|
|
1474
|
+
}
|
|
1475
|
+
},
|
|
1476
|
+
"prepend": {
|
|
1477
|
+
"type": "array",
|
|
1478
|
+
"items": {
|
|
1479
|
+
"type": "string"
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
},
|
|
1483
|
+
"additionalProperties": false
|
|
1484
|
+
}
|
|
1485
|
+
],
|
|
1486
|
+
"description": "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).",
|
|
1487
|
+
"default": ["src"]
|
|
1488
|
+
},
|
|
1489
|
+
"refreshTimeoutMs": {
|
|
1490
|
+
"type": "integer",
|
|
1491
|
+
"minimum": 1,
|
|
1492
|
+
"description": "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).",
|
|
1493
|
+
"default": 60000
|
|
1494
|
+
},
|
|
1495
|
+
"ignoreGlobs": {
|
|
1496
|
+
"type": "array",
|
|
1497
|
+
"items": {
|
|
1498
|
+
"type": "string",
|
|
1499
|
+
"minLength": 1
|
|
1500
|
+
},
|
|
1501
|
+
"description": "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).",
|
|
1502
|
+
"default": []
|
|
1503
|
+
}
|
|
1504
|
+
},
|
|
1505
|
+
"additionalProperties": false
|
|
1506
|
+
}
|
|
1507
|
+
},
|
|
1508
|
+
"additionalProperties": false
|
|
851
1509
|
},
|
|
852
|
-
{
|
|
853
|
-
"
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
"$ref": "#/$defs/components"
|
|
865
|
-
},
|
|
866
|
-
"targetDirs": {
|
|
867
|
-
"$ref": "#/$defs/listOrExtenderOfStrings",
|
|
868
|
-
"description": "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."
|
|
869
|
-
},
|
|
870
|
-
"refreshTag": {
|
|
871
|
-
"allOf": [
|
|
872
|
-
{
|
|
873
|
-
"$ref": "#/$defs/safeString"
|
|
1510
|
+
"formatAutofix": {
|
|
1511
|
+
"type": "object",
|
|
1512
|
+
"description": "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.",
|
|
1513
|
+
"properties": {
|
|
1514
|
+
"timeoutMs": {
|
|
1515
|
+
"type": "integer",
|
|
1516
|
+
"minimum": 1,
|
|
1517
|
+
"description": "Timeout (ms) for the format-write spawn.",
|
|
1518
|
+
"default": 60000
|
|
1519
|
+
}
|
|
1520
|
+
},
|
|
1521
|
+
"additionalProperties": false
|
|
874
1522
|
},
|
|
875
|
-
{
|
|
876
|
-
"
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
"properties": {
|
|
899
|
-
"enabled": {
|
|
900
|
-
"type": "boolean"
|
|
901
|
-
},
|
|
902
|
-
"baselinePath": {
|
|
903
|
-
"allOf": [
|
|
904
|
-
{
|
|
905
|
-
"$ref": "#/$defs/safeString"
|
|
1523
|
+
"codingGuardrails": {
|
|
1524
|
+
"type": "object",
|
|
1525
|
+
"description": "Authoring-time cyclomatic-complexity advisories surfaced by the quality-preview pre-commit gate.",
|
|
1526
|
+
"properties": {
|
|
1527
|
+
"cyclomaticFlag": {
|
|
1528
|
+
"type": "integer",
|
|
1529
|
+
"minimum": 1,
|
|
1530
|
+
"description": "Cyclomatic complexity at which a new or changed method is flagged for a refactor look.",
|
|
1531
|
+
"default": 8
|
|
1532
|
+
},
|
|
1533
|
+
"cyclomaticMustFix": {
|
|
1534
|
+
"type": "integer",
|
|
1535
|
+
"minimum": 1,
|
|
1536
|
+
"description": "Cyclomatic complexity at which a new or changed method must be decomposed before the diff closes.",
|
|
1537
|
+
"default": 12
|
|
1538
|
+
},
|
|
1539
|
+
"requireSiblingTest": {
|
|
1540
|
+
"type": "boolean",
|
|
1541
|
+
"description": "When true, a new source file with no colocated sibling test is reported by the guardrails pass.",
|
|
1542
|
+
"default": false
|
|
1543
|
+
}
|
|
1544
|
+
},
|
|
1545
|
+
"additionalProperties": false
|
|
906
1546
|
},
|
|
907
|
-
{
|
|
908
|
-
"
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
"properties": {
|
|
931
|
-
"path": {
|
|
932
|
-
"type": "string",
|
|
933
|
-
"minLength": 1
|
|
934
|
-
},
|
|
935
|
-
"formFactor": {
|
|
936
|
-
"type": "string",
|
|
937
|
-
"enum": ["mobile", "desktop"]
|
|
938
|
-
}
|
|
939
|
-
},
|
|
940
|
-
"additionalProperties": false
|
|
941
|
-
},
|
|
942
|
-
"lighthouseGate": {
|
|
943
|
-
"type": "object",
|
|
944
|
-
"properties": {
|
|
945
|
-
"enabled": {
|
|
946
|
-
"type": "boolean"
|
|
947
|
-
},
|
|
948
|
-
"baselinePath": {
|
|
949
|
-
"allOf": [
|
|
950
|
-
{
|
|
951
|
-
"$ref": "#/$defs/safeString"
|
|
1547
|
+
"autoRefresh": {
|
|
1548
|
+
"type": "object",
|
|
1549
|
+
"description": "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.",
|
|
1550
|
+
"properties": {
|
|
1551
|
+
"enabled": {
|
|
1552
|
+
"type": "boolean",
|
|
1553
|
+
"description": "Master switch for the auto-refresh path. When false, every baseline refresh is a deliberate operator action.",
|
|
1554
|
+
"default": true
|
|
1555
|
+
},
|
|
1556
|
+
"crapJumpCap": {
|
|
1557
|
+
"type": "number",
|
|
1558
|
+
"minimum": 0,
|
|
1559
|
+
"description": "Largest single-row CRAP jump the auto-refresh will absorb. A larger jump is reported as a regression rather than rewritten.",
|
|
1560
|
+
"default": 5
|
|
1561
|
+
},
|
|
1562
|
+
"scope": {
|
|
1563
|
+
"type": "string",
|
|
1564
|
+
"enum": ["diff", "full"],
|
|
1565
|
+
"description": "Whether an auto-refresh rescores only the changed files (`diff`) or every file in the target dirs (`full`).",
|
|
1566
|
+
"default": "diff"
|
|
1567
|
+
}
|
|
1568
|
+
},
|
|
1569
|
+
"additionalProperties": false
|
|
952
1570
|
},
|
|
953
|
-
{
|
|
954
|
-
"
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
"type": "object",
|
|
1000
|
-
"properties": {
|
|
1001
|
-
"enabled": {
|
|
1002
|
-
"type": "boolean"
|
|
1003
|
-
},
|
|
1004
|
-
"baselinePath": {
|
|
1005
|
-
"allOf": [
|
|
1006
|
-
{
|
|
1007
|
-
"$ref": "#/$defs/safeString"
|
|
1571
|
+
"baselineEpsilon": {
|
|
1572
|
+
"type": "object",
|
|
1573
|
+
"description": "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.",
|
|
1574
|
+
"properties": {
|
|
1575
|
+
"maintainability": {
|
|
1576
|
+
"type": "number",
|
|
1577
|
+
"minimum": 0,
|
|
1578
|
+
"default": 0.5
|
|
1579
|
+
},
|
|
1580
|
+
"crap": {
|
|
1581
|
+
"type": "number",
|
|
1582
|
+
"minimum": 0,
|
|
1583
|
+
"default": 0.5
|
|
1584
|
+
},
|
|
1585
|
+
"coverage": {
|
|
1586
|
+
"type": "number",
|
|
1587
|
+
"minimum": 0,
|
|
1588
|
+
"default": 0.1
|
|
1589
|
+
},
|
|
1590
|
+
"mutation": {
|
|
1591
|
+
"type": "number",
|
|
1592
|
+
"minimum": 0,
|
|
1593
|
+
"default": 0.5
|
|
1594
|
+
},
|
|
1595
|
+
"lint": {
|
|
1596
|
+
"type": "number",
|
|
1597
|
+
"minimum": 0,
|
|
1598
|
+
"default": 0
|
|
1599
|
+
},
|
|
1600
|
+
"lighthouse": {
|
|
1601
|
+
"type": "number",
|
|
1602
|
+
"minimum": 0,
|
|
1603
|
+
"default": 1
|
|
1604
|
+
},
|
|
1605
|
+
"bundle-size": {
|
|
1606
|
+
"type": "number",
|
|
1607
|
+
"minimum": 0,
|
|
1608
|
+
"default": 1024
|
|
1609
|
+
},
|
|
1610
|
+
"duplication": {
|
|
1611
|
+
"type": "number",
|
|
1612
|
+
"minimum": 0,
|
|
1613
|
+
"default": 0.5
|
|
1614
|
+
}
|
|
1615
|
+
},
|
|
1616
|
+
"additionalProperties": false
|
|
1008
1617
|
},
|
|
1009
|
-
{
|
|
1010
|
-
"
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
},
|
|
1014
|
-
"tolerance": {
|
|
1015
|
-
"$ref": "#/$defs/tolerance"
|
|
1016
|
-
},
|
|
1017
|
-
"floors": {
|
|
1018
|
-
"$ref": "#/$defs/floors"
|
|
1019
|
-
},
|
|
1020
|
-
"components": {
|
|
1021
|
-
"$ref": "#/$defs/components"
|
|
1022
|
-
},
|
|
1023
|
-
"bundles": {
|
|
1024
|
-
"type": "array",
|
|
1025
|
-
"items": {
|
|
1026
|
-
"$ref": "#/$defs/bundleDeclaration"
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
},
|
|
1030
|
-
"additionalProperties": false
|
|
1031
|
-
},
|
|
1032
|
-
"duplicationGate": {
|
|
1033
|
-
"type": "object",
|
|
1034
|
-
"properties": {
|
|
1035
|
-
"enabled": {
|
|
1036
|
-
"type": "boolean"
|
|
1037
|
-
},
|
|
1038
|
-
"baselinePath": {
|
|
1039
|
-
"allOf": [
|
|
1040
|
-
{
|
|
1041
|
-
"$ref": "#/$defs/safeString"
|
|
1618
|
+
"requireBaselines": {
|
|
1619
|
+
"type": "boolean",
|
|
1620
|
+
"description": "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).",
|
|
1621
|
+
"default": false
|
|
1042
1622
|
},
|
|
1043
|
-
{
|
|
1044
|
-
"
|
|
1623
|
+
"navigability": {
|
|
1624
|
+
"type": "object",
|
|
1625
|
+
"description": "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.",
|
|
1626
|
+
"properties": {
|
|
1627
|
+
"routeGlobs": {
|
|
1628
|
+
"type": "array",
|
|
1629
|
+
"items": {
|
|
1630
|
+
"type": "string"
|
|
1631
|
+
},
|
|
1632
|
+
"description": "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.",
|
|
1633
|
+
"default": []
|
|
1634
|
+
},
|
|
1635
|
+
"navRegistry": {
|
|
1636
|
+
"type": "array",
|
|
1637
|
+
"items": {
|
|
1638
|
+
"type": "string"
|
|
1639
|
+
},
|
|
1640
|
+
"description": "Tokens identifying the nav-registry SSOT the navigability lens checks every route resolves a nav door against.",
|
|
1641
|
+
"default": []
|
|
1642
|
+
},
|
|
1643
|
+
"journeySuite": {
|
|
1644
|
+
"type": "string",
|
|
1645
|
+
"description": "Path or command for the per-persona journey suite /deliver's per-Story ceremony runs."
|
|
1646
|
+
}
|
|
1647
|
+
},
|
|
1648
|
+
"additionalProperties": false
|
|
1045
1649
|
}
|
|
1046
|
-
]
|
|
1047
|
-
},
|
|
1048
|
-
"tolerance": {
|
|
1049
|
-
"$ref": "#/$defs/tolerance"
|
|
1050
|
-
},
|
|
1051
|
-
"floors": {
|
|
1052
|
-
"$ref": "#/$defs/floors"
|
|
1053
|
-
},
|
|
1054
|
-
"components": {
|
|
1055
|
-
"$ref": "#/$defs/components"
|
|
1056
|
-
},
|
|
1057
|
-
"targetDirs": {
|
|
1058
|
-
"$ref": "#/$defs/listOrExtenderOfStrings",
|
|
1059
|
-
"description": "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)."
|
|
1060
|
-
},
|
|
1061
|
-
"refreshTimeoutMs": {
|
|
1062
|
-
"type": "integer",
|
|
1063
|
-
"minimum": 1,
|
|
1064
|
-
"description": "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)."
|
|
1065
|
-
},
|
|
1066
|
-
"ignoreGlobs": {
|
|
1067
|
-
"type": "array",
|
|
1068
|
-
"items": {
|
|
1069
|
-
"type": "string",
|
|
1070
|
-
"minLength": 1
|
|
1071
1650
|
},
|
|
1072
|
-
"
|
|
1073
|
-
}
|
|
1074
|
-
},
|
|
1075
|
-
"additionalProperties": false
|
|
1076
|
-
},
|
|
1077
|
-
"gates": {
|
|
1078
|
-
"type": "object",
|
|
1079
|
-
"properties": {
|
|
1080
|
-
"lint": {
|
|
1081
|
-
"$ref": "#/$defs/lintGate"
|
|
1082
|
-
},
|
|
1083
|
-
"coverage": {
|
|
1084
|
-
"$ref": "#/$defs/coverageGate"
|
|
1085
|
-
},
|
|
1086
|
-
"crap": {
|
|
1087
|
-
"$ref": "#/$defs/crapGate"
|
|
1088
|
-
},
|
|
1089
|
-
"maintainability": {
|
|
1090
|
-
"$ref": "#/$defs/maintainabilityGate"
|
|
1091
|
-
},
|
|
1092
|
-
"mutation": {
|
|
1093
|
-
"$ref": "#/$defs/mutationGate"
|
|
1094
|
-
},
|
|
1095
|
-
"lighthouse": {
|
|
1096
|
-
"$ref": "#/$defs/lighthouseGate"
|
|
1097
|
-
},
|
|
1098
|
-
"bundle-size": {
|
|
1099
|
-
"$ref": "#/$defs/bundleSizeGate"
|
|
1100
|
-
},
|
|
1101
|
-
"duplication": {
|
|
1102
|
-
"$ref": "#/$defs/duplicationGate"
|
|
1103
|
-
}
|
|
1104
|
-
},
|
|
1105
|
-
"additionalProperties": false
|
|
1106
|
-
},
|
|
1107
|
-
"codingGuardrails": {
|
|
1108
|
-
"type": "object",
|
|
1109
|
-
"properties": {
|
|
1110
|
-
"cyclomaticFlag": {
|
|
1111
|
-
"type": "integer",
|
|
1112
|
-
"minimum": 1
|
|
1113
|
-
},
|
|
1114
|
-
"cyclomaticMustFix": {
|
|
1115
|
-
"type": "integer",
|
|
1116
|
-
"minimum": 1
|
|
1117
|
-
},
|
|
1118
|
-
"requireSiblingTest": {
|
|
1119
|
-
"type": "boolean"
|
|
1120
|
-
}
|
|
1121
|
-
},
|
|
1122
|
-
"additionalProperties": false
|
|
1123
|
-
},
|
|
1124
|
-
"autoRefresh": {
|
|
1125
|
-
"type": "object",
|
|
1126
|
-
"properties": {
|
|
1127
|
-
"enabled": {
|
|
1128
|
-
"type": "boolean"
|
|
1129
|
-
},
|
|
1130
|
-
"crapJumpCap": {
|
|
1131
|
-
"type": "number",
|
|
1132
|
-
"minimum": 0
|
|
1133
|
-
},
|
|
1134
|
-
"scope": {
|
|
1135
|
-
"type": "string",
|
|
1136
|
-
"enum": ["diff", "full"]
|
|
1137
|
-
}
|
|
1138
|
-
},
|
|
1139
|
-
"additionalProperties": false
|
|
1140
|
-
},
|
|
1141
|
-
"baselineEpsilon": {
|
|
1142
|
-
"type": "object",
|
|
1143
|
-
"description": "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.",
|
|
1144
|
-
"properties": {
|
|
1145
|
-
"maintainability": {
|
|
1146
|
-
"type": "number",
|
|
1147
|
-
"minimum": 0
|
|
1148
|
-
},
|
|
1149
|
-
"crap": {
|
|
1150
|
-
"type": "number",
|
|
1151
|
-
"minimum": 0
|
|
1152
|
-
},
|
|
1153
|
-
"coverage": {
|
|
1154
|
-
"type": "number",
|
|
1155
|
-
"minimum": 0
|
|
1156
|
-
},
|
|
1157
|
-
"mutation": {
|
|
1158
|
-
"type": "number",
|
|
1159
|
-
"minimum": 0
|
|
1160
|
-
},
|
|
1161
|
-
"lint": {
|
|
1162
|
-
"type": "number",
|
|
1163
|
-
"minimum": 0
|
|
1164
|
-
},
|
|
1165
|
-
"lighthouse": {
|
|
1166
|
-
"type": "number",
|
|
1167
|
-
"minimum": 0
|
|
1168
|
-
},
|
|
1169
|
-
"bundle-size": {
|
|
1170
|
-
"type": "number",
|
|
1171
|
-
"minimum": 0
|
|
1172
|
-
},
|
|
1173
|
-
"duplication": {
|
|
1174
|
-
"type": "number",
|
|
1175
|
-
"minimum": 0
|
|
1176
|
-
}
|
|
1177
|
-
},
|
|
1178
|
-
"additionalProperties": false
|
|
1179
|
-
},
|
|
1180
|
-
"gateScoping": {
|
|
1181
|
-
"type": "object",
|
|
1182
|
-
"properties": {
|
|
1183
|
-
"scope": {
|
|
1184
|
-
"type": "string",
|
|
1185
|
-
"enum": ["diff", "full"]
|
|
1186
|
-
},
|
|
1187
|
-
"diffRef": {
|
|
1188
|
-
"type": "string",
|
|
1189
|
-
"minLength": 1
|
|
1190
|
-
}
|
|
1191
|
-
},
|
|
1192
|
-
"additionalProperties": false
|
|
1193
|
-
},
|
|
1194
|
-
"formatAutofix": {
|
|
1195
|
-
"type": "object",
|
|
1196
|
-
"description": "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.",
|
|
1197
|
-
"properties": {
|
|
1198
|
-
"timeoutMs": {
|
|
1199
|
-
"type": "integer",
|
|
1200
|
-
"minimum": 1
|
|
1201
|
-
}
|
|
1202
|
-
},
|
|
1203
|
-
"additionalProperties": false
|
|
1204
|
-
},
|
|
1205
|
-
"mergeWatch": {
|
|
1206
|
-
"type": "object",
|
|
1207
|
-
"description": "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.",
|
|
1208
|
-
"properties": {
|
|
1209
|
-
"mode": {
|
|
1210
|
-
"type": "string",
|
|
1211
|
-
"enum": ["sync", "async"],
|
|
1212
|
-
"default": "sync",
|
|
1213
|
-
"description": "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."
|
|
1214
|
-
},
|
|
1215
|
-
"intervalSeconds": {
|
|
1216
|
-
"type": "integer",
|
|
1217
|
-
"minimum": 1,
|
|
1218
|
-
"default": 30,
|
|
1219
|
-
"description": "Seconds between merge-wait polls. Default 30."
|
|
1220
|
-
},
|
|
1221
|
-
"maxWaitSeconds": {
|
|
1222
|
-
"type": "integer",
|
|
1223
|
-
"minimum": 1,
|
|
1224
|
-
"default": 300,
|
|
1225
|
-
"description": "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."
|
|
1226
|
-
},
|
|
1227
|
-
"maxBudgetSeconds": {
|
|
1228
|
-
"type": "integer",
|
|
1229
|
-
"minimum": 1,
|
|
1230
|
-
"default": 3600,
|
|
1231
|
-
"description": "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."
|
|
1232
|
-
},
|
|
1233
|
-
"updateAttempts": {
|
|
1234
|
-
"type": "integer",
|
|
1235
|
-
"minimum": 0,
|
|
1236
|
-
"default": 3,
|
|
1237
|
-
"description": "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."
|
|
1238
|
-
}
|
|
1239
|
-
},
|
|
1240
|
-
"additionalProperties": false
|
|
1241
|
-
},
|
|
1242
|
-
"review": {
|
|
1243
|
-
"type": "object",
|
|
1244
|
-
"description": "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.",
|
|
1245
|
-
"properties": {
|
|
1246
|
-
"lensDiffFloor": {
|
|
1247
|
-
"type": "integer",
|
|
1248
|
-
"minimum": 0,
|
|
1249
|
-
"default": 40,
|
|
1250
|
-
"description": "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."
|
|
1251
|
-
}
|
|
1252
|
-
},
|
|
1253
|
-
"additionalProperties": false
|
|
1254
|
-
},
|
|
1255
|
-
"quality": {
|
|
1256
|
-
"type": "object",
|
|
1257
|
-
"properties": {
|
|
1258
|
-
"gateScoping": {
|
|
1259
|
-
"$ref": "#/$defs/gateScoping"
|
|
1260
|
-
},
|
|
1261
|
-
"gates": {
|
|
1262
|
-
"$ref": "#/$defs/gates"
|
|
1263
|
-
},
|
|
1264
|
-
"formatAutofix": {
|
|
1265
|
-
"$ref": "#/$defs/formatAutofix"
|
|
1266
|
-
},
|
|
1267
|
-
"codingGuardrails": {
|
|
1268
|
-
"$ref": "#/$defs/codingGuardrails"
|
|
1269
|
-
},
|
|
1270
|
-
"autoRefresh": {
|
|
1271
|
-
"$ref": "#/$defs/autoRefresh"
|
|
1272
|
-
},
|
|
1273
|
-
"baselineEpsilon": {
|
|
1274
|
-
"$ref": "#/$defs/baselineEpsilon"
|
|
1275
|
-
},
|
|
1276
|
-
"requireBaselines": {
|
|
1277
|
-
"type": "boolean",
|
|
1278
|
-
"description": "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)."
|
|
1651
|
+
"additionalProperties": false
|
|
1279
1652
|
},
|
|
1280
|
-
"
|
|
1653
|
+
"mergeWatch": {
|
|
1281
1654
|
"type": "object",
|
|
1282
|
-
"description": "
|
|
1655
|
+
"description": "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.",
|
|
1283
1656
|
"properties": {
|
|
1284
|
-
"
|
|
1285
|
-
"type": "
|
|
1286
|
-
"
|
|
1287
|
-
"
|
|
1288
|
-
"type": "string"
|
|
1289
|
-
}
|
|
1657
|
+
"mode": {
|
|
1658
|
+
"type": "string",
|
|
1659
|
+
"enum": ["sync", "async"],
|
|
1660
|
+
"description": "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."
|
|
1290
1661
|
},
|
|
1291
|
-
"
|
|
1292
|
-
"type": "
|
|
1293
|
-
"
|
|
1294
|
-
"
|
|
1295
|
-
|
|
1296
|
-
}
|
|
1662
|
+
"intervalSeconds": {
|
|
1663
|
+
"type": "integer",
|
|
1664
|
+
"minimum": 1,
|
|
1665
|
+
"description": "Seconds between merge-wait polls. Default 30.",
|
|
1666
|
+
"default": 30
|
|
1297
1667
|
},
|
|
1298
|
-
"
|
|
1299
|
-
"type": "
|
|
1300
|
-
"
|
|
1668
|
+
"maxWaitSeconds": {
|
|
1669
|
+
"type": "integer",
|
|
1670
|
+
"minimum": 1,
|
|
1671
|
+
"description": "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."
|
|
1672
|
+
},
|
|
1673
|
+
"maxBudgetSeconds": {
|
|
1674
|
+
"type": "integer",
|
|
1675
|
+
"minimum": 1,
|
|
1676
|
+
"description": "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.",
|
|
1677
|
+
"default": 3600
|
|
1678
|
+
},
|
|
1679
|
+
"updateAttempts": {
|
|
1680
|
+
"type": "integer",
|
|
1681
|
+
"minimum": 0,
|
|
1682
|
+
"description": "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."
|
|
1301
1683
|
}
|
|
1302
1684
|
},
|
|
1303
1685
|
"additionalProperties": false
|
|
1304
|
-
}
|
|
1305
|
-
},
|
|
1306
|
-
"additionalProperties": false
|
|
1307
|
-
},
|
|
1308
|
-
"refactorStage": {
|
|
1309
|
-
"type": "object",
|
|
1310
|
-
"description": "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.",
|
|
1311
|
-
"properties": {
|
|
1312
|
-
"enabled": {
|
|
1313
|
-
"type": "boolean",
|
|
1314
|
-
"default": false,
|
|
1315
|
-
"description": "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."
|
|
1316
|
-
}
|
|
1317
|
-
},
|
|
1318
|
-
"additionalProperties": false
|
|
1319
|
-
},
|
|
1320
|
-
"acceptanceEval": {
|
|
1321
|
-
"type": "object",
|
|
1322
|
-
"description": "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).",
|
|
1323
|
-
"properties": {
|
|
1324
|
-
"maxRounds": {
|
|
1325
|
-
"type": "integer",
|
|
1326
|
-
"minimum": 1,
|
|
1327
|
-
"description": "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)."
|
|
1328
|
-
},
|
|
1329
|
-
"clusterCeiling": {
|
|
1330
|
-
"type": "integer",
|
|
1331
|
-
"minimum": 1,
|
|
1332
|
-
"description": "Epic #4475 (M4-B). Max acceptance criteria one single-delivery acceptance critic scores in a single fresh-context pass. Single delivery clusters the Epic ## Acceptance Table ACs into ceil(totalACs / clusterCeiling) groups and spawns one maker-blind critic per cluster, restoring the distributed acceptance coverage the per-Story critic fan-out gave for free. Default 4; clamped into [1, 8] by lib/config/acceptance-eval.js so a large value cannot collapse the fan-out to a single diluted critic. Ignored on the fan-out route."
|
|
1333
|
-
}
|
|
1334
|
-
},
|
|
1335
|
-
"additionalProperties": false
|
|
1336
|
-
},
|
|
1337
|
-
"delivery": {
|
|
1338
|
-
"type": "object",
|
|
1339
|
-
"properties": {
|
|
1340
|
-
"execution": {
|
|
1341
|
-
"$ref": "#/$defs/execution"
|
|
1342
|
-
},
|
|
1343
|
-
"lease": {
|
|
1344
|
-
"$ref": "#/$defs/lease"
|
|
1345
|
-
},
|
|
1346
|
-
"docsFreshness": {
|
|
1347
|
-
"$ref": "#/$defs/docsFreshness"
|
|
1348
|
-
},
|
|
1349
|
-
"tempRetention": {
|
|
1350
|
-
"$ref": "#/$defs/tempRetention"
|
|
1351
|
-
},
|
|
1352
|
-
"deliverRunner": {
|
|
1353
|
-
"$ref": "#/$defs/deliverRunner"
|
|
1354
|
-
},
|
|
1355
|
-
"worktreeIsolation": {
|
|
1356
|
-
"$ref": "#/$defs/worktreeIsolation"
|
|
1357
|
-
},
|
|
1358
|
-
"signals": {
|
|
1359
|
-
"$ref": "#/$defs/signals"
|
|
1360
|
-
},
|
|
1361
|
-
"quality": {
|
|
1362
|
-
"$ref": "#/$defs/quality"
|
|
1363
|
-
},
|
|
1364
|
-
"mergeWatch": {
|
|
1365
|
-
"$ref": "#/$defs/mergeWatch"
|
|
1366
1686
|
},
|
|
1367
1687
|
"codeReview": {
|
|
1368
|
-
"
|
|
1688
|
+
"type": "object",
|
|
1689
|
+
"description": "Review-provider chain plus bounded-retry knobs for the /deliver code-review ceremony.",
|
|
1690
|
+
"properties": {
|
|
1691
|
+
"providers": {
|
|
1692
|
+
"type": "array",
|
|
1693
|
+
"items": {
|
|
1694
|
+
"type": "object",
|
|
1695
|
+
"required": ["name"],
|
|
1696
|
+
"properties": {
|
|
1697
|
+
"name": {
|
|
1698
|
+
"type": "string",
|
|
1699
|
+
"enum": [
|
|
1700
|
+
"native",
|
|
1701
|
+
"codex",
|
|
1702
|
+
"security-review",
|
|
1703
|
+
"ultrareview"
|
|
1704
|
+
],
|
|
1705
|
+
"description": "Registered provider key. Inline: native, codex, security-review. Manual-prompt: ultrareview."
|
|
1706
|
+
},
|
|
1707
|
+
"scopes": {
|
|
1708
|
+
"type": "array",
|
|
1709
|
+
"items": {
|
|
1710
|
+
"type": "string",
|
|
1711
|
+
"enum": ["story", "epic"]
|
|
1712
|
+
},
|
|
1713
|
+
"description": "Invocation scopes this entry fires on. Default (omitted) is both."
|
|
1714
|
+
},
|
|
1715
|
+
"optional": {
|
|
1716
|
+
"type": "boolean",
|
|
1717
|
+
"default": false,
|
|
1718
|
+
"description": "When true, construction failure (host missing CLI/plugin) is logged and the entry is skipped instead of hard-failing the chain. Use for cross-runtime portability."
|
|
1719
|
+
},
|
|
1720
|
+
"manualPrompt": {
|
|
1721
|
+
"type": "boolean",
|
|
1722
|
+
"default": false,
|
|
1723
|
+
"description": "When true, the entry is loaded from the manual-prompt registry: contributes a one-line operator suggestion via `renderPrompt()` instead of running a review via `runReview()`. Does not affect severity counts or `halted`."
|
|
1724
|
+
},
|
|
1725
|
+
"when": {
|
|
1726
|
+
"type": "object",
|
|
1727
|
+
"description": "Optional label-gated invocation predicate. Evaluated against the ticket's current labels at invocation time; when false, the entry is silently skipped for this run.",
|
|
1728
|
+
"properties": {
|
|
1729
|
+
"label": {
|
|
1730
|
+
"type": "string",
|
|
1731
|
+
"minLength": 1,
|
|
1732
|
+
"description": "Single label that MUST be present on the ticket."
|
|
1733
|
+
},
|
|
1734
|
+
"labelAny": {
|
|
1735
|
+
"type": "array",
|
|
1736
|
+
"items": {
|
|
1737
|
+
"type": "string",
|
|
1738
|
+
"minLength": 1
|
|
1739
|
+
},
|
|
1740
|
+
"minItems": 1,
|
|
1741
|
+
"description": "Run when ANY of the listed labels is present."
|
|
1742
|
+
}
|
|
1743
|
+
},
|
|
1744
|
+
"additionalProperties": false
|
|
1745
|
+
}
|
|
1746
|
+
},
|
|
1747
|
+
"additionalProperties": false
|
|
1748
|
+
},
|
|
1749
|
+
"description": "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.",
|
|
1750
|
+
"default": [
|
|
1751
|
+
{
|
|
1752
|
+
"name": "native"
|
|
1753
|
+
},
|
|
1754
|
+
{
|
|
1755
|
+
"name": "security-review",
|
|
1756
|
+
"scopes": ["story"],
|
|
1757
|
+
"optional": true
|
|
1758
|
+
},
|
|
1759
|
+
{
|
|
1760
|
+
"name": "ultrareview",
|
|
1761
|
+
"scopes": ["story"],
|
|
1762
|
+
"manualPrompt": true,
|
|
1763
|
+
"when": {
|
|
1764
|
+
"label": "risk::high"
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
]
|
|
1768
|
+
},
|
|
1769
|
+
"providerConfig": {
|
|
1770
|
+
"type": "object",
|
|
1771
|
+
"additionalProperties": true,
|
|
1772
|
+
"description": "Optional escape hatch for adapter-specific configuration. No documented keys in Epic #2815; reserved so future adapters can be configured without another schema migration."
|
|
1773
|
+
},
|
|
1774
|
+
"maxFixAttempts": {
|
|
1775
|
+
"type": "integer",
|
|
1776
|
+
"minimum": 0,
|
|
1777
|
+
"description": "Maximum auto-fix retry attempts per finding in /deliver Phase 5 (code-review). 0 disables auto-fix. Default 3.",
|
|
1778
|
+
"default": 3
|
|
1779
|
+
},
|
|
1780
|
+
"maxFixScopeFiles": {
|
|
1781
|
+
"type": "integer",
|
|
1782
|
+
"minimum": 1,
|
|
1783
|
+
"description": "Maximum file count a single auto-fix may modify before escalating to agent::blocked. Default 5.",
|
|
1784
|
+
"default": 5
|
|
1785
|
+
},
|
|
1786
|
+
"autoFixSeverity": {
|
|
1787
|
+
"type": "string",
|
|
1788
|
+
"enum": ["high", "medium"],
|
|
1789
|
+
"description": "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.",
|
|
1790
|
+
"default": "medium"
|
|
1791
|
+
}
|
|
1792
|
+
},
|
|
1793
|
+
"additionalProperties": false
|
|
1369
1794
|
},
|
|
1370
1795
|
"review": {
|
|
1371
|
-
"
|
|
1796
|
+
"type": "object",
|
|
1797
|
+
"description": "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.",
|
|
1798
|
+
"properties": {
|
|
1799
|
+
"lensDiffFloor": {
|
|
1800
|
+
"type": "integer",
|
|
1801
|
+
"minimum": 0,
|
|
1802
|
+
"description": "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."
|
|
1803
|
+
}
|
|
1804
|
+
},
|
|
1805
|
+
"additionalProperties": false
|
|
1372
1806
|
},
|
|
1373
1807
|
"refactorStage": {
|
|
1374
|
-
"
|
|
1808
|
+
"type": "object",
|
|
1809
|
+
"description": "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.",
|
|
1810
|
+
"properties": {
|
|
1811
|
+
"enabled": {
|
|
1812
|
+
"type": "boolean",
|
|
1813
|
+
"description": "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.",
|
|
1814
|
+
"default": false
|
|
1815
|
+
}
|
|
1816
|
+
},
|
|
1817
|
+
"additionalProperties": false
|
|
1375
1818
|
},
|
|
1376
1819
|
"acceptanceEval": {
|
|
1377
|
-
"
|
|
1820
|
+
"type": "object",
|
|
1821
|
+
"description": "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).",
|
|
1822
|
+
"properties": {
|
|
1823
|
+
"maxRounds": {
|
|
1824
|
+
"type": "integer",
|
|
1825
|
+
"minimum": 1,
|
|
1826
|
+
"description": "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).",
|
|
1827
|
+
"default": 2
|
|
1828
|
+
}
|
|
1829
|
+
},
|
|
1830
|
+
"additionalProperties": false
|
|
1831
|
+
},
|
|
1832
|
+
"feedbackLoop": {
|
|
1833
|
+
"type": "object",
|
|
1834
|
+
"description": "Opt-out toggles for the close-time auto-file graduators. All default to auto-filing on.",
|
|
1835
|
+
"properties": {
|
|
1836
|
+
"auditResultsAutoFile": {
|
|
1837
|
+
"type": "boolean",
|
|
1838
|
+
"description": "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.",
|
|
1839
|
+
"default": true
|
|
1840
|
+
},
|
|
1841
|
+
"retroProposals": {
|
|
1842
|
+
"type": "boolean",
|
|
1843
|
+
"description": "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.",
|
|
1844
|
+
"default": true
|
|
1845
|
+
},
|
|
1846
|
+
"frictionWindowDays": {
|
|
1847
|
+
"type": "integer",
|
|
1848
|
+
"minimum": 1,
|
|
1849
|
+
"description": "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."
|
|
1850
|
+
}
|
|
1851
|
+
},
|
|
1852
|
+
"additionalProperties": false
|
|
1853
|
+
},
|
|
1854
|
+
"auditToStories": {
|
|
1855
|
+
"type": "object",
|
|
1856
|
+
"description": "Knobs for the `/audit-to-stories` unattended (`--auto`) sweep (Story #4626).",
|
|
1857
|
+
"properties": {
|
|
1858
|
+
"severityFloor": {
|
|
1859
|
+
"type": "string",
|
|
1860
|
+
"enum": ["critical", "high", "medium", "low", "all"],
|
|
1861
|
+
"description": "Minimum severity a finding must meet to be proposed as a Story on an unattended `/audit-to-stories --auto` sweep (Story #4626). Default high."
|
|
1862
|
+
},
|
|
1863
|
+
"autoComment": {
|
|
1864
|
+
"type": "boolean",
|
|
1865
|
+
"description": "When true (default), `/audit-to-stories --auto` posts a re-detected comment on an already-open matched Issue instead of silently skipping it."
|
|
1866
|
+
}
|
|
1867
|
+
},
|
|
1868
|
+
"additionalProperties": false
|
|
1378
1869
|
},
|
|
1379
1870
|
"ci": {
|
|
1380
1871
|
"type": "object",
|
|
1872
|
+
"description": "CI-aware delivery namespace (Story #4356, Epic #4355): the merge/CI watch poll loop and the merge posture.",
|
|
1381
1873
|
"properties": {
|
|
1382
1874
|
"watch": {
|
|
1383
1875
|
"type": "object",
|
|
@@ -1385,20 +1877,27 @@
|
|
|
1385
1877
|
"properties": {
|
|
1386
1878
|
"pollIntervalMs": {
|
|
1387
1879
|
"type": "integer",
|
|
1388
|
-
"minimum": 1
|
|
1880
|
+
"minimum": 1,
|
|
1881
|
+
"description": "Milliseconds between check probes.",
|
|
1882
|
+
"default": 10000
|
|
1389
1883
|
},
|
|
1390
1884
|
"maxPolls": {
|
|
1391
1885
|
"type": "integer",
|
|
1392
|
-
"minimum": 1
|
|
1886
|
+
"minimum": 1,
|
|
1887
|
+
"description": "Total probes before the watcher gives up on one invocation.",
|
|
1888
|
+
"default": 180
|
|
1393
1889
|
},
|
|
1394
1890
|
"maxResumes": {
|
|
1395
1891
|
"type": "integer",
|
|
1396
|
-
"minimum": 0
|
|
1892
|
+
"minimum": 0,
|
|
1893
|
+
"description": "How many times the watcher may resume after a transient stall. 0 disables resuming.",
|
|
1894
|
+
"default": 3
|
|
1397
1895
|
},
|
|
1398
1896
|
"attachWindowMs": {
|
|
1399
1897
|
"type": "integer",
|
|
1400
1898
|
"minimum": 1,
|
|
1401
|
-
"description": "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)."
|
|
1899
|
+
"description": "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).",
|
|
1900
|
+
"default": 1200000
|
|
1402
1901
|
}
|
|
1403
1902
|
},
|
|
1404
1903
|
"additionalProperties": false
|
|
@@ -1406,7 +1905,8 @@
|
|
|
1406
1905
|
"autoMerge": {
|
|
1407
1906
|
"type": "string",
|
|
1408
1907
|
"enum": ["trust-ci", "strict"],
|
|
1409
|
-
"description": "Story #4356 (Epic #4355). Merge posture. 'trust-ci' (default) merges once required checks pass; 'strict' additionally requires a clean review gate."
|
|
1908
|
+
"description": "Story #4356 (Epic #4355). Merge posture. 'trust-ci' (default) merges once required checks pass; 'strict' additionally requires a clean review gate.",
|
|
1909
|
+
"default": "trust-ci"
|
|
1410
1910
|
}
|
|
1411
1911
|
},
|
|
1412
1912
|
"additionalProperties": false
|
|
@@ -1417,61 +1917,26 @@
|
|
|
1417
1917
|
"properties": {
|
|
1418
1918
|
"roleScopedAgents": {
|
|
1419
1919
|
"type": "boolean",
|
|
1420
|
-
"description": "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
|
|
1920
|
+
"description": "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.",
|
|
1921
|
+
"default": true
|
|
1421
1922
|
},
|
|
1422
1923
|
"freshCriticSampleRate": {
|
|
1423
1924
|
"type": "number",
|
|
1424
1925
|
"minimum": 0,
|
|
1425
1926
|
"maximum": 1,
|
|
1426
|
-
"description": "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.
|
|
1927
|
+
"description": "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).",
|
|
1928
|
+
"default": 0.2
|
|
1427
1929
|
},
|
|
1428
1930
|
"ceremonyProfile": {
|
|
1429
1931
|
"type": "string",
|
|
1430
1932
|
"enum": ["minimal", "standard", "strict"],
|
|
1431
|
-
"description": "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."
|
|
1933
|
+
"description": "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.",
|
|
1934
|
+
"default": "standard"
|
|
1432
1935
|
},
|
|
1433
1936
|
"closeAndLand": {
|
|
1434
1937
|
"type": "boolean",
|
|
1435
|
-
"description": "When true (default), single-story-close lands through merge in one close. Opt out per-run with --no-wait-merge."
|
|
1436
|
-
|
|
1437
|
-
},
|
|
1438
|
-
"additionalProperties": false
|
|
1439
|
-
},
|
|
1440
|
-
"feedbackLoop": {
|
|
1441
|
-
"type": "object",
|
|
1442
|
-
"properties": {
|
|
1443
|
-
"auditResultsAutoFile": {
|
|
1444
|
-
"type": "boolean",
|
|
1445
|
-
"default": true,
|
|
1446
|
-
"description": "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."
|
|
1447
|
-
},
|
|
1448
|
-
"retroProposals": {
|
|
1449
|
-
"type": "boolean",
|
|
1450
|
-
"default": true,
|
|
1451
|
-
"description": "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."
|
|
1452
|
-
},
|
|
1453
|
-
"frictionWindowDays": {
|
|
1454
|
-
"type": "integer",
|
|
1455
|
-
"minimum": 1,
|
|
1456
|
-
"default": 30,
|
|
1457
|
-
"description": "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."
|
|
1458
|
-
}
|
|
1459
|
-
},
|
|
1460
|
-
"additionalProperties": false
|
|
1461
|
-
},
|
|
1462
|
-
"auditToStories": {
|
|
1463
|
-
"type": "object",
|
|
1464
|
-
"properties": {
|
|
1465
|
-
"severityFloor": {
|
|
1466
|
-
"type": "string",
|
|
1467
|
-
"enum": ["critical", "high", "medium", "low", "all"],
|
|
1468
|
-
"default": "high",
|
|
1469
|
-
"description": "Minimum severity a finding must meet to be proposed as a Story on an unattended `/audit-to-stories --auto` sweep (Story #4626). Default high."
|
|
1470
|
-
},
|
|
1471
|
-
"autoComment": {
|
|
1472
|
-
"type": "boolean",
|
|
1473
|
-
"default": true,
|
|
1474
|
-
"description": "When true (default), `/audit-to-stories --auto` posts a re-detected comment on an already-open matched Issue instead of silently skipping it."
|
|
1938
|
+
"description": "When true (default), single-story-close lands through merge in one close. Opt out per-run with --no-wait-merge.",
|
|
1939
|
+
"default": true
|
|
1475
1940
|
}
|
|
1476
1941
|
},
|
|
1477
1942
|
"additionalProperties": false
|
|
@@ -1479,182 +1944,256 @@
|
|
|
1479
1944
|
},
|
|
1480
1945
|
"additionalProperties": false
|
|
1481
1946
|
},
|
|
1482
|
-
"
|
|
1483
|
-
"oneOf": [
|
|
1484
|
-
{
|
|
1485
|
-
"type": "object",
|
|
1486
|
-
"required": ["urlTemplate"],
|
|
1487
|
-
"additionalProperties": false,
|
|
1488
|
-
"properties": {
|
|
1489
|
-
"urlTemplate": {
|
|
1490
|
-
"allOf": [
|
|
1491
|
-
{
|
|
1492
|
-
"$ref": "#/$defs/safeString"
|
|
1493
|
-
},
|
|
1494
|
-
{
|
|
1495
|
-
"minLength": 1
|
|
1496
|
-
}
|
|
1497
|
-
]
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
},
|
|
1501
|
-
{
|
|
1502
|
-
"type": "object",
|
|
1503
|
-
"required": ["skill"],
|
|
1504
|
-
"additionalProperties": false,
|
|
1505
|
-
"properties": {
|
|
1506
|
-
"skill": {
|
|
1507
|
-
"allOf": [
|
|
1508
|
-
{
|
|
1509
|
-
"$ref": "#/$defs/safeString"
|
|
1510
|
-
},
|
|
1511
|
-
{
|
|
1512
|
-
"minLength": 1
|
|
1513
|
-
}
|
|
1514
|
-
]
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
]
|
|
1519
|
-
},
|
|
1520
|
-
"qaEnvironments": {
|
|
1521
|
-
"description": "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 `qaSignInSeam`), 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`.",
|
|
1947
|
+
"qa": {
|
|
1522
1948
|
"type": "object",
|
|
1523
|
-
"
|
|
1524
|
-
"
|
|
1525
|
-
"
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
"baseUrl": {
|
|
1530
|
-
"allOf": [
|
|
1531
|
-
{
|
|
1532
|
-
"$ref": "#/$defs/safeString"
|
|
1533
|
-
},
|
|
1534
|
-
{
|
|
1535
|
-
"minLength": 1
|
|
1536
|
-
}
|
|
1537
|
-
]
|
|
1949
|
+
"description": "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.",
|
|
1950
|
+
"properties": {
|
|
1951
|
+
"featureRoot": {
|
|
1952
|
+
"type": "string",
|
|
1953
|
+
"not": {
|
|
1954
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
1538
1955
|
},
|
|
1539
|
-
"
|
|
1540
|
-
|
|
1956
|
+
"minLength": 1,
|
|
1957
|
+
"description": "Directory holding the Gherkin feature files the QA sweep drives.",
|
|
1958
|
+
"default": "tests/features"
|
|
1959
|
+
},
|
|
1960
|
+
"fixturesManifest": {
|
|
1961
|
+
"type": "string",
|
|
1962
|
+
"not": {
|
|
1963
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
1541
1964
|
},
|
|
1542
|
-
"
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
},
|
|
1548
|
-
"qaPersonas": {
|
|
1549
|
-
"description": "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`/credential seam.",
|
|
1550
|
-
"oneOf": [
|
|
1551
|
-
{
|
|
1552
|
-
"type": "array",
|
|
1553
|
-
"minItems": 1,
|
|
1554
|
-
"items": {
|
|
1555
|
-
"allOf": [
|
|
1556
|
-
{
|
|
1557
|
-
"$ref": "#/$defs/safeString"
|
|
1558
|
-
},
|
|
1559
|
-
{
|
|
1560
|
-
"minLength": 1
|
|
1561
|
-
}
|
|
1562
|
-
]
|
|
1563
|
-
}
|
|
1965
|
+
"minLength": 1,
|
|
1966
|
+
"description": "Path to the persona/fixture manifest the harness seeds from.",
|
|
1967
|
+
"default": "tests/fixtures/personas.json"
|
|
1564
1968
|
},
|
|
1565
|
-
{
|
|
1969
|
+
"environments": {
|
|
1566
1970
|
"type": "object",
|
|
1971
|
+
"description": "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`.",
|
|
1972
|
+
"default": {
|
|
1973
|
+
"local": {
|
|
1974
|
+
"baseUrl": "http://localhost:3000",
|
|
1975
|
+
"signInSeam": {
|
|
1976
|
+
"urlTemplate": "/dev/sign-in-as/{persona}"
|
|
1977
|
+
}
|
|
1978
|
+
},
|
|
1979
|
+
"staging": {
|
|
1980
|
+
"baseUrl": "https://staging.example.test",
|
|
1981
|
+
"signInSeam": {
|
|
1982
|
+
"skill": "stack/qa/sign-in"
|
|
1983
|
+
},
|
|
1984
|
+
"allowWrites": false
|
|
1985
|
+
}
|
|
1986
|
+
},
|
|
1567
1987
|
"minProperties": 1,
|
|
1568
1988
|
"additionalProperties": {
|
|
1569
|
-
"
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
"
|
|
1573
|
-
"
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1989
|
+
"type": "object",
|
|
1990
|
+
"properties": {
|
|
1991
|
+
"baseUrl": {
|
|
1992
|
+
"type": "string",
|
|
1993
|
+
"not": {
|
|
1994
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
1995
|
+
},
|
|
1996
|
+
"minLength": 1
|
|
1997
|
+
},
|
|
1998
|
+
"signInSeam": {
|
|
1999
|
+
"oneOf": [
|
|
2000
|
+
{
|
|
2001
|
+
"type": "object",
|
|
2002
|
+
"properties": {
|
|
2003
|
+
"urlTemplate": {
|
|
2004
|
+
"type": "string",
|
|
2005
|
+
"not": {
|
|
2006
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
2007
|
+
},
|
|
1581
2008
|
"minLength": 1
|
|
1582
2009
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
"$ref": "#/$defs/safeString"
|
|
1596
|
-
},
|
|
1597
|
-
{
|
|
2010
|
+
},
|
|
2011
|
+
"required": ["urlTemplate"],
|
|
2012
|
+
"additionalProperties": false
|
|
2013
|
+
},
|
|
2014
|
+
{
|
|
2015
|
+
"type": "object",
|
|
2016
|
+
"properties": {
|
|
2017
|
+
"skill": {
|
|
2018
|
+
"type": "string",
|
|
2019
|
+
"not": {
|
|
2020
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
2021
|
+
},
|
|
1598
2022
|
"minLength": 1
|
|
1599
2023
|
}
|
|
1600
|
-
|
|
2024
|
+
},
|
|
2025
|
+
"required": ["skill"],
|
|
2026
|
+
"additionalProperties": false
|
|
1601
2027
|
}
|
|
1602
|
-
|
|
2028
|
+
]
|
|
2029
|
+
},
|
|
2030
|
+
"allowWrites": {
|
|
2031
|
+
"type": "boolean"
|
|
1603
2032
|
}
|
|
1604
|
-
|
|
2033
|
+
},
|
|
2034
|
+
"required": ["baseUrl", "signInSeam"],
|
|
2035
|
+
"additionalProperties": false
|
|
1605
2036
|
}
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
"additionalProperties": false,
|
|
1613
|
-
"properties": {
|
|
1614
|
-
"featureRoot": {
|
|
1615
|
-
"allOf": [
|
|
1616
|
-
{
|
|
1617
|
-
"$ref": "#/$defs/safeString"
|
|
2037
|
+
},
|
|
2038
|
+
"personas": {
|
|
2039
|
+
"description": "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.",
|
|
2040
|
+
"default": {
|
|
2041
|
+
"admin": {
|
|
2042
|
+
"credentialRef": "QA_ADMIN_CREDENTIAL"
|
|
1618
2043
|
},
|
|
1619
|
-
{
|
|
1620
|
-
"
|
|
2044
|
+
"member": {
|
|
2045
|
+
"signInSkill": "stack/qa/sign-in-member"
|
|
1621
2046
|
}
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
"fixturesManifest": {
|
|
1625
|
-
"allOf": [
|
|
2047
|
+
},
|
|
2048
|
+
"oneOf": [
|
|
1626
2049
|
{
|
|
1627
|
-
"
|
|
2050
|
+
"type": "array",
|
|
2051
|
+
"minItems": 1,
|
|
2052
|
+
"items": {
|
|
2053
|
+
"type": "string",
|
|
2054
|
+
"not": {
|
|
2055
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
2056
|
+
},
|
|
2057
|
+
"minLength": 1
|
|
2058
|
+
}
|
|
1628
2059
|
},
|
|
1629
2060
|
{
|
|
1630
|
-
"
|
|
2061
|
+
"type": "object",
|
|
2062
|
+
"minProperties": 1,
|
|
2063
|
+
"additionalProperties": {
|
|
2064
|
+
"oneOf": [
|
|
2065
|
+
{
|
|
2066
|
+
"type": "object",
|
|
2067
|
+
"properties": {
|
|
2068
|
+
"credentialRef": {
|
|
2069
|
+
"type": "string",
|
|
2070
|
+
"not": {
|
|
2071
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
2072
|
+
},
|
|
2073
|
+
"minLength": 1
|
|
2074
|
+
}
|
|
2075
|
+
},
|
|
2076
|
+
"required": ["credentialRef"],
|
|
2077
|
+
"additionalProperties": false
|
|
2078
|
+
},
|
|
2079
|
+
{
|
|
2080
|
+
"type": "object",
|
|
2081
|
+
"properties": {
|
|
2082
|
+
"signInSkill": {
|
|
2083
|
+
"type": "string",
|
|
2084
|
+
"not": {
|
|
2085
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
2086
|
+
},
|
|
2087
|
+
"minLength": 1
|
|
2088
|
+
}
|
|
2089
|
+
},
|
|
2090
|
+
"required": ["signInSkill"],
|
|
2091
|
+
"additionalProperties": false
|
|
2092
|
+
}
|
|
2093
|
+
]
|
|
2094
|
+
}
|
|
1631
2095
|
}
|
|
1632
2096
|
]
|
|
1633
2097
|
},
|
|
1634
|
-
"
|
|
1635
|
-
"
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
2098
|
+
"gherkinLint": {
|
|
2099
|
+
"type": "object",
|
|
2100
|
+
"description": "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.",
|
|
2101
|
+
"default": {
|
|
2102
|
+
"scopes": {
|
|
2103
|
+
"web": {
|
|
2104
|
+
"featureRoots": ["apps/web/tests/features"],
|
|
2105
|
+
"stepRoots": ["apps/web/tests/steps"]
|
|
2106
|
+
}
|
|
2107
|
+
},
|
|
2108
|
+
"exemptionTags": ["@skip"],
|
|
2109
|
+
"stepWaivers": []
|
|
2110
|
+
},
|
|
2111
|
+
"properties": {
|
|
2112
|
+
"scopes": {
|
|
2113
|
+
"type": "object",
|
|
2114
|
+
"description": "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.",
|
|
2115
|
+
"minProperties": 1,
|
|
2116
|
+
"additionalProperties": {
|
|
2117
|
+
"type": "object",
|
|
2118
|
+
"properties": {
|
|
2119
|
+
"featureRoots": {
|
|
2120
|
+
"type": "array",
|
|
2121
|
+
"minItems": 1,
|
|
2122
|
+
"items": {
|
|
2123
|
+
"type": "string",
|
|
2124
|
+
"not": {
|
|
2125
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
2126
|
+
},
|
|
2127
|
+
"minLength": 1
|
|
2128
|
+
},
|
|
2129
|
+
"description": "Directories holding the `.feature` files of this scope, walked recursively."
|
|
2130
|
+
},
|
|
2131
|
+
"stepRoots": {
|
|
2132
|
+
"type": "array",
|
|
2133
|
+
"minItems": 1,
|
|
2134
|
+
"items": {
|
|
2135
|
+
"type": "string",
|
|
2136
|
+
"not": {
|
|
2137
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
2138
|
+
},
|
|
2139
|
+
"minLength": 1
|
|
2140
|
+
},
|
|
2141
|
+
"description": "Directories holding the step definitions of this scope, walked recursively. Resolving zero definitions here is a fail-closed error, not a clean run."
|
|
2142
|
+
}
|
|
2143
|
+
},
|
|
2144
|
+
"required": ["featureRoots", "stepRoots"],
|
|
2145
|
+
"additionalProperties": false
|
|
2146
|
+
}
|
|
2147
|
+
},
|
|
2148
|
+
"exemptionTags": {
|
|
2149
|
+
"type": "array",
|
|
2150
|
+
"items": {
|
|
2151
|
+
"type": "string",
|
|
2152
|
+
"not": {
|
|
2153
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
2154
|
+
},
|
|
2155
|
+
"minLength": 1
|
|
2156
|
+
},
|
|
2157
|
+
"description": "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\"].",
|
|
2158
|
+
"default": ["@skip"]
|
|
2159
|
+
},
|
|
2160
|
+
"stepWaivers": {
|
|
2161
|
+
"type": "array",
|
|
2162
|
+
"items": {
|
|
2163
|
+
"type": "string",
|
|
2164
|
+
"minLength": 1
|
|
2165
|
+
},
|
|
2166
|
+
"description": "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: [].",
|
|
2167
|
+
"default": []
|
|
2168
|
+
}
|
|
2169
|
+
},
|
|
2170
|
+
"required": ["scopes"],
|
|
2171
|
+
"additionalProperties": false
|
|
1639
2172
|
},
|
|
1640
2173
|
"consoleAllowlist": {
|
|
1641
2174
|
"type": "array",
|
|
1642
2175
|
"items": {
|
|
1643
2176
|
"type": "string",
|
|
2177
|
+
"not": {
|
|
2178
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
2179
|
+
},
|
|
1644
2180
|
"minLength": 1
|
|
1645
|
-
}
|
|
2181
|
+
},
|
|
2182
|
+
"description": "Console-message substrings the QA run tolerates instead of reporting as a finding (framework dev-mode chatter).",
|
|
2183
|
+
"default": ["Download the React DevTools", "[HMR]"]
|
|
1646
2184
|
},
|
|
1647
2185
|
"designTokens": {
|
|
1648
|
-
"
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
]
|
|
2186
|
+
"type": "string",
|
|
2187
|
+
"not": {
|
|
2188
|
+
"pattern": "([;&|`]|\\$\\()"
|
|
2189
|
+
},
|
|
2190
|
+
"minLength": 1,
|
|
2191
|
+
"description": "Path to the design-token SSOT the UX/UI lens checks rendered styles against.",
|
|
2192
|
+
"default": "src/styles/tokens.css"
|
|
1656
2193
|
}
|
|
1657
|
-
}
|
|
2194
|
+
},
|
|
2195
|
+
"additionalProperties": false
|
|
1658
2196
|
}
|
|
1659
|
-
}
|
|
2197
|
+
},
|
|
2198
|
+
"additionalProperties": false
|
|
1660
2199
|
}
|