W-006 (operator decision: delete): - Remove dormant packs/en/el/grc/he/readback_rules.py (4 files, 0 live production callers). generate/realizer.py superseded the per-language readback path; per [[feedback-cleanup-as-you-find]], superseded code is removed rather than preserved. - Remove _gate_readback from packs/common/validator.py and drop it from the validate_pack_dir gate sequence. Add language to the report dict so the param remains non-vacuous. W-010 (operator decision: intentional token-level): - Amend ADR-0143 with "Vocabulary isolation is intentional" section. Token-level anti-unification derives its own structural vocabulary; importing VocabManifold adds no information at that level. Confirmed intentional by operator review 2026-05-25. W-014 (operator decision: evals-only): - Add deployment-scope note to core/cognition/provenance.py docstring: evals-only infrastructure, no live runtime caller. Confirmed evals-only by operator review 2026-05-25.
7.6 KiB
ADR-0143: Teaching-Derived Structural Recognition via Multi-Resolution Anti-Unification
Status: Accepted Date: 2026-05-24 Scope doc: teaching-derived-recognition-scope Related: ADR-0142 (epistemic state taxonomy), ADR-0144 (PropositionGraph — integration gate)
Context
CORE's recognition path currently uses hand-coded regex patterns that the engine neither derived nor can introspect. These patterns store finds rather than teaching finding. The thesis requires the engine's capacity to recognize proposition structure to emerge from reviewed teaching examples — not from hand-authored scaffolding.
The recognition scope document (v2, 2026-05-24) evaluated four candidate mechanisms and selected Mechanism D: multi-resolution anti-unification over token sequences as the only option that is simultaneously deterministic, exact, structural, introspectable, and well-defined on token sequences. The scope committed the spike to a two-phase acceptance test.
This ADR records the decision to implement Mechanism D and defines the output contract that all recognition work must conform to.
Decision
Adopt multi-resolution anti-unification over token sequences as the recognition mechanism.
The recognizer is derived deterministically from a reviewed teaching example set. It operates at two resolutions:
- Chunk level — anti-unify at noun-phrase / verb-phrase / quantifier-phrase chunks. If every chunk resolves with complete feature evidence, emit the bundle.
- Word-level fallback — for any chunk that fails at chunk level, drop to word-by-word anti-unification on just that chunk and attempt to lift the relevant feature slot.
At both resolutions, the recognizer refuses rather than guesses when evidence is absent or contradictory. Refusal is the primary signal for what to learn next.
Output contract
Every recognition output is a RecognitionOutcome (defined in
recognition/outcome.py). The contract is frozen; implementers must not
add alternative output types.
RecognitionOutcome:
state: EVIDENCED | UNDETERMINED | CONTRADICTED | AMBIGUOUS
proposition: FeatureBundle | None
refusal_reason: ShapeRefusal | FeatureEvidenceRefusal | FeatureConsistencyRefusal | None
provenance: RecognitionProvenance
Invariants:
state == EVIDENCED→propositionis a completeFeatureBundlewith evidence on every feature;refusal_reasonisNone.- Any refusal state →
propositionisNone;refusal_reasonis a typed instance naming exactly what is missing or contradictory. provenanceis always present. It carriesmechanism,teaching_set_id(SHA-256 of the canonical example set), andresolution_level.
Epistemic states emitted. Recognition produces only this subset of the ADR-0142 taxonomy: EVIDENCED (admitted), UNDETERMINED (shape refused), CONTRADICTED (feature contradiction), AMBIGUOUS (unresolvable ambiguity). VERIFIED and DECODED are downstream of substrate cross-reference work and are never emitted by the recognizer itself.
Three-layer refusal
| Layer | Class | Trigger |
|---|---|---|
| 1 — Shape | ShapeRefusal |
Input does not match any derived pattern |
| 2 — Feature evidence | FeatureEvidenceRefusal |
Shape matched; a required feature has no evidence span |
| 3 — Feature consistency | FeatureConsistencyRefusal |
Two evidence spans contradict each other on the same feature |
Every layer produces a deterministic, typed, introspectable refusal. The engine does not approximate or default — it points at exactly which substrate is missing.
Feature bundle requirements
Every BoundFeature in an admitted bundle carries:
name: the feature dimension (agent, relation, count, unit, polarity, modality, tense, intentionality, ...)value: the typed feature value (str | int | float)evidence: anEvidenceSpan(token indices + verbatim text) or aNegativeEvidencerecord (for features established by absence, e.g.polarity=affirmativefrom the absence of a negator)
No silent defaults. If a feature cannot be evidenced, the recognizer refuses at Layer 2.
Determinism requirements
derive_recognizer(examples)→ byte-identicalDerivedRecognizeron the same input across runs.recognize(recognizer, tokens)→ byte-identicalRecognitionOutcomeon the same recognizer and input across runs.DerivedRecognizermust be serializable to/from JSON for replay.teaching_set_idis SHA-256 of the sorted canonical example token sequences; it must be byte-identical across runs on the same examples.
Acceptance test (two-phase spike)
Phase 1 — Mechanism on uniform examples
Four teaching examples (all has-relation, all affirmative, all actual-
modality — see scope doc). Derived recognizer must:
- Admit
"A baker has 24 loaves"with full feature bundle and evidence spans. - Refuse
"John gave 5 apples to Mary"withShapeRefusal(Layer 1). - Produce byte-identical output on both cases across two runs.
- Every feature in the admitted bundle has non-None evidence.
Phase 1 pass → Phase 2 is warranted. Phase 1 fail → mechanism is wrong.
Phase 2 — Variation lifting and adversarial robustness
Eight teaching examples (varying polarity / modality / tense / intentionality). Derived recognizer must:
- Admit three positive variation cases with correct feature bundles.
- Refuse five adversarial cases at the correct refusal layer (Layer 2 or 3).
- Produce byte-identical output across two runs.
Full test cases in scope doc.
What this ADR does NOT commit
- Storage layer. Where derived recognizers live (pack / vault / substrate state) is deferred to the ADR that follows the spike.
- Integration into Engine A. Gated on ADR-0144 (PropositionGraph). Until then, the recognizer is a standalone module.
- Parsing framework. Token-sequence anti-unification is the starting point; syntactic parse trees are a fallback if token-level fails.
Vocabulary isolation is intentional (W-010)
derive_recognizer() and recognize() operate on raw token sequences and
taught FeatureBundle evidence without consuming VocabManifold, pack
lexicons, or domain namespaces. This is a deliberate design choice, not a
gap. Anti-unification derives its own structural vocabulary from the teaching
set; importing pack-resident vocabulary would create a dependency on L3 that
adds no information at the anti-unification level — the feature bundle carries
the semantically relevant structure. If a future scope requires
pack-vocabulary-typed slots (e.g. "subject must be a logos.* domain atom"),
that is a new ADR, not a retrofit of this one.
Confirmed intentional by operator review 2026-05-25 (W-010 closure).
- Counter-evidence vocabulary.
"Alleged","claimed","(this is a lie)"are refused at Layer 2/3 on first encounter. Teaching-loop consumption of those refusals as correction candidates is its own future scope. - Lens-conditional recognition. How different anchor lenses interact with derived recognizers is deferred.
Consequences
- All future recognition work targets
RecognitionOutcome. No alternative output contract is permitted without a new ADR. - Refusal is first-class. Every refusal carries a typed reason consumable by the teaching loop. Silent failure is a bug.
- The recognizer is not a classifier. It does not assign a proposition type directly — type emerges from the feature bundle via a downstream mapping that is itself derived from teaching.
- Integration into the runtime is gated on ADR-0144. Until then, the spike
lives in
recognition/as a standalone testable module.