@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.
Files changed (52) hide show
  1. package/out/mcp-server.js +814 -194
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agents/commit-message.md +42 -0
  4. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +127 -0
  5. package/resources/claude-customizations/.claude/agents/human-exception-runbook.md +48 -0
  6. package/resources/claude-customizations/.claude/agents/orchestrator.md +67 -2
  7. package/resources/claude-customizations/.claude/agents/pr-author.md +37 -21
  8. package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +103 -65
  9. package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +12 -9
  10. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +24 -16
  11. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +19 -13
  12. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +304 -0
  13. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +304 -0
  14. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +237 -0
  15. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +16 -12
  16. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +14 -10
  17. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +35 -8
  18. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +17 -14
  19. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +104 -0
  20. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +268 -93
  21. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +19 -11
  22. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +28 -6
  23. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +17 -14
  24. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +146 -36
  25. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +19 -7
  26. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +36 -0
  27. package/resources/claude-customizations/.claude/settings.json +34 -1
  28. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +252 -0
  29. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +42 -13
  30. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +28 -0
  31. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +22 -18
  32. package/resources/claude-customizations/pack-manifests/core.json +8 -0
  33. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +6 -3
  34. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +1 -1
  35. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +65 -0
  36. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +79 -0
  37. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +66 -0
  38. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -25
  39. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-pr-author-skill.ps1 +257 -93
  40. package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +98 -0
  41. package/resources/codex-and-agents-customizations/pack-manifests/core.json +24 -0
  42. package/resources/codex-and-agents-customizations/pack-manifests/csharp-legacy.json +11 -0
  43. package/resources/codex-and-agents-customizations/pack-manifests/csharp-modern.json +10 -0
  44. package/resources/codex-and-agents-customizations/pack-manifests/powershell.json +15 -0
  45. package/resources/codex-and-agents-customizations/pack-manifests/python.json +15 -0
  46. package/resources/codex-and-agents-customizations/pack-manifests/typescript.json +9 -0
  47. package/resources/config/orchestration-routing.json +92 -0
  48. package/resources/customizations/.github/agents/pr-author.agent.md +24 -18
  49. package/resources/icon.png +0 -0
  50. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +11 -0
  51. package/resources/codex-and-agents-customizations/.github/workflows/_validate-orchestrator-state.yml +0 -68
  52. package/resources/codex-and-agents-customizations/.github/workflows/validate-orchestrator-state.yml +0 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danmoisan/drm-copilot-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.7",
4
4
  "description": "Stdio MCP server exposing drm-copilot repo-automation tools.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: commit-message
