Skip to content

Address PR #232 app comments: asymmetric placement tolerance + import cleanup (rebased onto 688d16a) - #236

Merged
hyperpolymath merged 1 commit into
mainfrom
arena/526b10a9-metamanifold-webui
Oct 23, 2026
Merged

hyperpolymath merged 1 commit into
mainfrom
arena/526b10a9-metamanifold-webui

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Supersedes #232 and resolves #224 review findings against current main (688d16a).

Codex (chatgpt-codex-connector) — P2

P2 — Apply the relaxed tolerance only to weight shortfalls (#224#discussion_r4357212802, carried into #232).

  • Before: PLACEMENT_WEIGHT_SUM_TOL=1e-5 symmetric with PLACEMENT_SINGLE_MIN_LWR=0.5 — shortfall of 0.96 (0.80+0.16) was undecided, overweight 1.04 was also undecided but for the same threshold, so the documented rationale (filtering may drop mass, never gains it) was not enforced.
  • After: PLACEMENT_WEIGHT_SUM_TOL=0.05 lower shortfall + PLACEMENT_WEIGHT_OVER_TOL=1e-9 tight upper. parse_jplace now uses for length(cands)>1: weight_sum >1+OVER || weight_sum <1-TOL. Singles keep the existing 0.5 minimum (so _single_at(0.60) stays valid). This makes the Codex example 1.04 stay undecided while 0.98 and 0.96 are valid.
  • Test: _partition(0.80,0.16) now expects :valid (was :undecided under the symmetric 1e-5). Added inline rationale in commit message; the old expectation enforced the bug.

P1 (QueryCandidate distal_len/pendant_len/like_weight) was already correct on main at 688d16a (src/analysis/clade_cumulus.jl:1026 uses cand.distal_len etc.) and is kept.

Other app/CI findings

Merge-conflict note

#232 was branched from 8df1041 and conflicts with main (688d16a). This branch is rebased onto 688d16a and reapplies only the non-regressive parts of #232, so it is mergeable without the DataFrame/edge_num/PD regressions.

Fixes the actionable parts of #232's failing Julia tests check while preserving #232's intent.

—

Co-authored-by: arena-agent

…e duplicate Tools import

- CladeCumulus: tolerate weight shortfall to 0.95 (PLACEMENT_WEIGHT_SUM_TOL=0.05)
  while rejecting any overweight beyond 1e-9 (PLACEMENT_WEIGHT_OVER_TOL).
  Keep single-candidate 0.5 minimum (PLACEMENT_SINGLE_MIN_LWR) for the
  existing single-tip test; multi-candidate sums now use the asymmetric
  bounds so 0.96 (0.80+0.16) is valid and 1.04 remains undecided, matching
  the documented rationale that filtering may drop mass but never gains it.
  This addresses chatgpt-codex-connector P2 on PR #224/#232.

- Phylogeny: remove duplicate `import ..Tools` (sonar maintainability).

- Test: update _partition(0.80,0.16) expectation to :valid to reflect the
  tolerated shortfall; the previous :undecided expectation enforced a
  symmetric 1e-5 bound that contradicts the 5% shortfall documented above.

Co-authored-by: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com>

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Oct 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 57 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: ec4059a7-1775-460a-9079-63881e2f7431
📥 Commits

Reviewing files that changed from the base of the PR and between 688d16a and f4e9739.

📒 Files selected for processing: 3
  • src/analysis/clade_cumulus.jl
  • src/pipeline/phylogeny.jl
  • test/unit/test_clade_cumulus.jl
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T14:06:05.727679Z f4e9739 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

Fork governance

Lane Status Run
DEED syntax (repo deed) 🟢 conformant run 39966345247
Hypatia verdict grid purple (job: success) same run

Hypatia grid purple

🟣 critical · commit f4e97392fdf5 · run 39966345247

Reporter Status Critical High Medium Minor Since previous revision
Hypatia scan 🟣 critical 45 61 35 0 — no comparable baseline
panic-attack assail 🔴 high 0 6 13 0 — no comparable baseline
pons asinorum 🟡 medium 0 0 10 0 — no comparable baseline
rhodibot (RSR compliance) 🟠 indeterminate 0 0 0 0 — no comparable baseline
panicbot (panic recovery, error handling) 🟠 indeterminate 0 0 0 0 — no comparable baseline
echidnabot (formal verification: proofs/) 🟠 indeterminate 0 0 0 0 — no comparable baseline
seambot (integration health: API routes) 🟠 indeterminate 0 0 0 0 — no comparable baseline
finishingbot (release readiness) 🟠 indeterminate 0 0 0 0 — no comparable baseline
accessibilitybot (WCAG on the React UI) 🟠 indeterminate 0 0 0 0 — no comparable baseline
glambot (presentation quality) 🟠 indeterminate 0 0 0 0 — no comparable baseline
cipherbot ⚪ not applicable 0 0 0 0 — no comparable baseline
sustainabot ⚪ not applicable 0 0 0 0 — no comparable baseline
Hypatia scan: 141 finding(s)
  • critical cicd_rules/banned_language_file — frontend/src/views/run/parseFilterYaml.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/figure/encode.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/figure/charts.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/figure/export.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/figure/figure.test.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/figure/legend.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/figure/types.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/utils/download.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/utils/text.test.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/utils/timeago.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/utils/text.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/hooks/useUnsavedGuard.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/hooks/useApi.test.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/hooks/useApi.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/hooks/useAnalysis.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/hooks/useSSE.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/hooks/useTabParam.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/hooks/useNavRefresh.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/hooks/useJobEvents.ts — TypeScript file detected -- banned language
  • critical cicd_rules/banned_language_file — frontend/src/about.ts — TypeScript file detected -- banned language
  • 121 more; see the structured report in the artifact.
panic-attack assail: 19 finding(s)
  • high DynamicCodeExecution — src/pipeline/dada2/context.jl — eval/Meta.parse in src/pipeline/dada2/context.jl
  • high DynamicCodeExecution — src/analysis/analysis.jl — eval/Meta.parse in src/analysis/analysis.jl
  • high DynamicCodeExecution — src/analysis/differential.jl — eval/Meta.parse in src/analysis/differential.jl
  • high DynamicCodeExecution — src/core/provenance.jl — eval/Meta.parse in src/core/provenance.jl
  • high DynamicCodeExecution — scripts/check-julia-syntax.jl — eval/Meta.parse in scripts/check-julia-syntax.jl
  • high DynamicCodeExecution — test/unit/test_permanova.jl — eval/Meta.parse in test/unit/test_permanova.jl
  • medium UnsafeFFI — src/core/memory_probe.jl — 3 ccall/FFI calls in src/core/memory_probe.jl
  • medium InputBoundary — src/server/routes/runs.jl — 2 JSON3.read/JSON.parse call(s) with 0 try block(s) in src/server/routes/runs.jl — these throw on malformed input; wrap in try/catch
  • medium InputBoundary — src/server/routes/composition.jl — 6 JSON3.read/JSON.parse call(s) with 2 try block(s) in src/server/routes/composition.jl — these throw on malformed input; wrap in try/catch
  • medium InputBoundary — src/server/routes/studies.jl — 4 JSON3.read/JSON.parse call(s) with 2 try block(s) in src/server/routes/studies.jl — these throw on malformed input; wrap in try/catch
  • medium InputBoundary — src/server/routes/analysis.jl — 9 JSON3.read/JSON.parse call(s) with 4 try block(s) in src/server/routes/analysis.jl — these throw on malformed input; wrap in try/catch
  • medium UnsafeFFI — install.jl — 1 ccall/FFI calls in install.jl
  • medium ProofDrift — test/unit/test_occupancy_grade.jl — 2 `@test x isa Y` assertion(s) with no value check — type-only tests may substitute for an unwritten formal theorem in test/unit/test_occupancy_grade.jl
  • medium ProofDrift — test/unit/test_evidence.jl — 1 `@test x isa Y` assertion(s) with no value check — type-only tests may substitute for an unwritten formal theorem in test/unit/test_evidence.jl
  • medium InputBoundary — bench/advanced_zero_handling/benchmark.jl — 1 JSON3.read/JSON.parse call(s) with 0 try block(s) in bench/advanced_zero_handling/benchmark.jl — these throw on malformed input; wrap in try/catch
  • medium InputBoundary — frontend/src/figure/charts.ts — 1 JSON.parse call(s) with 0 try block(s) in frontend/src/figure/charts.ts — JSON.parse throws SyntaxError on malformed input; wrap in try-catch
  • medium InputBoundary — frontend/src/tree/model.ts — 1 JSON.parse call(s) with 0 try block(s) in frontend/src/tree/model.ts — JSON.parse throws SyntaxError on malformed input; wrap in try-catch
  • medium InputBoundary — frontend/src/api/events.ts — 2 JSON.parse call(s) with 0 try block(s) in frontend/src/api/events.ts — JSON.parse throws SyntaxError on malformed input; wrap in try-catch
  • medium InputBoundary — frontend/src/components/alphaMetrics.tsx — 1 JSON.parse call(s) with 0 try block(s) in frontend/src/components/alphaMetrics.tsx — JSON.parse throws SyntaxError on malformed input; wrap in try-catch
pons asinorum: 10 finding(s)
  • medium string-concat-in-loop — ./frontend/src/tree/model.ts:122 — string built by repeated concatenation in a loop (T0, HEURISTIC)
  • medium string-concat-in-loop — ./frontend/src/tree/model.ts:118 — string built by repeated concatenation in a loop (T0, HEURISTIC)
  • medium string-concat-in-loop — ./frontend/src/tree/TreeShapes.tsx:69 — string built by repeated concatenation in a loop (T0, HEURISTIC)
  • medium string-concat-in-loop — ./frontend/src/tree/TreeShapes.tsx:66 — string built by repeated concatenation in a loop (T0, HEURISTIC)
  • medium string-concat-in-loop — ./frontend/src/tree/TreeShapes.tsx:61 — string built by repeated concatenation in a loop (T0, HEURISTIC)
  • medium string-concat-in-loop — ./frontend/src/tree/TreeShapes.tsx:53 — string built by repeated concatenation in a loop (T0, HEURISTIC)
  • medium string-concat-in-loop — ./frontend/src/tree/TreeShapes.tsx:43 — string built by repeated concatenation in a loop (T0, HEURISTIC)
  • medium string-concat-in-loop — ./frontend/src/tree/TreeShapes.tsx:42 — string built by repeated concatenation in a loop (T0, HEURISTIC)
  • medium unreachable-after-jump — ./frontend/src/figure/PaneEditor.tsx:45 — statement is unreachable after an unconditional jump (T0, HEURISTIC)
  • medium unreachable-after-jump — ./frontend/src/components/PublicationTablesPanel.tsx:208 — statement is unreachable after an unconditional jump (T0, HEURISTIC)
rhodibot (RSR compliance): not_run — enrolled, but no report-back was received
  • 🟠 indeterminate: enrolled, but no report-back was received
panicbot (panic recovery, error handling): not_run — enrolled, but no report-back was received
  • 🟠 indeterminate: enrolled, but no report-back was received
echidnabot (formal verification: proofs/): not_run — enrolled, but no report-back was received
  • 🟠 indeterminate: enrolled, but no report-back was received
seambot (integration health: API routes): not_run — enrolled, but no report-back was received
  • 🟠 indeterminate: enrolled, but no report-back was received
finishingbot (release readiness): not_run — enrolled, but no report-back was received
  • 🟠 indeterminate: enrolled, but no report-back was received
accessibilitybot (WCAG on the React UI): not_run — enrolled, but no report-back was received
  • 🟠 indeterminate: enrolled, but no report-back was received
glambot (presentation quality): not_run — enrolled, but no report-back was received
  • 🟠 indeterminate: enrolled, but no report-back was received
cipherbot: not_applicable — no cryptography is implemented in this repository
  • ⚪ not applicable: no cryptography is implemented in this repository
sustainabot: not_applicable — fleet adapter slot reserved; no adapter exists yet (gitbot-fleet README)
  • ⚪ not applicable: fleet adapter slot reserved; no adapter exists yet (gitbot-fleet README)

Scope: static findings reported by each row's tool at the named commit. Purple, red, yellow and blue grade the worst finding present; orange is evidence missing, malformed or a scanner that did not run; grey is an explicit scope exclusion with its reason. No row is a security certification. report.json, badge.json and the raw findings are in the verdict-grid artifact on the linked run.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f4e97392fd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# A sum over 1 is never legitimate: weights are mutually exclusive placement
# ratios, so overweight evidence is malformed and is rejected by a tight upper
# bound while the lower shortfall is tolerated.
const PLACEMENT_WEIGHT_SUM_TOL = 0.05

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Separate parsing tolerance from unplaced-read accounting

Raising this shared constant to 0.05 also changes src/server/routes/clade_cumulus.jl:313-314, where it suppresses reporting any unplaced mass below 5%. For example, the existing route fixture has a valid placement with LWR 0.99; its missing 1% was previously returned as 0.05/0.10 unplaced reads, but now 0.99 < 0.95 is false, so both values become zero and the assertions at test/unit/test_clade_cumulus.jl:477-478 fail. Use a separate floating-point epsilon for unplaced-read accounting so the relaxed parser threshold does not hide discarded mass.

Useful? React with 👍 / 👎.

push!(queries, QueryPlacement(q_ord, q_names, cands, :undecided,
"weight_sum_out_of_tolerance ($(round(weight_sum; digits=6)))", nothing))
elseif length(cands) > 1 && abs(weight_sum - 1.0) > PLACEMENT_WEIGHT_SUM_TOL
elseif length(cands) > 1 && (weight_sum > 1.0 + PLACEMENT_WEIGHT_OVER_TOL || weight_sum < 1.0 - PLACEMENT_WEIGHT_SUM_TOL)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve missing mass in admitted short partitions

When this condition newly admits a partition such as 0.80 + 0.16, _graft_jplace_queries still computes each share as cand.like_weight / total_w at lines 1010-1022. That turns the weights into approximately 0.8333 + 0.1667 and allocates 100% of the ASV's reads to the grafted tips, manufacturing the missing 4% that the new comments describe as discarded or unplaced; before this change such a query never reached grafting because it was undecided. Preserve the raw candidate weights, or explicitly represent the missing share, so clade counts and frequencies do not disagree with the raw-LWR diversity calculation.

Useful? React with 👍 / 👎.

@hyperpolymath
hyperpolymath merged commit b8b6c9e into main Oct 23, 2026
18 of 19 checks passed
@hyperpolymath
hyperpolymath deleted the arena/526b10a9-metamanifold-webui branch October 23, 2026 14:10
hyperpolymath added a commit that referenced this pull request Oct 23, 2026
`main` is currently red on `Julia tests` — 5679 pass, **2 fail**, 0
error — and both failures come from #236, whose push-run CI was
cancelled by the workflow concurrency group before the suite ran.

```
Dual-hierarchy CladeCumulus, polyphyly detection, drag-and-drop, and HTTP routes: Test Failed at test/unit/test_clade_cumulus.jl:591
  Expression: ≈(jp_body.diversity.unplaced_reads.SampleA, 0.05, atol = 1.0e-9)
   Evaluated: 0 ≈ 0.05
  Expression: ≈(jp_body.diversity.unplaced_reads.SampleB, 0.1, atol = 1.0e-9)
   Evaluated: 0 ≈ 0.1
```

## Cause

#236 relaxed `PLACEMENT_WEIGHT_SUM_TOL` from `1e-5` to `0.05` so that a
candidate list truncated by the placement tool still parses as `:valid`
— a correct change for *validity*. But the `unplaced_reads` field of the
clade-cumulus diversity payload reused the same constant as its
reporting floor:

```julia
short_rows = [(ri, 1.0 - wsum) for (ri, wsum) in row_weight
              if wsum < 1.0 - MetaManifold.CladeCumulus.PLACEMENT_WEIGHT_SUM_TOL]
```

With `TOL = 0.05`, a row at weight `0.99` no longer qualified as short,
so a placement at LWR 0.99 — whose remaining 1% genuinely never reached
the tree — reported `0` unplaced reads instead of `0.01 × reads`. The
test's placement (`ASV_4` at LWR 0.99, 5 and 10 reads) therefore
expected `0.05` and `0.10` and got `0`.

## Fix

Reporting now uses the noise floor the metric actually means,
`PLACEMENT_WEIGHT_OVER_TOL` (`1e-9`, added by #236 for the opposite
bound of the same check):

```julia
noise_floor = MetaManifold.CladeCumulus.PLACEMENT_WEIGHT_OVER_TOL
short_rows = [(ri, 1.0 - wsum) for (ri, wsum) in row_weight
              if 1.0 - wsum > noise_floor]
```

`1.0 - wsum > 1e-9` is `wsum < 1.0 - 1e-9` for all shortfalls and
additionally excludes overweight rows, which a shortfall comparison
cannot express. The comment records why the two tolerances are not
interchangeable, so the next change to the placement tolerance cannot
silently re-break the report.

No test changes: the two existing assertions are exactly the regression
guard, and they were the only failures in the suite — my new
indexed-tree testset passed (`CladeCumulus & Phylogenetic Integration`:
196 pass, 0 fail).

Also worth knowing for future merges: `actions/runs/39967407149` (the
push run for `b8b6c9e`) shows `cancelled`, which is why #236 could merge
without its suite running.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
hyperpolymath added a commit that referenced this pull request Oct 23, 2026
`main` landed its own answer to most of #230 while this branch was open:
#236 adopted the asymmetric placement tolerance (shortfall tolerated to 0.05,
excess rejected at 1e-9) and #237 added the unplaced-reads noise floor. So
this merge gives up re-litigating the tolerance and keeps `main`'s model
verbatim: `PLACEMENT_WEIGHT_SUM_TOL = 0.05`, `PLACEMENT_WEIGHT_OVER_TOL = 1e-9`,
the asymmetric parse branch, and the partition expectations that follow from it
(0.96 is a truncated candidate list and is `:valid`; 1.04 is overweight
evidence and stays `:undecided`).

