Skip to content

Fix CladeCumulus graft edge field and tree-file validation type - #229

Merged
hyperpolymath merged 11 commits into
mainfrom
arena/86bcd6f5-metamanifold-webui
Oct 23, 2026
Merged

hyperpolymath merged 11 commits into
mainfrom
arena/86bcd6f5-metamanifold-webui

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Follow-up to #222 (merged before this commit arrived). The Julia suite on #222 reached 825 pass / 1 fail / 9 error, all remaining in CladeCumulus, from two root causes:

  • The jplace graft walk read u.edge_num/cur.edge_num; PhyloNode's field is edge_number (QueryCandidate.edge_num is a different, legitimate field). Grafting any valid placement threw FieldError.
  • _valid_tree_file only accepted ::String, but strip() of a request field yields a SubString, so POST clade-cumulus with treeSource: study_tree returned 500. It now accepts AbstractString.

Julia evidence is CI on this PR.

hyperpolymath and others added 3 commits October 23, 2026 07:24
…rance, route table loading

The CladeCumulus testset (the last red testset in the Julia suite:
817 pass / 3 fail / 14 error, all inside it) shipped in #207 with
defects that the long-broken build had masked:

- _graft_jplace_queries read QueryCandidate.weight/.distal_length/
  .pendant_length; the struct fields are like_weight/distal_len/
  pendant_len (FieldError on any valid jplace graft).
- A single retained placement at LWR 0.98 was marked undecided:
  PLACEMENT_WEIGHT_SUM_TOL=1e-5 rejects truncated candidate lists
  whose retained ratios legitimately sum to slightly less than 1.
  Tolerance is now 0.05; a 0.40 sum stays undecided.
- The Newick label reader consumed an NHX comment after ')' without
  capturing it, dropping [&&NHX:B=92] support; capture refs are now
  threaded through read_label_and_quoted.
- The sources route listed views via SHOW TABLES and then failed to
  bind a session-registered DataFrame view on a fresh connection
  (500); it now enumerates real tables via duckdb_tables().
- _load_run_table_df rejected the SubString table names JSON3
  returns; arguments are normalised to String at the boundary.
- Test used PH.Tools, but Phylogeny imports only selected names from
  Tools; the mock-tool override now addresses MetaManifold.Tools.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
src/core/doi.jl ended with 'end # module DOI' but never opened the
module, so including it into MetaManifold failed to parse
(ParseError at doi.jl:681) and no Julia test could even load the
package. Routes and tests address MetaManifold.DOI, so the file is
a submodule whose opener was lost in an earlier repair.

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

- The jplace graft walk read u.edge_num/cur.edge_num; PhyloNode's
  field is edge_number (QueryCandidate.edge_num is a different,
  legitimate field). Grafting any valid placement threw FieldError.
- _valid_tree_file only accepted ::String, but strip() of a request
  field yields a SubString, so POST clade-cumulus with treeSource
  study_tree returned 500. It now accepts AbstractString.

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 24 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: ef420d8d-e2be-4c19-a2ed-8a32c55c9c5d
📥 Commits

Reviewing files that changed from the base of the PR and between d337f74 and 49466a3.

📒 Files selected for processing: 2
  • src/server/routes/clade_cumulus.jl
  • src/server/routes/trees.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 ⚠️ Failed 2026-10-23T10:04:34.817554Z
We couldn’t complete this review. Comment @codex review to try again.
49466a3 New commits
ℹ️ 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.

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Fork governance

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

Hypatia grid purple

🟣 critical · commit 49466a3bec7b · run 39936482651

Reporter Status Critical High Medium Minor Since previous revision
Hypatia scan 🟣 critical 43 59 31 0 ↔ unchanged
panic-attack assail 🔴 high 0 6 13 0 ↔ unchanged
pons asinorum 🟡 medium 0 0 10 0 ↔ unchanged
rhodibot (RSR compliance) 🟠 indeterminate 0 0 0 0 — indeterminate
panicbot (panic recovery, error handling) 🟠 indeterminate 0 0 0 0 — indeterminate
echidnabot (formal verification: proofs/) 🟠 indeterminate 0 0 0 0 — indeterminate
seambot (integration health: API routes) 🟠 indeterminate 0 0 0 0 — indeterminate
finishingbot (release readiness) 🟠 indeterminate 0 0 0 0 — indeterminate
accessibilitybot (WCAG on the React UI) 🟠 indeterminate 0 0 0 0 — indeterminate
glambot (presentation quality) 🟠 indeterminate 0 0 0 0 — indeterminate
cipherbot ⚪ not applicable 0 0 0 0 ↔ unchanged
sustainabot ⚪ not applicable 0 0 0 0 ↔ unchanged
Hypatia scan: 133 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
  • 113 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: 238598e8e0

ℹ️ 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".