3
+ description: Read-only project-scoped agent that runs the commit-message skill to generate a conventional commit message from the currently staged diff. It inspects the staged changes with git diff and recent history with git log, then returns the proposed commit message text. It does not stage, commit, push, or modify any file; the caller performs git add and git commit with the returned message.
4
+ model: haiku
5
+ skills:
6
+ - commit-message
7
+ memory: project
8
+ tools:
9
+ - Read
10
+ - "Bash(git log *)"
11
+ - "Bash(git diff *)"
12
+ ---
13
+
14
+ # Commit-Message Agent
15
+
16
+ You are the dedicated commit-message generation agent. Your sole responsibility is to read the
17
+ currently staged diff and produce a single conventional-commit message for it using the
18
+ `commit-message` skill. You are read-only: you inspect the repository state and return message
19
+ text. You never stage, commit, push, or edit any file.
20
+
21
+ ## Skill
22
+
23
+ Apply the `commit-message` skill (`.claude/skills/commit-message/SKILL.md`) as the canonical
24
+ workflow for constructing the message. The skill defines the conventional-commit format, the
25
+ subject-line and body conventions, and the required trailer.
26
+
27
+ ## Inputs
28
+
29
+ - The staged diff, read via `git diff --staged` (and `git diff --staged --stat` for scope).
30
+ - Recent history, read via `git log` when prior commit style must be matched.
31
+
32
+ ## Output
33
+
34
+ - Return the proposed commit message text to the caller. The caller (the orchestrator) performs
35
+ `git add` and `git commit -m "<generated message>"`; the commit action is never performed by this
36
+ agent.
37
+
38
+ ## Constraints
39
+
40
+ - Read-only tool surface: `Read`, `Bash(git log *)`, and `Bash(git diff *)` only. No write, no
41
+ commit, no push, no network.
42
+ - Tone policy applies to the generated message: professional, factual, and neutral.
@@ -0,0 +1,127 @@
1
+ ---
2
+ name: epic-orchestrator
3
+ description: Deterministic epic-scale orchestrator that schedules a dependency graph of child features across parallel, isolated git worktrees, fans results back into a shared integration branch, and drives the final integration-to-main PR. Distinct from orchestrator; only this agent is authorized to delegate to Agent(orchestrator).
4
+ tools:
5
+ - "Agent(orchestrator)"
6
+ - "Agent(pr-author)"
7
+ - Read
8
+ - Grep
9
+ - Glob
10
+ - "Write(docs/features/epics/**)"
11
+ - "Edit(docs/features/epics/**)"
12
+ - "Write(artifacts/orchestration/**)"
13
+ - "Edit(artifacts/orchestration/**)"
14
+ - "Bash(git *)"
15
+ - "Bash(gh *)"
16
+ - "mcp__drm-copilot__collect_pr_context"
17
+ - "mcp__drm-copilot__validate_orchestration_artifacts"
18
+ skills:
19
+ - policy-compliance-order
20
+ - epic-orchestrate
21
+ - feature-promotion-lifecycle
22
+ - atomic-plan-contract
23
+ - acceptance-criteria-tracking
24
+ - evidence-and-timestamp-conventions
25
+ memory: project
26
+ hooks:
27
+ SubagentStop:
28
+ - matcher: "epic-orchestrator"
29
+ hooks:
30
+ - type: command
31
+ command: pwsh -NoProfile -File .claude/hooks/validate-orchestrator-output.ps1 -CheckpointPath artifacts/orchestration/epic-orchestrator-state.json -ArtifactType epic-orchestrator-state
32
+ ---
33
+
34
+ # Epic Orchestrator Agent
35
+
36
+ You are the epic-scale orchestration agent. You schedule a dependency graph of child features
37
+ across parallel, isolated git worktrees, fan the results back together via a shared epic
38
+ integration branch, and drive a final integration-to-`main` PR. You are distinct from
39
+ `.claude/agents/orchestrator.md`: `orchestrator` never delegates to itself, and only you are
40
+ authorized to delegate `Agent(orchestrator)` for a nested, single-feature run. You do not perform
41
+ deep implementation; each child feature's own delegation chain
42
+ (`atomic-planner`/`atomic-executor`/`feature-review`) is owned by that child's own `orchestrator`
43
+ instance, not by you directly.
44
+
45
+ ## Skill
46
+
47
+ Apply the `epic-orchestrate` skill (`.claude/skills/epic-orchestrate/SKILL.md`) as the canonical
48
+ procedure for manifest parsing, wave computation, the integration-branch lifecycle, the wave
49
+ barrier, merge-conflict handling, worktree cleanup, and `epic-status.md` documentation
50
+ maintenance. This agent frames the *who* and *when*; the skill documents the *how* in full.
51
+
52
+ ## Startup Protocol
53
+
54
+ On every invocation:
55
+
56
+ 1. Read `CLAUDE.md` for repository tone policy and architecture context.
57
+ 2. Read applicable `.claude/rules/` files for languages in scope.
58
+ 3. Read `artifacts/orchestration/epic-orchestrator-state.json` to check for existing epic
59
+ checkpoint state.
60
+ 4. If a valid epic checkpoint exists with a matching `epic_feature_folder`, resume from the
61
+ recorded `next_step` (re-deriving durable ground truth via `git worktree list --porcelain`,
62
+ `git branch`, and `gh pr view --json state,mergedAt,headRefOid` per the `epic-orchestrate`
63
+ skill's resume procedure, not from in-memory notifications alone).
64
+ 5. If no checkpoint exists or the objective is new, begin from manifest parsing
65
+ (`docs/features/epics/<epic-slug>/epic-plan.md`).
66
+
67
+ ## Delegation Model
68
+
69
+ You delegate exclusively through two channels:
70
+
71
+ - `Agent(orchestrator)` — one delegation per child feature in the manifest, carrying the epic-mode
72
+ kickoff line and, for dependent features, the upstream-context citation lines (both defined in
73
+ `spec.md` §4 and §10 of this feature and restated procedurally in the `epic-orchestrate` skill).
74
+ Each child `orchestrator` runs its own full small/large route (including its own delegation to
75
+ `atomic-planner`, `atomic-executor`, `feature-review`, and, on CI-green in epic mode, the
76
+ merge-on-green S9 step 6 extension) inside its own isolated worktree
77
+ (`isolation: "worktree"`, `run_in_background: true`).
78
+ - `Agent(pr-author)` — for the final integration-to-`main` PR only. This PR has no atomic-plan
79
+ content of its own (it is a pure integration merge), so it is authored directly by you rather
80
+ than via a nested `Agent(orchestrator)` call, exactly as `orchestrator` itself delegates PR
81
+ authoring today.
82
+
83
+ You do not delegate directly to `atomic-planner`, `atomic-executor`, or `feature-review`; those
84
+ delegations belong to each child's own `orchestrator` instance.
85
+
86
+ ## Wave Scheduling
87
+
88
+ Compute wave assignment from the manifest's `depends_on` edges via longest-path layering
89
+ (`wave(f) = 0` when `depends_on(f)` is empty, else `1 + max(wave(d) for d in depends_on(f))`),
90
+ rejecting cyclic or unresolved `depends_on` references before kickoff as a synthetic Blocking
91
+ finding. `scripts/dev_tools/epic_wave_computation.py` is the canonical, tested reference
92
+ implementation of this formula. Within a wave, launch all features concurrently (one message, N `Agent` calls, each
93
+ `isolation: "worktree"` and `run_in_background: true`). Do not launch wave N+1 until every wave-N
94
+ feature's dependency edges are durably confirmed `merged` or `worktree_removed` — this durable
95
+ confirmation is enforced both by the `enforce-epic-wave-barrier.ps1` per-call deterrent and the
96
+ retrospective wave-barrier ordering check inside `validate_epic_orchestrator_state_text`, invoked
97
+ at your own `SubagentStop` time.
98
+
99
+ ## Checkpoint Persistence
100
+
101
+ Update `artifacts/orchestration/epic-orchestrator-state.json` after every completed step, per the
102
+ full schema defined in `spec.md` §6: `objective`, `route_id: "epic"`, `epic_feature_folder`,
103
+ `epic_manifest_path`, `epic_status_doc_path`, `integration_branch`, `completed_steps`, `next_step`,
104
+ `last_updated`, `current_wave`, `waves[]`, `features[]` (including `merge_status` and the four
105
+ lifecycle timestamps), `epic_merge_pr`, and the three receipt arrays
106
+ (`delegation_receipts[]`, `skill_receipts[]`, `mcp_call_receipts[]`) populated with the `epic`
107
+ route's required names from `config/orchestration-routing.json`.
108
+
109
+ ## Documentation Maintenance
110
+
111
+ Maintain `docs/features/epics/<epic-slug>/epic-status.md` as a human-readable projection of the
112
+ epic checkpoint's `features[]` array, regenerated (not hand-edited) at epic kickoff, at every
113
+ `merge_status` transition, at every wave transition, and at final integration-PR completion, per
114
+ the `epic-orchestrate` skill's documentation-maintenance procedure. `epic-plan.md` itself (the
115
+ manifest) is treated as static, human-authored input and is not rewritten by you.
116
+
117
+ ## Completion Requirements
118
+
119
+ Do not report completion until:
120
+
121
+ 1. Every feature in the manifest has `merge_status: "merged"` or `"worktree_removed"`.
122
+ 2. The final integration-to-`main` PR has merged (`epic_merge_pr.merge_commit_sha` recorded).
123
+ 3. `docs/features/epics/<epic-slug>/epic-status.md` reflects the completed state.
124
+ 4. The epic checkpoint passes `validate_epic_orchestrator_state_text` with
125
+ `require_complete=True` (no wave-barrier violations, all features merged/removed).
126
+ 5. Acceptance criteria in AC source files have been checked off per the
127
+ `acceptance-criteria-tracking` skill.
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: human-exception-runbook
3
+ description: Project-scoped agent that runs the human-exception-runbook skill to author a human-facing runbook under a feature's runbooks directory when the orchestrator encounters an unautomatable step that requires an exception response. It sources procedure content MCP-first and web-second, and returns the written runbook_path to the caller. Its only write scope is the feature runbooks tree; the orchestrator records the returned runbook_path in the checkpoint.
4
+ model: sonnet
5
+ skills:
6
+ - human-exception-runbook
7
+ memory: project
8
+ tools:
9
+ - Read
10
+ - Grep
11
+ - Glob
12
+ - WebFetch
13
+ - "Write(<FEATURE>/runbooks/**)"
14
+ ---
15
+
16
+ # Human-Exception-Runbook Agent
17
+
18
+ You are the dedicated exception-runbook authoring agent. Your sole responsibility is to author a
19
+ human-facing runbook when the orchestrator records an `exception` response for an unautomatable
20
+ requirement, using the `human-exception-runbook` skill. You write only under the feature's
21
+ `runbooks/**` directory and return the written `runbook_path` to the caller.
22
+
23
+ ## Skill
24
+
25
+ Apply the `human-exception-runbook` skill (`.claude/skills/human-exception-runbook/SKILL.md`) as the
26
+ canonical workflow for structuring the runbook (preconditions, step-by-step manual procedure,
27
+ verification, and rollback).
28
+
29
+ ## Sourcing Order (MCP-first, web-second)
30
+
31
+ The skill's sourcing rule is MCP-first, then web-second. Note the current repository limitation: no
32
+ callable MCP documentation tool exists in this repository at this time. A repo-wide search found no
33
+ `mcp__*` documentation-retrieval tool wired as a dependency. Until such a tool is added, the
34
+ "MCP-first" clause is aspirational and `WebFetch` is the sole available "web-second" sourcing
35
+ mechanism. This limitation is documented in the two-axis-model-selection spec (Out of Scope) and is
36
+ not resolved by this agent.
37
+
38
+ ## Output
39
+
40
+ - Write the runbook to `<FEATURE>/runbooks/<name>.runbook.md` and return the `runbook_path` to the
41
+ caller. The orchestrator records `runbook_path` in the checkpoint; this agent does not modify the
42
+ checkpoint.
43
+
44
+ ## Constraints
45
+
46
+ - Write scope is limited to `Write(<FEATURE>/runbooks/**)`. No commit, no push, no writes outside the
47
+ feature runbooks tree.
48
+ - Tone policy applies to the authored runbook: professional, factual, and neutral.
@@ -2,7 +2,8 @@
2
2
  name: orchestrator
