diff --git a/.trinity/seals/t27f_T27fBuild.json b/.trinity/seals/t27f_T27fBuild.json new file mode 100644 index 0000000000..eed374e353 --- /dev/null +++ b/.trinity/seals/t27f_T27fBuild.json @@ -0,0 +1,28 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+fdb6b28dd", + "closure": [], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:fe94cacbbbd428a52723da994493d32806fd219a280fc1fd03bc8349c92995a7", + "gen_hash_rust": "sha256:e262afe0bb2e2d1585b3b24c387bc31d736ad9e49448cc5fc95bab6b60f7fa49", + "gen_hash_verilog": "sha256:22321a28bb8a76b75938d70e0586cf11490b6dd145528ed027edd9c57ae722cd", + "gen_hash_zig": "sha256:21401395a6b97026fbabc047f7fc5c8de343cc075909d90c1e38bea3deac3560", + "module": "T27fBuild", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T05:10:01Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:99a992a55d9f76082cdebc7aead17500b68f7f9b0a4cf2193a17e4935f192b45", + "spec_path": "specs/t27f/build.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 5, + "total": 5, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27f_T27fDeck.json b/.trinity/seals/t27f_T27fDeck.json new file mode 100644 index 0000000000..b510e36d1c --- /dev/null +++ b/.trinity/seals/t27f_T27fDeck.json @@ -0,0 +1,33 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+fdb6b28dd", + "closure": [ + { + "spec": "specs/t27f/reel.t27", + "spec_hash": "sha256:c47b21cea1d7169da6528e321cf2d2c513d3f552c0cd04fe9d37bbd5314ccacf" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:12cda40c24f3cb0e6a0dc585ad69da43d3f8dafa6cbb2d29f00769e91bc272c9", + "gen_hash_rust": "sha256:4c63e3dbea1ec78322a0888f58a2a7715a06ebf31b271e7f282dce4c2038a7f6", + "gen_hash_verilog": "sha256:34d4bc60d3317284658d56fc1041f2d5961d988ae21649aeac728a7f8cea1ceb", + "gen_hash_zig": "sha256:ced9ec60f5d5f557a97299480621720e29e32e7e5a2baf4aaec8ab3d7ba30c6a", + "module": "T27fDeck", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T05:05:43Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:bd8ef9fc155365c805ba716d97155bed1e4017c378dbc335e252bcb244512147", + "spec_path": "specs/t27f/deck.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 16, + "total": 16, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27f_T27fReel.json b/.trinity/seals/t27f_T27fReel.json new file mode 100644 index 0000000000..fc8418b065 --- /dev/null +++ b/.trinity/seals/t27f_T27fReel.json @@ -0,0 +1,28 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+fdb6b28dd", + "closure": [], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:ac88f356afdd5cf151a8d724eb0790cace584971058d674eb5871dcc68451543", + "gen_hash_rust": "sha256:4fcaf15a4108af091615cae0e4c0c3abf8cd5f4fbff37a0af5decd94d781c6c4", + "gen_hash_verilog": "sha256:204dc81066234ec28553fffb30b5053e875c6185a90cc8f1ad9577172e5947fd", + "gen_hash_zig": "sha256:997f3fc1d04e78ef3c8681c7b0b726d75341cc96928710771bca279cbd1184ba", + "module": "T27fReel", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T05:16:14Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:c47b21cea1d7169da6528e321cf2d2c513d3f552c0cd04fe9d37bbd5314ccacf", + "spec_path": "specs/t27f/reel.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 20, + "total": 20, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27f_t27f_spec_graph.json b/.trinity/seals/t27f_t27f_spec_graph.json new file mode 100644 index 0000000000..1c2d000bc6 --- /dev/null +++ b/.trinity/seals/t27f_t27f_spec_graph.json @@ -0,0 +1,33 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+fdb6b28dd", + "closure": [ + { + "spec": "specs/t27f/ui_plan.t27", + "spec_hash": "sha256:383fdfb78aa13f2e48172a20e9863d91aa5147b63b1685be8e3a40667243a3f1" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:0e44393e077e8b59c5959a3f7e5df729019eae00e8ad8150db2300a8620181b4", + "gen_hash_rust": "sha256:beb8f437539f0dbf6992685e14e8f65b8bc7521a35210fe0327eee14bbb1ea4a", + "gen_hash_verilog": "sha256:79d98974176de696ade1d8b1be37064a477464f3deb479780f63d6ffb2b35bbd", + "gen_hash_zig": "sha256:103904a8ca767af820f446c0591dc6b3bc91a90462e7f7155ac6036bb84e13d4", + "module": "t27f_spec_graph", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T05:05:44Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:a3dbe0ec5110c48183ebea101109598e8542bb0ffdf9e061c672162d7da03fe7", + "spec_path": "specs/t27f/spec_graph.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 14, + "total": 14, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27f_t27f_spec_graph_derive.json b/.trinity/seals/t27f_t27f_spec_graph_derive.json new file mode 100644 index 0000000000..a997db23f1 --- /dev/null +++ b/.trinity/seals/t27f_t27f_spec_graph_derive.json @@ -0,0 +1,37 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+fdb6b28dd", + "closure": [ + { + "spec": "specs/t27f/spec_graph.t27", + "spec_hash": "sha256:a3dbe0ec5110c48183ebea101109598e8542bb0ffdf9e061c672162d7da03fe7" + }, + { + "spec": "specs/t27f/spec_graph_export.t27", + "spec_hash": "sha256:ef4ee24d5debc51be4a10835947c86a0211b867dd6ca97890a15198daa744728" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:13cb24d89efd524e2696193b77f8ead388cff45918ea0a2de0bd6bc2700056c1", + "gen_hash_rust": "sha256:15eb11a1b6a6cc747a8f432a87bfc664cc97d4136c288e3177086ce4d5b2de6d", + "gen_hash_verilog": "sha256:73bcd3362a01b284626017fb0c22cbac2ac5320b4dd547dd1cf313c1e5c338c6", + "gen_hash_zig": "sha256:96b334592e4464a8f57a13782ac006988aa0281e78dffae8167be999e21e0f58", + "module": "t27f_spec_graph_derive", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T05:05:47Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:cecf18a0ea629ec07186d3d0b0bca2d8627a5e714588332012dc04ae93f1c188", + "spec_path": "specs/t27f/spec_graph_derive.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 10, + "total": 10, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27f_t27f_spec_graph_export.json b/.trinity/seals/t27f_t27f_spec_graph_export.json new file mode 100644 index 0000000000..9a8543a4bf --- /dev/null +++ b/.trinity/seals/t27f_t27f_spec_graph_export.json @@ -0,0 +1,33 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+fdb6b28dd", + "closure": [ + { + "spec": "specs/t27f/spec_graph.t27", + "spec_hash": "sha256:a3dbe0ec5110c48183ebea101109598e8542bb0ffdf9e061c672162d7da03fe7" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:839faf75367d9a0626d7c09a66d5ecd410cb385b52977feb084acba95348ae3c", + "gen_hash_rust": "sha256:0c873d1a4fa9aa2781f8adfe400046a7ebbca83884515fccd830a0ae3f6c6a57", + "gen_hash_verilog": "sha256:bbdd7ae0fefa4d2721cb569c082d0ab5088cb17fcd6c048d592b257bcf51db72", + "gen_hash_zig": "sha256:d4082d51071d7163cd24b3dd51824407eb155dbe64035736d307699aa5edf6a6", + "module": "t27f_spec_graph_export", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T05:05:47Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:ef4ee24d5debc51be4a10835947c86a0211b867dd6ca97890a15198daa744728", + "spec_path": "specs/t27f/spec_graph_export.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 20, + "total": 20, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27f_t27f_spec_graph_fold.json b/.trinity/seals/t27f_t27f_spec_graph_fold.json new file mode 100644 index 0000000000..f6fd3840fb --- /dev/null +++ b/.trinity/seals/t27f_t27f_spec_graph_fold.json @@ -0,0 +1,37 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+fdb6b28dd", + "closure": [ + { + "spec": "specs/t27f/spec_graph.t27", + "spec_hash": "sha256:a3dbe0ec5110c48183ebea101109598e8542bb0ffdf9e061c672162d7da03fe7" + }, + { + "spec": "specs/t27f/spec_graph_view.t27", + "spec_hash": "sha256:662df660969b2ebccaf99129ea8d9eb41f38ded82ff34e085cf5e07b629e3089" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:72bc1a2222d19a076e86843fa7bbf19a45cdb840c66fd45c899caa3e5d4457de", + "gen_hash_rust": "sha256:fe635c6967870d8f9e171532a27154ba16b4e3848bc67d29732ea4ee25bd4208", + "gen_hash_verilog": "sha256:ab53fb1acee830eb8f25c5dc2d0f3f485454ecd2d42972f3097d11f1086a1506", + "gen_hash_zig": "sha256:730a3b5ecc0d0dc6206ea13772632a5a9e406d22829093cbb1bb941689268b26", + "module": "t27f_spec_graph_fold", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T05:05:49Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:6a9050681b5663c183125d6e3b2402b886a1ea4e2cb7edb66b95866c77375275", + "spec_path": "specs/t27f/spec_graph_fold.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 12, + "total": 12, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27f_t27f_spec_graph_outline.json b/.trinity/seals/t27f_t27f_spec_graph_outline.json new file mode 100644 index 0000000000..91170b21c1 --- /dev/null +++ b/.trinity/seals/t27f_t27f_spec_graph_outline.json @@ -0,0 +1,33 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+fdb6b28dd", + "closure": [ + { + "spec": "specs/t27f/spec_graph.t27", + "spec_hash": "sha256:a3dbe0ec5110c48183ebea101109598e8542bb0ffdf9e061c672162d7da03fe7" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:6e2c05c8438808c5d481ffc2d3b53c2390ca33dd8582948fc73c68e0028dd941", + "gen_hash_rust": "sha256:46ea835fefae8cd93e598e4420eac674aaa971a8b6cd9303f893e068c105fd02", + "gen_hash_verilog": "sha256:868fce0e0d6018fdc78045ad4db95f9b53539c5f6fba1229ef38249dcd497c10", + "gen_hash_zig": "sha256:48d72aef022ba597fdf32c633248b354053f4f2890c22f709b0cf50ca80c32fa", + "module": "t27f_spec_graph_outline", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T05:05:48Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:92e0bf52018e6eabebec1705d04a15450a2ebd8abe1b737891a9ad3c0991a1c7", + "spec_path": "specs/t27f/spec_graph_outline.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 8, + "total": 8, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27f_t27f_spec_graph_view.json b/.trinity/seals/t27f_t27f_spec_graph_view.json new file mode 100644 index 0000000000..a90e96c199 --- /dev/null +++ b/.trinity/seals/t27f_t27f_spec_graph_view.json @@ -0,0 +1,37 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+fdb6b28dd", + "closure": [ + { + "spec": "specs/t27f/spec_graph.t27", + "spec_hash": "sha256:a3dbe0ec5110c48183ebea101109598e8542bb0ffdf9e061c672162d7da03fe7" + }, + { + "spec": "specs/t27f/ui_plan.t27", + "spec_hash": "sha256:383fdfb78aa13f2e48172a20e9863d91aa5147b63b1685be8e3a40667243a3f1" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:52a1db703b28fd1514901bb7e73f776d7e02df22613eece4bbd8295f9822b505", + "gen_hash_rust": "sha256:8899f02a4fbaa24baca22cdf050660915679fe0350ee92eea5d475f0b7aba4e4", + "gen_hash_verilog": "sha256:6ae77a9ed4797954cbae36339d5a583671d171410af7d46f195b4d8aa24e0541", + "gen_hash_zig": "sha256:457fef31a7f0c4de4042b81185723908d58e6c7ab4dce3001c0e0dc6ca089a80", + "module": "t27f_spec_graph_view", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T05:05:46Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:662df660969b2ebccaf99129ea8d9eb41f38ded82ff34e085cf5e07b629e3089", + "spec_path": "specs/t27f/spec_graph_view.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 9, + "total": 9, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27f_t27f_ui_plan.json b/.trinity/seals/t27f_t27f_ui_plan.json new file mode 100644 index 0000000000..5a5860e189 --- /dev/null +++ b/.trinity/seals/t27f_t27f_ui_plan.json @@ -0,0 +1,28 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+fdb6b28dd", + "closure": [], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:13e0d57ac1ad27da6e5c3526eaee133815f52c3c785959f546218a27fd281843", + "gen_hash_rust": "sha256:1751e5e2c2f56392770b48d23f2581910a1e8ffba718e800ff5c6324e976c878", + "gen_hash_verilog": "sha256:f641e82da807b5fb535ea792fd634ea7b6055ded6e14cc4c185bea7b31fa20c7", + "gen_hash_zig": "sha256:bc3a2018e197ed0cd5612012f8562a89dea384dd69d959d58982db3b945d1d09", + "module": "t27f_ui_plan", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T05:05:45Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:383fdfb78aa13f2e48172a20e9863d91aa5147b63b1685be8e3a40667243a3f1", + "spec_path": "specs/t27f/ui_plan.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 9, + "total": 9, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/docs/reports/t27b_expectations.json b/docs/reports/t27b_expectations.json index fbf6b28afb..9b99d066cf 100644 --- a/docs/reports/t27b_expectations.json +++ b/docs/reports/t27b_expectations.json @@ -1326,6 +1326,7 @@ {"path": "specs/skills/trinity-vsa-verify.t27", "t27b": "pass"}, {"path": "specs/skills/trinity-wave.t27", "t27b": "pass"}, {"path": "specs/system/unified-agent-operations.t27", "t27b": "pass"}, + {"path": "specs/t27f/build.t27", "t27b": "pass"}, {"path": "specs/t27f/deck.t27", "t27b": "pass"}, {"path": "specs/t27f/quest.t27", "t27b": "pass"}, {"path": "specs/t27f/reel.t27", "t27b": "pass"}, diff --git a/gen/rust/t27f/build.rs b/gen/rust/t27f/build.rs new file mode 100644 index 0000000000..d2ed2e1fca --- /dev/null +++ b/gen/rust/t27f/build.rs @@ -0,0 +1,111 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c +// NOT LOWERED BY THIS BACKEND: 5 test(s), 1 invariant(s), 0 function(s) with no body. +// This backend emits declarations only. The spec's checks live in +// the Zig and Verilog outputs; do not read this file as verified. + +pub const BUILD_RECORD: &'static str = "t27f.build"; + +pub const BUILD_RECORD_VERSION: u32 = 1; + +pub const BUILD_ROOTS: &'static str = "reel deck spec_graph_view spec_graph_export spec_graph_outline"; + +pub const BUILD_IMPORTED: &'static str = "spec_graph ui_plan"; + +pub const BUILD_SPEC_DIR: &'static str = "specs/t27f/"; + +pub const BUILD_GLUE: &'static str = "export.zig"; + +pub const BUILD_OUTPUTS: &'static str = "reel.zig deck.zig spec_graph_view.zig spec_graph_export.zig spec_graph_outline.zig reel.mjs deck.mjs spec_graph_view.mjs spec_graph_export.mjs spec_graph_outline.mjs t27f.wasm"; + +pub const BUILD_ZIG: &'static str = "build-exe export.zig -target wasm32-freestanding -fno-entry -rdynamic -O ReleaseSmall -femit-bin=t27f.wasm"; + +pub const KEY_CLOSURE: &'static str = "# closure "; + +pub const KEY_ZIG: &'static str = "# zig "; + +pub const KEY_T27C: &'static str = "# t27c "; + +pub const BUILD_NO_SEAL: &'static str = "No saved seal found"; + +pub const BUILD_REUSE: u32 = 0; + +pub const BUILD_NO_RECORD: u32 = 1; + +pub const BUILD_CLOSURE: u32 = 2; + +pub const BUILD_ZIG_CHANGED: u32 = 3; + +pub const BUILD_T27C_CHANGED: u32 = 4; + +pub const BUILD_GLUE_CHANGED: u32 = 5; + +pub const BUILD_OUTPUT_CHANGED: u32 = 6; + +pub const BUILD_REASON_WORDS: &'static str = "REUSE no-record closure zig t27c glue output"; + +pub const SEAL_WORDS: &'static str = "ok missing stale"; + +pub const FILE_WORDS: &'static str = "ok missing changed"; + +pub const SEAL_OK: u32 = 0; + +pub const SEAL_MISSING: u32 = 1; + +pub const SEAL_STALE: u32 = 2; + +pub const FILE_OK: u32 = 0; + +pub const FILE_MISSING: u32 = 1; + +pub const FILE_DRIFT: u32 = 2; + +pub fn build_reason(has_record: bool, closure_same: bool, zig_same: bool, t27c_same: bool, glue_same: bool, outputs_same: bool) -> u32 { + if (has_record == false) { + return BUILD_NO_RECORD; + } + if (closure_same == false) { + return BUILD_CLOSURE; + } + if (zig_same == false) { + return BUILD_ZIG_CHANGED; + } + if (t27c_same == false) { + return BUILD_T27C_CHANGED; + } + if (glue_same == false) { + return BUILD_GLUE_CHANGED; + } + if (outputs_same == false) { + return BUILD_OUTPUT_CHANGED; + } + return BUILD_REUSE; +} + +pub fn build_seal(seal_found: bool, hashes_match: bool) -> u32 { + if (seal_found == false) { + return SEAL_MISSING; + } + if (hashes_match == false) { + return SEAL_STALE; + } + return SEAL_OK; +} + +pub fn build_file(file_found: bool, sha_same: bool) -> u32 { + if (file_found == false) { + return FILE_MISSING; + } + if (sha_same == false) { + return FILE_DRIFT; + } + return FILE_OK; +} + +pub fn build_check_exit(files_off: u32) -> u32 { + if (files_off > 0) { + return 1; + } + return 0; +} + diff --git a/specs/t27f/build.t27 b/specs/t27f/build.t27 new file mode 100644 index 0000000000..40055db511 --- /dev/null +++ b/specs/t27f/build.t27 @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/t27f/build.t27 -- when the T27F exporter wasm is built again, and what its build record holds. +; Issue gHashTag/t27#10229. T27F hot loop, step: the make-style, seal-gated rule for t27f.wasm. +; -- +; Why. t27-mcp/wasm/t27f.wasm is the one artifact the graph page and the MCP tools t27f_graph, t27f_outline and +; t27f_view all run. It was rebuilt by ten hand-typed lab commands (wasm/BUILD), the only record of a build was +; a list of hand-pasted sha256 lines, and the copy under apps/website/public/t27f/graph/ in gHashTag/trinity +; drifted from it. The rest of the hot-reload loop (seal v2, `t27c frontier`, the gen-hash cache, frontier +; --watch) already says "reuse unless an input changed"; this spec says it for the wasm. +; -- +; The rule. Build again only when an input changed, else REUSE and run nothing: +; the closure the source hash of every spec the wasm is generated from, and of the specs they import +; (constants are inlined across imports, so a changed import changes the Zig); +; the zig the version of the compiler that links the wasm; +; the t27c the sha256 of the binary that generates the Zig and the JavaScript; +; the glue export.zig, the one hand-written input (wasm/BUILD describes it). +; The reasons are ordered; the first that holds is the one printed. +; -- +; Seals. A root whose seal is missing, or whose seal records another spec hash than the file now has, is +; refused: the build never turns an unsealed or unchecked spec into an artifact (`t27c seal --save` first). +; -- +; The record. One text file, wasm/t27f.build, written next to the outputs. `#` lines are the inputs; every other +; line is ` `, the format `shasum -a 256 -c` reads, so a checker in any repository is +; grep -v '^#' t27f.build | shasum -a 256 -c - +; with no code of ours. `tri t27f check ` does the same in tri and says which file drifted. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module T27fBuild; + +/// The record's file name, next to the outputs it describes. +pub const BUILD_RECORD : str = "t27f.build"; + +/// Bumped by hand when the record's keys or the build's composition change. +pub const BUILD_RECORD_VERSION : u32 = 1; + +/// The specs the wasm is generated from: each gets a Zig file (t27c gen) and a JavaScript file (t27c gen-js). +pub const BUILD_ROOTS : str = "reel deck spec_graph_view spec_graph_export spec_graph_outline"; + +/// The specs the roots import, whose items t27c gen inlines. They are inputs, not generated files. +pub const BUILD_IMPORTED : str = "spec_graph ui_plan"; + +/// The directory of the specs, under the repository root. +pub const BUILD_SPEC_DIR : str = "specs/t27f/"; + +/// The hand glue, an input that sits next to the outputs. +pub const BUILD_GLUE : str = "export.zig"; + +/// The generated files, in the order the record lists them. The wasm is last: it is what the others build. +pub const BUILD_OUTPUTS : str = "reel.zig deck.zig spec_graph_view.zig spec_graph_export.zig spec_graph_outline.zig reel.mjs deck.mjs spec_graph_view.mjs spec_graph_export.mjs spec_graph_outline.mjs t27f.wasm"; + +/// The zig command that links the wasm (run in the output directory). -rdynamic is required: without it +/// a -fno-entry wasm exports nothing. +pub const BUILD_ZIG : str = "build-exe export.zig -target wasm32-freestanding -fno-entry -rdynamic -O ReleaseSmall -femit-bin=t27f.wasm"; + +/// The record's input keys, `# ` lines. +pub const KEY_CLOSURE : str = "# closure "; +pub const KEY_ZIG : str = "# zig "; +pub const KEY_T27C : str = "# t27c "; + +/// What `t27c seal --verify` says when there is no seal to verify (it exits 1 for that and for a stale seal). +pub const BUILD_NO_SEAL : str = "No saved seal found"; + +/// Why the build runs again, or REUSE. +pub const BUILD_REUSE : u32 = 0; +pub const BUILD_NO_RECORD : u32 = 1; +pub const BUILD_CLOSURE : u32 = 2; +pub const BUILD_ZIG_CHANGED : u32 = 3; +pub const BUILD_T27C_CHANGED : u32 = 4; +pub const BUILD_GLUE_CHANGED : u32 = 5; +pub const BUILD_OUTPUT_CHANGED : u32 = 6; + +/// The words `tri t27f` prints for each code above, in code order (space separated). +pub const BUILD_REASON_WORDS : str = "REUSE no-record closure zig t27c glue output"; +pub const SEAL_WORDS : str = "ok missing stale"; +pub const FILE_WORDS : str = "ok missing changed"; + +/// A root's seal: usable, missing, or recording another spec hash than the file has. +pub const SEAL_OK : u32 = 0; +pub const SEAL_MISSING : u32 = 1; +pub const SEAL_STALE : u32 = 2; + +/// One file of a record against the file now: the same bytes, no file, other bytes. +pub const FILE_OK : u32 = 0; +pub const FILE_MISSING : u32 = 1; +pub const FILE_DRIFT : u32 = 2; + +/// REUSE, or the first reason to build again. Each flag says the record's value equals the value now; +/// `outputs_same` covers every generated file, `glue_same` export.zig. +pub fn build_reason(has_record: bool, closure_same: bool, zig_same: bool, t27c_same: bool, glue_same: bool, outputs_same: bool) -> u32 { + if (has_record == false) { return BUILD_NO_RECORD; } + if (closure_same == false) { return BUILD_CLOSURE; } + if (zig_same == false) { return BUILD_ZIG_CHANGED; } + if (t27c_same == false) { return BUILD_T27C_CHANGED; } + if (glue_same == false) { return BUILD_GLUE_CHANGED; } + if (outputs_same == false) { return BUILD_OUTPUT_CHANGED; } + return BUILD_REUSE; +} + +/// The seal of one root: `seal_found` is false when t27c said there is none, `hashes_match` is its exit code 0. +pub fn build_seal(seal_found: bool, hashes_match: bool) -> u32 { + if (seal_found == false) { return SEAL_MISSING; } + if (hashes_match == false) { return SEAL_STALE; } + return SEAL_OK; +} + +/// One record line against the file: `file_found`, and `sha_same` when the file's sha256 is the record's. +pub fn build_file(file_found: bool, sha_same: bool) -> u32 { + if (file_found == false) { return FILE_MISSING; } + if (sha_same == false) { return FILE_DRIFT; } + return FILE_OK; +} + +/// The exit code of `tri t27f check`: 1 as soon as one file drifted or is missing. +pub fn build_check_exit(files_off: u32) -> u32 { + if (files_off > 0) { return 1; } + return 0; +} + +test build_reuses_only_when_every_input_is_the_same { + assert build_reason(true, true, true, true, true, true) == 0; + assert build_reason(false, true, true, true, true, true) == 1; + assert build_reason(true, false, true, true, true, true) == 2; + assert build_reason(true, true, false, true, true, true) == 3; + assert build_reason(true, true, true, false, true, true) == 4; + assert build_reason(true, true, true, true, false, true) == 5; + assert build_reason(true, true, true, true, true, false) == 6; +} + +test build_reason_names_the_first_change { + // several inputs changed: the earlier reason wins, so the printed reason does not depend on the order checked + assert build_reason(true, false, false, false, false, false) == 2; + assert build_reason(true, true, false, false, true, true) == 3; + assert build_reason(true, true, true, true, false, false) == 5; + // no record: nothing else can be judged + assert build_reason(false, false, false, false, false, false) == 1; +} + +test build_refuses_a_root_without_a_current_seal { + assert build_seal(true, true) == 0; + assert build_seal(false, false) == 1; + assert build_seal(true, false) == 2; + // a missing seal stays missing even if the exit code looked like a match + assert build_seal(false, true) == 1; +} + +test build_check_names_the_drift { + assert build_file(true, true) == 0; + assert build_file(false, true) == 1; + assert build_file(true, false) == 2; + assert build_file(false, false) == 1; +} + +test build_check_exit_is_one_on_any_drift { + assert build_check_exit(0) == 0; + assert build_check_exit(1) == 1; + assert build_check_exit(11) == 1; +} + +invariant the_record_shape_never_moves_silently { + assert BUILD_RECORD_VERSION >= 1; + assert BUILD_REUSE == 0; + assert SEAL_OK == 0; + assert FILE_OK == 0; + assert BUILD_NO_RECORD != BUILD_CLOSURE; + assert BUILD_ZIG_CHANGED != BUILD_T27C_CHANGED; + assert BUILD_GLUE_CHANGED != BUILD_OUTPUT_CHANGED; + assert SEAL_MISSING != SEAL_STALE; + assert FILE_MISSING != FILE_DRIFT; +}