Repository navigation
refactor(intrinsics): guardian.py whole-file migration + documents= kw-only + auto-context discovery (Epic #929 Phase 1) #1139
Description
Parent epic: #929
Design proposal: PR #1080 §17 Q3
Phase: 1 (Wave 3 — start after 1.A merges)
Depends on: 1.A
Independent of: 1.B, 1.C, 2.1
Closes: #1094
⚠️ Pre-flight dependency: PR #935 must be merged (or conflict-plan agreed) before starting this issue. PR #935 is a large open docs PR touchingdocs/docs/advanced/intrinsics.md,AGENTS.md, and guardian examples — the same files this issue touches. Rebased onto main 2026-05-19.Do not start #1071 (adapter-backed Requirement subclass for Guardian) before this issue merges. Once merged, the new
Adaptertypes from Phase 0/1 are the correct foundation for that feature.
Problem
mellea/stdlib/components/intrinsic/guardian.py (221 lines, verified main) contains four helpers: policy_guardrails, guardian_check, factuality_detection, factuality_correction. All construct via the old API. Three related concerns:
factuality_detection/factuality_correctiontakedocumentsas a positional argument — inconsistent with kw-only patterns elsewhere- Return type for factuality helpers changed from
floattostrper fix: intrinsic function signatures #1003 (closed PR feat: normailze intinsics interfaces #1028 inherited the direction) — this PR codifies it - When
documents=None, callers are forced to pass documents explicitly even when those documents are already in conversation context
Agreed design
Follows the per-helper-file migration template from PR #1080. File-specific deltas:
(i) Migrate all four helpers to new types
Each helper has its own declared output contract; validate per-helper.
(ii) documents= keyword-only on factuality helpers
factuality_detection and factuality_correction get documents: list[str] | None = None as keyword-only. policy_guardrails and guardian_check do NOT take documents=.
(iii) Factuality return type str
Both factuality functions return str (per #1003). Update tests accordingly.
(iv) Auto-context document discovery (factuality only)
When documents=None: the helper auto-discovers user-supplied documents from ordinary conversation context (e.g. Message content). No _docs-specific extraction path — direction of PR #1028 adopted but not its specific code path. Per §17 Q3 (resolved 2026-05-20).
The helper reads whatever documents are present in the context it receives. Caller responsibility: populate context via explicit documents=, prior Messages, retrieval, etc.
Implementer's choice (and must be tested): what happens when documents=None AND no context documents exist — sentinel return or explicit error. Document the choice.
Why one PR
All four helpers share guardian.py. Three theoretical PRs (Guardian-core / factuality-pair / auto-context) would conflict on the same file.
Scope
mellea/stdlib/components/intrinsic/guardian.pydocs/examples/sessions/creating_a_new_type_of_session.py— migrate off deprecatedGuardianCheck(scope of Migrate creating_a_new_type_of_session.py example off deprecated GuardianCheck #1094; close that issue with this PR)- Tests under
test/stdlib/components/intrinsic/test_guardian.py
Out of scope
Other helper files (1.B, 1.C), reviving #1028's _docs scanning code, modifying how callers populate context, adding new Guardian capabilities.
Acceptance criteria
policy_guardrails, guardian_check:
- Construct via new types
- Accept
model_options: dict | None = None - Declared output contract; contract-break raises, forward-compat does not
- No
documents=parameter
factuality_detection, factuality_correction:
- Construct via new types
- Accept
model_options: dict | None = None -
documents: list[str] | None = Noneis keyword-only; positional second arg raisesTypeError - Return type is
str(per fix: intrinsic function signatures #1003) -
documents=[...]works (explicit pass-through) -
documents=Nonewith documents in conversation context works (auto-discovery) - No
_docs-specific code path in this file - Behaviour when
documents=Noneand no context documents: documented and tested explicitly
Common:
- All existing helper tests pass
-
docs/examples/sessions/creating_a_new_type_of_session.pymigrated offGuardianCheck; Migrate creating_a_new_type_of_session.py example off deprecated GuardianCheck #1094 closed -
docs/examples/intrinsics/examples that call these helpers updated -
ruff format,ruff check,mypyclean
Test plan
Common per-helper tests (see template). Plus:
test_factuality_detection_explicit_documents— pass-throughtest_factuality_detection_auto_discovery_from_contexttest_factuality_detection_no_documents_anywhere— implementer-chosen behaviourtest_factuality_documents_kwonly— positional second arg raisesTypeErrortest_factuality_return_type_is_str
Breaking changes
documents=becomes keyword-only forfactuality_detection/factuality_correction. Positional callers must update. Surface in changelog with migration example.- Return type change (
float → str) already in main per fix: intrinsic function signatures #1003 — this codifies it as a contract.
References
- PR docs: design proposal for adapter lifecycle (#929) — review channel, do not merge #1080 §13, §17 Q3 (resolved 2026-05-20)
- PR feat: normailze intinsics interfaces #1028 (closed) — direction inherited, mechanism shelved
- fix: intrinsic function signatures #1003 — original signature/return-type scope
- Migrate creating_a_new_type_of_session.py example off deprecated GuardianCheck #1094 — close with this issue
- feat: intrinsic-backed Requirement subclass for Guardian safety validation in repair loops #1071 — DO NOT start before this issue merges
Activity
planetf1 commentedon Jun 11, 2026
This issue uses vocabulary that has been superseded by the agreed Epic #929 glossary. Please use the following terms in any new work related to this issue:
The canonical definitions are now in the project glossary and were established as part of #1192. Python symbol names (
IntrinsicAdapter,fetch_intrinsic_metadata, etc.) keep their current names until Phase 1 (#1136) and Phase 4 (#1144).feat(intrinsics): shim old adapters onto Adapter dataclass; rewrite c…
feat(intrinsics): shim old adapters onto Adapter dataclass; rewrite c…
feat(intrinsics): shim old adapters onto Adapter dataclass; rewrite c…
feat(intrinsics): shim old adapters onto Adapter dataclass; rewrite c…
feat(intrinsics): shim old adapters onto Adapter dataclass; rewrite c…
feat(intrinsics): shim old adapters onto Adapter dataclass; rewrite c…
refactor(intrinsics): guardian.py whole-file migration + documents= k…
feat(intrinsics): shim old adapters onto Adapter dataclass; call_intr…
planetf1 commentedon Jun 24, 2026
The IOContract subclass + Adapter constants work deferred from this issue is tracked in #1332. That issue is blocked on this one merging (and on #1137 merging, for the
call_intrinsic(io_contract=)parameter).refactor(intrinsics): guardian.py whole-file migration + documents= k…