feat(adr-0021): epistemic_status surface wired across teaching + trace
ADR-0021 v1 schema land. epistemic_status is a position in the revision
graph, not a source-trust tier — coherence is the only admission signal.
Surfaces:
- teaching/epistemic.py: EpistemicStatus enum (COHERENT, CONTESTED,
SPECULATIVE, FALSIFIED); ADMISSIBLE_AS_EVIDENCE = {COHERENT}.
- PackMutationProposal.epistemic_status (default SPECULATIVE) + immutable
with_status() updater.
- ReviewedTeachingExample.epistemic_status (default SPECULATIVE);
orthogonal to acceptance per ADR §Schema impact.
- LexicalEntry.epistemic_status (default "coherent" for seed; absent in
JSONL is treated as the seed default — no retroactive tagging).
- compute_trace_hash + trace_hash_from_result + pipeline.py fold the
load-bearing proposal's epistemic_status into the trace hash so
replay detects different epistemic frames.
Non-hardening invariant (ADR-0021 §2): tests/test_epistemic_invariants.py
asserts no final/frozen/axiom/permanent flag on PackMutationProposal or
ReviewedTeachingExample, and EpistemicStatus contains no source-trust
tier names.
Docs: docs/runtime_contracts.md gains an Epistemic surface section.
Lanes green: smoke 27/27, teaching 10/10, packs 6/6, runtime 19/19,
cognition eval 100%.
This commit is contained in:
parent
e36998d25d
commit
ef95d3e609
10 changed files with 320 additions and 4 deletions
|
|
@ -117,6 +117,7 @@ class CognitiveTurnPipeline:
|
|||
# typed-operator invocation per ADR-0018).
|
||||
review_hash = reviewed_example.review_hash if reviewed_example is not None else ""
|
||||
proposal_id = proposal.proposal_id if proposal is not None else ""
|
||||
epistemic_status = proposal.epistemic_status.value if proposal is not None else ""
|
||||
operator_invocation = self._serialize_walk(walk_result)
|
||||
trace_hash = compute_trace_hash(
|
||||
input_text=text,
|
||||
|
|
@ -130,6 +131,7 @@ class CognitiveTurnPipeline:
|
|||
intent_tag=intent.tag.value,
|
||||
teaching_review_hash=review_hash,
|
||||
teaching_proposal_id=proposal_id,
|
||||
teaching_epistemic_status=epistemic_status,
|
||||
operator_invocation=operator_invocation,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ def compute_trace_hash(
|
|||
intent_tag: str = "unknown",
|
||||
teaching_review_hash: str = "",
|
||||
teaching_proposal_id: str = "",
|
||||
teaching_epistemic_status: str = "",
|
||||
operator_invocation: str = "",
|
||||
) -> str:
|
||||
"""Return a deterministic SHA-256 hex digest over the turn's key outputs.
|
||||
|
|
@ -48,6 +49,12 @@ def compute_trace_hash(
|
|||
string when no operator ran. Folding it explicitly makes operator
|
||||
invocation a load-bearing part of replay equality, not just an
|
||||
indirect consequence of surface-change.
|
||||
|
||||
``teaching_epistemic_status`` is the serialised EpistemicStatus of the
|
||||
pack mutation proposal load-bearing in this turn — empty string when
|
||||
no proposal was emitted. Folded per ADR-0021 §Consequences so replay
|
||||
detects when a downstream surface was produced under a different
|
||||
epistemic frame than at the time of recall.
|
||||
"""
|
||||
payload = {
|
||||
"input_text": input_text,
|
||||
|
|
@ -61,6 +68,7 @@ def compute_trace_hash(
|
|||
"intent_tag": intent_tag,
|
||||
"teaching_review_hash": teaching_review_hash,
|
||||
"teaching_proposal_id": teaching_proposal_id,
|
||||
"teaching_epistemic_status": teaching_epistemic_status,
|
||||
"operator_invocation": operator_invocation,
|
||||
}
|
||||
serialized = json.dumps(payload, sort_keys=True, ensure_ascii=False)
|
||||
|
|
@ -80,6 +88,11 @@ def trace_hash_from_result(result: "CognitiveTurnResult") -> str:
|
|||
if result.pack_mutation_proposal is not None
|
||||
else ""
|
||||
)
|
||||
epistemic_status = (
|
||||
result.pack_mutation_proposal.epistemic_status.value
|
||||
if result.pack_mutation_proposal is not None
|
||||
else ""
|
||||
)
|
||||
return compute_trace_hash(
|
||||
input_text=result.input_text,
|
||||
filtered_tokens=result.filtered_tokens,
|
||||
|
|
@ -92,5 +105,6 @@ def trace_hash_from_result(result: "CognitiveTurnResult") -> str:
|
|||
intent_tag=intent_tag,
|
||||
teaching_review_hash=review_hash,
|
||||
teaching_proposal_id=proposal_id,
|
||||
teaching_epistemic_status=epistemic_status,
|
||||
operator_invocation=result.operator_invocation,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -106,6 +106,55 @@ Tests should protect load-bearing behavior:
|
|||
Avoid tests that preserve stale constructors, private helper shapes, or exact
|
||||
formatting that is not part of a documented contract.
|
||||
|
||||
## Epistemic surface (ADR-0021)
|
||||
|
||||
CORE exposes a typed `epistemic_status` on the teaching and lexicon
|
||||
surfaces. The status is a **position in the revision graph**, not a
|
||||
source-trust tier:
|
||||
|
||||
| Status | Meaning |
|
||||
|---------------|------------------------------------------------------------------------------------------|
|
||||
| `COHERENT` | Fits current field geometry; no incoherence with reviewed claims detected at admission. |
|
||||
| `CONTESTED` | Incoherent with at least one reviewed claim; review pending; not load-bearing. |
|
||||
| `SPECULATIVE` | Proposed; not yet reviewed for coherence; admissible only as a candidate. |
|
||||
| `FALSIFIED` | Incoherent under accumulated evidence; eligible for Stage-3 inversion; retained. |
|
||||
|
||||
### Non-hardening invariant
|
||||
|
||||
No reviewed claim or proposition-graph edge ever becomes unrevisable.
|
||||
No `final`, `frozen`, `axiom`, or `permanent` flag exists or may be
|
||||
added on the runtime data model. The closest such property in the
|
||||
architecture is the *mathematical* closure check
|
||||
`versor_condition(F) < 1e-6` — never an epistemic seal on a claim.
|
||||
The invariant is enforced by `tests/test_epistemic_invariants.py`.
|
||||
|
||||
### Curator review rule
|
||||
|
||||
`epistemic_status` transitions are computed from coherence with the
|
||||
existing reviewed field — not asserted by source authority. At v1 the
|
||||
judgment is curator-mediated, with one rule:
|
||||
|
||||
> The curator's only admissible reasoning is *geometric*: does the
|
||||
> claim cohere with already-reviewed claims, or does it produce
|
||||
> incoherence? Source credentials, popularity, or institutional
|
||||
> position must not be invoked as justification.
|
||||
|
||||
### Schema surfaces
|
||||
|
||||
| Surface | Field | Default at creation |
|
||||
|-----------------------------------------|----------------------------------------|-----------------------|
|
||||
| `teaching.PackMutationProposal` | `epistemic_status: EpistemicStatus` | `SPECULATIVE` |
|
||||
| `teaching.ReviewedTeachingExample` | `epistemic_status: EpistemicStatus` | `SPECULATIVE` |
|
||||
| `language_packs.schema.LexicalEntry` | `epistemic_status: str` | `"coherent"` (seed) |
|
||||
| `core.cognition.trace.compute_trace_hash` | `teaching_epistemic_status: str` | `""` if no proposal |
|
||||
|
||||
Promotion of a proposal's status uses the immutable updater
|
||||
`PackMutationProposal.with_status(...)` — original is never mutated.
|
||||
|
||||
The status of the load-bearing proposal in a turn is folded into
|
||||
`trace_hash` so replay detects when a downstream surface was produced
|
||||
under a different epistemic frame than at the time of recall.
|
||||
|
||||
## Test organization target
|
||||
|
||||
Future test moves should follow this taxonomy:
|
||||
|
|
|
|||
|
|
@ -328,6 +328,7 @@ def _parse_entry(payload: dict) -> LexicalEntry:
|
|||
semantic_domains=tuple(payload.get("semantic_domains", [])),
|
||||
manifold_point_checksum=payload.get("manifold_point_checksum"),
|
||||
provenance_ids=tuple(payload.get("provenance_ids", [])),
|
||||
epistemic_status=payload.get("epistemic_status", "coherent"),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,15 @@ class MorphologyEntry:
|
|||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class LexicalEntry:
|
||||
"""One surface/lemma entry in a compiled linguistic manifold."""
|
||||
"""One surface/lemma entry in a compiled linguistic manifold.
|
||||
|
||||
`epistemic_status` follows ADR-0021: it is a *position in the
|
||||
revision graph*, not a source-trust tier. Seed vocabulary defaults
|
||||
to ``"coherent"`` per ADR §Schema impact; bumps to other statuses
|
||||
require a deliberate curator review at pack version bumps. Absent
|
||||
from the JSONL is treated as the seed default — no retroactive
|
||||
tagging without review.
|
||||
"""
|
||||
|
||||
entry_id: str
|
||||
surface: str
|
||||
|
|
@ -111,6 +119,7 @@ class LexicalEntry:
|
|||
semantic_domains: tuple[str, ...] = field(default_factory=tuple)
|
||||
manifold_point_checksum: str | None = None
|
||||
provenance_ids: tuple[str, ...] = field(default_factory=tuple)
|
||||
epistemic_status: str = "coherent"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
|
|
|
|||
|
|
@ -13,15 +13,19 @@ approval before they touch the vocabulary manifold.
|
|||
"""
|
||||
|
||||
from teaching.correction import CorrectionCandidate, extract_correction
|
||||
from teaching.epistemic import ADMISSIBLE_AS_EVIDENCE, EpistemicStatus, parse_status
|
||||
from teaching.review import ReviewedTeachingExample, ReviewOutcome, review_correction
|
||||
from teaching.store import TeachingStore, PackMutationProposal
|
||||
|
||||
__all__ = [
|
||||
"ADMISSIBLE_AS_EVIDENCE",
|
||||
"CorrectionCandidate",
|
||||
"extract_correction",
|
||||
"EpistemicStatus",
|
||||
"PackMutationProposal",
|
||||
"ReviewedTeachingExample",
|
||||
"ReviewOutcome",
|
||||
"review_correction",
|
||||
"TeachingStore",
|
||||
"PackMutationProposal",
|
||||
"extract_correction",
|
||||
"parse_status",
|
||||
"review_correction",
|
||||
]
|
||||
|
|
|
|||
56
teaching/epistemic.py
Normal file
56
teaching/epistemic.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
"""Epistemic Grade Policy — typed status surface per ADR-0021.
|
||||
|
||||
`EpistemicStatus` is a *position in the revision graph*, not a trust tier.
|
||||
Source labels (peer_consensus, outsider_empirical, established,
|
||||
unauthoritative) are deliberately not part of this enum — they would
|
||||
re-import the bias ADR-0021 refuses.
|
||||
|
||||
The four positions form an open lattice under review. No member carries
|
||||
a "hardened" or "permanent" flag (non-hardening invariant, ADR-0021 §2).
|
||||
Every claim remains revisable; a Stage-3 inversion path is always
|
||||
available for `FALSIFIED` claims.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum, unique
|
||||
|
||||
|
||||
@unique
|
||||
class EpistemicStatus(Enum):
|
||||
"""Position of a claim in the reviewed revision graph.
|
||||
|
||||
Coherence is the only admission signal (ADR-0021 §3). Transitions
|
||||
between statuses are *computed from coherence with the existing
|
||||
reviewed field*, not asserted by source authority.
|
||||
"""
|
||||
|
||||
COHERENT = "coherent"
|
||||
CONTESTED = "contested"
|
||||
SPECULATIVE = "speculative"
|
||||
FALSIFIED = "falsified"
|
||||
|
||||
|
||||
# Statuses that admit a claim as evidence in downstream inference.
|
||||
# `SPECULATIVE` is admissible only as a candidate, not as evidence.
|
||||
# `CONTESTED` is admissible but cannot drive inferences depending on its truth.
|
||||
# `FALSIFIED` is retained for provenance and Stage-3 inversion, not evidence.
|
||||
ADMISSIBLE_AS_EVIDENCE: frozenset[EpistemicStatus] = frozenset({
|
||||
EpistemicStatus.COHERENT,
|
||||
})
|
||||
|
||||
|
||||
def parse_status(value: str | None) -> EpistemicStatus:
|
||||
"""Parse a serialised status string, defaulting to SPECULATIVE.
|
||||
|
||||
SPECULATIVE is the safe default at proposal creation per ADR-0021
|
||||
§Schema impact: "transitions to COHERENT / CONTESTED / FALSIFIED only
|
||||
via the review path." An absent or unknown value must not silently
|
||||
promote a claim to COHERENT.
|
||||
"""
|
||||
if value is None or value == "":
|
||||
return EpistemicStatus.SPECULATIVE
|
||||
for status in EpistemicStatus:
|
||||
if status.value == value:
|
||||
return status
|
||||
return EpistemicStatus.SPECULATIVE
|
||||
|
|
@ -21,6 +21,7 @@ from enum import Enum, unique
|
|||
|
||||
from core.physics.identity import IdentityCheck, IdentityManifold, IdentityScore
|
||||
from teaching.correction import CorrectionCandidate
|
||||
from teaching.epistemic import EpistemicStatus
|
||||
|
||||
|
||||
@unique
|
||||
|
|
@ -224,9 +225,20 @@ def _review_hash(candidate: CorrectionCandidate, outcome: ReviewOutcome) -> str:
|
|||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ReviewedTeachingExample:
|
||||
"""A correction that has passed the review gate (or been rejected).
|
||||
|
||||
`epistemic_status` is orthogonal to `outcome` per ADR-0021
|
||||
§Schema impact: "Accepting a proposal is not the same as ratifying
|
||||
it as COHERENT — the two are orthogonal and both required for
|
||||
admission as evidence." At v1 the default is SPECULATIVE for any
|
||||
review outcome; promotion to COHERENT / CONTESTED / FALSIFIED is a
|
||||
separate curator-mediated coherence judgment, not implied by
|
||||
acceptance.
|
||||
"""
|
||||
candidate: CorrectionCandidate
|
||||
outcome: ReviewOutcome
|
||||
review_hash: str
|
||||
epistemic_status: EpistemicStatus = EpistemicStatus.SPECULATIVE
|
||||
|
||||
@property
|
||||
def accepted(self) -> bool:
|
||||
|
|
@ -237,6 +249,7 @@ class ReviewedTeachingExample:
|
|||
"candidate": self.candidate.as_dict(),
|
||||
"outcome": self.outcome.value,
|
||||
"review_hash": self.review_hash,
|
||||
"epistemic_status": self.epistemic_status.value,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -245,6 +258,7 @@ def review_correction(
|
|||
*,
|
||||
identity_score: IdentityScore | None = None,
|
||||
identity_manifold: IdentityManifold | None = None,
|
||||
epistemic_status: EpistemicStatus = EpistemicStatus.SPECULATIVE,
|
||||
) -> ReviewedTeachingExample:
|
||||
"""Review a correction candidate and produce a teaching example.
|
||||
|
||||
|
|
@ -271,4 +285,5 @@ def review_correction(
|
|||
candidate=candidate,
|
||||
outcome=outcome,
|
||||
review_hash=_review_hash(candidate, outcome),
|
||||
epistemic_status=epistemic_status,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import json
|
|||
from dataclasses import dataclass
|
||||
|
||||
from teaching.correction import CorrectionCandidate
|
||||
from teaching.epistemic import EpistemicStatus
|
||||
from teaching.review import ReviewedTeachingExample
|
||||
|
||||
|
||||
|
|
@ -25,6 +26,13 @@ class PackMutationProposal:
|
|||
stored alongside the opaque text so the inference operators in
|
||||
``generate.operators`` can walk the typed-relation graph that the
|
||||
teaching store represents (ADR-0018).
|
||||
|
||||
`epistemic_status` is set to SPECULATIVE at creation per ADR-0021
|
||||
§Schema impact: "transitions to COHERENT / CONTESTED / FALSIFIED
|
||||
only via the review path." It is a *position in the revision
|
||||
graph*, not a source-trust tier. No `final`, `frozen`, `axiom`, or
|
||||
`permanent` flag exists or may be added (non-hardening invariant,
|
||||
ADR-0021 §2).
|
||||
"""
|
||||
proposal_id: str
|
||||
candidate_id: str
|
||||
|
|
@ -33,6 +41,7 @@ class PackMutationProposal:
|
|||
prior_surface: str
|
||||
applied: bool = False
|
||||
triple: tuple[str, str, str] | None = None
|
||||
epistemic_status: EpistemicStatus = EpistemicStatus.SPECULATIVE
|
||||
|
||||
def as_dict(self) -> dict[str, object]:
|
||||
return {
|
||||
|
|
@ -43,8 +52,20 @@ class PackMutationProposal:
|
|||
"prior_surface": self.prior_surface,
|
||||
"applied": self.applied,
|
||||
"triple": list(self.triple) if self.triple is not None else None,
|
||||
"epistemic_status": self.epistemic_status.value,
|
||||
}
|
||||
|
||||
def with_status(self, status: EpistemicStatus) -> "PackMutationProposal":
|
||||
"""Return a new proposal with `epistemic_status` set to `status`.
|
||||
|
||||
Immutable update — never mutates the original. This is the only
|
||||
admissible transition path for a proposal's epistemic status; it
|
||||
must be driven by a coherence judgment, not by source authority
|
||||
(ADR-0021 §3).
|
||||
"""
|
||||
from dataclasses import replace
|
||||
return replace(self, epistemic_status=status)
|
||||
|
||||
|
||||
def _proposal_id(candidate: CorrectionCandidate) -> str:
|
||||
payload = json.dumps(
|
||||
|
|
@ -96,6 +117,7 @@ class TeachingStore:
|
|||
correction_text=example.candidate.correction_text,
|
||||
prior_surface=example.candidate.prior_surface,
|
||||
triple=triple,
|
||||
epistemic_status=example.epistemic_status,
|
||||
)
|
||||
self._proposals.append(proposal)
|
||||
return proposal
|
||||
|
|
|
|||
144
tests/test_epistemic_invariants.py
Normal file
144
tests/test_epistemic_invariants.py
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
"""ADR-0021 non-hardening invariant tests.
|
||||
|
||||
The Epistemic Grade Policy commits to:
|
||||
|
||||
1. `epistemic_status` is a position in the revision graph, not a trust
|
||||
tier.
|
||||
2. No reviewed claim, relation, or proposition-graph edge ever becomes
|
||||
unrevisable — no `final`, `frozen`, `axiom`, or `permanent` flag may
|
||||
exist or be added on the runtime data model.
|
||||
3. Coherence is the only admission signal — source-trust labels must not
|
||||
be part of the schema.
|
||||
|
||||
These tests are the structural checks behind those commitments. They
|
||||
are intentionally simple and read like contract assertions, not
|
||||
behaviour tests.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
|
||||
from teaching import EpistemicStatus, PackMutationProposal, ReviewedTeachingExample
|
||||
from teaching.correction import CorrectionCandidate
|
||||
from teaching.review import ReviewOutcome
|
||||
|
||||
|
||||
_FORBIDDEN_HARDENING_NAMES: frozenset[str] = frozenset({
|
||||
"final",
|
||||
"frozen",
|
||||
"axiom",
|
||||
"permanent",
|
||||
"immutable_truth",
|
||||
"sealed",
|
||||
})
|
||||
|
||||
# Source-trust tier names that ADR-0021 §3 forbids in the schema.
|
||||
_FORBIDDEN_TRUST_TIER_NAMES: frozenset[str] = frozenset({
|
||||
"peer_consensus",
|
||||
"outsider_empirical",
|
||||
"established",
|
||||
"unauthoritative",
|
||||
"credentialed",
|
||||
"source_trust",
|
||||
"authority",
|
||||
"trust_tier",
|
||||
})
|
||||
|
||||
|
||||
def _field_names(cls: type) -> set[str]:
|
||||
return {f.name for f in dataclasses.fields(cls)}
|
||||
|
||||
|
||||
def _enum_value_strings(enum_cls) -> set[str]:
|
||||
return {member.value for member in enum_cls}
|
||||
|
||||
|
||||
def test_pack_mutation_proposal_has_no_hardening_flag():
|
||||
"""PackMutationProposal must not expose any name implying permanence."""
|
||||
names = _field_names(PackMutationProposal)
|
||||
forbidden = names & _FORBIDDEN_HARDENING_NAMES
|
||||
assert forbidden == set(), (
|
||||
f"PackMutationProposal exposes hardening flag(s) {forbidden}; "
|
||||
"ADR-0021 §2 forbids non-revisable state on the runtime data model."
|
||||
)
|
||||
|
||||
|
||||
def test_reviewed_teaching_example_has_no_hardening_flag():
|
||||
"""ReviewedTeachingExample must not expose any name implying permanence."""
|
||||
names = _field_names(ReviewedTeachingExample)
|
||||
forbidden = names & _FORBIDDEN_HARDENING_NAMES
|
||||
assert forbidden == set(), (
|
||||
f"ReviewedTeachingExample exposes hardening flag(s) {forbidden}; "
|
||||
"ADR-0021 §2 forbids non-revisable state on the runtime data model."
|
||||
)
|
||||
|
||||
|
||||
def test_epistemic_status_enum_carries_no_trust_tier_names():
|
||||
"""EpistemicStatus must describe revision-graph position, not source trust."""
|
||||
values = _enum_value_strings(EpistemicStatus)
|
||||
forbidden = values & _FORBIDDEN_TRUST_TIER_NAMES
|
||||
assert forbidden == set(), (
|
||||
f"EpistemicStatus enum contains source-trust tier value(s) {forbidden}; "
|
||||
"ADR-0021 §3 forbids credentialing the schema."
|
||||
)
|
||||
|
||||
|
||||
def test_epistemic_status_enum_has_exactly_the_four_positions():
|
||||
"""ADR-0021 §1 names the enum members precisely; no silent additions."""
|
||||
assert _enum_value_strings(EpistemicStatus) == {
|
||||
"coherent",
|
||||
"contested",
|
||||
"speculative",
|
||||
"falsified",
|
||||
}
|
||||
|
||||
|
||||
def test_proposal_default_is_speculative():
|
||||
"""ADR-0021 §Schema impact: proposals start SPECULATIVE; promotion is
|
||||
a separate, review-mediated transition."""
|
||||
proposal = PackMutationProposal(
|
||||
proposal_id="x",
|
||||
candidate_id="y",
|
||||
subject="z",
|
||||
correction_text="a knows b",
|
||||
prior_surface="",
|
||||
)
|
||||
assert proposal.epistemic_status is EpistemicStatus.SPECULATIVE
|
||||
|
||||
|
||||
def test_proposal_with_status_returns_new_immutable_proposal():
|
||||
"""Immutable update — the original is never mutated."""
|
||||
original = PackMutationProposal(
|
||||
proposal_id="x",
|
||||
candidate_id="y",
|
||||
subject="z",
|
||||
correction_text="a knows b",
|
||||
prior_surface="",
|
||||
)
|
||||
promoted = original.with_status(EpistemicStatus.COHERENT)
|
||||
|
||||
assert promoted.epistemic_status is EpistemicStatus.COHERENT
|
||||
assert original.epistemic_status is EpistemicStatus.SPECULATIVE
|
||||
assert promoted is not original
|
||||
|
||||
|
||||
def test_reviewed_example_default_is_speculative_for_accepted_outcome():
|
||||
"""Acceptance is orthogonal to coherence ratification per ADR-0021
|
||||
§Schema impact: 'Accepting a proposal is not the same as ratifying
|
||||
it as COHERENT.'"""
|
||||
from generate.intent import DialogueIntent, IntentTag
|
||||
|
||||
intent = DialogueIntent(tag=IntentTag.DEFINITION, subject="a")
|
||||
candidate = CorrectionCandidate(
|
||||
candidate_id="c1",
|
||||
intent=intent,
|
||||
correction_text="a is b",
|
||||
prior_surface="",
|
||||
prior_turn=0,
|
||||
)
|
||||
from teaching import review_correction
|
||||
|
||||
reviewed = review_correction(candidate)
|
||||
assert reviewed.outcome is ReviewOutcome.ACCEPTED
|
||||
assert reviewed.epistemic_status is EpistemicStatus.SPECULATIVE
|
||||
Loading…
Reference in a new issue