@danmoisan/drm-copilot-mcp 1.0.23 → 1.0.26

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 (43) hide show
  1. package/out/mcp-server.js +504 -174
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agents/feature-review.md +5 -3
  4. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +11 -4
  5. package/resources/claude-customizations/.claude/agents/parallel-planner.md +5 -2
  6. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +28 -8
  7. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +28 -8
  8. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +117 -46
  9. package/resources/claude-customizations/.claude/lib/bash/parallel-manifest-validate.sh +115 -3
  10. package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +312 -0
  11. package/resources/claude-customizations/.claude/lib/codex-routing/CodexTopology.psm1 +392 -0
  12. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryValidation.psm1 +500 -0
  13. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +58 -67
  14. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCheckpointValue.psm1 +383 -0
  15. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCodexModelReceipts.psm1 +297 -0
  16. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCodexTopologyReceipts.psm1 +298 -0
  17. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletion.psm1 +232 -43
  18. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletionChecks.psm1 +416 -0
  19. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateModelReceipts.psm1 +366 -0
  20. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +408 -0
  21. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1 +428 -0
  22. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingMatrix.psm1 +377 -0
  23. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateUnconditional.psm1 +166 -0
  24. package/resources/claude-customizations/.claude/rules/general-unit-test.md +1 -1
  25. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +28 -3
  26. package/resources/claude-customizations/.claude/rules/powershell.md +1 -1
  27. package/resources/claude-customizations/.claude/rules/quality-tiers.md +3 -3
  28. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -4
  29. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +10 -5
  30. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -34
  31. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +71 -9
  32. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +7 -3
  33. package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +1 -1
  34. package/resources/claude-customizations/.claude-variants/csharp-legacy/rules/csharp.md +4 -4
  35. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +5 -3
  36. package/resources/claude-customizations/config/blast-radius.json +1 -3
  37. package/resources/claude-customizations/pack-manifests/core.json +12 -0
  38. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +1 -1
  39. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +3 -3
  40. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +3 -3
  41. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +5 -3
  42. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  43. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +21 -0
@@ -108,10 +108,10 @@ If the branch diff modifies any path matching `.github/workflows/**`, `scripts/b
108
108
  - Python: `poetry run pytest --cov` → artifact: `artifacts/python/lcov.info`
109
109
  - PowerShell: `mcp__drm-copilot__run_poshqc_test` → artifact: `artifacts/pester/powershell-coverage.xml`
110
110
  - C#: `dotnet test --collect:"XPlat Code Coverage"` → artifact: `artifacts/csharp/coverage.xml`
111
- - Coverage thresholds (uniform tier rule per quality-tiers.md):
112
- - New code files (added in this feature): line coverage >= 85% and branch coverage >= 75%. Flag as FAIL otherwise.
113
- - Modified files (changed but previously existing): line coverage >= 85%, branch coverage >= 75%, and no regression on changed lines relative to baseline. Flag as FAIL otherwise.
114
- - Repo-wide per language: line coverage >= 85% and branch coverage >= 75%. Flag as FAIL otherwise.
111
+ - Coverage thresholds (uniform tier rule per quality-tiers.md). The branch threshold applies only to branch-capable languages — TypeScript, Python, and C#. PowerShell is a coverage language and is fully subject to the line threshold and the no-regression requirement, but Pester measures command (instruction) coverage and line coverage only, so no branch percentage exists to evaluate and no branch threshold applies to it (see `.claude/rules/powershell.md`). Do not flag a missing PowerShell branch figure as FAIL:
112
+ - New code files (added in this feature): line coverage >= 85%, and branch coverage >= 75% for branch-capable languages. Flag as FAIL otherwise.
113
+ - Modified files (changed but previously existing): line coverage >= 85%, branch coverage >= 75% for branch-capable languages, and no regression on changed lines relative to baseline. Flag as FAIL otherwise.
114
+ - Repo-wide per language: line coverage >= 85%, and branch coverage >= 75% for branch-capable languages. Flag as FAIL otherwise.
115
115
  - If coverage artifacts already exist from the executor run, inspect them instead of re-running.
116
116
  - If no coverage artifact exists for a language that has changed files, flag as FAIL — coverage verification is mandatory for all languages with changed files.
117
117
  - Run the smallest relevant subset first when the repo policy permits it.
@@ -78,16 +78,21 @@ re-derivation is mandatory and is not an optimization to skip when the checkpoin
78
78
  current-cohort member, so no cohort assignment needs to change.
79
79
  - `DEFER_AND_RECOLOR` — the candidate shares an edge with at least one member of the current
80
80
  cohort, pinned or not-yet-launched. Defer it to a future cohort and recolor by calling
81
- `recolor_unstarted(unstarted_items, conflict_edges, pinned, current_generation, current_cohort=current_cohort)`.
81
+ `recolor_unstarted(unstarted_items, conflict_edges, pinned, current_generation, current_cohort=current_cohort, highest_pinned_cohort=highest_pinned_cohort)`.
82
+ `highest_pinned_cohort` is derived from re-verified durable state: the highest
83
+ current-generation cohort index occupied by any in-flight item.
82
84
  The recolor is a recompute: `recolor_generation` increments by exactly one, and it places every
83
- unstarted item at an index at or above `current_cohort`, strictly above it when a pinned
84
- conflict exists.
85
+ unstarted item at an index at or above `current_cohort`, strictly above
86
+ `highest_pinned_cohort` when a pinned conflict exists.
85
87
 
86
88
  Derive `current_cohort_members` from the re-verified durable state, not from the cached
87
89
  checkpoint: it is the full membership of the current-generation cohort at `current_cohort`,
88
90
  INCLUDING its not-yet-launched `scheduled` members. Derive `current_cohort` from that same
89
- re-verified state; it is F3's top-level `current_cohort` field and is the index the pinned items
90
- occupy. Both matter because `max_concurrency` caps simultaneously in-flight items independently
91
+ re-verified state; it is F3's top-level `current_cohort` field, the lowest current-generation
92
+ cohort index still holding a non-terminal item. Under the per-edge barrier an in-flight item is
93
+ not confined to that index, so derive `highest_pinned_cohort` from the same re-verified state as
94
+ well: the highest current-generation cohort index occupied by any in-flight item. Both matter
95
+ because `max_concurrency` caps simultaneously in-flight items independently
91
96
  of cohort size and refills each freed slot from the same current cohort — see
92
97
  `## Cohort Barrier and Max-Concurrency Slot Filling` in
