From c914a2b1b422ddb8b851dac74ea6071eb8ddc74f Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 19 Jun 2026 17:16:25 +0000 Subject: [PATCH 1/6] WIP: Rework render/glyphs modeling in a child PR (follow-up to merged #5281, --- dsl/extdeps/render/glyphs.dag | 87 +++++++++++++++++------------------ dsl/std/symbols.dag | 17 +++++-- 2 files changed, 54 insertions(+), 50 deletions(-) diff --git a/dsl/extdeps/render/glyphs.dag b/dsl/extdeps/render/glyphs.dag index 0e01385ef5c..673dd9214cc 100644 --- a/dsl/extdeps/render/glyphs.dag +++ b/dsl/extdeps/render/glyphs.dag @@ -16,60 +16,60 @@ import std.symbols { SemanticColor, SymbolEntry, SymbolId, Tier } data standard_symbols: List = [ // Node lifecycle // NOTE: emoji/unicode tiers collapsed to ASCII -- restore when tokenizer handles UTF-8 - { id: NodePending, emoji: "[ ]", unicode: "o", ascii: "[ ]", color: Dim }, - { id: NodeRunning, emoji: "[~]", unicode: "O", ascii: "[~]", color: Active }, - { id: NodeCompleted, emoji: "[x]", unicode: "*", ascii: "[x]", color: Success }, - { id: NodeFailed, emoji: "[!]", unicode: "x", ascii: "[!]", color: Error }, - { id: NodeSkipped, emoji: "[-]", unicode: "o", ascii: "[-]", color: Dim }, - { id: NodeIntercepted, emoji: "[m]", unicode: "<>", ascii: "[m]", color: Info }, + { id: NodePending, glyphs: { Emoji: "[ ]", Unicode: "o", Ascii: "[ ]" }, color: Dim }, + { id: NodeRunning, glyphs: { Emoji: "[~]", Unicode: "O", Ascii: "[~]" }, color: Active }, + { id: NodeCompleted, glyphs: { Emoji: "[x]", Unicode: "*", Ascii: "[x]" }, color: Success }, + { id: NodeFailed, glyphs: { Emoji: "[!]", Unicode: "x", Ascii: "[!]" }, color: Error }, + { id: NodeSkipped, glyphs: { Emoji: "[-]", Unicode: "o", Ascii: "[-]" }, color: Dim }, + { id: NodeIntercepted, glyphs: { Emoji: "[m]", Unicode: "<>", Ascii: "[m]" }, color: Info }, // Edge states - { id: EdgeIdle, emoji: "-", unicode: "-", ascii: "-", color: Dim }, - { id: EdgeFlowing, emoji: "=", unicode: "=", ascii: "=", color: Accent }, - { id: EdgeDone, emoji: "-", unicode: "-", ascii: "-", color: Success }, - { id: EdgeDead, emoji: ".", unicode: ".", ascii: ".", color: Dim }, + { id: EdgeIdle, glyphs: { Emoji: "-", Unicode: "-", Ascii: "-" }, color: Dim }, + { id: EdgeFlowing, glyphs: { Emoji: "=", Unicode: "=", Ascii: "=" }, color: Accent }, + { id: EdgeDone, glyphs: { Emoji: "-", Unicode: "-", Ascii: "-" }, color: Success }, + { id: EdgeDead, glyphs: { Emoji: ".", Unicode: ".", Ascii: "." }, color: Dim }, // DAG phases - { id: DagNotStarted, emoji: "[ ]", unicode: "[ ]", ascii: "[ ]", color: Dim }, - { id: DagRunning, emoji: "[>]", unicode: ">", ascii: "[>]", color: Active }, - { id: DagCompleted, emoji: "[X]", unicode: "#", ascii: "[X]", color: Success }, - { id: DagFailed, emoji: "[!]", unicode: "#", ascii: "[!]", color: Error }, + { id: DagNotStarted, glyphs: { Emoji: "[ ]", Unicode: "[ ]", Ascii: "[ ]" }, color: Dim }, + { id: DagRunning, glyphs: { Emoji: "[>]", Unicode: ">", Ascii: "[>]" }, color: Active }, + { id: DagCompleted, glyphs: { Emoji: "[X]", Unicode: "#", Ascii: "[X]" }, color: Success }, + { id: DagFailed, glyphs: { Emoji: "[!]", Unicode: "#", Ascii: "[!]" }, color: Error }, // Structural - { id: BoundaryMarker, emoji: "[B]", unicode: "<>", ascii: "[B]", color: Info }, + { id: BoundaryMarker, glyphs: { Emoji: "[B]", Unicode: "<>", Ascii: "[B]" }, color: Info }, // Spinners (ASCII fallback -- restore braille when tokenizer handles UTF-8) - { id: Spinner0, emoji: "|", unicode: "|", ascii: "|", color: Active }, - { id: Spinner1, emoji: "/", unicode: "/", ascii: "/", color: Active }, - { id: Spinner2, emoji: "-", unicode: "-", ascii: "-", color: Active }, - { id: Spinner3, emoji: "\\", unicode: "\\", ascii: "\\", color: Active }, - { id: Spinner4, emoji: "|", unicode: "|", ascii: "|", color: Active }, - { id: Spinner5, emoji: "/", unicode: "/", ascii: "/", color: Active }, - { id: Spinner6, emoji: "-", unicode: "-", ascii: "-", color: Active }, - { id: Spinner7, emoji: "\\", unicode: "\\", ascii: "\\", color: Active }, - { id: Spinner8, emoji: "|", unicode: "|", ascii: "|", color: Active }, - { id: Spinner9, emoji: "/", unicode: "/", ascii: "/", color: Active }, + { id: Spinner0, glyphs: { Emoji: "|", Unicode: "|", Ascii: "|" }, color: Active }, + { id: Spinner1, glyphs: { Emoji: "/", Unicode: "/", Ascii: "/" }, color: Active }, + { id: Spinner2, glyphs: { Emoji: "-", Unicode: "-", Ascii: "-" }, color: Active }, + { id: Spinner3, glyphs: { Emoji: "\\", Unicode: "\\", Ascii: "\\" }, color: Active }, + { id: Spinner4, glyphs: { Emoji: "|", Unicode: "|", Ascii: "|" }, color: Active }, + { id: Spinner5, glyphs: { Emoji: "/", Unicode: "/", Ascii: "/" }, color: Active }, + { id: Spinner6, glyphs: { Emoji: "-", Unicode: "-", Ascii: "-" }, color: Active }, + { id: Spinner7, glyphs: { Emoji: "\\", Unicode: "\\", Ascii: "\\" }, color: Active }, + { id: Spinner8, glyphs: { Emoji: "|", Unicode: "|", Ascii: "|" }, color: Active }, + { id: Spinner9, glyphs: { Emoji: "/", Unicode: "/", Ascii: "/" }, color: Active }, // Status indicators - { id: StatusSuccess, emoji: "OK", unicode: "v", ascii: "OK", color: Success }, - { id: StatusFailure, emoji: "FAIL", unicode: "x", ascii: "FAIL", color: Error }, - { id: StatusWarning, emoji: "WARN", unicode: "!", ascii: "WARN", color: Warning }, - { id: StatusInfo, emoji: "INFO", unicode: "i", ascii: "INFO", color: Info }, + { id: StatusSuccess, glyphs: { Emoji: "OK", Unicode: "v", Ascii: "OK" }, color: Success }, + { id: StatusFailure, glyphs: { Emoji: "FAIL", Unicode: "x", Ascii: "FAIL" }, color: Error }, + { id: StatusWarning, glyphs: { Emoji: "WARN", Unicode: "!", Ascii: "WARN" }, color: Warning }, + { id: StatusInfo, glyphs: { Emoji: "INFO", Unicode: "i", Ascii: "INFO" }, color: Info }, // Data type indicators - { id: DataList, emoji: "[L]", unicode: "=", ascii: "[L]", color: Default }, - { id: DataMap, emoji: "[M]", unicode: "[+]", ascii: "[M]", color: Default }, - { id: DataSecret, emoji: "[*]", unicode: "*", ascii: "[*]", color: Warning }, - { id: DataUrl, emoji: "[U]", unicode: ">", ascii: "[U]", color: Info }, - { id: DataTimer, emoji: "[T]", unicode: "[T]", ascii: "[T]", color: Dim }, + { id: DataList, glyphs: { Emoji: "[L]", Unicode: "=", Ascii: "[L]" }, color: Default }, + { id: DataMap, glyphs: { Emoji: "[M]", Unicode: "[+]", Ascii: "[M]" }, color: Default }, + { id: DataSecret, glyphs: { Emoji: "[*]", Unicode: "*", Ascii: "[*]" }, color: Warning }, + { id: DataUrl, glyphs: { Emoji: "[U]", Unicode: ">", Ascii: "[U]" }, color: Info }, + { id: DataTimer, glyphs: { Emoji: "[T]", Unicode: "[T]", Ascii: "[T]" }, color: Dim }, // Connectors - { id: ConnectorHorizontal, emoji: "--", unicode: "--", ascii: "--", color: Dim }, - { id: ConnectorVertical, emoji: "|", unicode: "|", ascii: "|", color: Dim }, - { id: ConnectorTeeDown, emoji: "+", unicode: "+", ascii: "+", color: Dim }, - { id: ConnectorTeeUp, emoji: "+", unicode: "+", ascii: "+", color: Dim }, - { id: ConnectorCornerBottomLeft, emoji: "+", unicode: "+", ascii: "+", color: Dim }, - { id: ConnectorCornerTopLeft, emoji: "+", unicode: "+", ascii: "+", color: Dim } + { id: ConnectorHorizontal, glyphs: { Emoji: "--", Unicode: "--", Ascii: "--" }, color: Dim }, + { id: ConnectorVertical, glyphs: { Emoji: "|", Unicode: "|", Ascii: "|" }, color: Dim }, + { id: ConnectorTeeDown, glyphs: { Emoji: "+", Unicode: "+", Ascii: "+" }, color: Dim }, + { id: ConnectorTeeUp, glyphs: { Emoji: "+", Unicode: "+", Ascii: "+" }, color: Dim }, + { id: ConnectorCornerBottomLeft, glyphs: { Emoji: "+", Unicode: "+", Ascii: "+" }, color: Dim }, + { id: ConnectorCornerTopLeft, glyphs: { Emoji: "+", Unicode: "+", Ascii: "+" }, color: Dim } ] // Lookup via fold + structural == (filter+field-equality hits a v1 interpreter bug). @@ -90,12 +90,7 @@ fn lookup_symbol_entry(id: SymbolId) -> SymbolEntry? { fn resolve_symbol(id: SymbolId, tier: Tier) -> String? { match lookup_symbol_entry(id: id) { - Present { value: entry } => - match tier { - Emoji => Present { value: entry.emoji } - Unicode => Present { value: entry.unicode } - Ascii => Present { value: entry.ascii } - } + Present { value: entry } => map_get(entry.glyphs, tier) Absent => none } } diff --git a/dsl/std/symbols.dag b/dsl/std/symbols.dag index 257ef749a7d..1d312d886c3 100644 --- a/dsl/std/symbols.dag +++ b/dsl/std/symbols.dag @@ -85,15 +85,24 @@ type SymbolId | ConnectorCornerBottomLeft | ConnectorCornerTopLeft +// --- Tier glyph --- +// +// One encoding-tier realization: the closed Tier enum keys the glyph text. +// Rows compose into SymbolEntry.glyphs; cited strings live in extdeps/render/glyphs.dag. + +type TierGlyph { + tier: Tier + text: String +} + // --- Symbol entry --- // -// A single symbol with all three tier encodings and a semantic color. +// A single symbol: identity, tier-keyed glyph encodings, and semantic color. +// Glyphs are keyed by the closed Tier enum — not parallel nickname columns. // The cited realization rows live in extdeps/render/glyphs.dag. type SymbolEntry { id: SymbolId - emoji: String - unicode: String - ascii: String + glyphs: List color: SemanticColor } From 0527516acd11e663059f249b7483a6a51f1ad366 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 19 Jun 2026 17:17:31 +0000 Subject: [PATCH 2/6] fix(render): use List for tier-keyed glyph encodings Map literal syntax parses as Record in the v1 interpreter (raw_map_lookup fails at runtime). SymbolEntry.glyphs is List keyed by Tier; resolve_glyph folds the list instead of a 3-way field match or map_get. render_glyphs_ansi_witnesses --claim-run is green. Co-authored-by: Cursor --- dsl/extdeps/render/glyphs.dag | 108 +++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 42 deletions(-) diff --git a/dsl/extdeps/render/glyphs.dag b/dsl/extdeps/render/glyphs.dag index 673dd9214cc..948931e370c 100644 --- a/dsl/extdeps/render/glyphs.dag +++ b/dsl/extdeps/render/glyphs.dag @@ -6,7 +6,16 @@ module extdeps.render.glyphs -import std.symbols { SemanticColor, SymbolEntry, SymbolId, Tier } +import std.symbols { SemanticColor, SymbolEntry, SymbolId, Tier, TierGlyph } + +// Data-authoring helper: binds each Tier variant to its glyph text in one place. +fn glyphs_for_tiers(emoji: String, unicode: String, ascii: String) -> List { + [ + { tier: Emoji, text: emoji }, + { tier: Unicode, text: unicode }, + { tier: Ascii, text: ascii } + ] +} // --- Standard symbol set --- // @@ -16,60 +25,60 @@ import std.symbols { SemanticColor, SymbolEntry, SymbolId, Tier } data standard_symbols: List = [ // Node lifecycle // NOTE: emoji/unicode tiers collapsed to ASCII -- restore when tokenizer handles UTF-8 - { id: NodePending, glyphs: { Emoji: "[ ]", Unicode: "o", Ascii: "[ ]" }, color: Dim }, - { id: NodeRunning, glyphs: { Emoji: "[~]", Unicode: "O", Ascii: "[~]" }, color: Active }, - { id: NodeCompleted, glyphs: { Emoji: "[x]", Unicode: "*", Ascii: "[x]" }, color: Success }, - { id: NodeFailed, glyphs: { Emoji: "[!]", Unicode: "x", Ascii: "[!]" }, color: Error }, - { id: NodeSkipped, glyphs: { Emoji: "[-]", Unicode: "o", Ascii: "[-]" }, color: Dim }, - { id: NodeIntercepted, glyphs: { Emoji: "[m]", Unicode: "<>", Ascii: "[m]" }, color: Info }, + { id: NodePending, glyphs: glyphs_for_tiers(emoji: "[ ]", unicode: "o", ascii: "[ ]"), color: Dim }, + { id: NodeRunning, glyphs: glyphs_for_tiers(emoji: "[~]", unicode: "O", ascii: "[~]"), color: Active }, + { id: NodeCompleted, glyphs: glyphs_for_tiers(emoji: "[x]", unicode: "*", ascii: "[x]"), color: Success }, + { id: NodeFailed, glyphs: glyphs_for_tiers(emoji: "[!]", unicode: "x", ascii: "[!]"), color: Error }, + { id: NodeSkipped, glyphs: glyphs_for_tiers(emoji: "[-]", unicode: "o", ascii: "[-]"), color: Dim }, + { id: NodeIntercepted, glyphs: glyphs_for_tiers(emoji: "[m]", unicode: "<>", ascii: "[m]"), color: Info }, // Edge states - { id: EdgeIdle, glyphs: { Emoji: "-", Unicode: "-", Ascii: "-" }, color: Dim }, - { id: EdgeFlowing, glyphs: { Emoji: "=", Unicode: "=", Ascii: "=" }, color: Accent }, - { id: EdgeDone, glyphs: { Emoji: "-", Unicode: "-", Ascii: "-" }, color: Success }, - { id: EdgeDead, glyphs: { Emoji: ".", Unicode: ".", Ascii: "." }, color: Dim }, + { id: EdgeIdle, glyphs: glyphs_for_tiers(emoji: "-", unicode: "-", ascii: "-"), color: Dim }, + { id: EdgeFlowing, glyphs: glyphs_for_tiers(emoji: "=", unicode: "=", ascii: "="), color: Accent }, + { id: EdgeDone, glyphs: glyphs_for_tiers(emoji: "-", unicode: "-", ascii: "-"), color: Success }, + { id: EdgeDead, glyphs: glyphs_for_tiers(emoji: ".", unicode: ".", ascii: "."), color: Dim }, // DAG phases - { id: DagNotStarted, glyphs: { Emoji: "[ ]", Unicode: "[ ]", Ascii: "[ ]" }, color: Dim }, - { id: DagRunning, glyphs: { Emoji: "[>]", Unicode: ">", Ascii: "[>]" }, color: Active }, - { id: DagCompleted, glyphs: { Emoji: "[X]", Unicode: "#", Ascii: "[X]" }, color: Success }, - { id: DagFailed, glyphs: { Emoji: "[!]", Unicode: "#", Ascii: "[!]" }, color: Error }, + { id: DagNotStarted, glyphs: glyphs_for_tiers(emoji: "[ ]", unicode: "[ ]", ascii: "[ ]"), color: Dim }, + { id: DagRunning, glyphs: glyphs_for_tiers(emoji: "[>]", unicode: ">", ascii: "[>]"), color: Active }, + { id: DagCompleted, glyphs: glyphs_for_tiers(emoji: "[X]", unicode: "#", ascii: "[X]"), color: Success }, + { id: DagFailed, glyphs: glyphs_for_tiers(emoji: "[!]", unicode: "#", ascii: "[!]"), color: Error }, // Structural - { id: BoundaryMarker, glyphs: { Emoji: "[B]", Unicode: "<>", Ascii: "[B]" }, color: Info }, + { id: BoundaryMarker, glyphs: glyphs_for_tiers(emoji: "[B]", unicode: "<>", ascii: "[B]"), color: Info }, // Spinners (ASCII fallback -- restore braille when tokenizer handles UTF-8) - { id: Spinner0, glyphs: { Emoji: "|", Unicode: "|", Ascii: "|" }, color: Active }, - { id: Spinner1, glyphs: { Emoji: "/", Unicode: "/", Ascii: "/" }, color: Active }, - { id: Spinner2, glyphs: { Emoji: "-", Unicode: "-", Ascii: "-" }, color: Active }, - { id: Spinner3, glyphs: { Emoji: "\\", Unicode: "\\", Ascii: "\\" }, color: Active }, - { id: Spinner4, glyphs: { Emoji: "|", Unicode: "|", Ascii: "|" }, color: Active }, - { id: Spinner5, glyphs: { Emoji: "/", Unicode: "/", Ascii: "/" }, color: Active }, - { id: Spinner6, glyphs: { Emoji: "-", Unicode: "-", Ascii: "-" }, color: Active }, - { id: Spinner7, glyphs: { Emoji: "\\", Unicode: "\\", Ascii: "\\" }, color: Active }, - { id: Spinner8, glyphs: { Emoji: "|", Unicode: "|", Ascii: "|" }, color: Active }, - { id: Spinner9, glyphs: { Emoji: "/", Unicode: "/", Ascii: "/" }, color: Active }, + { id: Spinner0, glyphs: glyphs_for_tiers(emoji: "|", unicode: "|", ascii: "|"), color: Active }, + { id: Spinner1, glyphs: glyphs_for_tiers(emoji: "/", unicode: "/", ascii: "/"), color: Active }, + { id: Spinner2, glyphs: glyphs_for_tiers(emoji: "-", unicode: "-", ascii: "-"), color: Active }, + { id: Spinner3, glyphs: glyphs_for_tiers(emoji: "\\", unicode: "\\", ascii: "\\"), color: Active }, + { id: Spinner4, glyphs: glyphs_for_tiers(emoji: "|", unicode: "|", ascii: "|"), color: Active }, + { id: Spinner5, glyphs: glyphs_for_tiers(emoji: "/", unicode: "/", ascii: "/"), color: Active }, + { id: Spinner6, glyphs: glyphs_for_tiers(emoji: "-", unicode: "-", ascii: "-"), color: Active }, + { id: Spinner7, glyphs: glyphs_for_tiers(emoji: "\\", unicode: "\\", ascii: "\\"), color: Active }, + { id: Spinner8, glyphs: glyphs_for_tiers(emoji: "|", unicode: "|", ascii: "|"), color: Active }, + { id: Spinner9, glyphs: glyphs_for_tiers(emoji: "/", unicode: "/", ascii: "/"), color: Active }, // Status indicators - { id: StatusSuccess, glyphs: { Emoji: "OK", Unicode: "v", Ascii: "OK" }, color: Success }, - { id: StatusFailure, glyphs: { Emoji: "FAIL", Unicode: "x", Ascii: "FAIL" }, color: Error }, - { id: StatusWarning, glyphs: { Emoji: "WARN", Unicode: "!", Ascii: "WARN" }, color: Warning }, - { id: StatusInfo, glyphs: { Emoji: "INFO", Unicode: "i", Ascii: "INFO" }, color: Info }, + { id: StatusSuccess, glyphs: glyphs_for_tiers(emoji: "OK", unicode: "v", ascii: "OK"), color: Success }, + { id: StatusFailure, glyphs: glyphs_for_tiers(emoji: "FAIL", unicode: "x", ascii: "FAIL"), color: Error }, + { id: StatusWarning, glyphs: glyphs_for_tiers(emoji: "WARN", unicode: "!", ascii: "WARN"), color: Warning }, + { id: StatusInfo, glyphs: glyphs_for_tiers(emoji: "INFO", unicode: "i", ascii: "INFO"), color: Info }, // Data type indicators - { id: DataList, glyphs: { Emoji: "[L]", Unicode: "=", Ascii: "[L]" }, color: Default }, - { id: DataMap, glyphs: { Emoji: "[M]", Unicode: "[+]", Ascii: "[M]" }, color: Default }, - { id: DataSecret, glyphs: { Emoji: "[*]", Unicode: "*", Ascii: "[*]" }, color: Warning }, - { id: DataUrl, glyphs: { Emoji: "[U]", Unicode: ">", Ascii: "[U]" }, color: Info }, - { id: DataTimer, glyphs: { Emoji: "[T]", Unicode: "[T]", Ascii: "[T]" }, color: Dim }, + { id: DataList, glyphs: glyphs_for_tiers(emoji: "[L]", unicode: "=", ascii: "[L]"), color: Default }, + { id: DataMap, glyphs: glyphs_for_tiers(emoji: "[M]", unicode: "[+]", ascii: "[M]"), color: Default }, + { id: DataSecret, glyphs: glyphs_for_tiers(emoji: "[*]", unicode: "*", ascii: "[*]"), color: Warning }, + { id: DataUrl, glyphs: glyphs_for_tiers(emoji: "[U]", unicode: ">", ascii: "[U]"), color: Info }, + { id: DataTimer, glyphs: glyphs_for_tiers(emoji: "[T]", unicode: "[T]", ascii: "[T]"), color: Dim }, // Connectors - { id: ConnectorHorizontal, glyphs: { Emoji: "--", Unicode: "--", Ascii: "--" }, color: Dim }, - { id: ConnectorVertical, glyphs: { Emoji: "|", Unicode: "|", Ascii: "|" }, color: Dim }, - { id: ConnectorTeeDown, glyphs: { Emoji: "+", Unicode: "+", Ascii: "+" }, color: Dim }, - { id: ConnectorTeeUp, glyphs: { Emoji: "+", Unicode: "+", Ascii: "+" }, color: Dim }, - { id: ConnectorCornerBottomLeft, glyphs: { Emoji: "+", Unicode: "+", Ascii: "+" }, color: Dim }, - { id: ConnectorCornerTopLeft, glyphs: { Emoji: "+", Unicode: "+", Ascii: "+" }, color: Dim } + { id: ConnectorHorizontal, glyphs: glyphs_for_tiers(emoji: "--", unicode: "--", ascii: "--"), color: Dim }, + { id: ConnectorVertical, glyphs: glyphs_for_tiers(emoji: "|", unicode: "|", ascii: "|"), color: Dim }, + { id: ConnectorTeeDown, glyphs: glyphs_for_tiers(emoji: "+", unicode: "+", ascii: "+"), color: Dim }, + { id: ConnectorTeeUp, glyphs: glyphs_for_tiers(emoji: "+", unicode: "+", ascii: "+"), color: Dim }, + { id: ConnectorCornerBottomLeft, glyphs: glyphs_for_tiers(emoji: "+", unicode: "+", ascii: "+"), color: Dim }, + { id: ConnectorCornerTopLeft, glyphs: glyphs_for_tiers(emoji: "+", unicode: "+", ascii: "+"), color: Dim } ] // Lookup via fold + structural == (filter+field-equality hits a v1 interpreter bug). @@ -88,9 +97,24 @@ fn lookup_symbol_entry(id: SymbolId) -> SymbolEntry? { ) } +fn resolve_glyph(glyphs: List, tier: Tier) -> String? { + fold(glyphs, + init: none, + f: (acc, g) => match acc { + Present { value: _ } => acc + Absent => + if g.tier == tier { + Present { value: g.text } + } else { + none + } + } + ) +} + fn resolve_symbol(id: SymbolId, tier: Tier) -> String? { match lookup_symbol_entry(id: id) { - Present { value: entry } => map_get(entry.glyphs, tier) + Present { value: entry } => resolve_glyph(glyphs: entry.glyphs, tier: tier) Absent => none } } From 5589c3d5396db0543638f93e79527a3a73cee330 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 19 Jun 2026 17:21:19 +0000 Subject: [PATCH 3/6] render: anchors (step 3), std/extdeps confirm (2), SGR decomposition (4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - glyphs.dag: Unicode latest + U+2500/U+2800/UTS #51 repertoire cite - terminal.dag: UAX #11 East Asian Width anchor (sibling to std.unicode) - ansi.dag: ECMA-48 anchor (replaces Wikipedia); fermi/colors.go provenance - ansi.dag: decompose AnsiMapping into SgrStyle + PaletteIndex; CSI framing via emit_sgr_csi (csi_esc/csi_end constants, not per-row literals) - std/symbols: Tier comment — project interface, not external authority render_glyphs_ansi_witnesses --claim-run green. Co-authored-by: Cursor --- dsl/extdeps/render/ansi.dag | 55 +++++++++++++++++++++++---------- dsl/extdeps/render/glyphs.dag | 3 ++ dsl/extdeps/render/terminal.dag | 2 ++ dsl/std/symbols.dag | 5 +-- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/dsl/extdeps/render/ansi.dag b/dsl/extdeps/render/ansi.dag index 12a4a2ddd5c..d52312169de 100644 --- a/dsl/extdeps/render/ansi.dag +++ b/dsl/extdeps/render/ansi.dag @@ -1,36 +1,57 @@ // extdeps/render/ansi.dag — ANSI escape / SGR color realization for SemanticColor. // -// Anchor: https://en.wikipedia.org/wiki/ANSI_escape_code +// Anchor: https://ecma-international.org/publications-and-standards/standards/ecma-48/ +// (ISO/IEC 6429 Select Graphic Rendition CSI sequences: ESC [ m) // -// Upstream: ECMA-48 (ISO/IEC 6429) / ANSI X3.64 — control sequences for character -// imputation. The cited escape strings are Select Graphic Rendition (SGR) CSI -// sequences (`ESC [ … m`); 256-color foreground uses `38;5;` (ISO 8613-6 -// indexed palette). Real spec names kept in the `code` field. +// Provenance: gunb.ai/pkg/fermi/colors.go — project palette-index choices per SemanticColor. // // P2 split: std.symbols owns the agnostic SemanticColor concept; the cited SGR -// escape sequences live here. Project palette index choices per semantic role -// align with gunb.ai/pkg/fermi/colors.go for consistent appearance. +// realization lives here. module extdeps.render.ansi import std.symbols { SemanticColor } +// ECMA-48 CSI framing — emitted once via emit_sgr_csi, not repeated per mapping row. +data csi_esc: String = "\x1b[" +data csi_end: String = "m" + +// ISO 8613-6 / xterm 256-color palette index (0..255). +type PaletteIndex = Int + +type SgrStyle + = SgrReset {} + | SgrDim {} + | SgrIndexedForeground { index: PaletteIndex } + type AnsiMapping { color: SemanticColor - code: String + style: SgrStyle } data ansi_mappings: List = [ - { color: Default, code: "\x1b[0m" }, - { color: Success, code: "\x1b[38;5;34m" }, - { color: Warning, code: "\x1b[38;5;208m" }, - { color: Error, code: "\x1b[38;5;196m" }, - { color: Info, code: "\x1b[38;5;39m" }, - { color: Dim, code: "\x1b[2m" }, - { color: Active, code: "\x1b[38;5;208m" }, - { color: Accent, code: "\x1b[38;5;75m" } + { color: Default, style: SgrReset {} }, + { color: Success, style: SgrIndexedForeground { index: 34 } }, + { color: Warning, style: SgrIndexedForeground { index: 208 } }, + { color: Error, style: SgrIndexedForeground { index: 196 } }, + { color: Info, style: SgrIndexedForeground { index: 39 } }, + { color: Dim, style: SgrDim {} }, + { color: Active, style: SgrIndexedForeground { index: 208 } }, + { color: Accent, style: SgrIndexedForeground { index: 75 } } ] +fn format_sgr_params(style: SgrStyle) -> String { + match style { + SgrReset {} => "0" + SgrDim {} => "2" + SgrIndexedForeground { index: n } => concat("38;5;", to_string(n)) + } +} + +fn emit_sgr_csi(style: SgrStyle) -> String { + concat(concat(csi_esc, format_sgr_params(style: style)), csi_end) +} + // Lookup via fold + structural == (filter+field-equality hits a v1 interpreter bug). fn ansi_code(c: SemanticColor) -> String? { fold(ansi_mappings, @@ -39,7 +60,7 @@ fn ansi_code(c: SemanticColor) -> String? { Present { value: _ } => acc Absent => if m.color == c { - Present { value: m.code } + Present { value: emit_sgr_csi(style: m.style) } } else { none } diff --git a/dsl/extdeps/render/glyphs.dag b/dsl/extdeps/render/glyphs.dag index 948931e370c..b20460c7367 100644 --- a/dsl/extdeps/render/glyphs.dag +++ b/dsl/extdeps/render/glyphs.dag @@ -1,5 +1,8 @@ // extdeps/render/glyphs.dag — Glyph realization table + encoding-tier dispatch. // +// Anchor: https://www.unicode.org/versions/latest/ +// Repertoire cited: U+2500 Box Drawing, U+2800 Braille Patterns; emoji tier per UTS #51. +// // Project visual vocabulary: three-tier encodings (Emoji / Unicode / Ascii) per // SymbolId. Dispatch lives in extdeps/, not std/ (DESIGN.md §3): std.symbols // owns only the agnostic glyph identity and SymbolEntry shape. diff --git a/dsl/extdeps/render/terminal.dag b/dsl/extdeps/render/terminal.dag index 97028504c92..6efafe8b1ef 100644 --- a/dsl/extdeps/render/terminal.dag +++ b/dsl/extdeps/render/terminal.dag @@ -1,5 +1,7 @@ // extdeps/render/terminal.dag — Terminal width composition over glyph realization. // +// Anchor: https://www.unicode.org/reports/tr11/ (UAX #11 East Asian Width; sibling to std.unicode) +// // Width-constrained frame/line composition depends on resolve_symbol (encoding-tier // dispatch in extdeps.render.glyphs). Lives in extdeps so std.render stays // realization-free (DESIGN.md §3). diff --git a/dsl/std/symbols.dag b/dsl/std/symbols.dag index 1d312d886c3..ba1d89fac18 100644 --- a/dsl/std/symbols.dag +++ b/dsl/std/symbols.dag @@ -23,8 +23,9 @@ type SemanticColor = Default | Success | Warning | Error | Info | Dim | Active | // --- Encoding tier --- // -// Ordered from richest to most compatible: -// Emoji > Unicode > Ascii +// Project interface: ordered richness (Emoji > Unicode > Ascii). +// Not an external authority — tier dispatch contract lives in std; cited +// repertoire per tier is anchored in extdeps/render/glyphs.dag. type Tier = Emoji | Unicode | Ascii From 1ffed344fcb7843c8653b2119c24517eb3a36db2 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 19 Jun 2026 17:32:25 +0000 Subject: [PATCH 4/6] fix(symbols): TierGlyphs total product replaces List MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address opus REQUEST_CHANGES: List made missing/duplicate tier rows representable and introduced a spurious Absent path in tier lookup. SymbolEntry.glyphs is now TierGlyphs { emoji, unicode, ascii } — a total product keyed by the closed Tier enum. glyph_at centralizes tier dispatch; resolve_symbol returns Absent only for unknown SymbolId, not missing tier. render_glyphs_ansi_witnesses --claim-run green. Co-authored-by: Cursor --- dsl/extdeps/render/glyphs.dag | 35 ++++++++++++----------------------- dsl/std/symbols.dag | 18 ++++++++++-------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/dsl/extdeps/render/glyphs.dag b/dsl/extdeps/render/glyphs.dag index b20460c7367..8bcaa65cb2b 100644 --- a/dsl/extdeps/render/glyphs.dag +++ b/dsl/extdeps/render/glyphs.dag @@ -9,15 +9,11 @@ module extdeps.render.glyphs -import std.symbols { SemanticColor, SymbolEntry, SymbolId, Tier, TierGlyph } - -// Data-authoring helper: binds each Tier variant to its glyph text in one place. -fn glyphs_for_tiers(emoji: String, unicode: String, ascii: String) -> List { - [ - { tier: Emoji, text: emoji }, - { tier: Unicode, text: unicode }, - { tier: Ascii, text: ascii } - ] +import std.symbols { SemanticColor, SymbolEntry, SymbolId, Tier, TierGlyphs } + +// Data-authoring helper: total Tier→String product (one field per closed variant). +fn glyphs_for_tiers(emoji: String, unicode: String, ascii: String) -> TierGlyphs { + { emoji: emoji, unicode: unicode, ascii: ascii } } // --- Standard symbol set --- @@ -100,24 +96,17 @@ fn lookup_symbol_entry(id: SymbolId) -> SymbolEntry? { ) } -fn resolve_glyph(glyphs: List, tier: Tier) -> String? { - fold(glyphs, - init: none, - f: (acc, g) => match acc { - Present { value: _ } => acc - Absent => - if g.tier == tier { - Present { value: g.text } - } else { - none - } - } - ) +fn glyph_at(glyphs: TierGlyphs, tier: Tier) -> String { + match tier { + Emoji => glyphs.emoji + Unicode => glyphs.unicode + Ascii => glyphs.ascii + } } fn resolve_symbol(id: SymbolId, tier: Tier) -> String? { match lookup_symbol_entry(id: id) { - Present { value: entry } => resolve_glyph(glyphs: entry.glyphs, tier: tier) + Present { value: entry } => Present { value: glyph_at(glyphs: entry.glyphs, tier: tier) } Absent => none } } diff --git a/dsl/std/symbols.dag b/dsl/std/symbols.dag index ba1d89fac18..c0a88647137 100644 --- a/dsl/std/symbols.dag +++ b/dsl/std/symbols.dag @@ -86,24 +86,26 @@ type SymbolId | ConnectorCornerBottomLeft | ConnectorCornerTopLeft -// --- Tier glyph --- +// --- Tier glyphs --- // -// One encoding-tier realization: the closed Tier enum keys the glyph text. -// Rows compose into SymbolEntry.glyphs; cited strings live in extdeps/render/glyphs.dag. +// Total tier→String product for the closed Tier enum (one field per variant). +// Illegal states (missing/duplicate tier) are unrepresentable; dispatch is +// centralized in extdeps/render/glyphs.glyph_at. Cited strings live in +// extdeps/render/glyphs.dag. -type TierGlyph { - tier: Tier - text: String +type TierGlyphs { + emoji: String + unicode: String + ascii: String } // --- Symbol entry --- // // A single symbol: identity, tier-keyed glyph encodings, and semantic color. -// Glyphs are keyed by the closed Tier enum — not parallel nickname columns. // The cited realization rows live in extdeps/render/glyphs.dag. type SymbolEntry { id: SymbolId - glyphs: List + glyphs: TierGlyphs color: SemanticColor } From ea2a62971a0aaa86e4206c00cd04279288c376ac Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 19 Jun 2026 18:20:55 +0000 Subject: [PATCH 5/6] render: ground tier glyphs in distinct extdeps encoding authorities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ascii_glyph (ECMA-6), unicode_glyph (Unicode std.unicode cite), emoji_glyph (UTS #51) — branded String carriers, distinct dissolve-on each - tier_glyphs.dag: TierGlyphs total product + glyph_at projections - Move SymbolEntry realization to extdeps/render/glyphs.dag; std/symbols keeps SymbolId / Tier / SemanticColor only - Behavioral witness w_glyph_at_selects_matching_tier_text (distinct A/U/E) render_glyphs_ansi_witnesses --claim-run green. Co-authored-by: Cursor --- dsl/extdeps/render/ascii_glyph.dag | 18 +++ dsl/extdeps/render/emoji_glyph.dag | 17 +++ dsl/extdeps/render/glyphs.dag | 106 +++++++++--------- dsl/extdeps/render/tier_glyphs.dag | 29 +++++ dsl/extdeps/render/unicode_glyph.dag | 21 ++++ dsl/std/symbols.dag | 24 ---- .../claim/render_glyphs_ansi_witness_test.dag | 22 +++- 7 files changed, 155 insertions(+), 82 deletions(-) create mode 100644 dsl/extdeps/render/ascii_glyph.dag create mode 100644 dsl/extdeps/render/emoji_glyph.dag create mode 100644 dsl/extdeps/render/tier_glyphs.dag create mode 100644 dsl/extdeps/render/unicode_glyph.dag diff --git a/dsl/extdeps/render/ascii_glyph.dag b/dsl/extdeps/render/ascii_glyph.dag new file mode 100644 index 00000000000..4409b64f035 --- /dev/null +++ b/dsl/extdeps/render/ascii_glyph.dag @@ -0,0 +1,18 @@ +// extdeps/render/ascii_glyph.dag — ASCII-tier glyph carrier (ECMA-6 repertoire). +// +// Anchor: https://www.ecma-international.org/publications-and-standards/standards/ecma-6/ +// (ECMA-6 / ISO 646 7-bit coded character set; RFC 20 is the IETF mirror) +// +// dissolve-on: Q-Regex repertoire refinement — ASCII printable 0x20–0x7E. + +module extdeps.render.ascii_glyph + +type AsciiGlyph = String where brand("AsciiGlyph") + +fn ascii_glyph(text: String) -> AsciiGlyph { + text +} + +fn ascii_glyph_text(g: AsciiGlyph) -> String { + g +} diff --git a/dsl/extdeps/render/emoji_glyph.dag b/dsl/extdeps/render/emoji_glyph.dag new file mode 100644 index 00000000000..0d006db7816 --- /dev/null +++ b/dsl/extdeps/render/emoji_glyph.dag @@ -0,0 +1,17 @@ +// extdeps/render/emoji_glyph.dag — Emoji-tier glyph carrier (UTS #51). +// +// Anchor: https://www.unicode.org/reports/tr51/ (UTS #51 Unicode Emoji) +// +// dissolve-on: Q-Regex UTS #51 emoji-sequence validity. + +module extdeps.render.emoji_glyph + +type EmojiGlyph = String where brand("EmojiGlyph") + +fn emoji_glyph(text: String) -> EmojiGlyph { + text +} + +fn emoji_glyph_text(g: EmojiGlyph) -> String { + g +} diff --git a/dsl/extdeps/render/glyphs.dag b/dsl/extdeps/render/glyphs.dag index 8bcaa65cb2b..ae9e393bc35 100644 --- a/dsl/extdeps/render/glyphs.dag +++ b/dsl/extdeps/render/glyphs.dag @@ -1,19 +1,21 @@ // extdeps/render/glyphs.dag — Glyph realization table + encoding-tier dispatch. // -// Anchor: https://www.unicode.org/versions/latest/ -// Repertoire cited: U+2500 Box Drawing, U+2800 Braille Patterns; emoji tier per UTS #51. -// // Project visual vocabulary: three-tier encodings (Emoji / Unicode / Ascii) per // SymbolId. Dispatch lives in extdeps/, not std/ (DESIGN.md §3): std.symbols -// owns only the agnostic glyph identity and SymbolEntry shape. +// owns only the agnostic glyph identity vocabulary (SymbolId, Tier, SemanticColor). module extdeps.render.glyphs -import std.symbols { SemanticColor, SymbolEntry, SymbolId, Tier, TierGlyphs } +import extdeps.render.ascii_glyph { ascii_glyph } +import extdeps.render.emoji_glyph { emoji_glyph } +import extdeps.render.unicode_glyph { unicode_glyph } +import extdeps.render.tier_glyphs { TierGlyphs, glyph_at, glyphs_for_tiers } +import std.symbols { SemanticColor, SymbolId, Tier } -// Data-authoring helper: total Tier→String product (one field per closed variant). -fn glyphs_for_tiers(emoji: String, unicode: String, ascii: String) -> TierGlyphs { - { emoji: emoji, unicode: unicode, ascii: ascii } +type SymbolEntry { + id: SymbolId + glyphs: TierGlyphs + color: SemanticColor } // --- Standard symbol set --- @@ -24,60 +26,60 @@ fn glyphs_for_tiers(emoji: String, unicode: String, ascii: String) -> TierGlyphs data standard_symbols: List = [ // Node lifecycle // NOTE: emoji/unicode tiers collapsed to ASCII -- restore when tokenizer handles UTF-8 - { id: NodePending, glyphs: glyphs_for_tiers(emoji: "[ ]", unicode: "o", ascii: "[ ]"), color: Dim }, - { id: NodeRunning, glyphs: glyphs_for_tiers(emoji: "[~]", unicode: "O", ascii: "[~]"), color: Active }, - { id: NodeCompleted, glyphs: glyphs_for_tiers(emoji: "[x]", unicode: "*", ascii: "[x]"), color: Success }, - { id: NodeFailed, glyphs: glyphs_for_tiers(emoji: "[!]", unicode: "x", ascii: "[!]"), color: Error }, - { id: NodeSkipped, glyphs: glyphs_for_tiers(emoji: "[-]", unicode: "o", ascii: "[-]"), color: Dim }, - { id: NodeIntercepted, glyphs: glyphs_for_tiers(emoji: "[m]", unicode: "<>", ascii: "[m]"), color: Info }, + { id: NodePending, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[ ]"), unicode: unicode_glyph(text: "o"), ascii: ascii_glyph(text: "[ ]")), color: Dim }, + { id: NodeRunning, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[~]"), unicode: unicode_glyph(text: "O"), ascii: ascii_glyph(text: "[~]")), color: Active }, + { id: NodeCompleted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[x]"), unicode: unicode_glyph(text: "*"), ascii: ascii_glyph(text: "[x]")), color: Success }, + { id: NodeFailed, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[!]"), unicode: unicode_glyph(text: "x"), ascii: ascii_glyph(text: "[!]")), color: Error }, + { id: NodeSkipped, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[-]"), unicode: unicode_glyph(text: "o"), ascii: ascii_glyph(text: "[-]")), color: Dim }, + { id: NodeIntercepted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[m]"), unicode: unicode_glyph(text: "<>"), ascii: ascii_glyph(text: "[m]")), color: Info }, // Edge states - { id: EdgeIdle, glyphs: glyphs_for_tiers(emoji: "-", unicode: "-", ascii: "-"), color: Dim }, - { id: EdgeFlowing, glyphs: glyphs_for_tiers(emoji: "=", unicode: "=", ascii: "="), color: Accent }, - { id: EdgeDone, glyphs: glyphs_for_tiers(emoji: "-", unicode: "-", ascii: "-"), color: Success }, - { id: EdgeDead, glyphs: glyphs_for_tiers(emoji: ".", unicode: ".", ascii: "."), color: Dim }, + { id: EdgeIdle, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Dim }, + { id: EdgeFlowing, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "="), unicode: unicode_glyph(text: "="), ascii: ascii_glyph(text: "=")), color: Accent }, + { id: EdgeDone, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Success }, + { id: EdgeDead, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "."), unicode: unicode_glyph(text: "."), ascii: ascii_glyph(text: ".")), color: Dim }, // DAG phases - { id: DagNotStarted, glyphs: glyphs_for_tiers(emoji: "[ ]", unicode: "[ ]", ascii: "[ ]"), color: Dim }, - { id: DagRunning, glyphs: glyphs_for_tiers(emoji: "[>]", unicode: ">", ascii: "[>]"), color: Active }, - { id: DagCompleted, glyphs: glyphs_for_tiers(emoji: "[X]", unicode: "#", ascii: "[X]"), color: Success }, - { id: DagFailed, glyphs: glyphs_for_tiers(emoji: "[!]", unicode: "#", ascii: "[!]"), color: Error }, + { id: DagNotStarted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[ ]"), unicode: unicode_glyph(text: "[ ]"), ascii: ascii_glyph(text: "[ ]")), color: Dim }, + { id: DagRunning, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[>]"), unicode: unicode_glyph(text: ">"), ascii: ascii_glyph(text: "[>]")), color: Active }, + { id: DagCompleted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[X]"), unicode: unicode_glyph(text: "#"), ascii: ascii_glyph(text: "[X]")), color: Success }, + { id: DagFailed, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[!]"), unicode: unicode_glyph(text: "#"), ascii: ascii_glyph(text: "[!]")), color: Error }, // Structural - { id: BoundaryMarker, glyphs: glyphs_for_tiers(emoji: "[B]", unicode: "<>", ascii: "[B]"), color: Info }, + { id: BoundaryMarker, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[B]"), unicode: unicode_glyph(text: "<>"), ascii: ascii_glyph(text: "[B]")), color: Info }, // Spinners (ASCII fallback -- restore braille when tokenizer handles UTF-8) - { id: Spinner0, glyphs: glyphs_for_tiers(emoji: "|", unicode: "|", ascii: "|"), color: Active }, - { id: Spinner1, glyphs: glyphs_for_tiers(emoji: "/", unicode: "/", ascii: "/"), color: Active }, - { id: Spinner2, glyphs: glyphs_for_tiers(emoji: "-", unicode: "-", ascii: "-"), color: Active }, - { id: Spinner3, glyphs: glyphs_for_tiers(emoji: "\\", unicode: "\\", ascii: "\\"), color: Active }, - { id: Spinner4, glyphs: glyphs_for_tiers(emoji: "|", unicode: "|", ascii: "|"), color: Active }, - { id: Spinner5, glyphs: glyphs_for_tiers(emoji: "/", unicode: "/", ascii: "/"), color: Active }, - { id: Spinner6, glyphs: glyphs_for_tiers(emoji: "-", unicode: "-", ascii: "-"), color: Active }, - { id: Spinner7, glyphs: glyphs_for_tiers(emoji: "\\", unicode: "\\", ascii: "\\"), color: Active }, - { id: Spinner8, glyphs: glyphs_for_tiers(emoji: "|", unicode: "|", ascii: "|"), color: Active }, - { id: Spinner9, glyphs: glyphs_for_tiers(emoji: "/", unicode: "/", ascii: "/"), color: Active }, + { id: Spinner0, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Active }, + { id: Spinner1, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "/"), unicode: unicode_glyph(text: "/"), ascii: ascii_glyph(text: "/")), color: Active }, + { id: Spinner2, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Active }, + { id: Spinner3, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "\\"), unicode: unicode_glyph(text: "\\"), ascii: ascii_glyph(text: "\\")), color: Active }, + { id: Spinner4, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Active }, + { id: Spinner5, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "/"), unicode: unicode_glyph(text: "/"), ascii: ascii_glyph(text: "/")), color: Active }, + { id: Spinner6, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Active }, + { id: Spinner7, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "\\"), unicode: unicode_glyph(text: "\\"), ascii: ascii_glyph(text: "\\")), color: Active }, + { id: Spinner8, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Active }, + { id: Spinner9, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "/"), unicode: unicode_glyph(text: "/"), ascii: ascii_glyph(text: "/")), color: Active }, // Status indicators - { id: StatusSuccess, glyphs: glyphs_for_tiers(emoji: "OK", unicode: "v", ascii: "OK"), color: Success }, - { id: StatusFailure, glyphs: glyphs_for_tiers(emoji: "FAIL", unicode: "x", ascii: "FAIL"), color: Error }, - { id: StatusWarning, glyphs: glyphs_for_tiers(emoji: "WARN", unicode: "!", ascii: "WARN"), color: Warning }, - { id: StatusInfo, glyphs: glyphs_for_tiers(emoji: "INFO", unicode: "i", ascii: "INFO"), color: Info }, + { id: StatusSuccess, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "OK"), unicode: unicode_glyph(text: "v"), ascii: ascii_glyph(text: "OK")), color: Success }, + { id: StatusFailure, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "FAIL"), unicode: unicode_glyph(text: "x"), ascii: ascii_glyph(text: "FAIL")), color: Error }, + { id: StatusWarning, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "WARN"), unicode: unicode_glyph(text: "!"), ascii: ascii_glyph(text: "WARN")), color: Warning }, + { id: StatusInfo, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "INFO"), unicode: unicode_glyph(text: "i"), ascii: ascii_glyph(text: "INFO")), color: Info }, // Data type indicators - { id: DataList, glyphs: glyphs_for_tiers(emoji: "[L]", unicode: "=", ascii: "[L]"), color: Default }, - { id: DataMap, glyphs: glyphs_for_tiers(emoji: "[M]", unicode: "[+]", ascii: "[M]"), color: Default }, - { id: DataSecret, glyphs: glyphs_for_tiers(emoji: "[*]", unicode: "*", ascii: "[*]"), color: Warning }, - { id: DataUrl, glyphs: glyphs_for_tiers(emoji: "[U]", unicode: ">", ascii: "[U]"), color: Info }, - { id: DataTimer, glyphs: glyphs_for_tiers(emoji: "[T]", unicode: "[T]", ascii: "[T]"), color: Dim }, + { id: DataList, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[L]"), unicode: unicode_glyph(text: "="), ascii: ascii_glyph(text: "[L]")), color: Default }, + { id: DataMap, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[M]"), unicode: unicode_glyph(text: "[+]"), ascii: ascii_glyph(text: "[M]")), color: Default }, + { id: DataSecret, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[*]"), unicode: unicode_glyph(text: "*"), ascii: ascii_glyph(text: "[*]")), color: Warning }, + { id: DataUrl, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[U]"), unicode: unicode_glyph(text: ">"), ascii: ascii_glyph(text: "[U]")), color: Info }, + { id: DataTimer, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[T]"), unicode: unicode_glyph(text: "[T]"), ascii: ascii_glyph(text: "[T]")), color: Dim }, // Connectors - { id: ConnectorHorizontal, glyphs: glyphs_for_tiers(emoji: "--", unicode: "--", ascii: "--"), color: Dim }, - { id: ConnectorVertical, glyphs: glyphs_for_tiers(emoji: "|", unicode: "|", ascii: "|"), color: Dim }, - { id: ConnectorTeeDown, glyphs: glyphs_for_tiers(emoji: "+", unicode: "+", ascii: "+"), color: Dim }, - { id: ConnectorTeeUp, glyphs: glyphs_for_tiers(emoji: "+", unicode: "+", ascii: "+"), color: Dim }, - { id: ConnectorCornerBottomLeft, glyphs: glyphs_for_tiers(emoji: "+", unicode: "+", ascii: "+"), color: Dim }, - { id: ConnectorCornerTopLeft, glyphs: glyphs_for_tiers(emoji: "+", unicode: "+", ascii: "+"), color: Dim } + { id: ConnectorHorizontal, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "--"), unicode: unicode_glyph(text: "--"), ascii: ascii_glyph(text: "--")), color: Dim }, + { id: ConnectorVertical, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Dim }, + { id: ConnectorTeeDown, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim }, + { id: ConnectorTeeUp, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim }, + { id: ConnectorCornerBottomLeft, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim }, + { id: ConnectorCornerTopLeft, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim } ] // Lookup via fold + structural == (filter+field-equality hits a v1 interpreter bug). @@ -96,14 +98,6 @@ fn lookup_symbol_entry(id: SymbolId) -> SymbolEntry? { ) } -fn glyph_at(glyphs: TierGlyphs, tier: Tier) -> String { - match tier { - Emoji => glyphs.emoji - Unicode => glyphs.unicode - Ascii => glyphs.ascii - } -} - fn resolve_symbol(id: SymbolId, tier: Tier) -> String? { match lookup_symbol_entry(id: id) { Present { value: entry } => Present { value: glyph_at(glyphs: entry.glyphs, tier: tier) } diff --git a/dsl/extdeps/render/tier_glyphs.dag b/dsl/extdeps/render/tier_glyphs.dag new file mode 100644 index 00000000000..499303425f6 --- /dev/null +++ b/dsl/extdeps/render/tier_glyphs.dag @@ -0,0 +1,29 @@ +// extdeps/render/tier_glyphs.dag — Total Tier→glyph product (one field per variant). +// +// Each field is a distinct encoding authority (ascii_glyph / unicode_glyph / emoji_glyph). +// Illegal states (missing/duplicate tier) are unrepresentable. + +module extdeps.render.tier_glyphs + +import extdeps.render.ascii_glyph { AsciiGlyph, ascii_glyph_text } +import extdeps.render.emoji_glyph { EmojiGlyph, emoji_glyph_text } +import extdeps.render.unicode_glyph { UnicodeGlyph, unicode_glyph_text } +import std.symbols { Tier } + +type TierGlyphs { + ascii: AsciiGlyph + unicode: UnicodeGlyph + emoji: EmojiGlyph +} + +fn glyphs_for_tiers(emoji: EmojiGlyph, unicode: UnicodeGlyph, ascii: AsciiGlyph) -> TierGlyphs { + { emoji: emoji, unicode: unicode, ascii: ascii } +} + +fn glyph_at(glyphs: TierGlyphs, tier: Tier) -> String { + match tier { + Emoji => emoji_glyph_text(g: glyphs.emoji) + Unicode => unicode_glyph_text(g: glyphs.unicode) + Ascii => ascii_glyph_text(g: glyphs.ascii) + } +} diff --git a/dsl/extdeps/render/unicode_glyph.dag b/dsl/extdeps/render/unicode_glyph.dag new file mode 100644 index 00000000000..ba0c9b522a4 --- /dev/null +++ b/dsl/extdeps/render/unicode_glyph.dag @@ -0,0 +1,21 @@ +// extdeps/render/unicode_glyph.dag — Unicode-tier glyph carrier. +// +// Anchor: https://www.unicode.org/versions/latest/ +// (Unicode Standard — same authority as std.unicode UCD 15.x; no parallel model) +// +// dissolve-on: Q-Regex repertoire refinement — cited blocks (U+2500 Box Drawing, +// U+2800 Braille Patterns, …). + +module extdeps.render.unicode_glyph + +import std.unicode {} + +type UnicodeGlyph = String where brand("UnicodeGlyph") + +fn unicode_glyph(text: String) -> UnicodeGlyph { + text +} + +fn unicode_glyph_text(g: UnicodeGlyph) -> String { + g +} diff --git a/dsl/std/symbols.dag b/dsl/std/symbols.dag index c0a88647137..85f7035b986 100644 --- a/dsl/std/symbols.dag +++ b/dsl/std/symbols.dag @@ -85,27 +85,3 @@ type SymbolId | ConnectorTeeUp | ConnectorCornerBottomLeft | ConnectorCornerTopLeft - -// --- Tier glyphs --- -// -// Total tier→String product for the closed Tier enum (one field per variant). -// Illegal states (missing/duplicate tier) are unrepresentable; dispatch is -// centralized in extdeps/render/glyphs.glyph_at. Cited strings live in -// extdeps/render/glyphs.dag. - -type TierGlyphs { - emoji: String - unicode: String - ascii: String -} - -// --- Symbol entry --- -// -// A single symbol: identity, tier-keyed glyph encodings, and semantic color. -// The cited realization rows live in extdeps/render/glyphs.dag. - -type SymbolEntry { - id: SymbolId - glyphs: TierGlyphs - color: SemanticColor -} diff --git a/dsl/test/claim/render_glyphs_ansi_witness_test.dag b/dsl/test/claim/render_glyphs_ansi_witness_test.dag index a79f7cd8ce1..bc0b8621aa5 100644 --- a/dsl/test/claim/render_glyphs_ansi_witness_test.dag +++ b/dsl/test/claim/render_glyphs_ansi_witness_test.dag @@ -11,10 +11,14 @@ module test.claim.render_glyphs_ansi_witness import extdeps.render.ansi { ansi_code } +import extdeps.render.ascii_glyph { ascii_glyph } +import extdeps.render.emoji_glyph { emoji_glyph } import extdeps.render.glyphs { resolve_symbol, symbol_color } -import std.symbols { Ascii, Error, NodeCompleted, NodeFailed, Success, Unicode } +import extdeps.render.tier_glyphs { glyph_at, glyphs_for_tiers } +import extdeps.render.unicode_glyph { unicode_glyph } +import std.symbols { Ascii, Emoji, Error, NodeCompleted, NodeFailed, Success, Unicode } -// -- Glyph tier dispatch ----------------------------------------------------- +// -- Glyph tier dispatch (standard_symbols table) ---------------------------- fn w_completed_ascii() -> Bool { match resolve_symbol(id: NodeCompleted, tier: Ascii) { Present { value: s } => s == "[x]" @@ -33,6 +37,19 @@ fn w_completed_unicode_differs_from_ascii() -> Bool { } } +// -- Behavioral glyph_at witness (distinct text per tier; not brand-reliant) - +// Perturbing glyph_at to project the wrong field makes this RED. +fn w_glyph_at_selects_matching_tier_text() -> Bool { + let g = glyphs_for_tiers( + emoji: emoji_glyph(text: "E"), + unicode: unicode_glyph(text: "U"), + ascii: ascii_glyph(text: "A") + ) + glyph_at(glyphs: g, tier: Ascii) == "A" && + glyph_at(glyphs: g, tier: Unicode) == "U" && + glyph_at(glyphs: g, tier: Emoji) == "E" +} + fn w_failed_color_is_error() -> Bool { match symbol_color(id: NodeFailed) { Present { value: c } => @@ -56,6 +73,7 @@ fn w_success_ansi_is_256_green() -> Bool { test fn render_glyphs_ansi_witnesses() -> Bool { w_completed_ascii() && w_completed_unicode_differs_from_ascii() && + w_glyph_at_selects_matching_tier_text() && w_failed_color_is_error() && w_success_ansi_is_256_green() } From ced72e82dfa34bc71d974e0b6ce8f3bcf88442a2 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 19 Jun 2026 18:30:02 +0000 Subject: [PATCH 6/6] fix(render): real std.unicode ref in unicode_glyph; re-trigger CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI failure on ea2a629 was sccache infra flake (failed to read response header during cargo build), not a witness/gate regression — prior runs green on this branch. Replace empty `import std.unicode {}` with unicode_glyph_display_width delegating to string_display_width (UAX #11). render_glyphs_ansi_witnesses --claim-run green. Co-authored-by: Cursor --- dsl/extdeps/render/unicode_glyph.dag | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dsl/extdeps/render/unicode_glyph.dag b/dsl/extdeps/render/unicode_glyph.dag index ba0c9b522a4..a20190af3da 100644 --- a/dsl/extdeps/render/unicode_glyph.dag +++ b/dsl/extdeps/render/unicode_glyph.dag @@ -8,7 +8,7 @@ module extdeps.render.unicode_glyph -import std.unicode {} +import std.unicode { string_display_width } type UnicodeGlyph = String where brand("UnicodeGlyph") @@ -19,3 +19,8 @@ fn unicode_glyph(text: String) -> UnicodeGlyph { fn unicode_glyph_text(g: UnicodeGlyph) -> String { g } + +// Display-width dispatch for Unicode-tier glyphs defers to std.unicode (UAX #11). +fn unicode_glyph_display_width(g: UnicodeGlyph) -> Int { + string_display_width(s: unicode_glyph_text(g: g)) +}