13-ai-condorwave: W6filled7 citations

Condor Diagnosis Engine — Deterministic Route Dispatch, Decompose, Plan Compilation

Audiences: developer, internal

Condor Diagnosis Engine — Deterministic Route Dispatch, Decompose, Plan Compilation

This topic covers the agentic engine view of diagnosis: how a clinical turn is routed deterministically to Condor, decomposed into parallel sub-goals, and compiled into an executable Mastra workflow. It is the runtime counterpart to Diagnosis Engine, which owns the clinical knowledge domain (DiseaseCards, Curator, document generation). The two do not overlap: that topic is what the clinical knowledge is, this one is how a turn becomes a plan and runs.

Purpose

A clinical diagnosis turn must not be freehanded by the LLM. Condor binds it to a deterministic route (flowKey = 'diagnosis-engine'), which unlocks clinical guardrails and a fixed workflow template set. From there a pure decompose pass expands multi-goal turns into parallel sub-goal clusters, and the orchestrator compiler lowers the resulting WorkflowPlan into a Mastra workflow graph. Every stage is deterministic and re-validated — the LLM contributes the plan content, never the plan shape.

Deterministic route dispatch — the sessions/new-only firing

DIAGNOSIS_ENGINE_FLOW_KEY = 'diagnosis-engine' is declared at the routing seam (agentic-flow-router.service.ts:48) and bound to the Condor engine at boot (condor.module.ts:585, setFlowEngine(…, 'condor', DIAGNOSIS_FLOW_CONFIG)).

The load-bearing trap: the clinical template fires only when the session was created with that flowKey. ai-sessions-lifecycle.service.ts:138 gates on it:

const boundToDiagnosis = input.flowKey === DIAGNOSIS_ENGINE_FLOW_KEY;
…  ...(boundToDiagnosis ? { medicalMode: true } : {}),
Entry pathflowKey suppliedmedicalMode at createClinical template
sessions/new with dispatch.id = diagnosis-engineyestrue (persisted)fires
generic /condor/chat "New chat"nonulldoes not fire

Consequence: linking a patient or an anamnesis from a generic chat session yields no symptom-driven routing — the deterministic diagnosis workflow was never bound. A per-turn fallback exists (condor.controller.ts:732 flips medicalMode when the dispatched flowKey matches, and medical-mode-derivation.ts:82 treats the per-turn flowKey as "source 2"), but it is a legacy compensator, not the route. Always dispatch through sessions/new.

Pipeline — decomposer → plan.schema → compiler

Rendering diagram…

1. Decompose — a pure dependsOn re-baser, not a planner

Despite the directory name, mastra/decompose/diagnosis-decomposer.ts does not author a diagnosis. It is a pure, deterministic, mirror-only transform (:18-29 hard contract: no DI, no fetch, no LLM, no I/O):

RuleBehaviorSite
Multi-goal gate>1 distinct flowConfig.workflowTemplateIds ⇒ multi-goal. clinicalIntent alone does not qualify:78-82
Wasted-cost guardsingle-goal turn returns the plan referentially unchanged:117-119
Fan-in anchorrequires exactly one terminal final step; 0 → skip, >1 → skip + needsReview:121-133
Cluster selectionnon-final leaves that depend on no other non-final leaf (graph sources):147-150
Transformre-base cluster to dependsOn: []; union the cluster into final.dependsOn:168-180
Mirror-or-marka shape not expressible as identical-dependsOn siblings is returned unchanged + flagged, never re-invented:23-26

The transform emits only the sibling shape the compiler already lowers to .parallel() — so it required zero compiler change.

2. plan.schema — the validation contract

mastra/orchestrator/plan.schema.ts owns workflowPlanSchema / WORKFLOW_PLAN_STEP_KINDS plus the structural gates the diagnosis path leans on: validatePlan (:101), assertValidStepKinds (:128), findOffListPlanTools (:237), findNonDependencyInputRefs (:272), and the mutation-honesty pair findProseClaimedMutations (:389) / planProseClaimsMutation (:436).

Defence-in-depth: run-knobs.ts:234 re-parses the expanded plan against WorkflowPlanSchema and falls back to the original plan on a miss rather than risk a compile throw.

Research-intent guard. plan-mutation-rewrite.ts once hijacked a research plan (a beta-blocker Q&A) into medication.manage{op:create} via prose-claimed mutation rewriting. The rewrite is now gated on plan.intent !== 'research' — a diagnosis research turn must never be rewritten into a mutation.

3. Compiler — graph lowering

mastra/orchestrator/compiler.ts is a pure-function compiler: it infers execution levels from per-step dependsOn, emits Mastra .then() / .parallel([…]), and auto-inserts a synthesizer when a downstream step depends on more than one sibling. Step kinds map to primitives (tool-callctx.tools[toolId].execute; agent-callctx.roleAgents[role].stream(…), never generate; final → the formatter agent). normalize-plan.ts conditions the plan before compilation. Full compiler detail lives in Condor Engine.

Web consumption — diagnosis frames are stream-only

The diagnosis cards do not arrive as message.parts data-parts. All diagnosis.* frames come over the AG-UI live/replay stream as Custom{ name: 'diagnosis.<suffix>' }, read from the CondorProvider aguiEventsByRun map by two pure selectors:

SelectorFrames
select-diagnosis-ranked.tslatest diagnosis.ranked for a run (never reaches into another run)
select-diagnosis-ops.ts6 op frames (search/find/compare/score/matcher/odds) + 5 outcome frames (diagnosis-outcome/treatment-plan/anamnesis/procedure/findings)

Frame-name law: names are read verbatim from the frozen DIAGNOSIS_OP_FRAME_NAMES / DIAGNOSIS_OUTCOME_FRAME_NAMES maps in @ddx/sse-contractdiagnosis.<suffix>, never condor.<suffix> and never condor.condor-*. Hardcoding a name in the consumer would drift from the emitter.

How this differs from the clinical Diagnosis Engine topic

AxisThis topic (06-ai-tucan)Diagnosis Engine (04-clinical)
Concernturn routing, plan shape, execution graphclinical knowledge + document output
Code homeddx-api/src/ai/condor/**ddx-api/src/diagnosis-engine/**, ddx-api/src/documents/**
Core artifactsWorkflowPlan, step graph, AG-UI framesDiseaseCard, KnowledgeMutation, generated documents
Determinism claimroute + decompose + compile are deterministic/pureCurator is LLM-driven with auditable rollback
Audiencedeveloper, internaldoctor, clinical-buyer, developer, investor
  • Condor Engine — the runtime this engine plugs into (flow router, transport, compiler).
  • Diagnosis Engine — the clinical knowledge domain (DiseaseCards, Curator, documents).
  • Condor RAG Tool Search — supplies the domain-scoped candidate tool set a diagnosis plan draws from.
  • Condor Tools Registry — the catalog whose tools compiled tool-call steps invoke.