core/generate/derivation/state/bind.py
Shay a719f1b58c
feat(adr-0184): extract semantic-state helper seam S1 (#490)
* feat(adr-0184): add semantic-state helper package

* feat(adr-0184): add referent binding helpers

* feat(adr-0184): add change cue helpers

* refactor(adr-0184): use semantic-state helpers in accumulation

* test(adr-0184): cover semantic-state helper guards
2026-05-30 08:36:02 -07:00

71 lines
2.2 KiB
Python

"""ADR-0184 S1 — conservative referent-binding helpers.
These helpers are behavior-equivalent extractions from
:mod:`generate.derivation.accumulate`. They are deliberately small: loose
surface subject collection plus a refusal-first same-referent guard. They do
not resolve ambiguous pronouns, do not gender-match, and do not choose the most
recent actor. A new named subject is treated as a referent hazard by callers.
"""
from __future__ import annotations
import re
from typing import Final
PRONOUNS: Final[frozenset[str]] = frozenset(
{
"he",
"she",
"they",
"it",
"him",
"her",
"them",
"his",
"hers",
"its",
"their",
"we",
"i",
"you",
}
)
_WORD_RE: Final[re.Pattern[str]] = re.compile(r"[A-Za-z]+")
def leading_subject_token(clause: str) -> str | None:
"""Return the clause's leading word token, or ``None`` if wordless.
This is a loose signal collector, not a grammar parser. It mirrors the
prior accumulation helper so S1 is behavior-equivalent.
"""
match = _WORD_RE.search(clause)
return match.group(0) if match is not None else None
def continues_anchor_referent(clause: str, anchor_subject: str | None) -> bool:
"""Whether ``clause`` can safely continue ``anchor_subject``.
Conservative ADR-0184 rule, extracted from accumulation:
* no leading token: no new actor signal, so allow;
* leading pronoun: allow as a continuation candidate;
* same leading subject as the anchor: allow;
* any other capitalized leading non-pronoun: new named actor, so disallow;
* lowercase leading token: no named-actor signal, so allow.
This does **not** prove pronoun resolution. Callers still gate the resulting
candidate through grounding/completeness/pooling. Multi-actor ambiguity must
be handled by future semantic-world logic, not by choosing a most-recent actor.
"""
subject = leading_subject_token(clause)
if subject is None:
return True
if subject.lower() in PRONOUNS:
return True
if anchor_subject is not None and subject == anchor_subject:
return True
return not subject[:1].isupper()