mandrel 2.54.0 → 2.56.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.
Files changed (134) hide show
  1. package/.agents/agents/story-worker.md +24 -23
  2. package/.agents/audit-checklists/accessibility.md +0 -3
  3. package/.agents/audit-checklists/mobile.md +0 -4
  4. package/.agents/docs/agentrc-reference.json +8 -2
  5. package/.agents/docs/configuration.md +5 -0
  6. package/.agents/rules/ci-remediation.md +39 -21
  7. package/.agents/schemas/agentrc.schema.json +34 -1
  8. package/.agents/schemas/lifecycle/merge.unlanded.schema.json +2 -1
  9. package/.agents/schemas/story-deliver-terminal.schema.json +1 -0
  10. package/.agents/scripts/audit-to-stories.js +374 -76
  11. package/.agents/scripts/check-audit-attribution.js +119 -62
  12. package/.agents/scripts/check-test-portability.js +512 -0
  13. package/.agents/scripts/coverage-capture.js +17 -10
  14. package/.agents/scripts/evidence-gate.js +31 -4
  15. package/.agents/scripts/file-ci-gap.js +306 -0
  16. package/.agents/scripts/generate-workflows-doc.js +65 -14
  17. package/.agents/scripts/git-cleanup.js +4 -0
  18. package/.agents/scripts/lib/ITicketingProvider.js +78 -0
  19. package/.agents/scripts/lib/audit-advisories.js +195 -0
  20. package/.agents/scripts/lib/audit-attribution.js +22 -0
  21. package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +25 -1
  22. package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +80 -29
  23. package/.agents/scripts/lib/audit-to-stories/finding-adapter.js +5 -1
  24. package/.agents/scripts/lib/audit-to-stories/issue-corpus.js +162 -0
  25. package/.agents/scripts/lib/audit-to-stories/issue-index.js +83 -0
  26. package/.agents/scripts/lib/audit-to-stories/issues-file.js +121 -0
  27. package/.agents/scripts/lib/audit-to-stories/ledger-commit.js +61 -115
  28. package/.agents/scripts/lib/audit-to-stories/ledger-pr.js +347 -0
  29. package/.agents/scripts/lib/audit-to-stories/ledger-record.js +126 -0
  30. package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +169 -44
  31. package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +11 -0
  32. package/.agents/scripts/lib/baselines/merge-envelopes.js +298 -32
  33. package/.agents/scripts/lib/bootstrap/baseline-merge-driver.js +180 -14
  34. package/.agents/scripts/lib/cli-args.js +26 -0
  35. package/.agents/scripts/lib/close-validation/gates.js +113 -7
  36. package/.agents/scripts/lib/close-validation/process.js +7 -3
  37. package/.agents/scripts/lib/close-validation/runner.js +62 -11
  38. package/.agents/scripts/lib/config/ci.js +28 -9
  39. package/.agents/scripts/lib/config-settings-schema-delivery.js +7 -0
  40. package/.agents/scripts/lib/config-settings-schema.js +52 -1
  41. package/.agents/scripts/lib/coverage-capture-fullscope.js +23 -11
  42. package/.agents/scripts/lib/coverage-capture-incremental.js +22 -16
  43. package/.agents/scripts/lib/coverage-capture-usage.js +5 -1
  44. package/.agents/scripts/lib/coverage-capture.js +77 -3
  45. package/.agents/scripts/lib/feedback-loop/graduator-core.js +53 -13
  46. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +71 -25
  47. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +18 -25
  48. package/.agents/scripts/lib/{audit-to-stories/ledger.js → findings/audit-ledger.js} +131 -24
  49. package/.agents/scripts/lib/findings/route-finding.js +42 -2
  50. package/.agents/scripts/lib/full-suite-lock.js +232 -6
  51. package/.agents/scripts/lib/generated/agentrc-validator.js +1 -1
  52. package/.agents/scripts/lib/git/sync-from-base.js +130 -13
  53. package/.agents/scripts/lib/github/framework-repo.js +148 -2
  54. package/.agents/scripts/lib/label-constants.js +6 -1
  55. package/.agents/scripts/lib/observability/source-classifier.js +2 -0
  56. package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +10 -2
  57. package/.agents/scripts/lib/orchestration/check-baselines/phases/refresh-ack.js +75 -15
  58. package/.agents/scripts/lib/orchestration/ci-gap-intake.js +605 -0
  59. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +13 -8
  60. package/.agents/scripts/lib/orchestration/deliver-recover.js +82 -43
  61. package/.agents/scripts/lib/orchestration/dependency-candidates.js +8 -4
  62. package/.agents/scripts/lib/orchestration/epic-candidates.js +9 -4
  63. package/.agents/scripts/lib/orchestration/epic-container.js +66 -4
  64. package/.agents/scripts/lib/orchestration/epic-rollup.js +233 -84
  65. package/.agents/scripts/lib/orchestration/file-assumptions.js +218 -16
  66. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +93 -7
  67. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +22 -6
  68. package/.agents/scripts/lib/orchestration/git-cleanup/phases/parse-args.js +26 -5
  69. package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +13 -2
  70. package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +35 -5
  71. package/.agents/scripts/lib/orchestration/merge-block-class.js +18 -3
  72. package/.agents/scripts/lib/orchestration/merge-poll.js +284 -40
  73. package/.agents/scripts/lib/orchestration/plan-persist/audit-provenance.js +197 -0
  74. package/.agents/scripts/lib/orchestration/plan-persist/epic-adoption.js +49 -2
  75. package/.agents/scripts/lib/orchestration/plan-persist/epic-ops.js +43 -7
  76. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +39 -3
  77. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +5 -0
  78. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +3 -0
  79. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +63 -0
  80. package/.agents/scripts/lib/orchestration/plan-persist/wave-serialisation.js +110 -0
  81. package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +130 -40
  82. package/.agents/scripts/lib/orchestration/resolve-stories.js +44 -1
  83. package/.agents/scripts/lib/orchestration/review-providers/native.js +31 -11
  84. package/.agents/scripts/lib/orchestration/review-providers/scoped-lint.js +27 -24
  85. package/.agents/scripts/lib/orchestration/run-epilogue.js +63 -42
  86. package/.agents/scripts/lib/orchestration/single-story-close/close-note.js +81 -0
  87. package/.agents/scripts/lib/orchestration/single-story-close/failed-terminal.js +40 -51
  88. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +10 -2
  89. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +101 -0
  90. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +351 -28
  91. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +27 -6
  92. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +117 -22
  93. package/.agents/scripts/lib/orchestration/story-close/baseline-upward-writeback.js +94 -12
  94. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +6 -1
  95. package/.agents/scripts/lib/orchestration/story-follow-ups.js +32 -20
  96. package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -14
  97. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +30 -0
  98. package/.agents/scripts/lib/orchestration/verify-credit.js +37 -0
  99. package/.agents/scripts/lib/pinned-override-notes.js +41 -53
  100. package/.agents/scripts/lib/pinned-override-resolve.js +212 -0
  101. package/.agents/scripts/lib/qa/resolve-qa-contract.js +18 -0
  102. package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +173 -9
  103. package/.agents/scripts/lib/skills/walk-skill-files.js +24 -7
  104. package/.agents/scripts/lib/test-temp.js +167 -30
  105. package/.agents/scripts/lib/validation-evidence.js +37 -0
  106. package/.agents/scripts/lib/wave-runner/footprint.js +167 -14
  107. package/.agents/scripts/lib/wave-runner/live-probe.js +7 -1
  108. package/.agents/scripts/lib/wave-runner/ready-set.js +1 -1
  109. package/.agents/scripts/merge-baseline.js +175 -21
  110. package/.agents/scripts/pr-watch-with-update.js +3 -2
  111. package/.agents/scripts/providers/github/errors.js +22 -1
  112. package/.agents/scripts/providers/github/issues.js +106 -1
  113. package/.agents/scripts/providers/github/sub-issue-add.js +18 -1
  114. package/.agents/scripts/providers/github.js +6 -0
  115. package/.agents/scripts/resolve-stories.js +44 -34
  116. package/.agents/scripts/single-story-close.js +5 -0
  117. package/.agents/scripts/stories-wave-tick.js +37 -13
  118. package/.agents/templates/docs/audit-sweep-runbook.md +41 -7
  119. package/.agents/workflows/audit-accessibility.md +16 -31
  120. package/.agents/workflows/audit-mobile.md +20 -37
  121. package/.agents/workflows/audit-to-stories.md +63 -27
  122. package/.agents/workflows/git-cleanup.md +17 -3
  123. package/.agents/workflows/helpers/audit-lens-core.md +45 -0
  124. package/.agents/workflows/helpers/deliver-digest.md +7 -6
  125. package/.agents/workflows/helpers/deliver-reference.md +35 -14
  126. package/.agents/workflows/helpers/deliver-story-reference.md +26 -8
  127. package/.agents/workflows/helpers/deliver-story.md +15 -12
  128. package/.agents/workflows/helpers/plan-reference.md +30 -0
  129. package/.agents/workflows/mandrel-plan.md +10 -13
  130. package/.agents/workflows/memory-consolidate.md +14 -9
  131. package/docs/CHANGELOG.md +37 -0
  132. package/lib/cli/registry.js +64 -21
  133. package/lib/cli/sync.js +27 -2
  134. package/package.json +7 -4
