C1: delete generate/math_versor_arithmetic.py and its 3 tests (ADR-0139 add-only arithmetic spike; no runtime consumers, no pipeline wiring, follow-on lift paused per module docstring). C3: gitignore engine_state runtime artifacts (manifest.json, recognizers.jsonl, discovery_candidates.jsonl). Module code (engine_state/__init__.py) remains tracked; generated checkpoint files should not be. C5: document reader zero-delta root cause in train_sample/v1/README.md. Both Phase 2 (whole-problem) and Phase 1 (question-only) reader paths are called but inert because all 47 refusals are statement-level NO_INJECTOR gaps, not question-sentence gaps. Reader unblocks when injector coverage expands (C2 work). report.json use_reader flag corrected to reflect last run. C6: add deprecation header to generate/math_parser.py pointing at generate.math_candidate_graph.parse_and_solve as the live path. C2/C4 briefs: docs/handoff/CLEANUP-C2-run-lane-migration.md and docs/handoff/CLEANUP-C4-compositions-compile.md added as operator dispatch docs for the medium-scope wiring tasks.
127 lines
4.2 KiB
Markdown
127 lines
4.2 KiB
Markdown
# C4 — Compile and commit `compositions.jsonl` for the math pack
|
||
|
||
**Classification**: Half-built producer/consumer loop (audit finding C4)
|
||
**Risk**: Low — purely additive, wrong == 0 guaranteed by registry design
|
||
**Scope**: `language_packs/data/en_core_math_v1/`, `language_packs/compile_compositions.py`
|
||
|
||
---
|
||
|
||
## Problem
|
||
|
||
`language_packs/data/en_core_math_v1/compositions/multiplicative_composition.jsonl`
|
||
was ratified via ADR-0169 and is present on disk. But
|
||
`language_packs/data/en_core_math_v1/compositions.jsonl` (the compiled
|
||
artifact that the runtime reads) **does not exist**.
|
||
|
||
The consumer chain is:
|
||
|
||
```
|
||
compositions/multiplicative_composition.jsonl ← ratified source (EXISTS)
|
||
↓ compile_compositions.py
|
||
compositions.jsonl ← compiled registry (MISSING)
|
||
↓ comprehension/composition_registry.py::load_composition_registry()
|
||
↓ recognizer_anchor_inject.py (line 157–159)
|
||
InjectorEmission[] ← runtime output (always empty)
|
||
```
|
||
|
||
`load_composition_registry()` handles the missing file gracefully — it
|
||
returns an empty registry when `compositions.jsonl` is absent and
|
||
`manifest.json` does not declare `composition_checksum`. So the system is
|
||
stable; the ratified claims simply have no effect on runtime decisions.
|
||
|
||
---
|
||
|
||
## What to do
|
||
|
||
### Step 1 — Run the compile step
|
||
|
||
```bash
|
||
uv run python -c "
|
||
from pathlib import Path
|
||
from language_packs.compile_compositions import compile_pack_compositions
|
||
|
||
pack = Path('language_packs/data/en_core_math_v1')
|
||
result = compile_pack_compositions(pack)
|
||
print('written to:', result.output_path)
|
||
print('sha256:', result.sha256)
|
||
print('entries:', result.entry_count)
|
||
"
|
||
```
|
||
|
||
Verify `entry_count > 0` (at least the multiplicative_composition entries).
|
||
|
||
### Step 2 — Update manifest.json with composition_checksum
|
||
|
||
The manifest checksum enforces that the committed compiled artifact matches
|
||
what `compile_compositions.py` would produce from the source files. Once the
|
||
compiled artifact is committed, `manifest.json` should be updated to declare
|
||
`composition_checksum` so any future drift raises
|
||
`CompositionRegistryLoadError` at load time (defense in depth).
|
||
|
||
```bash
|
||
sha=$(sha256sum language_packs/data/en_core_math_v1/compositions.jsonl | cut -d' ' -f1)
|
||
# or on macOS:
|
||
sha=$(shasum -a 256 language_packs/data/en_core_math_v1/compositions.jsonl | cut -d' ' -f1)
|
||
```
|
||
|
||
Then add `"composition_checksum": "<sha>"` to `manifest.json`.
|
||
|
||
### Step 3 — Verify the registry is non-empty at runtime
|
||
|
||
```bash
|
||
uv run python -c "
|
||
from generate.comprehension.composition_registry import load_composition_registry
|
||
r = load_composition_registry()
|
||
print('empty:', r.is_empty())
|
||
print('categories:', list(r.by_category.keys()))
|
||
"
|
||
```
|
||
|
||
Expected: `empty: False`, at least `multiplicative_composition` in categories.
|
||
|
||
### Step 4 — Run packs + smoke suites
|
||
|
||
```bash
|
||
uv run core test --suite packs -q
|
||
uv run core test --suite smoke -q
|
||
```
|
||
|
||
### Step 5 — Commit
|
||
|
||
```
|
||
language_packs/data/en_core_math_v1/compositions.jsonl (new)
|
||
language_packs/data/en_core_math_v1/manifest.json (updated: composition_checksum)
|
||
```
|
||
|
||
Commit message:
|
||
```
|
||
feat(packs): compile multiplicative_composition registry for math pack
|
||
|
||
Runs compile_compositions.py to produce compositions.jsonl from the
|
||
ratified multiplicative_composition.jsonl source. Updates manifest.json
|
||
with composition_checksum. load_composition_registry() now returns a
|
||
non-empty registry; recognizer_anchor_inject.py can emit InjectorEmission
|
||
for composition-shape matches. Closes the producer/consumer gap from
|
||
ADR-0169.
|
||
```
|
||
|
||
---
|
||
|
||
## Invariant gates
|
||
|
||
- `wrong == 0` is preserved by the registry's refusal-preferring discipline:
|
||
`is_falsified` returns `()` immediately; `is_affirmed` gates every emission.
|
||
- The `WrongCompositionCategory` check at load time prevents any unsafe
|
||
category from being accepted even if the source file is mutated.
|
||
- The manifest checksum (after Step 2) provides ongoing compile-drift
|
||
detection.
|
||
|
||
---
|
||
|
||
## Relation to other findings
|
||
|
||
- **C2** (run_lane migration): C4 should land at the same time or before C2
|
||
so the candidate-graph path immediately sees a non-empty composition
|
||
registry.
|
||
- **C5** (reader zero-delta): the reader's zero-delta is unrelated to
|
||
compositions — it is a statement-level injector gap. C4 does not fix C5.
|