Skip to content

P3: self-generated feature wiki + grounded retrieval & evaluation pipeline#11

Merged
ashbunnn merged 211 commits into
RocketChat:mainfrom
echo-xiao:p3-wiki-generation
Jul 22, 2026
Merged

P3: self-generated feature wiki + grounded retrieval & evaluation pipeline#11
ashbunnn merged 211 commits into
RocketChat:mainfrom
echo-xiao:p3-wiki-generation

Conversation

@echo-xiao

Copy link
Copy Markdown
Contributor

Summary

This PR delivers Phase 3 (P3): a self-generated, citation-grounded feature wiki for an indexed codebase, together with the retrieval engine and low-/zero-API evaluation harness that support it. The wiki is now produced end-to-end from the local index (no external wiki fetching).

What's included

Wiki generation pipeline (src/wiki/)

  • generate.ts orchestrates a 5-step pipeline: outline → guide tree → write → citations/verify → diagrams.
  • outline.ts plans chapters with deterministic file anchoring and coverage self-checks.
  • write.ts writes per-page prose with enforced path:Lstart-Lend citations, grounded against the index (unverified references are dropped).
  • 4-level guide tree assembly (guide.ts, tree.ts, families.ts, taxonomy.ts) with MECE + stability gates.
  • Three-scale Mermaid diagrams built from real graph edges (diagram.ts, src/wikimap/mermaid.ts).

Retrieval engine (src/server/engine/)

  • Semantic RRF fusion over local bge-small embeddings, affinity-guided directed walk with self-stop, entry-page/seed selection, and a candidate/architecture map.

Indexer (src/indexer/)

  • Module-graph clustering (Louvain + anchors), cross-layer edge extraction, structural facts, and chunk/skeleton lookup tables.

Evaluation (src/eval/)

  • trace + gold-check reporting (zero-API), an optional semantic judge, deterministic generation, and a per-question retrieval funnel.

Static wiki site (wiki-site/)

  • Read-only rendering (numbered nav tree, prose, Mermaid, clickable source-line citations). English-only UI.

Notes

  • All code comments, log/error strings, and commit messages are in English.
  • npm test223/224 passing. The single failing test (tests/indexer/modulegraph.test.tsfeatureKey … → ee:livechat-enterprise) is a pre-existing issue on this branch, unrelated to the work in this PR.

Echo Xiao and others added 30 commits June 8, 2026 11:31
… eval

Major changes:
- Extract architecture knowledge from AGENTS.md to architecture.json (30 entries, source-verified)
- AGENTS.md stripped to pure rules (tool order, answer format, navigation strategy)
- implement: class skeleton mode (10K+ → ~500 tokens), ClassName.methodName support
- implement: enforce search/graph before implement (session tracking)
- search/graph/implement: navigation hints in responses
- graph: architecture context injection from architecture.json
- grep: sorted by relevance, limited to top 10
- Callee skeletons removed (graph(down) replaces at 1/10th cost)

Bug fixes:
- retriever.ts: callee skeleton bug (objects treated as strings)
- Remove 5 unused deps, unused exports, stale params

Results:
- L1: 25/34 (unchanged, no artificial inflation)
- L2 tokens: 69K → 29K avg/question (-58%)
- L2 implement: 3,070 → 544 avg tokens (-82%)
- Claude judge: 6 GOOD, 16 ACCEPTABLE, 9 WEAK, 3 WRONG (65% usable)

Eval renames: tool-eval → layer1-tool-eval, agent-eval → layer2-agent-eval
New: compare.ts generates 3-way comparison report, comparison-report.md
Remove unused eval layers (layer0-baseline, layer1-tool, compare) and
their scripts. Add executive-report.md with manual quality assessment:
70% of answers rated GOOD/ACCEPTABLE. Update gemini-answers with latest
eval run.
…swers with improved formatting

- Add layer0-baseline-eval and layer1-tool-eval scripts and logs
- Add compare.ts eval utility
- Update all gemini answer logs with refined content
- Update layer2-agent-eval with latest results
- Replace executive-report.md with executive-report-flash.md
Adds the previously-untracked agy-session-runbook so the entire docs/
(proposal, eval log, diagrams, runbook) lives in one commit and stays
recoverable after the next commit deletes the folder.
Full contents preserved in commit dfc72c8. Restore anytime with:
  git checkout dfc72c8 -- docs
…ring; remove dead code

