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,519 @@
1
+ """Domain effect payloads carried by workflow effects.
2
+
3
+ These are MedNotes **domain** contracts — the concrete payloads (link subworkflow,
4
+ related-notes, specialist) attached to the framework's WorkflowEffect/Result
5
+ (which live in the pure-framework mednotes.kernel.effects). They are kept out of
6
+ the framework so the FSM kernel stays domain-agnostic. Layering rule: framework
7
+ <- domain <- adapters; enforced by tools/audit/import_layering.py.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from typing import Literal
12
+
13
+ from pydantic import Field, model_validator
14
+
15
+ from mednotes.domains.wiki.contracts.related_notes_runtime import RelatedNotesRecoveryState
16
+ from mednotes.domains.wiki.contracts.specialist import SpecialistTaskRunReceipt
17
+ from mednotes.kernel.base import ContractModel, JsonObject, JsonObjectAdapter, JsonValue
18
+ from mednotes.kernel.progress import WorkflowProgressViewModel
19
+ from mednotes.kernel.workflow import VersionControlSafety, WorkflowReceiptPayload
20
+
21
+
22
+ class LinkEffectCompletedOutcome(ContractModel):
23
+ """Domain outcome for a link effect that finished with safe evidence."""
24
+
25
+ code: Literal["link.completed"] = "link.completed"
26
+
27
+
28
+ class LinkEffectBlockedOutcome(ContractModel):
29
+ """Domain outcome for a link effect blocked before safe continuation."""
30
+
31
+ code: Literal["link.blocked"] = "link.blocked"
32
+ reason_code: str = ""
33
+
34
+
35
+ class LinkEffectGraphBlockedOutcome(ContractModel):
36
+ """Domain outcome for graph-level blockers returned by the link workflow."""
37
+
38
+ code: Literal["graph_blocked"] = "graph_blocked"
39
+
40
+
41
+ class LinkEffectLinkerBlockedOutcome(ContractModel):
42
+ """Domain outcome for linker/runtime blockers returned by the link workflow."""
43
+
44
+ code: Literal["linker_blocked"] = "linker_blocked"
45
+ reason_code: str = ""
46
+
47
+
48
+ class LinkEffectFailedOutcome(ContractModel):
49
+ """Domain outcome for failed link effect execution."""
50
+
51
+ code: Literal["link.failed"] = "link.failed"
52
+ reason_code: str = ""
53
+
54
+
55
+ class RelatedNotesExportCompletedOutcome(ContractModel):
56
+ """Domain outcome for a refreshed Related Notes export."""
57
+
58
+ code: Literal["related_notes.export_completed"] = "related_notes.export_completed"
59
+
60
+
61
+ class RelatedNotesQuotaWaitOutcome(ContractModel):
62
+ """Domain outcome for resumable external waits while preserving progress."""
63
+
64
+ code: Literal["related_notes.quota_wait"] = "related_notes.quota_wait"
65
+ reason_code: str = ""
66
+
67
+
68
+ class RelatedNotesBlockedOutcome(ContractModel):
69
+ """Domain outcome for non-resumable Related Notes blockers."""
70
+
71
+ code: Literal["related_notes.blocked"] = "related_notes.blocked"
72
+ reason_code: str = ""
73
+
74
+
75
+ class RelatedNotesSyncCompletedOutcome(ContractModel):
76
+ """Domain outcome for mutating Related Notes section sync."""
77
+
78
+ code: Literal["related_notes.sync_completed"] = "related_notes.sync_completed"
79
+
80
+
81
+ class RelatedNotesSyncWarningOutcome(ContractModel):
82
+ """Domain outcome for non-mutating previews or warning states."""
83
+
84
+ code: Literal["related_notes.sync_warning"] = "related_notes.sync_warning"
85
+ reason_code: str = ""
86
+
87
+
88
+ class SpecialistModelCompletedOutcome(ContractModel):
89
+ """Domain outcome for a validated specialist model receipt."""
90
+
91
+ code: Literal["style.specialist_completed"] = "style.specialist_completed"
92
+
93
+
94
+ class SpecialistModelCapacityWaitOutcome(ContractModel):
95
+ """Domain outcome for resumable specialist-capacity waits."""
96
+
97
+ code: Literal["style.capacity_wait"] = "style.capacity_wait"
98
+ reason_code: str = ""
99
+
100
+
101
+ class SpecialistModelBlockedOutcome(ContractModel):
102
+ """Domain outcome for specialist output that cannot be applied."""
103
+
104
+ code: Literal["style.blocked"] = "style.blocked"
105
+ reason_code: str = ""
106
+
107
+
108
+ class WaitExternalEffectOutcome(ContractModel):
109
+ """Domain-level wait outcome for generic resumable external waits."""
110
+
111
+ code: Literal["wait_external.waiting"] = "wait_external.waiting"
112
+ reason_code: str = ""
113
+
114
+
115
+ def _json_object(payload: object) -> JsonObject:
116
+ return JsonObjectAdapter.validate_python(payload)
117
+
118
+
119
+ def _json_field(source: JsonObject, key: str, default: JsonValue = None) -> JsonValue:
120
+ return source[key] if key in source else default
121
+
122
+
123
+ def _text_or_empty(value: object) -> str:
124
+ if value is None:
125
+ return ""
126
+ if isinstance(value, str):
127
+ return value
128
+ return f"{value}"
129
+
130
+
131
+ def _json_object_or_none(value: JsonValue) -> JsonObject | None:
132
+ return _json_object(value) if isinstance(value, dict) else None
133
+
134
+
135
+ def _json_object_or_empty(value: JsonValue) -> JsonObject:
136
+ return _json_object(value) if isinstance(value, dict) else {}
137
+
138
+
139
+ def _json_list_or_empty(value: JsonValue) -> list[JsonValue]:
140
+ return list(value) if isinstance(value, list) else []
141
+
142
+
143
+ class LinkSubworkflowEffectPayload(ContractModel):
144
+ """Typed result returned by the public `/mednotes:link` workflow adapter."""
145
+
146
+ schema_id: Literal["medical-notes-workbench.link-fsm-result.v1"] = Field(
147
+ default="medical-notes-workbench.link-fsm-result.v1",
148
+ alias="schema",
149
+ )
150
+ progress_view_model: WorkflowProgressViewModel
151
+ receipt: WorkflowReceiptPayload
152
+ reports: JsonObject
153
+ error_context: JsonObject = Field(default_factory=dict)
154
+ fsm_payload: JsonObject
155
+
156
+
157
+ LinkWorkflowRunKind = Literal[
158
+ "diagnose",
159
+ "link_run",
160
+ "vocabulary_bootstrap",
161
+ "agent_disambiguation",
162
+ "vocabulary_curator",
163
+ "apply_body_links",
164
+ "apply_related_notes",
165
+ "apply_vocabulary_semantic_repair",
166
+ ]
167
+ LINK_WORKFLOW_DIAGNOSTIC_KINDS = frozenset(
168
+ {"diagnose", "vocabulary_bootstrap", "agent_disambiguation", "vocabulary_curator"}
169
+ )
170
+ LINK_WORKFLOW_APPLY_KINDS = frozenset(
171
+ {"link_run", "apply_body_links", "apply_related_notes", "apply_vocabulary_semantic_repair"}
172
+ )
173
+
174
+
175
+ class LinkWorkflowRunEffectPayload(ContractModel):
176
+ """Typed intent for executing the public `/mednotes:link` workflow.
177
+
178
+ Parent FSMs and the link FSM itself must not ask the adapter to infer
179
+ mutation from loose booleans or legacy targets. This payload is the single
180
+ command contract consumed before `run_linker` or any equivalent adapter path
181
+ is allowed to touch the vault.
182
+ """
183
+
184
+ schema_id: Literal["medical-notes-workbench.link-workflow-run-effect.v1"] = Field(
185
+ default="medical-notes-workbench.link-workflow-run-effect.v1",
186
+ alias="schema",
187
+ )
188
+ kind: LinkWorkflowRunKind
189
+ diagnose: bool = Field(strict=True)
190
+ apply: bool = Field(strict=True)
191
+ diagnosis_path: str = ""
192
+ receipt_path: str = ""
193
+ trigger_context_path: str = ""
194
+ no_related_notes: bool = Field(default=False, strict=True)
195
+ force_diagnose: bool = Field(default=False, strict=True)
196
+ llm_disambiguation: str = "auto"
197
+ llm_model: str = ""
198
+ llm_timeout: int = Field(default=120, ge=1, strict=True)
199
+ db_path: str = ""
200
+ work_item_count: int = Field(default=0, ge=0, strict=True)
201
+ batch_plan_path: str = ""
202
+ # Mutating parent workflows pass the guard receipt as a first-class field so
203
+ # the adapter does not have to infer safety from opaque operation payloads.
204
+ version_control_safety: VersionControlSafety | None = None
205
+ operation_payload: JsonObject = Field(default_factory=dict)
206
+
207
+ @model_validator(mode="after")
208
+ def _run_linker_mode_must_be_explicit(self) -> LinkWorkflowRunEffectPayload:
209
+ if self.diagnose == self.apply:
210
+ raise ValueError("link workflow effect requires exactly one of diagnose/apply")
211
+ if self.kind in LINK_WORKFLOW_DIAGNOSTIC_KINDS and not self.diagnose:
212
+ raise ValueError(f"link workflow effect kind {self.kind!r} requires diagnose mode")
213
+ if self.kind in LINK_WORKFLOW_APPLY_KINDS and not self.apply:
214
+ raise ValueError(f"link workflow effect kind {self.kind!r} requires apply mode")
215
+ return self
216
+
217
+ @classmethod
218
+ def from_effect_payload(cls, payload: object) -> LinkWorkflowRunEffectPayload:
219
+ operation_payload = _json_object(payload)
220
+ return cls.model_validate(
221
+ {
222
+ "schema": _json_field(operation_payload, "schema")
223
+ or "medical-notes-workbench.link-workflow-run-effect.v1",
224
+ "kind": _json_field(operation_payload, "kind"),
225
+ "diagnose": _json_field(operation_payload, "diagnose"),
226
+ "apply": _json_field(operation_payload, "apply"),
227
+ "diagnosis_path": _json_field(operation_payload, "diagnosis_path", ""),
228
+ "receipt_path": _json_field(operation_payload, "receipt_path", ""),
229
+ "trigger_context_path": _json_field(operation_payload, "trigger_context_path", ""),
230
+ "no_related_notes": _json_field(operation_payload, "no_related_notes", False),
231
+ "force_diagnose": _json_field(operation_payload, "force_diagnose", False),
232
+ "llm_disambiguation": _json_field(operation_payload, "llm_disambiguation", "auto"),
233
+ "llm_model": _json_field(operation_payload, "llm_model", ""),
234
+ "llm_timeout": _json_field(operation_payload, "llm_timeout", 120),
235
+ "db_path": _json_field(operation_payload, "db_path", ""),
236
+ "work_item_count": _json_field(operation_payload, "work_item_count", 0),
237
+ "batch_plan_path": _json_field(operation_payload, "batch_plan_path", ""),
238
+ "version_control_safety": _json_field(operation_payload, "version_control_safety"),
239
+ "operation_payload": operation_payload,
240
+ }
241
+ )
242
+
243
+
244
+ class RelatedNotesRecoveryStateEffectPayload(RelatedNotesRecoveryState):
245
+ """Effect-facing alias of the canonical Related Notes recovery state.
246
+
247
+ The wait-external adapter must validate the same recovery payload emitted by
248
+ link, link-related and fix-wiki. Keeping this as a subclass preserves the
249
+ effect schema name while avoiding a narrower parallel contract.
250
+ """
251
+
252
+
253
+ class WaitExternalEffectPayload(ContractModel):
254
+ """Typed intent payload for an actual resumable external wait.
255
+
256
+ This prevents adapters from converting an arbitrary loose payload into
257
+ `waiting_external`. Related Notes waits carry their full recovery state;
258
+ other external waits carry an explicit target/blocker/resume contract.
259
+ """
260
+
261
+ schema_id: Literal["medical-notes-workbench.wait-external-effect-payload.v1"] = Field(
262
+ default="medical-notes-workbench.wait-external-effect-payload.v1",
263
+ alias="schema",
264
+ )
265
+ kind: Literal["wait_external"] = "wait_external"
266
+ related_notes_recovery_state: RelatedNotesRecoveryStateEffectPayload | None = None
267
+ wait_target: str = ""
268
+ blocked_reason: str = ""
269
+ next_action: str = ""
270
+ resume_supported: bool = Field(default=True, strict=True)
271
+ operation_payload: JsonObject = Field(default_factory=dict)
272
+
273
+ @model_validator(mode="after")
274
+ def _external_wait_requires_resumable_state(self) -> WaitExternalEffectPayload:
275
+ if not self.resume_supported:
276
+ raise ValueError("wait_external effect requires resume_supported")
277
+ if self.related_notes_recovery_state is not None:
278
+ if self.related_notes_recovery_state.status != "waiting_for_retry":
279
+ raise ValueError("wait_external effect requires waiting_for_retry recovery state")
280
+ if not self.related_notes_recovery_state.resume_supported:
281
+ raise ValueError("wait_external effect requires resume_supported recovery state")
282
+ if not self.related_notes_recovery_state.blocked_reason:
283
+ raise ValueError("wait_external effect requires blocked_reason")
284
+ return self
285
+ if not self.wait_target:
286
+ raise ValueError("wait_external effect requires wait_target when no recovery state exists")
287
+ if not self.blocked_reason:
288
+ raise ValueError("wait_external effect requires blocked_reason")
289
+ return self
290
+
291
+ @classmethod
292
+ def from_effect_payload(cls, payload: object) -> WaitExternalEffectPayload:
293
+ operation_payload = _json_object(payload)
294
+ return cls.model_validate(
295
+ {
296
+ "schema": _json_field(operation_payload, "schema")
297
+ or "medical-notes-workbench.wait-external-effect-payload.v1",
298
+ "kind": _json_field(operation_payload, "kind", "wait_external"),
299
+ "related_notes_recovery_state": _json_field(operation_payload, "related_notes_recovery_state"),
300
+ "wait_target": _json_field(operation_payload, "wait_target", ""),
301
+ "blocked_reason": _json_field(operation_payload, "blocked_reason", ""),
302
+ "next_action": _json_field(operation_payload, "next_action", ""),
303
+ "resume_supported": _json_field(operation_payload, "resume_supported", True),
304
+ "operation_payload": operation_payload,
305
+ }
306
+ )
307
+
308
+
309
+ class RelatedNotesSyncSectionEffectPayload(ContractModel):
310
+ """Typed intent for applying the Related Notes section sync.
311
+
312
+ The operation result uses `RelatedNotesSyncEffectPayload`; this class is the
313
+ FSM-owned command payload that decides whether the adapter may mutate.
314
+ """
315
+
316
+ schema_id: Literal["medical-notes-workbench.related-notes-sync-section-effect.v1"] = Field(
317
+ default="medical-notes-workbench.related-notes-sync-section-effect.v1",
318
+ alias="schema",
319
+ )
320
+ kind: Literal["sync_related_notes_section"] = "sync_related_notes_section"
321
+ apply: bool = Field(strict=True)
322
+ export_path: str = ""
323
+ receipt_path: str = ""
324
+ min_score: float = 0.2
325
+ max_links: int = Field(default=10, ge=0, strict=True)
326
+ max_age_hours: float = 168.0
327
+ operation_payload: JsonObject = Field(default_factory=dict)
328
+
329
+ @classmethod
330
+ def from_effect_payload(cls, payload: object) -> RelatedNotesSyncSectionEffectPayload:
331
+ operation_payload = _json_object(payload)
332
+ return cls.model_validate(
333
+ {
334
+ "schema": _json_field(operation_payload, "schema")
335
+ or "medical-notes-workbench.related-notes-sync-section-effect.v1",
336
+ "kind": _json_field(operation_payload, "kind", "sync_related_notes_section"),
337
+ "apply": _json_field(operation_payload, "apply"),
338
+ "export_path": _json_field(operation_payload, "export_path", ""),
339
+ "receipt_path": _json_field(operation_payload, "receipt_path", ""),
340
+ "min_score": _json_field(operation_payload, "min_score", 0.2),
341
+ "max_links": _json_field(operation_payload, "max_links", 10),
342
+ "max_age_hours": _json_field(operation_payload, "max_age_hours", 168.0),
343
+ "operation_payload": operation_payload,
344
+ }
345
+ )
346
+
347
+
348
+ class RelatedNotesExportEffectPayload(ContractModel):
349
+ """Typed intent for recovering or refreshing the Related Notes export.
350
+
351
+ Export recovery is non-mutating, but it still decides whether the workflow
352
+ can continue, must wait for quota, or needs Obsidian/user recovery. Adapters
353
+ must validate this command before invoking the runtime so a loose dict cannot
354
+ fabricate a recovery lane.
355
+ """
356
+
357
+ schema_id: Literal["medical-notes-workbench.related-notes-export-effect.v1"] = Field(
358
+ default="medical-notes-workbench.related-notes-export-effect.v1",
359
+ alias="schema",
360
+ )
361
+ kind: Literal["related_notes_export"] = "related_notes_export"
362
+ mode: str = "auto"
363
+ export_path: str = ""
364
+ reason_code: str = ""
365
+ operation_payload: JsonObject = Field(default_factory=dict)
366
+
367
+ @model_validator(mode="after")
368
+ def _export_mode_must_be_explicit(self) -> RelatedNotesExportEffectPayload:
369
+ normalized = self.mode.replace("-", "_").strip()
370
+ allowed = {
371
+ "auto",
372
+ "reindex_vault",
373
+ "index_missing",
374
+ "index_missing_notes",
375
+ "export_only_diagnostic",
376
+ }
377
+ if normalized not in allowed:
378
+ raise ValueError(f"unsupported related notes export mode: {self.mode!r}")
379
+ return self
380
+
381
+ @classmethod
382
+ def from_effect_payload(cls, payload: object) -> RelatedNotesExportEffectPayload:
383
+ operation_payload = _json_object(payload)
384
+ return cls.model_validate(
385
+ {
386
+ "schema": _json_field(operation_payload, "schema")
387
+ or "medical-notes-workbench.related-notes-export-effect.v1",
388
+ "kind": _json_field(operation_payload, "kind"),
389
+ "mode": _json_field(operation_payload, "mode", "auto"),
390
+ "export_path": _json_field(operation_payload, "export_path", ""),
391
+ "reason_code": _json_field(operation_payload, "reason_code", ""),
392
+ "operation_payload": operation_payload,
393
+ }
394
+ )
395
+
396
+
397
+ class RelatedNotesRecoveryEffectPayload(ContractModel):
398
+ schema_id: Literal["medical-notes-workbench.related-notes-export-recovery.v1"] = Field(
399
+ default="medical-notes-workbench.related-notes-export-recovery.v1",
400
+ alias="schema",
401
+ )
402
+ status: Literal["recovered", "completed", "blocked", "failed"]
403
+ blocked_reason: str = ""
404
+ next_action: str = ""
405
+ related_notes_recovery_state: RelatedNotesRecoveryStateEffectPayload | None = None
406
+ receipt: JsonObject | None = None
407
+ operation_payload: JsonObject = Field(default_factory=dict)
408
+
409
+ @classmethod
410
+ def from_operation_payload(cls, payload: object) -> RelatedNotesRecoveryEffectPayload:
411
+ operation_payload = _json_object(payload)
412
+ receipt = _json_object_or_none(_json_field(operation_payload, "receipt"))
413
+ return cls.model_validate(
414
+ {
415
+ "schema": _json_field(operation_payload, "schema")
416
+ or "medical-notes-workbench.related-notes-export-recovery.v1",
417
+ "status": _json_field(operation_payload, "status"),
418
+ "blocked_reason": _json_field(operation_payload, "blocked_reason", ""),
419
+ "next_action": _json_field(operation_payload, "next_action", ""),
420
+ "related_notes_recovery_state": _json_field(operation_payload, "related_notes_recovery_state"),
421
+ "receipt": receipt,
422
+ "operation_payload": operation_payload,
423
+ }
424
+ )
425
+
426
+
427
+ class RelatedNotesSyncEffectPayload(ContractModel):
428
+ schema_id: Literal["medical-notes-workbench.related-notes-sync.v1"] = Field(
429
+ default="medical-notes-workbench.related-notes-sync.v1",
430
+ alias="schema",
431
+ )
432
+ status: Literal["completed", "preview_ready", "completed_with_warnings", "blocked", "skipped"]
433
+ phase: str = ""
434
+ blocked_reason: str = ""
435
+ next_action: str = ""
436
+ applied_note_count: int = Field(default=0, ge=0, strict=True)
437
+ updates: list[JsonObject] = Field(default_factory=list)
438
+ receipt: JsonObject | None = None
439
+ operation_payload: JsonObject = Field(default_factory=dict)
440
+
441
+ @model_validator(mode="after")
442
+ def _completed_requires_receipt(self) -> RelatedNotesSyncEffectPayload:
443
+ if self.status == "completed" and self.receipt is None:
444
+ raise ValueError("completed related notes sync effect payload requires receipt")
445
+ return self
446
+
447
+ @classmethod
448
+ def from_operation_payload(cls, payload: object) -> RelatedNotesSyncEffectPayload:
449
+ operation_payload = _json_object(payload)
450
+ updates = _json_list_or_empty(_json_field(operation_payload, "updates"))
451
+ receipt = _json_object_or_none(_json_field(operation_payload, "receipt"))
452
+ return cls.model_validate(
453
+ {
454
+ "schema": _json_field(operation_payload, "schema") or "medical-notes-workbench.related-notes-sync.v1",
455
+ "status": _json_field(operation_payload, "status"),
456
+ "phase": _json_field(operation_payload, "phase", ""),
457
+ "blocked_reason": _json_field(operation_payload, "blocked_reason", ""),
458
+ "next_action": _json_field(operation_payload, "next_action", ""),
459
+ "applied_note_count": _json_field(operation_payload, "applied_note_count", 0),
460
+ "updates": updates,
461
+ "receipt": receipt,
462
+ "operation_payload": operation_payload,
463
+ }
464
+ )
465
+
466
+
467
+ class SpecialistModelEffectPayload(ContractModel):
468
+ schema_id: Literal["medical-notes-workbench.specialist-model-effect-payload.v1"] = Field(
469
+ default="medical-notes-workbench.specialist-model-effect-payload.v1",
470
+ alias="schema",
471
+ )
472
+ status: str = Field(min_length=1)
473
+ blocked_reason: str = ""
474
+ payload: JsonObject = Field(default_factory=dict)
475
+ receipt: SpecialistTaskRunReceipt | None = None
476
+ attestation: JsonObject | None = None
477
+ next_action: str = ""
478
+ required_inputs: list[str] = Field(default_factory=list)
479
+ operation_payload: JsonObject = Field(default_factory=dict)
480
+
481
+ @model_validator(mode="after")
482
+ def _completed_requires_receipt_and_attestation(self) -> SpecialistModelEffectPayload:
483
+ if self.status == "completed":
484
+ if self.receipt is None:
485
+ raise ValueError("completed specialist model effect payload requires receipt")
486
+ if self.attestation is None:
487
+ raise ValueError("completed specialist model effect payload requires attestation")
488
+ if self.receipt.specialist_output_attestation is None:
489
+ raise ValueError("completed specialist model effect payload requires receipt attestation")
490
+ expected_attestation = self.receipt.specialist_output_attestation.to_payload()
491
+ if self.attestation != expected_attestation:
492
+ raise ValueError("completed specialist model effect payload attestation must match receipt attestation")
493
+ return self
494
+
495
+ @classmethod
496
+ def from_operation_payload(cls, payload: object) -> SpecialistModelEffectPayload:
497
+ operation_payload = _json_object(payload)
498
+ specialist_payload = _json_object_or_empty(_json_field(operation_payload, "payload"))
499
+ receipt_payload = _json_object_or_none(_json_field(operation_payload, "receipt"))
500
+ attestation = _json_object_or_none(_json_field(operation_payload, "attestation"))
501
+ receipt = (
502
+ SpecialistTaskRunReceipt.from_operation_payload(receipt_payload)
503
+ if receipt_payload is not None
504
+ else None
505
+ )
506
+ return cls.model_validate(
507
+ {
508
+ "schema": _json_field(operation_payload, "schema")
509
+ or "medical-notes-workbench.specialist-model-effect-payload.v1",
510
+ "status": _json_field(operation_payload, "status"),
511
+ "blocked_reason": _text_or_empty(_json_field(operation_payload, "blocked_reason")),
512
+ "payload": specialist_payload,
513
+ "receipt": receipt,
514
+ "attestation": attestation,
515
+ "next_action": _text_or_empty(_json_field(operation_payload, "next_action")),
516
+ "required_inputs": _json_list_or_empty(_json_field(operation_payload, "required_inputs")),
517
+ "operation_payload": operation_payload,
518
+ }
519
+ )