diff --git a/.claude/skills/t27f/SKILL.md b/.claude/skills/t27f/SKILL.md index a74741b4aa..112356ce12 100644 --- a/.claude/skills/t27f/SKILL.md +++ b/.claude/skills/t27f/SKILL.md @@ -460,7 +460,7 @@ Rows start at y = 280 + 76k above the focus and 820 + 76k below it. Then check: On 255910235 (2483 edge lines, 813 specs named): - 783 cards drew. `base/types` is the hub: 378 direct importers, reached by 400. -- 30 were refused: +- 30 were refused (all drawn since t27#10728, below): - 29 because the spec's own name is over 58 bytes (`port/trios/...`); - 1 because a shown neighbour's name is. - No spec in the corpus is on an import cycle. @@ -479,6 +479,36 @@ Traps: - **Bands are in edge-list order, so a hub shows `account/*` first.** That is the stated rule, not a ranking; do not caption the first three as the most important. +#### Long names are abbreviated, not refused (t27#10728) + +A shown name of at most 58 bytes is drawn byte for byte. A longer one is drawn as its first +segment, `/...`, and the longest run of whole trailing segments that fits +(`port/.../rings/CR-CHAT-03/src/confirmation_tag_chain`). When no whole run fits, it is drawn as +`...` and its last 55 bytes. Only drawn bytes are checked, and the text size follows the drawn +length. `why_card` is unchanged and still refuses a name over 58 bytes. + +On 5440d35e1 the sweep compares the new binary with master's, card by card: + +- 30 cards are newly drawn, 783 are byte-identical to master, and 0 are lost or refused; +- 0 changed cards and 0 newly drawn cards have no `...`, and 0 identical cards have one; +- 29 abbreviated forms are drawn: 27 `/.../` and 2 `...`, the longest 58 bytes; +- the helpers card (29 importers), a `/.../` focus and a `...` focus passed every pixel check, + with 0 white pixels outside x 100..980. + +Lab traps from this run: + +- **`pkill -f dn_lab.sh` kills the fs-lab command that runs it**, because the pattern is in that + command's own line. Write `pkill -f '[d]n_lab.sh'`. +- **An untracked seal in the lab checkout makes `git checkout` fail, and the script then tests the + old commit.** Have the script take the commit it must test and stop with `WRONG COMMIT` when + `HEAD` differs. Once the PR adding a seal merges, that seal is tracked, so `rm -f` of it deletes + a tracked file: restore it with `git checkout --` before sealing. +- **Count a kill only on `^ ?FAIL [a-z]`.** The `FAIL 0` summary line otherwise counts as a failing + test, and a surviving mutant reads `KILLED by 1: 0`. +- **A `<` mutant of a 58-byte bound needs an unquotable byte that only the whole-name check + sees.** A 58-byte name with `:` in the middle is `true` under the mutant, because the dots form + hides that byte, and `false` under the rule. + ## What this skill does not claim The graph IR is a schema (`specs/t27f/spec_graph.t27`) and `specs/t27f/spec_graph_export.t27` turns source into it, but only exact declarations, spans and edges within the IR's capacity, with `specs/t27f/spec_graph_derive.t27` adding the derived and unknown part by stated rules, and no host calls either yet; no renderer draws a T27F view in the Trinity UI, no diff --git a/.trinity/seals/t27f_t27f_deps_card.json b/.trinity/seals/t27f_t27f_deps_card.json index 98eb98d323..2b40fbba0a 100644 --- a/.trinity/seals/t27f_t27f_deps_card.json +++ b/.trinity/seals/t27f_t27f_deps_card.json @@ -27,23 +27,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:e0771c20aea80a2d085ea8151707616be36da0a1075f48dcc81471b21dfb3624", - "gen_hash_rust": "sha256:8b918de8a45d5fb657999906ab375c03b0f4f75289bb4662a1d34143ba99886d", - "gen_hash_verilog": "sha256:ed6c7559eb3bb422722770679c14d41c53a6a56fe3521d444cdfc408b22eb66c", - "gen_hash_zig": "sha256:eecc2a4bc35cf81dff2bde43f15e53903728aa66d5898de7c916e60fefff1c38", + "gen_hash_c": "sha256:683eaa26f084480224ca235b1b3c8fd3b3054e8b40c21ba9cbda756746e1182f", + "gen_hash_rust": "sha256:2e5b5fa8665015b90bf5adc6868fc719b0017e11b94639791a306cbf0bffbf9e", + "gen_hash_verilog": "sha256:a3778e7e0be41b04d8f4e4bcd5a8584394a1a6f78595832b4292a8aaf781373d", + "gen_hash_zig": "sha256:59fe61003bfe57fce363d3b6072112d96309dfe0a3819486afb7e518d80d1352", "module": "t27f_deps_card", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T01:03:00Z", + "sealed_at": "2026-10-19T01:13:38Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:05e744da11ef490c883a7a46c9d5ee7869a1b21f50f1999e6dda0706d68b5f0d", + "spec_hash": "sha256:96b9f08f0229d5408e2b44e9ced7519f68e898a9d3e8bc7a3c055bd72a93c893", "spec_path": "specs/t27f/deps_card.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 11, - "total": 11, + "passed": 17, + "total": 17, "vacuous": 0 }, "toolchain": { diff --git a/specs/t27f/deps_card.t27 b/specs/t27f/deps_card.t27 index f5a262ddda..0738aa9950 100644 --- a/specs/t27f/deps_card.t27 +++ b/specs/t27f/deps_card.t27 @@ -18,10 +18,15 @@ ; tint): every edge is a line t27c resolved. The subtitle reads ON AN IMPORT CYCLE, in the ; derived gold, exactly when deps_on_cycle holds for the focus; ; - an empty band draws its label with 0 and no connector; +; - a shown name of at most CARD_NAME_MAX bytes is drawn byte for byte. A longer one is drawn +; abbreviated, and the abbreviation shows on the card (gHashTag/t27#10728): its first path +; segment, `/...`, and the longest run of whole trailing segments that keeps it within +; CARD_NAME_MAX; when no run of whole segments fits, `...` and its last CARD_NAME_MAX - 3 +; bytes. Only drawn bytes are checked, and the text size follows the drawn length; ; - nothing is drawn (dcard_graph returns 0) for a focus on no line of the edge list, a refused -; walk, or a shown name or font path the graph cannot quote or that does not fit. A name in a -; band past DCARD_ROWS is not drawn, so it is not checked. A graph that does not fit the -; caller's room is refused whole. +; walk, or a drawn byte or font path the graph cannot quote. A name in a band past DCARD_ROWS +; is not drawn, so it is not checked. A graph that does not fit the caller's room is refused +; whole. ; -- ; The drawing helpers and the name rules are why_card's, imported, not copied (duplicate-bodies). ; -- @@ -33,6 +38,8 @@ ; - a band shows DCARD_ROWS specs in edge-list order, not the "most important" ones: the card has ; no measure of importance, and does not pretend to; ; - a `use` line t27c does not resolve is in neither band (deps_unknown, #10702, lists those); +; - two long names can abbreviate to the same text. The abbreviation says a name was cut; it +; does not say which spec it is, and the card does not claim it does; ; - nothing posts the card. Publishing it is the owner's call. ; phi^2 + 1/phi^2 = 3 | TRINITY @@ -72,6 +79,8 @@ pub const DCARD_MORE : str = " more$"; pub const DCARD_REACHES : str = "reaches $"; pub const DCARD_REACHED : str = " / reached by $"; pub const DCARD_FOOT : str = " / every edge exact$"; +pub const DCARD_CUT : str = "/...$"; +pub const DCARD_DOTS : str = "...$"; // ---- geometry ---- @@ -99,6 +108,87 @@ pub fn dcard_name_font(len: usize) -> usize { return f; } +// ---- long names ---- + +/// Where the first path segment of the shown name s[a..b] ends: its first '/', or b. +pub fn dcard_head_end(s: []const u8, a: usize, b: usize) -> usize { + var i : usize = a; + while (i < b) { + if (s[i] == '/') { return i; } + i += 1; + } + return b; +} + +/// Where the kept tail of the shown name s[a..b] begins when it is whole segments: the first '/' +/// past the head from which head, `/...` and the rest fit CARD_NAME_MAX. b when none fits. +pub fn dcard_seg_from(s: []const u8, a: usize, b: usize) -> usize { + const h : usize = dcard_head_end(s, a, b); + const cut : usize = lit_len(DCARD_CUT); + if (h >= b or h - a + cut >= CARD_NAME_MAX) { return b; } + const room : usize = CARD_NAME_MAX - (h - a) - cut; + var j : usize = h + 1; + while (j < b) { + if (s[j] == '/' and b - j <= room) { return j; } + j += 1; + } + return b; +} + +/// Where the byte-cut tail of a shown name ending at b begins. +pub fn dcard_dots_from(b: usize) -> usize { + return b - (CARD_NAME_MAX - lit_len(DCARD_DOTS)); +} + +/// Is every byte of s[x..y] quotable. +pub fn dcard_quotable_span(s: []const u8, x: usize, y: usize) -> bool { + var i : usize = x; + while (i < y) { + if (card_quotable(s[i]) == false) { return false; } + i += 1; + } + return true; +} + +/// How many bytes the shown name of s[f..t] draws: itself up to CARD_NAME_MAX, else its +/// abbreviation. +pub fn dcard_drawn_len(s: []const u8, f: usize, t: usize) -> usize { + const a : usize = card_name_from(s, f, t); + const b : usize = card_name_to(s, a, t); + if (b - a <= CARD_NAME_MAX) { return b - a; } + const j : usize = dcard_seg_from(s, a, b); + if (j < b) { return dcard_head_end(s, a, b) - a + lit_len(DCARD_CUT) + (b - j); } + return CARD_NAME_MAX; +} + +/// Can s[f..t] be drawn: a shown name of at least one byte whose drawn bytes are all quotable. +pub fn dcard_name_ok(s: []const u8, f: usize, t: usize) -> bool { + const a : usize = card_name_from(s, f, t); + const b : usize = card_name_to(s, a, t); + if (b <= a) { return false; } + if (b - a <= CARD_NAME_MAX) { return dcard_quotable_span(s, a, b); } + const j : usize = dcard_seg_from(s, a, b); + if (j < b) { + return dcard_quotable_span(s, a, dcard_head_end(s, a, b)) and dcard_quotable_span(s, j, b); + } + return dcard_quotable_span(s, dcard_dots_from(b), b); +} + +/// Write the drawn form of s[f..t]'s shown name. +pub fn dcard_put_name(out: []u8, w: usize, cap: usize, s: []const u8, f: usize, t: usize) -> usize { + const a : usize = card_name_from(s, f, t); + const b : usize = card_name_to(s, a, t); + if (b - a <= CARD_NAME_MAX) { return deps_put_span(out, w, cap, s, a, b); } + const j : usize = dcard_seg_from(s, a, b); + if (j < b) { + var p : usize = deps_put_span(out, w, cap, s, a, dcard_head_end(s, a, b)); + p = deps_put_lit(out, p, cap, DCARD_CUT); + return deps_put_span(out, p, cap, s, j, b); + } + const q : usize = deps_put_lit(out, w, cap, DCARD_DOTS); + return deps_put_span(out, q, cap, s, dcard_dots_from(b), b); +} + // ---- reading the walks ---- /// Can the first DCARD_ROWS lines of set[lo..hi] be drawn. @@ -107,7 +197,7 @@ pub fn dcard_band_ok(set: []const u8, lo: usize, hi: usize) -> bool { var i : usize = lo; while (i < hi and r < DCARD_ROWS) { const e : usize = line_end(set, i, hi); - if (card_name_ok(set, i, e) == false) { return false; } + if (dcard_name_ok(set, i, e) == false) { return false; } r += 1; i = e + 1; } @@ -132,10 +222,8 @@ pub fn dcard_put_band(out: []u8, w: usize, cap: usize, font: []const u8, fnl: us const y : usize = row_y + r * dcard_row_step(); p = card_put_box(out, p, cap, CARD_BOX_X, y, CARD_BOX_W, DCARD_ROW_H, tint, 0); p = card_put_box(out, p, cap, CARD_BOX_X, y, CARD_BOX_W, DCARD_ROW_H, ok, CARD_BORDER); - const a : usize = card_name_from(set, i, e); - const b : usize = card_name_to(set, a, e); - p = card_text_open(out, p, cap, font, fnl, dcard_name_font(b - a)); - p = deps_put_span(out, p, cap, set, a, b); + p = card_text_open(out, p, cap, font, fnl, dcard_name_font(dcard_drawn_len(set, i, e))); + p = dcard_put_name(out, p, cap, set, i, e); p = card_text_close(out, p, cap, VIEW_TEXT, 0, y + DCARD_ROW_H / 2); r += 1; i = e + 1; @@ -171,7 +259,7 @@ pub fn dcard_graph(buf: []const u8, n: usize, focus: []const u8, ff: usize, ft: if (card_quotable(font[fi]) == false) { return 0; } fi += 1; } - if (card_name_ok(focus, ff, ft) == false) { return 0; } + if (dcard_name_ok(focus, ff, ft) == false) { return 0; } if (deps_walk(buf, n, focus, ff, ft, DEPS_IMPORTS, DEPS_MAX_DEPTH, imp, icap, ib) != DEPS_OK) { return 0; } if (deps_walk(buf, n, focus, ff, ft, DEPS_IMPORTERS, DEPS_MAX_DEPTH, rev, rcap, rb) != DEPS_OK) { return 0; } const isn : usize = ib[deps_depth(ib, DEPS_MAX_DEPTH) + 1]; @@ -210,10 +298,8 @@ pub fn dcard_graph(buf: []const u8, n: usize, focus: []const u8, ff: usize, ft: } w = card_put_box(out, w, cap, CARD_BOX_X, DCARD_FOCUS_Y, CARD_BOX_W, DCARD_FOCUS_H, tint, 0); w = card_put_box(out, w, cap, CARD_BOX_X, DCARD_FOCUS_Y, CARD_BOX_W, DCARD_FOCUS_H, ok, DCARD_FOCUS_BORDER); - const a : usize = card_name_from(focus, ff, ft); - const b : usize = card_name_to(focus, a, ft); - w = card_text_open(out, w, cap, font, fnl, card_font(b - a)); - w = deps_put_span(out, w, cap, focus, a, b); + w = card_text_open(out, w, cap, font, fnl, card_font(dcard_drawn_len(focus, ff, ft))); + w = dcard_put_name(out, w, cap, focus, ff, ft); w = card_text_close(out, w, cap, VIEW_TEXT, 0, DCARD_FOCUS_Y + DCARD_FOCUS_H / 2); if (di > 0) { w = dcard_put_uses(out, w, cap, font, fnl, DCARD_FOCUS_Y + DCARD_FOCUS_H, DCARD_DOWN_ROW_Y); @@ -498,3 +584,113 @@ test negative_control_a_font_path_the_graph_cannot_quote_is_not_drawn { assert dcard_graph(g, lit_len(g), "a$", 0, 1, bad, lit_len(bad), &imp, 256, &ib, &rev, 256, &rb, &out, 8192) == 0; assert dcard_graph(g, lit_len(g), "a$", 0, 1, CARD_TEST_FONT, 0, &imp, 256, &ib, &rev, 256, &rb, &out, 8192) == 0; } + +pub const DCARD_N58 : str = "abcdefghij/abcdefghij/abcdefghij/abcdefghij/abcdefghij/abc$"; +pub const DCARD_LSEG : str = "port/trios/crates/trios-chat/rings/CR-CHAT-07/src/cover_traffic_inter_arrival_autocorrelation_guard$"; +pub const DCARD_LSEG_DRAWN : str = "port/.../cover_traffic_inter_arrival_autocorrelation_guard$"; +pub const DCARD_LMID : str = "port/trios/crates/trios-chat/rings/CR-CHAT-03/src/external_commit$"; +pub const DCARD_LMID_DRAWN : str = "port/.../trios-chat/rings/CR-CHAT-03/src/external_commit$"; +pub const DCARD_LDOTS : str = "port/trios/crates/trios-chat/rings/CR-CHAT-07/src/cover_traffic_inter_destination_timing_uniformity_guard$"; +pub const DCARD_LDOTS_DRAWN : str = "...cover_traffic_inter_destination_timing_uniformity_guard$"; +pub const DCARD_LONG : str = "specs/port/trios/crates/trios-chat/rings/CR-CHAT-07/src/cover_traffic_inter_arrival_autocorrelation_guard.t27\tspecs/port/trios/crates/trios-chat/common/helpers.t27\nspecs/port/trios/crates/trios-chat/rings/CR-CHAT-03/src/external_commit.t27\tspecs/port/trios/crates/trios-chat/common/helpers.t27\n$"; + +test dcard_a_name_that_fits_is_drawn_byte_for_byte { + var out : [256]u8 = undefined; + const s : str = DCARD_N58; + assert lit_len(s) == CARD_NAME_MAX; + assert dcard_drawn_len(s, 0, lit_len(s)) == CARD_NAME_MAX; + const w : usize = dcard_put_name(&out, 0, 256, s, 0, lit_len(s)); + assert w == CARD_NAME_MAX; + assert card_count(&out, w, s) == 1; + assert card_count(&out, w, "...$") == 0; + const p : str = "specs/a/b.t27$"; + const wp : usize = dcard_put_name(&out, 0, 256, p, 0, lit_len(p)); + assert wp == 3; + assert card_count(&out, wp, "a/b$") == 1; +} + +test dcard_a_long_name_keeps_its_head_and_the_longest_whole_tail_that_fits { + var out : [256]u8 = undefined; + const s : str = DCARD_LSEG; + assert lit_len(s) == 99; + const w : usize = dcard_put_name(&out, 0, 256, s, 0, lit_len(s)); + assert w == lit_len(DCARD_LSEG_DRAWN); + assert w == CARD_NAME_MAX; + assert card_count(&out, w, DCARD_LSEG_DRAWN) == 1; + assert dcard_drawn_len(s, 0, lit_len(s)) == w; + const m : str = DCARD_LMID; + const wm : usize = dcard_put_name(&out, 0, 256, m, 0, lit_len(m)); + assert wm == lit_len(DCARD_LMID_DRAWN); + assert wm == 56; + assert card_count(&out, wm, DCARD_LMID_DRAWN) == 1; + assert dcard_drawn_len(m, 0, lit_len(m)) == wm; +} + +test dcard_a_long_name_with_no_whole_tail_that_fits_keeps_its_last_bytes { + var out : [256]u8 = undefined; + const s : str = DCARD_LDOTS; + assert lit_len(s) == 105; + assert dcard_seg_from(s, 0, lit_len(s)) == lit_len(s); + const w : usize = dcard_put_name(&out, 0, 256, s, 0, lit_len(s)); + assert w == CARD_NAME_MAX; + assert card_count(&out, w, DCARD_LDOTS_DRAWN) == 1; + assert dcard_drawn_len(s, 0, lit_len(s)) == w; + const flat : str = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijXYZ$"; + const wf : usize = dcard_put_name(&out, 0, 256, flat, 0, lit_len(flat)); + assert wf == CARD_NAME_MAX; + assert card_count(&out, wf, "...ijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijXYZ$") == 1; +} + +test dcard_a_long_name_is_checked_only_where_it_is_drawn { + const hid : str = "port/tr:os/crates/trios-chat/rings/CR-CHAT-07/src/cover_traffic_inter_arrival_autocorrelation_guard$"; + assert dcard_name_ok(hid, 0, lit_len(hid)); + const hd : str = "po:t/trios/crates/trios-chat/rings/CR-CHAT-07/src/cover_traffic_inter_arrival_autocorrelation_guard$"; + assert dcard_name_ok(hd, 0, lit_len(hd)) == false; + const tl : str = "port/trios/crates/trios-chat/rings/CR-CHAT-07/src/cover_traffic_inter:arrival_autocorrelation_guard$"; + assert dcard_name_ok(tl, 0, lit_len(tl)) == false; + const dh : str = "port/tr:os/crates/trios-chat/rings/CR-CHAT-07/src/cover_traffic_inter_destination_timing_uniformity_guard$"; + assert dcard_name_ok(dh, 0, lit_len(dh)); + const dt : str = "port/trios/crates/trios-chat/rings/CR-CHAT-07/src/cover_traffic_inter_destination:timing_uniformity_guard$"; + assert dcard_name_ok(dt, 0, lit_len(dt)) == false; + assert dcard_name_ok(DCARD_LSEG, 0, lit_len(DCARD_LSEG)); + assert dcard_name_ok(DCARD_LDOTS, 0, lit_len(DCARD_LDOTS)); + const n58 : str = "abcdefghij/abcd:fghij/abcdefghij/abcdefghij/abcdefghij/abc$"; + assert lit_len(n58) == CARD_NAME_MAX; + assert dcard_name_ok(n58, 0, lit_len(n58)) == false; +} + +test dcard_draws_a_card_with_long_names_abbreviated_at_their_drawn_size { + var imp : [512]u8 = undefined; + var rev : [512]u8 = undefined; + var ib : [35]usize = undefined; + var rb : [35]usize = undefined; + var out : [8192]u8 = undefined; + const g : str = DCARD_LONG; + const hp : str = "specs/port/trios/crates/trios-chat/common/helpers.t27$"; + const w : usize = dcard_graph(g, lit_len(g), hp, 0, lit_len(hp), CARD_TEST_FONT, lit_len(CARD_TEST_FONT), &imp, 512, &ib, &rev, 512, &rb, &out, 8192); + assert w > 0; + assert w <= 8192; + assert card_count(&out, w, "fontsize=32:text='port/trios/crates/trios-chat/common/helpers'$") == 1; + assert card_count(&out, w, "fontsize=24:text='port/.../cover_traffic_inter_arrival_autocorrelation_guard'$") == 1; + assert card_count(&out, w, "fontsize=25:text='port/.../trios-chat/rings/CR-CHAT-03/src/external_commit'$") == 1; + assert card_count(&out, w, "/.../$") == 2; + assert card_count(&out, w, "text='IMPORTED BY 2'$") == 1; + const fp : str = "specs/port/trios/crates/trios-chat/rings/CR-CHAT-07/src/cover_traffic_inter_arrival_autocorrelation_guard.t27$"; + const wf : usize = dcard_graph(g, lit_len(g), fp, 0, lit_len(fp), CARD_TEST_FONT, lit_len(CARD_TEST_FONT), &imp, 512, &ib, &rev, 512, &rb, &out, 8192); + assert wf > 0; + assert card_count(&out, wf, "fontsize=24:text='port/.../cover_traffic_inter_arrival_autocorrelation_guard'$") == 1; + assert card_count(&out, wf, "text='port/trios/crates/trios-chat/common/helpers'$") == 1; + assert card_count(&out, wf, "text='IMPORTS 1'$") == 1; +} + +test negative_control_a_long_name_whose_drawn_bytes_cannot_be_quoted_is_not_drawn { + var imp : [512]u8 = undefined; + var rev : [512]u8 = undefined; + var ib : [35]usize = undefined; + var rb : [35]usize = undefined; + var out : [8192]u8 = undefined; + const g : str = "port/trios/crates/trios-chat/rings/CR-CHAT-07/src/cover_traffic_inter:arrival_autocorrelation_guard\tx\n$"; + assert dcard_graph(g, lit_len(g), "x$", 0, 1, CARD_TEST_FONT, lit_len(CARD_TEST_FONT), &imp, 512, &ib, &rev, 512, &rb, &out, 8192) == 0; + const h : str = "port/tr:os/crates/trios-chat/rings/CR-CHAT-07/src/cover_traffic_inter_arrival_autocorrelation_guard\tx\n$"; + assert dcard_graph(h, lit_len(h), "x$", 0, 1, CARD_TEST_FONT, lit_len(CARD_TEST_FONT), &imp, 512, &ib, &rev, 512, &rb, &out, 8192) > 0; +}