@@ -48,20 +48,19 @@ the step-by-step. This shared core binds every role:
48
48
  You are a **Story delivery worker**: you take one Story from init through
49
49
  implementation to a **pushed branch**, then return. You do **not** close it —
50
50
  your caller owns the close-and-land tail. Follow the `helpers/deliver-story`
51
- prose your caller hands you; this delta states the non-negotiable
52
- MUSTs. Treat a blocking tool-permission prompt as a harness condition —
53
- flip to `agent::blocked` rather than waiting on an approval that cannot
54
- come.
51
+ prose your caller hands you; this delta states the MUSTs. Treat a
52
+ blocking tool-permission prompt as a harness condition — flip to
53
+ `agent::blocked` rather than waiting on an approval that cannot come.
55
54
 
56
55
  ## Worktree discipline (MUST)
57
56
 
58
57
  1. Initialize with
59
58
  `node .agents/scripts/single-story-init.js --story <storyId>` from the
60
- **main checkout**, synchronously at max Bash timeout — a per-worktree
61
- install can take minutes; do not background it.
59
+ **main checkout**, synchronously at max Bash timeout — the install
60
+ can take minutes; never background it.
62
61
  2. Capture `workCwd` and `dependenciesInstalled` from the envelope.
63
- Work only inside the absolute `workCwd`; never move the main checkout's
64
- HEAD. cwd may reset between calls, so anchor every path at `workCwd`.
62
+ Anchor every path at the absolute `workCwd` and work only there; never
63
+ move the main checkout HEAD.
65
64
 
