---
name: test-router
description: 测试路由规范 — 根据变更类型、影响范围与风险选择静态、单元、集成、API、E2E、场景/负载、pack 或发布验证，并记录跳过理由
---
# Test Router Skill

TestRoute 的稳定输入、route selector、固定输出与 skip 合同以同目录 `test-route-schema.json` 为唯一结构化事实源；领域专用证据保留在对应 Owner Skill，不再全部提升为 TestRoute 顶层字段。

## 职责

`test-router` 只负责选择验证路线和记录跳过理由，不替代 `dev-testing`、`api-verification`、`dev-scenario-test` 或项目自身测试规范。

## 输入

机器可读输入、selector、输出与跳过字段以同目录 `test-route-schema.json` 为唯一事实源。稳定输入只有：

`workflow / changeTypes / risk / publicSurface / runtimeBoundary / profileConstraints / candidateState / capabilitySurfaceDecision / requestedClaims / verificationIntent`

领域专属证据不得复制进 TestRoute 输入清单；先从 `../spec-governance/gate-registry.json` 解析适用 `gateGroup`，再由目标 Owner Skill 提供证据字段和阈值。

### VerificationIntentV2 请求面与执行层级

必须先判断“本轮要证明什么”，再选择可执行节点。`VerificationIntentV2` 只表达请求，不授予执行权；固定包含 `requesterClass / requestedLevel / requestedPurpose / affectedBoundaries / riskClass / requestSourceRef / project / candidateId / changedScopeDigest / claimCeiling / requestDigest`。执行必须另行取得绑定 `ValidationRunIdentityV1`、候选/HEAD/dirty scope、计划、预算、actor/context 与 hard deadline 的 `VerificationExecutionLeaseV2`；V1 只允许兼容读取，不能产生新 authority：

| level | 默认 purpose | 允许的最大声明 | 典型入口 |
|---|---|---|---|
| V0 | `edit-loop` | 单点编辑证据 | `test:fast` 或精确单测 |
| V1 | `delivery` | 当前变更影响闭包 | `test:changed` / `test:delivery` |
| V2 | `boundary` | 已列明边界的资格结论 | `test:boundary -- --boundary <owner>`、profile/package 兼容边界 |
| V3 | `full-audit` / `release` | 全审计或发布候选 | `npm test` / `test:full`；发布还需显式 release authorization |

`risk=high`、R3/R4、ECR、控制面命中、文件数、cache miss/stale、未知输入或消费者图不完整都不是 V3 授权。未知 executable input 在 V0～V2 必须升级到**当前等级允许的** full fallback closure，并显式排除更高等级、尤其 release consumer；这不是 V3，也不能提升 claim ceiling。V0/V1 命中高风险或控制面时，只能扩张到可证明的 V2 边界；无法安全推导时输出 BLOCK、缺失边界和下一步。V3 必须有明确 `full-audit` 请求，或 release-pipeline 在已有发布授权下请求 `release`，并取得匹配 `planDigest + budgetDigest` 的 BudgetCard 确认和 LeaseV2；请求对象本身不能启动执行。

消费者边必须区分 `runtimeConsumer / qualificationConsumer / releaseConsumer`。V1 只沿 runtime 边扩散，V2 可再沿 qualification 边扩散，release consumer 只在 V3 激活。`fast` 必须保持动态 V0；兼容入口 `profile-deploy` 与 `package-release` 分别固定为 profile/package V2 边界，不能因同一工作树还有其他变更而吸收其他边界或 V3 节点。V0～V2 的 `ValidationEvidenceV2` cache 身份绑定 `HEAD + 所有匹配节点声明输入的 dirty digest + node contract + node verification policy + dependency nodeReceiptDigest + runtime + cache policy`；缓存只写项目/工作区 `.tmp/devcodex/cache`，pack/install/security/real-host/time-sensitive/shared-write/delegated 节点永不命中。节点回执摘要向下游级联，不能用未变的 stdout 掩盖上游输入漂移，也不得绑定整个 candidateId 造成无关重跑。执行前必须重新核对 manifest identity、candidate identity 与 plan digest；任一绑定变化只重跑失效前沿。V3 禁止复用节点 cache，必须执行冻结候选的完整节点集合。

