@event4u/agent-config 11.0.0 → 12.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 (312) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +173 -229
  3. package/agents/templates/.ai-council.yml.example +46 -3
  4. package/dist/agent-src/commands/agent-handoff.md +0 -1
  5. package/dist/agent-src/commands/agent-status.md +0 -1
  6. package/dist/agent-src/commands/agents/audit.md +0 -1
  7. package/dist/agent-src/commands/agents/init.md +0 -1
  8. package/dist/agent-src/commands/agents/optimize.md +0 -1
  9. package/dist/agent-src/commands/agents/user/accept.md +0 -1
  10. package/dist/agent-src/commands/agents/user/delete.md +0 -1
  11. package/dist/agent-src/commands/agents/user/init.md +0 -1
  12. package/dist/agent-src/commands/agents/user/review.md +0 -1
  13. package/dist/agent-src/commands/agents/user/show.md +0 -1
  14. package/dist/agent-src/commands/agents/user/update.md +0 -1
  15. package/dist/agent-src/commands/agents/user.md +0 -1
  16. package/dist/agent-src/commands/agents.md +0 -1
  17. package/dist/agent-src/commands/analytics/prune.md +0 -1
  18. package/dist/agent-src/commands/analytics/show.md +0 -1
  19. package/dist/agent-src/commands/analytics.md +0 -1
  20. package/dist/agent-src/commands/analyze/conformance.md +0 -1
  21. package/dist/agent-src/commands/analyze/decision.md +0 -1
  22. package/dist/agent-src/commands/analyze/inbox.md +0 -1
  23. package/dist/agent-src/commands/analyze/incident.md +0 -1
  24. package/dist/agent-src/commands/analyze/near-miss.md +0 -1
  25. package/dist/agent-src/commands/analyze/postmortem.md +0 -1
  26. package/dist/agent-src/commands/analyze/premortem.md +0 -1
  27. package/dist/agent-src/commands/analyze/reference-repo.md +0 -1
  28. package/dist/agent-src/commands/analyze.md +0 -1
  29. package/dist/agent-src/commands/brand/identity.md +0 -1
  30. package/dist/agent-src/commands/brand/review.md +0 -1
  31. package/dist/agent-src/commands/brand/strategy.md +0 -1
  32. package/dist/agent-src/commands/brand/tokens.md +0 -1
  33. package/dist/agent-src/commands/brand/voice.md +0 -1
  34. package/dist/agent-src/commands/brand.md +0 -1
  35. package/dist/agent-src/commands/bug/fix.md +0 -1
  36. package/dist/agent-src/commands/bug/investigate.md +0 -1
  37. package/dist/agent-src/commands/bug.md +0 -1
  38. package/dist/agent-src/commands/challenge-me/vision.md +0 -1
  39. package/dist/agent-src/commands/challenge-me/with-docs.md +0 -1
  40. package/dist/agent-src/commands/challenge-me.md +0 -1
  41. package/dist/agent-src/commands/chat-history/import.md +0 -1
  42. package/dist/agent-src/commands/chat-history.md +0 -1
  43. package/dist/agent-src/commands/check-current-md.md +0 -1
  44. package/dist/agent-src/commands/commit/in-chunks.md +0 -1
  45. package/dist/agent-src/commands/commit.md +0 -1
  46. package/dist/agent-src/commands/condense.md +0 -1
  47. package/dist/agent-src/commands/context/create.md +0 -1
  48. package/dist/agent-src/commands/context/refactor.md +0 -1
  49. package/dist/agent-src/commands/context.md +0 -1
  50. package/dist/agent-src/commands/contribution-precheck.md +0 -1
  51. package/dist/agent-src/commands/cost/profile.md +0 -1
  52. package/dist/agent-src/commands/cost/report.md +0 -1
  53. package/dist/agent-src/commands/cost.md +0 -1
  54. package/dist/agent-src/commands/council/analysis.md +0 -1
  55. package/dist/agent-src/commands/council/debate.md +0 -1
  56. package/dist/agent-src/commands/council/default.md +0 -1
  57. package/dist/agent-src/commands/council/design.md +0 -1
  58. package/dist/agent-src/commands/council/optimize.md +0 -1
  59. package/dist/agent-src/commands/council/pr.md +0 -1
  60. package/dist/agent-src/commands/council.md +0 -1
  61. package/dist/agent-src/commands/design-system/capture.md +0 -1
  62. package/dist/agent-src/commands/design-system/generate.md +0 -1
  63. package/dist/agent-src/commands/design-system/import.md +0 -1
  64. package/dist/agent-src/commands/design-system.md +0 -1
  65. package/dist/agent-src/commands/estimate-ticket.md +0 -1
  66. package/dist/agent-src/commands/explain-run.md +0 -1
  67. package/dist/agent-src/commands/feature/dev.md +0 -1
  68. package/dist/agent-src/commands/feature/explore.md +0 -1
  69. package/dist/agent-src/commands/feature/plan.md +0 -1
  70. package/dist/agent-src/commands/feature/refactor.md +0 -1
  71. package/dist/agent-src/commands/feature/roadmap.md +0 -1
  72. package/dist/agent-src/commands/feature.md +0 -1
  73. package/dist/agent-src/commands/fix/ci.md +0 -1
  74. package/dist/agent-src/commands/fix/comments.md +0 -1
  75. package/dist/agent-src/commands/fix/portability.md +0 -1
  76. package/dist/agent-src/commands/fix/pr-comments-loop.md +0 -1
  77. package/dist/agent-src/commands/fix/pr-comments.md +0 -1
  78. package/dist/agent-src/commands/fix/quality.md +0 -1
  79. package/dist/agent-src/commands/fix/refs.md +0 -1
  80. package/dist/agent-src/commands/fix/route.md +0 -1
  81. package/dist/agent-src/commands/fix/seeder.md +0 -1
  82. package/dist/agent-src/commands/fix.md +0 -1
  83. package/dist/agent-src/commands/ghostwriter/delete.md +0 -1
  84. package/dist/agent-src/commands/ghostwriter/fetch.md +0 -1
  85. package/dist/agent-src/commands/ghostwriter/list.md +0 -1
  86. package/dist/agent-src/commands/ghostwriter/show.md +0 -1
  87. package/dist/agent-src/commands/ghostwriter/write.md +0 -1
  88. package/dist/agent-src/commands/ghostwriter.md +0 -1
  89. package/dist/agent-src/commands/grill-me.md +0 -1
  90. package/dist/agent-src/commands/humanize.md +0 -1
  91. package/dist/agent-src/commands/image/analyse.md +0 -1
  92. package/dist/agent-src/commands/image/create.md +0 -1
  93. package/dist/agent-src/commands/image/verify.md +0 -1
  94. package/dist/agent-src/commands/image.md +0 -1
  95. package/dist/agent-src/commands/implement-ticket.md +0 -1
  96. package/dist/agent-src/commands/jira-ticket.md +0 -1
  97. package/dist/agent-src/commands/judge/on-diff.md +0 -1
  98. package/dist/agent-src/commands/judge/solo.md +0 -1
  99. package/dist/agent-src/commands/judge/steps.md +0 -1
  100. package/dist/agent-src/commands/judge.md +0 -1
  101. package/dist/agent-src/commands/knowledge/cross-repo.md +0 -1
  102. package/dist/agent-src/commands/knowledge/forget.md +0 -1
  103. package/dist/agent-src/commands/knowledge/ingest.md +0 -1
  104. package/dist/agent-src/commands/knowledge/list.md +0 -1
  105. package/dist/agent-src/commands/knowledge.md +0 -1
  106. package/dist/agent-src/commands/memory/add.md +0 -1
  107. package/dist/agent-src/commands/memory/learn-low-impact.md +0 -1
  108. package/dist/agent-src/commands/memory/load.md +0 -1
  109. package/dist/agent-src/commands/memory/mine-session.md +0 -1
  110. package/dist/agent-src/commands/memory/promote.md +0 -1
  111. package/dist/agent-src/commands/memory/propose.md +0 -1
  112. package/dist/agent-src/commands/memory.md +0 -1
  113. package/dist/agent-src/commands/mission/upgrade.md +0 -1
  114. package/dist/agent-src/commands/mode.md +0 -1
  115. package/dist/agent-src/commands/module/create.md +0 -1
  116. package/dist/agent-src/commands/module/explore.md +0 -1
  117. package/dist/agent-src/commands/module.md +0 -1
  118. package/dist/agent-src/commands/optimize/agents-dir.md +0 -1
  119. package/dist/agent-src/commands/optimize/augmentignore.md +0 -1
  120. package/dist/agent-src/commands/optimize/deep.md +0 -1
  121. package/dist/agent-src/commands/optimize/project.md +0 -1
  122. package/dist/agent-src/commands/optimize/prompt.md +0 -1
  123. package/dist/agent-src/commands/optimize/rtk.md +0 -1
  124. package/dist/agent-src/commands/optimize/skills.md +0 -1
  125. package/dist/agent-src/commands/optimize.md +0 -1
  126. package/dist/agent-src/commands/orchestrate.md +0 -1
  127. package/dist/agent-src/commands/override/create.md +0 -1
  128. package/dist/agent-src/commands/override/manage.md +0 -1
  129. package/dist/agent-src/commands/override.md +0 -1
  130. package/dist/agent-src/commands/package/reset.md +0 -1
  131. package/dist/agent-src/commands/package/test.md +0 -1
  132. package/dist/agent-src/commands/package.md +0 -1
  133. package/dist/agent-src/commands/post-as/ghostwriter.md +0 -1
  134. package/dist/agent-src/commands/post-as/me.md +0 -1
  135. package/dist/agent-src/commands/post-as.md +0 -1
  136. package/dist/agent-src/commands/pr/create/description-only.md +0 -1
  137. package/dist/agent-src/commands/pr/create.md +23 -9
  138. package/dist/agent-src/commands/prediction-pool.md +0 -1
  139. package/dist/agent-src/commands/prepare-for-review.md +0 -1
  140. package/dist/agent-src/commands/profile/activate.md +0 -1
  141. package/dist/agent-src/commands/profile/deactivate.md +0 -1
  142. package/dist/agent-src/commands/profile/show.md +0 -1
  143. package/dist/agent-src/commands/profile.md +0 -1
  144. package/dist/agent-src/commands/project/analyze.md +0 -1
  145. package/dist/agent-src/commands/project/health.md +0 -1
  146. package/dist/agent-src/commands/project.md +0 -1
  147. package/dist/agent-src/commands/refine-ticket.md +0 -1
  148. package/dist/agent-src/commands/research/deep.md +0 -1
  149. package/dist/agent-src/commands/research/report.md +0 -1
  150. package/dist/agent-src/commands/research.md +0 -1
  151. package/dist/agent-src/commands/review/changes.md +0 -1
  152. package/dist/agent-src/commands/review/routing.md +0 -1
  153. package/dist/agent-src/commands/review.md +0 -1
  154. package/dist/agent-src/commands/roadmap/ai-council.md +0 -1
  155. package/dist/agent-src/commands/roadmap/create.md +0 -1
  156. package/dist/agent-src/commands/roadmap/materialize.md +0 -1
  157. package/dist/agent-src/commands/roadmap/next.md +0 -1
  158. package/dist/agent-src/commands/roadmap/process-full.md +0 -1
  159. package/dist/agent-src/commands/roadmap/process-phase.md +0 -1
  160. package/dist/agent-src/commands/roadmap/process-step.md +0 -1
  161. package/dist/agent-src/commands/roadmap.md +0 -1
  162. package/dist/agent-src/commands/rule-compliance-audit.md +0 -1
  163. package/dist/agent-src/commands/security-audit-config.md +0 -1
  164. package/dist/agent-src/commands/skill/preview.md +0 -1
  165. package/dist/agent-src/commands/skill.md +0 -1
  166. package/dist/agent-src/commands/skills/discover.md +0 -1
  167. package/dist/agent-src/commands/skills.md +0 -1
  168. package/dist/agent-src/commands/sync/agent-settings.md +0 -1
  169. package/dist/agent-src/commands/sync/gitignore/fix.md +0 -1
  170. package/dist/agent-src/commands/sync/gitignore.md +0 -1
  171. package/dist/agent-src/commands/sync.md +0 -1
  172. package/dist/agent-src/commands/tdd/green.md +0 -1
  173. package/dist/agent-src/commands/tdd/red.md +0 -1
  174. package/dist/agent-src/commands/tdd/refactor.md +0 -1
  175. package/dist/agent-src/commands/tdd.md +0 -1
  176. package/dist/agent-src/commands/team/adversarial.md +0 -1
  177. package/dist/agent-src/commands/team/delegate.md +0 -1
  178. package/dist/agent-src/commands/team/review.md +0 -1
  179. package/dist/agent-src/commands/team/status.md +0 -1
  180. package/dist/agent-src/commands/team-knowledge/bootstrap.md +0 -1
  181. package/dist/agent-src/commands/team-knowledge/consolidate.md +0 -1
  182. package/dist/agent-src/commands/team-knowledge.md +0 -1
  183. package/dist/agent-src/commands/team.md +0 -1
  184. package/dist/agent-src/commands/tests/create.md +0 -1
  185. package/dist/agent-src/commands/tests/e2e-heal.md +0 -1
  186. package/dist/agent-src/commands/tests/e2e-plan.md +0 -1
  187. package/dist/agent-src/commands/tests/execute.md +0 -1
  188. package/dist/agent-src/commands/tests.md +0 -1
  189. package/dist/agent-src/commands/threat-model.md +0 -1
  190. package/dist/agent-src/commands/update-form-request-messages.md +0 -1
  191. package/dist/agent-src/commands/upstream-contribute.md +0 -1
  192. package/dist/agent-src/commands/video/from-script.md +0 -1
  193. package/dist/agent-src/commands/video/from-song.md +0 -1
  194. package/dist/agent-src/commands/video/scene.md +0 -1
  195. package/dist/agent-src/commands/video/stitch.md +0 -1
  196. package/dist/agent-src/commands/video/storyboard.md +0 -1
  197. package/dist/agent-src/commands/video.md +0 -1
  198. package/dist/agent-src/commands/work.md +0 -1
  199. package/dist/agent-src/commands/worktree/cleanup.md +0 -1
  200. package/dist/agent-src/commands/worktree/create.md +0 -1
  201. package/dist/agent-src/commands/worktree/status.md +0 -1
  202. package/dist/agent-src/commands/worktree/verify.md +0 -1
  203. package/dist/agent-src/commands/worktree.md +0 -1
  204. package/dist/agent-src/contexts/model-recommendations.md +6 -4
  205. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  206. package/dist/agent-src/scripts/roadmap_gates.ts +30 -2
  207. package/dist/agent-src/scripts/update_roadmap_progress.ts +32 -1
  208. package/dist/agent-src/skills/docx-authoring/SKILL.md +1 -0
  209. package/dist/agent-src/skills/git-workflow/SKILL.md +9 -1
  210. package/dist/agent-src/skills/pdf-tools/SKILL.md +1 -0
  211. package/dist/agent-src/skills/roadmap-writing/SKILL.md +7 -7
  212. package/dist/agent-src/skills/skill-writing/SKILL.md +35 -0
  213. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  214. package/dist/agent-src/templates/roadmaps.md +41 -3
  215. package/dist/cli/commands/commands.candidates.test.js +1 -1
  216. package/dist/cli/commands/commands.candidates.test.js.map +1 -1
  217. package/dist/cli/commands/commands.js +9 -15
  218. package/dist/cli/commands/commands.js.map +1 -1
  219. package/dist/cli/commands/profiles.test.js +7 -7
  220. package/dist/cli/commands/profiles.test.js.map +1 -1
  221. package/dist/cli/discovery/loadManifest.js.map +1 -1
  222. package/dist/cli/main.js +2 -2
  223. package/dist/cli/main.js.map +1 -1
  224. package/dist/cli/registry.js +1 -0
  225. package/dist/cli/registry.js.map +1 -1
  226. package/dist/cli-delegate/chunk-K34QTSD5.js +3895 -0
  227. package/dist/cli-delegate/{chunk-L565YFXH.js → chunk-LKZ5DOYW.js} +557 -297
  228. package/dist/cli-delegate/{chunk-NN4KW4HZ.js → chunk-O52B4F5S.js} +123 -2338
  229. package/dist/cli-delegate/cmd_conformance.js +18 -6
  230. package/dist/cli-delegate/cmd_doctor.js +2 -2
  231. package/dist/cli-delegate/cmd_export.js +1 -1
  232. package/dist/cli-delegate/cmd_handoff.js +1 -1
  233. package/dist/cli-delegate/cmd_prune.js +1 -1
  234. package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
  235. package/dist/cli-delegate/cmd_sync.js +1 -1
  236. package/dist/cli-delegate/cmd_uninstall.js +1 -1
  237. package/dist/cli-delegate/cmd_validate.js +1 -1
  238. package/dist/discovery/deprecation-report.md +1 -1
  239. package/dist/discovery/discovery-manifest.json +213 -412
  240. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  241. package/dist/discovery/discovery-manifest.summary.md +2 -2
  242. package/dist/discovery/dormancy-report.md +1 -1
  243. package/dist/discovery/orphan-report.md +1 -1
  244. package/dist/discovery/packs.json +3 -3
  245. package/dist/discovery/trust-report.md +1 -1
  246. package/dist/discovery/workspaces.json +3 -3
  247. package/dist/hooks/dispatch.js +2588 -1082
  248. package/dist/install/install.mjs +744 -482
  249. package/dist/mcp/registry-manifest.json +3 -3
  250. package/dist/mcp/server.json +2 -2
  251. package/dist/mcp/server.mjs +159 -15
  252. package/dist/router.json +1 -1
  253. package/dist/scripts/_lib/model_tier.js +9 -0
  254. package/dist/scripts/_lib/model_tier.js.map +1 -1
  255. package/dist/scripts/condense.js +50 -0
  256. package/dist/scripts/condense.js.map +1 -1
  257. package/docs/MIGRATION.md +21 -1
  258. package/docs/contracts/settings-classes.md +41 -0
  259. package/package.json +1 -1
  260. package/src/agent-src/scripts/roadmap_gates.ts +30 -2
  261. package/src/agent-src/scripts/update_roadmap_progress.ts +32 -1
  262. package/src/config/agent-settings.template.yml +34 -0
  263. package/src/config/evaluator-budgets.json +4 -4
  264. package/src/config/gate-coverage.yml +39 -9
  265. package/src/config/gate-violation-baselines.json +9 -3
  266. package/src/config/hook-token-budget.json +7 -1
  267. package/src/config/preamble-payload-budget.json +28 -0
  268. package/src/scripts/_dispatch.bash +52 -5
  269. package/src/scripts/_lib/git_common_dir.ts +173 -0
  270. package/src/scripts/_lib/model_tier.ts +9 -0
  271. package/src/scripts/_lib/release_highlights.ts +108 -7
  272. package/src/scripts/_lib/skill_catalogue.ts +862 -0
  273. package/src/scripts/ai_council/clients.ts +182 -3
  274. package/src/scripts/ai_council/config.ts +16 -1
  275. package/src/scripts/ai_council/low_impact.ts +14 -1
  276. package/src/scripts/ai_council/orchestrator.ts +14 -1
  277. package/src/scripts/ai_council/transport_resolver.ts +32 -2
  278. package/src/scripts/audit_command_surface.ts +21 -15
  279. package/src/scripts/build_catalog_index.ts +1 -1
  280. package/src/scripts/build_discovery_manifest.ts +30 -34
  281. package/src/scripts/capture_skill_catalogue.ts +185 -328
  282. package/src/scripts/check_branch_freshness.ts +217 -62
  283. package/src/scripts/check_release_published.ts +6 -2
  284. package/src/scripts/check_release_trunk_sync.ts +18 -2
  285. package/src/scripts/condense.ts +50 -1
  286. package/src/scripts/dispatch_r2_reviewer.ts +15 -5
  287. package/src/scripts/evidence_report.ts +15 -4
  288. package/src/scripts/gen_discovery_baseline.ts +3 -3
  289. package/src/scripts/hook_manifest.yaml +67 -13
  290. package/src/scripts/hooks/concern_registry.ts +2 -0
  291. package/src/scripts/hooks/orchestration_record_hook.ts +21 -0
  292. package/src/scripts/hooks/subagent_ledger_hook.ts +17 -4
  293. package/src/scripts/hooks/tool_result_bytes_hook.ts +219 -0
  294. package/src/scripts/hot_context_hook.ts +13 -1
  295. package/src/scripts/install.ts +129 -0
  296. package/src/scripts/janitor.ts +77 -2
  297. package/src/scripts/lint_command_tiers.ts +29 -75
  298. package/src/scripts/lint_originality.ts +101 -14
  299. package/src/scripts/lint_plan_risk_register.ts +22 -9
  300. package/src/scripts/lint_roadmap_blockers.ts +109 -6
  301. package/src/scripts/lint_scheduled_deprecations.ts +738 -0
  302. package/src/scripts/migration_status.ts +17 -1
  303. package/src/scripts/probe_review_binding_drift.ts +731 -0
  304. package/src/scripts/release.ts +95 -1
  305. package/src/scripts/schemas/command.schema.json +6 -11
  306. package/src/scripts/schemas/skill.schema.json +12 -2
  307. package/src/scripts/schemas/subagent.schema.json +2 -2
  308. package/src/scripts/session_eol_report.ts +81 -0
  309. package/src/scripts/skill_linter.ts +82 -2
  310. package/src/scripts/workspace_doctor.ts +464 -0
  311. package/src/scripts/worktree_cleanup_check.ts +113 -8
  312. package/dist/cli-delegate/chunk-AYKQGIBG.js +0 -1594
