coding-os 0.3.2__py3-none-any.whl

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 (1304) hide show
  1. adapters/claude/README.md +6 -0
  2. adapters/claude/_install_helpers/extract_stacks.py +43 -0
  3. adapters/claude/_install_helpers/update_mcp_json.py +75 -0
  4. adapters/claude/adapter.yaml +164 -0
  5. adapters/claude/hooks/README.md +40 -0
  6. adapters/claude/hooks/agent_memory_sync.py +131 -0
  7. adapters/claude/hooks/ensure-agent-memory-link.sh +36 -0
  8. adapters/claude/hooks/sync-agent-memory.sh +21 -0
  9. adapters/claude/install.sh +86 -0
  10. adapters/claude/sdk_dispatcher.py +871 -0
  11. adapters/claude/settings.local.template.json +31 -0
  12. adapters/claude/settings.template.json +808 -0
  13. adapters/claude/update_mcp_json.py +85 -0
  14. adapters/codex/adapter.yaml +254 -0
  15. adapters/codex/chat_provider.py +230 -0
  16. adapters/codex/commands/formula-f1.md +129 -0
  17. adapters/codex/commands/formula-f10.md +100 -0
  18. adapters/codex/commands/formula-f11.md +123 -0
  19. adapters/codex/commands/formula-f2.md +139 -0
  20. adapters/codex/commands/formula-f3.md +127 -0
  21. adapters/codex/commands/formula-f4.md +101 -0
  22. adapters/codex/commands/formula-f5.md +135 -0
  23. adapters/codex/commands/formula-f6.md +147 -0
  24. adapters/codex/commands/formula-f7.md +111 -0
  25. adapters/codex/commands/formula-f8.md +133 -0
  26. adapters/codex/commands/formula-f9.md +112 -0
  27. adapters/codex/enable_codex_hooks.py +94 -0
  28. adapters/codex/ensure_codex_mcp.py +124 -0
  29. adapters/codex/hooks/codex-merge-hook-output.py +72 -0
  30. adapters/codex/hooks/codex-normalize-edit.py +96 -0
  31. adapters/codex/hooks/codex-postedit-dispatch.sh +75 -0
  32. adapters/codex/hooks/codex-posttool-dispatch.sh +70 -0
  33. adapters/codex/hooks/codex-preedit-dispatch.sh +83 -0
  34. adapters/codex/hooks/codex-pretool-dispatch.sh +82 -0
  35. adapters/codex/hooks/codex-sessionend-dispatch.sh +20 -0
  36. adapters/codex/hooks/codex-sessionstart-dispatch.sh +68 -0
  37. adapters/codex/hooks/codex-stop-dispatch.sh +73 -0
  38. adapters/codex/hooks/codex-userpromptsubmit-dispatch.sh +74 -0
  39. adapters/codex/hooks.template.json +208 -0
  40. adapters/codex/install.sh +83 -0
  41. adapters/codex/sdk_dispatcher.py +449 -0
  42. board_os/__init__.py +39 -0
  43. board_os/_agent_runtime.py +256 -0
  44. board_os/config.py +421 -0
  45. board_os/git_coherence.py +107 -0
  46. board_os/hub_adapter_manifest.py +140 -0
  47. board_os/mcp_tools.py +3228 -0
  48. board_os/migration.py +166 -0
  49. board_os/parser.py +317 -0
  50. board_os/presence.py +156 -0
  51. board_os/sync.py +320 -0
  52. board_os/transition_gates.py +224 -0
  53. board_os/transition_gates_cli.py +272 -0
  54. board_os/transition_gates_validator.py +551 -0
  55. board_os/verify_suites.py +126 -0
  56. board_os/verify_suites_cli.py +328 -0
  57. board_os/workflow.py +967 -0
  58. cli/__init__.py +0 -0
  59. cli/_data_types.py +248 -0
  60. cli/_init_helpers.py +587 -0
  61. cli/_resources.py +100 -0
  62. cli/adapter_registry.py +239 -0
  63. cli/add_stack.py +315 -0
  64. cli/aggregator.py +438 -0
  65. cli/board_commands.py +1218 -0
  66. cli/brain_commands.py +255 -0
  67. cli/cognition.py +345 -0
  68. cli/config_composer.py +349 -0
  69. cli/core_version.py +41 -0
  70. cli/cron_commands.py +278 -0
  71. cli/db_reset.py +298 -0
  72. cli/doc_commands.py +111 -0
  73. cli/doctor.py +2953 -0
  74. cli/doctor_board.py +365 -0
  75. cli/doctor_extras.py +1121 -0
  76. cli/doctor_graph.py +608 -0
  77. cli/doctor_tokens.py +254 -0
  78. cli/graph_commands.py +1265 -0
  79. cli/hook_renderer.py +393 -0
  80. cli/hub_commands.py +580 -0
  81. cli/list_adapters.py +79 -0
  82. cli/list_stacks.py +105 -0
  83. cli/logs_commands.py +89 -0
  84. cli/main.py +3070 -0
  85. cli/materialize_file.py +65 -0
  86. cli/mcp_start.py +153 -0
  87. cli/module_commands.py +513 -0
  88. cli/pr_commands.py +2024 -0
  89. cli/preset_commands.py +126 -0
  90. cli/preset_registry.py +171 -0
  91. cli/project_overrides.py +119 -0
  92. cli/registry.py +365 -0
  93. cli/remove_stack.py +492 -0
  94. cli/renderer.py +620 -0
  95. cli/setup.py +464 -0
  96. cli/skill_commands.py +689 -0
  97. cli/skill_registry.py +235 -0
  98. cli/skills_list.py +332 -0
  99. cli/stack_lint.py +351 -0
  100. cli/stack_registry.py +688 -0
  101. cli/subsystems.py +335 -0
  102. cli/sync_all.py +310 -0
  103. cli/tail_command.py +410 -0
  104. cli/update.py +608 -0
  105. cli/verify_since_edit.py +439 -0
  106. coding_os-0.3.2.dist-info/METADATA +508 -0
  107. coding_os-0.3.2.dist-info/RECORD +1304 -0
  108. coding_os-0.3.2.dist-info/WHEEL +5 -0
  109. coding_os-0.3.2.dist-info/entry_points.txt +5 -0
  110. coding_os-0.3.2.dist-info/licenses/LICENSE +201 -0
  111. coding_os-0.3.2.dist-info/top_level.txt +10 -0
  112. core/__init__.py +0 -0
  113. core/board_os/__init__.py +39 -0
  114. core/board_os/_agent_runtime.py +256 -0
  115. core/board_os/config.py +421 -0
  116. core/board_os/git_coherence.py +107 -0
  117. core/board_os/hub_adapter_manifest.py +140 -0
  118. core/board_os/mcp_tools.py +3228 -0
  119. core/board_os/migration.py +166 -0
  120. core/board_os/parser.py +317 -0
  121. core/board_os/presence.py +156 -0
  122. core/board_os/sync.py +320 -0
  123. core/board_os/transition-gates.yaml +176 -0
  124. core/board_os/transition_gates.py +224 -0
  125. core/board_os/transition_gates_cli.py +272 -0
  126. core/board_os/transition_gates_validator.py +551 -0
  127. core/board_os/verify-suites.yaml +113 -0
  128. core/board_os/verify_suites.py +126 -0
  129. core/board_os/verify_suites_cli.py +328 -0
  130. core/board_os/workflow.py +967 -0
  131. core/commands/board.md +27 -0
  132. core/commands/classify.md +23 -0
  133. core/commands/compose.md +23 -0
  134. core/commands/daily.md +31 -0
  135. core/commands/diagnose.md +7 -0
  136. core/commands/memory-search.md +23 -0
  137. core/commands/new-project.md +33 -0
  138. core/commands/retro.md +38 -0
  139. core/commands/review.md +14 -0
  140. core/commands/task.md +17 -0
  141. core/commands/verify.md +34 -0
  142. core/docs/thinking_os-final-edition.md +1449 -0
  143. core/doctor-config.yaml +74 -0
  144. core/graph_os/__init__.py +29 -0
  145. core/graph_os/backend.py +233 -0
  146. core/graph_os/backends/__init__.py +13 -0
  147. core/graph_os/backends/sqlite_backend.py +1053 -0
  148. core/graph_os/bench/__init__.py +17 -0
  149. core/graph_os/bench/fixtures.py +56 -0
  150. core/graph_os/bench/harness.py +95 -0
  151. core/graph_os/bench/persian_precision.py +142 -0
  152. core/graph_os/bench/scale_500k.py +120 -0
  153. core/graph_os/bench/token_cost.py +170 -0
  154. core/graph_os/bench/viewer_fps.py +110 -0
  155. core/graph_os/communities.py +410 -0
  156. core/graph_os/enterprise.py +218 -0
  157. core/graph_os/entry_points.py +226 -0
  158. core/graph_os/extractors/__init__.py +25 -0
  159. core/graph_os/extractors/code_generic.py +914 -0
  160. core/graph_os/extractors/code_go.py +1422 -0
  161. core/graph_os/extractors/code_json.py +340 -0
  162. core/graph_os/extractors/code_php.py +979 -0
  163. core/graph_os/extractors/code_python.py +1454 -0
  164. core/graph_os/extractors/code_shell.py +538 -0
  165. core/graph_os/extractors/code_toml.py +302 -0
  166. core/graph_os/extractors/code_ts.py +1665 -0
  167. core/graph_os/extractors/code_yaml.py +394 -0
  168. core/graph_os/extractors/contracts.py +1592 -0
  169. core/graph_os/extractors/md_links.py +890 -0
  170. core/graph_os/extractors/task_deps.py +345 -0
  171. core/graph_os/groups/__init__.py +22 -0
  172. core/graph_os/groups/cross_repo.py +156 -0
  173. core/graph_os/groups/manifest.py +141 -0
  174. core/graph_os/ingest/__init__.py +19 -0
  175. core/graph_os/ingest/base.py +306 -0
  176. core/graph_os/ingest/github.py +112 -0
  177. core/graph_os/ingest/zip.py +95 -0
  178. core/graph_os/toolchain.py +393 -0
  179. core/graph_os/tools/__init__.py +9 -0
  180. core/graph_os/tools/graph.py +5573 -0
  181. core/graph_os/tools/reindex_dispatch.py +730 -0
  182. core/graph_os/tree_sitter_overlay.py +235 -0
  183. core/graph_os/types.py +252 -0
  184. core/graph_os/vec_index.py +277 -0
  185. core/graph_os/viewer/__init__.py +12 -0
  186. core/graph_os/viewer/exporter.py +93 -0
  187. core/graph_os/viewer/template.py +189 -0
  188. core/hooks/_helpers/_paths.py +40 -0
  189. core/hooks/_helpers/advance_role.py +72 -0
  190. core/hooks/_helpers/auto_compose.py +228 -0
  191. core/hooks/_helpers/auto_validate_lessons.py +55 -0
  192. core/hooks/_helpers/branch_guard_check.py +796 -0
  193. core/hooks/_helpers/check_commit_message.py +108 -0
  194. core/hooks/_helpers/check_dangerous_rm.py +80 -0
  195. core/hooks/_helpers/check_git_bypass.py +154 -0
  196. core/hooks/_helpers/check_git_destructive.py +77 -0
  197. core/hooks/_helpers/check_settings_write.py +97 -0
  198. core/hooks/_helpers/consume_override.py +51 -0
  199. core/hooks/_helpers/context_budget.py +77 -0
  200. core/hooks/_helpers/cos_say_json.py +103 -0
  201. core/hooks/_helpers/destructive_edit_check.py +163 -0
  202. core/hooks/_helpers/detect_status_transition.py +82 -0
  203. core/hooks/_helpers/digest_regen.py +56 -0
  204. core/hooks/_helpers/doc_sync_check.py +498 -0
  205. core/hooks/_helpers/drain_embedding_outbox.py +52 -0
  206. core/hooks/_helpers/extract_additional_context.py +51 -0
  207. core/hooks/_helpers/extract_commit_msg_arg.py +74 -0
  208. core/hooks/_helpers/git_command_parse.py +424 -0
  209. core/hooks/_helpers/git_settings_fields.py +47 -0
  210. core/hooks/_helpers/graph_context_match.py +37 -0
  211. core/hooks/_helpers/graph_marker_check.py +70 -0
  212. core/hooks/_helpers/jit_recall.py +56 -0
  213. core/hooks/_helpers/json_field.py +41 -0
  214. core/hooks/_helpers/narrative_signal.py +59 -0
  215. core/hooks/_helpers/observation_count.py +31 -0
  216. core/hooks/_helpers/pre_commit_batch.py +177 -0
  217. core/hooks/_helpers/pre_commit_fake_input.py +42 -0
  218. core/hooks/_helpers/presence_gc.py +102 -0
  219. core/hooks/_helpers/presence_write.py +167 -0
  220. core/hooks/_helpers/recover_indirect.py +35 -0
  221. core/hooks/_helpers/routing_evolution.py +104 -0
  222. core/hooks/_helpers/session_recap.py +72 -0
  223. core/hooks/_helpers/skill_primer.py +229 -0
  224. core/hooks/_helpers/task_sync.py +59 -0
  225. core/hooks/_helpers/tool_failure_capture.py +147 -0
  226. core/hooks/_helpers/trajectory_autosnap.py +278 -0
  227. core/hooks/_helpers/trajectory_startup.py +62 -0
  228. core/hooks/_helpers/turn_summary.py +82 -0
  229. core/hooks/_helpers/validate_task_frontmatter.py +98 -0
  230. core/hooks/_helpers/wip_limit_check.py +103 -0
  231. core/hooks/_helpers/wip_lines.py +53 -0
  232. core/hooks/_helpers/work_log_append.py +89 -0
  233. core/hooks/_helpers/wrap_dispatch_output.py +82 -0
  234. core/hooks/advance-role.sh +48 -0
  235. core/hooks/agent-presence.sh +179 -0
  236. core/hooks/auto-brain-decay.sh +184 -0
  237. core/hooks/auto-compose-roles.sh +83 -0
  238. core/hooks/auto-graph-reconcile-shell.sh +119 -0
  239. core/hooks/auto-regen-doc-index.sh +120 -0
  240. core/hooks/auto-reindex-docs.sh +130 -0
  241. core/hooks/auto-task-sync.sh +56 -0
  242. core/hooks/auto-trace-rotate.sh +88 -0
  243. core/hooks/block-bad-patterns.sh +212 -0
  244. core/hooks/block-dangerous-commands.sh +182 -0
  245. core/hooks/block-hardcoded-literals.sh +90 -0
  246. core/hooks/block-migration-conflict.sh +114 -0
  247. core/hooks/block-protected-files.sh +129 -0
  248. core/hooks/block-secrets.sh +185 -0
  249. core/hooks/block-shared-tree-edit.sh +75 -0
  250. core/hooks/block-uv-heredoc.sh +78 -0
  251. core/hooks/branch-guard.sh +122 -0
  252. core/hooks/capture-observation.sh +76 -0
  253. core/hooks/capture-tool-failure.sh +24 -0
  254. core/hooks/capture-work-log.sh +89 -0
  255. core/hooks/check-agents-md-refs.sh +75 -0
  256. core/hooks/check-agents-md-size.sh +49 -0
  257. core/hooks/check-capture-worked.sh +148 -0
  258. core/hooks/check-doc-size.sh +61 -0
  259. core/hooks/check-mcp-extras.sh +92 -0
  260. core/hooks/check-state.sh +87 -0
  261. core/hooks/classify-task-mode.sh +103 -0
  262. core/hooks/cos-env.sh +1301 -0
  263. core/hooks/drain-embedding-outbox.sh +24 -0
  264. core/hooks/enforce-anti-ambiguity.sh +74 -0
  265. core/hooks/enforce-commit-message.sh +73 -0
  266. core/hooks/enforce-doc-anchor.sh +224 -0
  267. core/hooks/enforce-doc-sync.sh +206 -0
  268. core/hooks/enforce-graph-context.sh +89 -0
  269. core/hooks/enforce-graph-first-read.sh +94 -0
  270. core/hooks/enforce-memory-check.sh +128 -0
  271. core/hooks/enforce-rename-plan.sh +78 -0
  272. core/hooks/enforce-scaffold-boundary.sh +68 -0
  273. core/hooks/enforce-skill.sh +125 -0
  274. core/hooks/enforce-task-body.sh +52 -0
  275. core/hooks/enforce-task-start.sh +81 -0
  276. core/hooks/enforce-task-transition.sh +75 -0
  277. core/hooks/enforce-template.sh +143 -0
  278. core/hooks/enforce-verify.sh +112 -0
  279. core/hooks/enforce-wip-limit.sh +43 -0
  280. core/hooks/enforce-zoom.sh +69 -0
  281. core/hooks/ensure-hub-up.sh +67 -0
  282. core/hooks/inject-mcp-caller-session.sh +70 -0
  283. core/hooks/jit-recall.sh +65 -0
  284. core/hooks/link-commit-to-task.sh +143 -0
  285. core/hooks/lint-task.sh +40 -0
  286. core/hooks/nudge-docs-first.sh +71 -0
  287. core/hooks/nudge-git-mode.sh +29 -0
  288. core/hooks/nudge-graph-os.sh +118 -0
  289. core/hooks/nudge-learn-narrative.sh +36 -0
  290. core/hooks/nudge-model-routing.sh +32 -0
  291. core/hooks/nudge-reentry.sh +101 -0
  292. core/hooks/nudge-reuse-first.sh +68 -0
  293. core/hooks/nudge-task-discovery.sh +81 -0
  294. core/hooks/nudge-thinking-os.sh +109 -0
  295. core/hooks/pr-reap.sh +23 -0
  296. core/hooks/reclaim-sweep.sh +58 -0
  297. core/hooks/record-verify-auto.sh +77 -0
  298. core/hooks/record-verify.sh +74 -0
  299. core/hooks/regen-reminder.sh +104 -0
  300. core/hooks/registry.yaml +1262 -0
  301. core/hooks/remind-daily.sh +27 -0
  302. core/hooks/remind-dogfood.sh +70 -0
  303. core/hooks/remind-learn-validate.sh +94 -0
  304. core/hooks/rules-primer.sh +50 -0
  305. core/hooks/search-enforce-inventory.sh +108 -0
  306. core/hooks/search-verify-remaining.sh +132 -0
  307. core/hooks/session-context.sh +729 -0
  308. core/hooks/session-end.sh +145 -0
  309. core/hooks/session-skill-primer.sh +43 -0
  310. core/hooks/snapshot-transcript.sh +56 -0
  311. core/hooks/sync-task-current.sh +85 -0
  312. core/hooks/test-first-reminder.sh +120 -0
  313. core/hooks/test-governor.sh +173 -0
  314. core/hooks/thinking_os-gate.sh +52 -0
  315. core/hooks/track-backtrack.sh +35 -0
  316. core/hooks/track-discovery.sh +121 -0
  317. core/hooks/track-skill.sh +53 -0
  318. core/hooks/validate-task-frontmatter.sh +49 -0
  319. core/hooks/verify-rename-callers.sh +119 -0
  320. core/hooks/warn-abandoned-task.sh +99 -0
  321. core/hooks/warn-destructive-edit.sh +64 -0
  322. core/hooks/warn-diff-size.sh +43 -0
  323. core/hooks/warn-graph-empty.sh +81 -0
  324. core/hooks/warn-mcp-down.sh +190 -0
  325. core/hooks/write-state.sh +55 -0
  326. core/logging_os/__init__.py +33 -0
  327. core/logging_os/api.py +127 -0
  328. core/logging_os/bridge.py +80 -0
  329. core/logging_os/config.py +172 -0
  330. core/logging_os/fingerprint.py +25 -0
  331. core/logging_os/redact.py +53 -0
  332. core/logging_os/render.py +83 -0
  333. core/logging_os/sinks.py +164 -0
  334. core/rules/anti-overengineering.md +44 -0
  335. core/rules/api-contract-discipline.md +41 -0
  336. core/rules/dimension-registry.md +155 -0
  337. core/rules/git-workflow.md +57 -0
  338. core/rules/memory.md +46 -0
  339. core/rules/model-routing.md +22 -0
  340. core/rules/skill-enforcement.md +75 -0
  341. core/rules/test-discipline.md +38 -0
  342. core/rules/thinking_os.md +48 -0
  343. core/rules/transparency-banner.md +37 -0
  344. core/runtime_paths.yaml +36 -0
  345. core/scaffold_manifest.json +14430 -0
  346. core/scheduled/__init__.py +0 -0
  347. core/scheduled/_activity.py +126 -0
  348. core/scheduled/_state.py +113 -0
  349. core/scheduled/config.py +86 -0
  350. core/scheduled/dep_reconcile.py +135 -0
  351. core/scheduled/error_sweep.py +137 -0
  352. core/scheduled/nightly.py +930 -0
  353. core/scheduled/responsive_extract.py +65 -0
  354. core/schemas/adapter.schema.json +269 -0
  355. core/schemas/preset.schema.json +50 -0
  356. core/schemas/skill.schema.json +81 -0
  357. core/schemas/stack.schema.json +404 -0
  358. core/scripts/_lib.sh +9 -0
  359. core/scripts/docs-lint.sh +228 -0
  360. core/scripts/docs-nav-fix.sh +133 -0
  361. core/scripts/docs-staleness-check.sh +154 -0
  362. core/scripts/install-adapter.sh +266 -0
  363. core/scripts/link-stack-skills.sh +52 -0
  364. core/scripts/log-latest.sh +106 -0
  365. core/scripts/log-search.sh +89 -0
  366. core/scripts/log-write.sh +134 -0
  367. core/scripts/ref-resolve.sh +71 -0
  368. core/skills/a11y/SKILL.md +305 -0
  369. core/skills/a11y/assets/a11y-checklist.md +137 -0
  370. core/skills/a11y/references/aria-and-focus.md +247 -0
  371. core/skills/a11y/references/rn-accessibility.md +343 -0
  372. core/skills/a11y/references/screen-reader-testing.md +190 -0
  373. core/skills/agent-memory/SKILL.md +191 -0
  374. core/skills/agent-memory/assets/memory-checklist.md +21 -0
  375. core/skills/agent-memory/references/memory-recipes.md +57 -0
  376. core/skills/api-design/SKILL.md +232 -0
  377. core/skills/api-design/assets/api-design-checklist.md +110 -0
  378. core/skills/api-design/references/error-envelope.md +381 -0
  379. core/skills/api-design/references/idempotency-pagination.md +312 -0
  380. core/skills/api-design/references/rest-contracts.md +426 -0
  381. core/skills/auth-patterns/SKILL.md +352 -0
  382. core/skills/auth-patterns/assets/auth-checklist.md +118 -0
  383. core/skills/auth-patterns/references/jwt-and-service-tokens.md +343 -0
  384. core/skills/auth-patterns/references/passkeys-2fa.md +289 -0
  385. core/skills/auth-patterns/references/sessions-vs-jwt.md +230 -0
  386. core/skills/auth-patterns/scripts/cookie-flag-check.py +146 -0
  387. core/skills/backend-fundamentals/SKILL.md +238 -0
  388. core/skills/backend-fundamentals/assets/backend-checklist.md +27 -0
  389. core/skills/backend-fundamentals/references/backend-patterns.md +56 -0
  390. core/skills/backend-fundamentals/scripts/check_layering.py +83 -0
  391. core/skills/clean-code/SKILL.md +642 -0
  392. core/skills/clean-code/scripts/audit-fail-closed.py +167 -0
  393. core/skills/codebase-explorer/SKILL.md +89 -0
  394. core/skills/codebase-explorer/assets/reading-checklist.md +24 -0
  395. core/skills/codebase-explorer/references/reading-strategies.md +52 -0
  396. core/skills/codebase-explorer/scripts/outline.py +99 -0
  397. core/skills/db-design/SKILL.md +327 -0
  398. core/skills/db-design/assets/migration-template.sql +49 -0
  399. core/skills/db-design/references/migration-discipline.md +290 -0
  400. core/skills/db-design/references/postgres-patterns.md +340 -0
  401. core/skills/db-design/scripts/migration-safety.sh +150 -0
  402. core/skills/deployment-cicd/SKILL.md +260 -0
  403. core/skills/deployment-cicd/assets/deploy-checklist.md +26 -0
  404. core/skills/deployment-cicd/references/pipeline-and-release.md +54 -0
  405. core/skills/deployment-cicd/scripts/lint_workflow.py +79 -0
  406. core/skills/docker/SKILL.md +114 -0
  407. core/skills/docker/assets/dockerfile-checklist.md +31 -0
  408. core/skills/docker/references/compose-patterns.md +66 -0
  409. core/skills/docker/references/dockerfile-optimization.md +64 -0
  410. core/skills/docker/scripts/lint_dockerfile.sh +48 -0
  411. core/skills/docker/versions.json +16 -0
  412. core/skills/end-to-end-testing/SKILL.md +101 -0
  413. core/skills/end-to-end-testing/assets/e2e-checklist.md +23 -0
  414. core/skills/end-to-end-testing/references/maestro.md +63 -0
  415. core/skills/end-to-end-testing/references/playwright.md +68 -0
  416. core/skills/end-to-end-testing/scripts/lint_e2e.py +88 -0
  417. core/skills/end-to-end-testing/versions.json +16 -0
  418. core/skills/frontend-design/SKILL.md +76 -0
  419. core/skills/frontend-design/assets/design-checklist.md +29 -0
  420. core/skills/frontend-design/references/design-principles.md +65 -0
  421. core/skills/frontend-design/scripts/check_contrast.py +89 -0
  422. core/skills/frontend-fundamentals/SKILL.md +213 -0
  423. core/skills/frontend-fundamentals/assets/frontend-checklist.md +25 -0
  424. core/skills/frontend-fundamentals/references/rendering-and-state.md +66 -0
  425. core/skills/frontend-fundamentals/scripts/check_frontend.py +86 -0
  426. core/skills/graph-explorer/SKILL.md +215 -0
  427. core/skills/graph-explorer/scripts/explain-impact.sh +64 -0
  428. core/skills/graphql/SKILL.md +187 -0
  429. core/skills/grpc-microservices/SKILL.md +174 -0
  430. core/skills/hexagonal-architecture/SKILL.md +199 -0
  431. core/skills/hexagonal-architecture/assets/folder-scaffold.md +233 -0
  432. core/skills/hexagonal-architecture/references/anti-patterns.md +129 -0
  433. core/skills/hexagonal-architecture/references/go-fiber-layout.md +429 -0
  434. core/skills/hexagonal-architecture/references/python-fastapi-layout.md +453 -0
  435. core/skills/hexagonal-architecture/references/react-native-layout.md +428 -0
  436. core/skills/i18n/SKILL.md +126 -0
  437. core/skills/incident-response/SKILL.md +225 -0
  438. core/skills/incident-response/assets/incident-checklist.md +29 -0
  439. core/skills/incident-response/references/severity-and-runbook.md +53 -0
  440. core/skills/incident-response/scripts/classify_severity.py +86 -0
  441. core/skills/linux-sysadmin/SKILL.md +115 -0
  442. core/skills/linux-sysadmin/assets/hardening-checklist.md +29 -0
  443. core/skills/linux-sysadmin/references/ssh-hardening.md +62 -0
  444. core/skills/linux-sysadmin/references/systemd-and-services.md +77 -0
  445. core/skills/linux-sysadmin/scripts/triage.sh +50 -0
  446. core/skills/linux-sysadmin/versions.json +17 -0
  447. core/skills/llm-patterns/SKILL.md +410 -0
  448. core/skills/llm-patterns/assets/llm-feature-checklist.md +26 -0
  449. core/skills/llm-patterns/references/rag-and-evals.md +59 -0
  450. core/skills/llm-patterns/scripts/estimate_tokens.py +75 -0
  451. core/skills/messaging-queues/SKILL.md +142 -0
  452. core/skills/mobile-fundamentals/SKILL.md +406 -0
  453. core/skills/mobile-fundamentals/assets/mobile-launch-checklist.md +130 -0
  454. core/skills/mobile-fundamentals/references/navigation-and-deep-links.md +337 -0
  455. core/skills/mobile-fundamentals/references/offline-sync.md +339 -0
  456. core/skills/node-backend/SKILL.md +114 -0
  457. core/skills/node-backend/assets/node-checklist.md +25 -0
  458. core/skills/node-backend/references/async-and-errors.md +67 -0
  459. core/skills/node-backend/references/event-loop.md +65 -0
  460. core/skills/node-backend/scripts/check_package.py +78 -0
  461. core/skills/node-backend/versions.json +17 -0
  462. core/skills/observability/SKILL.md +289 -0
  463. core/skills/observability/assets/observability-checklist.md +27 -0
  464. core/skills/observability/references/instrumentation.md +57 -0
  465. core/skills/observability/scripts/lint_logging.py +77 -0
  466. core/skills/payments/SKILL.md +102 -0
  467. core/skills/performance/SKILL.md +305 -0
  468. core/skills/performance/assets/perf-checklist.md +143 -0
  469. core/skills/performance/references/mobile-performance.md +249 -0
  470. core/skills/performance/references/web-vitals.md +209 -0
  471. core/skills/php/SKILL.md +116 -0
  472. core/skills/php/assets/php-checklist.md +26 -0
  473. core/skills/php/references/modern-php.md +64 -0
  474. core/skills/php/references/security.md +72 -0
  475. core/skills/php/scripts/scan_php_smells.py +99 -0
  476. core/skills/php/versions.json +9 -0
  477. core/skills/pr-mode-driver/SKILL.md +65 -0
  478. core/skills/realtime-websockets/SKILL.md +152 -0
  479. core/skills/redis/SKILL.md +105 -0
  480. core/skills/redis/assets/redis-checklist.md +27 -0
  481. core/skills/redis/references/operations.md +66 -0
  482. core/skills/redis/references/patterns.md +62 -0
  483. core/skills/redis/scripts/analyze_info.py +101 -0
  484. core/skills/redis/versions.json +9 -0
  485. core/skills/search/SKILL.md +91 -0
  486. core/skills/search/references/grep.md +76 -0
  487. core/skills/search/scripts/verify-count.sh +73 -0
  488. core/skills/search-infra/SKILL.md +109 -0
  489. core/skills/security-mobile/SKILL.md +394 -0
  490. core/skills/security-mobile/assets/mobile-security-checklist.md +117 -0
  491. core/skills/security-mobile/references/masvs-l1-checklist.md +127 -0
  492. core/skills/security-web/SKILL.md +217 -0
  493. core/skills/security-web/assets/security-web-checklist.md +167 -0
  494. core/skills/security-web/references/owasp-top-10.md +551 -0
  495. core/skills/security-web/references/supply-chain.md +179 -0
  496. core/skills/security-web/scripts/csp-check.sh +153 -0
  497. core/skills/shell-scripting/SKILL.md +128 -0
  498. core/skills/shell-scripting/assets/script-checklist.md +33 -0
  499. core/skills/shell-scripting/references/argument-parsing.md +84 -0
  500. core/skills/shell-scripting/references/bash-robustness.md +78 -0
  501. core/skills/shell-scripting/scripts/lint_script.sh +53 -0
  502. core/skills/shell-scripting/scripts/new_script.py +131 -0
  503. core/skills/shell-scripting/versions.json +23 -0
  504. core/skills/sql-authoring/SKILL.md +104 -0
  505. core/skills/sql-authoring/assets/query-review-checklist.md +26 -0
  506. core/skills/sql-authoring/references/query-patterns.md +89 -0
  507. core/skills/sql-authoring/references/reading-explain.md +52 -0
  508. core/skills/sql-authoring/scripts/analyze_plan.py +100 -0
  509. core/skills/sql-authoring/versions.json +17 -0
  510. core/skills/state-management/SKILL.md +428 -0
  511. core/skills/state-management/references/tanstack-query-recipes.md +301 -0
  512. core/skills/state-management/references/zustand-recipes.md +364 -0
  513. core/skills/supabase/SKILL.md +108 -0
  514. core/skills/supabase/assets/supabase-checklist.md +26 -0
  515. core/skills/supabase/references/realtime-and-storage.md +55 -0
  516. core/skills/supabase/references/rls-and-auth.md +65 -0
  517. core/skills/supabase/scripts/check_rls.py +88 -0
  518. core/skills/supabase/versions.json +9 -0
  519. core/skills/task-driver/SKILL.md +272 -0
  520. core/skills/task-driver/scripts/task-lint.sh +163 -0
  521. core/skills/technical-writing/SKILL.md +85 -0
  522. core/skills/technical-writing/assets/doc-checklist.md +29 -0
  523. core/skills/technical-writing/references/doc-anatomy.md +53 -0
  524. core/skills/technical-writing/references/writing-craft.md +59 -0
  525. core/skills/technical-writing/scripts/new_doc.py +81 -0
  526. core/skills/terraform-k8s/SKILL.md +133 -0
  527. core/skills/testing-strategy/SKILL.md +266 -0
  528. core/skills/testing-strategy/assets/test-review-checklist.md +25 -0
  529. core/skills/testing-strategy/references/test-types.md +52 -0
  530. core/skills/testing-strategy/scripts/coverage_gate.py +79 -0
  531. core/skills/thinking_os/SKILL.md +288 -0
  532. core/skills/thinking_os/scripts/classify.sh +122 -0
  533. core/skills/typescript/SKILL.md +110 -0
  534. core/skills/typescript/assets/typescript-checklist.md +24 -0
  535. core/skills/typescript/references/strictness.md +53 -0
  536. core/skills/typescript/references/type-system.md +86 -0
  537. core/skills/typescript/scripts/check_tsconfig.py +92 -0
  538. core/skills/typescript/versions.json +9 -0
  539. core/subsystems.yaml +202 -0
  540. core/thinking_os/__init__.py +1 -0
  541. core/thinking_os/_agent_markers.py +32 -0
  542. core/thinking_os/agents/README.md +71 -0
  543. core/thinking_os/agents/analyst.md +139 -0
  544. core/thinking_os/agents/architect.md +127 -0
  545. core/thinking_os/agents/debugger.md +111 -0
  546. core/thinking_os/agents/deployer.md +112 -0
  547. core/thinking_os/agents/distiller.md +28 -0
  548. core/thinking_os/agents/documenter.md +101 -0
  549. core/thinking_os/agents/implementer.md +135 -0
  550. core/thinking_os/agents/internal/session_observer.md +39 -0
  551. core/thinking_os/agents/observer.md +100 -0
  552. core/thinking_os/agents/onboarder.md +81 -0
  553. core/thinking_os/agents/refactorer.md +123 -0
  554. core/thinking_os/agents/repairer.md +47 -0
  555. core/thinking_os/agents/researcher.md +129 -0
  556. core/thinking_os/agents/reviewer.md +147 -0
  557. core/thinking_os/agents/security_auditor.md +133 -0
  558. core/thinking_os/background.py +405 -0
  559. core/thinking_os/bootstrap_outcomes.py +200 -0
  560. core/thinking_os/budget.py +302 -0
  561. core/thinking_os/capture.py +495 -0
  562. core/thinking_os/cognition.py +516 -0
  563. core/thinking_os/cognition_schemas.py +517 -0
  564. core/thinking_os/compress.py +192 -0
  565. core/thinking_os/concepts.py +233 -0
  566. core/thinking_os/dashboard.py +159 -0
  567. core/thinking_os/database.py +2883 -0
  568. core/thinking_os/decay.py +393 -0
  569. core/thinking_os/digest.py +295 -0
  570. core/thinking_os/dispatcher.py +192 -0
  571. core/thinking_os/dispatcher_helpers.py +48 -0
  572. core/thinking_os/dispatchers/__init__.py +3 -0
  573. core/thinking_os/dispatchers/default.py +47 -0
  574. core/thinking_os/distill.py +192 -0
  575. core/thinking_os/doc_indexer.py +905 -0
  576. core/thinking_os/embeddings.py +943 -0
  577. core/thinking_os/formula_composer.py +556 -0
  578. core/thinking_os/gate_marker.py +75 -0
  579. core/thinking_os/graph.py +296 -0
  580. core/thinking_os/graph_indexer.py +360 -0
  581. core/thinking_os/health_check.py +517 -0
  582. core/thinking_os/impact.py +119 -0
  583. core/thinking_os/memory_gc.py +356 -0
  584. core/thinking_os/migrator_embeddings.py +318 -0
  585. core/thinking_os/precision.py +194 -0
  586. core/thinking_os/presets/registry.yaml +127 -0
  587. core/thinking_os/record_outcome.py +399 -0
  588. core/thinking_os/repair.py +105 -0
  589. core/thinking_os/retrieval_quality.py +239 -0
  590. core/thinking_os/roles/analyst.yaml +83 -0
  591. core/thinking_os/roles/architect.yaml +88 -0
  592. core/thinking_os/roles/debugger.yaml +71 -0
  593. core/thinking_os/roles/deployer.yaml +71 -0
  594. core/thinking_os/roles/documenter.yaml +72 -0
  595. core/thinking_os/roles/implementer.yaml +83 -0
  596. core/thinking_os/roles/observer.yaml +70 -0
  597. core/thinking_os/roles/refactorer.yaml +71 -0
  598. core/thinking_os/roles/researcher.yaml +72 -0
  599. core/thinking_os/roles/reviewer.yaml +79 -0
  600. core/thinking_os/roles/security_auditor.yaml +83 -0
  601. core/thinking_os/roles_state.py +168 -0
  602. core/thinking_os/sanitizer.py +320 -0
  603. core/thinking_os/server.py +3160 -0
  604. core/thinking_os/session_enrich.py +272 -0
  605. core/thinking_os/session_observe_worker.py +111 -0
  606. core/thinking_os/session_startup.py +74 -0
  607. core/thinking_os/session_summary.py +245 -0
  608. core/thinking_os/situations/registry.yaml +99 -0
  609. core/thinking_os/task_analyzer.py +462 -0
  610. core/thinking_os/task_parser.py +342 -0
  611. core/thinking_os/task_sync.py +73 -0
  612. core/thinking_os/tools/__init__.py +6 -0
  613. core/thinking_os/tools/_shared.py +947 -0
  614. core/thinking_os/tools/cognition.py +1867 -0
  615. core/thinking_os/tools/docs.py +770 -0
  616. core/thinking_os/tools/learning.py +2078 -0
  617. core/thinking_os/tools/logs.py +79 -0
  618. core/thinking_os/tools/memory.py +840 -0
  619. core/thinking_os/tools/metrics.py +200 -0
  620. core/thinking_os/tools/retrieve.py +415 -0
  621. core/thinking_os/tools/routing.py +658 -0
  622. core/thinking_os/tools/tasks.py +449 -0
  623. core/thinking_os/tools/trajectory.py +181 -0
  624. core/thinking_os/tracing.py +235 -0
  625. core/web/__init__.py +5 -0
  626. core/web/_cache.py +118 -0
  627. core/web/_deps.py +56 -0
  628. core/web/_envelope.py +85 -0
  629. core/web/_project_context.py +140 -0
  630. core/web/chat_providers.py +108 -0
  631. core/web/init_jobs.py +216 -0
  632. core/web/routes/__init__.py +25 -0
  633. core/web/routes/_bounded_read.py +76 -0
  634. core/web/routes/board.py +1089 -0
  635. core/web/routes/cognition.py +1838 -0
  636. core/web/routes/config.py +635 -0
  637. core/web/routes/graph.py +513 -0
  638. core/web/routes/health.py +180 -0
  639. core/web/routes/hooks.py +288 -0
  640. core/web/routes/hub.py +1219 -0
  641. core/web/routes/logs.py +374 -0
  642. core/web/routes/metrics.py +43 -0
  643. core/web/routes/observability.py +400 -0
  644. core/web/routes/patterns.py +227 -0
  645. core/web/routes/presence.py +609 -0
  646. core/web/routes/roles.py +446 -0
  647. core/web/routes/scheduled.py +261 -0
  648. core/web/routes/search.py +238 -0
  649. core/web/routes/sessions.py +220 -0
  650. core/web/routes/settings.py +363 -0
  651. core/web/routes/stream.py +547 -0
  652. core/web/security.py +157 -0
  653. core/web/server.py +283 -0
  654. graph_os/__init__.py +29 -0
  655. graph_os/backend.py +233 -0
  656. graph_os/backends/__init__.py +13 -0
  657. graph_os/backends/sqlite_backend.py +1053 -0
  658. graph_os/communities.py +410 -0
  659. graph_os/enterprise.py +218 -0
  660. graph_os/entry_points.py +226 -0
  661. graph_os/extractors/__init__.py +25 -0
  662. graph_os/extractors/code_generic.py +914 -0
  663. graph_os/extractors/code_go.py +1422 -0
  664. graph_os/extractors/code_json.py +340 -0
  665. graph_os/extractors/code_php.py +979 -0
  666. graph_os/extractors/code_python.py +1454 -0
  667. graph_os/extractors/code_shell.py +538 -0
  668. graph_os/extractors/code_toml.py +302 -0
  669. graph_os/extractors/code_ts.py +1665 -0
  670. graph_os/extractors/code_yaml.py +394 -0
  671. graph_os/extractors/contracts.py +1592 -0
  672. graph_os/extractors/md_links.py +890 -0
  673. graph_os/extractors/task_deps.py +345 -0
  674. graph_os/groups/__init__.py +22 -0
  675. graph_os/groups/cross_repo.py +156 -0
  676. graph_os/groups/manifest.py +141 -0
  677. graph_os/ingest/__init__.py +19 -0
  678. graph_os/ingest/base.py +306 -0
  679. graph_os/ingest/github.py +112 -0
  680. graph_os/ingest/zip.py +95 -0
  681. graph_os/toolchain.py +393 -0
  682. graph_os/tools/__init__.py +9 -0
  683. graph_os/tools/graph.py +5573 -0
  684. graph_os/tools/reindex_dispatch.py +730 -0
  685. graph_os/tree_sitter_overlay.py +235 -0
  686. graph_os/types.py +252 -0
  687. graph_os/vec_index.py +277 -0
  688. graph_os/viewer/__init__.py +12 -0
  689. graph_os/viewer/exporter.py +93 -0
  690. graph_os/viewer/template.py +189 -0
  691. scheduled/__init__.py +0 -0
  692. scheduled/_activity.py +126 -0
  693. scheduled/_state.py +113 -0
  694. scheduled/config.py +86 -0
  695. scheduled/dep_reconcile.py +135 -0
  696. scheduled/error_sweep.py +137 -0
  697. scheduled/nightly.py +930 -0
  698. scheduled/responsive_extract.py +65 -0
  699. scripts/__init__.py +4 -0
  700. scripts/_commit_msg_body.sh +31 -0
  701. scripts/_post_commit_body.sh +49 -0
  702. scripts/_pre_commit_body.sh +121 -0
  703. scripts/_prepare_commit_msg_body.sh +53 -0
  704. scripts/audit_mcp_tools.py +693 -0
  705. scripts/bench_sdk_dispatcher.py +177 -0
  706. scripts/capture_golden.py +169 -0
  707. scripts/check_graph_phantoms.py +75 -0
  708. scripts/dev/audit_doc_links.py +359 -0
  709. scripts/dev/audit_scaffold_module_tags.py +107 -0
  710. scripts/dev/backfill_doc_headers.py +328 -0
  711. scripts/dev/backfill_nav_lines.py +119 -0
  712. scripts/dev/fix_nav_placement.py +106 -0
  713. scripts/dev/inspect_sdk_options.py +45 -0
  714. scripts/dev/migrate_check_ids.py +170 -0
  715. scripts/dev/strip_purpose_blocks.py +154 -0
  716. scripts/dump_openapi.py +66 -0
  717. scripts/e2e_dispatch_tool.py +195 -0
  718. scripts/generate_manifest.py +166 -0
  719. scripts/golden_sections.py +20 -0
  720. scripts/graph_demo.py +161 -0
  721. scripts/install-git-hooks.sh +47 -0
  722. scripts/migrate_embeddings_minilm_to_bge_m3.py +84 -0
  723. scripts/operational_eval.py +445 -0
  724. scripts/probe_agent_session_resolver.py +59 -0
  725. scripts/prune_deleted_path.py +127 -0
  726. scripts/refactor_agent_dual_mode.py +171 -0
  727. scripts/refresh_skill_versions.py +302 -0
  728. scripts/regen_doc_index.py +209 -0
  729. scripts/regen_doctor_schema.py +62 -0
  730. scripts/regen_rules.py +94 -0
  731. scripts/rename_formulas_to_semantic.py +241 -0
  732. scripts/smoke_db_connections.py +183 -0
  733. scripts/smoke_doc_header.py +72 -0
  734. scripts/smoke_graph_e2e.py +374 -0
  735. scripts/smoke_sdk_dispatch.py +84 -0
  736. scripts/smoke_uid_resolver.py +164 -0
  737. scripts/verify_dispatchers.py +244 -0
  738. scripts/verify_phase_c_e2e.py +436 -0
  739. templates/__init__.py +6 -0
  740. templates/_base/Makefile.base +353 -0
  741. templates/_base/base.yaml +59 -0
  742. templates/_base/coding-os.yaml.template +39 -0
  743. templates/_base/dimension-registry.template.md +68 -0
  744. templates/_base/domain-config.template.json +46 -0
  745. templates/_base/fragments/anatomy-map.md.tmpl +11 -0
  746. templates/_base/fragments/context-discipline.md.tmpl +3 -0
  747. templates/_base/fragments/core-loop.md.tmpl +52 -0
  748. templates/_base/fragments/engineering-routing.md.tmpl +3 -0
  749. templates/_base/fragments/header.md.tmpl +6 -0
  750. templates/_base/fragments/identity.md.tmpl +3 -0
  751. templates/_base/fragments/principles.md.tmpl +3 -0
  752. templates/_base/fragments/retrieval-routing.md.tmpl +23 -0
  753. templates/_base/fragments/session-handoff.md.tmpl +3 -0
  754. templates/_base/fragments/skills.md.tmpl +3 -0
  755. templates/_base/fragments/ssot-map.md.tmpl +3 -0
  756. templates/_base/fragments/stop-conditions.md.tmpl +3 -0
  757. templates/_base/fragments/subagent-dispatch.md.tmpl +3 -0
  758. templates/_base/fragments/task-authoring.md.tmpl +69 -0
  759. templates/_base/fragments/task-logging.md.tmpl +8 -0
  760. templates/_base/fragments/tool-routing.md.tmpl +9 -0
  761. templates/_base/fragments/verification-matrix.md.tmpl +12 -0
  762. templates/_base/lang/dart/analysis_options.yaml +7 -0
  763. templates/_base/lang/php/phpcs.xml.dist +10 -0
  764. templates/_base/lang/python/pyproject.toml +19 -0
  765. templates/_base/lang/rust/clippy.toml +5 -0
  766. templates/_base/lang/rust/rustfmt.toml +3 -0
  767. templates/_base/lang/typescript/eslint.config.js +26 -0
  768. templates/_base/lang/typescript/tsconfig.json +15 -0
  769. templates/_base/lang/typescript/vitest.config.ts +10 -0
  770. templates/_base/scaffold/changes.log +1 -0
  771. templates/_base/scaffold/docs/00-index.md +55 -0
  772. templates/_base/scaffold/docs/_meta/feature-dependency-tree.md +30 -0
  773. templates/_base/scaffold/docs/_meta/foundation-map.md +56 -0
  774. templates/_base/scaffold/docs/_meta/questions.md +8 -0
  775. templates/_base/scaffold/docs/_meta/roadmap.md +33 -0
  776. templates/_base/scaffold/docs/api-contracts/00-index.md +58 -0
  777. templates/_base/scaffold/docs/api-contracts/error-format.md +58 -0
  778. templates/_base/scaffold/docs/architecture/00-index.md +42 -0
  779. templates/_base/scaffold/docs/architecture/adr/00-index.md +39 -0
  780. templates/_base/scaffold/docs/engineering/00-index.md +9 -0
  781. templates/_base/scaffold/docs/governance/00-index.md +55 -0
  782. templates/_base/scaffold/docs/governance/_templates/doc-cheat-sheet.md +202 -0
  783. templates/_base/scaffold/docs/governance/_templates/playbook-template.md +81 -0
  784. templates/_base/scaffold/docs/governance/_templates/post-mortem-template.md +85 -0
  785. templates/_base/scaffold/docs/governance/_templates/runbook-template.md +88 -0
  786. templates/_base/scaffold/docs/governance/_templates/security-review-template.md +111 -0
  787. templates/_base/scaffold/docs/governance/_templates/task-detail.md +61 -0
  788. templates/_base/scaffold/docs/governance/agent-workflow.md +101 -0
  789. templates/_base/scaffold/docs/governance/anatomy-contract.md +150 -0
  790. templates/_base/scaffold/docs/governance/critical-rules.md +224 -0
  791. templates/_base/scaffold/docs/governance/decision-records.md +58 -0
  792. templates/_base/scaffold/docs/governance/docs-first-protocol.md +157 -0
  793. templates/_base/scaffold/docs/governance/docs-system.md +151 -0
  794. templates/_base/scaffold/docs/governance/gdpr-compliance.md +66 -0
  795. templates/_base/scaffold/docs/governance/mcp-tool-inventory.md +112 -0
  796. templates/_base/scaffold/docs/governance/risk-register.md +26 -0
  797. templates/_base/scaffold/docs/governance/scaffold-boundary-contract.md +161 -0
  798. templates/_base/scaffold/docs/governance/task-lifecycle.md +125 -0
  799. templates/_base/scaffold/docs/governance/wrapper-derivation.md +50 -0
  800. templates/_base/scaffold/docs/insights/00-index.md +17 -0
  801. templates/_base/scaffold/docs/ops/00-index.md +59 -0
  802. templates/_base/scaffold/docs/ops/runbooks/00-index.md +9 -0
  803. templates/_base/scaffold/docs/playbooks/00-index.md +12 -0
  804. templates/_base/scaffold/docs/playbooks/research-validation.md +29 -0
  805. templates/_base/scaffold/docs/playbooks/security-review.md +41 -0
  806. templates/_base/scaffold/docs/prd/00-index.md +43 -0
  807. templates/_base/scaffold/docs/prd/01-snapshot-vision.md +56 -0
  808. templates/_base/scaffold/docs/workflow/workflow-guide.md +138 -0
  809. templates/_base/scaffold/src/shared/README.md +23 -0
  810. templates/_base/skill-enforcement.template.md +14 -0
  811. templates/_base/task-detail.template.md +61 -0
  812. templates/_presets/ai-saas.yaml +9 -0
  813. templates/_presets/django-next.yaml +8 -0
  814. templates/_presets/dotnet-react.yaml +8 -0
  815. templates/_presets/flutter-baas.yaml +8 -0
  816. templates/_presets/go-react.yaml +8 -0
  817. templates/_presets/hexagonal-product.yaml +16 -0
  818. templates/_presets/jamstack.yaml +8 -0
  819. templates/_presets/laravel-vue.yaml +8 -0
  820. templates/_presets/mean.yaml +8 -0
  821. templates/_presets/mern.yaml +9 -0
  822. templates/_presets/nest-angular.yaml +8 -0
  823. templates/_presets/nextjs-fastapi.yaml +10 -0
  824. templates/_presets/nuxt-fullstack.yaml +9 -0
  825. templates/_presets/pern.yaml +9 -0
  826. templates/_presets/rails-react.yaml +8 -0
  827. templates/_presets/rn-api.yaml +8 -0
  828. templates/_presets/rust-svelte.yaml +8 -0
  829. templates/_presets/spring-react.yaml +8 -0
  830. templates/_presets/t3-style.yaml +9 -0
  831. templates/_presets/tall.yaml +8 -0
  832. templates/_presets/wordpress-cms.yaml +8 -0
  833. templates/angular/rules/frontend.md +19 -0
  834. templates/angular/scaffold/docs/engineering/accessibility.md +46 -0
  835. templates/angular/scaffold/docs/engineering/angular-rules.md +35 -0
  836. templates/angular/scaffold/docs/playbooks/angular-app.md +42 -0
  837. templates/angular/scaffold/src/frontend/angular.json +52 -0
  838. templates/angular/scaffold/src/frontend/package.json +28 -0
  839. templates/angular/scaffold/src/frontend/src/app/app.component.ts +19 -0
  840. templates/angular/scaffold/src/frontend/src/app/app.config.ts +22 -0
  841. templates/angular/scaffold/src/frontend/src/app/app.routes.ts +8 -0
  842. templates/angular/scaffold/src/frontend/src/app/core/global-error-handler.ts +12 -0
  843. templates/angular/scaffold/src/frontend/src/app/health/health.component.ts +14 -0
  844. templates/angular/scaffold/src/frontend/src/app/health/health.service.spec.ts +16 -0
  845. templates/angular/scaffold/src/frontend/src/app/health/health.service.ts +10 -0
  846. templates/angular/scaffold/src/frontend/src/index.html +11 -0
  847. templates/angular/scaffold/src/frontend/src/main.ts +9 -0
  848. templates/angular/scaffold/src/frontend/src/styles.css +14 -0
  849. templates/angular/scaffold/src/frontend/tsconfig.app.json +8 -0
  850. templates/angular/scaffold/src/frontend/tsconfig.json +27 -0
  851. templates/angular/scaffold/src/frontend/tsconfig.spec.json +8 -0
  852. templates/angular/scaffold-boundary.yaml +27 -0
  853. templates/angular/skills/angular/SKILL.md +79 -0
  854. templates/angular/skills/angular/references/anatomy.md +69 -0
  855. templates/angular/stack.yaml +75 -0
  856. templates/aspnet-core/rules/backend.md +20 -0
  857. templates/aspnet-core/scaffold/docs/engineering/aspnet-core-rules.md +36 -0
  858. templates/aspnet-core/scaffold/docs/playbooks/aspnet-core-service.md +40 -0
  859. templates/aspnet-core/scaffold/src/backend/Backend.csproj +11 -0
  860. templates/aspnet-core/scaffold/src/backend/Backend.sln +27 -0
  861. templates/aspnet-core/scaffold/src/backend/Common/ExceptionHandlingMiddleware.cs +35 -0
  862. templates/aspnet-core/scaffold/src/backend/Features/Health/HealthEndpoints.cs +10 -0
  863. templates/aspnet-core/scaffold/src/backend/Features/Health/HealthService.cs +9 -0
  864. templates/aspnet-core/scaffold/src/backend/Program.cs +22 -0
  865. templates/aspnet-core/scaffold/src/backend/tests/Backend.Tests/Backend.Tests.csproj +22 -0
  866. templates/aspnet-core/scaffold/src/backend/tests/Backend.Tests/HealthServiceTests.cs +15 -0
  867. templates/aspnet-core/scaffold-boundary.yaml +24 -0
  868. templates/aspnet-core/skills/aspnet-core/SKILL.md +80 -0
  869. templates/aspnet-core/skills/aspnet-core/references/anatomy.md +65 -0
  870. templates/aspnet-core/stack.yaml +68 -0
  871. templates/astro/rules/frontend.md +20 -0
  872. templates/astro/scaffold/docs/engineering/astro-rules.md +40 -0
  873. templates/astro/scaffold/docs/playbooks/astro-app.md +57 -0
  874. templates/astro/scaffold/docs/playbooks/content-seo.md +37 -0
  875. templates/astro/scaffold/src/frontend/astro.config.mjs +10 -0
  876. templates/astro/scaffold/src/frontend/package.json +23 -0
  877. templates/astro/scaffold/src/frontend/src/components/Greeting.astro +13 -0
  878. templates/astro/scaffold/src/frontend/src/content/posts/hello.md +10 -0
  879. templates/astro/scaffold/src/frontend/src/content.config.ts +19 -0
  880. templates/astro/scaffold/src/frontend/src/lib/problem.test.ts +39 -0
  881. templates/astro/scaffold/src/frontend/src/lib/problem.ts +30 -0
  882. templates/astro/scaffold/src/frontend/src/pages/api/health.ts +13 -0
  883. templates/astro/scaffold/src/frontend/src/pages/index.astro +21 -0
  884. templates/astro/scaffold/src/frontend/tsconfig.json +9 -0
  885. templates/astro/scaffold/src/frontend/vitest.config.ts +10 -0
  886. templates/astro/scaffold-boundary.yaml +28 -0
  887. templates/astro/skills/astro/SKILL.md +71 -0
  888. templates/astro/skills/astro/references/anatomy.md +66 -0
  889. templates/astro/stack.yaml +75 -0
  890. templates/csharp-plain/scaffold/src/backend/Backend.csproj +12 -0
  891. templates/csharp-plain/scaffold/src/backend/Program.cs +1 -0
  892. templates/csharp-plain/scaffold-boundary.yaml +23 -0
  893. templates/csharp-plain/stack.yaml +50 -0
  894. templates/django/rules/backend.md +18 -0
  895. templates/django/scaffold/docs/engineering/anti-ambiguity.md +74 -0
  896. templates/django/scaffold/docs/engineering/backend-rules.md +133 -0
  897. templates/django/scaffold/docs/engineering/glossary.md +51 -0
  898. templates/django/scaffold/docs/engineering/logging-standards.md +107 -0
  899. templates/django/scaffold/docs/engineering/naming-conventions.md +68 -0
  900. templates/django/scaffold/docs/engineering/secrets-rotation-runbook.md +142 -0
  901. templates/django/scaffold/docs/playbooks/backend-api.md +119 -0
  902. templates/django/scaffold/src/backend/config/__init__.py +0 -0
  903. templates/django/scaffold/src/backend/config/settings.py +31 -0
  904. templates/django/scaffold/src/backend/config/urls.py +11 -0
  905. templates/django/scaffold/src/backend/config/wsgi.py +6 -0
  906. templates/django/scaffold/src/backend/manage.py +14 -0
  907. templates/django/scaffold/src/backend/pyproject.toml +37 -0
  908. templates/django/scaffold/src/backend/tests/test_health.py +4 -0
  909. templates/django/scaffold-boundary.yaml +25 -0
  910. templates/django/skills/python-django/SKILL.md +450 -0
  911. templates/django/skills/python-django/references/anatomy.md +117 -0
  912. templates/django/skills/python-django/scripts/new_endpoint.py +89 -0
  913. templates/django/stack.yaml +73 -0
  914. templates/fastapi/rules/backend.md +18 -0
  915. templates/fastapi/scaffold/docs/engineering/fastapi-rules.md +37 -0
  916. templates/fastapi/scaffold/docs/playbooks/fastapi-service.md +30 -0
  917. templates/fastapi/scaffold/src/backend/app/__init__.py +0 -0
  918. templates/fastapi/scaffold/src/backend/app/main.py +8 -0
  919. templates/fastapi/scaffold/src/backend/pyproject.toml +39 -0
  920. templates/fastapi/scaffold/src/backend/tests/test_health.py +11 -0
  921. templates/fastapi/scaffold-boundary.yaml +25 -0
  922. templates/fastapi/skills/python-fastapi/SKILL.md +75 -0
  923. templates/fastapi/skills/python-fastapi/references/anatomy.md +117 -0
  924. templates/fastapi/skills/python-fastapi/scripts/new_endpoint.py +101 -0
  925. templates/fastapi/stack.yaml +57 -0
  926. templates/flutter/rules/mobile.md +26 -0
  927. templates/flutter/scaffold/docs/engineering/flutter-rules.md +35 -0
  928. templates/flutter/scaffold/docs/playbooks/flutter-app.md +45 -0
  929. templates/flutter/scaffold/src/mobile/lib/core/error_mapper.dart +17 -0
  930. templates/flutter/scaffold/src/mobile/lib/core/router.dart +13 -0
  931. templates/flutter/scaffold/src/mobile/lib/main.dart +22 -0
  932. templates/flutter/scaffold/src/mobile/lib/screens/health_screen.dart +32 -0
  933. templates/flutter/scaffold/src/mobile/lib/services/health_service.dart +11 -0
  934. templates/flutter/scaffold/src/mobile/lib/state/health_provider.dart +13 -0
  935. templates/flutter/scaffold/src/mobile/pubspec.yaml +22 -0
  936. templates/flutter/scaffold/src/mobile/test/health_provider_test.dart +90 -0
  937. templates/flutter/scaffold-boundary.yaml +28 -0
  938. templates/flutter/skills/flutter/SKILL.md +76 -0
  939. templates/flutter/skills/flutter/references/anatomy.md +64 -0
  940. templates/flutter/stack.yaml +70 -0
  941. templates/go/rules/backend.md +19 -0
  942. templates/go/scaffold/docs/engineering/go-rules.md +45 -0
  943. templates/go/scaffold/docs/playbooks/go-service.md +30 -0
  944. templates/go/scaffold/src/backend/cmd/api/main.go +22 -0
  945. templates/go/scaffold/src/backend/cmd/api/main_test.go +20 -0
  946. templates/go/scaffold/src/backend/go.mod +3 -0
  947. templates/go/scaffold-boundary.yaml +25 -0
  948. templates/go/skills/go-patterns/SKILL.md +68 -0
  949. templates/go/skills/go-patterns/assets/go-checklist.md +29 -0
  950. templates/go/skills/go-patterns/references/anatomy.md +115 -0
  951. templates/go/skills/go-patterns/references/go-2026-idioms.md +92 -0
  952. templates/go/skills/go-patterns/scripts/new_endpoint.py +117 -0
  953. templates/go/skills/go-patterns/versions.json +16 -0
  954. templates/go/stack.yaml +54 -0
  955. templates/go-fiber/rules/backend.md +20 -0
  956. templates/go-fiber/scaffold/docs/engineering/fiber-rules.md +97 -0
  957. templates/go-fiber/scaffold/docs/playbooks/fiber-service.md +149 -0
  958. templates/go-fiber/scaffold/src/backend/cmd/api/main.go +21 -0
  959. templates/go-fiber/scaffold/src/backend/cmd/api/main_test.go +17 -0
  960. templates/go-fiber/scaffold/src/backend/go.mod +23 -0
  961. templates/go-fiber/scaffold/src/backend/go.sum +49 -0
  962. templates/go-fiber/scaffold-boundary.yaml +26 -0
  963. templates/go-fiber/skills/go-fiber/SKILL.md +203 -0
  964. templates/go-fiber/skills/go-fiber/assets/fiber-checklist.md +26 -0
  965. templates/go-fiber/skills/go-fiber/references/anatomy.md +116 -0
  966. templates/go-fiber/skills/go-fiber/references/fiber-v3-patterns.md +89 -0
  967. templates/go-fiber/skills/go-fiber/scripts/new_endpoint.py +107 -0
  968. templates/go-fiber/skills/go-fiber/versions.json +16 -0
  969. templates/go-fiber/stack.yaml +59 -0
  970. templates/go-plain/scaffold/src/backend/go.mod +3 -0
  971. templates/go-plain/scaffold/src/backend/main.go +7 -0
  972. templates/go-plain/scaffold-boundary.yaml +22 -0
  973. templates/go-plain/stack.yaml +51 -0
  974. templates/java-plain/scaffold/src/backend/mvnw +302 -0
  975. templates/java-plain/scaffold/src/backend/pom.xml +41 -0
  976. templates/java-plain/scaffold/src/backend/src/main/java/com/example/app/Main.java +10 -0
  977. templates/java-plain/scaffold-boundary.yaml +23 -0
  978. templates/java-plain/stack.yaml +51 -0
  979. templates/laravel/rules/backend.md +20 -0
  980. templates/laravel/scaffold/docs/engineering/laravel-rules.md +28 -0
  981. templates/laravel/scaffold/docs/playbooks/laravel-service.md +28 -0
  982. templates/laravel/scaffold/src/backend/app/Exceptions/Handler.php +27 -0
  983. templates/laravel/scaffold/src/backend/app/Http/Controllers/HealthController.php +15 -0
  984. templates/laravel/scaffold/src/backend/app/Support/HealthStatus.php +14 -0
  985. templates/laravel/scaffold/src/backend/composer.json +24 -0
  986. templates/laravel/scaffold/src/backend/phpunit.xml +10 -0
  987. templates/laravel/scaffold/src/backend/public/index.php +8 -0
  988. templates/laravel/scaffold/src/backend/routes/api.php +7 -0
  989. templates/laravel/scaffold/src/backend/tests/Unit/HealthStatusTest.php +16 -0
  990. templates/laravel/scaffold-boundary.yaml +23 -0
  991. templates/laravel/skills/laravel/SKILL.md +56 -0
  992. templates/laravel/skills/laravel/references/anatomy.md +65 -0
  993. templates/laravel/stack.yaml +66 -0
  994. templates/meta/rules/graph-first.md +27 -0
  995. templates/meta/rules/hook-author.md +19 -0
  996. templates/meta/rules/mcp-tool-author.md +18 -0
  997. templates/meta/rules/meta-engineering.md +17 -0
  998. templates/meta/scaffold-boundary.yaml +55 -0
  999. templates/meta/skills/claude-sdk-integration/SKILL.md +163 -0
  1000. templates/meta/skills/claude-sdk-integration/assets/sdk-checklist.md +27 -0
  1001. templates/meta/skills/claude-sdk-integration/scripts/check_model_ids.py +97 -0
  1002. templates/meta/skills/graph-os-authoring/SKILL.md +278 -0
  1003. templates/meta/skills/graph-os-authoring/assets/graph-os-checklist.md +25 -0
  1004. templates/meta/skills/graph-os-authoring/scripts/new_extractor.py +76 -0
  1005. templates/meta/skills/hook-authoring/SKILL.md +292 -0
  1006. templates/meta/skills/hook-authoring/assets/hook-checklist.md +30 -0
  1007. templates/meta/skills/hook-authoring/scripts/new_hook.sh +75 -0
  1008. templates/meta/skills/mcp-tool-authoring/SKILL.md +301 -0
  1009. templates/meta/skills/mcp-tool-authoring/assets/mcp-tool-checklist.md +29 -0
  1010. templates/meta/skills/mcp-tool-authoring/scripts/new_tool.py +74 -0
  1011. templates/meta/skills/meta-engineering/SKILL.md +151 -0
  1012. templates/meta/skills/meta-engineering/assets/meta-edit-checklist.md +28 -0
  1013. templates/meta/skills/meta-engineering/scripts/which_layer.py +61 -0
  1014. templates/meta/skills/python-meta-server/SKILL.md +162 -0
  1015. templates/meta/skills/python-meta-server/assets/meta-server-checklist.md +28 -0
  1016. templates/meta/skills/python-meta-server/scripts/check_envelope.py +91 -0
  1017. templates/meta/skills/react-vite-hub/SKILL.md +140 -0
  1018. templates/meta/skills/react-vite-hub/assets/hub-ui-checklist.md +23 -0
  1019. templates/meta/skills/react-vite-hub/scripts/check_vite_env.py +73 -0
  1020. templates/meta/stack.yaml +119 -0
  1021. templates/nestjs/rules/backend.md +20 -0
  1022. templates/nestjs/scaffold/docs/engineering/nestjs-rules.md +33 -0
  1023. templates/nestjs/scaffold/docs/playbooks/nestjs-service.md +39 -0
  1024. templates/nestjs/scaffold/src/backend/nest-cli.json +5 -0
  1025. templates/nestjs/scaffold/src/backend/package.json +28 -0
  1026. templates/nestjs/scaffold/src/backend/src/app.module.ts +9 -0
  1027. templates/nestjs/scaffold/src/backend/src/common/all-exceptions.filter.ts +59 -0
  1028. templates/nestjs/scaffold/src/backend/src/health/health.controller.ts +14 -0
  1029. templates/nestjs/scaffold/src/backend/src/health/health.module.ts +10 -0
  1030. templates/nestjs/scaffold/src/backend/src/health/health.service.spec.ts +21 -0
  1031. templates/nestjs/scaffold/src/backend/src/health/health.service.ts +9 -0
  1032. templates/nestjs/scaffold/src/backend/src/main.ts +26 -0
  1033. templates/nestjs/scaffold/src/backend/tsconfig.json +16 -0
  1034. templates/nestjs/scaffold/src/backend/vitest.config.ts +9 -0
  1035. templates/nestjs/scaffold-boundary.yaml +25 -0
  1036. templates/nestjs/skills/nestjs/SKILL.md +67 -0
  1037. templates/nestjs/skills/nestjs/references/anatomy.md +65 -0
  1038. templates/nestjs/stack.yaml +68 -0
  1039. templates/nextjs/rules/frontend.md +18 -0
  1040. templates/nextjs/scaffold/docs/design/00-index.md +23 -0
  1041. templates/nextjs/scaffold/docs/design/colors-tokens.md +141 -0
  1042. templates/nextjs/scaffold/docs/design/components-patterns.md +159 -0
  1043. templates/nextjs/scaffold/docs/design/motion-accessibility.md +137 -0
  1044. templates/nextjs/scaffold/docs/design/typography-spacing.md +107 -0
  1045. templates/nextjs/scaffold/docs/engineering/accessibility-web.md +56 -0
  1046. templates/nextjs/scaffold/docs/engineering/copywriting-standard.md +102 -0
  1047. templates/nextjs/scaffold/docs/engineering/formatting-rules.md +89 -0
  1048. templates/nextjs/scaffold/docs/engineering/frontend-rendering-rules.md +80 -0
  1049. templates/nextjs/scaffold/docs/engineering/frontend-rules.md +183 -0
  1050. templates/nextjs/scaffold/docs/engineering/i18n-policy.md +99 -0
  1051. templates/nextjs/scaffold/docs/pages-content-spec/00-index.md +78 -0
  1052. templates/nextjs/scaffold/docs/playbooks/content-seo.md +55 -0
  1053. templates/nextjs/scaffold/docs/playbooks/docs-governance.md +51 -0
  1054. templates/nextjs/scaffold/docs/playbooks/frontend-ui.md +63 -0
  1055. templates/nextjs/scaffold/src/frontend/app/layout.tsx +14 -0
  1056. templates/nextjs/scaffold/src/frontend/app/page.tsx +3 -0
  1057. templates/nextjs/scaffold/src/frontend/eslint.config.js +17 -0
  1058. templates/nextjs/scaffold/src/frontend/lib/greeting.test.ts +9 -0
  1059. templates/nextjs/scaffold/src/frontend/lib/greeting.ts +3 -0
  1060. templates/nextjs/scaffold/src/frontend/package.json +28 -0
  1061. templates/nextjs/scaffold/src/frontend/tsconfig.json +18 -0
  1062. templates/nextjs/scaffold/src/frontend/vitest.config.ts +10 -0
  1063. templates/nextjs/scaffold-boundary.yaml +30 -0
  1064. templates/nextjs/skills/nextjs-react/SKILL.md +485 -0
  1065. templates/nextjs/skills/nextjs-react/references/anatomy.md +116 -0
  1066. templates/nextjs/skills/nextjs-react/scripts/new_component.py +72 -0
  1067. templates/nextjs/stack.yaml +81 -0
  1068. templates/node-express/rules/backend.md +20 -0
  1069. templates/node-express/scaffold/docs/engineering/express-rules.md +30 -0
  1070. templates/node-express/scaffold/docs/playbooks/express-service.md +35 -0
  1071. templates/node-express/scaffold/src/backend/package.json +24 -0
  1072. templates/node-express/scaffold/src/backend/src/index.ts +17 -0
  1073. templates/node-express/scaffold/src/backend/src/middleware/error-handler.ts +12 -0
  1074. templates/node-express/scaffold/src/backend/src/routes/health.test.ts +33 -0
  1075. templates/node-express/scaffold/src/backend/src/routes/health.ts +7 -0
  1076. templates/node-express/scaffold/src/backend/tsconfig.json +15 -0
  1077. templates/node-express/scaffold/src/backend/types/express-bootstrap.d.ts +21 -0
  1078. templates/node-express/scaffold-boundary.yaml +25 -0
  1079. templates/node-express/skills/node-express/SKILL.md +70 -0
  1080. templates/node-express/skills/node-express/references/anatomy.md +63 -0
  1081. templates/node-express/stack.yaml +63 -0
  1082. templates/python/scaffold/docs/engineering/python-rules.md +27 -0
  1083. templates/python/scaffold/docs/playbooks/python-library.md +35 -0
  1084. templates/python/stack.yaml +60 -0
  1085. templates/rails/rules/backend.md +10 -0
  1086. templates/rails/scaffold/docs/engineering/rails-rules.md +33 -0
  1087. templates/rails/scaffold/docs/playbooks/rails-service.md +42 -0
  1088. templates/rails/scaffold/src/backend/Gemfile +12 -0
  1089. templates/rails/scaffold/src/backend/app/controllers/application_controller.rb +26 -0
  1090. templates/rails/scaffold/src/backend/app/controllers/health_controller.rb +6 -0
  1091. templates/rails/scaffold/src/backend/app/models/health.rb +6 -0
  1092. templates/rails/scaffold/src/backend/config/application.rb +12 -0
  1093. templates/rails/scaffold/src/backend/config/boot.rb +3 -0
  1094. templates/rails/scaffold/src/backend/config/routes.rb +4 -0
  1095. templates/rails/scaffold/src/backend/config.ru +5 -0
  1096. templates/rails/scaffold/src/backend/spec/rails_helper.rb +18 -0
  1097. templates/rails/scaffold/src/backend/spec/requests/health_spec.rb +24 -0
  1098. templates/rails/scaffold-boundary.yaml +25 -0
  1099. templates/rails/skills/rails/SKILL.md +62 -0
  1100. templates/rails/skills/rails/references/anatomy.md +71 -0
  1101. templates/rails/stack.yaml +72 -0
  1102. templates/react-native/rules/mobile.md +26 -0
  1103. templates/react-native/scaffold/docs/engineering/accessibility-mobile.md +95 -0
  1104. templates/react-native/scaffold/docs/engineering/mobile-rules.md +56 -0
  1105. templates/react-native/scaffold/docs/engineering/offline-first.md +61 -0
  1106. templates/react-native/scaffold/docs/playbooks/mobile-app.md +49 -0
  1107. templates/react-native/scaffold/src/mobile/App.tsx +9 -0
  1108. templates/react-native/scaffold/src/mobile/eslint.config.js +17 -0
  1109. templates/react-native/scaffold/src/mobile/package.json +23 -0
  1110. templates/react-native/scaffold/src/mobile/src/greeting.test.ts +9 -0
  1111. templates/react-native/scaffold/src/mobile/src/greeting.ts +3 -0
  1112. templates/react-native/scaffold/src/mobile/tsconfig.json +17 -0
  1113. templates/react-native/scaffold/src/mobile/vitest.config.ts +10 -0
  1114. templates/react-native/scaffold-boundary.yaml +30 -0
  1115. templates/react-native/skills/react-native-mobile/SKILL.md +119 -0
  1116. templates/react-native/skills/react-native-mobile/assets/rn-mobile-checklist.md +28 -0
  1117. templates/react-native/skills/react-native-mobile/references/anatomy.md +140 -0
  1118. templates/react-native/skills/react-native-mobile/references/rn-2026-practices.md +54 -0
  1119. templates/react-native/skills/react-native-mobile/scripts/new_screen.py +73 -0
  1120. templates/react-native/skills/react-native-mobile/versions.json +16 -0
  1121. templates/react-native/skills/react-native-patterns/SKILL.md +512 -0
  1122. templates/react-native/skills/react-native-patterns/assets/rn-review-checklist.md +26 -0
  1123. templates/react-native/skills/react-native-patterns/references/anatomy.md +62 -0
  1124. templates/react-native/skills/react-native-patterns/references/list-performance.md +70 -0
  1125. templates/react-native/skills/react-native-patterns/scripts/scan_rn_perf.py +77 -0
  1126. templates/react-native/stack.yaml +70 -0
  1127. templates/ruby-plain/scaffold/src/backend/Gemfile +8 -0
  1128. templates/ruby-plain/scaffold/src/backend/main.rb +3 -0
  1129. templates/ruby-plain/scaffold-boundary.yaml +23 -0
  1130. templates/ruby-plain/stack.yaml +50 -0
  1131. templates/rust-axum/rules/backend.md +20 -0
  1132. templates/rust-axum/scaffold/docs/engineering/rust-axum-rules.md +35 -0
  1133. templates/rust-axum/scaffold/docs/playbooks/rust-axum-service.md +43 -0
  1134. templates/rust-axum/scaffold/src/backend/Cargo.toml +20 -0
  1135. templates/rust-axum/scaffold/src/backend/src/app.rs +12 -0
  1136. templates/rust-axum/scaffold/src/backend/src/error.rs +48 -0
  1137. templates/rust-axum/scaffold/src/backend/src/main.rs +24 -0
  1138. templates/rust-axum/scaffold/src/backend/src/routes/health.rs +37 -0
  1139. templates/rust-axum/scaffold/src/backend/src/routes/mod.rs +2 -0
  1140. templates/rust-axum/scaffold-boundary.yaml +25 -0
  1141. templates/rust-axum/skills/rust/SKILL.md +73 -0
  1142. templates/rust-axum/skills/rust/references/anatomy.md +63 -0
  1143. templates/rust-axum/stack.yaml +65 -0
  1144. templates/rust-plain/scaffold/src/backend/Cargo.toml +6 -0
  1145. templates/rust-plain/scaffold/src/backend/src/main.rs +3 -0
  1146. templates/rust-plain/scaffold-boundary.yaml +23 -0
  1147. templates/rust-plain/stack.yaml +51 -0
  1148. templates/spring-boot/rules/backend.md +20 -0
  1149. templates/spring-boot/scaffold/docs/engineering/spring-boot-rules.md +35 -0
  1150. templates/spring-boot/scaffold/docs/playbooks/spring-boot-service.md +45 -0
  1151. templates/spring-boot/scaffold/src/backend/mvnw +302 -0
  1152. templates/spring-boot/scaffold/src/backend/pom.xml +69 -0
  1153. templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/Application.java +16 -0
  1154. templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/common/GlobalExceptionHandler.java +31 -0
  1155. templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/health/HealthController.java +22 -0
  1156. templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/health/HealthService.java +12 -0
  1157. templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/health/HealthStatus.java +4 -0
  1158. templates/spring-boot/scaffold/src/backend/src/test/java/com/example/app/health/HealthServiceTest.java +15 -0
  1159. templates/spring-boot/scaffold-boundary.yaml +26 -0
  1160. templates/spring-boot/skills/spring-boot/SKILL.md +84 -0
  1161. templates/spring-boot/skills/spring-boot/references/anatomy.md +63 -0
  1162. templates/spring-boot/stack.yaml +65 -0
  1163. templates/svelte-sveltekit/rules/frontend.md +20 -0
  1164. templates/svelte-sveltekit/scaffold/docs/engineering/svelte-sveltekit-rules.md +37 -0
  1165. templates/svelte-sveltekit/scaffold/docs/playbooks/svelte-sveltekit-app.md +36 -0
  1166. templates/svelte-sveltekit/scaffold/src/frontend/package.json +22 -0
  1167. templates/svelte-sveltekit/scaffold/src/frontend/src/app.html +12 -0
  1168. templates/svelte-sveltekit/scaffold/src/frontend/src/hooks.server.ts +14 -0
  1169. templates/svelte-sveltekit/scaffold/src/frontend/src/lib/components/Greeting.svelte +6 -0
  1170. templates/svelte-sveltekit/scaffold/src/frontend/src/lib/stores/count.test.ts +26 -0
  1171. templates/svelte-sveltekit/scaffold/src/frontend/src/lib/stores/count.ts +4 -0
  1172. templates/svelte-sveltekit/scaffold/src/frontend/src/routes/+layout.svelte +24 -0
  1173. templates/svelte-sveltekit/scaffold/src/frontend/src/routes/+page.svelte +9 -0
  1174. templates/svelte-sveltekit/scaffold/src/frontend/src/routes/+page.ts +7 -0
  1175. templates/svelte-sveltekit/scaffold/src/frontend/src/routes/health/+server.ts +7 -0
  1176. templates/svelte-sveltekit/scaffold/src/frontend/svelte.config.js +10 -0
  1177. templates/svelte-sveltekit/scaffold/src/frontend/tsconfig.json +7 -0
  1178. templates/svelte-sveltekit/scaffold/src/frontend/vite.config.ts +7 -0
  1179. templates/svelte-sveltekit/scaffold/src/frontend/vitest.config.ts +11 -0
  1180. templates/svelte-sveltekit/scaffold-boundary.yaml +29 -0
  1181. templates/svelte-sveltekit/skills/svelte/SKILL.md +90 -0
  1182. templates/svelte-sveltekit/skills/svelte/references/anatomy.md +62 -0
  1183. templates/svelte-sveltekit/stack.yaml +70 -0
  1184. templates/typescript-plain/scaffold/src/index.ts +3 -0
  1185. templates/typescript-plain/scaffold/tsconfig.json +13 -0
  1186. templates/typescript-plain/scaffold-boundary.yaml +22 -0
  1187. templates/typescript-plain/stack.yaml +44 -0
  1188. templates/vue-nuxt/rules/frontend.md +19 -0
  1189. templates/vue-nuxt/scaffold/docs/engineering/nuxt-rules.md +30 -0
  1190. templates/vue-nuxt/scaffold/docs/playbooks/nuxt-app.md +29 -0
  1191. templates/vue-nuxt/scaffold/src/frontend/app.vue +3 -0
  1192. templates/vue-nuxt/scaffold/src/frontend/nuxt.config.ts +11 -0
  1193. templates/vue-nuxt/scaffold/src/frontend/package.json +20 -0
  1194. templates/vue-nuxt/scaffold/src/frontend/pages/index.test.ts +19 -0
  1195. templates/vue-nuxt/scaffold/src/frontend/pages/index.vue +11 -0
  1196. templates/vue-nuxt/scaffold/src/frontend/vitest.config.ts +12 -0
  1197. templates/vue-nuxt/scaffold-boundary.yaml +26 -0
  1198. templates/vue-nuxt/skills/vue-nuxt/SKILL.md +57 -0
  1199. templates/vue-nuxt/skills/vue-nuxt/references/anatomy.md +60 -0
  1200. templates/vue-nuxt/stack.yaml +61 -0
  1201. templates/wordpress/rules/backend.md +19 -0
  1202. templates/wordpress/scaffold/docs/engineering/wordpress-rules.md +28 -0
  1203. templates/wordpress/scaffold/docs/playbooks/wordpress-service.md +29 -0
  1204. templates/wordpress/scaffold/src/backend/composer.json +17 -0
  1205. templates/wordpress/scaffold/src/backend/phpcs.xml.dist +11 -0
  1206. templates/wordpress/scaffold/src/backend/phpunit.xml +10 -0
  1207. templates/wordpress/scaffold/src/backend/plugin/inc/health.php +8 -0
  1208. templates/wordpress/scaffold/src/backend/plugin/plugin.php +28 -0
  1209. templates/wordpress/scaffold/src/backend/tests/HealthStatusTest.php +15 -0
  1210. templates/wordpress/scaffold/src/backend/theme/functions.php +18 -0
  1211. templates/wordpress/scaffold/src/backend/theme/style.css +11 -0
  1212. templates/wordpress/scaffold-boundary.yaml +23 -0
  1213. templates/wordpress/skills/wordpress/SKILL.md +110 -0
  1214. templates/wordpress/skills/wordpress/assets/wp-checklist.md +28 -0
  1215. templates/wordpress/skills/wordpress/references/wp-development.md +75 -0
  1216. templates/wordpress/skills/wordpress/references/wp-security.md +68 -0
  1217. templates/wordpress/skills/wordpress/scripts/scan_wp_smells.py +91 -0
  1218. templates/wordpress/skills/wordpress/versions.json +16 -0
  1219. templates/wordpress/stack.yaml +60 -0
  1220. thinking_os/__init__.py +1 -0
  1221. thinking_os/_agent_markers.py +32 -0
  1222. thinking_os/background.py +405 -0
  1223. thinking_os/bootstrap_outcomes.py +200 -0
  1224. thinking_os/budget.py +302 -0
  1225. thinking_os/capture.py +495 -0
  1226. thinking_os/cognition.py +516 -0
  1227. thinking_os/cognition_schemas.py +517 -0
  1228. thinking_os/compress.py +192 -0
  1229. thinking_os/concepts.py +233 -0
  1230. thinking_os/dashboard.py +159 -0
  1231. thinking_os/database.py +2883 -0
  1232. thinking_os/decay.py +393 -0
  1233. thinking_os/digest.py +295 -0
  1234. thinking_os/dispatcher.py +192 -0
  1235. thinking_os/dispatcher_helpers.py +48 -0
  1236. thinking_os/dispatchers/__init__.py +3 -0
  1237. thinking_os/dispatchers/default.py +47 -0
  1238. thinking_os/distill.py +192 -0
  1239. thinking_os/doc_indexer.py +905 -0
  1240. thinking_os/embeddings.py +943 -0
  1241. thinking_os/formula_composer.py +556 -0
  1242. thinking_os/gate_marker.py +75 -0
  1243. thinking_os/graph.py +296 -0
  1244. thinking_os/graph_indexer.py +360 -0
  1245. thinking_os/health_check.py +517 -0
  1246. thinking_os/impact.py +119 -0
  1247. thinking_os/memory_gc.py +356 -0
  1248. thinking_os/migrator_embeddings.py +318 -0
  1249. thinking_os/precision.py +194 -0
  1250. thinking_os/record_outcome.py +399 -0
  1251. thinking_os/repair.py +105 -0
  1252. thinking_os/retrieval_quality.py +239 -0
  1253. thinking_os/roles_state.py +168 -0
  1254. thinking_os/sanitizer.py +320 -0
  1255. thinking_os/server.py +3160 -0
  1256. thinking_os/session_enrich.py +272 -0
  1257. thinking_os/session_observe_worker.py +111 -0
  1258. thinking_os/session_startup.py +74 -0
  1259. thinking_os/session_summary.py +245 -0
  1260. thinking_os/task_analyzer.py +462 -0
  1261. thinking_os/task_parser.py +342 -0
  1262. thinking_os/task_sync.py +73 -0
  1263. thinking_os/tools/__init__.py +6 -0
  1264. thinking_os/tools/_shared.py +947 -0
  1265. thinking_os/tools/cognition.py +1867 -0
  1266. thinking_os/tools/docs.py +770 -0
  1267. thinking_os/tools/learning.py +2078 -0
  1268. thinking_os/tools/logs.py +79 -0
  1269. thinking_os/tools/memory.py +840 -0
  1270. thinking_os/tools/metrics.py +200 -0
  1271. thinking_os/tools/retrieve.py +415 -0
  1272. thinking_os/tools/routing.py +658 -0
  1273. thinking_os/tools/tasks.py +449 -0
  1274. thinking_os/tools/trajectory.py +181 -0
  1275. thinking_os/tracing.py +235 -0
  1276. web/__init__.py +5 -0
  1277. web/_cache.py +118 -0
  1278. web/_deps.py +56 -0
  1279. web/_envelope.py +85 -0
  1280. web/_project_context.py +140 -0
  1281. web/chat_providers.py +108 -0
  1282. web/init_jobs.py +216 -0
  1283. web/routes/__init__.py +25 -0
  1284. web/routes/_bounded_read.py +76 -0
  1285. web/routes/board.py +1089 -0
  1286. web/routes/cognition.py +1838 -0
  1287. web/routes/config.py +635 -0
  1288. web/routes/graph.py +513 -0
  1289. web/routes/health.py +180 -0
  1290. web/routes/hooks.py +288 -0
  1291. web/routes/hub.py +1219 -0
  1292. web/routes/logs.py +374 -0
  1293. web/routes/metrics.py +43 -0
  1294. web/routes/observability.py +400 -0
  1295. web/routes/patterns.py +227 -0
  1296. web/routes/presence.py +609 -0
  1297. web/routes/roles.py +446 -0
  1298. web/routes/scheduled.py +261 -0
  1299. web/routes/search.py +238 -0
  1300. web/routes/sessions.py +220 -0
  1301. web/routes/settings.py +363 -0
  1302. web/routes/stream.py +547 -0
  1303. web/security.py +157 -0
  1304. web/server.py +283 -0
