Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
229 changes: 229 additions & 0 deletions specs/port/trios-railway/crates/trios-railway-smoke/src/lib.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
// trios-railway-smoke — the IGLA RACE pipeline smoke test.
// Port of gHashTag/trios-railway:crates/trios-railway-smoke/src/lib.rs
// (at 8b229e9489ee, 200+ lines) — Roadmap Stage 6, goal #4548, target
// t27c gen-rust.
//
// The original validates the full pipeline in under sixty seconds with
// synthetic data: a deterministic trainer that emits one
// `step=N val_bpb=F` JSONL line per step (the BPB starts at 3.0 and
// decays by 0.99 per step; the loss is 80% of the BPB; every line is
// flushed; the done line reports the last step's BPB, zero with no
// steps), the seed-agent's line parser, and the local run that parses
// the outputs back with no database behind it.
//
// The stdout printing and flushing are plumbing; the decisions — the
// configuration defaults, the decay and loss formulas, the step range,
// the done-line best, the parser's four presence gates with their two
// markers, and the local run's shape (one line and one sample per step,
// no DB) — are ported here as pure functions and tested, including the
// original's own config and parser tests. The powi exponent has no
// builtin here: the decay is computed by repeated multiplication, one
// factor per step, exactly what the original's loop unfolds. No
// transport exists in this port (issue #9796, "Port the decision, not
// the plumbing").

module port::trios_railway_smoke_lib;

// ---------------------------------------------------------------------------
// Original lines 18-43: `pub struct SmokeConfig` and its Default.
// ---------------------------------------------------------------------------

pub struct SmokeConfig {
steps: u32,
batch: u32,
seed: u64,
synthetic: bool,
timeout_sec: u64,
}

pub fn SmokeConfig_default() -> SmokeConfig {
return SmokeConfig {
steps: 1,
batch: 2,
seed: 42,
synthetic: true,
timeout_sec: 30,
};
}

// ---------------------------------------------------------------------------
// Original lines 69-107: `pub fn run_synthetic_trainer(cfg) ->
// Vec<StepOutput>`. The deterministic trainer: one output per step for
// steps 1..=cfg.steps, the BPB starting at 3.0 and decaying by 0.99 per
// step (line 81), the loss at 80% of the BPB (line 82), and the done
// line reporting the last output's BPB, 0.0 with no outputs (line 102).
// The growable Vec and the stdout flush are plumbing; the plan this
// function decides is ported below with the per-step formulas pure.
// ---------------------------------------------------------------------------

pub struct TrainerPlan {
step_count: usize,
first_step: u32,
base_bpb: f64,
decay: f64,
loss_factor: f64,
done_best: f64,
}

// Original line 81: `let bpb = base_bpb * decay.powi(step as i32);` —
// computed by repeated multiplication, one decay factor per step.
pub fn smoke_bpb(step: u32) -> f64 {
var bpb: f64 = 3.0;
var i: u32 = 0;
while (i < step) {
bpb = bpb * 0.99;
i += 1;
}
return bpb;
}

// Original line 82: `let loss = bpb * 0.8;` — the loss is 80% of the BPB.
pub fn smoke_loss(bpb: f64) -> f64 {
return bpb * 0.8;
}

// Original lines 100-103: the done line reports the last output's BPB,
// 0.0 when there are no outputs.
pub fn trainer_done_best(steps: u32) -> f64 {
if (steps == 0) {
return 0.0;
}
return smoke_bpb(steps);
}

pub fn run_synthetic_trainer(cfg: SmokeConfig) -> TrainerPlan {
return TrainerPlan {
step_count: cfg.steps,
first_step: 1,
base_bpb: 3.0,
decay: 0.99,
loss_factor: 0.8,
done_best: trainer_done_best(cfg.steps),
};
}

// ---------------------------------------------------------------------------
// Original lines 110-126: `pub fn parse_step_line(line) ->
// Option<(u32, f64)>`. The seed-agent's parser: the line parses exactly
// when both markers are present AND a parseable token follows each.
// The string finding and number parsing are plumbing; the four presence
// gates and the two markers are the decisions.
// ---------------------------------------------------------------------------

pub fn step_marker() -> str {
return "step=";
}

pub fn bpb_marker() -> str {
return "val_bpb=";
}

pub fn parse_step_line(
step_marker_present: bool,
step_token_present: bool,
bpb_marker_present: bool,
bpb_token_present: bool,
) -> bool {
if (!step_marker_present) {
return false;
}
if (!step_token_present) {
return false;
}
if (!bpb_marker_present) {
return false;
}
if (!bpb_token_present) {
return false;
}
return true;
}

// ---------------------------------------------------------------------------
// Original lines 130-141: `pub fn run_local(cfg) -> SmokeResult`. The
// local pipeline: the trainer's outputs parsed back — one JSONL line and
// one sample per step — with no database behind the run, so the insert
// verdict is absent.
// ---------------------------------------------------------------------------

pub struct LocalSmokeView {
jsonl_lines: usize,
sample_count: usize,
db_insert_ok_present: bool,
}

pub fn run_local(cfg: SmokeConfig) -> LocalSmokeView {
return LocalSmokeView {
jsonl_lines: cfg.steps,
sample_count: cfg.steps,
db_insert_ok_present: false,
};
}

// ---------------------------------------------------------------------------
// Tests: every decision above asserted against values read from the
// original's own tests (lines 147-186).
// ---------------------------------------------------------------------------

test config_defaults {
const cfg = SmokeConfig_default();
assert(cfg.steps == 1);
assert(cfg.batch == 2);
assert(cfg.seed == 42);
assert(cfg.synthetic);
assert(cfg.timeout_sec == 30);
}

test bpb_decays_from_three {
assert(smoke_bpb(0) == 3.0);
const first = smoke_bpb(1);
assert(first > 2.96);
assert(first < 2.98);
assert(smoke_bpb(2) < smoke_bpb(1));
assert(smoke_bpb(3) < smoke_bpb(2));
}

test loss_is_eighty_percent_of_bpb {
const loss = smoke_loss(2.5);
assert(loss > 1.999);
assert(loss < 2.001);
const loss3 = smoke_loss(3.0);
assert(loss3 > 2.399);
assert(loss3 < 2.401);
}

test done_best_follows_the_last_step {
assert(trainer_done_best(0) == 0.0);
assert(trainer_done_best(3) == smoke_bpb(3));
}

test trainer_plan_shape {
const plan = run_synthetic_trainer(SmokeConfig_default());
assert(plan.step_count == 1);
assert(plan.first_step == 1);
assert(plan.base_bpb == 3.0);
assert(plan.decay == 0.99);
assert(plan.loss_factor == 0.8);
const three = run_synthetic_trainer(SmokeConfig { steps: 3, batch: 2, seed: 42, synthetic: true, timeout_sec: 30 });
assert(three.step_count == 3);
}

test parse_gates_and_markers {
assert(step_marker() == "step=");
assert(bpb_marker() == "val_bpb=");
assert(parse_step_line(true, true, true, true));
assert(!parse_step_line(false, true, true, true));
assert(!parse_step_line(true, false, true, true));
assert(!parse_step_line(true, true, false, true));
assert(!parse_step_line(true, true, true, false));
}

test local_view_parses_every_line_without_db {
const one = run_local(SmokeConfig_default());
assert(one.jsonl_lines == 1);
assert(one.sample_count == 1);
assert(!one.db_insert_ok_present);
const three = run_local(SmokeConfig { steps: 3, batch: 2, seed: 42, synthetic: true, timeout_sec: 30 });
assert(three.jsonl_lines == 3);
assert(three.sample_count == 3);
}
Loading