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,290 @@
1
+ """Canonical fix-wiki StateChart states and state-category mapping.
2
+
3
+ This module is pure domain data shared by the machine and public projector.
4
+ `fix_wiki_machine.py` owns transitions; `fix_wiki_fsm.py` only projects the
5
+ machine result and must not be imported by the machine for state truth.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from enum import StrEnum
11
+
12
+ from mednotes.kernel.state_machine import WorkflowStateCategory
13
+
14
+ FIX_WIKI_WORKFLOW = "/mednotes:fix-wiki"
15
+
16
+
17
+ class FixWikiState(StrEnum):
18
+ """Canonical leaf states for the fix-wiki StateChart."""
19
+
20
+ DIAGNOSIS_RUNNING = "diagnosis.running"
21
+ ENVIRONMENT_PATHS_MISSING = "environment.paths_missing"
22
+ ENVIRONMENT_WIKI_DIR_MISSING = "environment.wiki_dir_missing"
23
+ ENVIRONMENT_WINDOWS_PATH_OR_VENV_BLOCKED = "environment.windows_path_or_venv_blocked"
24
+ VAULT_GUARD_RUNNING = "vault_guard.running"
25
+ VAULT_GUARD_DECISION_REQUIRED = "vault_guard.decision_required"
26
+ SUBAGENT_PLAN_ATTESTATION_REQUIRED = "subagent_plan_attestation.required"
27
+ SUBAGENT_PLAN_ATTESTATION_INVALID = "subagent_plan_attestation.invalid"
28
+ AGENT_TOOL_CONTRACT_VIOLATION = "agent_tool_contract_violation"
29
+ DETERMINISTIC_REPAIRS_RUNNING = "deterministic_repairs.running"
30
+ DETERMINISTIC_REPAIRS_FAILED = "deterministic_repairs.failed"
31
+ STYLE_REWRITE_SPECIALIST_REQUESTED = "style_rewrite.specialist_requested"
32
+ STYLE_REWRITE_CAPACITY_WAIT = "style_rewrite.capacity_wait"
33
+ STYLE_REWRITE_REVIEW_REQUIRED = "style_rewrite.review_required"
34
+ STYLE_REWRITE_APPLY_RUNNING = "style_rewrite.apply_running"
35
+ TAXONOMY_DECISION_REQUIRED = "taxonomy.decision_required"
36
+ TAXONOMY_APPLY_RUNNING = "taxonomy.apply_running"
37
+ VOCABULARY_CURATOR_RUNNING = "vocabulary.curator_running"
38
+ VOCABULARY_SEMANTIC_INGESTION_PENDING = "vocabulary.semantic_ingestion_pending"
39
+ VOCABULARY_EVAL_RUNNING = "vocabulary.eval_running"
40
+ VOCABULARY_EVAL_NEEDS_REVIEW = "vocabulary.eval_needs_review"
41
+ VOCABULARY_APPLY_RUNNING = "vocabulary.apply_running"
42
+ VOCABULARY_SQLITE_INTEGRITY_FAILED = "vocabulary.sqlite_integrity_failed"
43
+ ATOMICITY_SPLIT_RUNNING = "atomicity_split.running"
44
+ ATOMICITY_SPLIT_REVIEW_REQUIRED = "atomicity_split.review_required"
45
+ RELATED_NOTES_EXPORT_RUNNING = "related_notes.export_running"
46
+ RELATED_NOTES_QUOTA_WAIT = "related_notes.quota_wait"
47
+ RELATED_NOTES_OBSIDIAN_NOT_READY = "related_notes.obsidian_not_ready"
48
+ RELATED_NOTES_BLOCKED = "related_notes.blocked"
49
+ LINK_RUN_REQUESTED = "link.run_requested"
50
+ LINK_GRAPH_BLOCKED = "link.graph_blocked"
51
+ LINK_GRAPH_REVIEW_REQUIRED = "link.graph_review_required"
52
+ LINKER_BLOCKED = "link.linker_blocked"
53
+ MERGE_RUNNING = "merge.running"
54
+ MERGE_REVIEW_REQUIRED = "merge.review_required"
55
+ CONTRACT_GAP_MISSING_NEXT_ACTION = "contract_gap.missing_next_action"
56
+ CONTRACT_GAP_MISSING_ERROR_CONTEXT = "contract_gap.missing_error_context"
57
+ ROLLBACK_RUNNING = "rollback.running"
58
+ ROLLBACK_PERFORMED = "rollback.performed"
59
+ ROLLBACK_FAILED = "rollback.failed"
60
+ FINAL_VALIDATION_RUNNING = "final_validation.running"
61
+ FINAL_VALIDATION_FAILED = "final_validation.failed"
62
+ PREVIEW_READY = "preview.ready"
63
+ COMPLETED = "completed"
64
+ COMPLETED_WITH_WARNINGS = "completed_with_warnings"
65
+ FAILED = "failed"
66
+
67
+
68
+ class FixWikiDiagnosisLane(StrEnum):
69
+ """Typed diagnosis lanes; guards are allowed to inspect only this value."""
70
+
71
+ ENVIRONMENT_PATHS_MISSING = "environment.paths_missing"
72
+ ENVIRONMENT_WIKI_DIR_MISSING = "environment.wiki_dir_missing"
73
+ ENVIRONMENT_WINDOWS_PATH_OR_VENV_BLOCKED = "environment.windows_path_or_venv_blocked"
74
+ VAULT_GUARD_DECISION_REQUIRED = "vault_guard.decision_required"
75
+ SUBAGENT_PLAN_ATTESTATION_REQUIRED = "subagent_plan_attestation.required"
76
+ SUBAGENT_PLAN_ATTESTATION_INVALID = "subagent_plan_attestation.invalid"
77
+ AGENT_TOOL_CONTRACT_VIOLATION = "agent_tool_contract_violation"
78
+ DETERMINISTIC_REPAIRS = "deterministic_repairs"
79
+ STYLE_REWRITE = "style_rewrite"
80
+ TAXONOMY = "taxonomy"
81
+ VOCABULARY_SEMANTIC_INGESTION_PENDING = "vocabulary.semantic_ingestion_pending"
82
+ VOCABULARY = "vocabulary"
83
+ ATOMICITY_SPLIT = "atomicity_split"
84
+ MERGE = "merge"
85
+ RELATED_NOTES = "related_notes"
86
+ LINK = "link"
87
+ CONTRACT_GAP_MISSING_NEXT_ACTION = "contract_gap.missing_next_action"
88
+ CONTRACT_GAP_MISSING_ERROR_CONTEXT = "contract_gap.missing_error_context"
89
+ ROLLBACK = "rollback"
90
+ FINAL_VALIDATION = "final_validation"
91
+
92
+
93
+ class FixWikiReason(StrEnum):
94
+ """Closed public reason labels derived from fix-wiki leaf states."""
95
+
96
+ COMPLETED = "completed"
97
+ COMPLETED_WITH_WARNINGS = "completed_with_warnings"
98
+ PREVIEW_READY = "preview_ready"
99
+ ENVIRONMENT_PATHS_MISSING = "environment.paths_missing"
100
+ ENVIRONMENT_WIKI_DIR_MISSING = "environment.wiki_dir_missing"
101
+ ENVIRONMENT_WINDOWS_PATH_OR_VENV_BLOCKED = "environment.windows_path_or_venv_blocked"
102
+ WAITING_EXTERNAL_RELATED_NOTES = "waiting_external_related_notes"
103
+ WAITING_EXTERNAL = "waiting_external"
104
+ WAITING_HUMAN = "waiting_human"
105
+ SUBAGENT_PLAN_ATTESTATION_REQUIRED = "subagent_plan_attestation_required"
106
+ SUBAGENT_PLAN_ATTESTATION_INVALID = "subagent_plan_attestation_invalid"
107
+ STYLE_REWRITE_REVIEW_REQUIRED = "style_rewrite_review_required"
108
+ TAXONOMY_DECISION_REQUIRED = "taxonomy_decision_required"
109
+ VOCABULARY_EVAL_NEEDS_REVIEW = "vocabulary_eval_needs_review"
110
+ ATOMICITY_SPLIT_REVIEW_REQUIRED = "atomicity_split_review_required"
111
+ MERGE_REVIEW_REQUIRED = "merge_review_required"
112
+ STYLE_REWRITE_READY = "style_rewrite_ready"
113
+ VOCABULARY_SEMANTIC_INGESTION_PENDING = "vocabulary_semantic_ingestion_pending"
114
+ GRAPH_BLOCKED = "graph_blocked"
115
+ ATOMICITY_SPLIT_REQUIRED = "atomicity_split_required"
116
+ RELATED_NOTES_BLOCKED = "related_notes_blocked"
117
+ LINK_RUN_REQUESTED = "link_run_requested"
118
+ LINKER_BLOCKED = "linker_blocked"
119
+ GRAPH_REVIEW_REQUIRED = "graph_review_required"
120
+ TAXONOMY_BLOCKED = "taxonomy_blocked"
121
+ VAULT_GUARD_REQUIRED = "vault_guard_required"
122
+ STYLE_REWRITE_REQUIRED = "style_rewrite_required"
123
+ FAILED = "failed"
124
+
125
+
126
+ FIX_WIKI_DIAGNOSIS_PRIORITY: tuple[FixWikiDiagnosisLane, ...] = (
127
+ FixWikiDiagnosisLane.ENVIRONMENT_PATHS_MISSING,
128
+ FixWikiDiagnosisLane.ENVIRONMENT_WIKI_DIR_MISSING,
129
+ FixWikiDiagnosisLane.ENVIRONMENT_WINDOWS_PATH_OR_VENV_BLOCKED,
130
+ FixWikiDiagnosisLane.VAULT_GUARD_DECISION_REQUIRED,
131
+ FixWikiDiagnosisLane.SUBAGENT_PLAN_ATTESTATION_REQUIRED,
132
+ FixWikiDiagnosisLane.SUBAGENT_PLAN_ATTESTATION_INVALID,
133
+ FixWikiDiagnosisLane.AGENT_TOOL_CONTRACT_VIOLATION,
134
+ FixWikiDiagnosisLane.DETERMINISTIC_REPAIRS,
135
+ FixWikiDiagnosisLane.STYLE_REWRITE,
136
+ FixWikiDiagnosisLane.TAXONOMY,
137
+ FixWikiDiagnosisLane.VOCABULARY_SEMANTIC_INGESTION_PENDING,
138
+ FixWikiDiagnosisLane.VOCABULARY,
139
+ FixWikiDiagnosisLane.ATOMICITY_SPLIT,
140
+ FixWikiDiagnosisLane.MERGE,
141
+ FixWikiDiagnosisLane.RELATED_NOTES,
142
+ FixWikiDiagnosisLane.LINK,
143
+ FixWikiDiagnosisLane.CONTRACT_GAP_MISSING_NEXT_ACTION,
144
+ FixWikiDiagnosisLane.CONTRACT_GAP_MISSING_ERROR_CONTEXT,
145
+ FixWikiDiagnosisLane.ROLLBACK,
146
+ FixWikiDiagnosisLane.FINAL_VALIDATION,
147
+ )
148
+
149
+
150
+ def category_for_state(state: str | FixWikiState) -> WorkflowStateCategory:
151
+ """Map every fix-wiki leaf state to the public FSM category."""
152
+
153
+ try:
154
+ state_value = state if isinstance(state, FixWikiState) else FixWikiState(str(state))
155
+ except ValueError as exc:
156
+ raise ValueError(f"unknown fix-wiki state: {state}") from exc
157
+
158
+ match state_value:
159
+ case (
160
+ FixWikiState.DIAGNOSIS_RUNNING
161
+ | FixWikiState.VAULT_GUARD_RUNNING
162
+ | FixWikiState.DETERMINISTIC_REPAIRS_RUNNING
163
+ | FixWikiState.STYLE_REWRITE_APPLY_RUNNING
164
+ | FixWikiState.TAXONOMY_APPLY_RUNNING
165
+ | FixWikiState.VOCABULARY_CURATOR_RUNNING
166
+ | FixWikiState.VOCABULARY_EVAL_RUNNING
167
+ | FixWikiState.VOCABULARY_APPLY_RUNNING
168
+ | FixWikiState.ATOMICITY_SPLIT_RUNNING
169
+ | FixWikiState.RELATED_NOTES_EXPORT_RUNNING
170
+ | FixWikiState.LINK_RUN_REQUESTED
171
+ | FixWikiState.MERGE_RUNNING
172
+ | FixWikiState.ROLLBACK_RUNNING
173
+ | FixWikiState.FINAL_VALIDATION_RUNNING
174
+ ):
175
+ return WorkflowStateCategory.RUNNING
176
+ case (
177
+ FixWikiState.STYLE_REWRITE_SPECIALIST_REQUESTED
178
+ | FixWikiState.VOCABULARY_SEMANTIC_INGESTION_PENDING
179
+ ):
180
+ return WorkflowStateCategory.WAITING_AGENT
181
+ case (
182
+ FixWikiState.STYLE_REWRITE_CAPACITY_WAIT
183
+ | FixWikiState.RELATED_NOTES_QUOTA_WAIT
184
+ ):
185
+ return WorkflowStateCategory.WAITING_EXTERNAL
186
+ case (
187
+ FixWikiState.STYLE_REWRITE_REVIEW_REQUIRED
188
+ | FixWikiState.TAXONOMY_DECISION_REQUIRED
189
+ | FixWikiState.SUBAGENT_PLAN_ATTESTATION_REQUIRED
190
+ | FixWikiState.SUBAGENT_PLAN_ATTESTATION_INVALID
191
+ | FixWikiState.VOCABULARY_EVAL_NEEDS_REVIEW
192
+ | FixWikiState.ATOMICITY_SPLIT_REVIEW_REQUIRED
193
+ | FixWikiState.MERGE_REVIEW_REQUIRED
194
+ | FixWikiState.LINK_GRAPH_REVIEW_REQUIRED
195
+ ):
196
+ return WorkflowStateCategory.WAITING_HUMAN
197
+ case (
198
+ FixWikiState.ENVIRONMENT_PATHS_MISSING
199
+ | FixWikiState.ENVIRONMENT_WIKI_DIR_MISSING
200
+ | FixWikiState.ENVIRONMENT_WINDOWS_PATH_OR_VENV_BLOCKED
201
+ | FixWikiState.RELATED_NOTES_OBSIDIAN_NOT_READY
202
+ | FixWikiState.RELATED_NOTES_BLOCKED
203
+ | FixWikiState.LINK_GRAPH_BLOCKED
204
+ | FixWikiState.LINKER_BLOCKED
205
+ | FixWikiState.VAULT_GUARD_DECISION_REQUIRED
206
+ | FixWikiState.ROLLBACK_FAILED
207
+ ):
208
+ return WorkflowStateCategory.BLOCKED
209
+ case (
210
+ FixWikiState.FAILED
211
+ | FixWikiState.AGENT_TOOL_CONTRACT_VIOLATION
212
+ | FixWikiState.CONTRACT_GAP_MISSING_NEXT_ACTION
213
+ | FixWikiState.CONTRACT_GAP_MISSING_ERROR_CONTEXT
214
+ | FixWikiState.DETERMINISTIC_REPAIRS_FAILED
215
+ | FixWikiState.VOCABULARY_SQLITE_INTEGRITY_FAILED
216
+ | FixWikiState.ROLLBACK_PERFORMED
217
+ | FixWikiState.FINAL_VALIDATION_FAILED
218
+ ):
219
+ return WorkflowStateCategory.FAILED
220
+ case FixWikiState.COMPLETED:
221
+ return WorkflowStateCategory.COMPLETED
222
+ case FixWikiState.PREVIEW_READY:
223
+ return WorkflowStateCategory.COMPLETED
224
+ case FixWikiState.COMPLETED_WITH_WARNINGS:
225
+ return WorkflowStateCategory.COMPLETED_WITH_WARNINGS
226
+ case _:
227
+ raise AssertionError(f"unclassified fix-wiki state: {state_value}")
228
+
229
+
230
+ def reason_for_state(state: str | FixWikiState) -> FixWikiReason:
231
+ """Derive the public reason from the canonical leaf state only.
232
+
233
+ Runtime outcomes and transition metadata may be useful audit evidence, but
234
+ they are not allowed to override this map. If a reason needs to become more
235
+ specific, the fix is to add a precise leaf state rather than a second
236
+ status/reason channel.
237
+ """
238
+
239
+ state_value = state if isinstance(state, FixWikiState) else FixWikiState(str(state))
240
+ if state_value == FixWikiState.PREVIEW_READY:
241
+ return FixWikiReason.PREVIEW_READY
242
+ if state_value == FixWikiState.ENVIRONMENT_PATHS_MISSING:
243
+ return FixWikiReason.ENVIRONMENT_PATHS_MISSING
244
+ if state_value == FixWikiState.ENVIRONMENT_WIKI_DIR_MISSING:
245
+ return FixWikiReason.ENVIRONMENT_WIKI_DIR_MISSING
246
+ if state_value == FixWikiState.ENVIRONMENT_WINDOWS_PATH_OR_VENV_BLOCKED:
247
+ return FixWikiReason.ENVIRONMENT_WINDOWS_PATH_OR_VENV_BLOCKED
248
+ if state_value == FixWikiState.COMPLETED:
249
+ return FixWikiReason.COMPLETED
250
+ if state_value == FixWikiState.COMPLETED_WITH_WARNINGS:
251
+ return FixWikiReason.COMPLETED_WITH_WARNINGS
252
+ if state_value == FixWikiState.RELATED_NOTES_QUOTA_WAIT:
253
+ return FixWikiReason.WAITING_EXTERNAL_RELATED_NOTES
254
+ if state_value == FixWikiState.STYLE_REWRITE_CAPACITY_WAIT:
255
+ return FixWikiReason.WAITING_EXTERNAL
256
+ if state_value == FixWikiState.STYLE_REWRITE_SPECIALIST_REQUESTED:
257
+ return FixWikiReason.STYLE_REWRITE_READY
258
+ if state_value == FixWikiState.VOCABULARY_SEMANTIC_INGESTION_PENDING:
259
+ return FixWikiReason.VOCABULARY_SEMANTIC_INGESTION_PENDING
260
+ if state_value == FixWikiState.LINK_GRAPH_BLOCKED:
261
+ return FixWikiReason.GRAPH_BLOCKED
262
+ if state_value == FixWikiState.LINK_GRAPH_REVIEW_REQUIRED:
263
+ return FixWikiReason.GRAPH_REVIEW_REQUIRED
264
+ if state_value == FixWikiState.LINK_RUN_REQUESTED:
265
+ return FixWikiReason.LINK_RUN_REQUESTED
266
+ if state_value == FixWikiState.LINKER_BLOCKED:
267
+ return FixWikiReason.LINKER_BLOCKED
268
+ if state_value in {FixWikiState.RELATED_NOTES_BLOCKED, FixWikiState.RELATED_NOTES_OBSIDIAN_NOT_READY}:
269
+ return FixWikiReason.RELATED_NOTES_BLOCKED
270
+ if state_value == FixWikiState.ATOMICITY_SPLIT_REVIEW_REQUIRED:
271
+ return FixWikiReason.ATOMICITY_SPLIT_REVIEW_REQUIRED
272
+ if state_value == FixWikiState.TAXONOMY_DECISION_REQUIRED:
273
+ return FixWikiReason.TAXONOMY_DECISION_REQUIRED
274
+ if state_value == FixWikiState.VAULT_GUARD_DECISION_REQUIRED:
275
+ return FixWikiReason.VAULT_GUARD_REQUIRED
276
+ if state_value == FixWikiState.STYLE_REWRITE_REVIEW_REQUIRED:
277
+ return FixWikiReason.STYLE_REWRITE_REVIEW_REQUIRED
278
+ if state_value == FixWikiState.SUBAGENT_PLAN_ATTESTATION_REQUIRED:
279
+ return FixWikiReason.SUBAGENT_PLAN_ATTESTATION_REQUIRED
280
+ if state_value == FixWikiState.SUBAGENT_PLAN_ATTESTATION_INVALID:
281
+ return FixWikiReason.SUBAGENT_PLAN_ATTESTATION_INVALID
282
+ if state_value == FixWikiState.VOCABULARY_EVAL_NEEDS_REVIEW:
283
+ return FixWikiReason.VOCABULARY_EVAL_NEEDS_REVIEW
284
+ if state_value == FixWikiState.MERGE_REVIEW_REQUIRED:
285
+ return FixWikiReason.MERGE_REVIEW_REQUIRED
286
+ if category_for_state(state_value) == WorkflowStateCategory.WAITING_HUMAN:
287
+ return FixWikiReason.WAITING_HUMAN
288
+ if category_for_state(state_value) == WorkflowStateCategory.FAILED:
289
+ return FixWikiReason.FAILED
290
+ return FixWikiReason.FAILED
@@ -0,0 +1,342 @@
1
+ """Human-readable redacted report for the `fix-wiki` workflow."""
2
+ from __future__ import annotations
3
+
4
+ import re
5
+ from pathlib import Path
6
+
7
+ from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr
8
+
9
+ from mednotes.domains.wiki.capabilities.notes.raw_chats import atomic_write_text
10
+ from mednotes.domains.wiki.flows.fix_wiki.fix_wiki_fsm import assert_fix_wiki_fsm_payload
11
+ from mednotes.domains.wiki.flows.fix_wiki.fix_wiki_primary_objective import fix_wiki_primary_objective_summary
12
+ from mednotes.kernel.base import JsonArrayAdapter, JsonObject, JsonObjectAdapter, JsonValue
13
+ from mednotes.kernel.public_report import WorkflowPublicReport
14
+
15
+ _SEVERITY_ORDER = {"critical": 0, "high": 1, "medium": 2, "low": 3, "info": 4, "": 5}
16
+ _TRANSIENT_MUTATION_LINE_RE = re.compile(r"\balterei\s+\d+\s+arquivo", re.IGNORECASE)
17
+ _PUBLIC_GRAPH_CURATION_ACTION = "Retomar a curadoria do grafo pela rota oficial antes de concluir a atualização de links."
18
+ _PUBLIC_HUMAN_DECISION_RESUME = "Após escolher uma opção, retome pelo fluxo oficial do /mednotes:fix-wiki."
19
+ _TECHNICAL_GRAPH_CURATION_MARKERS = (
20
+ "med-link-graph-curator",
21
+ "collect-curator-outputs",
22
+ "eval-curator-batch",
23
+ "apply-curator-batch",
24
+ "vocabulary-curator-batch-plan",
25
+ )
26
+
27
+
28
+ class _FixWikiUserReportFieldModel(BaseModel):
29
+ model_config = ConfigDict(extra="forbid", validate_assignment=True)
30
+
31
+
32
+ class _FixWikiUserReportProjectionModel(BaseModel):
33
+ model_config = ConfigDict(extra="ignore", populate_by_name=True, validate_assignment=True)
34
+
35
+
36
+ class _FsmProgressDisplayFields(_FixWikiUserReportProjectionModel):
37
+ status: StrictStr = ""
38
+ headline: StrictStr = ""
39
+ message: StrictStr = ""
40
+ detail: StrictStr = ""
41
+ count_label: StrictStr = ""
42
+ user_action: StrictStr = ""
43
+ resume_action: StrictStr = ""
44
+ can_continue_now: StrictBool | None = None
45
+
46
+
47
+ class _FsmSnapshotDisplayFields(_FixWikiUserReportProjectionModel):
48
+ current_state: StrictStr = ""
49
+
50
+
51
+ class _AgentCapabilitiesFields(_FixWikiUserReportProjectionModel):
52
+ continue_: StrictBool = Field(default=False, alias="continue")
53
+
54
+
55
+ class _AgentControlFields(_FixWikiUserReportProjectionModel):
56
+ status: StrictStr = ""
57
+ capabilities: _AgentCapabilitiesFields = Field(default_factory=_AgentCapabilitiesFields)
58
+ effects: list[JsonValue] = Field(default_factory=list)
59
+
60
+
61
+ class _AgentDirectiveFields(_FixWikiUserReportProjectionModel):
62
+ control: _AgentControlFields = Field(default_factory=_AgentControlFields)
63
+
64
+
65
+ class _FsmReportsDisplayFields(_FixWikiUserReportProjectionModel):
66
+ summary: StrictStr = ""
67
+ public_report: WorkflowPublicReport
68
+
69
+
70
+ class _FixWikiFsmReportRootFields(_FixWikiUserReportProjectionModel):
71
+ """Typed projection of the canonical FSM payload used by the user renderer."""
72
+
73
+ progress_view_model: JsonObject
74
+ state_machine_snapshot: JsonObject
75
+ reports: _FsmReportsDisplayFields
76
+ receipt: JsonObject
77
+ agent_directive: JsonObject
78
+ human_decision_packet: JsonObject | None = None
79
+
80
+
81
+ class _DecisionOptionFields(_FixWikiUserReportFieldModel):
82
+ id: StrictStr = ""
83
+ label: StrictStr = ""
84
+
85
+
86
+ class _RejectedAutomationFields(_FixWikiUserReportFieldModel):
87
+ kind: StrictStr = ""
88
+ reason: StrictStr = ""
89
+ reason_code: StrictStr = ""
90
+
91
+
92
+ class _HumanDecisionPacketFields(_FixWikiUserReportFieldModel):
93
+ question: StrictStr = ""
94
+ public_summary: StrictStr = ""
95
+ options: list[_DecisionOptionFields] = Field(default_factory=list)
96
+ rejected_automations: list[_RejectedAutomationFields] = Field(default_factory=list)
97
+ resume_action: StrictStr = ""
98
+
99
+
100
+ def _json_object(value: object) -> JsonObject:
101
+ return JsonObjectAdapter.validate_python(value)
102
+
103
+
104
+ def _json_array(value: object) -> list[JsonValue]:
105
+ return JsonArrayAdapter.validate_python(value)
106
+
107
+
108
+ def _field_payload(source: JsonObject, field_names: tuple[str, ...]) -> JsonObject:
109
+ payload: JsonObject = {}
110
+ for field_name in field_names:
111
+ if field_name in source:
112
+ payload[field_name] = source[field_name]
113
+ return payload
114
+
115
+
116
+ def _json_array_field(source: JsonObject, field_name: str) -> list[JsonValue]:
117
+ if field_name not in source:
118
+ return []
119
+ return _json_array(source[field_name])
120
+
121
+
122
+ def _object_field_payload(source: JsonObject, field_name: str, field_names: tuple[str, ...]) -> JsonObject:
123
+ if field_name not in source:
124
+ return {}
125
+ return _field_payload(_json_object(source[field_name]), field_names)
126
+
127
+
128
+ def _decision_options(source: JsonObject) -> list[_DecisionOptionFields]:
129
+ return [
130
+ _DecisionOptionFields.model_validate(_field_payload(_json_object(item), ("id", "label")))
131
+ for item in _json_array_field(source, "options")
132
+ ]
133
+
134
+
135
+ def _rejected_automations(source: JsonObject) -> list[_RejectedAutomationFields]:
136
+ return [
137
+ _RejectedAutomationFields.model_validate(_field_payload(_json_object(item), ("kind", "reason", "reason_code")))
138
+ for item in _json_array_field(source, "rejected_automations")
139
+ ]
140
+
141
+
142
+ def _human_decision_packet_fields(source: JsonObject) -> _HumanDecisionPacketFields:
143
+ payload = _field_payload(source, ("question", "public_summary", "resume_action"))
144
+ payload["options"] = _decision_options(source)
145
+ payload["rejected_automations"] = _rejected_automations(source)
146
+ return _HumanDecisionPacketFields.model_validate(payload)
147
+
148
+
149
+ def _human_decision_packet_has_data(packet: _HumanDecisionPacketFields) -> bool:
150
+ return bool(
151
+ packet.question
152
+ or packet.public_summary
153
+ or packet.options
154
+ or packet.rejected_automations
155
+ or packet.resume_action
156
+ )
157
+
158
+
159
+ def render_fix_wiki_user_report(report: object) -> str:
160
+ """Render only the canonical FSM payload; legacy root reports are rejected."""
161
+
162
+ payload = _json_object(report)
163
+ schema = payload["schema"] if "schema" in payload else ""
164
+ if schema != "medical-notes-workbench.fix-wiki-fsm-result.v1":
165
+ raise ValueError("fix-wiki user report requires the canonical FSM result payload")
166
+ assert_fix_wiki_fsm_payload(payload)
167
+ return _render_fix_wiki_fsm_report(payload)
168
+
169
+
170
+ def write_fix_wiki_user_report_v2(path: Path, payload: object) -> None:
171
+ """Write a compact human report without raw note bodies or textual diffs."""
172
+
173
+ atomic_write_text(path, render_fix_wiki_user_report(payload))
174
+
175
+
176
+ def _render_fix_wiki_fsm_report(report: JsonObject) -> str:
177
+ root = _FixWikiFsmReportRootFields.model_validate(report)
178
+ progress = _FsmProgressDisplayFields.model_validate(root.progress_view_model)
179
+ snapshot = _FsmSnapshotDisplayFields.model_validate(root.state_machine_snapshot)
180
+ public_report = root.reports.public_report
181
+ agent_directive = _AgentDirectiveFields.model_validate(root.agent_directive)
182
+ control = agent_directive.control
183
+ can_continue_without_human = (
184
+ control.status == "waiting_agent"
185
+ and control.capabilities.continue_ is True
186
+ and bool(control.effects)
187
+ )
188
+ public_lines = [
189
+ _public_safe_report_line(_clean(line))
190
+ for line in public_report.lines
191
+ if line.strip()
192
+ ]
193
+ if not can_continue_without_human:
194
+ public_lines = [line for line in public_lines if not _mentions_automatic_continuation(line)]
195
+ reports_summary = _clean(root.reports.summary)
196
+ summary = _clean(public_report.headline or reports_summary or progress.headline or progress.message or "Conferência concluída.")
197
+ if not can_continue_without_human and _mentions_automatic_continuation(summary):
198
+ summary = public_lines[0] if public_lines else "Workflow aguardando condição externa para retomar pela rota oficial."
199
+ next_action = _public_next_action_from_report_lines(public_lines)
200
+ public_lines = [line for line in public_lines if not _is_public_next_action_line(line)]
201
+ progress_status = progress.status
202
+ count_label = _clean(progress.count_label)
203
+ lines: list[str] = [
204
+ "# Fix Wiki Report",
205
+ "",
206
+ "## Resumo",
207
+ "",
208
+ f"- {summary}",
209
+ ]
210
+ for line in public_lines:
211
+ if _looks_like_transient_mutation_line(line):
212
+ continue
213
+ if line != summary:
214
+ lines.append(f"- {line}")
215
+ if count_label:
216
+ lines.append(f"- progresso: {count_label}")
217
+ headline = _clean(progress.headline or progress.message)
218
+ detail = _clean(progress.detail)
219
+ if headline and headline != summary:
220
+ lines.append(f"- {headline}")
221
+ if detail and detail not in {summary, headline}:
222
+ lines.append(f"- detalhe: {detail}")
223
+ can_continue_now = progress.can_continue_now
224
+ current_state = _clean(snapshot.current_state)
225
+ if can_continue_now is False and not can_continue_without_human:
226
+ lines.append(f"- agora: {_waiting_line_for_state(progress_status=progress_status, current_state=current_state)}")
227
+ if can_continue_without_human and not any("continuar automaticamente" in line.casefold() for line in lines):
228
+ lines.append("- vou continuar automaticamente pela próxima etapa segura.")
229
+ if current_state == "waiting_for_external_quota":
230
+ lines.append("- estado: progresso preservado para retomada quando a cota voltar.")
231
+ if next_action:
232
+ lines.extend(["", "## Próxima Ação", "", f"- {next_action}"])
233
+ decision_packet = (
234
+ _human_decision_packet_fields(root.human_decision_packet)
235
+ if root.human_decision_packet is not None
236
+ else _HumanDecisionPacketFields()
237
+ )
238
+ if _human_decision_packet_has_data(decision_packet):
239
+ lines.extend(["", "## Decisão Necessária", ""])
240
+ lines.extend(_human_decision_packet_details(decision_packet))
241
+ objective = fix_wiki_primary_objective_summary(report)
242
+ if objective is not None:
243
+ lines.extend(
244
+ [
245
+ "",
246
+ "## Resultado Da Wiki",
247
+ "",
248
+ f"- Wiki: {objective.wiki_summary}",
249
+ f"- Mudanças reais: {objective.mutation_summary}",
250
+ f"- Grafo: {objective.graph_summary}",
251
+ f"- Notas Relacionadas: {objective.related_notes_summary}",
252
+ ]
253
+ )
254
+ return "\n".join(lines).rstrip() + "\n"
255
+
256
+
257
+ def _clean(value: object) -> str:
258
+ if value is None:
259
+ raw = ""
260
+ elif isinstance(value, str):
261
+ raw = value
262
+ else:
263
+ raw = str(value)
264
+ text = raw.replace("\r", " ").replace("\n", " ").strip()
265
+ return text.replace("|", "\\|")
266
+
267
+
268
+ def _public_safe_report_line(value: str) -> str:
269
+ prefix = "Próxima ação:"
270
+ if value.casefold().startswith(prefix.casefold()):
271
+ action = value[len(prefix):].strip()
272
+ safe_action = _public_safe_next_action(action)
273
+ return f"{prefix} {safe_action}" if safe_action else ""
274
+ return _public_safe_next_action(value) if _is_technical_graph_curation_action(value) else value
275
+
276
+
277
+ def _is_public_next_action_line(value: str) -> bool:
278
+ return value.casefold().startswith("próxima ação:")
279
+
280
+
281
+ def _public_next_action_from_report_lines(lines: list[str]) -> str:
282
+ prefix = "Próxima ação:"
283
+ for line in lines:
284
+ if line.casefold().startswith(prefix.casefold()):
285
+ return _public_safe_next_action(_clean(line[len(prefix):]))
286
+ return ""
287
+
288
+
289
+ def _public_safe_next_action(value: str) -> str:
290
+ if not value:
291
+ return ""
292
+ if _is_technical_graph_curation_action(value):
293
+ return _PUBLIC_GRAPH_CURATION_ACTION
294
+ return value
295
+
296
+
297
+ def _is_technical_graph_curation_action(value: str) -> bool:
298
+ folded = value.casefold()
299
+ return any(marker in folded for marker in _TECHNICAL_GRAPH_CURATION_MARKERS)
300
+
301
+
302
+ def _looks_like_transient_mutation_line(value: str) -> bool:
303
+ return bool(_TRANSIENT_MUTATION_LINE_RE.search(value))
304
+
305
+
306
+ def _mentions_automatic_continuation(value: str) -> bool:
307
+ return "continuar automaticamente" in value.casefold()
308
+
309
+
310
+ def _waiting_line_for_state(*, progress_status: str, current_state: str) -> str:
311
+ if progress_status == "waiting_human":
312
+ return "aguardando decisão segura antes de continuar."
313
+ if progress_status == "waiting_external" or current_state == "waiting_for_external_quota":
314
+ return "aguardando condição externa antes de continuar."
315
+ return "aguardando a próxima etapa oficial antes de continuar."
316
+
317
+
318
+ def _human_decision_packet_details(packet: _HumanDecisionPacketFields) -> list[str]:
319
+ if not _human_decision_packet_has_data(packet):
320
+ return []
321
+ out: list[str] = []
322
+ question = _clean(packet.question or packet.public_summary)
323
+ if question:
324
+ out.append(f"- pergunta: {question}")
325
+ option_labels = [
326
+ _clean(option.label or option.id)
327
+ for option in packet.options[:8]
328
+ if option.label or option.id
329
+ ]
330
+ if option_labels:
331
+ out.append(f"- opções fechadas: {', '.join(option_labels)}")
332
+ if packet.rejected_automations:
333
+ out.append("- automações rejeitadas:")
334
+ for item in packet.rejected_automations[:8]:
335
+ kind = _clean(item.kind)
336
+ reason = _clean(item.reason or item.reason_code)
337
+ out.append(f" - {kind}: {reason}")
338
+ else:
339
+ out.append("- possível_bug_ux: decisão humana sem automações rejeitadas registradas.")
340
+ if packet.resume_action:
341
+ out.append(f"- retomada: {_PUBLIC_HUMAN_DECISION_RESUME}")
342
+ return out