@event4u/agent-config 15.0.0 → 16.1.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 (283) 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 +301 -347
  5. package/CONTRIBUTING.md +9 -0
  6. package/README.md +2 -2
  7. package/dist/agent-src/commands/analyze/inbox.md +26 -0
  8. package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
  9. package/dist/agent-src/commands/challenge-me/closure.md +178 -0
  10. package/dist/agent-src/commands/challenge-me.md +9 -2
  11. package/dist/agent-src/commands/council/default.md +25 -5
  12. package/dist/agent-src/commands/feature/plan.md +28 -1
  13. package/dist/agent-src/commands/feature/roadmap.md +28 -1
  14. package/dist/agent-src/commands/implement-ticket.md +27 -0
  15. package/dist/agent-src/commands/jira-ticket.md +27 -0
  16. package/dist/agent-src/commands/pr/create.md +29 -0
  17. package/dist/agent-src/commands/pr/merge.md +60 -9
  18. package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
  19. package/dist/agent-src/commands/roadmap/create.md +77 -29
  20. package/dist/agent-src/commands/roadmap/materialize.md +27 -0
  21. package/dist/agent-src/commands/roadmap/process-full.md +123 -18
  22. package/dist/agent-src/commands/team.md +22 -0
  23. package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
  24. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +97 -0
  25. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
  26. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +15 -15
  27. package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
  28. package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
  29. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
  30. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
  31. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  32. package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  33. package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
  34. package/dist/agent-src/rules/autonomous-execution.md +15 -8
  35. package/dist/agent-src/rules/delegation-policy.md +23 -45
  36. package/dist/agent-src/rules/design-fidelity.md +32 -37
  37. package/dist/agent-src/rules/evaluator-independence.md +43 -49
  38. package/dist/agent-src/rules/test-first.md +41 -0
  39. package/dist/agent-src/rules/user-interaction.md +31 -30
  40. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  41. package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
  42. package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
  43. package/dist/agent-src/skills/database/data/manifest.json +4 -4
  44. package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
  45. package/dist/agent-src/skills/design-review/SKILL.md +50 -0
  46. package/dist/agent-src/skills/error-handling-patterns/SKILL.md +9 -0
  47. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +14 -4
  48. package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
  49. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  50. package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
  51. package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
  52. package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
  53. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
  54. package/dist/agent-src/templates/agent-settings.md +1 -1
  55. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  56. package/dist/agent-src/templates/roadmaps.md +188 -0
  57. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  58. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  59. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  60. package/dist/cli-delegate/check_update_banner.js +40 -2
  61. package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
  62. package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
  63. package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
  64. package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
  65. package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
  66. package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-FXYUYU4N.js} +57 -13
  67. package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-GJCBB2UG.js} +168 -10
  68. package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-HDVVGPQW.js} +873 -539
  69. package/dist/cli-delegate/chunk-NUHLI2RX.js +80 -0
  70. package/dist/cli-delegate/{chunk-HZ52BRGG.js → chunk-P6ZHP5W2.js} +15 -90
  71. package/dist/cli-delegate/{chunk-TYSY33MD.js → chunk-QJWAUMJW.js} +12 -2
  72. package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
  73. package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
  74. package/dist/cli-delegate/{chunk-2KKXJW25.js → chunk-S3VFXMTM.js} +1 -1
  75. package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-UNCRJBT3.js} +31 -7
  76. package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
  77. package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
  78. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  79. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  80. package/dist/cli-delegate/cmd_conformance.js +918 -325
  81. package/dist/cli-delegate/cmd_doctor.js +7 -5
  82. package/dist/cli-delegate/cmd_explain.js +5 -3
  83. package/dist/cli-delegate/cmd_export.js +9 -7
  84. package/dist/cli-delegate/cmd_fleet.js +4 -2
  85. package/dist/cli-delegate/cmd_handoff.js +6 -6
  86. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  87. package/dist/cli-delegate/cmd_migrate.js +2 -2
  88. package/dist/cli-delegate/cmd_packs_active.js +3 -2
  89. package/dist/cli-delegate/cmd_preflight.js +4 -2
  90. package/dist/cli-delegate/cmd_prune.js +10 -8
  91. package/dist/cli-delegate/cmd_route_audit.js +2 -2
  92. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  93. package/dist/cli-delegate/cmd_session_recycle.js +4 -4
  94. package/dist/cli-delegate/cmd_settings_get.js +28 -2
  95. package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
  96. package/dist/cli-delegate/cmd_settings_set.js +14 -1
  97. package/dist/cli-delegate/cmd_sync.js +10 -8
  98. package/dist/cli-delegate/cmd_uninstall.js +16 -13
  99. package/dist/cli-delegate/cmd_update.js +1 -1
  100. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  101. package/dist/cli-delegate/cmd_validate.js +12 -10
  102. package/dist/cli-delegate/cmd_versions.js +1 -1
  103. package/dist/discovery/deprecation-report.md +1 -1
  104. package/dist/discovery/discovery-manifest.json +87 -40
  105. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  106. package/dist/discovery/discovery-manifest.summary.md +4 -4
  107. package/dist/discovery/dormancy-report.md +1 -1
  108. package/dist/discovery/orphan-report.md +1 -1
  109. package/dist/discovery/packs.json +10 -8
  110. package/dist/discovery/trust-report.md +3 -3
  111. package/dist/discovery/workspaces.json +8 -6
  112. package/dist/hooks/dispatch.js +2601 -1073
  113. package/dist/install/claudeRuleRewrite.js +14 -3
  114. package/dist/install/conflict.js +60 -10
  115. package/dist/install/conflictTracker.js +218 -0
  116. package/dist/install/fsPrimitives.js +54 -0
  117. package/dist/install/install.mjs +2008 -1481
  118. package/dist/install/manifestFiles.js +259 -0
  119. package/dist/install/preflight.js +14 -2
  120. package/dist/install/preserve.js +157 -0
  121. package/dist/install/recordedOwnership.js +132 -0
  122. package/dist/install/txlog.js +11 -2
  123. package/dist/mcp/registry-manifest.json +3 -3
  124. package/dist/mcp/server.json +2 -2
  125. package/dist/mcp/server.mjs +1612 -667
  126. package/dist/router.json +1 -1
  127. package/dist/scripts/_lib/agent_settings.js +3 -2
  128. package/dist/scripts/_lib/fs_atomic.js +167 -0
  129. package/dist/scripts/_lib/installed_lock.js +1 -1
  130. package/dist/scripts/_lib/installed_tools.js +460 -0
  131. package/dist/scripts/_lib/lean_projection_mode.js +3 -2
  132. package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
  133. package/dist/server/io/yamlIO.js +50 -0
  134. package/dist/server/routes/install.js +8 -0
  135. package/dist/server/routes/wizard.js +9 -3
  136. package/dist/server/schemas/settings.js +17 -5
  137. package/dist/shared/missionExecution.js +97 -0
  138. package/dist/ui/assets/{index-D0jaCRPM.js → index-DH1xc62v.js} +17 -17
  139. package/dist/ui/index.html +1 -1
  140. package/docs/MIGRATION.md +103 -0
  141. package/docs/contracts/settings-classes.md +26 -7
  142. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  143. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  144. package/docs/guidelines/design-fidelity-routing.md +29 -0
  145. package/package.json +1 -1
  146. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  147. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  148. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  149. package/src/config/agent-settings.template.yml +139 -27
  150. package/src/config/auto-rule-budget.json +16 -0
  151. package/src/config/ci-local-parity.yml +18 -5
  152. package/src/config/continuity-surface.json +28 -0
  153. package/src/config/gate-coverage.yml +146 -13
  154. package/src/config/gate-violation-baselines.json +6 -6
  155. package/src/config/hook-token-budget.json +2 -0
  156. package/src/config/host-injection-effect.json +1 -1
  157. package/src/config/loop-surfaces.yaml +215 -0
  158. package/src/config/platform-anchor.json +3 -2
  159. package/src/config/preamble-payload-budget.json +12 -7
  160. package/src/config/preamble-payload-exceptions.json +28 -0
  161. package/src/config/release-gate-locality.yml +11 -0
  162. package/src/config/routing-coverage-seed.json +18 -4
  163. package/src/config/rule-enforcement-baseline.json +0 -1
  164. package/src/install/claudeRuleRewrite.ts +16 -2
  165. package/src/install/conflict.ts +65 -9
  166. package/src/install/conflictTracker.ts +299 -0
  167. package/src/install/fsPrimitives.ts +54 -0
  168. package/src/install/manifestFiles.ts +312 -0
  169. package/src/install/preflight.ts +16 -2
  170. package/src/install/preserve.ts +205 -0
  171. package/src/install/recordedOwnership.ts +149 -0
  172. package/src/install/txlog.ts +11 -2
  173. package/src/install/types.ts +19 -4
  174. package/src/scripts/_cli/cmd_conformance.ts +88 -15
  175. package/src/scripts/_cli/cmd_doctor.ts +4 -3
  176. package/src/scripts/_cli/cmd_route_explain.ts +9 -2
  177. package/src/scripts/_cli/cmd_settings_get.ts +44 -0
  178. package/src/scripts/_cli/doctor_execution.ts +90 -0
  179. package/src/scripts/_dispatch.bash +3 -1
  180. package/src/scripts/_lib/activation_payload.ts +247 -0
  181. package/src/scripts/_lib/agent_settings.ts +4 -2
  182. package/src/scripts/_lib/authority_path.ts +102 -0
  183. package/src/scripts/_lib/base_ref_payload.ts +315 -0
  184. package/src/scripts/_lib/cascade_base.ts +94 -0
  185. package/src/scripts/_lib/changelog_eras.ts +7 -2
  186. package/src/scripts/_lib/continuation_ladder.ts +149 -2
  187. package/src/scripts/_lib/council_transport.ts +129 -0
  188. package/src/scripts/_lib/delivery_ready.ts +112 -0
  189. package/src/scripts/_lib/dropped_decision.ts +142 -0
  190. package/src/scripts/_lib/forge_protection.ts +267 -0
  191. package/src/scripts/_lib/hook_platform_keys.ts +27 -0
  192. package/src/scripts/_lib/installed_lock.ts +1 -1
  193. package/src/scripts/_lib/json_python_parity.ts +127 -0
  194. package/src/scripts/_lib/lean_projection_mode.ts +14 -3
  195. package/src/scripts/_lib/loop_surfaces.ts +382 -0
  196. package/src/scripts/_lib/measured_payload_ceiling.ts +424 -0
  197. package/src/scripts/_lib/mission_record.ts +190 -0
  198. package/src/scripts/_lib/obligation_frequency.ts +217 -0
  199. package/src/scripts/_lib/obligations.ts +402 -0
  200. package/src/scripts/_lib/orchestration_record.ts +11 -20
  201. package/src/scripts/_lib/payload_catalogue_completeness.ts +211 -0
  202. package/src/scripts/_lib/release_findings_ingest.ts +324 -0
  203. package/src/scripts/_lib/release_highlights.ts +199 -10
  204. package/src/scripts/_lib/release_holds.ts +493 -0
  205. package/src/scripts/_lib/release_migration_gate.ts +184 -0
  206. package/src/scripts/_lib/release_tag_dispatch.ts +63 -0
  207. package/src/scripts/_lib/review_baseline.ts +254 -0
  208. package/src/scripts/_lib/review_skipped_record.ts +61 -1
  209. package/src/scripts/_lib/rides_along.ts +102 -0
  210. package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
  211. package/src/scripts/_lib/standing_bound_ratchet.ts +216 -103
  212. package/src/scripts/_lib/test_delta.ts +166 -0
  213. package/src/scripts/_lib/test_provenance.ts +171 -0
  214. package/src/scripts/_lib/turn_end_refusals.ts +51 -4
  215. package/src/scripts/_lib/typed_op_grant.ts +246 -0
  216. package/src/scripts/_lib/typed_op_watch.ts +185 -0
  217. package/src/scripts/ai_council/config.ts +8 -11
  218. package/src/scripts/ai_council/fallback_config.ts +5 -3
  219. package/src/scripts/ai_council/ownership_classes.ts +118 -0
  220. package/src/scripts/ask_block_census.ts +285 -15
  221. package/src/scripts/check_beta_review_markers.ts +35 -4
  222. package/src/scripts/check_enforcement_matrix.ts +624 -0
  223. package/src/scripts/check_finding_dispositions.ts +167 -16
  224. package/src/scripts/check_gate_reachability.ts +173 -10
  225. package/src/scripts/check_generator_sync.ts +954 -0
  226. package/src/scripts/check_held_object_arrivals.ts +381 -0
  227. package/src/scripts/check_preamble_payload_budget.ts +474 -124
  228. package/src/scripts/check_release_highlights.ts +33 -1
  229. package/src/scripts/check_release_holds.ts +567 -0
  230. package/src/scripts/check_release_published.ts +1 -1
  231. package/src/scripts/check_reply_consistency.ts +86 -17
  232. package/src/scripts/check_rule_projection_integrity.ts +22 -12
  233. package/src/scripts/check_test_delta.ts +294 -0
  234. package/src/scripts/check_test_weakening.ts +299 -0
  235. package/src/scripts/closure_scan.ts +333 -0
  236. package/src/scripts/compile_hook_manifest.ts +43 -3
  237. package/src/scripts/council_record_shape.ts +209 -0
  238. package/src/scripts/hook_manifest.json +1 -1
  239. package/src/scripts/hook_manifest.yaml +127 -12
  240. package/src/scripts/hooks/block_kernel_rule_writes.ts +12 -2
  241. package/src/scripts/hooks/concern_registry.ts +6 -0
  242. package/src/scripts/hooks/design_pass_hook.ts +128 -3
  243. package/src/scripts/hooks/end_review_nudge_hook.ts +101 -21
  244. package/src/scripts/hooks/evidence_independence.ts +96 -0
  245. package/src/scripts/hooks/memory_recall_hook.ts +540 -0
  246. package/src/scripts/hooks/obligation_settle_hook.ts +182 -0
  247. package/src/scripts/hooks/review_baseline_hook.ts +130 -0
  248. package/src/scripts/hooks/rule_inject_hook.ts +49 -1
  249. package/src/scripts/hooks/run_continuation_hook.ts +17 -20
  250. package/src/scripts/hooks/turn_end_gate_hook.ts +232 -14
  251. package/src/scripts/hooks_doctor.ts +89 -1
  252. package/src/scripts/hooks_status.ts +19 -0
  253. package/src/scripts/install.ts +107 -207
  254. package/src/scripts/interruption_report.ts +8 -2
  255. package/src/scripts/janitor.ts +15 -1
  256. package/src/scripts/lint_decision_classes.ts +464 -0
  257. package/src/scripts/lint_hook_manifest.ts +11 -0
  258. package/src/scripts/lint_major_migration_sections.ts +450 -0
  259. package/src/scripts/lint_plan_risk_register.ts +47 -3
  260. package/src/scripts/lint_roadmap_blockers.ts +61 -0
  261. package/src/scripts/lint_roadmap_producers.ts +202 -0
  262. package/src/scripts/measure_rule_budget.ts +101 -5
  263. package/src/scripts/measure_turn_end_gate.ts +302 -5
  264. package/src/scripts/new_roadmap.ts +37 -0
  265. package/src/scripts/orchestration_record.ts +0 -5
  266. package/src/scripts/release.ts +66 -162
  267. package/src/scripts/release_drill.ts +460 -15
  268. package/src/scripts/release_env.ts +1 -1
  269. package/src/scripts/release_publication.ts +52 -1
  270. package/src/scripts/report_enforcement_drift.ts +566 -0
  271. package/src/scripts/report_held_object_arrivals.ts +456 -0
  272. package/src/scripts/report_host_injection_effect.ts +3 -11
  273. package/src/scripts/report_standing_payload_by_host.ts +26 -152
  274. package/src/scripts/schemas/agent-settings.schema.json +23 -0
  275. package/src/scripts/schemas/command.schema.json +4 -0
  276. package/src/scripts/scope_growth.ts +219 -0
  277. package/src/scripts/sync_agent_settings.ts +140 -18
  278. package/src/scripts/trigger_eval_grandfather.json +0 -1
  279. package/src/scripts/ui_conformance_probe.ts +622 -0
  280. package/src/server/io/yamlIO.ts +48 -0
  281. package/src/server/schemas/settings.ts +31 -7
  282. package/src/shared/missionExecution.ts +126 -0
  283. package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