`ValidationExecutionScheduleV2` 只并发 normal 且无共享写域的节点；默认 `--max-concurrency 2`、上限 4，package/Profile/TaskRecovery/security/real-host 与无法证明隔离的节点保持 singleton。managed runner 始终独占 plan、cache、checkpoint、progress 和 terminal receipt 写权，node worker 只返回结果；`--max-concurrency 1` 必须与原串行 selected/result/failure/exit 语义等价。run scratch 使用 `.tmp/devcodex/runs/<operationId>` 并在成功、失败、超时后精确清理。ETA 来自 `.tmp` 下的有界成功历史和 wave critical path；不足 5 个可比样本只显示低置信范围，不把 timeout 当 ETA。

SkillRoute、用户自定义 Skill 或分层解析变化必须选择 `static + unit-integration + runtime-e2e`，执行 manifest 节点 `skill-route-stage-b` 和既有 `skill-route-contracts/budget/state/lifecycle/closure` 链。证据至少覆盖 1/100/1000 Skill、未选正文读取为 0、`project > workspace > bundled/global`、`resolve_exact` 不经 catalog、完整依赖/正文分页、reserved/path/TOCTOU/超大/循环负例以及宿主个人 Skill 不重复注入；总 Skill 数或目录总页数不得成为主任务 deny。测试 scratch 只能进入项目/工作区 `.tmp` 并精确清理。

V3、非发布计划估算超过 600 秒或包含 heavy 节点时，先输出绑定 `planDigest + budgetDigest` 的 `BudgetCardV1`。`estimatedDurationMs` 必须使用 manifest estimate 或有上限的默认估算，并同时公开 `estimateConfidence`、独立的 `hardTimeoutUpperBoundMs`、`logBudgetBytes` 与 `waitReasons`；禁止把所有 timeout 简单相加冒充预计耗时。只有匹配摘要的确认才能签发 LeaseV2；确认不得复用于候选、计划或预算摘要已经变化的下一轮。执行由 `ManagedValidationRunnerV2` 消费 lease 并持久化 exactly-one terminal receipt，调用方不得用进程退出文字自行补写终态。

AI Hook 在生成 BudgetCard 前必须由当前宿主 session 解析出唯一、fresh、server-owned 的正式任务绑定，并把 `taskRecoveryKey + contextEpoch + authoritySourceRef` 纳入同一计划身份；缺任务、跨 session、任务键冲突或缺当前 ContextRead epoch 时直接 BLOCK，不得先让用户确认一个无法执行的摘要。确认后的执行还必须提交本次确认消息的 digest；TaskRecovery evidence store 必须重新读取同一 session 的任务身份并与 LeaseV2 对账，禁止仅凭 caller 提供的 task key、`sessionKey` 或“最近任务”落盘。

`BudgetCardV1` 永久不可变。confirm 模式把当前 exact card 先写成唯一 `PendingBudgetCardBindingV1`，用户只需确认当前卡，由服务端以 CAS 生成 `BudgetConfirmationReceiptV1`；有效 Sticky Auto 可为 V0～V2 签发同等精确的 server-owned receipt，但不能扩大到 V3/full/release。父运行失败后，`ValidationContinuationAuthorizationV1` 只接受三类证明：完整 `mutation-observation`；stable/clean candidate 的严格 Git 后继 `committed-repair-diff`（父 changed scope 未截断且为当前子集，完整 commit diff 被当前显式 changed scope 覆盖）；或同 HEAD、无新增 dirty 路径/节点/预算的 `same-scope-retry`。三类 child retry 合计最多两次且始终相对 root；禁止用同一旧 Auto authority 换根清零或 child-to-child 复利。前两类仍须满足 task/project/root/session/purpose/boundary/heavy/side-effect/revocation 一致或收窄，相对 root 新增节点不超过 `min(3,max(1,ceil(root*5%)))`，estimated/hard-timeout/log 增量分别不超过 `min(60000ms,ceil(root*5%))`、`min(600000ms,ceil(root*5%))`、`min(65536B,ceil(root*5%))`，且不得新增 release consumer；same-scope retry 的节点与全部预算增量必须为零。父运行已终态、无 live lease且父 candidate HEAD 为当前已提交 HEAD 的严格 Git 祖先时，root rollover 仍要求 task/project/root/session/revocation 一致；沿用原 context/AutoRef 时 level、purpose、节点、boundary、heavy、副作用、estimated/hard/log 必须与父根逐项完全相等并使用 `strict-descendant-same-scope`，当前 fresh server-owned Auto 的 exact scope 未变时可重绑 context/AutoRef 并使用 `strict-descendant-exact-scope-current-auto-rebind`。若一个晚于父终态的不同真实用户 Auto 指令形成 fresh control，且 source digest、AutoRef 与 context epoch 均更新，则它可用 `strict-descendant-current-auto-rescope` 为当前精确 V2 影响范围建立新 root；这不是 child continuation，不继承旧范围，也不得提升为 V3/full/release。三种 root rollover 都必须保留 `parentRootReceiptDigest + parentTerminalDigest`。已提交修复必须显式传入冻结 changed files；clean tree 不能偷偷收窄范围；同一或过期 Auto authority 的非续权扩大/收窄统一返回 `auto-root-rollover-scope-changed`。plan-only 与 execute 必须复用同一 continuation/root-rollover preflight；plan-only 只返回是否可续或滚动，不消费 authority。过期 Auto ingress 只允许继续既有 root，或在原 context/AutoRef 精确一致时执行 exact-scope 根滚动；其他创建/替换 root 需要当前 fresh control。pause/stop/scope reduction 立即递增 revocation epoch 并撤销 pending/child/lease；cold/terminal 状态不得恢复执行 authority。

