@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.
- package/out/mcp-server.js +504 -174
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/feature-review.md +5 -3
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +11 -4
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +5 -2
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +28 -8
- package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +28 -8
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +117 -46
- package/resources/claude-customizations/.claude/lib/bash/parallel-manifest-validate.sh +115 -3
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +312 -0
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexTopology.psm1 +392 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryValidation.psm1 +500 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +58 -67
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCheckpointValue.psm1 +383 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCodexModelReceipts.psm1 +297 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCodexTopologyReceipts.psm1 +298 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletion.psm1 +232 -43
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletionChecks.psm1 +416 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateModelReceipts.psm1 +366 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +408 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1 +428 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingMatrix.psm1 +377 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateUnconditional.psm1 +166 -0
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +1 -1
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +28 -3
- package/resources/claude-customizations/.claude/rules/powershell.md +1 -1
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +3 -3
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -4
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +10 -5
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -34
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +71 -9
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +7 -3
- package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +1 -1
- package/resources/claude-customizations/.claude-variants/csharp-legacy/rules/csharp.md +4 -4
- package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +5 -3
- package/resources/claude-customizations/config/blast-radius.json +1 -3
- package/resources/claude-customizations/pack-manifests/core.json +12 -0
- package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +3 -3
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +3 -3
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +5 -3
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- 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
|
|
113
|
-
- Modified files (changed but previously existing): line coverage >= 85%, branch coverage >= 75
|
|
114
|
-
- Repo-wide per language: line coverage >= 85
|
|
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
|
|
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
|
|
90
|
-
|
|
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
|
|
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
|
|
115
|
-
`max_concurrency` governs how many items
|
|
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.**
|
|
119
|
-
|
|
120
|
-
`
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
|
130
|
-
|
|
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
|
|
158
|
-
|
|
159
|
-
|
|
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
|
|
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
|
|
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
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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 `
|
|
484
|
-
item to a pinned item. When no such edge exists the lowest returned index equals
|
|
485
|
-
exactly, so unstarted items may share the running cohort and `max_concurrency` slot
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
|
510
|
-
|
|
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
|
|
617
|
-
`recolor_unstarted(unstarted_items, conflict_edges, pinned, current_generation, current_cohort=current_cohort)`,
|
|
618
|
-
where `current_cohort`
|
|
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
|
|
66
|
-
|
|
67
|
-
|
|
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.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
88
|
-
|
|
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%
|
|
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`.
|
|
15
|
-
2. **Linting — .NET Analyzers**: C# code must pass Roslyn/.NET analyzer diagnostics. Command: `msbuild <solution>.sln /t:
|
|
16
|
-
3. **Type Checking — Nullable Analysis**:
|
|
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:
|
|
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:
|
|
32
|
-
3. `msbuild <solution>.sln /t:
|
|
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)
|
|
@@ -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%
|
|
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`.
|
|
18
|
-
2. **Linting — .NET Analyzers**: C# code must pass Roslyn/.NET analyzer diagnostics. Command: `msbuild TaskMaster.sln /t:
|
|
19
|
-
3. **Type Checking — Nullable Analysis**:
|
|
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:
|
|
34
|
-
3. `msbuild TaskMaster.sln /t:
|
|
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)
|
|
@@ -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
|