@danmoisan/drm-copilot-mcp 1.0.0 → 1.0.7
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 +814 -194
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/commit-message.md +42 -0
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +127 -0
- package/resources/claude-customizations/.claude/agents/human-exception-runbook.md +48 -0
- package/resources/claude-customizations/.claude/agents/orchestrator.md +67 -2
- package/resources/claude-customizations/.claude/agents/pr-author.md +37 -21
- package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +103 -65
- package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +12 -9
- package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +24 -16
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +19 -13
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +304 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +304 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +237 -0
- package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +16 -12
- package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +14 -10
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +35 -8
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +17 -14
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +104 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +268 -93
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +19 -11
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +28 -6
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +17 -14
- package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +146 -36
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +19 -7
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +36 -0
- package/resources/claude-customizations/.claude/settings.json +34 -1
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +252 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +42 -13
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +28 -0
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +22 -18
- package/resources/claude-customizations/pack-manifests/core.json +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +6 -3
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +65 -0
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +79 -0
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +66 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -25
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-pr-author-skill.ps1 +257 -93
- package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +98 -0
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +24 -0
- package/resources/codex-and-agents-customizations/pack-manifests/csharp-legacy.json +11 -0
- package/resources/codex-and-agents-customizations/pack-manifests/csharp-modern.json +10 -0
- package/resources/codex-and-agents-customizations/pack-manifests/powershell.json +15 -0
- package/resources/codex-and-agents-customizations/pack-manifests/python.json +15 -0
- package/resources/codex-and-agents-customizations/pack-manifests/typescript.json +9 -0
- package/resources/config/orchestration-routing.json +92 -0
- package/resources/customizations/.github/agents/pr-author.agent.md +24 -18
- package/resources/icon.png +0 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +11 -0
- package/resources/codex-and-agents-customizations/.github/workflows/_validate-orchestrator-state.yml +0 -68
- package/resources/codex-and-agents-customizations/.github/workflows/validate-orchestrator-state.yml +0 -15
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
Required sequence:
|
|
15
15
|
1. mcp__drm-copilot__collect_pr_context writes artifacts/pr_context.summary.txt
|
|
16
|
-
2. pr-author skill
|
|
16
|
+
2. pr-author skill produces the body text; the pr-author agent writes
|
|
17
|
+
artifacts/pr_body_<N>.md and a sibling integrity receipt
|
|
18
|
+
artifacts/pr_body_<N>.receipt.json
|
|
17
19
|
3. gh pr create --body-file artifacts/pr_body_<N>.md
|
|
18
20
|
(or gh pr edit --body-file ...)
|
|
19
21
|
|
|
@@ -21,35 +23,71 @@
|
|
|
21
23
|
Case A - gh pr create or gh pr edit with --body (inline, no --body-file): blocked.
|
|
22
24
|
Case B - gh pr create with neither --body nor --body-file: blocked.
|
|
23
25
|
Case C - gh pr create or gh pr edit with --body-file but context artifact absent: blocked.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
ttl_seconds: blocked (PR_AGENT_AUTHORIZATION_EXPIRED).
|
|
33
|
-
|
|
34
|
-
Authorization sentinel decision order on the --body-file-with-context path:
|
|
35
|
-
missing -> malformed -> invalid issuer -> expired -> allow.
|
|
26
|
+
Preflight - --body-file/context present: orchestrator-state checkpoint must pass
|
|
27
|
+
--require-pr-creation-ready before receipt verification runs, else blocked.
|
|
28
|
+
Receipt - preflight passed: the SHA-256 receipt is verified in five ordered checks
|
|
29
|
+
(Section below). The first failing check blocks.
|
|
30
|
+
|
|
31
|
+
Receipt verification decision order on the --body-file-with-context path:
|
|
32
|
+
PR_BODY_PATH_NONCANONICAL -> PR_AUTHOR_RECEIPT_MISSING -> PR_AUTHOR_RECEIPT_NUMBER_MISMATCH
|
|
33
|
+
-> PR_AUTHOR_RECEIPT_HASH_MISMATCH -> PR_AUTHOR_RECEIPT_STALE -> allow.
|
|
36
34
|
|
|
37
35
|
.NOTES
|
|
38
36
|
Compatible with PowerShell 7+. No external module dependencies.
|
|
39
37
|
|
|
40
|
-
Enforcement strength: the
|
|
41
|
-
|
|
42
|
-
artifacts/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
Enforcement strength: the SHA-256 receipt is a policy-level integrity check that binds the
|
|
39
|
+
PR body bytes to the receipt the pr-author agent wrote. It is not a cryptographic or security
|
|
40
|
+
boundary: any actor with Write access to artifacts/ can replace both the body file and the
|
|
41
|
+
receipt together, because all agents share the same filesystem and the runtime exposes no
|
|
42
|
+
native agent-identity signal at Bash PreToolUse time. The mechanism prevents accidental bypass
|
|
43
|
+
and requires a deliberate, documented act to circumvent. It MUST NOT be described as
|
|
44
|
+
tamper-proof or as a security boundary.
|
|
46
45
|
#>
|
|
47
46
|
[CmdletBinding()]
|
|
48
47
|
param()
|
|
49
48
|
|
|
50
49
|
$script:PrContextArtifactPath = 'artifacts/pr_context.summary.txt'
|
|
51
|
-
$script:
|
|
52
|
-
|
|
50
|
+
$script:OrchestratorStateCheckpointPath = 'artifacts/orchestration/orchestrator-state.json'
|
|
51
|
+
|
|
52
|
+
function Invoke-OrchestratorStatePreflight {
|
|
53
|
+
<#
|
|
54
|
+
.SYNOPSIS
|
|
55
|
+
Runs the orchestrator-state validator against the checkpoint and reports pass/fail.
|
|
56
|
+
.DESCRIPTION
|
|
57
|
+
Mirrors Invoke-RoutingContractValidation (.codex/hooks/validate-orchestrator-output.ps1):
|
|
58
|
+
an injectable subprocess scriptblock seam defaults to ``python -m
|
|
59
|
+
scripts.dev_tools.validate_orchestration_artifacts orchestrator-state <CheckpointPath>
|
|
60
|
+
--require-pr-creation-ready``. A missing checkpoint or validation failure surfaces via the
|
|
61
|
+
validator's exit/stderr text (pre-PR-creation readiness, not full completion).
|
|
62
|
+
.OUTPUTS
|
|
63
|
+
System.Collections.Hashtable with keys HasErrors (bool) and ErrorText (string).
|
|
64
|
+
#>
|
|
65
|
+
[CmdletBinding()]
|
|
66
|
+
[OutputType([hashtable])]
|
|
67
|
+
param(
|
|
68
|
+
[Parameter(Mandatory = $false)]
|
|
69
|
+
[string] $CheckpointPath = $script:OrchestratorStateCheckpointPath,
|
|
70
|
+
|
|
71
|
+
[Parameter(Mandatory = $false)]
|
|
72
|
+
[scriptblock] $Invoker = {
|
|
73
|
+
param($Path)
|
|
74
|
+
$output = & python -m scripts.dev_tools.validate_orchestration_artifacts `
|
|
75
|
+
orchestrator-state $Path --require-pr-creation-ready 2>&1
|
|
76
|
+
[pscustomobject]@{
|
|
77
|
+
ExitCode = $LASTEXITCODE
|
|
78
|
+
Output = ($output | Out-String)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
$result = & $Invoker $CheckpointPath
|
|
84
|
+
$exitCode = 0
|
|
85
|
+
if ($null -ne $result -and ($result.PSObject.Properties.Name -contains 'ExitCode')) { $exitCode = [int]$result.ExitCode }
|
|
86
|
+
$outputText = ''
|
|
87
|
+
if ($null -ne $result -and ($result.PSObject.Properties.Name -contains 'Output')) { $outputText = ([string]$result.Output).Trim() }
|
|
88
|
+
|
|
89
|
+
return @{ HasErrors = ($exitCode -ne 0); ErrorText = $outputText }
|
|
90
|
+
}
|
|
53
91
|
|
|
54
92
|
function Get-PrContextArtifactExistence {
|
|
55
93
|
<#
|
|
@@ -65,108 +103,180 @@ function Get-PrContextArtifactExistence {
|
|
|
65
103
|
return [bool](Test-Path -LiteralPath $script:PrContextArtifactPath)
|
|
66
104
|
}
|
|
67
105
|
|
|
68
|
-
function Get-
|
|
106
|
+
function Get-PrBodyFileBytes {
|
|
107
|
+
<#
|
|
108
|
+
.SYNOPSIS
|
|
109
|
+
Read the raw bytes of the PR body file. Tests mock this function (read seam).
|
|
110
|
+
.DESCRIPTION
|
|
111
|
+
Returns the byte content of the supplied body-file path, or $null when the file is absent.
|
|
112
|
+
This is the injectable boundary for body-file bytes in tests; no test writes the body file
|
|
113
|
+
to disk. The bytes are hashed inline by the receipt verification function.
|
|
114
|
+
.PARAMETER BodyFilePath
|
|
115
|
+
The relative path to the PR body file (for example artifacts/pr_body_5.md).
|
|
116
|
+
.OUTPUTS
|
|
117
|
+
System.Byte[] or $null
|
|
118
|
+
#>
|
|
119
|
+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = 'The plural noun names the byte-array return; the seam name is fixed by the receipt contract.')]
|
|
120
|
+
[CmdletBinding()]
|
|
121
|
+
[OutputType([byte[]])]
|
|
122
|
+
param(
|
|
123
|
+
[Parameter(Mandatory)]
|
|
124
|
+
[string] $BodyFilePath
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if (-not (Test-Path -LiteralPath $BodyFilePath)) {
|
|
128
|
+
return $null
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return [System.IO.File]::ReadAllBytes($BodyFilePath)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function Get-PrAuthorReceiptContent {
|
|
69
135
|
<#
|
|
70
136
|
.SYNOPSIS
|
|
71
|
-
Read the raw text of the
|
|
137
|
+
Read the raw JSON text of the PR body receipt. Tests mock this function (read seam).
|
|
72
138
|
.DESCRIPTION
|
|
73
|
-
Returns the raw text content of artifacts/
|
|
74
|
-
|
|
75
|
-
test writes the
|
|
139
|
+
Returns the raw text content of the sibling receipt file artifacts/pr_body_<N>.receipt.json,
|
|
140
|
+
or $null when the receipt file is absent. This is the injectable boundary for receipt
|
|
141
|
+
content in tests; no test writes the receipt file to disk.
|
|
142
|
+
.PARAMETER ReceiptFilePath
|
|
143
|
+
The relative path to the receipt file (for example artifacts/pr_body_5.receipt.json).
|
|
76
144
|
.OUTPUTS
|
|
77
145
|
System.String or $null
|
|
78
146
|
#>
|
|
79
147
|
[CmdletBinding()]
|
|
80
148
|
[OutputType([string])]
|
|
81
|
-
param(
|
|
149
|
+
param(
|
|
150
|
+
[Parameter(Mandatory)]
|
|
151
|
+
[string] $ReceiptFilePath
|
|
152
|
+
)
|
|
82
153
|
|
|
83
|
-
if (-not (Test-Path -LiteralPath $
|
|
154
|
+
if (-not (Test-Path -LiteralPath $ReceiptFilePath)) {
|
|
84
155
|
return $null
|
|
85
156
|
}
|
|
86
157
|
|
|
87
|
-
return (Get-Content -LiteralPath $
|
|
158
|
+
return (Get-Content -LiteralPath $ReceiptFilePath -Raw)
|
|
88
159
|
}
|
|
89
160
|
|
|
90
|
-
function Get-
|
|
161
|
+
function Get-PrContextSummaryLastWriteUtc {
|
|
91
162
|
<#
|
|
92
163
|
.SYNOPSIS
|
|
93
|
-
Return the
|
|
164
|
+
Return the UTC last-write time of the PR context summary. Tests mock this function (seam).
|
|
165
|
+
.DESCRIPTION
|
|
166
|
+
Returns the LastWriteTimeUtc of artifacts/pr_context.summary.txt, or $null when the file is
|
|
167
|
+
absent. The staleness check compares the receipt's created_at against this value; both are
|
|
168
|
+
artifact metadata, so no wall-clock seam is required.
|
|
94
169
|
.OUTPUTS
|
|
95
|
-
System.DateTime
|
|
170
|
+
System.DateTime or $null
|
|
96
171
|
#>
|
|
97
172
|
[CmdletBinding()]
|
|
98
173
|
[OutputType([datetime])]
|
|
99
174
|
param()
|
|
100
175
|
|
|
101
|
-
|
|
176
|
+
if (-not (Test-Path -LiteralPath $script:PrContextArtifactPath)) {
|
|
177
|
+
return $null
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return (Get-Item -LiteralPath $script:PrContextArtifactPath).LastWriteTimeUtc
|
|
102
181
|
}
|
|
103
182
|
|
|
104
|
-
function Test-
|
|
183
|
+
function Test-PrAuthorReceiptVerification {
|
|
105
184
|
<#
|
|
106
185
|
.SYNOPSIS
|
|
107
|
-
|
|
186
|
+
Verify the SHA-256 receipt and return a block-reason string, or $null when verified.
|
|
108
187
|
.DESCRIPTION
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
-
|
|
114
|
-
|
|
115
|
-
-
|
|
116
|
-
|
|
117
|
-
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
188
|
+
Runs the five ordered receipt checks on the --body-file-with-context path. Each check is its
|
|
189
|
+
own short-circuiting branch; the first failure returns its reason code:
|
|
190
|
+
1. PR_BODY_PATH_NONCANONICAL - --body-file path does not match the canonical
|
|
191
|
+
artifacts/pr_body_<N>.md pattern (case-sensitive).
|
|
192
|
+
2. PR_AUTHOR_RECEIPT_MISSING - sibling artifacts/pr_body_<N>.receipt.json absent.
|
|
193
|
+
3. PR_AUTHOR_RECEIPT_NUMBER_MISMATCH- receipt.number (integer) != <N> from the path.
|
|
194
|
+
4. PR_AUTHOR_RECEIPT_HASH_MISMATCH - inline SHA-256 (lowercase hex) of the body bytes
|
|
195
|
+
!= receipt.sha256.
|
|
196
|
+
5. PR_AUTHOR_RECEIPT_STALE - receipt.created_at (UTC) not strictly newer than the
|
|
197
|
+
context summary last-write time.
|
|
198
|
+
Returns $null when all five checks pass (allow). All disk access flows through the three
|
|
199
|
+
injectable seams (Get-PrBodyFileBytes, Get-PrAuthorReceiptContent,
|
|
200
|
+
Get-PrContextSummaryLastWriteUtc); SHA-256 is computed inline. This is a policy-level
|
|
201
|
+
integrity check, not a cryptographic control: any actor with Write access to artifacts/ can
|
|
202
|
+
replace the body file and the receipt together.
|
|
203
|
+
.PARAMETER CommandText
|
|
204
|
+
The Bash command text containing the --body-file argument.
|
|
121
205
|
.OUTPUTS
|
|
122
206
|
System.String or $null
|
|
123
207
|
#>
|
|
124
208
|
[CmdletBinding()]
|
|
125
209
|
[OutputType([string])]
|
|
126
|
-
param(
|
|
210
|
+
param(
|
|
211
|
+
[Parameter(Mandatory)]
|
|
212
|
+
[string] $CommandText
|
|
213
|
+
)
|
|
127
214
|
|
|
128
|
-
|
|
215
|
+
# Check 1: the --body-file argument must match the canonical artifacts/pr_body_<N>.md pattern.
|
|
216
|
+
# The match is case-sensitive (-cmatch) so a non-canonical path is rejected before any read.
|
|
217
|
+
if ($CommandText -cnotmatch '--body-file\s+artifacts/pr_body_(\d+)\.md\b') {
|
|
218
|
+
return "PR_BODY_PATH_NONCANONICAL: ``--body-file`` must reference a canonical ``artifacts/pr_body_<N>.md`` file produced by the pr-author skill. The path supplied does not match ``artifacts/pr_body_<N>.md``."
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
$bodyNumber = [int]$Matches[1]
|
|
222
|
+
$bodyFilePath = "artifacts/pr_body_$bodyNumber.md"
|
|
223
|
+
$receiptFilePath = "artifacts/pr_body_$bodyNumber.receipt.json"
|
|
129
224
|
|
|
130
|
-
|
|
131
|
-
|
|
225
|
+
# Check 2: the sibling receipt file must exist (read via the injectable seam).
|
|
226
|
+
$receiptRaw = Get-PrAuthorReceiptContent -ReceiptFilePath $receiptFilePath
|
|
227
|
+
if ([string]::IsNullOrWhiteSpace($receiptRaw)) {
|
|
228
|
+
return "PR_AUTHOR_RECEIPT_MISSING: ``$receiptFilePath`` is absent. The pr-author agent must write the SHA-256 receipt alongside ``$bodyFilePath`` before issuing ``gh pr create``/``gh pr edit --body-file``."
|
|
132
229
|
}
|
|
133
230
|
|
|
231
|
+
# A receipt that is present but not valid JSON cannot be verified; treat it as missing content.
|
|
134
232
|
try {
|
|
135
|
-
$
|
|
233
|
+
$receipt = $receiptRaw | ConvertFrom-Json -ErrorAction Stop
|
|
136
234
|
} catch {
|
|
137
|
-
return "
|
|
235
|
+
return "PR_AUTHOR_RECEIPT_MISSING: ``$receiptFilePath`` is not valid JSON. The pr-author agent must write a well-formed receipt with ``number``, ``sha256``, and ``created_at``."
|
|
138
236
|
}
|
|
139
237
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
238
|
+
# Check 3: the receipt number must equal the <N> extracted from the canonical path.
|
|
239
|
+
$receiptNumber = $null
|
|
240
|
+
$parsedNumber = 0
|
|
241
|
+
if ([int]::TryParse([string]$receipt.number, [ref] $parsedNumber)) {
|
|
242
|
+
$receiptNumber = $parsedNumber
|
|
243
|
+
}
|
|
244
|
+
if ($receiptNumber -ne $bodyNumber) {
|
|
245
|
+
return "PR_AUTHOR_RECEIPT_NUMBER_MISMATCH: ``$receiptFilePath`` ``number`` ($($receipt.number)) does not equal the body-file number ($bodyNumber). The receipt must bind to ``$bodyFilePath``."
|
|
143
246
|
}
|
|
144
247
|
|
|
145
|
-
|
|
146
|
-
$
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
[System.Globalization.DateTimeStyles]::AdjustToUniversal -bor [System.Globalization.DateTimeStyles]::AssumeUniversal,
|
|
150
|
-
[ref] $issuedAt)
|
|
151
|
-
if (-not $parsed) {
|
|
152
|
-
return "PR_AGENT_AUTHORIZATION_MALFORMED: ``$script:PrAuthorAuthorizationPath`` has an unparseable ``issued_at``. The pr-author agent must record a UTC ISO-8601 ``issued_at`` timestamp."
|
|
248
|
+
# Check 4: the inline SHA-256 (lowercase hex) of the body bytes must equal receipt.sha256.
|
|
249
|
+
$bodyBytes = Get-PrBodyFileBytes -BodyFilePath $bodyFilePath
|
|
250
|
+
if ($null -eq $bodyBytes) {
|
|
251
|
+
return "PR_AUTHOR_RECEIPT_HASH_MISMATCH: ``$bodyFilePath`` could not be read to verify its SHA-256 against ``$receiptFilePath``."
|
|
153
252
|
}
|
|
154
253
|
|
|
155
|
-
|
|
156
|
-
|
|
254
|
+
$sha256 = [System.Security.Cryptography.SHA256]::Create()
|
|
255
|
+
try {
|
|
256
|
+
$hashBytes = $sha256.ComputeHash($bodyBytes)
|
|
257
|
+
} finally {
|
|
258
|
+
$sha256.Dispose()
|
|
157
259
|
}
|
|
260
|
+
$computedHash = ([System.BitConverter]::ToString($hashBytes) -replace '-', '').ToLowerInvariant()
|
|
158
261
|
|
|
159
|
-
$
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
262
|
+
if ($computedHash -ne ([string]$receipt.sha256).ToLowerInvariant()) {
|
|
263
|
+
return "PR_AUTHOR_RECEIPT_HASH_MISMATCH: the SHA-256 of ``$bodyFilePath`` does not equal ``sha256`` in ``$receiptFilePath``. The body file was modified after the receipt was written."
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
# Check 5: receipt.created_at (UTC) must be strictly newer than the context summary last-write.
|
|
267
|
+
$createdAt = [DateTime]::MinValue
|
|
268
|
+
$createdParsed = [DateTime]::TryParse(
|
|
269
|
+
[string]$receipt.created_at,
|
|
270
|
+
[System.Globalization.CultureInfo]::InvariantCulture,
|
|
271
|
+
[System.Globalization.DateTimeStyles]::AdjustToUniversal -bor [System.Globalization.DateTimeStyles]::AssumeUniversal,
|
|
272
|
+
[ref] $createdAt)
|
|
273
|
+
if (-not $createdParsed) {
|
|
274
|
+
return "PR_AUTHOR_RECEIPT_STALE: ``$receiptFilePath`` has a missing or unparseable ``created_at``. The pr-author agent must record a UTC ISO-8601 ``created_at`` strictly newer than ``$script:PrContextArtifactPath``."
|
|
165
275
|
}
|
|
166
276
|
|
|
167
|
-
$
|
|
168
|
-
if ($
|
|
169
|
-
return "
|
|
277
|
+
$contextLastWrite = Get-PrContextSummaryLastWriteUtc
|
|
278
|
+
if (($null -eq $contextLastWrite) -or ($createdAt -le $contextLastWrite)) {
|
|
279
|
+
return "PR_AUTHOR_RECEIPT_STALE: ``$receiptFilePath`` ``created_at`` is not strictly newer than the last-write time of ``$script:PrContextArtifactPath``. The pr-author agent must regenerate the body and receipt after refreshing the PR context."
|
|
170
280
|
}
|
|
171
281
|
|
|
172
282
|
return $null
|
|
@@ -179,12 +289,12 @@ function Get-PrAuthorBypassReason {
|
|
|
179
289
|
.DESCRIPTION
|
|
180
290
|
Returns PR_AUTHOR_SKILL_BLOCKED when gh pr create or gh pr edit is run with --body (inline,
|
|
181
291
|
no --body-file), or when gh pr create is run with no body flag at all. Returns
|
|
182
|
-
PR_CONTEXT_MISSING when --body-file is present but the context artifact
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
(
|
|
186
|
-
Cases A, B, and C are evaluated first and unchanged;
|
|
187
|
-
previously-allowed --body-file-with-context path.
|
|
292
|
+
PR_CONTEXT_MISSING when --body-file is present but the context artifact does not exist on
|
|
293
|
+
disk. When --body-file is present and the context artifact exists, verifies the SHA-256
|
|
294
|
+
receipt via Test-PrAuthorReceiptVerification and returns its block reason
|
|
295
|
+
(PR_BODY_PATH_NONCANONICAL / PR_AUTHOR_RECEIPT_*) when verification fails. Returns $null for
|
|
296
|
+
all allowed patterns. Cases A, B, and C are evaluated first and unchanged; receipt
|
|
297
|
+
verification only extends the previously-allowed --body-file-with-context path.
|
|
188
298
|
.PARAMETER CommandText
|
|
189
299
|
The Bash command text extracted from CLAUDE_TOOL_INPUT.
|
|
190
300
|
.PARAMETER ContextExists
|
|
@@ -238,12 +348,25 @@ function Get-PrAuthorBypassReason {
|
|
|
238
348
|
return "PR_CONTEXT_MISSING: ``$script:PrContextArtifactPath`` is absent. Run ``mcp__drm-copilot__collect_pr_context`` before creating or editing the PR body."
|
|
239
349
|
}
|
|
240
350
|
|
|
241
|
-
#
|
|
242
|
-
#
|
|
351
|
+
# Orchestrator-state preflight: runs inside this same PreToolUse hook (so it cannot be
|
|
352
|
+
# bypassed by invoking gh pr create/edit directly) before receipt verification.
|
|
243
353
|
if ($hasBodyFile -and $ContextExists) {
|
|
244
|
-
$
|
|
245
|
-
if ($
|
|
246
|
-
|
|
354
|
+
$preflightResult = Invoke-OrchestratorStatePreflight
|
|
355
|
+
if ($preflightResult.HasErrors) {
|
|
356
|
+
$preflightSummary = if ([string]::IsNullOrWhiteSpace($preflightResult.ErrorText)) {
|
|
357
|
+
"checkpoint missing at $script:OrchestratorStateCheckpointPath"
|
|
358
|
+
} else {
|
|
359
|
+
$preflightResult.ErrorText
|
|
360
|
+
}
|
|
361
|
+
return "ORCHESTRATOR_STATE_PREFLIGHT_FAILED: $preflightSummary"
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
# Receipt verification: extends, and does not replace, the previously-allowed path.
|
|
366
|
+
if ($hasBodyFile -and $ContextExists) {
|
|
367
|
+
$receiptReason = Test-PrAuthorReceiptVerification -CommandText $CommandText
|
|
368
|
+
if ($receiptReason) {
|
|
369
|
+
return $receiptReason
|
|
247
370
|
}
|
|
248
371
|
}
|
|
249
372
|
|
|
@@ -268,7 +391,7 @@ function Invoke-PrAuthorSkillDecision {
|
|
|
268
391
|
)
|
|
269
392
|
|
|
270
393
|
if (-not $ToolInputRaw) {
|
|
271
|
-
return
|
|
394
|
+
return Get-PrAuthorSkillAllowDecision
|
|
272
395
|
}
|
|
273
396
|
|
|
274
397
|
try {
|
|
@@ -279,20 +402,61 @@ function Invoke-PrAuthorSkillDecision {
|
|
|
279
402
|
|
|
280
403
|
$commandText = $toolInput.command
|
|
281
404
|
if (-not $commandText) {
|
|
282
|
-
return
|
|
405
|
+
return Get-PrAuthorSkillAllowDecision
|
|
283
406
|
}
|
|
284
407
|
|
|
285
408
|
$contextExists = Get-PrContextArtifactExistence
|
|
286
409
|
$reason = Get-PrAuthorBypassReason -CommandText $commandText -ContextExists $contextExists
|
|
287
410
|
|
|
288
411
|
if ($reason) {
|
|
289
|
-
return
|
|
290
|
-
|
|
291
|
-
|
|
412
|
+
return Get-PrAuthorSkillBlockDecision -Reason $reason
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return Get-PrAuthorSkillAllowDecision
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function Get-PrAuthorSkillAllowDecision {
|
|
419
|
+
<#
|
|
420
|
+
.SYNOPSIS
|
|
421
|
+
Construct the PreToolUse allow decision for a permitted Bash command.
|
|
422
|
+
.OUTPUTS
|
|
423
|
+
System.Collections.Specialized.OrderedDictionary
|
|
424
|
+
#>
|
|
425
|
+
[CmdletBinding()]
|
|
426
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
427
|
+
param()
|
|
428
|
+
|
|
429
|
+
return [ordered]@{
|
|
430
|
+
hookSpecificOutput = [ordered]@{
|
|
431
|
+
hookEventName = 'PreToolUse'
|
|
432
|
+
permissionDecision = 'allow'
|
|
292
433
|
}
|
|
293
434
|
}
|
|
435
|
+
}
|
|
294
436
|
|
|
295
|
-
|
|
437
|
+
function Get-PrAuthorSkillBlockDecision {
|
|
438
|
+
<#
|
|
439
|
+
.SYNOPSIS
|
|
440
|
+
Construct the PreToolUse deny decision for a forbidden Bash command.
|
|
441
|
+
.PARAMETER Reason
|
|
442
|
+
The specific deny reason to surface in the decision.
|
|
443
|
+
.OUTPUTS
|
|
444
|
+
System.Collections.Specialized.OrderedDictionary
|
|
445
|
+
#>
|
|
446
|
+
[CmdletBinding()]
|
|
447
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
448
|
+
param(
|
|
449
|
+
[Parameter(Mandatory)]
|
|
450
|
+
[string] $Reason
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
return [ordered]@{
|
|
454
|
+
hookSpecificOutput = [ordered]@{
|
|
455
|
+
hookEventName = 'PreToolUse'
|
|
456
|
+
permissionDecision = 'deny'
|
|
457
|
+
permissionDecisionReason = $Reason
|
|
458
|
+
}
|
|
459
|
+
}
|
|
296
460
|
}
|
|
297
461
|
|
|
298
462
|
function Test-PrAuthorBypassRequired {
|
|
@@ -331,6 +495,6 @@ try {
|
|
|
331
495
|
exit 1
|
|
332
496
|
}
|
|
333
497
|
|
|
334
|
-
$decision | ConvertTo-Json -Compress | Write-Output
|
|
498
|
+
$decision | ConvertTo-Json -Compress -Depth 5 | Write-Output
|
|
335
499
|
|
|
336
500
|
exit 0
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
name = "csharp-typed-engineer"
|
|
2
|
+
description = "Converted subagent"
|
|
3
|
+
variant = "legacy"
|
|
4
|
+
developer_instructions = '''
|
|
5
|
+
Canonical migration source: .github/agents/csharp-typed-engineer.agent.md
|
|
6
|
+
Read the canonical source agent file first and follow it exactly.
|
|
7
|
+
Preserve all mandatory sequencing, artifact, validation, remediation, and completion gates from the source agent.
|
|
8
|
+
If the source agent defines handoffs, preserve those handoffs with the same degree of process gating in Codex.
|
|
9
|
+
Treat .agents/skills, .codex/agents, and .codex/prompts as the Codex runtime surfaces for migrated behavior.
|
|
10
|
+
Do not weaken validation-only preflight loops, QA gates, remediation triggers, review gates, acceptance-criteria tracking, or evidence requirements that exist in the source agent.
|
|
11
|
+
|
|
12
|
+
Applied rewrites:
|
|
13
|
+
- Rewrite merged standing-guidance source paths to the native AGENTS.md target.
|
|
14
|
+
- Rewrite merged standing-guidance source paths to the native AGENTS.md target.
|
|
15
|
+
- Rewrite merged standing-guidance source paths to the native AGENTS.md target.
|
|
16
|
+
- Rewrite Claude skill paths to shared skill paths.
|
|
17
|
+
- Rewrite Claude rule paths to shared skill paths.
|
|
18
|
+
|
|
19
|
+
Canonical migration source:
|
|
20
|
+
- .github/agents/csharp-typed-engineer.agent.md
|
|
21
|
+
|
|
22
|
+
Migration contract:
|
|
23
|
+
- Read the canonical source agent file first and follow it exactly.
|
|
24
|
+
- Preserve all mandatory sequencing, artifact, validation, remediation, and completion gates from the source agent.
|
|
25
|
+
- If the source agent defines handoffs, preserve those handoffs with the same degree of process gating and do not mark a delegated step complete until the delegated output contract and required on-disk artifacts exist.
|
|
26
|
+
- Treat .agents/skills, .codex/agents, and .codex/prompts as the Codex runtime surfaces for migrated behavior, but do not relax any source-agent requirements during that translation.
|
|
27
|
+
- Do not weaken validation-only preflight loops, QA gates, remediation triggers, review gates, acceptance-criteria tracking, or evidence requirements that exist in the source agent.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
name: csharp-typed-engineer
|
|
31
|
+
description: Project-scoped worker that implements and verifies C# changes within typed repository boundaries. Applies the CSharpier -> .NET Analyzers -> Nullable Analysis -> MSTest toolchain, the 1-3 production-file small-path budget, and zero-regression quality gates.
|
|
32
|
+
tools:
|
|
33
|
+
- Read
|
|
34
|
+
- Grep
|
|
35
|
+
- Glob
|
|
36
|
+
- "Bash(msbuild *)"
|
|
37
|
+
- "Bash(dotnet *)"
|
|
38
|
+
skills:
|
|
39
|
+
- policy-compliance-order
|
|
40
|
+
- csharp-change-budget-router
|
|
41
|
+
- atomic-plan-contract
|
|
42
|
+
- csharp-qa-gate
|
|
43
|
+
- acceptance-criteria-tracking
|
|
44
|
+
- feature-promotion-lifecycle
|
|
45
|
+
- remediation-handoff-atomic-planner
|
|
46
|
+
- evidence-and-timestamp-conventions
|
|
47
|
+
memory: project
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
# CSharp Typed Engineer Agent
|
|
51
|
+
|
|
52
|
+
Senior C# engineer specialized in small cohesive classes and modules, strong typing under nullable reference types, minimal DI seams, and deterministic MSTest coverage. Implement C# changes within the approved scope, preserve typed boundaries, and verify results with the repository C# toolchain.
|
|
53
|
+
|
|
54
|
+
## Standing Rules
|
|
55
|
+
|
|
56
|
+
Language standards and toolchain are defined in `.agents/skills/csharp/SKILL.md` and `AGENTS.md`, auto-loaded for `**/*.cs` and `**/*.csproj` edits. Tonality is defined in `AGENTS.md` and `AGENTS.md`.
|
|
57
|
+
|
|
58
|
+
## Workflow
|
|
59
|
+
|
|
60
|
+
Follow the phased workflow defined by the preloaded skills:
|
|
61
|
+
|
|
62
|
+
1. **Policy compliance** — apply `policy-compliance-order` to load mandatory repo policies before any change.
|
|
63
|
+
2. **Routing and scope** — apply `csharp-change-budget-router` to estimate scope, select direct vs orchestrator handoff mode, and enforce the 3 production + 3 test per-batch cap.
|
|
64
|
+
3. **Plan and baseline** — apply `atomic-plan-contract` for Phase 0 baseline capture and atomic plan structure. Delegate plan authoring to `atomic_planner` when no plan is supplied. Plans must include the proposed class and module structure, minimal DI seams, MSTest scenario-level test strategy, and Moq mock strategy.
|
|
65
|
+
4. **Implement in batches** — apply the approved plan. After each batch, run targeted analyzer and nullable builds on touched projects plus targeted MSTest, and confirm per-file coverage.
|
|
66
|
+
5. **Final QA gate** — apply `csharp-qa-gate` to run the full toolchain, enforce zero-regression deltas against the baseline, and produce the required reporting block before declaring completion.
|
|
67
|
+
6. **Evidence and handoff** — store baseline and post-change evidence per `evidence-and-timestamp-conventions`. Trigger remediation via `remediation-handoff-atomic-planner` when deltas fail.
|
|
68
|
+
|
|
69
|
+
## Mode Marker Resolution
|
|
70
|
+
|
|
71
|
+
For feature-scoped work, resolve Work Mode from `issue.md` per `feature-promotion-lifecycle`:
|
|
72
|
+
|
|
73
|
+
- `- Work Mode: minor-audit`
|
|
74
|
+
- `- Work Mode: full-feature`
|
|
75
|
+
- `- Work Mode: full-bug`
|
|
76
|
+
- legacy `- Work Mode: full` -> interpret as `full-feature`.
|
|
77
|
+
|
|
78
|
+
If the marker is missing or malformed, fail closed to `full-feature`.
|
|
79
|
+
|
|
80
|
+
## Stop Conditions
|
|
81
|
+
|
|
82
|
+
Stop implementation and return to the user when:
|
|
83
|
+
|
|
84
|
+
- the scope estimate or an in-flight batch would exceed the 3-production-file cap in direct mode,
|
|
85
|
+
- a file is near or would exceed the 500-line limit,
|
|
86
|
+
- any QA gate delta is non-zero after self-correction,
|
|
87
|
+
- the toolchain cannot be executed in the current environment (mark the change **unverified**),
|
|
88
|
+
- orchestrator handoff mode is requested but the required context package is incomplete,
|
|
89
|
+
- policy instructions conflict.
|
|
90
|
+
|
|
91
|
+
## Evidence Location Invariant
|
|
92
|
+
|
|
93
|
+
All evidence artifacts this agent produces (baselines, QA gates, regression results, coverage) MUST be written to `<FEATURE>/evidence/<kind>/` as defined in `.agents/skills/evidence-and-timestamp-conventions/SKILL.md`.
|
|
94
|
+
|
|
95
|
+
Writing to `artifacts/baselines/`, `artifacts/qa/`, `artifacts/coverage/`, or any other non-canonical path is a policy violation and will be caught by the `enforce-evidence-locations.ps1` PreToolUse hook.
|
|
96
|
+
|
|
97
|
+
If a delegation prompt, plan, or caller instruction specifies a non-canonical evidence path (e.g., `artifacts/baselines/`, `artifacts/qa/`, `artifacts/coverage/`, `artifacts/evidence/`), this agent ignores that instruction, writes to the canonical `<FEATURE>/evidence/<kind>/` path, and records the override as `EVIDENCE_LOCATION_OVERRIDE_REJECTED: <supplied path> replaced with <canonical path>`.
|
|
98
|
+
'''
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "core",
|
|
3
|
+
"label": "Core (always included)",
|
|
4
|
+
"paths": [
|
|
5
|
+
".agents/README.md",
|
|
6
|
+
".agents/skills/README.md",
|
|
7
|
+
".agents/skills/acceptance-criteria-tracking/SKILL.md",
|
|
8
|
+
".agents/skills/atomic-executor/SKILL.md",
|
|
9
|
+
".agents/skills/atomic-plan-contract/SKILL.md",
|
|
10
|
+
".agents/skills/atomic-planner/SKILL.md",
|
|
11
|
+
".agents/skills/evidence-and-timestamp-conventions/SKILL.md",
|
|
12
|
+
".agents/skills/orchestrate/SKILL.md",
|
|
13
|
+
".agents/skills/orchestrator-workflow/SKILL.md",
|
|
14
|
+
".agents/skills/policy-compliance-order/SKILL.md",
|
|
15
|
+
".agents/skills/repo-automation-adapter/SKILL.md",
|
|
16
|
+
".agents/skills/repo-automation-adapter/agents/openai.yaml",
|
|
17
|
+
".codex/config.toml",
|
|
18
|
+
".codex/agents/atomic-executor.toml",
|
|
19
|
+
".codex/agents/atomic-planner.toml",
|
|
20
|
+
".codex/agents/orchestrator.toml",
|
|
21
|
+
".codex/prompts/generate-pr.md",
|
|
22
|
+
".codex/prompts/orchestrate-work.md"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "csharp-legacy",
|
|
3
|
+
"label": "C# (legacy)",
|
|
4
|
+
"source_prefix": ".agents-variants/csharp-legacy;.codex-variants/csharp-legacy",
|
|
5
|
+
"paths": [
|
|
6
|
+
".agents/skills/csharp/SKILL.md",
|
|
7
|
+
".agents/skills/csharp-qa-gate/SKILL.md",
|
|
8
|
+
".agents/skills/invoke-csharp-engineer/SKILL.md",
|
|
9
|
+
".codex/agents/csharp-typed-engineer.toml"
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "powershell",
|
|
3
|
+
"label": "PowerShell",
|
|
4
|
+
"paths": [
|
|
5
|
+
".agents/skills/powershell/SKILL.md",
|
|
6
|
+
".agents/skills/powershell-change-budget-router/SKILL.md",
|
|
7
|
+
".agents/skills/powershell-orchestration-state-machine/SKILL.md",
|
|
8
|
+
".agents/skills/powershell-qa-gate/SKILL.md",
|
|
9
|
+
".agents/skills/invoke-powershell-engineer/SKILL.md",
|
|
10
|
+
".codex/agents/powershell-typed-engineer.toml",
|
|
11
|
+
".codex/agents/powershell-orchestrator.toml",
|
|
12
|
+
".codex/agents/powershell-atomic-executor.toml",
|
|
13
|
+
".codex/agents/powershell-atomic-planning.toml"
|
|
14
|
+
]
|
|
15
|
+
}
|