From 6a9e9b9350a38c34b011c07ad8ca94e6f6021b83 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 2 Jul 2026 05:08:09 +0000 Subject: [PATCH 01/11] =?UTF-8?q?WIP:=202-emit-partition=20(c)=20=E2=80=94?= =?UTF-8?q?=20JSON=20grammar=20authority:=20one=20JSON=20emit=20authority?= =?UTF-8?q?=20i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dsl/extdeps/languages/json/emit.dag | 111 ++++++++++++++++++++++ dsl/gunbc/roadmap_spawner.dag | 92 ++++++++++-------- dsl/test/claim/json_emit_witness_test.dag | 91 ++++++++++++++++++ 3 files changed, 256 insertions(+), 38 deletions(-) create mode 100644 dsl/extdeps/languages/json/emit.dag create mode 100644 dsl/test/claim/json_emit_witness_test.dag diff --git a/dsl/extdeps/languages/json/emit.dag b/dsl/extdeps/languages/json/emit.dag new file mode 100644 index 00000000000..93144f1150d --- /dev/null +++ b/dsl/extdeps/languages/json/emit.dag @@ -0,0 +1,111 @@ +module extdeps.languages.json.emit + +import extdeps.external_authority { ExternalAuthority } +import extdeps.uri { Uri, Https } + +data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { + uri: Uri { + scheme: Https + locator: "www.rfc-editor.org/rfc/rfc8259" + } +} + +type JsonKeyValue { + key: String + value: JsonValue +} + +type JsonValue + = JsonNull + | JsonBool { value: Bool } + | JsonNumber { lexeme: String } + | JsonString { value: String } + | JsonArray { elements: List } + | JsonObject { members: List } + +fn json_kv(key: String, value: JsonValue) -> JsonKeyValue { + JsonKeyValue { key: key, value: value } +} + +fn json_null() -> JsonValue { + JsonNull +} + +fn json_bool(b: Bool) -> JsonValue { + JsonBool { value: b } +} + +fn json_number(lexeme: String) -> JsonValue { + JsonNumber { lexeme: lexeme } +} + +fn json_int(n: Int) -> JsonValue { + JsonNumber { lexeme: to_string(n) } +} + +fn json_string(s: String) -> JsonValue { + JsonString { value: s } +} + +fn json_array(elements: List) -> JsonValue { + JsonArray { elements: elements } +} + +fn json_object(members: List) -> JsonValue { + JsonObject { members: members } +} + +fn escape_json_string(s: String) -> String { + replace(replace(replace(s, "\\", "\\\\"), "\"", "\\\""), "\n", "\\n") +} + +fn quote_json_string(s: String) -> String { + concat("\"", concat(escape_json_string(s: s), "\"")) +} + +fn serialize_json_string(s: String) -> String { + quote_json_string(s: s) +} + +fn join_json_values(items: List) -> String { + fold(items, init: "", f: (acc, v) => { + let part = serialize_json(v: v) + if acc == "" { part } else { concat(acc, concat(", ", part)) } + }) +} + +fn serialize_json_array(elements: List) -> String { + concat("[", concat(join_json_values(items: elements), "]")) +} + +fn serialize_json_object(members: List) -> String { + concat( + "{", + concat( + fold(members, init: "", f: (acc, kv) => { + let part = concat(concat(quote_json_string(s: kv.key), ": "), serialize_json(v: kv.value)) + if acc == "" { part } else { concat(acc, concat(", ", part)) } + }), + "}" + ) + ) +} + +fn serialize_json(v: JsonValue) -> String { + match v { + JsonNull => "null" + JsonBool { value: b } => if b { "true" } else { "false" } + JsonNumber { lexeme: l } => l + JsonString { value: s } => quote_json_string(s: s) + JsonArray { elements: els } => serialize_json_array(elements: els) + JsonObject { members: m } => serialize_json_object(members: m) + } +} + +fn json_int_list(nums: List) -> String { + serialize_json_array(elements: nums |> map(n => json_int(n: n))) +} + +fn json_str_list(items: List) -> String { + serialize_json_array(elements: items |> map(s => json_string(s: s))) +} diff --git a/dsl/gunbc/roadmap_spawner.dag b/dsl/gunbc/roadmap_spawner.dag index 98c43edc064..e8fffb86a47 100644 --- a/dsl/gunbc/roadmap_spawner.dag +++ b/dsl/gunbc/roadmap_spawner.dag @@ -15,12 +15,24 @@ import gunbc.roadmap_document { RoadmapDocument, RoadmapSection } import gunbc.roadmap_authority { roadmap_authority, authored_merged_prs } import std.disposition { Disposition, Scaffold, SingleAuthority } import std.decl_ref { DeclarationRef, WholeDeclaration } +import extdeps.languages.json.emit { + JsonValue, JsonNull, + serialize_json, + quote_json_string, + json_null, + json_string, + json_int, + json_array, + json_object, + json_kv, + json_str_list, +} data json_emit_scaffold: Disposition = Scaffold { dissolves_to: SingleAuthority, bind: DeclarationRef { - module_path: "std.primitives", - decl_name: "to_json", + module_path: "extdeps.languages.json.emit", + decl_name: "serialize_json", field: WholeDeclaration } } @@ -114,24 +126,8 @@ fn spawn_frontier(doc: RoadmapDocument, merged: List) -> SpawnFrontier { } } -fn json_escape(s: String) -> String { - replace(replace(replace(s, "\\", "\\\\"), "\"", "\\\""), "\n", "\\n") -} - fn json_str(s: String) -> String { - concat("\"", concat(json_escape(s: s), "\"")) -} - -fn json_int_list(nums: List) -> String { - concat("[", - concat( - fold(nums, init: { s: "", first: true }, f: (acc, n) => { - s: if acc.first { to_string(n) } else { concat(acc.s, concat(", ", to_string(n))) }, - first: false - }).s, - "]" - ) - ) + quote_json_string(s: s) } fn intricacy_str(i: Intricacy) -> String { @@ -150,17 +146,29 @@ fn volume_str(v: Volume) -> String { } } -fn acceptance_json(a: Acceptance, line: RoadmapItem) -> String { +fn acceptance_json_value(a: Acceptance, line: RoadmapItem) -> JsonValue { match a { PrsMerged => match line { DerivableLine { prs, title: _, description: _ } => - concat("{\"kind\": \"prs_merged\", \"prs\": ", concat(json_int_list(nums: prs), "}")) + json_object(members: [ + json_kv(key: "kind", value: json_string(s: "prs_merged")), + json_kv(key: "prs", value: json_array(elements: prs |> map(n => json_int(n: n)))), + ]) AuthoredLine { done: _, content: _ } => - "{\"kind\": \"prs_merged_model_error\", \"prs\": []}" + json_object(members: [ + json_kv(key: "kind", value: json_string(s: "prs_merged_model_error")), + json_kv(key: "prs", value: json_array(elements: [])), + ]) PlainLine { content: _ } => - "{\"kind\": \"prs_merged_model_error\", \"prs\": []}" + json_object(members: [ + json_kv(key: "kind", value: json_string(s: "prs_merged_model_error")), + json_kv(key: "prs", value: json_array(elements: [])), + ]) } - ManualAcceptance => "{\"kind\": \"manual\"}" + ManualAcceptance => + json_object(members: [ + json_kv(key: "kind", value: json_string(s: "manual")), + ]) } } @@ -172,29 +180,37 @@ fn node_title(item: RoadmapItem) -> String { } } -fn node_plan_doc_json(carriers: List) -> String { +fn node_plan_doc_json(carriers: List) -> JsonValue { let plans = carriers |> filter(p => p.label == "plan") if count(plans) > 0 { - fold(plans, init: "null", f: (acc, p) => if acc == "null" { json_str(s: p.path) } else { acc }) + fold(plans, init: json_null(), f: (acc, p) => match acc { + JsonNull => json_string(s: p.path) + _ => acc + }) } else { - "null" + json_null() } } fn node_json(n: RoadmapNode, intricacy: Intricacy, volume: Volume, repo: String, acceptance: Acceptance, edges: List) -> String { - let parent_id_json = match roadmap_parent(edges: edges, node: n.node) { - TopLevel => "null" - UnderParent { parent: pid } => json_str(s: pid) + let parent_id_value = match roadmap_parent(edges: edges, node: n.node) { + TopLevel => json_null() + UnderParent { parent: pid } => json_string(s: pid) } - let plan_json = node_plan_doc_json(carriers: n.carriers) + let owner_value = if n.owner == "" { json_null() } else { json_string(s: n.owner) } let title = node_title(item: n.line) - let f1 = concat("\"node_id\": ", concat(json_str(s: n.node), concat(", \"title\": ", json_str(s: title)))) - let f2 = concat(", \"repo\": ", concat(json_str(s: repo), concat(", \"intricacy\": ", json_str(s: intricacy_str(i: intricacy))))) - let f3 = concat(", \"volume\": ", concat(json_str(s: volume_str(v: volume)), concat(", \"parent_node_id\": ", parent_id_json))) - let f3b = concat(", \"parent_node_ids\": ", json_str_list(items: node_parent_ids(edges: edges, id: n.node))) - let f3c = concat(", \"owner\": ", if n.owner == "" { "null" } else { json_str(s: n.owner) }) - let f4 = concat(", \"plan_doc\": ", concat(plan_json, concat(", \"acceptance\": ", acceptance_json(a: acceptance, line: n.line)))) - concat("{", concat(f1, concat(f2, concat(f3, concat(f3b, concat(f3c, concat(f4, "}"))))))) + serialize_json(v: json_object(members: [ + json_kv(key: "node_id", value: json_string(s: n.node)), + json_kv(key: "title", value: json_string(s: title)), + json_kv(key: "repo", value: json_string(s: repo)), + json_kv(key: "intricacy", value: json_string(s: intricacy_str(i: intricacy))), + json_kv(key: "volume", value: json_string(s: volume_str(v: volume))), + json_kv(key: "parent_node_id", value: parent_id_value), + json_kv(key: "parent_node_ids", value: json_array(elements: node_parent_ids(edges: edges, id: n.node) |> map(id => json_string(s: id)))), + json_kv(key: "owner", value: owner_value), + json_kv(key: "plan_doc", value: node_plan_doc_json(carriers: n.carriers)), + json_kv(key: "acceptance", value: acceptance_json_value(a: acceptance, line: n.line)), + ])) } fn join_strings(items: List) -> String { diff --git a/dsl/test/claim/json_emit_witness_test.dag b/dsl/test/claim/json_emit_witness_test.dag new file mode 100644 index 00000000000..50de1a8b2d7 --- /dev/null +++ b/dsl/test/claim/json_emit_witness_test.dag @@ -0,0 +1,91 @@ +module test.claim.json_emit_witness + +import extdeps.external_authority { ExternalAuthority } +import extdeps.languages.json.emit { + extdeps_external_authority_anchor, + escape_json_string, + quote_json_string, + serialize_json, + json_null, + json_bool, + json_int, + json_string, + json_array, + json_object, + json_kv, + json_int_list, + json_str_list, +} + +fn witness_json_cites_rfc8259() -> Bool { + match extdeps_external_authority_anchor { + ExternalAuthority { uri } => uri.locator == "www.rfc-editor.org/rfc/rfc8259" + } +} + +fn witness_escape_backslash_and_quote() -> Bool { + escape_json_string(s: "a\"b\\c") == "a\\\"b\\\\c" +} + +fn witness_escape_newline() -> Bool { + escape_json_string(s: "line one\nline two") == "line one\\nline two" +} + +fn witness_quote_json_string() -> Bool { + quote_json_string(s: "hello") == "\"hello\"" + && quote_json_string(s: "a\"b") == "\"a\\\"b\"" +} + +fn witness_serialize_null() -> Bool { + serialize_json(v: json_null()) == "null" +} + +fn witness_serialize_bool() -> Bool { + serialize_json(v: json_bool(b: true)) == "true" + && serialize_json(v: json_bool(b: false)) == "false" +} + +fn witness_serialize_int() -> Bool { + serialize_json(v: json_int(n: 42)) == "42" +} + +fn witness_serialize_object() -> Bool { + serialize_json(v: json_object(members: [ + json_kv(key: "host", value: json_string(s: "srv1")), + json_kv(key: "runner_count", value: json_int(n: 4)), + ])) == "{\"host\": \"srv1\", \"runner_count\": 4}" +} + +fn witness_serialize_nested_object() -> Bool { + serialize_json(v: json_object(members: [ + json_kv(key: "Password", value: json_string(s: "secret")), + ])) == "{\"Password\": \"secret\"}" +} + +fn witness_serialize_array() -> Bool { + serialize_json(v: json_array(elements: [json_string(s: "a"), json_string(s: "b")])) + == "[\"a\", \"b\"]" +} + +fn witness_int_list_helper() -> Bool { + json_int_list(nums: [1, 2, 3]) == "[1, 2, 3]" +} + +fn witness_str_list_helper() -> Bool { + json_str_list(items: ["x", "y"]) == "[\"x\", \"y\"]" +} + +test fn json_emit_witnesses() -> Bool { + witness_json_cites_rfc8259() + && witness_escape_backslash_and_quote() + && witness_escape_newline() + && witness_quote_json_string() + && witness_serialize_null() + && witness_serialize_bool() + && witness_serialize_int() + && witness_serialize_object() + && witness_serialize_nested_object() + && witness_serialize_array() + && witness_int_list_helper() + && witness_str_list_helper() +} From c519a57e377b370380b47f2669b8fd48afe1f5ad Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 2 Jul 2026 06:58:58 +0000 Subject: [PATCH 02/11] =?UTF-8?q?WIP:=202-emit-partition=20(c)=20=E2=80=94?= =?UTF-8?q?=20JSON=20grammar=20authority:=20one=20JSON=20emit=20authority?= =?UTF-8?q?=20i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dsl/extdeps/languages/json/emit.dag | 1 + dsl/extdeps/languages/json/grammar.dag | 31 ++++ dsl/gunbc/roadmap_spawner.dag | 108 +++++++----- dsl/gunbc/tailscale_acl_emit.dag | 166 +++++------------- dsl/gunbc/tools/bmc_onboard.dag | 31 ++-- dsl/test/claim/json_emit_witness_test.dag | 6 + dsl/test/claim/tailscale_acl_witness_test.dag | 13 +- dsl/tools/roadmap_dispatch.dag | 19 ++ src/v2/lens/disposition_redundancy_test.dag | 7 - src/v2/lens/medium_structure_containment.dag | 1 - 10 files changed, 180 insertions(+), 203 deletions(-) create mode 100644 dsl/extdeps/languages/json/grammar.dag diff --git a/dsl/extdeps/languages/json/emit.dag b/dsl/extdeps/languages/json/emit.dag index 93144f1150d..bf6d657e8d1 100644 --- a/dsl/extdeps/languages/json/emit.dag +++ b/dsl/extdeps/languages/json/emit.dag @@ -2,6 +2,7 @@ module extdeps.languages.json.emit import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } +import extdeps.languages.json.grammar { json_production_rows } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { diff --git a/dsl/extdeps/languages/json/grammar.dag b/dsl/extdeps/languages/json/grammar.dag new file mode 100644 index 00000000000..021fe229f87 --- /dev/null +++ b/dsl/extdeps/languages/json/grammar.dag @@ -0,0 +1,31 @@ +module extdeps.languages.json.grammar + +import extdeps.external_authority { ExternalAuthority } +import extdeps.uri { Uri, Https } + +data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { + uri: Uri { + scheme: Https + locator: "www.rfc-editor.org/rfc/rfc8259" + } +} + +type JsonProductionRow { + rfc_section: String + lhs: String + rhs: String + value_variant: String +} + +data json_production_rows: List = [ + JsonProductionRow { rfc_section: "3", lhs: "JSON-text", rhs: "ws value ws", value_variant: "JsonValue" }, + JsonProductionRow { rfc_section: "3", lhs: "value", rhs: "object | array | string | number | literal", value_variant: "JsonValue" }, + JsonProductionRow { rfc_section: "3", lhs: "object", rhs: "'{' ws member (',' ws member)* '}' | '{' ws '}'", value_variant: "JsonObject" }, + JsonProductionRow { rfc_section: "3", lhs: "member", rhs: "string ws ':' ws value", value_variant: "JsonKeyValue" }, + JsonProductionRow { rfc_section: "3", lhs: "array", rhs: "'[' ws value (',' ws value)* ']' | '[' ws ']'", value_variant: "JsonArray" }, + JsonProductionRow { rfc_section: "3", lhs: "string", rhs: "'\"' char* '\"'", value_variant: "JsonString" }, + JsonProductionRow { rfc_section: "3", lhs: "number", rhs: "minus? int frac? exp?", value_variant: "JsonNumber" }, + JsonProductionRow { rfc_section: "3", lhs: "literal", rhs: "'null' | 'true' | 'false'", value_variant: "JsonNull|JsonBool" }, +] + +data json_emit_fold_dissolution_trigger: String = "dissolve-on: v2 grammar-inverse ingest for JSON — dsl/extdeps/languages/json/{grammar,emit}.dag is the single dsl-tree authority (RFC 8259 rows + JsonValue fold); DISSOLVES WHEN src/v2/extdeps/languages/json.dag lands FormalProduction/TargetModel rows wired through 06_translate with ingest ∘ emit = id on json_production_rows witnesses" diff --git a/dsl/gunbc/roadmap_spawner.dag b/dsl/gunbc/roadmap_spawner.dag index e8fffb86a47..fbc9de9b42b 100644 --- a/dsl/gunbc/roadmap_spawner.dag +++ b/dsl/gunbc/roadmap_spawner.dag @@ -12,9 +12,12 @@ import gunbc.roadmap_model { } import gunbc.roadmap_status { RoadmapItem, DerivableLine, AuthoredLine, PlainLine, item_is_done } import gunbc.roadmap_document { RoadmapDocument, RoadmapSection } +<<<<<<< HEAD import gunbc.roadmap_authority { roadmap_authority, authored_merged_prs } import std.disposition { Disposition, Scaffold, SingleAuthority } import std.decl_ref { DeclarationRef, WholeDeclaration } +======= +>>>>>>> a68ecc417e (WIP: 2-emit-partition (c) — JSON grammar authority: one JSON emit authority i) import extdeps.languages.json.emit { JsonValue, JsonNull, serialize_json, @@ -28,15 +31,6 @@ import extdeps.languages.json.emit { json_str_list, } -data json_emit_scaffold: Disposition = Scaffold { - dissolves_to: SingleAuthority, - bind: DeclarationRef { - module_path: "extdeps.languages.json.emit", - decl_name: "serialize_json", - field: WholeDeclaration - } -} - fn element_nodes(e: SectionElement) -> List { match e { Prose { prose: _ } => [] @@ -192,14 +186,14 @@ fn node_plan_doc_json(carriers: List) -> JsonValue { } } -fn node_json(n: RoadmapNode, intricacy: Intricacy, volume: Volume, repo: String, acceptance: Acceptance, edges: List) -> String { +fn node_json_value(n: RoadmapNode, intricacy: Intricacy, volume: Volume, repo: String, acceptance: Acceptance, edges: List) -> JsonValue { let parent_id_value = match roadmap_parent(edges: edges, node: n.node) { TopLevel => json_null() UnderParent { parent: pid } => json_string(s: pid) } let owner_value = if n.owner == "" { json_null() } else { json_string(s: n.owner) } let title = node_title(item: n.line) - serialize_json(v: json_object(members: [ + json_object(members: [ json_kv(key: "node_id", value: json_string(s: n.node)), json_kv(key: "title", value: json_string(s: title)), json_kv(key: "repo", value: json_string(s: repo)), @@ -210,60 +204,64 @@ fn node_json(n: RoadmapNode, intricacy: Intricacy, volume: Volume, repo: String, json_kv(key: "owner", value: owner_value), json_kv(key: "plan_doc", value: node_plan_doc_json(carriers: n.carriers)), json_kv(key: "acceptance", value: acceptance_json_value(a: acceptance, line: n.line)), - ])) + ]) } -fn join_strings(items: List) -> String { - fold(items, init: { s: "", first: true }, f: (acc, item) => { - s: if acc.first { item } else { concat(acc.s, concat(", ", item)) }, - first: false - }).s +fn node_json(n: RoadmapNode, intricacy: Intricacy, volume: Volume, repo: String, acceptance: Acceptance, edges: List) -> String { + serialize_json(v: node_json_value(n: n, intricacy: intricacy, volume: volume, repo: repo, acceptance: acceptance, edges: edges)) +} + +fn node_unmet_parent_ids(nodes: List, edges: List, id: ProcessNodeId, merged: List) -> List { + node_parent_ids(edges: edges, id: id) + |> filter(pid => !any(nodes, n => n.node == pid && item_is_done(item: n.line, merged: merged))) } fn ready_array_json(nodes: List, edges: List) -> String { - let items = nodes |> map(n => match n.sizing { - Unsized => "{}" + serialize_json(v: json_array(elements: nodes |> map(n => match n.sizing { + Unsized => json_object(members: []) Sized { intricacy, volume, repo, acceptance } => - node_json(n: n, intricacy: intricacy, volume: volume, repo: repo, acceptance: acceptance, edges: edges) - }) - concat("[", concat(join_strings(items: items), "]")) + node_json_value(n: n, intricacy: intricacy, volume: volume, repo: repo, acceptance: acceptance, edges: edges) + }))) } fn roadmap_spawn_request_json(doc: RoadmapDocument, merged: List, anchor_commit: String) -> String { let nodes = next_spawnable(doc: doc, merged: merged) let edges = doc_all_edges(doc: doc) - concat("{", - concat("\"schema\": \"roadmap-spawn-request/v1\"", - concat(", \"anchor_commit\": ", concat(json_str(s: anchor_commit), - concat(", \"ready\": ", concat(ready_array_json(nodes: nodes, edges: edges), "}")))))) -} - -fn json_str_list(items: List) -> String { - concat("[", concat(join_strings(items: items |> map(s => json_str(s: s))), "]")) -} - -fn node_unmet_parent_ids(nodes: List, edges: List, id: ProcessNodeId, merged: List) -> List { - node_parent_ids(edges: edges, id: id) - |> filter(pid => !any(nodes, n => n.node == pid && item_is_done(item: n.line, merged: merged))) + serialize_json(v: json_object(members: [ + json_kv(key: "schema", value: json_string(s: "roadmap-spawn-request/v1")), + json_kv(key: "anchor_commit", value: json_string(s: anchor_commit)), + json_kv(key: "ready", value: json_array(elements: nodes |> map(n => match n.sizing { + Unsized => json_object(members: []) + Sized { intricacy, volume, repo, acceptance } => + node_json_value(n: n, intricacy: intricacy, volume: volume, repo: repo, acceptance: acceptance, edges: edges) + }))), + ])) } fn upcoming_node_json(n: RoadmapNode, nodes: List, edges: List, merged: List) -> String { let unmet = node_unmet_parent_ids(nodes: nodes, edges: edges, id: n.node, merged: merged) - concat("{\"node_id\": ", concat(json_str(s: n.node), - concat(", \"title\": ", concat(json_str(s: node_title(item: n.line)), - concat(", \"unmet_parents\": ", concat(json_str_list(items: unmet), "}")))))) + serialize_json(v: json_object(members: [ + json_kv(key: "node_id", value: json_string(s: n.node)), + json_kv(key: "title", value: json_string(s: node_title(item: n.line))), + json_kv(key: "unmet_parents", value: json_array(elements: unmet |> map(id => json_string(s: id)))), + ])) } fn upcoming_array_json(frontier_upcoming: List, nodes: List, edges: List, merged: List) -> String { - concat("[", - concat(join_strings(items: frontier_upcoming |> map(n => - upcoming_node_json(n: n, nodes: nodes, edges: edges, merged: merged))), "]")) + serialize_json(v: json_array(elements: frontier_upcoming |> map(n => + json_object(members: [ + json_kv(key: "node_id", value: json_string(s: n.node)), + json_kv(key: "title", value: json_string(s: node_title(item: n.line))), + json_kv(key: "unmet_parents", value: json_array(elements: node_unmet_parent_ids(nodes: nodes, edges: edges, id: n.node, merged: merged) |> map(id => json_string(s: id)))), + ]) + ))) } fn done_ids_json(frontier_done: List) -> String { json_str_list(items: frontier_done |> map(n => n.node)) } +<<<<<<< HEAD data dispatch_brief_template: String = "Node: \nWhy now: \nNon-goals: \nM0 evidence: \nM1 smallest change: \nM2 RED control: \nM3 green receipt: \nStop and return if: \nFiles likely touched: \nCommands to run: \nExpected artifact: " fn roadmap_dispatch_file_content() -> String { @@ -273,12 +271,30 @@ fn roadmap_dispatch_file_content() -> String { } fn roadmap_dispatch_json(doc: RoadmapDocument, merged: List) -> String { +======= +fn roadmap_dispatch_json_value(doc: RoadmapDocument, merged: List) -> JsonValue { +>>>>>>> a68ecc417e (WIP: 2-emit-partition (c) — JSON grammar authority: one JSON emit authority i) let nodes = doc_all_nodes(doc: doc) let edges = doc_all_edges(doc: doc) let frontier = spawn_frontier(doc: doc, merged: merged) - concat("{", - concat("\"schema\": \"roadmap-dispatch/v1\"", - concat(", \"ready\": ", concat(ready_array_json(nodes: frontier.ready, edges: edges), - concat(", \"upcoming\": ", concat(upcoming_array_json(frontier_upcoming: frontier.upcoming, nodes: nodes, edges: edges, merged: merged), - concat(", \"done\": ", concat(done_ids_json(frontier_done: frontier.done), "}")))))))) + json_object(members: [ + json_kv(key: "schema", value: json_string(s: "roadmap-dispatch/v1")), + json_kv(key: "ready", value: json_array(elements: frontier.ready |> map(n => match n.sizing { + Unsized => json_object(members: []) + Sized { intricacy, volume, repo, acceptance } => + node_json_value(n: n, intricacy: intricacy, volume: volume, repo: repo, acceptance: acceptance, edges: edges) + }))), + json_kv(key: "upcoming", value: json_array(elements: frontier.upcoming |> map(n => + json_object(members: [ + json_kv(key: "node_id", value: json_string(s: n.node)), + json_kv(key: "title", value: json_string(s: node_title(item: n.line))), + json_kv(key: "unmet_parents", value: json_array(elements: node_unmet_parent_ids(nodes: nodes, edges: edges, id: n.node, merged: merged) |> map(id => json_string(s: id)))), + ]) + ))), + json_kv(key: "done", value: json_array(elements: frontier.done |> map(n => json_string(s: n.node)))), + ]) +} + +fn roadmap_dispatch_json(doc: RoadmapDocument, merged: List) -> String { + serialize_json(v: roadmap_dispatch_json_value(doc: doc, merged: merged)) } diff --git a/dsl/gunbc/tailscale_acl_emit.dag b/dsl/gunbc/tailscale_acl_emit.dag index ad2ec775184..83c6f21eebb 100644 --- a/dsl/gunbc/tailscale_acl_emit.dag +++ b/dsl/gunbc/tailscale_acl_emit.dag @@ -1,152 +1,70 @@ module gunbc.tailscale_acl_emit -import std.types { String, NonEmptyStr, List } -import std.disposition { Disposition, Scaffold, SingleAuthority } -import std.decl_ref { DeclarationRef, WholeDeclaration } +import std.types { NonEmptyStr, List } +import extdeps.languages.json.emit { + JsonValue, JsonKeyValue, + serialize_json, + json_string, + json_array, + json_object, + json_kv, +} import extdeps.tailscale.acl { TailscaleGrant, TailscaleSshRule, TailscaleNodeAttr, TailscaleTagOwnerRow, TailscaleAclSelector, + TailscaleAclPolicy, render_tailscale_acl_selector, operator_tailscale_acl_policy, } -data tailscale_acl_json_emit_scaffold: Disposition = Scaffold { - dissolves_to: SingleAuthority, - bind: DeclarationRef { - module_path: "std.primitives", - decl_name: "to_json", - field: WholeDeclaration - } -} - -data tailscale_acl_json_emit_dissolution_trigger: String = "dissolve-on: tailscale_acl_json_emit concat serializer — JSON body built via nested string concat (medium-as-string) instead of grammar-row emit from extdeps.tailscale.acl authority; DISSOLVES WHEN Tailscale ACL policy emits through to_json/grammar rows with ingest o emit = id over operator_tailscale_acl_policy; retire medium_structure_exception_roster entry for dsl/gunbc/tailscale_acl_emit.dag" - -fn join_json_quoted(items: List) -> String { - fold(items, init: "", f: (acc, x) => if acc == "" { concat("\"", concat(x, "\"")) } else { concat(concat(acc, ", \""), concat(x, "\"")) }) -} - -fn join_json_quoted_selectors(items: List) -> String { - fold( - items, - init: "", - f: (acc, x) => { - let rendered = render_tailscale_acl_selector(s: x) - if acc == "" { - concat("\"", concat(rendered, "\"")) - } else { - concat(concat(acc, ", \""), concat(rendered, "\"")) - } - } - ) -} - -fn json_string_array(items: List) -> String { - concat("[", concat(join_json_quoted(items: items), "]")) -} - -fn json_selector_array(items: List) -> String { - concat("[", concat(join_json_quoted_selectors(items: items), "]")) -} - -fn emit_grant_row(g: TailscaleGrant) -> String { - concat( - concat( - concat( - concat("{\"src\": ", json_selector_array(items: g.src)), - concat(", \"dst\": ", json_selector_array(items: g.dst)) - ), - concat(", \"ip\": ", json_selector_array(items: g.ip)) - ), - "}" - ) -} - -fn emit_grant_rows(grants: List) -> String { - fold(grants, init: "", f: (acc, g) => if acc == "" { emit_grant_row(g: g) } else { concat(concat(acc, ",\n "), emit_grant_row(g: g)) }) +fn selector_array_json(items: List) -> JsonValue { + json_array(elements: items |> map(s => json_string(s: render_tailscale_acl_selector(s: s)))) } -fn emit_ssh_rule(r: TailscaleSshRule) -> String { - concat( - concat( - concat( - concat( - concat( - concat("{\n \"action\": \"", r.action), - concat("\",\n \"src\": ", json_selector_array(items: r.src)) - ), - concat(",\n \"dst\": ", json_selector_array(items: r.dst)) - ), - concat(",\n \"users\": ", json_selector_array(items: r.users)) - ), - "\n }" - ), - "" - ) +fn string_array_json(items: List) -> JsonValue { + json_array(elements: items |> map(s => json_string(s: s))) } -fn emit_ssh_rows(rules: List) -> String { - fold(rules, init: "", f: (acc, r) => if acc == "" { emit_ssh_rule(r: r) } else { concat(concat(acc, ",\n "), emit_ssh_rule(r: r)) }) +fn grant_json(g: TailscaleGrant) -> JsonValue { + json_object(members: [ + json_kv(key: "src", value: selector_array_json(items: g.src)), + json_kv(key: "dst", value: selector_array_json(items: g.dst)), + json_kv(key: "ip", value: selector_array_json(items: g.ip)), + ]) } -fn emit_node_attr_row(n: TailscaleNodeAttr) -> String { - concat( - concat( - concat("{\"target\": ", json_string_array(items: n.target)), - concat(", \"attr\": ", json_string_array(items: n.attr)) - ), - "}" - ) +fn ssh_rule_json(r: TailscaleSshRule) -> JsonValue { + json_object(members: [ + json_kv(key: "action", value: json_string(s: r.action)), + json_kv(key: "src", value: selector_array_json(items: r.src)), + json_kv(key: "dst", value: selector_array_json(items: r.dst)), + json_kv(key: "users", value: selector_array_json(items: r.users)), + ]) } -fn emit_node_attr_rows(attrs: List) -> String { - fold(attrs, init: "", f: (acc, n) => if acc == "" { emit_node_attr_row(n: n) } else { concat(concat(acc, ",\n "), emit_node_attr_row(n: n)) }) +fn node_attr_json(n: TailscaleNodeAttr) -> JsonValue { + json_object(members: [ + json_kv(key: "target", value: string_array_json(items: n.target)), + json_kv(key: "attr", value: string_array_json(items: n.attr)), + ]) } -fn emit_tag_owner_row(row: TailscaleTagOwnerRow) -> String { - concat( - concat(concat(" \"", row.tag), concat("\": ", json_string_array(items: row.owners))), - "" - ) +fn tag_owner_json(row: TailscaleTagOwnerRow) -> JsonKeyValue { + json_kv(key: row.tag, value: string_array_json(items: row.owners)) } -fn emit_tag_owners(rows: List) -> String { - fold(rows, init: "", f: (acc, row) => if acc == "" { emit_tag_owner_row(row: row) } else { concat(concat(acc, ",\n"), emit_tag_owner_row(row: row)) }) +fn tailscale_acl_policy_json(policy: TailscaleAclPolicy) -> JsonValue { + json_object(members: [ + json_kv(key: "grants", value: json_array(elements: policy.grants |> map(g => grant_json(g: g)))), + json_kv(key: "ssh", value: json_array(elements: policy.ssh |> map(r => ssh_rule_json(r: r)))), + json_kv(key: "nodeAttrs", value: json_array(elements: policy.node_attrs |> map(n => node_attr_json(n: n)))), + json_kv(key: "tagOwners", value: json_object(members: policy.tag_owners |> map(row => tag_owner_json(row: row)))), + ]) } fn expected_tailscale_acl_json() -> String { - let policy = operator_tailscale_acl_policy - concat( - concat( - concat( - concat( - concat( - concat( - concat( - concat( - "{\n \"grants\": [\n ", - emit_grant_rows(grants: policy.grants) - ), - "\n ],\n \"ssh\": [\n " - ), - emit_ssh_rows(rules: policy.ssh) - ), - "\n ],\n \"nodeAttrs\": [\n " - ), - emit_node_attr_rows(attrs: policy.node_attrs) - ), - concat( - concat( - "\n ],\n \"tagOwners\": {\n", - emit_tag_owners(rows: policy.tag_owners) - ), - "\n }\n}\n" - ) - ), - "" - ), - "" - ) + serialize_json(v: tailscale_acl_policy_json(policy: operator_tailscale_acl_policy)) } diff --git a/dsl/gunbc/tools/bmc_onboard.dag b/dsl/gunbc/tools/bmc_onboard.dag index f8e291b855b..6b505b4172e 100644 --- a/dsl/gunbc/tools/bmc_onboard.dag +++ b/dsl/gunbc/tools/bmc_onboard.dag @@ -21,6 +21,12 @@ import std.resources { Network } import std.process { ProcessExit, ExitSuccess, exit_failure } import std.disposition { Disposition, Scaffold, SingleAuthority, RealizationDispatch } import std.decl_ref { DeclarationRef, WholeDeclaration } +import extdeps.languages.json.emit { + serialize_json, + json_object, + json_kv, + json_string, +} data bmc_onboard_netrc_path: String = "/tmp/bmc_onboard_netrc" data bmc_onboard_pwbody_path: String = "/tmp/bmc_onboard_pwbody.json" @@ -38,30 +44,29 @@ data srv3_gcp_project: GcpProjectId = "gunbai-secrets" data bmc_credential_bytes: ByteSize = byte_size(20) -data bmc_redfish_json_concat_debt: Disposition = Scaffold { - dissolves_to: SingleAuthority, - bind: DeclarationRef { - module_path: "std.primitives", - decl_name: "to_json_contract", - field: WholeDeclaration - } -} - fn netrc_content(host: String, account: String, password: String) -> String { concat("machine ", concat(host, concat(" login ", concat(account, concat(" password ", concat(password, "\n")))))) } fn account_password_body(new_password: String) -> String { - concat("{\"Password\": \"", concat(new_password, "\"}")) + serialize_json(v: json_object(members: [ + json_kv(key: "Password", value: json_string(s: new_password)), + ])) } fn boot_override_body(target: String, enabled: String) -> String { - concat("{\"Boot\": {\"BootSourceOverrideTarget\": \"", - concat(target, concat("\", \"BootSourceOverrideEnabled\": \"", concat(enabled, "\"}}")))) + serialize_json(v: json_object(members: [ + json_kv(key: "Boot", value: json_object(members: [ + json_kv(key: "BootSourceOverrideTarget", value: json_string(s: target)), + json_kv(key: "BootSourceOverrideEnabled", value: json_string(s: enabled)), + ])), + ])) } fn reset_body(reset_type: String) -> String { - concat("{\"ResetType\": \"", concat(reset_type, "\"}")) + serialize_json(v: json_object(members: [ + json_kv(key: "ResetType", value: json_string(s: reset_type)), + ])) } fn gcp_secret_full_name(project: String, secret_id: String) -> String { diff --git a/dsl/test/claim/json_emit_witness_test.dag b/dsl/test/claim/json_emit_witness_test.dag index 50de1a8b2d7..925c6f145a3 100644 --- a/dsl/test/claim/json_emit_witness_test.dag +++ b/dsl/test/claim/json_emit_witness_test.dag @@ -16,6 +16,7 @@ import extdeps.languages.json.emit { json_int_list, json_str_list, } +import extdeps.languages.json.grammar { json_production_rows } fn witness_json_cites_rfc8259() -> Bool { match extdeps_external_authority_anchor { @@ -75,6 +76,10 @@ fn witness_str_list_helper() -> Bool { json_str_list(items: ["x", "y"]) == "[\"x\", \"y\"]" } +fn witness_grammar_rows_cover_json_value_variants() -> Bool { + length(xs: json_production_rows) >= 8 +} + test fn json_emit_witnesses() -> Bool { witness_json_cites_rfc8259() && witness_escape_backslash_and_quote() @@ -88,4 +93,5 @@ test fn json_emit_witnesses() -> Bool { && witness_serialize_array() && witness_int_list_helper() && witness_str_list_helper() + && witness_grammar_rows_cover_json_value_variants() } diff --git a/dsl/test/claim/tailscale_acl_witness_test.dag b/dsl/test/claim/tailscale_acl_witness_test.dag index ca241eda780..7fb226eb03f 100644 --- a/dsl/test/claim/tailscale_acl_witness_test.dag +++ b/dsl/test/claim/tailscale_acl_witness_test.dag @@ -16,8 +16,7 @@ import extdeps.tailscale.acl { } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Https } -import std.disposition { Scaffold, SingleAuthority } -import gunbc.tailscale_acl_emit { expected_tailscale_acl_json, tailscale_acl_json_emit_scaffold, tailscale_acl_json_emit_dissolution_trigger } +import gunbc.tailscale_acl_emit { expected_tailscale_acl_json } import extdeps.filesystem.filesystem_io { Filesystem } data tailscale_acl_reference_fixture_path: String = "dsl/test/fixture/tailscale_acl_reference.json" @@ -69,15 +68,6 @@ fn witness_dashboard_tag_not_ci_in_acl_model() -> Bool { && !string_contains(s: tag_dashboard, pattern: "tag:ci") } -fn witness_acl_json_emit_carries_dissolution_marker() -> Bool { - match tailscale_acl_json_emit_scaffold { - Scaffold { dissolves_to: SingleAuthority, bind: _ } => true - _ => false - } - && string_contains(s: tailscale_acl_json_emit_dissolution_trigger, pattern: "tailscale_acl_json_emit") - && string_contains(s: tailscale_acl_json_emit_dissolution_trigger, pattern: "medium_structure_exception_roster") -} - test fn tailscale_acl_witnesses() -> Bool { witness_acl_cites_tailscale_grants_authority() && witness_operator_policy_row_counts() @@ -88,5 +78,4 @@ test fn tailscale_acl_witnesses() -> Bool { && witness_security_spine() && witness_emit_matches_reference_fixture() && witness_dashboard_tag_not_ci_in_acl_model() - && witness_acl_json_emit_carries_dissolution_marker() } diff --git a/dsl/tools/roadmap_dispatch.dag b/dsl/tools/roadmap_dispatch.dag index a70759523be..75c2b21ac06 100644 --- a/dsl/tools/roadmap_dispatch.dag +++ b/dsl/tools/roadmap_dispatch.dag @@ -2,10 +2,29 @@ module tools.roadmap_dispatch import std.process { ProcessExit, ExitSuccess, exit_failure } import extdeps.filesystem.filesystem_io { Filesystem } +<<<<<<< HEAD import gunbc.roadmap_spawner { roadmap_dispatch_file_content } data roadmap_dispatch_output_path: String = "target/roadmap-dispatch.json" +======= +import gunbc.roadmap_authority { roadmap_authority, authored_merged_prs } +import gunbc.roadmap_spawner { roadmap_dispatch_json_value } +import extdeps.languages.json.emit { serialize_json, json_object, json_kv, json_string } + +data roadmap_dispatch_output_path: String = "target/roadmap-dispatch.json" + +data dispatch_brief_template: String = "Node: \nWhy now: \nNon-goals: \nM0 evidence: \nM1 smallest change: \nM2 RED control: \nM3 green receipt: \nStop and return if: \nFiles likely touched: \nCommands to run: \nExpected artifact: " + +fn roadmap_dispatch_content() -> String { + serialize_json(v: json_object(members: [ + json_kv(key: "schema", value: json_string(s: "roadmap-dispatch-file/v1")), + json_kv(key: "brief_template", value: json_string(s: dispatch_brief_template)), + json_kv(key: "dispatch", value: roadmap_dispatch_json_value(doc: roadmap_authority(), merged: authored_merged_prs())), + ])) +} + +>>>>>>> a68ecc417e (WIP: 2-emit-partition (c) — JSON grammar authority: one JSON emit authority i) func main() -> ProcessExit { let w = Filesystem.Write(path: roadmap_dispatch_output_path, content: roadmap_dispatch_file_content()) if w.success { diff --git a/src/v2/lens/disposition_redundancy_test.dag b/src/v2/lens/disposition_redundancy_test.dag index 7c45f2cf605..1302f7d421c 100644 --- a/src/v2/lens/disposition_redundancy_test.dag +++ b/src/v2/lens/disposition_redundancy_test.dag @@ -21,7 +21,6 @@ import extdeps.entropy { } import gunbc.tools.bmc_onboard { bmc_onboard_secret_on_disk_debt, - bmc_redfish_json_concat_debt } import gunbc.ci_runner_placement { gunbc_ci_cpu_weight_unit_modeling_marker, @@ -232,7 +231,6 @@ test fn redundancy_region_entropy_fires_on_bytesize_present() -> Bool { data redundancy_region_bmc_marks: List = [ bmc_onboard_secret_on_disk_debt, - bmc_redfish_json_concat_debt ] data redundancy_region_bmc_all_successors_present: List = [ @@ -241,11 +239,6 @@ data redundancy_region_bmc_all_successors_present: List = [ decl_name: "Filesystem", field: WholeDeclaration }, - DeclarationRef { - module_path: "std.primitives", - decl_name: "to_json_contract", - field: WholeDeclaration - } ] test fn redundancy_region_bmc_marks_nonempty() -> Bool { diff --git a/src/v2/lens/medium_structure_containment.dag b/src/v2/lens/medium_structure_containment.dag index 44e40653c51..9360fc84042 100644 --- a/src/v2/lens/medium_structure_containment.dag +++ b/src/v2/lens/medium_structure_containment.dag @@ -147,7 +147,6 @@ data medium_structure_exception_roster: List = [ "src/v2/test/claim/ci_spec_witness_test.dag", "dsl/gunbc/fleet_converge_emit.dag", - "dsl/gunbc/tailscale_acl_emit.dag", "dsl/gunbc/live_deploy/emit.dag", "dsl/gunbc/live_deploy/spec.dag", "dsl/gunbc/roadmap_static_site.dag", From df12985546679ae509e03e396fd08a39354dbf50 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 2 Jul 2026 05:18:37 +0000 Subject: [PATCH 03/11] =?UTF-8?q?WIP:=202-emit-partition=20(c)=20=E2=80=94?= =?UTF-8?q?=20JSON=20grammar=20authority:=20one=20JSON=20emit=20authority?= =?UTF-8?q?=20i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dsl/gunbc/roadmap_spawner.dag | 23 ++++++++--------------- dsl/tools/roadmap_dispatch.dag | 19 ------------------- 2 files changed, 8 insertions(+), 34 deletions(-) diff --git a/dsl/gunbc/roadmap_spawner.dag b/dsl/gunbc/roadmap_spawner.dag index fbc9de9b42b..e0bd1af9c3b 100644 --- a/dsl/gunbc/roadmap_spawner.dag +++ b/dsl/gunbc/roadmap_spawner.dag @@ -12,12 +12,7 @@ import gunbc.roadmap_model { } import gunbc.roadmap_status { RoadmapItem, DerivableLine, AuthoredLine, PlainLine, item_is_done } import gunbc.roadmap_document { RoadmapDocument, RoadmapSection } -<<<<<<< HEAD import gunbc.roadmap_authority { roadmap_authority, authored_merged_prs } -import std.disposition { Disposition, Scaffold, SingleAuthority } -import std.decl_ref { DeclarationRef, WholeDeclaration } -======= ->>>>>>> a68ecc417e (WIP: 2-emit-partition (c) — JSON grammar authority: one JSON emit authority i) import extdeps.languages.json.emit { JsonValue, JsonNull, serialize_json, @@ -261,19 +256,9 @@ fn done_ids_json(frontier_done: List) -> String { json_str_list(items: frontier_done |> map(n => n.node)) } -<<<<<<< HEAD data dispatch_brief_template: String = "Node: \nWhy now: \nNon-goals: \nM0 evidence: \nM1 smallest change: \nM2 RED control: \nM3 green receipt: \nStop and return if: \nFiles likely touched: \nCommands to run: \nExpected artifact: " -fn roadmap_dispatch_file_content() -> String { - concat("{\"schema\": \"roadmap-dispatch-file/v1\", \"brief_template\": ", - concat(json_str(s: dispatch_brief_template), - concat(", \"dispatch\": ", concat(roadmap_dispatch_json(doc: roadmap_authority(), merged: authored_merged_prs()), "}")))) -} - -fn roadmap_dispatch_json(doc: RoadmapDocument, merged: List) -> String { -======= fn roadmap_dispatch_json_value(doc: RoadmapDocument, merged: List) -> JsonValue { ->>>>>>> a68ecc417e (WIP: 2-emit-partition (c) — JSON grammar authority: one JSON emit authority i) let nodes = doc_all_nodes(doc: doc) let edges = doc_all_edges(doc: doc) let frontier = spawn_frontier(doc: doc, merged: merged) @@ -298,3 +283,11 @@ fn roadmap_dispatch_json_value(doc: RoadmapDocument, merged: List) -> JsonV fn roadmap_dispatch_json(doc: RoadmapDocument, merged: List) -> String { serialize_json(v: roadmap_dispatch_json_value(doc: doc, merged: merged)) } + +fn roadmap_dispatch_file_content() -> String { + serialize_json(v: json_object(members: [ + json_kv(key: "schema", value: json_string(s: "roadmap-dispatch-file/v1")), + json_kv(key: "brief_template", value: json_string(s: dispatch_brief_template)), + json_kv(key: "dispatch", value: roadmap_dispatch_json_value(doc: roadmap_authority(), merged: authored_merged_prs())), + ])) +} diff --git a/dsl/tools/roadmap_dispatch.dag b/dsl/tools/roadmap_dispatch.dag index 75c2b21ac06..a70759523be 100644 --- a/dsl/tools/roadmap_dispatch.dag +++ b/dsl/tools/roadmap_dispatch.dag @@ -2,29 +2,10 @@ module tools.roadmap_dispatch import std.process { ProcessExit, ExitSuccess, exit_failure } import extdeps.filesystem.filesystem_io { Filesystem } -<<<<<<< HEAD import gunbc.roadmap_spawner { roadmap_dispatch_file_content } data roadmap_dispatch_output_path: String = "target/roadmap-dispatch.json" -======= -import gunbc.roadmap_authority { roadmap_authority, authored_merged_prs } -import gunbc.roadmap_spawner { roadmap_dispatch_json_value } -import extdeps.languages.json.emit { serialize_json, json_object, json_kv, json_string } - -data roadmap_dispatch_output_path: String = "target/roadmap-dispatch.json" - -data dispatch_brief_template: String = "Node: \nWhy now: \nNon-goals: \nM0 evidence: \nM1 smallest change: \nM2 RED control: \nM3 green receipt: \nStop and return if: \nFiles likely touched: \nCommands to run: \nExpected artifact: " - -fn roadmap_dispatch_content() -> String { - serialize_json(v: json_object(members: [ - json_kv(key: "schema", value: json_string(s: "roadmap-dispatch-file/v1")), - json_kv(key: "brief_template", value: json_string(s: dispatch_brief_template)), - json_kv(key: "dispatch", value: roadmap_dispatch_json_value(doc: roadmap_authority(), merged: authored_merged_prs())), - ])) -} - ->>>>>>> a68ecc417e (WIP: 2-emit-partition (c) — JSON grammar authority: one JSON emit authority i) func main() -> ProcessExit { let w = Filesystem.Write(path: roadmap_dispatch_output_path, content: roadmap_dispatch_file_content()) if w.success { From 5151000986181a787e53fd45ed0833e76cd3e01a Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 2 Jul 2026 05:30:38 +0000 Subject: [PATCH 04/11] =?UTF-8?q?WIP:=202-emit-partition=20(c)=20=E2=80=94?= =?UTF-8?q?=20JSON=20grammar=20authority:=20one=20JSON=20emit=20authority?= =?UTF-8?q?=20i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MODULE.bazel | 1 + dsl/extdeps/languages/json/emit.dag | 1 - dsl/extdeps/languages/json/grammar.dag | 2 +- dsl/gunbc/tailscale_acl_emit.dag | 2 +- dsl/test/claim/json_emit_witness_test.dag | 3 +- dsl/test/fixture/tailscale_acl_reference.json | 36 +------------------ 6 files changed, 5 insertions(+), 40 deletions(-) create mode 100644 MODULE.bazel diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 00000000000..95da6de7c9f --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1 @@ +# Temporary marker for BuildBuddy Remote Bazel script mode in non-Bazel repos. diff --git a/dsl/extdeps/languages/json/emit.dag b/dsl/extdeps/languages/json/emit.dag index bf6d657e8d1..93144f1150d 100644 --- a/dsl/extdeps/languages/json/emit.dag +++ b/dsl/extdeps/languages/json/emit.dag @@ -2,7 +2,6 @@ module extdeps.languages.json.emit import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } -import extdeps.languages.json.grammar { json_production_rows } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { diff --git a/dsl/extdeps/languages/json/grammar.dag b/dsl/extdeps/languages/json/grammar.dag index 021fe229f87..072acbaecfd 100644 --- a/dsl/extdeps/languages/json/grammar.dag +++ b/dsl/extdeps/languages/json/grammar.dag @@ -28,4 +28,4 @@ data json_production_rows: List = [ JsonProductionRow { rfc_section: "3", lhs: "literal", rhs: "'null' | 'true' | 'false'", value_variant: "JsonNull|JsonBool" }, ] -data json_emit_fold_dissolution_trigger: String = "dissolve-on: v2 grammar-inverse ingest for JSON — dsl/extdeps/languages/json/{grammar,emit}.dag is the single dsl-tree authority (RFC 8259 rows + JsonValue fold); DISSOLVES WHEN src/v2/extdeps/languages/json.dag lands FormalProduction/TargetModel rows wired through 06_translate with ingest ∘ emit = id on json_production_rows witnesses" +data json_emit_fold_dissolution_trigger: String = "dissolve-on: v2 grammar-inverse ingest for JSON — dsl/extdeps/languages/json/grammar.dag plus emit.dag is the single dsl-tree authority (RFC 8259 rows + JsonValue fold); DISSOLVES WHEN src/v2/extdeps/languages/json.dag lands FormalProduction/TargetModel rows wired through 06_translate with ingest o emit = id on json_production_rows witnesses" diff --git a/dsl/gunbc/tailscale_acl_emit.dag b/dsl/gunbc/tailscale_acl_emit.dag index 83c6f21eebb..8d4199aea56 100644 --- a/dsl/gunbc/tailscale_acl_emit.dag +++ b/dsl/gunbc/tailscale_acl_emit.dag @@ -66,5 +66,5 @@ fn tailscale_acl_policy_json(policy: TailscaleAclPolicy) -> JsonValue { } fn expected_tailscale_acl_json() -> String { - serialize_json(v: tailscale_acl_policy_json(policy: operator_tailscale_acl_policy)) + concat(serialize_json(v: tailscale_acl_policy_json(policy: operator_tailscale_acl_policy)), "\n") } diff --git a/dsl/test/claim/json_emit_witness_test.dag b/dsl/test/claim/json_emit_witness_test.dag index 925c6f145a3..a660593263d 100644 --- a/dsl/test/claim/json_emit_witness_test.dag +++ b/dsl/test/claim/json_emit_witness_test.dag @@ -64,8 +64,7 @@ fn witness_serialize_nested_object() -> Bool { } fn witness_serialize_array() -> Bool { - serialize_json(v: json_array(elements: [json_string(s: "a"), json_string(s: "b")])) - == "[\"a\", \"b\"]" + serialize_json(v: json_array(elements: [json_string(s: "a"), json_string(s: "b")])) == "[\"a\", \"b\"]" } fn witness_int_list_helper() -> Bool { diff --git a/dsl/test/fixture/tailscale_acl_reference.json b/dsl/test/fixture/tailscale_acl_reference.json index 32366b7d099..77aa04a58bc 100644 --- a/dsl/test/fixture/tailscale_acl_reference.json +++ b/dsl/test/fixture/tailscale_acl_reference.json @@ -1,35 +1 @@ -{ - "grants": [ - {"src": ["autogroup:member"], "dst": ["*"], "ip": ["*"]}, - {"src": ["tag:ci"], "dst": ["100.65.94.55", "tag:dashboard"], "ip": ["tcp:3737"]}, - {"src": ["tag:dashboard"], "dst": ["tag:dashboard"], "ip": ["*"]}, - {"src": ["tag:dashboard"], "dst": ["tag:ci"], "ip": ["tcp:22"]} - ], - "ssh": [ - { - "action": "accept", - "src": ["autogroup:member"], - "dst": ["autogroup:self"], - "users": ["autogroup:nonroot", "root"] - }, - { - "action": "accept", - "src": ["autogroup:member"], - "dst": ["tag:ci"], - "users": ["autogroup:nonroot"] - }, - { - "action": "accept", - "src": ["tag:dashboard"], - "dst": ["tag:ci"], - "users": ["autogroup:nonroot", "root"] - } - ], - "nodeAttrs": [ - {"target": ["autogroup:member"], "attr": ["funnel"]} - ], - "tagOwners": { - "tag:ci": ["autogroup:admin"], - "tag:dashboard": ["autogroup:admin"] - } -} +{"grants": [{"src": ["autogroup:member"], "dst": ["*"], "ip": ["*"]}, {"src": ["tag:ci"], "dst": ["100.65.94.55", "tag:dashboard"], "ip": ["tcp:3737"]}, {"src": ["tag:dashboard"], "dst": ["tag:dashboard"], "ip": ["*"]}, {"src": ["tag:dashboard"], "dst": ["tag:ci"], "ip": ["tcp:22"]}], "ssh": [{"action": "accept", "src": ["autogroup:member"], "dst": ["autogroup:self"], "users": ["autogroup:nonroot", "root"]}, {"action": "accept", "src": ["autogroup:member"], "dst": ["tag:ci"], "users": ["autogroup:nonroot"]}, {"action": "accept", "src": ["tag:dashboard"], "dst": ["tag:ci"], "users": ["autogroup:nonroot", "root"]}], "nodeAttrs": [{"target": ["autogroup:member"], "attr": ["funnel"]}], "tagOwners": {"tag:ci": ["autogroup:admin"], "tag:dashboard": ["autogroup:admin"]}} From 5b4ec50d30ee68c1d497b4cdbebd975d7571d399 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 2 Jul 2026 05:32:07 +0000 Subject: [PATCH 05/11] Remove accidental MODULE.bazel from JSON emit authority PR. Co-authored-by: Cursor --- MODULE.bazel | 1 - 1 file changed, 1 deletion(-) delete mode 100644 MODULE.bazel diff --git a/MODULE.bazel b/MODULE.bazel deleted file mode 100644 index 95da6de7c9f..00000000000 --- a/MODULE.bazel +++ /dev/null @@ -1 +0,0 @@ -# Temporary marker for BuildBuddy Remote Bazel script mode in non-Bazel repos. From bec8cce611ad102298ec9d5e1722c0eb259ad11a Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 2 Jul 2026 05:45:38 +0000 Subject: [PATCH 06/11] =?UTF-8?q?WIP:=202-emit-partition=20(c)=20=E2=80=94?= =?UTF-8?q?=20JSON=20grammar=20authority:=20one=20JSON=20emit=20authority?= =?UTF-8?q?=20i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dsl/extdeps/languages/json/emit.dag | 36 ++++++++++++++++++++- dsl/test/claim/json_emit_witness_test.dag | 15 +++++++++ src/v2/lens/disposition_redundancy_test.dag | 2 +- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/dsl/extdeps/languages/json/emit.dag b/dsl/extdeps/languages/json/emit.dag index 93144f1150d..7304b0ac3bc 100644 --- a/dsl/extdeps/languages/json/emit.dag +++ b/dsl/extdeps/languages/json/emit.dag @@ -2,6 +2,7 @@ module extdeps.languages.json.emit import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } +import std.unicode { code_point } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { @@ -55,8 +56,41 @@ fn json_object(members: List) -> JsonValue { JsonObject { members: members } } +fn hex_digit_char(d: Int) -> String { + if d < 10 { from_code_point(cp: 48 + d) } + else { from_code_point(cp: 55 + d) } +} + +fn int_to_upper_hex_inner(n: Int, acc: String) -> String { + if n == 0 { acc } + else { int_to_upper_hex_inner(n: n / 16, acc: concat(hex_digit_char(d: n % 16), acc)) } +} + +fn int_to_upper_hex(n: Int) -> String { + if n == 0 { "0" } + else { int_to_upper_hex_inner(n: n, acc: "") } +} + +fn json_hex4(cp: Int) -> String { + let h = int_to_upper_hex(n: cp) + if cp <= 15 { concat("000", h) } + else { concat("00", h) } +} + +fn json_escape_code_point(cp: Int) -> String { + if cp == 8 { "\\b" } + else if cp == 9 { "\\t" } + else if cp == 10 { "\\n" } + else if cp == 12 { "\\f" } + else if cp == 13 { "\\r" } + else if cp == 34 { "\\\"" } + else if cp == 92 { "\\\\" } + else if cp >= 0 && cp <= 31 { concat("\\u", json_hex4(cp: cp)) } + else { from_code_point(cp: cp) } +} + fn escape_json_string(s: String) -> String { - replace(replace(replace(s, "\\", "\\\\"), "\"", "\\\""), "\n", "\\n") + fold(chars(s: s), init: "", f: (acc, c) => concat(acc, json_escape_code_point(cp: code_point(c: c)))) } fn quote_json_string(s: String) -> String { diff --git a/dsl/test/claim/json_emit_witness_test.dag b/dsl/test/claim/json_emit_witness_test.dag index a660593263d..ad7641c4b18 100644 --- a/dsl/test/claim/json_emit_witness_test.dag +++ b/dsl/test/claim/json_emit_witness_test.dag @@ -32,6 +32,18 @@ fn witness_escape_newline() -> Bool { escape_json_string(s: "line one\nline two") == "line one\\nline two" } +fn witness_escape_tab() -> Bool { + escape_json_string(s: concat("a", concat(from_code_point(cp: 9), "b"))) == "a\\tb" +} + +fn witness_escape_carriage_return() -> Bool { + escape_json_string(s: concat("x", concat(from_code_point(cp: 13), "y"))) == "x\\ry" +} + +fn witness_escape_control_u0001() -> Bool { + escape_json_string(s: from_code_point(cp: 1)) == "\\u0001" +} + fn witness_quote_json_string() -> Bool { quote_json_string(s: "hello") == "\"hello\"" && quote_json_string(s: "a\"b") == "\"a\\\"b\"" @@ -83,6 +95,9 @@ test fn json_emit_witnesses() -> Bool { witness_json_cites_rfc8259() && witness_escape_backslash_and_quote() && witness_escape_newline() + && witness_escape_tab() + && witness_escape_carriage_return() + && witness_escape_control_u0001() && witness_quote_json_string() && witness_serialize_null() && witness_serialize_bool() diff --git a/src/v2/lens/disposition_redundancy_test.dag b/src/v2/lens/disposition_redundancy_test.dag index 1302f7d421c..49cb7c82280 100644 --- a/src/v2/lens/disposition_redundancy_test.dag +++ b/src/v2/lens/disposition_redundancy_test.dag @@ -256,7 +256,7 @@ test fn redundancy_region_bmc_fires_on_all_successors_present() -> Bool { disposition_redundancy_violation_count( dispositions: redundancy_region_bmc_marks, present: redundancy_region_bmc_all_successors_present - ) == 2 + ) == 1 } data redundancy_region_runner_placement_marks: List = [ From be053cf04969f7bcc056832d8f7434400e37ad24 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 2 Jul 2026 05:45:52 +0000 Subject: [PATCH 07/11] Fix RFC 8259 string escaping and disposition redundancy witness count. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit escape_json_string now escapes all U+0000–U+001F control characters per RFC 8259 §7 (short escapes plus \uXXXX), with discriminating witnesses for tab and U+0001. disposition_redundancy_test BMC region expectation is 1 after bmc_redfish_json_concat_debt removal. Co-authored-by: Cursor --- dsl/extdeps/languages/json/emit.dag | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dsl/extdeps/languages/json/emit.dag b/dsl/extdeps/languages/json/emit.dag index 7304b0ac3bc..0af3bf5c9db 100644 --- a/dsl/extdeps/languages/json/emit.dag +++ b/dsl/extdeps/languages/json/emit.dag @@ -2,7 +2,6 @@ module extdeps.languages.json.emit import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } -import std.unicode { code_point } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { @@ -90,7 +89,7 @@ fn json_escape_code_point(cp: Int) -> String { } fn escape_json_string(s: String) -> String { - fold(chars(s: s), init: "", f: (acc, c) => concat(acc, json_escape_code_point(cp: code_point(c: c)))) + fold(chars(s: s), init: "", f: (acc, cp) => concat(acc, json_escape_code_point(cp: cp))) } fn quote_json_string(s: String) -> String { From fb1adb56fbd7defcbcf74098bbe1244630e08e03 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 2 Jul 2026 06:12:39 +0000 Subject: [PATCH 08/11] =?UTF-8?q?WIP:=202-emit-partition=20(c)=20=E2=80=94?= =?UTF-8?q?=20JSON=20grammar=20authority:=20one=20JSON=20emit=20authority?= =?UTF-8?q?=20i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dsl/extdeps/languages/json/emit.dag | 22 ++-- dsl/extdeps/languages/json/grammar.dag | 122 ++++++++++++++++++++++ dsl/test/claim/json_emit_witness_test.dag | 34 +++++- 3 files changed, 166 insertions(+), 12 deletions(-) diff --git a/dsl/extdeps/languages/json/emit.dag b/dsl/extdeps/languages/json/emit.dag index 0af3bf5c9db..96ae515291b 100644 --- a/dsl/extdeps/languages/json/emit.dag +++ b/dsl/extdeps/languages/json/emit.dag @@ -1,6 +1,7 @@ module extdeps.languages.json.emit import extdeps.external_authority { ExternalAuthority } +import extdeps.languages.json.grammar { JsonNumberLexeme, json_int_lexeme } import extdeps.uri { Uri, Https } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { @@ -18,7 +19,7 @@ type JsonKeyValue { type JsonValue = JsonNull | JsonBool { value: Bool } - | JsonNumber { lexeme: String } + | JsonNumber { lexeme: JsonNumberLexeme } | JsonString { value: String } | JsonArray { elements: List } | JsonObject { members: List } @@ -35,12 +36,8 @@ fn json_bool(b: Bool) -> JsonValue { JsonBool { value: b } } -fn json_number(lexeme: String) -> JsonValue { - JsonNumber { lexeme: lexeme } -} - fn json_int(n: Int) -> JsonValue { - JsonNumber { lexeme: to_string(n) } + JsonNumber { lexeme: json_int_lexeme(n: n) } } fn json_string(s: String) -> JsonValue { @@ -70,10 +67,13 @@ fn int_to_upper_hex(n: Int) -> String { else { int_to_upper_hex_inner(n: n, acc: "") } } -fn json_hex4(cp: Int) -> String { +fn json_unicode_escape_hex4(cp: Int) -> String { let h = int_to_upper_hex(n: cp) - if cp <= 15 { concat("000", h) } - else { concat("00", h) } + let len = h.length() + if len == 1 { concat("000", h) } + else if len == 2 { concat("00", h) } + else if len == 3 { concat("0", h) } + else { h } } fn json_escape_code_point(cp: Int) -> String { @@ -84,7 +84,7 @@ fn json_escape_code_point(cp: Int) -> String { else if cp == 13 { "\\r" } else if cp == 34 { "\\\"" } else if cp == 92 { "\\\\" } - else if cp >= 0 && cp <= 31 { concat("\\u", json_hex4(cp: cp)) } + else if cp >= 0 && cp <= 31 { concat("\\u", json_unicode_escape_hex4(cp: cp)) } else { from_code_point(cp: cp) } } @@ -128,7 +128,7 @@ fn serialize_json(v: JsonValue) -> String { match v { JsonNull => "null" JsonBool { value: b } => if b { "true" } else { "false" } - JsonNumber { lexeme: l } => l + JsonNumber { lexeme: l } => l.lexeme JsonString { value: s } => quote_json_string(s: s) JsonArray { elements: els } => serialize_json_array(elements: els) JsonObject { members: m } => serialize_json_object(members: m) diff --git a/dsl/extdeps/languages/json/grammar.dag b/dsl/extdeps/languages/json/grammar.dag index 072acbaecfd..b2125a4fd67 100644 --- a/dsl/extdeps/languages/json/grammar.dag +++ b/dsl/extdeps/languages/json/grammar.dag @@ -29,3 +29,125 @@ data json_production_rows: List = [ ] data json_emit_fold_dissolution_trigger: String = "dissolve-on: v2 grammar-inverse ingest for JSON — dsl/extdeps/languages/json/grammar.dag plus emit.dag is the single dsl-tree authority (RFC 8259 rows + JsonValue fold); DISSOLVES WHEN src/v2/extdeps/languages/json.dag lands FormalProduction/TargetModel rows wired through 06_translate with ingest o emit = id on json_production_rows witnesses" + +type JsonNumberLexeme { + lexeme: String +} + +type JsonNumberParseRest { + rest: String + ok: Bool +} + +fn is_json_digit(cp: Int) -> Bool { + cp >= 48 && cp <= 57 +} + +fn is_json_digit1(cp: Int) -> Bool { + cp >= 49 && cp <= 57 +} + +fn json_number_consume_digit_run(s: String) -> JsonNumberParseRest { + if s.length() == 0 { + JsonNumberParseRest { rest: "", ok: true } + } else { + let first = code_point_int(ch: char_at(s, 0)) + if !is_json_digit(cp: first) { + JsonNumberParseRest { rest: s, ok: true } + } else { + let inner = json_number_consume_digit_run(s: substring(s: s, start: 1, end: s.length())) + JsonNumberParseRest { rest: inner.rest, ok: inner.ok } + } + } +} + +fn json_number_consume_int(s: String) -> JsonNumberParseRest { + if s == "0" { + JsonNumberParseRest { rest: "", ok: true } + } else if s.length() == 0 { + JsonNumberParseRest { rest: s, ok: false } + } else { + let first = code_point_int(ch: char_at(s, 0)) + if !is_json_digit1(cp: first) { + JsonNumberParseRest { rest: s, ok: false } + } else { + json_number_consume_digit_run(s: s) + } + } +} + +fn json_number_consume_frac_digits(s: String) -> JsonNumberParseRest { + if s.length() == 0 { + JsonNumberParseRest { rest: "", ok: false } + } else { + let first = code_point_int(ch: char_at(s, 0)) + if !is_json_digit(cp: first) { + JsonNumberParseRest { rest: s, ok: false } + } else { + json_number_consume_digit_run(s: s) + } + } +} + +fn json_number_consume_optional_frac(s: String) -> JsonNumberParseRest { + if s.length() == 0 { + JsonNumberParseRest { rest: "", ok: true } + } else if char_at(s, 0) != "." { + JsonNumberParseRest { rest: s, ok: true } + } else { + let after = substring(s: s, start: 1, end: s.length()) + let parsed = json_number_consume_frac_digits(s: after) + JsonNumberParseRest { rest: parsed.rest, ok: parsed.ok } + } +} + +fn json_number_consume_optional_exp(s: String) -> Bool { + if s.length() == 0 { + true + } else { + let marker = code_point_int(ch: char_at(s, 0)) + if marker != 69 && marker != 101 { + false + } else { + let after = substring(s: s, start: 1, end: s.length()) + let signed = if after.length() > 0 && (char_at(after, 0) == "+" || char_at(after, 0) == "-") { + substring(s: after, start: 1, end: after.length()) + } else { + after + } + let parsed = json_number_consume_frac_digits(s: signed) + parsed.ok && parsed.rest.length() == 0 + } + } +} + +fn json_number_lexeme_valid(s: String) -> Bool { + if s.length() == 0 { + false + } else { + let body = if starts_with(s: s, prefix: "-") { + substring(s: s, start: 1, end: s.length()) + } else { + s + } + if starts_with(s: s, prefix: "-") && body.length() == 0 { + false + } else { + let int_parsed = json_number_consume_int(s: body) + if !int_parsed.ok { + false + } else { + let frac_parsed = json_number_consume_optional_frac(s: int_parsed.rest) + if !frac_parsed.ok { + false + } else { + json_number_consume_optional_exp(s: frac_parsed.rest) + } + } + } + } +} + +fn json_int_lexeme(n: Int) -> JsonNumberLexeme { + JsonNumberLexeme { lexeme: to_string(n) } +} diff --git a/dsl/test/claim/json_emit_witness_test.dag b/dsl/test/claim/json_emit_witness_test.dag index ad7641c4b18..1e35f20c915 100644 --- a/dsl/test/claim/json_emit_witness_test.dag +++ b/dsl/test/claim/json_emit_witness_test.dag @@ -15,8 +15,13 @@ import extdeps.languages.json.emit { json_kv, json_int_list, json_str_list, + json_unicode_escape_hex4, +} +import extdeps.languages.json.grammar { + json_production_rows, + json_number_lexeme_valid, + json_int_lexeme, } -import extdeps.languages.json.grammar { json_production_rows } fn witness_json_cites_rfc8259() -> Bool { match extdeps_external_authority_anchor { @@ -91,6 +96,33 @@ fn witness_grammar_rows_cover_json_value_variants() -> Bool { length(xs: json_production_rows) >= 8 } +fn witness_invalid_number_lexeme_rejected() -> Bool { + !json_number_lexeme_valid(s: "not-a-number") + && !json_number_lexeme_valid(s: "12abc") + && !json_number_lexeme_valid(s: "") + && !json_number_lexeme_valid(s: "01") +} + +fn witness_json_int_lexeme_always_valid() -> Bool { + json_number_lexeme_valid(s: json_int_lexeme(n: 42).lexeme) + && json_number_lexeme_valid(s: json_int_lexeme(n: -7).lexeme) + && json_number_lexeme_valid(s: json_int_lexeme(n: 0).lexeme) +} + +fn witness_json_number_lexeme_valid_accepts_fraction_and_exp() -> Bool { + json_number_lexeme_valid(s: "12.34") + && json_number_lexeme_valid(s: "1e10") + && json_number_lexeme_valid(s: "-0.5e+2") +} + +fn witness_unicode_escape_hex4_boundary_0fff() -> Bool { + json_unicode_escape_hex4(cp: 4095) == "0FFF" +} + +fn witness_unicode_escape_hex4_boundary_ffff() -> Bool { + json_unicode_escape_hex4(cp: 65535) == "FFFF" +} + test fn json_emit_witnesses() -> Bool { witness_json_cites_rfc8259() && witness_escape_backslash_and_quote() From 36f685512a8f3ffc787338caf6b83c46a4300ecf Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 2 Jul 2026 06:24:41 +0000 Subject: [PATCH 09/11] Fix JsonNumber grammar validation and wire operator-review witnesses. Ground number lexemes on RFC 8259 parse rules without code_point_int, fix leading-zero fractional acceptance, enroll RED witnesses for invalid lexeme refusal and unicode escape hex4 boundaries (0x0FFF, 0xFFFF). Co-authored-by: Cursor --- dsl/extdeps/languages/json/grammar.dag | 35 +++++++++++------------ dsl/test/claim/json_emit_witness_test.dag | 5 ++++ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/dsl/extdeps/languages/json/grammar.dag b/dsl/extdeps/languages/json/grammar.dag index b2125a4fd67..4027bef3597 100644 --- a/dsl/extdeps/languages/json/grammar.dag +++ b/dsl/extdeps/languages/json/grammar.dag @@ -39,20 +39,20 @@ type JsonNumberParseRest { ok: Bool } -fn is_json_digit(cp: Int) -> Bool { - cp >= 48 && cp <= 57 +fn is_json_digit_char(c: String) -> Bool { + (c != "") && string_contains(s: "0123456789", pattern: c) } -fn is_json_digit1(cp: Int) -> Bool { - cp >= 49 && cp <= 57 +fn is_json_digit1_char(c: String) -> Bool { + (c != "") && string_contains(s: "123456789", pattern: c) } fn json_number_consume_digit_run(s: String) -> JsonNumberParseRest { if s.length() == 0 { JsonNumberParseRest { rest: "", ok: true } } else { - let first = code_point_int(ch: char_at(s, 0)) - if !is_json_digit(cp: first) { + let first = char_at(s, 0) + if !is_json_digit_char(c: first) { JsonNumberParseRest { rest: s, ok: true } } else { let inner = json_number_consume_digit_run(s: substring(s: s, start: 1, end: s.length())) @@ -62,17 +62,14 @@ fn json_number_consume_digit_run(s: String) -> JsonNumberParseRest { } fn json_number_consume_int(s: String) -> JsonNumberParseRest { - if s == "0" { - JsonNumberParseRest { rest: "", ok: true } - } else if s.length() == 0 { + if s.length() == 0 { + JsonNumberParseRest { rest: s, ok: false } + } else if char_at(s, 0) == "0" { + JsonNumberParseRest { rest: substring(s: s, start: 1, end: s.length()), ok: true } + } else if !is_json_digit1_char(c: char_at(s, 0)) { JsonNumberParseRest { rest: s, ok: false } } else { - let first = code_point_int(ch: char_at(s, 0)) - if !is_json_digit1(cp: first) { - JsonNumberParseRest { rest: s, ok: false } - } else { - json_number_consume_digit_run(s: s) - } + json_number_consume_digit_run(s: s) } } @@ -80,8 +77,8 @@ fn json_number_consume_frac_digits(s: String) -> JsonNumberParseRest { if s.length() == 0 { JsonNumberParseRest { rest: "", ok: false } } else { - let first = code_point_int(ch: char_at(s, 0)) - if !is_json_digit(cp: first) { + let first = char_at(s, 0) + if !is_json_digit_char(c: first) { JsonNumberParseRest { rest: s, ok: false } } else { json_number_consume_digit_run(s: s) @@ -105,8 +102,8 @@ fn json_number_consume_optional_exp(s: String) -> Bool { if s.length() == 0 { true } else { - let marker = code_point_int(ch: char_at(s, 0)) - if marker != 69 && marker != 101 { + let marker = char_at(s, 0) + if marker != "E" && marker != "e" { false } else { let after = substring(s: s, start: 1, end: s.length()) diff --git a/dsl/test/claim/json_emit_witness_test.dag b/dsl/test/claim/json_emit_witness_test.dag index 1e35f20c915..58c33d40ed1 100644 --- a/dsl/test/claim/json_emit_witness_test.dag +++ b/dsl/test/claim/json_emit_witness_test.dag @@ -140,4 +140,9 @@ test fn json_emit_witnesses() -> Bool { && witness_int_list_helper() && witness_str_list_helper() && witness_grammar_rows_cover_json_value_variants() + && witness_invalid_number_lexeme_rejected() + && witness_json_int_lexeme_always_valid() + && witness_json_number_lexeme_valid_accepts_fraction_and_exp() + && witness_unicode_escape_hex4_boundary_0fff() + && witness_unicode_escape_hex4_boundary_ffff() } From 68fa66e6d5becb93ed7b7ee77defab9eea4f7e34 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 2 Jul 2026 06:29:26 +0000 Subject: [PATCH 10/11] Brand JsonNumberLexeme so invalid lexemes are unwritable by construction. Replace the plain record carrier with `String where brand("JsonNumberLexeme")`; json_int_lexeme is the sole mint site (`as` cast inside grammar.dag only). Add grep witnesses that the record form is gone and emit.dag has no unblessed cast. Co-authored-by: Cursor --- dsl/extdeps/languages/json/emit.dag | 2 +- dsl/extdeps/languages/json/grammar.dag | 7 ++--- dsl/test/claim/json_emit_witness_test.dag | 37 +++++++++++++++++++++-- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/dsl/extdeps/languages/json/emit.dag b/dsl/extdeps/languages/json/emit.dag index 96ae515291b..775eac87c11 100644 --- a/dsl/extdeps/languages/json/emit.dag +++ b/dsl/extdeps/languages/json/emit.dag @@ -128,7 +128,7 @@ fn serialize_json(v: JsonValue) -> String { match v { JsonNull => "null" JsonBool { value: b } => if b { "true" } else { "false" } - JsonNumber { lexeme: l } => l.lexeme + JsonNumber { lexeme: l } => l JsonString { value: s } => quote_json_string(s: s) JsonArray { elements: els } => serialize_json_array(elements: els) JsonObject { members: m } => serialize_json_object(members: m) diff --git a/dsl/extdeps/languages/json/grammar.dag b/dsl/extdeps/languages/json/grammar.dag index 4027bef3597..b1d55147ad0 100644 --- a/dsl/extdeps/languages/json/grammar.dag +++ b/dsl/extdeps/languages/json/grammar.dag @@ -1,5 +1,6 @@ module extdeps.languages.json.grammar +import std.types { String } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } @@ -30,9 +31,7 @@ data json_production_rows: List = [ data json_emit_fold_dissolution_trigger: String = "dissolve-on: v2 grammar-inverse ingest for JSON — dsl/extdeps/languages/json/grammar.dag plus emit.dag is the single dsl-tree authority (RFC 8259 rows + JsonValue fold); DISSOLVES WHEN src/v2/extdeps/languages/json.dag lands FormalProduction/TargetModel rows wired through 06_translate with ingest o emit = id on json_production_rows witnesses" -type JsonNumberLexeme { - lexeme: String -} +type JsonNumberLexeme = String where brand("JsonNumberLexeme") type JsonNumberParseRest { rest: String @@ -146,5 +145,5 @@ fn json_number_lexeme_valid(s: String) -> Bool { } fn json_int_lexeme(n: Int) -> JsonNumberLexeme { - JsonNumberLexeme { lexeme: to_string(n) } + to_string(n) as JsonNumberLexeme } diff --git a/dsl/test/claim/json_emit_witness_test.dag b/dsl/test/claim/json_emit_witness_test.dag index 58c33d40ed1..e455b2f1831 100644 --- a/dsl/test/claim/json_emit_witness_test.dag +++ b/dsl/test/claim/json_emit_witness_test.dag @@ -1,5 +1,6 @@ module test.claim.json_emit_witness +import extdeps.filesystem.filesystem_io import extdeps.external_authority { ExternalAuthority } import extdeps.languages.json.emit { extdeps_external_authority_anchor, @@ -103,10 +104,36 @@ fn witness_invalid_number_lexeme_rejected() -> Bool { && !json_number_lexeme_valid(s: "01") } +data json_grammar_path: String = "dsl/extdeps/languages/json/grammar.dag" +data json_emit_path: String = "dsl/extdeps/languages/json/emit.dag" + +fn witness_json_number_lexeme_is_branded_string() -> Bool { + let grammar = filesystem_read(path: json_grammar_path) + string_contains( + s: grammar.content, + pattern: "type JsonNumberLexeme = String where brand(\"JsonNumberLexeme\")" + ) +} + +fn witness_json_number_lexeme_record_carrier_gone() -> Bool { + let grammar = filesystem_read(path: json_grammar_path) + !string_contains(s: grammar.content, pattern: "JsonNumberLexeme { lexeme") +} + +fn witness_grammar_mints_json_number_lexeme() -> Bool { + let grammar = filesystem_read(path: json_grammar_path) + string_contains(s: grammar.content, pattern: "as JsonNumberLexeme") +} + +fn witness_emit_has_no_unblessed_json_number_lexeme_cast() -> Bool { + let emit = filesystem_read(path: json_emit_path) + !string_contains(s: emit.content, pattern: "as JsonNumberLexeme") +} + fn witness_json_int_lexeme_always_valid() -> Bool { - json_number_lexeme_valid(s: json_int_lexeme(n: 42).lexeme) - && json_number_lexeme_valid(s: json_int_lexeme(n: -7).lexeme) - && json_number_lexeme_valid(s: json_int_lexeme(n: 0).lexeme) + json_number_lexeme_valid(s: json_int_lexeme(n: 42) as String) + && json_number_lexeme_valid(s: json_int_lexeme(n: -7) as String) + && json_number_lexeme_valid(s: json_int_lexeme(n: 0) as String) } fn witness_json_number_lexeme_valid_accepts_fraction_and_exp() -> Bool { @@ -141,6 +168,10 @@ test fn json_emit_witnesses() -> Bool { && witness_str_list_helper() && witness_grammar_rows_cover_json_value_variants() && witness_invalid_number_lexeme_rejected() + && witness_json_number_lexeme_is_branded_string() + && witness_json_number_lexeme_record_carrier_gone() + && witness_grammar_mints_json_number_lexeme() + && witness_emit_has_no_unblessed_json_number_lexeme_cast() && witness_json_int_lexeme_always_valid() && witness_json_number_lexeme_valid_accepts_fraction_and_exp() && witness_unicode_escape_hex4_boundary_0fff() From 6546a9b026f07cb73a02b08486d6ab9941cf7c87 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 2 Jul 2026 06:32:08 +0000 Subject: [PATCH 11/11] Split JSON corpus grep witnesses out of hermetic floor discovery. Move filesystem_read brand/mint-site greps into wet-only json_emit_corpus_grep_witness (ProcessExit, not *_test.dag); keep json_emit_witnesses value-level oracles hermetic for the floor. Co-authored-by: Cursor --- dsl/test/claim/json_emit_corpus_witness.dag | 43 +++++++++++++++++++++ dsl/test/claim/json_emit_witness_test.dag | 31 --------------- 2 files changed, 43 insertions(+), 31 deletions(-) create mode 100644 dsl/test/claim/json_emit_corpus_witness.dag diff --git a/dsl/test/claim/json_emit_corpus_witness.dag b/dsl/test/claim/json_emit_corpus_witness.dag new file mode 100644 index 00000000000..f4fe0a06d2b --- /dev/null +++ b/dsl/test/claim/json_emit_corpus_witness.dag @@ -0,0 +1,43 @@ +module test.claim.json_emit_corpus_witness + +import std.process { ProcessExit, ExitSuccess, exit_failure } +import extdeps.filesystem.filesystem_io + +data json_grammar_path: String = "dsl/extdeps/languages/json/grammar.dag" +data json_emit_path: String = "dsl/extdeps/languages/json/emit.dag" + +fn json_number_lexeme_branded_in_grammar(grammar_content: String) -> Bool { + string_contains( + s: grammar_content, + pattern: "type JsonNumberLexeme = String where brand(\"JsonNumberLexeme\")" + ) +} + +fn json_number_lexeme_record_carrier_gone(grammar_content: String) -> Bool { + !string_contains(s: grammar_content, pattern: "JsonNumberLexeme { lexeme") +} + +fn grammar_mints_json_number_lexeme(grammar_content: String) -> Bool { + string_contains(s: grammar_content, pattern: "as JsonNumberLexeme") +} + +fn emit_has_no_unblessed_json_number_lexeme_cast(emit_content: String) -> Bool { + !string_contains(s: emit_content, pattern: "as JsonNumberLexeme") +} + +fn all_json_emit_corpus_grep_witnesses() -> Bool { + let grammar = filesystem_read(path: json_grammar_path) + let emit = filesystem_read(path: json_emit_path) + json_number_lexeme_branded_in_grammar(grammar_content: grammar.content) + && json_number_lexeme_record_carrier_gone(grammar_content: grammar.content) + && grammar_mints_json_number_lexeme(grammar_content: grammar.content) + && emit_has_no_unblessed_json_number_lexeme_cast(emit_content: emit.content) +} + +func json_emit_corpus_grep_witness() -> ProcessExit { + return if all_json_emit_corpus_grep_witnesses() { + ExitSuccess + } else { + exit_failure(reason: "json emit corpus grep witness failed") + } +} diff --git a/dsl/test/claim/json_emit_witness_test.dag b/dsl/test/claim/json_emit_witness_test.dag index e455b2f1831..02e05790ec1 100644 --- a/dsl/test/claim/json_emit_witness_test.dag +++ b/dsl/test/claim/json_emit_witness_test.dag @@ -1,6 +1,5 @@ module test.claim.json_emit_witness -import extdeps.filesystem.filesystem_io import extdeps.external_authority { ExternalAuthority } import extdeps.languages.json.emit { extdeps_external_authority_anchor, @@ -104,32 +103,6 @@ fn witness_invalid_number_lexeme_rejected() -> Bool { && !json_number_lexeme_valid(s: "01") } -data json_grammar_path: String = "dsl/extdeps/languages/json/grammar.dag" -data json_emit_path: String = "dsl/extdeps/languages/json/emit.dag" - -fn witness_json_number_lexeme_is_branded_string() -> Bool { - let grammar = filesystem_read(path: json_grammar_path) - string_contains( - s: grammar.content, - pattern: "type JsonNumberLexeme = String where brand(\"JsonNumberLexeme\")" - ) -} - -fn witness_json_number_lexeme_record_carrier_gone() -> Bool { - let grammar = filesystem_read(path: json_grammar_path) - !string_contains(s: grammar.content, pattern: "JsonNumberLexeme { lexeme") -} - -fn witness_grammar_mints_json_number_lexeme() -> Bool { - let grammar = filesystem_read(path: json_grammar_path) - string_contains(s: grammar.content, pattern: "as JsonNumberLexeme") -} - -fn witness_emit_has_no_unblessed_json_number_lexeme_cast() -> Bool { - let emit = filesystem_read(path: json_emit_path) - !string_contains(s: emit.content, pattern: "as JsonNumberLexeme") -} - fn witness_json_int_lexeme_always_valid() -> Bool { json_number_lexeme_valid(s: json_int_lexeme(n: 42) as String) && json_number_lexeme_valid(s: json_int_lexeme(n: -7) as String) @@ -168,10 +141,6 @@ test fn json_emit_witnesses() -> Bool { && witness_str_list_helper() && witness_grammar_rows_cover_json_value_variants() && witness_invalid_number_lexeme_rejected() - && witness_json_number_lexeme_is_branded_string() - && witness_json_number_lexeme_record_carrier_gone() - && witness_grammar_mints_json_number_lexeme() - && witness_emit_has_no_unblessed_json_number_lexeme_cast() && witness_json_int_lexeme_always_valid() && witness_json_number_lexeme_valid_accepts_fraction_and_exp() && witness_unicode_escape_hex4_boundary_0fff()