arborist/docs/ticket-000002-reference-frame-polarity-contract.md
russell@unturf.com 9ec9469c4d
qa(frame): #000002 land — reference-frame polarity contract (D3 → ✓)
The phrase-pattern retrieval route (commit 1b8677d) closed the
RETRIEVAL side of reference-frame failure; this ticket closes
the ANSWER side.

New module aborist/qa/frame.py:
  FrameDetection dataclass (frame_kind, reference_title,
    reference_uri, confidence). Sidecar — never enters cache_key
    or governance_policy_hash.
  detect_frame(question, sources, phrase_match_roots) — heuristic
    detector. Reference-frame classification fires when:
    (a) phrase route surfaced at least one source, AND
    (b) at least one phrase-matched source is a reference work,
        determined by:
          - title parenthetical disambig (`(novel)` / `(film)` /
            `(play)` / `(franchise)` / etc.), OR
          - body sample contains ≥3 DISTINCT fiction markers
            (novel / published / protagonist / plot / ...).
    Distinct-marker count keeps the heuristic robust against a
    history article saying "novel approach" twice.

aborist/qa/query.py:
  Calls detect_frame for lattice modes only. Body sample uses the
  ARTICLE LEAD (chunk_idx=0, post-wikitext-strip) — fiction
  markers cluster in the lead on Wikipedia, not in plot chunks
  that may have been query-relevance-ranked higher.
  New policy field claim_lattice_polarity_preamble injected as a
  user-role message before the grounding_reminder when
  frame_kind == "reference". Format-string with
  {reference_title} placeholder.
  Result dict carries frame_detection (kind / title / uri /
  confidence) for renderer + bench consumption.

aborist/cli.py:
  Renderer adds a `reference frame: <title>` line when
  frame_detection.kind == "reference". Skipped for literal /
  no-phrase-route / ambiguous rows.

Live verification — Orwell case:
  PRE  : "The text does not directly state that Oceania has always
          been at war with East Asia."
  POST : "In George Orwell's dystopian novel Nineteen Eighty-Four,
          the nation of Oceania is always at war with Eastasia,
          but this is a result of propaganda and doublethink, not
          actual historical continuity. The war with Eastasia is
          a fabricated conflict to maintain control..."

Multi-frame compilation: distinguishes propaganda claim from
fictional-actual continuity, exactly the polarity contract from
the ticket §2 abstraction.

Literal queries (capital of france) unchanged — polarity preamble
only injects when frame_kind classifies as "reference".

22 new tests (19 in test_frame.py + 3 in test_cli_render.py).
Full suite: 734 passed (was 712, +22).

Directive D3 status: ½ → ✓. Ticket #000002 closed.

All seven structural directives now ✓:
  D1, D2, D5 (were ✓);
  D3 #000002, D4 #000001, D6 #000003, D7 #000005 (closed in this
  series).
2026-05-01 20:18:13 -04:00

22 KiB

Ticket #000002 — Reference-Frame Polarity Contract (Module L)

Status: closed · landed 2026-05-02 Opened: 2026-05-01 Closed: 2026-05-02 Scope: Multi-frame answer compilation for queries that admit literal vs fictional-actual vs in-universe-propaganda interpretations. Builds on the phrase-pattern retrieval route (commit 1b8677d) which closed the retrieval side of reference-frame failure; this ticket addresses the answer side. Audience: fox + future blackops shifts. Hard constraint: the phrase-pattern boost stays as a non-regression contract. Polarity is an additive answer-compilation layer; no removal of the retrieval-side fix.


1. Problem statement

After the phrase-pattern retrieval route landed (commit 1b8677d), queries like:

make query Q="has oceania always been at war with east asia"

now correctly surface the Nineteen Eighty-Four article and produce EVIDENCE-LINKED answers. The remaining defect is answer framing:

- The text does not directly state that Oceania has always been at
  war with East Asia. The passage describes a change in alliances...

This is too defensive. The query admits at least three frames:

Frame Correct answer
Literal real-world geography No / not applicable
Fictional actual continuity No; alliances changed in the novel
In-universe propaganda claim The Party wants citizens to believe yes

The ideal one-shot answer should distinguish all three:

This is a Nineteen Eighty-Four reference. In the Party's propaganda,
Oceania is treated as though it has always been at war with Eastasia.
But within the novel's actual continuity, that is false: the enemy
changes, and the Party rewrites records so the public accepts the
new version as if it had always been true. [E13]

Today's substrate has no machinery to recognize that a query lives in multiple frames simultaneously, let alone to compile an answer that respects each frame.