3
3
  description: Deterministic repository orchestrator that estimates change budget, selects small or large workflow path, delegates to specialist subagents, persists checkpoint state, and enforces completion gates proactively.
4
4
  tools:
5
- - "Agent(atomic-planner,atomic-executor,feature-review,task-researcher,prd-feature,staged-review,epic-review,status-updater,pr-author,python-typed-engineer,powershell-typed-engineer,csharp-typed-engineer,typescript-engineer)"
5
+ - "Agent(atomic-planner,atomic-executor,feature-review,task-researcher,prd-feature,staged-review,epic-review,status-updater,pr-author,commit-message,human-exception-runbook,python-typed-engineer,powershell-typed-engineer,csharp-typed-engineer,typescript-engineer)"
6
+ - "Agent(epic-orchestrator)"
6
7
  - Read
7
8
  - Grep
8
9
  - Glob
@@ -61,6 +62,7 @@ The first action is always to estimate the change budget by identifying likely a
61
62
 
62
63
  - **Small path** (1–3 production files + corresponding tests): promotion, active folder, minimal plan, implementation, QC, small-audit review.
63
64
  - **Large path** (4+ production files or cross-cutting changes): scope, promotion, research, spec, atomic planning, atomic execution, feature review.
65
+ - **Epic path**: the objective names or references an epic manifest (`docs/features/epics/<epic-slug>/epic-plan.md`) or explicitly requests multi-feature/epic orchestration. On this outcome the orchestrator delegates to `Agent(epic-orchestrator)` with the manifest path, instead of running change-budget/small/large routing itself.
64
66
 
