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.
- package/.opencode/agents/med-chat-triager.md +204 -0
- package/.opencode/agents/med-flashcard-maker.md +63 -0
- package/.opencode/agents/med-knowledge-architect.md +230 -0
- package/.opencode/agents/med-link-graph-curator.md +177 -0
- package/.opencode/agents/med-publish-guard.md +62 -0
- package/.opencode/commands/flashcards.md +25 -0
- package/.opencode/commands/mednotes/create.md +25 -0
- package/.opencode/commands/mednotes/enrich.md +27 -0
- package/.opencode/commands/mednotes/fix-wiki.md +27 -0
- package/.opencode/commands/mednotes/history.md +22 -0
- package/.opencode/commands/mednotes/link-body.md +25 -0
- package/.opencode/commands/mednotes/link-related.md +27 -0
- package/.opencode/commands/mednotes/link.md +27 -0
- package/.opencode/commands/mednotes/pdf-library.md +27 -0
- package/.opencode/commands/mednotes/process-chats.md +23 -0
- package/.opencode/commands/mednotes/setup.md +21 -0
- package/.opencode/commands/mednotes/status.md +27 -0
- package/.opencode/commands/mednotes/telemetry.md +27 -0
- package/.opencode/commands/report.md +26 -0
- package/.opencode/mednotes/AGENTS.md +57 -0
- package/.opencode/mednotes/agents/med-chat-triager.md +197 -0
- package/.opencode/mednotes/agents/med-flashcard-maker.md +56 -0
- package/.opencode/mednotes/agents/med-knowledge-architect.md +224 -0
- package/.opencode/mednotes/agents/med-link-graph-curator.md +171 -0
- package/.opencode/mednotes/agents/med-publish-guard.md +55 -0
- package/.opencode/mednotes/contracts/.gitkeep +1 -0
- package/.opencode/mednotes/contracts/agents.json +116 -0
- package/.opencode/mednotes/contracts/opencode-plugin.json +70 -0
- package/.opencode/mednotes/docs/agent-prompt-hardening.md +567 -0
- package/.opencode/mednotes/docs/agent-role-contracts.md +94 -0
- package/.opencode/mednotes/docs/anki-mcp-twenty-rules.md +214 -0
- package/.opencode/mednotes/docs/anki-templates/README.md +39 -0
- package/.opencode/mednotes/docs/anki-templates/cloze.back.html +23 -0
- package/.opencode/mednotes/docs/anki-templates/cloze.front.html +14 -0
- package/.opencode/mednotes/docs/anki-templates/qa.back.html +24 -0
- package/.opencode/mednotes/docs/anki-templates/qa.front.html +14 -0
- package/.opencode/mednotes/docs/anki-templates/style.css +182 -0
- package/.opencode/mednotes/docs/atomicity-splitting-policy.md +113 -0
- package/.opencode/mednotes/docs/extension-docs.md +40 -0
- package/.opencode/mednotes/docs/flashcard-ingestion.md +278 -0
- package/.opencode/mednotes/docs/knowledge-architect.md +208 -0
- package/.opencode/mednotes/docs/merge-policy.md +110 -0
- package/.opencode/mednotes/docs/public-vocabulary.md +104 -0
- package/.opencode/mednotes/docs/semantic-linker.md +141 -0
- package/.opencode/mednotes/docs/taxonomy-policy.md +90 -0
- package/.opencode/mednotes/docs/triage-policy.md +187 -0
- package/.opencode/mednotes/docs/vault-version-control.md +758 -0
- package/.opencode/mednotes/docs/vocabulary-db-recovery.md +58 -0
- package/.opencode/mednotes/docs/workflow-output-contract.md +779 -0
- package/.opencode/mednotes/hooks/hooks.json +79 -0
- package/.opencode/mednotes/package-lock.json +6361 -0
- package/.opencode/mednotes/package.json +15 -0
- package/.opencode/mednotes/pyproject.toml +48 -0
- package/.opencode/mednotes/scripts/bootstrap_windows_python_uv.cmd +13 -0
- package/.opencode/mednotes/scripts/bootstrap_windows_python_uv.ps1 +172 -0
- package/.opencode/mednotes/scripts/enrich_notes.py +23 -0
- package/.opencode/mednotes/scripts/full_reset_windows_python_uv.cmd +13 -0
- package/.opencode/mednotes/scripts/hooks/antigravity_hook_status.mjs +212 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/antigravity.mjs +169 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/harness_payload.mjs +103 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/opencode_plugin.mjs +341 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/opencode_user_config_sync.mjs +177 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/anki_preflight.mjs +214 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/cli.mjs +143 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/diagnostics.mjs +11 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/domain/agent_directive_core.mjs +160 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/fsm_directive.mjs +1470 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/hook_errors.mjs +120 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/retention.mjs +114 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/runtime.mjs +174 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/telemetry_capture.mjs +511 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/vault_guard.mjs +624 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook.mjs +5 -0
- package/.opencode/mednotes/scripts/mednotes/_runtime_paths.py +24 -0
- package/.opencode/mednotes/scripts/mednotes/anki_model_validator.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/capture_extension_diff.py +1562 -0
- package/.opencode/mednotes/scripts/mednotes/feedback_report.py +16 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_index.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_pipeline.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_report.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_sources.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/obsidian/README.md +6 -0
- package/.opencode/mednotes/scripts/mednotes/obsidian_note_utils.py +20 -0
- package/.opencode/mednotes/scripts/mednotes/pdf_library/cli.py +16 -0
- package/.opencode/mednotes/scripts/mednotes/project_fsm.py +229 -0
- package/.opencode/mednotes/scripts/mednotes/setup_telemetry_email.py +404 -0
- package/.opencode/mednotes/scripts/mednotes/sync_anki_twenty_rules.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/sync_opencode_user_config.py +36 -0
- package/.opencode/mednotes/scripts/mednotes/wiki/cli.py +20 -0
- package/.opencode/mednotes/scripts/mednotes/wiki_graph.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/wiki_tree.py +134 -0
- package/.opencode/mednotes/scripts/reset_windows_python_uv.ps1 +625 -0
- package/.opencode/mednotes/scripts/run_python.mjs +109 -0
- package/.opencode/mednotes/scripts/vault/vault_commit.ps1 +19 -0
- package/.opencode/mednotes/scripts/vault/vault_commit.sh +18 -0
- package/.opencode/mednotes/scripts/vault/vault_git.ps1 +19 -0
- package/.opencode/mednotes/scripts/vault/vault_git.py +3107 -0
- package/.opencode/mednotes/scripts/vault/vault_git.sh +18 -0
- package/.opencode/mednotes/scripts/vault/vault_precommit.ps1 +19 -0
- package/.opencode/mednotes/scripts/vault/vault_precommit.sh +18 -0
- package/.opencode/mednotes/skills/THIRD_PARTY_NOTICES.md +45 -0
- package/.opencode/mednotes/skills/create-medical-flashcards/SKILL.md +113 -0
- package/.opencode/mednotes/skills/create-medical-note/SKILL.md +90 -0
- package/.opencode/mednotes/skills/enrich-medical-note/SKILL.md +120 -0
- package/.opencode/mednotes/skills/fix-medical-wiki/SKILL.md +559 -0
- package/.opencode/mednotes/skills/link-medical-wiki/SKILL.md +224 -0
- package/.opencode/mednotes/skills/obsidian-cli/SKILL.md +118 -0
- package/.opencode/mednotes/skills/obsidian-markdown/SKILL.md +207 -0
- package/.opencode/mednotes/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
- package/.opencode/mednotes/skills/obsidian-markdown/references/EMBEDS.md +63 -0
- package/.opencode/mednotes/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
- package/.opencode/mednotes/skills/obsidian-ops/SKILL.md +136 -0
- package/.opencode/mednotes/skills/pdf-library/SKILL.md +45 -0
- package/.opencode/mednotes/skills/process-medical-chats/SKILL.md +246 -0
- package/.opencode/mednotes/skills/workflow-report/SKILL.md +100 -0
- package/.opencode/mednotes/src/mednotes/__init__.py +5 -0
- package/.opencode/mednotes/src/mednotes/domains/__init__.py +5 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/README.md +26 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/__init__.py +2 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/build_demo_apkg.py +177 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/contracts.py +385 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/flashcards_machine.py +522 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/fsm.py +817 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/index.py +630 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/install_models.py +445 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/model.py +359 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/obsidian_links.py +135 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/obsidian_note_utils.py +546 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/pipeline.py +580 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/report.py +510 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/sources.py +682 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/sync_rules.py +184 -0
- package/.opencode/mednotes/src/mednotes/domains/history/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/history/history_fsm.py +852 -0
- package/.opencode/mednotes/src/mednotes/domains/history/history_machine.py +453 -0
- package/.opencode/mednotes/src/mednotes/domains/setup/__init__.py +7 -0
- package/.opencode/mednotes/src/mednotes/domains/setup/setup_fsm.py +808 -0
- package/.opencode/mednotes/src/mednotes/domains/setup/setup_machine.py +973 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/README.md +64 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/api.py +668 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/batch_state.py +102 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/atomicity/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/atomicity/atomicity.py +877 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/body_link/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/body_link/body_linker.py +1562 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/effect_adapters.py +949 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/fix_wiki_runtime_adapters.py +433 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/coverage.py +413 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/graph.py +396 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/graph_fixes.py +161 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/hygiene/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/hygiene/hygiene.py +483 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/__init__.py +2 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/anchors.py +185 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/__init__.py +0 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/cache.py +223 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/config.py +131 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/download.py +224 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/frontmatter.py +59 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/insert.py +227 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/local_import.py +54 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/__init__.py +42 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/web_profiles.py +99 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/web_search.py +203 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/wikimedia.py +102 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_db_adapter.mjs +434 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_node_runtime.py +274 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_query.py +227 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/artifacts.py +605 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/canonical_merge.py +277 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/markdown_zones.py +85 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/meaning_planner.py +307 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_iter.py +67 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_merge.py +278 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_plan.py +409 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_policy.py +22 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/__init__.py +79 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/fixes.py +264 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/frontmatter.py +435 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/models.py +208 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/prompts.py +37 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/tables.py +236 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/validate.py +404 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/provenance.py +478 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/raw_chats.py +273 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/sources_backfill.py +235 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/__init__.py +10 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/anchors.py +16 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/captions.py +47 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/cli.py +179 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/cloud.py +52 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/config.py +196 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/context_packets.py +76 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/db.py +81 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/doctor.py +102 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/figure_ids.py +42 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/ingest.py +326 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/insert.py +316 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/mentions.py +57 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/ocr.py +71 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/paths.py +35 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/pdf_engine.py +77 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/schema.py +155 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/search.py +188 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/app.py +89 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/image_backend.py +29 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/state.py +65 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish.py +1139 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish_receipts.py +365 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish_recovery.py +240 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_behavior_corpus.py +2069 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_report_validation.py +4448 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_run_audit.py +852 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/architect_prompt_eval.py +341 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/body_linker_eval.py +240 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/curator_output_validation.py +175 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/curator_prompt_eval.py +865 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/triager_prompt_eval.py +1295 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/related_notes.py +1920 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/related_notes_headless.py +1186 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/plan_attestation.py +148 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_receipts.py +360 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_runtime.py +52 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_task_runner.py +2470 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/style/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/style/style.py +1952 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/subagents/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/subagents/agents.py +1767 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/alias_projection.py +331 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/link_terms.py +151 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/llm_disambiguation.py +182 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/__init__.py +116 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/audit.py +201 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/migration.py +314 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/normalize.py +72 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/policy.py +135 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/resolve.py +413 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/schema.py +157 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/status.py +137 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_bootstrap.py +509 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_curator_batch.py +1115 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_ingestion.py +632 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_map.py +930 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_recovery.py +1388 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/cli.py +6665 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/common.py +69 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/config.py +210 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/__init__.py +74 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agent_report.py +242 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agent_run_audit.py +196 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agents.py +601 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/curator.py +256 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/effect_payloads.py +519 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/happy_path.py +190 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/link_git.py +110 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/link_runtime_artifact.py +52 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/note_plan.py +75 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/paths.py +114 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/public_report.py +53 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/publish.py +111 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/raw_coverage.py +217 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes.py +136 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes_headless.py +153 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes_runtime.py +395 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/schema_registry.py +637 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/specialist.py +432 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/status.py +62 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/style_rewrite.py +568 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/vocabulary_ingestion.py +223 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_blockers.py +510 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_guardrails.py +637 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_outcomes.py +121 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_receipts.py +100 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/__main__.py +4 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/cli.py +275 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/__init__.py +2 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/candidates.py +193 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/cli.py +189 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/gemini.py +220 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/inputs.py +120 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/models.py +34 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/parsing.py +48 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/prompts.py +216 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/quality.py +54 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/reporting.py +24 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/runner.py +433 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/utils.py +39 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/vault_guard_bridge.py +17 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_context_packets.py +454 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_decision_projection.py +133 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_effects.py +1260 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_fsm.py +2768 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_machine.py +1588 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_plan.py +306 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_primary_objective.py +316 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_problem.py +153 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_receipt_evidence.py +306 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_states.py +290 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_user_report.py +342 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/health.py +6332 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_fsm.py +1119 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_git.py +638 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_machine.py +1106 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_retry_governance.py +374 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_runtime_result.py +485 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_triggers.py +183 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/linking.py +2758 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/reference_repair.py +718 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/related_notes_fsm.py +1855 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link_related/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link_related/link_related_machine.py +834 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_fsm.py +1592 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_machine.py +3097 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_primary_objective.py +28 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_runtime_result.py +185 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/performance.py +97 -0
- package/.opencode/mednotes/src/mednotes/kernel/__init__.py +6 -0
- package/.opencode/mednotes/src/mednotes/kernel/agent_directive.py +336 -0
- package/.opencode/mednotes/src/mednotes/kernel/base.py +51 -0
- package/.opencode/mednotes/src/mednotes/kernel/blockers.py +39 -0
- package/.opencode/mednotes/src/mednotes/kernel/effect_executor.py +55 -0
- package/.opencode/mednotes/src/mednotes/kernel/effect_intent.py +69 -0
- package/.opencode/mednotes/src/mednotes/kernel/effects.py +160 -0
- package/.opencode/mednotes/src/mednotes/kernel/errors.py +38 -0
- package/.opencode/mednotes/src/mednotes/kernel/fsm_event.py +35 -0
- package/.opencode/mednotes/src/mednotes/kernel/fsm_model.py +55 -0
- package/.opencode/mednotes/src/mednotes/kernel/fsm_transition_result.py +75 -0
- package/.opencode/mednotes/src/mednotes/kernel/guardrails.py +188 -0
- package/.opencode/mednotes/src/mednotes/kernel/progress.py +319 -0
- package/.opencode/mednotes/src/mednotes/kernel/public_report.py +346 -0
- package/.opencode/mednotes/src/mednotes/kernel/state_machine.py +164 -0
- package/.opencode/mednotes/src/mednotes/kernel/workflow.py +619 -0
- package/.opencode/mednotes/src/mednotes/platform/__init__.py +5 -0
- package/.opencode/mednotes/src/mednotes/platform/backup_policy.py +382 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/__init__.py +62 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/cli.py +275 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/contracts.py +83 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/core.py +4168 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/integrity.py +989 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/operational_contract.py +2293 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/telemetry.py +875 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/telemetry_config.py +65 -0
- package/.opencode/mednotes/src/mednotes/platform/opencode_runtime_config.py +182 -0
- package/.opencode/mednotes/src/mednotes/platform/paths/__init__.py +1560 -0
- package/.opencode/mednotes/src/mednotes/platform/secrets.py +89 -0
- package/.opencode/mednotes/src/mednotes/platform/user_config.py +103 -0
- package/.opencode/mednotes/src/mednotes/platform/vault_guard.py +214 -0
- package/.opencode/mednotes/uv.lock +932 -0
- package/.opencode/mednotes.generated.json +395 -0
- package/.opencode/opencode.json +31 -0
- package/.opencode/plugins/mednotes-fsm.mjs +7 -0
- package/.opencode/plugins/mednotes_hook/adapters/antigravity.mjs +169 -0
- package/.opencode/plugins/mednotes_hook/adapters/harness_payload.mjs +103 -0
- package/.opencode/plugins/mednotes_hook/adapters/opencode_plugin.mjs +341 -0
- package/.opencode/plugins/mednotes_hook/adapters/opencode_user_config_sync.mjs +177 -0
- package/.opencode/plugins/mednotes_hook/anki_preflight.mjs +214 -0
- package/.opencode/plugins/mednotes_hook/cli.mjs +143 -0
- package/.opencode/plugins/mednotes_hook/diagnostics.mjs +11 -0
- package/.opencode/plugins/mednotes_hook/domain/agent_directive_core.mjs +160 -0
- package/.opencode/plugins/mednotes_hook/fsm_directive.mjs +1470 -0
- package/.opencode/plugins/mednotes_hook/hook_errors.mjs +120 -0
- package/.opencode/plugins/mednotes_hook/retention.mjs +114 -0
- package/.opencode/plugins/mednotes_hook/runtime.mjs +174 -0
- package/.opencode/plugins/mednotes_hook/telemetry_capture.mjs +511 -0
- package/.opencode/plugins/mednotes_hook/vault_guard.mjs +624 -0
- package/AGENTS.md +57 -0
- package/README.md +194 -0
- package/adapters/antigravity/agents.json +80 -0
- package/adapters/antigravity/templates/med-chat-triager.md +214 -0
- package/adapters/antigravity/templates/med-flashcard-maker.md +72 -0
- package/adapters/antigravity/templates/med-knowledge-architect.md +241 -0
- package/adapters/antigravity/templates/med-link-graph-curator.md +187 -0
- package/adapters/antigravity/templates/med-publish-guard.md +71 -0
- package/adapters/gemini-cli/gemini-extension.json +14 -0
- package/adapters/gemini-cli/package.json +15 -0
- package/adapters/gemini-cli/pyproject.toml +48 -0
- package/bin/mednotes-opencode.mjs +155 -0
- package/contracts/agents.json +116 -0
- package/core/agents/med-chat-triager.md +197 -0
- package/core/agents/med-flashcard-maker.md +56 -0
- package/core/agents/med-knowledge-architect.md +224 -0
- package/core/agents/med-link-graph-curator.md +171 -0
- package/core/agents/med-publish-guard.md +55 -0
- package/core/commands/flashcards.toml +22 -0
- package/core/commands/mednotes/create.toml +22 -0
- package/core/commands/mednotes/enrich.toml +24 -0
- package/core/commands/mednotes/fix-wiki.toml +24 -0
- package/core/commands/mednotes/history.toml +19 -0
- package/core/commands/mednotes/link-body.toml +22 -0
- package/core/commands/mednotes/link-related.toml +24 -0
- package/core/commands/mednotes/link.toml +24 -0
- package/core/commands/mednotes/pdf-library.toml +24 -0
- package/core/commands/mednotes/process-chats.toml +20 -0
- package/core/commands/mednotes/setup.toml +18 -0
- package/core/commands/mednotes/status.toml +24 -0
- package/core/commands/mednotes/telemetry.toml +24 -0
- package/core/commands/report.toml +23 -0
- package/core/skills/THIRD_PARTY_NOTICES.md +45 -0
- package/core/skills/create-medical-flashcards/SKILL.md +113 -0
- package/core/skills/create-medical-note/SKILL.md +90 -0
- package/core/skills/enrich-medical-note/SKILL.md +120 -0
- package/core/skills/fix-medical-wiki/SKILL.md +559 -0
- package/core/skills/link-medical-wiki/SKILL.md +224 -0
- package/core/skills/obsidian-cli/SKILL.md +118 -0
- package/core/skills/obsidian-markdown/SKILL.md +207 -0
- package/core/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
- package/core/skills/obsidian-markdown/references/EMBEDS.md +63 -0
- package/core/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
- package/core/skills/obsidian-ops/SKILL.md +136 -0
- package/core/skills/pdf-library/SKILL.md +45 -0
- package/core/skills/process-medical-chats/SKILL.md +246 -0
- package/core/skills/workflow-report/SKILL.md +100 -0
- package/package.json +45 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
"""Triage-authored note plan validation (schema v2, hard cut).
|
|
2
|
+
|
|
3
|
+
v2 actions:
|
|
4
|
+
|
|
5
|
+
- ``planned_meaning``: durable semantic unit declared from the raw chat.
|
|
6
|
+
Requires ``meaning_claim`` with label/scope/boundaries/kind/evidence_summary.
|
|
7
|
+
- ``attach_to_planned_meaning``: subordinate detail that belongs to another
|
|
8
|
+
unit of the same raw chat. Carries ``target_item_id`` pointing to a sibling
|
|
9
|
+
``planned_meaning`` item.
|
|
10
|
+
- ``not_a_note``: content that should not become a Wiki note.
|
|
11
|
+
- ``needs_context``: raw chat does not support safe segmentation for this
|
|
12
|
+
unit.
|
|
13
|
+
|
|
14
|
+
Older note-plan schemas are rejected at the boundary. Runtime code does not
|
|
15
|
+
migrate or reinterpret them; the triage contract must be regenerated as v2.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import re
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
from pydantic import ValidationError as PydanticValidationError
|
|
24
|
+
|
|
25
|
+
from mednotes.domains.wiki.batch_state import NOTE_PLAN_SELF_HASH_KEYS, artifact_json_hash, clean_state_value
|
|
26
|
+
from mednotes.domains.wiki.capabilities.vocabulary.link_terms import normalize_key
|
|
27
|
+
from mednotes.domains.wiki.common import MissingPathError, ValidationError
|
|
28
|
+
from mednotes.domains.wiki.config import _path
|
|
29
|
+
from mednotes.domains.wiki.contracts.note_plan import TriageNotePlan
|
|
30
|
+
from mednotes.kernel.base import JsonObject, JsonObjectAdapter, contract_error
|
|
31
|
+
|
|
32
|
+
TRIAGE_NOTE_PLAN_SCHEMA = "medical-notes-workbench.triage-note-plan.v2"
|
|
33
|
+
TRIAGE_NOTE_PLAN_V2_SCHEMA = TRIAGE_NOTE_PLAN_SCHEMA
|
|
34
|
+
|
|
35
|
+
PLANNED_MEANING_ACTION = "planned_meaning"
|
|
36
|
+
ATTACH_TO_PLANNED_MEANING_ACTION = "attach_to_planned_meaning"
|
|
37
|
+
NOT_A_NOTE_ACTION = "not_a_note"
|
|
38
|
+
NEEDS_CONTEXT_ACTION = "needs_context"
|
|
39
|
+
|
|
40
|
+
ALLOWED_ACTIONS = {
|
|
41
|
+
PLANNED_MEANING_ACTION,
|
|
42
|
+
ATTACH_TO_PLANNED_MEANING_ACTION,
|
|
43
|
+
NOT_A_NOTE_ACTION,
|
|
44
|
+
NEEDS_CONTEXT_ACTION,
|
|
45
|
+
}
|
|
46
|
+
ALLOWED_V2_ACTIONS = ALLOWED_ACTIONS
|
|
47
|
+
|
|
48
|
+
# Closed sets used by triage-policy.md.
|
|
49
|
+
MEANING_CLAIM_KINDS = {
|
|
50
|
+
"clinical_concept",
|
|
51
|
+
"drug_concept",
|
|
52
|
+
"diagnostic_criterion",
|
|
53
|
+
"management_strategy",
|
|
54
|
+
"procedure",
|
|
55
|
+
"physiology_or_mechanism",
|
|
56
|
+
"epidemiology_or_definition",
|
|
57
|
+
}
|
|
58
|
+
MEANING_CLAIM_REQUIRED_FIELDS = ("label", "scope", "boundaries", "kind", "evidence_summary")
|
|
59
|
+
|
|
60
|
+
ATTACH_REASON_CODES = {
|
|
61
|
+
"supporting_detail",
|
|
62
|
+
"boundary_clarification",
|
|
63
|
+
"example_or_case",
|
|
64
|
+
"cross_reference",
|
|
65
|
+
}
|
|
66
|
+
NOT_A_NOTE_REASON_CODES = {
|
|
67
|
+
"administrative_chatter",
|
|
68
|
+
"repetition_no_new_information",
|
|
69
|
+
"out_of_scope_for_medical_wiki",
|
|
70
|
+
"low_value_fragment",
|
|
71
|
+
}
|
|
72
|
+
NEEDS_CONTEXT_REASON_CODES = {
|
|
73
|
+
"evidence_insufficient",
|
|
74
|
+
"multiple_topics_undifferentiated",
|
|
75
|
+
"clinical_ambiguity",
|
|
76
|
+
"language_or_encoding_blocker",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
TRIAGE_NOTE_PLAN_BATCH_KEYS = ("batch_id", "run_id", "source_artifact_hash")
|
|
80
|
+
_FENCED_JSON_RE = re.compile(r"```(?:json|JSON)?[ \t]*\n(?P<body>.*?)\n```", re.DOTALL)
|
|
81
|
+
_MOJIBAKE_RE = re.compile(r"(?:Ã.|Â.|â€|–|—|�)")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _json_object(value: object, *, message: str) -> JsonObject:
|
|
85
|
+
"""Normalize raw JSON before note-plan policy reads operational fields."""
|
|
86
|
+
|
|
87
|
+
if not isinstance(value, dict):
|
|
88
|
+
raise ValidationError(message)
|
|
89
|
+
return JsonObjectAdapter.validate_python(value)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _json_field(payload: JsonObject, key: str) -> object:
|
|
93
|
+
if key not in payload:
|
|
94
|
+
return None
|
|
95
|
+
return payload[key]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _json_str_field(payload: JsonObject, key: str) -> str:
|
|
99
|
+
value = _json_field(payload, key)
|
|
100
|
+
return str(value).strip() if value else ""
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _paths_match(left: str, right: Path) -> bool:
|
|
104
|
+
left_path = _path(left)
|
|
105
|
+
try:
|
|
106
|
+
return left_path.resolve() == right.resolve()
|
|
107
|
+
except OSError:
|
|
108
|
+
return str(left_path) == str(right)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _load_json_file(path: Path) -> JsonObject:
|
|
112
|
+
if not path.exists():
|
|
113
|
+
raise MissingPathError(f"Triage note plan not found: {path}")
|
|
114
|
+
try:
|
|
115
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
116
|
+
except json.JSONDecodeError as exc:
|
|
117
|
+
raise ValidationError(f"Invalid triage note plan JSON: {exc}") from exc
|
|
118
|
+
return _json_object(data, message="Triage note plan must be a JSON object")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _validate_meaning_claim(item_id: str, raw_claim: object) -> JsonObject:
|
|
122
|
+
raw_claim = _json_object(
|
|
123
|
+
raw_claim,
|
|
124
|
+
message=f"note_plan_meaning_claim_missing: item {item_id} requires meaning_claim object",
|
|
125
|
+
)
|
|
126
|
+
claim: JsonObject = {}
|
|
127
|
+
for field in MEANING_CLAIM_REQUIRED_FIELDS:
|
|
128
|
+
if field not in raw_claim:
|
|
129
|
+
raise ValidationError(
|
|
130
|
+
f"note_plan_meaning_claim_missing: item {item_id} meaning_claim missing {field!r}"
|
|
131
|
+
)
|
|
132
|
+
label = _json_str_field(raw_claim, "label")
|
|
133
|
+
if not label:
|
|
134
|
+
raise ValidationError(
|
|
135
|
+
f"Triage note plan item {item_id} meaning_claim.label must be non-empty"
|
|
136
|
+
)
|
|
137
|
+
scope = _json_str_field(raw_claim, "scope")
|
|
138
|
+
if not scope:
|
|
139
|
+
raise ValidationError(
|
|
140
|
+
f"Triage note plan item {item_id} meaning_claim.scope must be non-empty"
|
|
141
|
+
)
|
|
142
|
+
boundaries_raw = _json_field(raw_claim, "boundaries")
|
|
143
|
+
if not isinstance(boundaries_raw, list):
|
|
144
|
+
raise ValidationError(
|
|
145
|
+
f"Triage note plan item {item_id} meaning_claim.boundaries must be a list"
|
|
146
|
+
)
|
|
147
|
+
boundaries = [str(entry).strip() for entry in boundaries_raw if str(entry).strip()]
|
|
148
|
+
kind = _json_str_field(raw_claim, "kind")
|
|
149
|
+
if kind not in MEANING_CLAIM_KINDS:
|
|
150
|
+
raise ValidationError(
|
|
151
|
+
f"Triage note plan item {item_id} meaning_claim.kind={kind!r} is not one of "
|
|
152
|
+
f"{sorted(MEANING_CLAIM_KINDS)}"
|
|
153
|
+
)
|
|
154
|
+
evidence_summary = _json_str_field(raw_claim, "evidence_summary")
|
|
155
|
+
if not evidence_summary:
|
|
156
|
+
raise ValidationError(
|
|
157
|
+
f"Triage note plan item {item_id} meaning_claim.evidence_summary must be non-empty"
|
|
158
|
+
)
|
|
159
|
+
for field, value in {
|
|
160
|
+
"meaning_claim.label": label,
|
|
161
|
+
"meaning_claim.scope": scope,
|
|
162
|
+
"meaning_claim.kind": kind,
|
|
163
|
+
"meaning_claim.evidence_summary": evidence_summary,
|
|
164
|
+
**{f"meaning_claim.boundaries[{idx}]": boundary for idx, boundary in enumerate(boundaries)},
|
|
165
|
+
}.items():
|
|
166
|
+
_reject_mojibake(value, field=field, item_id=item_id)
|
|
167
|
+
claim["label"] = label
|
|
168
|
+
claim["scope"] = scope
|
|
169
|
+
claim["boundaries"] = boundaries
|
|
170
|
+
claim["kind"] = kind
|
|
171
|
+
claim["evidence_summary"] = evidence_summary
|
|
172
|
+
meaning_id = _json_str_field(raw_claim, "id") or _json_str_field(raw_claim, "meaning_id")
|
|
173
|
+
if meaning_id:
|
|
174
|
+
claim["id"] = meaning_id
|
|
175
|
+
return claim
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _reject_mojibake(value: str, *, field: str, item_id: str) -> None:
|
|
179
|
+
if _MOJIBAKE_RE.search(value):
|
|
180
|
+
raise ValidationError(
|
|
181
|
+
f"Triage note plan item {item_id} {field} contains mojibake/encoding corruption: {value}"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _normalized_items(items: object) -> list[JsonObject]:
|
|
186
|
+
if not isinstance(items, list) or not items:
|
|
187
|
+
raise ValidationError("Triage note plan must contain a non-empty items list")
|
|
188
|
+
|
|
189
|
+
normalized: list[JsonObject] = []
|
|
190
|
+
seen_ids: set[str] = set()
|
|
191
|
+
seen_planned_titles: dict[str, str] = {}
|
|
192
|
+
planned_ids: set[str] = set()
|
|
193
|
+
pending_attach: list[tuple[int, str, str]] = []
|
|
194
|
+
|
|
195
|
+
for index, raw_item in enumerate(items, start=1):
|
|
196
|
+
raw_item = _json_object(raw_item, message=f"Triage note plan item #{index} must be an object")
|
|
197
|
+
item_id = _json_str_field(raw_item, "id") or f"T{index:03d}"
|
|
198
|
+
action = _json_str_field(raw_item, "action")
|
|
199
|
+
if not item_id:
|
|
200
|
+
raise ValidationError(f"Triage note plan item #{index} missing id")
|
|
201
|
+
if item_id in seen_ids:
|
|
202
|
+
raise ValidationError(f"Triage note plan item id duplicated: {item_id}")
|
|
203
|
+
if action not in ALLOWED_ACTIONS:
|
|
204
|
+
raise ValidationError(
|
|
205
|
+
f"Triage note plan item {item_id} has invalid action {action!r}; "
|
|
206
|
+
f"expected one of {', '.join(sorted(ALLOWED_ACTIONS))}"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
item: JsonObject = {"id": item_id, "action": action}
|
|
210
|
+
|
|
211
|
+
if action == PLANNED_MEANING_ACTION:
|
|
212
|
+
title = _json_str_field(raw_item, "title")
|
|
213
|
+
if not title:
|
|
214
|
+
raise ValidationError(
|
|
215
|
+
f"Triage note plan item {item_id} missing title"
|
|
216
|
+
)
|
|
217
|
+
staged_title = _json_str_field(raw_item, "staged_title") or title
|
|
218
|
+
if not staged_title:
|
|
219
|
+
raise ValidationError(
|
|
220
|
+
f"Triage note plan item {item_id} missing staged_title"
|
|
221
|
+
)
|
|
222
|
+
_reject_mojibake(title, field="title", item_id=item_id)
|
|
223
|
+
_reject_mojibake(staged_title, field="staged_title", item_id=item_id)
|
|
224
|
+
staged_key = normalize_key(staged_title)
|
|
225
|
+
if staged_key in seen_planned_titles:
|
|
226
|
+
raise ValidationError(
|
|
227
|
+
"note_plan_duplicate_meaning: planned_meaning title duplicated after accent/case "
|
|
228
|
+
f"normalization: {staged_title} conflicts with "
|
|
229
|
+
f"{seen_planned_titles[staged_key]}"
|
|
230
|
+
)
|
|
231
|
+
seen_planned_titles[staged_key] = staged_title
|
|
232
|
+
item["title"] = title
|
|
233
|
+
item["staged_title"] = staged_title
|
|
234
|
+
item["meaning_claim"] = _validate_meaning_claim(item_id, _json_field(raw_item, "meaning_claim"))
|
|
235
|
+
taxonomy_hint = _json_str_field(raw_item, "taxonomy_hint")
|
|
236
|
+
if taxonomy_hint:
|
|
237
|
+
_reject_mojibake(taxonomy_hint, field="taxonomy_hint", item_id=item_id)
|
|
238
|
+
item["taxonomy_hint"] = taxonomy_hint
|
|
239
|
+
aliases = _json_field(raw_item, "aliases")
|
|
240
|
+
if isinstance(aliases, list):
|
|
241
|
+
clean_aliases = [str(alias).strip() for alias in aliases if str(alias).strip()]
|
|
242
|
+
if clean_aliases:
|
|
243
|
+
for alias_idx, alias in enumerate(clean_aliases):
|
|
244
|
+
_reject_mojibake(alias, field=f"aliases[{alias_idx}]", item_id=item_id)
|
|
245
|
+
item["aliases"] = clean_aliases
|
|
246
|
+
planned_ids.add(item_id)
|
|
247
|
+
elif action == ATTACH_TO_PLANNED_MEANING_ACTION:
|
|
248
|
+
target = _json_str_field(raw_item, "target_item_id")
|
|
249
|
+
if not target:
|
|
250
|
+
raise ValidationError(
|
|
251
|
+
f"note_plan_target_item_id_missing: item {item_id} attach action missing target_item_id"
|
|
252
|
+
)
|
|
253
|
+
reason_code = _json_str_field(raw_item, "reason_code")
|
|
254
|
+
if reason_code not in ATTACH_REASON_CODES:
|
|
255
|
+
raise ValidationError(
|
|
256
|
+
f"note_plan_reason_code_missing: item {item_id} attach reason_code={reason_code!r} "
|
|
257
|
+
f"is not one of {sorted(ATTACH_REASON_CODES)}"
|
|
258
|
+
)
|
|
259
|
+
reason = _json_str_field(raw_item, "reason")
|
|
260
|
+
if not reason:
|
|
261
|
+
raise ValidationError(
|
|
262
|
+
f"Triage note plan item {item_id} attach action missing reason"
|
|
263
|
+
)
|
|
264
|
+
item["target_item_id"] = target
|
|
265
|
+
item["reason_code"] = reason_code
|
|
266
|
+
item["reason"] = reason
|
|
267
|
+
pending_attach.append((index, item_id, target))
|
|
268
|
+
elif action == NOT_A_NOTE_ACTION:
|
|
269
|
+
reason_code = _json_str_field(raw_item, "reason_code")
|
|
270
|
+
if reason_code not in NOT_A_NOTE_REASON_CODES:
|
|
271
|
+
raise ValidationError(
|
|
272
|
+
f"note_plan_reason_code_missing: item {item_id} not_a_note reason_code={reason_code!r} "
|
|
273
|
+
f"is not one of {sorted(NOT_A_NOTE_REASON_CODES)}"
|
|
274
|
+
)
|
|
275
|
+
reason = _json_str_field(raw_item, "reason")
|
|
276
|
+
if not reason:
|
|
277
|
+
raise ValidationError(
|
|
278
|
+
f"Triage note plan item {item_id} not_a_note missing reason"
|
|
279
|
+
)
|
|
280
|
+
item["reason_code"] = reason_code
|
|
281
|
+
item["reason"] = reason
|
|
282
|
+
elif action == NEEDS_CONTEXT_ACTION:
|
|
283
|
+
reason_code = _json_str_field(raw_item, "reason_code")
|
|
284
|
+
if reason_code not in NEEDS_CONTEXT_REASON_CODES:
|
|
285
|
+
raise ValidationError(
|
|
286
|
+
f"note_plan_reason_code_missing: item {item_id} needs_context reason_code={reason_code!r} "
|
|
287
|
+
f"is not one of {sorted(NEEDS_CONTEXT_REASON_CODES)}"
|
|
288
|
+
)
|
|
289
|
+
reason = _json_str_field(raw_item, "reason")
|
|
290
|
+
if not reason:
|
|
291
|
+
raise ValidationError(
|
|
292
|
+
f"Triage note plan item {item_id} needs_context missing reason"
|
|
293
|
+
)
|
|
294
|
+
item["reason_code"] = reason_code
|
|
295
|
+
item["reason"] = reason
|
|
296
|
+
|
|
297
|
+
normalized.append(item)
|
|
298
|
+
seen_ids.add(item_id)
|
|
299
|
+
|
|
300
|
+
for _, attach_id, target in pending_attach:
|
|
301
|
+
if target not in planned_ids:
|
|
302
|
+
raise ValidationError(
|
|
303
|
+
f"note_plan_target_item_id_missing: item {attach_id} attach target_item_id={target!r} "
|
|
304
|
+
"must reference a sibling planned_meaning item"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
return normalized
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def _reject_unsupported_schema(schema_value: str) -> None:
|
|
311
|
+
raise ValidationError(
|
|
312
|
+
f"Triage note plan schema must be {TRIAGE_NOTE_PLAN_SCHEMA}, got {schema_value!r}"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def normalize_triage_note_plan(data: JsonObject, raw_file: Path) -> JsonObject:
|
|
317
|
+
data = _json_object(data, message="Triage note plan must be a JSON object")
|
|
318
|
+
schema_value = _json_str_field(data, "schema")
|
|
319
|
+
if schema_value != TRIAGE_NOTE_PLAN_SCHEMA:
|
|
320
|
+
_reject_unsupported_schema(schema_value)
|
|
321
|
+
raw_value = _json_str_field(data, "raw_file")
|
|
322
|
+
if not raw_value:
|
|
323
|
+
raise ValidationError("Triage note plan missing raw_file")
|
|
324
|
+
if not _paths_match(raw_value, raw_file):
|
|
325
|
+
raise ValidationError(f"note_plan_raw_file_mismatch: triage note plan raw_file does not match: {raw_value}")
|
|
326
|
+
if _json_field(data, "exhaustive") is not True:
|
|
327
|
+
raise ValidationError("Triage note plan must set exhaustive: true")
|
|
328
|
+
normalized = {
|
|
329
|
+
"schema": TRIAGE_NOTE_PLAN_SCHEMA,
|
|
330
|
+
"raw_file": str(raw_file),
|
|
331
|
+
"exhaustive": True,
|
|
332
|
+
"items": _normalized_items(_json_field(data, "items")),
|
|
333
|
+
}
|
|
334
|
+
for key in TRIAGE_NOTE_PLAN_BATCH_KEYS:
|
|
335
|
+
value = clean_state_value(_json_field(data, key))
|
|
336
|
+
if value:
|
|
337
|
+
normalized[key] = value
|
|
338
|
+
try:
|
|
339
|
+
return TriageNotePlan.model_validate(normalized).to_payload()
|
|
340
|
+
except PydanticValidationError as exc:
|
|
341
|
+
raise contract_error(exc, prefix="triage_note_plan.contract_invalid") from exc
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def normalize_triage_note_plan_v2(data: JsonObject, raw_file: Path) -> JsonObject:
|
|
345
|
+
return normalize_triage_note_plan(data, raw_file)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def load_triage_note_plan(path: Path, raw_file: Path) -> JsonObject:
|
|
349
|
+
return normalize_triage_note_plan(_load_json_file(path), raw_file)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def parse_triage_note_plan(value: str, raw_file: Path) -> JsonObject:
|
|
353
|
+
try:
|
|
354
|
+
data = json.loads(value)
|
|
355
|
+
except json.JSONDecodeError as exc:
|
|
356
|
+
fences = [match.group("body").strip() for match in _FENCED_JSON_RE.finditer(value)]
|
|
357
|
+
if len(fences) > 1:
|
|
358
|
+
raise ValidationError(
|
|
359
|
+
"note_plan_invalid: multiple fenced JSON blocks found; provide exactly one triage-note-plan object"
|
|
360
|
+
) from exc
|
|
361
|
+
if len(fences) == 1:
|
|
362
|
+
try:
|
|
363
|
+
data = json.loads(fences[0])
|
|
364
|
+
except json.JSONDecodeError as fenced_exc:
|
|
365
|
+
raise ValidationError(
|
|
366
|
+
f"note_plan_invalid: invalid fenced triage note plan JSON: {fenced_exc}"
|
|
367
|
+
) from fenced_exc
|
|
368
|
+
else:
|
|
369
|
+
raise ValidationError(f"Invalid triage note plan in raw frontmatter: {exc}") from exc
|
|
370
|
+
return normalize_triage_note_plan(
|
|
371
|
+
_json_object(data, message="Triage note plan in raw frontmatter must be a JSON object"),
|
|
372
|
+
raw_file,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def serialize_triage_note_plan(data: JsonObject, raw_file: Path) -> str:
|
|
377
|
+
normalized = normalize_triage_note_plan(data, raw_file)
|
|
378
|
+
return json.dumps(normalized, ensure_ascii=False, separators=(",", ":"))
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def note_plan_hash(plan: JsonObject) -> str:
|
|
382
|
+
return artifact_json_hash(plan, exclude_keys=NOTE_PLAN_SELF_HASH_KEYS)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def planned_meaning_titles(plan: JsonObject) -> set[str]:
|
|
386
|
+
validated = TriageNotePlan.model_validate(plan)
|
|
387
|
+
return {
|
|
388
|
+
title
|
|
389
|
+
for item in validated.items
|
|
390
|
+
if item.action == PLANNED_MEANING_ACTION
|
|
391
|
+
for title in [(item.staged_title or item.title or "").strip()]
|
|
392
|
+
if title
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def note_plan_summary(plan: JsonObject) -> dict[str, int]:
|
|
397
|
+
validated = TriageNotePlan.model_validate(plan)
|
|
398
|
+
counts = dict.fromkeys(ALLOWED_ACTIONS, 0)
|
|
399
|
+
for item in validated.items:
|
|
400
|
+
action = item.action
|
|
401
|
+
if action in counts:
|
|
402
|
+
counts[action] += 1
|
|
403
|
+
return {
|
|
404
|
+
"note_plan_item_count": sum(counts.values()),
|
|
405
|
+
"note_plan_planned_meaning_count": counts[PLANNED_MEANING_ACTION],
|
|
406
|
+
"note_plan_attach_count": counts[ATTACH_TO_PLANNED_MEANING_ACTION],
|
|
407
|
+
"note_plan_not_a_note_count": counts[NOT_A_NOTE_ACTION],
|
|
408
|
+
"note_plan_needs_context_count": counts[NEEDS_CONTEXT_ACTION],
|
|
409
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Shared note classification policy for Wiki workflows."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from enum import StrEnum
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from mednotes.domains.wiki.capabilities.vocabulary.link_terms import is_index_note
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NoteKind(StrEnum):
|
|
11
|
+
MEDICAL_WIKI = "medical_wiki"
|
|
12
|
+
OPERATIONAL_INDEX = "operational_index"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def classify_note(path: Path, content: str) -> NoteKind:
|
|
16
|
+
if is_index_note(path, content):
|
|
17
|
+
return NoteKind.OPERATIONAL_INDEX
|
|
18
|
+
return NoteKind.MEDICAL_WIKI
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def is_operational_index_note(path: Path, content: str) -> bool:
|
|
22
|
+
return classify_note(path, content) == NoteKind.OPERATIONAL_INDEX
|
package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/__init__.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Deterministic style contract for Wiki_Medicina notes."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from importlib import import_module
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from mednotes.domains.wiki.capabilities.notes.note_style.frontmatter import (
|
|
8
|
+
canonical_wiki_tags,
|
|
9
|
+
infer_title,
|
|
10
|
+
normalize_wiki_frontmatter,
|
|
11
|
+
parse_frontmatter,
|
|
12
|
+
raw_meta_from_file,
|
|
13
|
+
split_frontmatter,
|
|
14
|
+
wiki_frontmatter_aliases,
|
|
15
|
+
)
|
|
16
|
+
from mednotes.domains.wiki.capabilities.notes.note_style.models import (
|
|
17
|
+
NOTE_MODEL_CHANGE_RULE,
|
|
18
|
+
NOTE_MODEL_ISSUE_COVERAGE,
|
|
19
|
+
PREFERRED_H2_EMOJIS,
|
|
20
|
+
REQUIRED_SECTION_LINES,
|
|
21
|
+
REWRITE_REQUIRED_CODES,
|
|
22
|
+
STYLE_AUDIT_SCHEMA,
|
|
23
|
+
STYLE_FIX_SCHEMA,
|
|
24
|
+
STYLE_REPORT_SCHEMA,
|
|
25
|
+
WIKI_INDEX_LINK,
|
|
26
|
+
StyleIssue,
|
|
27
|
+
)
|
|
28
|
+
from mednotes.domains.wiki.capabilities.notes.note_style.prompts import rewrite_prompt
|
|
29
|
+
from mednotes.domains.wiki.capabilities.notes.note_style.tables import (
|
|
30
|
+
check_tables,
|
|
31
|
+
escape_wikilink_alias_pipes_in_tables,
|
|
32
|
+
normalize_markdown_tables,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
_LAZY_EXPORTS = {
|
|
36
|
+
"fix_note_style": ("mednotes.domains.wiki.capabilities.notes.note_style.fixes", "fix_note_style"),
|
|
37
|
+
"index_style_report": ("mednotes.domains.wiki.capabilities.notes.note_style.validate", "index_style_report"),
|
|
38
|
+
"validate_note_style": ("mednotes.domains.wiki.capabilities.notes.note_style.validate", "validate_note_style"),
|
|
39
|
+
"validate_wiki_dir": ("mednotes.domains.wiki.capabilities.notes.note_style.validate", "validate_wiki_dir"),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def __getattr__(name: str) -> Any:
|
|
44
|
+
target = _LAZY_EXPORTS.get(name)
|
|
45
|
+
if target is None:
|
|
46
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
47
|
+
module_name, attr_name = target
|
|
48
|
+
value = getattr(import_module(module_name), attr_name)
|
|
49
|
+
globals()[name] = value
|
|
50
|
+
return value
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"NOTE_MODEL_CHANGE_RULE",
|
|
55
|
+
"NOTE_MODEL_ISSUE_COVERAGE",
|
|
56
|
+
"PREFERRED_H2_EMOJIS",
|
|
57
|
+
"REQUIRED_SECTION_LINES",
|
|
58
|
+
"REWRITE_REQUIRED_CODES",
|
|
59
|
+
"STYLE_AUDIT_SCHEMA",
|
|
60
|
+
"STYLE_FIX_SCHEMA",
|
|
61
|
+
"STYLE_REPORT_SCHEMA",
|
|
62
|
+
"StyleIssue",
|
|
63
|
+
"WIKI_INDEX_LINK",
|
|
64
|
+
"check_tables",
|
|
65
|
+
"canonical_wiki_tags",
|
|
66
|
+
"escape_wikilink_alias_pipes_in_tables",
|
|
67
|
+
"fix_note_style",
|
|
68
|
+
"infer_title",
|
|
69
|
+
"index_style_report",
|
|
70
|
+
"normalize_markdown_tables",
|
|
71
|
+
"normalize_wiki_frontmatter",
|
|
72
|
+
"parse_frontmatter",
|
|
73
|
+
"raw_meta_from_file",
|
|
74
|
+
"rewrite_prompt",
|
|
75
|
+
"split_frontmatter",
|
|
76
|
+
"validate_note_style",
|
|
77
|
+
"validate_wiki_dir",
|
|
78
|
+
"wiki_frontmatter_aliases",
|
|
79
|
+
]
|