@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
@@ -8,14 +8,26 @@
8
8
  operation. When the target file is a PowerShell source file (.ps1, .psm1, .psd1),
9
9
  it decides whether the change may proceed in direct mode.
10
10
 
11
- Direct mode counts distinct production PowerShell paths per Codex session. The
12
- running set is persisted under .codex/state/powershell-batch-budget.<session_id>.json,
13
- and repeated edits to the same file are counted once. The 4th distinct production
14
- path is denied with a POWERSHELL_LARGE_PATH_REQUIRED reason that instructs the
15
- caller to route the change through .codex/prompts/orchestrate-work.md.
16
-
17
- The orchestrated large path is detected from
18
- <root>/artifacts/orchestration/orchestrator-state.json. The selected route is the
11
+ Direct mode counts distinct production PowerShell paths per Codex session and per
12
+ resolved target worktree, and repeated edits to the same file are counted once. The
13
+ 4th distinct production path in one worktree is denied with a
14
+ POWERSHELL_LARGE_PATH_REQUIRED reason that instructs the caller to route the change
15
+ through .codex/prompts/orchestrate-work.md.
16
+
17
+ Each evaluated path is placed with Resolve-HookTargetWorktree from the dot-sourced
18
+ hook-target-worktree.ps1. An absolute path resolves to its nearest enclosing
19
+ registered git worktree; a relative path, a path inside no registered worktree, and
20
+ an ambiguous result use the session root. The route is read from
21
+ artifacts/orchestration/orchestrator-state.json in the resolved target worktree.
22
+ The running set is kept under the session root, in
23
+ .codex/state/powershell-batch-budget.<session_id>.json when the resolved target worktree
24
+ is the session root and in .codex/state/powershell-batch-budget.<session_id>.<worktree key>.json
25
+ otherwise; no state is written under another worktree. A path under the root-level
26
+ artifacts/ directory of the resolved target worktree is never counted and writes no
27
+ state. No containment filter applies: a path outside every registered worktree is
28
+ counted against the session root, as before.
29
+
30
+ The selected route is the
19
31
  route_id value when that key is present, otherwise the path_selected value, and it
20
32
  is usable only as a non-blank string. When the selected route is large,