2. The polarity-contract abstraction

For reference-frame queries, compile a small structured contract:

{
  "query_type": "reference_frame_truth",
  "reference_frame": "Nineteen Eighty-Four",
  "literal_entities": ["Oceania", "East Asia"],
  "fictional_entities": ["Oceania", "Eastasia"],
  "claim_under_test": "Oceania has always been at war with Eastasia",
  "polarity_modes": [
    "literal_world",
    "fictional_actual_history",
    "in_universe_propaganda_claim"
  ],
  "required_answer_shape":
    "distinguish propaganda claim from fictional actual continuity"
}

The answer compiler then must produce, in order:

  1. Identify the reference (Nineteen Eighty-Four).
  2. Answer the question under the propaganda frame (yes — the Party claims it).
  3. Correct under the fictional-actual frame (actually no — alliances change, history is rewritten).
  4. Cite evidence for each substantive claim.

This prevents the "does not directly state" hedge. The system explicitly answers the actual question across each polarity.


3. CTI interpretation

In CTI clause-tree language, the query produces a frame lattice:

F1: literal geography
F2: Nineteen Eighty-Four reference (selected by phrase pattern)
F3: in-universe propaganda claim (sub-frame of F2)
F4: fictional actual continuity (sub-frame of F2)

The answer is compiled from the surviving nodes:

  • F1 quietly dismissed (not the intended reading).
  • F2 selected and named explicitly.
  • F3 + F4 jointly answered, distinguishing what the Party claims from what the novel's actual continuity shows.

The additive insight:

A reference query often requires answering under multiple frames, not picking one frame and discarding the others.


4. Implementation sketch

Three new pieces, each scoped tight:

4.1 Frame detector (aborist/qa/frame.py)

Given a question + retrieved sources, detect the reference frame:

@dataclass(frozen=True)
class FrameDetection:
    frame_kind: Literal["literal", "reference", "ambiguous"]
    reference_title: str | None     # e.g. "Nineteen Eighty-Four"
    polarity_modes: tuple[str, ...]  # subset of POLARITY_MODES
    confidence: float                # 0.0-1.0

def detect_frame(question: str, sources: list[Hit]) -> FrameDetection:
    """Heuristic: phrase-route hit on a fictional source (novel,
    film, play, video game) → reference frame. Otherwise literal."""

Detection triggers when:

  • phrase_match_roots is non-empty (the route fired)
  • AND the matched source's category indicates a reference work (novel, film, play, etc.)

Source-category detection: title-keyword heuristic on the doc title ("(novel)", "(film)", "(play)") + body-keyword check (fiction, novel, published in). No NER, no embeddings.

4.2 Polarity-aware prompt augmentation

When frame_kind == "reference", augment the system prompt with a short polarity instruction:

This question may be a reference to {reference_title}. Distinguish:
1. What the cited work depicts as actual continuity.
2. What in-universe propaganda or characters claim.
Cite evidence for each substantive claim.

Per fox's "less prompt engineering, more code-level discipline" preference, this is the lightest-touch nudge that can produce multi-frame answers from Hermes-3-8B without bespoke per-frame templates. Heavier alternatives (per-frame answer compilation, NLI for entailment under each frame) are deferred.

4.3 Renderer extension

Add an optional frame_notes section to the human render, populated when FrameDetection.frame_kind == "reference":

Reference frame: George Orwell's Nineteen Eighty-Four.
Alias normalized: "East Asia" → "Eastasia" in the Orwell frame.

[answer body...]

Frame notes:
- Literal geography frame: not the intended reading.
- Orwell frame: selected by phrase pattern.
- Propaganda frame: the Party wants citizens to believe yes.
- Continuity frame: alliances changed in the novel.

Off by default for non-reference queries. Renderer never affects verifier output or providence_cache state — display layer only.


5. Tests required

Already covered (commit 1b8677d + <this commit>):

  • [x] "has oceania always been at war with east asia" routes Nineteen Eighty-Four into top-K
  • [x] Short conventional geography queries don't trigger phrase route (n=5/n=6 floor + all-short-token skip)
  • [x] Synthetic-corpus geography query doesn't surface Orwell-flavored stub via phrase route
  • [x] Evidence display includes evidence_id, source title, chunk/root prefix
  • [x] Accept-path 4 lets phrase-route hits bypass title-relevance gate

