@danmoisan/drm-copilot-mcp 1.1.23 → 1.1.24

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 (199) hide show
  1. package/out/mcp-server.js +13347 -7926
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agents/discovery-capability-sentinel.md +34 -0
  4. package/resources/claude-customizations/.claude/agents/legacy-parity-analyst.md +101 -43
  5. package/resources/claude-customizations/.claude/agents/migration-coverage-reviewer.md +107 -43
  6. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +14 -0
  7. package/resources/claude-customizations/.claude/agents/parallel-planner.md +21 -1
  8. package/resources/claude-customizations/.claude/agents/pr-author.md +17 -1
  9. package/resources/claude-customizations/.claude/agents/requirements-reconciler.md +117 -42
  10. package/resources/claude-customizations/.claude/agents/runtime-characterization-analyst.md +103 -43
  11. package/resources/claude-customizations/.claude/hooks/discovery-capability-probe.ps1 +102 -0
  12. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +69 -194
  13. package/resources/claude-customizations/.claude/hooks/enforce-discovery-planning-gate.ps1 +87 -0
  14. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +7 -1
  15. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +6 -3
  16. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +168 -48
  17. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +1 -1
  18. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +59 -20
  19. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +44 -3
  20. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +60 -4
  21. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +4 -2
  22. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +59 -20
  23. package/resources/claude-customizations/.claude/hooks/record-discovery-agent-start.ps1 +132 -0
  24. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +56 -212
  25. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-rejection.ps1 +172 -0
  26. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +9 -1
  27. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +9 -3
  28. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusAggregate.psm1 +154 -0
  29. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusCombination.psm1 +228 -0
  30. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +1 -0
  31. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusNormalization.psm1 +4 -2
  32. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +63 -16
  33. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +56 -1
  34. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusWriteIntent.psm1 +4 -1
  35. package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +5 -1
  36. package/resources/claude-customizations/.claude/lib/codex-routing/CodexDiscoveryRouting.psm1 +161 -0
  37. package/resources/claude-customizations/.claude/lib/codex-routing/CodexTopology.psm1 +25 -2
  38. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthority.psd1 +90 -0
  39. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthority.psm1 +493 -0
  40. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthorization.psm1 +479 -0
  41. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryCapability.psm1 +468 -0
  42. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryEncoding.psm1 +156 -0
  43. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryManifest.psm1 +384 -0
  44. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryObservation.psm1 +217 -0
  45. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryPayload.psm1 +276 -0
  46. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryPlanning.psm1 +336 -0
  47. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryRootGrant.psm1 +164 -0
  48. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStart.psm1 +440 -0
  49. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStartRecord.psm1 +296 -0
  50. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStop.psm1 +336 -0
  51. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryToolRouting.psm1 +228 -0
  52. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryValidation.psm1 +188 -341
  53. package/resources/claude-customizations/.claude/lib/worktree-resolution/HookTargetWorktree.psm1 +391 -0
  54. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +1 -0
  55. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +150 -18
  56. package/resources/claude-customizations/.claude/settings.json +32 -3
  57. package/resources/claude-customizations/.claude/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
  58. package/resources/claude-customizations/.claude/skills/characterize-runtime-behavior/SKILL.md +134 -0
  59. package/resources/claude-customizations/.claude/skills/discovery-behavior-reconciliation/SKILL.md +28 -47
  60. package/resources/claude-customizations/.claude/skills/discovery-coverage-ledger/SKILL.md +32 -48
  61. package/resources/claude-customizations/.claude/skills/discovery-parity-matrix/SKILL.md +28 -45
  62. package/resources/claude-customizations/.claude/skills/discovery-repo-inventory/SKILL.md +28 -62
  63. package/resources/claude-customizations/.claude/skills/discovery-runtime-characterization/SKILL.md +28 -45
  64. package/resources/claude-customizations/.claude/skills/discovery-validate-artifacts/SKILL.md +28 -61
  65. package/resources/claude-customizations/.claude/skills/discovery-workflow/SKILL.md +55 -139
  66. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +24 -9
  67. package/resources/claude-customizations/.claude/skills/extract-feature-contract/SKILL.md +133 -0
  68. package/resources/claude-customizations/.claude/skills/generate-acceptance-scenarios/SKILL.md +132 -0
  69. package/resources/claude-customizations/.claude/skills/initialize-discovery-workspace/SKILL.md +131 -0
  70. package/resources/claude-customizations/.claude/skills/inventory-legacy-system/SKILL.md +131 -0
  71. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +5 -4
  72. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +21 -1
  73. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +11 -1
  74. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +34 -0
  75. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +69 -6
  76. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +15 -0
  77. package/resources/claude-customizations/.claude/skills/parallel-run/SKILL.md +6 -0
  78. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +21 -4
  79. package/resources/claude-customizations/.claude/skills/reconcile-parity-matrix/SKILL.md +131 -0
  80. package/resources/claude-customizations/.claude/skills/review-discovery-coverage/SKILL.md +129 -0
  81. package/resources/claude-customizations/.claude/skills/validate-discovery-artifacts/SKILL.md +127 -0
  82. package/resources/claude-customizations/config/blast-radius.json +6 -5
  83. package/resources/claude-customizations/config/orchestration-routing.json +61 -1
  84. package/resources/claude-customizations/pack-manifests/core.json +33 -1
  85. package/resources/codex-and-agents-customizations/.agents/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
  86. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +7 -0
  87. package/resources/codex-and-agents-customizations/.agents/skills/characterize-runtime-behavior/SKILL.md +134 -0
  88. package/resources/codex-and-agents-customizations/.agents/skills/discovery-behavior-reconciliation/SKILL.md +46 -0
  89. package/resources/codex-and-agents-customizations/.agents/skills/discovery-coverage-ledger/SKILL.md +50 -0
  90. package/resources/codex-and-agents-customizations/.agents/skills/discovery-parity-matrix/SKILL.md +46 -0
  91. package/resources/codex-and-agents-customizations/.agents/skills/discovery-repo-inventory/SKILL.md +46 -0
  92. package/resources/codex-and-agents-customizations/.agents/skills/discovery-runtime-characterization/SKILL.md +46 -0
  93. package/resources/codex-and-agents-customizations/.agents/skills/discovery-validate-artifacts/SKILL.md +46 -0
  94. package/resources/codex-and-agents-customizations/.agents/skills/discovery-workflow/SKILL.md +62 -0
  95. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +24 -9
  96. package/resources/codex-and-agents-customizations/.agents/skills/extract-feature-contract/SKILL.md +133 -0
  97. package/resources/codex-and-agents-customizations/.agents/skills/generate-acceptance-scenarios/SKILL.md +132 -0
  98. package/resources/codex-and-agents-customizations/.agents/skills/initialize-discovery-workspace/SKILL.md +131 -0
  99. package/resources/codex-and-agents-customizations/.agents/skills/inventory-legacy-system/SKILL.md +131 -0
  100. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +8 -0
  101. package/resources/codex-and-agents-customizations/.agents/skills/reconcile-parity-matrix/SKILL.md +131 -0
  102. package/resources/codex-and-agents-customizations/.agents/skills/review-discovery-coverage/SKILL.md +129 -0
  103. package/resources/codex-and-agents-customizations/.agents/skills/validate-discovery-artifacts/SKILL.md +127 -0
  104. package/resources/codex-and-agents-customizations/.codex/agents/discovery-capability-sentinel.toml +51 -0
  105. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c1.toml +148 -0
  106. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c2.toml +148 -0
  107. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c3-elevated.toml +148 -0
  108. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c3.toml +148 -0
  109. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c4.toml +148 -0
  110. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst.toml +148 -0
  111. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c1.toml +154 -0
  112. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c2.toml +154 -0
  113. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c3-elevated.toml +154 -0
  114. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c3.toml +154 -0
  115. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c4.toml +154 -0
  116. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer.toml +154 -0
  117. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c1.toml +164 -0
  118. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c2.toml +164 -0
  119. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c3-elevated.toml +164 -0
  120. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c3.toml +164 -0
  121. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c4.toml +164 -0
  122. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler.toml +164 -0
  123. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c1.toml +150 -0
  124. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c2.toml +150 -0
  125. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c3-elevated.toml +150 -0
  126. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c3.toml +150 -0
  127. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c4.toml +150 -0
  128. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst.toml +150 -0
  129. package/resources/codex-and-agents-customizations/.codex/config.toml +33 -3
  130. package/resources/codex-and-agents-customizations/.codex/hooks/codex-agent-profile-attestation.ps1 +22 -0
  131. package/resources/codex-and-agents-customizations/.codex/hooks/discovery-capability-probe.ps1 +97 -0
  132. package/resources/codex-and-agents-customizations/.codex/hooks/discovery-start-attestation.ps1 +171 -0
  133. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +4 -1
  134. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-discovery-artifact-gate.ps1 +187 -0
  135. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-discovery-planning-gate.ps1 +149 -0
  136. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +239 -2
  137. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +6 -15
  138. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +54 -15
  139. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +54 -15
  140. package/resources/codex-and-agents-customizations/.codex/hooks/hook-target-worktree.ps1 +369 -0
  141. package/resources/codex-and-agents-customizations/.codex/hooks/record-subagent-routing-attestation.ps1 +8 -0
  142. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +22 -1
  143. package/resources/codex-and-agents-customizations/.codex/hooks/validate-discovery-agent-output.ps1 +178 -0
  144. package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +11 -1
  145. package/resources/codex-and-agents-customizations/pack-manifests/core.json +63 -1
  146. package/resources/config/orchestration-routing.json +61 -1
  147. package/resources/customizations/.github/agents/discovery-capability-sentinel.agent.md +34 -0
  148. package/resources/customizations/.github/agents/legacy-parity-analyst.agent.md +122 -0
  149. package/resources/customizations/.github/agents/migration-coverage-reviewer.agent.md +128 -0
  150. package/resources/customizations/.github/agents/requirements-reconciler.agent.md +138 -0
  151. package/resources/customizations/.github/agents/runtime-characterization-analyst.agent.md +124 -0
  152. package/resources/customizations/.github/hooks/discovery-agent-start.ps1 +139 -0
  153. package/resources/customizations/.github/hooks/discovery-agent-stop.ps1 +123 -0
  154. package/resources/customizations/.github/hooks/discovery-capability-probe.ps1 +98 -0
  155. package/resources/customizations/.github/hooks/discovery-planning.ps1 +91 -0
  156. package/resources/customizations/.github/hooks/discovery-pre-tool.ps1 +123 -0
  157. package/resources/customizations/.github/hooks/discovery.json +35 -0
  158. package/resources/customizations/.github/instructions/mermaid.instructions.md +85 -0
  159. package/resources/customizations/.github/prompts/execute-hard-lock.prompt.md +105 -0
  160. package/resources/customizations/.github/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
  161. package/resources/customizations/.github/skills/characterize-runtime-behavior/SKILL.md +134 -0
  162. package/resources/customizations/.github/skills/discovery-behavior-reconciliation/SKILL.md +46 -0
  163. package/resources/customizations/.github/skills/discovery-coverage-ledger/SKILL.md +50 -0
  164. package/resources/customizations/.github/skills/discovery-parity-matrix/SKILL.md +46 -0
  165. package/resources/customizations/.github/skills/discovery-repo-inventory/SKILL.md +46 -0
  166. package/resources/customizations/.github/skills/discovery-runtime-characterization/SKILL.md +46 -0
  167. package/resources/customizations/.github/skills/discovery-validate-artifacts/SKILL.md +46 -0
  168. package/resources/customizations/.github/skills/discovery-workflow/SKILL.md +62 -0
  169. package/resources/customizations/.github/skills/execution-environment/SKILL.md +24 -9
  170. package/resources/customizations/.github/skills/extract-feature-contract/SKILL.md +133 -0
  171. package/resources/customizations/.github/skills/generate-acceptance-scenarios/SKILL.md +132 -0
  172. package/resources/customizations/.github/skills/initialize-discovery-workspace/SKILL.md +131 -0
  173. package/resources/customizations/.github/skills/inventory-legacy-system/SKILL.md +131 -0
  174. package/resources/customizations/.github/skills/reconcile-parity-matrix/SKILL.md +131 -0
  175. package/resources/customizations/.github/skills/review-discovery-coverage/SKILL.md +129 -0
  176. package/resources/customizations/.github/skills/validate-discovery-artifacts/SKILL.md +127 -0
  177. package/resources/lib/codex-routing/CodexDeployment.psm1 +5 -1
  178. package/resources/lib/codex-routing/CodexDiscoveryRouting.psm1 +161 -0
  179. package/resources/lib/codex-routing/CodexTopology.psm1 +25 -2
  180. package/resources/lib/discovery-validation/DiscoveryAuthority.psd1 +90 -0
  181. package/resources/lib/discovery-validation/DiscoveryAuthority.psm1 +493 -0
  182. package/resources/lib/discovery-validation/DiscoveryAuthorization.psm1 +479 -0
  183. package/resources/lib/discovery-validation/DiscoveryCapability.psm1 +468 -0
  184. package/resources/lib/discovery-validation/DiscoveryEncoding.psm1 +156 -0
  185. package/resources/lib/discovery-validation/DiscoveryManifest.psm1 +384 -0
  186. package/resources/lib/discovery-validation/DiscoveryObservation.psm1 +217 -0
  187. package/resources/lib/discovery-validation/DiscoveryPayload.psm1 +276 -0
  188. package/resources/lib/discovery-validation/DiscoveryPlanning.psm1 +336 -0
  189. package/resources/lib/discovery-validation/DiscoveryRootGrant.psm1 +164 -0
  190. package/resources/lib/discovery-validation/DiscoveryStart.psm1 +440 -0
  191. package/resources/lib/discovery-validation/DiscoveryStartRecord.psm1 +296 -0
  192. package/resources/lib/discovery-validation/DiscoveryStop.psm1 +336 -0
  193. package/resources/lib/discovery-validation/DiscoveryToolRouting.psm1 +228 -0
  194. package/resources/powershell/PoshQC/PoshQC.Parallel.psm1 +452 -0
  195. package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +106 -8
  196. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +12 -0
  197. package/resources/powershell/PoshQC/PoshQC.psd1 +6 -1
  198. package/resources/powershell/PoshQC/PoshQC.psm1 +7 -1
  199. package/resources/powershell/PoshQC/README.md +20 -1
