Skip to content

fix: close ADR-index drift, rebuild stale darwin-mode dist, correct README/package.json claims#141

Draft
ruvnet wants to merge 4 commits into
mainfrom
claude/metaharness-package-review-lqzyda
Draft

fix: close ADR-index drift, rebuild stale darwin-mode dist, correct README/package.json claims#141
ruvnet wants to merge 4 commits into
mainfrom
claude/metaharness-package-review-lqzyda

Conversation

@ruvnet

@ruvnet ruvnet commented Jul 11, 2026

Copy link
Copy Markdown
Owner

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:

  • ADR index drift (real, test-covered): __tests__/adr-index.test.ts requires every docs/adrs/ADR-*.md file to be linked from docs/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/## Decision headings) is a much larger, separate cleanup and is intentionally left out of scope here.
  • Stale compiled output shipping a ReDoS regex (real bug, not just docs): packages/darwin-mode/src/gepa/loop.ts already contains a CodeQL-driven fix (linear-time extractFence replacing a backtracking regex in parseReflection), but the checked-in dist/gepa/loop.js — which is what darwin-mode's main field actually publishes — still had the old vulnerable regex. Rebuilt dist/ from source so the published package matches source.
  • README factual corrections: ADR count badge (31 → 219), host-adapter count/list (6 → 9, was missing copilot/opencode/github-actions already documented elsewhere in the same README), and the test badge (568 tests/67 files → 2,254 passing/246 files, matching the file/test counts I measured locally and cross-checked against the latest green main CI run).
  • package.json identity: homepage/repository.url pointed at ruvnet/agent-harness-generator; updated to ruvnet/metaharness to match the actual git remote and the README's own self-declared repo slug.
  • Removed a stray tracked node_modules symlink pointing to an author-local absolute path (/home/ruvultra/projects/...) — node_modules/ is already gitignored, this was accidentally committed.
  • package-lock.json resync: workspace-probe's locked version/bin entry was behind its already-bumped package.json (0.1.0 → 0.1.1 + bin field).

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).
  • Full workspace build (node scripts/build-ordered.mjs) succeeds; full vitest 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 like openclaw), not real regressions, by cross-checking that the latest main CI run (commit c86bc12) is fully green across the Rust/WASM/Node matrix via the GitHub Actions API.
  • Verified the dist/gepa/loop.js rebuild is a clean, minimal diff matching src/gepa/loop.ts exactly (plus an expected cli.js executable-bit fix from the build), no unrelated churn.

Generated by Claude Code

claude added 4 commits July 11, 2026 16:19
…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.
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