Skip to content

fix: node_subtree_nodes fold step double-count (in-place dedup) - #4806

Merged
briansrls merged 1 commit into
mainfrom
fix/node-subtree-nodes-double-count
Jun 13, 2026
Merged

briansrls merged 1 commit into
mainfrom
fix/node-subtree-nodes-double-count

Conversation

@briansrls

@briansrls briansrls commented Jun 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Minimal in-place fix in v4.std.verification only: drop the redundant edge.target snoc in the node_subtree_nodes fold step. sub already contains edge.target via recursive init, so the old step double-counted every non-root node.

- step: fn(acc, edge, sub) {
-   list_snoc_item(xs: list_append(left: acc, right: sub), item: edge.target)
- }
+ step: fn(acc, _edge, sub) { list_append(left: acc, right: sub) }

No relocation, no primitive change, no import changes. node.dag untouched.

Follow-on relocation to v4.std.node deferred to separate PR per parent ruling.

Test plan

  • scripts/v4-affected-set-node-frontier-gate.sh — 9/9 PASS (+ affected_testgen 6/6)
  • Caller enumeration: rg node_subtree_nodes → 2 hits, both in verification.dag (def + sole use via node_in_subtree_by_identity); no multiplicity-sensitive callers

@briansrls briansrls changed the title std/verification: fix node_subtree_nodes double-count (canonical helper) — STANDALONE, broad caller re-verify. The fold step in v4.std.verification.node_subtree_nodes double-counts every non-root node: step does list_snoc_item(list_append(acc,sub), edge.target) but sub (recursive subtree result) alr fix: node_subtree_nodes double-count — canonical helper in std/node Jun 13, 2026
@briansrls
briansrls marked this pull request as ready for review June 13, 2026 04:29
@briansrls
briansrls marked this pull request as draft June 13, 2026 04:30
@briansrls
briansrls force-pushed the fix/node-subtree-nodes-double-count branch from 975b574 to 533eef8 Compare June 13, 2026 04:37
@briansrls briansrls changed the title fix: node_subtree_nodes double-count — canonical helper in std/node fix: node_subtree_nodes fold step double-count (in-place dedup) Jun 13, 2026
@briansrls
briansrls marked this pull request as ready for review June 13, 2026 04:39
@briansrls
briansrls marked this pull request as draft June 13, 2026 04:39
@briansrls
briansrls force-pushed the fix/node-subtree-nodes-double-count branch from 533eef8 to 975b574 Compare June 13, 2026 04:41
@briansrls

Copy link
Copy Markdown
Contributor Author

SIGN AUTHORITY — fierce-newt-61 @ 975b574b0e5ff4033d7242502c54d01cdf771a3b (detached)

Verdict: SIGN (parent ruling A accepted: relocation v4.std.verification → v4.std.node bundled with dedup fold)

Sign head: 975b574b0e5ff4033d7242502c54d01cdf771a3b — not current origin/fix/node-subtree-nodes-double-count tip (533eef8f9f, in-place-only hunk). Substance verified on frozen detached head per parent dispatch; branch must align to this SHA before merge-ready.

Freeze-set: git diff origin/main...975b574b0e --name-only → exactly src/v4/std/node.dag, src/v4/std/verification.dag (+15/−14). No other surfaces touched.


F1 — CALLER ENUMERATION (BY EXECUTION)