- layer1: add LCS chain-order score (ordered Qs only, partial credit, report-only, not a pass gate) alongside precision/F1/Recall@k/MRR
- layer0: fix to use loadTestcases (grouped-v1); was crashing on the new format
- layer2: drop unused locals
- retriever: remove unused getContext() + orphaned imports
- add judge.ts (shared Claude judge), load-testcases.ts (grouped loader), agy-session-eval.ts (Track B)
- testcases.json -> grouped-v1 (core/supporting/ordered)
benchmark-notools -> layer0-answers (pairs with layer0-baseline-eval.md)
gemini-answers    -> layer2-answers  (pairs with layer2-agent-eval.md)
benchmark         -> reference-claude (gold reference, not a layer)
- src/eval: gen-gemini-nomcp / gen-gemini-mcp-selfloop (generators),
  eval-1..4 (one report per comparison), utils/ (eval-util, load-testcases, testcases.json)
- drop layer0/1/2 naming and the API judge (judge.ts); semantic is Claude-judged
- indexer: mtime/size fast-path in hasher so unchanged files skip read+md5
- package.json: gen:nomcp/gen:mcp + eval:1..4 scripts
Accuracy fixes (registry.ts, skeleton.ts):
- impact mode no longer import-scopes event/pubsub edges, so event-driven
  dependents (callbacks.add / Meteor.subscribe) survive the blast radius
- search runs a content-grep fallback before returning "no results"
  (stops the agent giving up on a wrong entry-symbol guess)
- graph gains a `file` param + centrality-based root selection, so colliding
  symbols (Streamer, sendMessage) traverse the canonical definition
- skeleton indexes module-top-level statements, so top-level callbacks.add
  registrations (notifications listening on afterSaveMessage) become visible

Index loading (index.ts, local-db.ts):
- incremental update patches only changed mappings instead of re-reading
  all ~7600 (minute-scale rebuild -> sub-second)
- parallel batched reads + progress bar for the cold full rebuild
- compact JSON for .global_index.json (drop pretty-print)
- ensureIndex() unifies the load path across server + eval scripts

Infra:
- OUTPUT_DIR -> output.nosync to exclude the regenerable index from iCloud
  sync (the sync storm was throttling index reads)

Eval (single run, new vs baseline; see logs/):
- eval-3 semantic: baseline FAIL 4 -> 1; all 4 cut-targeted weak spots lifted
  (federation/apps-engine/impact-streamer/impact-settings), new-27 -> PASS
- eval-2 deterministic: 29/34 (no regression), index 18.5k -> 19.2k symbols
- eval-1 token regressed (Cut 3 surfaces larger correct subgraphs + token/loop
  workstream not yet done) — tracked as next step
search() was pure lexical fuzzy over symbol *definition sites*, so one
query could only ever surface the seed's own file (recall ceiling ~1/N).
Reframe retrieval as navigation: seed lexically, expand along the call
graph (depth-2, incl. event/pubsub edges), then rank the neighbourhood by
proximity + lexical + intra-cluster cohesion + centrality - hub penalty.
Wire the ranked cluster into the search tool so the agent gets the whole
subsystem on every query (incl. exact-symbol ones). Rank graph(down)
children by relevance before truncating instead of arbitrary slice(0,6).

Make eval-2 honest: promote single-query retrieval (R@10>=0.3) to the
pass gate; demote feed-every-symbol substring recall to a sanity floor.

eval-2: P@5 24.1->27.6, R@10 41->49, R@20 44->58, recall-miss 26->11,
file-recall 97->99.5%; honest combined gate 24/34.
Add dependency-free load-env util so .env overrides stale shell-exported
keys (e.g. an old GEMINI_API_KEY in ~/.zshrc that shadowed the file).
Import it in both gemini generators and refresh regenerated answer logs
and eval reports.
Generators (gen:nomcp / gen:mcp): temperature=0 greedy decode so answers are
reproducible run-to-run — kills the ±3 PASS noise that made single-run
comparisons meaningless. gen:mcp now records "Files Seen In Tool Results" per
answer to power the synthesis split.

