RT 2026-08-04 - area 11 - opus (Opus 5) - yield 9 #167
ms609-agent
announced in
11 · Zero-length-branch collapse (MPT set)
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
opus (Opus 5) | effort: unrecorded | 2026-08-04
Migrated record. This round predates the move to Discussions; it is reproduced
verbatim below from
dev/red-team/log.md, which is now closed to new entries. It isposted here so this area has a round record whose
createdAtorders correctly againstthe other areas' — the backfill ran oldest-first for that reason. All fourteen posting
timestamps are later than the rounds they describe; the real date is in the title and
in the
date:field.area: 11 (Zero-length-branch collapse / MPT set)
reviewed_by: opus finder afcd025b + opus verifier a115632e (T-400) + opus verifier a3698b79 (T-402) + opus verifier ace8de75 (medium batch) + haiku verifier a96123f8 (6 low-sev) + orchestrator duplication check & independent re-derivations
date: 2026-08-04
tier: opus (Opus 5)
yield: 9 filed (T-400/T-402 P1, T-403/T-404 P2, T-401/T-405/T-408/T-410/T-411 P3) + T-324 amended + 3 comment-only defects fixed inline, from 12 candidates; 2 refuted (T-406, T-407); T-409 verified REAL but fixed inline, so deliberately NOT filed
notes: Rotation
(10 mod 13) + 1 = 11, taken as written -- the 2026-08-03 area-10 round correctly leftlast_focusat 10 after its out-of-rotation escalation, so this slot was still area 11's. Legend reconciled at round start:opus-> Opus 5 since 2026-07-27, unchanged, so no version-bump re-eligibility fired. Seam was recorded still yielding (3 findings incl. a P1 on 2026-07-02, the area's first-ever review) -> stayed at opus with a fresh agent, per the yielding-seam rule;escalation-backlog.mdconfirms area 11 escalates nothing.THE HEADLINE IS NOT IN THIS AREA'S FILES, AND THAT IS THE THIRD CONSECUTIVE ROUND WHERE THE LIVE SEAM TURNED OUT TO BE THE CONSUMERS. T-400 (P1):
TreeLength()on a multifurcating tree performs out-of-bounds heap writes and returns a score read from memory before the buffer -- reachable with no error and no warning fromMaximizeParsimony()'s default output, becausecollapse = TRUEhas been default-on since 2026-06-24 and returns polytomies.TreeState::init_from_edgederivesn_tip = n_edge / 2 + 1, and both edge-count parities break, differently: odd ->parent[c]andleft/right[pi]write one int past the end; even -> indices stay in bounds butparent[n_tip] = n_tipputs the root on a real tip, sobuild_postorderreturns a 1-element postorder andts_fitch.cpp:88-91formsprelim.data() - total_words. Not a mis-resolved traversal on either path.TreeLength.phyloguards with "treemust be binary";TreeLength.list/.multiPhylochecks only that the trees agree with each other. Measured: five identical calls returned12, 9, 12, 12, 12(truth 12); a 12-taxon matrix4, 3, 6, 0, 6(truth 13). Exposure is not confined toTreeLength--CharacterLength()/FastCharacterLength()(returned0for a truth of2),TreeScore()andEdgeListScore()share it, andEdgeListScoreis the defaultTreeScorerforTreeSearch()/Ratchet()/Jackknife(). Filed as ONE row because one boundary check ininit_from_edgefixes every entry point; the verifier enumerated the guarded/unreachable callers so a fixer need not re-sweep. Issue ms609#259 does NOT cover this and the direction is easy to get backwards: ms609#259 is an open low-priority enhancement asking to support polytomous scoring, and it asserts as background that the API already refuses them. This is a bug inside today's contract -- the refusal was never implemented uniformly. Closing ms609#259 would not close T-400.THE BRIEF'S BET PAID, BUT NOT WHERE IT AIMED -- RECORD BOTH HALVES. The bet was area 10's transferable rule: review the four fixes that landed inside this area's files since the last round (
13dcebd8T-330,7f9c1966T-331,4b833e7fT-332,9033ea9bT-382, ~55 net-new lines, never red-teamed) as new code, with the specific hypothesis that a fix's new early-exit might break an invariant a sibling function documents. That hypothesis was correct and landed exactly where predicted: T-331's star branch broke two written invariant claims --compute_collapsed_regions:289-291andts_rcpp.cpp:222, both of which assert the kernels never flag root's children, which the star branch deliberately does. Both fixed inline (the second as T-409, verified REAL then fixed rather than filed). T-382's one-sidedness argument atts_collapsed.cpp:116-122reaches the right conclusion by the wrong reasoning (it claims staleness makes equality harder; the stale words are all-zero whenever the block was never active and non-NA blocks'down2/subtree_activesare never written at all, so equality is made easier -- still safe, because a zero-weight block should be ignored, but the comment does not describe the case that occurs). T-330's guard is over-broad (T-408) and is currently the only thing shieldingMaximizeParsimony's own XFORM report block from T-400 (T-401). T-332 verified sound. But the P1 came from a different question entirely -- "what consumes the collapse output?" -- so the durable rule is now: review a feature's fix stack as new code AND follow its output to every consumer. Area 10 learned the consumer half on 2026-08-03 (T-392, ints_tbr.cpprather than in the kernels); area 11 has now learned it independently.BOTH 2026-07-02 "RULED OUT BY DERIVATION" CLAIMS RE-DERIVED. BOTH HOLD -- one needed a better argument, and the other's predicate turned out to be broken even though its rooting is sound. (a) IW/profile-vs-EW flag consistency CONFIRMED, but the recorded reason was the weak one. "Concavity is a downstream reweight" is true of the score, not obviously of the flags. The load-bearing fact is structural:
simplify_patterns()(src/ts_data.cpp:62) takes no scoring-mode argument and groups blocks byweightalone, sods.blocks[],block_word_offset,active_mask,n_statesandds.tip_statesare bit-identical across EW/IW/XPIWE/PROFILE for a given matrix, and both kernels read only those plusprelim/local_cost/final_/down2/subtree_actives-- neverconcavity,min_steps,info_amounts,eff_k,phiorblk.weight. So the flags are bit-identical, not merely equivalent. Confirmed end-to-end: a 10-taxon soft-polytomy matrix gives the identical collapsed topology (Nnode 5) under EW, IW k=10, IW k=1 and PROFILE against 100 binary MPTs. Do not reopen unlesssimplify_patternsgains a mode argument -- that is the precise reopening condition. (b) Constraint-canonicalisation rooting CONFIRMED sound, and that is exactly why the finding is elsewhere..PrepareConstraintemitsconsSplitsinnames(dataset)order =tip_datarow order, every start tree is aligned byPreorder(RenumberTips(tr, names(dataset))), andts_collapse_poolreroots in C++ viareroot_at_tip(tree, 0)and never in R -- so tip indices are preserved and the [[na-validation-alignment-gotcha]] trap is genuinely avoided. The rooting is fine; the predicate built on top of it is too strict -> T-403.T-406 REFUTED, AND THE REFUTATION CORRECTS A FACT THE FINDER GOT BACKWARDS -- this is the most reusable negative result of the round. The claim was that the pool dedups on the conservative criterion at the search's rooting while the output dedups on the aggressive criterion at the tip-0 rooting, so the pool could merge two trees whose output collapsed topologies differ, silently losing an MPT shape. The inclusion
key_out(T) subset of key_pool(T)is sound. But the finder's stated reason for being unable to trigger it is itself wrong: it reported 0 occurrences of the conservative criterion flagging an internal edge acrosscongreveLamsdell1-4 and Vinther2008 and concluded the criterion effectively never fires. It fires trivially -- any clade constant across all retained characters qualifies -- and did so in 520 of 535 swept matrices. The 0-count is a property of those two datasets (many characters make the full-rowmemcmpatts_collapsed.cpp:125essentially unreachable), not of the criterion. With the real blocker removed, the harm was then tested properly and does not occur: exhaustive enumeration of all unrooted binary trees at n=7 and n=8, exact MPT sets, both keys computed per tree, MPTs grouped bykey_pooland searched for any group spanning more than onekey_out-- 0 hits in 535 matrices across two different pool rootings, with demonstrated power (designed cases produced 9 MPTs -> 2 pool keys -> 1 output key, and 22 MPTs -> 13 pool keys -> 6 output keys). Sokey_poolrefineskey_outand no output-distinct shape can be lost; the rooting-dependence is real but points the harmless way (a different rooting makes the pool key finer, causing over-retention that the output dedup then removes). Not filed. Reopening condition: only a counterexample to the refinement, not a fresh derivation.T-407 REFUTED, BUT THE VERIFIER'S BOTTOM LINE WAS RIGHT FOR THE WRONG REASON AND THE FINDER'S OWN MEASUREMENT CONVICTED THE OTHER LINE -- re-derived by the orchestrator rather than locked either way (cf. [[redteam-reverify-flawed-refutes]], applied in the confirming direction for once). The claim was that
@return n_topologies(MaximizeParsimony.R:933-935) contradicts@param collapse(:913-915). The finder's evidence wascollapse = FALSE->n_topologies100 vscollapse = TRUE-> 1 -- which, read carefully, is consistent with the@returntext it accused and inconsistent with the@paramtext it trusted. Orchestrator re-derivation: everyadd_collapsedcall site in C++ is unconditional (ts_driven.cpp:1066,:1074,:1217,:1427;ts_parallel.cpp:106,:236;ts_tbr.cpp:2915,:2960), so the pool always dedups on the conservative criterion regardless of thecollapseargument, and the 100 arose because the conservative criterion flagged nothing on that matrix -- the same fact T-406's refutation then explained properly. Both doc blocks are defensible if "collapsed topology" means the conservative/pool criterion, so there is no contradiction. The residual -- the docs use one term for two different criteria -- is now provably harmless given T-406's refutation, so it was not filed. Any future rewording should also correct the record that the conservative criterion does flag internal edges.TWO DUPLICATION CATCHES, AND ONE OF THEM IS THE PROCESS RESULT OF THE ROUND. (1) The medium-batch verifier independently rediscovered T-400 while checking T-401 and reported it as "a live P2 that the finding does not claim and deserves its own row", with a Vinther2008
inapplicable = "missing"repro (40 40 40 22 ...against a true 78). It had not been told about T-400. Caught at the orchestrator, not filed -- and it is now recorded inside T-400 as independent corroboration on different data by a verifier with no knowledge of the claim, which is stronger evidence than the original. Lesson: a verifier briefed narrowly on one finding will rediscover its siblings and confidently ask for a new ID; the orchestrator's cross-batch duplication pass is load-bearing, not ceremonial. (2) T-402 vs T-324/T-390/T-391 settled as a genuinely distinct third entry point. T-390 issprFirst = TRUE(the phase has no constraint parameter at all), T-391 ispruneReinsertCycles > 0(the constraint arrives and is lost inside re-insertion, and its row already says "Distinct from T-390"), T-402 is the user-suppliedtree =start, never imposed or verified. Different entry point, fix location and reachability. P1 by the project's own stated rule, not by escalation: T-390 and T-391 are each held at "Severity P2 not P1 only because no shipped configuration reaches it" (sprFirst = FALSE,pruneReinsertCycles = 0Lin every preset) -- a gate T-402 lacks, needing only two documented top-level arguments used together, in the workflow?MaximizeParsimonyitself advertises. T-324's reachability question is not settled by T-402's repro, and the row says so.T-402 ALSO FORCED AN AMENDMENT TO T-324, WHICH MATTERS FOR ANY FIXER OF THIS CLASS. T-324 said
nni_perturb'simpose_constraint"can repair it only ifnni_perturb_per > 0and the heuristic succeeds". In the score-better-than-constrained regime it can never repair:best_tree = treesnapshots the violating start atts_nni_perturb.cpp:80before repair, and the repaired legal (therefore worse-scoring) tree is rejected at:120byaccept = tbr_result.best_score < best_score. The illegal score is an unbeatable baseline, so a verify-and-revert gate of the T-390/T-391 shape does not fix this class; and gating the pool capture alone leaves the pool empty atmaxReplicates = 1, whereuponMaximizeParsimony.R:1682-1684falls back to returning the user's violating start anyway. Both traps recorded in T-402. Second-order finding worth its own note: the illegal score evicts compliant trees, so an 8-replicate run returned the violating tree even though replicates 2-8 each reached the true constrained optimum -- and the bug is masked when the supplied tree is bad, so it bites precisely when the user supplies a good one.T-403 IS THE PRECISE LIMIT OF THE FINDER'S OWN CLEAN ANSWER, AND BOTH HALVES SHOULD BE KEPT. Asked whether collapse can violate a constraint, the finder answered no with a sound derivation: 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. That is correct in the min-length sense and should not be re-derived. But in the displayed-split sense -- which is what
@param constraintpromises the user -- it does: the protection atts_rcpp.cpp:2244-2269is a bit-identical match againstconsSplitMatrixrows and has no access toconsZero(computed atMaximizeParsimony.R:134/:143/:153, then dropped from the returned list at:191-198), so a constraint split realised by any node that is not exactly the 1-group is unprotected. Measured through the public API:collapse = TRUE-> 20/20 returned trees violate the constraint;collapse = FALSE-> 0/20.TOOLING RESULT THAT CONTRADICTS A STANDING PROJECT NOTE, AND SHOULD CHANGE HOW THIS CLASS IS HUNTED. [[feedback-memory-checking]] said all local memory checking is blocked on Windows/MinGW (Dr. Memory, gflags, local ASan) and to use GHA CI for everything. That remains true of sanitizers, but
-D_GLIBCXX_ASSERTIONSworks fine locally -- it is libstdc++ hardened mode, not a sanitizer, so it needs no runtime library -- and it caught T-400's three OOB write sites in one run, where static tracing had taken far longer and still understated the bug. Inject viaR_MAKEVARS_USER(not asrc/Makevars.win, whichAGENTS.mdforbids leaving behind) and put it inPKG_CPPFLAGS, per [[makevars-pkgcxxflags-clobber]]. Given how much of this project's bug class is container-index OOB (T-323/T-328/T-400/WGN-13-C), reach for this before dispatching an ASan CI round. Memory updated this round.Seam status: STILL YIELDING (two P1s) -> next area-11 visit stays opus with a fresh agent. No out-of-rotation escalation is claimed: both high-severity signals were confirmed and filed this round, so what remains is fixer work with a decided mechanism and an undecided fix shape, not an unresolved review question. Next by rotation is area 12 (meta-review), which suits -- this round generated real process material (the cross-batch duplication catch; the verifier-right-for-the-wrong-reason pattern appearing twice in one round). NOT REACHED, carried forward for the next area-11 round: (1) nobody has run the collapse path under
nThreads = 2--ts_parallel.cpp:105/:235callcompute_collapsed_flagson per-workerds_localcopies whoseactive_mask/upweight_maskratchet state is per-worker, so the flags are too; not obviously wrong, never looked at; (2) HSJ/XFORMn_topologiesinflation -- the unquantified price of T-330's fix-by-disabling, worth one measurement before anyone folds the HSJa(n)/p(n)DP into the collapse test; (3) the T-325-territory dedup over-merge re-hunt is now largely closed by T-406's enumeration (59 returned trees -> 59 distinct independent tip-label-bipartition keys oncongreveLamsdell[[1]]+ 3 near-empty taxa;splits_equalexact andhash_splitsorder-independent), leaving only a bug in split enumeration itself as a route.All reactions