diff --git a/.trinity/seals/claw_ClawGates.json b/.trinity/seals/claw_ClawGates.json index 08de0933c1..d10256d23d 100644 --- a/.trinity/seals/claw_ClawGates.json +++ b/.trinity/seals/claw_ClawGates.json @@ -7,23 +7,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:e83faa16fdda54ccb71a5cef820dcbcb0b115a1a964b1b769e8efa3fe4f46de4", - "gen_hash_rust": "sha256:f5a230d83ed3b53022cda5241bcb97a98a6f7d35118813dd896bfd94a408db57", - "gen_hash_verilog": "sha256:e60c97323fe49104fa91dec8d4d70db8c3070acb09e10955bae928b09a0aaeac", - "gen_hash_zig": "sha256:2bb85ab7683c3ce5f9e68d5cdeec753084408e7c0b36e65e68a3aba22a8312e8", + "gen_hash_c": "sha256:5ea99f4bd997bc13d2fbe28e6c2233288882316921a77b3629cae88dc9c98bb3", + "gen_hash_rust": "sha256:6ffb149e2944c1cbfdea5fc806fdf457ff6134c9dfeec6d516ab25a55f9a6196", + "gen_hash_verilog": "sha256:9a8147bffc70fa6445ab2e5b07adb5d8e9394998701a175e9cf956002a60ede3", + "gen_hash_zig": "sha256:29203b78b4748ecea4123004d55f88435200878599e3d41c511c8b24679fefde", "module": "ClawGates", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-18T22:41:04Z", + "sealed_at": "2026-10-18T22:53:22Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:944d2af8d1a9f6ad261a527445f94922e3f299d5bb3657341f6f5cdc5c38f9c1", + "spec_hash": "sha256:020e33b38748419eb5d46d108a64adb0246f5b4c2ef9c83f06735daebcb1e0aa", "spec_path": "specs/claw/gates.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 23, - "total": 23, + "passed": 24, + "total": 24, "vacuous": 0 }, "toolchain": { diff --git a/gen/c/claw/gates.c b/gen/c/claw/gates.c index 6e66ee09e5..98d1321010 100644 --- a/gen/c/claw/gates.c +++ b/gen/c/claw/gates.c @@ -53,6 +53,7 @@ static const size_t CLASS_LABEL_LENS[3] = { 10, 6, 11 }; uint8_t class_of(const char* name, size_t name_len); bool is_blocking(const char* name, size_t name_len); const char* events_of(const char* name, size_t name_len); +size_t events_len_of(const char* name, size_t name_len); uint32_t count_class(uint8_t c); bool rows_filled(size_t name_lens[static 67], size_t event_lens[static 67]); bool every_row_filled(void); @@ -98,6 +99,15 @@ const char* events_of(const char* name, size_t name_len) { return ""; } +size_t events_len_of(const char* name, size_t name_len) { + for (size_t i = 0; i < 67; i++) { + if (str_eq(NAMES[i], NAME_LENS[i], name, name_len)) { + return EVENT_LENS[i]; + } + } + return 0; +} + uint32_t count_class(uint8_t c) { uint32_t n = 0; for (size_t i = 0; i < 67; i++) { @@ -442,20 +452,26 @@ void test_rows_stay_aligned_name_class_events(void) { const char* ol = "own-language"; const char* ev_qp = "push,schedule,workflow_dispatch"; const char* ev_ol = "pull_request"; - assert((NAMES[1] == want1)); + assert((str_eq(NAMES[1], NAME_LENS[1], want1, 20) == true)); assert((CLASSES[1] == G_REPORTING)); - assert((NAMES[42] == want42)); + assert((str_eq(NAMES[42], NAME_LENS[42], want42, 13) == true)); assert((CLASSES[42] == G_REPORTING)); - assert((NAMES[29] == want29)); + assert((str_eq(NAMES[29], NAME_LENS[29], want29, 14) == true)); assert((CLASSES[29] == G_SOFT)); - assert((events_of(qp, 13) == ev_qp)); - assert((events_of(ol, 12) == ev_ol)); + assert((str_eq(events_of(qp, 13), events_len_of(qp, 13), ev_qp, 31) == true)); + assert((str_eq(events_of(ol, 12), events_len_of(ol, 12), ev_ol, 12) == true)); } void test_unknown_name_is_unknown_and_not_blocking(void) { assert((class_of("no-such-gate", 12) == G_UNKNOWN)); assert((is_blocking("no-such-gate", 12) == false)); - assert((events_of("no-such-gate", 12) == "")); + assert((events_len_of("no-such-gate", 12) == 0)); +} + +void test_events_len_of_answers_every_row(void) { + for (size_t i = 0; i < 67; i++) { + assert((events_len_of(NAMES[i], NAME_LENS[i]) == EVENT_LENS[i])); + } } void test_nightly_pair_contrast(void) { @@ -523,14 +539,10 @@ void test_audit_ignores_blank_lines_in_the_directory(void) { } void test_audit_prefixes_name_the_problem_kind(void) { - const char* spec_only = LINE_PREFIX[P_SPEC_ONLY]; - assert((spec_only == "SPEC-ONLY ")); - const char* dir_only = LINE_PREFIX[P_DIR_ONLY]; - assert((dir_only == "DIR-ONLY ")); - const char* ok = LINE_PREFIX[P_OK]; - assert((ok == "")); - const char* tail = DRIFT_TAIL; - assert((tail == "claw: DRIFT -- regenerate specs/claw/gates.t27 (docs/claw/README.md)")); + assert((str_eq(LINE_PREFIX[P_SPEC_ONLY], PREFIX_LENS[P_SPEC_ONLY], "SPEC-ONLY ", 11) == true)); + assert((str_eq(LINE_PREFIX[P_DIR_ONLY], PREFIX_LENS[P_DIR_ONLY], "DIR-ONLY ", 11) == true)); + assert((PREFIX_LENS[P_OK] == 0)); + assert((str_eq(DRIFT_TAIL, DRIFT_TAIL_LEN, "claw: DRIFT -- regenerate specs/claw/gates.t27 (docs/claw/README.md)", 68) == true)); } void test_every_row_round_trips_through_the_name_lookup(void) { @@ -547,9 +559,9 @@ void test_every_row_round_trips_through_the_name_lookup(void) { } void test_soft_gates_report_their_events(void) { - assert((events_of("oracle-nightly", 14) == "schedule,workflow_dispatch")); - assert((events_of("queen-doctor", 12) == "schedule,workflow_dispatch")); - assert((events_of("rings-rust", 10) == "push,pull_request,workflow_dispatch")); + assert((str_eq(events_of("oracle-nightly", 14), events_len_of("oracle-nightly", 14), "schedule,workflow_dispatch", 26) == true)); + assert((str_eq(events_of("queen-doctor", 12), events_len_of("queen-doctor", 12), "schedule,workflow_dispatch", 26) == true)); + assert((str_eq(events_of("rings-rust", 10), events_len_of("rings-rust", 10), "push,pull_request,workflow_dispatch", 35) == true)); } void test_coverage_report_carries_every_class(void) { @@ -679,6 +691,7 @@ int main(void) { test_class_census_62_3_2(); test_rows_stay_aligned_name_class_events(); test_unknown_name_is_unknown_and_not_blocking(); + test_events_len_of_answers_every_row(); test_nightly_pair_contrast(); test_reporting_gates_never_block_a_merge(); test_audit_clean_when_directory_matches_inventory(); @@ -694,7 +707,7 @@ int main(void) { test_a_hole_in_either_column_fails_the_filled_check(); test_a_repeated_name_is_caught_wherever_it_sits(); test_a_class_outside_the_three_is_unknown(); - printf("All %d tests passed.\n", 23); + printf("All %d tests passed.\n", 24); return 0; } #endif /* T27_TEST_MAIN */ diff --git a/specs/claw/gates.t27 b/specs/claw/gates.t27 index 21945b9738..334e9408f8 100644 --- a/specs/claw/gates.t27 +++ b/specs/claw/gates.t27 @@ -159,6 +159,16 @@ pub fn events_of(name: str, name_len: usize) -> str { return ""; } +/// Length of events_of(name); 0 when the name is not in the inventory. A caller that lowers +/// through gen-c compares the result with str_eq and this length: `events_of(x) == "..."` would +/// lower to a C pointer comparison (#8013). +pub fn events_len_of(name: str, name_len: usize) -> usize { + for i in 0..67 { + if str_eq(NAMES[i], NAME_LENS[i], name, name_len) { return EVENT_LENS[i]; } + } + return 0; +} + /// How many workflows carry a given class. pub fn count_class(c: u8) -> u32 { let mut n : u32 = 0; @@ -448,20 +458,30 @@ test rows_stay_aligned_name_class_events { var ol : str = "own-language"; var ev_qp : str = "push,schedule,workflow_dispatch"; var ev_ol : str = "pull_request"; - assert NAMES[1] == want1; + // Every string compare carries its lengths through str_eq: `==` on str lowers to a C + // pointer comparison under gen-c (#8013, #10698). + assert str_eq(NAMES[1], NAME_LENS[1], want1, 20) == true; assert CLASSES[1] == G_REPORTING; - assert NAMES[42] == want42; + assert str_eq(NAMES[42], NAME_LENS[42], want42, 13) == true; assert CLASSES[42] == G_REPORTING; - assert NAMES[29] == want29; + assert str_eq(NAMES[29], NAME_LENS[29], want29, 14) == true; assert CLASSES[29] == G_SOFT; - assert events_of(qp, 13) == ev_qp; - assert events_of(ol, 12) == ev_ol; + assert str_eq(events_of(qp, 13), events_len_of(qp, 13), ev_qp, 31) == true; + assert str_eq(events_of(ol, 12), events_len_of(ol, 12), ev_ol, 12) == true; } test unknown_name_is_unknown_and_not_blocking { assert class_of("no-such-gate", 12) == G_UNKNOWN; assert is_blocking("no-such-gate", 12) == false; - assert events_of("no-such-gate", 12) == ""; + assert events_len_of("no-such-gate", 12) == 0; +} + +test events_len_of_answers_every_row { + // Every row through the length lookup, so a lookup that returned the wrong row's length, or + // the sentinel for a known name, fails here and not only on the five rows named above. + for i in 0..67 { + assert events_len_of(NAMES[i], NAME_LENS[i]) == EVENT_LENS[i]; + } } test nightly_pair_contrast { @@ -536,14 +556,10 @@ test audit_ignores_blank_lines_in_the_directory { } test audit_prefixes_name_the_problem_kind { - var spec_only : str = LINE_PREFIX[P_SPEC_ONLY]; - assert spec_only == "SPEC-ONLY "; - var dir_only : str = LINE_PREFIX[P_DIR_ONLY]; - assert dir_only == "DIR-ONLY "; - var ok : str = LINE_PREFIX[P_OK]; - assert ok == ""; - var tail : str = DRIFT_TAIL; - assert tail == "claw: DRIFT -- regenerate specs/claw/gates.t27 (docs/claw/README.md)"; + assert str_eq(LINE_PREFIX[P_SPEC_ONLY], PREFIX_LENS[P_SPEC_ONLY], "SPEC-ONLY ", 11) == true; + assert str_eq(LINE_PREFIX[P_DIR_ONLY], PREFIX_LENS[P_DIR_ONLY], "DIR-ONLY ", 11) == true; + assert PREFIX_LENS[P_OK] == 0; + assert str_eq(DRIFT_TAIL, DRIFT_TAIL_LEN, "claw: DRIFT -- regenerate specs/claw/gates.t27 (docs/claw/README.md)", 68) == true; } test every_row_round_trips_through_the_name_lookup { // Before T8, 8 of 66 names ever went through class_of -- a class swap @@ -566,9 +582,9 @@ test soft_gates_report_their_events { // events_of answered for blocking and reporting rows before T8; the // three soft rows were assumed. Lengths are hand-counted literals // (gen-c carries no .len); the tables are pinned in gates_pins.t27. - assert events_of("oracle-nightly", 14) == "schedule,workflow_dispatch"; - assert events_of("queen-doctor", 12) == "schedule,workflow_dispatch"; - assert events_of("rings-rust", 10) == "push,pull_request,workflow_dispatch"; + assert str_eq(events_of("oracle-nightly", 14), events_len_of("oracle-nightly", 14), "schedule,workflow_dispatch", 26) == true; + assert str_eq(events_of("queen-doctor", 12), events_len_of("queen-doctor", 12), "schedule,workflow_dispatch", 26) == true; + assert str_eq(events_of("rings-rust", 10), events_len_of("rings-rust", 10), "push,pull_request,workflow_dispatch", 35) == true; } test coverage_report_carries_every_class {