66
65
  ## Verify branch before every commit (MUST)
67
66
 
@@ -81,8 +80,8 @@ follow-up commit, never amend.
81
80
  ## Docs context — digest first
82
81
 
83
82
  Do **not** re-read every file in `project.docsContextFiles`. Read the
84
- `docsDigestPath` digest your caller passes, then pull files on demand at
85
- the lines it names. A null `docsDigestPath` means no mandate.
83
+ digest your caller passes, then pull files on demand at the lines it
84
+ names. A null digest path means no docs mandate.
86
85
 
87
86
  ## Close gates — one credited run
88
87
 
@@ -90,7 +89,7 @@ the lines it names. A null `docsDigestPath` means no mandate.
90
89
  (**typecheck, lint, test, format, maintainability, coverage, crap**) and is
91
90
  the authoritative gate — do not pre-run it. The **one** exception is
92
91
  the full suite: run it exactly once, after the self-eval loop's last fix
93
- commit and immediately before the push, in the shape close credits. A bare
92
+ commit and immediately after the push, in the shape close credits. A bare
94
93
  `npm test` / `pnpm run test` deposits **no** credit:
95
94
 
96
95
  ```bash
@@ -102,8 +101,9 @@ node <main-repo>/.agents/scripts/evidence-gate.js --standalone \
102
101
  ```
103
102
 
104
103
  Dispatch it in the **background**: it routinely outruns the host's sync
105
- Bash ceiling, and its completion re-invokes you — that is the signal. Never spawn a task to poll or `sleep`-loop
106
- against it; a waiter whose condition is wrong outlives the agent. Share
104
+ Bash ceiling, and its completion re-invokes you — which is why the push
105
+ comes first. Never spawn a task to poll or `sleep`-loop
106
+ against it; a waiter with a wrong condition outlives the agent. Share
107
107
  `lint` / `typecheck` evidence with close via `evidence-gate.js`; never
108
108
  stamp coverage / CRAP fresh any other way.
109
109
 
@@ -117,13 +117,12 @@ Waiter traps: [`parallel-tooling.md`](../workflows/helpers/parallel-tooling.md)
117
117
 
118
118
  ## Acceptance self-eval before close (MUST)
119
119
 
120
- After the implementation commits land and **before** flipping to `closing`,
121
- run the bounded acceptance self-eval loop
120
+ **Before** flipping to `closing`, run the bounded self-eval loop
122
121
  ([`acceptance-self-eval.md`](../workflows/helpers/acceptance-self-eval.md)).
123
122
  It scores the change set you computed **once** and injected into the critic
124
123
  — never one it re-derives — against each `acceptance[]` item,
125
124
  consuming `verify[]` output as evidence. **proceed** → flip to `closing`,
126
- push, hand off; **redraft** → fix the flagged criteria, commit, re-eval;
125
+ push, capture, hand off; **redraft** → fix the criteria, commit, re-eval;
127
126
  **block** → take the blocked path below. Never hand off an unscored
128
127
  branch.