### ConvergenceFirstValidationV1

多 finding / 多批次 / 发布收口必须先冻结完整问题集，再批量实施，最后统一验证。issue-set、repair generation、implementation-complete、统一 affected run 与 final freeze 由工作流编排及项目证据 Owner 持有；validation runner 不自行发明或覆盖这些状态。编排 Owner 未证明全部 repair batch 完成时不得启动资格级 V1/V2/V3；语法/schema/materialization 检查不产生 ValidationEvidence。批次完成后，编排 Owner 只允许为同一冻结问题集、候选与 dirty scope 创建一张统一 affected V2 计划。Runner 继续执行独立节点并在 receipt 的 `failedNodes` 中一次收集全部失败，durable terminal projection 有界保留完整失败节点集合与紧凑摘要，只把依赖失败/依赖阻断节点写入 `abortedNodes + abortedNodeReasons`；首个失败不得触发逐项修测。affected 收敛并冻结候选后，发布管线只能创建一次 V3/full root。

新增关联 P0/P1 会使工作流 Owner 持有的 issue-set generation 单调递增，并使已建但未执行的计划 stale。验证计划和回执只绑定 exact candidate/HEAD/dirty scope、节点与授权；issue-set/freeze 证据由发布编排在启动 runner 前对账并随正式任务产物留存，不能把这些尚未进入 ValidationPlanV3 schema 的字段宣称为 runner 原生输出。

## 路由选择

| selector | 触发边界 | 最小证据 |
|----------|----------|----------|
| `static` | 源码或契约变化 | command、exitCode |
| `unit-integration` | 行为或跨模块变化 | suite、result、coverageDecision |
| `api` | HTTP 或 public API 边界 | endpointMatrix、双 API 产物、result |
| `runtime-e2e` | 用户路径或运行时状态变化 | target、stateMatrix、result |
| `package-release` | package candidate 或 release 声明 | candidateDiff、pack、install、registry |
| `profile-deploy` | Profile、宿主或分发面变化 | profileValidation、deploymentParity |

先按变化事实选择所有适用 selector，再用风险扩大 V0～V2 内的影响边界。风险本身不得升级到 V3。不得仅因“已有单测”跳过跨边界、真实 runtime、package candidate、Profile 或部署副本验证；不适用的 selector 必须形成结构化跳过记录。

`brandVisualQuality` 是 `brand-visual-quality` gateGroup 的领域绑定，不新增顶层 selector：至少选择 `static`，并用 Owner Skill 的同画布渲染、微尺寸/单色预览和人工视觉结论补充 evidence；涉及网站或产品运行态采用资产时再叠加 `runtime-e2e`。若任务只改 token/component 而不生产品牌资产，写结构化 skipReason 并交给设计系统 Owner。

`localObservability` 是 `local-observability-contract` gateGroup 的领域绑定：CLI JSON/human/error/exit 选择 `unit-integration + runtime-e2e`；typed local probe 追加 dependency/error/zero-write fixture；进入 package/公开文档时叠加 `package-release / profile-deploy`。