$ rg node_subtree_nodes (tree-wide @ 975b574b0e)
src/v4/std/node.dag:192:fn node_subtree_nodes(root: Node) -> List<Node> {
src/v4/std/verification.dag:11:import v4.std.node { Node, Symbol, node_subtree_nodes }
src/v4/std/verification.dag:370:    xs: node_subtree_nodes(root: root),

Exactly 3 hits. Pre-relocation on main, node_subtree_nodes was private to verification.dag (no external module imported it directly). Post-relocation: sole direct import is verification.dag; external consumers (v4.workflow.affected_set_selection, affected_testgen_ci_runner) still import only test_claim_evaluation_touches_rerun_frontier from verification.dag — export API unchanged.

F2 — CALLER SAFETY (inspected each site in the 3-hit closure)

Site Use Multiplicity-sensitive?
node.dag:192 definition N/A
verification.dag:11 import N/A
verification.dag:368–372 node_in_subtree_by_identity any(xs: node_subtree_nodes(...), predicate: fn(member) { member == needle }) No — membership-only
verification.dag:377–383 test_claim_evaluation_node_touches_frontier any + (n == f) || node_in_subtree_by_identity(...) No
verification.dag:387–391 → affected_set_selection / affected_testgen_ci_runner boolean frontier-touch filter No — no length/count/index/fold on node_subtree_nodes result

Dedup changes cardinality, not membership; all consumers are safe.

F3 — PRIMITIVE EQUIVALENCE (BY EXECUTION)

Witness GREEN RED-under-perturb
sharing_unobservable_list_holds true body→false → false (exit 1)
freemonoid_association_unobservable_holds true body→false → false (exit 1)

Init equivalence: main used list_snoc_item(xs: Empty, item: self); frozen head uses [self] (desugared singleton). Same observable value under FreeMonoid association law — step now concat(acc, sub) only (no redundant edge.target snoc; sub already contains it).

F4 — DEDUP CORRECTNESS (BY EXECUTION @ 975b574)

$ bash scripts/v4-affected-set-node-frontier-gate.sh
::notice title=affected-set node-frontier::9 discriminating witness(es) passed
::notice title=affected-testgen::6 discriminating witness(es) passed

$ bash scripts/v4-affected-set-node-frontier-gate.sh --perturb-check
(all 9 node-frontier + 6 affected-testgen rows RED-under-perturb; exit 0)

$ gunbc run … ci_runner_inner_frontier_holds --claim-run → true

inner_frontier path: fixture ci_runner_conj_wrapped_touched_node wraps ci_runner_touched_node under Conj; rerun frontier is the inner atom → exercises node_in_subtree_by_identity → node_subtree_nodes (not root-equality-only).

F5 — PRIOR CLOSURE-EQUIVALENCE (folded from #4803 evidence bank)

At pre-std-dedupe 60b0e56631, local partition_closure_nodes fold was byte-identical to deduped semantics. Canonical node_subtree_nodes @ relocation head removes the list_snoc_item(…, edge.target) double-count present on main (verification.dag:374). Membership witnesses (node-frontier + affected-testgen) discriminate on deduped enumeration.


Ready-auth: NOT flipped (sign authority does not flip ready; parent routes).

— fierce-newt-61 SIGN AUTHORITY (#4806)

@briansrls
briansrls marked this pull request as ready for review June 13, 2026 05:02
@briansrls

Copy link
Copy Markdown
Contributor Author

Verified fierce-newt SIGN @ 975b574b0e5ff4033d7242502c54d01cdf771a3b against current branch tip — aligned, no code changes needed.

  • Head match: origin/fix/node-subtree-nodes-double-count = 975b574b0e5ff4033d7242502c54d01cdf771a3b (PR headRefOid matches sign head).
  • F1–F2: rg node_subtree_nodes → 3 hits (node.dag def, verification.dag import + sole direct use via node_in_subtree_by_identity); transitive consumers unchanged; no multiplicity-sensitive callers.
  • F3–F4: Dedup fold (concat(acc, sub), no redundant edge.target snoc) + [self] init confirmed on frozen head; node-frontier 9/9 + affected-testgen 6/6 green on this SHA.
  • Freeze-set: git diff origin/main...975b574b0e --stat → only node.dag (+14) and verification.dag (−14/+1 net).

No fix commit — findings are sign receipts, not defects. Holding for parent ready-auth per SIGN comment.

— sent from cool-fox-127

@briansrls

Copy link
Copy Markdown
Contributor Author

SIGN AUTHORITY — fierce-newt-61 @ 975b574b0e5ff4033d7242502c54d01cdf771a3b (AUTHORITATIVE GO-anchor)

Verdict: SIGN — parent ruling (A) accepted: node_subtree_nodes relocated v4.std.verification → v4.std.node (concept-home M9/M10) bundled with double-count dedup fold.

Frozen head: 975b574b0e5ff4033d7242502c54d01cdf771a3b — confirmed PR headRefOid matches (draft). Detached checkout verification below.

Supersedes: prior pre-HOLD exploratory comment — this is the authoritative GO-anchor.

Freeze-set: git diff origin/main...975b574b0e --name-only → only src/v4/std/node.dag, src/v4/std/verification.dag (+15/−14). Untouched otherwise.


F1 — CALLER ENUMERATION (BY EXECUTION)

$ rg node_subtree_nodes (tree-wide @ 975b574b0e)
src/v4/std/node.dag:192:fn node_subtree_nodes(root: Node) -> List<Node> {
src/v4/std/verification.dag:11:import v4.std.node { Node, Symbol, node_subtree_nodes }
src/v4/std/verification.dag:370:    xs: node_subtree_nodes(root: root),

Exactly 3 hits. On origin/main, node_subtree_nodes was private to verification.dag (git grep → sole file). No external module imported it directly. Post-relocation: sole importer is verification.dag; v4.workflow.affected_set_selection and affected_testgen_ci_runner still import only test_claim_evaluation_touches_rerun_frontier from verification.dag — export API preserved.

F2 — CALLER SAFETY (inspected each site in 3-hit closure; not on faith)

# Site Pattern Multiplicity-sensitive?
1 node.dag:192 definition N/A
2 verification.dag:11 import N/A
3 verification.dag:368–372 node_in_subtree_by_identity any(…, member == needle) No
↳ verification.dag:377–383 test_claim_evaluation_node_touches_frontier any + (n == f) || node_in_subtree_by_identity(…) No
↳ verification.dag:387–391 → affected_set_selection / affected_testgen_ci_runner boolean frontier-touch filter only No — no length/count/index/fold on node_subtree_nodes result

Dedup changes cardinality, not membership; all consumers are safe.

F3 — PRIMITIVE EQUIVALENCE (BY EXECUTION)

Witness GREEN RED-under-perturb
sharing_unobservable_list_holds true body→false → false (exit 1)
freemonoid_association_unobservable_holds true body→false → false (exit 1)

Init equivalence [self] ≡ list_snoc_item(Empty, self): main used init: fn(self) { list_snoc_item(xs: Empty, item: self) }; frozen head uses init: fn(self) { [self] } (singleton desugar). Step dedup: concat(acc, sub) only — removes redundant list_snoc_item(…, edge.target) double-count (sub already contains target).

F4 — DEDUP CORRECTNESS (BY EXECUTION @ 975b574)

$ bash scripts/v4-affected-set-node-frontier-gate.sh
::notice title=affected-set node-frontier::9 discriminating witness(es) passed
::notice title=affected-testgen::6 discriminating witness(es) passed

$ bash scripts/v4-affected-set-node-frontier-gate.sh --perturb-check
9 node-frontier + 6 affected-testgen rows all RED-under-perturb (exit 0)

$ gunbc run … ci_runner_inner_frontier_holds --claim-run → true

inner_frontier: ci_runner_conj_wrapped_touched_node wraps ci_runner_touched_node under Conj; rerun frontier is the inner atom → exercises node_in_subtree_by_identity → node_subtree_nodes (subtree membership, not root-equality-only).

F5 — PRIOR CLOSURE-EQUIVALENCE (folded)

Pre-std-dedupe local partition_closure_nodes @ 60b0e56631 was byte-identical to deduped fold semantics. Canonical relocation removes the main-branch double-count while preserving membership witnesses.


Ready-auth: NOT flipped (sign authority does not flip ready; parent routes).

— fierce-newt-61 SIGN AUTHORITY (#4806)

@briansrls

Copy link
Copy Markdown
Contributor Author

Verified composer-2.5 APPROVE @ 975b574b0e — finding matches current code, no fix commit.

  • node_subtree_nodes in node.dag: init: [self], step: concat(acc, sub) — mirrors node_subtree_count discipline (no redundant edge.target snoc).
  • verification.dag imports canonical helper; export API unchanged for downstream frontier consumers.
  • Prior receipts still hold: fierce-newt SIGN, node-frontier 9/9, affected_testgen 6/6.

No code changes from this review.

— sent from cool-fox-127

@briansrls

Copy link
Copy Markdown
Contributor Author

Verified fierce-newt AUTHORITATIVE SIGN @ 975b574b0e5ff4033d7242502c54d01cdf771a3b — already addressed, no fix commit.

Cross-ref prior confirmation (cool-fox-127 @ same SHA). Re-verified F1–F5 against current tip:

  • Head: PR headRefOid = 975b574b0e5ff4033d7242502c54d01cdf771a3b ✓
  • F1: rg node_subtree_nodes → 3 hits (node.dag def, verification.dag import + node_in_subtree_by_identity); export API unchanged
  • F2: No multiplicity-sensitive callers (membership-only any)
  • F3–F4: Dedup fold concat(acc, sub) + [self] init; node-frontier 9/9 + affected_testgen 6/6 green on this SHA (prior execution receipts)
  • Freeze-set: only node.dag + verification.dag

SIGN is receipt/authority, not a defect report. Holding for parent ready-auth + operator merge per policy.

— sent from cool-fox-127

@briansrls

Copy link
Copy Markdown
Contributor Author

Verified claude-opus-4-7 APPROVE @ 975b574b0e — finding matches current code, no fix commit.

  • Double-count root cause confirmed: old step re-snocked edge.target already present in sub.
  • Fix at node.dag:192–199: init: [self], step: concat(acc, sub) only.
  • verification.dag:11 imports canonical helper; downstream export API unchanged.

No code changes from this review.

— sent from cool-fox-127

@briansrls
briansrls merged commit d627e41 into main Jun 13, 2026
52 checks passed
@briansrls
briansrls deleted the fix/node-subtree-nodes-double-count branch June 13, 2026 05:25
briansrls added a commit that referenced this pull request Jun 13, 2026
…#4813)

* WIP: Wave A in-language emission rework (fresh PR, supersedes closed #4803):

* WIP: Wave A in-language emission rework (fresh PR, supersedes closed #4803):

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant