@event4u/agent-config 14.22.0 → 15.0.0

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 (557) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +333 -356
  5. package/README.md +1 -1
  6. package/dist/agent-src/commands/agent-handoff.md +8 -7
  7. package/dist/agent-src/commands/agents/init.md +1 -1
  8. package/dist/agent-src/commands/agents/user/accept.md +4 -4
  9. package/dist/agent-src/commands/agents/user/delete.md +2 -2
  10. package/dist/agent-src/commands/agents/user/init.md +3 -3
  11. package/dist/agent-src/commands/agents/user/review.md +3 -3
  12. package/dist/agent-src/commands/agents/user/show.md +3 -3
  13. package/dist/agent-src/commands/agents/user/update.md +2 -2
  14. package/dist/agent-src/commands/agents/user.md +6 -6
  15. package/dist/agent-src/commands/analytics/prune.md +1 -1
  16. package/dist/agent-src/commands/analytics/show.md +2 -2
  17. package/dist/agent-src/commands/analyze/decision.md +2 -2
  18. package/dist/agent-src/commands/analyze/inbox.md +5 -5
  19. package/dist/agent-src/commands/analyze/incident.md +3 -3
  20. package/dist/agent-src/commands/analyze/near-miss.md +3 -3
  21. package/dist/agent-src/commands/analyze/postmortem.md +4 -4
  22. package/dist/agent-src/commands/analyze/premortem.md +2 -2
  23. package/dist/agent-src/commands/analyze.md +2 -2
  24. package/dist/agent-src/commands/bug/fix.md +1 -1
  25. package/dist/agent-src/commands/bug/investigate.md +2 -2
  26. package/dist/agent-src/commands/cost/profile.md +3 -3
  27. package/dist/agent-src/commands/design.md +1 -1
  28. package/dist/agent-src/commands/feature/explore.md +1 -1
  29. package/dist/agent-src/commands/feature/plan.md +2 -2
  30. package/dist/agent-src/commands/feature/roadmap.md +2 -2
  31. package/dist/agent-src/commands/ghostwriter/fetch.md +4 -4
  32. package/dist/agent-src/commands/ghostwriter/list.md +1 -1
  33. package/dist/agent-src/commands/ghostwriter/show.md +1 -1
  34. package/dist/agent-src/commands/ghostwriter/write.md +9 -9
  35. package/dist/agent-src/commands/humanize.md +6 -6
  36. package/dist/agent-src/commands/jira-ticket.md +1 -1
  37. package/dist/agent-src/commands/judge/solo.md +1 -1
  38. package/dist/agent-src/commands/judge.md +1 -1
  39. package/dist/agent-src/commands/knowledge/cross-repo.md +1 -1
  40. package/dist/agent-src/commands/knowledge/forget.md +1 -1
  41. package/dist/agent-src/commands/knowledge/ingest.md +2 -2
  42. package/dist/agent-src/commands/knowledge/list.md +1 -1
  43. package/dist/agent-src/commands/memory/add.md +3 -3
  44. package/dist/agent-src/commands/memory/load.md +4 -4
  45. package/dist/agent-src/commands/memory/mine-session.md +1 -1
  46. package/dist/agent-src/commands/memory/promote.md +4 -4
  47. package/dist/agent-src/commands/memory/propose.md +2 -2
  48. package/dist/agent-src/commands/mode.md +5 -5
  49. package/dist/agent-src/commands/post-as/ghostwriter.md +3 -3
  50. package/dist/agent-src/commands/post-as/me.md +7 -7
  51. package/dist/agent-src/commands/pr/create.md +1 -1
  52. package/dist/agent-src/commands/pr/merge.md +48 -39
  53. package/dist/agent-src/commands/profile/activate.md +1 -1
  54. package/dist/agent-src/commands/profile/show.md +1 -1
  55. package/dist/agent-src/commands/research/deep.md +2 -2
  56. package/dist/agent-src/commands/research/report.md +2 -2
  57. package/dist/agent-src/commands/review/changes.md +1 -1
  58. package/dist/agent-src/commands/review/routing.md +1 -1
  59. package/dist/agent-src/commands/roadmap/create.md +1 -1
  60. package/dist/agent-src/commands/roadmap/materialize.md +2 -2
  61. package/dist/agent-src/commands/roadmap/next.md +9 -1
  62. package/dist/agent-src/commands/roadmap/process-full.md +2 -2
  63. package/dist/agent-src/commands/security-audit-config.md +3 -3
  64. package/dist/agent-src/commands/skill/preview.md +1 -1
  65. package/dist/agent-src/commands/skills/discover.md +1 -1
  66. package/dist/agent-src/commands/sync/agent-settings.md +2 -2
  67. package/dist/agent-src/commands/sync/gitignore/fix.md +1 -1
  68. package/dist/agent-src/commands/tests/create.md +1 -1
  69. package/dist/agent-src/commands/tests/e2e-heal.md +1 -1
  70. package/dist/agent-src/commands/tests/e2e-plan.md +1 -1
  71. package/dist/agent-src/contexts/authority/kernel-rule-edits.md +6 -6
  72. package/dist/agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +1 -2
  73. package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +2 -2
  74. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +88 -1
  75. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +1 -1
  76. package/dist/agent-src/contexts/contracts/frugality-charter.md +1 -1
  77. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +1 -1
  78. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +1 -1
  79. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +13 -1
  80. package/dist/agent-src/contexts/execution/cheap-question-mechanics.md +1 -1
  81. package/dist/agent-src/contexts/execution/contract-decision-sheet.md +3 -3
  82. package/dist/agent-src/contexts/execution/mandated-lines.md +1 -1
  83. package/dist/agent-src/contexts/execution/non-interactive-contract.md +1 -1
  84. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +1 -1
  85. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +5 -5
  86. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +1 -1
  87. package/dist/agent-src/contexts/execution/subagent-modes-detail.md +1 -1
  88. package/dist/agent-src/contexts/execution/subagent-routing.md +2 -2
  89. package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +1 -1
  90. package/dist/agent-src/contexts/execution/user-memory-channels.md +3 -3
  91. package/dist/agent-src/contexts/execution/verification-mechanics.md +1 -1
  92. package/dist/agent-src/contexts/judges/persona-voice-rubric.md +2 -2
  93. package/dist/agent-src/guidelines/abstraction-thresholds.md +88 -0
  94. package/dist/agent-src/guidelines/agent-infra/5w2h-analysis.md +260 -0
  95. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +99 -0
  96. package/dist/agent-src/guidelines/agent-infra/agent-interaction-and-decision-quality.md +325 -0
  97. package/dist/agent-src/guidelines/agent-infra/api-cost-levers.md +177 -0
  98. package/dist/agent-src/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +222 -0
  99. package/dist/agent-src/guidelines/agent-infra/ask-when-uncertain-demos.md +134 -0
  100. package/dist/agent-src/guidelines/agent-infra/asking-and-brevity-examples.md +198 -0
  101. package/dist/agent-src/guidelines/agent-infra/break-glass-usage.md +113 -0
  102. package/dist/agent-src/guidelines/agent-infra/carve-out-predicates.md +17 -0
  103. package/dist/agent-src/guidelines/agent-infra/comparison-matrix.md +179 -0
  104. package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +165 -0
  105. package/dist/agent-src/guidelines/agent-infra/corpus-grounding-authoring.md +91 -0
  106. package/dist/agent-src/guidelines/agent-infra/critical-thinking.md +156 -0
  107. package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +109 -0
  108. package/dist/agent-src/guidelines/agent-infra/developer-judgment.md +82 -0
  109. package/dist/agent-src/guidelines/agent-infra/direct-answers-demos.md +142 -0
  110. package/dist/agent-src/guidelines/agent-infra/domain-adoption-gates.md +112 -0
  111. package/dist/agent-src/guidelines/agent-infra/domain-eval-anti-pattern.md +53 -0
  112. package/dist/agent-src/guidelines/agent-infra/domain-pack-architecture.md +56 -0
  113. package/dist/agent-src/guidelines/agent-infra/downstream-changes-mechanics.md +91 -0
  114. package/dist/agent-src/guidelines/agent-infra/emphasis-budget.md +45 -0
  115. package/dist/agent-src/guidelines/agent-infra/engineering-memory-data-format.md +210 -0
  116. package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
  117. package/dist/agent-src/guidelines/agent-infra/existence-question-verdicts.md +80 -0
  118. package/dist/agent-src/guidelines/agent-infra/failure-signatures.md +70 -0
  119. package/dist/agent-src/guidelines/agent-infra/false-green.md +233 -0
  120. package/dist/agent-src/guidelines/agent-infra/first-principles.md +192 -0
  121. package/dist/agent-src/guidelines/agent-infra/framework-neutrality-patterns.md +66 -0
  122. package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +169 -0
  123. package/dist/agent-src/guidelines/agent-infra/gate-authoring.md +177 -0
  124. package/dist/agent-src/guidelines/agent-infra/guarded-baseline.md +39 -0
  125. package/dist/agent-src/guidelines/agent-infra/installed-tools-manifest.md +142 -0
  126. package/dist/agent-src/guidelines/agent-infra/inversion-thinking.md +388 -0
  127. package/dist/agent-src/guidelines/agent-infra/ios-simulator-guide.md +378 -0
  128. package/dist/agent-src/guidelines/agent-infra/language-and-tone-examples.md +141 -0
  129. package/dist/agent-src/guidelines/agent-infra/layered-settings.md +317 -0
  130. package/dist/agent-src/guidelines/agent-infra/linked-projects-onboarding-gate.md +80 -0
  131. package/dist/agent-src/guidelines/agent-infra/mcp-request-signing.md +197 -0
  132. package/dist/agent-src/guidelines/agent-infra/memory-access.md +149 -0
  133. package/dist/agent-src/guidelines/agent-infra/mental-models.md +314 -0
  134. package/dist/agent-src/guidelines/agent-infra/minimal-safe-diff-mechanics.md +241 -0
  135. package/dist/agent-src/guidelines/agent-infra/missing-tool-handling.md +66 -0
  136. package/dist/agent-src/guidelines/agent-infra/model-recommendation.md +101 -0
  137. package/dist/agent-src/guidelines/agent-infra/naming.md +69 -0
  138. package/dist/agent-src/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
  139. package/dist/agent-src/guidelines/agent-infra/output-patterns.md +117 -0
  140. package/dist/agent-src/guidelines/agent-infra/recurring-criticism-mechanics.md +96 -0
  141. package/dist/agent-src/guidelines/agent-infra/reuse-verdict-mechanics.md +71 -0
  142. package/dist/agent-src/guidelines/agent-infra/review-routing-data-format.md +143 -0
  143. package/dist/agent-src/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
  144. package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +274 -0
  145. package/dist/agent-src/guidelines/agent-infra/role-contracts.md +209 -0
  146. package/dist/agent-src/guidelines/agent-infra/role-mode-router.md +89 -0
  147. package/dist/agent-src/guidelines/agent-infra/rule-body-migration-inventory.md +241 -0
  148. package/dist/agent-src/guidelines/agent-infra/rule-type-governance.md +73 -0
  149. package/dist/agent-src/guidelines/agent-infra/runtime-layer.md +89 -0
  150. package/dist/agent-src/guidelines/agent-infra/scqa-framework.md +526 -0
  151. package/dist/agent-src/guidelines/agent-infra/security-lint-containment.md +121 -0
  152. package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +134 -0
  153. package/dist/agent-src/guidelines/agent-infra/simplicity-and-goal-demos.md +211 -0
  154. package/dist/agent-src/guidelines/agent-infra/six-hats.md +353 -0
  155. package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +229 -0
  156. package/dist/agent-src/guidelines/agent-infra/skill-quality-checklist.md +157 -0
  157. package/dist/agent-src/guidelines/agent-infra/source-confidentiality-mechanics.md +109 -0
  158. package/dist/agent-src/guidelines/agent-infra/symptom-driven-harvest-loop.md +104 -0
  159. package/dist/agent-src/guidelines/agent-infra/systems-thinking.md +220 -0
  160. package/dist/agent-src/guidelines/agent-infra/tool-description-as-policy.md +35 -0
  161. package/dist/agent-src/guidelines/agent-infra/tool-integration.md +73 -0
  162. package/dist/agent-src/guidelines/agent-infra/traceability-field-mechanics.md +50 -0
  163. package/dist/agent-src/guidelines/agent-infra/untrusted-input-spotlighting.md +166 -0
  164. package/dist/agent-src/guidelines/agent-infra/verify-before-complete-demos.md +128 -0
  165. package/dist/agent-src/guidelines/augment-portability-patterns.md +70 -0
  166. package/dist/agent-src/guidelines/code-clarity.md +350 -0
  167. package/dist/agent-src/guidelines/component-oriented-and-oop-development.md +189 -0
  168. package/dist/agent-src/guidelines/cross-role-handoff.md +127 -0
  169. package/dist/agent-src/guidelines/design-antipatterns-triggers.json +18 -0
  170. package/dist/agent-src/guidelines/design-antipatterns.md +327 -0
  171. package/dist/agent-src/guidelines/design-asset-discipline.md +38 -0
  172. package/dist/agent-src/guidelines/design-canon.md +63 -0
  173. package/dist/agent-src/guidelines/design-fidelity-mechanics.md +293 -0
  174. package/dist/agent-src/guidelines/design-fidelity-routing.md +78 -0
  175. package/dist/agent-src/guidelines/design-handover-extraction.md +117 -0
  176. package/dist/agent-src/guidelines/design-modes.md +143 -0
  177. package/dist/agent-src/guidelines/design-reconciliation.md +172 -0
  178. package/dist/agent-src/guidelines/docs/readme-size-and-splitting.md +209 -0
  179. package/dist/agent-src/guidelines/e2e/playwright.md +363 -0
  180. package/dist/agent-src/guidelines/gtm-handoff.md +114 -0
  181. package/dist/agent-src/guidelines/monorepo-antipatterns.md +141 -0
  182. package/dist/agent-src/guidelines/php/api-design.md +115 -0
  183. package/dist/agent-src/guidelines/php/artisan-commands.md +81 -0
  184. package/dist/agent-src/guidelines/php/blade-ui.md +78 -0
  185. package/dist/agent-src/guidelines/php/controllers.md +90 -0
  186. package/dist/agent-src/guidelines/php/database.md +194 -0
  187. package/dist/agent-src/guidelines/php/eloquent.md +208 -0
  188. package/dist/agent-src/guidelines/php/flux.md +80 -0
  189. package/dist/agent-src/guidelines/php/general.md +207 -0
  190. package/dist/agent-src/guidelines/php/git.md +164 -0
  191. package/dist/agent-src/guidelines/php/jobs.md +111 -0
  192. package/dist/agent-src/guidelines/php/livewire.md +71 -0
  193. package/dist/agent-src/guidelines/php/logging.md +79 -0
  194. package/dist/agent-src/guidelines/php/naming.md +89 -0
  195. package/dist/agent-src/guidelines/php/patterns/dependency-injection.md +57 -0
  196. package/dist/agent-src/guidelines/php/patterns/dtos.md +199 -0
  197. package/dist/agent-src/guidelines/php/patterns/events.md +67 -0
  198. package/dist/agent-src/guidelines/php/patterns/factory.md +53 -0
  199. package/dist/agent-src/guidelines/php/patterns/pipelines.md +66 -0
  200. package/dist/agent-src/guidelines/php/patterns/policies.md +66 -0
  201. package/dist/agent-src/guidelines/php/patterns/repositories.md +122 -0
  202. package/dist/agent-src/guidelines/php/patterns/service-layer.md +64 -0
  203. package/dist/agent-src/guidelines/php/patterns/strategy.md +247 -0
  204. package/dist/agent-src/guidelines/php/patterns.md +28 -0
  205. package/dist/agent-src/guidelines/php/performance.md +92 -0
  206. package/dist/agent-src/guidelines/php/php-coding-patterns.md +80 -0
  207. package/dist/agent-src/guidelines/php/resources.md +100 -0
  208. package/dist/agent-src/guidelines/php/security.md +110 -0
  209. package/dist/agent-src/guidelines/php/sql.md +97 -0
  210. package/dist/agent-src/guidelines/php/validations.md +119 -0
  211. package/dist/agent-src/guidelines/php/websocket.md +100 -0
  212. package/dist/agent-src/guidelines/prompt-templates.md +189 -0
  213. package/dist/agent-src/guidelines/redundancy-taxonomy.md +208 -0
  214. package/dist/agent-src/guidelines/wing4-handoff.md +127 -0
  215. package/dist/agent-src/personas/README.md +1 -1
  216. package/dist/agent-src/personas/_template-specialist/persona.md +1 -1
  217. package/dist/agent-src/personas/product-owner.md +1 -1
  218. package/dist/agent-src/rules/active-remediation.md +1 -1
  219. package/dist/agent-src/rules/architecture.md +2 -2
  220. package/dist/agent-src/rules/artifact-drafting-protocol.md +1 -1
  221. package/dist/agent-src/rules/ask-when-uncertain.md +2 -2
  222. package/dist/agent-src/rules/brand-source-of-truth.md +9 -7
  223. package/dist/agent-src/rules/code-comment-discipline.md +2 -2
  224. package/dist/agent-src/rules/code-provenance.md +1 -1
  225. package/dist/agent-src/rules/content-quoting-floor.md +1 -1
  226. package/dist/agent-src/rules/context-hygiene.md +5 -5
  227. package/dist/agent-src/rules/cross-source-consistency.md +1 -1
  228. package/dist/agent-src/rules/design-fidelity.md +36 -46
  229. package/dist/agent-src/rules/direct-answers.md +2 -2
  230. package/dist/agent-src/rules/domain-adoption-policy.md +1 -1
  231. package/dist/agent-src/rules/evaluator-independence.md +1 -1
  232. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
  233. package/dist/agent-src/rules/icon-consistency.md +5 -1
  234. package/dist/agent-src/rules/improve-before-implement.md +1 -1
  235. package/dist/agent-src/rules/language-and-tone.md +3 -3
  236. package/dist/agent-src/rules/minimal-safe-diff.md +1 -1
  237. package/dist/agent-src/rules/notes-first-reasoning.md +36 -25
  238. package/dist/agent-src/rules/recurring-criticism.md +1 -1
  239. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  240. package/dist/agent-src/rules/role-mode-adherence.md +2 -2
  241. package/dist/agent-src/rules/security-sensitive-stop.md +1 -1
  242. package/dist/agent-src/rules/size-enforcement.md +1 -1
  243. package/dist/agent-src/rules/source-confidentiality.md +2 -2
  244. package/dist/agent-src/rules/think-before-action.md +1 -1
  245. package/dist/agent-src/rules/token-efficiency.md +7 -5
  246. package/dist/agent-src/rules/ui-audit-gate.md +3 -2
  247. package/dist/agent-src/rules/untrusted-input-defense.md +3 -3
  248. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +106 -4
  249. package/dist/agent-src/skills/activation-design/SKILL.md +2 -2
  250. package/dist/agent-src/skills/adr-create/SKILL.md +5 -5
  251. package/dist/agent-src/skills/ai-council/SKILL.md +1 -1
  252. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +1 -1
  253. package/dist/agent-src/skills/blade-ui/SKILL.md +1 -1
  254. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +1 -1
  255. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +24 -3
  256. package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
  257. package/dist/agent-src/skills/build-buy-partner/SKILL.md +1 -1
  258. package/dist/agent-src/skills/churn-prevention/SKILL.md +2 -2
  259. package/dist/agent-src/skills/code-intelligence/SKILL.md +57 -1
  260. package/dist/agent-src/skills/code-refactoring/SKILL.md +3 -3
  261. package/dist/agent-src/skills/code-review/SKILL.md +3 -3
  262. package/dist/agent-src/skills/command-writing/SKILL.md +3 -3
  263. package/dist/agent-src/skills/comp-banding/SKILL.md +1 -1
  264. package/dist/agent-src/skills/competitive-moat-analysis/SKILL.md +1 -1
  265. package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
  266. package/dist/agent-src/skills/complexity-first-planning/SKILL.md +1 -1
  267. package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
  268. package/dist/agent-src/skills/content-funnel-design/SKILL.md +2 -2
  269. package/dist/agent-src/skills/contracts-cognition/SKILL.md +1 -1
  270. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +2 -2
  271. package/dist/agent-src/skills/corpus-grounding/SKILL.md +2 -2
  272. package/dist/agent-src/skills/customer-research/SKILL.md +2 -2
  273. package/dist/agent-src/skills/dashboard-design/SKILL.md +1 -1
  274. package/dist/agent-src/skills/data-handling-judgment/SKILL.md +1 -1
  275. package/dist/agent-src/skills/deal-qualification-meddic/SKILL.md +2 -2
  276. package/dist/agent-src/skills/decision-record/SKILL.md +3 -3
  277. package/dist/agent-src/skills/decision-review/SKILL.md +8 -8
  278. package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +22 -22
  279. package/dist/agent-src/skills/design-intelligence/SKILL.md +4 -4
  280. package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +2 -2
  281. package/dist/agent-src/skills/design-review/SKILL.md +4 -4
  282. package/dist/agent-src/skills/design-system-capture/references/design-system-json.md +20 -1
  283. package/dist/agent-src/skills/design-tokens/SKILL.md +17 -2
  284. package/dist/agent-src/skills/design-variations/SKILL.md +2 -2
  285. package/dist/agent-src/skills/developer-like-execution/SKILL.md +2 -2
  286. package/dist/agent-src/skills/discovery-interview/SKILL.md +2 -2
  287. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +3 -3
  288. package/dist/agent-src/skills/editorial-calendar/SKILL.md +2 -2
  289. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +44 -1
  290. package/dist/agent-src/skills/existing-ui-audit/references/anti-slop-cross-reference.md +2 -2
  291. package/dist/agent-src/skills/expansion-playbook/SKILL.md +2 -2
  292. package/dist/agent-src/skills/experiment-loop/SKILL.md +1 -1
  293. package/dist/agent-src/skills/experiment-loop/references/protocol.md +1 -1
  294. package/dist/agent-src/skills/fe-design/SKILL.md +3 -3
  295. package/dist/agent-src/skills/fe-design/references/design-patterns.md +1 -1
  296. package/dist/agent-src/skills/fe-design/references/design-read-and-memory.md +3 -3
  297. package/dist/agent-src/skills/fe-design/references/source-led-port.md +1 -1
  298. package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
  299. package/dist/agent-src/skills/flux/SKILL.md +1 -1
  300. package/dist/agent-src/skills/forecast-accuracy/SKILL.md +2 -2
  301. package/dist/agent-src/skills/forecasting/SKILL.md +2 -2
  302. package/dist/agent-src/skills/fundraising-narrative/SKILL.md +2 -2
  303. package/dist/agent-src/skills/funnel-analysis/SKILL.md +2 -2
  304. package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +9 -0
  305. package/dist/agent-src/skills/gtm-launch/SKILL.md +2 -2
  306. package/dist/agent-src/skills/hiring-loop-design/SKILL.md +1 -1
  307. package/dist/agent-src/skills/html-deck/SKILL.md +5 -5
  308. package/dist/agent-src/skills/humanizer/SKILL.md +1 -1
  309. package/dist/agent-src/skills/humanizer/data/patterns.md +1 -1
  310. package/dist/agent-src/skills/iconography/SKILL.md +3 -3
  311. package/dist/agent-src/skills/judge-synthesis/SKILL.md +1 -1
  312. package/dist/agent-src/skills/laravel-api-endpoint/SKILL.md +2 -2
  313. package/dist/agent-src/skills/launch-readiness/SKILL.md +3 -3
  314. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +6 -6
  315. package/dist/agent-src/skills/livewire/SKILL.md +1 -1
  316. package/dist/agent-src/skills/market-entry-analysis/SKILL.md +1 -1
  317. package/dist/agent-src/skills/mcp-builder/SKILL.md +1 -1
  318. package/dist/agent-src/skills/memory-consolidation/SKILL.md +3 -3
  319. package/dist/agent-src/skills/messaging-architecture/SKILL.md +2 -2
  320. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  321. package/dist/agent-src/skills/onboarding-design/SKILL.md +2 -2
  322. package/dist/agent-src/skills/onboarding-program/SKILL.md +1 -1
  323. package/dist/agent-src/skills/one-on-one-cadence/SKILL.md +1 -1
  324. package/dist/agent-src/skills/org-design/SKILL.md +1 -1
  325. package/dist/agent-src/skills/overbuild-review-lens/SKILL.md +3 -3
  326. package/dist/agent-src/skills/override-management/SKILL.md +1 -1
  327. package/dist/agent-src/skills/perf-feedback-craft/SKILL.md +1 -1
  328. package/dist/agent-src/skills/persona-writing/SKILL.md +1 -1
  329. package/dist/agent-src/skills/php-coder/SKILL.md +1 -1
  330. package/dist/agent-src/skills/pipeline-strategy/SKILL.md +2 -2
  331. package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -3
  332. package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
  333. package/dist/agent-src/skills/positioning-strategy/SKILL.md +2 -2
  334. package/dist/agent-src/skills/privacy-review/SKILL.md +1 -1
  335. package/dist/agent-src/skills/prompt-optimizer/SKILL.md +3 -3
  336. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
  337. package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +2 -2
  338. package/dist/agent-src/skills/receiving-code-review/SKILL.md +1 -1
  339. package/dist/agent-src/skills/refine-ticket/SKILL.md +2 -2
  340. package/dist/agent-src/skills/release-comms/SKILL.md +2 -2
  341. package/dist/agent-src/skills/retention-loops/SKILL.md +2 -2
  342. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  343. package/dist/agent-src/skills/roadmap-management/SKILL.md +1 -1
  344. package/dist/agent-src/skills/roadmap-management/references/archival.md +1 -1
  345. package/dist/agent-src/skills/roadmap-writing/SKILL.md +5 -5
  346. package/dist/agent-src/skills/rule-writing/SKILL.md +6 -6
  347. package/dist/agent-src/skills/runway-cognition/SKILL.md +2 -2
  348. package/dist/agent-src/skills/scenario-modeling/SKILL.md +2 -2
  349. package/dist/agent-src/skills/skill-writing/SKILL.md +2 -2
  350. package/dist/agent-src/skills/skill-writing/references/procedure.md +1 -1
  351. package/dist/agent-src/skills/skill-writing/references/section-patterns.md +3 -3
  352. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +1 -1
  353. package/dist/agent-src/skills/stakeholder-tradeoff/SKILL.md +3 -3
  354. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
  355. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +1 -1
  356. package/dist/agent-src/skills/systematic-debugging/SKILL.md +2 -2
  357. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +60 -4
  358. package/dist/agent-src/skills/throughput-vs-morale-tradeoff/SKILL.md +1 -1
  359. package/dist/agent-src/skills/token-optimizer/SKILL.md +1 -1
  360. package/dist/agent-src/skills/typography-system/SKILL.md +2 -2
  361. package/dist/agent-src/skills/ui-component-architect/SKILL.md +28 -8
  362. package/dist/agent-src/skills/unit-economics-modeling/SKILL.md +2 -2
  363. package/dist/agent-src/skills/upstream-contribute/SKILL.md +1 -1
  364. package/dist/agent-src/skills/validate-feature-fit/SKILL.md +2 -2
  365. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
  366. package/dist/agent-src/skills/vision-articulation/SKILL.md +1 -1
  367. package/dist/agent-src/skills/voc-extract/SKILL.md +2 -2
  368. package/dist/agent-src/skills/voice-and-tone-design/SKILL.md +2 -2
  369. package/dist/agent-src/skills/wireframe/SKILL.md +1 -1
  370. package/dist/agent-src/skills/workspace-link/SKILL.md +1 -1
  371. package/dist/agent-src/templates/agent-settings.md +6 -6
  372. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  373. package/dist/agent-src/templates/contexts/knowledge-pages.md +2 -3
  374. package/dist/agent-src/templates/copilot-instructions.md +1 -1
  375. package/dist/agent-src/templates/roadmaps.md +1 -1
  376. package/dist/agent-src/templates/scripts/README.md +2 -2
  377. package/dist/agent-src/user-types/_template/user-type.md +1 -1
  378. package/dist/cli/registry.js +1 -1
  379. package/dist/cli-delegate/archive_completed_roadmaps.js +52 -4
  380. package/dist/cli-delegate/check_update_banner.js +12 -1
  381. package/dist/cli-delegate/{chunk-TGIPZBCW.js → chunk-2KKXJW25.js} +22 -8
  382. package/dist/cli-delegate/{chunk-NLP54U3J.js → chunk-2XY7ILBT.js} +13 -1
  383. package/dist/cli-delegate/{chunk-4FS4WRXP.js → chunk-3FKR64R5.js} +34 -6
  384. package/dist/cli-delegate/{chunk-J3RFNKG4.js → chunk-55ACLDUE.js} +2 -2
  385. package/dist/cli-delegate/{chunk-PKOBQADP.js → chunk-57PN4GYJ.js} +1 -1
  386. package/dist/cli-delegate/{chunk-BIXEDQPZ.js → chunk-AK6HODNQ.js} +4 -4
  387. package/dist/cli-delegate/{chunk-2FTMENJL.js → chunk-GYC3VRYN.js} +1 -1
  388. package/dist/cli-delegate/{chunk-LWTYTX6F.js → chunk-HZ52BRGG.js} +24 -18
  389. package/dist/cli-delegate/{chunk-6RJA2F5A.js → chunk-IVBPRCQO.js} +1 -1
  390. package/dist/cli-delegate/{chunk-I6RHFQEG.js → chunk-YUM5KURG.js} +17 -1
  391. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  392. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  393. package/dist/cli-delegate/cmd_conformance.js +3 -3
  394. package/dist/cli-delegate/cmd_doctor.js +3 -3
  395. package/dist/cli-delegate/cmd_explain.js +2 -2
  396. package/dist/cli-delegate/cmd_export.js +4 -4
  397. package/dist/cli-delegate/cmd_handoff.js +24 -16
  398. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  399. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  400. package/dist/cli-delegate/cmd_migrate.js +2 -2
  401. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  402. package/dist/cli-delegate/cmd_prune.js +4 -4
  403. package/dist/cli-delegate/cmd_route_audit.js +3 -3
  404. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  405. package/dist/cli-delegate/cmd_session_recycle.js +4 -14
  406. package/dist/cli-delegate/cmd_settings_get.js +15 -9
  407. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  408. package/dist/cli-delegate/cmd_settings_set.js +41 -11
  409. package/dist/cli-delegate/cmd_sync.js +4 -4
  410. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  411. package/dist/cli-delegate/cmd_update.js +1 -1
  412. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  413. package/dist/cli-delegate/cmd_validate.js +4 -4
  414. package/dist/cli-delegate/cmd_versions.js +1 -1
  415. package/dist/discovery/deprecation-report.md +1 -1
  416. package/dist/discovery/discovery-manifest.json +27 -80
  417. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  418. package/dist/discovery/discovery-manifest.summary.md +3 -3
  419. package/dist/discovery/dormancy-report.md +1 -1
  420. package/dist/discovery/orphan-report.md +1 -1
  421. package/dist/discovery/packs.json +5 -7
  422. package/dist/discovery/trust-report.md +2 -2
  423. package/dist/discovery/workspaces.json +4 -6
  424. package/dist/hooks/dispatch.js +9249 -8981
  425. package/dist/install/install.mjs +47 -8
  426. package/dist/mcp/registry-manifest.json +3 -3
  427. package/dist/mcp/server.json +2 -2
  428. package/dist/mcp/server.mjs +5496 -2400
  429. package/dist/router.json +1 -1
  430. package/dist/scripts/_lib/agent_settings.js +73 -0
  431. package/dist/scripts/_lib/agent_src.js +20 -0
  432. package/dist/scripts/_lib/guidelines_lane.js +158 -0
  433. package/dist/scripts/_lib/lean_projection_mode.js +108 -0
  434. package/dist/scripts/_lib/router_match.js +243 -0
  435. package/dist/scripts/_lib/rule_injection.js +216 -0
  436. package/dist/scripts/condense.js +22 -25
  437. package/dist/scripts/project_thin_rules.js +139 -19
  438. package/dist/server/io/yamlIO.js +125 -17
  439. package/dist/server/schemas/settings.js +32 -5
  440. package/dist/shared/settingsSurface.js +6 -1
  441. package/docs/MIGRATION.md +56 -0
  442. package/docs/contracts/settings-classes.md +48 -7
  443. package/docs/guidelines/abstraction-thresholds.md +35 -0
  444. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +3 -3
  445. package/docs/guidelines/agent-infra/false-green.md +2 -2
  446. package/docs/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
  447. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  448. package/docs/guidelines/agent-infra/skill-quality-checklist.md +1 -1
  449. package/docs/guidelines/design-fidelity-mechanics.md +4 -0
  450. package/docs/guidelines/design-fidelity-routing.md +78 -0
  451. package/docs/guidelines/design-reconciliation.md +172 -0
  452. package/docs/guidelines/gtm-handoff.md +2 -2
  453. package/docs/guidelines/wing4-handoff.md +2 -2
  454. package/llms.txt +1 -1
  455. package/package.json +7 -3
  456. package/src/agent-src/scripts/archive_completed_roadmaps.ts +106 -4
  457. package/src/config/agent-settings.template.yml +94 -1
  458. package/src/config/ci-local-parity.yml +34 -0
  459. package/src/config/continuity-surface.json +8 -29
  460. package/src/config/evaluator-budgets.json +4 -4
  461. package/src/config/gate-coverage.yml +31 -8
  462. package/src/config/gate-reachability-exemptions.json +1 -0
  463. package/src/config/gate-violation-baselines.json +3 -3
  464. package/src/config/hook-token-budget.json +8 -7
  465. package/src/config/host-injection-effect.json +2 -2
  466. package/src/config/pack-size-budget.json +55 -4
  467. package/src/config/packed-binary-manifest.json +1 -1
  468. package/src/config/platform-anchor.json +47 -0
  469. package/src/config/preamble-payload-budget.json +2 -3
  470. package/src/config/publish-surface.json +4 -2
  471. package/src/config/ratification-policy.json +7 -0
  472. package/src/config/routing-coverage-seed.json +9 -2
  473. package/src/config/rule-stub-ceilings.json +16 -9
  474. package/src/scripts/_cli/cmd_session_recycle.ts +2 -25
  475. package/src/scripts/_cli/cmd_settings_get.ts +22 -7
  476. package/src/scripts/_cli/handoff_sessions.ts +14 -44
  477. package/src/scripts/_lib/agent_settings.ts +85 -0
  478. package/src/scripts/_lib/agent_src.ts +21 -0
  479. package/src/scripts/_lib/artifact_maturity.ts +242 -0
  480. package/src/scripts/_lib/conformance_report.ts +403 -0
  481. package/src/scripts/_lib/continuity_slot.ts +295 -0
  482. package/src/scripts/_lib/continuity_writer.ts +312 -0
  483. package/src/scripts/_lib/design_system_import.ts +20 -0
  484. package/src/scripts/_lib/design_tolerance.ts +281 -0
  485. package/src/scripts/_lib/guidelines_lane.ts +164 -0
  486. package/src/scripts/_lib/hook_settings.ts +85 -0
  487. package/src/scripts/_lib/lean_projection_mode.ts +127 -0
  488. package/src/scripts/_lib/obligation_frequency.ts +2 -3
  489. package/src/scripts/_lib/packed_binary_predicate.ts +37 -0
  490. package/src/scripts/_lib/platform_anchor.ts +1053 -0
  491. package/src/scripts/_lib/ratification_artifact.ts +278 -0
  492. package/src/scripts/_lib/recycle_envelope_paths.ts +109 -21
  493. package/src/scripts/_lib/regression_neighbourhood.ts +159 -22
  494. package/src/scripts/_lib/security_lint.ts +23 -2
  495. package/src/scripts/_lib/self_repair.ts +21 -0
  496. package/src/scripts/_lib/session_eol.ts +44 -0
  497. package/src/scripts/_lib/session_index_trust.ts +387 -0
  498. package/src/scripts/_lib/standing_bound_ratchet.ts +220 -0
  499. package/src/scripts/_lib/tolerance_shadow.ts +177 -0
  500. package/src/scripts/_lib/vendored_grammar_upstream.ts +280 -0
  501. package/src/scripts/ai_council/redact_low_impact_entry.ts +8 -0
  502. package/src/scripts/audit_mcp_tools.ts +69 -25
  503. package/src/scripts/autonomy_friction_traffic.ts +379 -0
  504. package/src/scripts/build_mcp_catalog.ts +24 -1
  505. package/src/scripts/check_host_tree_parity.ts +291 -0
  506. package/src/scripts/check_kernel_edit_ratified.ts +462 -0
  507. package/src/scripts/check_loss_class_declared.ts +793 -46
  508. package/src/scripts/check_platform_anchor.ts +390 -0
  509. package/src/scripts/check_preamble_payload_budget.ts +424 -7
  510. package/src/scripts/check_projected_rule_routes.ts +395 -0
  511. package/src/scripts/check_rule_projection_integrity.ts +102 -2
  512. package/src/scripts/ci_settle.ts +205 -5
  513. package/src/scripts/code_graph/build.ts +79 -0
  514. package/src/scripts/code_graph/cli.ts +264 -7
  515. package/src/scripts/code_graph/detect.ts +38 -0
  516. package/src/scripts/code_graph/query.ts +25 -2
  517. package/src/scripts/code_graph/sqlite_store.ts +16 -1
  518. package/src/scripts/code_graph/types.ts +52 -3
  519. package/src/scripts/code_graph/validate.ts +3 -1
  520. package/src/scripts/code_graph/verbs.ts +590 -0
  521. package/src/scripts/condense.ts +22 -26
  522. package/src/scripts/generate_host_cost_table.ts +354 -0
  523. package/src/scripts/handoff_context_hook.ts +39 -1
  524. package/src/scripts/hook_manifest.json +1 -1
  525. package/src/scripts/hook_manifest.yaml +59 -28
  526. package/src/scripts/hooks/block_speaking_inbox_dir.ts +182 -16
  527. package/src/scripts/hooks/category_a.ts +1 -1
  528. package/src/scripts/hooks/chain_nudge_hook.ts +311 -25
  529. package/src/scripts/hooks/code_graph_context_hook.ts +7 -18
  530. package/src/scripts/hooks/push_settle_hook.ts +3 -2
  531. package/src/scripts/hooks/rule_inject_hook.ts +107 -20
  532. package/src/scripts/hooks/session_eol_hook.ts +177 -145
  533. package/src/scripts/hooks/source_first_gate_hook.ts +12 -0
  534. package/src/scripts/hooks/ui_route_nudge_hook.ts +52 -15
  535. package/src/scripts/hot_context_hook.ts +103 -408
  536. package/src/scripts/lint_evidence_artifacts.ts +1 -0
  537. package/src/scripts/lint_knowledge_scale.ts +0 -26
  538. package/src/scripts/lint_roadmap_blockers.ts +164 -1
  539. package/src/scripts/mcp_server/consumer_tool_catalog.json +152 -2
  540. package/src/scripts/mcp_server/graph_tools.ts +294 -0
  541. package/src/scripts/mcp_server/path_util.ts +31 -0
  542. package/src/scripts/mcp_server/tool_catalog_source.ts +7 -1
  543. package/src/scripts/mcp_server/tools.ts +3 -13
  544. package/src/scripts/memory_lookup.ts +9 -9
  545. package/src/scripts/model_rule_injection.ts +61 -4
  546. package/src/scripts/project_thin_rules.ts +149 -16
  547. package/src/scripts/report_standing_payload_by_host.ts +650 -0
  548. package/src/scripts/schemas/agent-settings.schema.json +7 -1
  549. package/src/scripts/session_memory_index.ts +86 -8
  550. package/src/scripts/sync_agent_settings.ts +270 -10
  551. package/src/server/io/yamlIO.ts +127 -17
  552. package/src/server/schemas/settings.ts +45 -6
  553. package/src/shared/settingsSurface.ts +13 -2
  554. package/src/templates/compact-instructions.md +8 -3
  555. package/src/vendor/grammars/README.md +27 -2
  556. package/dist/agent-src/commands/chat-history/import.md +0 -170
  557. package/dist/agent-src/commands/chat-history.md +0 -63