@@ -1,228 +1,103 @@
1
1
  <#
2
2
  .SYNOPSIS
3
- Pre-tool-use hook that enforces discovery-artifact completion gates by
4
- invoking the discovery validators.
3
+ Native PreToolUse authorization adapter for Claude Code discovery agents.
5
4
 
6
5
  .DESCRIPTION
7
- Invoked by the Claude Code PreToolUse hook on Write or Edit operations
8
- (matcher "Write|Edit"). The hook reads the PreToolUse envelope JSON
9
- containing file_path, and either content (Write) or old_string/new_string
10
- (Edit).
11
-
12
- For a Write call whose file_path resolves to a recognized discovery
13
- artifact type, and when a required-artifact declaration for that type is
14
- present, the hook invokes the discovery validator CLI via
15
- Invoke-DiscoveryValidatorExe and maps a non-zero exit code or non-empty
16
- error output to a deny decision. Edit calls are allowed unconditionally
17
- (Edit supplies only a partial patch, not full file content, so it cannot
18
- be reliably validated here; the SubagentStop gate is the authoritative
19
- backstop). A file_path that does not resolve to a recognized discovery
20
- artifact type, or a required-artifact declaration that is absent, results
21
- in an allow decision without invoking the validator (fail-open).
22
-
23
- This hook never implements or reimplements discovery-validator logic; it
24
- only routes to the validator CLI delivered by a separate feature and
25
- interprets the CLI's exit code and captured output.
6
+ Invoked by the Claude Code PreToolUse hook. The hook reads the PreToolUse
7
+ envelope, derives the trusted principal from the payload alone (session, the
8
+ host-assigned agent_id and agent_type, and the literal parent root), and takes
9
+ its decision from the shared DiscoveryAuthorization module:
10
+
11
+ - a root call (no agent_id) of prepare_discovery_agent, run_discovery_doctor or
12
+ a controlled discovery tool is allowed with a one-use root grant the MCP
13
+ server consumes; every other root call and every call of an agent that is
14
+ not a discovery specialist is allowed, so ordinary sessions keep the
15
+ behavior of their other gates;
16
+ - a sentinel envelope is observed for the capability probe and never granted;
17
+ - a discovery specialist may use read-only native tools and the controlled
18
+ discovery MCP tools only; shell, terminal, exec and evaluation tools, generic
19
+ Write/Edit/apply_patch mutations, nested agents, foreign MCP servers,
20
+ root-only discovery operations and caller-supplied identity fields are denied
21
+ before execution;
22
+ - the specialist's first controlled call binds the single prepared invocation
23
+ of its session, parent and agent type; every controlled call must name that
24
+ invocation through invocation_ref and is recorded as one short-lived, one-use
25
+ tool-authorization in the protected store, which the gateway consumes exactly once.
26
+
27
+ Envelope anomalies and dependency failures deny with the
28
+ DISCOVERY_ARTIFACT_GATE_BLOCKED: prefix. The hook performs no validation of
29
+ artifact content itself: the installed Python runtime owns artifact semantics
30
+ and the gateway validates proposed bytes at the service boundary (issue #980).
26
31
 
27
32
  .NOTES
28
- Requires PowerShell 7.4+ (the shared validation module uses
29
- `Test-Json -SchemaFile` Draft 2020-12 support). Read-only validation gate that
30
- invokes NO external process: validation runs in-process through
31
- `.claude/lib/discovery-validation/DiscoveryValidation.psm1` (issue #475).
33
+ Requires PowerShell 7+. Invokes NO external process and never reads a profile,
34
+ locator or request field to decide identity or authority.
32
35
  #>
33
36
  [CmdletBinding()]
34
37
  param()
35
38
  $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
36
39
 
37
-
38
40
  try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
39
- $script:DiscoveryValidationImportFailure = $null; try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryValidation.psm1') -Force -Global -ErrorAction Stop } catch { $script:DiscoveryValidationImportFailure = 'DiscoveryValidation.psm1' }
40
- function Invoke-DiscoveryValidatorExe {
41
- <#
42
- .SYNOPSIS
43
- Wrapper around the discovery-artifact validator. Mockable seam.
44
- .DESCRIPTION
45
- Delegates to the portable PowerShell implementation in
46
- `.claude/lib/discovery-validation/DiscoveryValidation.psm1`, keeping this
47
- function's name, its `-ValidatorArgs <string[]>` parameter, and its
48
- `@{ ExitCode; Output }` return shape unchanged so existing mocks and
49
- `Should -Invoke` assertions continue to bind.
50
-
51
- This no longer invokes a Python interpreter (issue #475). The `.claude/**`
52
- payload ships to destinations with no guaranteed Python, Poetry, or
53
- `scripts/dev_tools`, where the previous `python -m ...` call failed
54
- obscurely or blocked every operation.
55
-
56
- Success is SILENT by contract: a passing validation returns `ExitCode = 0`
57
- with an EMPTY `Output`. The caller denies on a non-zero exit code OR on
58
- non-empty output, so any success chatter here would deny a passing
59
- validation (defect D-2).
60
- #>
61
- [CmdletBinding()]
62
- [OutputType([hashtable])]
63
- param(
64
- [Parameter(Mandatory = $true)]
65
- [string[]] $ValidatorArgs
66
- )
67
-
68
- $modulePath = Join-Path -Path $PSScriptRoot `
69
- -ChildPath '../lib/discovery-validation/DiscoveryValidation.psm1'
70
- if (-not (Test-Path -LiteralPath $modulePath -PathType Leaf)) {
71
- return @{ ExitCode = 1; Output = "Discovery-validation module not found: $modulePath" }
72
- }
73
-
74
- Import-Module -Name $modulePath -Force -ErrorAction Stop
75
- return Invoke-DiscoveryArtifactValidation -ValidatorArgs $ValidatorArgs
76
- }
77
-
78
- function Get-DiscoveryArtifactType {
41
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryEncoding.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryEncoding.psm1' -ErrorRecord $_ }
42
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryAuthority.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryAuthority.psm1' -ErrorRecord $_ }
43
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryPayload.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryPayload.psm1' -ErrorRecord $_ }
44
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryToolRouting.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryToolRouting.psm1' -ErrorRecord $_ }
45
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryStartRecord.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryStartRecord.psm1' -ErrorRecord $_ }
46
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryRootGrant.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryRootGrant.psm1' -ErrorRecord $_ }
47
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryObservation.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryObservation.psm1' -ErrorRecord $_ }
48
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryStart.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryStart.psm1' -ErrorRecord $_ }
49
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryAuthorization.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryAuthorization.psm1' -ErrorRecord $_ }
50
+
51
+ $script:DiscoveryGateReasonPrefix = 'DISCOVERY_ARTIFACT_GATE_BLOCKED:'
52
+ $script:DiscoveryGateHandlerPath = $PSCommandPath
53
+
54
+ function Get-DiscoveryGateHandlerSha256 {
79
55
  <#
80
56
  .SYNOPSIS
81
- Maps a normalized file path to a discovery-artifact-type token.
82
- .DESCRIPTION
83
- Returns one of the eight validator subcommand tokens (profile,
84
- feature-contract, coverage-ledger, runtime-scenario, parity-matrix,
85
- unspecified-behavior, product-decision, evidence-reference), or $null
86
- when the path does not resolve to a recognized discovery-artifact
87
- type.
88
-
89
- # TODO(#9002): this is a narrow, replaceable directory/filename lookup.
90
- The schema-versioned directory/filename convention this mapping
91
- depends on is owned by #9002 and is not finalized in this branch.
92
- Replace this lookup once #9002 ships its versioning convention.
57
+ Returns the SHA-256 of this handler's own bytes for the authorization writer record.
93
58
  #>
94
59
  [CmdletBinding()]
95
60
  [OutputType([string])]
96
- param(
97
- [Parameter(Mandatory = $true)]
98
- [string] $Path
99
- )
100
-
101
- $normalized = $Path -replace '\\', '/'
102
-
103
- $typeMap = [ordered]@{
104
- 'discovery/profile' = 'profile'
105
- 'discovery/feature-contract' = 'feature-contract'
106
- 'discovery/coverage-ledger' = 'coverage-ledger'
107
- 'discovery/runtime-scenario' = 'runtime-scenario'
108
- 'discovery/parity-matrix' = 'parity-matrix'
109
- 'discovery/unspecified-behavior' = 'unspecified-behavior'
110
- 'discovery/product-decision' = 'product-decision'
111
- 'discovery/evidence-reference' = 'evidence-reference'
112
- }
113
-
114
- foreach ($prefix in $typeMap.Keys) {
115
- if ($normalized -match "(^|/)$([regex]::Escape($prefix))") {
116
- return $typeMap[$prefix]
117
- }
118
- }
61
+ param([Parameter(Mandatory = $false)][AllowNull()][string]$HandlerPath = $script:DiscoveryGateHandlerPath)
119
62
 
120
- return $null
121
- }
122
-
123
- function Get-RequiredDiscoveryArtifactDeclaration {
124
- <#
125
- .SYNOPSIS
126
- Reads the domain-profile required-artifact declaration, if present.
127
- .DESCRIPTION
128
- # TODO(#9001): this is a narrow, injectable RequiredArtifactPathsReader
129
- seam. The discovery-workspace root and which of the eight artifact
130
- types are "required" for a given gate are domain-profile runtime
131
- configuration owned by #9001, which has no shipped parser/schema in
132
- this branch.
133
-
134
- Default behavior on absence is documented inline as fail-open (allow):
135
- when no domain profile is present, this function returns an object
136
- with Present = $false, and callers must treat that as "always allow,
137
- never invoke the validator" rather than as an error.
138
- #>
139
- [CmdletBinding()]
140
- [OutputType([hashtable])]
141
- param(
142
- [Parameter(Mandatory = $false)]
143
- [scriptblock] $ProfileReader = { $null }
144
- )
145
-
146
- $declaration = & $ProfileReader
147
- if ($null -eq $declaration) {
148
- # Fail open: no domain profile / required-artifact declaration present.
149
- return @{ Present = $false }
63
+ if ([string]::IsNullOrEmpty($HandlerPath) -or -not (Test-Path -LiteralPath $HandlerPath -PathType Leaf)) {
64
+ return ('0' * 64)
150
65
  }
151
-
152
- return @{ Present = $true; Declaration = $declaration }
66
+ return Get-DiscoveryBytesSha256Hex -Bytes ([System.IO.File]::ReadAllBytes($HandlerPath))
153
67
  }
154
68
 
155
69
  function Invoke-DiscoveryArtifactGateDecision {
156
70
  <#
157
71
  .SYNOPSIS
158
- Parses the PreToolUse envelope and returns an allow-or-deny decision for a
159
- discovery-artifact completion gate.
72
+ Parses the PreToolUse envelope and returns the allow-or-deny decision for a discovery agent.
73
+ .DESCRIPTION
74
+ Every seam defaults to the real host (platform state roots, protected
75
+ store, clock, nonce) and is injectable so tests run over in-memory values.
76
+ A recorded dependency failure denies before the envelope is read.
160
77
  #>
161
78
  [CmdletBinding()]
162
79
  [OutputType([System.Collections.Specialized.OrderedDictionary])]
163
80
  param(
164
- [string] $ToolInputRaw,
165
-
166
- [Parameter(Mandatory = $false)]
167
- [scriptblock] $RequiredArtifactReader = { Get-RequiredDiscoveryArtifactDeclaration }
81
+ [Parameter(Mandatory = $false)][AllowNull()][AllowEmptyString()][string]$ToolInputRaw,
82
+ [Parameter(Mandatory = $false)][AllowNull()][hashtable]$HostEnvironment = $null,
83
+ [Parameter(Mandatory = $false)][AllowNull()][hashtable]$Store = $null,
84
+ [Parameter(Mandatory = $false)][AllowNull()][nullable[datetime]]$Now = $null,
85
+ [Parameter(Mandatory = $false)][AllowNull()][string]$Nonce = $null
168
86
  )
169
- $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'DISCOVERY_ARTIFACT_GATE_BLOCKED:'
87
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix $script:DiscoveryGateReasonPrefix
170
88
  if ($null -ne $dependencyDecision) { return $dependencyDecision }
171
89
 
172
- $envelope = Resolve-ClaudeHookToolInput -Raw $ToolInputRaw
173
- if (-not $envelope.IsValid) {
174
- return [ordered]@{
175
- hookSpecificOutput = [ordered]@{
176
- hookEventName = 'PreToolUse'
177
- permissionDecision = 'deny'
178
- permissionDecisionReason = 'DISCOVERY_ARTIFACT_GATE_BLOCKED: payload anomaly - ' +
179
- (Get-ClaudeHookPayloadAnomalyReason -Anomaly $envelope.Anomaly) +
180
- '. The gate fails closed on an envelope it cannot read.'
181
- }
182
- }
183
- }
184
-
185
- $toolInput = $envelope.Value
186
- $filePath = Get-ClaudeHookToolInputString -ToolInput $toolInput -Name 'file_path'
187
- if (-not $filePath) {
188
- return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
189
- }
190
-
191
- # Edit calls supply only old_string/new_string (a partial patch), not full
192
- # file content. They are allowed unconditionally; the SubagentStop gate is
193
- # the authoritative backstop for artifacts touched by Edit.
194
- $toolInputProps = @($toolInput.PSObject.Properties.Name)
195
- $hasContent = $toolInputProps -contains 'content' -and $null -ne $toolInput.content
196
- if (-not $hasContent) {
197
- return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
198
- }
199
-
200
- $artifactType = Get-DiscoveryArtifactType -Path $filePath
201
- if (-not $artifactType) {
202
- return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
203
- }
204
- if ($script:DiscoveryValidationImportFailure) { return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'deny'; permissionDecisionReason = "DISCOVERY_ARTIFACT_GATE_BLOCKED: the validation module '$($script:DiscoveryValidationImportFailure)' failed to load, so the $artifactType artifact cannot be validated; the gate fails closed." } } }
205
-
206
- $requiredDeclaration = & $RequiredArtifactReader
207
- if (-not $requiredDeclaration.Present) {
208
- # Fail open: no domain profile / required-artifact declaration present.
209
- return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
210
- }
211
-
212
- $result = Invoke-DiscoveryValidatorExe -ValidatorArgs @($artifactType, $filePath)
213
- $hasErrorOutput = -not [string]::IsNullOrWhiteSpace($result.Output)
214
- if ($result.ExitCode -ne 0 -or $hasErrorOutput) {
215
- $reason = "DISCOVERY_ARTIFACT_GATE_BLOCKED: $($result.Output)"
216
- return [ordered]@{
217
- hookSpecificOutput = [ordered]@{
218
- hookEventName = 'PreToolUse'
219
- permissionDecision = 'deny'
220
- permissionDecisionReason = $reason
221
- }
222
- }
223
- }
224
-
225
- return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
90
+ # Seams resolve only after the dependency check so a missing module denies
91
+ # instead of raising while a default is evaluated.
92
+ if ($null -eq $HostEnvironment) { $HostEnvironment = Get-DiscoveryTrustedHostEnvironment }
93
+ if ($null -eq $Store) { $Store = Get-DiscoveryDefaultStore }
94
+ if ($null -eq $Now) { $Now = [datetime]::UtcNow }
95
+ if ([string]::IsNullOrEmpty($Nonce)) { $Nonce = Get-DiscoveryAuthorizationNonce }
96
+
97
+ $normalized = ConvertFrom-DiscoveryHostPayloadText -HostName 'claude-code' -Text $ToolInputRaw
98
+ $outcome = Get-DiscoveryPreToolDecision -Normalized $normalized -HostEnvironment $HostEnvironment `
99
+ -HandlerSha256 (Get-DiscoveryGateHandlerSha256) -Store $Store -Now $Now -Nonce $Nonce
100
+ return ConvertTo-DiscoveryPreToolHookOutput -Outcome $outcome -ReasonPrefix $script:DiscoveryGateReasonPrefix
226
101
  }
227
102
 
228
103
  # Guard allows dot-sourcing in tests without executing the entrypoint.
@@ -0,0 +1,87 @@
1
+ <#
2
+ .SYNOPSIS
3
+ PreToolUse hook that gates planner and orchestrator delegations on current configured discovery authority.
4
+
5
+ .DESCRIPTION
6
+ Invoked by the Claude Code PreToolUse hook on the Agent tool. The hook reads the
7
+ PreToolUse envelope, recognizes an atomic-planner or orchestrator launch, and
8
+ takes its decision from the shared DiscoveryPlanning module:
9
+
10
+ - a call that is not a planner or orchestrator launch, and a workspace without
11
+ a protected discovery activation (or with a deliberate deactivation), are
12
+ allowed, so the existing planning gates apply unchanged;
13
+ - an active workspace must still hold the activated locator and domain profile,
14
+ and the protected session must hold a current, passing, gateway-written
15
+ planning-mode receipt for this host and session whose inputs are unchanged
16
+ and whose manifest reflects the profile's planning prerequisite; a
17
+ structural, agent-output, completion-mode or version-1 receipt, a failing or
18
+ stale receipt, and a missing, disabled or malformed locator or profile deny
19
+ with the DISCOVERY_PLANNING_GATE_BLOCKED: prefix before the delegation runs.
20
+
21
+ The hook only verifies authority: it never runs Python or starts validation
22
+ (issue #980). Envelope anomalies and dependency failures deny with the prefix.
23
+
24
+ .NOTES
25
+ Requires PowerShell 7+. Invokes NO external process.
26
+ #>
27
+ [CmdletBinding()]
28
+ param()
29
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
30
+
31
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/hook-payload/HookPayload.psm1') -Force -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'HookPayload.psm1' -ErrorRecord $_ }
32
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryEncoding.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryEncoding.psm1' -ErrorRecord $_ }
33
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryAuthority.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryAuthority.psm1' -ErrorRecord $_ }
34
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryManifest.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryManifest.psm1' -ErrorRecord $_ }
35
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryPayload.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryPayload.psm1' -ErrorRecord $_ }
36
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryToolRouting.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryToolRouting.psm1' -ErrorRecord $_ }
37
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryAuthorization.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryAuthorization.psm1' -ErrorRecord $_ }
38
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryStartRecord.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryStartRecord.psm1' -ErrorRecord $_ }
39
+ try { Import-Module (Join-Path $PSScriptRoot '../lib/discovery-validation/DiscoveryPlanning.psm1') -Force -Global -ErrorAction Stop } catch { Add-HookDependencyFailure -Name 'DiscoveryPlanning.psm1' -ErrorRecord $_ }
40
+
41
+ $script:DiscoveryPlanningReasonPrefix = 'DISCOVERY_PLANNING_GATE_BLOCKED:'
42
+
43
+ function Invoke-DiscoveryPlanningGateDecision {
44
+ <#
45
+ .SYNOPSIS
46
+ Parses the PreToolUse envelope and returns the allow-or-deny decision for a planning delegation.
47
+ .DESCRIPTION
48
+ Every seam defaults to the real host (platform state roots, protected store,
49
+ workspace bytes) and is injectable so tests run over in-memory values. A
50
+ recorded dependency failure denies before the envelope is read.
51
+ #>
52
+ [CmdletBinding()]
53
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
54
+ param(
55
+ [Parameter(Mandatory = $false)][AllowNull()][AllowEmptyString()][string]$ToolInputRaw,
56
+ [Parameter(Mandatory = $false)][AllowNull()][hashtable]$HostEnvironment = $null,
57
+ [Parameter(Mandatory = $false)][AllowNull()][hashtable]$Store = $null,
58
+ [Parameter(Mandatory = $false)][AllowNull()][scriptblock]$ReadBytes = $null
59
+ )
60
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix $script:DiscoveryPlanningReasonPrefix
61
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
62
+
63
+ # Seams resolve only after the dependency check so a missing module denies
64
+ # instead of raising while a default is evaluated.
65
+ if ($null -eq $HostEnvironment) { $HostEnvironment = Get-DiscoveryTrustedHostEnvironment }
66
+ if ($null -eq $Store) { $Store = Get-DiscoveryStartStore }
67
+
68
+ $normalized = ConvertFrom-DiscoveryHostPayloadText -HostName 'claude-code' -Text $ToolInputRaw
69
+ $parameters = @{ Normalized = $normalized; HostEnvironment = $HostEnvironment; Store = $Store }
70
+ if ($null -ne $ReadBytes) { $parameters['ReadBytes'] = $ReadBytes }
71
+ $outcome = Get-DiscoveryPlanningDecision @parameters
72
+ return ConvertTo-DiscoveryPlanningHookOutput -Outcome $outcome -ReasonPrefix $script:DiscoveryPlanningReasonPrefix
73
+ }
74
+
75
+ # Guard allows dot-sourcing in tests without executing the entrypoint.
76
+ if ($MyInvocation.InvocationName -eq '.') {
77
+ return
78
+ }
79
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('DISCOVERY_PLANNING_GATE_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
80
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'DISCOVERY_PLANNING_GATE_BLOCKED:'
81
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
82
+
83
+ $decision = Invoke-DiscoveryPlanningGateDecision -ToolInputRaw (Read-ClaudeHookRawPayload)
84
+
85
+ $decision | ConvertTo-Json -Compress -Depth 5 | Write-Output
86
+
87
+ exit 0
@@ -12,6 +12,8 @@
12
12
  1. Child-feature path: artifacts/orchestration/orchestrator-state.json exists,
13
13
  epic_mode == true, and step9_status == "passed" (the per-feature orchestrator has
14
14
  already run S9 step 6's CI-green gate before attempting its own merge-on-green).
15
+ Only the JSON boolean true counts as epic mode; a string or number such as "false"
16
+ does not (issue #1001).
15
17
  2. Epic-integration path: artifacts/orchestration/epic-orchestrator-state.json exists,
16
18
  epic_merge_pr.ci_gate.conclusion == "success", and, when the command names an
17
19
  explicit PR number, that number matches epic_merge_pr.pr_number.
@@ -155,6 +157,10 @@ function Test-ChildCheckpointAllowsEpicMerge {
155
157
  <#
156
158
  .SYNOPSIS
157
159
  Decision logic for the child-feature checkpoint path (branch 1).
160
+ .DESCRIPTION
161
+ Only the JSON boolean true counts as epic mode: epic_mode must be a System.Boolean
162
+ $true after ConvertFrom-Json. A string or number such as "false", "true", or 1 is not
163
+ epic mode, so branch 1 declines and later branches decide (issue #1001).
158
164
  .PARAMETER Checkpoint
159
165
  Parsed child orchestrator checkpoint, or $null when absent/unreadable.
160
166
  .OUTPUTS
@@ -171,7 +177,7 @@ function Test-ChildCheckpointAllowsEpicMerge {
171
177
  return $false
172
178
  }
173
179
  $props = @($Checkpoint.PSObject.Properties.Name)
174
- if ($props -notcontains 'epic_mode' -or -not [bool]$Checkpoint.epic_mode) {
180
+ if ($props -notcontains 'epic_mode' -or -not (($Checkpoint.epic_mode -is [bool]) -and $Checkpoint.epic_mode)) {
175
181
  return $false
176
182
  }
177
183
  if ($props -notcontains 'step9_status') {
@@ -45,7 +45,8 @@
45
45
  `epic_mode` is not true requires the prompt line
46
46
  `Author-asserted autoclose issues: #<issue-num>` at the start of a line. Its absence denies
47
47
  with the AUTHOR_ASSERTED_AUTOCLOSE_MISSING sub-code (Get-ModelRoutingAuthorAssertedReason).
48
- The epic-scope path is not subject to this rule.
48
+ The epic-scope path is not subject to this rule. Only the JSON boolean true waives the line;
49
+ a string or number such as "true" or 1 does not (issue #1001).
49
50
 
50
51
  .NOTES
51
52
  Compatible with PowerShell 7+. Read-only presence-gating deterrent.
@@ -214,7 +215,9 @@ function Get-ModelRoutingAuthorAssertedReason {
214
215
  epic_mode property whose value is true (an epic child whose integration PR owns the
215
216
  closing keyword), or when a prompt line starts with 'Author-asserted autoclose issues:'
216
217
  and names '#<issue-num>' later on that line with no digit after the number. Property
217
- presence is tested first because strict mode throws on a missing property.
218
+ presence is tested first because strict mode throws on a missing property. Only the
219
+ JSON boolean true waives the line: a string or number such as "true" or 1 does not
220
+ (issue #1001).
218
221
  .PARAMETER Checkpoint
219
222
  The resolved per-feature checkpoint object.
220
223
  .PARAMETER PromptText
@@ -231,7 +234,7 @@ function Get-ModelRoutingAuthorAssertedReason {
231
234
 
232
235
  $names = @($Checkpoint.PSObject.Properties.Name)
233
236
  $issue = if ($names -contains 'issue-num') { ConvertTo-WorktreeItemIssueNumber -Value $Checkpoint.'issue-num' } else { $null }
234
- if ($null -eq $issue -or ($names -contains 'epic_mode' -and $Checkpoint.epic_mode -eq $true)) {
237
+ if ($null -eq $issue -or ($names -contains 'epic_mode' -and ($Checkpoint.epic_mode -is [bool]) -and $Checkpoint.epic_mode)) {
235
238
  return $null
236
239
  }
237
240
  $line = "Author-asserted autoclose issues: #$issue"