129
128
 
@@ -141,20 +140,22 @@ branch.
141
140
 
142
141
  The init envelope carries `remoteVerified` + `remoteProbe`. When
143
142
  `remoteVerified` is `false`, flip to `agent::blocked` quoting
144
- `remoteProbe.detail` and stop. A PR opened by
145
- `single-story-close.js` is the only sanctioned landing.
143
+ `remoteProbe.detail` and stop. A PR opened by `single-story-close.js` is
144
+ the only sanctioned landing.
146
145
 
147
146
  ## Your turn ends at a pushed branch (MUST)
148
147
 
149
148
  You do **not** run close. Push `story-<storyId>` to `origin` — confirming
150
- the remote ref moved — and return. The dispatching orchestrator runs
149
+ the remote ref moved — **before** the credited capture, then return: the
150
+ capture is backgrounded, so its completion ends your turn, and a turn that
151
+ ends unpushed reads as unfinished work. The orchestrator runs
151
152
  `single-story-close.js` in its own session, serialized against your
152
- siblings. Do not open the PR, flip `agent::done`, or spawn a child to close
153
- on your behalf. If the push fails, take the blocked path above.
153
+ siblings. Do not open the PR, flip `agent::done`, or spawn a child to
154
+ close for you. If the push fails, take the blocked path above.
154
155
 
155
156
  ## Return contract — the hand-off report
156
157
 
157
158
  A short, literal hand-off your caller can act on: Story id, `workCwd`,
158
- branch, pushed head SHA, self-eval verdict, `verify[]` evidence. Say plainly
159
- the branch is pushed and unclosed. Never hand-compose a terminal envelope —
159
+ branch, pushed head SHA, self-eval verdict, `verify[]` evidence. Say the
160
+ branch is pushed and unclosed. Never hand-compose a terminal envelope —
160
161
  inventing one makes an unlanded Story look landed.
@@ -24,7 +24,4 @@ Self-check your change against this lens's concerns before you ship:
24
24
  - [ ] Media alternatives
25
25
  - [ ] Contrast where statically derivable
26
26
  - [ ] Raw-element census
27
- - [ ] Resolve the target from config — never a hardcoded URL.
28
- - [ ] Sample routes from the navigability SSOT.
29
27
  - [ ] Run an accessibility engine per sampled route.
30
- - [ ] Median-of-3 or provisional.
@@ -28,8 +28,4 @@ Self-check your change against this lens's concerns before you ship:
28
28
  - [ ] Typography and spacing
29
29
  - [ ] Coverage — is anything exercised at a non-desktop viewport?
30
30
  - [ ] Effectiveness — does that exercise assert anything mobile-specific?
31
- - [ ] Resolve the target from config — never a hardcoded URL.
32
- - [ ] Sample routes from the navigability SSOT.
33
31
  - [ ] Drive two form factors per route.
34
- - [ ] Median-of-3 or provisional.
35
- - [ ] Leave the viewport as you found it.
@@ -28,6 +28,10 @@
28
28
  "projectOwner": null,
29
29
  "operatorHandle": "@[USERNAME]",
30
30
  "defaultTimeoutMs": 60000,
31
+ "followUpRepos": {
32
+ "framework": "dsj1984/mandrel",
33
+ "platform": null
34
+ },
31
35
  "branchProtection": {
32
36
  "enforce": true,
33
37
  "requiredChecks": [
@@ -75,7 +79,8 @@
75
79
  ],
76
80
  "memoryPool": {
77
81
  "staleAfterDays": 30,
78
- "growthDelta": 25
82
+ "growthDelta": 25,
83
+ "indexByteCeiling": 24576
79
84
  },
80
85
  "failOnSharedEditors": false,
81
86
  "requireExplicitCrossStoryDeps": false,
@@ -353,7 +358,8 @@
353
358
  },
354
359
  "autoMerge": "trust-ci",
355
360
  "blockOnAdvisoryFailure": true,
356
- "advisoryAllowlist": []
361
+ "advisoryAllowlist": [],
362
+ "rerunAdvisory": 0
357
363
  },