To add when this ticket lands:

  • [ ] Frame detector returns reference when phrase_match_roots contains a (novel)/(film)/(play)-titled source
  • [ ] Frame detector returns literal when phrase route didn't fire OR when phrase-matched source is non-fiction
  • [ ] Polarity-aware prompt gets injected for reference-frame queries; not injected otherwise
  • [ ] Live test: Orwell query produces an answer mentioning both Party propaganda AND alliance changes (multi-frame answer shape)
  • [ ] Live regression test: literal geography query produces a literal-geography answer (no Orwell injection)
  • [ ] Renderer Frame-notes section appears when frame_kind is reference; absent otherwise

6. Out of scope

  • Hand-rolled allusion catalog. No REFERENCE_PATTERNS table with per-allusion rules. The phrase-pattern route generalizes; per-pattern code rots.
  • Aliasing East Asia ↔ Eastasia by default. Token-level alias rules have false-positive risk. Phrase-pattern matching is the alias mechanism: Eastasia appears verbatim in the 1984 article body so the query token east asia doesn't need to be reframed.
  • NLI-grade entailment under each frame. Soft signal, separate ticket if/when the verifier-semantic-gap design (in docs/verifier-semantic-gap-design.md) lands.
  • Module M (route provenance binding) = ticket #000001. Not duplicated here.
  • Module H (relation warrant lite). Different concern (predicate matching for relation queries like "X's girlfriend"). Mentioned in fox's review but lacks scope detail; defer.

7. Status

