# Paddock Proposition Matching v2 — Technical Specification

## 1. Purpose

This specification replaces stem-group matching with evidence-backed proposition adjudication.

The user-facing behavior remains the same at the product level:

- the student sees a rule,
- the student sees a fact pattern,
- the student types free-form analysis,
- circles light as the student demonstrates specific ideas.

The implementation contract changes completely:

1. A circle may become **confirmed** only when the system has evidence for every required semantic slot of that proposition.
2. Every confirmation must be explainable with cited spans from the student's actual text.
3. Generic legal filler may create a **hint** at most; it may not create a confirmation.
4. States are stable but reversible. The system may not use “once lit stays lit.”
5. Content is authored semantically and compiled into runtime artifacts. Authors do not author stems.

This is the normative implementation spec. Any behavior not explicitly permitted here should be treated as out of spec.

---

## 2. Design goals

### 2.1 Functional goals

- Detect that the student has expressed a target legal idea, even when phrased differently.
- Distinguish true demonstration from surface-word overlap.
- Preserve fast live feedback.
- Produce inspectable reasoning for every lit circle.
- Allow controlled ambiguity handling with an LLM, but only as structured adjudication.

### 2.2 Non-functional goals

- p95 local hint latency: under 150 ms.
- p95 server confirmation latency without LLM: under 500 ms.
- p95 server response with LLM on idle/submit: under 1800 ms.
- No proposition may be published without benchmark coverage.
- No proposition may ship with only generic legal aliases.

### 2.3 Non-goals

- The system does not grade writing style.
- The system does not generate a full answer for the student.
- The system does not rely on a single whole-response embedding to confirm concepts.
- The system does not use raw Porter stems as the authored content format.

---

## 3. Core terms

**Proposition**: one assessable idea represented by one circle.  
**Slot**: one required semantic part of a proposition.  
**Evidence**: a cited span from the student text plus source metadata and confidence.  
**Element**: a doctrinal group of one or more propositions.  
**Concept**: a reusable semantic unit in the concept library, such as `claim.equal_pay_act` or `outcome.summary_judgment`.  
**Hint**: provisional UI state showing the student is moving toward a proposition.  
**Confirmed**: all required slots are satisfied with evidence.  
**Conflicted**: both supporting and contradictory evidence exist, or a proposition is undercut by explicit opposite evidence.  
**Rejected**: current text does not support confirmation and has no meaningful partial support.

---

## 4. System overview

The system has four layers.

### 4.1 Authoring layer

Authors or generators produce a `DrillAuthoringV2` document containing:

- normalized entities and proceedings,
- reusable concept references,
- propositions with required semantic slots,
- pedagogical explanations,
- benchmark examples.

### 4.2 Compiler layer

The compiler validates authoring content and emits a `CompiledDrillV2` package:

- normalized aliases,
- entity and role lookup maps,
- client-safe hint rules,
- dense prototype texts,
- negative prototype texts,
- analysis policy defaults.

### 4.3 Runtime analysis layer

The server analyzes the student draft incrementally:

1. segment text,
2. retrieve candidate propositions,
3. detect slot evidence,
4. score slot satisfaction,
5. adjudicate proposition state,
6. apply hysteresis,
7. return evidence-backed states.

### 4.4 Evaluation layer

Every authored proposition must pass benchmark suites before publication:

- direct positives,
- paraphrase positives,
- near-miss negatives,
- filler negatives,
- contradictions when polarity matters.

---

## 5. Authoring model

## 5.1 Authoring rules

The following rules are mandatory.

1. Each circle corresponds to one proposition.
2. A proposition must have 1-4 required slots.
3. Every slot must be discriminative. Generic words like `claim`, `court`, `action`, `rule`, `act`, `raised`, and `judgment` may not satisfy a slot on their own.
4. Every proposition must include authored positive and negative examples.
5. Near misses must reflect realistic student wording.
6. Propositions should capture meaning, not surface tokens.

### 5.1.1 When a proposition may have a single slot

A single-slot proposition is allowed only when the slot is intrinsically specific and cannot be satisfied by generic legal filler. Examples:

- a named statutory claim,
- a distinctive amount,
- a specific factual act with strong lexical anchors.

The content gate must reject one-slot propositions whose slot aliases are generic.

### 5.1.2 Slot kinds

Supported slot kinds:

- `entity`
- `role`
- `proceeding`
- `forum`
- `legal_claim`
- `action`
- `state`
- `outcome`
- `amount`
- `date`
- `relation`
- `polarity`