21
33
  remediation, or preparation and the checkpoint is not terminal (next_step is not
@@ -36,9 +48,12 @@
36
48
  hookSpecificOutput.permissionDecision = 'deny' and exits 0. Files already counted
37
49
  are always allowed through.
38
50
 
39
- Known limitation: a stale non-terminal large-path checkpoint left at the root
40
- exempts a later direct-mode session at that root. Orchestrator checkpoint hygiene
41
- (issue #673) moves foreign checkpoints aside before a new run.
51
+ Known limitations: an 8.3 short-name path, or a path reached through a junction,
52
+ that differs textually from the long path git reports matches no registered
53
+ worktree and falls back to the session root. A stale non-terminal large-path
54
+ checkpoint in a worktree exempts a later direct-mode session editing that worktree;
55
+ orchestrator checkpoint hygiene (issue #673) moves foreign checkpoints aside before
56
+ a new run.
42
57
 
43
58
  .NOTES
44
59
  Compatible with PowerShell 7+.
@@ -51,6 +66,9 @@ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot
51
66
  # mapping for every tool name the ^(apply_patch|Edit|Write)$ matcher admits.
52
67
  try { . (Join-Path $PSScriptRoot 'codex-pretooluse-file-mapping.ps1') } catch { Add-HookDependencyFailure -Name 'codex-pretooluse-file-mapping.ps1' -ErrorRecord $_ }
53
68
 
69
+ # Shared target-worktree resolver; the Codex twin of HookTargetWorktree.psm1.
70
+ try { . (Join-Path $PSScriptRoot 'hook-target-worktree.ps1') } catch { Add-HookDependencyFailure -Name 'hook-target-worktree.ps1' -ErrorRecord $_ }
71
+
54
72
  # Shared route helpers; this file is byte-identical to the Claude runtime copy.
55
73
  $script:PowerShellBatchBudgetRouteImportFailure = $null; try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { $script:PowerShellBatchBudgetRouteImportFailure = 'enforce-batch-budget-route.ps1' }
56
74
 
@@ -194,7 +212,8 @@ function Invoke-PowerShellBatchBudgetHook {
194
212
  [scriptblock] $WriteState = {
195
213
  param([string] $Path, [System.Collections.IDictionary] $State)
196
214
  $State | ConvertTo-Json -Depth 5 | Set-Content -Path $Path -Encoding UTF8
197
- }
215
+ },
216
+ [scriptblock] $WorktreeListInvoker
198
217
  )
199
218
 
200
219
  if (-not $ToolInputRaw) {
@@ -218,12 +237,31 @@ function Invoke-PowerShellBatchBudgetHook {
218
237
  }
219
238
  if ($script:PowerShellBatchBudgetRouteImportFailure) { return Get-PowerShellBatchBudgetBlockDecision -Reason "POWERSHELL_LARGE_PATH_REQUIRED: the route helper '$($script:PowerShellBatchBudgetRouteImportFailure)' failed to load, so the batch budget for '$normalized' cannot be evaluated; the gate fails closed." }
220
239
 
240
+ # The path is evaluated against the registered worktree that holds it; the session root
241
+ # is the fallback for a relative path, a path in no registered worktree, and an ambiguous result.
242
+ $resolverArguments = @{ SessionRoot = $Root; FilePath = @($filePath) }
243
+ if ($PSBoundParameters.ContainsKey('WorktreeListInvoker')) { $resolverArguments['WorktreeListInvoker'] = $WorktreeListInvoker }
244
+ $resolution = Resolve-HookTargetWorktree @resolverArguments
245
+ $targetRoot = $Root
246
+ $isSessionRoot = $true
247
+ if (-not $resolution.Ambiguous -and $null -ne $resolution.WorktreeRoot -and -not [string]::Equals($resolution.WorktreeRoot, (ConvertTo-HookTargetWorktreeNormalizedPath -Path $Root), [System.StringComparison]::OrdinalIgnoreCase)) {
248
+ $targetRoot = $resolution.WorktreeRoot
249
+ $isSessionRoot = $false
250
+ }
251
+
252
+ # The root-level artifacts/ directory is gitignored scratch, never a production file.
253
+ $relativePath = if ($normalized -match '^(/|[A-Za-z]:/)') { Get-HookTargetWorktreeRelativePath -Path $normalized -WorktreeRoot $targetRoot } else { $normalized }
254
+ if ($null -ne $relativePath -and $relativePath.StartsWith('artifacts/', [System.StringComparison]::OrdinalIgnoreCase)) {
255
+ return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
256
+ }
257
+
221
258
  $stateDir = Join-Path -Path $Root -ChildPath '.codex/state'
222
- $stateFile = Join-Path -Path $stateDir -ChildPath ("powershell-batch-budget.$SessionId.json")
259
+ $stateLeaf = if ($isSessionRoot) { "powershell-batch-budget.$SessionId.json" } else { "powershell-batch-budget.$SessionId.$(Get-HookTargetWorktreeKey -WorktreeRoot $targetRoot).json" }
260
+ $stateFile = Join-Path -Path $stateDir -ChildPath $stateLeaf
223
261
 
224
262
  # The checkpoint is read before any state operation, so the large path neither
225
263
  # creates the state directory nor reads or writes the state file.
226
- $checkpointPath = Join-Path -Path $Root -ChildPath 'artifacts/orchestration/orchestrator-state.json'
264
+ $checkpointPath = Join-Path -Path $targetRoot -ChildPath 'artifacts/orchestration/orchestrator-state.json'
227
265
  $checkpointText = ''
228
266
  try {
229
267
  $checkpointText = [string](& $ReadCheckpoint $checkpointPath)
@@ -279,7 +317,8 @@ function Invoke-PowerShellBatchBudgetCodexEntryPoint {
279
317
  .PARAMETER PayloadRaw
280
318
  Raw PreToolUse payload text.
281
319
  .PARAMETER RepositoryRoot
282
- Root used to locate the Codex state directory and the orchestrator checkpoint.
320
+ Session root: it locates the Codex state directory and is the fallback root. The
321
+ orchestrator checkpoint comes from the resolved target worktree of each path.
283
322
  .PARAMETER HookSeams
284
323
  Optional seam overrides splatted into Invoke-PowerShellBatchBudgetHook.
285
324
  #>
@@ -8,14 +8,26 @@
8
8
  operation. When the target file is a Python file (.py), it decides whether the
9
9
  change may proceed in direct mode.
10
10
 
11
- Direct mode counts distinct production Python paths per Codex session. The running
12
- set is persisted under .codex/state/python-batch-budget.<session_id>.json, and
13
- repeated edits to the same file are counted once. The 4th distinct production path
14
- is denied with a PYTHON_LARGE_PATH_REQUIRED reason that instructs the caller to
15
- route the change through .codex/prompts/orchestrate-work.md.
16
-
17
- The orchestrated large path is detected from
18
- <root>/artifacts/orchestration/orchestrator-state.json. The selected route is the
11
+ Direct mode counts distinct production Python paths per Codex session and per
12
+ resolved target worktree, and repeated edits to the same file are counted once. The
13
+ 4th distinct production path in one worktree is denied with a
14
+ PYTHON_LARGE_PATH_REQUIRED reason that instructs the caller to route the change
15
+ through .codex/prompts/orchestrate-work.md.
16
+
17
+ Each evaluated path is placed with Resolve-HookTargetWorktree from the dot-sourced
18
+ hook-target-worktree.ps1. An absolute path resolves to its nearest enclosing
19
+ registered git worktree; a relative path, a path inside no registered worktree, and
20
+ an ambiguous result use the session root. The route is read from
21
+ artifacts/orchestration/orchestrator-state.json in the resolved target worktree.
22
+ The running set is kept under the session root, in
23
+ .codex/state/python-batch-budget.<session_id>.json when the resolved target worktree
24
+ is the session root and in .codex/state/python-batch-budget.<session_id>.<worktree key>.json
25
+ otherwise; no state is written under another worktree. A path under the root-level
26
+ artifacts/ directory of the resolved target worktree is never counted and writes no
27
+ state. No containment filter applies: a path outside every registered worktree is
28
+ counted against the session root, as before.
29
+
30
+ The selected route is the
19
31
  route_id value when that key is present, otherwise the path_selected value, and it
20
32
  is usable only as a non-blank string. When the selected route is large,
21
33
  remediation, or preparation and the checkpoint is not terminal (next_step is not
@@ -36,9 +48,12 @@
36
48
  hookSpecificOutput.permissionDecision = 'deny' and exits 0. Files already counted
37
49
  are always allowed through.
38
50
 
39
- Known limitation: a stale non-terminal large-path checkpoint left at the root
40
- exempts a later direct-mode session at that root. Orchestrator checkpoint hygiene
41
- (issue #673) moves foreign checkpoints aside before a new run.
51
+ Known limitations: an 8.3 short-name path, or a path reached through a junction,
52
+ that differs textually from the long path git reports matches no registered
53
+ worktree and falls back to the session root. A stale non-terminal large-path
54
+ checkpoint in a worktree exempts a later direct-mode session editing that worktree;
55
+ orchestrator checkpoint hygiene (issue #673) moves foreign checkpoints aside before
56
+ a new run.
42
57
 
43
58
  .NOTES
44
59
  Compatible with PowerShell 7+.
@@ -51,6 +66,9 @@ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot
51
66
  # mapping for every tool name the ^(apply_patch|Edit|Write)$ matcher admits.
52
67
  try { . (Join-Path $PSScriptRoot 'codex-pretooluse-file-mapping.ps1') } catch { Add-HookDependencyFailure -Name 'codex-pretooluse-file-mapping.ps1' -ErrorRecord $_ }
53
68
 
69
+ # Shared target-worktree resolver; the Codex twin of HookTargetWorktree.psm1.
70
+ try { . (Join-Path $PSScriptRoot 'hook-target-worktree.ps1') } catch { Add-HookDependencyFailure -Name 'hook-target-worktree.ps1' -ErrorRecord $_ }
71
+
54
72
  # Shared route helpers; this file is byte-identical to the Claude runtime copy.
55
73
  $script:PythonBatchBudgetRouteImportFailure = $null; try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { $script:PythonBatchBudgetRouteImportFailure = 'enforce-batch-budget-route.ps1' }
56
74
 
@@ -197,7 +215,8 @@ function Invoke-PythonBatchBudgetHook {
197
215
  [scriptblock] $WriteState = {
198
216
  param([string] $Path, [System.Collections.IDictionary] $State)
199
217
  $State | ConvertTo-Json -Depth 5 | Set-Content -Path $Path -Encoding UTF8
200
- }
218
+ },
219
+ [scriptblock] $WorktreeListInvoker
201
220
  )
202
221
 
203
222
  if (-not $ToolInputRaw) {
@@ -221,12 +240,31 @@ function Invoke-PythonBatchBudgetHook {
221
240
  }
222
241
  if ($script:PythonBatchBudgetRouteImportFailure) { return Get-PythonBatchBudgetBlockDecision -Reason "PYTHON_LARGE_PATH_REQUIRED: the route helper '$($script:PythonBatchBudgetRouteImportFailure)' failed to load, so the batch budget for '$normalized' cannot be evaluated; the gate fails closed." }
223
242
 
243
+ # The path is evaluated against the registered worktree that holds it; the session root
244
+ # is the fallback for a relative path, a path in no registered worktree, and an ambiguous result.
245
+ $resolverArguments = @{ SessionRoot = $Root; FilePath = @($filePath) }
246
+ if ($PSBoundParameters.ContainsKey('WorktreeListInvoker')) { $resolverArguments['WorktreeListInvoker'] = $WorktreeListInvoker }
247
+ $resolution = Resolve-HookTargetWorktree @resolverArguments
248
+ $targetRoot = $Root
249
+ $isSessionRoot = $true
250
+ if (-not $resolution.Ambiguous -and $null -ne $resolution.WorktreeRoot -and -not [string]::Equals($resolution.WorktreeRoot, (ConvertTo-HookTargetWorktreeNormalizedPath -Path $Root), [System.StringComparison]::OrdinalIgnoreCase)) {
251
+ $targetRoot = $resolution.WorktreeRoot
252
+ $isSessionRoot = $false
253
+ }
254
+
255
+ # The root-level artifacts/ directory is gitignored scratch, never a production file.
256
+ $relativePath = if ($normalized -match '^(/|[A-Za-z]:/)') { Get-HookTargetWorktreeRelativePath -Path $normalized -WorktreeRoot $targetRoot } else { $normalized }
257
+ if ($null -ne $relativePath -and $relativePath.StartsWith('artifacts/', [System.StringComparison]::OrdinalIgnoreCase)) {
258
+ return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
259
+ }
260
+
224
261
  $stateDir = Join-Path -Path $Root -ChildPath '.codex/state'
225
- $stateFile = Join-Path -Path $stateDir -ChildPath ("python-batch-budget.$SessionId.json")
262
+ $stateLeaf = if ($isSessionRoot) { "python-batch-budget.$SessionId.json" } else { "python-batch-budget.$SessionId.$(Get-HookTargetWorktreeKey -WorktreeRoot $targetRoot).json" }
263
+ $stateFile = Join-Path -Path $stateDir -ChildPath $stateLeaf
226
264
 
227
265
  # The checkpoint is read before any state operation, so the large path neither
228
266
  # creates the state directory nor reads or writes the state file.
229
- $checkpointPath = Join-Path -Path $Root -ChildPath 'artifacts/orchestration/orchestrator-state.json'
267
+ $checkpointPath = Join-Path -Path $targetRoot -ChildPath 'artifacts/orchestration/orchestrator-state.json'
230
268
  $checkpointText = ''
231
269
  try {
232
270
  $checkpointText = [string](& $ReadCheckpoint $checkpointPath)
@@ -282,7 +320,8 @@ function Invoke-PythonBatchBudgetCodexEntryPoint {
282
320
  .PARAMETER PayloadRaw
283
321
  Raw PreToolUse payload text.
284
322
  .PARAMETER RepositoryRoot
285
- Root used to locate the Codex state directory and the orchestrator checkpoint.
323
+ Session root: it locates the Codex state directory and is the fallback root. The
324
+ orchestrator checkpoint comes from the resolved target worktree of each path.
286
325
  .PARAMETER HookSeams
287
326
  Optional seam overrides splatted into Invoke-PythonBatchBudgetHook.
288
327
  #>
@@ -0,0 +1,421 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Codex twin of the tiered file-size policy functions (500 guidance, 700 advisory, 800 hard limit).
4
+
5
+ .DESCRIPTION
6
+ Dot-sourced by .codex/hooks/enforce-file-size-limit.ps1 as:
7
+ . (Join-Path $PSScriptRoot 'file-size-policy.ps1')
8
+
9
+ Codex hooks cannot import .claude/lib in a consumer repository, so the policy
10
+ functions of .claude/lib/file-size-policy/FileSizePolicy.psm1 are mirrored here.
11
+ Every function definition in this file is text-identical to the definition of the
12
+ same name in that module; tests/scripts/codex-hooks/file-size-policy.Tests.ps1
13
+ parses both files and fails on any difference, and runs the shared case rows
14
+ against this file. Edit the module first, then copy each changed definition here.
15
+
16
+ .NOTES
17
+ Compatible with PowerShell 7+.
18
+
19
+ Entrypoint-free by design: this file contains only function definitions, so
20
+ dot-sourcing it has no side effects and no exit-code semantics. It never reads
21
+ standard input and never reads any legacy Claude environment variable. Every
22
+ function is pure: no filesystem, process, network, environment, or clock access.
23
+ #>
24
+
25
+ function Get-FileSizePolicyLimit {
26
+ <#
27
+ .SYNOPSIS
28
+ Return the three tier limits of the file-size policy.
29
+ .OUTPUTS
30
+ A PSCustomObject with Guidance (500), Advisory (700), and HardLimit (800).
31
+ #>
32
+ [CmdletBinding()]
33
+ [OutputType([pscustomobject])]
34
+ param()
35
+
36
+ return [pscustomobject]@{
37
+ Guidance = 500
38
+ Advisory = 700
39
+ HardLimit = 800
40
+ }
41
+ }
42
+
43
+ function Get-FileSizeExcludedSegment {
44
+ <#
45
+ .SYNOPSIS
46
+ Return the directory names excluded from the file-size policy.
47
+ .DESCRIPTION
48
+ A path with any of these names as a directory segment is not covered. This
49
+ function is the single source of the excluded names: Test-FileSizeCoveredPath
50
+ and the repository gate traversal both read it. Each call returns a new array.
51
+ .OUTPUTS
52
+ System.String[].
53
+ #>
54
+ [CmdletBinding()]
55
+ [OutputType([string[]])]
56
+ param()
57
+
58
+ return [string[]]@('.git', 'node_modules', 'artifacts', 'coverage', 'dist', 'out', 'virtual', 'docs',
59
+ 'build', '.venv', 'venv', 'env', '__pycache__', '.pytest_cache', '.mypy_cache', '.ruff_cache', '.tox',
60
+ '.nox', 'htmlcov', '.vscode-test')
61
+ }
62
+
63
+ function Get-FileSizeLogicalLineCount {
64
+ <#
65
+ .SYNOPSIS
66
+ Count the logical lines of a text.
67
+ .DESCRIPTION
68
+ Splits the content on CRLF or LF. A trailing line terminator does not add a
69
+ line, and empty or null content has zero lines.
70
+ .PARAMETER Content
71
+ The file content.
72
+ .OUTPUTS
73
+ System.Int32.
74
+ #>
75
+ [CmdletBinding()]
76
+ [OutputType([int])]
77
+ param(
78
+ [Parameter(Mandatory)]
79
+ [AllowNull()]
80
+ [AllowEmptyString()]
81
+ [string] $Content
82
+ )
83
+
84
+ if ([string]::IsNullOrEmpty($Content)) {
85
+ return 0
86
+ }
87
+
88
+ $count = [regex]::Split($Content, '\r?\n').Count
89
+ if ($Content.EndsWith("`n")) {
90
+ # The split yields one empty trailing element after a final terminator.
91
+ $count--
92
+ }
93
+ return $count
94
+ }
95
+
96
+ function Get-FileSizeTier {
97
+ <#
98
+ .SYNOPSIS
99
+ Classify a logical line count into a policy tier.
100
+ .DESCRIPTION
101
+ Returns 'Below' (under 500), 'Guidance' (500 through 699), 'Advisory' (700
102
+ through 799), or 'HardLimit' (800 and above). The hard block applies only
103
+ above 800; a count of exactly 800 is in the HardLimit tier and still passes.
104
+ .PARAMETER LineCount
105
+ A non-negative logical line count.
106
+ .OUTPUTS
107
+ System.String.
108
+ #>
109
+ [CmdletBinding()]
110
+ [OutputType([string])]
111
+ param(
112
+ [Parameter(Mandatory)]
113
+ [int] $LineCount
114
+ )
115
+
116
+ if ($LineCount -lt 0) {
117
+ throw "Get-FileSizeTier: LineCount must be non-negative; received $LineCount."
118
+ }
119
+
120
+ $limit = Get-FileSizePolicyLimit
121
+ if ($LineCount -ge $limit.HardLimit) { return 'HardLimit' }
122
+ if ($LineCount -ge $limit.Advisory) { return 'Advisory' }
123
+ if ($LineCount -ge $limit.Guidance) { return 'Guidance' }
124
+ return 'Below'
125
+ }
126
+
127
+ function ConvertTo-FileSizeRelativePath {
128
+ <#
129
+ .SYNOPSIS
130
+ Convert a file path to a repository-relative forward-slash path.
131
+ .DESCRIPTION
132
+ Backslashes are normalized to forward slashes. A relative path is returned
133
+ without a leading './'. An absolute path under the repository root is
134
+ returned relative to the root (compared case-insensitively, because the
135
+ root may come from a case-insensitive file system). A path outside the root,
136
+ a relative path that climbs above the root, and the root itself return
137
+ $null, which callers treat as not covered.
138
+ .PARAMETER Path
139
+ The file path from a tool payload or a discovery.
140
+ .PARAMETER RepositoryRoot
141
+ The repository root directory.
142
+ .OUTPUTS
143
+ System.String, or $null for a path outside the repository root.
144
+ #>
145
+ [CmdletBinding()]
146
+ [OutputType([string])]
147
+ param(
148
+ [Parameter(Mandatory)]
149
+ [AllowNull()]
150
+ [AllowEmptyString()]
151
+ [string] $Path,
152
+
153
+ [Parameter(Mandatory)]
154
+ [string] $RepositoryRoot
155
+ )
156
+
157
+ if ([string]::IsNullOrWhiteSpace($Path)) {
158
+ return $null
159
+ }
160
+
161
+ $normalized = $Path.Replace('\', '/')
162
+ $isRooted = $normalized.StartsWith('/') -or ($normalized -match '^[A-Za-z]:/')
163
+ if (-not $isRooted) {
164
+ while ($normalized.StartsWith('./')) {
165
+ $normalized = $normalized.Substring(2)
166
+ }
167
+ if ($normalized -eq '..' -or $normalized.StartsWith('../') -or $normalized.Length -eq 0) {
168
+ return $null
169
+ }
170
+ return $normalized
171
+ }
172
+
173
+ $root = $RepositoryRoot.Replace('\', '/').TrimEnd('/')
174
+ $prefix = $root + '/'
175
+ if (-not $normalized.StartsWith($prefix, [System.StringComparison]::OrdinalIgnoreCase)) {
176
+ return $null
177
+ }
178
+
179
+ $relative = $normalized.Substring($prefix.Length)
180
+ if ($relative.Length -eq 0) {
181
+ return $null
182
+ }
183
+ return $relative
184
+ }
185
+
186
+ function Test-FileSizeCoveredPath {
187
+ <#
188
+ .SYNOPSIS
189
+ Report whether a repository-relative path is covered by the file-size policy.
190
+ .DESCRIPTION
191
+ A path is covered when its extension (case-insensitive) is in the allowlist
192
+ (py, ps1, psm1, ts, tsx, js, cjs, mjs, cs, sh, and bats), no directory segment
193
+ is an excluded name, and it is not under the worktrees folder of .claude.
194
+ Excluded directory names are matched exactly. Null or empty input is not covered.
195
+ .PARAMETER RelativePath
196
+ A repository-relative path, for example from ConvertTo-FileSizeRelativePath.
197
+ .OUTPUTS
198
+ System.Boolean.
199
+ #>
200
+ [CmdletBinding()]
201
+ [OutputType([bool])]
202
+ param(
203
+ [Parameter(Mandatory)]
204
+ [AllowNull()]
205
+ [AllowEmptyString()]
206
+ [string] $RelativePath
207
+ )
208
+
209
+ if ([string]::IsNullOrWhiteSpace($RelativePath)) {
210
+ return $false
211
+ }
212
+
213
+ $allowedExtension = @('.py', '.ps1', '.psm1', '.ts', '.tsx', '.js', '.cjs', '.mjs', '.cs', '.sh', '.bats')
214
+ $excludedSegment = @(Get-FileSizeExcludedSegment)
215
+
216
+ $normalized = $RelativePath.Replace('\', '/')
217
+ if ($normalized.StartsWith('.claude/worktrees/')) {
218
+ return $false
219
+ }
220
+
221
+ $segment = @($normalized.Split('/'))
222
+ for ($index = 0; $index -lt ($segment.Count - 1); $index++) {
223
+ if ($excludedSegment -ccontains $segment[$index]) {
224
+ return $false
225
+ }
226
+ }
227
+
228
+ $extension = [System.IO.Path]::GetExtension($segment[-1]).ToLowerInvariant()
229
+ return ($allowedExtension -contains $extension)
230
+ }
231
+
232
+ function Compare-FileSizeBaselineEntry {
233
+ <#
234
+ .SYNOPSIS
235
+ Compare one file's current size with its baseline entry under the strict ratchet.
236
+ .DESCRIPTION
237
+ Rows (S is the current size, B the recorded size):
238
+ S <= 800, no entry -> pass
239
+ S <= 800, entry -> 'stale-entry' (remove the entry)
240
+ S > 800, no entry -> 'new-over-limit'
241
+ S > 800, entry, S > B -> 'grew'
242
+ S > 800, entry, S == B -> pass
243
+ S > 800, entry, S < B -> 'tighten' (tighten the entry to S)
244
+ no file, entry -> 'missing-file' (stale entry for a deleted or renamed file)
245
+ no file, no entry -> pass
246
+ .PARAMETER Path
247
+ The repository-relative path, used in the violation message.
248
+ .PARAMETER CurrentSize
249
+ The current logical line count, or $null when no file exists on disk.
250
+ .PARAMETER BaselineSize
251
+ The recorded size, or $null when the baseline has no entry.
252
+ .OUTPUTS
253
+ $null on pass; otherwise a PSCustomObject with Path, Rule, and Message.
254
+ #>
255
+ [CmdletBinding()]
256
+ [OutputType([pscustomobject])]
257
+ param(
258
+ [Parameter(Mandatory)]
259
+ [string] $Path,
260
+
261
+ [AllowNull()]
262
+ [Nullable[int]] $CurrentSize,
263
+
264
+ [AllowNull()]
265
+ [Nullable[int]] $BaselineSize
266
+ )
267
+
268
+ $hardLimit = (Get-FileSizePolicyLimit).HardLimit
269
+ $rule = $null
270
+ $message = $null
271
+
272
+ if ($null -eq $CurrentSize) {
273
+ if ($null -ne $BaselineSize) {
274
+ $rule = 'missing-file'
275
+ $message = "stale baseline entry: '$Path' has no file on disk (deleted or renamed); remove the entry or move it with its recorded size unchanged."
276
+ }
277
+ } elseif ($CurrentSize -le $hardLimit) {
278
+ if ($null -ne $BaselineSize) {
279
+ $rule = 'stale-entry'
280
+ $message = "stale baseline entry: '$Path' has $CurrentSize lines, at or below the $hardLimit-line hard limit; remove the entry."
281
+ }
282
+ } elseif ($null -eq $BaselineSize) {
283
+ $rule = 'new-over-limit'
284
+ $message = "new file over the hard limit: '$Path' has $CurrentSize lines, above the $hardLimit-line hard limit; split the file."
285
+ } elseif ($CurrentSize -gt $BaselineSize) {
286
+ $rule = 'grew'
287
+ $message = "baselined file grew: '$Path' has $CurrentSize lines, above its recorded $BaselineSize; a baselined file may not grow."
288
+ } elseif ($CurrentSize -lt $BaselineSize) {
289
+ $rule = 'tighten'
290
+ $message = "baselined file shrank: '$Path' has $CurrentSize lines, below its recorded $BaselineSize; tighten the entry to $CurrentSize."
291
+ }
292
+
293
+ if ($null -eq $rule) {
294
+ return $null
295
+ }
296
+ return [pscustomobject]@{ Path = $Path; Rule = $rule; Message = $message }
297
+ }
298
+
299
+ function Resolve-FileSizeEditContent {
300
+ <#
301
+ .SYNOPSIS
302
+ Apply an Edit-tool old_string to new_string replacement to pre-edit content in memory.
303
+ .DESCRIPTION
304
+ Follows the replacement approach of Resolve-EditedCheckpointContent in
305
+ .claude/hooks/enforce-completion-consistency.ps1: CRLF is normalized to LF in
306
+ all three strings, matching is ordinal (no regular expression, so '$' in the
307
+ new text stays literal), one occurrence is replaced unless ReplaceAll is true,
308
+ and a second match after the first is ambiguous. Failure is 'no-old-string'
309
+ (absent or empty old string), 'not-found', or 'ambiguous'.
310
+ .PARAMETER Content
311
+ The pre-edit file content.
312
+ .PARAMETER OldString
313
+ The text to replace.
314
+ .PARAMETER NewString
315
+ The replacement text; null is treated as empty.
316
+ .PARAMETER ReplaceAll
317
+ Replace every occurrence instead of exactly one.
318
+ .OUTPUTS
319
+ A PSCustomObject with Content (the edited text, or $null on failure) and
320
+ Failure (a failure code, or $null on success).
321
+ #>
322
+ [CmdletBinding()]
323
+ [OutputType([pscustomobject])]
324
+ param(
325
+ [Parameter(Mandatory)]
326
+ [AllowNull()]
327
+ [AllowEmptyString()]
328
+ [string] $Content,
329
+
330
+ [Parameter(Mandatory)]
331
+ [AllowNull()]
332
+ [AllowEmptyString()]
333
+ [string] $OldString,
334
+
335
+ [AllowNull()]
336
+ [AllowEmptyString()]
337
+ [string] $NewString = '',
338
+
339
+ [bool] $ReplaceAll = $false
340
+ )
341
+
342
+ if ([string]::IsNullOrEmpty($OldString)) {
343
+ return [pscustomobject]@{ Content = $null; Failure = 'no-old-string' }
344
+ }
345
+
346
+ $text = ([string]$Content).Replace("`r`n", "`n")
347
+ $old = $OldString.Replace("`r`n", "`n")
348
+ $new = ([string]$NewString).Replace("`r`n", "`n")
349
+
350
+ $first = $text.IndexOf($old, [System.StringComparison]::Ordinal)
351
+ if ($first -lt 0) {
352
+ return [pscustomobject]@{ Content = $null; Failure = 'not-found' }
353
+ }
354
+ if ($ReplaceAll) {
355
+ return [pscustomobject]@{ Content = $text.Replace($old, $new); Failure = $null }
356
+ }
357
+
358
+ $second = $text.IndexOf($old, $first + $old.Length, [System.StringComparison]::Ordinal)
359
+ if ($second -ge 0) {
360
+ return [pscustomobject]@{ Content = $null; Failure = 'ambiguous' }
361
+ }
362
+
363
+ $edited = $text.Substring(0, $first) + $new + $text.Substring($first + $old.Length)
364
+ return [pscustomobject]@{ Content = $edited; Failure = $null }
365
+ }
366
+
367
+ function Get-FileSizeHookDecision {
368
+ <#
369
+ .SYNOPSIS
370
+ Decide whether a Write or Edit may leave a file at its resulting size.
371
+ .DESCRIPTION
372
+ Baseline-free pre-edit-size ratchet. A path that is not covered is allowed.
373
+ A covered result of at most 800 lines is allowed. A covered result above 800
374
+ lines is allowed only when the file already existed and the result is not
375
+ larger than its pre-edit size, so an already-large file may shrink or stay
376
+ the same size but may not grow; every other case is denied. The deny reason
377
+ starts with 'FILE_SIZE_LIMIT_BLOCKED:' and names the path, the resulting
378
+ count, the three tiers, and the split requirement.
379
+ .PARAMETER RelativePath
380
+ The repository-relative path; $null or empty (outside the repository) is not covered.
381
+ .PARAMETER ResultLineCount
382
+ The logical line count the file would have after the operation.
383
+ .PARAMETER PreEditLineCount
384
+ The logical line count before the operation, or $null for a new file.
385
+ .OUTPUTS
386
+ A PSCustomObject with Allowed (bool) and Reason ($null when allowed).
387
+ #>
388
+ [CmdletBinding()]
389
+ [OutputType([pscustomobject])]
390
+ param(
391
+ [Parameter(Mandatory)]
392
+ [AllowNull()]
393
+ [AllowEmptyString()]
394
+ [string] $RelativePath,
395
+
396
+ [Parameter(Mandatory)]
397
+ [int] $ResultLineCount,
398
+
399
+ [AllowNull()]
400
+ [Nullable[int]] $PreEditLineCount
401
+ )
402
+
403
+ $limit = Get-FileSizePolicyLimit
404
+ $isAllowed = (-not (Test-FileSizeCoveredPath -RelativePath $RelativePath)) -or
405
+ ($ResultLineCount -le $limit.HardLimit) -or
406
+ (($null -ne $PreEditLineCount) -and ($ResultLineCount -le $PreEditLineCount))
407
+ if ($isAllowed) {
408
+ return [pscustomobject]@{ Allowed = $true; Reason = $null }
409
+ }
410
+
411
+ $growth = ''
412
+ if ($null -ne $PreEditLineCount) {
413
+ $growth = " It has $PreEditLineCount lines now; a file already above the hard limit may shrink or stay the same size but may not grow."
414
+ }
415
+ $reason = "FILE_SIZE_LIMIT_BLOCKED: '$RelativePath' would have $ResultLineCount logical lines, above the " +
416
+ "$($limit.HardLimit)-line hard limit.$growth File-size policy tiers: $($limit.Guidance) lines is guidance " +
417
+ "(look for a logical split), $($limit.Advisory) lines is advisory (split more aggressively; reviewers report " +
418
+ "a non-blocking Warning), and above $($limit.HardLimit) lines a split is enforced regardless of logical " +
419
+ "separability. Split the file into smaller files so each stays at or below $($limit.HardLimit) lines."
420
+ return [pscustomobject]@{ Allowed = $false; Reason = $reason }
421
+ }