93
98
  `.claude/skills/parallel-orchestrate/SKILL.md` — so the current cohort durably holds `scheduled`
@@ -65,7 +65,7 @@ Consumption rules:
65
65
  so it does not drift when an item's folder moves from `docs/features/active/` to
66
66
  `docs/features/completed/`.
67
67
  - Read `mode` (`closed` or `open`, defaulting to `closed`), `max_concurrency` (an integer from 1
68
- through 8, defaulting to 4), and each item's identity and state: `feature_folder`, `kind`,
68
+ through 32, defaulting to 4), and each item's identity and state: `feature_folder`, `kind`,
69
69
  `state`, and `blast_radius`.
70
70
  - The manifest is read-only to `parallel-orchestrator`. It is static input authored by
71
71
  `parallel-planner`: never write it, rewrite it, or back-fill a field into it.
@@ -111,23 +111,58 @@ in order to combine two cohorts or widen a launch batch.
111
111
 
112
112
  ## Cohort Barrier and Max-Concurrency Slot Filling
113
113
 
114
- Two independent controls govern every launch. The cohort barrier governs when a cohort may start.
115
- `max_concurrency` governs how many items of a started cohort run at once. Neither substitutes for
114
+ Two independent controls govern every launch. The cohort barrier governs when an individual item
115
+ may start. `max_concurrency` governs how many eligible items run at once. Neither substitutes for
116
116
  the other.
117
117
 
118
- **Cohort barrier.** Cohort `N+1` branches from `main` only after every cohort-`N` item is `merged`
119
- or `worktree_removed`. Increment `current_cohort` only on durable confirmation from
120
- `git worktree list --porcelain`, `git branch`, and
121
- `gh pr view --json state,mergedAt,headRefOid` never from an in-memory completion notification. A
122
- blocked item (`blocked_ci_loop_limit` or `blocked_drift`) is neither `merged` nor
123
- `worktree_removed`, so a blocked item holds the barrier and cohort `N+1` does not start.
118
+ **Cohort barrier (per-edge).** An item may start only when every conflicting neighbour
119
+ (`conflict_edges[]`) that sits in a strictly prior current-generation cohort has `merge_status` of
120
+ `merged` or `worktree_removed`. `ci_green` does not satisfy the barrier: the pull request is not
121
+ merged, so its work is not on `main`. Same-cohort and later-cohort neighbours do not hold an item
122
+ back, and items with no conflicting prior-cohort neighbour may start regardless of other cohorts'
123
+ progress. The barrier is a predicate over one item's own conflict edges, not a global gate over
124
+ whole cohorts.
125
+
126
+ Evaluate the predicate only against durable state read from `git worktree list --porcelain`,
127
+ `git branch`, and `gh pr view --json state,mergedAt,headRefOid` — never from an in-memory
128
+ completion notification. A blocked item (`blocked_ci_loop_limit` or `blocked_drift`) is neither
129
+ `merged` nor `worktree_removed`, so it holds every conflicting later-cohort neighbour and,
130
+ transitively, the tail of its own conflict component; items outside that component are unaffected.
131
+
132
+ **`current_cohort` is a progress indicator, not a gate.** `current_cohort` is the LOWEST
133
+ current-generation cohort index that still contains a non-terminal, non-withdrawn item. It is
134
+ recomputed and written only on durable confirmation from `git worktree list --porcelain`,
135
+ `git branch`, and `gh pr view --json state,mergedAt,headRefOid` — never from an in-memory
136
+ completion notification. It gates nothing: no item's eligibility is decided by comparing it to
137
+ `current_cohort`, because eligibility is the per-edge predicate above. It is reported in the status
138
+ document, is the base index the mutation engine recolors from, and is bounded by rule invariant 14,
139
+ whose text is unchanged. Because the barrier is per-edge, in-flight items are not confined to
140
+ `current_cohort`; the highest current-generation index any pinned item occupies is a separate value
141
+ (`highest_pinned_cohort`, see `## Membership Mutation Protocol (F6)`).
142
+
143
+ **Safety argument.** An item that starts under the per-edge rule while a non-conflicting
144
+ prior-cohort item is still open branches from a `main` that lacks only non-conflicting merged work.
145
+ That is byte-for-byte the situation the same-cohort merge-order text above already accepts as safe:
146
+ "Items within a cohort are non-conflicting by construction — a cohort is an independent set in the
147
+ conflict graph — so they may branch from the same `main` tip and may merge in any order." The
148
+ per-edge barrier extends that accepted situation across cohort boundaries without weakening it,
149
+ because the only work the starting item can be missing is work it does not conflict with. That
150
+ same-cohort text is unchanged by this rule.
151
+
152
+ **Availability argument.** Under a global barrier a single `blocked_ci_loop_limit` or
153
+ `blocked_drift` item halts every lane, because no item of the next cohort may start until every
154
+ item of the current one is terminal. Under the per-edge rule the blocked item holds only its own
155
+ conflict component's tail: its conflicting later-cohort neighbours, and transitively theirs.
156
+ Unrelated lanes keep advancing. That is the difference between one stuck item stalling a 13-lane
157
+ run and one stuck item stalling one lane.
124
158
 
125
159
  **`max_concurrency` slot filling.** `max_concurrency` caps the number of simultaneously in-flight
126
160
  items independently of cohort size: a cohort of twelve items executes at most `max_concurrency`
127
161
  items at a time. Fill slots in ascending item-key order, keyed on `issue_num`, and refill each
128
162
  freed slot with the next unstarted item of the current cohort in that same ascending item-key
129
- order. A cohort larger than `max_concurrency` therefore launches in several batches from the same
130
- recorded `main` tip. The batching is a pure function, reached on the destination-runtime path as
163
+ order. A cohort larger than `max_concurrency` therefore launches in several batches, each from the
164
+ `main` tip recorded for that batch. The batching is a pure function, reached on the
165
+ destination-runtime path as
131
166
  `bash .claude/lib/bash/compute-concurrency-batches.sh --keys "<k1> <k2> ..." --max-concurrency <n>`.