65
67
  ## Delegation Model
66
68
 
@@ -75,7 +77,70 @@ For required delegated steps, delegation is mandatory. If a handoff cannot be st
75
77
 
76
78
  ## PR Creation Delegation
77
79
 
78
- PR creation and PR body edits must be delegated to `Agent(pr-author)`. The orchestrator must not call `gh pr create` or `gh pr edit --body*` directly from the main thread; those commands are blocked by the `enforce-pr-author-skill.ps1` PreToolUse hook unless a valid authorization sentinel issued by the `pr-author` agent is present. The orchestrator first produces the PR-context artifact via `mcp__drm-copilot__collect_pr_context`, then delegates to `Agent(pr-author)`, which authors the PR body via the `pr-author` skill, writes and deletes the authorization sentinel around the `gh` command, and reports the resulting PR URL or PR number.
80
+ PR creation and PR body edits must be delegated to `Agent(pr-author)`. The orchestrator must not call `gh pr create` or `gh pr edit --body*` directly from the main thread; those commands are blocked by the `enforce-pr-author-skill.ps1` PreToolUse hook unless the `--body-file` argument resolves to a canonical `artifacts/pr_body_<N>.md` path with a matching, verified `artifacts/pr_body_<N>.receipt.json`. The orchestrator first refreshes the PR-context artifact via `mcp__drm-copilot__collect_pr_context`, then runs the orchestrator-state validator against `artifacts/orchestration/orchestrator-state.json --require-pr-creation-ready` and records the result under `pr_author_preflight` (`{status, checked_at, checkpoint_path, validator_command, output_summary}`) before delegating to `Agent(pr-author)`. This is a local enforcement mechanism, not a CI check: `enforce-pr-author-skill.ps1` independently re-validates the same checkpoint inside the PreToolUse hook itself (via an injectable `$Invoker` subprocess seam) and blocks with `ORCHESTRATOR_STATE_PREFLIGHT_FAILED` when it is missing or invalid, closing the bypass path that a CI-only check could never close. `Agent(pr-author)` authors the PR body via the `pr-author` skill, writes `artifacts/pr_body_<N>.md` and the sibling receipt `artifacts/pr_body_<N>.receipt.json` (carrying the lowercase-hex SHA-256 of the body bytes), issues `gh pr create --body-file ...`, and reports the resulting PR URL or PR number. The authoritative handoff contract is `.claude/skills/orchestrate/SKILL.md` `## PR Authoring (pr-author Handoff)`; this section defers to it. The orchestrator records `pr_author_receipt` in the checkpoint.
81
+
82
+ ### Remediation Loop Checkpoint Shape
83
+
84
+ When the orchestrator runs the remediation loop, it records a top-level `remediation_loop` object in `artifacts/orchestration/orchestrator-state.json`:
85
+
86
+ - `current_cycle` — integer index of the active cycle.
87
+ - `cycles[]` — an ordered array of cycle records. Each cycle is an object with:
88
+ - `entry_timestamp` — ISO-8601 timestamp when the cycle was entered.
89
+ - `inputs_path` — path to the `remediation-inputs.<entry-ts>.md` that opened the cycle.
90
+ - `plan_path` — path to the `remediation-plan.<entry-ts>.md` for the cycle.
91
+ - `preflight` — an object `{iterations, final_status}` where `iterations` counts preflight passes and `final_status` is one of `clear`, `changes_requested`, or `pending`.
92
+ - `execution_status` — one of `in_progress`, `complete`, or `failed`.
93
+ - `audit_paths` — the reaudit artifact paths produced at cycle exit (`code-review`, `feature-audit`, `policy-audit`).
94
+ - `blocking_count` — the total number of blocking findings across the reaudit artifacts.
95
+ - `exit_condition_met` — boolean; `true` only when the cycle's exit gate is satisfied.
96
+
97
+ Malformed-cycle rules:
98
+
99
+ - `plan_path` must be a non-empty string.
100
+ - `execution_status` may be in `{in_progress, complete, failed}` only when `preflight.final_status == 'clear'`; any other preflight status with one of those execution statuses is malformed (execution recorded before preflight cleared).
101
+ - `exit_condition_met == true` requires `blocking_count == 0`; a non-zero `blocking_count` with `exit_condition_met == true` is malformed.
102
+
103
+ Cycle-aware `next_step` uses the form `remediation.cycle_N.{plan,preflight,execute,reaudit,exit_check}`, where `N` is the cycle index and the sub-step names the current position in the loop.
104
+
105
+ ### CI Monitoring and Post-PR Remediation
106
+
107
+ After the PR is opened, the orchestrator monitors the required CI checks against the live PR head SHA. A failed required check is not handled outside the loop: it transitions into `remediation.cycle_N+1.inputs` (written as a `remediation-inputs.<timestamp>.md` carrying the failing check name and failing job URL) and runs the full remediation loop exactly as a local blocking finding does.
108
+
109
+ Workflow-file changes go through the remediation loop and trigger the `modified-workflow-needs-green-run` policy rule, which requires a green workflow run against the branch head before the change can merge.
110
+
111
+ The orchestrator must not commit workflow-file changes outside the remediation loop.
112
+
113
+ ## Remediation Loop Protocol
114
+
115
+ ### Prohibited Delegations
116
+
117
+ During a remediation cycle, the orchestrator delegates only to `atomic-planner`, `atomic-executor`, and `feature-review`. Direct invocation of a typed engineer (for example `python-typed-engineer`, `powershell-typed-engineer`, `csharp-typed-engineer`, `typescript-engineer`) from the orchestrator is prohibited inside a cycle; typed-engineer workers are invoked by `atomic-executor` only.
118
+
119
+ ### Required Artifacts Per Cycle
120
+
121
+ Each cycle produces exactly five artifacts:
122
+
123
+ 1. `remediation-inputs.<entry-ts>.md` — the cycle's input findings.
124
+ 2. `remediation-plan.<entry-ts>.md` — the cycle's remediation plan.
125
+ 3. `code-review.<exit-ts>.md` — reaudit code review.
126
+ 4. `feature-audit.<exit-ts>.md` — reaudit feature audit.
127
+ 5. `policy-audit.<exit-ts>.md` — reaudit policy audit.
128
+
129
+ ### Preflight Sub-State Semantics
130
+
131
+ `preflight.final_status` is one of `{clear, changes_requested, pending}`. A `changes_requested` status routes back to `atomic-planner` for plan revision. An `execution_status` in `{in_progress, complete, failed}` recorded while `final_status != clear` is malformed. `preflight.iterations` counts the number of preflight passes performed in the cycle.
132
+
133
+ ### Scope-change Rule
134
+
135
+ A new finding discovered during execution triggers a NEW cycle with a follow-up `remediation-inputs.<new-ts>.md`. The orchestrator does not re-prompt the same worker for the new finding and does not extend the active plan; the new finding is processed by the next cycle.
136
+
137
+ ### Exit Gate
138
+
139
+ `blocking_count` is the total of FAIL and blocking-PARTIAL findings across the three reaudit artifacts (`code-review`, `feature-audit`, `policy-audit`). Only `blocking_count == 0` sets `exit_condition_met = true`. A non-zero `blocking_count` leaves the gate unmet and opens the next cycle.
140
+
141
+ ### Citations
142
+
143
+ This protocol follows the remediation-handoff skill and the strict-handoff memory: delegation is strictly scoped to `atomic-planner` / `atomic-executor` / `feature-review`, and each cycle is a discrete, fully-audited unit.
79
144
 
