core/docs/analysis/kernel-substrate-tranche1-implementation-2026-06-18.md
Shay 58a94c8e4b
feat: implement kernel substrate tranche 1 base-layer foundations (#829)
* feat: implement kernel substrate tranche 1 base-layer foundations

* fix(kernel): ground scalar spans and tighten morphology labels

Add extract_scalar_candidates() with exact source spans and problem_text
provenance while keeping canonicalize_scalar() as the detached pack helper.
Morphology labels now emit missing_* only when substrate frame/unit lookups
actually fail, not on mere trigger-surface presence.
2026-06-18 18:46:20 -07:00

195 lines
11 KiB
Markdown

# Kernel Substrate Tranche 1 — Implementation Documentation
This document records the implementation of the Kernel Substrate Tranche 1 base-layer foundations in the `AssetOverflow/core` repository.
---
## 1. Purpose
The Kernel Substrate layer establishes a deterministic, reusable knowledge base of math facts, unit conversions, process triggers, and ambiguity hazard indicators. Previously, math derivation and candidate alignment organs (A2 organs) duplicated the parsing and validation logic of fundamental surfaces. By centralizing this base knowledge into a domain-agnostic substrate, future organs can consume unified intermediate representations (IR) and focus solely on structural planning and alignment.
## 2. Relationship to PR #828
PR #827 and PR #828 introduced the core semantic packs (`en_units_v1` and `en_numerics_v1`) along with the low-level loader modules (`loader.py` and `numerics_loader.py`). This tranche constructs thin, contract-backed facade layers on top of those low-level loaders. It refines the raw dictionary/list lookup structures into typed, immutable records and enforces strict verification gates without altering the underlying pack schemas.
## 3. Modules Added
Seven new Python modules have been introduced to establish the substrate layer:
1. `generate/kernel_facts.py` — Immutable, typed records for facts, hazard annotations, and provenance verification.
2. `language_packs/scalar_equivalence.py` — Facade over the numerics loader returning exact rational fractions for scalar surfaces.
3. `language_packs/unit_dimensions.py` — Facade over the units loader offering dimension compatibility checks and exact conversions.
4. `language_packs/ambiguity_hazards.py` — Registry of known ambiguous terms, their hazard categories, and required resolving contexts.
5. `generate/process_frames.py` — Declarative, non-arithmetic process frame schemas declaring semantic roles and triggers.
6. `generate/problem_frame.py` — Target IR skeleton (`ProblemFrame`) and builder class (`ProblemFrameBuilder`) for intermediate problem state representation.
7. `scripts/gsm8k_substrate_morphology.py` — Diagnostic script mapping refused problems to missing-substrate categories.
---
## 4. Scalar Equivalence Coverage
### Canonicalize vs grounded extraction
`language_packs/scalar_equivalence.py` exposes two complementary APIs:
- **`canonicalize_scalar(surface)`** — pack-level helper for detached surface strings.
Returns a `ScalarCandidate` with canonical `Fraction`, source kind, entry id, and
hazards. Provenance and span fields remain `None` (no problem-text grounding).
- **`extract_scalar_candidates(text)`** — text-level extraction for ProblemFrame
substrate facts. Every emitted candidate carries:
- `source_surface` — exact substring from the original text
- `source_span``(start, end)` character offsets (Python slice semantics)
- `provenance_kind = "problem_text"`
- `canonical` — exact `Fraction`
- `source` — scalar resolution kind (`fraction_word`, `decimal`, etc.)
- `hazards` — ambiguity hazard IDs
Pack/world/derived values (`classify_dimension`, detached `canonicalize_scalar`)
do not masquerade as `problem_text`. Multiple scalars are emitted in deterministic
left-to-right span order. Unsupported forms (`.5`, `1 / 2`) are omitted from
extraction and flagged separately by the morphology atlas.
Exposed scalar surfaces via the facade:
- **Word forms:** `half`, `one half`, `one-half`, `third`, `one third`, `two thirds`, `quarter`, `one quarter`, `three quarters`.
- **Symbols:** Unicode symbols (`½`, `¼`, `¾`, `⅓`, `⅔`).
- **Mixed numbers / slash forms:** `1/2`, `3/4`, `1 1/2`.
- **Decimals:** `0.5` -> `Fraction(1, 2)`, `0.25` -> `Fraction(1, 4)`, `0.75` -> `Fraction(3, 4)` (canonical values are strictly exact rational fractions).
- **Percentages:** `50%` -> `Fraction(1, 2)`, `25%` -> `Fraction(1, 4)`, `75%` -> `Fraction(3, 4)`, `100%` -> `Fraction(1, 1)`.
---
## 5. Unit / Dimension Coverage
Exposed via `language_packs/unit_dimensions.py`:
- **Supported Families:** `count` (items), `money` (dollars, cents), `time` (seconds, minutes, hours, days, weeks), `length` (inches, feet, yards, miles).
- **Rate Dimensions:** wage (`money/time`), speed (`length/time`), unit price (`money/count`), frequency (`count/time`), density (`mass/volume`), items per container (`count/container`).
- **Compatibility:** Two dimensions are compatible if they match exactly, or if both are rate dimensions with compatible numerator and denominator components.
- **Conversion:** Exact conversions (e.g. feet to inches, dollars to cents) return exact rational ratios. Multi-hop conversions are not licensed in Tranche 1.
---
## 6. Kernel Fact / Provenance Model
Exposed via `generate/kernel_facts.py`:
- **Fact Records:** `GroundedScalar`, `GroundedUnit`, `CandidateRelation`, and the union wrapper `SubstrateFact`.
- **Provenance Verification:** Enforced in `__post_init__`:
- `problem_text` provenance must carry exact character-offset `SourceSpan` records.
- `derived` provenance must specify `input_fact_ids`.
- Pack-backed facts (`kernel_unit`, `kernel_calendar`, `kernel_math`, `reviewed_pack`) must not carry source spans (masquerading as problem text is forbidden).
- `speculative` facts can be registered in the IR but are explicitly blocked from serving consumption (`is_speculative` flag).
---
## 7. Ambiguity Hazard Model
Exposed via `language_packs/ambiguity_hazards.py`:
- **16 Required Surfaces:** `half`, `quarter`, `third`, `percent`, `percentage points`, `times`, `more than`, `less than`, `of`, `per`, `each`, `some`, `remaining`, `left`, `total`, `altogether`.
- **16 Required Categories:** `unbound_base_quantity`, `half_duration`, `quarter_coin`, `quarter_calendar_period`, `quarter_school_term`, `third_ordinal`, `ordinal_context`, `currency_context`, `temporal_context`, `percent_change_vs_percent_of`, `multiplicative_vs_occurrence_times`, `comparative_direction_ambiguity`, `indefinite_quantity`, `remainder_context_required`, `total_question_target_required`, `blocked_provenance_gap`.
- **Design:** Statically built at module load. Provides deterministic categories and hazard ID lists (`haz-0001` to `haz-0021`) indicating resolving signals (like `numeric_base_quantity` for `half`).
---
## 8. Process Frame Schemas
Exposed via `generate/process_frames.py`:
- Exposes 8 declarative frames: `transfer`, `consumption`, `transaction`, `labor_rate`, `travel`, `container_packing`, `partition`, and `comparison`.
- Each schema declares:
- Trigger surfaces (e.g. `give` / `receive` for `transfer`).
- Required and optional semantic roles (e.g. `buyer`, `seller`, `price` for `transaction`).
- Target candidate relation type.
- Known ambiguity hazards.
- Explicit **not-licensed** constraints (e.g., executing arithmetic or solving is strictly forbidden).
---
## 9. ProblemFrame IR Skeleton
Exposed via `generate/problem_frame.py`:
- **IR Node:** `ProblemFrame` holds tuples of grounded scalars, facade candidates, units, actors, objects, relations, process frames, question targets, hazards, and provenance.
- **QuestionTarget:** Represents the question structure (`surface`, `target_type`, `unit`).
- **Builder:** `ProblemFrameBuilder` aggregates facts, automatically gathering nested hazards and provenance records to yield the immutable `ProblemFrame`.
---
## 10. Morphology / Flywheel Labels
Exposed via `scripts/gsm8k_substrate_morphology.py`:
- A diagnostic utility for labeling refused problems using 10 missing-substrate categories:
`missing_scalar_equivalence`, `missing_unit_dimension`, `missing_process_frame`, `missing_part_whole_frame`, `missing_container_frame`, `missing_temporal_frame`, `missing_route_frame`, `missing_question_target`, `blocked_ambiguity_hazard`, `blocked_provenance_gap`.
- Exposes a deterministic function `classify_missing_substrate` and a CLI interface to batch-process cases.
### Corrected label semantics (post-patch)
`missing_*` labels now mean **substrate lookup failure**, not mere trigger-surface
presence. For frame-backed categories, the classifier checks
`generate/process_frames.lookup_frame` before emitting a label:
- Text containing **give** does **not** receive `missing_process_frame` when the
`transfer` frame is registered.
- Text containing **box** does **not** receive `missing_container_frame` when
`container_packing` is registered.
- Similarly for **split** / `partition`, **drive** / `travel`, and other
registered frame triggers.
Labels that remain trigger-based (not frame lookup):
- `missing_scalar_equivalence` — unsupported numeric surfaces (`.5`, `1 / 2`, etc.)
- `missing_unit_dimension` — unknown unit-like nouns after digits
- `missing_temporal_frame` — time surfaces with no registered process frame
- `missing_question_target`, `blocked_ambiguity_hazard`, `blocked_provenance_gap`
All labels are deterministic and sorted.
---
## 11. Serving Integration Status
All modules implemented in Tranche 1 are **substrate-only**. None of the new classes are imported or consumed inside hot-path candidate generation (`math_candidate_graph.py`), solver layers, or scoring pipelines. Serving behavior is fully preserved.
---
## 12. Unsupported Surfaces
Documented and explicitly refused surfaces in the scalar facade:
- `.5` (refused by number format matching to prevent ambiguous tokenization).
- `1 / 2` (refused due to spaces around the division slash).
- `a half` in multiplier contexts (forces refusal or flags an unbound base hazard).
---
## 13. Validation
### Post-patch verification (2026-06-18)
After grounding scalar spans and tightening morphology label semantics:
1. **`git diff --check origin/main...HEAD`** — no whitespace errors.
2. **Kernel substrate unit tests** — all pass:
- `tests/test_kernel_facts.py`
- `tests/test_language_packs_scalar_equivalence.py` (includes span/provenance extraction)
- `tests/test_language_packs_unit_dimensions.py`
- `tests/test_ambiguity_hazards.py`
- `tests/test_process_frames.py`
- `tests/test_problem_frame_skeleton.py`
- `tests/test_gsm8k_morphology_missing_kernel_labels.py`
3. **Capability safety** — ADR-0128 numeric format and math candidate graph sprint tests pass.
4. **Evaluation scores (unchanged):**
- `train_sample`: 30 correct / 20 refused / 0 wrong — `wrong_ids: []`
- `holdout_dev`: 5 correct / 495 refused / 0 wrong — `wrong_ids: []`
5. **Smoke suite:** `core test --suite smoke -q` green.
### Initial tranche verification
Verification from the initial implementation:
1. **Unit tests:** 7 new test files containing 33 unit test cases verify all primitives, facade mappings, hazard lookups, and schemas. All 33 pass.
2. **Capability safety:** 227 existing tests pass, confirming zero regression.
3. **Evaluation scores:**
- `train_sample` observed: 30 correct / 20 refused / 0 wrong (exact match to target).
- `holdout_dev` observed: 5 correct / 495 refused / 0 wrong (exact match to target).
- `wrong_ids: []` in both sets.
---
## 14. Next PR: First ProblemFrame-Consuming Organ
With the foundations locked, the subsequent step (Tranche 2) will write the first derivation organ consuming the `ProblemFrame` IR. This organ will transition raw token spans to `GroundedScalar` nodes, resolve ambiguity hazards using local context signals, and plan relations deterministically via the declarative process frame schemas.