mandrel 2.30.0 → 2.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +13 -17
- package/.agents/agents/acceptance-critic.md +1 -2
- package/.agents/docs/SDLC.md +4 -4
- package/.agents/docs/agentrc-reference.json +61 -57
- package/.agents/docs/configuration.md +274 -227
- package/.agents/docs/execution-reference.md +13 -14
- package/.agents/docs/quality-gates.md +195 -23
- package/.agents/instructions.md +2 -5
- package/.agents/rules/ci-remediation.md +44 -1
- package/.agents/rules/git-conventions-reference.md +27 -27
- package/.agents/rules/git-conventions.md +4 -2
- package/.agents/rules/known-tooling-behavior.md +66 -30
- package/.agents/rules/testing-standards.md +35 -71
- package/.agents/runtime-deps.json +0 -1
- package/.agents/schemas/agentrc.schema.json +1939 -1400
- package/.agents/schemas/lifecycle/README.md +21 -14
- package/.agents/schemas/lifecycle/ledger-record.schema.json +76 -22
- package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
- package/.agents/scripts/README.md +7 -29
- package/.agents/scripts/apply-quality-bootstrap.js +27 -34
- package/.agents/scripts/bootstrap.js +28 -26
- package/.agents/scripts/check-baseline-drift.js +73 -13
- package/.agents/scripts/check-baseline-scope.js +362 -0
- package/.agents/scripts/check-dead-exports.js +9 -1
- package/.agents/scripts/check-gherkin-corpus.js +508 -0
- package/.agents/scripts/check-knip-entries.js +136 -0
- package/.agents/scripts/check-lifecycle-lint.js +36 -112
- package/.agents/scripts/check-schema-references.js +1 -1
- package/.agents/scripts/diagnose-friction.js +7 -4
- package/.agents/scripts/generate-config-docs.js +263 -171
- package/.agents/scripts/install-matrix-assert.js +0 -1
- package/.agents/scripts/lib/ITicketingProvider.js +0 -58
- package/.agents/scripts/lib/audit-baselines/staleness.js +6 -6
- package/.agents/scripts/lib/audit-baselines/trend.js +7 -8
- package/.agents/scripts/lib/audit-baselines/weights.js +4 -5
- package/.agents/scripts/lib/audit-suite/checklist-threading.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +0 -1
- package/.agents/scripts/lib/baselines/envelope.js +41 -60
- package/.agents/scripts/lib/baselines/git-base.js +30 -37
- package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +103 -0
- package/.agents/scripts/lib/baselines/kinds/_crap-read.js +150 -0
- package/.agents/scripts/lib/baselines/kinds/crap.js +25 -65
- package/.agents/scripts/lib/baselines/orphan-pruner.js +233 -0
- package/.agents/scripts/lib/baselines/refresh-service.js +6 -8
- package/.agents/scripts/lib/baselines/scope-assert.js +223 -0
- package/.agents/scripts/lib/baselines/scope-inventory.js +314 -0
- package/.agents/scripts/lib/bdd-step-index.js +326 -0
- package/.agents/scripts/lib/bootstrap/install-ledger.js +5 -3
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +4 -6
- package/.agents/scripts/lib/bootstrap/manifest.js +17 -40
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +12 -59
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +62 -2
- package/.agents/scripts/lib/checks/loop-health.js +9 -37
- package/.agents/scripts/lib/child-exec.js +193 -0
- package/.agents/scripts/lib/cli/standard-args.js +1 -1
- package/.agents/scripts/lib/cli-args.js +64 -0
- package/.agents/scripts/lib/close-validation/gates.js +2 -2
- package/.agents/scripts/lib/close-validation/runner.js +3 -3
- package/.agents/scripts/lib/config/acceptance-eval.js +5 -52
- package/.agents/scripts/lib/config/commands.js +3 -5
- package/.agents/scripts/lib/config/explain.js +5 -7
- package/.agents/scripts/lib/config/gates/bundle-size.schema.js +32 -6
- package/.agents/scripts/lib/config/gates/coverage.schema.js +25 -5
- package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +12 -2
- package/.agents/scripts/lib/config/gates/crap.schema.js +68 -23
- package/.agents/scripts/lib/config/gates/duplication.schema.js +29 -17
- package/.agents/scripts/lib/config/gates/index.js +5 -2
- package/.agents/scripts/lib/config/gates/lighthouse.schema.js +34 -6
- package/.agents/scripts/lib/config/gates/lint.schema.js +11 -2
- package/.agents/scripts/lib/config/gates/maintainability.schema.js +37 -15
- package/.agents/scripts/lib/config/gates/mutation.schema.js +15 -3
- package/.agents/scripts/lib/config/gates/shared.js +58 -9
- package/.agents/scripts/lib/config/github.js +0 -1
- package/.agents/scripts/lib/config/limits.js +3 -48
- package/.agents/scripts/lib/config/qa.js +105 -0
- package/.agents/scripts/lib/config/temp-paths.js +6 -5
- package/.agents/scripts/lib/config-settings-schema-delivery.js +237 -56
- package/.agents/scripts/lib/config-settings-schema-quality.js +209 -29
- package/.agents/scripts/lib/config-settings-schema.js +386 -39
- package/.agents/scripts/lib/crap-baseline-join.js +126 -9
- package/.agents/scripts/lib/crap-utils.js +84 -520
- package/.agents/scripts/lib/dead-exports-knip.js +79 -10
- package/.agents/scripts/lib/degraded-mode.js +2 -2
- package/.agents/scripts/lib/doc-tiers.js +3 -3
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +46 -104
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +10 -8
- package/.agents/scripts/lib/fs-walk.js +52 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
- package/.agents/scripts/lib/git-utils.js +16 -36
- package/.agents/scripts/lib/knip-entry-sync.js +469 -0
- package/.agents/scripts/lib/observability/metrics-ledger.js +1 -1
- package/.agents/scripts/lib/observability/runtime-friction.js +10 -0
- package/.agents/scripts/lib/observability/signal-validator.js +5 -85
- package/.agents/scripts/lib/observability/signals-writer.js +19 -62
- package/.agents/scripts/lib/observability/source-classifier.js +5 -7
- package/.agents/scripts/lib/observability/terse-result.js +3 -3
- package/.agents/scripts/lib/orchestration/behind-recovery.js +114 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +7 -8
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +34 -33
- package/.agents/scripts/lib/orchestration/code-review.js +2 -2
- package/.agents/scripts/lib/orchestration/complexity-gate.js +43 -161
- package/.agents/scripts/lib/orchestration/diff-magnitude.js +4 -4
- package/.agents/scripts/lib/orchestration/label-transitions.js +3 -2
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +12 -38
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +5 -6
- package/.agents/scripts/lib/orchestration/plan-metrics.js +2 -3
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +6 -0
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +0 -1
- package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
- package/.agents/scripts/lib/orchestration/resolve-stories.js +5 -15
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +130 -0
- package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +37 -26
- package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +376 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +161 -151
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +15 -3
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +10 -15
- package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +17 -2
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +5 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +157 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +0 -14
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +65 -25
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +20 -31
- package/.agents/scripts/lib/orchestration/spec-spill.js +17 -3
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +7 -6
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +2 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +4 -1
- package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -127
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -1
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +5 -5
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -4
- package/.agents/scripts/lib/orchestration/verify-tier-repair.js +107 -0
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -87
- package/.agents/scripts/lib/signals/detectors/common.js +1 -1
- package/.agents/scripts/lib/signals/index.js +8 -6
- package/.agents/scripts/lib/signals/schema.js +20 -25
- package/.agents/scripts/lib/signals/write.js +8 -8
- package/.agents/scripts/lib/story-body/story-body.js +12 -59
- package/.agents/scripts/lib/temp-retention.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +16 -14
- package/.agents/scripts/lib/ticket-body-sections.js +4 -5
- package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +13 -45
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +4 -5
- package/.agents/scripts/lib/worktree-manager.js +2 -3
- package/.agents/scripts/lint-label-vocabulary.js +2 -24
- package/.agents/scripts/pr-watch-with-update.js +7 -5
- package/.agents/scripts/providers/github/cache.js +2 -2
- package/.agents/scripts/providers/github/comments.js +6 -28
- package/.agents/scripts/providers/github/compose.js +0 -15
- package/.agents/scripts/providers/github/errors.js +10 -27
- package/.agents/scripts/providers/github/request-helpers.js +1 -2
- package/.agents/scripts/providers/github/sub-issues.js +10 -218
- package/.agents/scripts/providers/github.js +4 -7
- package/.agents/scripts/prune-baseline-orphans.js +181 -0
- package/.agents/scripts/resolve-stories.js +0 -2
- package/.agents/scripts/run-lint.js +61 -61
- package/.agents/scripts/run-test-profile.js +6 -6
- package/.agents/scripts/run-verify.js +48 -30
- package/.agents/scripts/single-story-close.js +20 -0
- package/.agents/scripts/single-story-init.js +12 -35
- package/.agents/scripts/update-dead-exports-baseline.js +321 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +2 -2
- package/.agents/skills/skills.index.json +2 -12
- package/.agents/skills/stack/qa/playwright/SKILL.md +48 -0
- package/.agents/workflows/audit-documentation.md +5 -6
- package/.agents/workflows/audit-to-stories.md +2 -2
- package/.agents/workflows/helpers/audit-lens-core.md +11 -12
- package/.agents/workflows/helpers/code-quality-guardrails.md +15 -14
- package/.agents/workflows/helpers/code-review.md +3 -8
- package/.agents/workflows/helpers/deliver-reference.md +2 -1
- package/.agents/workflows/helpers/deliver-story-reference.md +27 -16
- package/.agents/workflows/helpers/worktree-lifecycle.md +1 -2
- package/.agents/workflows/mandrel-update.md +10 -10
- package/.agents/workflows/qa-assist.md +15 -20
- package/.agents/workflows/qa-explore.md +9 -8
- package/README.md +1 -1
- package/docs/CHANGELOG.md +49 -0
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +127 -0
- package/package.json +12 -3
- package/.agents/schemas/lifecycle/checkpoint.written.schema.json +0 -13
- package/.agents/schemas/lifecycle/close-validate.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/close-validate.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/code-review.end.schema.json +0 -30
- package/.agents/schemas/lifecycle/code-review.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/intervention.recorded.schema.json +0 -15
- package/.agents/schemas/lifecycle/loop.tick.schema.json +0 -20
- package/.agents/schemas/lifecycle/notification.emitted.schema.json +0 -18
- package/.agents/schemas/lifecycle/pr.created.schema.json +0 -14
- package/.agents/schemas/lifecycle/retro.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/retro.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/story.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +0 -17
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +0 -15
- package/.agents/schemas/lifecycle/story.merged.schema.json +0 -13
- package/.agents/scripts/check-gherkin-placeholders.js +0 -663
- package/.agents/scripts/check-lifecycle-doc-drift.js +0 -411
- package/.agents/scripts/lib/audit-suite/cli.js +0 -64
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +0 -202
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +0 -212
- package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +0 -104
- package/.agents/scripts/lib/checks/push-hook-parity.js +0 -106
- package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +0 -92
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +0 -81
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +0 -55
- package/.agents/scripts/lib/crap-baseline-index.js +0 -46
- package/.agents/scripts/lib/crap-utils-incremental.js +0 -113
- package/.agents/scripts/lib/dynamic-workflow/capability.js +0 -396
- package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -335
- package/.agents/scripts/lib/mutation/baseline-snapshot.js +0 -239
- package/.agents/scripts/lib/mutation/config-detector.js +0 -119
- package/.agents/scripts/lib/mutation/stryker-runner.js +0 -306
- package/.agents/scripts/lib/mutation/survivor-report.js +0 -160
- package/.agents/scripts/lib/observability/active-story-env.js +0 -170
- package/.agents/scripts/lib/observability/tool-trace-hook.js +0 -456
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +0 -111
- package/.agents/scripts/lib/orchestration/context-envelope.js +0 -277
- package/.agents/scripts/lib/orchestration/detectors-phase.js +0 -194
- package/.agents/scripts/lib/orchestration/lifecycle/bus.js +0 -309
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +0 -181
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +0 -229
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +0 -54
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +0 -344
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +0 -114
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +0 -37
- package/.agents/scripts/lib/orchestration/resolves-token.js +0 -127
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +0 -130
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +0 -55
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +0 -211
- package/.agents/scripts/lib/planning-corpus.js +0 -37
- package/.agents/scripts/lib/qa/coverage-report.js +0 -181
- package/.agents/scripts/lib/qa/propose-missing-test.js +0 -95
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +0 -217
- package/.agents/scripts/lib/signals/detectors/index.js +0 -14
- package/.agents/scripts/lib/signals/detectors/retry.js +0 -253
- package/.agents/scripts/lib/signals/detectors/rework.js +0 -167
- package/.agents/scripts/lib/signals/read.js +0 -268
- package/.agents/scripts/lib/signals/span-tree.js +0 -291
- package/.agents/scripts/lib/story-lifecycle.js +0 -194
- package/.agents/scripts/lib/story-plan.js +0 -379
- package/.agents/scripts/lib/util/phase-timer-state.js +0 -72
- package/.agents/scripts/lib/util/phase-timer.js +0 -163
- package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +0 -169
- package/.agents/scripts/lint-baseline.js +0 -507
- package/.agents/scripts/providers/github/prs.js +0 -103
- package/.agents/scripts/signals-view.js +0 -309
- package/.agents/scripts/story-plan.js +0 -370
- package/.agents/scripts/sync-branch-from-base.js +0 -149
- package/.agents/scripts/validate-docs-freshness.js +0 -314
- package/.agents/skills/core/diagnose-friction/SKILL.md +0 -78
- package/.agents/workflows/helpers/signals.md +0 -112
|
@@ -2,12 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
import Ajv from 'ajv';
|
|
4
4
|
|
|
5
|
+
import { COMMANDS_DEFAULTS } from './config/commands.js';
|
|
6
|
+
import {
|
|
7
|
+
BRANCH_PROTECTION_DEFAULTS,
|
|
8
|
+
DEFAULT_REQUIRED_CHECKS,
|
|
9
|
+
MERGE_METHODS_DEFAULTS,
|
|
10
|
+
NOTIFICATIONS_DEFAULTS,
|
|
11
|
+
} from './config/github.js';
|
|
12
|
+
import { PATHS_DEFAULTS } from './config/paths.js';
|
|
5
13
|
import { SHELL_INJECTION_PATTERN_STRING } from './config-schema-shared.js';
|
|
6
14
|
// `delivery.*` sub-schemas were extracted to a sibling module (refs #3457)
|
|
7
15
|
// to keep this aggregate module above the maintainability floor. The
|
|
8
16
|
// resolved AGENTRC_SCHEMA is unchanged.
|
|
9
17
|
import { DELIVERY_SCHEMA } from './config-settings-schema-delivery.js';
|
|
10
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Annotation contract (Story #5007). These schema literals are the SINGLE
|
|
21
|
+
* annotated source for the whole `.agentrc.json` surface:
|
|
22
|
+
*
|
|
23
|
+
* - `description` — the operator-facing gloss. `generate-config-docs.js`
|
|
24
|
+
* serializes it into the shipped JSON-Schema mirror
|
|
25
|
+
* (`.agents/schemas/agentrc.schema.json`, which every consumer config
|
|
26
|
+
* points `$schema` at) and into the `configuration.md` key table.
|
|
27
|
+
* - `default` — the value that appears in the generated defaults
|
|
28
|
+
* inventory `.agents/docs/agentrc-reference.json` (the SSOT
|
|
29
|
+
* `lib/config/defaults.js` reads for `mandrel explain` and the
|
|
30
|
+
* sync-agentrc redundancy advisory). Import the matching runtime
|
|
31
|
+
* `*_DEFAULTS` constant rather than restating a literal wherever one
|
|
32
|
+
* exists, so the annotation and the resolver cannot drift.
|
|
33
|
+
*
|
|
34
|
+
* A key with a runtime default but no `default` annotation is deliberately
|
|
35
|
+
* out of the inventory — annotating it would change what `mandrel explain`
|
|
36
|
+
* reports and what sync-agentrc flags redundant, which is a behaviour
|
|
37
|
+
* change, not a representation one.
|
|
38
|
+
*
|
|
39
|
+
* Nothing here is hand-mirrored any more: run `npm run docs:gen` after
|
|
40
|
+
* editing, and `npm run docs:check` fails closed on drift.
|
|
41
|
+
*/
|
|
42
|
+
|
|
11
43
|
const SAFE_STRING = {
|
|
12
44
|
type: 'string',
|
|
13
45
|
not: { pattern: SHELL_INJECTION_PATTERN_STRING },
|
|
@@ -65,39 +97,100 @@ export const AGENT_SETTINGS_STRING_FIELDS = Object.freeze([]);
|
|
|
65
97
|
*/
|
|
66
98
|
const PATHS_SCHEMA = {
|
|
67
99
|
type: 'object',
|
|
100
|
+
description:
|
|
101
|
+
'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`.',
|
|
68
102
|
required: ['agentRoot', 'docsRoot', 'tempRoot'],
|
|
69
103
|
properties: {
|
|
70
|
-
agentRoot: {
|
|
71
|
-
|
|
72
|
-
|
|
104
|
+
agentRoot: {
|
|
105
|
+
...SAFE_STRING,
|
|
106
|
+
minLength: 1,
|
|
107
|
+
description:
|
|
108
|
+
'Repo-relative root of the materialized framework tree (`mandrel sync` writes here).',
|
|
109
|
+
default: PATHS_DEFAULTS.agentRoot,
|
|
110
|
+
},
|
|
111
|
+
docsRoot: {
|
|
112
|
+
...SAFE_STRING,
|
|
113
|
+
minLength: 1,
|
|
114
|
+
description:
|
|
115
|
+
'Repo-relative root of the project documentation the planner reads for context.',
|
|
116
|
+
default: PATHS_DEFAULTS.docsRoot,
|
|
117
|
+
},
|
|
118
|
+
tempRoot: {
|
|
119
|
+
...SAFE_STRING,
|
|
120
|
+
minLength: 1,
|
|
121
|
+
description:
|
|
122
|
+
'Repo-relative gitignored scratch root. Every temporary artifact — gate transcripts, audit reports, plan authoring dirs — lands under it.',
|
|
123
|
+
default: PATHS_DEFAULTS.tempRoot,
|
|
124
|
+
},
|
|
73
125
|
},
|
|
74
126
|
additionalProperties: false,
|
|
75
127
|
};
|
|
76
128
|
|
|
77
129
|
/**
|
|
78
|
-
* `project.commands` — names of the
|
|
130
|
+
* `project.commands` — names of the test/typecheck/format commands the
|
|
79
131
|
* close-validation chain spawns. `typecheck` accepts `null` to mean
|
|
80
132
|
* "disabled". `validate` and `build` were dropped (no production consumers).
|
|
81
133
|
*/
|
|
82
134
|
const COMMANDS_SCHEMA = {
|
|
83
135
|
type: 'object',
|
|
136
|
+
description:
|
|
137
|
+
'Shell commands the close-validation chain spawns. Each is run from the repo root.',
|
|
84
138
|
properties: {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
139
|
+
test: {
|
|
140
|
+
...SAFE_STRING,
|
|
141
|
+
minLength: 1,
|
|
142
|
+
description: 'Full test-suite command run by the close-validation chain.',
|
|
143
|
+
default: COMMANDS_DEFAULTS.test,
|
|
144
|
+
},
|
|
145
|
+
typecheck: {
|
|
146
|
+
...NULLABLE_NONEMPTY_SAFE_STRING,
|
|
147
|
+
description:
|
|
148
|
+
'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.',
|
|
149
|
+
default: COMMANDS_DEFAULTS.typecheck,
|
|
150
|
+
},
|
|
151
|
+
formatCheck: {
|
|
152
|
+
...SAFE_STRING,
|
|
153
|
+
minLength: 1,
|
|
154
|
+
description:
|
|
155
|
+
'Non-mutating format verification run as a close-validation gate.',
|
|
156
|
+
default: COMMANDS_DEFAULTS.formatCheck,
|
|
157
|
+
},
|
|
158
|
+
formatWrite: {
|
|
159
|
+
...SAFE_STRING,
|
|
160
|
+
minLength: 1,
|
|
161
|
+
description:
|
|
162
|
+
'Mutating format command the close-time format-autofix step spawns.',
|
|
163
|
+
default: COMMANDS_DEFAULTS.formatWrite,
|
|
164
|
+
},
|
|
90
165
|
},
|
|
91
166
|
additionalProperties: false,
|
|
92
167
|
};
|
|
93
168
|
|
|
94
169
|
const PROJECT_SCHEMA = {
|
|
95
170
|
type: 'object',
|
|
171
|
+
description:
|
|
172
|
+
'Project identity, filesystem roots, planner docs context, and the commands the close-validation chain spawns.',
|
|
96
173
|
required: ['paths'],
|
|
97
174
|
properties: {
|
|
98
|
-
baseBranch:
|
|
175
|
+
baseBranch: {
|
|
176
|
+
...SAFE_STRING,
|
|
177
|
+
description:
|
|
178
|
+
'Branch every `story-<id>` branch is seeded from and every Story PR targets.',
|
|
179
|
+
default: 'main',
|
|
180
|
+
},
|
|
99
181
|
paths: PATHS_SCHEMA,
|
|
100
|
-
docsContextFiles: {
|
|
182
|
+
docsContextFiles: {
|
|
183
|
+
type: 'array',
|
|
184
|
+
items: { type: 'string' },
|
|
185
|
+
description:
|
|
186
|
+
'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.',
|
|
187
|
+
default: [
|
|
188
|
+
'architecture.md',
|
|
189
|
+
'data-dictionary.md',
|
|
190
|
+
'decisions.md',
|
|
191
|
+
'patterns.md',
|
|
192
|
+
],
|
|
193
|
+
},
|
|
101
194
|
commands: COMMANDS_SCHEMA,
|
|
102
195
|
},
|
|
103
196
|
additionalProperties: false,
|
|
@@ -110,10 +203,19 @@ const PROJECT_SCHEMA = {
|
|
|
110
203
|
/**
|
|
111
204
|
* Curated webhook event vocabulary. The webhook channel is gated by an
|
|
112
205
|
* explicit allowlist of event names — the vocabulary mirrors the events the
|
|
113
|
-
* v2 runtime actually emits through `notify()` (Story transitions
|
|
114
|
-
* outcomes
|
|
206
|
+
* v2 runtime actually emits through `notify()` (Story transitions and merge
|
|
207
|
+
* outcomes).
|
|
208
|
+
*
|
|
209
|
+
* `loop.tick` was retired here on the same rule (Story #5024). Its only
|
|
210
|
+
* producer was `emit-loop-tick.js`, which published to the lifecycle bus and
|
|
211
|
+
* never called `notify()` at all — and the bus had no production caller, so
|
|
212
|
+
* the event could not reach a webhook by any path. The notify CLI cannot
|
|
213
|
+
* substitute: it hardcodes `event: 'operator-message'` and exposes no
|
|
214
|
+
* `--event` flag, so a consumer could not dispatch it either. It shipped in
|
|
215
|
+
* `NOTIFICATIONS_DEFAULTS`, which meant every consumer was subscribed by
|
|
216
|
+
* default to something that could never fire.
|
|
115
217
|
*
|
|
116
|
-
* `story.heartbeat` was retired here (A22): the vocabulary's contract is
|
|
218
|
+
* `story.heartbeat` was retired here first (A22): the vocabulary's contract is
|
|
117
219
|
* "events the runtime actually emits", and nothing could emit this one. Its
|
|
118
220
|
* emitter (`emit-story-heartbeat.js`) demanded an `epicId >= 1` while the
|
|
119
221
|
* sole call path (`single-story-init.js` → `setActiveStoryEnv`) passed
|
|
@@ -131,7 +233,6 @@ export const WEBHOOK_EVENT_NAMES = Object.freeze([
|
|
|
131
233
|
'operator-message',
|
|
132
234
|
'merge.unlanded',
|
|
133
235
|
'merge.flip-failed',
|
|
134
|
-
'loop.tick',
|
|
135
236
|
]);
|
|
136
237
|
|
|
137
238
|
/**
|
|
@@ -142,11 +243,17 @@ export const WEBHOOK_EVENT_NAMES = Object.freeze([
|
|
|
142
243
|
* is ticket scope, not importance. A comment is written *onto a Story
|
|
143
244
|
* issue*, so only events that are about one Story, and whose message reads
|
|
144
245
|
* as narrative an operator wants durably on the ticket, belong here. The
|
|
145
|
-
* webhook-only remainder — `merge.unlanded
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
246
|
+
* webhook-only remainder — `merge.unlanded` and `merge.flip-failed` — are
|
|
247
|
+
* run-scoped beats; mirroring them onto the ticket would bury the narrative
|
|
248
|
+
* under machine chatter, and `notify()` drops a comment for any dispatch
|
|
249
|
+
* without a resolvable ticket id regardless.
|
|
250
|
+
*
|
|
251
|
+
* Note both webhook-only names are allowlistable but have no `notify()`
|
|
252
|
+
* dispatcher today — they reach the run ledger via `appendLedgerEvent`, not
|
|
253
|
+
* the notify path. That is a wiring gap, deliberately left alone by Story
|
|
254
|
+
* #5024 (which only removed `loop.tick`, whose producer went with the bus):
|
|
255
|
+
* unlike `loop.tick` these two have a live producer, so whether to wire the
|
|
256
|
+
* dispatch or drop the allowlist entries is an open decision, not dead code.
|
|
150
257
|
*
|
|
151
258
|
* `story-closing` IS in scope by that rule (Story-scoped, `level: 'story'`,
|
|
152
259
|
* human-readable — the same shape as `story-merged`) and its earlier
|
|
@@ -165,17 +272,30 @@ export const COMMENT_EVENT_NAMES = Object.freeze([
|
|
|
165
272
|
|
|
166
273
|
const NOTIFICATIONS_SCHEMA = {
|
|
167
274
|
type: 'object',
|
|
275
|
+
description:
|
|
276
|
+
"Allowlist-gated notification channels. An event fires on a channel only when it is named in that channel's array.",
|
|
168
277
|
properties: {
|
|
169
|
-
mentionOperator: {
|
|
278
|
+
mentionOperator: {
|
|
279
|
+
type: 'boolean',
|
|
280
|
+
description:
|
|
281
|
+
'When true, `github.operatorHandle` is @-mentioned in the comments the notifier posts.',
|
|
282
|
+
default: NOTIFICATIONS_DEFAULTS.mentionOperator,
|
|
283
|
+
},
|
|
170
284
|
commentEvents: {
|
|
171
285
|
type: 'array',
|
|
172
286
|
items: { type: 'string', enum: [...COMMENT_EVENT_NAMES] },
|
|
173
287
|
uniqueItems: true,
|
|
288
|
+
description:
|
|
289
|
+
'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.',
|
|
290
|
+
default: [...NOTIFICATIONS_DEFAULTS.commentEvents],
|
|
174
291
|
},
|
|
175
292
|
webhookEvents: {
|
|
176
293
|
type: 'array',
|
|
177
294
|
items: { type: 'string', enum: [...WEBHOOK_EVENT_NAMES] },
|
|
178
295
|
uniqueItems: true,
|
|
296
|
+
description:
|
|
297
|
+
'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.',
|
|
298
|
+
default: [...NOTIFICATIONS_DEFAULTS.webhookEvents],
|
|
179
299
|
},
|
|
180
300
|
},
|
|
181
301
|
additionalProperties: false,
|
|
@@ -183,13 +303,21 @@ const NOTIFICATIONS_SCHEMA = {
|
|
|
183
303
|
|
|
184
304
|
const BRANCH_PROTECTION_CHECK_SCHEMA = {
|
|
185
305
|
type: 'object',
|
|
306
|
+
description:
|
|
307
|
+
'One required status check: the context name GitHub gates the merge on, plus the argv the framework runs locally to reproduce it.',
|
|
186
308
|
required: ['name', 'cmd'],
|
|
187
309
|
properties: {
|
|
188
|
-
name: {
|
|
310
|
+
name: {
|
|
311
|
+
type: 'string',
|
|
312
|
+
minLength: 1,
|
|
313
|
+
description: 'Required status-check context name as GitHub reports it.',
|
|
314
|
+
},
|
|
189
315
|
cmd: {
|
|
190
316
|
type: 'array',
|
|
191
317
|
minItems: 1,
|
|
192
318
|
items: { type: 'string', minLength: 1 },
|
|
319
|
+
description:
|
|
320
|
+
'argv array (never a shell string) the local pre-push validation runs to reproduce the check.',
|
|
193
321
|
},
|
|
194
322
|
},
|
|
195
323
|
additionalProperties: false,
|
|
@@ -197,11 +325,24 @@ const BRANCH_PROTECTION_CHECK_SCHEMA = {
|
|
|
197
325
|
|
|
198
326
|
const BRANCH_PROTECTION_SCHEMA = {
|
|
199
327
|
type: 'object',
|
|
328
|
+
description:
|
|
329
|
+
'Branch-protection stance applied to `project.baseBranch` by the GitHub bootstrap, and reproduced locally before every push.',
|
|
200
330
|
properties: {
|
|
201
|
-
enforce: {
|
|
331
|
+
enforce: {
|
|
332
|
+
type: 'boolean',
|
|
333
|
+
description:
|
|
334
|
+
'When true, the GitHub bootstrap writes the required-check ruleset. False leaves the remote stance alone.',
|
|
335
|
+
default: BRANCH_PROTECTION_DEFAULTS.enforce,
|
|
336
|
+
},
|
|
202
337
|
requiredChecks: {
|
|
203
338
|
type: 'array',
|
|
204
339
|
items: BRANCH_PROTECTION_CHECK_SCHEMA,
|
|
340
|
+
description:
|
|
341
|
+
'Checks that must pass before a Story PR merges. Each entry carries both the remote context name and the local argv.',
|
|
342
|
+
default: DEFAULT_REQUIRED_CHECKS.map((c) => ({
|
|
343
|
+
name: c.name,
|
|
344
|
+
cmd: [...c.cmd],
|
|
345
|
+
})),
|
|
205
346
|
},
|
|
206
347
|
},
|
|
207
348
|
additionalProperties: false,
|
|
@@ -209,26 +350,79 @@ const BRANCH_PROTECTION_SCHEMA = {
|
|
|
209
350
|
|
|
210
351
|
const MERGE_METHODS_SCHEMA = {
|
|
211
352
|
type: 'object',
|
|
353
|
+
description:
|
|
354
|
+
'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.',
|
|
212
355
|
properties: {
|
|
213
|
-
allow_squash_merge: {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
356
|
+
allow_squash_merge: {
|
|
357
|
+
type: 'boolean',
|
|
358
|
+
default: MERGE_METHODS_DEFAULTS.allow_squash_merge,
|
|
359
|
+
},
|
|
360
|
+
allow_rebase_merge: {
|
|
361
|
+
type: 'boolean',
|
|
362
|
+
default: MERGE_METHODS_DEFAULTS.allow_rebase_merge,
|
|
363
|
+
},
|
|
364
|
+
allow_merge_commit: {
|
|
365
|
+
type: 'boolean',
|
|
366
|
+
default: MERGE_METHODS_DEFAULTS.allow_merge_commit,
|
|
367
|
+
},
|
|
368
|
+
allow_auto_merge: {
|
|
369
|
+
type: 'boolean',
|
|
370
|
+
default: MERGE_METHODS_DEFAULTS.allow_auto_merge,
|
|
371
|
+
},
|
|
372
|
+
delete_branch_on_merge: {
|
|
373
|
+
type: 'boolean',
|
|
374
|
+
default: MERGE_METHODS_DEFAULTS.delete_branch_on_merge,
|
|
375
|
+
},
|
|
218
376
|
},
|
|
219
377
|
additionalProperties: false,
|
|
220
378
|
};
|
|
221
379
|
|
|
222
380
|
const GITHUB_SCHEMA = {
|
|
223
381
|
type: 'object',
|
|
382
|
+
description:
|
|
383
|
+
'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.',
|
|
224
384
|
required: ['owner', 'repo', 'operatorHandle'],
|
|
225
385
|
properties: {
|
|
226
|
-
owner: {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
386
|
+
owner: {
|
|
387
|
+
type: 'string',
|
|
388
|
+
minLength: 1,
|
|
389
|
+
description: 'GitHub owner (user or org) that hosts the repository.',
|
|
390
|
+
default: '[OWNER]',
|
|
391
|
+
},
|
|
392
|
+
repo: {
|
|
393
|
+
type: 'string',
|
|
394
|
+
minLength: 1,
|
|
395
|
+
description: 'Repository name under `owner`.',
|
|
396
|
+
default: '[REPO]',
|
|
397
|
+
},
|
|
398
|
+
projectNumber: {
|
|
399
|
+
type: ['integer', 'null'],
|
|
400
|
+
minimum: 1,
|
|
401
|
+
description:
|
|
402
|
+
'Projects V2 board number the orchestrator syncs Story status onto. `null` disables board sync.',
|
|
403
|
+
default: null,
|
|
404
|
+
},
|
|
405
|
+
projectOwner: {
|
|
406
|
+
type: ['string', 'null'],
|
|
407
|
+
minLength: 1,
|
|
408
|
+
description:
|
|
409
|
+
'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`.',
|
|
410
|
+
default: null,
|
|
411
|
+
},
|
|
412
|
+
operatorHandle: {
|
|
413
|
+
type: 'string',
|
|
414
|
+
pattern: '^@.+',
|
|
415
|
+
description:
|
|
416
|
+
'The human the framework escalates to, `@`-prefixed. Used for HITL @-mentions on `agent::blocked`.',
|
|
417
|
+
default: '@[USERNAME]',
|
|
418
|
+
},
|
|
419
|
+
defaultTimeoutMs: {
|
|
420
|
+
type: 'integer',
|
|
421
|
+
minimum: 1000,
|
|
422
|
+
description:
|
|
423
|
+
'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.',
|
|
424
|
+
default: 60000,
|
|
425
|
+
},
|
|
232
426
|
branchProtection: BRANCH_PROTECTION_SCHEMA,
|
|
233
427
|
mergeMethods: MERGE_METHODS_SCHEMA,
|
|
234
428
|
notifications: NOTIFICATIONS_SCHEMA,
|
|
@@ -260,8 +454,21 @@ const GITHUB_SCHEMA = {
|
|
|
260
454
|
|
|
261
455
|
const PLANNING_SCHEMA = {
|
|
262
456
|
type: 'object',
|
|
457
|
+
description:
|
|
458
|
+
'Inputs to `/plan`: risk escalation heuristics, ceremony-lite routing, and the cross-Story conflict-finding severity gates.',
|
|
263
459
|
properties: {
|
|
264
|
-
riskHeuristics:
|
|
460
|
+
riskHeuristics: {
|
|
461
|
+
...LIST_OR_EXTENDER_OF_STRINGS,
|
|
462
|
+
description:
|
|
463
|
+
'Prose heuristics the planner escalates a Story against. A plain array replaces the framework list; the `{ append, prepend }` extender form deep-merges with it.',
|
|
464
|
+
default: [
|
|
465
|
+
'Destructive or irreversible data mutations (dropping tables, deleting rows without soft-delete or backup, truncating production state).',
|
|
466
|
+
'Modifications to shared security or auth infrastructure (IAM policies, auth middleware, session or token handling, secret rotation).',
|
|
467
|
+
'Changes to CI/CD, deployment pipelines, or release gating that could disable safety checks or ship unverified code to production.',
|
|
468
|
+
'Monorepo-wide AST or text replacements touching overlapping files in parallel (catastrophic merge-conflict risk across concurrent agents).',
|
|
469
|
+
'Schema migrations that rewrite existing rows or drop columns without a backfill or rollback plan.',
|
|
470
|
+
],
|
|
471
|
+
},
|
|
265
472
|
// Story #4722 (superseding #4683's word-count gate) — shape-derived
|
|
266
473
|
// ceremony-lite routing. Complexity routes on the objective shape of the
|
|
267
474
|
// authored work (changes[] count, acceptance count, creates-vs-refactors
|
|
@@ -303,11 +510,13 @@ const PLANNING_SCHEMA = {
|
|
|
303
510
|
type: 'boolean',
|
|
304
511
|
description:
|
|
305
512
|
'When true, upgrade shared-editor conflict findings to hard errors (default false — advisory soft findings only).',
|
|
513
|
+
default: false,
|
|
306
514
|
},
|
|
307
515
|
requireExplicitCrossStoryDeps: {
|
|
308
516
|
type: 'boolean',
|
|
309
517
|
description:
|
|
310
518
|
'When true, upgrade implicit cross-Story dependency findings to hard errors (default false — advisory soft findings only).',
|
|
519
|
+
default: false,
|
|
311
520
|
},
|
|
312
521
|
// Cross-cutting registry conflict knobs consumed by
|
|
313
522
|
// `ticket-validator-conflicts.js` (wired through
|
|
@@ -321,22 +530,35 @@ const PLANNING_SCHEMA = {
|
|
|
321
530
|
...LIST_OR_EXTENDER_OF_STRINGS,
|
|
322
531
|
description:
|
|
323
532
|
'Registry path patterns whose concurrent edits across Stories are flagged as conflicts. Defaults to the framework listener/handler index patterns when omitted.',
|
|
533
|
+
// Mirrors DEFAULT_REGISTRY_PATTERNS in
|
|
534
|
+
// `lib/orchestration/ticket-validator-conflicts.js`. Restated rather
|
|
535
|
+
// than imported: that module pulls in the story-body parser and the
|
|
536
|
+
// reachability walker, which have no business loading behind a schema
|
|
537
|
+
// declaration. The rewritten parity suite asserts the two agree.
|
|
538
|
+
default: [
|
|
539
|
+
'lib/orchestration/lifecycle/listeners/index.js',
|
|
540
|
+
'**/listeners/index.js',
|
|
541
|
+
'**/handlers/index.js',
|
|
542
|
+
],
|
|
324
543
|
},
|
|
325
544
|
failOnRegistryConflicts: {
|
|
326
545
|
type: 'boolean',
|
|
327
546
|
description:
|
|
328
547
|
'When true, upgrade cross-cutting registry conflict findings to hard errors (default false).',
|
|
548
|
+
default: false,
|
|
329
549
|
},
|
|
330
550
|
failOnLargeFanOut: {
|
|
331
551
|
type: 'boolean',
|
|
332
552
|
description:
|
|
333
553
|
'When true, upgrade fan-out-warning findings (delete blast radius) to hard errors (default false — soft advisory).',
|
|
554
|
+
default: false,
|
|
334
555
|
},
|
|
335
556
|
largeFanOutThreshold: {
|
|
336
557
|
type: 'integer',
|
|
337
558
|
minimum: 0,
|
|
338
559
|
description:
|
|
339
560
|
'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.',
|
|
561
|
+
default: 10,
|
|
340
562
|
},
|
|
341
563
|
// Navigability-reachability config consumed by the plan-persist draft
|
|
342
564
|
// reachability gate (Epic #4131 F7; demoted into persist by #4474 PR6).
|
|
@@ -351,12 +573,14 @@ const PLANNING_SCHEMA = {
|
|
|
351
573
|
items: { type: 'string' },
|
|
352
574
|
description:
|
|
353
575
|
'Glob patterns (e.g. pages/**, app/**/route.ts) marking paths that add a user-facing route.',
|
|
576
|
+
default: [],
|
|
354
577
|
},
|
|
355
578
|
navRegistry: {
|
|
356
579
|
type: 'array',
|
|
357
580
|
items: { type: 'string' },
|
|
358
581
|
description:
|
|
359
582
|
'Tokens identifying the nav-registry SSOT a route-adding Story is expected to reference.',
|
|
583
|
+
default: [],
|
|
360
584
|
},
|
|
361
585
|
},
|
|
362
586
|
additionalProperties: false,
|
|
@@ -426,6 +650,13 @@ const QA_SIGN_IN_SEAM_SCHEMA = {
|
|
|
426
650
|
// for `skill`/credential seams where per-persona material is genuinely
|
|
427
651
|
// consulted. The resolver normalizes both to one canonical internal form.
|
|
428
652
|
const QA_PERSONAS_SCHEMA = {
|
|
653
|
+
description:
|
|
654
|
+
'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.',
|
|
655
|
+
// Inventory value: an illustrative map showing both per-persona shapes.
|
|
656
|
+
default: {
|
|
657
|
+
admin: { credentialRef: 'QA_ADMIN_CREDENTIAL' },
|
|
658
|
+
member: { signInSkill: 'stack/qa/sign-in-member' },
|
|
659
|
+
},
|
|
429
660
|
oneOf: [
|
|
430
661
|
{
|
|
431
662
|
type: 'array',
|
|
@@ -470,6 +701,23 @@ const QA_PERSONAS_SCHEMA = {
|
|
|
470
701
|
// `.agents/rules/git-conventions.md` § Contract Cutovers).
|
|
471
702
|
const QA_ENVIRONMENTS_SCHEMA = {
|
|
472
703
|
type: 'object',
|
|
704
|
+
description:
|
|
705
|
+
'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`.',
|
|
706
|
+
// Inventory value: an illustrative two-environment map, not a resolvable
|
|
707
|
+
// default. The QA harness is opt-in and every value here is
|
|
708
|
+
// project-specific; the entry exists so `mandrel explain` can show the
|
|
709
|
+
// expected shape.
|
|
710
|
+
default: {
|
|
711
|
+
local: {
|
|
712
|
+
baseUrl: 'http://localhost:3000',
|
|
713
|
+
signInSeam: { urlTemplate: '/dev/sign-in-as/{persona}' },
|
|
714
|
+
},
|
|
715
|
+
staging: {
|
|
716
|
+
baseUrl: 'https://staging.example.test',
|
|
717
|
+
signInSeam: { skill: 'stack/qa/sign-in' },
|
|
718
|
+
allowWrites: false,
|
|
719
|
+
},
|
|
720
|
+
},
|
|
473
721
|
minProperties: 1,
|
|
474
722
|
additionalProperties: {
|
|
475
723
|
type: 'object',
|
|
@@ -483,18 +731,113 @@ const QA_ENVIRONMENTS_SCHEMA = {
|
|
|
483
731
|
},
|
|
484
732
|
};
|
|
485
733
|
|
|
734
|
+
// `gherkinLint` is the static corpus gate's contract (Story #5013). It is
|
|
735
|
+
// deliberately its own sub-block rather than more top-level `qa` keys: the
|
|
736
|
+
// gate is opt-in as a whole, so presence of the block IS the opt-in signal,
|
|
737
|
+
// and `check-gherkin-corpus.js` needs exactly one thing to test for. `scopes`
|
|
738
|
+
// is a map rather than an array because a scope's name appears verbatim in
|
|
739
|
+
// every finding, and a map makes naming it mandatory. Defaults for the two
|
|
740
|
+
// escape hatches live in `lib/config/qa.js` (GHERKIN_LINT_DEFAULTS).
|
|
741
|
+
const QA_GHERKIN_LINT_SCHEMA = {
|
|
742
|
+
type: 'object',
|
|
743
|
+
description:
|
|
744
|
+
'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.',
|
|
745
|
+
// Inventory value: an illustrative single-scope map, not a resolvable
|
|
746
|
+
// default. Every path here is project-specific; the entry exists so
|
|
747
|
+
// `mandrel explain` can show the expected shape.
|
|
748
|
+
default: {
|
|
749
|
+
scopes: {
|
|
750
|
+
web: {
|
|
751
|
+
featureRoots: ['apps/web/tests/features'],
|
|
752
|
+
stepRoots: ['apps/web/tests/steps'],
|
|
753
|
+
},
|
|
754
|
+
},
|
|
755
|
+
exemptionTags: ['@skip'],
|
|
756
|
+
stepWaivers: [],
|
|
757
|
+
},
|
|
758
|
+
properties: {
|
|
759
|
+
scopes: {
|
|
760
|
+
type: 'object',
|
|
761
|
+
description:
|
|
762
|
+
'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.',
|
|
763
|
+
minProperties: 1,
|
|
764
|
+
additionalProperties: {
|
|
765
|
+
type: 'object',
|
|
766
|
+
properties: {
|
|
767
|
+
featureRoots: {
|
|
768
|
+
type: 'array',
|
|
769
|
+
minItems: 1,
|
|
770
|
+
items: { ...SAFE_STRING, minLength: 1 },
|
|
771
|
+
description:
|
|
772
|
+
'Directories holding the `.feature` files of this scope, walked recursively.',
|
|
773
|
+
},
|
|
774
|
+
stepRoots: {
|
|
775
|
+
type: 'array',
|
|
776
|
+
minItems: 1,
|
|
777
|
+
items: { ...SAFE_STRING, minLength: 1 },
|
|
778
|
+
description:
|
|
779
|
+
'Directories holding the step definitions of this scope, walked recursively. Resolving zero definitions here is a fail-closed error, not a clean run.',
|
|
780
|
+
},
|
|
781
|
+
},
|
|
782
|
+
required: ['featureRoots', 'stepRoots'],
|
|
783
|
+
additionalProperties: false,
|
|
784
|
+
},
|
|
785
|
+
},
|
|
786
|
+
exemptionTags: {
|
|
787
|
+
type: 'array',
|
|
788
|
+
items: { ...SAFE_STRING, minLength: 1 },
|
|
789
|
+
description:
|
|
790
|
+
'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"].',
|
|
791
|
+
default: ['@skip'],
|
|
792
|
+
},
|
|
793
|
+
stepWaivers: {
|
|
794
|
+
type: 'array',
|
|
795
|
+
items: { type: 'string', minLength: 1 },
|
|
796
|
+
description:
|
|
797
|
+
'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: [].',
|
|
798
|
+
default: [],
|
|
799
|
+
},
|
|
800
|
+
},
|
|
801
|
+
required: ['scopes'],
|
|
802
|
+
additionalProperties: false,
|
|
803
|
+
};
|
|
804
|
+
|
|
486
805
|
export const QA_SCHEMA = {
|
|
487
806
|
type: 'object',
|
|
807
|
+
description:
|
|
808
|
+
'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.',
|
|
488
809
|
properties: {
|
|
489
|
-
featureRoot: {
|
|
490
|
-
|
|
810
|
+
featureRoot: {
|
|
811
|
+
...SAFE_STRING,
|
|
812
|
+
minLength: 1,
|
|
813
|
+
description:
|
|
814
|
+
'Directory holding the Gherkin feature files the QA sweep drives.',
|
|
815
|
+
default: 'tests/features',
|
|
816
|
+
},
|
|
817
|
+
fixturesManifest: {
|
|
818
|
+
...SAFE_STRING,
|
|
819
|
+
minLength: 1,
|
|
820
|
+
description:
|
|
821
|
+
'Path to the persona/fixture manifest the harness seeds from.',
|
|
822
|
+
default: 'tests/fixtures/personas.json',
|
|
823
|
+
},
|
|
491
824
|
environments: QA_ENVIRONMENTS_SCHEMA,
|
|
492
825
|
personas: QA_PERSONAS_SCHEMA,
|
|
826
|
+
gherkinLint: QA_GHERKIN_LINT_SCHEMA,
|
|
493
827
|
consoleAllowlist: {
|
|
494
828
|
type: 'array',
|
|
495
829
|
items: { ...SAFE_STRING, minLength: 1 },
|
|
830
|
+
description:
|
|
831
|
+
'Console-message substrings the QA run tolerates instead of reporting as a finding (framework dev-mode chatter).',
|
|
832
|
+
default: ['Download the React DevTools', '[HMR]'],
|
|
833
|
+
},
|
|
834
|
+
designTokens: {
|
|
835
|
+
...SAFE_STRING,
|
|
836
|
+
minLength: 1,
|
|
837
|
+
description:
|
|
838
|
+
'Path to the design-token SSOT the UX/UI lens checks rendered styles against.',
|
|
839
|
+
default: 'src/styles/tokens.css',
|
|
496
840
|
},
|
|
497
|
-
designTokens: { ...SAFE_STRING, minLength: 1 },
|
|
498
841
|
},
|
|
499
842
|
additionalProperties: false,
|
|
500
843
|
};
|
|
@@ -503,7 +846,11 @@ export const AGENTRC_SCHEMA = {
|
|
|
503
846
|
type: 'object',
|
|
504
847
|
required: ['project'],
|
|
505
848
|
properties: {
|
|
506
|
-
$schema: {
|
|
849
|
+
$schema: {
|
|
850
|
+
type: 'string',
|
|
851
|
+
description:
|
|
852
|
+
'Editor pointer at the shipped JSON-Schema mirror. Not read by the runtime.',
|
|
853
|
+
},
|
|
507
854
|
project: PROJECT_SCHEMA,
|
|
508
855
|
github: GITHUB_SCHEMA,
|
|
509
856
|
planning: PLANNING_SCHEMA,
|