Severity: sev:low · Area: 15 (Legacy pure-R search API)
Red-team candidate A15-4. This closes out a lead the 2026-08-04 area-13 round explicitly handed to this area.
What area 13 asked
That round traced the constraint/fuse/sector/pool machinery and left this untraced:
Resample.R/SuccessiveApproximations.R also call .PrepareConstraint (R/Resample.R:311, R/SuccessiveApproximations.R:71) and route to ts_resample_search/ts_parallel_resample/ts_successive_approx — not traced this round for whether they carry the fuse/sector/pool gates at all.
Answer: they do not, and the entry point cannot carry them
R/SuccessiveApproximations.R:88–109 builds searchArgs from only saK, maxSAIter, maxReplicates, targetHits, tbrMaxHits, ratchetCycles, min_steps, concavity, the xpiwe* group, plus .KernelConstraintArgs(consArgs) and the profile args.
Cross-checked against the kernel signature at src/ts_rcpp.cpp:2778–2803, ts_successive_approx()'s formals are exactly that list plus ratchetPerturbProb and driftCycles. No sectorMinSize, sectorMaxSize, fuseInterval, fuseAcceptEqual, poolMaxSize, or any other sectorial/pool parameter exists on this entry point at all — unlike the kernel calls MaximizeParsimony() makes (R/MaximizeParsimony.R:349, :365, :388).
So SuccessiveApproximations() is a self-contained ratchet + drift + TBR loop with none of the sectorial or tree-fusing escape machinery the main search relies on to get off local optima on larger or harder datasets.
Why this is filed as a documentation gap, not a bug
The reduced algorithm may well be intentional — successive approximations is Farris's (1969) procedure, and a lighter search per weighting round is a defensible design. Nothing here computes a wrong number.
The problem is that nothing tells the user. SuccessiveApproximations()'s roxygen uses @inheritParams MaximizeParsimony, and because the R signature does not accept the sectorial parameters they simply never appear in the generated man/SuccessiveApproximations.Rd — there is no note, no @section, nothing distinguishing "this parameter is not applicable here" from "this search is materially weaker on hard data". A user reaching for SuccessiveApproximations() to do a custom k-weighted analysis on a nontrivial matrix would reasonably expect search quality comparable to MaximizeParsimony(), and would have no way to learn otherwise short of reading src/ts_rcpp.cpp.
Suggested fix
A short @section (or a sentence in @details) stating that this function runs a ratchet/drift/TBR search without the sectorial-search, tree-fusing and pool machinery available through MaximizeParsimony(), and pointing users with hard datasets at the latter.
Worth deciding at the same time whether the gap itself should close — but that is a separate, larger question and should not block the doc fix.
Adjacent, not chased
SuccessiveWeights() (R/SuccessiveApproximations.R:175–189) calls CharacterLength() rather than the kernel. Whether its scoring agrees with the C++ kernel's for the same tree and weights was not traced this round; given #83's finding that TreeLength() and EdgeListScore() already disagree by default on extended IW, it is a reasonable next thread. Note that SuccessiveApproximations() itself is not exposed to #83's two-path divergence — it builds contrast/tip_data/weight directly from the dataset attributes and calls the kernel itself, so it has only the one path.
Verification
Confirmed REAL by an independent verifier against trunk tip e0629be61: argument lists and the C++ signature read directly, and the absence of any warning in the generated .Rd confirmed.
Found by /red-team area 15, 2026-08-05, sonnet (Sonnet 5) — the first review this area has ever had.
Severity: sev:low · Area: 15 (Legacy pure-R search API)
Red-team candidate A15-4. This closes out a lead the 2026-08-04 area-13 round explicitly handed to this area.
What area 13 asked
That round traced the constraint/fuse/sector/pool machinery and left this untraced:
Answer: they do not, and the entry point cannot carry them
R/SuccessiveApproximations.R:88–109buildssearchArgsfrom onlysaK,maxSAIter,maxReplicates,targetHits,tbrMaxHits,ratchetCycles,min_steps,concavity, thexpiwe*group, plus.KernelConstraintArgs(consArgs)and the profile args.Cross-checked against the kernel signature at
src/ts_rcpp.cpp:2778–2803,ts_successive_approx()'s formals are exactly that list plusratchetPerturbProbanddriftCycles. NosectorMinSize,sectorMaxSize,fuseInterval,fuseAcceptEqual,poolMaxSize, or any other sectorial/pool parameter exists on this entry point at all — unlike the kernel callsMaximizeParsimony()makes (R/MaximizeParsimony.R:349,:365,:388).So
SuccessiveApproximations()is a self-contained ratchet + drift + TBR loop with none of the sectorial or tree-fusing escape machinery the main search relies on to get off local optima on larger or harder datasets.Why this is filed as a documentation gap, not a bug
The reduced algorithm may well be intentional — successive approximations is Farris's (1969) procedure, and a lighter search per weighting round is a defensible design. Nothing here computes a wrong number.
The problem is that nothing tells the user.
SuccessiveApproximations()'s roxygen uses@inheritParams MaximizeParsimony, and because the R signature does not accept the sectorial parameters they simply never appear in the generatedman/SuccessiveApproximations.Rd— there is no note, no@section, nothing distinguishing "this parameter is not applicable here" from "this search is materially weaker on hard data". A user reaching forSuccessiveApproximations()to do a custom k-weighted analysis on a nontrivial matrix would reasonably expect search quality comparable toMaximizeParsimony(), and would have no way to learn otherwise short of readingsrc/ts_rcpp.cpp.Suggested fix
A short
@section(or a sentence in@details) stating that this function runs a ratchet/drift/TBR search without the sectorial-search, tree-fusing and pool machinery available throughMaximizeParsimony(), and pointing users with hard datasets at the latter.Worth deciding at the same time whether the gap itself should close — but that is a separate, larger question and should not block the doc fix.
Adjacent, not chased
SuccessiveWeights()(R/SuccessiveApproximations.R:175–189) callsCharacterLength()rather than the kernel. Whether its scoring agrees with the C++ kernel's for the same tree and weights was not traced this round; given #83's finding thatTreeLength()andEdgeListScore()already disagree by default on extended IW, it is a reasonable next thread. Note thatSuccessiveApproximations()itself is not exposed to #83's two-path divergence — it buildscontrast/tip_data/weightdirectly from the dataset attributes and calls the kernel itself, so it has only the one path.Verification
Confirmed REAL by an independent verifier against trunk tip
e0629be61: argument lists and the C++ signature read directly, and the absence of any warning in the generated.Rdconfirmed.Found by
/red-teamarea 15, 2026-08-05,sonnet(Sonnet 5) — the first review this area has ever had.