for q in jd.queries
q.status === :valid && !isempty(q.candidates) || continue
total_w = sum((c.weight for c in q.candidates); init = 0.0)
total_w = sum((c.like_weight for c in q.candidates); init = 0.0)

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 Propagate the weight-field fix into the HTTP diversity pass

When a CladeCumulus POST uses a valid .jplace file and the selected table has sample columns, execution now gets past this corrected grafting code but reparses the candidates in src/server/routes/clade_cumulus.jl:279-285 using c.weight and cand.weight. QueryCandidate exposes only like_weight, so this raises a FieldError outside the route's analysis try/catch and the request still returns 500 instead of the diversity result; update those remaining accesses as part of this field-name fix.

Useful? React with 👍 / 👎.

_tree_format(file::String) = endswith(lowercase(file), ".jplace") ? "jplace" : "newick"

function _valid_tree_file(file::String)::Bool
function _valid_tree_file(file::AbstractString)::Bool

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 Normalize the tree filename before calling _tree_format

When treeFile comes from the JSON request, _resolve_tree_inputs_from_request obtains a SubString from strip; widening this validator lets that value pass, but the same path then calls the still-String-only _tree_format(tree_file) at src/server/routes/clade_cumulus.jl:113. Thus every treeSource: "study_tree" POST reaches a MethodError before the analysis try/catch and returns 500; convert tree_file to String at the request boundary or widen _tree_format too.

Useful? React with 👍 / 👎.

Comment thread src/analysis/clade_cumulus.jl Outdated
# like-weight ratios can legitimately sum to slightly less than 1 (a single
# retained placement at 0.98 is complete evidence). Sums far from 1 indicate
# a malformed or partial record and stay undecided.
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.

P2 Badge Reject placement-weight totals above one

When malformed .jplace candidates total between 1 and 1.05, such as 0.60 + 0.44, this symmetric tolerance marks the query valid even though candidate truncation can only explain a total below one. The grafting path then normalizes those invalid weights into abundance shares and reports derived clade and diversity results rather than leaving the query undecided; keep only a small rounding epsilon above 1 while applying the wider allowance on the lower side.

Useful? React with 👍 / 👎.

…ing like_weight fields

The POST handler let unhandled exceptions escape as bare Oxygen
500s with no body, leaving CI annotations with nothing to diagnose.
It is now wrapped: failures are logged with a backtrace and returned
as a structured clade_cumulus_failed error naming the exception.
Also fixes the last QueryCandidate .weight reads in the diversity
tip-weight computation (field is like_weight), and the route test
surfaces the error body when the status assertion fails.

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

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

_tree_format only accepted ::String, but the clade-cumulus route
passes strip() output (a SubString) for the requested tree file, so
study_tree requests failed with MethodError (surfaced by the new
structured clade_cumulus_failed body). The tree helpers now accept
AbstractString throughout.

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

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

…SV in validated tests

- _reaggregate_after_move's recompute stored subtree_pd excluding
  the node's own stem branch, while the build path stores
  clade_faith_pd (stem-inclusive except at the root). A proposed
  move into a clade therefore appeared to *reduce* its PD. The
  recompute now threads stem-inclusive PD up the recursion and
  stores it with build-path semantics.
- test_validated_statistics.jl assigned SV without const; with
  test_clade_cumulus.jl's const SV in the same Main session the
  full suite died with 'invalid assignment to constant Main.SV'
  before the file's tests could run.

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

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Every server test file assigns SV = MetaManifold.Server at top
level; test_clade_cumulus.jl (and briefly
test_validated_statistics.jl) declared it const, so whichever file
runtests.jl included next died with 'invalid assignment to
constant Main.SV' in the full-suite run. Plain assignment
everywhere matches the established convention.

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

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

…mial test config

- _wald called bare erfc, but CompositionalModels deliberately
  carries no SpecialFunctions dependency (local loggamma/digamma),
  so every multinomial/Dirichlet-multinomial fit died with
  UndefVarError. compositional_erfc computes Q(1/2, x^2) by series
  / Lentz continued fraction (machine-precision against libm erfc).
- The 'no composition to model' test passed min_prevalence as a
  keyword; it is the second positional of DifferentialConfig.

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

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

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

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Main landed #226, which contains equivalents of this branch's
_erfc implementation, min_prevalence test fix, edge_number graft
fix and stem-inclusive subtree PD, plus #217's validated-statistics
implementation and the CLADE_SV rename that removes the Main.SV
const collision. Conflicts resolved in favour of main for those;
this branch keeps its unique additions: AbstractString tree
helpers and the structured clade_cumulus_failed catch-all with the
like_weight diversity fixes.

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

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@hyperpolymath
hyperpolymath merged commit fe3aa46 into main Oct 23, 2026
9 checks passed
@hyperpolymath
hyperpolymath deleted the arena/86bcd6f5-metamanifold-webui branch October 23, 2026 10:04
@sonarqubecloud

