schema_version: 1
case_id: cpu-spike-during-incident-window
ground_truth:
  root_cause: TiDB node count reduction increased per-node load; plan cache hit=0 and similar signals were not the differentiating primary root cause
judge:
  semantic_strategy: undecided
  contract:
    checks:
    - id: time_window_match
      required: true
      description: Actual diagnosis time window matches one allowed case window.
      match: all_of
      rules:
      - source: trace_fact
        matcher: bool_true
        path: time_window_match
    - id: cluster_scope_match
      required: true
      description: Actual diagnosis cluster scope matches expected cluster id.
      match: all_of
      rules:
      - source: trace_fact
        matcher: bool_true
        path: cluster_scope_match
  workflow:
    checks:
    - id: architecture_context_loaded
      weight: 5
      required: true
      description: Architecture context is loaded.
      match: all_of
      rules:
      - source: workflow_fact
        matcher: bool_true
        path: architecture_context_loaded
    - id: misdiagnosis_guardrails_loaded
      weight: 5
      required: true
      description: Misdiagnosis guardrails are loaded.
      match: all_of
      rules:
      - source: workflow_fact
        matcher: bool_true
        path: misdiagnosis_guardrails_loaded
    - id: diagnosis_run_initialized
      weight: 5
      required: true
      description: Diagnosis run is initialized.
      match: all_of
      rules:
      - source: workflow_fact
        matcher: bool_true
        path: diagnosis_run_initialized
    - id: evidence_collection_completed
      weight: 10
      required: true
      description: Evidence collection completes.
      match: all_of
      rules:
      - source: workflow_fact
        matcher: bool_true
        path: evidence_collection_completed
    - id: summary_read_completed
      weight: 5
      required: false
      description: Summary artifact is read when available.
      match: all_of
      rules:
      - source: workflow_fact
        matcher: bool_true
        path: summary_read_completed
    - id: final_answer_emitted
      weight: 0
      required: false
      description: Final answer is emitted.
      match: all_of
      rules:
      - source: workflow_fact
        matcher: bool_true
        path: final_answer_emitted
  execution_contract:
    checks:
    - id: required_workflow_present
      required: true
      description: Required workflow milestones are present.
      match: all_of
      rules:
      - source: workflow_check
        matcher: subset_observed
        paths:
        - architecture_context_loaded
        - misdiagnosis_guardrails_loaded
        - diagnosis_run_initialized
        - evidence_collection_completed
    - id: required_artifacts_present
      required: true
      description: Required evidence artifacts are present.
      match: all_of
      rules:
      - source: artifact_fact
        matcher: subset_observed
        paths:
        - core_evidence_bundle_present
    - id: preferred_channels_minimum_observed
      required: true
      description: At least one preferred evidence channel is observed.
      match: all_of
      rules:
      - source: channel_fact
        matcher: count_gte
        paths:
        - query_or_metric
        threshold: 1
    - id: preferred_channels_all_observed
      required: false
      description: Preferred evidence channels are all observed.
      match: all_of
      rules:
      - source: channel_fact
        matcher: subset_observed
        paths:
        - query_or_metric
  evidence_collection:
    weight: 5
    checks:
    - id: required_artifacts_present
      weight: 4
      required: true
      description: Required evidence artifacts are present and readable.
      match: all_of
      rules:
      - source: artifact_fact
        matcher: subset_observed
        paths:
        - core_evidence_bundle_present
    - id: preferred_channels_minimum_observed
      weight: 0
      required: true
      description: At least the minimum number of preferred evidence channels are observed.
      match: all_of
      rules:
      - source: channel_fact
        matcher: count_gte
        paths:
        - query_or_metric
        threshold: 1
    - id: preferred_channels_all_observed
      weight: 6
      required: false
      description: Preferred evidence channels are all observed.
      match: all_of
      rules:
      - source: channel_fact
        matcher: subset_observed
        paths:
        - query_or_metric
  critical_evidence:
    weight: 30
    checks:
    - id: primary_symptom_evidence_present
      weight: 8
      description: Primary CPU hotspot evidence is present for the incident window.
      match: any_of
      rules:
      - source: artifact_json
        artifacts:
        - gate_checks
        matcher: json_text_contains_any
        keywords:
        - HOTSPOT
        - CPU
      - source: artifact_json
        artifacts:
        - metrics
        matcher: json_text_contains_any
        keywords:
        - process_cpu_seconds_total
        - tidb_cpu
        - cpu
    - id: capacity_comparison_context_present
      weight: 17
      description: Evidence includes baseline-vs-incident capacity comparison context.
      match: any_of
      rules:
      - source: artifact_json
        artifacts:
        - cluster_meta
        matcher: json_path_unique_count_gte
        path: cluster.components.COMPONENT_TYPE_TIDB.tidbGroups.replicas
        threshold: 2
      - source: artifact_json
        artifacts:
        - workspace_report
        matcher: json_path_unique_count_gte
        path: metadata.cluster.components.COMPONENT_TYPE_TIDB.tidbGroups.replicas
        threshold: 2
      - source: artifact_json
        artifacts:
        - summary_json
        matcher: json_text_contains_any
        keywords:
        - per-node
        - node count
        - fewer nodes
        - same workload
        - baseline
        - comparison
  evidence_routing:
    weight: 5
    checks:
    - id: analysis_patch_valid_json
      weight: 1
      required: true
      description: Analysis patch is valid JSON whenever the patch exists.
      match: any_of
      rules:
      - source: routing_fact
        matcher: bool_false
        path: analysis_patch_present
      - source: routing_fact
        matcher: bool_true
        path: analysis_patch_valid_json
    - id: analysis_items_shape_valid
      weight: 2
      required: true
      description: Analysis patch items are structurally valid whenever the patch exists.
      match: any_of
      rules:
      - source: routing_fact
        matcher: bool_false
        path: analysis_patch_present
      - source: routing_fact
        matcher: bool_true
        path: analysis_items_shape_valid
    - id: analysis_key_evidence_present
      weight: 2
      required: true
      description: Analysis patch includes key evidence whenever the patch exists.
      match: any_of
      rules:
      - source: routing_fact
        matcher: bool_false
        path: analysis_patch_present
      - source: routing_fact
        matcher: bool_true
        path: analysis_has_key_evidence
    - id: metric_refs_resolvable
      weight: 1
      required: true
      description: Metric references resolve whenever the patch requires them.
      match: any_of
      rules:
      - source: routing_fact
        matcher: bool_false
        path: analysis_metric_refs_required
      - source: routing_fact
        matcher: bool_true
        path: analysis_metric_refs_resolvable
    - id: query_refs_resolvable
      weight: 1
      required: true
      description: Query references resolve whenever the patch requires them.
      match: any_of
      rules:
      - source: routing_fact
        matcher: bool_false
        path: analysis_query_refs_required
      - source: routing_fact
        matcher: bool_true
        path: analysis_query_refs_resolvable
    - id: supporting_evidence_present
      weight: 0
      required: false
      description: Analysis patch includes supporting evidence whenever the patch exists.
      match: any_of
      rules:
      - source: routing_fact
        matcher: bool_false
        path: analysis_patch_present
      - source: routing_fact
        matcher: bool_true
        path: analysis_has_supporting_evidence
    - id: analysis_log_or_event_channel_resolvable
      weight: 0
      required: false
      description: Routed log or event items stay resolvable against observed channels.
      match: all_of
      rules:
      - source: routing_fact
        matcher: bool_true
        path: analysis_log_or_event_channel_resolvable
  diagnosis:
    checks:
    - id: identify_capacity_shift_as_primary_driver
      mode: blended
      weight: 3
      required: true
      description: Identifies a capacity or node-count shift as the primary driver of the CPU increase.
      deterministic:
        match: any_of
        rules:
        - source: output_text
          matcher: keyword_any
          keywords:
          - node count reduction
          - fewer TiDB nodes
          - scale-in
          - capacity reduction
          - 节点数减少
          - 实例数减少
          - 节点数量下降
      semantic:
        id: 1
        title: 主路径判断
        description: |
          判断回答是否把主路径识别为容量变化或节点数下降导致的 per-node load increase，
          而不是把 plan cache hit=0 当成主根因。
    - id: reject_plan_cache_or_secondary_signals_as_primary_root_cause
      mode: blended
      scoring_mode: penalty
      weight: 3
      required: false
      description: Avoids elevating plan cache hit=0 or similar secondary signals into the primary differentiating root cause.
      deterministic:
        match: any_of
        rules:
        - source: output_text
          matcher: keyword_any
          keywords:
          - plan cache hit=0 is expected
          - plan cache miss is expected
          - plan cache is expected
          - plan cache is not the root cause
          - plan cache is not the differentiator
          - plan cache miss is not the root cause
          - plan cache miss is not the differentiator
          - plan cache 不是根因
          - plan cache miss 不是根因
        - source: output_text
          matcher: regex_any
          patterns:
          - "plan cache.{0,80}(not the root cause|not the differentiator|expected|normal)"
          - "(not the root cause|not the differentiator).{0,80}plan cache"
          - "plan cache.{0,80}(不是根因|不是差异因子|属于正常现象)"
          - "(不是根因|不是差异因子).{0,80}plan cache"
          - "(cache miss|plan cache miss).{0,80}(not the root cause|not the differentiator|expected|normal)"
          - "(不是根因|不是差异因子).{0,80}(cache miss|plan cache miss)"
          - "(secondary symptom|false lead).{0,80}(plan cache|cache miss)"
      semantic:
        id: 2
        title: 误导线索排除
        description: |
          判断回答是否避免把 plan cache hit=0、plan cache miss 或类似次级现象抬升为 primary cause。
          回答可以不显式提到 plan cache；只要主因稳定落在容量/节点数变化上，且没有把这些次级现象
          当成主因，即可判为通过。若回答明确将其视为 false lead / secondary symptom，也应判为通过。
    - id: recommend_capacity_adjustment_over_cache_tuning
      mode: blended
      weight: 3
      required: false
      description: Recommends capacity adjustment rather than plan cache tuning.
      deterministic:
        match: any_of
        rules:
        - source: output_text
          matcher: keyword_any
          keywords:
          - scale TiDB back
          - restore node count
          - capacity adjustment
          - 扩容
          - 恢复节点数
        - source: output_text
          matcher: regex_any
          patterns:
          - "(do not|don't|should not).{0,40}(fix|tune).{0,20}plan cache"
          - "不要.{0,20}(修|调).{0,20}plan cache"
      semantic:
        id: 3
        title: 修复建议对齐
        description: |
          判断回答是否给出容量调整、workload redistribution 等对齐 remediation，
          而不是优先建议做 plan cache tuning。