@@ -0,0 +1,267 @@
1
+ /**
2
+ * The five `forge_protection` rows, read from the forge rather than guessed.
3
+ *
4
+ * `road-to-adversarial-verification-and-long-runs` Phase 3.2. ADR-268 § 3 trades
5
+ * the owner's confirmation for a mechanical check, and branch protection is the
6
+ * check it trades for — so a run that cannot say whether protection exists has
7
+ * not established the thing the trade depends on.
8
+ *
9
+ * **Every row carries the API call its value came from, and a row that was not
10
+ * read says so.** That is the whole shape: the step's word is *read, never
11
+ * guessed*, and the only way a reader can tell one from the other is if the
12
+ * provenance travels with the value. A row is `satisfied` / `unsatisfied` only
13
+ * when a named call produced it; otherwise it is `unread`, which is a third
14
+ * state and never a false.
15
+ *
16
+ * **Rulesets, not the classic branch-protection endpoint.** On this repository
17
+ * `repos/<owner>/<repo>/branches/main/protection` returns `404 {"message":"Branch
18
+ * not protected"}` while an active ruleset protects the default branch — so a
19
+ * checker reading the classic endpoint reports "unprotected" for a repository
20
+ * that is protected. The `forge-protection-settings` blocker was re-scoped for
21
+ * exactly this reason on 2026-09-10, and the re-scope forbids treating that 404
22
+ * as absence of protection. No ruleset ID is pinned: rulesets can be replaced or
23
+ * split, and pinning one would break on the first change.
24
+ *
25
+ * Pure. The network lives in the caller, so both polarities are testable offline.
26
+ */
27
+
28
+ import type { BypassActor, RulesetDetail } from './platform_anchor.js';
29
+
30
+ /** Whether a row was established, refuted, or never read. */
31
+ export type RowState = 'satisfied' | 'unsatisfied' | 'unread';
32
+
33
+ /** One `forge_protection` row. */
34
+ export interface ProtectionRow {
35
+ /** Stable key. Rows are addressed by this, never by position. */
36
+ readonly id: string;
37
+ readonly state: RowState;
38
+ /**
39
+ * The forge API call this value came from — or would come from, when the row
40
+ * is `unread`. Never empty: a value whose provenance is unstated is the
41
+ * guess this block exists to refuse.
42
+ */
43
+ readonly source: string;
44
+ /** One line a human can act on when the row is not satisfied. */
45
+ readonly detail: string;
46
+ }
47
+
48
+ /** The five rows, in the order the blocker's own table lists them. */
49
+ export const PROTECTION_ROW_IDS: readonly string[] = [
50
+ 'default_branch_protected',
51
+ 'required_checks_present',
52
+ 'force_push_disabled',
53
+ 'auto_merge_available',
54
+ 'deploy_via_pipeline_only',
55
+ ] as const;
56
+
57
+ /** What a caller must supply for a row to be readable. `null` = not read. */
58
+ export interface ForgeReading {
59
+ /** `repos/<repo>/rulesets` + `/rulesets/<id>`, or `null` when unread. */
60
+ readonly rulesets: readonly RulesetDetail[] | null;
61
+ /** The repository's default branch, or `null` when unread. */
62
+ readonly defaultBranch: string | null;
63
+ /** `repos/<repo>` → `allow_auto_merge`, or `null` when unread. */
64
+ readonly allowAutoMerge: boolean | null;
65
+ /**
66
+ * `repos/<repo>/environments` → whether every environment with deployment
67
+ * branch policies restricts them, or `null` when unread.
68
+ *
69
+ * Deliberately a boolean the caller derives rather than a raw payload: what
70
+ * the row asserts is a property of the SET of environments, and handing this
71
+ * module the raw list would put that judgement in two places.
72
+ */
73
+ readonly deployRestricted: boolean | null;
74
+ }
75
+
76
+ const SOURCE = {
77
+ rulesets: 'GET repos/{owner}/{repo}/rulesets + GET .../rulesets/{id}',
78
+ repo: 'GET repos/{owner}/{repo} (allow_auto_merge)',
79
+ environments: 'GET repos/{owner}/{repo}/environments (deployment_branch_policy)',
80
+ } as const;
81
+
82
+ /** `true` when this ruleset is active and targets branches. */
83
+ function isActiveBranchRuleset(rs: RulesetDetail): boolean {
84
+ return rs.enforcement === 'active' && rs.target === 'branch';
85
+ }
86
+
87
+ /** `true` when the ruleset's ref conditions reach the default branch. */
88
+ function reachesDefaultBranch(rs: RulesetDetail, defaultBranch: string): boolean {
89
+ const include = rs.conditions?.ref_name?.include ?? [];
90
+ return include.some(
91
+ (p) => p === '~DEFAULT_BRANCH' || p === '~ALL' || p === `refs/heads/${defaultBranch}`,
92
+ );
93
+ }
94
+
95
+ function hasRule(rs: RulesetDetail, type: string): boolean {
96
+ return (rs.rules ?? []).some((r) => r.type === type);
97
+ }
98
+
99
+ function requiredContexts(rs: RulesetDetail): string[] {
100
+ for (const r of rs.rules ?? []) {
101
+ if (r.type !== 'required_status_checks') continue;
102
+ const p = r.parameters;
103
+ if (p === null || typeof p !== 'object') continue;
104
+ const list = (p as Record<string, unknown>)['required_status_checks'];
105
+ if (!Array.isArray(list)) continue;
106
+ return list
107
+ .map((e) =>
108
+ e !== null && typeof e === 'object'
109
+ ? (e as Record<string, unknown>)['context']
110
+ : null,
111
+ )
112
+ .filter((c): c is string => typeof c === 'string');
113
+ }
114
+ return [];
115
+ }
116
+
117
+ /**
118
+ * Map a forge reading onto the five rows.
119
+ *
120
+ * Never throws and never invents: an input of `null` produces `unread`, which is
121
+ * why the return type has three states rather than two. A gate that reports
122
+ * `false` for something it did not look at is the failure this separation exists
123
+ * to prevent — and it is the failure the `forge-protection-settings` re-scope
124
+ * names, one layer up.
125
+ */
126
+ export function forgeProtectionRows(reading: ForgeReading): ProtectionRow[] {
127
+ const rows: ProtectionRow[] = [];
128
+ const rulesets = reading.rulesets;
129
+ const branch = reading.defaultBranch;
130
+
131
+ const covering =
132
+ rulesets === null || branch === null
133
+ ? null
134
+ : rulesets.filter((rs) => isActiveBranchRuleset(rs) && reachesDefaultBranch(rs, branch));
135
+
136
+ rows.push(
137
+ covering === null
138
+ ? {
139
+ id: 'default_branch_protected',
140
+ state: 'unread',
141
+ source: SOURCE.rulesets,
142
+ detail: 'the rulesets were not queried',
143
+ }
144
+ : covering.length > 0
145
+ ? {
146
+ id: 'default_branch_protected',
147
+ state: 'satisfied',
148
+ source: SOURCE.rulesets,
149
+ detail: `${String(covering.length)} active branch ruleset(s) cover the default branch`,
150
+ }
151
+ : {
152
+ id: 'default_branch_protected',
153
+ state: 'unsatisfied',
154
+ source: SOURCE.rulesets,
155
+ detail:
156
+ 'no active branch ruleset covers the default branch. NOTE: the classic ' +
157
+ 'branches/<b>/protection endpoint 404s on a ruleset-protected repository, ' +
158
+ 'so a 404 there is not evidence for this row.',
159
+ },
160
+ );
161
+
162
+ const contexts = covering === null ? null : covering.flatMap(requiredContexts);
163
+ rows.push(
164
+ contexts === null
165
+ ? {
166
+ id: 'required_checks_present',
167
+ state: 'unread',
168
+ source: SOURCE.rulesets,
169
+ detail: 'the rulesets were not queried',
170
+ }
171
+ : contexts.length > 0
172
+ ? {
173
+ id: 'required_checks_present',
174
+ state: 'satisfied',
175
+ source: SOURCE.rulesets,
176
+ detail: `${String(contexts.length)} required context(s): ${contexts.join(', ')}`,
177
+ }
178
+ : {
179
+ id: 'required_checks_present',
180
+ state: 'unsatisfied',
181
+ source: SOURCE.rulesets,
182
+ detail: 'no required status check context is configured',
183
+ },
184
+ );
185
+
186
+ const noFastForward = covering === null ? null : covering.some((rs) => hasRule(rs, 'non_fast_forward'));
187
+ rows.push(
188
+ noFastForward === null
189
+ ? {
190
+ id: 'force_push_disabled',
191
+ state: 'unread',
192
+ source: SOURCE.rulesets,
193
+ detail: 'the rulesets were not queried',
194
+ }
195
+ : {
196
+ id: 'force_push_disabled',
197
+ state: noFastForward ? 'satisfied' : 'unsatisfied',
198
+ source: SOURCE.rulesets,
199
+ detail: noFastForward
200
+ ? 'a non_fast_forward rule is active on the default branch'
201
+ : 'no non_fast_forward rule covers the default branch',
202
+ },
203
+ );
204
+
205
+ rows.push(
206
+ reading.allowAutoMerge === null
207
+ ? {
208
+ id: 'auto_merge_available',
209
+ state: 'unread',
210
+ source: SOURCE.repo,
211
+ detail: 'the repository record was not queried',
212
+ }
213
+ : {
214
+ id: 'auto_merge_available',
215
+ state: reading.allowAutoMerge ? 'satisfied' : 'unsatisfied',
216
+ source: SOURCE.repo,
217
+ detail: reading.allowAutoMerge
218
+ ? 'allow_auto_merge is enabled'
219
+ : 'allow_auto_merge is disabled — merge delivery cannot queue behind checks',
220
+ },
221
+ );
222
+
223
+ rows.push(
224
+ reading.deployRestricted === null
225
+ ? {
226
+ id: 'deploy_via_pipeline_only',
227
+ state: 'unread',
228
+ source: SOURCE.environments,
229
+ detail: 'the environments were not queried',
230
+ }
231
+ : {
232
+ id: 'deploy_via_pipeline_only',
233
+ state: reading.deployRestricted ? 'satisfied' : 'unsatisfied',
234
+ source: SOURCE.environments,
235
+ detail: reading.deployRestricted
236
+ ? 'every environment restricts its deployment branches'
237
+ : 'at least one environment accepts a deployment from any branch',
238
+ },
239
+ );
240
+
241
+ return rows;
242
+ }
243
+
244
+ /**
245
+ * The human ACTION lines for rows that are not satisfied.
246
+ *
247
+ * **Not a halt, and the step says so explicitly**: `doctor` LISTS what is
248
+ * missing and the run continues. An `unread` row produces an action too — "go
249
+ * and read it" is a real instruction, and silently omitting it would make an
250
+ * unmeasured row indistinguishable from a satisfied one, which is the same
251
+ * conflation the three-state row type exists to prevent.
252
+ */
253
+ export function protectionActions(rows: readonly ProtectionRow[]): string[] {
254
+ return rows
255
+ .filter((r) => r.state !== 'satisfied')
256
+ .map((r) => `${r.id}: ${r.state} — ${r.detail} (source: ${r.source})`);
257
+ }
258
+
259
+ /** Unconditional bypass actors across the covering rulesets, for reporting. */
260
+ export function unconditionalBypasses(
261
+ rulesets: readonly RulesetDetail[],
262
+ defaultBranch: string,
263
+ ): BypassActor[] {
264
+ return rulesets
265
+ .filter((rs) => isActiveBranchRuleset(rs) && reachesDefaultBranch(rs, defaultBranch))
266
+ .flatMap((rs) => (rs.bypass_actors ?? []).filter((a) => a.bypass_mode === 'always'));
267
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Keys under a `hook_manifest.yaml` platform block that are NOT lifecycle slots.
3
+ *
4
+ * One definition, because three consumers had grown their own literal and a
5
+ * fourth key would have had to be added to each of them independently: the
6
+ * manifest lint (which reads an unknown key as an unknown EVENT), the
7
+ * injection-effect report (which counts slots per host), and the install
8
+ * snapshot's drift guard (which asserts every manifest event is bound). Each
9
+ * of those is a different question about the same block, and none of them is
10
+ * asking about metadata.
11
+ *
12
+ * `fallback_only` this package binds nothing on the host; counting it made
13
+ * copilot read one slot against the zero the host table
14
+ * records.
15
+ * `ask` the shape of the host's user-facing question primitive,
16
+ * `native` or `text`. It binds no event, so counting it
17
+ * would add one phantom slot to every host at once.
18
+ *
19
+ * A key added here is a key excluded from every event-shaped reading of the
20
+ * manifest, so add one only when it genuinely carries no binding.
21
+ */
22
+ export const PLATFORM_METADATA_KEYS: ReadonlySet<string> = new Set(['fallback_only', 'ask']);
23
+
24
+ /** True when a platform-block key describes the host rather than binding an event. */
25
+ export function isPlatformMetadataKey(key: string): boolean {
26
+ return PLATFORM_METADATA_KEYS.has(key);
27
+ }
@@ -200,7 +200,7 @@ function splitlines(text: string): string[] {
200
200
  return parts;
201
201
  }
202
202
 
203
- function _render(
203
+ export function _render(
204
204
  version: string,
205
205
  tools: string[],
206
206
  installed_at: string,
@@ -0,0 +1,127 @@
1
+ /**
2
+ * `json.dumps` byte-parity helpers — the emit half of the ADR-200 port.
3
+ *
4
+ * The retired Python installer wrote every JSON artefact through
5
+ * `json.dumps(..., ensure_ascii=False)`, and the port's contract is
6
+ * byte-identical output, not merely equivalent JSON. `JSON.stringify` does not
7
+ * satisfy it: it escapes nothing above `\u001f` the same way, renders `NaN` /
8
+ * `Infinity` as `null` rather than as Python's bare tokens, and offers no
9
+ * separator control for the compact NDJSON form. So the three dump shapes the
10
+ * installer needs are written out here, mirroring CPython's encoder:
11
+ *
12
+ * - {@link jsonDumpsIndent} — `json.dumps(data, indent=N, ensure_ascii=False)`
13
+ * with `sort_keys=False` (insertion order preserved, as JS object key order
14
+ * already matches Python dict order for our string keys);
15
+ * - {@link jsonDumpsCompact} — `json.dumps(obj, separators=(",", ":"))`;
16
+ * - {@link jsonStrNoAscii} — the string encoder both build on.
17
+ *
18
+ * Sibling of `src/scripts/install.ts`, which is its only caller. It lives here
19
+ * rather than inline because the dump shapes are pure, total functions of their
20
+ * input with no installer state — and because `install.ts` is far over the
21
+ * 1,500-line source ceiling (`check_source_size_budget`), so a self-contained
22
+ * unit belongs outside it.
23
+ *
24
+ * This module is in the installer's bundled import closure
25
+ * (`npm run build:install-bundle`), so it carries no module-level side effect
26
+ * and no `process.exit` — see `check_installer_import_purity`.
27
+ */
28
+
29
+ export function jsonStrNoAscii(s: string): string {
30
+ // json.dumps(ensure_ascii=False): escape control chars + " + \, keep >=0x20
31
+ let out = '"';
32
+ for (const ch of s) {
33
+ const code = ch.codePointAt(0) as number;
34
+ switch (ch) {
35
+ case '"':
36
+ out += '\\"';
37
+ break;
38
+ case '\\':
39
+ out += '\\\\';
40
+ break;
41
+ case '\n':
42
+ out += '\\n';
43
+ break;
44
+ case '\r':
45
+ out += '\\r';
46
+ break;
47
+ case '\t':
48
+ out += '\\t';
49
+ break;
50
+ case '\b':
51
+ out += '\\b';
52
+ break;
53
+ case '\f':
54
+ out += '\\f';
55
+ break;
56
+ default:
57
+ if (code < 0x20) {
58
+ out += '\\u' + code.toString(16).padStart(4, '0');
59
+ } else {
60
+ out += ch;
61
+ }
62
+ }
63
+ }
64
+ return out + '"';
65
+ }
66
+
67
+ function _jsonScalar(value: unknown): string | null {
68
+ if (value === null || value === undefined) return 'null';
69
+ if (typeof value === 'boolean') return value ? 'true' : 'false';
70
+ if (typeof value === 'number') {
71
+ if (!Number.isFinite(value)) {
72
+ if (Number.isNaN(value)) return 'NaN';
73
+ return value > 0 ? 'Infinity' : '-Infinity';
74
+ }
75
+ // Our payloads carry only integers; render as-is.
76
+ return String(value);
77
+ }
78
+ if (typeof value === 'string') return jsonStrNoAscii(value);
79
+ return null;
80
+ }
81
+
82
+ function _dumpIndent(value: unknown, indent: number, depth: number): string {
83
+ const scalar = _jsonScalar(value);
84
+ if (scalar !== null) return scalar;
85
+ const pad = ' '.repeat(indent * (depth + 1));
86
+ const closePad = ' '.repeat(indent * depth);
87
+ if (Array.isArray(value)) {
88
+ if (value.length === 0) return '[]';
89
+ const items = value.map((v) => pad + _dumpIndent(v, indent, depth + 1));
90
+ return `[\n${items.join(',\n')}\n${closePad}]`;
91
+ }
92
+ if (typeof value === 'object' && value !== null) {
93
+ const obj = value as Record<string, unknown>;
94
+ const keys = Object.keys(obj);
95
+ if (keys.length === 0) return '{}';
96
+ const items = keys.map(
97
+ (k) => `${pad}${jsonStrNoAscii(k)}: ${_dumpIndent(obj[k], indent, depth + 1)}`,
98
+ );
99
+ return `{\n${items.join(',\n')}\n${closePad}}`;
100
+ }
101
+ return jsonStrNoAscii(String(value));
102
+ }
103
+
104
+ /** `json.dumps(data, indent=N, ensure_ascii=False)` (sort_keys=False). */
105
+ export function jsonDumpsIndent(value: unknown, indent: number): string {
106
+ return _dumpIndent(value, indent, 0);
107
+ }
108
+
109
+ /** `json.dumps(obj, separators=(",", ":"))` — compact, ensure_ascii=False here. */
110
+ export function jsonDumpsCompact(value: unknown): string {
111
+ const scalar = _jsonScalar(value);
112
+ if (scalar !== null) return scalar;
113
+ if (Array.isArray(value)) {
114
+ return '[' + value.map((v) => jsonDumpsCompact(v)).join(',') + ']';
115
+ }
116
+ if (typeof value === 'object' && value !== null) {
117
+ const obj = value as Record<string, unknown>;
118
+ return (
119
+ '{' +
120
+ Object.keys(obj)
121
+ .map((k) => `${jsonStrNoAscii(k)}:${jsonDumpsCompact(obj[k])}`)
122
+ .join(',') +
123
+ '}'
124
+ );
125
+ }
126
+ return jsonStrNoAscii(String(value));
127
+ }
@@ -11,11 +11,22 @@
11
11
  *
12
12
  * So each side supplies the raw string from its own reader and this module
13
13
  * decides what it means. Anything unrecognised — absent key, typo, `null`,
14
- * a non-string — is `eager-all`, today's shipped behaviour: a mode nobody can
15
- * spell must never silently thin the standing corpus.
14
+ * a non-string — is `eager-all`: the parser fallback, which is NOT the value
15
+ * the template ships (see the type below), because a mode nobody can spell
16
+ * must never silently thin the standing corpus.
16
17
  */
17
18
 
18
- /** The three projection shapes. `eager-all` is the shipped default. */
19
+ /**
20
+ * The three projection shapes, and the two different defaults they answer to.
21
+ *
22
+ * The TEMPLATE default — what a consumer is given — is `delivery` with
23
+ * `hosts: [claude-code]`, shipped in `src/config/agent-settings.template.yml`
24
+ * since ADR-267, so on that one host a normal read resolves to `delivery`.
25
+ * The PARSER FALLBACK — what applies when no value resolves from any layer —
26
+ * is `eager-all` (`DEFAULT_LEAN_PROJECTION_MODE` below), deliberately not
27
+ * flipped with the template per ADR-267 decision 4: the template says what was
28
+ * chosen, the constant says what happens when nothing could be read.
29
+ */
19
30
  export type LeanProjectionMode = 'eager-all' | 'thin' | 'delivery';
20
31
 
21
32
  export const DEFAULT_LEAN_PROJECTION_MODE: LeanProjectionMode = 'eager-all';