132
167
  It prints a compact JSON array of arrays, returns the batches in order, and sorts the keys itself,
133
168
  so determinism does not depend on caller ordering.
@@ -152,11 +187,33 @@ fire per call with no cross-call state visibility.
152
187
  Neither layer is shipped by this feature; both are named here so the obligation is legible to an
153
188
  operator and to the F7 planner. Until F7 lands, the barrier is enforced by this procedure alone.
154
189
 
190
+ **The two layers fail closed differently, and the difference is deliberate.** Do not read either
191
+ layer's silence as permission.
192
+
193
+ - Layer 1 is PROSPECTIVE and evaluated per launch, so it denies fail-closed on every condition that
194
+ leaves the target's own eligibility unknowable: a missing or unparseable checkpoint, an unresolved
195
+ feature-folder token, a missing `items[]` record for the target, a target with no
196
+ current-generation cohort assignment, a missing neighbour record, and a missing neighbour
197
+ `merge_status`. Its one permissive case is neighbour-side: a NEIGHBOUR that carries no
198
+ current-generation cohort assignment is skipped rather than denied, because such a neighbour sits
199
+ in no prior cohort and therefore constrains nothing.
200
+ - Layer 2 is RETROSPECTIVE and evaluated per edge, so it is deliberately silent on an edge it cannot
201
+ judge; a malformed edge is invariant 15's to report, not the barrier's. It applies three readings
202
+ of the same edge. The STRUCTURAL reading rejects two conflicting items colored into the same
203
+ current-generation cohort outright — a violation Layer 1 has no counterpart for, because Layer 1
204
+ only ever asks about strictly prior cohorts. The STATUS reading is the retrospective
205
+ contrapositive of the per-edge launch rule. The TEMPORAL reading rejects
206
+ `merged_at(earlier) > worktree_created_at(later)`, and degrades to the status reading alone when
207
+ either timestamp is absent or is not a string.
208
+
155
209
  ## Per-Item Branch and Worktree Lifecycle
156
210
 
157
- 1. Run one `git fetch origin main` immediately before each cohort launch, so every item in that
158
- cohort branches from the same current remote `main` tip rather than from a stale local ref.
159
- Record the fetched tip.
211
+ 1. Run one `git fetch origin main` immediately before each launch batch, so every item in that
212
+ batch branches from the same current remote `main` tip rather than from a stale local ref.
213
+ The unit is the launch batch, not the cohort: under the per-edge barrier a cohort's items
214
+ become eligible at different times, and a cohort larger than `max_concurrency` launches in
215
+ several batches, so a per-cohort fetch would leave later batches on a stale tip. Record the
216
+ fetched tip for each batch.
160
217
  2. Each item's worktree is created by that item's delegation spawn,
161
218
  `Agent(orchestrator, isolation: "worktree", run_in_background: true)`, branched from
162
219
  `origin/main`. Do not create or check out item worktrees by hand.
@@ -310,8 +367,11 @@ this surface.
310
367
  feature.
311
368
  5. On loop exhaustion, the parent records the terminal `merge_status: blocked_ci_loop_limit` for the
312
369
  item; the child's own checkpoint retains its precise blocked status. A blocked item is neither
313
- `merged` nor `worktree_removed`, so it holds the cohort barrier defined in
314
- `## Cohort Barrier and Max-Concurrency Slot Filling`.
370
+ `merged` nor `worktree_removed`, so under the per-edge barrier defined in
371
+ `## Cohort Barrier and Max-Concurrency Slot Filling` it holds back exactly its own conflicting
372
+ later-cohort neighbours — and, transitively, the tail of its own conflict component. Every item
373
+ outside that component, including every item of a later cohort that shares no conflict edge with
374
+ it, remains eligible and continues to launch.
315
375
 
316
376
  Boundary with F8: a merge conflict between two same-cohort items is evidence that the declared
317
377
  blast radius under-reported, and this feature records the child's blocked or remediated outcome
@@ -469,23 +529,29 @@ integers (`items[].issue_num`) everywhere on this surface.
469
529
  ### Pinning invariant
470
530
 
471
531
  **In-flight items are pinned. Scheduling is recomputed only over the not-yet-started subgraph, and
472
- recoloring is a pure function of `(remaining subgraph, pinned set, pinned cohort index)`.**
532
+ recoloring is a pure function of `(remaining subgraph, pinned set, pinned cohort indices)`.**
473
533
 
474
534
  The recolor function takes the induced subgraph of unstarted items (states `proposed`, `admitted`,
475
- `prepared`, `scheduled`), the pinned set (state `in_flight`), the current generation, and — as a
476
- third scheduling inputthe current cohort index `current_cohort` that the pinned items occupy. It
477
- returns cohort assignments for unstarted items ONLY: the returned mapping's key set equals the
478
- unstarted set exactly and contains no pinned key. A pinned item is therefore absent from the result
479
- rather than reassigned, and that absence IS the guarantee that a mutation never moves work already
480
- running.
535
+ `prepared`, `scheduled`), the pinned set (state `in_flight`), the current generation, and — as two
536
+ further scheduling inputs — `current_cohort` and `highest_pinned_cohort`, the highest
537
+ current-generation cohort index occupied by any pinned item. Both are derived from re-verified
538
+ durable state. Two inputs are required because the per-edge barrier does not confine in-flight items
539
+ to one index: an item starts as soon as its own conflicting prior-cohort neighbours are terminal, so
540
+ the pinned frontier can span several cohorts. It returns cohort assignments for unstarted items
541
+ ONLY: the returned mapping's key set equals the unstarted set exactly and contains no pinned key. A
542
+ pinned item is therefore absent from the result rather than reassigned, and that absence IS the
543
+ guarantee that a mutation never moves work already running.
481
544
 
482
545
  **Pinned-barrier offset.** The returned indices are ABSOLUTE checkpoint cohort indices at or above