`turnLiveness` 是 `agent-turn-liveness` gateGroup 的领域绑定，不新增顶层 selector：至少选择 `unit-integration + runtime-e2e`，执行 fixed-clock 状态机、Hook direct replay、no-continuation、active lease、restart rehydrate 与 duplicate recovery；`CheckpointValidationResultV1` 必须覆盖 response-time/post-execution、缺证据与 timeout；`LocalTaskTraceV1` 必须覆盖 sequence/duplicate/terminal/restart、payload 不执行和 source state zero-write。触达 Profile/部署或 gray sidecar package 时再叠加 `profile-deploy / package-release`。Hook 无事件自唤醒能力未由宿主或 sidecar 实证时，coverageClaim 必须降级并保留 residualRisk。

`context-acquisition` 是同名 gateGroup 的领域绑定，不新增顶层 selector：契约、Profile/Memory MCP 或 Hook receipt 变化至少选择 `unit-integration + runtime-e2e`；规范、Prompt、README/website 或部署面变化叠加 `static`，触达 Profile/宿主分发时再叠加 `profile-deploy`。Owner evidence 至少链接 IntentSeed/plan/receipt correlation、**`ContextReadBindingV1`（request-bound；legacy-unbound 不得 claim complete）**、`ProfilePlanNoHiddenFullReadProbe`、bounded memory query、failed-Pre/false-complete 负例、legacy compatibility 与 V99。ValidationExecutionReceipt 须携带 **`testRouteDigest`** 与可选 **`intentExpansionDigest`**（PF-149），使 TestRoute 选择与上下文绑定可对账。性能证据记录 bytes/chars/latency/cache/escalation；input tokens 不可观测时必须标 N/A，不能用 chars 冒充。staged consumer 只允许精确列出 missing consumer 与后续 Owner batch，不得把 known-red 泛化为通过。

`executionChainOptimization` 是执行链性能与稳定演进的领域绑定：至少选择 `static + unit-integration + runtime-e2e`，执行 manifest 节点 `execution-chain-evolution`、V101、任务/Context/Profile/Skill/knowledge 的 full-only 负例，并逐一验证 `ExecutionOptimizationFeatureDecisionV1` 在 `off / shadow / rolled-back / sunset` 下真实阻断六类优化消费者。validation 的关闭路径必须是保留显式 intent/route 且禁用 cache/reuse 的 `direct-validation-plan`，不得转成 full；其余消费者走各自完整读取 fallback。触达 Profile/部署叠加 `profile-deploy`，公开 package/benchmark 脚本叠加 `package-release` smoke，网站说明变化追加 website build/link。只有安全 fallback、correctness oracle 和可比 benchmark 都通过才可声明 accepted；否则状态保持 `provisional`，但 `full-only` 正确路径必须继续为 green。

`derivedArtifactFreshness` 绑定 `skill-lifecycle` 或对应派生资产 Owner，不新增顶层 selector：至少选择 `static + unit-integration + runtime-e2e`，覆盖确定性生成、先生成后 stage 的负例、精确 staged/index candidate check 与 post-commit clean-tree replay；触达 Profile/部署副本叠加 `profile-deploy`，触达 package/release candidate 叠加 `package-release`。working-tree check、staged check 与 post-commit replay 必须分别记录 candidateState，不能复用一次结果冒充三种状态。

`repairPreventionAssessment` 绑定 active `repair-prevention-assessment`，不新增顶层 selector：所有 repair 至少选择 `static + unit-integration`，分别绑定当前 defect 的 regression/negative evidence 与 prevention decision 的 prospective/rollback evidence。高风险、repeat escape、emergency-active、控制面或公共契约再叠加 `runtime-e2e` 和适用的 package/profile 路线；当前修复重跑只能进入 immediate closure，不得填充 prospective effectiveness。返工指标或长期效果验证才额外绑定 gray `rework-prevention-engineering`。

`baseAdmissionGovernance` 绑定 `spec-absorption` / `skill-lifecycle-governance`，不新增顶层 selector：新增或晋级规范、Skill、Prompt、流程、验证器或部署消费者至少选择 `static + unit-integration`，并记录 `BaseImpactAssessmentV1`、`ComplexityDeltaBudgetV1`、`UnaffectedIntentRegression`、`replacementOrRetirementCredit`、回滚和退役/删除条件。`base-changing` 叠加 `runtime-e2e + profile-deploy`，且必须有单独确认和未受影响意图负样本；普通 chat/dev/fix 不因此增加默认验证路径。V96 负责正负向分类器证据。

