core/scripts/generate_phase5_domain_lanes.py
Shay ad7993e861 feat(phase5): land 5.2–5.7 — six new fluency lanes, parallel sweep
Completes the Phase 5 curriculum-era lane checklist alongside 5.1.

English-substrate domain lanes (5.4–5.7) — extend the proven
english_fluency_ood pattern with new vocabulary domains. Same
13-construction realizer, same grammatical_coverage rubric, new
triples. All four lanes land at 100% on both splits:

  5.4 elementary_mathematics_ood    117/117 public + 39/39 holdouts
      domains: arithmetic, set, geometry  |  holdout: probability
  5.5 foundational_physics_ood      117/117 + 39/39
      domains: mechanics, electricity, thermodynamics  |  holdout: optics
  5.6 foundational_biology_ood      117/117 + 39/39
      domains: cell, organism, ecosystem  |  holdout: genetics
  5.7 classical_literature_ood      117/117 + 39/39
      domains: epic, tragedy, lyric  |  holdout: comedy

New-language lanes (5.2 Hebrew, 5.3 Koine Greek) — scoped honestly to
v1 = C01 only, script + length rubric. The realizer's
tense/aspect/quantifier/negation logic in generate/templates.py is
English-only; C02-C13 in HE/GRC requires Hebrew/Greek morphology +
rhetorical templates, named explicitly in each lane's gaps.md as the
v2 unblock path. v1 measures what infrastructure exists:

  5.2 hebrew_fluency       3/3  (predicate-subject-object assembly,
                                  Hebrew script gate)
  5.3 koine_greek_fluency  3/3  (subject-object-predicate assembly,
                                  Greek script gate)

Lane scaffolds follow the established pattern: contract.md, runner.py,
__init__.py, gaps.md, public/v1/cases.jsonl, dev/cases.jsonl,
holdouts/v1/cases.jsonl (5.4–5.7 only; HE/GRC holdouts deferred to v2
when vocabulary expands).

Generators + scorers:
  scripts/generate_phase5_domain_lanes.py      — 5.4–5.7 case emit
  scripts/scaffold_phase5_domain_lanes.py      — 5.4–5.7 contracts/runners
  scripts/generate_phase5_language_lanes.py    — 5.2/5.3 case emit
  scripts/score_phase5_holdouts.py             — parallel holdouts scoring
                                                 via multiprocessing.Pool
                                                 (mirrors the parallel-eval
                                                 pattern from evals/parallel.py)

Lanes are wired into core eval --list automatically through the
framework's lane discovery; parallel sweeps via bash background jobs
(one process per lane).

Regression clean: smoke 54, runtime 19, teaching 17, packs 6,
cognition 57, algebra 132. Cognition eval 100% across all metrics.
2026-05-16 20:59:31 -07:00

