统一入口挂号演示实施计划.md 24 KB


doc_id: DEV-202606-003 feature_id: FEAT-202606-001-unified-entry-client type: dev-progress title: Unified Entry Registration Demo Implementation Plan status: implemented owner: 医梦研发团队 created_at: 2026-06-02 updated_at: 2026-06-02 reviewers: [] related_docs:

  • PRD-202606-001
  • DDS-202606-001
  • API-202606-001 related_modules:
  • emoon-openplatform
  • emoon-ai-agent
  • emoon-ai-card
  • emoon-ai-mcp tags:
  • 统一入口客户端
  • 挂号演示
  • 开发计划 ---

Unified Entry Registration Demo Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Build a next-week demonstrable outpatient registration loop from unified Web client → AI platform → MCP tool service → SQLite Mock HIS, with card confirmation, idempotency, mock payment, and reproducible acceptance scripts.

Architecture: Keep production-facing backend capabilities inside the existing AI platform modules. Add a separate mock-his-service demo service using SQLite; do not add it to the root Maven reactor and do not use MySQL. emoon-openplatform remains the only terminal-facing API, emoon-ai-agent owns routing/task orchestration, emoon-ai-card owns card state/actions, and emoon-ai-mcp owns the HIS tool facade and adapter boundary.

Tech Stack: Java 17, Spring Boot 3, Maven, SQLite for Mock HIS, existing RuoYi-Vue-Plus backend modules, MyBatis-Plus where already used, OkHttp/RestTemplate-style HTTP adapter according to existing project conventions, JUnit 5, Maven module tests, curl-based acceptance scripts.


Scope

This plan implements one frozen demo flow:

patient message
→ POST /api/v1/agent/chat/stream
→ AgentRouter creates/continues REGISTRATION task
→ Dify or deterministic demo response suggests department card
→ card actions select department / doctor / time slot / confirm appointment
→ MCP tool facade calls SQLite Mock HIS
→ Mock HIS locks slot, creates mock payment order, marks mock paid, creates appointment
→ appointment-success card returns with mock label and traceId

Do not implement real HIS, real payment,医保,退号退费,三端完整硬件接入,舌诊,报告解读, or a public /tasks API in this plan.

Existing Baseline

The repo already contains useful skeletons and tests:

  • emoon-openplatform/src/main/java/com/emoon/openplatform/controller/v1/AgentChatController.java
  • emoon-openplatform/src/main/java/com/emoon/openplatform/controller/v1/CardActionController.java
  • emoon-openplatform/src/main/java/com/emoon/openplatform/controller/v1/DeviceController.java
  • emoon-openplatform/src/main/java/com/emoon/openplatform/auth/HmacAuthInterceptor.java
  • emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent/src/main/java/com/emoon/ai/agent/application/AgentRouterService.java
  • emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent/src/main/java/com/emoon/ai/agent/application/AgentActionOrchestrator.java
  • emoon-infra/emoon-modules/emoon-ai/emoon-ai-card/src/main/java/com/emoon/ai/card/application/CardActionService.java
  • emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/ai/mcp/application/McpToolService.java
  • emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/client/MockHisClient.java
  • emoon-openplatform/src/test/java/com/emoon/openplatform/acceptance/TerminalMvpAcceptanceTest.java

Treat these as the starting point. Do not recreate modules that already exist.

Task 1: Build SQLite Mock HIS Service