What is kept from this branch is what `main` does not have:

* the library-size denominators (`_sample_total`, `totals` / `total_a` /
  `total_b`) that stop weighted UniFrac renormalising unplaced mass back onto
  the tips that were placed -- the Codex P1 finding;
* the route's whole-table `unplaced_reads`, which counts an ASV the tree does
  not cover at all -- the Codex P2 finding -- instead of summing per-row
  placement shortfalls. It subsumes #237's noise floor: subtracting the placed
  mass from the library size reports a 0.99 placement's 1% exactly, with no
  threshold to tune;
* the emptied-clade fix (`own_count`, `own_residual`) and its regression test;
* the exact method+path route assertions;
* the corrected unplaced-reads wording in the panel and the API types.

The single-candidate reason stays `single_candidate_below_min_lwr (<lwr>)`:
under `main`'s asymmetric rule a lone candidate's sum is in tolerance by
construction, so the old wording named a weight-sum error that had not
happened.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
hyperpolymath added a commit that referenced this pull request Oct 23, 2026
`main` now holds all of it: #236/#237 the asymmetric placement tolerance and
the unplaced-reads accounting, #239 the #235 work itself -- the library-size
denominators (`_sample_total`, `totals`), the route's whole-table
`unplaced_reads`, the emptied-clade `own_count`/`own_residual` fix with its
regression test, the exact method+path route assertions, the corrected
unplaced-reads wording, and the vacated-clade tip-count rule.