eval-3: add a synthesis-recall split (retrieval-recall = tools surfaced it →
synthesis-recall = agent then wrote it; the gap, not retrieval, is the lever),
a deterministic FROZEN rubric verdict gated on core-spine coverage (not
Claude's over-cited file list), and a hand-judged MANUAL verdict layer in
logs/semantic-verdicts.json (answerHash ties each verdict to the answer it
judged; stale ones are flagged loudly). 34/34 manually judged: PASS 16 /
PARTIAL 14 / FAIL 4 (2 FAILs are gen-time rate-limit errors).

eval-1: inline a token-/answer-size-matched naive (search-only) baseline as a
control — answers "is the lift the graph or just more tokens?" (graph +17 pts).

eval-2: promote chain-order LCS from report-only to a pass gate for ordered Qs.

eval-1/2/3 now delete their own report before regenerating (no stale file on
crash). Regenerated answers + reports.
- report.ts joins the eval-1/2/3 sidecars into one retrieval-funnel report:
  token efficiency, cumulative funnel (index -> retrieval -> synthesis),
  per-testcase detail, and a per-type summary (type = diagnostic lens).
- eval-1/2/3 emit machine-readable *-data.json sidecars; eval-2 adds Recall@50
  (the single-query ranking ceiling).
- logs/ reorg: reports/ (per-eval .md appendices) + data/ (generated json,
  gitignored); report.md + semantic-verdicts.json stay at top.
- README: retrieval-funnel section + metric dictionary.
- package.json: refresh runs the report as its last step.
Reports: strip hardcoded editorial conclusions from the eval-1/2/3 and
unified-funnel generators — emit metrics/tables/definitions only, no
interpretive takeaways. Regenerate the four report .md files accordingly.

Indexer: extract cross-layer & dynamic-dispatch edges so previously
disconnected seams connect in the graph —
- REST: rest.get/post('/vN/x') <-> addRoute('x') / API.vN.get/post('x')
- service bus: broadcast('x') <-> onEvent('x')
- streamer: new Streamer('x') <-> sdk.stream('x')
- slash commands: slashCommands.add('x')
- class heritage: extends/implements -> type edges (service<->interface)
GENERATOR_VERSION 7->10 to force a full skeleton rebuild.

Verified: client<->server seams (e.g. livechat/room) now connect; R@10
unchanged (49.4%) — bottleneck is ranking, not reachability.
Weight BFS proximity by edge type instead of raw hop count — strong
structural edges (call/jsx/new) keep a symbol close, weak `type` refs push
it far, dynamic-dispatch edges sit between. Same 2-hop neighbourhood
(recall unchanged); refines ranking so real-call neighbours outrank
type-only references among equidistant candidates.

eval-2: R@5 40.4->42.8 (+2.4), R@10 49.4->50.3 (+0.9), R@20/pass unchanged.
Regenerate gen:nomcp / gen:mcp answers against the cross-layer-edge index
(GENERATOR_VERSION 10) and rebuild the eval-1 / eval-3 reports so they
reflect the new agent runs (prior reports were on pre-edge answers).
The file-overlap auto rubric under-rates capability (right mechanism, different
files). Replace the stale in-pipeline manual-verdict apparatus with a Claude-
judged semantic verdict written to
logs/reports/eval-3-mcp-agent-vs-claude-semantic-judgment.md
(PASS 21 / PARTIAL 10 / FAIL 3, vs the auto rubric's PASS 10 / PARTIAL 14 / FAIL 10).

- eval-3-mcp-vs-claude.ts: remove loadSidecar/answerHash/STALE-warning/manual
  column; keep the deterministic hard / synthesis-split / auto-verdict diagnostics.
- report.ts: drop the now-dead MEAS classifier; label funnel rows as per-file
  pooled (they differ from eval-2's per-testcase mean R@k by design).
- delete logs/semantic-verdicts.json (stale hand verdicts, superseded).
…, eval → gen/tools/token/report

Server: registry becomes a thin MCP adapter; one tool per file (tools/), one
algorithm per file (engine/: seeds, expand, down, up, source, common). New plan
tool + intent.ts recipes set SESSION.intent; graph collapses direction+mode
into move=expand|down|up with intent-driven defaults. implement renamed to
details. Channel ablation (chanOn/ABLATE/ONLY), prefix/score search channels,
AGENTS.md constitution and CodeRetriever all removed; ranking formula untouched.

Eval: gen.ts (--mode=nomcp|mcp, --oracle intent), tools.ts (deterministic gate,
pools search+graph(expand) at the new tool boundary), token.ts, report.ts
(joins tools/token sidecars + answer-derived gather/synth + manual verdicts.md).

Gate: tools eval 24/34 — per-testcase identical to the pre-refactor baseline
(logs/reports/eval-2-BASELINE-pre-refactor.md); R@k/MRR bit-identical.
…seded reports & dead code

- verdicts.md now carries the 34 manual verdicts (PASS 21/PARTIAL 10/FAIL 3) judged
  on the current answers — still valid, answers unchanged since judgment
- delete superseded logs: eval-1/2/3 reports, ablation (ABLATE/ONLY) set, old
  semantic-judgment doc (all recoverable from git history), stale data sidecars
- regenerate logs/report.md via the new pipeline (eval:token + report)
- eval-util: drop unused LOGS export, unexport internal symMatch
…N.md

- README gains a Roadmap section (phase 1 done / phase 2 = seed recall on the 6
  retrievalRecall=0 concept queries) and a note on the frozen tools baseline
- REFACTOR_PLAN.md removed — the plan is executed; recoverable from git history
- rename eval-2-BASELINE-pre-refactor.md → tools-BASELINE-pre-refactor.md
  (the eval-2 script is now tools.ts; content byte-identical, still the frozen anchor)
…) post-refactor

Re-ran gen:mcp (786k tok, 34/34, 0 err) and gen:nomcp on the refactored
plan/search/graph/details engine; refreshed all answers.

Re-judged every verdicts.md row against the new answers: 22 PASS / 9 PARTIAL /
3 FAIL (was 21/10/3). Headline ~flat but 8/34 rows flipped (4 up: claude-06,
claude-07, new-17, new-19; 4 down: tour-07, tour-08, tour-10, new-10) —
free-tier sampling noise, per the frozen refresh discipline.

Regenerated reports: tools 24/34 (deterministic gate — refactor did not
regress tool capability), token MCP 45% vs naive 12% vs no-MCP 14% (agent
+33 pts), report funnel indexed 100% -> top50 55% -> surfaced 51% -> written
40% (bottleneck OK15 / G2 11 / G3 4 / G1-recall 3).

gen.ts: log the full error message instead of truncating to 100 chars.
…rch/graph/details

Design for reframing logs/report.md around the refactored pipeline (approach
A x C: keep the cumulative funnel + add per-tool scorecards; PLAN routing as a
lightweight parsed-accuracy diagnostic). No reindex, no GENERATOR_VERSION
change, no new eval/Gemini run. Structure approved; pending spec review.
Untrack the report-redesign spec and gitignore docs/ — design specs stay
on disk for ongoing work but are not pushed (per user preference). The
spec file itself is unchanged locally.
4th `mode` column on all 34 rows: agent failure mode for the 12 non-PASS
(no-pivot 5 / wrong-subsystem 2 / engine-unrankable 2 / sloppy-source 1 /
dropped-synth 1 / gave-up 1), PASS rows —. Feeds report.ts §3 behavior
diagnosis + agent/engine split. agent-fault 10 / engine-fault 2.
Add resolvedIntent (SESSION.intent after the loop) to AnswerRecord and
write a `## Plan\n\nintent: <x>` section, so report.ts can read the
routing decision untruncated (tool-call args are capped at 100 chars,
which was clipping the intent). Forward-looking: current answers use
report.ts's prefix/classifier fallback; next gen:mcp run is clean.
Widen verdicts parser to 4 cols (id|verdict|mode|reason); add resolveIntent
(## Plan line → truncated-args prefix → classifyIntent fallback); compute
per-row resolvedIntent/routeOk/failureMode/fault/bindingTool on J. Semantic
mode from verdicts.md wins; mechanical fallback via R@50/gather/synth.
Render unchanged this commit (backbone only).
…orecards

§3 (new): agent-fault vs engine-fault diagnosis — 12 non-PASS split into
10 agent-fault / 2 engine-fault, per-mode fix-levers. §4 funnel relabeled to
SEARCH+GRAPH rank / GRAPH loop / SYNTH with ENGINE/agent-fault annotations.
§5 (new): per-tool scorecards (plan route 21/34, search, graph, synth) with
binds# + fix-lever. §6 detail: route ✓/✗, mode, binding columns; binding now
= the semantic failure mode's pipeline stage (consistent with mode). Old
G1/G2/G3 classifier retired. §1/§2 byte-identical (regression guard).
echo-xiao added 28 commits July 14, 2026 09:11
…race rendering + gold scope/recall + drift guard) + unit tests
…s core ⭐ (correctness pushed down into walk) + tests
…ly walk catches it / no hit + which step first hits) + tests
…the paid judge / default reads cache; PASS/PARTIAL/FAIL per question + summary) + tests
…move eval:tools script / refresh reference + delete orphaned metrics.md
…--semantic to use), remove the standalone CLI / verdicts.md output + drop judge from refresh
… script, zero references / no script / no tests)
…eport correctness + semantics; retired eval:tools / judge-CLI / metrics / verdicts)
…andidateModules · summarize summarizeOne orphan · verify --rewrite empty stub + unused MODEL_TIERS import)
…only production code) + rewrite relative imports + test glob → tests/**
….ts (write 527→326; straighten out verify dependency: switch from write.js to citations.js)
…s → extract-edges.ts (extractCalls+extractHeritage+4 constants; skeleton 585→303, edge logic becomes its own module)
…nderWalk/renderAgent + fix scope duplication (name carries the score, drop the duplicate reason line) + tests
… steps · stop reason; dedupe stop reason on the top line; fold STOP into the group header) + tests
…sections + scope/walk use bullets/nested bullets; cut per-round affinity noise; core⭐ shown only on hit) — fixes rendering mashed into one blob
…es a short path (last two segments, to distinguish same-named functions/methods sendMessage.ts) + tests
…ty + ↳ sub-entries for reach/core hit including 0), carried via nested bullets + tests
…DME with current state + strip DeepWiki name repo-wide

This session:
- gen:mcp recovery: under the old key gemini-2.5-flash returned 404 (model retired), wiping out all 34/34 answers;
  switched to a new key (model restored) and regenerated 34 agent answers, with new-20-proxify re-run for a single question (tool loop hadn't wrapped up)
- semantic evaluation: report --semantic real run → PASS 13 / PARTIAL 19 / FAIL 2 (scope 17/34, recall 56%)
- README fully aligned: removed DeepWiki-crawling narrative, deleted dead gen:nomcp command, fixed the refresh chain;
  added wiki/ layer · wiki-site · wiki:gen 5-step pipeline; added latest semantic + Phase 3
- strip DeepWiki name: README/wiki-site frontend comments/write prompt/gate/package.json/prose-generated content
- gitignore: exclude .envrc · logs/reports/ · data/.wiki-prose-hashes.json

Merged in existing workspace changes (P3 wiki-generation):
- gen.ts sendWithRetry (backoff retry on Gemini transient 404/429/5xx, without affecting determinism)
- indexer/modulegraph · engine/down · wiki-site/app.js · scripts/wiki-serve · deleted wiki/extractors/prose.ts
- data/wiki-map · wiki-prose · wiki-meta/{deployment,development}
…+ build-site assembly script

- scripts/build-site.mjs: wiki-site/ + data/{wiki-map,wiki-prose}.json → dist/ (the viewer fetches via /data/ absolute paths, so the publish root must contain data/)
- vercel.json / netlify.toml: buildCommand=build-site, output directory dist
- package.json adds build:site; gitignore excludes dist/.vercel/.netlify
- verified locally: serve dist, both / and /data/*.json return 200, JSON is valid
…tomy, remove Roadmap

- How it works: end-to-end two-stage throughline — offline (ts-morph extracts skeleton → extract-edges 12 edges →
  GLOBAL_INDEX → summarize+embed summary vectors) + online four tools (plan→search→graph:expand uses
  RRF to fuse structural score with semantic cosine → details uses ts-morph to read real source). Highlights the "zero runtime external dependency"
  north star, the self-generated wiki doubling as a routing layer, and the RRF two-signal complementarity motivation (see the selfgen-wiki / semantic-ranking design docs, only writing what's landed)
- correct the stale assertion "No embeddings — pure graph arithmetic" → graph-primary + semantic cosine RRF fusion
- Evaluation: rewritten as a "semantic pass/fail / trace pinpoints the culprit" dichotomy; delete the Retired bullet
- delete the whole Roadmap section; clear the last remaining DeepWiki wording
… mistake

report.md / verdicts-latest.json / wiki-verify.md brought into git (a snapshot of evaluation results, including the latest semantic 13/19/2 baseline);
removed logs/reports/ from .gitignore. Note: report.md changes on every run, so after a refresh it must be recommitted to keep GitHub up to date.
…ason text of trace/walk/entry

- report.ts rendering text, walk.ts walk stop reasons/affinity reason, entry.ts seed/page selection reason,
  trace.ts fallback reason all translated CN→EN (code comments remain in Chinese)
- report.ts stopLabel includes-matching synced to walk.ts's new English keywords (coupling fix)
- zero-API re-run of trace + report; numbers unchanged (34/34 · scope 17/34 · recall 56% · semantic 13/19/2)
- regenerated the English logs/reports/report.md (zero residual Chinese)
…glish-only

- Comments, JSDoc, log/error messages, and test titles across src/ and tests/
- README, scripts/, package.json, .gitignore
- wiki-site: drop the zh dictionary and language toggle, render English-only UI
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@ashbunnn
ashbunnn merged commit 35f21fe into RocketChat:main Jul 22, 2026
1 check was pending
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.

3 participants