mednotes-opencode 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (430) hide show
  1. package/.opencode/agents/med-chat-triager.md +204 -0
  2. package/.opencode/agents/med-flashcard-maker.md +63 -0
  3. package/.opencode/agents/med-knowledge-architect.md +230 -0
  4. package/.opencode/agents/med-link-graph-curator.md +177 -0
  5. package/.opencode/agents/med-publish-guard.md +62 -0
  6. package/.opencode/commands/flashcards.md +25 -0
  7. package/.opencode/commands/mednotes/create.md +25 -0
  8. package/.opencode/commands/mednotes/enrich.md +27 -0
  9. package/.opencode/commands/mednotes/fix-wiki.md +27 -0
  10. package/.opencode/commands/mednotes/history.md +22 -0
  11. package/.opencode/commands/mednotes/link-body.md +25 -0
  12. package/.opencode/commands/mednotes/link-related.md +27 -0
  13. package/.opencode/commands/mednotes/link.md +27 -0
  14. package/.opencode/commands/mednotes/pdf-library.md +27 -0
  15. package/.opencode/commands/mednotes/process-chats.md +23 -0
  16. package/.opencode/commands/mednotes/setup.md +21 -0
  17. package/.opencode/commands/mednotes/status.md +27 -0
  18. package/.opencode/commands/mednotes/telemetry.md +27 -0
  19. package/.opencode/commands/report.md +26 -0
  20. package/.opencode/mednotes/AGENTS.md +57 -0
  21. package/.opencode/mednotes/agents/med-chat-triager.md +197 -0
  22. package/.opencode/mednotes/agents/med-flashcard-maker.md +56 -0
  23. package/.opencode/mednotes/agents/med-knowledge-architect.md +224 -0
  24. package/.opencode/mednotes/agents/med-link-graph-curator.md +171 -0
  25. package/.opencode/mednotes/agents/med-publish-guard.md +55 -0
  26. package/.opencode/mednotes/contracts/.gitkeep +1 -0
  27. package/.opencode/mednotes/contracts/agents.json +116 -0
  28. package/.opencode/mednotes/contracts/opencode-plugin.json +70 -0
  29. package/.opencode/mednotes/docs/agent-prompt-hardening.md +567 -0
  30. package/.opencode/mednotes/docs/agent-role-contracts.md +94 -0
  31. package/.opencode/mednotes/docs/anki-mcp-twenty-rules.md +214 -0
  32. package/.opencode/mednotes/docs/anki-templates/README.md +39 -0
  33. package/.opencode/mednotes/docs/anki-templates/cloze.back.html +23 -0
  34. package/.opencode/mednotes/docs/anki-templates/cloze.front.html +14 -0
  35. package/.opencode/mednotes/docs/anki-templates/qa.back.html +24 -0
  36. package/.opencode/mednotes/docs/anki-templates/qa.front.html +14 -0
  37. package/.opencode/mednotes/docs/anki-templates/style.css +182 -0
  38. package/.opencode/mednotes/docs/atomicity-splitting-policy.md +113 -0
  39. package/.opencode/mednotes/docs/extension-docs.md +40 -0
  40. package/.opencode/mednotes/docs/flashcard-ingestion.md +278 -0
  41. package/.opencode/mednotes/docs/knowledge-architect.md +208 -0
  42. package/.opencode/mednotes/docs/merge-policy.md +110 -0
  43. package/.opencode/mednotes/docs/public-vocabulary.md +104 -0
  44. package/.opencode/mednotes/docs/semantic-linker.md +141 -0
  45. package/.opencode/mednotes/docs/taxonomy-policy.md +90 -0
  46. package/.opencode/mednotes/docs/triage-policy.md +187 -0
  47. package/.opencode/mednotes/docs/vault-version-control.md +758 -0
  48. package/.opencode/mednotes/docs/vocabulary-db-recovery.md +58 -0
  49. package/.opencode/mednotes/docs/workflow-output-contract.md +779 -0
  50. package/.opencode/mednotes/hooks/hooks.json +79 -0
  51. package/.opencode/mednotes/package-lock.json +6361 -0
  52. package/.opencode/mednotes/package.json +15 -0
  53. package/.opencode/mednotes/pyproject.toml +48 -0
  54. package/.opencode/mednotes/scripts/bootstrap_windows_python_uv.cmd +13 -0
  55. package/.opencode/mednotes/scripts/bootstrap_windows_python_uv.ps1 +172 -0
  56. package/.opencode/mednotes/scripts/enrich_notes.py +23 -0
  57. package/.opencode/mednotes/scripts/full_reset_windows_python_uv.cmd +13 -0
  58. package/.opencode/mednotes/scripts/hooks/antigravity_hook_status.mjs +212 -0
  59. package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/antigravity.mjs +169 -0
  60. package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/harness_payload.mjs +103 -0
  61. package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/opencode_plugin.mjs +341 -0
  62. package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/opencode_user_config_sync.mjs +177 -0
  63. package/.opencode/mednotes/scripts/hooks/mednotes_hook/anki_preflight.mjs +214 -0
  64. package/.opencode/mednotes/scripts/hooks/mednotes_hook/cli.mjs +143 -0
  65. package/.opencode/mednotes/scripts/hooks/mednotes_hook/diagnostics.mjs +11 -0
  66. package/.opencode/mednotes/scripts/hooks/mednotes_hook/domain/agent_directive_core.mjs +160 -0
  67. package/.opencode/mednotes/scripts/hooks/mednotes_hook/fsm_directive.mjs +1470 -0
  68. package/.opencode/mednotes/scripts/hooks/mednotes_hook/hook_errors.mjs +120 -0
  69. package/.opencode/mednotes/scripts/hooks/mednotes_hook/retention.mjs +114 -0
  70. package/.opencode/mednotes/scripts/hooks/mednotes_hook/runtime.mjs +174 -0
  71. package/.opencode/mednotes/scripts/hooks/mednotes_hook/telemetry_capture.mjs +511 -0
  72. package/.opencode/mednotes/scripts/hooks/mednotes_hook/vault_guard.mjs +624 -0
  73. package/.opencode/mednotes/scripts/hooks/mednotes_hook.mjs +5 -0
  74. package/.opencode/mednotes/scripts/mednotes/_runtime_paths.py +24 -0
  75. package/.opencode/mednotes/scripts/mednotes/anki_model_validator.py +18 -0
  76. package/.opencode/mednotes/scripts/mednotes/capture_extension_diff.py +1562 -0
  77. package/.opencode/mednotes/scripts/mednotes/feedback_report.py +16 -0
  78. package/.opencode/mednotes/scripts/mednotes/flashcard_index.py +18 -0
  79. package/.opencode/mednotes/scripts/mednotes/flashcard_pipeline.py +18 -0
  80. package/.opencode/mednotes/scripts/mednotes/flashcard_report.py +18 -0
  81. package/.opencode/mednotes/scripts/mednotes/flashcard_sources.py +18 -0
  82. package/.opencode/mednotes/scripts/mednotes/obsidian/README.md +6 -0
  83. package/.opencode/mednotes/scripts/mednotes/obsidian_note_utils.py +20 -0
  84. package/.opencode/mednotes/scripts/mednotes/pdf_library/cli.py +16 -0
  85. package/.opencode/mednotes/scripts/mednotes/project_fsm.py +229 -0
  86. package/.opencode/mednotes/scripts/mednotes/setup_telemetry_email.py +404 -0
  87. package/.opencode/mednotes/scripts/mednotes/sync_anki_twenty_rules.py +18 -0
  88. package/.opencode/mednotes/scripts/mednotes/sync_opencode_user_config.py +36 -0
  89. package/.opencode/mednotes/scripts/mednotes/wiki/cli.py +20 -0
  90. package/.opencode/mednotes/scripts/mednotes/wiki_graph.py +18 -0
  91. package/.opencode/mednotes/scripts/mednotes/wiki_tree.py +134 -0
  92. package/.opencode/mednotes/scripts/reset_windows_python_uv.ps1 +625 -0
  93. package/.opencode/mednotes/scripts/run_python.mjs +109 -0
  94. package/.opencode/mednotes/scripts/vault/vault_commit.ps1 +19 -0
  95. package/.opencode/mednotes/scripts/vault/vault_commit.sh +18 -0
  96. package/.opencode/mednotes/scripts/vault/vault_git.ps1 +19 -0
  97. package/.opencode/mednotes/scripts/vault/vault_git.py +3107 -0
  98. package/.opencode/mednotes/scripts/vault/vault_git.sh +18 -0
  99. package/.opencode/mednotes/scripts/vault/vault_precommit.ps1 +19 -0
  100. package/.opencode/mednotes/scripts/vault/vault_precommit.sh +18 -0
  101. package/.opencode/mednotes/skills/THIRD_PARTY_NOTICES.md +45 -0
  102. package/.opencode/mednotes/skills/create-medical-flashcards/SKILL.md +113 -0
  103. package/.opencode/mednotes/skills/create-medical-note/SKILL.md +90 -0
  104. package/.opencode/mednotes/skills/enrich-medical-note/SKILL.md +120 -0
  105. package/.opencode/mednotes/skills/fix-medical-wiki/SKILL.md +559 -0
  106. package/.opencode/mednotes/skills/link-medical-wiki/SKILL.md +224 -0
  107. package/.opencode/mednotes/skills/obsidian-cli/SKILL.md +118 -0
  108. package/.opencode/mednotes/skills/obsidian-markdown/SKILL.md +207 -0
  109. package/.opencode/mednotes/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
  110. package/.opencode/mednotes/skills/obsidian-markdown/references/EMBEDS.md +63 -0
  111. package/.opencode/mednotes/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
  112. package/.opencode/mednotes/skills/obsidian-ops/SKILL.md +136 -0
  113. package/.opencode/mednotes/skills/pdf-library/SKILL.md +45 -0
  114. package/.opencode/mednotes/skills/process-medical-chats/SKILL.md +246 -0
  115. package/.opencode/mednotes/skills/workflow-report/SKILL.md +100 -0
  116. package/.opencode/mednotes/src/mednotes/__init__.py +5 -0
  117. package/.opencode/mednotes/src/mednotes/domains/__init__.py +5 -0
  118. package/.opencode/mednotes/src/mednotes/domains/flashcards/README.md +26 -0
  119. package/.opencode/mednotes/src/mednotes/domains/flashcards/__init__.py +2 -0
  120. package/.opencode/mednotes/src/mednotes/domains/flashcards/build_demo_apkg.py +177 -0
  121. package/.opencode/mednotes/src/mednotes/domains/flashcards/contracts.py +385 -0
  122. package/.opencode/mednotes/src/mednotes/domains/flashcards/flashcards_machine.py +522 -0
  123. package/.opencode/mednotes/src/mednotes/domains/flashcards/fsm.py +817 -0
  124. package/.opencode/mednotes/src/mednotes/domains/flashcards/index.py +630 -0
  125. package/.opencode/mednotes/src/mednotes/domains/flashcards/install_models.py +445 -0
  126. package/.opencode/mednotes/src/mednotes/domains/flashcards/model.py +359 -0
  127. package/.opencode/mednotes/src/mednotes/domains/flashcards/obsidian_links.py +135 -0
  128. package/.opencode/mednotes/src/mednotes/domains/flashcards/obsidian_note_utils.py +546 -0
  129. package/.opencode/mednotes/src/mednotes/domains/flashcards/pipeline.py +580 -0
  130. package/.opencode/mednotes/src/mednotes/domains/flashcards/report.py +510 -0
  131. package/.opencode/mednotes/src/mednotes/domains/flashcards/sources.py +682 -0
  132. package/.opencode/mednotes/src/mednotes/domains/flashcards/sync_rules.py +184 -0
  133. package/.opencode/mednotes/src/mednotes/domains/history/__init__.py +1 -0
  134. package/.opencode/mednotes/src/mednotes/domains/history/history_fsm.py +852 -0
  135. package/.opencode/mednotes/src/mednotes/domains/history/history_machine.py +453 -0
  136. package/.opencode/mednotes/src/mednotes/domains/setup/__init__.py +7 -0
  137. package/.opencode/mednotes/src/mednotes/domains/setup/setup_fsm.py +808 -0
  138. package/.opencode/mednotes/src/mednotes/domains/setup/setup_machine.py +973 -0
  139. package/.opencode/mednotes/src/mednotes/domains/wiki/README.md +64 -0
  140. package/.opencode/mednotes/src/mednotes/domains/wiki/__init__.py +1 -0
  141. package/.opencode/mednotes/src/mednotes/domains/wiki/api.py +668 -0
  142. package/.opencode/mednotes/src/mednotes/domains/wiki/batch_state.py +102 -0
  143. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/__init__.py +1 -0
  144. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/atomicity/__init__.py +1 -0
  145. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/atomicity/atomicity.py +877 -0
  146. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/body_link/__init__.py +1 -0
  147. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/body_link/body_linker.py +1562 -0
  148. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/__init__.py +1 -0
  149. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/effect_adapters.py +949 -0
  150. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/fix_wiki_runtime_adapters.py +433 -0
  151. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/__init__.py +1 -0
  152. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/coverage.py +413 -0
  153. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/graph.py +396 -0
  154. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/graph_fixes.py +161 -0
  155. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/hygiene/__init__.py +1 -0
  156. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/hygiene/hygiene.py +483 -0
  157. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/__init__.py +2 -0
  158. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/anchors.py +185 -0
  159. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/__init__.py +0 -0
  160. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/cache.py +223 -0
  161. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/config.py +131 -0
  162. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/download.py +224 -0
  163. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/frontmatter.py +59 -0
  164. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/insert.py +227 -0
  165. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/local_import.py +54 -0
  166. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/__init__.py +42 -0
  167. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/web_profiles.py +99 -0
  168. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/web_search.py +203 -0
  169. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/wikimedia.py +102 -0
  170. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/__init__.py +1 -0
  171. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_db_adapter.mjs +434 -0
  172. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_node_runtime.py +274 -0
  173. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_query.py +227 -0
  174. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/__init__.py +1 -0
  175. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/artifacts.py +605 -0
  176. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/canonical_merge.py +277 -0
  177. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/markdown_zones.py +85 -0
  178. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/meaning_planner.py +307 -0
  179. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_iter.py +67 -0
  180. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_merge.py +278 -0
  181. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_plan.py +409 -0
  182. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_policy.py +22 -0
  183. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/__init__.py +79 -0
  184. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/fixes.py +264 -0
  185. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/frontmatter.py +435 -0
  186. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/models.py +208 -0
  187. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/prompts.py +37 -0
  188. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/tables.py +236 -0
  189. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/validate.py +404 -0
  190. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/provenance.py +478 -0
  191. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/raw_chats.py +273 -0
  192. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/sources_backfill.py +235 -0
  193. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/__init__.py +10 -0
  194. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/anchors.py +16 -0
  195. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/captions.py +47 -0
  196. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/cli.py +179 -0
  197. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/cloud.py +52 -0
  198. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/config.py +196 -0
  199. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/context_packets.py +76 -0
  200. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/db.py +81 -0
  201. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/doctor.py +102 -0
  202. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/figure_ids.py +42 -0
  203. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/ingest.py +326 -0
  204. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/insert.py +316 -0
  205. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/mentions.py +57 -0
  206. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/ocr.py +71 -0
  207. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/paths.py +35 -0
  208. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/pdf_engine.py +77 -0
  209. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/schema.py +155 -0
  210. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/search.py +188 -0
  211. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/__init__.py +1 -0
  212. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/app.py +89 -0
  213. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/image_backend.py +29 -0
  214. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/state.py +65 -0
  215. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/__init__.py +1 -0
  216. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish.py +1139 -0
  217. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish_receipts.py +365 -0
  218. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish_recovery.py +240 -0
  219. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/__init__.py +1 -0
  220. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_behavior_corpus.py +2069 -0
  221. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_report_validation.py +4448 -0
  222. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_run_audit.py +852 -0
  223. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/architect_prompt_eval.py +341 -0
  224. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/body_linker_eval.py +240 -0
  225. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/curator_output_validation.py +175 -0
  226. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/curator_prompt_eval.py +865 -0
  227. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/triager_prompt_eval.py +1295 -0
  228. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/__init__.py +1 -0
  229. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/related_notes.py +1920 -0
  230. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/related_notes_headless.py +1186 -0
  231. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/__init__.py +1 -0
  232. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/plan_attestation.py +148 -0
  233. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_receipts.py +360 -0
  234. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_runtime.py +52 -0
  235. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_task_runner.py +2470 -0
  236. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/style/__init__.py +1 -0
  237. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/style/style.py +1952 -0
  238. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/subagents/__init__.py +1 -0
  239. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/subagents/agents.py +1767 -0
  240. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/__init__.py +1 -0
  241. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/alias_projection.py +331 -0
  242. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/link_terms.py +151 -0
  243. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/llm_disambiguation.py +182 -0
  244. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/__init__.py +116 -0
  245. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/audit.py +201 -0
  246. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/migration.py +314 -0
  247. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/normalize.py +72 -0
  248. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/policy.py +135 -0
  249. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/resolve.py +413 -0
  250. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/schema.py +157 -0
  251. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/status.py +137 -0
  252. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_bootstrap.py +509 -0
  253. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_curator_batch.py +1115 -0
  254. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_ingestion.py +632 -0
  255. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_map.py +930 -0
  256. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_recovery.py +1388 -0
  257. package/.opencode/mednotes/src/mednotes/domains/wiki/cli.py +6665 -0
  258. package/.opencode/mednotes/src/mednotes/domains/wiki/common.py +69 -0
  259. package/.opencode/mednotes/src/mednotes/domains/wiki/config.py +210 -0
  260. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/__init__.py +74 -0
  261. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agent_report.py +242 -0
  262. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agent_run_audit.py +196 -0
  263. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agents.py +601 -0
  264. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/curator.py +256 -0
  265. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/effect_payloads.py +519 -0
  266. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/happy_path.py +190 -0
  267. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/link_git.py +110 -0
  268. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/link_runtime_artifact.py +52 -0
  269. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/note_plan.py +75 -0
  270. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/paths.py +114 -0
  271. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/public_report.py +53 -0
  272. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/publish.py +111 -0
  273. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/raw_coverage.py +217 -0
  274. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes.py +136 -0
  275. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes_headless.py +153 -0
  276. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes_runtime.py +395 -0
  277. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/schema_registry.py +637 -0
  278. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/specialist.py +432 -0
  279. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/status.py +62 -0
  280. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/style_rewrite.py +568 -0
  281. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/vocabulary_ingestion.py +223 -0
  282. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_blockers.py +510 -0
  283. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_guardrails.py +637 -0
  284. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_outcomes.py +121 -0
  285. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_receipts.py +100 -0
  286. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/__init__.py +1 -0
  287. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/__init__.py +1 -0
  288. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/__main__.py +4 -0
  289. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/cli.py +275 -0
  290. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/__init__.py +2 -0
  291. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/candidates.py +193 -0
  292. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/cli.py +189 -0
  293. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/gemini.py +220 -0
  294. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/inputs.py +120 -0
  295. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/models.py +34 -0
  296. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/parsing.py +48 -0
  297. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/prompts.py +216 -0
  298. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/quality.py +54 -0
  299. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/reporting.py +24 -0
  300. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/runner.py +433 -0
  301. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/utils.py +39 -0
  302. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/vault_guard_bridge.py +17 -0
  303. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/__init__.py +1 -0
  304. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_context_packets.py +454 -0
  305. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_decision_projection.py +133 -0
  306. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_effects.py +1260 -0
  307. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_fsm.py +2768 -0
  308. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_machine.py +1588 -0
  309. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_plan.py +306 -0
  310. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_primary_objective.py +316 -0
  311. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_problem.py +153 -0
  312. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_receipt_evidence.py +306 -0
  313. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_states.py +290 -0
  314. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_user_report.py +342 -0
  315. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/health.py +6332 -0
  316. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/__init__.py +1 -0
  317. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_fsm.py +1119 -0
  318. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_git.py +638 -0
  319. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_machine.py +1106 -0
  320. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_retry_governance.py +374 -0
  321. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_runtime_result.py +485 -0
  322. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_triggers.py +183 -0
  323. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/linking.py +2758 -0
  324. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/reference_repair.py +718 -0
  325. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/related_notes_fsm.py +1855 -0
  326. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link_related/__init__.py +1 -0
  327. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link_related/link_related_machine.py +834 -0
  328. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/__init__.py +1 -0
  329. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_fsm.py +1592 -0
  330. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_machine.py +3097 -0
  331. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_primary_objective.py +28 -0
  332. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_runtime_result.py +185 -0
  333. package/.opencode/mednotes/src/mednotes/domains/wiki/performance.py +97 -0
  334. package/.opencode/mednotes/src/mednotes/kernel/__init__.py +6 -0
  335. package/.opencode/mednotes/src/mednotes/kernel/agent_directive.py +336 -0
  336. package/.opencode/mednotes/src/mednotes/kernel/base.py +51 -0
  337. package/.opencode/mednotes/src/mednotes/kernel/blockers.py +39 -0
  338. package/.opencode/mednotes/src/mednotes/kernel/effect_executor.py +55 -0
  339. package/.opencode/mednotes/src/mednotes/kernel/effect_intent.py +69 -0
  340. package/.opencode/mednotes/src/mednotes/kernel/effects.py +160 -0
  341. package/.opencode/mednotes/src/mednotes/kernel/errors.py +38 -0
  342. package/.opencode/mednotes/src/mednotes/kernel/fsm_event.py +35 -0
  343. package/.opencode/mednotes/src/mednotes/kernel/fsm_model.py +55 -0
  344. package/.opencode/mednotes/src/mednotes/kernel/fsm_transition_result.py +75 -0
  345. package/.opencode/mednotes/src/mednotes/kernel/guardrails.py +188 -0
  346. package/.opencode/mednotes/src/mednotes/kernel/progress.py +319 -0
  347. package/.opencode/mednotes/src/mednotes/kernel/public_report.py +346 -0
  348. package/.opencode/mednotes/src/mednotes/kernel/state_machine.py +164 -0
  349. package/.opencode/mednotes/src/mednotes/kernel/workflow.py +619 -0
  350. package/.opencode/mednotes/src/mednotes/platform/__init__.py +5 -0
  351. package/.opencode/mednotes/src/mednotes/platform/backup_policy.py +382 -0
  352. package/.opencode/mednotes/src/mednotes/platform/feedback/__init__.py +62 -0
  353. package/.opencode/mednotes/src/mednotes/platform/feedback/cli.py +275 -0
  354. package/.opencode/mednotes/src/mednotes/platform/feedback/contracts.py +83 -0
  355. package/.opencode/mednotes/src/mednotes/platform/feedback/core.py +4168 -0
  356. package/.opencode/mednotes/src/mednotes/platform/feedback/integrity.py +989 -0
  357. package/.opencode/mednotes/src/mednotes/platform/feedback/operational_contract.py +2293 -0
  358. package/.opencode/mednotes/src/mednotes/platform/feedback/telemetry.py +875 -0
  359. package/.opencode/mednotes/src/mednotes/platform/feedback/telemetry_config.py +65 -0
  360. package/.opencode/mednotes/src/mednotes/platform/opencode_runtime_config.py +182 -0
  361. package/.opencode/mednotes/src/mednotes/platform/paths/__init__.py +1560 -0
  362. package/.opencode/mednotes/src/mednotes/platform/secrets.py +89 -0
  363. package/.opencode/mednotes/src/mednotes/platform/user_config.py +103 -0
  364. package/.opencode/mednotes/src/mednotes/platform/vault_guard.py +214 -0
  365. package/.opencode/mednotes/uv.lock +932 -0
  366. package/.opencode/mednotes.generated.json +395 -0
  367. package/.opencode/opencode.json +31 -0
  368. package/.opencode/plugins/mednotes-fsm.mjs +7 -0
  369. package/.opencode/plugins/mednotes_hook/adapters/antigravity.mjs +169 -0
  370. package/.opencode/plugins/mednotes_hook/adapters/harness_payload.mjs +103 -0
  371. package/.opencode/plugins/mednotes_hook/adapters/opencode_plugin.mjs +341 -0
  372. package/.opencode/plugins/mednotes_hook/adapters/opencode_user_config_sync.mjs +177 -0
  373. package/.opencode/plugins/mednotes_hook/anki_preflight.mjs +214 -0
  374. package/.opencode/plugins/mednotes_hook/cli.mjs +143 -0
  375. package/.opencode/plugins/mednotes_hook/diagnostics.mjs +11 -0
  376. package/.opencode/plugins/mednotes_hook/domain/agent_directive_core.mjs +160 -0
  377. package/.opencode/plugins/mednotes_hook/fsm_directive.mjs +1470 -0
  378. package/.opencode/plugins/mednotes_hook/hook_errors.mjs +120 -0
  379. package/.opencode/plugins/mednotes_hook/retention.mjs +114 -0
  380. package/.opencode/plugins/mednotes_hook/runtime.mjs +174 -0
  381. package/.opencode/plugins/mednotes_hook/telemetry_capture.mjs +511 -0
  382. package/.opencode/plugins/mednotes_hook/vault_guard.mjs +624 -0
  383. package/AGENTS.md +57 -0
  384. package/README.md +194 -0
  385. package/adapters/antigravity/agents.json +80 -0
  386. package/adapters/antigravity/templates/med-chat-triager.md +214 -0
  387. package/adapters/antigravity/templates/med-flashcard-maker.md +72 -0
  388. package/adapters/antigravity/templates/med-knowledge-architect.md +241 -0
  389. package/adapters/antigravity/templates/med-link-graph-curator.md +187 -0
  390. package/adapters/antigravity/templates/med-publish-guard.md +71 -0
  391. package/adapters/gemini-cli/gemini-extension.json +14 -0
  392. package/adapters/gemini-cli/package.json +15 -0
  393. package/adapters/gemini-cli/pyproject.toml +48 -0
  394. package/bin/mednotes-opencode.mjs +155 -0
  395. package/contracts/agents.json +116 -0
  396. package/core/agents/med-chat-triager.md +197 -0
  397. package/core/agents/med-flashcard-maker.md +56 -0
  398. package/core/agents/med-knowledge-architect.md +224 -0
  399. package/core/agents/med-link-graph-curator.md +171 -0
  400. package/core/agents/med-publish-guard.md +55 -0
  401. package/core/commands/flashcards.toml +22 -0
  402. package/core/commands/mednotes/create.toml +22 -0
  403. package/core/commands/mednotes/enrich.toml +24 -0
  404. package/core/commands/mednotes/fix-wiki.toml +24 -0
  405. package/core/commands/mednotes/history.toml +19 -0
  406. package/core/commands/mednotes/link-body.toml +22 -0
  407. package/core/commands/mednotes/link-related.toml +24 -0
  408. package/core/commands/mednotes/link.toml +24 -0
  409. package/core/commands/mednotes/pdf-library.toml +24 -0
  410. package/core/commands/mednotes/process-chats.toml +20 -0
  411. package/core/commands/mednotes/setup.toml +18 -0
  412. package/core/commands/mednotes/status.toml +24 -0
  413. package/core/commands/mednotes/telemetry.toml +24 -0
  414. package/core/commands/report.toml +23 -0
  415. package/core/skills/THIRD_PARTY_NOTICES.md +45 -0
  416. package/core/skills/create-medical-flashcards/SKILL.md +113 -0
  417. package/core/skills/create-medical-note/SKILL.md +90 -0
  418. package/core/skills/enrich-medical-note/SKILL.md +120 -0
  419. package/core/skills/fix-medical-wiki/SKILL.md +559 -0
  420. package/core/skills/link-medical-wiki/SKILL.md +224 -0
  421. package/core/skills/obsidian-cli/SKILL.md +118 -0
  422. package/core/skills/obsidian-markdown/SKILL.md +207 -0
  423. package/core/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
  424. package/core/skills/obsidian-markdown/references/EMBEDS.md +63 -0
  425. package/core/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
  426. package/core/skills/obsidian-ops/SKILL.md +136 -0
  427. package/core/skills/pdf-library/SKILL.md +45 -0
  428. package/core/skills/process-medical-chats/SKILL.md +246 -0
  429. package/core/skills/workflow-report/SKILL.md +100 -0
  430. package/package.json +45 -0