The runtime MUST use kind-specific detection logic.

## 5.2 Concept library

Concepts are reusable, doctrine-independent or doctrine-specific semantic units.

A concept contains:

- canonical text,
- positive aliases,
- optional negative aliases,
- optional incompatible concept references.

Authoring principles:

- aliases should be human language, not stems,
- aliases should include paraphrases students would plausibly write,
- negative aliases should capture common confusions or opposite meanings,
- aliases that are safe for local hinting may be marked `clientSafe`.

## 5.3 Drill authoring structure

A drill must contain:

- `ruleText`
- `factPattern`
- `entityRegistry`
- `factTable`
- `elements`
- `propositions`
- `conceptLibraryRefs`

The `factTable` is required because it makes the fact pattern machine-readable and reusable in prompts, tests, and explanations.

## 5.4 Proposition authoring structure

A proposition must define:

- `circleLabel`: short UI label,
- `canonical`: full proposition,
- `requiredSlots`,
- `positives`,
- `paraphrasePositives`,
- `nearMisses`,
- `fillerNegatives`,
- optional `contradictions`,
- `whyMatters`.

A proposition may not rely on implicit context only. If a student needs to mention a prior proceeding, that requirement must be represented as a slot.

---

## 6. Compiler specification

## 6.1 Inputs

Inputs:

- one `DrillAuthoringV2`,
- referenced concept library documents.

Output:

- one `CompiledDrillV2`.

## 6.2 Compiler pipeline

1. Validate JSON schema.
2. Validate authoring semantics.
3. Expand referenced concepts into proposition slots.
4. Normalize alias text.
5. Build client hint rules.
6. Build server lexical matcher tables.
7. Generate dense prototype texts.
8. Generate negative prototype texts.
9. Produce hashable build artifact.
10. Run benchmark evaluation.
11. Publish only on passing results.

## 6.3 Alias normalization

Normalization algorithm:

1. Unicode normalize to NFKC.
2. Lowercase.
3. Trim whitespace.
4. Collapse repeated whitespace to single spaces.
5. Strip outer punctuation.
6. Lemmatize tokens where possible.
7. Retain original text alongside normalized form.
8. Do not collapse distinct statutory names into generic stems.

The normalized form is used for lexical retrieval only. The original alias text is preserved for explanation and debugging.

## 6.4 Dense prototypes

For each proposition, the compiler must generate dense prototype texts from:

- canonical proposition text,
- direct positive examples,
- paraphrase positives.

Negative prototype texts must be created from:

- near misses,
- filler negatives,
- contradictions.

These prototypes are embedded offline and stored or referenced in the compiled artifact.

Dense retrieval is candidate retrieval only. It is not itself a confirmation rule.

## 6.5 Client runtime generation

The compiler emits a client-safe runtime package containing only quick hint material:

- proposition id,
- a small set of `clientSafe` alias texts.

Rules:

- client hints may create `hinted` only,
- client hints may never create `confirmed`,
- client hints may never consider contradictory evidence,
- client hints are an optimization, not the source of truth.

## 6.6 Content gate

A drill fails compilation if any of the following are true:

1. Any proposition has zero benchmark cases in any required category.
2. Any proposition has only generic aliases.
3. Any contradiction case confirms instead of conflicts or rejects.
4. Any filler negative confirms.
5. Any slot has no plausible authored way to satisfy it.
6. Any proposition has duplicate meaning with another proposition in the same element without explicit intent.
7. Any proposition cannot cite evidence spans.

---

## 7. Runtime analysis specification

## 7.1 Input model

The primary endpoint receives:

- full response text,
- changed ranges,
- session id,
- monotonically increasing revision,
- mode: `live`, `idle`, or `submit`.

The full response text is authoritative. Diffs are advisory.

## 7.2 Segmentation

The server must segment the response into analysis spans.

Recommended algorithm:

1. sentence split on `.`, `?`, `!`, line breaks, list markers;
2. clause split on semicolons, em dashes, `but`, `because`, `although`, `however`, `while`, and commas when the clause exceeds 12 tokens;
3. preserve character offsets for every span;
4. keep a ±1 span context window.

The runtime may analyze sentence-level and clause-level spans together. Offsets must always reference the original response text.

## 7.3 Entity and literal detection

Before proposition retrieval, the server must detect the following across all spans:

- known entity aliases from `entityRegistry`,
- role mentions,
- court/forum mentions,
- numeric amounts,
- dates,
- statutes if listed as entities or concepts,
- coreference links when safe.

