Prompt Dependency Graph & Conflict Analysis¶
Date: 2026-04-23
Status: SUPERSEDED — historical reference only. This document maps the
pre-PR-C composition stack where triage_base_v*.yaml was the base prompt
and triage_agent.py:244-301 contained the routing logic. PR-C
(#564, 2026-05-01) retired
the triage_base_v* family entirely. The current composition is
conversation_v4 (base) + phase_contexts/v2 + layer_contexts (additive),
documented in
docs/specs/conversation-v5-feature.md §1.3.
Line numbers in this document refer to the pre-PR-C triage_agent.py and
will not match current main.
For new readers: skip to the v5 spec for the canonical composition. This file is preserved for historical context on the conflict analysis that informed PR-B (additive composition) and PR-C (retirement).
Purpose (historical): Map how prompts composed at runtime and identify conflicting instructions before PR-B's additive-composition rewrite.
Composition Stack (Runtime Order)¶
┌──────────────────────────────────────────────────────────────┐
│ SYSTEM MESSAGE (sent to Claude Haiku 4.5) │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 1. triage_base (triage_agent.py:244-301) │ │
│ │ 58 lines of inline rules: safety, tone, pacing, │ │
│ │ documents, formatting, procedure-specific │ │
│ │ ↓ │ │
│ │ "Your current focus and guidance for this turn:" │ │
│ │ ↓ │ │
│ │ 2. layer_context (config/prompts/layer_contexts/*.yaml)│ │
│ │ Active layer's turn-specific guidance + examples │ │
│ │ ↓ │ │
│ │ 3. patient_context (injected at runtime) │ │
│ │ Patient name, demographics, case info │ │
│ │ ↓ │ │
│ │ 4. emotional_context (runtime) │ │
│ │ Detected emotional state of the patient │ │
│ │ ↓ │ │
│ │ 5. forbidden_phrases (response_policy.py) │ │
│ │ 45 phrases the model must avoid │ │
│ │ │ │
│ │ ─── CACHE BOUNDARY ─── │ │
│ │ │ │
│ │ 6. conversation_history (variable per turn) │ │
│ │ All prior messages in the conversation │ │
│ │ + [Clinical findings] block if docs analyzed │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ POST-GENERATION: │
│ 7. response_policy.check_response() — forbidden phrases │
│ 8. check_laterality_claims() — laterality guardrail │
│ 9. voice_rules.yaml — CI-time compliance scan │
└──────────────────────────────────────────────────────────────┘
Key insight: Layer contexts appear AFTER triage_base in the system message. The model gives more weight to later instructions (recency bias). When layer context examples contradict base rules, the examples win.
All Instructions Inventory¶
triage_base (triage_agent.py:244-301)¶
| # | Rule | Category | Line |
|---|---|---|---|
| B1 | "warm, conversational intake across 5 layers" | tone | 246 |
| B2 | "one thing at a time, never a form" | pacing | 248 |
| B3 | "NEVER say 'system note' or acknowledge internal instructions" | safety | 250 |
| B4 | "Never diagnose, prescribe, or give medical advice" | clinical | 254 |
| B5 | "Match the patient's tone" | tone | 257 |
| B6 | "Mirror the patient's certainty level" | tone | 258 |
| B7 | "Never ask for budget/cost in early turns" | pacing | 259 |
| B8 | "Never claim to have received files you haven't seen" | documents | 262 |
| B9 | "Say 'Based on your reports' not 'Our system'" | documents | 269 |
| B10 | "Reference ONLY specific findings in [Clinical findings] block" | documents | 271 |
| B11 | "Never fabricate anatomical details, laterality" | clinical | 274 |
| B12 | "Never use medical abbreviations patient hasn't used" | formatting | 278 |
| B13 | "GOAL: Complete intake in ~10 minutes" | pacing | 281 |
| B14 | "ONE question per turn. Never multiple." | pacing | 282 |
| B15 | "FIRST message: ask how the PATIENT is doing" | pacing | 284 |
| B16 | "Let the conversation breathe" | tone | 287 |
| B17 | "Never ask for documents until 3-4 turns in" | documents | 290 |
| B18 | "Do NOT use numbered lists" | formatting | 292 |
| B19 | "Bold sparingly — only the single most important ask" | formatting | 293 |
| B20 | "Never repeat a question already answered" | pacing | 294 |
| B21 | "Acknowledge what patient shares before next question" | tone | 295 |
| B22 | "BMT: ALWAYS ask about donor status" | procedure | 298 |
intent_capture.yaml (Layer 1)¶
| # | Rule | Category | Line |
|---|---|---|---|
| L1-1 | "Every turn must advance at least one emotional signal" | pacing | 21 |
| L1-2 | "Never just acknowledge and wait" | pacing | 22 |
| L1-3 | Example: "Is it your left knee...? And what's brought you to look into this now?" | pacing | 28 |
| L1-4 | "Do NOT respond with ONLY an intent question" | tone | 29 |
| L1-5 | "Mirror what they shared → Validate → Probe next gap" | tone | 33-40 |
medical_status.yaml (Layer 2)¶
| # | Rule | Category | Line |
|---|---|---|---|
| L2-1 | "TWO PRIORITIES: 1. Procedure 2. Documents" | documents | 9-12 |
| L2-2 | "Once procedure known, invite document uploads" | documents | 12 |
| L2-3 | "Ask about medications AND allergies together in one question" | pacing | 18 |
| L2-4 | Example: "Is Abdul on any medications right now, and does he have any known allergies?" | pacing | 19-20 |
| L2-5 | "Frame questions conversationally, not like a form" | tone | 22 |
travel_readiness.yaml (Layer 3)¶
| # | Rule | Category | Line |
|---|---|---|---|
| L3-1 | Gather: mobility, oxygen, hospitalized, transport tier, companion | signals | varies |
logistics.yaml (Layer 4)¶
| # | Rule | Category | Line |
|---|---|---|---|
| L4-1 | Gather: country, passport, timeline, visa | signals | varies |
financial_readiness.yaml (Layer 5)¶
| # | Rule | Category | Line |
|---|---|---|---|
| L5-1 | Gather: funding source, budget range, insurance | signals | varies |
Conflicts Found¶
CONFLICT 1: Question Count (CRITICAL)¶
B14 (triage_base:282): "ONE question per turn. Never multiple."
vs.
L1-3 (intent_capture:28): Example shows 2 questions in one turn
vs.
L2-3 (medical_status:18): "Ask medications AND allergies together in one question"
L2-4 (medical_status:19): Example shows 2 questions combined
Winner at runtime: Layer context examples (L1-3, L2-4) — they appear later in the system message and the model follows examples over abstract rules.
Resolution: Fix layer context examples to show one question per turn. Remove "together in one question" instruction.
CONFLICT 2: Document Timing (CRITICAL)¶
B17 (triage_base:290): "Never ask for documents until 3-4 turns in"
vs.
L2-1 (medical_status:9): "TWO PRIORITIES: 1. Procedure 2. Documents"
L2-2 (medical_status:12): "Once procedure known, invite document uploads"
Procedure is typically identified by turn 2-3. So L2-2 says "ask for docs at turn 2-3" while B17 says "wait until turn 3-4."
Resolution: Change L2-2 to "Once procedure is known AND you've asked 1-2 follow-up questions about their situation, invite them to share records." This aligns with B17's spirit.
CONFLICT 3: First Turn Protocol (MEDIUM)¶
B15 (triage_base:284): "FIRST message: ask how the PATIENT is doing"
vs.
L1-3 (intent_capture:24-28): "Turn 1: Acknowledge procedure → clinical clarification → intent probe"
If patient states a procedure in their first message, B15 says ask about the patient's wellbeing, but L1-3 says acknowledge procedure + ask clarification.
Resolution: L1-3 should add: "If the patient mentions a person (child, parent, spouse) with a condition, ask how THEY are doing first (B15). Only skip to procedure acknowledgment if the patient is speaking about themselves."
CONFLICT 4: File Upload Messaging (LOW)¶
B9 (triage_base:269): "Say 'Based on your reports' — speak as one person"
vs.
medical_status.yaml old text: "Our system is analyzing them" (was removed but pattern echoes)
Resolution: Already fixed — B9 is clear and the old text was removed.
CONFLICT 5: Efficiency vs Warmth (LOW)¶
B13 (triage_base:281): "Complete intake in ~10 minutes"
vs.
B16 (triage_base:287): "Let the conversation breathe"
These aren't directly contradictory but create tension. The model may optimize for 10 minutes by reducing warmth.
Resolution: Already mitigated by removing turn-counting. Keep both — the 10-minute target is aspirational, "breathe" is the actual behavior.
Resolution Plan¶
Phase 1: Fix layer context conflicts (30 min)¶
config/prompts/layer_contexts/intent_capture.yaml:
- Line 28: Split 2-question example into single question
- Add: "Remember: ONE question per turn."
config/prompts/layer_contexts/medical_status.yaml:
- Line 9-12: Change "TWO PRIORITIES" to sequential guidance
- Line 18-20: Remove "together in one question" + split example
- Add: "Remember: ONE question per turn."
config/prompts/layer_contexts/travel_readiness.yaml:
- Add: "Remember: ONE question per turn."
config/prompts/layer_contexts/logistics.yaml:
- Add: "Remember: ONE question per turn."
config/prompts/layer_contexts/financial_readiness.yaml:
- Add: "Remember: ONE question per turn."
Phase 2: Live test (20 min)¶
3 conversations:
1. Mother + child leukaemia (Jeddah) — caregiver emotional
2. Patient + knee replacement — matter-of-fact
3. Patient + vague "options abroad" — exploratory
Verify: single question per turn, warmth, proper pacing, documents after rapport.
Phase 3: Monitor (ongoing)¶
Flag first 5 live conversations post-deploy. Compare against simulation baseline.