`visibleOutputContract` 绑定 `user-visible-output-contract`，不新增顶层 selector：任何 `ArtifactDeliveryManifestV1`、`UserFacingArtifactSetV1`、`PostCompletionActionSetV1`、`EntryCheckModelV3`、`DevCodexVisibleEnvelopeV3`、`HostLinkCapabilityDecisionV2`、`ArtifactDeliveryAttemptV1`、V1/V2 兼容 parser、renderer 或 visible-reply Hook 变化至少选择 `static + unit-integration + runtime-e2e`，覆盖 planned/observed/internalDelivered 对账、required hidden=0、计数守恒、六 message kinds、完成态 requiredNow=0、动作适用性与授权负例、V1/V2 只读/V3 只写、PC0~PC10、presentationSurface 主选 renderer、renderer-only、action/readback、missing target、absolute fallback、compact eligibility、legacy/unobserved ceiling、semanticDigest 和 rich/portable/plain 等价性。触达 README/website/Profile/部署副本时叠加 `profile-deploy`，进入 package public surface 时叠加 `package-release`。宿主 capability 未 direct replay 时必须保持 ready/fallback 或 unverified；未同时观察 action 与 readback 成功不得声明 opened。

`evidenceFreshness` 绑定 `report` / `analyze-default` / `audit-report` / `review-checklist`，不新增顶层 selector：强主张新鲜度、summary-only 降级、外部 finding 采纳、artifact anchor 或 final validation summary 绑定变化至少选择 `static + unit-integration`，执行 `npm run test:evidence-freshness` 并在控制面或当前消费者同步时叠加 `node scripts/validate.js` 与 `profile-deploy`。若只是普通报告没有 strong claim，写 `N/A + skipReason=no-strong-claims`。

`expertOutputQuality` 绑定 `expert-output-quality` / V84，不新增顶层 selector：代码、文档、示例、fixture、技术方案或报告命中专家型质量时，至少选择 `static`（并优先 `node scripts/test-spec-governance.js` + 控制面变更时 `npm run test:core`），证据链覆盖 `ExpertOutputQualityGate`、生产推荐路径、fixture 边界与 **MeasuredVerificationStandard**（生产入口命令 + exitCode；隔离 harness 不得冒充 V84 成败）。Owner 字段与完整门禁见 `skills/expert-output-quality/SKILL.md` 与 gate-registry `expert-output-quality`。

`requirementParallelOrchestration` 绑定 `requirement-parallel-orchestration`，不新增顶层 selector：新增或修改多需求并行判定、`SharedSurfaceLockMapV1`、`ParallelLaunchCardV1`、`IntegrationMergeProtocolV1`、`HostSubagentDispatchPlanV1`、`AgentWorkLeaseV1`、`ChildAgentEvidenceV1`、取消围栏或根 Agent 汇合时，至少选择 `static + unit-integration`，执行 `npm run test:requirement-parallel-orchestration`。正负例必须覆盖 direct capability identity、absent/stale/docs-only、低收益、fanout≤4/depth=1、无 worktree、共享写入、dispatch/timeout/cancel/late、changed realpath/dirty/patch/test identity、owned resource cleanup、固定 merge order、root retest 与同 work graph 串行回退。只有实际宿主 spawn/wait/interrupt 和隔离结果均有 direct evidence 才可标该宿主并行 PASS；否则为 UNVERIFIED 且必须证明自动串行。触达 Skill portfolio、validation manifest、package/plugin、README/website/Profile 或部署副本时叠加 `control-plane / profile-deploy` 对应命令；缺任一关键负向探针不得声明可并行。

`capabilitySurfaceDecision` 只接受中央 `decisionRef / status / identity / preferredSurface / validationRoute` 的只读投影，不复制判定矩阵。新增或改变 Rule/Skill、Prompt、MCP Resource/Resource Template/Tool、Task 增强 Tool、CLI 或 Hook 时，至少选择 `static + unit-integration` 并执行 `npm run test:capability-surface-decision`；触达 MCP runtime、宿主 adapter、package、Profile、public docs 或部署副本时按 decision 的 `validationRoute` 叠加 `runtime-e2e / package-release / profile-deploy`。decision 缺失、stale、blocked 或 identity 不匹配时不得降级为 skipped。

### 发布候选验证

