Severity: P2 · Area: 11 (Collapse), 13 (Constraint)
Location: src/ts_rcpp.cpp:2244-2269 (bit-identical comparison against canonicalised consSplitMatrix rows); consZero computed at R/MaximizeParsimony.R:134, :143, :153 and dropped from the returned list at :191-198; promise at :918-921
The flag is cleared only when a node's descendant bitset is bit-identical to a constraint row, and the kernel has no access to consZero, so any constraint split realised by a node that is not exactly the 1-group is unprotected. Verified REAL end-to-end, and worse than the finder framed it (opus verifier). Promise quoted verbatim from :918-921; names(cargs) confirmed at runtime to carry no consZero. Kernel-level, deterministic (only (g,h) supported, cons = c(a=1, b=1, c=0, d=0, e:h="?")): realising node {a,b} exactly -> protection fires, constraint still shown; realising node {a,b,e} -> (a,(b,c,d,e,f,(g,h))), not shown; realising node {a,b,e,f} -> same, not shown. End-to-end through the public API (8 taxa, 2 characters supporting (a,e) + 2 supporting (b,f), constrained optimum 4 from tree = (((a,e),(b,f)),(c,(d,(g,h))))): collapse = TRUE -> 20/20 returned trees violate the constraint (e.g. (a,(e,(c,d,g,h,(b,f)))) at score 4, where no split separates {a,b} from {c,d}); collapse = FALSE -> 0/20. So the defect is not merely a hidden grouping — the returned tree breaks a hard user-specified contract. This is the precise limit of the finder's own answer to key question 6. It correctly derived that collapse cannot violate a constraint in the min-length sense (contracting an edge replaces a resolution by a polytomy whose Fitch length is the min over resolutions, so no character's length can rise, and a "constraint characters at minimum length" constraint survives any contraction) — but in the displayed-split sense, which is what @param constraint promises the user, it does. Record both halves: the min-length argument is sound and should not be re-derived. Fix direction confirmed to match the mechanism: protect the MRCA of the 1-group when it excludes the 0-group, which requires plumbing consZero through .PrepareConstraint's return. P2 rather than P1 only because the regime is narrow — the constraint's realising split must be both unsupported and not exactly the 1-group; the case for P1 is that it silently breaks a hard contract on the default path.
Migrated 2026-08-04 from dev/red-team/findings.md; pre-tracker ID T-403. References to T-403 in source comments, dev/red-team/log.md, commit messages and PR bodies mean this issue — see dev/red-team/migration-map.tsv. Cross-repo references use the fully-qualified form agent-issues/TreeSearch#<n>; a bare #n means this repo.
Severity: P2 · Area: 11 (Collapse), 13 (Constraint)
Location:
src/ts_rcpp.cpp:2244-2269(bit-identical comparison against canonicalisedconsSplitMatrixrows);consZerocomputed atR/MaximizeParsimony.R:134,:143,:153and dropped from the returned list at:191-198; promise at:918-921The flag is cleared only when a node's descendant bitset is bit-identical to a constraint row, and the kernel has no access to
consZero, so any constraint split realised by a node that is not exactly the 1-group is unprotected. Verified REAL end-to-end, and worse than the finder framed it (opus verifier). Promise quoted verbatim from:918-921;names(cargs)confirmed at runtime to carry noconsZero. Kernel-level, deterministic (only(g,h)supported,cons = c(a=1, b=1, c=0, d=0, e:h="?")): realising node{a,b}exactly -> protection fires, constraint still shown; realising node{a,b,e}->(a,(b,c,d,e,f,(g,h))), not shown; realising node{a,b,e,f}-> same, not shown. End-to-end through the public API (8 taxa, 2 characters supporting(a,e)+ 2 supporting(b,f), constrained optimum 4 fromtree = (((a,e),(b,f)),(c,(d,(g,h))))):collapse = TRUE-> 20/20 returned trees violate the constraint (e.g.(a,(e,(c,d,g,h,(b,f))))at score 4, where no split separates{a,b}from{c,d});collapse = FALSE-> 0/20. So the defect is not merely a hidden grouping — the returned tree breaks a hard user-specified contract. This is the precise limit of the finder's own answer to key question 6. It correctly derived that collapse cannot violate a constraint in the min-length sense (contracting an edge replaces a resolution by a polytomy whose Fitch length is the min over resolutions, so no character's length can rise, and a "constraint characters at minimum length" constraint survives any contraction) — but in the displayed-split sense, which is what@param constraintpromises the user, it does. Record both halves: the min-length argument is sound and should not be re-derived. Fix direction confirmed to match the mechanism: protect the MRCA of the 1-group when it excludes the 0-group, which requires plumbingconsZerothrough.PrepareConstraint's return. P2 rather than P1 only because the regime is narrow — the constraint's realising split must be both unsupported and not exactly the 1-group; the case for P1 is that it silently breaks a hard contract on the default path.Migrated 2026-08-04 from
dev/red-team/findings.md; pre-tracker ID T-403. References toT-403in source comments,dev/red-team/log.md, commit messages and PR bodies mean this issue — seedev/red-team/migration-map.tsv. Cross-repo references use the fully-qualified formagent-issues/TreeSearch#<n>; a bare#nmeans this repo.