arborist/aborist/qa
russell@unturf.com c36e85c86c
qa(#000009): preflight stage binds into run_dag_root
Closes ticket #000009 zero-shot. Scope expanded to cover BOTH
ticket #000008 (broad-quantifier) AND ticket #000010
(meta-cognition) preflight contracts in a single combined node —
both share the same audit-replay gap and inserting two separate
nodes between question and retrieval was operationally awkward.

aborist/qa/dag.py:
  + preflight_node_hash() — combines QuestionState +
    quantifier classifier output + behavioral policy_state into
    one canonical SHA-256 hex.
  + build_run_dag() gains optional preflight_hash parameter.
    When supplied, inserts {"stage": "preflight", "hash": ...}
    at position 1 (between question and retrieval).
    Backward-compat: None → original 7/9-stage shapes preserved
    for legacy run_dag_root re-validation.

  Quote-mode: 7 → 8 stages with preflight.
  Pointer-mode CTI: 9 → 10 stages with preflight.

aborist/qa/query.py + runner.py:
  Both build the preflight payload from question_state +
  quantifier dict + 10-field policy_state (guard_enabled,
  guard_apply_caps, guard_apply_caps_mode_gated,
  claim_cap_resolved, claim_cap_actually_applied,
  reminder_enabled, reminder_eligible, reject_broad_active,
  metacognition_enabled, block_on_contradiction).

  This means two cache rows that share the same question + same
  model output + same verifier verdict but DIFFERENT preflight
  policy state now produce different run_dag_root values. Audit
  replay can pin the policy decision per row.

9 new tests in tests/test_dag.py:
  - preflight_node_hash determinism
  - hash bumps on question_state change
  - hash bumps on policy_state change (the audit-replay payoff)
  - all-None defensive shape
  - 7→8 stage transition (quote mode)
  - 9→10 stage transition (pointer mode), preflight at index 1
  - run_dag_root bumps when preflight_hash bumps
  - verify_run_dag round-trips through preflight stage

Live verification: latest providence_cache row carries
['question', 'preflight', 'retrieval', 'evidence_map', 'prompt',
'raw_answer', 'parsed_claim_lattice', 'verify', 'render',
'final_label'] — preflight stage living in the persisted DAG.

987 tests passing (9 new); 36 skipped.

Tickets:
  #000009 status: closed · landed 2026-05-03 (zero-shot)
  #000010 cross-ref updated: "DAG binding shipped via #000009"

What's NOT in this ticket (logged in §7.2):
  - CLI flag for inspecting preflight node from cache_key
  - Bench harness preflight_hash field for cross-row comparison
  - SOFT_PREFLIGHT_HINT (model-assisted preflight sidecar)
2026-05-03 18:34:16 -04:00
..
__init__.py multi-source corpus query: pose a question, the tree pulls related cached docs 2026-04-27 11:43:57 -04:00
client.py qa(client): scrub lone UTF-16 surrogates before httpx encode 2026-05-02 11:58:25 -04:00
concepts.py qa(concepts): corpus-derived concept_relations table replaces frozensets 2026-05-01 21:14:53 -04:00
dag.py qa(#000009): preflight stage binds into run_dag_root 2026-05-03 18:34:16 -04:00
evidence.py speed: pytest-xdist, bench smoke, concurrency default; UTF surrogate fix 2026-05-02 09:29:40 -04:00
frame.py docs: tickets/ subdir + delete naming-deferral, ref-frame, coverage-audit 2026-05-02 12:51:09 -04:00
inspect.py qa(inspect): wordlist union (US+UK) + configurable supplemental dictionaries 2026-05-02 14:27:24 -04:00
keys.py qa(#000010): Phases 2-4 land — wired, governed, labeled, benched 2026-05-03 18:22:18 -04:00
metacognition.py qa(#000010): Phase 1 — metacognition.py module + 42 tests 2026-05-03 18:10:50 -04:00
model_profiles.py qa(#000008): Phase 2 — model-profile caps + governance hash (dry-run) 2026-05-03 07:30:28 -04:00
parse_claims.py qa: G0 claim-lattice-pointer answer mode — CTI quote-by-pointer 2026-04-29 21:19:19 -04:00
prompts.py docs(bench): rename qa-modes-bench-2026-05-02.md → qa-modes-bench.md 2026-05-02 12:27:38 -04:00
quantifier.py qa(#000008): classifier fix — count-question short-circuit + bounded fixtures 2026-05-03 08:29:36 -04:00
quantifier_reminder.py qa(#000008): Phase 3 — broad-quantifier reminder mechanism (default off) 2026-05-03 07:33:40 -04:00
query.py qa(#000009): preflight stage binds into run_dag_root 2026-05-03 18:34:16 -04:00
repair.py qa: chain-segment failure localization + re-prompt repair tier 2026-04-29 19:27:33 -04:00
retrieval_plan.py docs: tickets/ subdir + delete naming-deferral, ref-frame, coverage-audit 2026-05-02 12:51:09 -04:00
runner.py qa(#000009): preflight stage binds into run_dag_root 2026-05-03 18:34:16 -04:00
verify.py qa(verify): FORMAT_COLLAPSED soft-demote + open #000008 (broad-quantifier preflight) 2026-05-02 16:43:41 -04:00
warrant.py docs: tickets/ subdir + delete naming-deferral, ref-frame, coverage-audit 2026-05-02 12:51:09 -04:00