@@ -115,7 +115,37 @@ export const DEFAULT_PERPLEXITY_MODEL = 'sonar-pro';
115
115
  // divergence is a one-line change, not a hunt through inline literals.
116
116
  // (`xai` / `perplexity` CLI reuse their API constants: their CLIs are
117
117
  // community wrappers around the same paid API, so the values do not diverge.)
118
- export const DEFAULT_OPENAI_CLI_MODEL = 'gpt-5';
118
+ /**
119
+ * Sentinel: let the codex CLI pick its own model, by omitting `--model`.
120
+ *
121
+ * This is not a placeholder for a value nobody looked up. Measured
122
+ * 2026-08-15 against `codex exec --json` on a ChatGPT-account (subscription)
123
+ * transport, every explicitly named candidate was refused with
124
+ * `400 invalid_request_error: The '<model>' model is not supported when using
125
+ * Codex with a ChatGPT account.` — `gpt-4o`, `gpt-5` (this constant's previous
126
+ * value) and `gpt-5.1-codex` alike. Omitting the flag answered normally.
127
+ *
128
+ * So the only value known to work on this transport is "whatever the CLI
129
+ * chooses", and pinning ANY name here would re-break the seat the next time
130
+ * the vendor rotates its lineup. The label is carried into records as-is so a
131
+ * reader can see that no model was pinned, rather than a name being implied
132
+ * that nobody verified.
133
+ */
134
+ export const OPENAI_CLI_VENDOR_DEFAULT = 'codex-default';
135
+
136
+ /**
137
+ * Models MEASURED to be refused by the codex subscription transport, with the
138
+ * date they were measured. This is a deny-list on purpose: the CLI publishes
139
+ * no allow-list, so the honest claim is "these were rejected when we tried",
140
+ * never "only these are allowed".
141
+ */
142
+ export const CODEX_MEASURED_UNSERVABLE: ReadonlyMap<string, string> = new Map([
143
+ ['gpt-4o', '2026-08-15'],
144
+ ['gpt-5', '2026-08-15'],
145
+ ['gpt-5.1-codex', '2026-08-15'],
146
+ ]);
147
+
148
+ export const DEFAULT_OPENAI_CLI_MODEL = OPENAI_CLI_VENDOR_DEFAULT;
119
149
  export const DEFAULT_ANTHROPIC_CLI_MODEL = 'claude-sonnet-4-5';