358
364
  "routing": {
359
365
  "roleScopedAgents": true,
@@ -103,6 +103,9 @@ GitHub provider identity plus the remote stance the bootstrap enforces. `owner`,
103
103
  | `projectOwner` | No | `string` \| `null` | `null` | Owner of the Projects V2 board when it lives outside `owner` (an org board fed by a user repo). `null` means the board shares `owner`. |
104
104
  | `operatorHandle` | Yes | `string` | `"@[USERNAME]"` | The human the framework escalates to, `@`-prefixed. Used for HITL @-mentions on `agent::blocked`. |
105
105
  | `defaultTimeoutMs` | No | `integer` | `60000` | Default `timeoutMs` applied to every `gh` subprocess the provider facade spawns, so a stalled socket or long-poll cannot hang an orchestration indefinitely. A `GhExecTimeoutError` from a hit ceiling is classified `transient` and retried by `withTransientRetry`. Story #2860. |
106
+ | `followUpRepos` | No | `object` | — | Repository slugs for the non-consumer follow-up ownership buckets, used when a CI gap, retro proposal, or audit finding belongs to someone other than the repo that surfaced it. |
107
+ | `followUpRepos.framework` | No | `string` | `"dsj1984/mandrel"` | `<owner>/<repo>` that owns framework-level defects. Defaults to the Mandrel mirror — the one bucket with a knowable default. |
108
+ | `followUpRepos.platform` | No | `string` \| `null` | `null` | `<owner>/<repo>` for a shared platform or infrastructure tracker (a shared base config, a runner fleet, a cross-repo toolchain). No default — nothing can guess a shared repo. Left unset, platform-owned findings file locally and say so. |
106
109
  | `branchProtection` | No | `object` | — | Branch-protection stance applied to `project.baseBranch` by the GitHub bootstrap, and reproduced locally before every push. |
107
110
  | `branchProtection.enforce` | No | `boolean` | `true` | When true, the GitHub bootstrap writes the required-check ruleset. False leaves the remote stance alone. |
108
111
  | `branchProtection.requiredChecks[]` | No | `array<object>` | `[{"name":"lint","cmd":["npm","run","lint"]},{"name":"test","cmd":["npm","test"]},{"name":"baselines","cmd":["node",".agents/scripts/check-baselines.js"]}]` | Checks that must pass before a Story PR merges. Each entry carries both the remote context name and the local argv. Each item has: name, cmd. |
@@ -130,6 +133,7 @@ Inputs to `/mandrel-plan`: risk escalation heuristics, ceremony-lite routing, th
130
133
  | `memoryPool` | No | `object` | — | Thresholds for the memory-hygiene advisory `/mandrel-plan` surfaces at Gate #1. Advisory only: it recommends `/memory-consolidate` and never gates, reroutes, or mutates the memory pool. |
131
134
  | `memoryPool.staleAfterDays` | No | `integer` | `30` | Recommend a consolidation pass once the pool's stamp is older than this many days. Default 30. |
132
135
  | `memoryPool.growthDelta` | No | `integer` | `25` | Recommend a consolidation pass once this many entries have been written since the last one. Measured against the entry count the last pass stamped, so a stamp predating that field leaves growth unmeasured and only the age threshold applies. Default 25. |
136
+ | `memoryPool.indexByteCeiling` | No | `integer` | `24576` | Recommend a consolidation pass once the pool's `MEMORY.md` index exceeds this many bytes. Independent of the age and growth thresholds: the harness truncates the index it loads into each session at its own byte cap, so an oversized index is a loss already happening — every entry listed after the cut is invisible — rather than a hygiene forecast. Default 24576, the harness cap itself. |
133
137
  | `failOnSharedEditors` | No | `boolean` | `false` | When true, upgrade shared-editor conflict findings to hard errors (default false — advisory soft findings only). |
134
138
  | `requireExplicitCrossStoryDeps` | No | `boolean` | `false` | When true, upgrade implicit cross-Story dependency findings to hard errors (default false — advisory soft findings only). |
135
139
  | `crossCuttingRegistries` | No | `string[]` or `{ append?, prepend? }` | `["lib/orchestration/lifecycle/listeners/index.js","**/listeners/index.js","**/handlers/index.js"]` | Registry path patterns whose concurrent edits across Stories are flagged as conflicts. Defaults to the framework listener/handler index patterns when omitted. |
@@ -330,6 +334,7 @@ Everything `/mandrel-deliver` and `single-story-close` consume: execution timeou
330
334
  | `ci.autoMerge` | No | `"trust-ci"` \| `"strict"` | `"trust-ci"` | Story #4356 (Epic #4355). Merge posture. 'trust-ci' (default) merges once required checks pass; 'strict' additionally requires a clean review gate. |
331
335
  | `ci.blockOnAdvisoryFailure` | No | `boolean` | `true` | Story #5096. When true (default), delivery refuses to arm — and disarms — GitHub native auto-merge while a non-required (advisory) check is genuinely red on the PR head and GitHub reports the PR mergeable anyway (mergeStateStatus=UNSTABLE). `--auto` waits on REQUIRED contexts only, so without this a red advisory quality gate merges unattended. Set false to restore the pre-#5096 behaviour verbatim. |
332
336
  | `ci.advisoryAllowlist` | No | `array<string>` | `[]` | 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. |
337
+ | `ci.rerunAdvisory` | No | `integer` | `0` | Story #5266. How many times close may re-run a failed advisory workflow run before blocking on it, per close invocation. Default 0: close spends no CI minutes and issues no GitHub mutation on an advisory red unless asked. At n > 0 the failed run(s) are re-run within that allowance and the merge wait re-polls inside its existing budget, landing or blocking on the re-run verdict. Overridden per invocation by --rerun-advisory <n>. |
333
338
  | `routing` | No | `object` | — | v2 delivery-spawn routing: role-scoped boot contexts and maker-checker sampling. The v1 singleDelivery epic-route kill-switch was removed in Stage 6. |
334
339
  | `routing.roleScopedAgents` | No | `boolean` | `true` | Epic #4478 (M7-B). Kill-switch for the role-scoped boot contexts. When true (default), a converted delivery spawn (`story-worker`, `acceptance-critic`) boots on its own `.claude/agents/<role>.md` system prompt instead of re-paying the full CLAUDE.md @-import closure. When false, every converted spawn falls back to `subagent_type: general-purpose` — the instant, code-rollback-free per-consumer revert, and the universal escape for hosts that ignore `.claude/agents/`. The fallback is the full-closure agent that ran before M7-B, so flipping it off never drops a gate. |
335
340
  | `routing.freshCriticSampleRate` | No | `number` | `0.2` | Epic #4478 (M7-B, Part 2). Maker-checker sampling floor. Under the standard profile, a change set touching no sensitive path routes its acceptance clusters down the contract-identical inline critic path, but this fraction of them is still forced through a fresh-context critic so a low derived level never means zero independent checking. Clamped to [0, 1]; 0 disables the floor, 1 forces every cluster fresh. Consumed by resolveCeremonyForRisk (lib/orchestration/ceremony-routing.js). |
@@ -27,13 +27,29 @@ exactly one of two ways, and no others:
27
27
  through the fix table in
28
28
  [`deliver-story-reference.md` § Step 4](../workflows/helpers/deliver-story-reference.md#step-4--ci-watch--fix-recovery);
29
29
  refresh a baseline only when the diff demonstrably can't be covered.
30
- 2. **File a `meta::framework-gap` issue** when the root cause is outside this
30
+ 2. **File the CI-gap intake issue** when the root cause is outside this
31
31
  delivery's scope — a pre-existing flaky test, a runner/infra weakness, a
32
- framework-level environment gap. Open the issue with the `meta::framework-gap`
33
- label (see [`git-conventions.md`](git-conventions.md)) carrying **the run
34
- link and the failure signature** so a later `/mandrel-plan` Phase 0 sweep can act on
35
- it. Remediate this delivery only if the pre-existing defect is genuinely
36
- blocking it.
32
+ framework-level environment gap. One command does it, and it is the only
33
+ sanctioned filing surface:
34
+
35
+ ```bash
36
+ node .agents/scripts/file-ci-gap.js --story <id> --verdict <verdict> \
37
+ --owner <consumer|framework|platform> --evidence "<proof reading>" [--block]
38
+ ```
39
+
40
+ It reads the digest for the run link and failure signature, routes the
41
+ filing to the repository that **owns** the fault, dedups by signature so
42
+ the Nth occurrence updates the existing ticket, posts the `friction`
43
+ comment, and (with `--block`) flips the Story. Hand-running `gh issue
44
+ create` is not the fallback: it files an issue no `/mandrel-plan` pass can
45
+ graduate, in whichever repo you happen to be standing in. Remediate this
46
+ delivery only if the pre-existing defect is genuinely blocking it.
47
+
48
+ **`--owner` is the judgement call**, and it is yours to make from the
49
+ evidence: `consumer` for this repository's own code, `framework` for a
50
+ Mandrel defect, `platform` for a shared base config, runner fleet, or
51
+ cross-repo toolchain that neither owns. An unconfigured bucket files
52
+ locally and says so in the issue body — it never pretends to be routed.
37
53
 
38
54
  Infra, transient, and flaky failures are root-cause defects too — a flaky test
39
55
  that passes on a rerun is still a bug that will fail a future run. They route
@@ -49,9 +65,9 @@ the two options above. Name the verdict you reached in the `friction` comment.
49
65
  | Verdict | Evidence | Routes to |
50
66
  | --- | --- | --- |
51
67
  | **defect-in-diff** | The failure reproduces on the branch and not on an unmodified `main` | Option 1 — fix at source |
52
- | **pre-existing** | The same check fails on an unmodified `main` too | Option 2 — file `meta::framework-gap`; remediate here only if it blocks this delivery |
53
- | **capacity** | Proven exhaustion of a runner resource, not a property of the diff (see below) | Option 2 — file `meta::framework-gap` **and** escalate to the operator |
54
- | **unreproducible-tier** | The tier cannot be exercised in this sandbox at all, proven by an attempted attach (see below) | Option 2 — file `meta::framework-gap` **and** escalate on first encounter |
68
+ | **pre-existing** | The same check fails on an unmodified `main` too | Option 2 — `file-ci-gap.js --verdict pre-existing`; remediate here only if it blocks this delivery |
69
+ | **capacity** | Proven exhaustion of a runner resource, not a property of the diff (see below) | Option 2 — `file-ci-gap.js --verdict capacity` (`meta::framework-gap` unless `--owner` routes it elsewhere) **and** escalate to the operator |
70
+ | **unreproducible-tier** | The tier cannot be exercised in this sandbox at all, proven by an attempted attach (see below) | Option 2 — `file-ci-gap.js --verdict unreproducible-tier` (`meta::framework-gap` unless `--owner` routes it elsewhere) **and** escalate on first encounter |
55
71
 
56
72
  Why the verdict set carries these last two is recorded in
57
73
  [`docs/decisions.md` ADR 20260906-5160a](../../docs/decisions.md).
@@ -72,10 +88,12 @@ line naming the exhausted limit (an OOM kill, `ENOSPC`, `EMFILE`,
72
88
  timeout), plus the fact that the failure is not specific to this diff. Absent
73
89
  that reading the verdict is **flaky, not capacity**, and it routes to Option 1.
74
90
 
75
- On a `capacity` verdict: file the `meta::framework-gap` issue with the run link,
76
- the failure signature, and the resource reading; flip the Story to
77
- `agent::blocked` with a `friction` comment naming the verdict; and hand back to
78
- the operator, who owns the runner pool. Do not sit in a retry loop waiting for
91
+ On a `capacity` verdict: run `file-ci-gap.js --verdict capacity --block`, passing
92
+ the resource reading as `--evidence` (the run link and failure signature come
93
+ from the digest). That files the intake issue — `meta::framework-gap`, or
94
+ `meta::platform-gap` when `--owner platform` names a shared runner fleet — posts
95
+ the `friction` comment and flips the Story in one call; then hand back to the
96
+ operator, who owns the runner pool. Do not sit in a retry loop waiting for
79
97
  capacity to return.
80
98
 
81
99
  **Rerunning a failed job to reach green stays forbidden under every verdict,
@@ -106,10 +124,9 @@ both:
106
124
  failure in the app under test.
107
125
 
108
126
  Absent both readings the verdict is unavailable and the failure routes as it did
109
- before. On the verdict: file the `meta::framework-gap` issue with the run link,
110
- the failure signature, and the attach attempt; flip the Story to
111
- `agent::blocked` with a `friction` comment naming the verdict; and hand back to
112
- the operator, who owns the sandbox. Do not author a fix for a tier you could not
127
+ before. On the verdict: run
128
+ `file-ci-gap.js --verdict unreproducible-tier --block`, passing the failed attach
129
+ as `--evidence`; then hand back to the operator, who owns the sandbox. Do not author a fix for a tier you could not
113
130
  run — a blind fix to a suite nobody exercised is how the gap compounds.
114
131
 
115
132
  ## Verifier
@@ -131,8 +148,9 @@ alongside the failing check-run identity. On green it adjudicates:
131
148
 
132
149
  - **Same head SHA** → the green came from re-running the failed job. The
133
150
  watcher exits non-zero, flips the Story to `agent::blocked` with a
134
- `friction` comment, and requires the `meta::framework-gap` issue (run link +
135
- failure signature, both already in the digest) before the delivery proceeds.
151
+ `friction` comment, and requires the CI-gap intake issue
152
+ (`file-ci-gap.js` — run link and failure signature are already in the
153
+ digest) before the delivery proceeds.
136
154
  - **New head SHA** → fix at source. The digest is retired, auto-merge is
137
155
  re-armed, and the delivery continues unobstructed.
138
156
 
@@ -153,8 +171,8 @@ operator under **any** of:
153
171
  - **Clearly-environmental → escalate immediately.** An unambiguously
154
172
  environmental failure outside your control (runner provisioning, a persistent
155
173
  registry/network outage, a branch-protection or CI misconfiguration, an
156
- expired credential) — file the `meta::framework-gap` issue (with run link +
157
- signature) and escalate on the first encounter rather than burning iterations
174
+ expired credential) — run `file-ci-gap.js --block` and escalate on the
175
+ first encounter rather than burning iterations
158
176
  trying to code around it. A proven-capacity failure is this case: reach the
159
177
  `capacity` verdict above and escalate on the first encounter.
160
178
  - **Unrunnable tier → escalate immediately.** A tier the sandbox cannot host at
@@ -169,6 +169,25 @@
169
169
  "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.",
170
170
  "default": 60000
171
171
  },
