fix: close ADR-index drift, rebuild stale darwin-mode dist, correct README/package.json claims#141
Draft
ruvnet wants to merge 4 commits into
Draft
fix: close ADR-index drift, rebuild stale darwin-mode dist, correct README/package.json claims#141ruvnet wants to merge 4 commits into
ruvnet wants to merge 4 commits into
Conversation
…EADME/package.json claims Closes the two structural checks in __tests__/adr-index.test.ts by appending the 112 ADR-*.md files that existed on disk but were never linked from docs/adrs/INDEX.md (auto-extracted title/status/summary from each file's own front matter, verbatim). The remaining canonical- sections check (95 files missing Context/Decision headings) is a much larger, separate cleanup left out of scope here. Rebuilds packages/darwin-mode/dist/gepa/loop.js from source: the checked-in dist still shipped the pre-CodeQL-fix backtracking regex in parseReflection even though src/gepa/loop.ts already carries the linear-time extractFence replacement — the published package was serving a ReDoS-vulnerable build. Also: - README: correct the ADR count (31 -> 219), host-adapter count/list (6 -> 9, was missing copilot/opencode/github-actions), and the test badge (568/67 files -> the current 2,254 passing / 246 files, CI green on main as of c86bc12). - package.json: point homepage/repository at the actual repo (ruvnet/metaharness), matching the git remote and the README's own self-declared repo slug. - Untrack node_modules, which was committed as a symlink to an author-local absolute path and does not belong in version control (already covered by .gitignore). - package-lock.json: resync workspace-probe's locked version/bin entry with its already-bumped package.json (0.1.0 -> 0.1.1).
route() is called once per query against a candidate set that never changes after construction, but predict()'s cosine() recomputed both vectors' L2 norms from scratch on every call: each candidate's example norms got redone on every route() call (they never change), and the query's own norm got redone once per (candidate, example) pair within a single route() call (it's the same query throughout). Caches example norms by candidate object identity (WeakMap) and computes the query norm once per route() call, reusing both through a new cosineWithNorms() internal path. cosine() itself, predict()'s public signature, and route()'s k-NN/tie-breaking semantics are unchanged — same outputs, less redundant arithmetic. Added bench/route-throughput.mjs (deterministic, seeded synthetic data) to measure it: ~15-21% more routes/sec across the 20-vs-200-examples x 128-vs-1536-dim grid (results committed under bench/results/). The isolated numeric-kernel cost wasn't as flop-bound as the naive 3-accumulator-vs-1 model suggested (V8's loop here is memory-bound, not arithmetic-bound) — the real win is avoiding the array/closure allocation overhead of rebuilding the k-NN candidate list from scratch every call, documented inline. Also fixes packages/router's stale homepage/repository.url (same ruvnet/agent-harness-generator -> ruvnet/metaharness drift already fixed at the workspace root).
cladeThompsonSelect() (ADR-094, Huxley-Gödel selection) called cladeOutcomes() once per scored archive record, and each call walks that record's ENTIRE descendant subtree from scratch. For n scored records this is O(n * avg-subtree-size) per selection pass, which degrades to O(n^2) on a roughly linear lineage (common in practice — each generation's subtree walk almost entirely re-walks its parent's). cladeThompsonSelect runs once per generation in evolve(), so a full evolve run is worse still. Adds cladeOutcomesAll(): one bottom-up (post-order) pass over the whole archive that computes every node's subtree outcome as its own outcome plus its already-computed children's — every node and edge visited exactly once, O(n) total. cladeThompsonSelect now does one O(n) aggregation instead of n separate O(subtree) walks; the RNG draw order (and therefore every seeded/reproducible result) is unchanged since the outer scored.map() iteration order is untouched. The existing cladeOutcomes() point-query export is left as-is — it's the right tool for a single lookup and other callers may still use it. Measured on bench/selection/clade-throughput.mjs (deterministic synthetic archives, npm run bench:clade): 6.4x on a 200-node chain, 52.9x on an 800-node chain, ~1.4-1.5x on branching trees of similar size (already closer to O(n log n) before the fix). The chain/ branching gap also shrinks from ~41x to ~1x, confirming the fix removes the pathological tree-shape dependency rather than just speeding up one shape. All 67 darwin-mode test files (591 tests) pass unchanged, including the seeded-reproducibility and fertile-clade-wins statistical tests in clade.test.ts.
Same pattern already fixed in @metaharness/router: detectConcepts() scores every (state x concept) pair via cosine(z, c.vector), and cosine() recomputes both vectors' L2 norms from scratch on every call. Within one call, the projected activation z is fixed for the whole inner `concepts` loop, so its norm was recomputed once per concept instead of once per state. Across calls, concept vectors are pre-fitted, static directions — detectConcepts() runs once per governed decision/trace against the same concept library — so each concept's norm was recomputed on every single call instead of once ever. Hoists norm(z) out of the inner loop and caches each concept's norm by object identity (WeakMap<ConceptVector, number>), computing dot(z, c.vector) / (nz * nc) directly instead of calling cosine() blind. Same formula, same zero-guard, same scores/thresholds/triggers — cosine() itself is untouched and still exported for other callers. Added bench/detect-concepts-throughput.mjs (deterministic synthetic lens + concept library) to measure it — care was needed to keep lens.project()'s unrelated O(dModel^2) per-state matVec from dominating the benchmark and masking the signal (first attempt showed near-zero delta because of exactly that; documented inline). With dModel kept small relative to the concept-library size so the optimized loop actually dominates: ~1.9-2.5x more calls/sec, growing with concept-library size. All 20 workspace-lens tests and the 15 downstream workspace-probe tests pass unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up from a review/comparison of metaharness against a related project (RightNow-AI/auto). This PR fixes the credibility/documentation drift the review surfaced, plus one real bug it uncovered along the way:
__tests__/adr-index.test.tsrequires everydocs/adrs/ADR-*.mdfile to be linked fromdocs/adrs/INDEX.md. 112 ADR files existed on disk but were never indexed. Closed by appending an auto-generated appendix (title/status/summary extracted verbatim from each file's own front matter — not hand-authored). The third check in that test file (95 ADRs missing canonical## Context/## Decisionheadings) is a much larger, separate cleanup and is intentionally left out of scope here.packages/darwin-mode/src/gepa/loop.tsalready contains a CodeQL-driven fix (linear-timeextractFencereplacing a backtracking regex inparseReflection), but the checked-indist/gepa/loop.js— which is whatdarwin-mode'smainfield actually publishes — still had the old vulnerable regex. Rebuiltdist/from source so the published package matches source.mainCI run).package.jsonidentity:homepage/repository.urlpointed atruvnet/agent-harness-generator; updated toruvnet/metaharnessto match the actual git remote and the README's own self-declared repo slug.node_modulessymlink pointing to an author-local absolute path (/home/ruvultra/projects/...) —node_modules/is already gitignored, this was accidentally committed.package-lock.jsonresync:workspace-probe's locked version/bin entry was behind its already-bumpedpackage.json(0.1.0 → 0.1.1 +binfield).Test plan
npx vitest run __tests__/adr-index.test.ts— the two structural checks ("every ADR-*.md listed in INDEX.md", "every INDEX.md reference exists") now pass; the pre-existing canonical-sections check still fails as expected (95 files, out of scope).node scripts/build-ordered.mjs) succeeds; fullvitest run(sequential, single-fork) gives 234/246 files and 2,237–2,254 tests passing locally — the ~10 local failures were confirmed to be sandbox-specific (missing host CLIs likeopenclaw), not real regressions, by cross-checking that the latestmainCI run (commitc86bc12) is fully green across the Rust/WASM/Node matrix via the GitHub Actions API.dist/gepa/loop.jsrebuild is a clean, minimal diff matchingsrc/gepa/loop.tsexactly (plus an expectedcli.jsexecutable-bit fix from the build), no unrelated churn.Generated by Claude Code