197 lines
6.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Generate Phase 5.45.7 domain fluency OOD lanes.
Each lane reuses the english_fluency_ood case-builder over a
domain-specific vocabulary set, demonstrating that fluency is
mechanistic in the realizer (templates over typed graph nodes), not
lexical (pack-bound). Same 13 constructions, same scoring rubric,
new vocabulary.
Lanes produced:
5.4 elementary_mathematics_ood public: arithmetic / set / geometry
holdout: probability
5.5 foundational_physics_ood public: mechanics / electricity / thermo
holdout: optics
5.6 foundational_biology_ood public: cell / organism / ecosystem
holdout: genetics
5.7 classical_literature_ood public: epic / tragedy / lyric
holdout: comedy
Predicates default to regular verbs so morphology gaps do not
confound the structural fluency claim.
Run:
.venv/bin/python scripts/generate_phase5_domain_lanes.py
"""
from __future__ import annotations
import json
from pathlib import Path
# Import the proven case-builder + construction list from the 5.1 generator.
from scripts.generate_english_fluency_ood import CONSTRUCTIONS, build_case
_Triple = tuple[str, str, str]
_Domain = dict[str, list[_Triple]]
_LaneSpec = dict[str, str | _Domain]
LANES: dict[str, _LaneSpec] = {
"elementary_mathematics_ood": {
"prefix_pub": "EMO-PUB",
"prefix_hold": "EMO-HOLD",
"public": {
"arithmetic": [
("addition", "yields", "sum"),
("product", "equals", "factor"),
("difference", "shows", "remainder"),
],
"set": [
("union", "joins", "element"),
("subset", "fits", "superset"),
("intersection", "shares", "member"),
],
"geometry": [
("triangle", "encloses", "angle"),
("circle", "bounds", "region"),
("polygon", "contains", "vertex"),
],
},
"holdout": {
"probability": [
("event", "carries", "weight"),
("outcome", "favors", "sample"),
("distribution", "covers", "range"),
],
},
},
"foundational_physics_ood": {
"prefix_pub": "FPO-PUB",
"prefix_hold": "FPO-HOLD",
"public": {
"mechanics": [
("force", "moves", "object"),
("torque", "rotates", "wheel"),
("impulse", "changes", "momentum"),
],
"electricity": [
("current", "powers", "circuit"),
("voltage", "drives", "charge"),
("resistor", "limits", "flow"),
],
"thermodynamics": [
("heat", "raises", "temperature"),
("piston", "compresses", "gas"),
("entropy", "tracks", "disorder"),
],
},
"holdout": {
"optics": [
("lens", "focuses", "ray"),
("mirror", "reflects", "beam"),
("prism", "separates", "color"),
],
},
},
"foundational_biology_ood": {
"prefix_pub": "FBO-PUB",
"prefix_hold": "FBO-HOLD",
"public": {
"cell": [
("ribosome", "assembles", "protein"),
("membrane", "guards", "interior"),
("mitochondrion", "produces", "energy"),
],
"organism": [
("plant", "absorbs", "sunlight"),
("animal", "consumes", "food"),
("fungus", "decomposes", "matter"),
],
"ecosystem": [
("predator", "hunts", "prey"),
("forest", "shelters", "creature"),
("river", "feeds", "wetland"),
],
},
"holdout": {
"genetics": [
("gene", "encodes", "trait"),
("allele", "varies", "expression"),
("chromosome", "carries", "marker"),
],
},
},
"classical_literature_ood": {
"prefix_pub": "CLO-PUB",
"prefix_hold": "CLO-HOLD",
"public": {
"epic": [
("hero", "leaves", "homeland"),
("warrior", "seeks", "glory"),
("voyager", "crosses", "ocean"),
],
"tragedy": [
("king", "loses", "kingdom"),
("daughter", "warns", "father"),
("oracle", "reveals", "doom"),
],
"lyric": [
("poet", "praises", "season"),
("singer", "mourns", "absence"),
("muse", "inspires", "verse"),
],
},
"holdout": {
"comedy": [
("servant", "tricks", "master"),
("twin", "confuses", "spouse"),
("rogue", "outwits", "judge"),
],
},
},
}
def emit_split(
domains: dict[str, list[tuple[str, str, str]]],
prefix: str,
out_path: Path,
) -> int:
out_path.parent.mkdir(parents=True, exist_ok=True)
lines: list[str] = []
for domain, triples in domains.items():
for code, name in CONSTRUCTIONS:
for i, triple in enumerate(triples):
aux = triples[(i + 1) % len(triples)]
cid = f"{prefix}_{domain}_{code}_{i+1:02d}"
case = build_case(cid, code, name, triple, aux=aux)
lines.append(json.dumps(case))
out_path.write_text("\n".join(lines) + "\n")
return len(lines)
def emit_dev(domains: dict[str, list[tuple[str, str, str]]], prefix: str, out_path: Path) -> int:
out_path.parent.mkdir(parents=True, exist_ok=True)
# One case per construction from the first domain — the same shape
# english_fluency_ood uses for its dev split.
first_domain = next(iter(domains))
triples = domains[first_domain]
lines: list[str] = []
for code, name in CONSTRUCTIONS:
case = build_case(f"{prefix}-DEV_{code}", code, name, triples[0], aux=triples[1])
lines.append(json.dumps(case))
out_path.write_text("\n".join(lines) + "\n")
return len(lines)
if __name__ == "__main__":
root = Path(__file__).resolve().parent.parent
for lane, spec in LANES.items():
lane_dir = root / "evals" / lane
pub = spec["public"]; assert isinstance(pub, dict)
hold = spec["holdout"]; assert isinstance(hold, dict)
prefix_pub = spec["prefix_pub"]; assert isinstance(prefix_pub, str)
prefix_hold = spec["prefix_hold"]; assert isinstance(prefix_hold, str)
n_pub = emit_split(pub, prefix_pub, lane_dir / "public" / "v1" / "cases.jsonl")
n_hold = emit_split(hold, prefix_hold, lane_dir / "holdouts" / "v1" / "cases.jsonl")
n_dev = emit_dev(pub, prefix_pub, lane_dir / "dev" / "cases.jsonl")
print(f"{lane:38s} public={n_pub:3d} holdouts={n_hold:3d} dev={n_dev:2d}")