172
+ "followUpRepos": {
173
+ "type": "object",
174
+ "description": "Repository slugs for the non-consumer follow-up ownership buckets, used when a CI gap, retro proposal, or audit finding belongs to someone other than the repo that surfaced it.",
175
+ "properties": {
176
+ "framework": {
177
+ "type": "string",
178
+ "pattern": "^[^/\\s]+/[^/\\s]+$",
179
+ "description": "`<owner>/<repo>` that owns framework-level defects. Defaults to the Mandrel mirror — the one bucket with a knowable default.",
180
+ "default": "dsj1984/mandrel"
181
+ },
182
+ "platform": {
183
+ "type": ["string", "null"],
184
+ "pattern": "^[^/\\s]+/[^/\\s]+$",
185
+ "description": "`<owner>/<repo>` for a shared platform or infrastructure tracker (a shared base config, a runner fleet, a cross-repo toolchain). No default — nothing can guess a shared repo. Left unset, platform-owned findings file locally and say so.",
186
+ "default": null
187
+ }
188
+ },
189
+ "additionalProperties": false
190
+ },
172
191
  "branchProtection": {
173
192
  "type": "object",
174
193
  "description": "Branch-protection stance applied to `project.baseBranch` by the GitHub bootstrap, and reproduced locally before every push.",
@@ -377,6 +396,12 @@
377
396
  "minimum": 1,
378
397
  "description": "Recommend a consolidation pass once this many entries have been written since the last one. Measured against the entry count the last pass stamped, so a stamp predating that field leaves growth unmeasured and only the age threshold applies. Default 25.",
379
398
  "default": 25
399
+ },
400
+ "indexByteCeiling": {
401
+ "type": "integer",
402
+ "minimum": 1,
403
+ "description": "Recommend a consolidation pass once the pool's `MEMORY.md` index exceeds this many bytes. Independent of the age and growth thresholds: the harness truncates the index it loads into each session at its own byte cap, so an oversized index is a loss already happening — every entry listed after the cut is invisible — rather than a hygiene forecast. Default 24576, the harness cap itself.",
404
+ "default": 24576
380
405
  }