483
- `current_cohort`, and strictly above `current_cohort` whenever any conflict edge joins an unstarted
484
- item to a pinned item. When no such edge exists the lowest returned index equals `current_cohort`
485
- exactly, so unstarted items may share the running cohort and `max_concurrency` slot filling is
486
- preserved. The offset is a single uniform shift applied to every color class, so F2's distinct color
487
- classes remain distinct cohort indices and independence within the unstarted set is preserved
488
- exactly.
546
+ `current_cohort`, and strictly above `highest_pinned_cohort` whenever any conflict edge joins an
547
+ unstarted item to a pinned item. When no such edge exists the lowest returned index equals
548
+ `current_cohort` exactly, so unstarted items may share the running cohort and `max_concurrency` slot
549
+ filling is preserved. Shifting above the highest pinned index rather than above `current_cohort`
550
+ alone is what keeps a deferred candidate off the index of any pinned item it conflicts with when
551
+ the pinned frontier spans more than one cohort. The offset is a single uniform shift applied to
552
+ every color class, so F2's distinct color classes remain distinct cohort indices and independence
553
+ within the unstarted set is preserved exactly. When every pinned item sits at `current_cohort` the
554
+ two inputs coincide and the offset is identical to the earlier single-frontier rule.
489
555
 
490
556
  Write the returned indices VERBATIM into `cohorts[].index`; never re-base them to zero. `cohorts[]`
491
557
  carries exactly ONE current-generation entry per index, so returned keys landing on index
@@ -506,8 +572,14 @@ freed slot from the same current cohort — see
506
572
  `## Cohort Barrier and Max-Concurrency Slot Filling` — so the current cohort durably holds
507
573
  not-yet-launched `scheduled` members that a candidate can contend with. Recoloring previously
508
574
  dropped the candidate-to-pinned edges together with the pinned vertices, which discarded the pinned
509
- CONSTRAINT as well as the pinned VERTICES and returned a deferred candidate to cohort 0, the current
510
- cohort, whenever the cohort barrier held `current_cohort` at 0.
575
+ CONSTRAINT as well as the pinned VERTICES and returned a deferred candidate to the current cohort,
576
+ undoing the deferral.
577
+
578
+ **Third design correction (per-edge barrier).** The offset previously shifted to
579
+ `current_cohort + 1`, which was sound only while the documented barrier was global and every pinned
580
+ item therefore sat at `current_cohort`. Under the per-edge barrier the pinned frontier can span
581
+ several indices, so the offset now shifts above `highest_pinned_cohort`. Where the frontier is a
582
+ single index the two expressions agree, so no reachable earlier recoloring changed.
511
583
 
512
584
  ### Recompute boundary
513
585
 
@@ -613,9 +685,11 @@ engine's `build_requeue_entry` constructor and the recolor through `recolor_unst
613
685
  `new_state: blocked`, `disposition: null`, and `recolor_generation` equal to `g` + 1 — the requeue
614
686
  is a recompute.
615
687
  - The recolor runs over the unstarted subgraph only, so no other in-flight item moves. Its call
616
- shape is the five-argument form
617
- `recolor_unstarted(unstarted_items, conflict_edges, pinned, current_generation, current_cohort=current_cohort)`,
618
- where `current_cohort` is required and keyword-only.
688
+ shape is the six-argument form
689
+ `recolor_unstarted(unstarted_items, conflict_edges, pinned, current_generation, current_cohort=current_cohort, highest_pinned_cohort=highest_pinned_cohort)`,
690
+ where `current_cohort` and `highest_pinned_cohort` are both required and keyword-only.
691
+ `highest_pinned_cohort` is derived from re-verified durable state: the highest
692
+ current-generation cohort index occupied by any in-flight item.
619
693
 
620
694
  The drift event itself is recorded in `drift_events[]`, which this protocol does not write. See
621
695
  `## Radius Drift Detection (F8)`.
@@ -62,9 +62,42 @@ Intake proceeds directly to preparation fan-out.
62
62
  ## Preparation Fan-Out
63
63
 
64
64
  One preparation-mode `Agent(orchestrator)` run per item. Preparation produces documents and plans
65
- rather than code, and items carry no ordering constraint, so launch ALL item preparations
66
- concurrently: one message, N `Agent` calls, each `isolation: "worktree"` and
67
- `run_in_background: true`. Create each preparation worktree's branch from `origin/main`.
65
+ rather than code, and items carry no ordering constraint, so preparations may run concurrently —
66
+ but they are BOUNDED, not unbounded.
67
+
68
+ **Launch preparations in waves of at most `max_concurrency`.** Compute the waves with the same
69
+ deterministic chunker the execution phase uses:
70
+
71
+ ```
72
+ bash .claude/lib/bash/compute-concurrency-batches.sh --keys "<all item keys>" --max-concurrency <n>
73
+ ```
74
+
75
+ (already granted to this agent at `.claude/agents/parallel-planner.md:18`). It prints a compact
76
+ JSON array of arrays, returns the batches in order, and sorts the keys itself, so wave membership
77
+ does not depend on caller ordering. Launch wave *k* as one message carrying that wave's `Agent`
78
+ calls, each `isolation: "worktree"` and `run_in_background: true`.
79
+ Create each preparation worktree's branch from `origin/main`.
80
+ Launch wave *k+1* only after every child of wave *k* has TERMINATED — not merely reported
81
+ progress. All of this happens inside a single `/parallel-plan` invocation with no operator action
82
+ between waves.
83
+
84
+ The bound is `max_concurrency` itself, and no new knob is introduced. A preparation child and an
85
+ execution child are the same workload class — one background orchestrator per item — so the
86
+ operator's declared appetite for concurrent children applies to both phases. At the motivating
87
+ scale, `max_concurrency: 13` over 69 items runs `ceil(69 / 13) = 6` waves.
88
+
89
+ **A `max_preparation_concurrency` manifest key was considered and is explicitly NOT adopted now.**
90
+ Such a key would carry the same `1..32` bounds and the same boolean rejection as M4 and would
91
+ default to `max_concurrency`. It is deferred because no evidence yet shows the two phases need
92
+ different caps, and adding a second knob would force every operator to reason about two numbers
93
+ where one suffices. Revisit it only if a real run shows preparation and execution have materially
94
+ different concurrency profiles.
95
+
96
+ **`/parallel-add` is NOT the intake path.** It performs incremental admission into an
97
+ already-running open-mode queue: exactly one item per invocation, with a single sequential
98
+ preparation child. Preparing 69 items through it would take 57 or more separate operator
99
+ invocations after the initial plan. Use `/parallel-plan` for intake and `/parallel-add` only to
100
+ admit an item into a run that is already in flight.
68
101
 