Copy link
Copy Markdown

hyperpolymath added a commit that referenced this pull request Oct 23, 2026
…ings (#234)

This is #227's contribution, re-landed on current `main` with its review
findings fixed.

## Why not merge #227 directly

#227's branch point is `8b76b71`, which predates #222, #229 and #233 on
`main`, so its diff *also* reverted:

- NHX `[&&NHX:B=…]` capture threading in `parse_newick` (#222)
- `PLACEMENT_SINGLE_MIN_LWR` handling for single-candidate placements
(#222/#233)
- the `edge_number` graft walk fix (#229)
- `unplaced_reads` in the clade-cumulus diversity payload and its test
(#233)
- Canonical I-JSON writers (`jcs_json`/`ijson_read`) and the
`AbstractString` request boundaries in `routes/trees.jl`,
`routes/clade_cumulus.jl`, `src/server/server.jl`

Taking the union naively would have regressed all of that, so this
branch takes **only** what `main` is genuinely missing.

## What `main` is actually missing

`src/server/routes/cladistics.jl` calls `CC.asv_tip_name` (line 123) and
`CC.analyse_phylogeny` (line 211), and `bench/cladistics.jl` calls the
latter, but neither was defined after the #207/#206 merges — every
cladistics build and every compute job died with
`UndefVarError`/`MethodError`. Restored here as an indexed study-tree
layer alongside the existing `PhyloNode` API:

| Symbol | Purpose |
| --- | --- |
| `IndexedPhyloNode`, `PhylogeneticTree` | parents-precede-children node
vector + `tips` index |
| `parse_phylogenetic_tree` | strict Newick → `PhylogeneticTree`
(quoted/Unicode labels, comments, support labels, non-binary nodes) |
| `asv_tip_name` | content-addressed, tool-safe ASV tip names |
| `clade_counts` | nodes × samples cumulative roll-up |
| `unifrac` | raw samples × ASVs **or** an existing roll-up,
unweighted/weighted |
| `analyse_phylogeny` | the single call whose NamedTuple the cladistics
route writes to `results.json` |

The payload matches `CladisticsResult` in `frontend/src/api/types.ts`
exactly.

## Codex findings from #227

1. **P1 — `SubString` from the byte buffer.** `_ip_label!` and
`_ip_length!` called `SubString(p.bytes, first:last)` on a
`Vector{UInt8}`, which has no such method, so every ordinary unquoted
label and branch length raised `MethodError`. Labels/lengths are now
sliced straight into `String`s; the label return is an explicit `String`
instead of a `strip` SubString.
2. **P1 — graft walk read `u.edge_num`.** Already fixed on `main` by
#229 and not reintroduced: `edge_num` now only ever names
`QueryCandidate`'s field, and the new code touches no `PhyloNode` field.
3. **P2 — Faith PD accepted invalid cumulative counts.**
`_faith_pd_indexed` treated negative/`NaN` entries as absent (`> 0` is
false) and returned a plausible but underestimated PD. It now validates
finite, non-negative counts like `clade_counts` and `unifrac`.
4. **P2 — raw-count UniFrac overload missing.** `unifrac(tree, counts,
tip_names; weighted=…)` now exists and delegates through `clade_counts`,
matching the adjacent `faith_pd(tree, counts, tip_names)`.

## Also

- `bench/cladistics.jl` builds its tree with `parse_phylogenetic_tree`
(it calls `analyse_phylogeny`, which needs the indexed type).
- Dropped the duplicated `import ..Tools` in
`src/pipeline/phylogeny.jl`.
- New testset covers the parser (byte-buffer labels, multi-byte/quoted
labels, trailing comments, duplicate tips, negative lengths,
missing/extra `;`), the roll-up, Faith's PD including the new
rejections, both UniFrac variants and the raw-count overload, and the
JSON payload with the same row-major conversion the route performs.
- `docs/cladistics/clade-cumulus.md` documents the new API.

## Verification

- tree-sitter-julia parse of every touched file: clean (the two files
over the JS binding's 32 KiB limit were parsed in top-level chunks; the
inserted block parses standalone).
- No Julia runtime is reachable from the authoring sandbox (julialang-s3
and GitHub release assets are blocked), so the Julia suite is CI on this
PR.

#227 can be closed once this lands.

---------

Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
hyperpolymath added a commit that referenced this pull request Oct 23, 2026
## Scope
Security/governance follow-up to #227. All four Codex findings on that
old PR already landed in #229/#234 and follow-ups; I answered/resolved
its threads and closed it as superseded, rather than merging a
conflicting branch that could undo newer fixes. CodeRabbit's remaining
notices are account limits/billing, not code questions.

## Changes
- Add pinned, least-privileged full-history/working-tree secret scanning
and default-branch Scorecard publication; no inherited secrets or new
alert suppressions.
- Add Dependabot, SECURITY.md, editor settings and a repository-specific
AI manifest without changing existing licensing/architecture.
- Add independent pinned K9 conformance and its artifact to the PR
verdict. The current empty corpus is explicitly grey, not a semantic
pass. Missing semantic evidence fails closed. Keep DEED syntax and
advisory Hypatia as separate evidence families.
- Bound Julia CI at 90 minutes and preserve producer **and** log-writer
failures. Add 8 negative/mutation tests and 4 exit-combination tests.
- Refresh the bun lockfile within current major ranges for real security
advisories, notably DOMPurify, Vite and transitive build dependencies;
raise minimum Vite to 6.4.5 and Router DOM to 6.30.6. This is why the
lockfile changes. No generated assets are included.
- Audit high/critical frontend dependencies in CI.

## Verification
Bun **1.3.10**: frozen install, TypeScript checking, **72 frontend tests
/ 200 assertions**, production build, and high/critical audit pass. Full
audit reduced from **31 advisories (1 critical, 13 high, 14 moderate, 3
low) to 2 moderate Router advisories**. This is not a zero-vulnerability
claim.

Canonical pinned DEED lint, all 39 governance contract checks, 8
weakening mutations, 4 pipeline-exit combinations, unique-key YAML
parsing, all embedded shell syntax, Sonar reliability lint, proof axiom
audit (7 modules), and git diff checks pass locally.

Julia/R/Agda/Nickel are not installed in this sandbox; hosted CI must
supply those results. No local runtime/semantic pass is claimed.

## Hosted wrapper fixes
The first run found two issues in the pinned standards wrappers.
Fork-local copies retain the same immutable standards
validator/renderer/config sources and all gates, but fix the terminal
optional-log copy for an empty K9 corpus and upgrade Gitleaks from
8.18.4 to 8.30.1 (upstream release-asset digest). The old rules falsely
matched historical DNA/FASTQ and schema labels; no history exclusions or
local rule suppressions were added. A synthetic AWS credential must
still fail in CI. Three K9 copy cases and a credential-sensitivity test
also pass locally. Hosted checks are rerunning.

## Remaining / intentionally not done
See `docs/release-notes/2026-10-23-governance-security.md` for the
source Hypatia run and complete triage. Two moderate React Router
advisories need a v7 migration review. Estate language bans conflict
with the settled React/TS stack and independent Python reference
oracles; W3C SVG namespace URIs are not HTTP requests. Do not fix those
by rewriting the architecture or breaking namespaces. No configured
mirror target exists. Existing upstream licensing headers remain
protected. Fleet bots without report-backs remain not_run. No fake
conformance reports have been added.

The integration returns 403 for code-scanning/Dependabot alert listing
and the sandbox blocks Actions artifact/log storage hosts; no alerts
have been dismissed. Published grids/check APIs are still readable.
Scorecard's default-branch publication can only be verified after
landing; K9 has no committed contracts and therefore cannot claim
substantive contract coverage yet.


## PR #228 security follow-up (`1be406d`)
Independent verification found that the recorded conflict-resolution
tree for #228 is identical to main; #228 is being closed as superseded,
not conflict-merged. Its valid leaf-PD finding is already implemented in
**unmerged** #242/#244 and is not duplicated here. #242's integration
run 39986326202 passed all six jobs; #244's Julia validation remains
pending at review time.

The R-evaluation warning was not dismissed as a language-detector false
positive. This follow-up validates metadata column identifiers before
they enter PERMANOVA's parsed R formula and validates package names
before provenance interpolates them into R source. Expressions,
quotes/escapes, controls and reserved formula terms are covered by
regression cases. Plain identifiers preserve the existing formula and
scientific tests; non-identifier metadata columns must be renamed, as
documented in the function contract.

Local verification for the new commit: 39 governance assertions, 8
weakening mutations, 3 K9 copy cases, 4 CI logging cases,
scanner-boundary tests, complete working-tree/history shell scanning,
git diff checks and syntax parsing of all four changed Julia files pass.
**Syntax parsing is not execution**: the new Julia/R regressions await
hosted CI. This does not certify all remaining R call sites or dismiss
outstanding advisory findings.

### Main advanced during review
#242 has now merged as `b656d2a99e53267108d51ad87d7eaee13222d368`
(16:46:42 UTC), after its full integration suite passed. The leaf-PD fix
is therefore on main. #228 is confirmed closed/unmerged. This PR (#243)
now has conflicts with the newly updated main and is **not
merge-ready**; no main-merge commit or unrequested history rewrite has
been made. The new R-boundary/scanner tests are running at `1be406d`;
earlier passing checks must not be mistaken for validation of a future
rebased head.

---------

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.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