`package-release` selector 可在 V2 只表示 package compatibility 边界；它不会执行 tag、publish 或 registry mutation。只有 release-pipeline 在当前发布授权下取得 purpose=`release`、level=`V3` 的 LeaseV2，才必须调用 `release-verification`，并把 `npm run test:audit`、package completeness gate、publish dry-run、ExactReleaseArtifactV1 和远端 CI 作为独立证据记录。publish dry-run 只验证发布通道、registry 与候选元数据，不等价于真实 publish，也不能替代 exact artifact、远端 CI 或发布后 registry 对账。pack 或本地 install 通过不能替代远端 CI，也不能替代发布前的 package completeness gate；未形成真实发布候选时必须记录 skipReason，不得把普通开发验证写成发布完成。

### CI 影响面执行

远端 CI 必须从 `scripts/validation-manifest.json` 的同一 `ValidationImpactGraphV2` 生成 `CiValidationPlanV1`，不得在 workflow 内维护第二套路经分类。普通 push/PR/手动 affected 只执行计划选中的节点；package/installer/host activation 变更才追加 package boundary。nightly、release、用户手动 full，以及 changed-file 截断、impact graph 不完整或 planner blocker 才进入 full。planner 异常必须使 aggregate BLOCK 并公开原因，禁止静默改成无条件 full。

`CiValidationAggregateReceiptV1` 必须对账 planner digest、目标 HEAD、required node receipt 与各条件 job 结果。同一 SHA 已有可信 receipt 时，本地不得机械重复等价 full、pack 或 install；修复 CI 失败时默认只重跑失败节点及其失效依赖，除非实际边界或候选身份已经改变。

## TestRoute 输出

输出固定为 `verificationIntent / validationPlan / budgetCard / verificationExecutionLease / validationExecutionReceipt / selectedRoutes / commands / evidence / skipped / residualRisk / coverageClaim`。推荐使用以下最小结构：

```yaml
workflow: fix
changeTypes: [control-plane, documentation]
risk: high
verificationIntent:
  schemaVersion: VerificationIntentV2
  requesterClass: ai
  requestedLevel: V2
  requestedPurpose: boundary
  affectedBoundaries: [validation-control-plane]
  riskClass: high
  requestSourceRef: confirmed-CP3
  project: devcodex
  candidateId: <candidate-id>
  changedScopeDigest: <sha256>
  claimCeiling: boundary-qualified
  requestDigest: <sha256>
validationPlan:
  schemaVersion: ValidationPlanV3
  executionState: ready
budgetCard:
  schemaVersion: BudgetCardV1
  confirmationRequired: false
verificationExecutionLease:
  schemaVersion: VerificationExecutionLeaseV2
  runIdentitySchema: ValidationRunIdentityV1
  runId: <stable-run-id>
  hardDeadlineAt: <iso-time>
validationExecutionReceipt:
  runnerSchema: ManagedValidationRunnerV2
  terminalState: passed
selectedRoutes:
  - selector: static
    commands: [npm run test:control-plane]
    evidence: [{ command: npm run test:control-plane, exitCode: 0 }]
  - selector: profile-deploy
    commands: [node scripts/validate-all-profiles.js]
    evidence: [{ profileValidation: passed, deploymentParity: passed }]
skipped:
  - route: runtime-e2e
    reason: no runtime user path changed
    authority: source diff and execution contract
    residualRisk: none identified
    upgradeCondition: runtime boundary enters scope
coverageClaim: executed-selected-routes
```

## 跳过规则

每条跳过记录必须包含 `route / reason / authority / residualRisk / upgradeCondition`。缺少任一字段、用“暂不需要”作理由、或目标声明需要该路线却没有可替代证据时，TestRoute 不得标记完整。

高风险、公共契约、控制面、发布候选和跨宿主分发变更至少要求两类独立证据；若无法执行，结果必须降级为 partial/blocked，并把升级条件带入报告与 `ContextHandoffCard`。

## 报告要求

dev/fix/optimization/scenario-test 报告应包含 TestRoute 或明确 `N/A`，ECR-3/ECR-4 应引用实际执行结果。若命中宿主验证，还应同时引用 `host-contract-verification` 的 HostContractRoute 结果，包括适用时的 `mcpFallback=used`；若命中用户可见输出，必须引用 manifest/visible set/envelope/capability/attempt 的同一 semanticDigest、action/readback/fallback 与 renderer parity 结果。
