diff --git a/HANDOFF-gpt55-2026-06-20.md b/HANDOFF-gpt55-2026-06-20.md index 2a438707..cb17159b 100644 --- a/HANDOFF-gpt55-2026-06-20.md +++ b/HANDOFF-gpt55-2026-06-20.md @@ -279,3 +279,99 @@ The substantive result of this session is not just “0005 runnable.” It is th - **Versor condition**: No geometric algebra files were touched; versor invariants remain fully satisfied. - **Docs-Only Scope**: Verified that all changes are purely documentation additions or comments, with no runtime code or test suite modifications. + +--- + +## PR #840 Addendum — feat(kernel): add foundational family registry schema + +### Agent and Session + +- **Agent:** gpt55 +- **Date:** 2026-06-20 +- **Reasoning effort used:** medium +- **Grok Build mode used:** Headless +- **Session entry point:** Implement the typed, read-only foundational-family registry schema for the two #839 specifications without adding recognition, assessment, routing, solving, or serving behavior. +- **Branch:** `feat/kernel-foundational-family-registry-schema` +- **Base:** `origin/main` at `5cd377b1b8169826ea2406a93136b7615c616286` + +### Smoke Suite + Bootstrap Status + +```text +uv run python -m core.cli test --suite smoke -q +108 passed in 123.94s (baseline) +108 passed in 131.98s (post-edit) +``` + +`core-bootstrap` was not available as a skill; the GPT55.md checklist was completed manually. The acknowledged dirty/diverged `/Users/kaizenpro/Projects/core` main worktree was not modified, stashed, reset, cleaned, or reconciled. + +### Modules Touched + +| File | Change type | Summary | +|---|---|---| +| `generate/foundational_families.py` | new | Frozen specification/evidence records, two manually mirrored unauthorized family specs, immutable indices, and public accessors. | +| `tests/test_foundational_families.py` | new | Public-API invariant tests for exact registry membership/order, uniqueness, immutability, required fields, cross-domain evidence, authorization gates, and absence of file/Markdown loading. | +| `HANDOFF-gpt55-2026-06-20.md` | modified | Added this PR #840 continuity record. | + +### Invariants Verified + +- **Field closure:** Preserved by construction. No algebra, field, propagation, vault, normalization, or runtime field-state path was imported or modified. +- **Exact recall:** Preserved. No CGA lookup, cosine, ANN, HNSW, or memory path was added. +- **Serving and implementation gates:** Both families explicitly carry `serving_allowed=False` and `implementation_authorized=False`; tests enforce both gates. +- **No behavior slice:** The module is unreferenced by runtime code and provides descriptive immutable metadata only. No parser, recognizer, adapter, `ContractAssessment`, routing, derivation, eval, report, or serving behavior was added. +- **No live-substrate claim:** Module and current-state text explicitly state that general substrate/CGA retrieval is not live for these families. +- **No universal IR:** The schema mirrors only ADR-0224/#839 specification fields and contains only the two authorized registry entries. +- **Determinism:** Registry iteration is an explicit immutable tuple; family IDs and primary relation types are unique and tested. + +### Subagent / Arena Reconciliation + +- Number of subagents spawned: 0 +- Each subagent independently verified versor closure: N/A +- Reconciliation: N/A + +### Tests Run + +```bash +# exit 0 — 13 passed +uv run python -m pytest -q \ + tests/test_foundational_families.py \ + tests/test_construction_affordances.py + +# exit 0 — 108 passed +uv run python -m core.cli test --suite smoke -q + +# exit 0 +python3 -m compileall -q \ + generate/foundational_families.py \ + tests/test_foundational_families.py + +# exit 0 +git diff --check +``` + +### Open Tasks / Next Session Entry Point + +1. Review and publish PR #840 with the required body sections from the implementation brief. +2. Do not connect this registry to `ProblemFrame`, `ContractAssessment`, proposal routing, recognition, adapters, or serving without a separately authorized behavior PR. + +### Known Hazards / Do Not Touch + +- The two #839 specifications are gating artifacts, not implementation authorization. +- Do not parse `docs/specs/foundational-families/*.md` at runtime. +- Do not add acquisition, transaction, another family, or a universal family IR in this slice. +- The acknowledged local-only state under `/Users/kaizenpro/Projects/core` remains outside this PR and must not be altered. + +### Architectural Decision + +The foundational registry remains separate from `ConstructionFamily`. The latter describes live diagnostic construction proposals; this schema mirrors constitutional family gates. Combining them would falsely couple specification completeness to executable assessment machinery and blur current versus target state. + +### What Must Not Be Forgotten + +PR #840 makes the family-spec gate structurally inspectable; it authorizes nothing. Both entries remain non-serving and non-implementable until a separate evidence-backed PR is approved. + +### Skills Used + +- core-bootstrap: unavailable; checklist completed manually +- versor-coherence-guardian: unavailable; closure preserved by scope and smoke validation +- pre-edit-sweep: completed manually across imports/call sites in `generate/`, `tests/`, `evals/`, and `calibration/` +- claim-proposal-guardian: not needed; no claim or proposal mutation path touched +- Other: none diff --git a/generate/foundational_families.py b/generate/foundational_families.py new file mode 100644 index 00000000..cfeaffe0 --- /dev/null +++ b/generate/foundational_families.py @@ -0,0 +1,294 @@ +"""Read-only registry of approved foundational-family specifications. + +This module manually mirrors the constitutional family specifications approved +under ADR-0224. It is descriptive metadata only: it does not read specification +documents, recognize constructions, assess ``ProblemFrame`` contracts, route +proposals, or authorize implementation or serving. + +The current registry deliberately contains only the two specifications approved +by the initial family-spec gate. General substrate/CGA retrieval for these +families is not live. +""" +from __future__ import annotations + +from dataclasses import dataclass +from types import MappingProxyType + + +@dataclass(frozen=True, slots=True) +class FoundationalDomainEvidence: + """One cross-domain example declared by a foundational-family spec.""" + + domain: str + example: str + expected_roles: tuple[str, ...] + + +@dataclass(frozen=True, slots=True) +class FoundationalFamilySpec: + """Immutable descriptive mirror of an approved family specification. + + The fields state the future contract shape and its constitutional gates. + They do not implement recognition, binding, assessment, verification, or + serving behavior. + """ + + family_id: str + display_name: str + status: str + related_adrs: tuple[str, ...] + domains: tuple[str, ...] + summary: str + surface_chunk_patterns: tuple[str, ...] + semantic_neighborhood: tuple[str, ...] + construction_signatures: tuple[str, ...] + required_roles: tuple[str, ...] + optional_roles: tuple[str, ...] + hazards_confusers: tuple[str, ...] + frame_representation: tuple[str, ...] + contract_readiness_criteria: tuple[str, ...] + verification_style: tuple[str, ...] + refusal_conditions: tuple[str, ...] + cross_domain_evidence: tuple[FoundationalDomainEvidence, ...] + current_state: str + target_state: str + serving_status: str + serving_allowed: bool + implementation_authorized: bool + primary_relation_type: str + future_adapter: str | None = None + + +_QUANTITY_ENTITY_BINDING = FoundationalFamilySpec( + family_id="binding.quantity_entity", + display_name="Quantity-Entity Binding", + status="Proposed (gating specification)", + related_adrs=("ADR-0223", "ADR-0224"), + domains=( + "arithmetic_quantitative", + "physical_science", + "charts_tables_data", + "social_studies", + ), + summary=( + "Binds a grounded scalar quantity to the entity, object, category, or " + "measured property that it quantifies." + ), + surface_chunk_patterns=( + " ", + " of the ", + ": ", + " of ", + ), + semantic_neighborhood=( + "quantified_group", + "measured_property", + "category_cardinality", + "resource_population", + ), + construction_signatures=("quantity_entity_binding",), + required_roles=("quantity", "entity"), + optional_roles=("unit",), + hazards_confusers=( + "PF-EN-002 quantity_entity_unbound", + "PF-LX-004 span_collision", + "PF-EN-005 role_alias_collision", + "PF-HZ-005 conflict_auto_resolved", + ), + frame_representation=( + "GroundedMention for the quantity and entity spans", + "MentionBinding(binding_type='quantity_entity') from quantity to entity", + "Optional MentionBinding(binding_type='quantity_unit') from quantity to unit", + ), + contract_readiness_criteria=( + "Quantity is grounded to an exact parsed scalar.", + "Entity is grounded to a valid noun phrase or category span.", + "No active overlap or scalar span collision remains.", + "No unresolved quantity-entity hazard remains.", + "Independent verification and wrong-zero preservation are required before serving.", + ), + verification_style=( + "Lexical-substitution invariance must preserve binding topology modulo spans.", + "Comparative confusers must not be accepted as generic quantity-entity bindings.", + ), + refusal_conditions=( + "Ambiguous referents remain unresolved while multiple entity candidates are active.", + "Overlapping scalar spans cannot be deterministically ordered.", + ), + cross_domain_evidence=( + FoundationalDomainEvidence( + domain="physical_science", + example="A block of iron has a mass of 12 grams.", + expected_roles=("quantity", "entity", "unit"), + ), + FoundationalDomainEvidence( + domain="charts_tables_data", + example="The bar graph shows: Apples: 15, Bananas: 8.", + expected_roles=("quantity", "entity"), + ), + FoundationalDomainEvidence( + domain="social_studies", + example="The town of Shelbyville has a population of 50,000 residents.", + expected_roles=("quantity", "entity"), + ), + ), + current_state=( + "Not implemented as a general family. Local parser heuristics and selected " + "math diagnostic proposal traces exist; general substrate/CGA retrieval does not." + ), + target_state=( + "Proposal-first quantity-entity binding with span grounding, contract assessment, " + "independent verification, and cross-domain evidence." + ), + serving_status="Not implemented / not serving.", + serving_allowed=False, + implementation_authorized=False, + primary_relation_type="quantity_entity", + future_adapter="quantity_entity_adapter", +) + + +_STATE_CHANGE = FoundationalFamilySpec( + family_id="state_change.transition", + display_name="State Change", + status="Proposed (gating specification)", + related_adrs=("ADR-0223", "ADR-0224"), + domains=( + "arithmetic_proportional", + "physical_science", + "life_science", + "reading_comprehension", + "procedural_reasoning", + ), + summary=( + "Represents an entity or system transitioning from an initial state to a " + "final state through an event, action, or process." + ), + surface_chunk_patterns=( + " to ", + " by ", + "originally , now ", + "after , has ", + ), + semantic_neighborhood=( + "possession_change", + "proportional_scaling", + "temperature_transition", + "growth_stage", + "procedural_step", + ), + construction_signatures=("state_change",), + required_roles=("entity", "initial_state", "transition_event", "final_state"), + optional_roles=("scale", "delta"), + hazards_confusers=( + "PF-BD-004 positional_binding", + "PF-TG-004 target_direction_unknown", + "PF-TP-006 state_transition_open", + "PF-HZ-003 hazard_ignored_by_contract", + ), + frame_representation=( + "BoundRelation(relation_type='state_change')", + "RelationRole bindings for entity, initial_state, transition, and final_state", + "Optional RelationRole bindings for scale or delta", + "Exact SourceSpan evidence for the proposed transition", + ), + contract_readiness_criteria=( + "The changing entity is bound with continuity across the transition span.", + "Transition direction is explicitly resolved from grounded evidence.", + "The question target names a mathematically closed variable.", + "No unresolved 'by' versus 'to' directional hazard remains.", + "Independent verification and wrong-zero preservation are required before serving.", + ), + verification_style=( + "Validate the applicable state equation over grounded roles.", + "Changing 'decreased to' to 'decreased by' must require rebinding and reverification.", + ), + refusal_conditions=( + "Event order is missing or temporally ambiguous.", + "The transition is open because initial, delta, and final values cannot close a target.", + ), + cross_domain_evidence=( + FoundationalDomainEvidence( + domain="physical_science", + example="Water originally at 20 degrees Celsius is heated to 80 degrees Celsius.", + expected_roles=("entity", "initial_state", "transition_event", "final_state"), + ), + FoundationalDomainEvidence( + domain="life_science", + example="The plant grew from 5 inches tall to 9 inches tall over the summer.", + expected_roles=("entity", "initial_state", "transition_event", "final_state"), + ), + FoundationalDomainEvidence( + domain="reading_comprehension", + example=( + "Before the storm, the streets were dry. After the storm, the streets " + "were flooded." + ), + expected_roles=("entity", "initial_state", "transition_event", "final_state"), + ), + FoundationalDomainEvidence( + domain="procedural_reasoning", + example="Step 1: Set X to 10. Step 2: Decrement X by 2.", + expected_roles=("entity", "initial_state", "transition_event", "delta"), + ), + ), + current_state=( + "Selected math constructions have assessment-backed diagnostic proposal traces. " + "A general state-change adapter and non-math serving frames do not exist." + ), + target_state=( + "Proposal-first state-change binding with role-complete contract assessment, " + "independent verification, typed refusal, and cross-domain evidence." + ), + serving_status="Not implemented / not serving.", + serving_allowed=False, + implementation_authorized=False, + primary_relation_type="state_change", + future_adapter="state_change_adapter", +) + + +_FAMILIES = (_QUANTITY_ENTITY_BINDING, _STATE_CHANGE) +_BY_ID = MappingProxyType({family.family_id: family for family in _FAMILIES}) +_BY_RELATION_TYPE = MappingProxyType( + {family.primary_relation_type: family for family in _FAMILIES} +) + + +def iter_foundational_families() -> tuple[FoundationalFamilySpec, ...]: + """Return all approved foundational-family specs in deterministic order.""" + + return _FAMILIES + + +def get_foundational_family(family_id: str) -> FoundationalFamilySpec | None: + """Return the registered spec for ``family_id``, if present.""" + + return _BY_ID.get(family_id) + + +def require_foundational_family(family_id: str) -> FoundationalFamilySpec: + """Return the registered spec or raise ``KeyError`` with a clear identifier.""" + + family = get_foundational_family(family_id) + if family is None: + raise KeyError(f"unknown foundational family: {family_id!r}") + return family + + +def get_foundational_family_by_relation_type( + relation_type: str, +) -> FoundationalFamilySpec | None: + """Return the spec whose primary relation type matches ``relation_type``.""" + + return _BY_RELATION_TYPE.get(relation_type) + + +__all__ = ( + "FoundationalDomainEvidence", + "FoundationalFamilySpec", + "get_foundational_family", + "get_foundational_family_by_relation_type", + "iter_foundational_families", + "require_foundational_family", +) diff --git a/tests/test_foundational_families.py b/tests/test_foundational_families.py new file mode 100644 index 00000000..0937a983 --- /dev/null +++ b/tests/test_foundational_families.py @@ -0,0 +1,159 @@ +from __future__ import annotations + +import ast +from dataclasses import FrozenInstanceError +from pathlib import Path + +import pytest + +import generate.foundational_families as foundational_families +from generate.foundational_families import ( + FoundationalFamilySpec, + get_foundational_family, + get_foundational_family_by_relation_type, + iter_foundational_families, + require_foundational_family, +) + + +EXPECTED_FAMILY_IDS = ( + "binding.quantity_entity", + "state_change.transition", +) + + +def test_registry_contains_only_the_two_approved_specs_in_deterministic_order() -> None: + families = iter_foundational_families() + + assert isinstance(families, tuple) + assert tuple(family.family_id for family in families) == EXPECTED_FAMILY_IDS + assert tuple(family.family_id for family in iter_foundational_families()) == ( + EXPECTED_FAMILY_IDS + ) + + +def test_public_family_accessors() -> None: + quantity = get_foundational_family("binding.quantity_entity") + state_change = require_foundational_family("state_change.transition") + + assert quantity is not None + assert quantity.family_id == "binding.quantity_entity" + assert state_change.family_id == "state_change.transition" + assert get_foundational_family("missing.family") is None + + with pytest.raises(KeyError, match="missing.family"): + require_foundational_family("missing.family") + + +def test_lookup_by_primary_relation_type() -> None: + quantity = get_foundational_family_by_relation_type("quantity_entity") + state_change = get_foundational_family_by_relation_type("state_change") + + assert quantity is get_foundational_family("binding.quantity_entity") + assert state_change is get_foundational_family("state_change.transition") + assert get_foundational_family_by_relation_type("missing_relation") is None + + +def test_registry_keys_are_unique() -> None: + families = iter_foundational_families() + family_ids = tuple(family.family_id for family in families) + relation_types = tuple(family.primary_relation_type for family in families) + + assert len(family_ids) == len(set(family_ids)) + assert len(relation_types) == len(set(relation_types)) + + +def test_specs_are_frozen_and_explicitly_not_authorized() -> None: + for family in iter_foundational_families(): + assert isinstance(family, FoundationalFamilySpec) + assert family.serving_allowed is False + assert family.implementation_authorized is False + assert "not serving" in family.serving_status.lower() + + with pytest.raises(FrozenInstanceError): + family.display_name = "mutated" # type: ignore[misc] + + +def test_required_adr_0224_fields_are_populated() -> None: + required_fields = ( + "family_id", + "display_name", + "status", + "related_adrs", + "domains", + "summary", + "surface_chunk_patterns", + "semantic_neighborhood", + "construction_signatures", + "required_roles", + "optional_roles", + "hazards_confusers", + "frame_representation", + "contract_readiness_criteria", + "verification_style", + "refusal_conditions", + "cross_domain_evidence", + "current_state", + "target_state", + "serving_status", + "primary_relation_type", + "future_adapter", + ) + + for family in iter_foundational_families(): + assert all(getattr(family, field) for field in required_fields) + assert "ADR-0224" in family.related_adrs + assert family.hazards_confusers + assert family.refusal_conditions + assert family.current_state != family.target_state + + +def test_each_family_has_at_least_two_non_math_evidence_examples() -> None: + math_domains = { + "arithmetic", + "arithmetic_quantitative", + "arithmetic_proportional", + "mathematics", + } + + for family in iter_foundational_families(): + non_math_evidence = tuple( + evidence + for evidence in family.cross_domain_evidence + if evidence.domain not in math_domains + ) + assert len(non_math_evidence) >= 2 + assert all(evidence.example for evidence in non_math_evidence) + assert all(evidence.expected_roles for evidence in non_math_evidence) + + +def test_registry_module_has_no_markdown_or_filesystem_loading_surface() -> None: + source_path = Path(foundational_families.__file__) + tree = ast.parse(source_path.read_text(encoding="utf-8")) + + imported_modules = { + alias.name + for node in ast.walk(tree) + if isinstance(node, ast.Import) + for alias in node.names + } + imported_from_modules = { + node.module + for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) and node.module is not None + } + called_names = { + node.func.id + for node in ast.walk(tree) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) + } + called_attributes = { + node.func.attr + for node in ast.walk(tree) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) + } + + assert "pathlib" not in imported_modules | imported_from_modules + assert "markdown" not in imported_modules | imported_from_modules + assert "open" not in called_names + assert not ({"read_text", "read_bytes"} & called_attributes)