381
406
  },
382
407
  "additionalProperties": false
@@ -1975,6 +2000,12 @@
1975
2000
  },
1976
2001
  "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.",
1977
2002
  "default": []
2003
+ },
2004
+ "rerunAdvisory": {
2005
+ "type": "integer",
2006
+ "minimum": 0,
2007
+ "description": "Story #5266. How many times close may re-run a failed advisory workflow run before blocking on it, per close invocation. Default 0: close spends no CI minutes and issues no GitHub mutation on an advisory red unless asked. At n > 0 the failed run(s) are re-run within that allowance and the merge wait re-polls inside its existing budget, landing or blocking on the re-run verdict. Overridden per invocation by --rerun-advisory <n>.",
2008
+ "default": 0
1978
2009
  }
1979
2010
  },
1980
2011
  "additionalProperties": false
@@ -2084,7 +2115,9 @@
2084
2115
  "not": {
2085
2116
  "pattern": "([;&|`]|\\$\\()"
2086
2117
  },
2087
- "minLength": 1
2118
+ "minLength": 1,
2119
+ "pattern": "^[a-z0-9][a-z0-9._-]*(?:\\/[a-z0-9][a-z0-9._-]*)+$",
2120
+ "description": "Tier-relative skill id, e.g. `stack/qa/acme-sso`: lowercase segments of letters, digits, `.`, `_` or `-`, at least two of them, separated by `/`. A traversal (`../..`), an absolute path, a backslash or an uppercase segment is rejected here rather than normalized."
2088
2121
  }
2089
2122
  },
2090
2123
  "required": ["skill"],
@@ -30,7 +30,8 @@
30
30
  "arm-failure",
31
31
  "api-race-other",
32
32
  "predicate-refused",
33
- "advisory-gate-red"
33
+ "advisory-gate-red",
34
+ "advisory-gate-inconclusive"
34
35
  ]
35
36
  },
36
37
  "reason": { "type": "string", "minLength": 1 },
@@ -131,6 +131,7 @@
131
131
  "api-race-other",
132
132
  "predicate-refused",
133
133
  "advisory-gate-red",
134
+ "advisory-gate-inconclusive",
134
135
  "merged-flip-failed"
135
136
  ]
136
137
  },