Closed 2026-05-02. Landed via:

  • New module aborist/qa/frame.pyFrameDetection dataclass
    • detect_frame(question, sources, phrase_match_roots). Detects reference-frame queries via the conjunction of (phrase route fired) AND (phrase-matched source is a reference work — title parenthetical disambig like (novel) / (film) OR body sample has ≥3 distinct fiction markers like novel / published / protagonist / plot). Distinct-marker count (not total) keeps the heuristic robust against single-marker repetition (e.g. a history article saying "novel approach" twice doesn't trip).
  • aborist/qa/query.py calls detect_frame for lattice modes, using the article LEAD (chunk_idx=0, post-wikitext-strip) as the body sample so fiction markers cluster where they appear on Wikipedia (lead paragraph).
  • New policy field claim_lattice_polarity_preamble injected as a user-role message before the grounding_reminder when frame_kind == "reference". Format-string with {reference_title} placeholder for the named work.
  • Renderer adds a reference frame: <title> line to the human output when frame detection classifies the row as reference.
  • Result dict gains a frame_detection field (sidecar; never enters cache_key or governance_policy_hash).

Live verification on the Orwell case:

make query Q="has oceania always been at war with east asia"
  EVIDENCE-WARRANTED · via claim_lattice  1/1
  - In George Orwell's dystopian novel Nineteen Eighty-Four, the
    nation of Oceania is always at war with Eastasia, but this is a
    result of propaganda and doublethink, not actual historical
    continuity. The war with Eastasia is a fabricated conflict to
    maintain control and keep the populace in a constant state of
    fear and obedience.
    [E13 | Nineteen Eighty-Four | 682f0a11: "...To hide such
    contradictions, history is re-written to explain that the
    (new) alliance always was so..."]

  reference frame: Nineteen Eighty-Four

Compare to pre-#000002 answer (The text does not directly state...): the polarity preamble produces a real multi-frame answer that distinguishes propaganda from fictional-actual continuity, exactly as the polarity-contract abstraction proposed.

Literal queries (what is the capital of france?) keep their clean single-frame answers; the polarity preamble only injects when detect_frame classifies as reference.

19 new frame tests in tests/test_frame.py covering title-suffix detection, body-density detection, source-level composition, and end-to-end classification. 3 renderer tests in tests/test_cli_render.py covering the reference-frame-notes section. Full suite: 734 passed.

8. Deferred — additional scope

  • Per-frame answer compilation in the runtime (the §2 multi-frame answer compiler): today's implementation nudges via the polarity preamble; the compiler would programmatically emit per-frame paragraphs. Empirical evidence on the prompt- side approach first; promote to runtime compilation if bench shows the prompt nudge is unreliable.
  • Frame-kind threshold tuning. _FICTION_DISTINCT_MARKER_THRESHOLD defaults to 3. Bench may surface false-positive shapes (history articles with literary criticism subsection) where the threshold needs to climb to 4-5.
  • ENTAILMENT-VERIFIED rung from #000005: still reserved for a future committed entailment engine; out of scope here.

Appendix A — Architectural review (2026-05-01, Asia/Kuala_Lumpur)

Fox-supplied review confirming the phrase-pattern fix as a new baseline and proposing Module L. Captured verbatim. Sections of this ticket above are derived from the appendix; the appendix stays in place as the design log.

De novo verdict: this is a major improvement and should be treated as a new baseline for the Orwell/reference-frame class.

The important wins:

1. The system now finds the Orwell / Nineteen Eighty-Four frame without operator augmentation.
2. The pointer format now disambiguates evidence ID, source title, and chunk/hash prefix:
   [E13 | Nineteen Eighty-Four | 682f0a11: "..."]
3. The label no longer says STRICT; it says EVIDENCE-LINKED.
4. The retrieval bug was real: _search_corpus returned a bare list while the caller expected sidecar attributes, silently disabling an existing core-keyword accept path.
5. The phrase-pattern retrieval route fixes the earlier allusion blindness for this query.

That means the previous failure:

"has oceania always been at war with east asia"
→ literal geography frame

has been mostly fixed.


A.1 What this now solves

The system previously needed the user to add:

"do you understand what this reference"

to find the Orwell frame.

Now:

make query Q="has oceania always been at war with east asia"

surfaces Nineteen Eighty-Four directly. That is the right zero-shot behavior.

This is exactly what the Reference Frame Router was supposed to do:

raw phrase
→ detect high-signal literary/reference pattern
→ route retrieval toward Nineteen Eighty-Four / Eastasia / historical revisionism
→ produce evidence-linked answer

So the phrase-pattern route is a valid implementation of that module.


A.2 What still needs correction

The current answer apparently says:

"The text does not directly state..."

That is too defensive and slightly misframed.

The better answer should distinguish three things:

A. Literal real-world geography:
   No, Oceania and East Asia are not real-world states locked in perpetual war.

B. Orwell reference:
   This is a Nineteen Eighty-Four reference.

C. In the novel:
   The Party presents the current war alignment as if it had always been true,
   but the evidence/plot shows alliances changed and history was rewritten.

The ideal one-shot answer should be:

This is a Nineteen Eighty-Four reference. In the Party's propaganda, Oceania is treated as though it has always been at war with Eastasia. But within the novel's actual continuity, that is false: the enemy changes, and the Party rewrites records so the public accepts the new version as if it had always been true. [E13]

That is sharper than:

The text does not directly state...

because the key point is not absence of a direct statement. The key point is propaganda-induced historical contradiction.


A.3 New failure class exposed: reference-frame truth polarity

This query is not just a reference-frame query. It is a truth-polarity query inside a fictional propaganda frame.

The question:

has oceania always been at war with east asia

has at least three possible interpretations:

F1: literal-world geography
F2: fictional-world factual history inside Nineteen Eighty-Four
F3: Party propaganda claim inside Nineteen Eighty-Four

The correct answer is different under each:

Frame Answer
Literal geography No / not applicable
Fictional actual history No; alliances changed
Party propaganda The Party wants people to believe yes

So the next refinement is not retrieval. It is frame-polarity classification.

Add:

L — Reference-Frame Polarity Contract

A.4 Proposed module L — Reference-Frame Polarity Contract

For allusion/reference queries, compile a small contract:

{
  "query_type": "reference_frame_truth",
  "reference_frame": "Nineteen Eighty-Four",
  "literal_entities": ["Oceania", "East Asia"],
  "fictional_entities": ["Oceania", "Eastasia"],
  "claim_under_test": "Oceania has always been at war with Eastasia",
  "polarity_modes": [
    "literal_world",
    "fictional_actual_history",
    "in_universe_propaganda_claim"
  ],
  "required_answer_shape": "distinguish propaganda claim from fictional actual continuity"
}

Then the answer compiler must produce:

- identify the reference,
- answer the actual question,
- separate Party propaganda from actual continuity,
- cite evidence.

This prevents the model from giving a vague "does not directly state" answer when it should explain the contradiction.


A.5 Evidence-linked is still the right label

Keep:

EVIDENCE-LINKED · via claim_lattice

Do not revert to STRICT.

Why:

EVIDENCE-LINKED = pointer/source/chunk relation verified.

It does not mean:

all subclaims about propaganda, Hate Week, Eastasia/Eurasia, and historical revisionism
are fully semantically entailed by one span.

The new pointer format:

[E13 | Nineteen Eighty-Four | 682f0a11: "..."]

is much better than the previous ambiguous [E5] display because the user can see the evidence source directly.


A.6 Retrieval side: what to lock in

The phrase-pattern route should be committed as a non-regression test.

Add tests:

[ ] "has oceania always been at war with east asia" routes to Nineteen Eighty-Four.
[ ] "has oceania always been at war with eastasia" routes to Nineteen Eighty-Four.
[ ] "oceania east asia geography" does not over-route to Orwell unless phrase hints appear.
[ ] "oceania population east asia trade" remains literal/geographic.
[ ] phrase route metadata appears in run DAG.
[ ] accept-path 4 remains enabled; no bare-list sidecar regression.
[ ] evidence display includes evidence_id, source title, chunk/root prefix.

The false-positive tests matter. Phrase routing is powerful, but it must not hijack ordinary geography queries.


A.7 Provenance side: ensure the route is audit-bound

Because this fix depends on the phrase-pattern retrieval route, the route itself must be captured in provenance.

Bind:

{
  "retrieval_route": "phrase_pattern_reference",
  "reference_pattern_id": "orwell_1984_oceania_eastasia_war",
  "accept_path": 4,
  "retrieval_keywords": [
    "Nineteen Eighty-Four",
    "Oceania",
    "Eastasia",
    "always been at war"
  ],
  "route_confidence": "high"
}

into:

retrieval_plan_hash
run_dag.retrieval_stage
audit_event: retrieval_plan_built

This is important because earlier we identified a provenance gap: retrieval keywords/routes must be committed, not just the resulting sources.

If the route is not bound, the answer is correct today but not fully replay-auditable.


A.8 CTI interpretation

In CTI terms, this query should produce a frame lattice:

Frame node F1:
    literal geography

Frame node F2:
    Nineteen Eighty-Four reference

Frame node F3:
    in-universe propaganda claim

Frame node F4:
    fictional actual continuity

Then the answer should be compiled from the surviving nodes:

F2 selected: reference frame
F3 qualified: Party propaganda says/acts as if yes
F4 factual correction: actual continuity says no, alliances changed

This is better than a flat answer.

The core CTI additive insight:

A reference query often requires answering under multiple frames, not picking one frame and discarding the others.


A.9 Better final answer format for this case

Recommended output:

has oceania always been at war with east asia

EVIDENCE-LINKED · via claim_lattice 1/1 · 11.5s · fresh
Reference frame: George Orwell's Nineteen Eighty-Four.
Alias normalized: "East Asia" → "Eastasia" in the Orwell frame.

- Not in the novel's actual continuity. The point is that the Party rewrites
  history so the current enemy appears to have always been the enemy. In the
  relevant Orwell frame, Oceania's alliances/enemies change, and records are
  rewritten to hide the contradiction. [E13]

Evidence:
[E13 | Nineteen Eighty-Four | 682f0a11]
"To hide such contradictions, history is re-written..."

Frame notes:
- Literal geography frame: not the intended reading.
- Orwell frame: selected by phrase pattern "always been at war" + Oceania/Eastasia.
- Propaganda frame: the Party wants citizens to believe the current war alignment
  has always been true.

This is clear, scoped, and audit-honest.


A.10 Additive roadmap update

Current state:

G0 — Pointer / claim-lattice mode: working.
G1 — Evidence display + EVIDENCE-LINKED label: working.
I  — Reference Frame Router: now partially working via phrase-pattern route.
J/K — pointer format + label tests: working.

Next:

L — Reference-Frame Polarity Contract
M — Route provenance binding / retrieval_plan_hash if not already landed
N — Literal-vs-reference false-positive guard tests
H — Relation Warrant Lite for Homer/Mr. Burns class

Priority depends on what you are optimizing:

If optimizing the Orwell class:
    L + N

If optimizing audit replay:
    M

If optimizing general QA warrant:
    H

My recommendation:

1. Land M if retrieval-route provenance is not already committed.
2. Add L for reference-frame polarity.
3. Add N false-positive tests.
4. Then return to H relation-warrant.

A.11 Final answer

This update is good. It fixes the main reference-frame retrieval defect.

But the next issue is:

The system found the right book, but the answer still needs a frame-polarity contract.

The system should not merely say the text "does not directly state" the proposition. It should say:

This is an Orwell reference. The Party wants people to believe Oceania has always been at war with Eastasia, but the novel shows the enemy/alliance changed and history was rewritten to conceal the contradiction.

Best next implementation:

L — Reference-Frame Polarity Contract

plus route provenance binding if not already included.

This preserves the no-regression architecture:

Hermes proposes.
CTI frames and structures.
Reverse-RAG points to evidence.
Renderer displays exact source.
Merkle-DAG commits route + evidence + answer.
PROMETHEUS-Σ labels only what is actually verified.