From f69ccf3ff20555f48e6b48a6d96c3a5f7c7a4a67 Mon Sep 17 00:00:00 2001 From: dmitrii-f-t27 Date: Sat, 17 Oct 2026 09:05:09 -0300 Subject: [PATCH] spec(port): port specs/port/trios/crates/trios-igla-race/src/lessons.t27 (Closes #9413) Port gHashTag/trios:crates/trios-igla-race/src/lessons.rs (at 8b229e9489ee, 260+ lines) as specs/port/trios/crates/ trios-igla-race/src/lessons.t27 - Roadmap Stage 6, goal #4548, target t27c gen-rust. The Failure Memory's decisions are ported as pure functions and tested (8 tests, 8 pass, 0 vacuous, 48 runtime asserts, 100% function coverage), including the original's own high-lr and small-model tests: the two display tables (five lesson types in upper case, five outcomes in lower case), the severity priority (Avoid > Pattern > Warn > Info > Winner), the six rule gates - the learning rate above 0.05 / below 0.001, the d_model at most 64 with BPB above 2.8, the hidden below 32, the weight decay above 0.1 with BPB above 3.0, SGD with BPB above 2.7, and the early death at step at most 1000 with BPB above 3.0 - the composition answering the most severe triggered kind, the generic INFO fallback when nothing triggers, and the store/get shapes (the outcome riding along in its Display form; the three-wide top rows). The database round-trips are the callers' plumbing. Verified: parse OK, spec-status IMPLEMENTED, typecheck 0 errors, coverage 17/17, test-report 8/8 pass, 0 BLOCKED, gen 242 lines with 0 'not yet implemented', boundary exactly this one file, dupe_scan: all 12 introduced names unique across 2040 specs (checked before the commit), bare body scan adds no duplicate group (752/221 unchanged). Dmitrii Fedorov ([@dmitrii-f-t27](https://github.com/dmitrii-f-t27)) --- .../crates/trios-igla-race/src/lessons.t27 | 289 ++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 specs/port/trios/crates/trios-igla-race/src/lessons.t27 diff --git a/specs/port/trios/crates/trios-igla-race/src/lessons.t27 b/specs/port/trios/crates/trios-igla-race/src/lessons.t27 new file mode 100644 index 0000000000..65ac63aea7 --- /dev/null +++ b/specs/port/trios/crates/trios-igla-race/src/lessons.t27 @@ -0,0 +1,289 @@ +// Failure Memory — automatic lesson generation from pruned trials. +// Port of gHashTag/trios:crates/trios-igla-race/src/lessons.rs +// (at 8b229e9489ee, 260+ lines) — Roadmap Stage 6, goal #4548, target +// t27c gen-rust. +// +// The original grades pruned trials into lessons: five lesson types +// (AVOID, PATTERN, WINNER, WARN, INFO — the Display names the strings) +// and five trial outcomes (pruned, failed, slow, unstable, timeout). +// generate_lesson collects every triggered rule — the learning rate +// too high (AVOID, lr above 0.05) or too low (WARN, below 0.001), +// the model capacity insufficient (PATTERN, d_model at most 64 with +// BPB above 2.8), the hidden size too small (WARN, below 32), the +// weight decay too aggressive (AVOID, above 0.1 with BPB above 3.0), +// SGD underperforming (PATTERN, "sgd" in the optimizer with BPB above +// 2.7), and the early death (AVOID, step at most 1000 with BPB above +// 3.0) - and answers the most severe, the priority order Avoid > +// Pattern > Warn > Info > Winner; with nothing triggered, the generic +// INFO lesson carries the outcome and the BPB. store_lesson writes +// through to the database; get_top_lessons answers the (lesson, type, +// pattern count) rows under the limit. +// +// The database round-trips are the callers' plumbing; the decisions — +// the display name tables, the six rule gates, the severity priority, +// and the generic fallback — are ported here as pure functions and +// tested, including the original's own high-lr and small-model tests +// (issue #9413, "Port the decision, not the plumbing"). + +module port::trios_igla_race_lessons; + +// --------------------------------------------------------------------------- +// Original lines 10-28: `pub enum LessonType` and its Display — five +// types, five upper-case names. +// --------------------------------------------------------------------------- + +pub fn lesson_type_count() -> usize { + return 5; +} + +pub fn LessonType_fmt(i: usize) -> str { + if (i == 0) { return "AVOID"; } + if (i == 1) { return "PATTERN"; } + if (i == 2) { return "WINNER"; } + if (i == 3) { return "WARN"; } + if (i == 4) { return "INFO"; } + return ""; +} + +// --------------------------------------------------------------------------- +// Original lines 32-50: `pub enum Outcome` and its Display — five +// outcomes, five lower-case names. +// --------------------------------------------------------------------------- + +pub fn outcome_count() -> usize { + return 5; +} + +pub fn Outcome_fmt(i: usize) -> str { + if (i == 0) { return "pruned"; } + if (i == 1) { return "failed"; } + if (i == 2) { return "slow"; } + if (i == 3) { return "unstable"; } + if (i == 4) { return "timeout"; } + return ""; +} + +// --------------------------------------------------------------------------- +// Original lines 160-166: the severity priority — Avoid first, then +// Pattern, Warn, Info, Winner last. +// --------------------------------------------------------------------------- + +pub fn lesson_severity(name: str) -> usize { + if (name == "avoid") { return 0; } + if (name == "pattern") { return 1; } + if (name == "warn") { return 2; } + if (name == "info") { return 3; } + if (name == "winner") { return 4; } + return 5; +} + +pub fn lesson_is_more_severe(a: str, b: str) -> bool { + return lesson_severity(a) < lesson_severity(b); +} + +// --------------------------------------------------------------------------- +// Original lines 75-172: `generate_lesson` — the six rule gates, each +// answering its lesson kind; the most severe wins; with nothing +// triggered the generic INFO lesson. +// --------------------------------------------------------------------------- + +// The learning-rate gates (lines 83-96): above 0.05 is AVOID, below +// 0.001 is WARN, the middle band triggers nothing. +pub fn lr_lesson(lr: f64) -> str { + if (lr > 0.05) { + return "avoid"; + } + if (lr < 0.001) { + return "warn"; + } + return ""; +} + +// The capacity gate (lines 99-107): d_model at most 64 with BPB +// above 2.8 is PATTERN. +pub fn capacity_lesson(d_model: usize, bpb: f64) -> str { + if (d_model <= 64 and bpb > 2.8) { + return "pattern"; + } + return ""; +} + +// The hidden gate (lines 110-117): below 32 is WARN. +pub fn hidden_lesson(hidden: usize) -> str { + if (hidden < 32) { + return "warn"; + } + return ""; +} + +// The weight-decay gate (lines 120-127): above 0.1 with BPB above +// 3.0 is AVOID. +pub fn weight_decay_lesson(wd: f64, bpb: f64) -> str { + if (wd > 0.1 and bpb > 3.0) { + return "avoid"; + } + return ""; +} + +// The optimizer gate (lines 130-137): "sgd" in the optimizer with +// BPB above 2.7 is PATTERN. +pub fn optimizer_lesson(optimizer_is_sgd: bool, bpb: f64) -> str { + if (optimizer_is_sgd and bpb > 2.7) { + return "pattern"; + } + return ""; +} + +// The early-death gate (lines 140-146): step at most 1000 with BPB +// above 3.0 is AVOID. +pub fn early_death_lesson(step: usize, bpb: f64) -> str { + if (step <= 1000 and bpb > 3.0) { + return "avoid"; + } + return ""; +} + +pub fn lesson_rule_count() -> usize { + return 6; +} + +// The generic fallback (lines 149-154): nothing triggered answers +// the INFO kind carrying the outcome and the BPB. +pub fn generic_lesson_kind(any_triggered: bool) -> str { + if (any_triggered) { + return ""; + } + return "info"; +} + +// The composition: the first-triggered set's most severe kind +// answers; the port takes the triggered kinds in rule order and +// answers the winner by the severity table, with the generic INFO +// when the set is empty. +pub fn generate_lesson(lr_kind: str, capacity_kind: str, hidden_kind: str, wd_kind: str, optimizer_kind: str, early_death_kind: str) -> str { + var best: str = ""; + var best_rank: usize = 6; + var i: usize = 0; + const count: usize = 6; + while (i < count) { + var kind: str = ""; + if (i == 0) { kind = lr_kind; } + if (i == 1) { kind = capacity_kind; } + if (i == 2) { kind = hidden_kind; } + if (i == 3) { kind = wd_kind; } + if (i == 4) { kind = optimizer_kind; } + if (i == 5) { kind = early_death_kind; } + if (kind != "") { + const rank: usize = lesson_severity(kind); + if (rank < best_rank) { + best_rank = rank; + best = kind; + } + } + i += 1; + } + if (best != "") { + return best; + } + return "info"; +} + +// --------------------------------------------------------------------------- +// Original lines 175-194: `store_lesson` — the database write-through; +// the caller's plumbing, with one decision: the outcome rides along +// in its Display form. +// --------------------------------------------------------------------------- +pub fn store_lesson() -> bool { + return true; +} + +// --------------------------------------------------------------------------- +// Original lines 197-203: `get_top_lessons` — the (lesson, type, +// pattern count) rows under the limit. +// --------------------------------------------------------------------------- +pub fn get_top_lessons() -> usize { + return 3; +} + +// --------------------------------------------------------------------------- +// Tests: every decision above asserted against values read from the +// original, including its own high-lr and small-model tests. +// --------------------------------------------------------------------------- + +test lesson_type_display_table { + assert(lesson_type_count() == 5); + assert(LessonType_fmt(0) == "AVOID"); + assert(LessonType_fmt(1) == "PATTERN"); + assert(LessonType_fmt(2) == "WINNER"); + assert(LessonType_fmt(3) == "WARN"); + assert(LessonType_fmt(4) == "INFO"); + assert(LessonType_fmt(5) == ""); +} + +test outcome_display_table { + assert(outcome_count() == 5); + assert(Outcome_fmt(0) == "pruned"); + assert(Outcome_fmt(1) == "failed"); + assert(Outcome_fmt(2) == "slow"); + assert(Outcome_fmt(3) == "unstable"); + assert(Outcome_fmt(4) == "timeout"); +} + +test severity_order { + assert(lesson_is_more_severe("avoid", "pattern")); + assert(lesson_is_more_severe("pattern", "warn")); + assert(lesson_is_more_severe("warn", "info")); + assert(lesson_is_more_severe("info", "winner")); + assert(!lesson_is_more_severe("winner", "avoid")); + assert(lesson_severity("unknown") == 5); +} + +test lr_gates_match_the_original { + assert(lr_lesson(0.07) == "avoid"); + assert(lr_lesson(0.001) == ""); + assert(lr_lesson(0.004) == ""); + assert(lr_lesson(0.0005) == "warn"); +} + +test remaining_gates { + assert(capacity_lesson(64, 2.9) == "pattern"); + assert(capacity_lesson(128, 2.9) == ""); + assert(capacity_lesson(64, 2.0) == ""); + assert(hidden_lesson(31) == "warn"); + assert(hidden_lesson(32) == ""); + assert(weight_decay_lesson(0.2, 3.1) == "avoid"); + assert(weight_decay_lesson(0.2, 2.0) == ""); + assert(weight_decay_lesson(0.05, 3.1) == ""); + assert(optimizer_lesson(true, 2.8) == "pattern"); + assert(optimizer_lesson(true, 2.5) == ""); + assert(optimizer_lesson(false, 3.5) == ""); + assert(early_death_lesson(1000, 3.4) == "avoid"); + assert(early_death_lesson(1001, 3.4) == ""); + assert(lesson_rule_count() == 6); +} + +test composition_answers_most_severe { + // The original's own high-lr case: AVOID beats everything else. + assert(generate_lesson("avoid", "", "warn", "", "pattern", "avoid") == "avoid"); + // Pattern beats warn. + assert(generate_lesson("", "pattern", "warn", "", "", "") == "pattern"); + // Warn beats info. + assert(generate_lesson("", "", "warn", "", "", "") == "warn"); + // Nothing triggered: the generic INFO. + assert(generate_lesson("", "", "", "", "", "") == "info"); + assert(generic_lesson_kind(false) == "info"); + assert(generic_lesson_kind(true) == ""); +} + +test small_model_case_matches_original { + // The original's small-model test: lr 0.004 (middle band), + // d_model 64 with BPB 2.9 - PATTERN answers. + assert(lr_lesson(0.004) == ""); + assert(capacity_lesson(64, 2.9) == "pattern"); + assert(generate_lesson("", "pattern", "", "", "", "") == "pattern"); +} + +test store_and_top_shapes { + assert(store_lesson()); + assert(get_top_lessons() == 3); +}