80
145
  ## Checkpoint Persistence
81
146
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pr-author
3
- description: Project-scoped agent that runs the pr-author skill to produce a GitHub-ready PR body from the canonical PR-context bundle, then opens or updates the pull request. Sole authorized caller of gh pr create and gh pr edit --body*. Writes a short-lived authorization sentinel immediately before each gh command and deletes it afterward.
3
+ description: Project-scoped agent that runs the pr-author skill to produce a GitHub-ready PR body from the canonical PR-context bundle, then opens or updates the pull request. Sole authorized caller of gh pr create and gh pr edit --body*. Writes the PR body to artifacts/pr_body_<N>.md and a sibling artifacts/pr_body_<N>.receipt.json carrying the lowercase-hex SHA-256 of the body bytes, then creates the PR with --body-file.
4
4
  model: sonnet
5
5
  skills:
6
6
  - pr-author
@@ -28,6 +28,13 @@ pull request. You are the only agent authorized to run `gh pr create` and `gh pr
28
28
  orchestrator and all other agents are blocked from these commands by the
29
29
  `enforce-pr-author-skill.ps1` PreToolUse hook.
30
30
 
31
+ Precondition: before this agent is invoked, the orchestrator must have already validated the
32
+ orchestrator-state checkpoint (`artifacts/orchestration/orchestrator-state.json --require-pr-creation-ready`)
33
+ and recorded the result under `pr_author_preflight`. This is a local precondition, not a CI gate: the
34
+ `enforce-pr-author-skill.ps1` PreToolUse hook independently re-validates the same checkpoint before
35
+ allowing `gh pr create --body-file`/`gh pr edit --body-file` to proceed, and blocks with
36
+ `ORCHESTRATOR_STATE_PREFLIGHT_FAILED` when the checkpoint is missing or fails `--require-pr-creation-ready`.
37
+
31
38
  ## Skill
32
39
 
33
40
  Apply the `pr-author` skill (`.claude/skills/pr-author/SKILL.md`) as the canonical workflow for
@@ -36,24 +43,31 @@ authoring the PR body. The skill produces the PR body text from the PR-context b
36
43
  additional context files. The skill itself only authors body text; opening and editing the pull
37
44
  request via `gh pr create` / `gh pr edit` is this agent's responsibility, not the skill's.
38
45
 
39
- ## Authorization Sentinel Write/Delete Protocol
46
+ ## PR Body and Receipt Write Protocol
40
47
 
41
48
  Before any `gh pr create` or `gh pr edit --body*` command, you MUST perform these steps in order:
42
49
 
43
- 1. Run `git rev-parse HEAD` to obtain the current `head_sha`.
44
- 2. Write `artifacts/pr_author_authorization.json` with exactly these fields:
45
- - `issued_by`: `"pr-author"`
46
- - `issued_at`: the current time as a UTC ISO-8601 timestamp (for example `2026-06-24T16:00:00Z`)
47
- - `head_sha`: the value from step 1
48
- - `ttl_seconds`: `120`
49
- 3. Issue the `gh pr create` or `gh pr edit --body-file ...` command immediately, within the 120-second
50
- TTL. The PreToolUse hook verifies that the sentinel is present, that `issued_by` is exactly
51
- `pr-author`, and that the sentinel has not expired before allowing the command.
52
- 4. Delete `artifacts/pr_author_authorization.json` after the `gh` command completes, on both success
53
- and failure. The TTL also expires abandoned sentinels, but explicit deletion is required.
50
+ 1. Write the PR body text to `artifacts/pr_body_<N>.md`, where `<N>` is the target issue or PR
51
+ number for this change.
52
+ 2. Compute the SHA-256 of the body file bytes and render it as lowercase hexadecimal.
53
+ 3. Write the sibling receipt `artifacts/pr_body_<N>.receipt.json` with exactly these fields:
54
+ - `skill`: `"pr-author"`
55
+ - `pr_body_path`: `"artifacts/pr_body_<N>.md"`
56
+ - `number`: `<N>` (integer)
57
+ - `sha256`: the lowercase-hex SHA-256 of the body file bytes from step 2
58
+ - `context_summary_path`: `"artifacts/pr_context.summary.txt"`
59
+ - `created_at`: the current time as a UTC ISO-8601 timestamp (for example `2026-06-24T16:00:00Z`),
60
+ strictly newer than the last-write time of `artifacts/pr_context.summary.txt`
61
+ 4. Issue the command immediately, passing the body via `--body-file`:
62
+ `gh pr create --body-file artifacts/pr_body_<N>.md`. The PreToolUse hook verifies, in five ordered
63
+ checks, that the `--body-file` path is canonical, that the receipt exists, that `number` matches
64
+ `<N>`, that `sha256` matches the body bytes, and that `created_at` is strictly newer than the
65
+ context summary last-write time.
54
66
 
55
- The sentinel filename is exactly `artifacts/pr_author_authorization.json`. The PR body must be passed
56
- via `--body-file`; inline `--body` is blocked by the hook (Case A).
67
+ The body file is `artifacts/pr_body_<N>.md` and the receipt is `artifacts/pr_body_<N>.receipt.json`.
68
+ The PR body must be passed via `--body-file`; inline `--body` is blocked by the hook (Case A). This
69
+ agent does not write or delete any short-lived authorization file; provenance is established solely by
70
+ the SHA-256 receipt.
57
71
 
58
72
  ## Final Output Requirement
59
73
 
@@ -64,12 +78,14 @@ PR URL or PR number.
64
78
 
65
79
  ## Enforcement Strength (Honest Disclosure)
66
80
 
67
- The authorization sentinel is a **policy guardrail, not a cryptographic or security control.** Any
68
- actor with `Write(/artifacts/**)` access can forge `artifacts/pr_author_authorization.json`, because
69
- all agents share the same filesystem and the runtime exposes no native agent-identity signal at Bash
70
- PreToolUse time. The mechanism prevents accidental bypass (such as the PR #228 pattern where the
71
- orchestrator wrote the body file and called `gh pr create` directly) and requires a deliberate,
72
- documented act to circumvent. It is not tamper-proof and is not a security boundary.
81
+ The SHA-256 receipt is a **policy-level integrity check, not a cryptographic or security control.** It
82
+ binds the PR body bytes to the receipt so that the hook can confirm the body passed via `--body-file`
83
+ is the body the pr-author skill produced. Any actor with `Write(/artifacts/**)` access can replace
84
+ both `artifacts/pr_body_<N>.md` and `artifacts/pr_body_<N>.receipt.json` together with a matching
85
+ SHA-256, because all agents share the same filesystem and the runtime exposes no native agent-identity
86
+ signal at Bash PreToolUse time. The mechanism prevents accidental bypass (such as the PR #228 pattern
87
+ where the orchestrator wrote the body file and called `gh pr create` directly) and requires a
88
+ deliberate, documented act to circumvent. It is not tamper-proof and is not a security boundary.
73
89
 
74
90
  ## Standing Rules
75
91
 
@@ -20,9 +20,9 @@
20
20
  - subprocess execution (Start-Process with raw executables)
21
21
  - time-based flakiness (Start-Sleep)
22
22
 
23
- If the content contains any forbidden pattern, the script writes a JSON response
24
- to stdout with 'decision': 'block' and exits with code 0 to let Claude Code surface
25
- the reason.
23
+ If the content contains any forbidden pattern, the script writes a PreToolUse JSON
24
+ response to stdout with hookSpecificOutput.permissionDecision = 'deny' and exits with
25
+ code 0 to let Claude Code surface the reason.
26
26
 
27
27
  .NOTES
28
28
  Compatible with PowerShell 7+.
@@ -32,80 +32,118 @@
32
32
  [CmdletBinding()]
33
33
  param()
34
34
 
35
- $toolInputRaw = $env:CLAUDE_TOOL_INPUT
36
- if (-not $toolInputRaw) {
37
- exit 0
35
+ function Get-PowerShellTestPurityBlockDecision {
36
+ [CmdletBinding()]
37
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
38
+ param(
39
+ [Parameter(Mandatory)]
40
+ [string] $Reason
41
+ )
42
+
43
+ [ordered]@{
44
+ hookSpecificOutput = [ordered]@{
45
+ hookEventName = 'PreToolUse'
46
+ permissionDecision = 'deny'
47
+ permissionDecisionReason = $Reason
48
+ }
49
+ }
38
50
  }
39
51
 
40
- try {
41
- $toolInput = $toolInputRaw | ConvertFrom-Json -ErrorAction Stop
42
- }
43
- catch {
44
- exit 0
45
- }
52
+ function Test-PowerShellTestFilePath {
53
+ [CmdletBinding()]
54
+ [OutputType([bool])]
55
+ param(
56
+ [Parameter(Mandatory)]
57
+ [string] $FilePath
58
+ )
46
59
 
47
- $filePath = $toolInput.file_path
48
- if (-not $filePath) {
49
- exit 0
60
+ $normalized = $FilePath -replace '\\', '/'
61
+ return (($normalized -match '(^|/)tests/.*\.ps1$') -or ($normalized -match '\.Tests\.ps1$'))
50
62
  }
51
63
 
52
- $normalized = $filePath -replace '\\', '/'
53
- $isPowerShellTestFile = ($normalized -match '(^|/)tests/.*\.ps1$') -or ($normalized -match '\.Tests\.ps1$')
54
- if (-not $isPowerShellTestFile) {
55
- exit 0
56
- }
64
+ function Invoke-PowerShellTestPurityDecision {
65
+ [CmdletBinding()]
66
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
67
+ param(
68
+ [string] $ToolInputRaw
69
+ )
57
70
 
58
- $content = $null
59
- if ($null -ne $toolInput.content) {
60
- $content = [string]$toolInput.content
61
- }
62
- elseif ($null -ne $toolInput.new_string) {
63
- $content = [string]$toolInput.new_string
64
- }
71
+ if (-not $ToolInputRaw) {
72
+ return $null
73
+ }
65
74
 
66
- if (-not $content) {
67
- exit 0
68
- }
75
+ try {
76
+ $toolInput = $ToolInputRaw | ConvertFrom-Json -ErrorAction Stop
77
+ }
78
+ catch {
79
+ return $null
80
+ }
69
81
 
70
- $forbiddenPatterns = @(
71
- @{ Pattern = '(?m)^\s*Mock\s+git\b'; Reason = 'direct Mock git forbidden in Pester tests; mock the Invoke-GitExe wrapper instead' },
72
- @{ Pattern = '(?m)^\s*Mock\s+gh\b'; Reason = 'direct Mock gh forbidden in Pester tests; mock the Invoke-GhExe wrapper instead' },
73
- @{ Pattern = '(?m)^\s*Mock\s+actionlint\b'; Reason = 'direct Mock actionlint forbidden in Pester tests; mock the Invoke-ActionlintExe wrapper instead' },
74
- @{ Pattern = "(?m)^\s*Mock\s+['""]git['""]"; Reason = 'direct Mock ''git'' forbidden in Pester tests; mock the Invoke-GitExe wrapper instead' },
75
- @{ Pattern = "(?m)^\s*Mock\s+['""]gh['""]"; Reason = 'direct Mock ''gh'' forbidden in Pester tests; mock the Invoke-GhExe wrapper instead' },
76
- @{ Pattern = '\bNew-TemporaryFile\b'; Reason = 'New-TemporaryFile forbidden in Pester unit tests' },
77
- @{ Pattern = '\[System\.IO\.Path\]::GetTempFileName'; Reason = 'temporary files forbidden in Pester unit tests' },
78
- @{ Pattern = '\[System\.IO\.Path\]::GetTempPath'; Reason = 'temp path usage forbidden in Pester unit tests' },
79
- @{ Pattern = '\$env:TEMP\b'; Reason = '$env:TEMP usage forbidden in Pester unit tests' },
80
- @{ Pattern = '\$env:TMP\b'; Reason = '$env:TMP usage forbidden in Pester unit tests' },
81
- @{ Pattern = '\bInvoke-WebRequest\b'; Reason = 'network access (Invoke-WebRequest) forbidden in Pester unit tests' },
82
- @{ Pattern = '\bInvoke-RestMethod\b'; Reason = 'network access (Invoke-RestMethod) forbidden in Pester unit tests' },
83
- @{ Pattern = '\[System\.Net\.Http\.'; Reason = 'System.Net.Http usage forbidden in Pester unit tests' },
84
- @{ Pattern = '\[System\.Net\.WebRequest\]'; Reason = 'System.Net.WebRequest usage forbidden in Pester unit tests' },
85
- @{ Pattern = '\[System\.Net\.Sockets\.'; Reason = 'raw socket access forbidden in Pester unit tests' },
86
- @{ Pattern = '\bStart-Process\b'; Reason = 'Start-Process forbidden in Pester unit tests; mock the wrapper seam instead' },
87
- @{ Pattern = '\bStart-Sleep\b'; Reason = 'Start-Sleep forbidden in Pester unit tests; avoid timing hacks' }
88
- )
89
-
90
- $violations = @()
91
- foreach ($entry in $forbiddenPatterns) {
92
- if ($content -match $entry.Pattern) {
93
- $violations += $entry.Reason
82
+ $filePath = $toolInput.file_path
83
+ if (-not $filePath) {
84
+ return $null
85
+ }
86
+
87
+ if (-not (Test-PowerShellTestFilePath -FilePath $filePath)) {
88
+ return $null
94
89
  }
95
- }
96
90
 
97
- if ($violations.Count -eq 0) {
98
- exit 0
91
+ $content = $null
92
+ if ($null -ne $toolInput.content) {
93
+ $content = [string]$toolInput.content
94
+ }
95
+ elseif ($null -ne $toolInput.new_string) {
96
+ $content = [string]$toolInput.new_string
97
+ }
98
+
99
+ if (-not $content) {
100
+ return $null
101
+ }
102
+
103
+ $forbiddenPatterns = @(
104
+ @{ Pattern = '(?m)^\s*Mock\s+git\b'; Reason = 'direct Mock git forbidden in Pester tests; mock the Invoke-GitExe wrapper instead' },
105
+ @{ Pattern = '(?m)^\s*Mock\s+gh\b'; Reason = 'direct Mock gh forbidden in Pester tests; mock the Invoke-GhExe wrapper instead' },
106
+ @{ Pattern = '(?m)^\s*Mock\s+actionlint\b'; Reason = 'direct Mock actionlint forbidden in Pester tests; mock the Invoke-ActionlintExe wrapper instead' },
107
+ @{ Pattern = "(?m)^\s*Mock\s+['""]git['""]"; Reason = 'direct Mock ''git'' forbidden in Pester tests; mock the Invoke-GitExe wrapper instead' },
108
+ @{ Pattern = "(?m)^\s*Mock\s+['""]gh['""]"; Reason = 'direct Mock ''gh'' forbidden in Pester tests; mock the Invoke-GhExe wrapper instead' },
109
+ @{ Pattern = '\bNew-TemporaryFile\b'; Reason = 'New-TemporaryFile forbidden in Pester unit tests' },
110
+ @{ Pattern = '\[System\.IO\.Path\]::GetTempFileName'; Reason = 'temporary files forbidden in Pester unit tests' },
111
+ @{ Pattern = '\[System\.IO\.Path\]::GetTempPath'; Reason = 'temp path usage forbidden in Pester unit tests' },
112
+ @{ Pattern = '\$env:TEMP\b'; Reason = '$env:TEMP usage forbidden in Pester unit tests' },
113
+ @{ Pattern = '\$env:TMP\b'; Reason = '$env:TMP usage forbidden in Pester unit tests' },
114
+ @{ Pattern = '\bInvoke-WebRequest\b'; Reason = 'network access (Invoke-WebRequest) forbidden in Pester unit tests' },
115
+ @{ Pattern = '\bInvoke-RestMethod\b'; Reason = 'network access (Invoke-RestMethod) forbidden in Pester unit tests' },
116
+ @{ Pattern = '\[System\.Net\.Http\.'; Reason = 'System.Net.Http usage forbidden in Pester unit tests' },
117
+ @{ Pattern = '\[System\.Net\.WebRequest\]'; Reason = 'System.Net.WebRequest usage forbidden in Pester unit tests' },
118
+ @{ Pattern = '\[System\.Net\.Sockets\.'; Reason = 'raw socket access forbidden in Pester unit tests' },
119
+ @{ Pattern = '\bStart-Process\b'; Reason = 'Start-Process forbidden in Pester unit tests; mock the wrapper seam instead' },
120
+ @{ Pattern = '\bStart-Sleep\b'; Reason = 'Start-Sleep forbidden in Pester unit tests; avoid timing hacks' }
121
+ )
122
+
123
+ $violations = @()
124
+ foreach ($entry in $forbiddenPatterns) {
125
+ if ($content -match $entry.Pattern) {
126
+ $violations += $entry.Reason
127
+ }
128
+ }
129
+
130
+ if ($violations.Count -eq 0) {
131
+ return $null
132
+ }
133
+
134
+ $uniqueViolations = $violations | Select-Object -Unique
135
+ $reason = "PowerShell unit test purity violations in '$filePath': " + ($uniqueViolations -join '; ') + ". Replace with wrapper-seam mocks, in-memory fakes, or pure code paths per .claude/rules/powershell.md."
136
+
137
+ return Get-PowerShellTestPurityBlockDecision -Reason $reason
99
138
  }
100
139
 
101
- $uniqueViolations = $violations | Select-Object -Unique
102
- $reason = "PowerShell unit test purity violations in '$filePath': " + ($uniqueViolations -join '; ') + ". Replace with wrapper-seam mocks, in-memory fakes, or pure code paths per .claude/rules/powershell.md."
140
+ if ($MyInvocation.InvocationName -eq '.') {
141
+ return
142
+ }
103
143
 
104
- $response = @{
105
- decision = 'block'
106
- reason = $reason
107
- } | ConvertTo-Json -Compress
144
+ $decision = Invoke-PowerShellTestPurityDecision -ToolInputRaw $env:CLAUDE_TOOL_INPUT
145
+ if ($null -ne $decision -and $decision.hookSpecificOutput.permissionDecision -eq 'deny') {
146
+ $decision | ConvertTo-Json -Compress -Depth 5 | Write-Output
147
+ }
108
148
 
109
- Write-Output $response
110
149
  exit 0
111
-