69
102
  Each delegation prompt includes this literal kickoff line, followed by the model-budget marker
70
103
  line:
@@ -250,10 +283,27 @@ The library returns the partition; the planner supplies the record fields.
250
283
  item is `prepared` and radius-validated. Derive the conflict edge set by applying
251
284
  `Test-BlastRadiusConflict` to every unordered pair of `declared` radii, then pass the pairs as
252
285
  `--edges "<a>:<b> ..."` and the item keys as `--keys "<k1> <k2> ..."`.
253
- 2. Record `cohorts[]` at `generation: 0`, each cohort's `item_keys[]` sorted ascending.
254
- 3. Record `conflict_edges[]` as `{a, b, reason}` entries for auditability.
255
- 4. Record `recolor_generation: 0` and `current_cohort: 0`.
256
- 5. Record `max_concurrency` default 4, bounded 1 through 8 by the F3 schema — without enforcing
286
+ 2. Immediately after the conflict-edge set is derived and before anything consumes it, run the
287
+ lane-assertion diagnostic:
288
+ `poetry run python -m scripts.dev_tools.parallel_lane_assertion --manifest docs/features/parallel/<slug>/parallel.md --edges "<a>:<b> ..."`
289
+ (covered by the planner's existing `Bash(poetry run *)` grant). It compares the manifest's
290
+ optional `expected_conflict_components` assertion (invariant M8) against the connected
291
+ components of the DERIVED conflict graph and prints one `ADVISORY` line per finding in four
292
+ classes: expected-together-but-derived-apart, expected-apart-but-derived-together, a member
293
+ naming no manifest item, and — informational only — a manifest item covered by no expected
294
+ component.
295
+ **The diagnostic is ADVISORY ONLY.** It never blocks the run, never modifies or suppresses a
296
+ derived edge, never feeds `compute_cohorts`, and never influences scheduling. It always exits 0,
297
+ including when it reports disagreements. A disagreement is a signal to re-examine the blast
298
+ radii; it is never a licence to narrow a radius to suppress an edge, which stays prohibited.
299
+ When the manifest carries no `expected_conflict_components` key the diagnostic still runs and
300
+ reports every item as uncovered, which is the expected output for a run with no assertion.
301
+ Recording the diagnostic's result in the planner checkpoint is a tolerated extra field, not a
302
+ validated one; no validator changes for it.
303
+ 3. Record `cohorts[]` at `generation: 0`, each cohort's `item_keys[]` sorted ascending.
304
+ 4. Record `conflict_edges[]` as `{a, b, reason}` entries for auditability.
305
+ 5. Record `recolor_generation: 0` and `current_cohort: 0`.
306
+ 6. Record `max_concurrency` — default 4, bounded 1 through 32 by the F3 schema — without enforcing
257
307
  it. Enforcement is F5's, through
258
308
  `bash .claude/lib/bash/compute-concurrency-batches.sh --keys "<k1> ..." --max-concurrency <n>`
259
309
  (the bash port of `compute_concurrency_batches(cohort_item_keys, max_concurrency)`), which fills
@@ -281,16 +331,23 @@ No production module is added for this check; it is a re-invocation of the lande
281
331
  ## Manifest Authoring
282
332
 
283
333
  Write `docs/features/parallel/<slug>/parallel.md` conforming to the F3-owned frontmatter schema
284
- recorded in `.claude/rules/parallel-orchestration.md` (manifest invariants M1-M7):
334
+ recorded in `.claude/rules/parallel-orchestration.md` (manifest invariants M1-M8):
285
335
 
286
336
  - `parallel` — the run slug, a non-empty string.
287
337
  - `mode` — `closed` or `open`; defaults to `closed` when absent.
288
- - `max_concurrency` — an integer from 1 through 8; defaults to `4` when absent.
338
+ - `max_concurrency` — an integer from 1 through 32; defaults to `4` when absent.
289
339
  - `created_at` — a non-empty ISO-8601 string.
290
340
  - `items[]` — one entry per item, each carrying `issue_num` (a positive integer, unique across
291
341
  items), `feature_folder` (a non-empty string), `kind` (`feature` or `bug`), `state`, and
292
342
  `blast_radius` carrying `paths`, `modules`, `shared_surfaces`, `contracts`, `source: "declared"`,
293
343
  and `computed_at`.
344
+ - `expected_conflict_components[]` — OPTIONAL (invariant M8). A block sequence of objects, each
345
+ carrying a required non-empty `members` list of positive `issue_num` integers that resolve to
346
+ declared items, with no item in two components, plus an optional non-empty-string `name` used as
347
+ a diagnostic label only. A flow-style value (`members: [101, 102]`) is outside the bash YAML
348
+ subset and must not be authored. The field is an ASSERTION consumed by the advisory lane
349
+ diagnostic in `### Seeding procedure`: it never overrides a derived edge, never feeds
350
+ `compute_cohorts`, and never influences scheduling.
294
351
 
295
352
  The manifest carries no `depends_on` field at any level and no top-level `integration_branch`
296
353
  field; both are prohibited-key rejections in the schema. Commit it to `parallel/<slug>-plan` in
@@ -454,6 +511,11 @@ The final report to the operator must include:
454
511
  - Per item: one `plan-path:` line, the branch name, the preflight status, and the
455
512
  radius-validation result, including any V3 Advisory findings.
456
513
  - The cohort table at `generation 0`, together with the result of the recomputation-parity check.
514
+ - The lane-assertion diagnostic's result: the derived conflict-component count, the disagreement
515
+ count, and every `ADVISORY` line it emitted. This line-item is REQUIRED and is reported even when
516
+ the manifest carries no `expected_conflict_components` assertion and even when the diagnostic
517
+ found nothing, so its silence is never ambiguous. Report it as advisory information: it does not
518
+ gate the report, does not change the cohort table, and no finding is escalated to Blocking.
457
519
  - Both kickoff artifact paths: `artifacts/orchestration/parallel-kickoff-<slug>.md` and
458
520
  `docs/features/parallel/<slug>/parallel-kickoff.md`.
459
521
 
@@ -79,13 +79,17 @@ Do not record a partial removal, and do not record the rejection itself in `muta
79
79
 
80
80
  3. **Unstarted removal (recompute).** Set the item's state to `withdrawn`, drop its vertex, and
81
81
  recolor by calling `recolor_unstarted(unstarted_items, conflict_edges, pinned,
82
- current_generation, current_cohort=current_cohort)`. Write
82
+ current_generation, current_cohort=current_cohort,
83
+ highest_pinned_cohort=highest_pinned_cohort)`. Write
83
84
  `RecolorResult.cohort_assignments` into `cohorts[]` and set the top-level `recolor_generation`
84
85
  to `RecolorResult.generation`; the generation increments by exactly one. The result names no
85
86
  pinned key, so no in-flight item moves.
86
87
 
87
- `current_cohort` is F3's top-level field, read from the re-verified durable state, and is the
88
- index the pinned items occupy. The returned indices are ABSOLUTE and are written VERBATIM into
88
+ `current_cohort` is F3's top-level field, read from the re-verified durable state: the lowest
89
+ current-generation cohort index still holding a non-terminal item. Under the per-edge barrier an
90
+ in-flight item is not confined to that index, so `highest_pinned_cohort` — the highest
91
+ current-generation cohort index occupied by any in-flight item — is read from the same
92
+ re-verified state. The returned indices are ABSOLUTE and are written VERBATIM into
89
93
  `cohorts[].index`, never re-based to zero. Returned keys whose index equals `current_cohort` are
90
94
  MERGED into the single existing current-generation cohort entry at that index alongside its
91
95
  pinned members, never written as a second entry carrying the same `index`, which F3 invariant 13
@@ -42,7 +42,7 @@ Compare the final results to the Phase A baseline. All of the following must hol
42
42
  - **Pester delta**: 0 new failing tests.
43
43
  - **Per-file coverage delta**: coverage for every touched file is greater than or equal to the baseline for that file.
44
44
  - **Overall coverage delta** (when the repo enforces it): overall coverage is greater than or equal to the baseline.
45
- - **New modules, classes, or methods**: line coverage >= 85% and branch coverage >= 75% per the uniform tier rule (`.claude/rules/quality-tiers.md`). No tier-specific lower thresholds. No regression on changed lines.
45
+ - **New modules, classes, or methods**: line coverage >= 85% per the uniform tier rule (`.claude/rules/quality-tiers.md`). No tier-specific lower thresholds. No regression on changed lines. Pester measures command (instruction) coverage and line coverage only; branch coverage is not measurable for PowerShell, so no branch-coverage gate applies here (see `.claude/rules/powershell.md`). Command coverage is informational and carries no threshold.
46
46
 
47
47
  If any delta check fails, the agent must revert or fix immediately and rerun the full toolchain. Do not proceed to reporting until all deltas are clean.
48
48
 
@@ -11,9 +11,9 @@ This rule file summarizes the C#-specific policies for this repository.
11
11
 
12
12
  ## Toolchain
13
13
 
14
- 1. **Formatting — CSharpier**: All C# source files must be formatted with CSharpier. Do not use `dotnet format`. Command: `dotnet tool run csharpier .` or `csharpier .`
15
- 2. **Linting — .NET Analyzers**: C# code must pass Roslyn/.NET analyzer diagnostics. Command: `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`
16
- 3. **Type Checking — Nullable Analysis**: Enable nullable reference types and fail on warnings. Command: `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true`
14
+ 1. **Formatting — CSharpier**: All C# source files must be formatted with CSharpier. Do not use `dotnet format`. Run `dotnet tool restore` first when the manifest tool has not been restored. Apply formatting with `dotnet tool run csharpier format .` and verify read-only with `dotnet tool run csharpier check .`. Always invoke through `dotnet tool run` so the manifest-pinned CSharpier version is used.
15
+ 2. **Linting — .NET Analyzers**: C# code must pass Roslyn/.NET analyzer diagnostics. Command: `msbuild <solution>.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`. `/t:Rebuild` is intentional for a warm local worktree: `/t:Build` can skip `CoreCompile` through MSBuild incrementality and exit 0 without running analyzers. CI may retain `/t:Build` on a cold checkout.
16
+ 3. **Type Checking — Nullable Analysis**: Compiler and nullable-flow diagnostics must pass with warnings as errors. Command: `msbuild <solution>.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`. `/t:Rebuild` is required locally so compiler and nullable-flow diagnostics actually run. Projects opt into nullable per file with `#nullable enable`; do not pass `/p:Nullable=enable`, which opts every unannotated file in at once.
17
17
  4. **Testing — MSTest + Moq + FluentAssertions**: Run tests with: `vstest.console.exe <test-assembly-paths> /EnableCodeCoverage`
18
18
 
19
19
  Run the toolchain in order: format → lint → type-check → test. Restart from step 1 if any step fails or changes files.
@@ -80,7 +80,7 @@ This repository adopts a fixed set of FIVE static-analysis packages, wired into
80
80
 
81
81
  ### Severity-first ordering invariant
82
82
 
83
- All new analyzer rule severities are configured in `.editorconfig` at `severity = suggestion` (never `warning`/`error`) BEFORE any `<Analyzer Include>` item is wired into a project. This is required because the type-check toolchain step runs `msbuild ... /p:Nullable=enable /p:TreatWarningsAsErrors=true`, which promotes any `warning`-severity analyzer diagnostic to a build error. Keeping new analyzer diagnostics at `suggestion` (message level) prevents the analyzer adoption from breaking the protected nullable gate.
83
+ All new analyzer rule severities are configured in `.editorconfig` at `severity = suggestion` (never `warning`/`error`) BEFORE any `<Analyzer Include>` item is wired into a project. This is required because the type-check toolchain step runs `msbuild ... /p:TreatWarningsAsErrors=true`, which promotes any `warning`-severity analyzer diagnostic to a build error. Keeping new analyzer diagnostics at `suggestion` (message level) prevents the analyzer adoption from breaking the protected nullable gate.
84
84
 
85
85
  ### Deferred analyzer — SecurityCodeScan.VS2019
86
86
 
@@ -27,11 +27,13 @@ Before invoking this gate, the agent must have:
27
27
 
28
28
  Run the full toolchain in this exact order. If any step fails or modifies files, fix the issue and restart from step 1. Do not stop the loop until all four steps complete without errors in a single pass.
29
29
 
30
- 1. `dotnet tool run csharpier .`
31
- 2. `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`
32
- 3. `msbuild <solution>.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true`
30
+ 1. `dotnet tool restore` (when the manifest tool has not been restored), then `dotnet tool run csharpier format .` to apply formatting and `dotnet tool run csharpier check .` to verify read-only.
31
+ 2. `msbuild <solution>.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`
32
+ 3. `msbuild <solution>.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`
33
33
  4. `vstest.console.exe <test-assembly-paths> /EnableCodeCoverage`
34
34
 
35
+ `/t:Rebuild /m` is intentional for the local gate: on a warm worktree `/t:Build` can skip `CoreCompile` through MSBuild incrementality and exit 0 without running analyzers or the compiler. CI may retain `/t:Build` on a cold checkout. Projects opt into nullable per file with `#nullable enable`; do not pass `/p:Nullable=enable`.
36
+
35
37
  If the environment prevents running any tool, stop and report the change as **unverified**. Do not declare completion.
36
38
 
37
39
  ## Delta Requirements (Zero-Regression Hard Gate)
@@ -8,9 +8,7 @@
8
8
  "shared_surface_globs": [],
9
9
  "modules": {
10
10
  "claude-runtime": [".claude/**"],
11
- "config": ["config/**"],
12
- "docs": ["docs/**"],
13
- "tests": ["tests/**"]
11
+ "config": ["config/**"]
14
12
  },
15
13
  "over_breadth_fraction": 0.25
16
14
  }