Mandatory behavior:

- entity detection must preserve the exact surface span,
- amount detection must normalize `$40,000`, `40000`, and `forty thousand`,
- role mentions alone may not satisfy a proposition unless the proposition explicitly allows a role slot.

## 7.4 Candidate retrieval

Candidate retrieval is proposition-level and span-local.

### 7.4.1 Lexical retrieval

For each span, compute lexical hits against concept aliases and entity aliases.

A lexical candidate is created when at least one discriminative required slot has a lexical or registry hit.

Lexical retrieval score:

`lexicalScore = matchedDistinctRequiredSlots / totalRequiredSlots`

Lexical retrieval uses only discriminative alias hits. Generic aliases listed in `disallowAliases` do not count.

### 7.4.2 Dense retrieval

For each span, compute similarity to proposition positive prototype embeddings.

Dense retrieval candidate threshold:

- default minimum similarity: `0.62`

Dense retrieval score is the maximum similarity across positive prototypes, penalized by negative prototype closeness:

`denseScore = maxPositiveSim - 0.35 * maxNegativeSim`

Dense retrieval must be bounded to `[0,1]`.

### 7.4.3 Candidate inclusion rule

A proposition is sent to slot adjudication for a given span if any condition holds:

- lexicalScore > 0,
- denseScore >= 0.62,
- the proposition is already in a non-terminal state from a previous revision and the changed range overlaps supporting evidence.

At most 8 propositions per span may advance to full adjudication, ranked by:

`candidateRank = 0.6 * lexicalScore + 0.4 * denseScore`

If the span already contains confirmed evidence for a proposition, that proposition always remains in the adjudication set.

## 7.5 Slot detection

Each proposition is adjudicated slot by slot.

### 7.5.1 Source families

Permitted evidence source families:

- `registry`: exact entity/proceeding lookup,
- `lexical`: exact alias phrase match,
- `lemma`: lemmatized phrase match,
- `fuzzy`: character-distance tolerant match on a strong alias,
- `dense`: semantic similarity between span and slot/proposition prototype,
- `numeric`: normalized amount/date extraction,
- `coref`: safe coreference transfer,
- `llm`: structured LLM slot finding,
- `negation`: explicit contradiction or polarity evidence.

### 7.5.2 Base confidence values

Default base confidences:

- registry exact entity/proceeding: `0.90`
- numeric normalized amount/date: `0.95`
- lexical exact phrase: `0.85`
- lemma phrase: `0.80`
- fuzzy phrase: `0.70`
- dense semantic support: map similarity `[0.62, 0.82]` linearly to confidence `[0.55, 0.75]`
- coreference: `0.70`
- llm structured support: `0.80`

These defaults may be overridden at the drill or proposition level, but overrides must be explicit in policy configuration.

### 7.5.3 Slot confidence aggregation

For each slot, gather all candidate evidence for the relevant span window.

Aggregation rule:

1. Let `maxBase` be the maximum base confidence among evidence for the slot.
2. Add `0.05` if at least two independent source families agree.
3. Add `0.05` if the evidence occurs in the same span as evidence for another required slot of the same proposition.
4. Cap at `0.95`.

Formula:

`slotConfidence = min(0.95, maxBase + agreementBonus + cooccurrenceBonus)`

A slot is satisfied when:

`slotConfidence >= slot.minConfidence`

### 7.5.4 Hard negatives

A slot or proposition is blocked if any explicit negative alias or contradictory concept matches with confidence `>= 0.80`, unless stronger contrary evidence resolves the contradiction in the same span.

Examples:

- `raised in the first case` blocks `not raised in prior action`,
- `different transaction` blocks `same transaction`,
- `settled` blocks `summary judgment`.

### 7.5.5 Disallow aliases

Every slot may define `disallowAliases`.

Any evidence whose exact text is a disallowed alias does not count toward slot satisfaction. This is how the runtime prevents generic fillers such as `claim`, `action`, or `judgment` from satisfying a slot by themselves.

## 7.6 Proposition adjudication

### 7.6.1 Coverage

Coverage is weighted required-slot completion:

`coverage = sum(weight_i * satisfied_i) / sum(weight_i)`

where `satisfied_i` is 1 if the slot is satisfied, else 0.

### 7.6.2 State rules

Apply states in this order.

**conflicted**
- Any required slot has both supporting and contradicting evidence with confidence `>= 0.75`, or
- a contradiction example pattern is matched with confidence `>= 0.80`.