@@ -0,0 +1,808 @@
1
+ """Public projection for `/mednotes:setup` StateChart results.
2
+
3
+ This module is a projector, not a second state engine. It formats the persisted
4
+ `WorkflowModel` produced by `setup_machine.py` into the public FSM-first shape
5
+ consumed by hooks, agents and user-facing reports.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Literal, cast
11
+
12
+ from pydantic import BaseModel, ConfigDict, Field, StrictStr, model_validator
13
+ from pydantic import ValidationError as PydanticValidationError
14
+ from pydantic.json_schema import SkipJsonSchema
15
+
16
+ from mednotes.domains.setup.setup_machine import (
17
+ SETUP_WORKFLOW,
18
+ ConfigValidationBlockedEvent,
19
+ ConfigValidationCompletedEvent,
20
+ SetupMachine,
21
+ SetupState,
22
+ SetupVaultAdapterPayload,
23
+ category_for_setup_state,
24
+ resume_action_for_setup_state,
25
+ setup_event_from_vault_adapter_payload,
26
+ )
27
+ from mednotes.kernel.agent_directive import (
28
+ AgentDirective,
29
+ agent_directive_from_progress_view_model,
30
+ assert_agent_directive_matches_progress,
31
+ )
32
+ from mednotes.kernel.base import ContractModel, JsonObject, JsonObjectAdapter
33
+ from mednotes.kernel.effects import WorkflowEffectKind
34
+ from mednotes.kernel.fsm_event import WorkflowEventLike
35
+ from mednotes.kernel.fsm_model import WorkflowModel
36
+ from mednotes.kernel.fsm_transition_result import WorkflowTransitionResult
37
+ from mednotes.kernel.progress import (
38
+ WorkflowProgressEventType,
39
+ WorkflowProgressState,
40
+ WorkflowProgressStatus,
41
+ WorkflowProgressViewModel,
42
+ build_progress_view_model,
43
+ progress_state_from_view_model,
44
+ )
45
+ from mednotes.kernel.public_report import (
46
+ WorkflowPrimaryObjectiveSummary,
47
+ WorkflowPublicReport,
48
+ WorkflowReports,
49
+ assert_public_report_matches_progress,
50
+ public_progress_followup_line,
51
+ )
52
+ from mednotes.kernel.state_machine import (
53
+ WorkflowStateCategory,
54
+ WorkflowStateMachineSnapshot,
55
+ WorkflowTransition,
56
+ send_workflow_event,
57
+ )
58
+ from mednotes.kernel.workflow import (
59
+ HumanDecisionPacket,
60
+ ReceiptStatus,
61
+ VersionControlSafety,
62
+ WorkflowDecision,
63
+ WorkflowReceiptPayload,
64
+ assert_diagnostic_context_evidence_only,
65
+ diagnostic_context_evidence_only,
66
+ )
67
+
68
+ SETUP_SCHEMA = "medical-notes-workbench.setup-fsm-result.v1"
69
+ SETUP_RECEIPT_SCHEMA = "medical-notes-workbench.setup-receipt.v1"
70
+ SETUP_AGENT_DIRECTIVE_FIELD = "agent_directive"
71
+ SETUP_FAILED_CATEGORIES = frozenset({WorkflowStateCategory.FAILED.value})
72
+ SETUP_RECEIPT_TERMINAL_EMPTY_ACTION_STATUSES = frozenset({WorkflowProgressStatus.COMPLETED})
73
+ SETUP_RECEIPT_HUMAN_DECISION_STATUSES = frozenset({WorkflowProgressStatus.WAITING_HUMAN})
74
+
75
+ SETUP_ALLOWED_ROOT_KEYS = frozenset(
76
+ {
77
+ "schema",
78
+ "workflow",
79
+ "run_id",
80
+ "state_machine_snapshot",
81
+ "progress_view_model",
82
+ "decision",
83
+ "human_decision_packet",
84
+ "receipt",
85
+ "reports",
86
+ "agent_directive",
87
+ "artifacts",
88
+ "version_control_safety",
89
+ "diagnostic_context",
90
+ "error_context",
91
+ }
92
+ )
93
+ SETUP_FORBIDDEN_ROOT_KEYS = frozenset(
94
+ {
95
+ "status",
96
+ "phase",
97
+ "blocked_reason",
98
+ "next_action",
99
+ "required_inputs",
100
+ "human_decision_required",
101
+ "workflow_exit_code",
102
+ "public_report",
103
+ "orchestration_plan",
104
+ }
105
+ )
106
+
107
+
108
+ class _SetupPayloadProgressView(ContractModel):
109
+ status: StrictStr
110
+
111
+
112
+ class _SetupPayloadSnapshot(ContractModel):
113
+ current_category: StrictStr
114
+
115
+
116
+ class _SetupPayloadReceipt(ContractModel):
117
+ status: StrictStr
118
+
119
+
120
+ class _SetupPayloadFields(ContractModel):
121
+ progress_view_model: _SetupPayloadProgressView
122
+ state_machine_snapshot: _SetupPayloadSnapshot
123
+ receipt: _SetupPayloadReceipt
124
+
125
+
126
+ class SetupFsmResult(ContractModel):
127
+ """Canonical public setup result projected from the StateChart model."""
128
+
129
+ schema_id: Literal["medical-notes-workbench.setup-fsm-result.v1"] = Field(default=SETUP_SCHEMA, alias="schema")
130
+ workflow: Literal["/mednotes:setup"] = SETUP_WORKFLOW
131
+ run_id: str = Field(min_length=1)
132
+ progress_state: SkipJsonSchema[WorkflowProgressState]
133
+ progress_view_model: WorkflowProgressViewModel
134
+ state_machine_snapshot: WorkflowStateMachineSnapshot
135
+ decision: WorkflowDecision | None = None
136
+ human_decision_packet: HumanDecisionPacket | None = None
137
+ receipt: WorkflowReceiptPayload
138
+ reports: WorkflowReports
139
+ agent_directive: JsonObject
140
+ artifacts: JsonObject = Field(default_factory=dict)
141
+ version_control_safety: VersionControlSafety
142
+ diagnostic_context: JsonObject = Field(default_factory=dict)
143
+ error_context: JsonObject = Field(default_factory=dict)
144
+
145
+ @model_validator(mode="before")
146
+ @classmethod
147
+ def _hydrate_progress_state_from_public_payload(cls, value: object) -> object:
148
+ """Accept public payloads where progress_state is intentionally hidden."""
149
+
150
+ if not isinstance(value, dict) or "progress_state" in value or "progress_view_model" not in value:
151
+ return value
152
+ hydrated = dict(value)
153
+ progress_view = WorkflowProgressViewModel.model_validate(value["progress_view_model"])
154
+ hydrated["progress_state"] = progress_state_from_view_model(progress_view).to_payload()
155
+ return hydrated
156
+
157
+ @model_validator(mode="after")
158
+ def _progress_view_model_matches_state(self) -> SetupFsmResult:
159
+ expected = build_progress_view_model(self.progress_state).to_payload()
160
+ if self.progress_view_model.to_payload() != expected:
161
+ raise ValueError("progress_view_model must match progress_state")
162
+ return self
163
+
164
+ def to_payload(self) -> JsonObject:
165
+ payload: JsonObject = {
166
+ "schema": self.schema_id,
167
+ "workflow": self.workflow,
168
+ "run_id": self.run_id,
169
+ "state_machine_snapshot": self.state_machine_snapshot.to_payload(),
170
+ "progress_view_model": self.progress_view_model.to_payload(),
171
+ "decision": self.decision.to_payload() if self.decision is not None else None,
172
+ "human_decision_packet": self.human_decision_packet.to_payload()
173
+ if self.human_decision_packet is not None
174
+ else None,
175
+ "receipt": self.receipt.to_payload(),
176
+ "reports": self.reports.to_payload(),
177
+ "agent_directive": dict(self.agent_directive),
178
+ "artifacts": dict(self.artifacts),
179
+ "version_control_safety": self.version_control_safety.to_payload(),
180
+ "error_context": dict(self.error_context),
181
+ }
182
+ if self.diagnostic_context:
183
+ payload["diagnostic_context"] = dict(self.diagnostic_context)
184
+ payload = JsonObjectAdapter.validate_python(payload)
185
+ assert_setup_fsm_payload(payload)
186
+ return payload
187
+
188
+
189
+ def build_setup_fsm_result(
190
+ model: WorkflowModel,
191
+ *,
192
+ version_control_safety: VersionControlSafety | None = None,
193
+ error_context: JsonObject | None = None,
194
+ ) -> SetupFsmResult:
195
+ """Project a persisted setup machine model into the public FSM contract."""
196
+
197
+ _validate_setup_model(model)
198
+ state = SetupState(model.state)
199
+ category = category_for_setup_state(state)
200
+ progress_state = _progress_state(model, state, category)
201
+ progress_view_model = build_progress_view_model(progress_state)
202
+ snapshot = _snapshot(model, state, category)
203
+ receipt = _receipt(
204
+ model,
205
+ progress_state=progress_state,
206
+ progress_view_model=progress_view_model,
207
+ snapshot=snapshot,
208
+ version_control_safety=version_control_safety or _default_version_control_safety(),
209
+ )
210
+ reports_model = _reports(state, progress_state)
211
+ public_report = reports_model.public_report
212
+ diagnostic_context = _diagnostic_context(model, state, category)
213
+ directive = agent_directive_from_progress_view_model(
214
+ progress_view_model,
215
+ schema="medical-notes-workbench.agent-directive.v1",
216
+ reason=_reason_code(model, state),
217
+ effects=model.pending_effects,
218
+ blockers=_blockers_for(category, state),
219
+ resume=progress_state.resume_action,
220
+ report_requires=["setup_state", "recovery_route"],
221
+ summary=public_report.summary_text(),
222
+ instructions=_agent_instructions(category),
223
+ ).to_payload()
224
+ return SetupFsmResult(
225
+ run_id=model.run_id,
226
+ progress_state=progress_state,
227
+ progress_view_model=progress_view_model,
228
+ state_machine_snapshot=snapshot,
229
+ decision=model.last_transition.decision if model.last_transition is not None else None,
230
+ human_decision_packet=model.last_transition.human_decision_packet if model.last_transition is not None else None,
231
+ receipt=receipt,
232
+ reports=reports_model,
233
+ agent_directive=directive,
234
+ version_control_safety=receipt.version_control_safety,
235
+ diagnostic_context=diagnostic_context,
236
+ error_context=error_context or _setup_error_context(model, state, category, progress_state),
237
+ )
238
+
239
+
240
+ def _setup_error_context(
241
+ model: WorkflowModel,
242
+ state: SetupState,
243
+ category: WorkflowStateCategory,
244
+ progress_state: WorkflowProgressState,
245
+ ) -> JsonObject:
246
+ """Produce a recoverable failure context for terminal setup failures."""
247
+
248
+ if category != WorkflowStateCategory.FAILED:
249
+ return {}
250
+ reason_code = _reason_code(model, state)
251
+ next_action = (
252
+ progress_state.resume_action
253
+ or "Revisar a política/ambiente informado e retomar /mednotes:setup pela rota oficial."
254
+ )
255
+ return {
256
+ "blocked_reason": reason_code,
257
+ "root_cause": reason_code,
258
+ "affected_artifact": "mednotes_setup_environment",
259
+ "error_summary": "O setup terminou em falha antes de preparar o Workbench.",
260
+ "suggested_fix": next_action,
261
+ "next_action": next_action,
262
+ "retry_scope": "setup_environment",
263
+ "human_decision_required": False,
264
+ "missing_inputs": [],
265
+ }
266
+
267
+
268
+ def setup_fsm_payload_from_model(model: WorkflowModel) -> JsonObject:
269
+ """Convenience boundary for callers that need the JSON payload directly."""
270
+
271
+ return build_setup_fsm_result(model).to_payload()
272
+
273
+
274
+ class _SetupConfigValidationPayload(BaseModel):
275
+ """Typed adapter lens for the private setup config validation receipt."""
276
+
277
+ model_config = ConfigDict(extra="forbid", strict=True)
278
+
279
+ schema_id: Literal["medical-notes-workbench.setup-config-validation.v1"] = Field(alias="schema")
280
+ status: Literal["valid", "blocked"]
281
+ config_path: str = Field(min_length=1)
282
+ reason_code: Literal["config_encoding_invalid"] | None = None
283
+
284
+ @model_validator(mode="after")
285
+ def _blocked_status_requires_reason(self) -> _SetupConfigValidationPayload:
286
+ match self.status:
287
+ case "blocked":
288
+ if self.reason_code != "config_encoding_invalid":
289
+ raise ValueError("blocked setup config validation requires reason_code=config_encoding_invalid")
290
+ case "valid":
291
+ if self.reason_code is not None:
292
+ raise ValueError("valid setup config validation must not carry a blocker reason")
293
+ return self
294
+
295
+
296
+ def setup_fsm_payload_from_vault_payload(payload: object, *, run_id: str = "setup-vault") -> JsonObject:
297
+ """Convert the vault setup adapter result into the public setup FSM payload."""
298
+
299
+ raw = SetupVaultAdapterPayload.model_validate(payload)
300
+ initial_state, event = setup_event_from_vault_adapter_payload(raw, run_id=run_id)
301
+ model = WorkflowModel.start(workflow=SETUP_WORKFLOW, run_id=run_id, initial_state=initial_state.value)
302
+ # `setup_event_from_vault_adapter_payload` returns the closed discriminated
303
+ # setup event union; every member has the StateChart `name` field required by
304
+ # the kernel protocol even though the abstract base intentionally does not.
305
+ send_workflow_event(
306
+ SetupMachine(model=model, state_field=WorkflowModel.STATECHART_STATE_FIELD),
307
+ cast(WorkflowEventLike, event),
308
+ )
309
+ return build_setup_fsm_result(model).to_payload()
310
+
311
+
312
+ def setup_fsm_payload_from_config_validation_payload(
313
+ payload: object,
314
+ *,
315
+ run_id: str = "setup-config-validation",
316
+ ) -> JsonObject:
317
+ """Convert the config validation adapter receipt into a setup FSM payload."""
318
+
319
+ raw = _SetupConfigValidationPayload.model_validate(payload)
320
+ model = WorkflowModel.start(
321
+ workflow=SETUP_WORKFLOW,
322
+ run_id=run_id,
323
+ initial_state=SetupState.CONFIG_VALIDATION_RUNNING.value,
324
+ )
325
+ machine = SetupMachine(model=model, state_field=WorkflowModel.STATECHART_STATE_FIELD)
326
+ match raw.status:
327
+ case "valid":
328
+ send_workflow_event(
329
+ machine,
330
+ ConfigValidationCompletedEvent(
331
+ workflow=SETUP_WORKFLOW,
332
+ run_id=run_id,
333
+ current_state=SetupState.CONFIG_VALIDATION_RUNNING.value,
334
+ config_path=raw.config_path,
335
+ ),
336
+ )
337
+ case "blocked":
338
+ send_workflow_event(
339
+ machine,
340
+ ConfigValidationBlockedEvent(
341
+ workflow=SETUP_WORKFLOW,
342
+ run_id=run_id,
343
+ current_state=SetupState.CONFIG_VALIDATION_RUNNING.value,
344
+ reason_code="config_encoding_invalid",
345
+ config_path=raw.config_path,
346
+ audit_evidence={"config_path": raw.config_path},
347
+ ),
348
+ )
349
+ return build_setup_fsm_result(model).to_payload()
350
+
351
+
352
+ def assert_setup_fsm_payload(payload: JsonObject) -> None:
353
+ """Reject stale setup payload shapes before hooks or agents consume them."""
354
+
355
+ required = SETUP_ALLOWED_ROOT_KEYS - {"diagnostic_context"}
356
+ missing = required - set(payload)
357
+ if missing:
358
+ raise ValueError("setup FSM payload missing canonical root keys: " + ", ".join(sorted(missing)))
359
+ forbidden = SETUP_FORBIDDEN_ROOT_KEYS & set(payload)
360
+ if forbidden:
361
+ raise ValueError("setup FSM payload contains forbidden root keys: " + ", ".join(sorted(forbidden)))
362
+ extra = set(payload) - SETUP_ALLOWED_ROOT_KEYS
363
+ if extra:
364
+ raise ValueError("setup FSM payload contains unknown root keys: " + ", ".join(sorted(extra)))
365
+ diagnostic_context = payload["diagnostic_context"] if "diagnostic_context" in payload else {}
366
+ assert_diagnostic_context_evidence_only(diagnostic_context)
367
+ if isinstance(diagnostic_context, dict) and "agent_directive" in diagnostic_context:
368
+ raise ValueError("setup FSM diagnostic_context must not contain agent_directive")
369
+ fields = _setup_payload_fields(payload)
370
+ if fields.progress_view_model.status != fields.state_machine_snapshot.current_category:
371
+ raise ValueError("setup FSM status must match state_machine_snapshot category")
372
+ if fields.receipt.status != fields.progress_view_model.status:
373
+ raise ValueError("setup FSM receipt status must match progress_view_model")
374
+ if fields.progress_view_model.status in SETUP_FAILED_CATEGORIES and not payload["error_context"]:
375
+ raise ValueError("setup FSM failed payload requires error_context")
376
+ reports_model = WorkflowReports.model_validate(payload["reports"])
377
+ snapshot = WorkflowStateMachineSnapshot.model_validate(payload["state_machine_snapshot"])
378
+ progress_view_model = WorkflowProgressViewModel.model_validate(payload["progress_view_model"])
379
+ assert_public_report_matches_progress(
380
+ reports_model.public_report,
381
+ workflow=SETUP_WORKFLOW,
382
+ run_id=str(payload["run_id"]),
383
+ progress_view_model=progress_view_model,
384
+ label="setup FSM",
385
+ )
386
+ assert_agent_directive_matches_progress(
387
+ AgentDirective.model_validate(payload[SETUP_AGENT_DIRECTIVE_FIELD]),
388
+ workflow=SETUP_WORKFLOW,
389
+ run_id=str(payload["run_id"]),
390
+ progress_view_model=progress_view_model,
391
+ snapshot=snapshot,
392
+ allowed_effect_kinds=_allowed_agent_effect_kinds_for_category(snapshot.current_category),
393
+ label="setup FSM",
394
+ )
395
+
396
+
397
+ def _allowed_agent_effect_kinds_for_category(category: WorkflowStateCategory) -> set[WorkflowEffectKind]:
398
+ """Setup currently recovers by human/runtime action, not hidden effects."""
399
+
400
+ match category:
401
+ case WorkflowStateCategory.WAITING_AGENT:
402
+ return {WorkflowEffectKind.RUN_SUBWORKFLOW}
403
+ case WorkflowStateCategory.WAITING_EXTERNAL:
404
+ return {WorkflowEffectKind.WAIT_EXTERNAL}
405
+ case WorkflowStateCategory.WAITING_HUMAN:
406
+ return {WorkflowEffectKind.ASK_HUMAN}
407
+ case _:
408
+ return set()
409
+
410
+
411
+ def _setup_payload_fields(payload: JsonObject) -> _SetupPayloadFields:
412
+ raw_fields: JsonObject = {
413
+ "progress_view_model": _json_object_subset(payload, "progress_view_model", ("status",)),
414
+ "state_machine_snapshot": _json_object_subset(payload, "state_machine_snapshot", ("current_category",)),
415
+ "receipt": _json_object_subset(payload, "receipt", ("status",)),
416
+ }
417
+ try:
418
+ return _SetupPayloadFields.model_validate(raw_fields)
419
+ except PydanticValidationError as exc:
420
+ first = exc.errors()[0] if exc.errors() else {}
421
+ loc = ".".join(str(part) for part in first.get("loc", ())) or "$"
422
+ msg = str(first.get("msg") or str(exc))
423
+ raise ValueError(f"setup FSM payload invalid: {loc}: {msg}") from exc
424
+
425
+
426
+ def _json_object_subset(payload: JsonObject, key: str, fields: tuple[str, ...]) -> JsonObject:
427
+ value = payload[key]
428
+ if not isinstance(value, dict):
429
+ raise ValueError(f"setup FSM payload {key} must be an object")
430
+ subset = {field: value[field] for field in fields if field in value}
431
+ return JsonObjectAdapter.validate_python(subset)
432
+
433
+
434
+ def _validate_setup_model(model: WorkflowModel) -> None:
435
+ if model.workflow != SETUP_WORKFLOW:
436
+ raise ValueError(f"setup FSM projector requires workflow={SETUP_WORKFLOW}")
437
+ SetupState(model.state)
438
+
439
+
440
+ def _progress_state(
441
+ model: WorkflowModel,
442
+ state: SetupState,
443
+ category: WorkflowStateCategory,
444
+ ) -> WorkflowProgressState:
445
+ status = _progress_status(category)
446
+ return WorkflowProgressState(
447
+ workflow=SETUP_WORKFLOW,
448
+ run_id=model.run_id,
449
+ state=state.value,
450
+ phase=_phase_for_state(state),
451
+ event_type=_event_type_for_status(status),
452
+ message=_message_for_state(state),
453
+ status=status,
454
+ resume_action=_resume_action(model, state),
455
+ resume_supported=status in {
456
+ WorkflowProgressStatus.WAITING_AGENT,
457
+ WorkflowProgressStatus.WAITING_EXTERNAL,
458
+ WorkflowProgressStatus.WAITING_HUMAN,
459
+ },
460
+ can_continue_now=status
461
+ in {
462
+ WorkflowProgressStatus.RUNNING,
463
+ WorkflowProgressStatus.WAITING_AGENT,
464
+ },
465
+ decision=model.last_transition.decision.to_payload()
466
+ if model.last_transition is not None and model.last_transition.decision is not None
467
+ else None,
468
+ technical_context={"reason": _reason_code(model, state), "category": category.value},
469
+ )
470
+
471
+
472
+ def _snapshot(
473
+ model: WorkflowModel,
474
+ state: SetupState,
475
+ category: WorkflowStateCategory,
476
+ ) -> WorkflowStateMachineSnapshot:
477
+ return WorkflowStateMachineSnapshot(
478
+ workflow=SETUP_WORKFLOW,
479
+ run_id=model.run_id,
480
+ current_state=state.value,
481
+ current_category=category,
482
+ transitions=[_snapshot_transition(transition) for transition in model.transition_log],
483
+ metadata={"reason": _reason_code(model, state)},
484
+ )
485
+
486
+
487
+ def _snapshot_transition(transition: WorkflowTransitionResult) -> WorkflowTransition:
488
+ return WorkflowTransition(
489
+ workflow=transition.workflow,
490
+ from_state=transition.from_state,
491
+ to_state=transition.to_state,
492
+ to_category=category_for_setup_state(SetupState(transition.to_state)),
493
+ trigger=transition.trigger,
494
+ effects=list(transition.effects),
495
+ decision=transition.decision,
496
+ resume_action=transition.resume_action,
497
+ )
498
+
499
+
500
+ def _receipt(
501
+ model: WorkflowModel,
502
+ *,
503
+ progress_state: WorkflowProgressState,
504
+ progress_view_model: WorkflowProgressViewModel,
505
+ snapshot: WorkflowStateMachineSnapshot,
506
+ version_control_safety: VersionControlSafety,
507
+ ) -> WorkflowReceiptPayload:
508
+ return WorkflowReceiptPayload(
509
+ schema=SETUP_RECEIPT_SCHEMA,
510
+ workflow=SETUP_WORKFLOW,
511
+ run_id=model.run_id,
512
+ status=_receipt_status(progress_state.status),
513
+ mutated=False,
514
+ next_action="" if progress_state.status in SETUP_RECEIPT_TERMINAL_EMPTY_ACTION_STATUSES else progress_state.resume_action,
515
+ human_decision_required=progress_state.status in SETUP_RECEIPT_HUMAN_DECISION_STATUSES,
516
+ human_decision_packet=model.last_transition.human_decision_packet if model.last_transition is not None else None,
517
+ version_control_safety=version_control_safety,
518
+ progress_state=progress_state,
519
+ progress_view_model=progress_view_model,
520
+ state_machine_snapshot=snapshot,
521
+ )
522
+
523
+
524
+ def _receipt_status(status: WorkflowProgressStatus) -> ReceiptStatus:
525
+ match status:
526
+ case WorkflowProgressStatus.RUNNING:
527
+ return "running"
528
+ case WorkflowProgressStatus.COMPLETED:
529
+ return "completed"
530
+ case WorkflowProgressStatus.COMPLETED_WITH_WARNINGS:
531
+ return "completed_with_warnings"
532
+ case WorkflowProgressStatus.WAITING_AGENT:
533
+ return "waiting_agent"
534
+ case WorkflowProgressStatus.WAITING_EXTERNAL:
535
+ return "waiting_external"
536
+ case WorkflowProgressStatus.WAITING_HUMAN:
537
+ return "waiting_human"
538
+ case WorkflowProgressStatus.FAILED:
539
+ return "failed"
540
+ case WorkflowProgressStatus.BLOCKED:
541
+ return "blocked"
542
+ case _:
543
+ return "blocked"
544
+
545
+
546
+ def _progress_status(category: WorkflowStateCategory) -> WorkflowProgressStatus:
547
+ match category:
548
+ case WorkflowStateCategory.PREPARING | WorkflowStateCategory.RUNNING:
549
+ return WorkflowProgressStatus.RUNNING
550
+ case WorkflowStateCategory.WAITING_AGENT:
551
+ return WorkflowProgressStatus.WAITING_AGENT
552
+ case WorkflowStateCategory.WAITING_EXTERNAL:
553
+ return WorkflowProgressStatus.WAITING_EXTERNAL
554
+ case WorkflowStateCategory.WAITING_HUMAN:
555
+ return WorkflowProgressStatus.WAITING_HUMAN
556
+ case WorkflowStateCategory.BLOCKED:
557
+ return WorkflowProgressStatus.BLOCKED
558
+ case WorkflowStateCategory.FAILED:
559
+ return WorkflowProgressStatus.FAILED
560
+ case WorkflowStateCategory.COMPLETED:
561
+ return WorkflowProgressStatus.COMPLETED
562
+ case WorkflowStateCategory.COMPLETED_WITH_WARNINGS:
563
+ return WorkflowProgressStatus.COMPLETED_WITH_WARNINGS
564
+
565
+
566
+ def _event_type_for_status(status: WorkflowProgressStatus) -> WorkflowProgressEventType:
567
+ match status:
568
+ case WorkflowProgressStatus.COMPLETED | WorkflowProgressStatus.COMPLETED_WITH_WARNINGS:
569
+ return WorkflowProgressEventType.WORKFLOW_COMPLETED
570
+ case WorkflowProgressStatus.FAILED:
571
+ return WorkflowProgressEventType.WORKFLOW_FAILED
572
+ case WorkflowProgressStatus.WAITING_EXTERNAL:
573
+ return WorkflowProgressEventType.EXTERNAL_WAIT_STARTED
574
+ case WorkflowProgressStatus.WAITING_HUMAN:
575
+ return WorkflowProgressEventType.DECISION_EMITTED
576
+ case _:
577
+ return WorkflowProgressEventType.STATE_ENTERED
578
+
579
+
580
+ def _phase_for_state(state: SetupState) -> str:
581
+ match state:
582
+ case SetupState.CHECKING_ENVIRONMENT | SetupState.PYTHON_ENV_REQUIRED | SetupState.PYTHON_ENV_READY:
583
+ return "environment"
584
+ case (
585
+ SetupState.PATHS_REQUIRED
586
+ | SetupState.PATHS_CONFIGURED
587
+ | SetupState.CONFIG_VALIDATION_RUNNING
588
+ | SetupState.CONFIG_ENCODING_REQUIRED
589
+ ):
590
+ return "paths"
591
+ case (
592
+ SetupState.OBSIDIAN_NOT_READY
593
+ | SetupState.MARKDOWN_RUNTIME_REQUIRED
594
+ | SetupState.MARKDOWN_INDEX_REQUIRED
595
+ | SetupState.MARKDOWN_RUNTIME_READY
596
+ ):
597
+ return "markdown_runtime"
598
+ case SetupState.VAULT_GUARD_REQUIRED | SetupState.VAULT_LOCAL_READY:
599
+ return "vault_guard"
600
+ case (
601
+ SetupState.LOCAL_READY_GITHUB_PENDING
602
+ | SetupState.GITHUB_LOGIN_REQUIRED
603
+ | SetupState.GITHUB_REMOTE_CONFIRMATION_REQUIRED
604
+ | SetupState.GITHUB_REMOTE_AMBIGUOUS
605
+ ):
606
+ return "remote_backup"
607
+ case SetupState.BRANCH_CONFIRMATION_REQUIRED:
608
+ return "branch_policy"
609
+ case SetupState.POLICY_DECISION_REQUIRED:
610
+ return "policy"
611
+ case SetupState.READY:
612
+ return "ready"
613
+ case SetupState.FAILED:
614
+ return "failed"
615
+
616
+
617
+ def _message_for_state(state: SetupState) -> str:
618
+ match state:
619
+ case SetupState.PATHS_REQUIRED:
620
+ return "Setup aguardando os caminhos oficiais da Wiki e dos raw chats."
621
+ case SetupState.PYTHON_ENV_REQUIRED:
622
+ return "Setup precisa preparar Python/uv pela rota oficial."
623
+ case SetupState.CONFIG_VALIDATION_RUNNING:
624
+ return "Setup precisa validar a configuracao antes de continuar."
625
+ case SetupState.CONFIG_ENCODING_REQUIRED:
626
+ return "Setup precisa reparar a configuracao em UTF-8."
627
+ case SetupState.OBSIDIAN_NOT_READY:
628
+ return "Setup aguardando Obsidian/plugin ficar pronto."
629
+ case SetupState.MARKDOWN_RUNTIME_REQUIRED:
630
+ return "Setup precisa reconstruir o runtime Markdown."
631
+ case SetupState.MARKDOWN_INDEX_REQUIRED:
632
+ return "Setup precisa reconstruir o indice Markdown."
633
+ case SetupState.VAULT_GUARD_REQUIRED:
634
+ return "Setup precisa configurar a protecao do vault."
635
+ case SetupState.LOCAL_READY_GITHUB_PENDING:
636
+ return "Protecao local pronta; backup online ainda pendente."
637
+ case SetupState.GITHUB_LOGIN_REQUIRED:
638
+ return "Setup aguardando decisao sobre login do GitHub."
639
+ case SetupState.GITHUB_REMOTE_CONFIRMATION_REQUIRED:
640
+ return "Setup aguardando confirmacao para criar backup privado."
641
+ case SetupState.GITHUB_REMOTE_AMBIGUOUS:
642
+ return "Setup aguardando escolha do remote GitHub."
643
+ case SetupState.BRANCH_CONFIRMATION_REQUIRED:
644
+ return "Setup aguardando confirmacao para ajustar a branch principal."
645
+ case SetupState.POLICY_DECISION_REQUIRED:
646
+ return "Setup aguardando decisao de politica do ambiente."
647
+ case SetupState.READY:
648
+ return "Setup concluido; workflows dependentes podem retomar."
649
+ case SetupState.FAILED:
650
+ return "Setup falhou antes de liberar workflows dependentes."
651
+ case _:
652
+ return "Setup em andamento."
653
+
654
+
655
+ def _reports(state: SetupState, progress_state: WorkflowProgressState) -> WorkflowReports:
656
+ summary = _message_for_state(state)
657
+ public_lines = [summary]
658
+ followup_line = public_progress_followup_line(progress_state)
659
+ if followup_line:
660
+ public_lines.append(followup_line)
661
+ public_report = WorkflowPublicReport(
662
+ workflow=SETUP_WORKFLOW,
663
+ run_id=progress_state.run_id,
664
+ headline=summary,
665
+ lines=public_lines,
666
+ )
667
+ return WorkflowReports(
668
+ summary=summary,
669
+ public_report=public_report,
670
+ details={
671
+ "primary_objective_summary": _setup_primary_objective_summary(
672
+ state=state,
673
+ progress_state=progress_state,
674
+ ).to_payload()
675
+ },
676
+ )
677
+
678
+
679
+ def _setup_primary_objective_summary(
680
+ *,
681
+ state: SetupState,
682
+ progress_state: WorkflowProgressState,
683
+ ) -> WorkflowPrimaryObjectiveSummary:
684
+ """State-owned answer to whether setup released dependent workflows."""
685
+
686
+ completed = state == SetupState.READY
687
+ return WorkflowPrimaryObjectiveSummary(
688
+ workflow=SETUP_WORKFLOW,
689
+ run_id=progress_state.run_id,
690
+ objective="Preparar caminhos, runtime local e proteção do vault para os workflows públicos.",
691
+ completed=completed,
692
+ status=state.value,
693
+ mutation_state="not_applicable",
694
+ mutation_summary="Setup não é um fluxo de limpeza/mutação da Wiki.",
695
+ remaining_work_summary=_setup_remaining_work_summary(state, completed),
696
+ next_step_summary=_setup_next_step_summary(progress_state, completed),
697
+ blocked_reason="" if completed else _reason_code_from_state(state),
698
+ )
699
+
700
+
701
+ def _setup_remaining_work_summary(state: SetupState, completed: bool) -> str:
702
+ if completed:
703
+ return "Ambiente local pronto para retomar workflows dependentes."
704
+ return _message_for_state(state)
705
+
706
+
707
+ def _setup_next_step_summary(progress_state: WorkflowProgressState, completed: bool) -> str:
708
+ if completed:
709
+ return "Nenhuma ação pendente para o setup local."
710
+ return progress_state.resume_action or "Retomar /mednotes:setup pela rota oficial."
711
+
712
+
713
+ def _diagnostic_context(
714
+ model: WorkflowModel,
715
+ state: SetupState,
716
+ category: WorkflowStateCategory,
717
+ ) -> JsonObject:
718
+ if category == WorkflowStateCategory.COMPLETED:
719
+ return {}
720
+ return diagnostic_context_evidence_only({
721
+ "schema": "medical-notes-workbench.setup-fsm-diagnostic-context.v1",
722
+ "state": state.value,
723
+ "category": category.value,
724
+ "reason": _reason_code(model, state),
725
+ })
726
+
727
+
728
+ def _blockers_for(category: WorkflowStateCategory, state: SetupState) -> list[str]:
729
+ if category in {
730
+ WorkflowStateCategory.WAITING_AGENT,
731
+ WorkflowStateCategory.WAITING_EXTERNAL,
732
+ WorkflowStateCategory.WAITING_HUMAN,
733
+ WorkflowStateCategory.BLOCKED,
734
+ WorkflowStateCategory.FAILED,
735
+ }:
736
+ return [_reason_code_from_state(state)]
737
+ return []
738
+
739
+
740
+ def _agent_instructions(category: WorkflowStateCategory) -> list[str]:
741
+ if category == WorkflowStateCategory.WAITING_AGENT:
742
+ return ["Execute somente os efeitos em agent_directive.control.effects e retome o setup depois do resultado."]
743
+ if category == WorkflowStateCategory.WAITING_EXTERNAL:
744
+ return ["Aguarde a condicao externa indicada; nao fabrique sucesso enquanto ela nao estiver pronta."]
745
+ if category == WorkflowStateCategory.WAITING_HUMAN:
746
+ return ["Peca a decisao humana fechada indicada no human_decision_packet antes de continuar."]
747
+ if category == WorkflowStateCategory.COMPLETED:
748
+ return ["Reporte que o setup esta pronto para os workflows dependentes."]
749
+ return ["Use o estado da FSM como fonte de verdade do setup."]
750
+
751
+
752
+ def _resume_action(model: WorkflowModel, state: SetupState) -> str:
753
+ if state == SetupState.READY:
754
+ return ""
755
+ if model.last_transition is not None and model.last_transition.resume_action:
756
+ return model.last_transition.resume_action
757
+ return resume_action_for_setup_state(state)
758
+
759
+
760
+ def _reason_code(model: WorkflowModel, state: SetupState) -> str:
761
+ if model.last_transition is not None:
762
+ return model.last_transition.reason_code
763
+ return _reason_code_from_state(state)
764
+
765
+
766
+ def _reason_code_from_state(state: SetupState) -> str:
767
+ match state:
768
+ case SetupState.PATHS_REQUIRED:
769
+ return "paths_missing"
770
+ case SetupState.PYTHON_ENV_REQUIRED:
771
+ return "environment_blocker.windows_path_or_venv"
772
+ case SetupState.CONFIG_VALIDATION_RUNNING:
773
+ return "config_validation_required"
774
+ case SetupState.CONFIG_ENCODING_REQUIRED:
775
+ return "config_encoding_invalid"
776
+ case SetupState.OBSIDIAN_NOT_READY:
777
+ return "obsidian_not_ready"
778
+ case SetupState.MARKDOWN_RUNTIME_REQUIRED:
779
+ return "markdown_runtime_missing"
780
+ case SetupState.MARKDOWN_INDEX_REQUIRED:
781
+ return "markdown_index_missing"
782
+ case SetupState.VAULT_GUARD_REQUIRED:
783
+ return "vault_guard_required"
784
+ case SetupState.LOCAL_READY_GITHUB_PENDING:
785
+ return "github_remote_missing"
786
+ case SetupState.GITHUB_LOGIN_REQUIRED:
787
+ return "github_login_required"
788
+ case SetupState.GITHUB_REMOTE_CONFIRMATION_REQUIRED:
789
+ return "github_remote_missing"
790
+ case SetupState.GITHUB_REMOTE_AMBIGUOUS:
791
+ return "github_remote_ambiguous"
792
+ case SetupState.BRANCH_CONFIRMATION_REQUIRED:
793
+ return "blocked_branch_confirmation_required"
794
+ case SetupState.POLICY_DECISION_REQUIRED:
795
+ return "unsupported_host_or_policy_gap"
796
+ case SetupState.FAILED:
797
+ return "setup_failed"
798
+ case _:
799
+ return state.value
800
+
801
+
802
+ def _default_version_control_safety() -> VersionControlSafety:
803
+ return VersionControlSafety(
804
+ no_resource_mutation=True,
805
+ rollback_declared=False,
806
+ direct_mutation_forbidden=True,
807
+ agent_instruction="Setup nao deve mutar conteudo medico da Wiki.",
808
+ )