@@ -107,6 +107,18 @@
107
107
  ".claude/lib/model-routing/ModelRouting.psm1",
108
108
  ".claude/lib/orchestrator-state/OrchestratorState.psm1",
109
109
  ".claude/lib/orchestrator-state/OrchestratorStateCompletion.psm1",
110
+ ".claude/lib/orchestrator-state/OrchestratorStateCheckpointValue.psm1",
111
+ ".claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1",
112
+ ".claude/lib/orchestrator-state/OrchestratorStateModelReceipts.psm1",
113
+ ".claude/lib/orchestrator-state/OrchestratorStateCodexModelReceipts.psm1",
114
+ ".claude/lib/orchestrator-state/OrchestratorStateCodexTopologyReceipts.psm1",
115
+ ".claude/lib/orchestrator-state/OrchestratorStateRoutingMatrix.psm1",
116
+ ".claude/lib/orchestrator-state/OrchestratorStateCompletionChecks.psm1",
117
+ ".claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1",
118
+ ".claude/lib/orchestrator-state/OrchestratorStateUnconditional.psm1",
119
+ ".claude/lib/discovery-validation/DiscoveryValidation.psm1",
120
+ ".claude/lib/codex-routing/CodexDeployment.psm1",
121
+ ".claude/lib/codex-routing/CodexTopology.psm1",
110
122
  ".claude/lib/blast-radius/BlastRadiusExtraction.psm1",