@@ -0,0 +1,1053 @@
1
+ /**
2
+ * The platform anchor: does the forge actually enforce what the ratification
3
+ * mechanism's trust argument says it enforces?
4
+ *
5
+ * `check_kernel_edit_ratified` proves that a diff touching a kernel rule, a
6
+ * governance hook or the ratification mechanism itself carries a reviewed
7
+ * artifact. What no diff can contain is what the forge itself requires, and
8
+ * that is what this module reads.
9
+ *
10
+ * It does NOT establish that the artifact was reviewed by anyone the repository
11
+ * insisted on: by owner ruling of 2026-09-10 this repository requires no
12
+ * approving review, because one maintainer is active and a mandatory approval
13
+ * would be a stop rather than a control. So what this module checks is branch
14
+ * integrity plus the presence of the required context that carries the gate —
15
+ * see `docs/contracts/ratification-artifact.md` for what a green anchor is and
16
+ * is not evidence of, which is the shorter list than it once was.
17
+ *
18
+ * It is deliberately PURE: it evaluates ruleset payloads a caller fetched, so
19
+ * both polarities are testable offline and no test needs a token. The impure
20
+ * half — the `gh api` calls and the fail-closed exit codes — lives in
21
+ * `src/scripts/check_platform_anchor.ts`.
22
+ *
23
+ * Five constraints below are structural, and the code would read as arbitrary
24
+ * without them:
25
+ *
26
+ * 1. **Effective combination, never a ruleset by id.** A repository may carry
27
+ * several active branch rulesets and an administrator may split one at any
28
+ * time. Assuming ruleset `17749383` — which is what exists today — or its
29
+ * name would make this check wrong on the first split. So every applicable
30
+ * active ruleset is unioned: booleans OR together, the approval count takes
31
+ * the MAXIMUM, contexts union, and bypass actors accumulate.
32
+ * 2. **A non-negotiable floor in the code, in two tiers.** The expectation lives
33
+ * in `src/config/platform-anchor.json` so that changing it is a reviewable
34
+ * governance diff. That alone is not enough: a policy-only edit could lower a
35
+ * threshold and make itself green, so `NON_NEGOTIABLE_FLOOR` below is what
36
+ * such an edit cannot cross. `NEVER_WAIVABLE` is the harder tier inside it —
37
+ * the dimensions no waiver may reach at all.
38
+ * 3. **Two distinct negative outcomes, and one qualified positive.**
39
+ * `noncompliant` means the configuration was read and violates the policy;
40
+ * `unverifiable` means it could not be established, and both are failures
41
+ * because a control that passes when it cannot measure is advisory. They stay
42
+ * separate statuses because they are different repairs.
43
+ * `compliant-with-accepted-risk` is the third: every hard dimension present,
44
+ * and at least one baseline expectation covered by a complete, unexpired,
45
+ * owner-authorised waiver. It is neither a pass nor a failure, and the caller
46
+ * must not print it as a plain pass.
47
+ * 4. **Configured bypass actors, not the caller's own capability.** GitHub
48
+ * reports `current_user_can_bypass` for the acting identity, which varies by
49
+ * token and says nothing durable about the repository. The durable fact is
50
+ * the configured `bypass_actors` list, so that is what is judged.
51
+ * 5. **A malformed waiver is worse than no waiver.** An incomplete, expired or
52
+ * impermissible waiver is refused, and the dimension it named is then judged
53
+ * normally. The opposite direction — a badly written waiver skipping a check
54
+ * — is how a waiver mechanism becomes an exemption registry.
55
+ *
56
+ * A required status check pins a JOB's reported name, never the steps inside it.
57
+ * `check_kernel_edit_ratified` is a step inside `Sync + Generate Tools
58
+ * Consistency` (`.github/workflows/consistency.yml:811-825`), and that job IS
59
+ * the ruleset's one required context — so the gate is platform-required
60
+ * transitively. It is necessary and not sufficient: a candidate branch may
61
+ * delete the step and the job still reports the same context, green. That is the
62
+ * defect the round-2 ratification review refused the deny-retirement over, and
63
+ * this module does not close it. It asserts the necessary half and says so.
64
+ */
65
+
66
+ /** A rule inside a ruleset. `parameters` is untyped until the type is known. */
67
+ export interface RulesetRule {
68
+ type?: string | undefined;
69
+ parameters?: unknown;
70
+ }
71
+
72
+ /** One entry of a ruleset's bypass list. */
73
+ export interface BypassActor {
74
+ actor_type?: string | undefined;
75
+ actor_id?: number | undefined;
76
+ bypass_mode?: string | undefined;
77
+ }
78
+
79
+ /** The ruleset shape this module reads. Extra fields are ignored. */
80
+ export interface RulesetDetail {
81
+ id?: number | undefined;
82
+ name?: string | undefined;
83
+ target?: string | undefined;
84
+ enforcement?: string | undefined;
85
+ conditions?:
86
+ | {
87
+ ref_name?:
88
+ | {
89
+ include?: string[] | undefined;
90
+ exclude?: string[] | undefined;
91
+ }
92
+ | undefined;
93
+ }
94
+ | undefined;
95
+ rules?: RulesetRule[] | undefined;
96
+ bypass_actors?: BypassActor[] | undefined;
97
+ }
98
+
99
+ /** The committed expectation, as `src/config/platform-anchor.json` carries it. */
100
+ export interface AnchorPolicy {
101
+ enforcement: string;
102
+ target: string;
103
+ covers_default_branch: boolean;
104
+ required_review_thread_resolution: boolean;
105
+ block_deletion: boolean;
106
+ block_non_fast_forward: boolean;
107
+ strict_required_status_checks: boolean;
108
+ required_status_check_contexts: string[];
109
+ allow_unconditional_bypass: boolean;
110
+ }
111
+
112
+ /**
113
+ * The floor a policy edit may not cross.
114
+ *
115
+ * openai/codex-default's condition, quoted: *"Reject unknown fields, missing
116
+ * requirements, and policy values weaker than a small non-negotiable floor
117
+ * embedded in the verifier. That last safeguard prevents a policy-only edit from
118
+ * redefining `minimumApprovals` to zero and making itself green."*
119
+ */
120
+ export const NON_NEGOTIABLE_FLOOR = {
121
+ // `minimum_approving_reviews` and `require_last_push_approval` are NOT here,
122
+ // and their absence is a recorded trust-model decision rather than an
123
+ // oversight or a convenience. This repository has one active maintainer;
124
+ // the others hold write access and are rarely available. A mandatory
125
+ // approving review is therefore not a control here, it is a stop — and a
126
+ // floor nothing can satisfy makes red mean "the repository chose a
127
+ // different model" instead of "the platform violates its policy", which is
128
+ // the reading that gets a gate deleted or wired `continue-on-error`.
129
+ //
130
+ // The boundary this does NOT cross, stated because the mechanism cannot
131
+ // check it: a floor reduction is legitimate only where the operating model
132
+ // CANNOT satisfy the floor — no second human exists — never where waiting
133
+ // is merely inconvenient.
134
+ //
135
+ // `required_review_thread_resolution` IS here, and it was briefly not.
136
+ // Removing it was the implementer's own judgement — approval-adjacent,
137
+ // near-vacuous once no review is required — and a blind review caught that
138
+ // the same change's record says only the owner may authorize a floor
139
+ // reduction while the ruling names two dimensions and not this one. It also
140
+ // left the check policy-lowerable: one boolean flip in the expectation
141
+ // deleted it while the anchor still read compliant. Restored, and the forge
142
+ // has it true, so restoring costs nothing and removes an unauthorized
143
+ // reduction rather than adding a requirement.
144
+ required_review_thread_resolution: true,
145
+ block_deletion: true,
146
+ block_non_fast_forward: true,
147
+ allow_unconditional_bypass: false,
148
+ minimum_required_contexts: 1,
149
+ // `strict_required_status_checks` is NOT in the floor, and that is the
150
+ // second half of the same correction. It carries an active waiver, and a
151
+ // floored dimension is unwaivable by construction — holding it in both
152
+ // places was the contradiction a blind review surfaced from the other side.
153
+ // It stays a baseline expectation in `platform-anchor.json`, which is the
154
+ // tier a waiver may trade against.
155
+ //
156
+ // The SELECTOR half. The values above are thresholds a ruleset is
157
+ // measured against; these three decide WHICH rulesets are measured at all,
158
+ // and leaving them unfloored left the whole check reachable through a door
159
+ // the floor did not watch. A blind review probed it against this evaluator:
160
+ // `enforcement: "evaluate"` credited a non-enforcing dry-run ruleset as
161
+ // full protection and returned `compliant` with zero findings, and
162
+ // `covers_default_branch: false` credited a `refs/heads/release`-only
163
+ // ruleset to `main` while `main` carried no ruleset. Neither touched a
164
+ // threshold, so neither was caught.
165
+ enforcement: 'active',
166
+ target: 'branch',
167
+ covers_default_branch: true,
168
+ } as const;
169
+
170
+ /**
171
+ * A dimension the owner has deliberately left unsatisfied, on record.
172
+ *
173
+ * The third state exists because the first two were both wrong for a real case.
174
+ * Deleting a dimension the repository still considers the safer configuration
175
+ * erases the difference between "never expected" and "expected and knowingly
176
+ * waived"; leaving it enforced-and-red makes the gate say the configuration
177
+ * violates its policy when the policy is what chose it. openai: *"If
178
+ * noncompliance does not fail the gate, that dimension is not enforced for the
179
+ * duration of the waiver. It remains a baseline expectation subject to an
180
+ * active exception."* This type is that exception, made checkable.
181
+ *
182
+ * Every field is required, and a waiver missing any of them is not honoured —
183
+ * an incomplete waiver is an undocumented deviation wearing a record's clothes.
184
+ * `expires` is the load-bearing one: without it a waiver outlives the cost
185
+ * premise that justified it, and the premise here is a CI duration, which is
186
+ * not a constant.
187
+ */
188
+ export interface AcceptedRiskWaiver {
189
+ dimension: string;
190
+ baseline: boolean;
191
+ accepted: boolean;
192
+ /** What can go wrong, in concrete terms rather than as a category. */
193
+ failure_mode: string;
194
+ /** What the waiver buys, with the measurement it rests on. */
195
+ cost_avoided: string;
196
+ /** Explicitly an assumption unless incident data supports it. */
197
+ frequency_assumption: string;
198
+ /** How a breakage is noticed and by whom, and how it gets repaired. */
199
+ detection_and_repair: string;
200
+ /** What still protects the surface, and what that protection does NOT cover. */
201
+ residual_protection: string;
202
+ authority: string;
203
+ decided: string;
204
+ /** ISO date. A waiver past it is not honoured, renewal is a fresh decision. */
205
+ expires: string;
206
+ review_triggers: string[];
207
+ id: string;
208
+ }
209
+
210
+ /**
211
+ * Dimensions no waiver may reach, whatever it documents.
212
+ *
213
+ * DERIVED FROM THE FLOOR, not hand-listed beside it. A blind review found the
214
+ * hand-written version had drifted from it in the worst possible direction:
215
+ * `required_review_thread_resolution` sat in the floor and was NOT on the
216
+ * never-waivable list, so the same change that restored it to the floor "to
217
+ * remove an unauthorized reduction" opened a second, unauthenticated route to
218
+ * exactly that reduction. Deriving the set makes the two impossible to
219
+ * disagree.
220
+ *
221
+ * The invariant this expresses: the FLOOR is the hard tier and is unwaivable by
222
+ * construction; the expectation's remaining dimensions are baseline
223
+ * expectations a complete, owner-authorised, unexpired waiver may trade. A
224
+ * dimension cannot be both floored and waivable, which is what the drift had
225
+ * made possible.
226
+ *
227
+ * `minimum_required_contexts` is excluded because it is a COUNT rather than a
228
+ * dimension name — it has no matching key in the expectation for a waiver to
229
+ * name, so listing it would bound nothing.
230
+ */
231
+ export const NEVER_WAIVABLE: readonly string[] = Object.keys(NON_NEGOTIABLE_FLOOR).filter(
232
+ (k) => k !== 'minimum_required_contexts',
233
+ );
234
+
235
+ export type AnchorStatus =
236
+ | 'compliant'
237
+ | 'compliant-with-accepted-risk'
238
+ | 'noncompliant'
239
+ | 'unverifiable';
240
+
241
+ /** Stable codes, so a test asserts the reason rather than the wording. */
242
+ export type AnchorCode =
243
+ | 'policy-missing'
244
+ | 'policy-unparseable'
245
+ | 'policy-missing-field'
246
+ | 'policy-unknown-field'
247
+ | 'policy-repository-mismatch'
248
+ | 'waiver-incomplete'
249
+ | 'waiver-expired'
250
+ | 'waiver-not-permitted'
251
+ | 'waiver-unused'
252
+ | 'policy-below-floor'
253
+ | 'rulesets-unreadable'
254
+ | 'default-branch-unknown'
255
+ | 'no-applicable-ruleset'
256
+ | 'thread-resolution-missing'
257
+ | 'deletion-not-blocked'
258
+ | 'non-fast-forward-not-blocked'
259
+ | 'status-checks-not-strict'
260
+ | 'required-context-missing'
261
+ | 'unconditional-bypass';
262
+
263
+ export interface AnchorFinding {
264
+ code: AnchorCode;
265
+ message: string;
266
+ }
267
+
268
+ export interface AnchorReading {
269
+ status: AnchorStatus;
270
+ findings: AnchorFinding[];
271
+ /** Human-readable facts the verdict rests on, for the gate's output. */
272
+ evidence: string[];
273
+ }
274
+
275
+ /** The effective configuration, unioned across every applicable ruleset. */
276
+ export interface EffectiveProtection {
277
+ rulesetIds: number[];
278
+ approvingReviews: number;
279
+ requireLastPushApproval: boolean;
280
+ requireThreadResolution: boolean;
281
+ blocksDeletion: boolean;
282
+ blocksNonFastForward: boolean;
283
+ strictStatusChecks: boolean;
284
+ requiredContexts: string[];
285
+ unconditionalBypassActors: BypassActor[];
286
+ }
287
+
288
+ const POLICY_FIELDS: readonly (keyof AnchorPolicy)[] = [
289
+ 'enforcement',
290
+ 'target',
291
+ 'covers_default_branch',
292
+ 'required_review_thread_resolution',
293
+ 'block_deletion',
294
+ 'block_non_fast_forward',
295
+ 'strict_required_status_checks',
296
+ 'required_status_check_contexts',
297
+ 'allow_unconditional_bypass',
298
+ ];
299
+
300
+ function isObject(v: unknown): v is Record<string, unknown> {
301
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
302
+ }
303
+
304
+ /**
305
+ * Read and validate the committed policy.
306
+ *
307
+ * Returns `null` for the policy on any problem, so a caller cannot accidentally
308
+ * evaluate against a half-read expectation. Missing and unparseable are distinct
309
+ * codes because they are distinct repairs.
310
+ */
311
+ export function readAnchorPolicy(text: string | null): {
312
+ policy: AnchorPolicy | null;
313
+ findings: AnchorFinding[];
314
+ } {
315
+ if (text === null) {
316
+ return {
317
+ policy: null,
318
+ findings: [
319
+ {
320
+ code: 'policy-missing',
321
+ message:
322
+ 'src/config/platform-anchor.json is absent, so the expected platform ' +
323
+ 'configuration is unknown. This check fails closed rather than assuming one.',
324
+ },
325
+ ],
326
+ };
327
+ }
328
+ let parsed: unknown;
329
+ try {
330
+ parsed = JSON.parse(text);
331
+ } catch (e) {
332
+ return {
333
+ policy: null,
334
+ findings: [
335
+ {
336
+ code: 'policy-unparseable',
337
+ message: `src/config/platform-anchor.json is not valid JSON: ${String(e)}`,
338
+ },
339
+ ],
340
+ };
341
+ }
342
+ if (!isObject(parsed) || !isObject(parsed['required'])) {
343
+ return {
344
+ policy: null,
345
+ findings: [
346
+ {
347
+ code: 'policy-missing-field',
348
+ message: 'src/config/platform-anchor.json carries no `required` object.',
349
+ },
350
+ ],
351
+ };
352
+ }
353
+ const req = parsed['required'];
354
+ const findings: AnchorFinding[] = [];
355
+ for (const field of POLICY_FIELDS) {
356
+ if (!(field in req)) {
357
+ findings.push({
358
+ code: 'policy-missing-field',
359
+ message: `src/config/platform-anchor.json \`required\` is missing \`${field}\`.`,
360
+ });
361
+ }
362
+ }
363
+ // Unknown keys are refused, not ignored. A misspelled or stale key would
364
+ // otherwise sit in the expectation looking authoritative while the field it
365
+ // was meant to set falls back to a default nobody chose — and the council
366
+ // condition quoted above this file's floor says "reject unknown fields" in
367
+ // as many words, so ignoring them documented the code as doing the
368
+ // opposite of what it did.
369
+ for (const key of Object.keys(req)) {
370
+ if (!(POLICY_FIELDS as readonly string[]).includes(key)) {
371
+ findings.push({
372
+ code: 'policy-unknown-field',
373
+ message:
374
+ `src/config/platform-anchor.json \`required\` carries an unknown key ` +
375
+ `\`${key}\`. Every key must be one of: ${POLICY_FIELDS.join(', ')}. ` +
376
+ 'Explanatory prose belongs in a sibling `*_note` key outside `required`.',
377
+ });
378
+ }
379
+ }
380
+ if (findings.length > 0) {
381
+ return { policy: null, findings };
382
+ }
383
+
384
+ const contexts = req['required_status_check_contexts'];
385
+ if (!Array.isArray(contexts) || contexts.some((c) => typeof c !== 'string')) {
386
+ return {
387
+ policy: null,
388
+ findings: [
389
+ {
390
+ code: 'policy-missing-field',
391
+ message:
392
+ '`required_status_check_contexts` must be an array of strings naming the ' +
393
+ 'CI contexts that must be required checks.',
394
+ },
395
+ ],
396
+ };
397
+ }
398
+
399
+ const policy: AnchorPolicy = {
400
+ enforcement: String(req['enforcement']),
401
+ target: String(req['target']),
402
+ covers_default_branch: req['covers_default_branch'] === true,
403
+ required_review_thread_resolution: req['required_review_thread_resolution'] === true,
404
+ block_deletion: req['block_deletion'] === true,
405
+ block_non_fast_forward: req['block_non_fast_forward'] === true,
406
+ strict_required_status_checks: req['strict_required_status_checks'] === true,
407
+ required_status_check_contexts: contexts as string[],
408
+ allow_unconditional_bypass: req['allow_unconditional_bypass'] === true,
409
+ };
410
+
411
+ const floor = enforceFloor(policy);
412
+ if (floor.length > 0) {
413
+ return { policy: null, findings: floor };
414
+ }
415
+ return { policy, findings: [] };
416
+ }
417
+
418
+ /** The expectation's supported schema version. Bump only with its reader. */
419
+ export const SUPPORTED_ANCHOR_SCHEMA = 2;
420
+
421
+ /**
422
+ * The two top-level fields that say WHICH expectation this is.
423
+ *
424
+ * Both were declared and read by nothing, which a blind review named: the gate
425
+ * could report `compliant` for a fork, or for whatever `--repo` was handed it,
426
+ * while quoting an expectation that names another repository. A verdict that
427
+ * does not know whose settings it measured is not a verdict.
428
+ */
429
+ export function checkAnchorIdentity(text: string | null, repo: string): AnchorFinding[] {
430
+ if (text === null) {
431
+ return [];
432
+ }
433
+ let parsed: unknown;
434
+ try {
435
+ parsed = JSON.parse(text);
436
+ } catch {
437
+ return [];
438
+ }
439
+ if (!isObject(parsed)) {
440
+ return [];
441
+ }
442
+ const f: AnchorFinding[] = [];
443
+ const version = parsed['schema_version'];
444
+ if (version !== SUPPORTED_ANCHOR_SCHEMA) {
445
+ f.push({
446
+ code: 'policy-missing-field',
447
+ message:
448
+ `src/config/platform-anchor.json declares \`schema_version\` ` +
449
+ `${JSON.stringify(version)}; this reader supports ${SUPPORTED_ANCHOR_SCHEMA}. ` +
450
+ 'A schema bump must land with the reader that understands it.',
451
+ });
452
+ }
453
+ const declared = parsed['repository'];
454
+ if (typeof declared === 'string' && declared !== repo) {
455
+ f.push({
456
+ code: 'policy-repository-mismatch',
457
+ message:
458
+ `src/config/platform-anchor.json states the expectation for \`${declared}\`, ` +
459
+ `but this run measured \`${repo}\`. A pass here would credit one repository's ` +
460
+ "settings to another's expectation — most likely a fork, or a --repo argument " +
461
+ 'that does not match the checkout.',
462
+ });
463
+ }
464
+ return f;
465
+ }
466
+
467
+ /**
468
+ * Reject a policy weaker than the floor.
469
+ *
470
+ * This is what stops the expectation file from being the weak link: the file is
471
+ * on the gated-surface list so an edit to it needs its own ratification, and
472
+ * even a ratified edit cannot go below these values without changing this code —
473
+ * which is itself a gated surface.
474
+ */
475
+ export function enforceFloor(policy: AnchorPolicy): AnchorFinding[] {
476
+ const f: AnchorFinding[] = [];
477
+ const under = (what: string, got: unknown, floorValue: unknown): void => {
478
+ f.push({
479
+ code: 'policy-below-floor',
480
+ message:
481
+ `src/config/platform-anchor.json sets \`${what}\` to ${JSON.stringify(got)}, ` +
482
+ `below the non-negotiable floor ${JSON.stringify(floorValue)} in ` +
483
+ 'src/scripts/_lib/platform_anchor.ts. A policy edit may not lower the floor.',
484
+ });
485
+ };
486
+
487
+ // The selectors first, because a weakened selector makes every threshold
488
+ // below it moot — it changes which rulesets are read rather than how they
489
+ // are judged, so a policy can neutralise the check without touching a
490
+ // single floored threshold.
491
+ if (policy.enforcement !== NON_NEGOTIABLE_FLOOR.enforcement) {
492
+ under('enforcement', policy.enforcement, NON_NEGOTIABLE_FLOOR.enforcement);
493
+ }
494
+ if (policy.target !== NON_NEGOTIABLE_FLOOR.target) {
495
+ under('target', policy.target, NON_NEGOTIABLE_FLOOR.target);
496
+ }
497
+ if (!policy.covers_default_branch) {
498
+ under('covers_default_branch', policy.covers_default_branch, true);
499
+ }
500
+
501
+ if (!policy.required_review_thread_resolution) {
502
+ under('required_review_thread_resolution', false, true);
503
+ }
504
+ if (!policy.block_deletion) {
505
+ under('block_deletion', false, true);
506
+ }
507
+ if (!policy.block_non_fast_forward) {
508
+ under('block_non_fast_forward', false, true);
509
+ }
510
+ if (policy.allow_unconditional_bypass) {
511
+ under('allow_unconditional_bypass', true, false);
512
+ }
513
+ if (
514
+ policy.required_status_check_contexts.length < NON_NEGOTIABLE_FLOOR.minimum_required_contexts
515
+ ) {
516
+ under(
517
+ 'required_status_check_contexts.length',
518
+ policy.required_status_check_contexts.length,
519
+ NON_NEGOTIABLE_FLOOR.minimum_required_contexts,
520
+ );
521
+ }
522
+ return f;
523
+ }
524
+
525
+ /**
526
+ * Match one ruleset ref pattern against a concrete ref.
527
+ *
528
+ * The forge treats these as fnmatch patterns, not literals, and comparing them
529
+ * with `===` was the one fail-OPEN direction in this evaluator: a blind review
530
+ * probed `exclude: ["refs/heads/m*"]` under `include: ["~ALL"]` and the equality
531
+ * form returned "covered" for `main`, crediting a ruleset that in fact excludes
532
+ * it. The mirror error also existed — `include: ["refs/heads/**"]` read as no
533
+ * match at all.
534
+ *
535
+ * `**` crosses `/`; `*` and `?` stay inside one segment. Everything else is
536
+ * escaped, so a pattern with regex metacharacters cannot widen itself.
537
+ */
538
+ export function refPatternMatches(pattern: string, ref: string): boolean {
539
+ let re = '';
540
+ for (let i = 0; i < pattern.length; i += 1) {
541
+ const c = pattern[i] ?? '';
542
+ if (c === '*') {
543
+ if (pattern[i + 1] === '*') {
544
+ re += '.*';
545
+ i += 1;
546
+ } else {
547
+ re += '[^/]*';
548
+ }
549
+ } else if (c === '?') {
550
+ re += '[^/]';
551
+ } else {
552
+ re += c.replace(/[.+^${}()|[\]\\]/g, '\\$&');
553
+ }
554
+ }
555
+ try {
556
+ return new RegExp(`^${re}$`).test(ref);
557
+ } catch {
558
+ return false;
559
+ }
560
+ }
561
+
562
+ /**
563
+ * True when a ruleset's ref conditions reach the default branch.
564
+ *
565
+ * An empty `include` is NOT a match: a ruleset that names no ref covers no ref,
566
+ * and reading absence as "everything" would be the fail-open reading again.
567
+ */
568
+ export function coversDefaultBranch(rs: RulesetDetail, defaultBranch: string): boolean {
569
+ const include = rs.conditions?.ref_name?.include ?? [];
570
+ const exclude = rs.conditions?.ref_name?.exclude ?? [];
571
+ const full = `refs/heads/${defaultBranch}`;
572
+ const hits = (p: string): boolean =>
573
+ p === '~DEFAULT_BRANCH' ||
574
+ p === '~ALL' ||
575
+ refPatternMatches(p, full) ||
576
+ refPatternMatches(p, defaultBranch);
577
+ if (exclude.some(hits)) {
578
+ return false;
579
+ }
580
+ return include.some(hits);
581
+ }
582
+
583
+ function ruleParams(rs: RulesetDetail, type: string): Record<string, unknown> | null {
584
+ for (const r of rs.rules ?? []) {
585
+ if (r.type === type) {
586
+ return isObject(r.parameters) ? r.parameters : {};
587
+ }
588
+ }
589
+ return null;
590
+ }
591
+
592
+ /**
593
+ * Union every applicable active ruleset into one effective configuration.
594
+ *
595
+ * Booleans OR, the approval count takes the maximum, contexts union: a second
596
+ * ruleset can only ever ADD protection, which is how GitHub evaluates them and
597
+ * is why reading one ruleset by id would understate the real protection as
598
+ * easily as it overstates it.
599
+ */
600
+ export function effectiveProtection(
601
+ rulesets: readonly RulesetDetail[],
602
+ defaultBranch: string,
603
+ policy: AnchorPolicy,
604
+ ): EffectiveProtection {
605
+ const eff: EffectiveProtection = {
606
+ rulesetIds: [],
607
+ approvingReviews: 0,
608
+ requireLastPushApproval: false,
609
+ requireThreadResolution: false,
610
+ blocksDeletion: false,
611
+ blocksNonFastForward: false,
612
+ strictStatusChecks: false,
613
+ requiredContexts: [],
614
+ unconditionalBypassActors: [],
615
+ };
616
+ for (const rs of rulesets) {
617
+ if (rs.enforcement !== policy.enforcement) {
618
+ continue;
619
+ }
620
+ if (rs.target !== policy.target) {
621
+ continue;
622
+ }
623
+ if (policy.covers_default_branch && !coversDefaultBranch(rs, defaultBranch)) {
624
+ continue;
625
+ }
626
+ eff.rulesetIds.push(rs.id ?? -1);
627
+
628
+ const pr = ruleParams(rs, 'pull_request');
629
+ if (pr !== null) {
630
+ const n = Number(pr['required_approving_review_count'] ?? 0);
631
+ if (Number.isFinite(n) && n > eff.approvingReviews) {
632
+ eff.approvingReviews = n;
633
+ }
634
+ eff.requireLastPushApproval ||= pr['require_last_push_approval'] === true;
635
+ eff.requireThreadResolution ||= pr['required_review_thread_resolution'] === true;
636
+ }
637
+ eff.blocksDeletion ||= ruleParams(rs, 'deletion') !== null;
638
+ eff.blocksNonFastForward ||= ruleParams(rs, 'non_fast_forward') !== null;
639
+
640
+ const sc = ruleParams(rs, 'required_status_checks');
641
+ if (sc !== null) {
642
+ eff.strictStatusChecks ||= sc['strict_required_status_checks_policy'] === true;
643
+ const list = sc['required_status_checks'];
644
+ if (Array.isArray(list)) {
645
+ for (const entry of list) {
646
+ if (isObject(entry) && typeof entry['context'] === 'string') {
647
+ if (!eff.requiredContexts.includes(entry['context'])) {
648
+ eff.requiredContexts.push(entry['context']);
649
+ }
650
+ }
651
+ }
652
+ }
653
+ }
654
+ for (const a of rs.bypass_actors ?? []) {
655
+ if (a.bypass_mode === 'always') {
656
+ eff.unconditionalBypassActors.push(a);
657
+ }
658
+ }
659
+ }
660
+ return eff;
661
+ }
662
+
663
+ const WAIVER_FIELDS: readonly (keyof AcceptedRiskWaiver)[] = [
664
+ 'dimension',
665
+ 'baseline',
666
+ 'accepted',
667
+ 'failure_mode',
668
+ 'cost_avoided',
669
+ 'frequency_assumption',
670
+ 'detection_and_repair',
671
+ 'residual_protection',
672
+ 'authority',
673
+ 'decided',
674
+ 'expires',
675
+ 'review_triggers',
676
+ 'id',
677
+ ];
678
+
679
+ export interface WaiverReading {
680
+ /** Keyed by dimension. Only complete, permitted, unexpired waivers appear. */
681
+ honoured: Map<string, AcceptedRiskWaiver>;
682
+ findings: AnchorFinding[];
683
+ }
684
+
685
+ /** The only values `authority` may carry. A floor trade is the owner's alone. */
686
+ export const WAIVER_AUTHORITIES: readonly string[] = ['owner'];
687
+
688
+ /**
689
+ * Read the waivers, refusing every one that is not fully in order.
690
+ *
691
+ * A refused waiver is absent from `honoured`, so the dimension it meant to
692
+ * cover is judged normally — AND its refusal is a finding the caller must merge
693
+ * into the verdict. Both halves are required, and the second was missing:
694
+ * a blind review reproduced the inversion end to end. The refusals were printed
695
+ * and never reached the exit code, so an EXPIRED waiver over a dimension the
696
+ * forge happened to satisfy left the gate green, while a WELL-FORMED waiver for
697
+ * the same satisfied dimension reded via `waiver-unused`. Malformed was strictly
698
+ * better than correct — the exact thing this function's contract forbids, and
699
+ * reachable by an ordinary lapsed expiry rather than only by malice.
700
+ *
701
+ * `evaluateAnchor` now takes the whole reading rather than just the map, so the
702
+ * findings cannot be dropped at a call site again.
703
+ */
704
+ export function readWaivers(text: string | null, now: Date): WaiverReading {
705
+ const honoured = new Map<string, AcceptedRiskWaiver>();
706
+ const findings: AnchorFinding[] = [];
707
+ if (text === null) {
708
+ return { honoured, findings };
709
+ }
710
+ let parsed: unknown;
711
+ try {
712
+ parsed = JSON.parse(text);
713
+ } catch {
714
+ return { honoured, findings };
715
+ }
716
+ if (!isObject(parsed) || parsed['accepted_risk_reductions'] === undefined) {
717
+ return { honoured, findings };
718
+ }
719
+ const list = parsed['accepted_risk_reductions'];
720
+ if (!Array.isArray(list)) {
721
+ findings.push({
722
+ code: 'waiver-incomplete',
723
+ message: '`accepted_risk_reductions` must be an array of waiver objects.',
724
+ });
725
+ return { honoured, findings };
726
+ }
727
+
728
+ for (const entry of list) {
729
+ if (!isObject(entry)) {
730
+ findings.push({
731
+ code: 'waiver-incomplete',
732
+ message: 'a waiver entry is not an object.',
733
+ });
734
+ continue;
735
+ }
736
+ const id = typeof entry['id'] === 'string' ? entry['id'] : '<no id>';
737
+
738
+ // Presence AND substance. Presence-only validation honoured an empty
739
+ // string and a null in every prose field, which is an undocumented
740
+ // deviation wearing thirteen keys — the blind review filed it as its
741
+ // own finding and it is the same defect class as the refusals not
742
+ // reaching the verdict: a check that reads a field without judging it.
743
+ const missing = WAIVER_FIELDS.filter((f) => {
744
+ if (!(f in entry)) {
745
+ return true;
746
+ }
747
+ const v = entry[f];
748
+ if (f === 'review_triggers') {
749
+ return !Array.isArray(v) || v.length === 0 || v.some((x) => String(x).trim() === '');
750
+ }
751
+ if (f === 'baseline' || f === 'accepted') {
752
+ return typeof v !== 'boolean';
753
+ }
754
+ return typeof v !== 'string' || v.trim() === '';
755
+ });
756
+ if (missing.length > 0) {
757
+ findings.push({
758
+ code: 'waiver-incomplete',
759
+ message:
760
+ `waiver \`${id}\` is missing or empty at ${missing.join(', ')}. An incomplete ` +
761
+ 'waiver is an undocumented deviation, so it is refused rather than honoured ' +
762
+ 'and the dimension it names is judged normally.',
763
+ });
764
+ continue;
765
+ }
766
+ if (!WAIVER_AUTHORITIES.includes(String(entry['authority']))) {
767
+ findings.push({
768
+ code: 'waiver-not-permitted',
769
+ message:
770
+ `waiver \`${id}\` claims authority \`${String(entry['authority'])}\`, which is ` +
771
+ `not one of ${WAIVER_AUTHORITIES.join(', ')}. Trading a baseline control is ` +
772
+ 'the owner\'s decision; a waiver that names anything else — an agent, a ' +
773
+ 'council, a session — is refused. The field was presence-checked and never ' +
774
+ 'value-checked, so `agent-self-service` was honoured while four prose ' +
775
+ 'surfaces rested on "only the owner may authorize a floor reduction".',
776
+ });
777
+ continue;
778
+ }
779
+ if (entry['baseline'] === entry['accepted']) {
780
+ findings.push({
781
+ code: 'waiver-incomplete',
782
+ message:
783
+ `waiver \`${id}\` records the same value for \`baseline\` and \`accepted\`, so ` +
784
+ 'it waives nothing. Both were printed as record and neither was checked.',
785
+ });
786
+ continue;
787
+ }
788
+ const dimension = String(entry['dimension']);
789
+ if (!(POLICY_FIELDS as readonly string[]).includes(dimension)) {
790
+ findings.push({
791
+ code: 'waiver-not-permitted',
792
+ message:
793
+ `waiver \`${id}\` names \`${dimension}\`, which is not a dimension of the ` +
794
+ 'expectation. A typo was previously honoured and then mis-diagnosed as an ' +
795
+ 'unused waiver, which sends a reader looking for the wrong problem.',
796
+ });
797
+ continue;
798
+ }
799
+ if (honoured.has(dimension)) {
800
+ findings.push({
801
+ code: 'waiver-not-permitted',
802
+ message:
803
+ `waiver \`${id}\` is a second entry for \`${dimension}\`. Duplicates used to ` +
804
+ 'resolve last-wins, so which record governed depended on file order.',
805
+ });
806
+ continue;
807
+ }
808
+ if (Number.isNaN(new Date(String(entry['decided'])).getTime())) {
809
+ findings.push({
810
+ code: 'waiver-incomplete',
811
+ message: `waiver \`${id}\` has an unparseable \`decided\`; it must be an ISO date.`,
812
+ });
813
+ continue;
814
+ }
815
+ if (NEVER_WAIVABLE.includes(dimension)) {
816
+ findings.push({
817
+ code: 'waiver-not-permitted',
818
+ message:
819
+ `waiver \`${id}\` names \`${dimension}\`, which no waiver may reach. A ruleset ` +
820
+ 'that is not active, does not cover the branch, or lets an actor bypass it ' +
821
+ 'unconditionally makes every other assertion here meaningless, and that is ' +
822
+ 'not tradeable against an operational saving.',
823
+ });
824
+ continue;
825
+ }
826
+ const expires = new Date(String(entry['expires']));
827
+ if (Number.isNaN(expires.getTime())) {
828
+ findings.push({
829
+ code: 'waiver-incomplete',
830
+ message: `waiver \`${id}\` has an unparseable \`expires\`; it must be an ISO date.`,
831
+ });
832
+ continue;
833
+ }
834
+ if (expires.getTime() <= now.getTime()) {
835
+ findings.push({
836
+ code: 'waiver-expired',
837
+ message:
838
+ `waiver \`${id}\` expired on ${String(entry['expires'])}. Renewal is a fresh ` +
839
+ 'decision with refreshed measurements rather than an extension — the premise ' +
840
+ 'it rests on is a cost, and a cost is not a constant.',
841
+ });
842
+ continue;
843
+ }
844
+ honoured.set(dimension, entry as unknown as AcceptedRiskWaiver);
845
+ }
846
+ return { honoured, findings };
847
+ }
848
+
849
+ /**
850
+ * The verdict.
851
+ *
852
+ * `rulesets === null` means the caller could not read them — network, token
853
+ * scope, offline — and yields `unverifiable`, never `compliant`. That is the
854
+ * fail-closed direction both council seats required, and its blast radius is
855
+ * only ever a diff on the gated surface, because the caller runs this check
856
+ * exclusively when `requiresRatification` is true.
857
+ *
858
+ * A dimension covered by an honoured waiver does not become a finding; it is
859
+ * reported as a known accepted risk and the status becomes
860
+ * `compliant-with-accepted-risk`, which is neither a pass nor a failure. An
861
+ * unused waiver is itself reported: a waiver for a dimension the forge now
862
+ * satisfies is stale record, and stale records are how a reader learns to
863
+ * distrust the whole file.
864
+ */
865
+ export function evaluateAnchor(
866
+ policy: AnchorPolicy,
867
+ rulesets: readonly RulesetDetail[] | null,
868
+ defaultBranch: string | null,
869
+ waiverReading: WaiverReading = { honoured: new Map(), findings: [] },
870
+ ): AnchorReading {
871
+ // Seeded, not appended later. The refusals ARE part of the verdict, and a
872
+ // caller that received only the map could drop them — which is the shape
873
+ // that produced the inversion this parameter now prevents.
874
+ const findings: AnchorFinding[] = [...waiverReading.findings];
875
+ const waivers = waiverReading.honoured;
876
+ const evidence: string[] = [];
877
+ const accepted: string[] = [];
878
+ const used = new Set<string>();
879
+
880
+ /**
881
+ * Record a violated dimension as a finding, or as an accepted risk.
882
+ *
883
+ * The waiver check happens HERE rather than at the end, so a waived
884
+ * dimension never enters the findings list at all — filtering findings
885
+ * afterwards would leave the two states one refactor apart.
886
+ */
887
+ const violated = (dimension: string, finding: AnchorFinding): void => {
888
+ const w = waivers.get(dimension);
889
+ if (w === undefined) {
890
+ findings.push(finding);
891
+ return;
892
+ }
893
+ used.add(dimension);
894
+ accepted.push(
895
+ `${dimension}: baseline ${String(w.baseline)}, accepted ${String(w.accepted)} — ` +
896
+ `${w.id}, ${w.authority} ${w.decided}, expires ${w.expires}. ` +
897
+ `Residual: ${w.residual_protection}`,
898
+ );
899
+ };
900
+
901
+ if (rulesets === null) {
902
+ return {
903
+ status: 'unverifiable',
904
+ findings: [
905
+ ...findings,
906
+ {
907
+ code: 'rulesets-unreadable',
908
+ message:
909
+ 'the repository rulesets could not be read (network, credentials, token ' +
910
+ 'scope, or an offline run). The platform anchor is UNVERIFIABLE, which is ' +
911
+ 'a failure and not a pass: a control that succeeds when it cannot measure ' +
912
+ 'is advisory.',
913
+ },
914
+ ],
915
+ evidence,
916
+ };
917
+ }
918
+ if (defaultBranch === null) {
919
+ return {
920
+ status: 'unverifiable',
921
+ findings: [
922
+ ...findings,
923
+ {
924
+ code: 'default-branch-unknown',
925
+ message:
926
+ 'the default branch could not be resolved, so no ruleset can be matched ' +
927
+ 'against it.',
928
+ },
929
+ ],
930
+ evidence,
931
+ };
932
+ }
933
+
934
+ const eff = effectiveProtection(rulesets, defaultBranch, policy);
935
+ evidence.push(
936
+ `default branch: ${defaultBranch} · applicable active ${policy.target} ruleset(s): ` +
937
+ (eff.rulesetIds.length > 0 ? eff.rulesetIds.join(', ') : 'none'),
938
+ );
939
+
940
+ if (eff.rulesetIds.length === 0) {
941
+ findings.push({
942
+ code: 'no-applicable-ruleset',
943
+ message:
944
+ `no ruleset with target \`${policy.target}\` and enforcement ` +
945
+ `\`${policy.enforcement}\` covers \`${defaultBranch}\`. Note that classic branch ` +
946
+ 'protection is a different API surface and is not read here; if this repository ' +
947
+ 'moved back to it, src/config/platform-anchor.json needs correcting rather than ' +
948
+ 'this result overriding.',
949
+ });
950
+ return { status: 'noncompliant', findings, evidence };
951
+ }
952
+
953
+ // Observed and reported, never judged. The two approval dimensions left the
954
+ // enforced set with the trust-model decision above, and deleting the
955
+ // MEASUREMENT with the requirement would have been the wrong half to drop:
956
+ // a reader still wants to see what the forge actually says, and a number
957
+ // that is reported without being a threshold cannot quietly become one.
958
+ evidence.push(
959
+ `approving reviews required: ${eff.approvingReviews} · last-push approval: ` +
960
+ `${eff.requireLastPushApproval} — observed, not required here (see NON_NEGOTIABLE_FLOOR)`,
961
+ );
962
+ if (policy.required_review_thread_resolution && !eff.requireThreadResolution) {
963
+ violated('required_review_thread_resolution', {
964
+ code: 'thread-resolution-missing',
965
+ message: '`required_review_thread_resolution` is off, so an open objection cannot block a merge.',
966
+ });
967
+ }
968
+ if (policy.block_deletion && !eff.blocksDeletion) {
969
+ violated('block_deletion', {
970
+ code: 'deletion-not-blocked',
971
+ message: 'the default branch is not protected against deletion.',
972
+ });
973
+ }
974
+ if (policy.block_non_fast_forward && !eff.blocksNonFastForward) {
975
+ violated('block_non_fast_forward', {
976
+ code: 'non-fast-forward-not-blocked',
977
+ message: 'the default branch is not protected against force-pushes (non-fast-forward).',
978
+ });
979
+ }
980
+ if (policy.strict_required_status_checks && !eff.strictStatusChecks) {
981
+ violated('strict_required_status_checks', {
982
+ code: 'status-checks-not-strict',
983
+ message:
984
+ '`strict_required_status_checks_policy` is off, so a branch may merge on checks ' +
985
+ 'that passed against a base it has not caught up with. The staleness is not ' +
986
+ 'bounded to one commit: any number of merges may have landed since, so two ' +
987
+ 'independently green branches can compose into a broken base.',
988
+ });
989
+ }
990
+ evidence.push(
991
+ `required status check contexts: ` +
992
+ (eff.requiredContexts.length > 0 ? eff.requiredContexts.join(' · ') : 'none'),
993
+ );
994
+ for (const want of policy.required_status_check_contexts) {
995
+ if (!eff.requiredContexts.includes(want)) {
996
+ findings.push({
997
+ code: 'required-context-missing',
998
+ message:
999
+ `\`${want}\` is not a required status check. That context is the job which ` +
1000
+ 'carries the ratification gate, so without it a branch can merge without the ' +
1001
+ 'gate having run at all.',
1002
+ });
1003
+ }
1004
+ }
1005
+ if (!policy.allow_unconditional_bypass && eff.unconditionalBypassActors.length > 0) {
1006
+ const who = eff.unconditionalBypassActors
1007
+ .map((a) => `${a.actor_type ?? '?'}#${a.actor_id ?? '?'}`)
1008
+ .join(', ');
1009
+ evidence.push(`unconditional bypass actors: ${who}`);
1010
+ findings.push({
1011
+ code: 'unconditional-bypass',
1012
+ message:
1013
+ `${who} may bypass every rule above unconditionally (\`bypass_mode: always\`), so ` +
1014
+ 'each rule is advisory for that actor. The owner ruled on 2026-09-10 that ' +
1015
+ 'administrators are the root of trust here, and that ruling REMOVED the approval ' +
1016
+ 'dimensions rather than this one: an unconditional bypass actor is still a ' +
1017
+ 'failure and no waiver may reach it. See `owner_ruling_2026_09_10` in ' +
1018
+ 'src/config/platform-anchor.json.',
1019
+ });
1020
+ }
1021
+
1022
+ // A waiver nobody needed is stale record, and stale records teach a reader
1023
+ // to distrust the file they sit in. It IS a finding and therefore reds —
1024
+ // the comment here used to say "deliberately NOT a failure" while the code
1025
+ // returned noncompliant, which a blind review filed as its own
1026
+ // contradiction. Kept as a failure and the comment corrected to match: the
1027
+ // repair is deleting one stale entry, and leaving it green would let the
1028
+ // file drift out of step with the forge unnoticed.
1029
+ for (const [dimension, w] of waivers) {
1030
+ if (!used.has(dimension)) {
1031
+ findings.push({
1032
+ code: 'waiver-unused',
1033
+ message:
1034
+ `waiver \`${w.id}\` covers \`${dimension}\`, which the forge now satisfies. ` +
1035
+ 'The waiver is stale and should be removed — the risk it accepted is not ' +
1036
+ 'being taken.',
1037
+ });
1038
+ }
1039
+ }
1040
+
1041
+ for (const a of accepted) {
1042
+ evidence.push(`ACCEPTED RISK — ${a}`);
1043
+ }
1044
+
1045
+ if (findings.length > 0) {
1046
+ return { status: 'noncompliant', findings, evidence };
1047
+ }
1048
+ return {
1049
+ status: accepted.length > 0 ? 'compliant-with-accepted-risk' : 'compliant',
1050
+ findings,
1051
+ evidence,
1052
+ };
1053
+ }