@danmoisan/drm-copilot-mcp 1.1.22 → 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 (261) hide show
  1. package/out/mcp-server.js +18758 -12030
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +1 -1
  4. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_test_files_count_against_500_cap.md +4 -4
  5. package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +1 -1
  6. package/resources/claude-customizations/.claude/agents/discovery-capability-sentinel.md +34 -0
  7. package/resources/claude-customizations/.claude/agents/feature-review.md +8 -0
  8. package/resources/claude-customizations/.claude/agents/legacy-parity-analyst.md +101 -43
  9. package/resources/claude-customizations/.claude/agents/migration-coverage-reviewer.md +107 -43
  10. package/resources/claude-customizations/.claude/agents/orchestrator.md +1 -1
  11. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +60 -6
  12. package/resources/claude-customizations/.claude/agents/parallel-planner.md +21 -1
  13. package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +1 -1
  14. package/resources/claude-customizations/.claude/agents/pr-author.md +48 -7
  15. package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +1 -1
  16. package/resources/claude-customizations/.claude/agents/requirements-reconciler.md +117 -42
  17. package/resources/claude-customizations/.claude/agents/runtime-characterization-analyst.md +103 -43
  18. package/resources/claude-customizations/.claude/hooks/discovery-capability-probe.ps1 +102 -0
  19. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +69 -194
  20. package/resources/claude-customizations/.claude/hooks/enforce-discovery-planning-gate.ps1 +87 -0
  21. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-green-ci.ps1 +325 -0
  22. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +29 -7
  23. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +23 -10
  24. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +29 -21
  25. package/resources/claude-customizations/.claude/hooks/enforce-file-size-limit.ps1 +244 -0
  26. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +51 -0
  27. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +168 -48
  28. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +1 -1
  29. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +22 -11
  30. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +59 -20
  31. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +51 -4
  32. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +72 -11
  33. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.current-branch.ps1 +149 -0
  34. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +4 -2
  35. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +59 -20
  36. package/resources/claude-customizations/.claude/hooks/record-discovery-agent-start.ps1 +132 -0
  37. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +56 -212
  38. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-feature-folder.ps1 +146 -0
  39. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-rejection.ps1 +172 -0
  40. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +34 -4
  41. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +9 -3
  42. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusAggregate.psm1 +154 -0
  43. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusCombination.psm1 +228 -0
  44. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +1 -0
  45. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusNormalization.psm1 +4 -2
  46. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +63 -16
  47. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +56 -1
  48. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusWriteIntent.psm1 +4 -1
  49. package/resources/claude-customizations/.claude/lib/cleanup-manifest/CleanupWorktreeManifest.psm1 +33 -6
  50. package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +5 -1
  51. package/resources/claude-customizations/.claude/lib/codex-routing/CodexDiscoveryRouting.psm1 +161 -0
  52. package/resources/claude-customizations/.claude/lib/codex-routing/CodexTopology.psm1 +25 -2
  53. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthority.psd1 +90 -0
  54. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthority.psm1 +493 -0
  55. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthorization.psm1 +479 -0
  56. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryCapability.psm1 +468 -0
  57. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryEncoding.psm1 +156 -0
  58. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryManifest.psm1 +384 -0
  59. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryObservation.psm1 +217 -0
  60. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryPayload.psm1 +276 -0
  61. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryPlanning.psm1 +336 -0
  62. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryRootGrant.psm1 +164 -0
  63. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStart.psm1 +440 -0
  64. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStartRecord.psm1 +296 -0
  65. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStop.psm1 +336 -0
  66. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryToolRouting.psm1 +228 -0
  67. package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryValidation.psm1 +188 -341
  68. package/resources/claude-customizations/.claude/lib/file-size-policy/FileSizePolicy.psm1 +434 -0
  69. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateFeatureFolderRecord.psm1 +166 -0
  70. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +8 -3
  71. package/resources/claude-customizations/.claude/lib/worktree-resolution/HookTargetWorktree.psm1 +391 -0
  72. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunRecordPath.psm1 +185 -0
  73. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +11 -21
  74. package/resources/claude-customizations/.claude/rules/general-code-change.md +4 -1
  75. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +29 -5
  76. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +152 -20
  77. package/resources/claude-customizations/.claude/rules/powershell.md +1 -1
  78. package/resources/claude-customizations/.claude/rules/shell.md +1 -1
  79. package/resources/claude-customizations/.claude/settings.json +36 -3
  80. package/resources/claude-customizations/.claude/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
  81. package/resources/claude-customizations/.claude/skills/characterize-runtime-behavior/SKILL.md +134 -0
  82. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +2 -2
  83. package/resources/claude-customizations/.claude/skills/discovery-behavior-reconciliation/SKILL.md +28 -47
  84. package/resources/claude-customizations/.claude/skills/discovery-coverage-ledger/SKILL.md +32 -48
  85. package/resources/claude-customizations/.claude/skills/discovery-parity-matrix/SKILL.md +28 -45
  86. package/resources/claude-customizations/.claude/skills/discovery-repo-inventory/SKILL.md +28 -62
  87. package/resources/claude-customizations/.claude/skills/discovery-runtime-characterization/SKILL.md +28 -45
  88. package/resources/claude-customizations/.claude/skills/discovery-validate-artifacts/SKILL.md +28 -61
  89. package/resources/claude-customizations/.claude/skills/discovery-workflow/SKILL.md +55 -139
  90. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +25 -10
  91. package/resources/claude-customizations/.claude/skills/extract-feature-contract/SKILL.md +133 -0
  92. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -0
  93. package/resources/claude-customizations/.claude/skills/generate-acceptance-scenarios/SKILL.md +132 -0
  94. package/resources/claude-customizations/.claude/skills/initialize-discovery-workspace/SKILL.md +131 -0
  95. package/resources/claude-customizations/.claude/skills/inventory-legacy-system/SKILL.md +131 -0
  96. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +15 -7
  97. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +25 -2
  98. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +11 -1
  99. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +124 -14
  100. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +72 -7
  101. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +15 -0
  102. package/resources/claude-customizations/.claude/skills/parallel-run/SKILL.md +6 -0
  103. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +56 -9
  104. package/resources/claude-customizations/.claude/skills/reconcile-parity-matrix/SKILL.md +131 -0
  105. package/resources/claude-customizations/.claude/skills/review-discovery-coverage/SKILL.md +129 -0
  106. package/resources/claude-customizations/.claude/skills/validate-discovery-artifacts/SKILL.md +127 -0
  107. package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +1 -1
  108. package/resources/claude-customizations/config/blast-radius.json +6 -5
  109. package/resources/claude-customizations/config/orchestration-routing.json +61 -1
  110. package/resources/claude-customizations/pack-manifests/core.json +40 -1
  111. package/resources/codex-and-agents-customizations/.agents/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
  112. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +7 -0
  113. package/resources/codex-and-agents-customizations/.agents/skills/characterize-runtime-behavior/SKILL.md +134 -0
  114. package/resources/codex-and-agents-customizations/.agents/skills/discovery-behavior-reconciliation/SKILL.md +46 -0
  115. package/resources/codex-and-agents-customizations/.agents/skills/discovery-coverage-ledger/SKILL.md +50 -0
  116. package/resources/codex-and-agents-customizations/.agents/skills/discovery-parity-matrix/SKILL.md +46 -0
  117. package/resources/codex-and-agents-customizations/.agents/skills/discovery-repo-inventory/SKILL.md +46 -0
  118. package/resources/codex-and-agents-customizations/.agents/skills/discovery-runtime-characterization/SKILL.md +46 -0
  119. package/resources/codex-and-agents-customizations/.agents/skills/discovery-validate-artifacts/SKILL.md +46 -0
  120. package/resources/codex-and-agents-customizations/.agents/skills/discovery-workflow/SKILL.md +62 -0
  121. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +25 -10
  122. package/resources/codex-and-agents-customizations/.agents/skills/extract-feature-contract/SKILL.md +133 -0
  123. package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  124. package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +8 -0
  125. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +4 -1
  126. package/resources/codex-and-agents-customizations/.agents/skills/generate-acceptance-scenarios/SKILL.md +132 -0
  127. package/resources/codex-and-agents-customizations/.agents/skills/initialize-discovery-workspace/SKILL.md +131 -0
  128. package/resources/codex-and-agents-customizations/.agents/skills/inventory-legacy-system/SKILL.md +131 -0
  129. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +8 -0
  130. package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +1 -1
  131. package/resources/codex-and-agents-customizations/.agents/skills/reconcile-parity-matrix/SKILL.md +131 -0
  132. package/resources/codex-and-agents-customizations/.agents/skills/review-discovery-coverage/SKILL.md +129 -0
  133. package/resources/codex-and-agents-customizations/.agents/skills/validate-discovery-artifacts/SKILL.md +127 -0
  134. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +1 -1
  135. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +1 -1
  136. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +1 -1
  137. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +1 -1
  138. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +1 -1
  139. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +1 -1
  140. package/resources/codex-and-agents-customizations/.codex/agents/discovery-capability-sentinel.toml +51 -0
  141. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +8 -0
  142. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c1.toml +148 -0
  143. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c2.toml +148 -0
  144. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c3-elevated.toml +148 -0
  145. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c3.toml +148 -0
  146. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c4.toml +148 -0
  147. package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst.toml +148 -0
  148. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c1.toml +154 -0
  149. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c2.toml +154 -0
  150. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c3-elevated.toml +154 -0
  151. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c3.toml +154 -0
  152. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c4.toml +154 -0
  153. package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer.toml +154 -0
  154. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +1 -1
  155. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +1 -1
  156. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +1 -1
  157. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +1 -1
  158. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +1 -1
  159. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +1 -1
  160. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +1 -1
  161. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +1 -1
  162. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +1 -1
  163. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +1 -1
  164. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +1 -1
  165. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +1 -1
  166. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c1.toml +164 -0
  167. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c2.toml +164 -0
  168. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c3-elevated.toml +164 -0
  169. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c3.toml +164 -0
  170. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c4.toml +164 -0
  171. package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler.toml +164 -0
  172. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c1.toml +150 -0
  173. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c2.toml +150 -0
  174. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c3-elevated.toml +150 -0
  175. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c3.toml +150 -0
  176. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c4.toml +150 -0
  177. package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst.toml +150 -0
  178. package/resources/codex-and-agents-customizations/.codex/config.toml +39 -3
  179. package/resources/codex-and-agents-customizations/.codex/hooks/codex-agent-profile-attestation.ps1 +22 -0
  180. package/resources/codex-and-agents-customizations/.codex/hooks/discovery-capability-probe.ps1 +97 -0
  181. package/resources/codex-and-agents-customizations/.codex/hooks/discovery-start-attestation.ps1 +171 -0
  182. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +4 -1
  183. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-discovery-artifact-gate.ps1 +187 -0
  184. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-discovery-planning-gate.ps1 +149 -0
  185. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-file-size-limit.ps1 +282 -0
  186. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +239 -2
  187. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +6 -15
  188. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +54 -15
  189. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +54 -15
  190. package/resources/codex-and-agents-customizations/.codex/hooks/file-size-policy.ps1 +421 -0
  191. package/resources/codex-and-agents-customizations/.codex/hooks/hook-target-worktree.ps1 +369 -0
  192. package/resources/codex-and-agents-customizations/.codex/hooks/record-subagent-routing-attestation.ps1 +8 -0
  193. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +22 -1
  194. package/resources/codex-and-agents-customizations/.codex/hooks/validate-discovery-agent-output.ps1 +178 -0
  195. package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +11 -1
  196. package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +1 -1
  197. package/resources/codex-and-agents-customizations/AGENTS.md +4 -1
  198. package/resources/codex-and-agents-customizations/pack-manifests/core.json +65 -1
  199. package/resources/config/orchestration-routing.json +61 -1
  200. package/resources/customizations/.github/agents/commentary-remediation.agent.md +1 -1
  201. package/resources/customizations/.github/agents/discovery-capability-sentinel.agent.md +34 -0
  202. package/resources/customizations/.github/agents/legacy-parity-analyst.agent.md +122 -0
  203. package/resources/customizations/.github/agents/migration-coverage-reviewer.agent.md +128 -0
  204. package/resources/customizations/.github/agents/python-execution-only-typed.agent.md +1 -1
  205. package/resources/customizations/.github/agents/python-typed-engineer.agent.md +1 -1
  206. package/resources/customizations/.github/agents/requirements-reconciler.agent.md +138 -0
  207. package/resources/customizations/.github/agents/runtime-characterization-analyst.agent.md +124 -0
  208. package/resources/customizations/.github/hooks/discovery-agent-start.ps1 +139 -0
  209. package/resources/customizations/.github/hooks/discovery-agent-stop.ps1 +123 -0
  210. package/resources/customizations/.github/hooks/discovery-capability-probe.ps1 +98 -0
  211. package/resources/customizations/.github/hooks/discovery-planning.ps1 +91 -0
  212. package/resources/customizations/.github/hooks/discovery-pre-tool.ps1 +123 -0
  213. package/resources/customizations/.github/hooks/discovery.json +35 -0
  214. package/resources/customizations/.github/instructions/general-code-change.instructions.md +5 -2
  215. package/resources/customizations/.github/instructions/mermaid.instructions.md +85 -0
  216. package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +1 -1
  217. package/resources/customizations/.github/prompts/execute-hard-lock.prompt.md +105 -0
  218. package/resources/customizations/.github/prompts/remediate-comments.prompt.md +1 -1
  219. package/resources/customizations/.github/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
  220. package/resources/customizations/.github/skills/characterize-runtime-behavior/SKILL.md +134 -0
  221. package/resources/customizations/.github/skills/discovery-behavior-reconciliation/SKILL.md +46 -0
  222. package/resources/customizations/.github/skills/discovery-coverage-ledger/SKILL.md +50 -0
  223. package/resources/customizations/.github/skills/discovery-parity-matrix/SKILL.md +46 -0
  224. package/resources/customizations/.github/skills/discovery-repo-inventory/SKILL.md +46 -0
  225. package/resources/customizations/.github/skills/discovery-runtime-characterization/SKILL.md +46 -0
  226. package/resources/customizations/.github/skills/discovery-validate-artifacts/SKILL.md +46 -0
  227. package/resources/customizations/.github/skills/discovery-workflow/SKILL.md +62 -0
  228. package/resources/customizations/.github/skills/execution-environment/SKILL.md +25 -10
  229. package/resources/customizations/.github/skills/extract-feature-contract/SKILL.md +133 -0
  230. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +8 -0
  231. package/resources/customizations/.github/skills/generate-acceptance-scenarios/SKILL.md +132 -0
  232. package/resources/customizations/.github/skills/initialize-discovery-workspace/SKILL.md +131 -0
  233. package/resources/customizations/.github/skills/inventory-legacy-system/SKILL.md +131 -0
  234. package/resources/customizations/.github/skills/reconcile-parity-matrix/SKILL.md +131 -0
  235. package/resources/customizations/.github/skills/review-discovery-coverage/SKILL.md +129 -0
  236. package/resources/customizations/.github/skills/validate-discovery-artifacts/SKILL.md +127 -0
  237. package/resources/lib/codex-routing/CodexDeployment.psm1 +5 -1
  238. package/resources/lib/codex-routing/CodexDiscoveryRouting.psm1 +161 -0
  239. package/resources/lib/codex-routing/CodexTopology.psm1 +25 -2
  240. package/resources/lib/discovery-validation/DiscoveryAuthority.psd1 +90 -0
  241. package/resources/lib/discovery-validation/DiscoveryAuthority.psm1 +493 -0
  242. package/resources/lib/discovery-validation/DiscoveryAuthorization.psm1 +479 -0
  243. package/resources/lib/discovery-validation/DiscoveryCapability.psm1 +468 -0
  244. package/resources/lib/discovery-validation/DiscoveryEncoding.psm1 +156 -0
  245. package/resources/lib/discovery-validation/DiscoveryManifest.psm1 +384 -0
  246. package/resources/lib/discovery-validation/DiscoveryObservation.psm1 +217 -0
  247. package/resources/lib/discovery-validation/DiscoveryPayload.psm1 +276 -0
  248. package/resources/lib/discovery-validation/DiscoveryPlanning.psm1 +336 -0
  249. package/resources/lib/discovery-validation/DiscoveryRootGrant.psm1 +164 -0
  250. package/resources/lib/discovery-validation/DiscoveryStart.psm1 +440 -0
  251. package/resources/lib/discovery-validation/DiscoveryStartRecord.psm1 +296 -0
  252. package/resources/lib/discovery-validation/DiscoveryStop.psm1 +336 -0
  253. package/resources/lib/discovery-validation/DiscoveryToolRouting.psm1 +228 -0
  254. package/resources/powershell/PoshQC/PoshQC.Parallel.psm1 +452 -0
  255. package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +106 -8
  256. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +12 -0
  257. package/resources/powershell/PoshQC/PoshQC.psd1 +6 -1
  258. package/resources/powershell/PoshQC/PoshQC.psm1 +7 -1
  259. package/resources/powershell/PoshQC/README.md +20 -1
  260. package/resources/templates/policy_audit/code-review.yyyy-MM-ddTHH-mm.md +4 -4
  261. package/resources/templates/policy_audit/policy-audit.yyyy-MM-ddTHH-mm.md +4 -4
