From 55e0d81724dcd654df28b7c2b3fc782647e5a0c9 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Wed, 30 Sep 2026 13:03:46 +0000 Subject: [PATCH 1/3] v2 grammar: an admit_callers list may end in a comma, as v1 reads it Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v2/extdeps/languages/dag.dag | 7 ++- .../admit_callers_trailing_comma_test.dag | 58 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag diff --git a/src/v2/extdeps/languages/dag.dag b/src/v2/extdeps/languages/dag.dag index 9345e5efb62..f5d5fae4062 100644 --- a/src/v2/extdeps/languages/dag.dag +++ b/src/v2/extdeps/languages/dag.dag @@ -1916,6 +1916,8 @@ fn dag_grammar_data_decl_expr() -> GrammarExpr { // declarations allowed to call it (std.integer qualified_octets). v1.compiler.parse // parse_optional_admit_callers reads it as a field-init on the fn node and refuses an empty list; // the list here requires a first element for the same reason, so `admit_callers: []` still refuses. +// A trailing comma before `]` is admitted, as dag_grammar_comma_list_expr admits it and as v1 reads +// the list; without it every multi-line caller list written with one refused its whole module. // `admit_callers` is matched as an exact-word terminal and stays an ordinary identifier everywhere // else. The clause is consumed and then DISCARDED when the fn lowers to an Arrow: nothing on the // native route checks caller admission, and the stage that does adds its Arrow carrier together @@ -1939,7 +1941,10 @@ fn dag_grammar_admit_callers_clause_expr() -> GrammarExpr { right: dag_grammar_nonterminal(production: ^dag_production_expr) ) ), - right: dag_grammar_terminal(token_class: ^dag_token_rbracket) + right: dag_grammar_sequence( + left: dag_grammar_optional(element: dag_grammar_terminal(token_class: ^dag_token_comma)), + right: dag_grammar_terminal(token_class: ^dag_token_rbracket) + ) ) ) ) diff --git a/src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag b/src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag new file mode 100644 index 00000000000..803e77b1c5d --- /dev/null +++ b/src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag @@ -0,0 +1,58 @@ +module v2.test.claim.parse.admit_callers_trailing_comma + +import v2.compiler.parse { parse_module_prepared } +import v2.compiler.program_assembly { dag_prepared_grammar } +import v2.compiler.tokenize { tokenize } +import v2.extdeps.languages.dag { dag_lex } +import v2.std.diagnostic { Accepted, Outcome, Rejected, bind_outcome } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } +import v2.std.logic { Bool } +import v2.std.node { Node } +import v2.std.text { String } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +// AN admit_callers LIST MAY END IN A COMMA (v2.extdeps.languages.dag +// dag_grammar_admit_callers_clause_expr), as a comma list does everywhere else and as v1 reads it; +// the list still needs a first element, so a list that is only a comma still refuses. +fn admit_source(list: String) -> String { + "module probe.admit\n\nfn sealed(n: Int) -> Int\n admit_callers: [" + list + "]\n = n\n" +} + +fn admit_parse(text: String) -> Outcome { + match dag_prepared_grammar() { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + Accepted { value: prepared, diagnostics: residue } => + bind_outcome( + o: tokenize(text: text, file: ^admit_trailing_comma_probe, rules: dag_lex()), + f: fn(token_stream) { + match parse_module_prepared(tokens: token_stream, prepared: prepared, validation_residue: residue) { + Rejected { diagnostics: r } => Rejected { diagnostics: r } + Accepted { value: artifact, diagnostics: d } => Accepted { value: artifact.tree, diagnostics: d } + } + } + ) + } +} + +fn admit_parses(list: String) -> Bool { + match admit_parse(text: admit_source(list: list)) { + Accepted { value: _, diagnostics: _ } => true + Rejected { diagnostics: _ } => false + } +} + +test fn a_trailing_comma_in_admit_callers_parses_holds() -> Bool { + admit_parses(list: "decl_ref(module_path: \"m\", decl_name: \"a\"),\n decl_ref(module_path: \"m\", decl_name: \"b\"),\n ") +} + +test fn an_admit_callers_list_without_a_trailing_comma_parses_holds() -> Bool { + admit_parses(list: "decl_ref(module_path: \"m\", decl_name: \"a\"), decl_ref(module_path: \"m\", decl_name: \"b\")") +} + +test fn an_admit_callers_list_of_only_a_comma_refuses_holds() -> Bool { + match admit_parses(list: ",") { + true => false + false => true + } +} From f07d7641fd84d4a0469a8cdb3a49ba0a07f43266 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Wed, 30 Sep 2026 15:48:50 +0000 Subject: [PATCH 2/3] admit_callers trailing-comma controls: bare-name elements, one small parse each (decl_ref elements were over the new-witness budget) Co-Authored-By: Claude Opus 5.5 (1M context) --- .../claim/parse/admit_callers_trailing_comma_test.dag | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag b/src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag index 803e77b1c5d..803b8ffd99a 100644 --- a/src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag +++ b/src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag @@ -14,7 +14,10 @@ data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly // AN admit_callers LIST MAY END IN A COMMA (v2.extdeps.languages.dag // dag_grammar_admit_callers_clause_expr), as a comma list does everywhere else and as v1 reads it; -// the list still needs a first element, so a list that is only a comma still refuses. +// the list still needs a first element, so a list that is only a comma still refuses. The elements +// are bare names: the clause admits any expression, so the comma behaviour is the same and each +// claim stays one small parse (a decl_ref element list cost 118k eval steps against the 72.3k +// new-witness budget). fn admit_source(list: String) -> String { "module probe.admit\n\nfn sealed(n: Int) -> Int\n admit_callers: [" + list + "]\n = n\n" } @@ -43,11 +46,11 @@ fn admit_parses(list: String) -> Bool { } test fn a_trailing_comma_in_admit_callers_parses_holds() -> Bool { - admit_parses(list: "decl_ref(module_path: \"m\", decl_name: \"a\"),\n decl_ref(module_path: \"m\", decl_name: \"b\"),\n ") + admit_parses(list: "caller_a,\n caller_b,\n ") } test fn an_admit_callers_list_without_a_trailing_comma_parses_holds() -> Bool { - admit_parses(list: "decl_ref(module_path: \"m\", decl_name: \"a\"), decl_ref(module_path: \"m\", decl_name: \"b\")") + admit_parses(list: "caller_a, caller_b") } test fn an_admit_callers_list_of_only_a_comma_refuses_holds() -> Bool { From 9b97527f74203b91a4352e5b50e7e43c484099ac Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Wed, 30 Sep 2026 15:58:04 +0000 Subject: [PATCH 3/3] admit_callers trailing-comma controls: parse once as warm-enrolled nullary values, claims read them Co-Authored-By: Claude Opus 5.5 (1M context) --- .../parse/admit_callers_trailing_comma_test.dag | 17 ++++++++++++++--- src/v2/workflow/floor_pure_producer_share.dag | 3 +++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag b/src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag index 803b8ffd99a..8f70d377940 100644 --- a/src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag +++ b/src/v2/test/claim/parse/admit_callers_trailing_comma_test.dag @@ -45,16 +45,27 @@ fn admit_parses(list: String) -> Bool { } } -test fn a_trailing_comma_in_admit_callers_parses_holds() -> Bool { +// Each parse is computed ONCE as a nullary value, enrolled WARM in +// v2.workflow.floor_pure_producer_share, and each claim reads it: a parse is mostly fixed front-end +// work, so a claim that parsed its own source was over the new-witness eval-step budget. +fn trailing_comma_parsed() -> Bool { admit_parses(list: "caller_a,\n caller_b,\n ") } -test fn an_admit_callers_list_without_a_trailing_comma_parses_holds() -> Bool { +fn no_trailing_comma_parsed() -> Bool { admit_parses(list: "caller_a, caller_b") } +fn only_a_comma_parsed() -> Bool { + admit_parses(list: ",") +} + +test fn a_trailing_comma_in_admit_callers_parses_holds() -> Bool { trailing_comma_parsed() } + +test fn an_admit_callers_list_without_a_trailing_comma_parses_holds() -> Bool { no_trailing_comma_parsed() } + test fn an_admit_callers_list_of_only_a_comma_refuses_holds() -> Bool { - match admit_parses(list: ",") { + match only_a_comma_parsed() { true => false false => true } diff --git a/src/v2/workflow/floor_pure_producer_share.dag b/src/v2/workflow/floor_pure_producer_share.dag index afc0f2948b0..958f68242ef 100644 --- a/src/v2/workflow/floor_pure_producer_share.dag +++ b/src/v2/workflow/floor_pure_producer_share.dag @@ -1054,6 +1054,9 @@ data floor_cross_claim_pure_producers_warm: List = [ "v2.test.claim.namespace_xl0.reference_conservation_census.under_floor_fixture_files", "v2.test.claim.namespace_xl0.reference_conservation_census.half_share_fixture_files", "v2.test.claim.occurrence_role.occurrence_role.roles_fixture_outcome", + "v2.test.claim.parse.admit_callers_trailing_comma.trailing_comma_parsed", + "v2.test.claim.parse.admit_callers_trailing_comma.no_trailing_comma_parsed", + "v2.test.claim.parse.admit_callers_trailing_comma.only_a_comma_parsed", "v2.test.claim.occurrence_role.occurrence_role.binders_fixture_outcome", "v2.test.claim.occurrence_role.occurrence_role.mixed_param_list_parsed", "v2.test.claim.occurrence_role.occurrence_role.mixed_param_list_planted",