**confirmed**
- All required slots satisfied, and
- no active contradiction, and
- at least one evidence item exists for every required slot.

**hinted**
- Not confirmed, and
- at least one required slot satisfied, or
- coverage >= `0.45`.

**rejected**
- No meaningful support, or
- only generic legal filler found.

**unseen**
- No signals have been observed yet for this revision history.

### 7.6.3 Rationale generation

The server must return a concise rationale string for each non-`unseen` proposition.

The rationale must be generated from rule templates, not free-form model text, unless the result is coming from a submit-only explanation endpoint.

Example rationale template:

- confirmed: `All required slots supported by cited spans.`
- hinted: `Some required slots supported; omission from prior action still missing.`
- conflicted: `The response contains both omission language and language stating the claim was raised.`

## 7.7 LLM adjudication

LLM use is allowed only for structured slot adjudication.

### 7.7.1 When to call the LLM

The LLM may be called only when all of the following are true:

1. mode is `idle` or `submit`, or `live` with explicit feature flag,
2. proposition is not already confirmed,
3. lexical or dense retrieval produced a non-zero candidate,
4. at least one required slot remains unresolved,
5. session has not exceeded `llmMaxCallsPerSession`,
6. the same proposition has not already been asked for the same or later revision.

### 7.7.2 LLM input

The LLM prompt must include only:

- proposition canonical text,
- slot definitions,
- relevant entity aliases,
- current span and at most one adjacent span,
- strict JSON output contract.

The full drill answer may be included only on submit; live and idle should use local span windows.

### 7.7.3 LLM output contract

The model must return:

- proposition id,
- whether supported,
- per-slot support boolean,
- per-slot confidence,
- exact evidence substring and offsets,
- conflicts if any.

The server must reject an LLM output if:

- any cited span is not an exact substring of the supplied student text,
- any required field is missing,
- the JSON is invalid,
- the model asserts support with no evidence.

Rejected LLM outputs must not affect state.

### 7.7.4 LLM merge policy

LLM evidence is merged like any other evidence source. It may satisfy unresolved slots, but it may not override explicit contradiction evidence unless it directly explains that the contradiction was outside the proposition scope. For the initial implementation, do not allow override; mark `conflicted` instead.

## 7.8 State hysteresis

State changes must be stable but reversible.

Rules:

- upgrades to `hinted`, `confirmed`, or `conflicted` apply immediately,
- downgrades require two consecutive analyses without sufficient support and a minimum hold window of `750 ms`,
- deletion of supporting text may downgrade a proposition after the hold window,
- a proposition may move from `confirmed` back to `hinted` or `rejected`,
- `conflicted` supersedes `hinted`,
- `confirmed` supersedes `hinted` but not `conflicted`.

This replaces “once lit stays lit.”

---

## 8. Client behavior

## 8.1 Local hint engine

The client may use the `clientRuntime` package to create fast hints.

Behavior:

- debounce at `100-150 ms`,
- scan changed text only,
- if a client-safe alias matches, move the circle to `hinted`,
- never show `confirmed` from the client hint engine.

## 8.2 Server authoritative update

The client sends full text plus changed ranges to the server at `300 ms` debounce. The server response is authoritative and may overwrite local hint states.

## 8.3 Visual states

Recommended UI states:

- `unseen`: empty circle
- `hinted`: dotted or partially filled circle
- `confirmed`: filled circle
- `conflicted`: warning outline or contrasting badge
- `rejected`: not shown distinctly during typing; used mainly in telemetry and submit explanations

## 8.4 Submit behavior

On submit, run a final `submit` analysis pass. This pass may use one LLM call per unresolved proposition if within policy.

The submit result must include:

- final proposition states,
- missed propositions,
- pedagogical explanation,
- formula scaffold,
- optional explanation of why a proposition did not confirm.

---

## 9. API contracts

The normative external API surface is:

- `GET /api/drills/{drillId}/runtime`
- `POST /api/drills/{drillId}/analyze`
- `POST /api/drills/{drillId}/submit`

See `openapi/drill-analysis.openapi.yaml`.

### 9.1 Response invariants

The analysis response must satisfy all of the following:

1. Every `confirmed` proposition includes evidence for every required slot.
2. Every evidence span has valid offsets.
3. Every returned proposition has `updatedAtRevision <= revision`.
4. Element completion is derived from proposition states, never the reverse.
5. The server must not return stale older revisions.

---

## 10. Quality assurance and release gate