111
123
  ".claude/lib/blast-radius/BlastRadiusGlob.psm1",
112
124
  ".claude/lib/blast-radius/BlastRadiusConfig.psm1",
@@ -26,7 +26,7 @@ Every unit test must satisfy all five of these properties:
26
26
  ## Coverage Requirements
27
27
 
28
28
  - **Line coverage must remain >= 85% across all tiers (T1–T4).**
29
- - **Branch coverage must remain >= 75% across all tiers (T1–T4).**
29
+ - **Branch coverage must remain >= 75% across all tiers (T1–T4) for languages whose coverage tooling measures branch coverage.** PowerShell (Pester) and bash (kcov) are the exceptions: neither tool measures branch coverage in any output format, so only the line threshold applies to them and there is no branch-coverage gate. This is a threshold exemption only; PowerShell and bash production files remain in the coverage denominator under the Coverage Exclusion Policy below.
30
30
  - Code changes or refactors must not reduce coverage for the lines that were changed.
31
31
  - Tier-specific lower coverage thresholds are not used in this repository. See `.agents/skills/quality-tiers.md` for the full tier system.
32
32
  - Coverage is a supporting metric, not the sole quality gate. Untested critical behavior is not acceptable even if the overall percentage looks good.
@@ -27,7 +27,7 @@ This rule defines the T1–T4 module rigor tier system used by all CI gates in t
27
27
 
28
28
  ## Uniform-vs-Tier-Dependent Gate Matrix
29
29
 
30
- Per Authoritative Decision #2, line and branch coverage thresholds are uniform across all tiers. Other gates remain tier-dependent.
30
+ Per Authoritative Decision #2, line and branch coverage thresholds are uniform across all tiers. The line threshold applies to every coverage language; the branch threshold applies to languages whose coverage tooling measures branch coverage. Other gates remain tier-dependent.
31
31
 
32
32
  ### Uniform across all tiers (T1–T4)
33
33
 
@@ -36,7 +36,7 @@ Per Authoritative Decision #2, line and branch coverage thresholds are uniform a
36
36
  - Type errors: 0.
37
37
  - Architecture violations: 0.
38
38
  - Line coverage: >= 85%.
39
- - Branch coverage: >= 75%.
39
+ - Branch coverage: >= 75% for languages whose coverage tooling measures branch coverage. PowerShell (Pester) and bash (kcov) are exempt from this threshold because neither tool measures branch coverage; no branch-coverage gate applies to them.
40
40
  - No regression on changed lines.
41
41
 
42
42
  ### Tier-dependent
@@ -53,4 +53,4 @@ Per Authoritative Decision #2, line and branch coverage thresholds are uniform a
53
53
 
54
54
  ## Rationale (uniform coverage thresholds)
55
55
 
56
- High test coverage is a fundamental quality-control design choice that enables autonomous agentic development and trust in the work product. For that reason, line coverage >= 85% and branch coverage >= 75% apply uniformly across T1–T4; tier-specific lower coverage floors are not used in this repository.
56
+ High test coverage is a fundamental quality-control design choice that enables autonomous agentic development and trust in the work product. For that reason, line coverage >= 85% applies uniformly across T1–T4 to every coverage language, and branch coverage >= 75% applies uniformly across T1–T4 to every language whose coverage tooling measures branch coverage; tier-specific lower coverage floors are not used in this repository. The branch threshold is not applied to PowerShell or bash because Pester and kcov do not measure branch coverage. That exemption is a capability limit on an unevaluable threshold, not a licence to exclude files from measurement: PowerShell and bash production files remain in the coverage denominator under the Coverage Exclusion Policy in `.agents/skills/general-unit-test/SKILL.md`.
@@ -14,9 +14,9 @@ This rule file summarizes the C#-specific policies for this repository.
14
14
 