So this merge takes `main`'s side throughout, including over the two
CodeRabbit autofix commits that landed on the branch afterwards: `main`'s
in-tree tip rule is the same rule as the branch's `counts_as_tip` helper, and
the branch's extra 'zero-read terminal' testset asserted a target purity of
0.5 where the code computes 2/3 (two GenusA tips out of three, the unassigned
tip included), so it could not have passed. Nothing here needs re-landing.

The branch tree is now identical to `main`.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
hyperpolymath added a commit that referenced this pull request Oct 23, 2026
…N with 400 (#244)

## Change and reason

Three things, in the order the linked review asked for them.

### 1. PR #228's merge conflicts, resolved (and shown to be a no-op)

PR #228 (`arena/7805092e`) branched from `8df1041`; all six files it
touched
have since been rewritten on `main` by #231, #233, #234, #236, #237 and
#239.
Resolving the six conflicted files in favour of `main` produces a tree
that is
**byte-identical** to `main` (`git diff --stat origin/main` is empty),
so the
merge commit records the resolution and changes no content. Concretely:

* `ci.yml` - main publishes a per-assertion digest of every failing
validated-statistics test plus a log tail; #228's awk windows are
superseded.
* `src/analysis/clade_cumulus.jl` - main already reads
`PhyloNode.edge_number`
in `_graft_jplace_queries`, already sums `rk.subtree_pd` plus the node's
own
branch, and additionally recomputes an emptied clade from
`direct_count`.
* `src/server/routes/clade_cumulus.jl` - main already uses
`with_results_db`
and `String(strip(...))`, and deliberately does **not** renormalise
placement
like-weights by the retained total: `parse_jplace` already rejects
ratios
outside [0, 1], a multi-candidate placement already partitions its mass,
and
dividing a lone 0.6 candidate by its own total would move the 0.4 the
tool
  left unplaced onto the reported edge (#236/#237 report it as
  `unplaced_reads` instead).
* `src/server/routes/validated_statistics.jl` - main validates raw
tokens for
the whole body after `ijson_read` proves it I-JSON, and streams bounded
rows
  instead of materialising a DataFrame. Both are strict supersets.
* `test/unit/*` - main's suites are far larger; the one test #228 had
that main
lacked ("Nested-clade drag-and-drop counts each active branch once") is
  restored here.

### 2. Codex P2 on PR #228: leaf subtree PD

`_reaggregate_after_move` credits an active leaf with its own incoming
branch,
so a parent can sum `subtree_pd` alone and count each active edge once.
The
ordinary builder (`build_node`) hard-coded `0.0` for leaves, so
validating a
drag-and-drop changed the reported PD of leaves whose topology had not
changed
and made the proposed tree's metrics incomparable with the original.

Both builders now take a leaf's PD from `clade_faith_pd` - the function
the
internal-node branch already used - so the conventions cannot drift
again.
Internal nodes and inactive tips are unchanged.

### 3. panic-attack `InputBoundary`: malformed bodies are 400s

35 unguarded `JSON3.read(String(req.body))` call sites across eight
route files
raised a JSON3 parse error out of the handler, so a truncated or hostile
body
produced a 500. Every route now parses through `Server._request_json`,
which
throws the existing `BadRequest`. The helper copies `req.body`, because
`String(::Vector{UInt8})` can consume the buffer.

Rescuing that turned out to need one more step, which CI caught: Oxygen
builds
its chain as `custom(serializer(router))` and its serializer catches
every
handler exception and answers 500 before our middleware runs, so
`_bad_request_middleware` never saw the throw. Oxygen's error mapper is
the
hook inside the serializer, and it is already a dispatch point
(`handle_error(::ValidationError)` answers 400). Extending it for
`BadRequest`
keeps this server's `{"error", "message"}` shape and makes the test's
`status == 400` true.

### 4. pons asinorum: tree path building is linear

The six `string-concat-in-loop` hits in `TreeShapes.tsx` were real:
every node
appended to four shared strings inside a `useMemo` over the whole
layout, which
is quadratic in the node count on a view that renders every node. The
fragments
are collected per node and joined once; the emitted order is unchanged.

### 5. panic-attack `ProofDrift` and a guarded benchmark baseline

The `@test x isa Y` assertions the rule flagged were real tautologies:
they
passed for any string. The occupancy pair now asserts the text the code
emits
(`measured 201 KiB above the predicted 101 KiB`) and the evidence round
trip
asserts the ISO-8601 UTC stamp `save_document` writes. The advanced
zero-handling benchmark parsed its baseline outside any guard, so a
truncated
baseline surfaced as a JSON3 failure from inside the timing loop; it now
names
the file it could not read.

## Validation

- `cd frontend && npx tsc --noEmit -p .` - clean.
- `cd frontend && npx vite build` - clean (chunk-size warning only,
pre-existing).
- `cd frontend && bun test src` - **72 pass, 0 fail** (11 files).
- `bash scripts/check-governance-contract.sh` - **all 20 checks
passed**.
- `bash scripts/lint-sonar-reliability.sh` - clean.
- `bash -n` on every `scripts/*.sh` - clean.
- All four workflows parse as YAML; the DEED lints clean with the pinned
  `deed_lint.js` from `hyperpolymath/standards@75bd5994` (see below).
- **Not run locally:** the Julia suite (no Julia runtime in this
environment;
  the two changed Julia files are covered by CI's `Julia tests` job).

## Hypatia and K9/DEED conformance

* **DEED** - `MetaManifold-WebUI_chora.deed` lints clean with the exact
linter
the reusable lane runs (`bun 1-formats/deed/tools/deed_lint.js`). Note
the
stdlib Python port in the same directory is older and rejects `#u8`; the
JS
linter the workflow uses accepts it, so the deed is correct as written.
* **K9** - this repository has no `.machine_readable/contractiles/`
registry or
Mustfile, and `governance.yml` does not call `k9-contractile.yml`, so
the K9
contractile lane is not in scope here. What *was* wrong is that the deed
declared `:report-backs ".gitbot-fleet/report-backs/"` while the
directory
did not exist; it now does, with the envelope contract documented, and
the
grid keeps rendering those members orange until a real report-back
lands.
* **Hypatia** - the 43 critical findings are all
`cicd_rules/banned_language_file` for `frontend/src/**/*.ts`. That rule
is a
repo-side-unfixable policy finding: TypeScript's only carve-outs are
narrow
path prefixes (`bindings/typescript/`, `.d.ts`, `vite.config.ts`, ...),
none
of which cover an upstream-authored React frontend, and the rule cannot
be
  suppressed from the repository. Migrating upstream's frontend to
  AffineScript is an estate project, not a change this fork can make.
* **panic-attack / pons asinorum** - most remaining findings are stale
or
false positives at `main`: `api/events.ts` and `tree/model.ts` already
wrap
`JSON.parse` in try/catch (hardened in #239), the `DynamicCodeExecution`
  hits are `RCall.reval` (R source strings, not Julia `eval`), and the
`unreachable-after-jump` hits point at plain arrow-function bodies. The
two
classes that were real - unguarded request-body parsing and quadratic
path
building - are fixed above. The grid's residual after this PR: Hypatia
45
critical (all `banned_language_file` on upstream TypeScript), 61 high,
35
medium; panic-attack 6 high (all `RCall.reval` false positives), 7
medium;
pons asinorum 2 medium (`unreachable-after-jump` on a `useMemo` return
and a `useEffect` cleanup, both false positives).

## Scope

- [x] Focused on the linked review plus conformance evidence
- [x] Does not alter dependencies, lockfiles, generated assets, or
vendored files
- [x] Secrets, local paths, and run outputs are not committed

## Contribution checks

- [x] I followed CONTRIBUTING.md
- [x] My commits include `Signed-off-by:` (DCO) and the licence is
unchanged

---------

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
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.

2 participants