mandrel 2.38.0 → 2.40.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 +51 -11
- package/.agents/agents/auditor.md +5 -0
- package/.agents/docs/SDLC.md +21 -12
- package/.agents/docs/agentrc-reference.json +1 -4
- package/.agents/docs/configuration.md +2 -2
- package/.agents/instructions.md +17 -16
- package/.agents/schemas/agentrc.schema.json +6 -7
- package/.agents/scripts/audit-to-stories.js +510 -66
- package/.agents/scripts/generate-skills-index.js +158 -75
- package/.agents/scripts/lib/audit-to-stories/epic-grouping-directive.js +39 -0
- package/.agents/scripts/lib/audit-to-stories/ledger-commit.js +290 -0
- package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +94 -3
- package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +10 -0
- package/.agents/scripts/lib/changed-files.js +100 -9
- package/.agents/scripts/lib/config-settings-schema.js +25 -7
- package/.agents/scripts/lib/generated/agentrc-validator.js +1 -1
- package/.agents/scripts/lib/label-constants.js +18 -0
- package/.agents/scripts/lib/label-taxonomy.js +18 -5
- package/.agents/scripts/lib/orchestration/epic-container.js +186 -0
- package/.agents/scripts/lib/orchestration/epic-expansion.js +148 -0
- package/.agents/scripts/lib/orchestration/plan-persist/epic-ops.js +320 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +18 -0
- package/.agents/scripts/lib/orchestration/run-epilogue.js +130 -1
- package/.agents/scripts/lib/qa/resolve-qa-contract.js +58 -6
- package/.agents/scripts/lib/skills/skills-index.js +168 -0
- package/.agents/scripts/lib/skills/walk-skill-files.js +133 -9
- package/.agents/scripts/plan-persist.js +39 -1
- package/.agents/scripts/providers/github/sub-issue-add.js +218 -0
- package/.agents/scripts/quality-preview.js +50 -9
- package/.agents/scripts/resolve-stories.js +42 -2
- package/.agents/scripts/validate-skills.js +53 -66
- package/.agents/templates/docs/audit-sweep-runbook.md +169 -0
- package/.agents/workflows/audit-to-stories.md +85 -7
- package/.agents/workflows/helpers/audit-lens-core.md +24 -4
- package/.agents/workflows/helpers/deliver-reference.md +8 -0
- package/.agents/workflows/helpers/plan-reference.md +28 -0
- package/.agents/workflows/mandrel-deliver.md +47 -43
- package/.agents/workflows/mandrel-plan.md +44 -38
- package/.agents/workflows/qa-run.md +13 -5
- package/docs/CHANGELOG.md +28 -0
- package/package.json +1 -1
package/.agents/README.md
CHANGED
|
@@ -322,9 +322,12 @@ in `runtime-deps.json`.
|
|
|
322
322
|
Orchestration and planning are **Story-only** (`type::story`) — `/mandrel-plan`
|
|
323
323
|
persists Stories with inline `acceptance[]` / `verify[]` and a folded
|
|
324
324
|
`## Spec`; `/mandrel-deliver` runs `helpers/deliver-story` on `story-<id>` → PR →
|
|
325
|
-
`main`. There is no `type::
|
|
326
|
-
|
|
327
|
-
|
|
325
|
+
`main`. There is no `type::task` label, Epic issue form, or `epic/<id>`
|
|
326
|
+
integration branch; a ticket carrying an `Epic: #N` footer is refused by
|
|
327
|
+
`/mandrel-deliver`. `type::epic` exists as a **pure container** — a goal plus a
|
|
328
|
+
child checklist, never delivered itself, expanded to its open Stories by
|
|
329
|
+
`/mandrel-deliver <epicId>` — and its linkage is parent→child only, which is
|
|
330
|
+
what keeps that footer refused. The execution-model contract is owned by
|
|
328
331
|
[`instructions.md` § 5.B](instructions.md) and [`docs/SDLC.md`](docs/SDLC.md).
|
|
329
332
|
|
|
330
333
|
---
|
|
@@ -775,7 +778,16 @@ time by `resolveQaContract`. Copy the reference shape from
|
|
|
775
778
|
"qa": {
|
|
776
779
|
"featureRoot": "tests/features", // root the selector resolves .feature files against
|
|
777
780
|
"fixturesManifest": "tests/fixtures/personas.json", // persona → seed-data manifest
|
|
778
|
-
"
|
|
781
|
+
"environments": { // one entry per deployment target
|
|
782
|
+
"local": {
|
|
783
|
+
"baseUrl": "http://localhost:3000",
|
|
784
|
+
"signInSeam": { "urlTemplate": "/dev/sign-in-as/{persona}" } // dev seam (see step 3)
|
|
785
|
+
},
|
|
786
|
+
"staging": {
|
|
787
|
+
"baseUrl": "https://staging.example.test",
|
|
788
|
+
"allowWrites": false // no signInSeam — an honestly seamless target
|
|
789
|
+
}
|
|
790
|
+
},
|
|
779
791
|
"personas": ["admin", "member"], // name-only array — the honest shape for a url-template seam
|
|
780
792
|
"consoleAllowlist": ["[HMR]"], // optional benign-noise filter (default [])
|
|
781
793
|
"designTokens": "src/styles/tokens.css" // optional visual-check pointer (default null)
|
|
@@ -783,8 +795,9 @@ time by `resolveQaContract`. Copy the reference shape from
|
|
|
783
795
|
}
|
|
784
796
|
```
|
|
785
797
|
|
|
786
|
-
`featureRoot`, `fixturesManifest`, `
|
|
787
|
-
omitting any one makes the resolver throw a field-named error.
|
|
798
|
+
`featureRoot`, `fixturesManifest`, `environments`, and `personas` are mandatory;
|
|
799
|
+
omitting any one makes the resolver throw a field-named error. Within an
|
|
800
|
+
environment only `baseUrl` is required — `signInSeam` is optional (see step 3).
|
|
788
801
|
`consoleAllowlist` and `designTokens` default to `[]` and `null`.
|
|
789
802
|
|
|
790
803
|
`personas` accepts **two shapes** (the resolver normalizes both to one
|
|
@@ -800,13 +813,16 @@ canonical internal map keyed by persona name):
|
|
|
800
813
|
(or credential) seam where that material is genuinely consulted:
|
|
801
814
|
|
|
802
815
|
```jsonc
|
|
803
|
-
"signInSeam": { "skill": "stack/qa/
|
|
816
|
+
"signInSeam": { "skill": "stack/qa/acme-sso" }, // a skill YOU author (see step 3)
|
|
804
817
|
"personas": {
|
|
805
818
|
"admin": { "credentialRef": "QA_ADMIN_CREDENTIAL" }, // stored-credential reference, never an inline secret
|
|
806
|
-
"member": { "signInSkill": "stack/qa/
|
|
819
|
+
"member": { "signInSkill": "stack/qa/acme-sso-member" } // or a per-persona sign-in skill
|
|
807
820
|
}
|
|
808
821
|
```
|
|
809
822
|
|
|
823
|
+
Both skill ids above are **illustrative names for skills you write** — the
|
|
824
|
+
framework ships no sign-in skill. Step 3 says where they go.
|
|
825
|
+
|
|
810
826
|
### 2. Author the fixtures manifest
|
|
811
827
|
|
|
812
828
|
Create the file referenced by `fixturesManifest`. It binds each persona to the
|
|
@@ -824,9 +840,33 @@ credentials are never entered. Expose one of two shapes:
|
|
|
824
840
|
- **`{ urlTemplate }`** — a dev sign-in route where `{persona}` is substituted
|
|
825
841
|
(e.g. `/dev/sign-in-as/{persona}` → `/dev/sign-in-as/admin`); gate it to
|
|
826
842
|
non-production builds. Pair with the name-only `personas` array.
|
|
827
|
-
- **`{ skill }`** — when sign-in is multi-step or non-URL,
|
|
828
|
-
|
|
829
|
-
`personas` form (per step 1).
|
|
843
|
+
- **`{ skill }`** — when sign-in is multi-step or non-URL, name a skill by its
|
|
844
|
+
tier-relative id (e.g. `stack/qa/acme-sso`) whose `SKILL.md` the harness
|
|
845
|
+
reads. Pair with the object-map `personas` form (per step 1).
|
|
846
|
+
- **Omit it entirely** — the honest shape for a target with no sign-in seam at
|
|
847
|
+
all, such as a deployed build whose dev bypass is tree-shaken out. The
|
|
848
|
+
workflows then drive the unauthenticated surface and record the gap rather
|
|
849
|
+
than fabricating a session.
|
|
850
|
+
|
|
851
|
+
**Where a `{ skill }` seam resolves.** The id is looked up under
|
|
852
|
+
`.agents/skills/` (the package payload) and then `.agents/local/skills/` —
|
|
853
|
+
the consumer-writable zone. Author your own sign-in skill in the local zone:
|
|
854
|
+
|
|
855
|
+
```text
|
|
856
|
+
.agents/local/skills/stack/qa/acme-sso/SKILL.md → id: stack/qa/acme-sso
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
`.agents/local/` is never copied into by `mandrel sync`, never pruned, and
|
|
860
|
+
never reported as payload drift by `mandrel doctor`, so a skill you write
|
|
861
|
+
there survives every upgrade. It is held to the same bar as a shipped skill —
|
|
862
|
+
`validate-skills.js` checks its frontmatter and Policy Capsule, and
|
|
863
|
+
`generate-skills-index.js` writes it into its own
|
|
864
|
+
`.agents/local/skills/skills.index.json` (never into the shipped manifest,
|
|
865
|
+
which must stay byte-identical to the package payload).
|
|
866
|
+
|
|
867
|
+
A seam naming an id that resolves under neither root is rejected by
|
|
868
|
+
`resolveQaEnvironment` when the contract is resolved — not silently carried
|
|
869
|
+
until a sweep reaches its sign-in step.
|
|
830
870
|
|
|
831
871
|
Once these three `qa.*` keys are in place, `/qa-explore <surface>`, `/qa-assist`,
|
|
832
872
|
and `/qa-run <selector>` all resolve the contract and operate against the bound
|
|
@@ -130,6 +130,11 @@ Record the outcome in the Executive Summary as a single line —
|
|
|
130
130
|
`Self-cross-check: kept <k> / dropped <d>.` — and, when `d > 0`, name the
|
|
131
131
|
dropped findings with their reason. The line's absence is itself a defect.
|
|
132
132
|
|
|
133
|
+
Beside it, carry one machine-readable tally of the findings you kept —
|
|
134
|
+
`Severity tally: Critical <n> / High <n> / Medium <n> / Low <n>`, zeros
|
|
135
|
+
included, `Info` never counted. `audit-to-stories` cross-checks that line
|
|
136
|
+
against its parse and refuses a report whose tally is missing or wrong.
|
|
137
|
+
|
|
133
138
|
## Fan-out (heavyweight lenses)
|
|
134
139
|
|
|
135
140
|
When your caller dispatches you for a single dimension of a heavyweight lens
|
package/.agents/docs/SDLC.md
CHANGED
|
@@ -7,12 +7,13 @@ each Story is delivered on its own `story-<id>` branch and reaches `main`
|
|
|
7
7
|
through its own PR.
|
|
8
8
|
|
|
9
9
|
An Epic may still exist as an **optional untyped human umbrella issue**
|
|
10
|
-
(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
(the only shipped issue form is `.github/ISSUE_TEMPLATE/story.yml`), and
|
|
11
|
+
**delivery and planning orchestration are Story-only**: there is no Epic
|
|
12
|
+
wave loop, no `epic/<id>` integration branch, no `epic.yaml` reconciler,
|
|
13
|
+
and any ticket that still carries an `Epic: #N` footer is **refused** by
|
|
14
|
+
`/mandrel-deliver` (close it or re-plan it as a v2 Story). `type::epic`
|
|
15
|
+
exists as a **container only** — a grouping ticket with no execution
|
|
16
|
+
payload, expanded to its children at delivery (ADR `20260905-5139`).
|
|
16
17
|
|
|
17
18
|
The framework is **Claude Code-first**: `.claude/`, hooks, skills, and
|
|
18
19
|
the slash-command surface lean in on Claude Code as the reference
|
|
@@ -247,9 +248,10 @@ self-eval, ceremony, close, CI watch, confirm-merge, cleanup) lives in the
|
|
|
247
248
|
|
|
248
249
|
The single operator-facing entry point is `/mandrel-deliver`. It performs no
|
|
249
250
|
git/label mutations itself — `deliver-story` owns every script invocation
|
|
250
|
-
per Story.
|
|
251
|
-
|
|
252
|
-
|
|
251
|
+
per Story. A `type::epic` id expands to its open child Stories before
|
|
252
|
+
resolution. Any ticket that is neither of those two types, or that still
|
|
253
|
+
carries an `Epic: #N` reference, is a hard error naming the ID and the fix
|
|
254
|
+
(close or re-plan as a v2 Story).
|
|
253
255
|
|
|
254
256
|
### Branch model (authoritative)
|
|
255
257
|
|
|
@@ -560,9 +562,15 @@ Editing the main checkout's `.agentrc.json` only affects **the next**
|
|
|
560
562
|
|
|
561
563
|
### `Epic: #N` refusal
|
|
562
564
|
|
|
563
|
-
`/mandrel-deliver` refuses any ticket that still carries an `Epic: #N` footer
|
|
564
|
-
|
|
565
|
-
the ticket or re-plan the work as a v2 Story
|
|
565
|
+
`/mandrel-deliver` refuses any ticket that still carries an `Epic: #N` footer,
|
|
566
|
+
or that is neither `type::story` nor `type::epic`. This is expected — v2 has
|
|
567
|
+
no Epic *delivery* path. Close the ticket or re-plan the work as a v2 Story
|
|
568
|
+
via `/mandrel-plan --tickets <id>`.
|
|
569
|
+
|
|
570
|
+
The container Epic (ADR `20260905-5139`) does **not** soften this. Its linkage
|
|
571
|
+
runs parent→child only — the Epic body lists its children, and no Story body
|
|
572
|
+
ever gains a footer pointing back — so a ticket carrying `Epic: #N` is still a
|
|
573
|
+
v1 ticket and still refused.
|
|
566
574
|
|
|
567
575
|
---
|
|
568
576
|
|
|
@@ -573,6 +581,7 @@ the ticket or re-plan the work as a v2 Story via `/mandrel-plan --tickets <id>`.
|
|
|
573
581
|
| `npx mandrel init` | Cold-start — install `mandrel` (if absent), `mandrel sync`, `bootstrap.js` (provisions repo + Projects V2 board, labels, branch protection), then the onboarding tail (stack detection, docs scaffolding, doctor gate, `/mandrel-plan` handoff). |
|
|
574
582
|
| `/mandrel-plan --seed "<text>"` | Plan from chat text — interrogate → author **one Story by default** → persist `type::story`. |
|
|
575
583
|
| `/mandrel-plan --seed-file <path>` | Plan from on-disk notes / a plan seed (the `/audit-to-stories` handoff). |
|
|
584
|
+
| `/mandrel-deliver <epicId>` | Deliver every open Story under a container Epic — the id expands before resolution. |
|
|
576
585
|
| `/mandrel-plan --tickets <ids>` | Analyze existing issue(s) into proper Stories (prefer an N=1 rewrite). |
|
|
577
586
|
| `/mandrel-deliver <storyId>` | Deliver one Story via `helpers/deliver-story` — `story-<id>` → PR → `main`. |
|
|
578
587
|
| `/mandrel-deliver <storyId> [<storyId>…]` | Deliver multiple Stories in `depends_on` order (resolved from live state), then run the per-run epilogue. |
|
|
@@ -363,9 +363,6 @@
|
|
|
363
363
|
},
|
|
364
364
|
"staging": {
|
|
365
365
|
"baseUrl": "https://staging.example.test",
|
|
366
|
-
"signInSeam": {
|
|
367
|
-
"skill": "stack/qa/sign-in"
|
|
368
|
-
},
|
|
369
366
|
"allowWrites": false
|
|
370
367
|
}
|
|
371
368
|
},
|
|
@@ -374,7 +371,7 @@
|
|
|
374
371
|
"credentialRef": "QA_ADMIN_CREDENTIAL"
|
|
375
372
|
},
|
|
376
373
|
"member": {
|
|
377
|
-
"
|
|
374
|
+
"credentialRef": "QA_MEMBER_CREDENTIAL"
|
|
378
375
|
}
|
|
379
376
|
},
|
|
380
377
|
"gherkinLint": {
|
|
@@ -336,8 +336,8 @@ Agent-driven QA harness contract (Epic #3214; environment-keyed by Epic #4326).
|
|
|
336
336
|
| --- | --- | --- | --- | --- |
|
|
337
337
|
| `featureRoot` | No | `string` | `"tests/features"` | Directory holding the Gherkin feature files the QA sweep drives. |
|
|
338
338
|
| `fixturesManifest` | No | `string` | `"tests/fixtures/personas.json"` | Path to the persona/fixture manifest the harness seeds from. |
|
|
339
|
-
| `environments` | No | `object<map>` | `{"local":{"baseUrl":"http://localhost:3000","signInSeam":{"urlTemplate":"/dev/sign-in-as/{persona}"}},"staging":{"baseUrl":"https://staging.example.test","
|
|
340
|
-
| `personas` | No | one of: `array`, `object` | `{"admin":{"credentialRef":"QA_ADMIN_CREDENTIAL"},"member":{"
|
|
339
|
+
| `environments` | No | `object<map>` | `{"local":{"baseUrl":"http://localhost:3000","signInSeam":{"urlTemplate":"/dev/sign-in-as/{persona}"}},"staging":{"baseUrl":"https://staging.example.test","allowWrites":false}}` | Deployment targets the QA harness can run against (Epic #4326). A map keyed by environment name (e.g. `local`, `staging`), each carrying its own `baseUrl`, an optional per-environment sign-in seam, and an optional `allowWrites` gate. `signInSeam` is the union `{ urlTemplate }` (a dev impersonation route) or `{ skill }` (a skill id such as `stack/qa/acme-sso`, resolved against `.agents/skills/` then the consumer-writable `.agents/local/skills/` zone, and rejected loudly by resolveQaEnvironment when it resolves under neither); omit it entirely for a target with no sign-in seam. 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`. |
|
|
340
|
+
| `personas` | No | one of: `array`, `object` | `{"admin":{"credentialRef":"QA_ADMIN_CREDENTIAL"},"member":{"credentialRef":"QA_MEMBER_CREDENTIAL"}}` | 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. |
|
|
341
341
|
| `gherkinLint` | No | `object` | `{"scopes":{"web":{"featureRoots":["apps/web/tests/features"],"stepRoots":["apps/web/tests/steps"]}},"exemptionTags":["@skip"],"stepWaivers":[]}` | Static Gherkin corpus gate (Story #5013). Optional; the gate runs only when this block is present, so an upgrade never reddens the lint of a consumer that never asked the framework to police its `.feature` files. Inside the opt-in it fails closed: an unresolvable `@cucumber/gherkin` parser, or a scope resolving zero step definitions, exits 1 rather than reporting a clean run. |
|
|
342
342
|
| `gherkinLint.scopes` | Yes | `object<map>` | — | Binding scopes, keyed by name. Each scope resolves its own features against its own step definitions only — pooling every step root into one matcher list is what makes a cross-app false bind possible, where a step defined solely in app B silently vouches for app A. The scope name appears verbatim in every unbound finding. |
|
|
343
343
|
| `gherkinLint.exemptionTags` | No | `array<string>` | `["@skip"]` | Tags marking a scenario as intentionally non-binding, so must-bind skips it. Never an escape from must-compile: a parse error in the file still fails the run. Default: ["@skip"]. |
|
package/.agents/instructions.md
CHANGED
|
@@ -147,9 +147,9 @@ sizing) **fail closed** naming what to trim:
|
|
|
147
147
|
spawn only when the work justifies replicating context. One objective
|
|
148
148
|
per subagent; depth compounds the cost (every nested level re-pays).
|
|
149
149
|
- **Anti-Laziness / No Dead Code.** NEVER use placeholder comments like
|
|
150
|
-
`// ... existing code ...`; every edit must leave complete, runnable
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
`// ... existing code ...`; every edit must leave complete, runnable code.
|
|
151
|
+
Remove unused imports, commented-out code, and dead branches before
|
|
152
|
+
finalizing.
|
|
153
153
|
- **Verification.** Include explicit verification steps in every plan.
|
|
154
154
|
|
|
155
155
|
---
|
|
@@ -157,28 +157,29 @@ sizing) **fail closed** naming what to trim:
|
|
|
157
157
|
## 5. Git & Story Protocol (Strict Standards)
|
|
158
158
|
|
|
159
159
|
[`rules/git-conventions.md`](rules/git-conventions.md) is the canonical
|
|
160
|
-
reference
|
|
161
|
-
|
|
162
|
-
(`helpers/deliver-story` / `single-story-close.js`).
|
|
160
|
+
reference for branch shapes and commit subjects, and is always loaded —
|
|
161
|
+
this section does not restate it.
|
|
163
162
|
|
|
164
163
|
### A. Status Tracking & Commit Standards
|
|
165
164
|
|
|
166
165
|
State mutations are GitHub labels (`agent::ready`, `agent::executing`,
|
|
167
166
|
`agent::done`) via
|
|
168
167
|
`node .agents/scripts/update-ticket-state.js --ticket [ID] --state [STATUS]`.
|
|
169
|
-
Do NOT manually update issue descriptions or status fields unless
|
|
170
|
-
prompted.
|
|
168
|
+
Do NOT manually update issue descriptions or status fields unless prompted.
|
|
171
169
|
|
|
172
|
-
### B. Ticket hierarchy
|
|
170
|
+
### B. Ticket hierarchy
|
|
173
171
|
|
|
174
|
-
The
|
|
172
|
+
The Story is the only executable ticket: `acceptance[]` / `verify[]`
|
|
175
173
|
inline plus the folded Tech Spec in `## Spec` (over-budget Specs fail
|
|
176
|
-
closed — split or tighten; never
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
`
|
|
174
|
+
closed — split or tighten; never under `docs/`). Optional `depends_on`
|
|
175
|
+
edges order rare multi-Story runs, resolved by `/mandrel-deliver` from
|
|
176
|
+
live state; `plan-run::<id>` is filter metadata. Commit subjects
|
|
177
|
+
reference the Story via `(refs #<storyId>)`. There is no `type::task`.
|
|
178
|
+
|
|
179
|
+
`type::epic` is a container only (goal + child checklist, no `agent::*`,
|
|
180
|
+
never delivered): `/mandrel-plan` offers one above 2 Stories and
|
|
181
|
+
`/mandrel-deliver <epicId>` expands it. Linkage is parent→child only, so
|
|
182
|
+
an `Epic: #N` footer is still refused.
|
|
182
183
|
|
|
183
184
|
---
|
|
184
185
|
|
|
@@ -1921,7 +1921,9 @@
|
|
|
1921
1921
|
},
|
|
1922
1922
|
"advisoryAllowlist": {
|
|
1923
1923
|
"type": "array",
|
|
1924
|
-
"items": {
|
|
1924
|
+
"items": {
|
|
1925
|
+
"type": "string"
|
|
1926
|
+
},
|
|
1925
1927
|
"description": "Story #5096. Check-run names exempt from blockOnAdvisoryFailure — a red run whose name matches exactly never blocks arming. Matching is exact; an unnamed run can never match and always blocks.",
|
|
1926
1928
|
"default": []
|
|
1927
1929
|
}
|
|
@@ -1985,7 +1987,7 @@
|
|
|
1985
1987
|
},
|
|
1986
1988
|
"environments": {
|
|
1987
1989
|
"type": "object",
|
|
1988
|
-
"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`,
|
|
1990
|
+
"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`, an optional per-environment sign-in seam, and an optional `allowWrites` gate. `signInSeam` is the union `{ urlTemplate }` (a dev impersonation route) or `{ skill }` (a skill id such as `stack/qa/acme-sso`, resolved against `.agents/skills/` then the consumer-writable `.agents/local/skills/` zone, and rejected loudly by resolveQaEnvironment when it resolves under neither); omit it entirely for a target with no sign-in seam. 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`.",
|
|
1989
1991
|
"default": {
|
|
1990
1992
|
"local": {
|
|
1991
1993
|
"baseUrl": "http://localhost:3000",
|
|
@@ -1995,9 +1997,6 @@
|
|
|
1995
1997
|
},
|
|
1996
1998
|
"staging": {
|
|
1997
1999
|
"baseUrl": "https://staging.example.test",
|
|
1998
|
-
"signInSeam": {
|
|
1999
|
-
"skill": "stack/qa/sign-in"
|
|
2000
|
-
},
|
|
2001
2000
|
"allowWrites": false
|
|
2002
2001
|
}
|
|
2003
2002
|
},
|
|
@@ -2048,7 +2047,7 @@
|
|
|
2048
2047
|
"type": "boolean"
|
|
2049
2048
|
}
|
|
2050
2049
|
},
|
|
2051
|
-
"required": ["baseUrl"
|
|
2050
|
+
"required": ["baseUrl"],
|
|
2052
2051
|
"additionalProperties": false
|
|
2053
2052
|
}
|
|
2054
2053
|
},
|
|
@@ -2059,7 +2058,7 @@
|
|
|
2059
2058
|
"credentialRef": "QA_ADMIN_CREDENTIAL"
|
|
2060
2059
|
},
|
|
2061
2060
|
"member": {
|
|
2062
|
-
"
|
|
2061
|
+
"credentialRef": "QA_MEMBER_CREDENTIAL"
|
|
2063
2062
|
}
|
|
2064
2063
|
},
|
|
2065
2064
|
"oneOf": [
|