15
15
  ## Toolchain
16
16
 
17
- 1. **Formatting — CSharpier**: All C# source files must be formatted with CSharpier. Do not use `dotnet format`. Command: `dotnet tool run csharpier .` or `csharpier .`
18
- 2. **Linting — .NET Analyzers**: C# code must pass Roslyn/.NET analyzer diagnostics. Command: `msbuild TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`
19
- 3. **Type Checking — Nullable Analysis**: Enable nullable reference types and fail on warnings. Command: `msbuild TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true`
17
+ 1. **Formatting — CSharpier**: All C# source files must be formatted with CSharpier. Do not use `dotnet format`. Run `dotnet tool restore` first when the manifest tool has not been restored. Apply formatting with `dotnet tool run csharpier format .` and verify read-only with `dotnet tool run csharpier check .`. Always invoke through `dotnet tool run` so the manifest-pinned CSharpier version is used.
18
+ 2. **Linting — .NET Analyzers**: C# code must pass Roslyn/.NET analyzer diagnostics. Command: `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`. `/t:Rebuild` is intentional for a warm local worktree: `/t:Build` can skip `CoreCompile` through MSBuild incrementality and exit 0 without running analyzers. CI may retain `/t:Build` on a cold checkout.
19
+ 3. **Type Checking — Nullable Analysis**: Compiler and nullable-flow diagnostics must pass with warnings as errors. Command: `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`. `/t:Rebuild` is required locally so compiler and nullable-flow diagnostics actually run. Projects opt into nullable per file with `#nullable enable`; do not pass `/p:Nullable=enable`, which opts every unannotated file in at once.
20
20
  4. **Testing — MSTest + Moq + FluentAssertions**: Run tests with: `vstest.console.exe <test-assembly-paths> /EnableCodeCoverage`
21
21
 
22
22
  Run the toolchain in order: format → lint → type-check → test. Restart from step 1 if any step fails or changes files.
@@ -29,11 +29,13 @@ Before invoking this gate, the agent must have:
29
29
 
30
30
  Run the full toolchain in this exact order. If any step fails or modifies files, fix the issue and restart from step 1. Do not stop the loop until all four steps complete without errors in a single pass.
31
31
 
32
- 1. `dotnet tool run csharpier .`
33
- 2. `msbuild TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`
34
- 3. `msbuild TaskMaster.sln /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /p:Nullable=enable /p:TreatWarningsAsErrors=true`
32
+ 1. `dotnet tool restore` (when the manifest tool has not been restored), then `dotnet tool run csharpier format .` to apply formatting and `dotnet tool run csharpier check .` to verify read-only.
33
+ 2. `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`
34
+ 3. `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`
35
35
  4. `vstest.console.exe <test-assembly-paths> /EnableCodeCoverage`
36
36
 
37
+ `/t:Rebuild /m` is intentional for the local gate: on a warm worktree `/t:Build` can skip `CoreCompile` through MSBuild incrementality and exit 0 without running analyzers or the compiler. CI may retain `/t:Build` on a cold checkout. Projects opt into nullable per file with `#nullable enable`; do not pass `/p:Nullable=enable`.
38
+
37
39
  If the environment prevents running any tool, stop and report the change as **unverified**. Do not declare completion.
38
40
 
39
41
  ## Delta Requirements (Zero-Regression Hard Gate)
@@ -2,7 +2,7 @@ default_permissions = ":danger-full-access"
2
2
 
3
3
  [mcp_servers.drm-copilot]
4
4
  command = "npx"
5
- args = ["-y", "@danmoisan/drm-copilot-mcp@1.0.23"]
5
+ args = ["-y", "@danmoisan/drm-copilot-mcp@1.0.26"]
6
6
  required = true
7
7
  enabled_tools = [
8
8
  "collect_commit_context",
@@ -90,6 +90,27 @@
90
90
  # coverage.
91
91
  '.claude/hooks/enforce-discovery-artifact-gate.ps1'
92
92
  '.claude/hooks/validate-discovery-artifact-gate.ps1'
93
+ # Issue #475 added the portable discovery-validation module so both discovery
94
+ # hooks stop invoking a Python interpreter; measured here so the new production
95
+ # module is not excluded from coverage.
96
+ '.claude/lib/discovery-validation/DiscoveryValidation.psm1'
97
+ # Issue #475 added the portable orchestrator-state parity modules so the
98
+ # completion hook validates the checkpoint without a Python interpreter;
99
+ # measured here so the new production modules are not excluded from coverage.
100
+ '.claude/lib/orchestrator-state/OrchestratorStateCheckpointValue.psm1'
101
+ '.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1'
102
+ '.claude/lib/orchestrator-state/OrchestratorStateModelReceipts.psm1'
103
+ # Issue #475 added the portable Codex routing resolvers so the U6.X and U6.T
104
+ # checkpoint checks resolve deployments and topologies without a Python
105
+ # interpreter; measured here so the new production modules are not excluded.
106
+ '.claude/lib/codex-routing/CodexDeployment.psm1'
107
+ '.claude/lib/codex-routing/CodexTopology.psm1'
108
+ '.claude/lib/orchestrator-state/OrchestratorStateCodexModelReceipts.psm1'
109
+ '.claude/lib/orchestrator-state/OrchestratorStateCodexTopologyReceipts.psm1'
110
+ '.claude/lib/orchestrator-state/OrchestratorStateRoutingMatrix.psm1'
111
+ '.claude/lib/orchestrator-state/OrchestratorStateCompletionChecks.psm1'
112
+ '.claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1'
113
+ '.claude/lib/orchestrator-state/OrchestratorStateUnconditional.psm1'
93
114
  # Issue #392 changed the Invoke-PoshQCTest default seams ($EnsureModule -Global
94
115
  # import and the global-session-state $InvokePester trampoline) so the bundled
95
116
  # entry path hosts the Pester run in the global session state; measured here so