## 10.1 Benchmark bundle requirements

Each proposition must include benchmark cases in these categories:

- at least 3 direct positives,
- at least 3 paraphrase positives,
- at least 3 near-miss negatives,
- at least 3 filler negatives,
- at least 2 contradictions when polarity matters.

Recommended stronger production target:

- 5 direct positives,
- 5 paraphrase positives,
- 5 near misses,
- 8 filler negatives,
- 3 contradictions.

## 10.2 Release thresholds

Minimum release thresholds for a drill:

- confirmation precision on negative cases: `>= 0.97`
- recall on positive cases: `>= 0.90`
- unsupported confirmations: `0`
- contradiction misclassifications: `0`
- filler-negative confirmations: `0`

A proposition that misses threshold blocks publication of the whole drill.

## 10.3 Telemetry

The system must log:

- session id,
- drill id,
- proposition id,
- state transitions,
- evidence sources,
- proposition coverage,
- whether LLM was invoked,
- response latency,
- submit outcome.

A weekly review should inspect:

- most common false confirms,
- propositions with highest LLM dependence,
- propositions with repeated conflicts,
- propositions with low recall on submit.

---

## 11. Storage model

Recommended persistent stores:

### 11.1 Authoring store
File-based JSON or database JSONB documents:
- concept libraries,
- drill authoring documents.

### 11.2 Build store
Compiled artifacts keyed by build hash:
- compiled drill JSON,
- embedding blobs or vector references,
- benchmark results.

### 11.3 Runtime telemetry store
Append-only event records:
- revision,
- proposition decisions,
- evidence snapshots.

Minimum tables/collections:

- `concept_library`
- `drill_authoring_v2`
- `compiled_drill_v2`
- `benchmark_run`
- `drill_session`
- `drill_analysis_event`

---

## 12. Reference algorithms

## 12.1 Main analyze flow

```ts
function analyzeDraft(req: AnalyzeRequest, drill: CompiledDrillV2): AnalyzeResponse {
  const spans = segmentText(req.responseText, req.changedRanges);
  const entities = detectEntitiesAndLiterals(req.responseText, drill.entityRegistry);

  const propositionDecisions = new Map<string, PropositionDecision>();

  for (const span of spans) {
    const candidates = retrieveCandidates(span, drill, entities);

    for (const proposition of candidates) {
      const evidence = collectEvidence({
        proposition,
        span,
        fullText: req.responseText,
        entities,
        mode: req.mode,
      });

      let mergedEvidence = evidence;

      if (shouldInvokeLlm(req.mode, proposition, evidence)) {
        const llmEvidence = runStructuredLlmAdjudication(proposition, span, drill);
        if (validateLlmEvidence(llmEvidence, span.text)) {
          mergedEvidence = mergeEvidence(evidence, llmEvidence);
        }
      }

      const decision = adjudicateProposition(proposition, mergedEvidence);
      propositionDecisions.set(
        proposition.id,
        applyHysteresis(previousDecisionFor(req.sessionId, proposition.id), decision, drill.analysisPolicy)
      );
    }
  }

  return buildAnalyzeResponse(req, propositionDecisions, drill);
}
```

## 12.2 Evidence collection

```ts
function collectEvidence(input: {
  proposition: PropositionRuntime;
  span: Span;
  fullText: string;
  entities: DetectedEntity[];
  mode: "live" | "idle" | "submit";
}): SlotEvidence[] {
  const out: SlotEvidence[] = [];

  for (const slot of input.proposition.requiredSlots) {
    out.push(...detectRegistryEvidence(slot, input.span, input.entities));
    out.push(...detectLexicalEvidence(slot, input.span));
    out.push(...detectDenseEvidence(slot, input.span, input.proposition));
    out.push(...detectNumericEvidence(slot, input.span));
    out.push(...detectCorefEvidence(slot, input.span, input.fullText));
    out.push(...detectContradictions(slot, input.span));
  }

  return out;
}
```

## 12.3 Proposition adjudication

