reindex-brain
Safely ADD new knowledge to the Ramco ERP "Brain" (the LLM-wiki / vectorless-RAG knowledge system) as a strictly additive, non-regressing positive delta. Use this skill WHENEVER you are extending, rei
SKILL.mdplugins/ramco-brain/skills/reindex-brain/SKILL.md
GitHubReindexing the Brain — additive, non-regressing changes only
The Brain is a compiled LLM wiki: raw product files are parsed into JSONL
registers, generators project registers into cross-linked Markdown wiki pages
(+ a kb/ machine projection + overlays), and a BM25 index makes it queryable. This
skill governs how to add to that artefact so it strictly grows.
Prime directive: positive delta, never regress
Treat the current Brain as pre-eminent and immutable in effect. Every change must be a positive delta:
- Nothing already indexed is removed or overwritten. Registers are run-id'd and never clobbered; generators are idempotent; a fact class lives in exactly one page and others link to it.
- Earlier answers must not change. After any change, every question the Brain could already answer must still answer at least as well (verified by re-running the eval — see the Regression Gate below). This is the whole point: we never regress.
- A valid change is exactly one (or more) of these three shapes. If your change is not one
of these, stop and reclassify it:
- A new artefact — a new source file/type becomes new register rows and new/enriched wiki content.
- A new cross-connection — a new edge (
[[wikilink]]/ CIM / call-graph / FK / journey) fuses more of the graph together. - A new prompt addition — new guidance/tool in the chatbot's system prompt so it uses knowledge it already has.
Deletion, renaming that breaks links, replacing a good run with an ungated one, or merging two sources so a prior row disappears are all forbidden — they regress the Brain.
Which change am I making?
New raw source / file class / more of an existing source ......... NEW ARTEFACT → §A
New relationship between existing things (edge/link/journey) ..... CROSS-CONNECTION → §B
The Brain HAS the fact but the chatbot doesn't surface it well ... PROMPT ADDITION → §C
Most "it can't answer X" problems are not missing data — the V1 eval found ~45% of misses were surfacing/synthesis gaps (the fact was present, just not assembled). So check §C first: try a prompt/tool addition before adding a parser. Cheapest fix wins.
The one class where §C is NOT enough: impact / blast-radius queries. A question like
"if I widen column bodit_bpono, where does it get affected?" needs the chatbot to traverse
edges the graph does not contain — column→SP (which SPs read/write this column),
column→screen, column→service, and cross-component document-number links (a sibling
component storing the same *_bpono reference). You cannot prompt your way to an edge that
isn't in the graph — no §C rule makes the chatbot name an SP or a downstream component when
there is no column→SP or cross-component edge to follow. These are §A/§B data gaps first
(build the edge), and §C second (add an impact rule + tool that fans out over the new edges).
Diagnose honestly: if the missing answer is a name that lives on an edge nobody built, it's
a data gap, not a surfacing gap. Worked example + the full remediation roadmap:
references/case-impact-queries.md.
The pipeline you are extending (recap)
RAW (ramco-erp-brain/, immutable) → PARSER P-* → REGISTER (JSONL, run-id'd)
→ GENERATOR gen_*.py (pure projection, idempotent) → WIKI PAGES (L1–L6)
→ kb/ projection + overlays (journeys / a2ui / migration) → BM25 index + ToC
brain/_registers/<parser_id>/<run_id>/*.jsonl — every batch carries an accepted
pointer (last gate-passing --accept run — what generators read) and a current pointer
(last run of any mode). The accepted pointer only advances on a full gate pass — this
is the core device that makes a bad new run invisible to the wiki.
Full mechanics for each stage are in references/pipeline.md — read it before touching a parser or generator.
§A — Add a NEW ARTEFACT (source → registers → pages)
- Decide the mode (
PARSER/HYBRID/LLM-DIRECT). Build a real parser only if the source is structured and repeated at scale, a binary container the LLM can't read raw, or too big for context; otherwise route it LLM-DIRECT. Add a class row toparser_plan.md(V1 classes 1–44; V2 adds 45–53). Dispatch by magic bytes, not extension. - Inherit the output contract: run-id'd JSONL, a
record_type:headerfirst line withcounts.in/out/skipped_named, named residuals,source_path+locationper record, and a golden-file regression suite. - Widening an existing glob? The new glob must be disjoint from the old one (verify
0 overlap); recount per BPC so
old_total + new_disjoint = new_totalwith the delta enumerated; tag new sub-classes (e.g.logic_class) — never silently merge into main counts. Re-run only the newly-included dirs. - Rule: add sources, not new ontology. A new parser must feed existing wiki layers. Do not invent orphan page types.
- Acceptance discipline: golden-count reproduction → LLM spot-audit (cross-read ≥5 random
source files vs parser output) → gate pass → then the
acceptedpointer is written. A failed--acceptrun is suffixed.FAILEDand links nothing. - Rebuild run-maps (
build_runmaps.py) so generators find the new run, then re-run the generators (idempotent, register-only). New rows flow into pages by projection.
See references/pipeline.md §"Add a parser/artefact" for the exact commands and the per-component ingest loop (scout → mine → compile → lint → maintain).
§B — Add a NEW CROSS-CONNECTION (an edge in the graph)
Cross-connections are added by extending one of the five edge-layer generators, all of which converge on the component basename as the node id:
[[Name]]wikilinks (basename resolution) ·gen_cim_cardinality.py(CIM topology) ·gen_sp_callgraph.py(EXEC edges) ·gen_reverse_indexes.py+gen_cross_fk.py(P-CSV/P-DDL lineage) ·gen_journey_index(journey → component/screen links).
Rules that keep an edge from regressing the graph:
- Resolve by basename, use the
_SERalias merge, and the`CODE`†backtick-escape for partners with no page — or you create dangling links. - Every emitted
[[link]]must resolve —check_links.pyis a binding gate (exit 0 iff 0 dangling and 0 unreconciled A2UI packs). New journeys are gated byverify_journeys.py(schema + all refs resolve). - Preserve frontier honesty in the call graph (disclose
dynamic/relay/externalas an unresolved frontier; never invent a resolution). Raise-helper edges stay out of the logic graph.
Sub-component (column-level) impact edges — a known frontier (gaps G-43…G-47). The five generators above all key on the component basename, so today the graph carries table↔table (intra-component FK), SP→SP (call graph), and component↔component (CIM / service call) edges — but no edge finer than the component. Impact queries need four edge types that are not yet built:
- column→SP read/write — the write targets exist in the SP text (P-SP-SKEL captured them,
then collapsed to a per-table count); re-emit them as
sp_writes_column/sp_reads_columnso "which SPs touchbodit_bpono" is answerable, not just "873 SPs in the component". - cross-component document-number linkage — a sibling component storing the same document
number (e.g.
Pur_Rel_Slip.prs_mn_bpono→ Bl_PO'sbpoh_bpono). Match on the composite key (*_bpono+*_bpoamendno+*_bpoou) as the reliable signal — same-UDD alone is too noisy (everyudd_documentnocolumn would false-match). - column→screen (control
dataIndex→column) and column→service reverse indexes — capture the screen control→column binding, then project column→screen and column→service.
These are §A/§B work (build the capture pass + a new edge generator), added under the same
non-regression discipline (basename convergence where possible, [[link]]s that resolve,
check_links.py stays at 0 dangling). Until built, they are first-class declared gaps
in status.json (G-43…G-47) — never a silent "the Brain just didn't try hard enough".
See references/pipeline.md §"Cross-connections" and references/case-impact-queries.md.
§C — Add a PROMPT ADDITION (help the chatbot surface what it has)
The chatbot never re-reads raw files; it navigates the wiki with 9 tools driven by a system prompt. To make it use existing knowledge better:
- System prompt: edit the template in
vercel_deploy/brain_chatbot/src/prompt.js(buildSystemPrompt()). Add a rule, a doc-type hint, or an enumerate-before-answering nudge. - Tool descriptions also steer the model —
TOOL_SCHEMASinsrc/tools.js. A new tool needs aTOOL_SCHEMASentry + anEXECUTORSexecutor + anindexStore.jsfunction. - Additive only: append/clarify guidance; do not remove or contradict existing rules (e.g. the ARM cross-check, the GRN caveat, the "don't refuse a vague question" rule).
- New pages need indexing: after adding content, rebuild the BM25 index
(
scripts/build_index.js, ~6s, deterministic) so the chatbot can find it.
See references/pipeline.md §"Prompt & tools".
Non-regression: the guarantees you must preserve
These eight devices are what make the Brain safe to grow. Do not weaken any of them. Full detail + the exact gate names in references/non-regression.md:
- Accepted pointer advances only on gate pass (bad runs stay
.FAILED, invisible). - count-in == count-out per class (sums must reconcile; nothing silently dropped).
- Skip only by class, never by item (unextracted → named residual with a class label).
- ≥1 evidence path per claim; never invent (a name exists only if it's in a source).
- Gaps are first-class (declared in
status.jsongaps[]; a silent gap is corruption). - Golden-file regression + tripwires (reproduce golden counts or update with a changelog).
- Provenance-tagged UNION, not replacement (broaden a source as a superset; log conflicts).
- Blast-radius re-run on any parser fix (re-run every batch it touched; recompile dependents).
The Regression Gate (mandatory before you call it done)
A change is only "done" when you have proven no regression:
-
check_links.py→ 0 dangling, 0 unreconciled A2UI packs. -
verify_journeys.py→ 0 schema-invalid, 0 dangling refs. - Every touched register batch's
gates.jsonall pass; count sums reconcile. - Re-run the Layer-1 eval (see the
eval-brainskill). Compare to the last accepted run: FULL% and answerable% must not drop, and no previously-FULL question may regress to PARTIAL/MISSING. New coverage should only add FULL answers. - Spot-check 3–5 questions the Brain already answered well — same or better, same citations.
-
status.json: new/closed gaps recorded with the run that changed them; version bumped.
If any previously-answered question got worse, do not accept the run — the accepted
pointer stays on the prior baseline (that's your rollback) until you fix the regression.
Operator checklist (every reindex)
- Classify the change (§A / §B / §C). Prefer §C, then §B, then §A.
- Make the additive change following that section's rules.
- Rebuild run-maps → re-run affected generators (idempotent) → rebuild BM25 index.
- Run the Regression Gate above.
- Only on all-green: advance the
acceptedpointer and record provenance instatus.json. - Never delete, never overwrite a good run, never merge away a prior row.