@@ -0,0 +1,493 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Narrow protected-authority primitives shared by every native discovery hook.
4
+
5
+ .DESCRIPTION
6
+ Resolves the fixed external discovery authority location from trusted host
7
+ values and decodes the protected activation record, with the same rules the
8
+ TypeScript gateway (`discovery-authority-paths.ts`, `discovery-authority-record.ts`)
9
+ and the Python runtime (`RegistryLocation.for_host`, `ActivationRecord`) apply:
10
+
11
+ <user-state>/drm-copilot/discovery/workspaces/<sha256(canonical workspace)>
12
+
13
+ Windows hashes the invariant-lowercase native spelling; POSIX preserves case.
14
+ Session and invocation partitions are the digest of their correlation id.
15
+ Hashing and canonical JSON come from the sibling DiscoveryEncoding module so
16
+ hooks reproduce the runtime's digests byte for byte.
17
+
18
+ The module performs no writes, invokes no process and never reads a profile,
19
+ locator or request field to decide a location; every input is a trusted host
20
+ value or an injected seam. A malformed or foreign record is a typed failure,
21
+ never a silent pass.
22
+
23
+ .NOTES
24
+ Issue #980. Compatible with PowerShell 7+. No external module dependencies.
25
+ Mirrored byte-identically under extensions/drm-copilot/resources/claude-customizations/
26
+ and published to the Codex bundle under .codex/lib/discovery-validation/.
27
+ CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
28
+ #>
29
+
30
+ Set-StrictMode -Version Latest
31
+ $ErrorActionPreference = 'Stop'
32
+
33
+ Import-Module (Join-Path $PSScriptRoot 'DiscoveryEncoding.psm1') -ErrorAction Stop
34
+
35
+ $script:AuthorityRootSegments = @('drm-copilot', 'discovery')
36
+ $script:ActivationRecordFile = 'activation.json'
37
+ $script:ActivationSchemaVersion = '2.0.0'
38
+ $script:ActivationRecordKeys = @(
39
+ 'action', 'generation', 'locator_sha256', 'previous_sha256',
40
+ 'profile_path', 'profile_sha256', 'schema_version', 'workspace_sha256'
41
+ )
42
+ $script:CorrelationIdPattern = '^[A-Za-z0-9][A-Za-z0-9._:-]*$'
43
+ $script:Sha256Pattern = '^[0-9a-f]{64}$'
44
+ $script:RelativePathPattern = '^(?!/)(?!.*(?:^|/)\.{1,2}(?:/|$))[^/\\:\u2028\u2029*?<>|]+(?:/[^/\\:\u2028\u2029*?<>|]+)*$'
45
+ $script:ReservedDevicePattern = '^(?:CON|PRN|AUX|NUL|CONIN\$|CONOUT\$|CLOCK\$|(?:COM|LPT)[1-9\u00b9\u00b2\u00b3])$'
46
+ $script:MaxRecordBytes = 65536
47
+
48
+ function ConvertTo-DiscoveryCanonicalPath {
49
+ <#
50
+ .SYNOPSIS
51
+ Splits a trusted absolute path into anchor and segments the way pathlib does.
52
+ .DESCRIPTION
53
+ Separators collapse, `.` segments vanish, trailing separators are dropped and
54
+ `..` is rejected because an unresolved alias cannot identify a workspace.
55
+ #>
56
+ [CmdletBinding()]
57
+ [OutputType([hashtable])]
58
+ param(
59
+ [Parameter(Mandatory = $true)][string]$Path,
60
+ [Parameter(Mandatory = $true)][ValidateSet('windows', 'macos', 'linux')][string]$Platform,
61
+ [Parameter(Mandatory = $true)][string]$Label
62
+ )
63
+
64
+ $windowsPlatform = $Platform -eq 'windows'
65
+ $driveRooted = $windowsPlatform -and ($Path -match '^[A-Za-z]:[\\/]')
66
+ $uncRooted = $windowsPlatform -and ($Path -match '^[\\/]{2}[^\\/]+[\\/][^\\/]+')
67
+ if (-not ($driveRooted -or $uncRooted -or ((-not $windowsPlatform) -and $Path.StartsWith('/')))) {
68
+ throw "AUTHORITY_PATH_NOT_CANONICAL: $Label must be an absolute path: $Path"
69
+ }
70
+ $unified = $(if ($windowsPlatform) { $Path.Replace('\', '/') } else { $Path })
71
+ # The anchor is the drive, the UNC share or the POSIX root (two leading
72
+ # slashes stay a distinct root, as pathlib treats them).
73
+ if ($driveRooted) {
74
+ $anchor = $unified.Substring(0, 2) + '/'
75
+ $rest = $unified.Substring(3)
76
+ } elseif ($uncRooted) {
77
+ $match = [regex]::Match($unified, '^//([^/]+)/([^/]+)/?')
78
+ $anchor = '//' + $match.Groups[1].Value + '/' + $match.Groups[2].Value + '/'
79
+ $rest = $unified.Substring($match.Length)
80
+ } else {
81
+ $anchor = $(if ($unified -match '^//(?!/)') { '//' } else { '/' })
82
+ $rest = $unified.TrimStart('/')
83
+ }
84
+ $parts = [System.Collections.Generic.List[string]]::new()
85
+ # Drop empty and current-directory segments; reject parent references.
86
+ foreach ($segment in $rest.Split('/')) {
87
+ if ($segment -eq '' -or $segment -eq '.') { continue }
88
+ if ($segment -eq '..') {
89
+ throw "AUTHORITY_PATH_NOT_CANONICAL: $Label must not contain parent references: $Path"
90
+ }
91
+ $parts.Add($segment)
92
+ }
93
+ return @{ Platform = $Platform; Anchor = $anchor; Parts = $parts.ToArray() }
94
+ }
95
+
96
+ function ConvertTo-DiscoveryPosixPath {
97
+ <#
98
+ .SYNOPSIS
99
+ Renders a canonical path in POSIX spelling (what PurePath.as_posix prints).
100
+ #>
101
+ [CmdletBinding()]
102
+ [OutputType([string])]
103
+ param([Parameter(Mandatory = $true)][hashtable]$CanonicalPath)
104
+
105
+ $joined = ($CanonicalPath.Parts -join '/')
106
+ if ($joined -eq '') { return $CanonicalPath.Anchor }
107
+ return $CanonicalPath.Anchor + $joined
108
+ }
109
+
110
+ function Test-DiscoveryCanonicalPathWithin {
111
+ <#
112
+ .SYNOPSIS
113
+ True when Candidate equals or descends from Ancestor (case-insensitive on Windows).
114
+ #>
115
+ [CmdletBinding()]
116
+ [OutputType([bool])]
117
+ param(
118
+ [Parameter(Mandatory = $true)][hashtable]$Candidate,
119
+ [Parameter(Mandatory = $true)][hashtable]$Ancestor
120
+ )
121
+
122
+ $fold = $Candidate.Platform -eq 'windows'
123
+ $comparison = $(if ($fold) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal })
124
+ if (-not [string]::Equals($Candidate.Anchor, $Ancestor.Anchor, $comparison)) { return $false }
125
+ if ($Candidate.Parts.Count -lt $Ancestor.Parts.Count) { return $false }
126
+ # Every ancestor segment must match the candidate prefix segment-wise.
127
+ for ($index = 0; $index -lt $Ancestor.Parts.Count; $index++) {
128
+ if (-not [string]::Equals($Candidate.Parts[$index], $Ancestor.Parts[$index], $comparison)) { return $false }
129
+ }
130
+ return $true
131
+ }
132
+
133
+ function Resolve-DiscoveryAuthorityLocation {
134
+ <#
135
+ .SYNOPSIS
136
+ Resolves the protected authority location for a workspace from trusted host values.
137
+ .DESCRIPTION
138
+ Mirrors `RegistryLocation.for_host`: the workspace, home and state roots must
139
+ be absolute without parent references, the state root is fixed platform
140
+ policy (LocalApplicationData, ~/Library/Application Support, or
141
+ $XDG_STATE_HOME / ~/.local/state), and the authority root must lie outside
142
+ the workspace. No directory is created or read.
143
+ .OUTPUTS
144
+ [pscustomobject] Platform, Workspace, WorkspaceSha256, Root, Directory (POSIX spelling).
145
+ #>
146
+ [CmdletBinding()]
147
+ [OutputType([pscustomobject])]
148
+ param(
149
+ [Parameter(Mandatory = $true)][string]$WorkspaceRoot,
150
+ [Parameter(Mandatory = $true)][ValidateSet('windows', 'macos', 'linux')][string]$Platform,
151
+ [Parameter(Mandatory = $true)][string]$UserHome,
152
+ [Parameter(Mandatory = $false)][AllowNull()][AllowEmptyString()][string]$LocalAppData,
153
+ [Parameter(Mandatory = $false)][AllowNull()][AllowEmptyString()][string]$XdgStateHome
154
+ )
155
+
156
+ $workspace = ConvertTo-DiscoveryCanonicalPath -Path $WorkspaceRoot -Platform $Platform -Label 'Workspace root'
157
+ $userHomePath = ConvertTo-DiscoveryCanonicalPath -Path $UserHome -Platform $Platform -Label 'User home'
158
+ # Platform state locations are fixed policy; profiles cannot redirect them.
159
+ switch ($Platform) {
160
+ 'windows' {
161
+ if ([string]::IsNullOrEmpty($LocalAppData)) {
162
+ throw 'AUTHORITY_STATE_ROOT_MISSING: Trusted LocalApplicationData is required on Windows.'
163
+ }
164
+ $base = ConvertTo-DiscoveryCanonicalPath -Path $LocalAppData -Platform $Platform -Label 'LocalApplicationData'
165
+ }
166
+ 'macos' {
167
+ $base = @{ Platform = $Platform; Anchor = $userHomePath.Anchor; Parts = @($userHomePath.Parts + @('Library', 'Application Support')) }
168
+ }
169
+ default {
170
+ if (-not [string]::IsNullOrEmpty($XdgStateHome)) {
171
+ $base = ConvertTo-DiscoveryCanonicalPath -Path $XdgStateHome -Platform $Platform -Label 'XDG_STATE_HOME'
172
+ } else {
173
+ $base = @{ Platform = $Platform; Anchor = $userHomePath.Anchor; Parts = @($userHomePath.Parts + @('.local', 'state')) }
174
+ }
175
+ }
176
+ }
177
+ $root = @{ Platform = $Platform; Anchor = $base.Anchor; Parts = @($base.Parts + $script:AuthorityRootSegments) }
178
+ if (Test-DiscoveryCanonicalPathWithin -Candidate $root -Ancestor $workspace) {
179
+ throw 'AUTHORITY_INSIDE_WORKSPACE: Activation authority must remain outside the workspace.'
180
+ }
181
+ $posixWorkspace = ConvertTo-DiscoveryPosixPath -CanonicalPath $workspace
182
+ # Windows identity uses native separators and invariant lowercase, matching
183
+ # the Python runtime and the TypeScript gateway; POSIX preserves case.
184
+ $identity = $(if ($Platform -eq 'windows') { $posixWorkspace.Replace('/', '\').ToLowerInvariant() } else { $posixWorkspace })
185
+ $digest = Get-DiscoverySha256Hex -Text $identity
186
+ $directory = @{ Platform = $Platform; Anchor = $root.Anchor; Parts = @($root.Parts + @('workspaces', $digest)) }
187
+ return [pscustomobject]@{
188
+ Platform = $Platform
189
+ Workspace = $posixWorkspace
190
+ WorkspaceSha256 = $digest
191
+ Root = ConvertTo-DiscoveryPosixPath -CanonicalPath $root
192
+ Directory = ConvertTo-DiscoveryPosixPath -CanonicalPath $directory
193
+ }
194
+ }
195
+
196
+ function Get-DiscoveryTrustedHostEnvironment {
197
+ <#
198
+ .SYNOPSIS
199
+ Returns the trusted host environment (platform, home, state roots) with injectable seams.
200
+ .DESCRIPTION
201
+ Defaults read the running host, never a profile or request value. Tests pass
202
+ every value explicitly so no assertion depends on the machine.
203
+ #>
204
+ [CmdletBinding()]
205
+ [OutputType([hashtable])]
206
+ param(
207
+ [Parameter(Mandatory = $false)][AllowNull()][string]$Platform,
208
+ [Parameter(Mandatory = $false)][AllowNull()][string]$UserHome,
209
+ [Parameter(Mandatory = $false)][AllowNull()][string]$LocalAppData,
210
+ [Parameter(Mandatory = $false)][AllowNull()][string]$XdgStateHome
211
+ )
212
+
213
+ if ([string]::IsNullOrEmpty($Platform)) {
214
+ $Platform = $(if ($IsWindows) { 'windows' } elseif ($IsMacOS) { 'macos' } else { 'linux' })
215
+ }
216
+ if ([string]::IsNullOrEmpty($UserHome)) { $UserHome = [System.Environment]::GetFolderPath('UserProfile') }
217
+ # An omitted [string] parameter arrives as an empty string, never as $null, so the
218
+ # host defaults apply on the empty value as well.
219
+ if ([string]::IsNullOrEmpty($LocalAppData)) { $LocalAppData = [System.Environment]::GetFolderPath('LocalApplicationData') }
220
+ if ([string]::IsNullOrEmpty($XdgStateHome)) { $XdgStateHome = [string]$env:XDG_STATE_HOME }
221
+ return @{ Platform = $Platform; UserHome = $UserHome; LocalAppData = $LocalAppData; XdgStateHome = $XdgStateHome }
222
+ }
223
+
224
+ function Get-DiscoveryPartitionName {
225
+ <#
226
+ .SYNOPSIS
227
+ Returns the digest partition name for a correlation identifier.
228
+ .DESCRIPTION
229
+ Correlation identifiers may contain `:` and are case-sensitive, so the
230
+ partition is the SHA-256 of the identifier rather than the identifier itself.
231
+ #>
232
+ [CmdletBinding()]
233
+ [OutputType([string])]
234
+ param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$CorrelationId)
235
+
236
+ if ($CorrelationId -cnotmatch $script:CorrelationIdPattern -or $CorrelationId.Length -gt 256) {
237
+ throw "AUTHORITY_PARTITION_INVALID: Partition identifier is not a valid correlation id: $CorrelationId"
238
+ }
239
+ return Get-DiscoverySha256Hex -Text $CorrelationId
240
+ }
241
+
242
+ function Get-DiscoverySessionDirectory {
243
+ <#
244
+ .SYNOPSIS
245
+ Returns `<directory>/sessions/<sha256(sessionId)>`.
246
+ #>
247
+ [CmdletBinding()]
248
+ [OutputType([string])]
249
+ param(
250
+ [Parameter(Mandatory = $true)][pscustomobject]$Location,
251
+ [Parameter(Mandatory = $true)][string]$SessionId
252
+ )
253
+
254
+ return "$($Location.Directory)/sessions/$(Get-DiscoveryPartitionName -CorrelationId $SessionId)"
255
+ }
256
+
257
+ function Get-DiscoveryInvocationDirectory {
258
+ <#
259
+ .SYNOPSIS
260
+ Returns `<session>/invocations/<sha256(invocationId)>`.
261
+ #>
262
+ [CmdletBinding()]
263
+ [OutputType([string])]
264
+ param(
265
+ [Parameter(Mandatory = $true)][pscustomobject]$Location,
266
+ [Parameter(Mandatory = $true)][string]$SessionId,
267
+ [Parameter(Mandatory = $true)][string]$InvocationId
268
+ )
269
+
270
+ $session = Get-DiscoverySessionDirectory -Location $Location -SessionId $SessionId
271
+ return "$session/invocations/$(Get-DiscoveryPartitionName -CorrelationId $InvocationId)"
272
+ }
273
+
274
+ function Get-DiscoveryActivationRecordPath {
275
+ <#
276
+ .SYNOPSIS
277
+ Returns the path of the fixed activation record for a location.
278
+ #>
279
+ [CmdletBinding()]
280
+ [OutputType([string])]
281
+ param([Parameter(Mandatory = $true)][pscustomobject]$Location)
282
+
283
+ return "$($Location.Directory)/$($script:ActivationRecordFile)"
284
+ }
285
+
286
+ function Test-DiscoveryProtectedPath {
287
+ <#
288
+ .SYNOPSIS
289
+ True only when Candidate lies inside the protected directory and outside the workspace.
290
+ .DESCRIPTION
291
+ Analyst-authored reports, invocation mirrors and workspace copies of the
292
+ registry are editable; a path anywhere other than the protected directory
293
+ can never be read as native authority.
294
+ #>
295
+ [CmdletBinding()]
296
+ [OutputType([bool])]
297
+ param(
298
+ [Parameter(Mandatory = $true)][pscustomobject]$Location,
299
+ [Parameter(Mandatory = $true)][string]$Candidate
300
+ )
301
+
302
+ $target = ConvertTo-DiscoveryCanonicalPath -Path $Candidate -Platform $Location.Platform -Label 'Candidate path'
303
+ $workspace = ConvertTo-DiscoveryCanonicalPath -Path $Location.Workspace -Platform $Location.Platform -Label 'Workspace root'
304
+ $directory = ConvertTo-DiscoveryCanonicalPath -Path $Location.Directory -Platform $Location.Platform -Label 'Authority directory'
305
+ if (Test-DiscoveryCanonicalPathWithin -Candidate $target -Ancestor $workspace) { return $false }
306
+ return Test-DiscoveryCanonicalPathWithin -Candidate $target -Ancestor $directory
307
+ }
308
+
309
+ function Test-DiscoveryRelativePath {
310
+ <#
311
+ .SYNOPSIS
312
+ True when Path is a normalized workspace-relative path (no root, no `.`/`..`, no reserved device).
313
+ #>
314
+ [CmdletBinding()]
315
+ [OutputType([bool])]
316
+ param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Path)
317
+
318
+ if ($Path -cnotmatch $script:RelativePathPattern) { return $false }
319
+ # Every segment, with and without its extension, must avoid Windows device names.
320
+ foreach ($segment in $Path.Split('/')) {
321
+ $stem = $segment.Split('.')[0]
322
+ if ($segment -cmatch $script:ReservedDevicePattern -or $stem -cmatch $script:ReservedDevicePattern) { return $false }
323
+ }
324
+ return $true
325
+ }
326
+
327
+ function Test-DiscoveryDigestText {
328
+ <#
329
+ .SYNOPSIS
330
+ True when Value is a lowercase 64-hex SHA-256 string.
331
+ #>
332
+ [CmdletBinding()]
333
+ [OutputType([bool])]
334
+ param([Parameter(Mandatory = $false)][AllowNull()][object]$Value)
335
+
336
+ return ($Value -is [string]) -and ($Value -cmatch $script:Sha256Pattern)
337
+ }
338
+
339
+ function ConvertFrom-DiscoveryActivationRecord {
340
+ <#
341
+ .SYNOPSIS
342
+ Decodes protected activation text, rejecting malformed data and foreign workspaces.
343
+ .DESCRIPTION
344
+ Field rules mirror `ActivationRecord.__post_init__` and the TypeScript
345
+ decoder: exactly the eight keys, schema version 2.0.0, action activate or
346
+ deactivate, a positive integer generation whose textual literal is a plain
347
+ integer, lowercase digests, a workspace-relative profile path, and
348
+ all-or-nothing profile/locator bindings. A workspace digest that differs from
349
+ WorkspaceSha256 is `ACTIVATION_WORKSPACE`; every other defect is
350
+ `ACTIVATION_INVALID`.
351
+ .OUTPUTS
352
+ [pscustomobject] The validated record with the eight snake_case members.
353
+ #>
354
+ [CmdletBinding()]
355
+ [OutputType([pscustomobject])]
356
+ param(
357
+ [Parameter(Mandatory = $true)][AllowEmptyString()][string]$Text,
358
+ [Parameter(Mandatory = $true)][string]$WorkspaceSha256
359
+ )
360
+
361
+ if ([System.Text.Encoding]::UTF8.GetByteCount($Text) -gt $script:MaxRecordBytes) {
362
+ throw 'ACTIVATION_INVALID: Protected activation record exceeds the size limit.'
363
+ }
364
+ try {
365
+ $parsed = ConvertFrom-Json -InputObject $Text -Depth 4 -NoEnumerate
366
+ } catch {
367
+ throw 'ACTIVATION_INVALID: Protected activation record is not JSON.'
368
+ }
369
+ if ($null -eq $parsed -or $parsed -isnot [System.Management.Automation.PSCustomObject]) {
370
+ throw 'ACTIVATION_INVALID: Protected activation record is malformed.'
371
+ }
372
+ [string[]]$keys = @($parsed.PSObject.Properties.Name)
373
+ [Array]::Sort($keys, [System.StringComparer]::Ordinal)
374
+ if (($keys -join ',') -ne ($script:ActivationRecordKeys -join ',')) {
375
+ throw 'ACTIVATION_INVALID: Protected activation record is malformed.'
376
+ }
377
+ $generation = $parsed.generation
378
+ # ConvertFrom-Json turns `1.0` into a double, so the textual literal is checked
379
+ # as well to keep parity with Python's exact-int rule.
380
+ $integerLiteral = [regex]::IsMatch($Text, '"generation"\s*:\s*[1-9][0-9]*\s*[,}]')
381
+ if ($parsed.schema_version -ne $script:ActivationSchemaVersion -or
382
+ ($parsed.action -ne 'activate' -and $parsed.action -ne 'deactivate') -or
383
+ -not ($generation -is [int32] -or $generation -is [int64]) -or $generation -lt 1 -or -not $integerLiteral) {
384
+ throw 'ACTIVATION_INVALID: Invalid activation version, action or generation.'
385
+ }
386
+ $digests = @($parsed.workspace_sha256, $parsed.profile_sha256, $parsed.locator_sha256, $parsed.previous_sha256)
387
+ foreach ($digest in $digests) {
388
+ if ($null -ne $digest -and -not (Test-DiscoveryDigestText -Value $digest)) {
389
+ throw 'ACTIVATION_INVALID: Activation digests must be lowercase SHA-256.'
390
+ }
391
+ }
392
+ if (-not (Test-DiscoveryDigestText -Value $parsed.workspace_sha256)) {
393
+ throw 'ACTIVATION_INVALID: Activation requires a canonical workspace digest.'
394
+ }
395
+ if ($null -ne $parsed.profile_path) {
396
+ if ($parsed.profile_path -isnot [string]) {
397
+ throw 'ACTIVATION_INVALID: Activation profile path must be a string or null.'
398
+ }
399
+ if (-not (Test-DiscoveryRelativePath -Path $parsed.profile_path)) {
400
+ throw 'ACTIVATION_INVALID: Activation profile path is not workspace-relative.'
401
+ }
402
+ }
403
+ # Bindings are all-or-nothing, and activation always carries them.
404
+ $linked = @($parsed.profile_path, $parsed.profile_sha256, $parsed.locator_sha256)
405
+ $incomplete = @($linked | Where-Object { $null -eq $_ }).Count -gt 0
406
+ $anyBound = @($linked | Where-Object { $null -ne $_ }).Count -gt 0
407
+ if ($incomplete -and ($parsed.action -eq 'activate' -or $anyBound)) {
408
+ throw 'ACTIVATION_INVALID: Activation profile and locator bindings must be complete.'
409
+ }
410
+ if ($parsed.workspace_sha256 -ne $WorkspaceSha256) {
411
+ throw 'ACTIVATION_WORKSPACE: Protected activation belongs to another workspace.'
412
+ }
413
+ return [pscustomobject]@{
414
+ action = [string]$parsed.action
415
+ generation = [int64]$generation
416
+ locator_sha256 = $parsed.locator_sha256
417
+ previous_sha256 = $parsed.previous_sha256
418
+ profile_path = $parsed.profile_path
419
+ profile_sha256 = $parsed.profile_sha256
420
+ schema_version = $script:ActivationSchemaVersion
421
+ workspace_sha256 = [string]$parsed.workspace_sha256
422
+ }
423
+ }
424
+
425
+ function Read-DiscoveryActivationState {
426
+ <#
427
+ .SYNOPSIS
428
+ Reads the protected activation record and classifies the workspace state.
429
+ .DESCRIPTION
430
+ Status is `missing` when no record exists, `invalid` when the text fails the
431
+ decoder (Reason carries the typed message), `inactive` when the record is a
432
+ deactivate tombstone, and `active` otherwise. The read goes through the
433
+ ReadText seam, which returns the file text or $null; the default reads the
434
+ path with UTF-8 and returns $null for a missing file.
435
+ .OUTPUTS
436
+ [pscustomobject] Status, Record, Reason, Path.
437
+ #>
438
+ [CmdletBinding()]
439
+ [OutputType([pscustomobject])]
440
+ param(
441
+ [Parameter(Mandatory = $true)][pscustomobject]$Location,
442
+ [Parameter(Mandatory = $false)][scriptblock]$ReadText = {
443
+ param([string]$Path)
444
+ if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return $null }
445
+ return [System.IO.File]::ReadAllText($Path, [System.Text.Encoding]::UTF8)
446
+ }
447
+ )
448
+
449
+ $path = Get-DiscoveryActivationRecordPath -Location $Location
450
+ $text = & $ReadText $path
451
+ if ($null -eq $text) {
452
+ return [pscustomobject]@{ Status = 'missing'; Record = $null; Reason = 'no protected activation record exists'; Path = $path }
453
+ }
454
+ try {
455
+ $record = ConvertFrom-DiscoveryActivationRecord -Text ([string]$text) -WorkspaceSha256 $Location.WorkspaceSha256
456
+ } catch {
457
+ return [pscustomobject]@{ Status = 'invalid'; Record = $null; Reason = $_.Exception.Message; Path = $path }
458
+ }
459
+ $status = $(if ($record.action -eq 'deactivate') { 'inactive' } else { 'active' })
460
+ return [pscustomobject]@{ Status = $status; Record = $record; Reason = $null; Path = $path }
461
+ }
462
+
463
+ function Invoke-DiscoveryStoreSeam {
464
+ <#
465
+ .SYNOPSIS
466
+ Invokes one injected store seam (ReadText, ListEntries, WriteText, ReplaceText, EnsureDirectory) with its arguments.
467
+ .DESCRIPTION
468
+ Hooks hand their store as a hashtable of script blocks; this is the single
469
+ place that invokes one, so every store call is a typed script-block seam and
470
+ never a dynamic command position.
471
+ #>
472
+ [CmdletBinding()]
473
+ [OutputType([object])]
474
+ param(
475
+ [Parameter(Mandatory = $true)][scriptblock]$Seam,
476
+ [Parameter(Mandatory = $false)][AllowEmptyCollection()][object[]]$Arguments = @()
477
+ )
478
+
479
+ return & $Seam @Arguments
480
+ }
481
+
482
+ Export-ModuleMember -Function `
483
+ Invoke-DiscoveryStoreSeam, `
484
+ Resolve-DiscoveryAuthorityLocation, `
485
+ Get-DiscoveryTrustedHostEnvironment, `
486
+ Get-DiscoveryPartitionName, `
487
+ Get-DiscoverySessionDirectory, `
488
+ Get-DiscoveryInvocationDirectory, `
489
+ Get-DiscoveryActivationRecordPath, `
490
+ Test-DiscoveryProtectedPath, `
491
+ Test-DiscoveryRelativePath, `
492
+ ConvertFrom-DiscoveryActivationRecord, `
493
+ Read-DiscoveryActivationState