```ts
function adjudicateProposition(
  proposition: PropositionRuntime,
  evidence: SlotEvidence[]
): PropositionDecision {
  const slotDecisions = proposition.requiredSlots.map((slot) =>
    scoreSlot(slot, evidence.filter((e) => e.slotId === slot.id))
  );

  const hasConflict = slotDecisions.some(
    (slot) => (slot.conflicts?.length ?? 0) > 0
  );

  const coverage =
    slotDecisions.reduce((sum, slot) => {
      const weight = proposition.requiredSlots.find((s) => s.id === slot.slotId)?.weight ?? 1;
      return sum + (slot.satisfied ? weight : 0);
    }, 0) /
    proposition.requiredSlots.reduce((sum, slot) => sum + slot.weight, 0);

  let state: PropositionState = "unseen";
  if (hasConflict) state = "conflicted";
  else if (slotDecisions.every((slot) => slot.satisfied)) state = "confirmed";
  else if (slotDecisions.some((slot) => slot.satisfied) || coverage >= 0.45) state = "hinted";
  else state = "rejected";

  return {
    propositionId: proposition.id,
    state,
    coverage,
    rationale: buildRationale(state, proposition, slotDecisions),
    slotDecisions,
    updatedAtRevision: 0, // caller fills this
  };
}
```

---

## 13. Migration from the current matcher

## 13.1 What must be removed

Remove these as content primitives:

- authored `match.groups`,
- majority group thresholds,
- proximity bonus,
- whole-response confirmation by embedding alone,
- irreversible “once lit stays lit.”

## 13.2 What may be reused

You may reuse:

- rule text,
- fact pattern text,
- building blocks UI grouping by element,
- embeddings infrastructure,
- existing telemetry plumbing,
- existing drill ids if desired.

## 13.3 Migration sequence

1. Define schema v2 and compiler.
2. Seed concept library for top doctrines.
3. Author one exemplar drill end-to-end in v2.
4. Build benchmark harness and gating.
5. Implement server analyzer.
6. Implement client hint state.
7. Shadow-run against real historical student answers.
8. Tune thresholds.
9. Migrate remaining drills doctrine by doctrine.
10. Remove v1 matcher after parity and precision targets are met.

## 13.4 Backward compatibility

If legacy drills must run temporarily, wrap them in a compatibility adapter that turns each legacy key fact into a proposition with explicit slots. Do not extend the life of raw stem-group matching.

---

## 14. Implementation checklist

A build is not implementation-complete until all items below are done.

### 14.1 Data contracts
- [ ] TypeScript interfaces committed
- [ ] JSON Schemas committed
- [ ] compiler input validation
- [ ] compiled artifact serializer

### 14.2 Runtime
- [ ] segmentation
- [ ] entity and numeric detection
- [ ] lexical matcher
- [ ] dense retrieval
- [ ] slot scoring
- [ ] contradiction handling
- [ ] LLM structured adjudication
- [ ] hysteresis

### 14.3 Client
- [ ] local hint runtime loading
- [ ] live analyze requests
- [ ] circle state rendering
- [ ] submit flow

### 14.4 Quality
- [ ] benchmark harness
- [ ] release gate
- [ ] telemetry dashboards
- [ ] offline shadow evaluation

---

## 15. Worked example

The file `examples/drill-authoring.claim-preclusion.json` shows the source authoring format.

The file `examples/compiled-drill.claim-preclusion.json` shows the compiled runtime format.

The file `examples/analyze-response.json` demonstrates the expected result when the student writes:

> Jordan already sued Brightfield in state court over the $40,000 bonus and lost on summary judgment. She later filed an Equal Pay Act suit based on the same compensation dispute, but she never brought that EPA theory in the first case.

This example is intentionally important because it demonstrates the core contract:

- the proposition for the omitted EPA claim is confirmed only when the response includes:
  - the statutory claim,
  - omission language,
  - the prior case reference.

A legacy word-overlap matcher would confirm too early on words like `claim`, `action`, or `act`. This system cannot do that because none of those generic words satisfy the required slots.

---

## 16. Default policy values

These are the default values to ship in the first production version.

| Policy | Default |
|---|---:|
| Client hint debounce | 120 ms |
| Server live debounce | 300 ms |
| Idle LLM debounce | 1500 ms |
| Downgrade hold | 750 ms |
| Lexical candidate min distinct slots | 1 |
| Dense candidate min similarity | 0.62 |
| Max LLM calls per session | 2 |
| Hinted coverage threshold | 0.45 |
| Contradiction block threshold | 0.80 |

These defaults should be stored in the compiled artifact or global analysis policy config so that benchmark runs remain reproducible.

---

## 17. Summary

The implementation target is not “better fuzzy matching.” The implementation target is:

- semantic propositions,
- typed required slots,
- evidence-backed confirmation,
- benchmark-gated content,
- stable but reversible UI state,
- LLM only for structured ambiguity resolution.

If the implementation preserves those six properties, the system will behave as intended.
