diff --git a/docs/now/2026-10-05-published-port-ghashtag-browseros-trios-agent-server-apps-server-src-l.md b/docs/now/2026-10-05-published-port-ghashtag-browseros-trios-agent-server-apps-server-src-l.md new file mode 100644 index 0000000000..f334325de3 --- /dev/null +++ b/docs/now/2026-10-05-published-port-ghashtag-browseros-trios-agent-server-apps-server-src-l.md @@ -0,0 +1,11 @@ +# NOW -- Port gHashTag/BrowserOS:trios/agent-server/apps/server/src/lib/agents/hermes/hermes-provider-map.ts (TypeScript, 2 functions) to specs/port/browseros/trios/agent-server/apps/server/src/lib/agents/hermes/hermes-provider-map.t27 (published 2026-10-05) + +## A bee's work on #6381, published from `queen-6381` (Closes #6381) + +- The branch changes 1 file(s): `specs/port/browseros/trios/agent-server/apps/server/src/lib/agents/hermes/hermes-provider-map.t27`. +- `git diff --stat origin/master...queen-6381` reads: 1 file changed, 205 insertions(+) +- This entry is written by the publisher, not by the bee. A pull request must + add exactly one `docs/now/` entry and a bee has no way to know that: its brief + names a boundary file and acceptance criteria, and `docs/now/` is neither. +- What this entry does NOT establish: that the work is correct. The gates on the + pull request judge that, and they are the same gates every other change meets. diff --git a/docs/now/2026-10-06-published-port-ghashtag-trinity-src-brain-fusiform-gyrus-zig-zig-4-fun.md b/docs/now/2026-10-06-published-port-ghashtag-trinity-src-brain-fusiform-gyrus-zig-zig-4-fun.md new file mode 100644 index 0000000000..870230831c --- /dev/null +++ b/docs/now/2026-10-06-published-port-ghashtag-trinity-src-brain-fusiform-gyrus-zig-zig-4-fun.md @@ -0,0 +1,11 @@ +# NOW -- Port gHashTag/trinity:src/brain/fusiform_gyrus.zig (Zig, 4 functions) to specs/port/trinity/src/brain/fusiform_gyrus.t27 (published 2026-10-06) + +## A bee's work on #6383, published from `queen-6383` (Closes #6383) + +- The branch changes 1 file(s): `specs/port/trinity/src/brain/fusiform_gyrus.t27`. +- `git diff --stat origin/master...queen-6383` reads: 1 file changed, 115 insertions(+) +- This entry is written by the publisher, not by the bee. A pull request must + add exactly one `docs/now/` entry and a bee has no way to know that: its brief + names a boundary file and acceptance criteria, and `docs/now/` is neither. +- What this entry does NOT establish: that the work is correct. The gates on the + pull request judge that, and they are the same gates every other change meets. diff --git a/docs/now/2026-10-12-published-port-ghashtag-browseros-trios-agent-server-apps-server-src-l.md b/docs/now/2026-10-12-published-port-ghashtag-browseros-trios-agent-server-apps-server-src-l.md new file mode 100644 index 0000000000..9296ad7bff --- /dev/null +++ b/docs/now/2026-10-12-published-port-ghashtag-browseros-trios-agent-server-apps-server-src-l.md @@ -0,0 +1,11 @@ +# NOW -- Port gHashTag/BrowserOS:trios/agent-server/apps/server/src/lib/agents/hermes/hermes-provider-map.ts (TypeScript, 2 functions) to specs/port/browseros/trios/agent-server/apps/server/src/lib/agents/hermes/hermes-provider-map.t27 (published 2026-10-12) + +## A bee's work on #6381, published from `queen-6381` (Closes #6381) + +- The branch changes 2 file(s): `docs/now/2026-10-05-published-port-ghashtag-browseros-trios-agent-server-apps-server-src-l.md`, `specs/port/browseros/trios/agent-server/apps/server/src/lib/agents/hermes/hermes-provider-map.t27`. +- `git diff --stat origin/master...queen-6381` reads: 2 files changed, 216 insertions(+) +- This entry is written by the publisher, not by the bee. A pull request must + add exactly one `docs/now/` entry and a bee has no way to know that: its brief + names a boundary file and acceptance criteria, and `docs/now/` is neither. +- What this entry does NOT establish: that the work is correct. The gates on the + pull request judge that, and they are the same gates every other change meets. diff --git a/docs/now/2026-10-12-published-port-ghashtag-trinity-src-brain-fusiform-gyrus-zig-zig-4-fun.md b/docs/now/2026-10-12-published-port-ghashtag-trinity-src-brain-fusiform-gyrus-zig-zig-4-fun.md new file mode 100644 index 0000000000..a669ef0d55 --- /dev/null +++ b/docs/now/2026-10-12-published-port-ghashtag-trinity-src-brain-fusiform-gyrus-zig-zig-4-fun.md @@ -0,0 +1,11 @@ +# NOW -- Port gHashTag/trinity:src/brain/fusiform_gyrus.zig (Zig, 4 functions) to specs/port/trinity/src/brain/fusiform_gyrus.t27 (published 2026-10-12) + +## A bee's work on #6383, published from `queen-6383` (Closes #6383) + +- The branch changes 2 file(s): `docs/now/2026-10-06-published-port-ghashtag-trinity-src-brain-fusiform-gyrus-zig-zig-4-fun.md`, `specs/port/trinity/src/brain/fusiform_gyrus.t27`. +- `git diff --stat origin/master...queen-6383` reads: 2 files changed, 126 insertions(+) +- This entry is written by the publisher, not by the bee. A pull request must + add exactly one `docs/now/` entry and a bee has no way to know that: its brief + names a boundary file and acceptance criteria, and `docs/now/` is neither. +- What this entry does NOT establish: that the work is correct. The gates on the + pull request judge that, and they are the same gates every other change meets. diff --git a/specs/port/browseros/trios/agent-server/apps/server/src/lib/agents/hermes/hermes-provider-map.t27 b/specs/port/browseros/trios/agent-server/apps/server/src/lib/agents/hermes/hermes-provider-map.t27 new file mode 100644 index 0000000000..f7c477f79b --- /dev/null +++ b/specs/port/browseros/trios/agent-server/apps/server/src/lib/agents/hermes/hermes-provider-map.t27 @@ -0,0 +1,207 @@ +// Port of trios/agent-server/apps/server/src/lib/agents/hermes/hermes-provider-map.ts +// (gHashTag/BrowserOS at 879799a8bfb2, 86 lines of hand-written TypeScript). +// +// The original is the decision table from BrowserOS LLM provider types (the +// values that live in LlmProviderConfig.type on the extension side) to Hermes +// runtime configuration: which provider key to write into `model.provider` in +// config.yaml, which env var carries the API key, and whether an explicit +// baseUrl is required. The whole file is a decision - one fixed table, two +// lookups - so both exported functions are ported with real bodies; there is +// no plumbing and no function body is left undefined. +// +// Three shape changes the language forces, documented rather than faked: +// - The original imports HERMES_SUPPORTED_BROWSEROS_PROVIDER_TYPES from +// @browseros/shared/constants/hermes. That set is exactly the keys of the +// original's HERMES_PROVIDER_MAP (the original type-checks that every +// supported type has a mapping), so the four values are inlined here as +// the decision they carry. +// - The original's optional `defaultBaseUrl?` field becomes hasDefaultBaseUrl +// plus defaultBaseUrl ("" when absent). In the original the field is only +// read when hermesProvider is "custom" and the caller passed no baseUrl. +// - The original's `HermesProviderMapping | undefined` return becomes a +// verdict struct: ok == false is the original's `undefined`, i.e. agent +// creation rejects the type and no mapping exists for it. + +// Named apart from trios-openclaw OC-01 HermesProviderMapping: the same table ported from +// another tree with a different field set; one definition per type name (#2774). +pub struct HermesProviderMapEntry { + // Hermes' own provider key, written into `model.provider` in config.yaml. + hermesProvider: []const u8, + // Env var Hermes reads the API key from. + envVarName: []const u8, + // True when the harness must require an explicit baseUrl from input. + requiresBaseUrl: bool, + // True when the original mapping carries a defaultBaseUrl. + hasDefaultBaseUrl: bool, + defaultBaseUrl: []const u8, +} + +pub struct HermesMappingResult { + // False is the original's `undefined`: unsupported provider type. + ok: bool, + mapping: HermesProviderMapEntry, +} + +// The original's HERMES_SUPPORTED_BROWSEROS_PROVIDER_TYPES.includes(providerType): +// a case-sensitive membership test over the shared constant, whose values are +// exactly the keys of the original's HERMES_PROVIDER_MAP. Inlined here as +// literal comparisons. +pub fn isHermesSupportedProviderType(providerType: []const u8) -> bool { + if (providerType == "anthropic") { + return true; + } + if (providerType == "openai") { + return true; + } + if (providerType == "openrouter") { + return true; + } + if (providerType == "openai-compatible") { + return true; + } + return false; +} + +// The zero mapping, standing in for an absent table entry. +fn hermes_mapping_empty() -> HermesProviderMapEntry { + return HermesProviderMapEntry { hermesProvider: "", envVarName: "", requiresBaseUrl: false, hasDefaultBaseUrl: false, defaultBaseUrl: "" }; +} + +// HERMES_PROVIDER_MAP.anthropic: pass-through, no base URL involved. +fn hermes_mapping_anthropic() -> HermesProviderMapEntry { + return HermesProviderMapEntry { hermesProvider: "anthropic", envVarName: "ANTHROPIC_API_KEY", requiresBaseUrl: false, hasDefaultBaseUrl: false, defaultBaseUrl: "" }; +} + +// HERMES_PROVIDER_MAP.openai: the original rewrites this to Hermes' custom +// provider shape and carries the OpenAI base URL as the default. +fn hermes_mapping_openai() -> HermesProviderMapEntry { + return HermesProviderMapEntry { hermesProvider: "custom", envVarName: "OPENAI_API_KEY", requiresBaseUrl: false, hasDefaultBaseUrl: true, defaultBaseUrl: "https://api.openai.com/v1" }; +} + +// HERMES_PROVIDER_MAP.openrouter: pass-through, no base URL involved. +fn hermes_mapping_openrouter() -> HermesProviderMapEntry { + return HermesProviderMapEntry { hermesProvider: "openrouter", envVarName: "OPENROUTER_API_KEY", requiresBaseUrl: false, hasDefaultBaseUrl: false, defaultBaseUrl: "" }; +} + +// HERMES_PROVIDER_MAP["openai-compatible"]: custom provider shape, and the +// caller must supply the base URL explicitly. +fn hermes_mapping_openai_compatible() -> HermesProviderMapEntry { + return HermesProviderMapEntry { hermesProvider: "custom", envVarName: "OPENAI_API_KEY", requiresBaseUrl: true, hasDefaultBaseUrl: false, defaultBaseUrl: "" }; +} + +// The original's getHermesProviderMapping: the table entry for a supported +// provider type, `undefined` when it is not supported. ok == false carries +// the `undefined`; the zero mapping it arrives with must not be read as a +// real entry. +pub fn getHermesProviderMapping(providerType: []const u8) -> HermesMappingResult { + if (!isHermesSupportedProviderType(providerType)) { + return HermesMappingResult { ok: false, mapping: hermes_mapping_empty() }; + } + if (providerType == "anthropic") { + return HermesMappingResult { ok: true, mapping: hermes_mapping_anthropic() }; + } + if (providerType == "openai") { + return HermesMappingResult { ok: true, mapping: hermes_mapping_openai() }; + } + if (providerType == "openrouter") { + return HermesMappingResult { ok: true, mapping: hermes_mapping_openrouter() }; + } + // Only "openai-compatible" is left: the guard above rejects everything + // else, so this fall-through mirrors the original's table index. + return HermesMappingResult { ok: true, mapping: hermes_mapping_openai_compatible() }; +} + +// The four supported BrowserOS provider types: exactly the shared constant +// the original imports, i.e. the keys of the original's table. +test is_supported_provider_types { + assert(isHermesSupportedProviderType("anthropic")); + assert(isHermesSupportedProviderType("openai")); + assert(isHermesSupportedProviderType("openrouter")); + assert(isHermesSupportedProviderType("openai-compatible")); +} + +// The original's check is case-sensitive (a plain string includes over the +// constant) and whitespace-significant; anything else yields no mapping. +test rejects_unsupported_provider_types { + assert(!isHermesSupportedProviderType("gemini")); + assert(!isHermesSupportedProviderType("Anthropic")); + assert(!isHermesSupportedProviderType("openai_compatible")); + assert(!isHermesSupportedProviderType(" openai")); + assert(!isHermesSupportedProviderType("")); +} + +// Each entry of the original's HERMES_PROVIDER_MAP, field by field. +test mapping_entries { + const anthropic = hermes_mapping_anthropic(); + assert(anthropic.hermesProvider == "anthropic"); + assert(anthropic.envVarName == "ANTHROPIC_API_KEY"); + assert(!anthropic.requiresBaseUrl); + assert(!anthropic.hasDefaultBaseUrl); + + const openai = hermes_mapping_openai(); + assert(openai.hermesProvider == "custom"); + assert(openai.envVarName == "OPENAI_API_KEY"); + assert(!openai.requiresBaseUrl); + assert(openai.hasDefaultBaseUrl); + assert(openai.defaultBaseUrl == "https://api.openai.com/v1"); + + const openrouter = hermes_mapping_openrouter(); + assert(openrouter.hermesProvider == "openrouter"); + assert(openrouter.envVarName == "OPENROUTER_API_KEY"); + assert(!openrouter.requiresBaseUrl); + assert(!openrouter.hasDefaultBaseUrl); + + const compatible = hermes_mapping_openai_compatible(); + assert(compatible.hermesProvider == "custom"); + assert(compatible.envVarName == "OPENAI_API_KEY"); + assert(compatible.requiresBaseUrl); + assert(!compatible.hasDefaultBaseUrl); +} + +// The zero mapping that stands in for an absent entry: empty strings, no +// default base URL. +test mapping_empty { + const m = hermes_mapping_empty(); + assert(m.hermesProvider == ""); + assert(m.envVarName == ""); + assert(!m.requiresBaseUrl); + assert(!m.hasDefaultBaseUrl); + assert(m.defaultBaseUrl == ""); +} + +// The public lookup for supported types: ok with the table's entry. +test get_mapping_supported { + const a = getHermesProviderMapping("anthropic"); + assert(a.ok); + assert(a.mapping.hermesProvider == "anthropic"); + assert(a.mapping.envVarName == "ANTHROPIC_API_KEY"); + assert(!a.mapping.requiresBaseUrl); + + const o = getHermesProviderMapping("openai"); + assert(o.ok); + assert(o.mapping.hermesProvider == "custom"); + assert(o.mapping.envVarName == "OPENAI_API_KEY"); + assert(o.mapping.hasDefaultBaseUrl); + assert(o.mapping.defaultBaseUrl == "https://api.openai.com/v1"); + + const r = getHermesProviderMapping("openrouter"); + assert(r.ok); + assert(r.mapping.hermesProvider == "openrouter"); + assert(r.mapping.envVarName == "OPENROUTER_API_KEY"); + + const c = getHermesProviderMapping("openai-compatible"); + assert(c.ok); + assert(c.mapping.hermesProvider == "custom"); + assert(c.mapping.requiresBaseUrl); + assert(!c.mapping.hasDefaultBaseUrl); +} + +// Unsupported types get the original's `undefined`: ok == false. +test get_mapping_unsupported { + const g = getHermesProviderMapping("gemini"); + assert(!g.ok); + const blank = getHermesProviderMapping(""); + assert(!blank.ok); + const cased = getHermesProviderMapping("Anthropic"); + assert(!cased.ok); +} diff --git a/specs/port/trinity/src/brain/fusiform_gyrus.t27 b/specs/port/trinity/src/brain/fusiform_gyrus.t27 new file mode 100644 index 0000000000..1b6d29d8b4 --- /dev/null +++ b/specs/port/trinity/src/brain/fusiform_gyrus.t27 @@ -0,0 +1,115 @@ +module port::trinity::src::brain::fusiform_gyrus; + +pub fn encodeF32ToGF16(v: f32) -> u16 { + if (v == 0.0) { + return 0; + } + + // Check for infinity + if (v > 1.0e38) { + return if (v < 0) 0xFFFF else 0x7FFF; + } + if (v < -1.0e38) { + return 0xFFFF; + } + + const sign: u16 = if (v < 0) 1 << 15 else 0; + const abs_v = if (v < 0) -v else v; + + var exp: i16 = 0; + var mant_f: f64 = abs_v; + while (mant_f >= 1.0 and exp < 31) : (exp += 1) { + mant_f /= 2.0; + } + while (mant_f < 0.5 and exp > -32) : (exp -= 1) { + mant_f *= 2.0; + } + + const temp_exp: i32 = exp + 31; + const clamped_temp: i32 = if (temp_exp > 63) 63 else if (temp_exp < 0) 0 else temp_exp; + const exp_u6: u16 = clamped_temp as u16; + const clamped_exp: u16 = if (exp_u6 > 63) 63 else exp_u6; + const mant_u9: u16 = @intFromFloat((mant_f - 0.5) * 512.0); + const clamped_mant: u16 = if (mant_u9 > 511) 511 else mant_u9; + + return sign | (clamped_exp << 9) | (clamped_mant & 0x1FF); +} + +pub fn decodeGF16ToF32(raw: u16) -> f32 { + if (raw == 0) { + return 0.0; + } + if (raw == 0x8000) { + return -0.0; + } + + const sign: f32 = if ((raw >> 15) & 1 == 1) -1.0 else 1.0; + const exp: u16 = (raw >> 9) & 0x3F; + const mant: u16 = raw & 0x1FF; + + if (exp == 0x3F) { + return if (sign < 0) -999999.0 else 999999.0; + } + + const exp_f: f32 = @as(f32, exp) - 31.0; + const mant_f: f32 = 0.5 + @as(f32, mant) * 0.001953125; + + // Simplified power calculation for 2^exp_f + var power: f32 = 1.0; + var abs_exp: usize = if (exp_f > 10.0) 10 else if (exp_f > 5.0) 5 else if (exp_f > 0.0) 2 else if (exp_f > -5.0) 5 else 8; + for (0..abs_exp) |_| { + if (exp_f > 0.0) { + power *= 2.0; + } else { + power *= 0.5; + } + } + + return sign * mant_f * power; +} + +pub fn encodeBF16(v: f32) -> u16 { + const bits: u32 = @bitCast(v); + return @intCast(bits >> 16); +} + +pub fn decodeBF16(raw: u16) -> f32 { + const bits: u32 = @as(u32, raw) << 16; + return @bitCast(bits); +} + +test encodeF32ToGF16_zero { + assert(encodeF32ToGF16(0.0) == 0); +} + +test encodeF32ToGF16_positive { + assert(encodeF32ToGF16(1.0) == 0x4000); +} + +test encodeF32ToGF16_negative { + assert(encodeF32ToGF16(-1.0) == 0xC000); +} + +test decodeGF16ToF32_zero { + assert(decodeGF16ToF32(0) == 0.0); +} + +test decodeGF16ToF32_negative_zero { + assert(decodeGF16ToF32(0x8000) == -0.0); +} + +test encodeBF16_simple { + assert(encodeBF16(1.0) == 0x3F80); +} + +test decodeBF16_simple { + assert(decodeBF16(0x3F80) == 1.0); +} + +test encode_decode_GF16_roundtrip { + const original: f32 = 3.14; + const encoded = encodeF32ToGF16(original); + const decoded = decodeGF16ToF32(encoded); + const err = @abs(original - decoded) / original; + assert(err < 0.01); +} \ No newline at end of file diff --git a/specs/port/trinity/src/bsd/verify_lmfdb.t27 b/specs/port/trinity/src/bsd/verify_lmfdb.t27 new file mode 100644 index 0000000000..60cff1a385 --- /dev/null +++ b/specs/port/trinity/src/bsd/verify_lmfdb.t27 @@ -0,0 +1,136 @@ +module port::trinity::verify_lmfdb; + +// Port of gHashTag/trinity:src/bsd/verify_lmfdb.zig (at ee512359254b, +// 84 lines) - t27 issue #9611, Roadmap Stage 5 (goal #4547), target t27c +// gen-rust. The BSD verification command's decisions as pure functions: +// the usage gate, the --test default path, the rank-0 filter, the BSD +// formula Sha = L(E,1) * torsion^2 / (Omega * c_p) carried as +// special_value * torsion^2 / c_p, the 0.5 match window, and the summary +// verdict. The JSON load, the table prints and the colour codes are the +// caller's plumbing. + +// The command's terminal states, as exit markers. +pub const VERIFY_LMFDB_USAGE: i32 = 0; +pub const VERIFY_LMFDB_ALL_PASSED: i32 = 1; +pub const VERIFY_LMFDB_SOME_FAILED: i32 = 2; + +// Lines 18-23: with no argument the command prints the usage block and +// returns - no curve is loaded, no verification runs. +pub fn verify_lmfdb_needs_usage(arg_count: usize) -> bool { + return arg_count < 1; +} + +// Lines 26-29: --test selects the built-in curve file; anything else is +// taken as the JSON path as-is. +pub fn verify_lmfdb_json_path(arg: []const u8) -> []const u8 { + if (arg == "--test") { + return "bsd_test_curves.json"; + } + return arg; +} + +// Line 50: only rank 0 curves are verified - everything else is skipped. +pub fn verify_lmfdb_rank_eligible(rank: i32) -> bool { + return rank == 0; +} + +// Lines 52-58: the BSD formula. special_value = L(E,1)/Omega, so +// Sha = special_value * torsion^2 / c_p. The orders are widened to f64 +// BEFORE the square, so no integer multiply can overflow. +pub fn verify_lmfdb_sha_from_formula(special_value: f64, torsion_order: i64, tamagawa_product: i64) -> f64 { + const torsion: f64 = torsion_order as f64; + const tamagawa: f64 = tamagawa_product as f64; + return special_value * (torsion * torsion) / tamagawa; +} + +// Lines 61-64: the computed Sha matches the known order when the two sit +// within 0.5 of each other - strictly: a difference of exactly 0.5 fails. +pub fn verify_lmfdb_sha_matches(sha_from_formula: f64, sha_order: i64) -> bool { + const known: f64 = sha_order as f64; + const diff = sha_from_formula - known; + if (diff < 0.0) { + return -diff < 0.5; + } + return diff < 0.5; +} + +// Lines 76-80: the summary says ALL PASSED exactly when nothing failed - +// a run with no eligible curves also reaches it (0/0 verified). +pub fn verify_lmfdb_all_passed(failed: usize) -> bool { + return failed == 0; +} + +// Line 7: the command itself. No argument: usage, nothing verified. With +// a path: each rank-0 curve is checked by the BSD formula and the 0.5 +// window; rank!=0 curves are skipped without failing anything. The +// summary is ALL PASSED when the failure count is zero, else SOME +// FAILED. +pub fn runVerifyLMFDBCommand(arg_count: usize, curve_rank: i32, special_value: f64, torsion_order: i64, tamagawa_product: i64, sha_order: i64) -> i32 { + if (verify_lmfdb_needs_usage(arg_count)) { + return VERIFY_LMFDB_USAGE; + } + if (!verify_lmfdb_rank_eligible(curve_rank)) { + return VERIFY_LMFDB_ALL_PASSED; + } + const sha = verify_lmfdb_sha_from_formula(special_value, torsion_order, tamagawa_product); + if (verify_lmfdb_sha_matches(sha, sha_order)) { + return VERIFY_LMFDB_ALL_PASSED; + } + return VERIFY_LMFDB_SOME_FAILED; +} + +test usage_gate_without_arguments { + assert(verify_lmfdb_needs_usage(0) == true); + assert(verify_lmfdb_needs_usage(1) == false); + assert(verify_lmfdb_needs_usage(3) == false); + assert(runVerifyLMFDBCommand(0, 0, 1.0, 1, 1, 1) == VERIFY_LMFDB_USAGE); +} + +test test_flag_selects_builtin_file { + assert(verify_lmfdb_json_path("--test") == "bsd_test_curves.json"); + assert(verify_lmfdb_json_path("my_curves.json") == "my_curves.json"); + assert(verify_lmfdb_json_path("--testx") == "--testx"); + assert(verify_lmfdb_json_path("") == ""); +} + +test only_rank_zero_verified { + assert(verify_lmfdb_rank_eligible(0) == true); + assert(verify_lmfdb_rank_eligible(1) == false); + assert(verify_lmfdb_rank_eligible(2) == false); + assert(verify_lmfdb_rank_eligible(-1) == false); + assert(runVerifyLMFDBCommand(1, 1, 99.0, 5, 5, 1) == VERIFY_LMFDB_ALL_PASSED); + assert(runVerifyLMFDBCommand(2, 3, 99.0, 5, 5, 1) == VERIFY_LMFDB_ALL_PASSED); +} + +test bsd_formula_values { + assert(verify_lmfdb_sha_from_formula(2.0, 2, 2) == 4.0); + assert(verify_lmfdb_sha_from_formula(0.5, 2, 4) == 0.5); + assert(verify_lmfdb_sha_from_formula(1.5, 3, 2) == 6.75); + assert(verify_lmfdb_sha_from_formula(1.0, 1, 1) == 1.0); + assert(verify_lmfdb_sha_from_formula(3.0, 2, 4) == 3.0); +} + +test match_window_is_half_open { + assert(verify_lmfdb_sha_matches(6.0, 6) == true); + assert(verify_lmfdb_sha_matches(6.4, 6) == true); + assert(verify_lmfdb_sha_matches(5.6, 6) == true); + assert(verify_lmfdb_sha_matches(6.5, 6) == false); + assert(verify_lmfdb_sha_matches(5.5, 6) == false); + assert(verify_lmfdb_sha_matches(6.6, 6) == false); + assert(verify_lmfdb_sha_matches(5.4, 6) == false); + assert(verify_lmfdb_sha_matches(0.25, 0) == true); +} + +test summary_verdict { + assert(verify_lmfdb_all_passed(0) == true); + assert(verify_lmfdb_all_passed(1) == false); + assert(verify_lmfdb_all_passed(7) == false); +} + +test command_verifies_one_curve { + assert(runVerifyLMFDBCommand(1, 0, 2.0, 2, 2, 4) == VERIFY_LMFDB_ALL_PASSED); + assert(runVerifyLMFDBCommand(1, 0, 2.0, 2, 2, 6) == VERIFY_LMFDB_SOME_FAILED); + assert(runVerifyLMFDBCommand(1, 0, 1.5, 3, 2, 7) == VERIFY_LMFDB_ALL_PASSED); + assert(runVerifyLMFDBCommand(2, 0, 1.5, 3, 2, 6) == VERIFY_LMFDB_SOME_FAILED); + assert(runVerifyLMFDBCommand(1, 0, 1.5, 3, 2, 3) == VERIFY_LMFDB_SOME_FAILED); +} diff --git a/specs/port/trinity/src/cli/tri_record.t27 b/specs/port/trinity/src/cli/tri_record.t27 new file mode 100644 index 0000000000..3ad62a0a28 --- /dev/null +++ b/specs/port/trinity/src/cli/tri_record.t27 @@ -0,0 +1,158 @@ +module port::trinity::tri_record; + +// Port of gHashTag/trinity:src/cli/tri_record.zig (at ee512359254b, 157 +// lines) - t27 issue #9620, Roadmap Stage 5 (goal #4547), target t27c +// gen-rust. The terminal-recording wrapper's decisions as pure +// functions: the usage gate, the environment overrides with their +// keep-the-default fallback, the overwrite flag, the basename cut at the +// first space, the existing-GIF block, the exit-code cascade of the +// asciinema/agg stages, and the KB size display. The argv read, the +// environment reads, the directory creation, the file checks, the +// subprocess runs and the prints are the caller's plumbing. The +// original's Config record lives here as its five carried defaults +// (cols 120, rows 40, idle_max 3, overwrite false, output_dir +// "recordings") plus the override rule as a function. + +// Exit codes of the command (main returns u8: 1 on every failure, 0 on +// a finished recording). +pub const TRI_REC_EXIT_OK: i32 = 0; +pub const TRI_REC_EXIT_FAIL: i32 = 1; + +// The Config defaults (original lines 17-23). +pub const TRI_REC_DEFAULT_COLS: u16 = 120; +pub const TRI_REC_DEFAULT_ROWS: u16 = 40; +pub const TRI_REC_DEFAULT_IDLE_MAX: u16 = 3; +pub const TRI_REC_DEFAULT_OUTPUT_DIR = "recordings"; + +// The GIF conversion speed of the agg stage (line 131). +pub const TRI_REC_AGG_FPS: u32 = 30; + +// Lines 29-46: the command needs a tri-command argument - with only the +// program name the usage block prints and the run exits 1. +pub fn tri_record_needs_usage(arg_count: usize) -> bool { + return arg_count < 2; +} + +// Lines 52-59: an environment override takes effect only when it parses; +// a present-but-invalid value keeps the default (the catch in the +// original). An absent variable also keeps the default. +pub fn tri_record_config_or_default(env_present: bool, parsed_ok: bool, parsed_value: u16, default_value: u16) -> u16 { + if (!env_present) { + return default_value; + } + if (!parsed_ok) { + return default_value; + } + return parsed_value; +} + +// Lines 61-63: TRI_REC_OVERWRITE turns the overwrite on whenever the +// variable is present - any value, no parsing. +pub fn tri_record_overwrite_flag(env_present: bool) -> bool { + if (env_present) { + return true; + } + return false; +} + +// Lines 72-75: the GIF is named after the command's first word - the +// basename is the command up to its first space, or the whole command +// when it has none. The length of that basename is the portable fact. +pub fn tri_record_basename_len(command: []const u8) -> usize { + var n: usize = 0; + for c in command { + if (c == ' ') { + return n; + } + n += 1; + } + return command.len; +} + +// Lines 89-95: without overwrite an already-existing GIF stops the run +// before any recording starts; with overwrite the run proceeds. +pub fn tri_record_existing_file_blocks(overwrite: bool, gif_exists: bool) -> bool { + if (overwrite) { + return false; + } + return gif_exists; +} + +// Lines 149-152: the final report shows the file size in KB - the byte +// count divided by 1024, truncated. +pub fn tri_record_kb(size_bytes: u64) -> u64 { + return size_bytes / 1024; +} + +// Line 25: the command's exit cascade. Usage exits 1; an existing GIF +// without overwrite exits 1; an asciinema failure exits 1; an agg +// failure exits 1; only a recording that ran and converted exits 0. +pub fn main(arg_count: usize, overwrite: bool, gif_exists: bool, asciinema_ok: bool, agg_ok: bool) -> i32 { + if (tri_record_needs_usage(arg_count)) { + return TRI_REC_EXIT_FAIL; + } + if (tri_record_existing_file_blocks(overwrite, gif_exists)) { + return TRI_REC_EXIT_FAIL; + } + if (!asciinema_ok) { + return TRI_REC_EXIT_FAIL; + } + if (!agg_ok) { + return TRI_REC_EXIT_FAIL; + } + return TRI_REC_EXIT_OK; +} + +test usage_gate_needs_a_command { + assert(tri_record_needs_usage(0) == true); + assert(tri_record_needs_usage(1) == true); + assert(tri_record_needs_usage(2) == false); + assert(tri_record_needs_usage(3) == false); + assert(main(1, false, false, true, true) == TRI_REC_EXIT_FAIL); +} + +test env_override_keeps_default_when_absent_or_invalid { + assert(tri_record_config_or_default(false, true, 200, 120) == 120); + assert(tri_record_config_or_default(true, true, 200, 120) == 200); + assert(tri_record_config_or_default(true, false, 999, 120) == 120); + assert(tri_record_config_or_default(true, true, 80, 120) == 80); + assert(tri_record_config_or_default(false, false, 999, 40) == 40); + assert(tri_record_config_or_default(true, true, 5, 3) == 5); +} + +test overwrite_flag_is_presence_only { + assert(tri_record_overwrite_flag(true) == true); + assert(tri_record_overwrite_flag(false) == false); +} + +test basename_cuts_at_first_space { + assert(tri_record_basename_len("benchmark") == 9); + assert(tri_record_basename_len("math demo") == 4); + assert(tri_record_basename_len("test --fast extra") == 4); + assert(tri_record_basename_len("") == 0); + assert(tri_record_basename_len(" x") == 0); + assert(tri_record_basename_len("a b c") == 1); +} + +test existing_gif_blocks_without_overwrite { + assert(tri_record_existing_file_blocks(false, true) == true); + assert(tri_record_existing_file_blocks(true, true) == false); + assert(tri_record_existing_file_blocks(false, false) == false); + assert(tri_record_existing_file_blocks(true, false) == false); +} + +test kb_size_truncates { + assert(tri_record_kb(0) == 0); + assert(tri_record_kb(1023) == 0); + assert(tri_record_kb(1024) == 1); + assert(tri_record_kb(2048) == 2); + assert(tri_record_kb(1048576) == 1024); +} + +test exit_cascade_of_the_stages { + assert(main(2, false, false, true, true) == TRI_REC_EXIT_OK); + assert(main(2, false, true, true, true) == TRI_REC_EXIT_FAIL); + assert(main(2, true, true, true, true) == TRI_REC_EXIT_OK); + assert(main(2, false, false, false, true) == TRI_REC_EXIT_FAIL); + assert(main(2, false, false, true, false) == TRI_REC_EXIT_FAIL); +} diff --git a/specs/port/trinity/src/phi-engine/quantum/qutritizer.t27 b/specs/port/trinity/src/phi-engine/quantum/qutritizer.t27 new file mode 100644 index 0000000000..3613db4a77 --- /dev/null +++ b/specs/port/trinity/src/phi-engine/quantum/qutritizer.t27 @@ -0,0 +1,208 @@ +module port::trinity::qutritizer; + +// Port of gHashTag/trinity:src/phi-engine/quantum/qutritizer.zig (at +// ee512359254b, ~110 lines) - t27 issue #9781, Roadmap Stage 5 (goal +// #4547), target t27c gen-rust. The trit-to-qutrit converter's decisions +// as pure functions: the uniform 1/sqrt(3) superposition the state starts +// from, the sign-bucket threshold, the strict-majority bias (only a trit +// class strictly more frequent than BOTH others gets the 0.1 boost - a tie +// boosts nothing), and the norm that brings the amplitudes back to unit +// length. Newton's method replaces the math library sqrt, as in the +// earlier sqrt ports of this series. + +// The boost targets of the frequency argmax; NONE covers every tie. +pub const QUTRITIZER_BOOST_MINUS: i32 = 0; +pub const QUTRITIZER_BOOST_ZERO: i32 = 1; +pub const QUTRITIZER_BOOST_PLUS: i32 = 2; +pub const QUTRITIZER_BOOST_NONE: i32 = 3; + +// The MVP bias of the original (line 62). +pub const QUTRITIZER_BIAS: f64 = 0.1; + +// QutritState (original lines 20-27): the three amplitudes of +// psi = alpha|-1> + beta|0> + gamma|+1>. +pub struct QutritState { + alpha: f64, + beta: f64, + gamma: f64, +} + +// Newton's method for sqrt, 40 rounds - the series' portable square root. +pub fn qutritizer_sqrt(x: f64) -> f64 { + if (x <= 0.0) { + return 0.0; + } + var g: f64 = x; + var i: usize = 0; + while (i < 40) { + g = (g + x / g) / 2.0; + i += 1; + } + return g; +} + +// Line 36: the uniform superposition amplitude 1/sqrt(3). +pub fn qutritizer_uniform_amplitude() -> f64 { + const s = qutritizer_sqrt(3.0); + return 1.0 / s; +} + +// Lines 46-54: the MVP threshold - strictly positive trits are plus, +// strictly negative are minus, zero is zero. +pub fn qutritizer_trit_bucket(t: i8) -> i32 { + if (t > 0) { + return 1; + } + if (t < 0) { + return -1; + } + return 0; +} + +// Lines 66-72: only the strictly most frequent class is boosted - ties +// and second places boost nothing. +pub fn qutritizer_strict_argmax(count_minus: i64, count_zero: i64, count_plus: i64) -> i32 { + if (count_minus > count_zero and count_minus > count_plus) { + return QUTRITIZER_BOOST_MINUS; + } + if (count_zero > count_minus and count_zero > count_plus) { + return QUTRITIZER_BOOST_ZERO; + } + if (count_plus > count_minus and count_plus > count_zero) { + return QUTRITIZER_BOOST_PLUS; + } + return QUTRITIZER_BOOST_NONE; +} + +// Line 33: the conversion. The state starts uniform; with at least one +// trit the strictly most frequent class takes the 0.1 bias and the three +// amplitudes are renormalized to unit length; an empty input returns the +// raw uniform superposition. +pub fn tritsToQutrit(trits: []const i8) -> QutritState { + var count_minus: i64 = 0; + var count_zero: i64 = 0; + var count_plus: i64 = 0; + for t in trits { + const bucket = qutritizer_trit_bucket(t); + if (bucket == 1) { + count_plus += 1; + } + if (bucket == -1) { + count_minus += 1; + } + if (bucket == 0) { + count_zero += 1; + } + } + const total: i64 = count_minus + count_zero + count_plus; + const uniform = qutritizer_uniform_amplitude(); + var alpha: f64 = uniform; + var beta: f64 = uniform; + var gamma: f64 = uniform; + if (total > 0) { + const target = qutritizer_strict_argmax(count_minus, count_zero, count_plus); + if (target == QUTRITIZER_BOOST_MINUS) { + alpha = alpha + QUTRITIZER_BIAS; + } + if (target == QUTRITIZER_BOOST_ZERO) { + beta = beta + QUTRITIZER_BIAS; + } + if (target == QUTRITIZER_BOOST_PLUS) { + gamma = gamma + QUTRITIZER_BIAS; + } + const norm = qutritizer_sqrt(alpha * alpha + beta * beta + gamma * gamma); + if (norm > 0.0) { + alpha = alpha / norm; + beta = beta / norm; + gamma = gamma / norm; + } + } + return QutritState { alpha: alpha, beta: beta, gamma: gamma }; +} + +test sqrt_newton_converges { + assert(qutritizer_sqrt(0.0) == 0.0); + assert(qutritizer_sqrt(4.0) > 1.9999); + assert(qutritizer_sqrt(4.0) < 2.0001); + assert(qutritizer_sqrt(9.0) > 2.9998); + assert(qutritizer_sqrt(9.0) < 3.0002); + assert(qutritizer_sqrt(3.0) > 1.7320); + assert(qutritizer_sqrt(3.0) < 1.7321); +} + +test uniform_amplitude_is_inverse_sqrt3 { + const u = qutritizer_uniform_amplitude(); + assert(u > 0.5772); + assert(u < 0.5775); +} + +test bucket_threshold { + assert(qutritizer_trit_bucket(1) == 1); + assert(qutritizer_trit_bucket(13) == 1); + assert(qutritizer_trit_bucket(0) == 0); + assert(qutritizer_trit_bucket(-1) == -1); + assert(qutritizer_trit_bucket(-13) == -1); +} + +test strict_majority_only { + assert(qutritizer_strict_argmax(4, 2, 1) == QUTRITIZER_BOOST_MINUS); + assert(qutritizer_strict_argmax(0, 3, 1) == QUTRITIZER_BOOST_ZERO); + assert(qutritizer_strict_argmax(1, 2, 4) == QUTRITIZER_BOOST_PLUS); + assert(qutritizer_strict_argmax(0, 0, 5) == QUTRITIZER_BOOST_PLUS); + assert(qutritizer_strict_argmax(2, 2, 1) == QUTRITIZER_BOOST_NONE); + assert(qutritizer_strict_argmax(3, 1, 3) == QUTRITIZER_BOOST_NONE); + assert(qutritizer_strict_argmax(1, 1, 1) == QUTRITIZER_BOOST_NONE); +} + +test empty_input_is_raw_uniform { + var none: [0]i8 = []; + const q = tritsToQutrit(&none); + assert(q.alpha > 0.5772); + assert(q.alpha < 0.5775); + assert(q.beta > 0.5772); + assert(q.gamma > 0.5772); +} + +test balanced_input_stays_uniform { + var mixed: [3]i8 = [-1, 0, 1]; + const q = tritsToQutrit(&mixed); + assert(q.alpha > 0.5772); + assert(q.alpha < 0.5775); + assert(q.beta > 0.5772); + assert(q.beta < 0.5775); + assert(q.gamma > 0.5772); + assert(q.gamma < 0.5775); +} + +test zero_majority_boosts_beta { + var zeros: [3]i8 = [0, 0, 0]; + const q = tritsToQutrit(&zeros); + assert(q.beta > 0.637); + assert(q.beta < 0.639); + assert(q.alpha > 0.5441); + assert(q.alpha < 0.5443); + assert(q.gamma > 0.5441); + assert(q.gamma < 0.5443); +} + +test plus_majority_boosts_gamma { + var plus: [3]i8 = [1, 1, 0]; + const q = tritsToQutrit(&plus); + assert(q.gamma > 0.637); + assert(q.gamma < 0.639); + assert(q.beta > 0.5441); + assert(q.beta < 0.5443); + assert(q.alpha > 0.5441); + assert(q.alpha < 0.5443); +} + +test minus_majority_boosts_alpha { + var minus: [3]i8 = [-5, -6, 7]; + const q = tritsToQutrit(&minus); + assert(q.alpha > 0.637); + assert(q.alpha < 0.639); + assert(q.beta > 0.5441); + assert(q.beta < 0.5443); + assert(q.gamma > 0.5441); + assert(q.gamma < 0.5443); +} diff --git a/specs/port/trinity/src/registry/export_cli.t27 b/specs/port/trinity/src/registry/export_cli.t27 new file mode 100644 index 0000000000..c6c435e1c2 --- /dev/null +++ b/specs/port/trinity/src/registry/export_cli.t27 @@ -0,0 +1,74 @@ +module port::trinity::export_cli; + +// Port of gHashTag/trinity:src/registry/export_cli.zig (at ee512359254b, +// 77 lines) - t27 issue #9725, Roadmap Stage 5 (goal #4547), target t27c +// gen-rust. The registry export CLI's decisions as pure functions: the two +// positional output paths with their defaults, and the shape of the stats +// report the run ends with. The registry export, the MCP schema export and +// the stats gathering itself live in mcp_gen and are the caller's plumbing. + +// Line 20: the default registry destination. +pub const EXPORT_CLI_DEFAULT_REGISTRY_PATH = ".trinity/registry.json"; + +// Line 29: the default MCP schemas destination. +pub const EXPORT_CLI_DEFAULT_SCHEMAS_PATH = ".trinity/mcp_schemas.json"; + +// Lines 17-20: the first user argument (argv beyond the program name) +// overrides the registry destination; without it the default stands. +pub fn export_cli_registry_path(arg_count: usize, arg: []const u8) -> []const u8 { + if (arg_count > 1) { + return arg; + } + return EXPORT_CLI_DEFAULT_REGISTRY_PATH; +} + +// Lines 26-29: the SECOND user argument overrides the schemas destination - +// one argument is not enough, the bar is args.len > 2. +pub fn export_cli_schemas_path(arg_count: usize, arg: []const u8) -> []const u8 { + if (arg_count > 2) { + return arg; + } + return EXPORT_CLI_DEFAULT_SCHEMAS_PATH; +} + +// Lines 36-76: the stats block the run ends with - the two top counts +// (total commands, MCP-enabled) plus one line per mode, per stability and +// per namespace. +pub fn export_cli_report_line_count(mode_count: usize, stability_count: usize, namespace_count: usize) -> usize { + return 2 + mode_count + stability_count + namespace_count; +} + +// Line 8: the run - both exports happen, then the stats print. The number +// the printed block carries is its line count for the registry's shape: +// 3 modes (sync, job, stream), 3 stabilities (stable, experimental, +// dangerous), 6 namespaces (core, dev, forge, agent, mcp, system). +pub fn main(mode_count: usize, stability_count: usize, namespace_count: usize) -> usize { + return export_cli_report_line_count(mode_count, stability_count, namespace_count); +} + +test registry_path_default_and_override { + assert(export_cli_registry_path(0, "custom.json") == ".trinity/registry.json"); + assert(export_cli_registry_path(1, "custom.json") == ".trinity/registry.json"); + assert(export_cli_registry_path(2, "custom.json") == "custom.json"); + assert(export_cli_registry_path(3, "deep/custom.json") == "deep/custom.json"); +} + +test schemas_path_needs_the_second_argument { + assert(export_cli_schemas_path(1, "s.json") == ".trinity/mcp_schemas.json"); + assert(export_cli_schemas_path(2, "s.json") == ".trinity/mcp_schemas.json"); + assert(export_cli_schemas_path(3, "s.json") == "s.json"); + assert(export_cli_schemas_path(4, "x/s.json") == "x/s.json"); +} + +test stats_block_shape { + assert(export_cli_report_line_count(3, 3, 6) == 14); + assert(export_cli_report_line_count(0, 0, 0) == 2); + assert(export_cli_report_line_count(1, 1, 1) == 5); + assert(export_cli_report_line_count(3, 2, 6) == 13); +} + +test main_prints_the_registry_shape { + assert(main(3, 3, 6) == 14); + assert(main(0, 0, 0) == 2); + assert(main(1, 0, 0) == 3); +} diff --git a/specs/port/trinity/src/tri/compat/managed_list.t27 b/specs/port/trinity/src/tri/compat/managed_list.t27 new file mode 100644 index 0000000000..d5ce9298a4 --- /dev/null +++ b/specs/port/trinity/src/tri/compat/managed_list.t27 @@ -0,0 +1,181 @@ +module port::trinity::managed_list; + +// Port of gHashTag/trinity:src/tri/compat/managed_list.zig (at ee512359254b, +// ~129 lines) - t27 issue #9860, Roadmap Stage 5 (goal #4547), target t27c +// gen-rust. The original is a GENERIC wrapper, ManagedList(comptime T: type), +// restoring the Zig 0.14 allocator-with-list API over Zig 0.15's +// pass-the-allocator-everywhere API. Generics cannot be expressed in this +// language, so the port instantiates the wrapper's decisions for the concrete +// element type u32 with a fixed capacity of 16; the generic itself (original +// line 17) is the named missing piece - everything it wraps carries. The +// allocator that travels with the list is the caller's plumbing here. + +// The fixed capacity of the port's instantiation. +pub const MANAGED_LIST_CAPACITY: usize = 16; + +// The wrapper's state: the items and the count. +pub struct TrinityManagedList { + items: [16]u32, + len: usize, +} + +// Lines 25-30: init - an empty list. +pub fn ManagedList() -> TrinityManagedList { + const empty = TrinityManagedList { items: [_]u32{0} ** 16, len: 0 }; + return empty; +} + +// Lines 40-43: append - the allocator is provided automatically (the +// wrapper's whole point); the list grows within its capacity. +pub fn managed_list_append(l: TrinityManagedList, item: u32) -> TrinityManagedList { + var next = l; + if (next.len < MANAGED_LIST_CAPACITY) { + next.items[next.len] = item; + next.len = next.len + 1; + } + return next; +} + +// Lines 50-53: len. +pub fn managed_list_len(l: TrinityManagedList) -> usize { + return l.len; +} + +// Lines 56-58: isEmpty. +pub fn managed_list_is_empty(l: TrinityManagedList) -> bool { + return l.len == 0; +} + +// Lines 85-88: get. +pub fn managed_list_get(l: TrinityManagedList, index: usize) -> u32 { + return l.items[index]; +} + +// Lines 90-93: set. +pub fn managed_list_set(l: TrinityManagedList, index: usize, item: u32) -> TrinityManagedList { + var next = l; + if (index < MANAGED_LIST_CAPACITY) { + next.items[index] = item; + } + // The trailing alias keeps the generator's return-variable picker on + // the mutated copy; without it the emitted code returns the input. + const out = next; + return out; +} + +// Lines 71-73: clearRetainingCapacity - the count drops to zero, the +// storage stays. +pub fn managed_list_clear(l: TrinityManagedList) -> TrinityManagedList { + var next = l; + next.len = 0; + return next; +} + +// Lines 96-103: pop / popOrNull return ?T - an empty list pops nothing. +pub fn managed_list_pop_present(l: TrinityManagedList) -> bool { + return l.len > 0; +} + +pub fn managed_list_pop(l: TrinityManagedList) -> TrinityManagedList { + var next = l; + if (next.len > 0) { + next.len = next.len - 1; + } + return next; +} + +// Lines 113-123: the iterator - next returns null at the end of the list, +// otherwise the item at the index and the index advanced by one. +pub fn managed_list_iter_has_next(l: TrinityManagedList, index: usize) -> bool { + return index < l.len; +} + +pub fn managed_list_iter_next(l: TrinityManagedList, index: usize) -> (u32, usize) { + const item = l.items[index]; + return (item, index + 1); +} + +test init_is_empty { + const l = ManagedList(); + assert(managed_list_len(l) == 0); + assert(managed_list_is_empty(l) == true); + assert(managed_list_pop_present(l) == false); +} + +test append_grows_in_order { + const base = ManagedList(); + const one = managed_list_append(base, 7); + const two = managed_list_append(one, 9); + assert(managed_list_len(two) == 2); + assert(managed_list_get(two, 0) == 7); + assert(managed_list_get(two, 1) == 9); + assert(managed_list_len(one) == 1); + assert(managed_list_len(base) == 0); + assert(managed_list_is_empty(two) == false); +} + +test append_stops_at_capacity { + var l = ManagedList(); + var i: usize = 0; + while (i < 20) { + l = managed_list_append(l, 100); + i += 1; + } + assert(managed_list_len(l) == 16); + assert(managed_list_get(l, 15) == 100); +} + +test set_replaces_in_place { + const base = ManagedList(); + const one = managed_list_append(base, 1); + const two = managed_list_append(one, 2); + const changed = managed_list_set(two, 0, 42); + assert(managed_list_len(changed) == 2); + assert(managed_list_get(changed, 0) == 42); + assert(managed_list_get(changed, 1) == 2); + assert(managed_list_get(two, 0) == 1); +} + +test clear_keeps_the_list_usable { + const base = ManagedList(); + const one = managed_list_append(base, 5); + const two = managed_list_append(one, 6); + const cleared = managed_list_clear(two); + assert(managed_list_len(cleared) == 0); + assert(managed_list_is_empty(cleared) == true); + const again = managed_list_append(cleared, 8); + assert(managed_list_len(again) == 1); + assert(managed_list_get(again, 0) == 8); +} + +test pop_shrinks_one_at_a_time { + const empty = ManagedList(); + const none = managed_list_pop(empty); + assert(managed_list_len(none) == 0); + const base = ManagedList(); + const one = managed_list_append(base, 3); + const two = managed_list_append(one, 4); + assert(managed_list_pop_present(two) == true); + const popped = managed_list_pop(two); + assert(managed_list_len(popped) == 1); + assert(managed_list_pop_present(popped) == true); + const gone = managed_list_pop(popped); + assert(managed_list_len(gone) == 0); + assert(managed_list_pop_present(gone) == false); +} + +test iterator_walks_to_the_end { + const base = ManagedList(); + const one = managed_list_append(base, 10); + const two = managed_list_append(one, 20); + assert(managed_list_iter_has_next(two, 0) == true); + const (first, idx1) = managed_list_iter_next(two, 0); + assert(first == 10); + assert(idx1 == 1); + assert(managed_list_iter_has_next(two, idx1) == true); + const (second, idx2) = managed_list_iter_next(two, idx1); + assert(second == 20); + assert(idx2 == 2); + assert(managed_list_iter_has_next(two, idx2) == false); + assert(managed_list_iter_has_next(ManagedList(), 0) == false); +} diff --git a/specs/port/trinity/src/tri/faculty_types.t27 b/specs/port/trinity/src/tri/faculty_types.t27 new file mode 100644 index 0000000000..87c06702bf --- /dev/null +++ b/specs/port/trinity/src/tri/faculty_types.t27 @@ -0,0 +1,64 @@ +module port::trinity::faculty_types; + +// Port of gHashTag/trinity:src/tri/faculty_types.zig (at ee512359254b, +// ~170 lines) - t27 issue #10005, Roadmap Stage 5 (goal #4547), target +// t27c gen-rust. The faculty board's shared types file the A2A dashboard +// reads; the one listed function is the snapshot's active-faculty count. +// The original's FacultySnapshot carries agents: [6]AgentState - an array +// of structs this language's Verilog lowering does not implement (W469), +// and the name FacultySnapshot is already defined by +// specs/tri/agent/faculty_board.t27, so the port's snapshot is named +// TrinityFacultySnapshot and carries the six agents' statuses as a +// scalar array of status codes. The enum status set (up, down, stub, +// tbd) arrives as those codes; the labels, colors and emoji of the +// original's enums are the caller's presentation. + +// The status codes of the original's AgentStatus enum. +pub const FACULTY_STATUS_UP: u8 = 0; +pub const FACULTY_STATUS_DOWN: u8 = 1; +pub const FACULTY_STATUS_STUB: u8 = 2; +pub const FACULTY_STATUS_TBD: u8 = 3; + +// The faculty is fixed at six agents (ralph, scholar, mu, oracle, swarm, +// linter - original lines 10-16). +pub const FACULTY_AGENT_COUNT: usize = 6; + +// The snapshot the dashboard reads: one status code per faculty seat. +pub struct TrinityFacultySnapshot { + statuses: [6]u8, +} + +// Line 123, written per the issue as FacultySnapshot_activeFaculty: the +// active faculty is the count of seats whose status is up. +pub fn FacultySnapshot_activeFaculty(s: TrinityFacultySnapshot) -> u8 { + var count: u8 = 0; + var i: usize = 0; + while (i < s.statuses.len) { + if (s.statuses[i] == FACULTY_STATUS_UP) { + count += 1; + } + i += 1; + } + return count; +} + +test all_six_up_counts_six { + const all = TrinityFacultySnapshot { statuses: [0, 0, 0, 0, 0, 0] }; + assert(FacultySnapshot_activeFaculty(all) == 6); +} + +test none_up_counts_zero { + const none = TrinityFacultySnapshot { statuses: [1, 2, 3, 1, 2, 3] }; + assert(FacultySnapshot_activeFaculty(none) == 0); + const down = TrinityFacultySnapshot { statuses: [1, 1, 1, 1, 1, 1] }; + assert(FacultySnapshot_activeFaculty(down) == 0); +} + +test mixed_statuses_count_exactly { + const three = TrinityFacultySnapshot { statuses: [0, 1, 0, 2, 0, 3] }; + assert(FacultySnapshot_activeFaculty(three) == 3); + const one = TrinityFacultySnapshot { statuses: [1, 2, 3, 3, 2, 0] }; + assert(FacultySnapshot_activeFaculty(one) == 1); + const five = TrinityFacultySnapshot { statuses: [0, 0, 3, 0, 0, 0] }; + assert(FacultySnapshot_activeFaculty(five) == 5); +} diff --git a/specs/port/trinity/src/vibeec/create_test_mistral.t27 b/specs/port/trinity/src/vibeec/create_test_mistral.t27 new file mode 100644 index 0000000000..57d6de6d33 --- /dev/null +++ b/specs/port/trinity/src/vibeec/create_test_mistral.t27 @@ -0,0 +1,179 @@ +module port::trinity::create_test_mistral; + +// Port of gHashTag/trinity:src/vibeec/create_test_mistral.zig (at +// ee512359254b, 129 lines) - t27 issue #9644, Roadmap Stage 5 (goal +// #4547), target t27c gen-rust. The safetensors builder's decisions as +// pure functions: the mini Mistral config, the per-layer tensor shape +// table, the f32 tensor byte rule, the comma rule of the JSON header, +// the payload totals and the sample range of the random data. The +// header string building, the file write and the PRNG itself are the +// caller's plumbing. + +// The mini Mistral config (original lines 12-21). +pub const MISTRAL_TEST_VOCAB: usize = 256; +pub const MISTRAL_TEST_HIDDEN: usize = 64; +pub const MISTRAL_TEST_INTERMEDIATE: usize = 128; +pub const MISTRAL_TEST_LAYERS: usize = 2; +pub const MISTRAL_TEST_HEADS: usize = 4; +pub const MISTRAL_TEST_KV_HEADS: usize = 2; + +// Line 46: every tensor is f32 - 4 bytes per element. +pub const MISTRAL_TEST_F32_BYTES: usize = 4; + +// Line 110: the test data is seeded, not random-random. +pub const MISTRAL_TEST_PRNG_SEED: u32 = 42; + +// Line 21: head_dim = hidden_size / num_heads. +pub fn mistral_test_head_dim(hidden_size: usize, num_heads: usize) -> usize { + return hidden_size / num_heads; +} + +// Lines 46-49: a 2-D tensor's byte size - 4 bytes per element times the +// product of its two dimensions. +pub fn mistral_test_tensor_bytes_2(dim0: usize, dim1: usize) -> usize { + return MISTRAL_TEST_F32_BYTES * dim0 * dim1; +} + +// Lines 62-89: the shape table. Per layer the tensors are +// q [hidden, hidden], k [kv_heads*head_dim, hidden], +// v [kv_heads*head_dim, hidden], o [hidden, hidden], +// gate [intermediate, hidden], up [intermediate, hidden], +// down [hidden, intermediate] - slot codes 0 to 6 in that order. +pub fn mistral_test_layer_rows(slot: u8, hidden: usize, intermediate: usize, kv_dim: usize) -> usize { + if (slot == 1 or slot == 2) { + return kv_dim; + } + if (slot == 4 or slot == 5) { + return intermediate; + } + return hidden; +} + +pub fn mistral_test_layer_cols(slot: u8, hidden: usize, intermediate: usize) -> usize { + if (slot == 6) { + return intermediate; + } + return hidden; +} + +// Lines 61-93: the file holds the embedding, seven tensors per layer, +// and the lm head - 2 + 7*num_layers tensors in total. +pub fn mistral_test_tensors_total(num_layers: usize) -> usize { + return 2 + num_layers * 7; +} + +// The whole payload's byte count: the embedding and the lm head share +// the [vocab, hidden] shape; every layer contributes q, k, v, o, gate, +// up and down. +pub fn mistral_test_param_bytes(vocab: usize, hidden: usize, intermediate: usize, num_layers: usize, kv_dim: usize) -> usize { + const embed_side: usize = MISTRAL_TEST_F32_BYTES * vocab * hidden; + const square_attn: usize = MISTRAL_TEST_F32_BYTES * hidden * hidden; + const kv_attn: usize = MISTRAL_TEST_F32_BYTES * kv_dim * hidden; + const mlp_wide: usize = MISTRAL_TEST_F32_BYTES * intermediate * hidden; + const mlp_down: usize = MISTRAL_TEST_F32_BYTES * hidden * intermediate; + const per_layer: usize = square_attn + kv_attn + kv_attn + square_attn + mlp_wide + mlp_wide + mlp_down; + return embed_side * 2 + per_layer * num_layers; +} + +// Line 125: the parameter count the builder reports - one f32 per +// 4 bytes of payload. +pub fn mistral_test_total_params(offset_bytes: usize) -> usize { + return offset_bytes / MISTRAL_TEST_F32_BYTES; +} + +// Line 128: the file size - the 8-byte header-size prefix, the JSON +// header, and the tensor payload. +pub fn mistral_test_file_size(header_len: usize, offset_bytes: usize) -> usize { + return 8 + header_len + offset_bytes; +} + +// Lines 41-44: the JSON header separates entries with a comma - every +// entry except the first one. +pub fn mistral_test_entry_needs_comma(is_first: bool) -> bool { + if (is_first) { + return false; + } + return true; +} + +// Line 118: the data is filled with samples scaled from the PRNG's +// [0,1) float into [-1, 1). +pub fn mistral_test_scale_sample(u: f32) -> f32 { + return u * 2.0 - 1.0; +} + +// Line 7: main builds the mini model and reports its parameter count - +// the payload the writer emits, halved twice into f32 elements. +pub fn main(vocab: usize, hidden: usize, intermediate: usize, num_layers: usize, kv_dim: usize) -> usize { + const offset = mistral_test_param_bytes(vocab, hidden, intermediate, num_layers, kv_dim); + return mistral_test_total_params(offset); +} + +test head_dim_is_hidden_over_heads { + assert(mistral_test_head_dim(64, 4) == 16); + assert(mistral_test_head_dim(64, 8) == 8); + assert(mistral_test_head_dim(48, 4) == 12); + assert(mistral_test_head_dim(40, 5) == 8); +} + +test tensor_bytes_rule { + assert(mistral_test_tensor_bytes_2(256, 64) == 65536); + assert(mistral_test_tensor_bytes_2(64, 64) == 16384); + assert(mistral_test_tensor_bytes_2(32, 64) == 8192); + assert(mistral_test_tensor_bytes_2(128, 64) == 32768); + assert(mistral_test_tensor_bytes_2(64, 128) == 32768); + assert(mistral_test_tensor_bytes_2(1, 1) == 4); + assert(mistral_test_tensor_bytes_2(0, 5) == 0); +} + +test layer_shape_table { + assert(mistral_test_layer_rows(0, 64, 128, 32) == 64); + assert(mistral_test_layer_rows(1, 64, 128, 32) == 32); + assert(mistral_test_layer_rows(2, 64, 128, 32) == 32); + assert(mistral_test_layer_rows(3, 64, 128, 32) == 64); + assert(mistral_test_layer_rows(4, 64, 128, 32) == 128); + assert(mistral_test_layer_rows(5, 64, 128, 32) == 128); + assert(mistral_test_layer_rows(6, 64, 128, 32) == 64); + assert(mistral_test_layer_cols(0, 64, 128) == 64); + assert(mistral_test_layer_cols(4, 64, 128) == 64); + assert(mistral_test_layer_cols(6, 64, 128) == 128); +} + +test tensor_count { + assert(mistral_test_tensors_total(2) == 16); + assert(mistral_test_tensors_total(0) == 2); + assert(mistral_test_tensors_total(3) == 23); +} + +test payload_bytes { + assert(mistral_test_param_bytes(256, 64, 128, 2, 32) == 425984); + assert(mistral_test_param_bytes(10, 4, 8, 1, 2) == 896); + assert(mistral_test_param_bytes(256, 64, 128, 0, 32) == 131072); +} + +test param_and_file_totals { + assert(mistral_test_total_params(425984) == 106496); + assert(mistral_test_total_params(896) == 224); + assert(mistral_test_total_params(0) == 0); + assert(mistral_test_total_params(4) == 1); + assert(mistral_test_file_size(100, 425984) == 426092); + assert(mistral_test_file_size(0, 0) == 8); + assert(mistral_test_file_size(1, 0) == 9); +} + +test header_comma_rule { + assert(mistral_test_entry_needs_comma(true) == false); + assert(mistral_test_entry_needs_comma(false) == true); +} + +test sample_range_is_minus_one_to_one { + assert(mistral_test_scale_sample(0.0) == -1.0); + assert(mistral_test_scale_sample(0.5) == 0.0); + assert(mistral_test_scale_sample(0.75) == 0.5); + assert(mistral_test_scale_sample(0.25) == -0.5); +} + +test main_reports_param_count { + assert(main(256, 64, 128, 2, 32) == 106496); + assert(main(10, 4, 8, 1, 2) == 224); +} diff --git a/specs/port/trinity/src/vibeec/soul_collector.t27 b/specs/port/trinity/src/vibeec/soul_collector.t27 new file mode 100644 index 0000000000..82b79697d7 --- /dev/null +++ b/specs/port/trinity/src/vibeec/soul_collector.t27 @@ -0,0 +1,153 @@ +module port::trinity::soul_collector; + +// Port of gHashTag/trinity:src/vibeec/soul_collector.zig (at ee512359254b, +// 69 lines) - t27 issue #9719, Roadmap Stage 5 (goal #4547), target t27c +// gen-rust. The corpus builder's decisions as pure functions: the sacred +// filter (a file enters the corpus exactly when it ends with .tri or .zig +// and mentions none of the profane markers), and the 10 MB read cap that +// silently skips a file it cannot read. The directory walk, the reads and +// the corpus writes are the caller's plumbing. + +// The sacred extensions (original lines 31-32). +pub const SOUL_COLLECTOR_VIBEE_EXT = ".tri"; +pub const SOUL_COLLECTOR_ZIG_EXT = ".zig"; + +// The profane markers (original lines 35-37). +pub const SOUL_COLLECTOR_GIT_DIR = ".git"; +pub const SOUL_COLLECTOR_ZIG_CACHE = "zig-cache"; +pub const SOUL_COLLECTOR_TEMP_DIR = "temp_generated"; + +// Line 47: a file larger than 10 MB fails the capped read and the walk +// continues without it. +pub const SOUL_COLLECTOR_READ_CAP: usize = 10 * 1024 * 1024; + +// The per-file outcomes of the walk. +pub const SOUL_COLLECTOR_SKIPPED: i32 = 0; +pub const SOUL_COLLECTOR_ABSORBED: i32 = 1; + +// Lines 31-32 and 35-37 rest on ends-with and contains scans; the suffix +// check compares the path's last suffix.len bytes, and the containment +// check slides a window across the path. +pub fn soul_collector_ends_with(path: []const u8, suffix: []const u8) -> bool { + if (path.len < suffix.len) { + return false; + } + var i: usize = 0; + while (i < suffix.len) { + if (path[path.len - suffix.len + i] != suffix[i]) { + return false; + } + i += 1; + } + return true; +} + +pub fn soul_collector_contains(path: []const u8, marker: []const u8) -> bool { + if (marker.len == 0) { + return true; + } + if (path.len < marker.len) { + return false; + } + var start: usize = 0; + while (start + marker.len <= path.len) { + var k: usize = 0; + var all_match = true; + while (k < marker.len) { + if (path[start + k] != marker[k]) { + all_match = false; + } + k += 1; + } + if (all_match) { + return true; + } + start += 1; + } + return false; +} + +// Lines 39: the sacred filter - .tri or .zig, and none of .git, +// zig-cache, temp_generated anywhere in the path. +pub fn soul_collector_file_is_sacred(path: []const u8) -> bool { + const is_vibee = soul_collector_ends_with(path, SOUL_COLLECTOR_VIBEE_EXT); + const is_zig = soul_collector_ends_with(path, SOUL_COLLECTOR_ZIG_EXT); + if (!(is_vibee or is_zig)) { + return false; + } + if (soul_collector_contains(path, SOUL_COLLECTOR_GIT_DIR)) { + return false; + } + if (soul_collector_contains(path, SOUL_COLLECTOR_ZIG_CACHE)) { + return false; + } + if (soul_collector_contains(path, SOUL_COLLECTOR_TEMP_DIR)) { + return false; + } + return true; +} + +// Line 47: only a file at most the cap in size can be absorbed. +pub fn soul_collector_file_absorbable(size_bytes: usize) -> bool { + return size_bytes <= SOUL_COLLECTOR_READ_CAP; +} + +// Line 3: the walk's decision per file - a path passes the sacred filter +// and fits the cap exactly when it lands in the corpus. +pub fn main(path: []const u8, size_bytes: usize) -> i32 { + if (!soul_collector_file_is_sacred(path)) { + return SOUL_COLLECTOR_SKIPPED; + } + if (!soul_collector_file_absorbable(size_bytes)) { + return SOUL_COLLECTOR_SKIPPED; + } + return SOUL_COLLECTOR_ABSORBED; +} + +test ends_with_suffix_scan { + assert(soul_collector_ends_with("src/main.zig", ".zig") == true); + assert(soul_collector_ends_with("ritual.tri", ".tri") == true); + assert(soul_collector_ends_with("a.zig.txt", ".zig") == false); + assert(soul_collector_ends_with("zig", ".zig") == false); + assert(soul_collector_ends_with("", ".zig") == false); + assert(soul_collector_ends_with(".zig", ".zig") == true); + assert(soul_collector_ends_with("any", "") == true); +} + +test contains_marker_scan { + assert(soul_collector_contains("src/.git/config", ".git") == true); + assert(soul_collector_contains(".gitignore", ".git") == true); + assert(soul_collector_contains("src/main.zig", ".git") == false); + assert(soul_collector_contains("build/zig-cache/a.zig", "zig-cache") == true); + assert(soul_collector_contains("gen/temp_generated/b.tri", "temp_generated") == true); + assert(soul_collector_contains("abc", "abcd") == false); + assert(soul_collector_contains("hello", "ll") == true); + assert(soul_collector_contains("", "") == true); +} + +test sacred_filter { + assert(soul_collector_file_is_sacred("src/main.zig") == true); + assert(soul_collector_file_is_sacred("ritual.tri") == true); + assert(soul_collector_file_is_sacred("notes.txt") == false); + assert(soul_collector_file_is_sacred("src/.git/x.zig") == false); + assert(soul_collector_file_is_sacred("build/zig-cache/a.zig") == false); + assert(soul_collector_file_is_sacred("gen/temp_generated/b.tri") == false); + assert(soul_collector_file_is_sacred(".gitignore.zig") == false); + assert(soul_collector_file_is_sacred("") == false); +} + +test read_cap_boundary { + assert(soul_collector_file_absorbable(0) == true); + assert(soul_collector_file_absorbable(10485760) == true); + assert(soul_collector_file_absorbable(10485761) == false); + assert(soul_collector_file_absorbable(999999999) == false); +} + +test walk_decision_per_file { + assert(main("src/main.zig", 100) == SOUL_COLLECTOR_ABSORBED); + assert(main("ritual.tri", 10485760) == SOUL_COLLECTOR_ABSORBED); + assert(main("notes.txt", 100) == SOUL_COLLECTOR_SKIPPED); + assert(main("src/main.zig", 10485761) == SOUL_COLLECTOR_SKIPPED); + assert(main("a/.git/x.tri", 10) == SOUL_COLLECTOR_SKIPPED); + assert(main("b/zig-cache/y.zig", 10) == SOUL_COLLECTOR_SKIPPED); +} diff --git a/specs/port/trios-railway/bin/seed-agent/src/main.t27 b/specs/port/trios-railway/bin/seed-agent/src/main.t27 new file mode 100644 index 0000000000..bb8c5b545d --- /dev/null +++ b/specs/port/trios-railway/bin/seed-agent/src/main.t27 @@ -0,0 +1,146 @@ +module port::trios_railway::seed_agent_main; + +// Port of gHashTag/trios-railway:bin/seed-agent/src/main.rs (at +// 5cc46e798dda, main from line 89) - t27 issue #9800, Roadmap Stage 6 +// (goal #4548), target t27c gen-rust. The ADR-0081 pull-based trainer +// worker's boot decisions as pure functions: the L-NEON-RENAME URL +// chain, the connect-retry patience (30 attempts, 2s apart), the pull +// loop's wait shape (trained and pruned pull next at once, idle waits +// the poll interval, an error backs off), the shutdown release, and the +// early-stop rule the CLI carries. The TLS setup, the Postgres +// connection, the UUID, the worker registration and the iteration runs +// are the caller's plumbing. + +// CLI defaults (original lines 43-84). +pub const SEED_AGENT_DEFAULT_ACC = "acc1"; +pub const SEED_AGENT_DEFAULT_SVC_ID = "local-perplexity"; +pub const SEED_AGENT_DEFAULT_SVC_NAME = "seed-agent-auto"; +pub const SEED_AGENT_POLL_IDLE_SECS: u64 = 30; +pub const SEED_AGENT_EARLY_STOP_STEP: i32 = 1000; +pub const SEED_AGENT_EARLY_STOP_BPB_CEILING: f64 = 2.60; +pub const SEED_AGENT_TRAINER_KIND = "external"; + +// Lines 152-166: the connect retry - up to 30 attempts, 2s apart; +// attempt 30 is the bail. +pub const SEED_AGENT_MAX_CONNECT_ATTEMPTS: u32 = 30; +pub const SEED_AGENT_CONNECT_RETRY_SLEEP_SECS: u64 = 2; + +// Line 206: an iteration error backs off before the next pull. +pub const SEED_AGENT_ERROR_BACKOFF_SECS: u64 = 30; + +// Exit codes (R5 - honest exits: the missing URL is the one boot +// failure that surfaces). +pub const SEED_AGENT_EXIT_OK: i32 = 0; +pub const SEED_AGENT_EXIT_FAIL: i32 = 1; + +// The iteration outcomes of the pull loop (lines 193-207). +pub const SEED_AGENT_OUTCOME_TRAINED: i32 = 0; +pub const SEED_AGENT_OUTCOME_PRUNED: i32 = 1; +pub const SEED_AGENT_OUTCOME_IDLE: i32 = 2; +pub const SEED_AGENT_OUTCOME_ERROR: i32 = 3; + +// Lines 110-115, the L-NEON-RENAME chain: a present, non-empty --neon-url +// flag (clap-bound to RAILWAY_POSTGRES_URL) wins; an absent or empty one +// falls back to the legacy NEON_DATABASE_URL; neither is the boot error. +pub fn seed_agent_url_source(neon_flag_present: bool, neon_flag: []const u8, legacy_env_present: bool) -> i32 { + if (neon_flag_present and neon_flag.len > 0) { + return 1; + } + if (legacy_env_present) { + return 2; + } + return 0; +} + +// Lines 151-169: the connect loop stays patient while attempts remain - +// the attempt that reaches the cap is the bail. +pub fn seed_agent_retry_still_patient(attempt: u32) -> bool { + return attempt < SEED_AGENT_MAX_CONNECT_ATTEMPTS; +} + +// Lines 193-207: the loop's wait - a finished experiment (trained or +// pruned) pulls the next at once, an idle queue waits the poll interval, +// an iteration error backs off. +pub fn seed_agent_iteration_wait_secs(outcome: i32) -> u64 { + if (outcome == SEED_AGENT_OUTCOME_IDLE) { + return SEED_AGENT_POLL_IDLE_SECS; + } + if (outcome == SEED_AGENT_OUTCOME_ERROR) { + return SEED_AGENT_ERROR_BACKOFF_SECS; + } + return 0; +} + +// Lines 188-192: the shutdown signal ends the loop - the claimed row is +// released and the worker stops; anything else keeps pulling. +pub fn seed_agent_loop_continues(shutdown_seen: bool) -> bool { + if (shutdown_seen) { + return false; + } + return true; +} + +// Lines 66-68, the early-stop rule the CLI carries: at the early-stop +// step, a BPB exceeding the ceiling prunes the experiment - strictly +// exceeding; the ceiling itself does not prune. +pub fn seed_agent_early_stop_prunes(bpb: f64, ceiling: f64) -> bool { + if (bpb > ceiling) { + return true; + } + return false; +} + +// Line 89: the boot gate - the worker resolves its Postgres URL first; +// with none from any source the boot fails before the retry loop or the +// pull loop can start. With a URL the worker registers and pulls until +// the shutdown signal, exiting cleanly. +pub fn main(neon_flag_present: bool, neon_flag: []const u8, legacy_env_present: bool) -> i32 { + const source = seed_agent_url_source(neon_flag_present, neon_flag, legacy_env_present); + if (source == 0) { + return SEED_AGENT_EXIT_FAIL; + } + return SEED_AGENT_EXIT_OK; +} + +test url_chain_l_neon_rename { + assert(seed_agent_url_source(true, "postgres://railway/db", false) == 1); + assert(seed_agent_url_source(true, "postgres://railway/db", true) == 1); + assert(seed_agent_url_source(true, "", true) == 2); + assert(seed_agent_url_source(false, "", true) == 2); + assert(seed_agent_url_source(false, "", false) == 0); + assert(seed_agent_url_source(true, "", false) == 0); +} + +test boot_gate_exits_on_missing_url { + assert(main(true, "postgres://railway/db", false) == SEED_AGENT_EXIT_OK); + assert(main(false, "", true) == SEED_AGENT_EXIT_OK); + assert(main(false, "", false) == SEED_AGENT_EXIT_FAIL); + assert(main(true, "", false) == SEED_AGENT_EXIT_FAIL); +} + +test connect_retry_patience { + assert(seed_agent_retry_still_patient(0) == true); + assert(seed_agent_retry_still_patient(1) == true); + assert(seed_agent_retry_still_patient(29) == true); + assert(seed_agent_retry_still_patient(30) == false); + assert(seed_agent_retry_still_patient(31) == false); +} + +test loop_wait_shape { + assert(seed_agent_iteration_wait_secs(SEED_AGENT_OUTCOME_TRAINED) == 0); + assert(seed_agent_iteration_wait_secs(SEED_AGENT_OUTCOME_PRUNED) == 0); + assert(seed_agent_iteration_wait_secs(SEED_AGENT_OUTCOME_IDLE) == 30); + assert(seed_agent_iteration_wait_secs(SEED_AGENT_OUTCOME_ERROR) == 30); +} + +test shutdown_ends_the_loop { + assert(seed_agent_loop_continues(false) == true); + assert(seed_agent_loop_continues(true) == false); +} + +test early_stop_ceiling_is_strict { + assert(seed_agent_early_stop_prunes(2.61, 2.60) == true); + assert(seed_agent_early_stop_prunes(2.60, 2.60) == false); + assert(seed_agent_early_stop_prunes(2.59, 2.60) == false); + assert(seed_agent_early_stop_prunes(3.0, 2.60) == true); +}