120
150
  export const DEFAULT_GEMINI_CLI_MODEL = 'gemini-2.5-pro';
121
151
 
@@ -665,7 +695,18 @@ export class OpenAIClient extends ExternalAIClient {
665
695
 
666
696
  constructor(opts: ApiClientOptions = {}) {
667
697
  super();
668
- this.model = opts.model ?? DEFAULT_OPENAI_MODEL;
698
+ // `codex-default` is the CLI transport's "let the host choose" sentinel,
699
+ // and ONE `model:` field in `.ai-council.yml` feeds both transports —
700
+ // which one a member resolves to is decided at run time, not in the
701
+ // config. The API has no such sentinel (there is no endpoint named
702
+ // `codex-default`), so it resolves to this client's own default instead
703
+ // of being sent verbatim and rejected. Without this, making the shipped
704
+ // template CLI-safe would break every api-transport user in the same
705
+ // commit.
706
+ this.model =
707
+ opts.model === undefined || opts.model === OPENAI_CLI_VENDOR_DEFAULT
708
+ ? DEFAULT_OPENAI_MODEL
709
+ : opts.model;
669
710
  if (opts.client !== undefined && opts.client !== null) {
670
711
  this._client = opts.client;
671
712
  return;
@@ -1252,6 +1293,20 @@ export abstract class CliClient extends ExternalAIClient {
1252
1293
  }
1253
1294
  }
1254
1295
 
1296
+ /**
1297
+ * Refuse a call this transport cannot serve, BEFORE spawning.
1298
+ *
1299
+ * `null` means "nothing known to be wrong" — deliberately not "verified
1300
+ * servable". No vendor CLI here publishes the set of models its
1301
+ * subscription tier accepts, so an allow-list would be invented and would
1302
+ * reject valid pins the day the vendor adds one. What CAN be stated is the
1303
+ * complement: models this estate has MEASURED being rejected. Subclasses
1304
+ * override; the base transport knows of none.
1305
+ */
1306
+ protected _preflight_transport(): { code: string; detail: string } | null {
1307
+ return null;
1308
+ }
1309
+
1255
1310
  /** Return text to send on stdin, or `null` to inherit caller's stdin. */
1256
1311
  protected _stdin_payload(system_prompt: string, user_prompt: string): string | null {
1257
1312
  void system_prompt;
@@ -1391,6 +1446,26 @@ export abstract class CliClient extends ExternalAIClient {
1391
1446
  }
1392
1447
  }
1393
1448
 
1449
+ // 1b. transport pre-flight — refuse BEFORE spending.
1450
+ //
1451
+ // A subscription transport can be structurally unable to serve the
1452
+ // configured model. Discovering that by spawning costs a call against
1453
+ // the daily cap and returns an opaque `exit_1`, which reads as "the CLI
1454
+ // is flaky" rather than "this pin can never work here". A refusal that
1455
+ // names the model and the transport is the difference between a config
1456
+ // bug someone fixes in a minute and a seat that quietly stays dead.
1457
+ const preflight = this._preflight_transport();
1458
+ if (preflight !== null) {
1459
+ return new CouncilResponse({
1460
+ provider: this.name,
1461
+ model: this.model,
1462
+ text: '',
1463
+ latency_ms: _elapsedMs(t0),
1464
+ error: preflight.code,
1465
+ metadata: { cli: true, transport: this.transport, detail: preflight.detail },
1466
+ });
1467
+ }
1468
+
1394
1469
  // 2. build command + spawn.
1395
1470
  const cmd = this._build_command(system_prompt, user_prompt, max_tokens);
1396
1471
  const stdinPayload = this._stdin_payload(system_prompt, user_prompt);
@@ -1726,9 +1801,46 @@ export class OpenAICliClient extends CliClient {
1726
1801
  void system_prompt;
1727
1802
  void user_prompt;
1728
1803
  void max_tokens;
1804
+ // `--skip-git-repo-check` is not a convenience flag. Without it codex
1805
+ // refuses any directory it does not consider trusted — captured
1806
+ // `stderr_tail`: `Not inside a trusted directory and
1807
+ // --skip-git-repo-check was not specified.` A worktree path is never
1808
+ // trusted, which is the whole explanation for the standing observation
1809
+ // that the openai seat dies in a worktree while working from the main
1810
+ // checkout. The council reads a prompt and writes nothing through this
1811
+ // process, so the trust gate protects nothing here that the council's
1812
+ // own gates do not already cover.
1813
+ //
1729
1814
  // `-` is codex's documented "read the prompt from stdin" positional;
1730
1815
  // the payload itself is assembled in `_stdin_payload`.
1731
- return [this.binary, 'exec', '--json', '--model', this.model, '-'];
1816
+ const modelArgs =
1817
+ this.model === OPENAI_CLI_VENDOR_DEFAULT ? [] : ['--model', this.model];
1818
+ return [this.binary, 'exec', '--json', '--skip-git-repo-check', ...modelArgs, '-'];
1819
+ }
1820
+
1821
+ /**
1822
+ * Refuse a pin this estate has measured the transport rejecting.
1823
+ *
1824
+ * The remedy is named because it is not guessable: removing the `model:`
1825
+ * line is the fix, and "remove a setting" is the last thing an operator
1826
+ * tries when a member is silent.
1827
+ */
1828
+ protected override _preflight_transport(): { code: string; detail: string } | null {
1829
+ const measuredOn = CODEX_MEASURED_UNSERVABLE.get(this.model);
1830
+ if (measuredOn === undefined) return null;
1831
+ return {
1832
+ code: 'model_unsupported_on_transport',
1833
+ detail:
1834
+ `openai member pins model \`${this.model}\`, which the codex CLI ` +
1835
+ `(subscription transport) refused when measured on ${measuredOn}: ` +
1836
+ `"The '${this.model}' model is not supported when using Codex with a ` +
1837
+ 'ChatGPT account." The CLI publishes no list of models it DOES accept, ' +
1838
+ 'so there is no supported set to offer here — the one configuration ' +
1839
+ 'measured to work is no pin at all. Fix: remove the `model:` line from ' +
1840
+ 'the openai member (or set it to `codex-default`) to use the CLI\'s own ' +
1841
+ 'default. A pinned model is only reachable on the API transport ' +
1842
+ '(`api_key_ref:` without `mode: cli`).',
1843
+ };
1732
1844
  }
1733
1845
 
1734
1846
  /**
@@ -1752,6 +1864,7 @@ export class OpenAICliClient extends CliClient {
1752
1864
  let text = '';
1753
1865
  let input_tokens = 0;
1754
1866
  let output_tokens = 0;
1867
+ let failure: string | null = null;
1755
1868
  const meta: Record<string, unknown> = {};
1756
1869
  for (let line of _splitlines(stdout)) {
1757
1870
  line = line.trim();
@@ -1794,6 +1907,31 @@ export class OpenAICliClient extends CliClient {
1794
1907
  text = chunks.join('\n').trim();
1795
1908
  }
1796
1909
  }
1910
+ // The FLAT shape, and it is the one the CLI emits today:
1911
+ // {"item":{"type":"agent_message","text":"OK"}}
1912
+ // The `content[]` branch above is the older nested form. Both
1913
+ // are read because this adapter has no way to pin a CLI
1914
+ // version, and reading only the nested one is why the seat
1915
+ // returned an empty string with NO error for every call —
1916
+ // measured 2026-08-15, and indistinguishable downstream from
1917
+ // a model that answered with nothing. Only an `agent_message`
1918
+ // is taken: an `error` item also carries `text`-adjacent
1919
+ // fields, and treating one as an answer would turn the
1920
+ // skills-budget warning into the member's response.
1921
+ //
1922
+ // LAST message wins, never the first. A codex turn emits
1923
+ // several `agent_message` items — a preamble ("I'll check X
1924
+ // first…") and then the answer — so keeping the first one
1925
+ // truncates the response to its throat-clearing. Measured
1926
+ // 2026-08-15 on a real council run: 1,479 output tokens
1927
+ // billed, 134 characters captured. A one-word probe cannot
1928
+ // catch this, which is why it survived the first fix.
1929
+ if (_dictGet(itemObj, 'type', undefined) === 'agent_message') {
1930
+ const flat = _pyStr(_dictGet(itemObj, 'text', '')).trim();
1931
+ if (flat) {
1932
+ text = flat;
1933
+ }
1934
+ }
1797
1935
  if (_pyTruthy(_dictGet(itemObj, 'id', undefined))) {
1798
1936
  meta['item_id'] = _pyStr(itemObj['id']);
1799
1937
  }
@@ -1812,8 +1950,49 @@ export class OpenAICliClient extends CliClient {
1812
1950
  if (_pyTruthy(_dictGet(ev, 'session_id', undefined))) {
1813
1951
  meta['session_id'] = _pyStr(ev['session_id']);
1814
1952
  }
1953
+ } else if (event_type === 'turn.failed') {
1954
+ // The turn's real cause lives ONLY here, and codex still exits
1955
+ // 0. Without this branch the response is an empty text, which
1956
+ // the caller reports as `no response` — indistinguishable from
1957
+ // a model that answered with nothing. Measured 2026-08-15: a
1958
+ // pinned `gpt-4o` produced exactly this shape, and the seat
1959
+ // read as flaky for weeks instead of as misconfigured.
1960
+ let err = _dictGet(ev, 'error', null);
1961
+ if (!_pyTruthy(err)) err = {};
1962
+ if (_isPlainObject(err)) {
1963
+ const raw = _pyStr(_dictGet(err as Record<string, unknown>, 'message', ''));
1964
+ if (raw) failure = raw;
1965
+ }
1815
1966
  }
1816
1967
  }
1968
+
1969
+ if (failure !== null && !text) {
1970
+ const unsupported = /not supported when using Codex/i.test(failure);
1971
+ return new CouncilResponse({
1972
+ provider: this.name,
1973
+ model: this.model,
1974
+ text: '',
1975
+ input_tokens,
1976
+ output_tokens,
1977
+ error: unsupported ? 'model_unsupported_on_transport' : 'turn_failed',
1978
+ metadata: {
1979
+ ...meta,
1980
+ // The vendor's own sentence, kept because it names the
1981
+ // model and the account type — the two facts an operator
1982
+ // needs and neither of which the error code carries.
1983
+ detail: failure.slice(0, 400),
1984
+ ...(unsupported
1985
+ ? {
1986
+ hint:
1987
+ 'the codex subscription transport serves no explicitly pinned ' +
1988
+ 'model measured so far — remove the `model:` line from the ' +
1989
+ 'openai member to use the CLI default',
1990
+ }
1991
+ : {}),
1992
+ },
1993
+ });
1994
+ }
1995
+
1817
1996
  return new CouncilResponse({
1818
1997
  provider: this.name,
1819
1998
  model: this.model,
@@ -81,6 +81,7 @@ import * as user_global_paths from '../_lib/user_global_paths.js';
81
81
  // `import type` and is erased at build, so there is no runtime cycle today.
82
82
  // Converting it to a value import — or adding any other runtime import from
83
83
  // quorum into config — creates a real ESM cycle in the loader's init path.
84
+ import { OPENAI_CLI_VENDOR_DEFAULT } from './clients.js';
84
85
  import { SOLO_FLOOR_MIN_PRESENT } from './quorum.js';
85
86
 
86
87
  const _VALID_PROVIDERS: ReadonlySet<string> = new Set([
@@ -1843,7 +1844,21 @@ function _build_member(
1843
1844
  }
1844
1845
  entries.push(entry);
1845
1846
  }
1846
- if (member_enabled && model && !entries.includes(model)) {
1847
+ // `codex-default` is a SENTINEL, not a model id — "let the transport
1848
+ // choose". Requiring it to appear on a downgrade ladder of concrete ids
1849
+ // is a category error, and it is the check that blocks the one config
1850
+ // that actually works on a subscription account: the shipped template
1851
+ // cannot pin an id (every id this package shipped is on
1852
+ // `CODEX_MEASURED_UNSERVABLE`), and its ladder is an api-transport
1853
+ // concern the CLI's own default cannot be ordered against. Exempting
1854
+ // the sentinel is narrower than dropping the check — a real pin is
1855
+ // still required to be on its own ladder.
1856
+ if (
1857
+ member_enabled &&
1858
+ model &&
1859
+ model !== OPENAI_CLI_VENDOR_DEFAULT &&
1860
+ !entries.includes(model)
1861
+ ) {
1847
1862
  throw new CouncilConfigError(
1848
1863
  `members.${name}.model_ladder must include the active ` +
1849
1864
  `\`model\` (${_pyReprStr(model)}); got ${_pyRepr(entries)}.`,
@@ -35,6 +35,7 @@
35
35
  // - dict insertion order + `dict(sorted(...))` for the stats aggregation.
36
36
 
37
37
  import { pyRound } from '../_lib/value_ladder.js';
38
+ import { OPENAI_CLI_VENDOR_DEFAULT } from './clients.js';
38
39
  import type { CouncilResponse, ExternalAIClient } from './clients.js';
39
40
  import type { LowImpactFastPathConfig, MemberConfig } from './config.js';
40
41
  import type { ImpactVerdict } from './necessity.js';
@@ -630,7 +631,19 @@ export function resolve_low_impact(
630
631
  const pinned = model_downgrade.model_tier_override?.[member.name];
631
632
  const cheapest = member.model_ladder.length > 0 ? member.model_ladder[0] : null;
632
633
  const target = pinned && pinned.length > 0 ? pinned : cheapest;
633
- if (target && target !== client.model) {
634
+ // A member left on the vendor-default sentinel is NOT downgradable
635
+ // off the ladder. There is nothing to downgrade FROM — the
636
+ // transport picks the model — and writing a rung onto it re-creates
637
+ // the pinned-id shape the seat repair removed. Concretely: the
638
+ // shipped ladder's cheapest rung is `gpt-4o-mini` and its
639
+ // neighbours are `gpt-4o` / `gpt-4.1`, so this line could hand the
640
+ // codex transport an id `CODEX_MEASURED_UNSERVABLE` already records
641
+ // as refused — on a path whose `model_downgrade.enabled` defaults
642
+ // to true. An explicit per-run `model_tier_override` still wins,
643
+ // because that is a human naming a model for this run.
644
+ const unpinned = client.model === OPENAI_CLI_VENDOR_DEFAULT;
645
+ const overrideWins = pinned !== undefined && pinned.length > 0;
646
+ if (target && target !== client.model && (!unpinned || overrideWins)) {
634
647
  client.model = target;
635
648
  }
636
649
  }
@@ -2029,7 +2029,20 @@ export function render(responses: CouncilResponse[], opts: RenderOptions = {}):
2029
2029
  function _render_quorum_line(q: QuorumResult): string {
2030
2030
  const verdict = q.status === 'concluded' ? 'concluded' : 'INCONCLUSIVE — release gate holds';
2031
2031
  const solo = isSoloConcluded(q) ? ' · **solo** — one voice concluded this pass' : '';
2032
- return `**Quorum:** ${q.present}/${q.total} present, needed ${q.threshold} — ${verdict}.${solo}`;
2032
+ // The DEGRADED marker shipped on stdout (`council_cli.ts::_format_quorum_line`,
2033
+ // round 7 § 5.3) and did NOT ship here, although this function is the one
2034
+ // the mirror was copied FROM. The two then diverged in the worse direction:
2035
+ // stdout scrolls away, this string is what gets committed as evidence and
2036
+ // cited months later. `1/2 present, needed 1 — concluded` is literally true
2037
+ // and reads as agreement; the solo marker alone covers only present === 1,
2038
+ // so 2-of-4 rendered as plain "concluded" with nothing to stop a reader
2039
+ // inferring convergence. Same sentence as stdout on purpose — one wording,
2040
+ // so neither surface can drift into being the softer one again.
2041
+ const degraded =
2042
+ q.present < q.total
2043
+ ? ` ⚠️ DEGRADED — ${String(q.total - q.present)} member(s) did not answer; this is not convergence.`
2044
+ : '';
2045
+ return `**Quorum:** ${q.present}/${q.total} present, needed ${q.threshold} — ${verdict}.${solo}${degraded}`;
2033
2046
  }
2034
2047
 
2035
2048
  /**
@@ -356,6 +356,7 @@ export type CliFailureClass =
356
356
  | 'binary_missing'
357
357
  | 'auth_rejected'
358
358
  | 'cli_unsupported'
359
+ | 'model_unservable'
359
360
  | 'timeout'
360
361
  | 'server_error'
361
362
  | 'quota_exhausted'
@@ -376,6 +377,20 @@ const FALLBACK_ELIGIBLE: ReadonlySet<CliFailureClass> = new Set([
376
377
  'binary_missing',
377
378
  'auth_rejected',
378
379
  'cli_unsupported',
380
+ // `model_unservable` satisfies the same no-double-charge property in both
381
+ // of its shapes: the pre-spend gate refuses before any subprocess exists,
382
+ // and the call-time shape is a provider 400 — rejected at the request
383
+ // boundary, no generation performed.
384
+ //
385
+ // HONEST SCOPE: this set is not consumed by any shipped path today.
386
+ // `isFallbackEligible` and `MidFlightFallback` have zero call sites outside
387
+ // this module and its tests, so membership here is a stated property of the
388
+ // class, NOT a behaviour a caller performs. Said plainly because the
389
+ // opposite reading is the attractive one — the constraint really is the
390
+ // subscription transport's rather than the model's, so the api rung really
391
+ // would serve the same id, and it would be easy to write that as if it
392
+ // happened. It does not happen yet.
393
+ 'model_unservable',
379
394
  ]);
380
395
 
381
396
  /** Normalise a raw client error / skip reason into a `CliFailureClass`. */
@@ -388,6 +403,15 @@ export function classifyCliFailure(raw: string): CliFailureClass {
388
403
  if (s === 'cli_unsupported' || s === 'unsupported' || s === 'parse_failed') {
389
404
  return 'cli_unsupported';
390
405
  }
406
+ // Prefix match, not equality: the detail carries the operator-facing reason
407
+ // after the code, and that sentence IS the value of this class — collapsing
408
+ // it back to a bare token would restore the opaque `exit_1` this replaced.
409
+ // Also matched on the provider's own wording, so a model refused at call
410
+ // time (one the deny-list has never seen) classifies the same as one the
411
+ // pre-spend gate caught.
412
+ if (s.startsWith('model_unservable') || s.includes('is not supported when using codex')) {
413
+ return 'model_unservable';
414
+ }
391
415
  if (s === 'timeout') return 'timeout';
392
416
  if (s === 'cli_quota_exhausted' || s === 'quota_exhausted') return 'quota_exhausted';
393
417
  if (/^exit_5\d\d$/.test(s) || s === 'server_error' || s === 'os') return 'server_error';
@@ -406,8 +430,14 @@ export function isFallbackEligible(failure: CliFailureClass): boolean {
406
430
  * can bucket every absent member — whether it never got a transport at all
407
431
  * or lost one partway through — under one vocabulary. Returns `null` for a
408
432
  * failure class outside the four-value enum (`cli_unsupported`,
409
- * `server_error`, `other`); a caller falls back to the raw failure detail
410
- * for those rather than mis-classifying them.
433
+ * `server_error`, `model_unservable`, `other`); a caller falls back to the raw
434
+ * failure detail for those rather than mis-classifying them.
435
+ *
436
+ * `model_unservable` is deliberately unmapped rather than folded into
437
+ * `no_auth`: the credential is fine, the model is not, and the `detail` string
438
+ * carries the specific reason. So the bucket a reader sees is still the generic
439
+ * `unavailable`, while the line beside it now names the cause — an incomplete
440
+ * improvement, stated as one.
411
441
  */
412
442
  export function absentReasonFromCliFailure(failure: CliFailureClass): AbsentReason | null {
413
443
  switch (failure) {
@@ -57,9 +57,14 @@ const SIZE_BUDGETS: Record<string, number> = {
57
57
  platform: 10,
58
58
  };
59
59
  // ADR-092: `visibility:` is the named source of truth (visible / advanced /
60
- // internal); the integer `tier:` is a back-compat alias. A command counts
61
- // toward the per-pack budget when it is surfaced — visibility in {visible,
62
- // advanced} (or, when only the alias is present, tier in {0,1}). `internal`
60
+ // internal). The integer `tier:` alias was REMOVED from live command files in
61
+ // road-to-tier-removal Phase 4, but this file still parses it on purpose: the
62
+ // surface-growth check reads PAST git revisions (`_tier_at_ref`), and pre-ADR-092
63
+ // blobs carry only the alias. The live-file tier read below is therefore a
64
+ // harmless residue on current files and the correct read on historical ones.
65
+ // A command counts toward the per-pack budget when it is surfaced — visibility
66
+ // in {visible, advanced} (or, when only the alias is present, tier in {0,1}).
67
+ // `internal`
63
68
  // (or absent → defaults to internal) is uncapped.
64
69
  const VISIBLE_TIERS: ReadonlySet<number> = new Set([0, 1]);
65
70
  const VISIBLE_VISIBILITIES: ReadonlySet<string> = new Set(['visible', 'advanced']);
@@ -622,17 +627,17 @@ export function render_md(commands: Command[], pairs: OverlapPair[]): string {
622
627
  '[`command-surface-synthesis.md`](command-surface-synthesis.md). Every command ' +
623
628
  "in this table maps to `keep` unless named in that file's tables.",
624
629
  '',
625
- '| Name | Path | Tier | Cluster | Aliases | Lines | Commits | Age (d) | Bucket |',
630
+ '| Name | Path | Visibility | Cluster | Aliases | Lines | Commits | Age (d) | Bucket |',
626
631
  '|---|---|---:|---|---|---:|---:|---:|---|',
627
632
  );
628
633
  const sortedCmds = _stableSortStr(commands, (c) => c.relpath);
629
634
  for (const c of sortedCmds) {
630
635
  const aliases = c.aliases.length ? c.aliases.join(', ') : '—';
631
- const tier = c.tier === null ? '—' : String(c.tier);
636
+ const visibility = c.visibility === null ? '—' : c.visibility;
632
637
  const cluster = c.cluster || '—';
633
638
  const age = c.days_since_first_commit === null ? '—' : String(c.days_since_first_commit);
634
639
  lines.push(
635
- `| \`${c.name}\` | \`${c.relpath}\` | ${tier} | ${cluster} | ${aliases} | ` +
640
+ `| \`${c.name}\` | \`${c.relpath}\` | ${visibility} | ${cluster} | ${aliases} | ` +
636
641
  `${c.line_count} | ${c.commit_count} | ${age} | |`,
637
642
  );
638
643
  }
@@ -738,7 +743,7 @@ function _is_visible(c: Command): boolean {
738
743
 
739
744
  interface VisibleCommand {
740
745
  name: string;
741
- tier: number | null;
746
+ visibility: string | null;
742
747
  cluster: string;
743
748
  citations_docs: number;
744
749
  commit_count: number;
@@ -797,7 +802,7 @@ export function build_budget_audit(
797
802
  if (over) {
798
803
  const vis = visible.map((c) => ({
799
804
  name: c.name,
800
- tier: c.tier,
805
+ visibility: c.visibility,
801
806
  cluster: c.cluster,
802
807
  citations_docs: citation_count(c.name),
803
808
  commit_count: c.commit_count,
@@ -832,8 +837,9 @@ export function render_budget_md(audit: BudgetAudit): string {
832
837
  L.push('# Command budget audit (6.0.0-B Phase 2 Step 4)\n');
833
838
  L.push(
834
839
  '> Per-pack VISIBLE-command counts vs. the `size_class` budget ' +
835
- '(capability-packs.md). `visible` = tier ∈ {0,1}; tier 2 / absent = ' +
836
- 'internal (uncapped). Citations = docs/ files referencing `/<name>`.\n' +
840
+ '(capability-packs.md). `visible` = visibility ∈ {visible, advanced}; ' +
841
+ '`internal` / absent = uncapped. Citations = docs/ files referencing ' +
842
+ '`/<name>`.\n' +
837
843
  '>\n' +
838
844
  '> **Signal note:** docs-citations is the load-bearing signal (rank ' +
839
845
  'candidates low→high). The git commit/idle columns are low-variance on ' +
@@ -862,11 +868,11 @@ export function render_budget_md(audit: BudgetAudit): string {
862
868
  `budget ${p.budget} (${p.size_class}), over by ${p.over_by}\n`,
863
869
  );
864
870
  L.push('Decide per command: keep-visible · set `internal` · relocate-to-pack-X.\n');
865
- L.push('| Command | tier | cluster | docs citations | commits | days idle |');
866
- L.push('|---|--:|---|--:|--:|--:|');
871
+ L.push('| Command | visibility | cluster | docs citations | commits | days idle |');
872
+ L.push('|---|---|---|--:|--:|--:|');
867
873
  for (const c of p.visible_commands ?? []) {
868
874
  L.push(
869
- `| \`${c.name}\` | ${c.tier} | ${c.cluster || '—'} | ` +
875
+ `| \`${c.name}\` | ${c.visibility ?? '—'} | ${c.cluster || '—'} | ` +
870
876
  `${c.citations_docs} | ${c.commit_count} | ` +
871
877
  `${c.days_since_modified !== null ? c.days_since_modified : '—'} |`,
872
878
  );
@@ -1050,8 +1056,8 @@ export function check_new_budget(baseline: string, quiet: boolean): number {
1050
1056
  );
1051
1057
  }
1052
1058
  process.stdout.write(
1053
- '\nResolve by one of: set the command to `tier: 2` (internal, ' +
1054
- 'uncapped) · merge into a sibling cluster · relocate to a pack ' +
1059
+ '\nResolve by one of: set the command to `visibility: internal` ' +
1060
+ '(uncapped) · merge into a sibling cluster · relocate to a pack ' +
1055
1061
  'with budget headroom · file a budget-exemption ADR ' +
1056
1062
  '(docs/contracts/capability-packs.md § Budget exemption process).\n',
1057
1063
  );
@@ -138,7 +138,7 @@ export function buildEntries(): CatalogIndexEntry[] {
138
138
  cls: 'command',
139
139
  name,
140
140
  description: _str(fm['description']) || _str(fm['intent']),
141
- tags: [_str(fm['pack']), _str(fm['tier']), _str(fm['visibility'])].filter((s) => s.length > 0),
141
+ tags: [_str(fm['pack']), _str(fm['visibility'])].filter((s) => s.length > 0),
142
142
  path: _rel(abs),
143
143
  });
144
144
  }
@@ -657,26 +657,13 @@ function _build(strict: boolean): [JsonObject, JsonObject[]] {
657
657
  // 6.0.0-C: surface command routing metadata. Does not affect the
658
658
  // per-file checksum (computed over frontmatter, below).
659
659
  if (category === 'command' && fm !== null && _isObject(fm)) {
660
- if (fm['tier'] !== undefined && fm['tier'] !== null) {
661
- entry['tier'] = fm['tier'];
662
- }
663
- // ADR-090: `visibility:` is the named source of truth; the integer
664
- // `tier:` is a back-compat alias. Dual-emit BOTH into the manifest
665
- // (a published data contract) during the deprecation window so
666
- // external consumers reading the integer key keep working. The
667
- // top-level `deprecations` block (ADR-092 / road-to-tier-removal)
668
- // announces the `tier` deprecation; removal is Phase 4. Prefer the
669
- // explicit field; derive from tier when absent.
670
- const _tierToVis: Record<number, string> = { 0: 'visible', 1: 'advanced', 2: 'internal' };
671
- let _vis: Json = fm['visibility'];
672
- if (
673
- (_vis === undefined || _vis === null) &&
674
- fm['tier'] !== undefined &&
675
- fm['tier'] !== null
676
- ) {
677
- // `.get(fm["tier"])` returns None when tier is not 0/1/2 → undefined here.
678
- _vis = typeof fm['tier'] === 'number' ? _tierToVis[fm['tier']] : undefined;
679
- }
660
+ // ADR-090 named `visibility:` the source of truth; the integer
661
+ // `tier:` alias it superseded was dual-emitted here through the
662
+ // deprecation window and REMOVED in road-to-tier-removal Phase 4.
663
+ // The manifest no longer carries `tier` in any entry — the
664
+ // top-level `deprecations` block records the removal for consumers
665
+ // that still read the old key.
666
+ const _vis: Json = fm['visibility'];
680
667
  if (_vis !== undefined && _vis !== null) {
681
668
  entry['visibility'] = _vis;
682
669
  }
@@ -788,27 +775,36 @@ function _build(strict: boolean): [JsonObject, JsonObject[]] {
788
775
  const stats = _compute_stats(artefacts, unassigned, documentedUnassigned);
789
776
 
790
777
  const manifest: JsonObject = {
791
- version: 2,
792
- // Machine-readable deprecation signal (road-to-tier-removal Phase 1,
793
- // ADR-092). The integer command `tier` (0/1/2) is a back-compat alias
794
- // for the named `visibility` field (ADR-090). It is STILL emitted into
795
- // command entries above (non-breaking) — this block only ANNOUNCES the
796
- // deprecation so external manifest consumers can migrate to `visibility`
797
- // during the soak window. Removal of `tier` is Phase 4 of
798
- // road-to-tier-removal (cheaply reversible per ADR-092); `sunset` is
799
- // maintainer-owned and stays null until the soak clears.
778
+ // v3 (road-to-tier-removal Phase 4): the integer command `tier` is no
779
+ // longer emitted on `category: command` artefacts. That is a breaking
780
+ // change to a published data contract, and this schema's own rule is
781
+ // "bump on breaking change" — so the version carries the break rather
782
+ // than leaving a v2 consumer to discover a silently-absent field.
783
+ version: 3,
784
+ // Machine-readable removal record (road-to-tier-removal Phase 1 added
785
+ // this block as a deprecation ANNOUNCEMENT; Phase 4 turned it into the
786
+ // record of an executed removal). The entry is retained on purpose: a
787
+ // consumer that still reads the old key needs to find out what replaced
788
+ // it and when it went, and an absent entry would say neither. `sunset`
789
+ // is maintainer-owned: SET on 2026-08-13, with the soak wait explicitly
790
+ // waived ("wir haben lange genug gewartet"). ADR-137 had already reduced
791
+ // the re-open trigger set to this one act — a concrete sunset — after
792
+ // withdrawing the second trigger as structurally impossible in a
793
+ // no-runtime package.
800
794
  deprecations: [
801
795
  {
802
796
  key: 'tier',
803
797
  scope: 'command',
804
798
  replacement: 'visibility',
805
799
  since: 'ADR-092',
806
- sunset: null,
800
+ sunset: '2026-08-13',
801
+ removed_in: 3,
807
802
  note:
808
- 'Integer command `tier` (0/1/2) is a deprecated back-compat ' +
809
- 'alias for the named `visibility` field (ADR-090). Read ' +
810
- '`visibility`; `tier` is still emitted (non-breaking) but ' +
811
- 'scheduled for removal — see road-to-tier-removal.',
803
+ 'Integer command `tier` (0/1/2) was a back-compat alias for ' +
804
+ 'the named `visibility` field (ADR-090). REMOVED in manifest ' +
805
+ 'v3 — command artefacts no longer carry `tier`. Read ' +
806
+ '`visibility` (`visible` / `advanced` / `internal`); the old ' +
807
+ 'mapping was 0↔visible, 1↔advanced, 2↔internal.',
812
808
  },
813
809
  ],
814
810
  generated_at: _nowUtc(),