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,808 @@
|
|
|
1
|
+
"""Public projection for `/mednotes:setup` StateChart results.
|
|
2
|
+
|
|
3
|
+
This module is a projector, not a second state engine. It formats the persisted
|
|
4
|
+
`WorkflowModel` produced by `setup_machine.py` into the public FSM-first shape
|
|
5
|
+
consumed by hooks, agents and user-facing reports.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Literal, cast
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field, StrictStr, model_validator
|
|
13
|
+
from pydantic import ValidationError as PydanticValidationError
|
|
14
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
15
|
+
|
|
16
|
+
from mednotes.domains.setup.setup_machine import (
|
|
17
|
+
SETUP_WORKFLOW,
|
|
18
|
+
ConfigValidationBlockedEvent,
|
|
19
|
+
ConfigValidationCompletedEvent,
|
|
20
|
+
SetupMachine,
|
|
21
|
+
SetupState,
|
|
22
|
+
SetupVaultAdapterPayload,
|
|
23
|
+
category_for_setup_state,
|
|
24
|
+
resume_action_for_setup_state,
|
|
25
|
+
setup_event_from_vault_adapter_payload,
|
|
26
|
+
)
|
|
27
|
+
from mednotes.kernel.agent_directive import (
|
|
28
|
+
AgentDirective,
|
|
29
|
+
agent_directive_from_progress_view_model,
|
|
30
|
+
assert_agent_directive_matches_progress,
|
|
31
|
+
)
|
|
32
|
+
from mednotes.kernel.base import ContractModel, JsonObject, JsonObjectAdapter
|
|
33
|
+
from mednotes.kernel.effects import WorkflowEffectKind
|
|
34
|
+
from mednotes.kernel.fsm_event import WorkflowEventLike
|
|
35
|
+
from mednotes.kernel.fsm_model import WorkflowModel
|
|
36
|
+
from mednotes.kernel.fsm_transition_result import WorkflowTransitionResult
|
|
37
|
+
from mednotes.kernel.progress import (
|
|
38
|
+
WorkflowProgressEventType,
|
|
39
|
+
WorkflowProgressState,
|
|
40
|
+
WorkflowProgressStatus,
|
|
41
|
+
WorkflowProgressViewModel,
|
|
42
|
+
build_progress_view_model,
|
|
43
|
+
progress_state_from_view_model,
|
|
44
|
+
)
|
|
45
|
+
from mednotes.kernel.public_report import (
|
|
46
|
+
WorkflowPrimaryObjectiveSummary,
|
|
47
|
+
WorkflowPublicReport,
|
|
48
|
+
WorkflowReports,
|
|
49
|
+
assert_public_report_matches_progress,
|
|
50
|
+
public_progress_followup_line,
|
|
51
|
+
)
|
|
52
|
+
from mednotes.kernel.state_machine import (
|
|
53
|
+
WorkflowStateCategory,
|
|
54
|
+
WorkflowStateMachineSnapshot,
|
|
55
|
+
WorkflowTransition,
|
|
56
|
+
send_workflow_event,
|
|
57
|
+
)
|
|
58
|
+
from mednotes.kernel.workflow import (
|
|
59
|
+
HumanDecisionPacket,
|
|
60
|
+
ReceiptStatus,
|
|
61
|
+
VersionControlSafety,
|
|
62
|
+
WorkflowDecision,
|
|
63
|
+
WorkflowReceiptPayload,
|
|
64
|
+
assert_diagnostic_context_evidence_only,
|
|
65
|
+
diagnostic_context_evidence_only,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
SETUP_SCHEMA = "medical-notes-workbench.setup-fsm-result.v1"
|
|
69
|
+
SETUP_RECEIPT_SCHEMA = "medical-notes-workbench.setup-receipt.v1"
|
|
70
|
+
SETUP_AGENT_DIRECTIVE_FIELD = "agent_directive"
|
|
71
|
+
SETUP_FAILED_CATEGORIES = frozenset({WorkflowStateCategory.FAILED.value})
|
|
72
|
+
SETUP_RECEIPT_TERMINAL_EMPTY_ACTION_STATUSES = frozenset({WorkflowProgressStatus.COMPLETED})
|
|
73
|
+
SETUP_RECEIPT_HUMAN_DECISION_STATUSES = frozenset({WorkflowProgressStatus.WAITING_HUMAN})
|
|
74
|
+
|
|
75
|
+
SETUP_ALLOWED_ROOT_KEYS = frozenset(
|
|
76
|
+
{
|
|
77
|
+
"schema",
|
|
78
|
+
"workflow",
|
|
79
|
+
"run_id",
|
|
80
|
+
"state_machine_snapshot",
|
|
81
|
+
"progress_view_model",
|
|
82
|
+
"decision",
|
|
83
|
+
"human_decision_packet",
|
|
84
|
+
"receipt",
|
|
85
|
+
"reports",
|
|
86
|
+
"agent_directive",
|
|
87
|
+
"artifacts",
|
|
88
|
+
"version_control_safety",
|
|
89
|
+
"diagnostic_context",
|
|
90
|
+
"error_context",
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
SETUP_FORBIDDEN_ROOT_KEYS = frozenset(
|
|
94
|
+
{
|
|
95
|
+
"status",
|
|
96
|
+
"phase",
|
|
97
|
+
"blocked_reason",
|
|
98
|
+
"next_action",
|
|
99
|
+
"required_inputs",
|
|
100
|
+
"human_decision_required",
|
|
101
|
+
"workflow_exit_code",
|
|
102
|
+
"public_report",
|
|
103
|
+
"orchestration_plan",
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class _SetupPayloadProgressView(ContractModel):
|
|
109
|
+
status: StrictStr
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class _SetupPayloadSnapshot(ContractModel):
|
|
113
|
+
current_category: StrictStr
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class _SetupPayloadReceipt(ContractModel):
|
|
117
|
+
status: StrictStr
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class _SetupPayloadFields(ContractModel):
|
|
121
|
+
progress_view_model: _SetupPayloadProgressView
|
|
122
|
+
state_machine_snapshot: _SetupPayloadSnapshot
|
|
123
|
+
receipt: _SetupPayloadReceipt
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class SetupFsmResult(ContractModel):
|
|
127
|
+
"""Canonical public setup result projected from the StateChart model."""
|
|
128
|
+
|
|
129
|
+
schema_id: Literal["medical-notes-workbench.setup-fsm-result.v1"] = Field(default=SETUP_SCHEMA, alias="schema")
|
|
130
|
+
workflow: Literal["/mednotes:setup"] = SETUP_WORKFLOW
|
|
131
|
+
run_id: str = Field(min_length=1)
|
|
132
|
+
progress_state: SkipJsonSchema[WorkflowProgressState]
|
|
133
|
+
progress_view_model: WorkflowProgressViewModel
|
|
134
|
+
state_machine_snapshot: WorkflowStateMachineSnapshot
|
|
135
|
+
decision: WorkflowDecision | None = None
|
|
136
|
+
human_decision_packet: HumanDecisionPacket | None = None
|
|
137
|
+
receipt: WorkflowReceiptPayload
|
|
138
|
+
reports: WorkflowReports
|
|
139
|
+
agent_directive: JsonObject
|
|
140
|
+
artifacts: JsonObject = Field(default_factory=dict)
|
|
141
|
+
version_control_safety: VersionControlSafety
|
|
142
|
+
diagnostic_context: JsonObject = Field(default_factory=dict)
|
|
143
|
+
error_context: JsonObject = Field(default_factory=dict)
|
|
144
|
+
|
|
145
|
+
@model_validator(mode="before")
|
|
146
|
+
@classmethod
|
|
147
|
+
def _hydrate_progress_state_from_public_payload(cls, value: object) -> object:
|
|
148
|
+
"""Accept public payloads where progress_state is intentionally hidden."""
|
|
149
|
+
|
|
150
|
+
if not isinstance(value, dict) or "progress_state" in value or "progress_view_model" not in value:
|
|
151
|
+
return value
|
|
152
|
+
hydrated = dict(value)
|
|
153
|
+
progress_view = WorkflowProgressViewModel.model_validate(value["progress_view_model"])
|
|
154
|
+
hydrated["progress_state"] = progress_state_from_view_model(progress_view).to_payload()
|
|
155
|
+
return hydrated
|
|
156
|
+
|
|
157
|
+
@model_validator(mode="after")
|
|
158
|
+
def _progress_view_model_matches_state(self) -> SetupFsmResult:
|
|
159
|
+
expected = build_progress_view_model(self.progress_state).to_payload()
|
|
160
|
+
if self.progress_view_model.to_payload() != expected:
|
|
161
|
+
raise ValueError("progress_view_model must match progress_state")
|
|
162
|
+
return self
|
|
163
|
+
|
|
164
|
+
def to_payload(self) -> JsonObject:
|
|
165
|
+
payload: JsonObject = {
|
|
166
|
+
"schema": self.schema_id,
|
|
167
|
+
"workflow": self.workflow,
|
|
168
|
+
"run_id": self.run_id,
|
|
169
|
+
"state_machine_snapshot": self.state_machine_snapshot.to_payload(),
|
|
170
|
+
"progress_view_model": self.progress_view_model.to_payload(),
|
|
171
|
+
"decision": self.decision.to_payload() if self.decision is not None else None,
|
|
172
|
+
"human_decision_packet": self.human_decision_packet.to_payload()
|
|
173
|
+
if self.human_decision_packet is not None
|
|
174
|
+
else None,
|
|
175
|
+
"receipt": self.receipt.to_payload(),
|
|
176
|
+
"reports": self.reports.to_payload(),
|
|
177
|
+
"agent_directive": dict(self.agent_directive),
|
|
178
|
+
"artifacts": dict(self.artifacts),
|
|
179
|
+
"version_control_safety": self.version_control_safety.to_payload(),
|
|
180
|
+
"error_context": dict(self.error_context),
|
|
181
|
+
}
|
|
182
|
+
if self.diagnostic_context:
|
|
183
|
+
payload["diagnostic_context"] = dict(self.diagnostic_context)
|
|
184
|
+
payload = JsonObjectAdapter.validate_python(payload)
|
|
185
|
+
assert_setup_fsm_payload(payload)
|
|
186
|
+
return payload
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def build_setup_fsm_result(
|
|
190
|
+
model: WorkflowModel,
|
|
191
|
+
*,
|
|
192
|
+
version_control_safety: VersionControlSafety | None = None,
|
|
193
|
+
error_context: JsonObject | None = None,
|
|
194
|
+
) -> SetupFsmResult:
|
|
195
|
+
"""Project a persisted setup machine model into the public FSM contract."""
|
|
196
|
+
|
|
197
|
+
_validate_setup_model(model)
|
|
198
|
+
state = SetupState(model.state)
|
|
199
|
+
category = category_for_setup_state(state)
|
|
200
|
+
progress_state = _progress_state(model, state, category)
|
|
201
|
+
progress_view_model = build_progress_view_model(progress_state)
|
|
202
|
+
snapshot = _snapshot(model, state, category)
|
|
203
|
+
receipt = _receipt(
|
|
204
|
+
model,
|
|
205
|
+
progress_state=progress_state,
|
|
206
|
+
progress_view_model=progress_view_model,
|
|
207
|
+
snapshot=snapshot,
|
|
208
|
+
version_control_safety=version_control_safety or _default_version_control_safety(),
|
|
209
|
+
)
|
|
210
|
+
reports_model = _reports(state, progress_state)
|
|
211
|
+
public_report = reports_model.public_report
|
|
212
|
+
diagnostic_context = _diagnostic_context(model, state, category)
|
|
213
|
+
directive = agent_directive_from_progress_view_model(
|
|
214
|
+
progress_view_model,
|
|
215
|
+
schema="medical-notes-workbench.agent-directive.v1",
|
|
216
|
+
reason=_reason_code(model, state),
|
|
217
|
+
effects=model.pending_effects,
|
|
218
|
+
blockers=_blockers_for(category, state),
|
|
219
|
+
resume=progress_state.resume_action,
|
|
220
|
+
report_requires=["setup_state", "recovery_route"],
|
|
221
|
+
summary=public_report.summary_text(),
|
|
222
|
+
instructions=_agent_instructions(category),
|
|
223
|
+
).to_payload()
|
|
224
|
+
return SetupFsmResult(
|
|
225
|
+
run_id=model.run_id,
|
|
226
|
+
progress_state=progress_state,
|
|
227
|
+
progress_view_model=progress_view_model,
|
|
228
|
+
state_machine_snapshot=snapshot,
|
|
229
|
+
decision=model.last_transition.decision if model.last_transition is not None else None,
|
|
230
|
+
human_decision_packet=model.last_transition.human_decision_packet if model.last_transition is not None else None,
|
|
231
|
+
receipt=receipt,
|
|
232
|
+
reports=reports_model,
|
|
233
|
+
agent_directive=directive,
|
|
234
|
+
version_control_safety=receipt.version_control_safety,
|
|
235
|
+
diagnostic_context=diagnostic_context,
|
|
236
|
+
error_context=error_context or _setup_error_context(model, state, category, progress_state),
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _setup_error_context(
|
|
241
|
+
model: WorkflowModel,
|
|
242
|
+
state: SetupState,
|
|
243
|
+
category: WorkflowStateCategory,
|
|
244
|
+
progress_state: WorkflowProgressState,
|
|
245
|
+
) -> JsonObject:
|
|
246
|
+
"""Produce a recoverable failure context for terminal setup failures."""
|
|
247
|
+
|
|
248
|
+
if category != WorkflowStateCategory.FAILED:
|
|
249
|
+
return {}
|
|
250
|
+
reason_code = _reason_code(model, state)
|
|
251
|
+
next_action = (
|
|
252
|
+
progress_state.resume_action
|
|
253
|
+
or "Revisar a política/ambiente informado e retomar /mednotes:setup pela rota oficial."
|
|
254
|
+
)
|
|
255
|
+
return {
|
|
256
|
+
"blocked_reason": reason_code,
|
|
257
|
+
"root_cause": reason_code,
|
|
258
|
+
"affected_artifact": "mednotes_setup_environment",
|
|
259
|
+
"error_summary": "O setup terminou em falha antes de preparar o Workbench.",
|
|
260
|
+
"suggested_fix": next_action,
|
|
261
|
+
"next_action": next_action,
|
|
262
|
+
"retry_scope": "setup_environment",
|
|
263
|
+
"human_decision_required": False,
|
|
264
|
+
"missing_inputs": [],
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def setup_fsm_payload_from_model(model: WorkflowModel) -> JsonObject:
|
|
269
|
+
"""Convenience boundary for callers that need the JSON payload directly."""
|
|
270
|
+
|
|
271
|
+
return build_setup_fsm_result(model).to_payload()
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class _SetupConfigValidationPayload(BaseModel):
|
|
275
|
+
"""Typed adapter lens for the private setup config validation receipt."""
|
|
276
|
+
|
|
277
|
+
model_config = ConfigDict(extra="forbid", strict=True)
|
|
278
|
+
|
|
279
|
+
schema_id: Literal["medical-notes-workbench.setup-config-validation.v1"] = Field(alias="schema")
|
|
280
|
+
status: Literal["valid", "blocked"]
|
|
281
|
+
config_path: str = Field(min_length=1)
|
|
282
|
+
reason_code: Literal["config_encoding_invalid"] | None = None
|
|
283
|
+
|
|
284
|
+
@model_validator(mode="after")
|
|
285
|
+
def _blocked_status_requires_reason(self) -> _SetupConfigValidationPayload:
|
|
286
|
+
match self.status:
|
|
287
|
+
case "blocked":
|
|
288
|
+
if self.reason_code != "config_encoding_invalid":
|
|
289
|
+
raise ValueError("blocked setup config validation requires reason_code=config_encoding_invalid")
|
|
290
|
+
case "valid":
|
|
291
|
+
if self.reason_code is not None:
|
|
292
|
+
raise ValueError("valid setup config validation must not carry a blocker reason")
|
|
293
|
+
return self
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def setup_fsm_payload_from_vault_payload(payload: object, *, run_id: str = "setup-vault") -> JsonObject:
|
|
297
|
+
"""Convert the vault setup adapter result into the public setup FSM payload."""
|
|
298
|
+
|
|
299
|
+
raw = SetupVaultAdapterPayload.model_validate(payload)
|
|
300
|
+
initial_state, event = setup_event_from_vault_adapter_payload(raw, run_id=run_id)
|
|
301
|
+
model = WorkflowModel.start(workflow=SETUP_WORKFLOW, run_id=run_id, initial_state=initial_state.value)
|
|
302
|
+
# `setup_event_from_vault_adapter_payload` returns the closed discriminated
|
|
303
|
+
# setup event union; every member has the StateChart `name` field required by
|
|
304
|
+
# the kernel protocol even though the abstract base intentionally does not.
|
|
305
|
+
send_workflow_event(
|
|
306
|
+
SetupMachine(model=model, state_field=WorkflowModel.STATECHART_STATE_FIELD),
|
|
307
|
+
cast(WorkflowEventLike, event),
|
|
308
|
+
)
|
|
309
|
+
return build_setup_fsm_result(model).to_payload()
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def setup_fsm_payload_from_config_validation_payload(
|
|
313
|
+
payload: object,
|
|
314
|
+
*,
|
|
315
|
+
run_id: str = "setup-config-validation",
|
|
316
|
+
) -> JsonObject:
|
|
317
|
+
"""Convert the config validation adapter receipt into a setup FSM payload."""
|
|
318
|
+
|
|
319
|
+
raw = _SetupConfigValidationPayload.model_validate(payload)
|
|
320
|
+
model = WorkflowModel.start(
|
|
321
|
+
workflow=SETUP_WORKFLOW,
|
|
322
|
+
run_id=run_id,
|
|
323
|
+
initial_state=SetupState.CONFIG_VALIDATION_RUNNING.value,
|
|
324
|
+
)
|
|
325
|
+
machine = SetupMachine(model=model, state_field=WorkflowModel.STATECHART_STATE_FIELD)
|
|
326
|
+
match raw.status:
|
|
327
|
+
case "valid":
|
|
328
|
+
send_workflow_event(
|
|
329
|
+
machine,
|
|
330
|
+
ConfigValidationCompletedEvent(
|
|
331
|
+
workflow=SETUP_WORKFLOW,
|
|
332
|
+
run_id=run_id,
|
|
333
|
+
current_state=SetupState.CONFIG_VALIDATION_RUNNING.value,
|
|
334
|
+
config_path=raw.config_path,
|
|
335
|
+
),
|
|
336
|
+
)
|
|
337
|
+
case "blocked":
|
|
338
|
+
send_workflow_event(
|
|
339
|
+
machine,
|
|
340
|
+
ConfigValidationBlockedEvent(
|
|
341
|
+
workflow=SETUP_WORKFLOW,
|
|
342
|
+
run_id=run_id,
|
|
343
|
+
current_state=SetupState.CONFIG_VALIDATION_RUNNING.value,
|
|
344
|
+
reason_code="config_encoding_invalid",
|
|
345
|
+
config_path=raw.config_path,
|
|
346
|
+
audit_evidence={"config_path": raw.config_path},
|
|
347
|
+
),
|
|
348
|
+
)
|
|
349
|
+
return build_setup_fsm_result(model).to_payload()
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def assert_setup_fsm_payload(payload: JsonObject) -> None:
|
|
353
|
+
"""Reject stale setup payload shapes before hooks or agents consume them."""
|
|
354
|
+
|
|
355
|
+
required = SETUP_ALLOWED_ROOT_KEYS - {"diagnostic_context"}
|
|
356
|
+
missing = required - set(payload)
|
|
357
|
+
if missing:
|
|
358
|
+
raise ValueError("setup FSM payload missing canonical root keys: " + ", ".join(sorted(missing)))
|
|
359
|
+
forbidden = SETUP_FORBIDDEN_ROOT_KEYS & set(payload)
|
|
360
|
+
if forbidden:
|
|
361
|
+
raise ValueError("setup FSM payload contains forbidden root keys: " + ", ".join(sorted(forbidden)))
|
|
362
|
+
extra = set(payload) - SETUP_ALLOWED_ROOT_KEYS
|
|
363
|
+
if extra:
|
|
364
|
+
raise ValueError("setup FSM payload contains unknown root keys: " + ", ".join(sorted(extra)))
|
|
365
|
+
diagnostic_context = payload["diagnostic_context"] if "diagnostic_context" in payload else {}
|
|
366
|
+
assert_diagnostic_context_evidence_only(diagnostic_context)
|
|
367
|
+
if isinstance(diagnostic_context, dict) and "agent_directive" in diagnostic_context:
|
|
368
|
+
raise ValueError("setup FSM diagnostic_context must not contain agent_directive")
|
|
369
|
+
fields = _setup_payload_fields(payload)
|
|
370
|
+
if fields.progress_view_model.status != fields.state_machine_snapshot.current_category:
|
|
371
|
+
raise ValueError("setup FSM status must match state_machine_snapshot category")
|
|
372
|
+
if fields.receipt.status != fields.progress_view_model.status:
|
|
373
|
+
raise ValueError("setup FSM receipt status must match progress_view_model")
|
|
374
|
+
if fields.progress_view_model.status in SETUP_FAILED_CATEGORIES and not payload["error_context"]:
|
|
375
|
+
raise ValueError("setup FSM failed payload requires error_context")
|
|
376
|
+
reports_model = WorkflowReports.model_validate(payload["reports"])
|
|
377
|
+
snapshot = WorkflowStateMachineSnapshot.model_validate(payload["state_machine_snapshot"])
|
|
378
|
+
progress_view_model = WorkflowProgressViewModel.model_validate(payload["progress_view_model"])
|
|
379
|
+
assert_public_report_matches_progress(
|
|
380
|
+
reports_model.public_report,
|
|
381
|
+
workflow=SETUP_WORKFLOW,
|
|
382
|
+
run_id=str(payload["run_id"]),
|
|
383
|
+
progress_view_model=progress_view_model,
|
|
384
|
+
label="setup FSM",
|
|
385
|
+
)
|
|
386
|
+
assert_agent_directive_matches_progress(
|
|
387
|
+
AgentDirective.model_validate(payload[SETUP_AGENT_DIRECTIVE_FIELD]),
|
|
388
|
+
workflow=SETUP_WORKFLOW,
|
|
389
|
+
run_id=str(payload["run_id"]),
|
|
390
|
+
progress_view_model=progress_view_model,
|
|
391
|
+
snapshot=snapshot,
|
|
392
|
+
allowed_effect_kinds=_allowed_agent_effect_kinds_for_category(snapshot.current_category),
|
|
393
|
+
label="setup FSM",
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _allowed_agent_effect_kinds_for_category(category: WorkflowStateCategory) -> set[WorkflowEffectKind]:
|
|
398
|
+
"""Setup currently recovers by human/runtime action, not hidden effects."""
|
|
399
|
+
|
|
400
|
+
match category:
|
|
401
|
+
case WorkflowStateCategory.WAITING_AGENT:
|
|
402
|
+
return {WorkflowEffectKind.RUN_SUBWORKFLOW}
|
|
403
|
+
case WorkflowStateCategory.WAITING_EXTERNAL:
|
|
404
|
+
return {WorkflowEffectKind.WAIT_EXTERNAL}
|
|
405
|
+
case WorkflowStateCategory.WAITING_HUMAN:
|
|
406
|
+
return {WorkflowEffectKind.ASK_HUMAN}
|
|
407
|
+
case _:
|
|
408
|
+
return set()
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _setup_payload_fields(payload: JsonObject) -> _SetupPayloadFields:
|
|
412
|
+
raw_fields: JsonObject = {
|
|
413
|
+
"progress_view_model": _json_object_subset(payload, "progress_view_model", ("status",)),
|
|
414
|
+
"state_machine_snapshot": _json_object_subset(payload, "state_machine_snapshot", ("current_category",)),
|
|
415
|
+
"receipt": _json_object_subset(payload, "receipt", ("status",)),
|
|
416
|
+
}
|
|
417
|
+
try:
|
|
418
|
+
return _SetupPayloadFields.model_validate(raw_fields)
|
|
419
|
+
except PydanticValidationError as exc:
|
|
420
|
+
first = exc.errors()[0] if exc.errors() else {}
|
|
421
|
+
loc = ".".join(str(part) for part in first.get("loc", ())) or "$"
|
|
422
|
+
msg = str(first.get("msg") or str(exc))
|
|
423
|
+
raise ValueError(f"setup FSM payload invalid: {loc}: {msg}") from exc
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def _json_object_subset(payload: JsonObject, key: str, fields: tuple[str, ...]) -> JsonObject:
|
|
427
|
+
value = payload[key]
|
|
428
|
+
if not isinstance(value, dict):
|
|
429
|
+
raise ValueError(f"setup FSM payload {key} must be an object")
|
|
430
|
+
subset = {field: value[field] for field in fields if field in value}
|
|
431
|
+
return JsonObjectAdapter.validate_python(subset)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def _validate_setup_model(model: WorkflowModel) -> None:
|
|
435
|
+
if model.workflow != SETUP_WORKFLOW:
|
|
436
|
+
raise ValueError(f"setup FSM projector requires workflow={SETUP_WORKFLOW}")
|
|
437
|
+
SetupState(model.state)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def _progress_state(
|
|
441
|
+
model: WorkflowModel,
|
|
442
|
+
state: SetupState,
|
|
443
|
+
category: WorkflowStateCategory,
|
|
444
|
+
) -> WorkflowProgressState:
|
|
445
|
+
status = _progress_status(category)
|
|
446
|
+
return WorkflowProgressState(
|
|
447
|
+
workflow=SETUP_WORKFLOW,
|
|
448
|
+
run_id=model.run_id,
|
|
449
|
+
state=state.value,
|
|
450
|
+
phase=_phase_for_state(state),
|
|
451
|
+
event_type=_event_type_for_status(status),
|
|
452
|
+
message=_message_for_state(state),
|
|
453
|
+
status=status,
|
|
454
|
+
resume_action=_resume_action(model, state),
|
|
455
|
+
resume_supported=status in {
|
|
456
|
+
WorkflowProgressStatus.WAITING_AGENT,
|
|
457
|
+
WorkflowProgressStatus.WAITING_EXTERNAL,
|
|
458
|
+
WorkflowProgressStatus.WAITING_HUMAN,
|
|
459
|
+
},
|
|
460
|
+
can_continue_now=status
|
|
461
|
+
in {
|
|
462
|
+
WorkflowProgressStatus.RUNNING,
|
|
463
|
+
WorkflowProgressStatus.WAITING_AGENT,
|
|
464
|
+
},
|
|
465
|
+
decision=model.last_transition.decision.to_payload()
|
|
466
|
+
if model.last_transition is not None and model.last_transition.decision is not None
|
|
467
|
+
else None,
|
|
468
|
+
technical_context={"reason": _reason_code(model, state), "category": category.value},
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _snapshot(
|
|
473
|
+
model: WorkflowModel,
|
|
474
|
+
state: SetupState,
|
|
475
|
+
category: WorkflowStateCategory,
|
|
476
|
+
) -> WorkflowStateMachineSnapshot:
|
|
477
|
+
return WorkflowStateMachineSnapshot(
|
|
478
|
+
workflow=SETUP_WORKFLOW,
|
|
479
|
+
run_id=model.run_id,
|
|
480
|
+
current_state=state.value,
|
|
481
|
+
current_category=category,
|
|
482
|
+
transitions=[_snapshot_transition(transition) for transition in model.transition_log],
|
|
483
|
+
metadata={"reason": _reason_code(model, state)},
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _snapshot_transition(transition: WorkflowTransitionResult) -> WorkflowTransition:
|
|
488
|
+
return WorkflowTransition(
|
|
489
|
+
workflow=transition.workflow,
|
|
490
|
+
from_state=transition.from_state,
|
|
491
|
+
to_state=transition.to_state,
|
|
492
|
+
to_category=category_for_setup_state(SetupState(transition.to_state)),
|
|
493
|
+
trigger=transition.trigger,
|
|
494
|
+
effects=list(transition.effects),
|
|
495
|
+
decision=transition.decision,
|
|
496
|
+
resume_action=transition.resume_action,
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def _receipt(
|
|
501
|
+
model: WorkflowModel,
|
|
502
|
+
*,
|
|
503
|
+
progress_state: WorkflowProgressState,
|
|
504
|
+
progress_view_model: WorkflowProgressViewModel,
|
|
505
|
+
snapshot: WorkflowStateMachineSnapshot,
|
|
506
|
+
version_control_safety: VersionControlSafety,
|
|
507
|
+
) -> WorkflowReceiptPayload:
|
|
508
|
+
return WorkflowReceiptPayload(
|
|
509
|
+
schema=SETUP_RECEIPT_SCHEMA,
|
|
510
|
+
workflow=SETUP_WORKFLOW,
|
|
511
|
+
run_id=model.run_id,
|
|
512
|
+
status=_receipt_status(progress_state.status),
|
|
513
|
+
mutated=False,
|
|
514
|
+
next_action="" if progress_state.status in SETUP_RECEIPT_TERMINAL_EMPTY_ACTION_STATUSES else progress_state.resume_action,
|
|
515
|
+
human_decision_required=progress_state.status in SETUP_RECEIPT_HUMAN_DECISION_STATUSES,
|
|
516
|
+
human_decision_packet=model.last_transition.human_decision_packet if model.last_transition is not None else None,
|
|
517
|
+
version_control_safety=version_control_safety,
|
|
518
|
+
progress_state=progress_state,
|
|
519
|
+
progress_view_model=progress_view_model,
|
|
520
|
+
state_machine_snapshot=snapshot,
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def _receipt_status(status: WorkflowProgressStatus) -> ReceiptStatus:
|
|
525
|
+
match status:
|
|
526
|
+
case WorkflowProgressStatus.RUNNING:
|
|
527
|
+
return "running"
|
|
528
|
+
case WorkflowProgressStatus.COMPLETED:
|
|
529
|
+
return "completed"
|
|
530
|
+
case WorkflowProgressStatus.COMPLETED_WITH_WARNINGS:
|
|
531
|
+
return "completed_with_warnings"
|
|
532
|
+
case WorkflowProgressStatus.WAITING_AGENT:
|
|
533
|
+
return "waiting_agent"
|
|
534
|
+
case WorkflowProgressStatus.WAITING_EXTERNAL:
|
|
535
|
+
return "waiting_external"
|
|
536
|
+
case WorkflowProgressStatus.WAITING_HUMAN:
|
|
537
|
+
return "waiting_human"
|
|
538
|
+
case WorkflowProgressStatus.FAILED:
|
|
539
|
+
return "failed"
|
|
540
|
+
case WorkflowProgressStatus.BLOCKED:
|
|
541
|
+
return "blocked"
|
|
542
|
+
case _:
|
|
543
|
+
return "blocked"
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
def _progress_status(category: WorkflowStateCategory) -> WorkflowProgressStatus:
|
|
547
|
+
match category:
|
|
548
|
+
case WorkflowStateCategory.PREPARING | WorkflowStateCategory.RUNNING:
|
|
549
|
+
return WorkflowProgressStatus.RUNNING
|
|
550
|
+
case WorkflowStateCategory.WAITING_AGENT:
|
|
551
|
+
return WorkflowProgressStatus.WAITING_AGENT
|
|
552
|
+
case WorkflowStateCategory.WAITING_EXTERNAL:
|
|
553
|
+
return WorkflowProgressStatus.WAITING_EXTERNAL
|
|
554
|
+
case WorkflowStateCategory.WAITING_HUMAN:
|
|
555
|
+
return WorkflowProgressStatus.WAITING_HUMAN
|
|
556
|
+
case WorkflowStateCategory.BLOCKED:
|
|
557
|
+
return WorkflowProgressStatus.BLOCKED
|
|
558
|
+
case WorkflowStateCategory.FAILED:
|
|
559
|
+
return WorkflowProgressStatus.FAILED
|
|
560
|
+
case WorkflowStateCategory.COMPLETED:
|
|
561
|
+
return WorkflowProgressStatus.COMPLETED
|
|
562
|
+
case WorkflowStateCategory.COMPLETED_WITH_WARNINGS:
|
|
563
|
+
return WorkflowProgressStatus.COMPLETED_WITH_WARNINGS
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def _event_type_for_status(status: WorkflowProgressStatus) -> WorkflowProgressEventType:
|
|
567
|
+
match status:
|
|
568
|
+
case WorkflowProgressStatus.COMPLETED | WorkflowProgressStatus.COMPLETED_WITH_WARNINGS:
|
|
569
|
+
return WorkflowProgressEventType.WORKFLOW_COMPLETED
|
|
570
|
+
case WorkflowProgressStatus.FAILED:
|
|
571
|
+
return WorkflowProgressEventType.WORKFLOW_FAILED
|
|
572
|
+
case WorkflowProgressStatus.WAITING_EXTERNAL:
|
|
573
|
+
return WorkflowProgressEventType.EXTERNAL_WAIT_STARTED
|
|
574
|
+
case WorkflowProgressStatus.WAITING_HUMAN:
|
|
575
|
+
return WorkflowProgressEventType.DECISION_EMITTED
|
|
576
|
+
case _:
|
|
577
|
+
return WorkflowProgressEventType.STATE_ENTERED
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
def _phase_for_state(state: SetupState) -> str:
|
|
581
|
+
match state:
|
|
582
|
+
case SetupState.CHECKING_ENVIRONMENT | SetupState.PYTHON_ENV_REQUIRED | SetupState.PYTHON_ENV_READY:
|
|
583
|
+
return "environment"
|
|
584
|
+
case (
|
|
585
|
+
SetupState.PATHS_REQUIRED
|
|
586
|
+
| SetupState.PATHS_CONFIGURED
|
|
587
|
+
| SetupState.CONFIG_VALIDATION_RUNNING
|
|
588
|
+
| SetupState.CONFIG_ENCODING_REQUIRED
|
|
589
|
+
):
|
|
590
|
+
return "paths"
|
|
591
|
+
case (
|
|
592
|
+
SetupState.OBSIDIAN_NOT_READY
|
|
593
|
+
| SetupState.MARKDOWN_RUNTIME_REQUIRED
|
|
594
|
+
| SetupState.MARKDOWN_INDEX_REQUIRED
|
|
595
|
+
| SetupState.MARKDOWN_RUNTIME_READY
|
|
596
|
+
):
|
|
597
|
+
return "markdown_runtime"
|
|
598
|
+
case SetupState.VAULT_GUARD_REQUIRED | SetupState.VAULT_LOCAL_READY:
|
|
599
|
+
return "vault_guard"
|
|
600
|
+
case (
|
|
601
|
+
SetupState.LOCAL_READY_GITHUB_PENDING
|
|
602
|
+
| SetupState.GITHUB_LOGIN_REQUIRED
|
|
603
|
+
| SetupState.GITHUB_REMOTE_CONFIRMATION_REQUIRED
|
|
604
|
+
| SetupState.GITHUB_REMOTE_AMBIGUOUS
|
|
605
|
+
):
|
|
606
|
+
return "remote_backup"
|
|
607
|
+
case SetupState.BRANCH_CONFIRMATION_REQUIRED:
|
|
608
|
+
return "branch_policy"
|
|
609
|
+
case SetupState.POLICY_DECISION_REQUIRED:
|
|
610
|
+
return "policy"
|
|
611
|
+
case SetupState.READY:
|
|
612
|
+
return "ready"
|
|
613
|
+
case SetupState.FAILED:
|
|
614
|
+
return "failed"
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
def _message_for_state(state: SetupState) -> str:
|
|
618
|
+
match state:
|
|
619
|
+
case SetupState.PATHS_REQUIRED:
|
|
620
|
+
return "Setup aguardando os caminhos oficiais da Wiki e dos raw chats."
|
|
621
|
+
case SetupState.PYTHON_ENV_REQUIRED:
|
|
622
|
+
return "Setup precisa preparar Python/uv pela rota oficial."
|
|
623
|
+
case SetupState.CONFIG_VALIDATION_RUNNING:
|
|
624
|
+
return "Setup precisa validar a configuracao antes de continuar."
|
|
625
|
+
case SetupState.CONFIG_ENCODING_REQUIRED:
|
|
626
|
+
return "Setup precisa reparar a configuracao em UTF-8."
|
|
627
|
+
case SetupState.OBSIDIAN_NOT_READY:
|
|
628
|
+
return "Setup aguardando Obsidian/plugin ficar pronto."
|
|
629
|
+
case SetupState.MARKDOWN_RUNTIME_REQUIRED:
|
|
630
|
+
return "Setup precisa reconstruir o runtime Markdown."
|
|
631
|
+
case SetupState.MARKDOWN_INDEX_REQUIRED:
|
|
632
|
+
return "Setup precisa reconstruir o indice Markdown."
|
|
633
|
+
case SetupState.VAULT_GUARD_REQUIRED:
|
|
634
|
+
return "Setup precisa configurar a protecao do vault."
|
|
635
|
+
case SetupState.LOCAL_READY_GITHUB_PENDING:
|
|
636
|
+
return "Protecao local pronta; backup online ainda pendente."
|
|
637
|
+
case SetupState.GITHUB_LOGIN_REQUIRED:
|
|
638
|
+
return "Setup aguardando decisao sobre login do GitHub."
|
|
639
|
+
case SetupState.GITHUB_REMOTE_CONFIRMATION_REQUIRED:
|
|
640
|
+
return "Setup aguardando confirmacao para criar backup privado."
|
|
641
|
+
case SetupState.GITHUB_REMOTE_AMBIGUOUS:
|
|
642
|
+
return "Setup aguardando escolha do remote GitHub."
|
|
643
|
+
case SetupState.BRANCH_CONFIRMATION_REQUIRED:
|
|
644
|
+
return "Setup aguardando confirmacao para ajustar a branch principal."
|
|
645
|
+
case SetupState.POLICY_DECISION_REQUIRED:
|
|
646
|
+
return "Setup aguardando decisao de politica do ambiente."
|
|
647
|
+
case SetupState.READY:
|
|
648
|
+
return "Setup concluido; workflows dependentes podem retomar."
|
|
649
|
+
case SetupState.FAILED:
|
|
650
|
+
return "Setup falhou antes de liberar workflows dependentes."
|
|
651
|
+
case _:
|
|
652
|
+
return "Setup em andamento."
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def _reports(state: SetupState, progress_state: WorkflowProgressState) -> WorkflowReports:
|
|
656
|
+
summary = _message_for_state(state)
|
|
657
|
+
public_lines = [summary]
|
|
658
|
+
followup_line = public_progress_followup_line(progress_state)
|
|
659
|
+
if followup_line:
|
|
660
|
+
public_lines.append(followup_line)
|
|
661
|
+
public_report = WorkflowPublicReport(
|
|
662
|
+
workflow=SETUP_WORKFLOW,
|
|
663
|
+
run_id=progress_state.run_id,
|
|
664
|
+
headline=summary,
|
|
665
|
+
lines=public_lines,
|
|
666
|
+
)
|
|
667
|
+
return WorkflowReports(
|
|
668
|
+
summary=summary,
|
|
669
|
+
public_report=public_report,
|
|
670
|
+
details={
|
|
671
|
+
"primary_objective_summary": _setup_primary_objective_summary(
|
|
672
|
+
state=state,
|
|
673
|
+
progress_state=progress_state,
|
|
674
|
+
).to_payload()
|
|
675
|
+
},
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def _setup_primary_objective_summary(
|
|
680
|
+
*,
|
|
681
|
+
state: SetupState,
|
|
682
|
+
progress_state: WorkflowProgressState,
|
|
683
|
+
) -> WorkflowPrimaryObjectiveSummary:
|
|
684
|
+
"""State-owned answer to whether setup released dependent workflows."""
|
|
685
|
+
|
|
686
|
+
completed = state == SetupState.READY
|
|
687
|
+
return WorkflowPrimaryObjectiveSummary(
|
|
688
|
+
workflow=SETUP_WORKFLOW,
|
|
689
|
+
run_id=progress_state.run_id,
|
|
690
|
+
objective="Preparar caminhos, runtime local e proteção do vault para os workflows públicos.",
|
|
691
|
+
completed=completed,
|
|
692
|
+
status=state.value,
|
|
693
|
+
mutation_state="not_applicable",
|
|
694
|
+
mutation_summary="Setup não é um fluxo de limpeza/mutação da Wiki.",
|
|
695
|
+
remaining_work_summary=_setup_remaining_work_summary(state, completed),
|
|
696
|
+
next_step_summary=_setup_next_step_summary(progress_state, completed),
|
|
697
|
+
blocked_reason="" if completed else _reason_code_from_state(state),
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _setup_remaining_work_summary(state: SetupState, completed: bool) -> str:
|
|
702
|
+
if completed:
|
|
703
|
+
return "Ambiente local pronto para retomar workflows dependentes."
|
|
704
|
+
return _message_for_state(state)
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
def _setup_next_step_summary(progress_state: WorkflowProgressState, completed: bool) -> str:
|
|
708
|
+
if completed:
|
|
709
|
+
return "Nenhuma ação pendente para o setup local."
|
|
710
|
+
return progress_state.resume_action or "Retomar /mednotes:setup pela rota oficial."
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def _diagnostic_context(
|
|
714
|
+
model: WorkflowModel,
|
|
715
|
+
state: SetupState,
|
|
716
|
+
category: WorkflowStateCategory,
|
|
717
|
+
) -> JsonObject:
|
|
718
|
+
if category == WorkflowStateCategory.COMPLETED:
|
|
719
|
+
return {}
|
|
720
|
+
return diagnostic_context_evidence_only({
|
|
721
|
+
"schema": "medical-notes-workbench.setup-fsm-diagnostic-context.v1",
|
|
722
|
+
"state": state.value,
|
|
723
|
+
"category": category.value,
|
|
724
|
+
"reason": _reason_code(model, state),
|
|
725
|
+
})
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def _blockers_for(category: WorkflowStateCategory, state: SetupState) -> list[str]:
|
|
729
|
+
if category in {
|
|
730
|
+
WorkflowStateCategory.WAITING_AGENT,
|
|
731
|
+
WorkflowStateCategory.WAITING_EXTERNAL,
|
|
732
|
+
WorkflowStateCategory.WAITING_HUMAN,
|
|
733
|
+
WorkflowStateCategory.BLOCKED,
|
|
734
|
+
WorkflowStateCategory.FAILED,
|
|
735
|
+
}:
|
|
736
|
+
return [_reason_code_from_state(state)]
|
|
737
|
+
return []
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
def _agent_instructions(category: WorkflowStateCategory) -> list[str]:
|
|
741
|
+
if category == WorkflowStateCategory.WAITING_AGENT:
|
|
742
|
+
return ["Execute somente os efeitos em agent_directive.control.effects e retome o setup depois do resultado."]
|
|
743
|
+
if category == WorkflowStateCategory.WAITING_EXTERNAL:
|
|
744
|
+
return ["Aguarde a condicao externa indicada; nao fabrique sucesso enquanto ela nao estiver pronta."]
|
|
745
|
+
if category == WorkflowStateCategory.WAITING_HUMAN:
|
|
746
|
+
return ["Peca a decisao humana fechada indicada no human_decision_packet antes de continuar."]
|
|
747
|
+
if category == WorkflowStateCategory.COMPLETED:
|
|
748
|
+
return ["Reporte que o setup esta pronto para os workflows dependentes."]
|
|
749
|
+
return ["Use o estado da FSM como fonte de verdade do setup."]
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
def _resume_action(model: WorkflowModel, state: SetupState) -> str:
|
|
753
|
+
if state == SetupState.READY:
|
|
754
|
+
return ""
|
|
755
|
+
if model.last_transition is not None and model.last_transition.resume_action:
|
|
756
|
+
return model.last_transition.resume_action
|
|
757
|
+
return resume_action_for_setup_state(state)
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
def _reason_code(model: WorkflowModel, state: SetupState) -> str:
|
|
761
|
+
if model.last_transition is not None:
|
|
762
|
+
return model.last_transition.reason_code
|
|
763
|
+
return _reason_code_from_state(state)
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def _reason_code_from_state(state: SetupState) -> str:
|
|
767
|
+
match state:
|
|
768
|
+
case SetupState.PATHS_REQUIRED:
|
|
769
|
+
return "paths_missing"
|
|
770
|
+
case SetupState.PYTHON_ENV_REQUIRED:
|
|
771
|
+
return "environment_blocker.windows_path_or_venv"
|
|
772
|
+
case SetupState.CONFIG_VALIDATION_RUNNING:
|
|
773
|
+
return "config_validation_required"
|
|
774
|
+
case SetupState.CONFIG_ENCODING_REQUIRED:
|
|
775
|
+
return "config_encoding_invalid"
|
|
776
|
+
case SetupState.OBSIDIAN_NOT_READY:
|
|
777
|
+
return "obsidian_not_ready"
|
|
778
|
+
case SetupState.MARKDOWN_RUNTIME_REQUIRED:
|
|
779
|
+
return "markdown_runtime_missing"
|
|
780
|
+
case SetupState.MARKDOWN_INDEX_REQUIRED:
|
|
781
|
+
return "markdown_index_missing"
|
|
782
|
+
case SetupState.VAULT_GUARD_REQUIRED:
|
|
783
|
+
return "vault_guard_required"
|
|
784
|
+
case SetupState.LOCAL_READY_GITHUB_PENDING:
|
|
785
|
+
return "github_remote_missing"
|
|
786
|
+
case SetupState.GITHUB_LOGIN_REQUIRED:
|
|
787
|
+
return "github_login_required"
|
|
788
|
+
case SetupState.GITHUB_REMOTE_CONFIRMATION_REQUIRED:
|
|
789
|
+
return "github_remote_missing"
|
|
790
|
+
case SetupState.GITHUB_REMOTE_AMBIGUOUS:
|
|
791
|
+
return "github_remote_ambiguous"
|
|
792
|
+
case SetupState.BRANCH_CONFIRMATION_REQUIRED:
|
|
793
|
+
return "blocked_branch_confirmation_required"
|
|
794
|
+
case SetupState.POLICY_DECISION_REQUIRED:
|
|
795
|
+
return "unsupported_host_or_policy_gap"
|
|
796
|
+
case SetupState.FAILED:
|
|
797
|
+
return "setup_failed"
|
|
798
|
+
case _:
|
|
799
|
+
return state.value
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
def _default_version_control_safety() -> VersionControlSafety:
|
|
803
|
+
return VersionControlSafety(
|
|
804
|
+
no_resource_mutation=True,
|
|
805
|
+
rollback_declared=False,
|
|
806
|
+
direct_mutation_forbidden=True,
|
|
807
|
+
agent_instruction="Setup nao deve mutar conteudo medico da Wiki.",
|
|
808
|
+
)
|