Files:

  • Create: mock-his-service/pom.xml
  • Create: mock-his-service/src/main/java/com/emoon/mockhis/MockHisApplication.java
  • Create: mock-his-service/src/main/java/com/emoon/mockhis/controller/PatientController.java
  • Create: mock-his-service/src/main/java/com/emoon/mockhis/controller/CatalogController.java
  • Create: mock-his-service/src/main/java/com/emoon/mockhis/controller/SlotController.java
  • Create: mock-his-service/src/main/java/com/emoon/mockhis/controller/PaymentController.java
  • Create: mock-his-service/src/main/java/com/emoon/mockhis/controller/AppointmentController.java
  • Create: mock-his-service/src/main/java/com/emoon/mockhis/service/RegistrationFlowService.java
  • Create: mock-his-service/src/main/java/com/emoon/mockhis/domain/*.java
  • Create: mock-his-service/src/main/resources/application.yml
  • Create: mock-his-service/src/main/resources/schema.sql
  • Create: mock-his-service/src/main/resources/seed.sql
  • Create: mock-his-service/src/test/java/com/emoon/mockhis/RegistrationFlowServiceTest.java
  • Create: mock-his-service/src/test/java/com/emoon/mockhis/MockHisApiTest.java

  • [ ] Step 1: Write failing service tests for real HIS semantics

Add tests covering:

@Test
void lockSlotDecreasesRemainingCountAndExpiresAfterFiveMinutes() {
    LockSlotResult first = service.lockSlot("slot_neuro_001", "patient_demo_001", "idem-lock-001");
    assertThat(first.lockId()).isNotBlank();
    assertThat(first.expiresAt()).isAfter(Instant.now());
    assertThat(service.getSlot("slot_neuro_001").remainingCount()).isEqualTo(2);

    LockSlotResult replay = service.lockSlot("slot_neuro_001", "patient_demo_001", "idem-lock-001");
    assertThat(replay.lockId()).isEqualTo(first.lockId());
    assertThat(service.getSlot("slot_neuro_001").remainingCount()).isEqualTo(2);
}

@Test
void createAppointmentRequiresPaidMockOrderAndIsIdempotent() {
    LockSlotResult lock = service.lockSlot("slot_neuro_001", "patient_demo_001", "idem-lock-002");
    PaymentOrder order = service.createPaymentOrder(lock.lockId(), "idem-pay-001");

    assertThatThrownBy(() -> service.createAppointment(lock.lockId(), order.orderId(), "idem-appt-001"))
        .hasMessageContaining("PAYMENT_NOT_PAID");

    service.markMockPaid(order.orderId());
    Appointment first = service.createAppointment(lock.lockId(), order.orderId(), "idem-appt-001");
    Appointment replay = service.createAppointment(lock.lockId(), order.orderId(), "idem-appt-001");

    assertThat(replay.appointmentId()).isEqualTo(first.appointmentId());
    assertThat(first.mock()).isTrue();
}
  • Step 2: Run tests and verify they fail

Run:

mvn -f mock-his-service/pom.xml -DskipTests=false test

Expected: compilation fails until the service, schema, and DTOs are implemented.

  • Step 3: Implement SQLite schema and seed data

schema.sql must include these tables:

create table if not exists his_patient (
  patient_id text primary key,
  name text not null,
  identity_no text,
  phone text,
  created_at text not null
);
create unique index if not exists uk_his_patient_identity on his_patient(identity_no) where identity_no is not null;
create unique index if not exists uk_his_patient_phone on his_patient(phone) where phone is not null;

create table if not exists his_department (
  department_id text primary key,
  department_code text not null unique,
  name text not null,
  floor text not null
);

create table if not exists his_doctor (
  doctor_id text primary key,
  department_id text not null,
  name text not null,
  title text not null,
  specialties text not null,
  room text not null
);

create table if not exists his_schedule (
  schedule_id text primary key,
  doctor_id text not null,
  visit_date text not null,
  session_name text not null,
  amount_cent integer not null
);

create table if not exists his_slot (
  slot_id text primary key,
  schedule_id text not null,
  time_period text not null,
  total_count integer not null,
  remaining_count integer not null,
  version integer not null default 0
);

create table if not exists his_slot_lock (
  lock_id text primary key,
  slot_id text not null,
  patient_id text not null,
  status text not null,
  expires_at text not null,
  idempotency_key text not null unique,
  created_at text not null
);

create table if not exists his_payment_order (
  order_id text primary key,
  lock_id text not null,
  amount_cent integer not null,
  status text not null,
  qr_content text not null,
  mock integer not null,
  idempotency_key text not null unique,
  created_at text not null
);

create table if not exists his_appointment (
  appointment_id text primary key,
  lock_id text not null,
  order_id text not null,
  appointment_no text not null,
  status text not null,
  mock integer not null,
  idempotency_key text not null unique,
  created_at text not null
);

Seed at least:

神经内科: 李明 主任医师, 王佳 副主任医师
普通内科: 陈涛 主治医师
tomorrow morning slots: 09:30-09:45, 09:45-10:00
patient_demo_001: 张三 / 13800000000
  • Step 4: Implement REST endpoints

Implement exactly these demo endpoints:

POST /mock-his/patients/search
POST /mock-his/patients
GET  /mock-his/departments
GET  /mock-his/doctors?departmentId=
GET  /mock-his/schedules?doctorId=&date=
POST /mock-his/slots/lock
POST /mock-his/slots/release
POST /mock-his/payments/orders
POST /mock-his/payments/mock-paid
GET  /mock-his/payments/{orderId}
POST /mock-his/appointments
GET  /mock-his/appointments/{appointmentId}
  • Step 5: Run Mock HIS tests

Run:

mvn -f mock-his-service/pom.xml -DskipTests=false test

Expected: all Mock HIS tests pass.

Commit: feat(mock-his): add sqlite registration demo service

Task 2: Extend MCP Tool Facade For Registration

Files:

  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/ai/mcp/application/McpToolService.java
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/client/MockHisClient.java
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/domain/HisSlotLockResult.java
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/domain/HisPaymentOrder.java
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/domain/HisAppointment.java
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/test/java/com/emoon/ai/mcp/application/McpToolServiceTest.java

  • [ ] Step 1: Write failing MCP tests

Add tests that assert:

@Test
void lockSlotDelegatesToMockHisAndReturnsLockId() {
    HisSlotLockResult result = service.lockSlot("slot_neuro_001", "patient_demo_001", "trace-001", "idem-lock-001");
    assertThat(result.lockId()).startsWith("LOCK");
    assertThat(result.expiresAt()).isNotNull();
}

@Test
void paymentAndAppointmentUseMockFlagAndIdempotencyKey() {
    HisPaymentOrder order = service.createPaymentOrder("LOCK202606020001", "trace-001", "idem-pay-001");
    assertThat(order.qrContent()).startsWith("demo-payment://orderId=");
    assertThat(order.mock()).isTrue();

    service.markMockPaid(order.orderId(), "trace-001");
    HisAppointment appt = service.createAppointment("LOCK202606020001", order.orderId(), "trace-001", "idem-appt-001");
    assertThat(appt.mock()).isTrue();
    assertThat(appt.appointmentNo()).isNotBlank();
}
  • Step 2: Run MCP tests and verify failure

Run:

mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp -DskipTests=false test

Expected: tests fail until new methods and DTOs exist.

  • Step 3: Implement new tool methods

Add methods to McpToolService:

HisSlotLockResult lockSlot(String slotId, String patientId, String traceId, String idempotencyKey);
void releaseSlot(String lockId, String traceId, String idempotencyKey);
HisPaymentOrder createPaymentOrder(String lockId, String traceId, String idempotencyKey);
void markMockPaid(String orderId, String traceId);
HisPaymentOrder queryPaymentStatus(String orderId, String traceId);
HisAppointment createAppointment(String lockId, String orderId, String traceId, String idempotencyKey);

These methods must log tool name, traceId, risk level, mock flag, and result status. Query methods are allowed from Dify workflow; write/financial methods must be called only from Card Action orchestration.

  • Step 4: Run MCP tests

Run:

mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp -DskipTests=false test

Expected: pass.

Commit: feat(mcp): add registration mock his tools

Task 3: Fix Card Action Registration Flow

Files:

  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent/src/main/java/com/emoon/ai/agent/application/AgentActionOrchestrator.java
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-card/src/main/java/com/emoon/ai/card/application/CardActionService.java
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-card/src/main/java/com/emoon/ai/card/application/CardInstanceService.java
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent/src/test/java/com/emoon/ai/agent/application/AgentActionOrchestratorTest.java
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-card/src/test/java/com/emoon/ai/card/application/CardActionServiceTest.java

  • [ ] Step 1: Write failing orchestration test for lock → payment → appointment

Assert the exact card sequence:

select_department -> doctor-selection
select_doctor -> time-slot-selection
select_time_slot -> confirm-appointment with lockId and expiresAt
confirm_appointment -> payment-qrcode with mock qrContent
mock_payment_paid -> appointment-success

Test expectation:

assertThat(confirmCard.nextCard().get("cardKey")).isEqualTo("payment-qrcode");
assertThat(paymentCard.nextCard().get("cardKey")).isEqualTo("appointment-success");
assertThat(paymentCard.nextCardData().get("mock")).isEqualTo(true);
  • Step 2: Run agent/card tests and verify failure

Run:

mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent,emoon-infra/emoon-modules/emoon-ai/emoon-ai-card -DskipTests=false test

Expected: fail until the current direct createAppointment shortcut is replaced.

  • Step 3: Implement proper action handlers

Change AgentActionOrchestrator behavior:

select_time_slot:
  read patientId from task context or use patient_demo_001 for demo
  call mcpToolService.lockSlot(slotId, patientId, traceId, idempotencyKey)
  patch task context with slotId, lockId, lockExpiresAt
  create confirm-appointment card

confirm_appointment:
  read lockId from task context
  call mcpToolService.createPaymentOrder(lockId, traceId, idempotencyKey)
  patch task context with orderId
  create payment-qrcode card with mock:true

mock_payment_paid:
  call mcpToolService.markMockPaid(orderId, traceId)
  call mcpToolService.createAppointment(lockId, orderId, traceId, idempotencyKey)
  complete task
  create appointment-success card with mock:true

Keep Card Runtime from directly calling MCP. The call chain must remain:

CardActionController -> CardActionService -> AgentActionOrchestrator -> McpToolService -> Mock HIS
  • Step 4: Enforce action idempotency

CardActionService.submit() must return the first result for repeated idempotencyKey on the same card/action. It must reject:

expired card
completed card with a new idempotencyKey
unknown actionName
high-risk action without confirm:true
  • Step 5: Run tests

Run:

mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent,emoon-infra/emoon-modules/emoon-ai/emoon-ai-card -DskipTests=false test

Expected: pass.

Commit: feat(agent): complete registration card action flow

Task 4: Make Agent/SSE Demo Entry Stable

Files:

  • Modify: emoon-openplatform/src/main/java/com/emoon/openplatform/controller/v1/AgentChatController.java
  • Modify: emoon-openplatform/src/main/java/com/emoon/openplatform/service/impl/AgentChatApplicationServiceImpl.java
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent/src/main/java/com/emoon/ai/agent/application/AgentRouterService.java
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent/src/main/java/com/emoon/ai/agent/application/TaskStateService.java
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent/src/main/java/com/emoon/ai/agent/application/TerminalReplyTemplateService.java
  • Modify: emoon-openplatform/src/test/java/com/emoon/openplatform/acceptance/TerminalMvpAcceptanceTest.java

  • [ ] Step 1: Add failing acceptance tests

Cover:

POST /agent/chat/stream with "我头疼三天想挂号"
  emits task_updated REGISTRATION
  emits message_delta
  emits card_created department-selection
  emits completed with conversationId and traceId

POST /agent/chat/stream with "下午" while WAIT_SELECT_TIME_SLOT
  does not call DeepSeek
  routes to current REGISTRATION task

guide_screen attempts registration
  emits blocked message and no registration card
  • Step 2: Run acceptance test and verify failure

Run:

mvn -pl emoon-openplatform -DskipTests=false -Dtest=TerminalMvpAcceptanceTest test

Expected: fail until SSE events and task/card behavior match the frozen contract.

  • Step 3: Implement stable SSE event order

The stream must emit:

event: task_updated
event: message_delta
event: message_completed
event: card_created
event: completed

For errors:

event: error
event: completed

Do not expose Dify raw event names to the frontend.

  • Step 4: Implement router priority

Enforce:

1. device policy
2. activeTask unless explicit cancel/switch
3. waitingCard
4. deterministic registration/payment/cancel rules
5. DeepSeek JSON classification
6. clarification
  • Step 5: Run acceptance test

Run:

mvn -pl emoon-openplatform -DskipTests=false -Dtest=TerminalMvpAcceptanceTest test

Expected: pass.

Commit: feat(openplatform): stabilize registration sse entry

Task 5: Wire Dify As Optional Demo Engine

Files:

  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent/src/main/java/com/emoon/mcp/engine/impl/DifyAgentEngine.java
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent/src/main/java/com/emoon/ai/agent/infrastructure/normalizer/DifyOutputNormalizer.java
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent/src/test/java/com/emoon/ai/agent/infrastructure/normalizer/DifyOutputNormalizerTest.java
  • Modify: docs/initiatives/FEAT-202606-001-unified-entry-client/需求文档.md only if implementation finds a contract mismatch.

  • [ ] Step 1: Write failing normalizer tests

Add tests:

@Test
void validDepartmentCardOutputNormalizesToPlatformCard() {
    NormalizedAgentResponse result = normalizer.normalize("""
      {"schemaVersion":"1.0","answer":"建议优先看神经内科",
       "taskUpdate":{"taskType":"REGISTRATION","currentStep":"TRIAGE_RECOMMEND_DEPARTMENT"},
       "cards":[{"cardKey":"department-selection","cardData":{"departments":[{"departmentId":"D010","name":"神经内科","reason":"头痛伴恶心"}]}}],
       "safety":{"riskLevel":"LOW","needEmergencyWarning":false}}
      """);
    assertThat(result.cards()).hasSize(1);
    assertThat(result.cards().get(0).cardKey()).isEqualTo("department-selection");
}

@Test
void invalidJsonFallsBackToTextWithTraceId() {
    NormalizedAgentResponse result = normalizer.normalize("not json");
    assertThat(result.cards()).isEmpty();
    assertThat(result.degraded()).isTrue();
}
  • Step 2: Run normalizer tests and verify failure

Run:

mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent -DskipTests=false -Dtest=DifyOutputNormalizerTest test

Expected: fail until the normalizer exists.

  • Step 3: Implement normalizer

Normalizer rules:

invalid JSON -> text-only degraded response
unknown cardKey -> no card, warning
cardData schema mismatch -> no card, error message with traceId
sensitive identity fields in Dify output -> reject card
high risk output -> force confirm card or manual warning
  • Step 4: Configure one Dify app for demo

Use one Dify app:

outpatient-registration-agent

Branches inside the app:

registration_intake
triage_recommendation
registration_reply

Dify must not directly call write/financial HIS tools. Dify can suggest a card and explanation; Card Action performs writes.

  • Step 5: Run agent tests

Run:

mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent -DskipTests=false test

Expected: pass.

Commit: feat(agent): normalize dify registration output

Task 6: Add End-To-End Acceptance Scripts

Files:

  • Create: scripts/demo-registration/README.md
  • Create: scripts/demo-registration/start-demo.sh
  • Create: scripts/demo-registration/reset-mock-his.sh
  • Create: scripts/demo-registration/registration-happy-path.sh
  • Create: scripts/demo-registration/registration-edge-cases.sh
  • Create: scripts/demo-registration/assertions.sh

  • [ ] Step 1: Write executable happy-path script

registration-happy-path.sh must perform:

1. health check Mock HIS
2. health check AI platform
3. call /agent/chat/stream with "我头疼三天,还有点恶心,想挂号"
4. extract department cardInstanceId
5. POST select_department
6. POST select_doctor
7. POST select_time_slot
8. POST confirm_appointment
9. POST mock_payment_paid
10. assert appointment-success card contains mock:true and appointmentNo
  • Step 2: Write edge-case script

registration-edge-cases.sh must cover:

duplicate confirm returns same result
unpaid order cannot create appointment
expired lock forces reselect time slot
guide screen cannot register
Mock HIS timeout returns error card with traceId
Dify invalid JSON degrades to text and no card
  • Step 3: Run scripts locally

Run:

bash scripts/demo-registration/reset-mock-his.sh
bash scripts/demo-registration/registration-happy-path.sh
bash scripts/demo-registration/registration-edge-cases.sh

Expected: all scripts exit 0 and print the final traceId, appointmentId, appointmentNo, and mock:true.

Commit: test(demo): add registration acceptance scripts

Task 7: Final Verification

Files:

  • Review: all files changed in Tasks 1-6.
  • Update only if needed: docs/initiatives/FEAT-202606-001-unified-entry-client/需求文档.md
  • Update only if needed: docs/initiatives/FEAT-202606-001-unified-entry-client/MVP接口契约.md

  • [ ] Step 1: Run module tests

Run:

mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp -DskipTests=false test
mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-agent -DskipTests=false test
mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-card -DskipTests=false test
mvn -pl emoon-openplatform -DskipTests=false test

Expected: all pass.

  • Step 2: Run compile check

Run:

mvn -pl emoon-openplatform -am -DskipTests compile

Expected: compile succeeds.

  • Step 3: Run architecture test

Run:

mvn -pl emoon-admin -DskipTests=false -Dprofiles.active= -Dtest=AiPlatformArchitectureTest test

Expected: architecture test passes. No Controller imports Mapper. No API module contains implementation classes. Card module does not call MCP directly.

  • Step 4: Prepare demo evidence

Collect:

Mock HIS sqlite db path
seed data version
conversationId
taskId
cardInstanceId sequence
traceId
appointmentId
appointmentNo
mock:true screenshot or API response

Commit: chore(demo): verify registration demo loop

Self-Review

Spec coverage:

  • SQLite Mock HIS replaces the MySQL assumption.
  • Dify remains optional/controlled and cannot write HIS.
  • DeepSeek is a fallback classifier, not the task controller.
  • Card Action owns write confirmation and idempotency.
  • Mock payment is explicitly marked and cannot impersonate real payment.
  • Acceptance scripts cover happy path and core failure cases.

Placeholder scan:

  • No unresolved placeholder terms or unspecified implementation steps remain.

Type consistency:

  • lockId, orderId, appointmentId, appointmentNo, traceId, idempotencyKey, and mock:true are used consistently across Mock HIS, MCP, Card Action, and scripts.

Execution Choice

Plan complete and saved to docs/initiatives/FEAT-202606-001-unified-entry-client/dev-progress/统一入口挂号演示实施计划.md.

Two execution options:

  1. Subagent-Driven (recommended) - dispatch a fresh subagent per task, review between tasks, fast iteration.
  2. Inline Execution - execute tasks in this session using executing-plans, batch execution with checkpoints.

Recommended for this repo: Subagent-Driven, because Mock HIS, MCP/Card, OpenPlatform SSE, and Dify normalizer are separate review surfaces.