@@ -0,0 +1,871 @@
1
+ """Coding OS — Claude-SDK dispatcher (adapters/claude)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ import logging
8
+ import os
9
+ import re
10
+ import time
11
+ import uuid
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ from thinking_os.dispatcher import DispatchRequest, DispatchResult
16
+ from thinking_os.dispatcher_helpers import extract_json_block, load_agent_prompt
17
+
18
+ logger = logging.getLogger("coding_os.dispatcher.claude_sdk")
19
+
20
+ # High-tier reasoning models (Fable 5, Opus 4.8/4.7) take the "xhigh"
21
+ # effort level — the best setting for coding/agentic work and the Claude
22
+ # Code default. "xhigh" is available in the Py SDK since 0.1.74 (it was
23
+ # TS-only before) and falls back to "high" on models that don't support
24
+ # it. Prefix-matched so dated snapshots (…-20260101) are covered too.
25
+ _XHIGH_EFFORT_MODEL_PREFIXES = (
26
+ "claude-fable-5",
27
+ "claude-opus-4-8",
28
+ "claude-opus-4-7",
29
+ )
30
+
31
+ # Default MCP allow-list pattern. coding-os exposes ~60 cos_* tools via
32
+ # the central FastMCP server registered at .mcp.json::mcpServers.coding-os.
33
+ # `acceptEdits` and `default` permission modes do NOT auto-approve MCP,
34
+ # so we list them explicitly. Wildcard form per SDK docs §D.2.
35
+ _DEFAULT_COS_MCP_ALLOW = "mcp__coding-os__*"
36
+
37
+ # Destructive Bash patterns headless dispatch must never run. Hard-deny
38
+ # wins over allow rules even in bypassPermissions (digest §B.4 order),
39
+ # so this is defense-in-depth on top of permission_mode="dontAsk".
40
+ _DESTRUCTIVE_BASH_DENY = (
41
+ "Bash(rm -rf:*)",
42
+ "Bash(rm -fr:*)",
43
+ "Bash(git push --force:*)",
44
+ "Bash(git push -f:*)",
45
+ "Bash(git reset --hard:*)",
46
+ "Bash(git clean -f:*)",
47
+ "Bash(sudo:*)",
48
+ "Bash(curl * | bash:*)",
49
+ "Bash(curl * | sh:*)",
50
+ "Bash(wget * | bash:*)",
51
+ "Bash(wget * | sh:*)",
52
+ )
53
+
54
+ # Base tool allow-list for the interactive Hub-chat surface. The Claude CLI
55
+ # treats --allowedTools as an EXCLUSIVE allow-list under permission_mode=
56
+ # "dontAsk" (verified live: listing only the MCP wildcard denies Read), so
57
+ # the chat profile must enumerate the base tools it keeps. Write/Edit/
58
+ # MultiEdit are intentionally absent — chat is read+research+MCP and must
59
+ # not mutate code (closes the dogfood-corruption gap by construction).
60
+ _CHAT_BASE_TOOLS = (
61
+ "Read",
62
+ "Grep",
63
+ "Glob",
64
+ "Bash",
65
+ "WebFetch",
66
+ "WebSearch",
67
+ "TodoWrite",
68
+ )
69
+
70
+ _CHAT_PROFILES = ("chat", "chat_resume")
71
+
72
+
73
+ def _cos_mcp_servers(cwd: str) -> dict[str, Any]:
74
+ try:
75
+ data = json.loads((Path(cwd) / ".mcp.json").read_text(encoding="utf-8"))
76
+ cos = (data.get("mcpServers") or {}).get("coding-os")
77
+ return {"coding-os": cos} if cos else {}
78
+ except (OSError, ValueError) as exc:
79
+ logger.debug("cos mcp_servers unavailable for %s: %s", cwd, exc)
80
+ return {}
81
+
82
+
83
+ _MODEL_ALIAS_CACHE: dict[str, str] = {}
84
+
85
+
86
+ # Map a tier alias (sonnet/opus/haiku) → a concrete adapter.yaml model id before
87
+ # it reaches the SDK: the kernel router speaks in tiers (claude-sdk.md — the
88
+ # adapter owns alias→id), so a routed 'sonnet' must become 'claude-sonnet-4-6'.
89
+ # `claude-*` ids and None pass through; an unknown non-id falls back to the
90
+ # adapter default so the SDK never receives a bare tier (R10/F6).
91
+ def _resolve_model_alias(model: str | None) -> str | None:
92
+ if not model or model.startswith("claude-"):
93
+ return model
94
+ alias = model.strip().lower()
95
+ if not alias:
96
+ return model
97
+ if alias in _MODEL_ALIAS_CACHE:
98
+ return _MODEL_ALIAS_CACHE[alias]
99
+ resolved = model
100
+ try:
101
+ import yaml
102
+
103
+ data = (
104
+ yaml.safe_load(
105
+ (Path(__file__).resolve().parent / "adapter.yaml").read_text(encoding="utf-8")
106
+ )
107
+ or {}
108
+ )
109
+ models = [m for m in (data.get("models") or []) if isinstance(m, dict) and m.get("id")]
110
+ match = next((str(m["id"]) for m in models if alias in str(m["id"]).lower()), None)
111
+ if match is None:
112
+ match = next((str(m["id"]) for m in models if m.get("default")), None)
113
+ if match:
114
+ logger.warning("unknown model alias %r → adapter default %s", model, match)
115
+ if match:
116
+ resolved = match
117
+ except Exception as exc:
118
+ logger.debug("model alias resolution skipped for %r: %s", model, exc)
119
+ _MODEL_ALIAS_CACHE[alias] = resolved
120
+ return resolved
121
+
122
+
123
+ def _hub_settings_path(cwd: str) -> Path:
124
+ state_dir = os.environ.get("COS_STATE_DIR")
125
+ if state_dir:
126
+ return Path(state_dir) / "hub-settings.json"
127
+ return Path(cwd or os.getcwd()) / ".coding-os" / "hub-settings.json"
128
+
129
+
130
+ # Deterministic auth-mode override (TASK-756): Hub Settings → Claude Auth lets a
131
+ # project pick "subscription" (default — the CLI's own OAuth session, byte-
132
+ # identical to before this existed) or "api_key" (forward the user's key as
133
+ # ANTHROPIC_API_KEY). Per platform.claude.com/docs/en/authentication, an API
134
+ # key set on the subprocess env beats subscription OAuth in non-interactive/SDK
135
+ # mode — so "subscription" mode must EXPLICITLY clear the var (not merely omit
136
+ # it), or a stray ANTHROPIC_API_KEY already in the Hub server's own shell would
137
+ # silently override the user's chosen mode. Always returns an override (never
138
+ # {} = no-op) so this is a real switch, not a best-effort hint.
139
+ def _claude_auth_env(cwd: str) -> dict[str, str]:
140
+ try:
141
+ data = json.loads(_hub_settings_path(cwd).read_text(encoding="utf-8"))
142
+ auth = data.get("claude_auth") if isinstance(data, dict) else None
143
+ if isinstance(auth, dict) and auth.get("mode") == "api_key":
144
+ key = auth.get("api_key")
145
+ if isinstance(key, str) and key:
146
+ return {"ANTHROPIC_API_KEY": key}
147
+ except (OSError, ValueError) as exc:
148
+ logger.debug("claude_auth resolution skipped for cwd=%r: %s", cwd, exc)
149
+ return {"ANTHROPIC_API_KEY": ""}
150
+
151
+
152
+ def claude_session_options(
153
+ profile: str,
154
+ *,
155
+ cwd: str,
156
+ model: str | None = None,
157
+ system_prompt: Any = None,
158
+ resume: str | None = None,
159
+ fork: bool = False,
160
+ effort: str | None = None,
161
+ ):
162
+ """Build ClaudeAgentOptions for a profile — the SSOT for Claude SDK sessions (docs/adapters/session-options-builder.md)."""
163
+ from claude_agent_sdk import ClaudeAgentOptions
164
+
165
+ if profile not in _CHAT_PROFILES:
166
+ raise NotImplementedError(
167
+ f"claude_session_options: profile {profile!r} not yet migrated (TASK-417 phases)"
168
+ )
169
+
170
+ model = _resolve_model_alias(model) # tier alias → concrete id (R10/F6)
171
+ opts: dict[str, Any] = dict(
172
+ cwd=cwd,
173
+ model=model,
174
+ permission_mode="dontAsk",
175
+ # Fast conversational surface: skip the ~40s project SessionStart hook
176
+ # suite. Capability comes from programmatic mcp_servers below, NOT from
177
+ # setting_sources, so cos_* works without that latency.
178
+ setting_sources=[],
179
+ include_partial_messages=True,
180
+ # P2 capability: register coding-os MCP programmatically — renders
181
+ # --mcp-config independent of setting_sources (subprocess_cli.py:307).
182
+ mcp_servers=_cos_mcp_servers(cwd),
183
+ # Exclusive allow-list under dontAsk: base tools + the MCP wildcard.
184
+ allowed_tools=[*_CHAT_BASE_TOOLS, _DEFAULT_COS_MCP_ALLOW],
185
+ # P3: destructive-Bash deny floor (rm -rf / force-push / sudo / pipe-to-sh).
186
+ disallowed_tools=list(_DESTRUCTIVE_BASH_DENY),
187
+ # Hub Settings → Claude Auth (TASK-756): subscription OAuth by default,
188
+ # ANTHROPIC_API_KEY when the project opted into api_key mode.
189
+ env=_claude_auth_env(cwd),
190
+ )
191
+ if system_prompt is not None:
192
+ opts["system_prompt"] = system_prompt
193
+ if effort:
194
+ opts["effort"] = effort
195
+ if profile == "chat_resume":
196
+ if resume:
197
+ opts["resume"] = resume
198
+ opts["fork_session"] = fork
199
+ return ClaudeAgentOptions(**opts)
200
+
201
+
202
+ def claude_agent_options(**kwargs: Any):
203
+ """Generic ClaudeAgentOptions constructor — the adapter seam core routes every
204
+ non-profile option build through (P8: core never constructs the SDK type itself)."""
205
+ from claude_agent_sdk import ClaudeAgentOptions
206
+
207
+ return ClaudeAgentOptions(**kwargs)
208
+
209
+
210
+ # OTEL env vars the dispatcher copies from the parent process so the
211
+ # sub-session emits to the same collector. No collector defaults are
212
+ # bundled (D5) — operator sets exporter / endpoint / headers.
213
+ _OTEL_FORWARDED_VARS = (
214
+ "OTEL_TRACES_EXPORTER",
215
+ "OTEL_METRICS_EXPORTER",
216
+ "OTEL_LOGS_EXPORTER",
217
+ "OTEL_EXPORTER_OTLP_PROTOCOL",
218
+ "OTEL_EXPORTER_OTLP_ENDPOINT",
219
+ "OTEL_EXPORTER_OTLP_HEADERS",
220
+ "OTEL_RESOURCE_ATTRIBUTES",
221
+ "OTEL_METRIC_EXPORT_INTERVAL",
222
+ "OTEL_LOGS_EXPORT_INTERVAL",
223
+ "OTEL_TRACES_EXPORT_INTERVAL",
224
+ "OTEL_LOG_USER_PROMPTS",
225
+ "OTEL_LOG_TOOL_DETAILS",
226
+ "OTEL_LOG_TOOL_CONTENT",
227
+ "OTEL_LOG_RAW_API_BODIES",
228
+ "ENABLE_BETA_TRACING_DETAILED",
229
+ "BETA_TRACING_ENDPOINT",
230
+ "CLAUDE_CODE_ENABLE_TELEMETRY",
231
+ "CLAUDE_CODE_ENHANCED_TELEMETRY_BETA",
232
+ )
233
+
234
+
235
+ def _resolve_output_schema(meta: dict[str, Any]) -> dict[str, Any] | None:
236
+ raw = meta.get("output_schema") if isinstance(meta, dict) else None
237
+ if not isinstance(raw, str) or not raw.strip():
238
+ return None
239
+ cls_name = raw.split(".")[-1].strip()
240
+ if not cls_name.isidentifier():
241
+ logger.warning("invalid output_schema reference %r — ignoring", raw)
242
+ return None
243
+ try:
244
+ from thinking_os import cognition_schemas as _schemas
245
+ except ImportError as exc:
246
+ logger.warning("cognition_schemas import failed: %s", exc)
247
+ return None
248
+ schema_cls = getattr(_schemas, cls_name, None)
249
+ if schema_cls is None or not hasattr(schema_cls, "model_json_schema"):
250
+ logger.warning("no Pydantic class %s in cognition_schemas", cls_name)
251
+ return None
252
+ try:
253
+ return schema_cls.model_json_schema()
254
+ except (TypeError, ValueError) as exc:
255
+ logger.warning("model_json_schema() failed for %s: %s", cls_name, exc)
256
+ return None
257
+
258
+
259
+ # ---------------------------------------------------------------------------
260
+ # Dispatcher
261
+ # ---------------------------------------------------------------------------
262
+
263
+
264
+ def _presence_write(
265
+ project_root: Path, agent: str, session_id: str, event: str, pid: int | None = None
266
+ ) -> None:
267
+ """Write a single presence event for an SDK-spawned sub-agent."""
268
+ import json as _json
269
+ import os as _os
270
+ import time as _time
271
+
272
+ try:
273
+ d = project_root / ".coding-os" / agent / "sessions"
274
+ d.mkdir(parents=True, exist_ok=True)
275
+ path = d / f"{session_id}.json"
276
+ prev: dict[str, Any] = {}
277
+ if path.exists():
278
+ try:
279
+ prev = _json.loads(path.read_text(encoding="utf-8"))
280
+ except (OSError, _json.JSONDecodeError):
281
+ prev = {}
282
+ now = int(_time.time())
283
+ # Schema parity with _helpers/presence_write.py (the canonical writer):
284
+ # preserve model/sdk_uuid/used_tokens/context_updated_at so the Hub
285
+ # reader resolves them for SDK sub-agents too (P5).
286
+ new = {
287
+ "agent": agent,
288
+ "session_id": session_id,
289
+ "pid": int(pid) if pid is not None else int(prev.get("pid") or _os.getpid()),
290
+ "started_at": prev.get("started_at"),
291
+ "last_prompt_at": prev.get("last_prompt_at"),
292
+ "last_tool_at": prev.get("last_tool_at"),
293
+ "last_stop_at": prev.get("last_stop_at"),
294
+ "ended_at": prev.get("ended_at"),
295
+ "model": prev.get("model"),
296
+ "sdk_uuid": prev.get("sdk_uuid"),
297
+ "used_tokens": prev.get("used_tokens"),
298
+ "context_updated_at": prev.get("context_updated_at"),
299
+ }
300
+ if event == "start":
301
+ new["started_at"] = now
302
+ new["ended_at"] = None
303
+ new["last_stop_at"] = None
304
+ elif event == "prompt":
305
+ new["last_prompt_at"] = now
306
+ new["last_stop_at"] = None
307
+ new["started_at"] = new["started_at"] or now
308
+ elif event == "tool":
309
+ new["last_tool_at"] = now
310
+ new["started_at"] = new["started_at"] or now
311
+ elif event == "stop":
312
+ new["last_stop_at"] = now
313
+ elif event == "end":
314
+ new["ended_at"] = now
315
+ # Keep the .json stem on the temp file (canonical writer uses
316
+ # f"{path}.tmp.{pid}") so presence_gc reaps crash-orphaned temps (P31).
317
+ tmp = path.parent / f"{path.name}.tmp.{_os.getpid()}"
318
+ tmp.write_text(_json.dumps(new, separators=(",", ":")), encoding="utf-8")
319
+ _os.replace(tmp, path)
320
+ except OSError as exc:
321
+ logger.debug("SDK presence write failed for %s: %s", session_id, exc)
322
+
323
+
324
+ def _dispatch_trace_content_enabled() -> bool:
325
+ return os.environ.get("COS_DISPATCH_EVENT_CONTENT", "").strip().lower() in {
326
+ "1",
327
+ "true",
328
+ "yes",
329
+ "on",
330
+ }
331
+
332
+
333
+ def _emit_dispatch_trace(
334
+ session_id: str, kind: str, formula_id: str | None, data: dict[str, Any] | None = None
335
+ ) -> None:
336
+ # Tee a dispatch lifecycle/turn event to the append-only cognition trace
337
+ # sink (thinking_os.tracing) so the Hub can tail + replay the run. Fail-open:
338
+ # a tracing failure must never alter the returned EvidenceBundle or break the
339
+ # dispatch. Partial-message text rides along only when content is explicitly
340
+ # enabled (COS_DISPATCH_EVENT_CONTENT), off by default.
341
+ try:
342
+ from thinking_os.tracing import emit
343
+
344
+ emit(session_id, kind, data or {}, role=formula_id)
345
+ except Exception as exc:
346
+ logger.debug("dispatch trace emit skipped (%s): %s", kind, exc)
347
+
348
+
349
+ class ClaudeSDKDispatcher:
350
+ name = "claude-sdk"
351
+
352
+ def __init__(self) -> None:
353
+ self._sdk_ok = False
354
+ self._import_error: str | None = None
355
+ try:
356
+ import claude_agent_sdk # noqa: F401
357
+
358
+ self._sdk_ok = True
359
+ except ImportError as exc:
360
+ self._import_error = str(exc)
361
+
362
+ def available(self) -> bool:
363
+ return self._sdk_ok
364
+
365
+ async def dispatch(self, request: DispatchRequest) -> DispatchResult:
366
+ t0 = time.monotonic()
367
+ if not self._sdk_ok:
368
+ return DispatchResult(
369
+ formula_id=request.formula_id,
370
+ status="error",
371
+ error=f"claude-agent-sdk not importable: {self._import_error}",
372
+ dispatcher_name=self.name,
373
+ latency_ms=int((time.monotonic() - t0) * 1000),
374
+ )
375
+
376
+ from claude_agent_sdk import (
377
+ AssistantMessage,
378
+ ClaudeAgentOptions,
379
+ HookMatcher,
380
+ ResultMessage,
381
+ TextBlock,
382
+ UserMessage,
383
+ query,
384
+ )
385
+
386
+ # SDK searches cwd if agent_file is relative — silently picks the
387
+ # wrong file if a same-named role lives in another path. Force
388
+ # absolute so a misconfigured caller fails loudly here, not 30s
389
+ # into a wrong dispatch.
390
+ if not Path(request.agent_file).is_absolute():
391
+ return DispatchResult(
392
+ formula_id=request.formula_id,
393
+ status="error",
394
+ error=f"agent_file must be absolute, got: {request.agent_file!r}",
395
+ dispatcher_name=self.name,
396
+ latency_ms=int((time.monotonic() - t0) * 1000),
397
+ )
398
+
399
+ try:
400
+ system_prompt_body, agent_meta = load_agent_prompt(request.agent_file)
401
+ except FileNotFoundError as exc:
402
+ return DispatchResult(
403
+ formula_id=request.formula_id,
404
+ status="error",
405
+ error=str(exc),
406
+ dispatcher_name=self.name,
407
+ latency_ms=int((time.monotonic() - t0) * 1000),
408
+ )
409
+
410
+ # Skill inheritance — formula sub-sessions get a fresh context, so
411
+ # they don't inherit parent skills. Each role's frontmatter
412
+ # declares which skills it needs (e.g. implementer → ["clean-code"]).
413
+ # Other adapters that don't honor this key ignore it (Rule 1).
414
+ role_skills = agent_meta.get("skills") if isinstance(agent_meta, dict) else None
415
+ if role_skills is not None and (
416
+ not isinstance(role_skills, list) or not all(isinstance(s, str) for s in role_skills)
417
+ ):
418
+ role_skills = None
419
+ logger.warning(
420
+ "agent file %s has invalid `skills` frontmatter — ignoring",
421
+ request.agent_file,
422
+ )
423
+
424
+ # Append the formula spec to Claude Code's preset prompt. The
425
+ # preset carries Claude Code's coding/safety baseline; the formula
426
+ # body adds the role-specific Output contract. Setting
427
+ # `exclude_dynamic_sections=True` strips per-cwd state (git,
428
+ # date, OS, memory paths) from the system prompt and emits it as
429
+ # a first-user-message block instead — this is what makes the
430
+ # prompt cache reusable across consumer projects.
431
+ formula_append = (
432
+ f"{system_prompt_body}\n\n"
433
+ f"## Dispatch Context\n"
434
+ f"- Formula: {request.formula_id}\n"
435
+ f"- Persona: {request.persona_id or 'n/a'}\n"
436
+ f"- Intensity: {request.intensity}\n\n"
437
+ f"## Instruction\n"
438
+ f"Produce the EvidenceBundle slice for this formula as a single "
439
+ f"```json ... ``` block at the end of your response. Do not wrap "
440
+ f"it in additional prose."
441
+ )
442
+ system_prompt: dict[str, Any] = {
443
+ "type": "preset",
444
+ "preset": "claude_code",
445
+ "append": formula_append,
446
+ "exclude_dynamic_sections": True,
447
+ }
448
+ user_prompt = (
449
+ f"Input slice (upstream formulas only):\n"
450
+ f"```json\n{json.dumps(request.input_slice, indent=2, default=str)}\n```\n\n"
451
+ f"{request.prompt}"
452
+ )
453
+
454
+ # Always include the coding-os MCP wildcard alongside any caller-
455
+ # provided allow-list. `acceptEdits` does NOT auto-approve MCP,
456
+ # so without an explicit entry every cos_* tool call would deny
457
+ # in dontAsk mode.
458
+ allow_list = list(request.allowed_tools or [])
459
+ if not any(item.startswith("mcp__coding-os") for item in allow_list):
460
+ allow_list.append(_DEFAULT_COS_MCP_ALLOW)
461
+
462
+ # Resolve a routed tier alias (sonnet/opus/...) to a concrete adapter
463
+ # model id BEFORE it reaches the SDK or the effort gate (R10/F6).
464
+ resolved_model = _resolve_model_alias(request.model)
465
+
466
+ # High-tier models get "xhigh"; everything else uses the SDK
467
+ # default (None → "high"). See _XHIGH_EFFORT_MODEL_PREFIXES.
468
+ effort: str | None = None
469
+ if resolved_model and resolved_model.startswith(_XHIGH_EFFORT_MODEL_PREFIXES):
470
+ effort = "xhigh"
471
+
472
+ # Structured output (T1) — opt-in per role via
473
+ # `structured_output: true` frontmatter. SDK enforces the
474
+ # schema and surfaces failures as
475
+ # subtype="error_max_structured_output_retries".
476
+ output_format: dict[str, Any] | None = None
477
+ wants_structured = bool(
478
+ isinstance(agent_meta, dict) and agent_meta.get("structured_output")
479
+ )
480
+ if wants_structured:
481
+ schema = _resolve_output_schema(agent_meta)
482
+ if schema is not None:
483
+ output_format = {"type": "json_schema", "schema": schema}
484
+ else:
485
+ logger.warning(
486
+ "role %s requested structured_output but schema could not be "
487
+ "resolved — falling back to regex extraction",
488
+ request.formula_id,
489
+ )
490
+
491
+ # Forward telemetry env so the sub-session emits to the same
492
+ # OTEL collector as the parent (D5 leaves the collector to
493
+ # operators; we just propagate). OTEL_SERVICE_NAME identifies
494
+ # the dispatcher distinctly from a normal claude-code session.
495
+ # Hub Settings → Claude Auth (TASK-756) first, so an OTEL var can never
496
+ # shadow the ANTHROPIC_API_KEY override (disjoint key sets, but explicit
497
+ # ordering keeps the precedence obvious to a future reader).
498
+ env: dict[str, str] = _claude_auth_env(request.cwd or os.getcwd())
499
+ for var in _OTEL_FORWARDED_VARS:
500
+ value = os.environ.get(var)
501
+ if value:
502
+ env[var] = value
503
+ env.setdefault("OTEL_SERVICE_NAME", "coding-os-claude")
504
+ env.setdefault("OTEL_METRICS_INCLUDE_SESSION_ID", "true")
505
+
506
+ # Long-context beta (D6). Caller opts in per request; default
507
+ # short context to keep cache hits high and cost predictable.
508
+ betas = ["context-1m-2025-08-07"] if request.long_context else None
509
+
510
+ logger.debug(
511
+ "claude-sdk dispatch: formula=%s model=%s effort=%s "
512
+ "structured=%s budget_usd=%s tools=%s",
513
+ request.formula_id,
514
+ resolved_model,
515
+ effort,
516
+ bool(output_format),
517
+ request.max_budget_usd,
518
+ allow_list,
519
+ )
520
+
521
+ # Pre-compute sub-session identity so programmatic hook closures
522
+ # below can stamp presence updates with a stable id. Same path
523
+ # safety as the existing presence_write call (formula_id already
524
+ # restricted to [A-Za-z0-9_-] by DispatchRequest validator).
525
+ safe_formula = re.sub(r"[^A-Za-z0-9_-]", "_", request.formula_id) or "formula"
526
+ sub_session_id = f"ses-claude-sdk-{safe_formula}-{int(time.time())}-{os.getpid()}"
527
+ project_root = Path(request.cwd) if request.cwd else Path(os.getcwd())
528
+
529
+ # Per-dispatch state captured by programmatic hooks (T3.1, T3.2)
530
+ # and by the message-stream handler (T3.3 / T1.3). Closures
531
+ # below bind to these locals so concurrent dispatches stay
532
+ # isolated.
533
+ transcript_parts: list[str] = []
534
+ # v27: capture the model the dispatcher requested so persistence
535
+ # can fill formula_dispatches.model without re-deriving from usage.
536
+ result_meta: dict[str, Any] = {"model": resolved_model} if resolved_model else {}
537
+ result_subtype: str | None = None
538
+ structured_output: Any = None
539
+ tool_calls: list[dict[str, Any]] = []
540
+ tool_failures: list[dict[str, Any]] = []
541
+ # T9.2 — UserMessage UUIDs for file checkpointing replay
542
+ checkpoint_uuids: list[str] = []
543
+
544
+ # Hook callbacks must NEVER raise — the SDK propagates exceptions
545
+ # back to the CLI subprocess which exits 1, killing the dispatch.
546
+ # Wrap every body in try/except + log; always return `{}` so the
547
+ # SDK keeps streaming.
548
+ async def _pre_tool_use(
549
+ input_data: dict[str, Any],
550
+ tool_use_id: str | None,
551
+ _ctx: Any,
552
+ ) -> dict[str, Any]:
553
+ try:
554
+ tool_calls.append(
555
+ {
556
+ "tool_use_id": tool_use_id,
557
+ "tool_name": input_data.get("tool_name"),
558
+ "tool_input": input_data.get("tool_input"),
559
+ }
560
+ )
561
+ except Exception as exc:
562
+ logger.debug("PreToolUse hook capture failed: %s", exc)
563
+ return {}
564
+
565
+ async def _post_tool_use_failure(
566
+ input_data: dict[str, Any],
567
+ tool_use_id: str | None,
568
+ _ctx: Any,
569
+ ) -> dict[str, Any]:
570
+ try:
571
+ tool_failures.append(
572
+ {
573
+ "tool_use_id": tool_use_id,
574
+ "tool_name": input_data.get("tool_name"),
575
+ "tool_response": input_data.get("tool_response"),
576
+ }
577
+ )
578
+ logger.warning(
579
+ "claude-sdk dispatch tool failure: formula=%s tool=%s",
580
+ request.formula_id,
581
+ input_data.get("tool_name"),
582
+ )
583
+ except Exception as exc:
584
+ logger.debug("PostToolUseFailure hook capture failed: %s", exc)
585
+ return {}
586
+
587
+ # `--json-schema` activates a `StructuredOutput` tool the model
588
+ # invokes to deliver the typed payload. That tool call AND the
589
+ # tool_result it produces each burn a turn, plus the model
590
+ # often emits a closing assistant turn after the result. Using
591
+ # 3 turns avoids spurious `error_max_turns` subtypes when the
592
+ # model decides to acknowledge before stopping. Even then the
593
+ # post-stream handler treats a populated `structured_output`
594
+ # as success regardless of subtype.
595
+ max_turns = (
596
+ request.max_turns
597
+ if request.max_turns is not None
598
+ else (3 if output_format is not None else 1)
599
+ )
600
+ opts_kwargs: dict[str, Any] = dict(
601
+ system_prompt=system_prompt,
602
+ max_turns=max_turns,
603
+ allowed_tools=allow_list,
604
+ disallowed_tools=list(_DESTRUCTIVE_BASH_DENY),
605
+ cwd=request.cwd,
606
+ # Headless dispatch: never prompt the user. Allow-list is the
607
+ # contract; unmatched tools deny silently.
608
+ permission_mode="dontAsk",
609
+ # Isolate from the host user's ~/.claude/. The formula sub-
610
+ # session must be reproducible across machines — user
611
+ # settings, memory, and CLAUDE.md outside the project root
612
+ # would silently change behavior.
613
+ setting_sources=["project"],
614
+ model=resolved_model,
615
+ effort=effort,
616
+ # Per-role skill inheritance. Sub-sessions don't inherit
617
+ # parent skills, so each role declares its required skills
618
+ # in its agent file's frontmatter. None = use SDK default.
619
+ skills=role_skills,
620
+ env=env,
621
+ )
622
+ if output_format is not None:
623
+ opts_kwargs["output_format"] = output_format
624
+ if request.max_budget_usd is not None:
625
+ opts_kwargs["max_budget_usd"] = float(request.max_budget_usd)
626
+ if betas is not None:
627
+ opts_kwargs["betas"] = betas
628
+ # The CLI validates --session-id as a UUID, so the SDK gets a real
629
+ # UUID while presence/trace keys keep the readable sub_session_id;
630
+ # result_meta carries the mapping for resume debugging.
631
+ sdk_session_uuid = str(uuid.uuid4())
632
+ opts_kwargs["session_id"] = sdk_session_uuid
633
+ result_meta["sdk_session_id"] = sdk_session_uuid
634
+ # T9.1: enable file checkpointing for roles that declare it in frontmatter.
635
+ # Implements checkpoint/replay for edit-heavy roles (implementer/refactorer).
636
+ if isinstance(agent_meta, dict) and agent_meta.get("enable_file_checkpointing"):
637
+ opts_kwargs["enable_file_checkpointing"] = True
638
+ # Programmatic hooks (T3) — these run inline in the
639
+ # dispatcher process, capturing tool call metadata and failures
640
+ # for the formula_dispatches audit row. Empty matcher = match
641
+ # every tool. PostToolUseFailure surfaces tool errors that the
642
+ # SDK would otherwise swallow into the transcript.
643
+ # T3.4 [-]: SubagentStart / SubagentStop not wired here — D1
644
+ # chose query() over Agent-tool, so no sub-agents are spawned.
645
+ # These events still fire in interactive sessions via registry.yaml.
646
+ opts_kwargs["hooks"] = {
647
+ "PreToolUse": [HookMatcher(matcher="", hooks=[_pre_tool_use])],
648
+ "PostToolUseFailure": [HookMatcher(matcher="", hooks=[_post_tool_use_failure])],
649
+ }
650
+ options = ClaudeAgentOptions(**opts_kwargs)
651
+
652
+ # Presence: mark "start" only after options is built so the
653
+ # board doesn't show a session that never actually launched
654
+ # (e.g. ClaudeAgentOptions raised). The matching "end" emits
655
+ # in the finally block below.
656
+ _presence_write(project_root, "claude", sub_session_id, "start")
657
+ _emit_dispatch_trace(
658
+ sub_session_id,
659
+ "dispatch_started",
660
+ request.formula_id,
661
+ {"formula_id": request.formula_id},
662
+ )
663
+
664
+ dispatch_turn_seq = 0
665
+
666
+ async def _run() -> None:
667
+ nonlocal result_subtype, structured_output, dispatch_turn_seq
668
+ async for msg in query(prompt=user_prompt, options=options):
669
+ if isinstance(msg, UserMessage):
670
+ # Capture checkpoint UUIDs for file-checkpointing replay (T9.2)
671
+ try:
672
+ uuid_val = getattr(msg, "uuid", None)
673
+ if uuid_val is not None:
674
+ checkpoint_uuids.append(str(uuid_val))
675
+ except Exception as exc:
676
+ logger.debug("UUID capture failed: %s", exc)
677
+ elif isinstance(msg, AssistantMessage):
678
+ _presence_write(project_root, "claude", sub_session_id, "tool")
679
+ _turn_texts: list[str] = []
680
+ for block in msg.content:
681
+ if isinstance(block, TextBlock):
682
+ transcript_parts.append(block.text)
683
+ _turn_texts.append(block.text)
684
+ # Count + emit only text-bearing turns so `turns` tracks
685
+ # reply turns (tool-only assistant messages are captured
686
+ # separately in result_meta['tool_calls']); a pure tool
687
+ # turn otherwise inflated the count with a chars=0 event.
688
+ if _turn_texts:
689
+ dispatch_turn_seq += 1
690
+ _turn_data: dict[str, Any] = {
691
+ "seq": dispatch_turn_seq,
692
+ "chars": sum(len(t) for t in _turn_texts),
693
+ }
694
+ if _dispatch_trace_content_enabled():
695
+ _turn_data["text"] = "".join(_turn_texts)
696
+ _emit_dispatch_trace(
697
+ sub_session_id, "dispatch_turn", request.formula_id, _turn_data
698
+ )
699
+ elif isinstance(msg, ResultMessage):
700
+ # ResultMessage is emitted once per query, at the
701
+ # end. Capture every field the post-stream handler
702
+ # might need.
703
+ result_subtype = getattr(msg, "subtype", None)
704
+ structured_output = getattr(msg, "structured_output", None)
705
+ for attr in (
706
+ "total_cost_usd",
707
+ "duration_ms",
708
+ "duration_api_ms",
709
+ "num_turns",
710
+ "stop_reason",
711
+ "session_id",
712
+ ):
713
+ val = getattr(msg, attr, None)
714
+ if val is not None:
715
+ result_meta[attr] = val
716
+ # usage / model_usage carry the per-step token
717
+ # breakdown (input, output, cache_read,
718
+ # cache_creation). Persist verbatim — caller
719
+ # decides whether to summarize.
720
+ for attr in ("usage", "model_usage", "permission_denials"):
721
+ val = getattr(msg, attr, None)
722
+ if val is not None:
723
+ result_meta[attr] = val
724
+
725
+ # Single-writer for the terminal presence event so a future
726
+ # side-effect (emit a stream event, decrement a gauge) can't
727
+ # double-fire. Happy path calls "stop" then finally emits "end";
728
+ # error/timeout paths skip straight to "end".
729
+ dispatch_outcome: DispatchResult | None = None
730
+ try:
731
+ try:
732
+ await asyncio.wait_for(_run(), timeout=request.timeout_s)
733
+ _presence_write(project_root, "claude", sub_session_id, "stop")
734
+ except asyncio.TimeoutError:
735
+ dispatch_outcome = DispatchResult(
736
+ formula_id=request.formula_id,
737
+ status="timeout",
738
+ error=f"timed out after {request.timeout_s}s",
739
+ dispatcher_name=self.name,
740
+ latency_ms=int((time.monotonic() - t0) * 1000),
741
+ raw_transcript="\n".join(transcript_parts) or None,
742
+ )
743
+ except Exception as exc:
744
+ logger.exception("claude-sdk dispatch failed")
745
+ dispatch_outcome = DispatchResult(
746
+ formula_id=request.formula_id,
747
+ status="error",
748
+ error=f"{type(exc).__name__}: {exc}",
749
+ dispatcher_name=self.name,
750
+ latency_ms=int((time.monotonic() - t0) * 1000),
751
+ raw_transcript="\n".join(transcript_parts) or None,
752
+ )
753
+ finally:
754
+ _presence_write(project_root, "claude", sub_session_id, "end")
755
+ # Derive the terminal status from what is known here: an early
756
+ # timeout/error sets dispatch_outcome; otherwise only the budget /
757
+ # max-turns subtypes become a status="error" DispatchResult below —
758
+ # error_max_structured_output_retries is recoverable (it falls
759
+ # through to regex extraction and can still return "ok"), so match
760
+ # those two exactly rather than any "error"-prefixed subtype.
761
+ if dispatch_outcome is not None:
762
+ _final_status = dispatch_outcome.status
763
+ elif result_subtype in ("error_max_budget_usd", "error_max_turns"):
764
+ _final_status = "error"
765
+ else:
766
+ _final_status = "ok"
767
+ _emit_dispatch_trace(
768
+ sub_session_id,
769
+ "dispatch_completed",
770
+ request.formula_id,
771
+ {"status": _final_status, "turns": dispatch_turn_seq},
772
+ )
773
+
774
+ # Programmatic-hook captures ride into result_meta so the
775
+ # `formula_dispatches` audit row records full tool history.
776
+ if tool_calls:
777
+ result_meta["tool_calls"] = tool_calls
778
+ if tool_failures:
779
+ result_meta["tool_failures"] = tool_failures
780
+ # T9.2: file-checkpointing replay UUIDs (only populated when
781
+ # enable_file_checkpointing is set in role frontmatter).
782
+ if checkpoint_uuids:
783
+ result_meta["checkpoints"] = checkpoint_uuids
784
+
785
+ if dispatch_outcome is not None:
786
+ return dispatch_outcome
787
+
788
+ transcript = "\n".join(transcript_parts)
789
+ latency_ms = int((time.monotonic() - t0) * 1000)
790
+
791
+ # Map SDK error subtypes to dispatcher status. Budget exhaustion
792
+ # and retry exhaustion are operationally distinct from a generic
793
+ # "error_during_execution" — keep the original subtype in the
794
+ # error string so callers can pattern-match.
795
+ if result_subtype == "error_max_budget_usd":
796
+ return DispatchResult(
797
+ formula_id=request.formula_id,
798
+ status="error",
799
+ error=(
800
+ f"max_budget_usd={request.max_budget_usd} exhausted; "
801
+ f"actual_cost_usd={result_meta.get('total_cost_usd')!r}"
802
+ ),
803
+ output_json={"_meta": dict(result_meta)},
804
+ latency_ms=latency_ms,
805
+ dispatcher_name=self.name,
806
+ raw_transcript=transcript or None,
807
+ )
808
+ if result_subtype == "error_max_turns":
809
+ return DispatchResult(
810
+ formula_id=request.formula_id,
811
+ status="error",
812
+ error="max_turns exhausted",
813
+ output_json={"_meta": dict(result_meta)},
814
+ latency_ms=latency_ms,
815
+ dispatcher_name=self.name,
816
+ raw_transcript=transcript or None,
817
+ )
818
+ if result_subtype == "error_max_structured_output_retries":
819
+ # Schema enforcement gave up; fall through to regex extraction
820
+ # so the dispatcher still surfaces partial work instead of an
821
+ # opaque error. Caller sees the subtype via raw_transcript +
822
+ # output_json._meta.subtype.
823
+ result_meta["structured_output_retry_exhausted"] = True
824
+
825
+ # Prefer SDK-enforced structured output. extract_json_block is the
826
+ # 0.1.x fallback for roles that don't opt into structured output
827
+ # AND for retry-exhausted runs (logged above).
828
+ output_json: dict[str, Any]
829
+ if isinstance(structured_output, dict):
830
+ output_json = dict(structured_output)
831
+ else:
832
+ output_json = extract_json_block(transcript)
833
+
834
+ if result_meta:
835
+ output_json.setdefault("_meta", {}).update(result_meta)
836
+ if result_subtype:
837
+ output_json.setdefault("_meta", {})["subtype"] = result_subtype
838
+
839
+ ok = bool(output_json) and any(k != "_meta" for k in output_json.keys())
840
+ # T1.5: surface the retry-exhausted subtype in the error field so callers
841
+ # can route to a retry-with-relaxed-prompt path. Status stays "ok" when
842
+ # regex fallback recovered usable JSON — the output bundle is still
843
+ # populated. Callers that need strict schema compliance should check error.
844
+ if not ok:
845
+ error_str = "no usable JSON in dispatch output"
846
+ elif result_subtype == "error_max_structured_output_retries":
847
+ error_str = (
848
+ "error_max_structured_output_retries: schema enforcement exhausted, "
849
+ "fell back to regex extraction"
850
+ )
851
+ else:
852
+ error_str = None
853
+ return DispatchResult(
854
+ formula_id=request.formula_id,
855
+ status="ok" if ok else "error",
856
+ output_json=output_json,
857
+ latency_ms=latency_ms,
858
+ dispatcher_name=self.name,
859
+ error=error_str,
860
+ raw_transcript=transcript,
861
+ )
862
+
863
+
864
+ # ---------------------------------------------------------------------------
865
+ # Factory — imported by core/thinking_os/dispatcher.py via importlib
866
+ # ---------------------------------------------------------------------------
867
+
868
+
869
+ def build_dispatcher() -> ClaudeSDKDispatcher:
870
+ """Entry point the factory looks for when loading this module."""
871
+ return ClaudeSDKDispatcher()