As-built completion record for phase 3 of the knowledgebase: on-demand consolidated repair-article
generation from a cross-source cluster via claude -p --model opus, in a draft → human review →
publish workflow. This is the final phase — the knowledge system is now complete end-to-end
(OCR ingest → vectorize → semantic clustering → on-demand cited consolidation). Proven live: two
real articles generated and one published to
https://knowledgebase.taild4189d.ts.net/consolidated/engine-cooling/.
Design in 2026-07-25-kb-consolidated-gen-phase3-design, task-by-task plan in
2026-07-25-kb-consolidated-gen-phase3-plan — this note is the completion record and hard-won
knowledge, not a restatement of either. Builds directly on
2026-07-24-kb-semantics-phase2-complete (whose clusters.json is the retrieval bundle) and reads the
2026-07-24-knowledgebase library.
For Agents
Code:
/home/levander/knowledgebase/on telep-mainframe (runs aslevander). New modules:genprompt.py(pure — builds the never-invent prompt),speccheck.py(pure — number-token spec check),gen.py(background job: cluster → gather →claude -p→ draft → speccheck),kbgen.py(CLI) + tests. Co-located inknowledgebase/, NOTkb-vectors/(the design’s proposed home) — deliberately, to avoid cross-venv imports; the generator reuses the KB app’sbuild_site,slugs.safe_slug, anddocs/access directly. Routes added to the existing knowledgebase Flask app (app.py):POST /generate/<cluster_id>,GET /drafts,GET /drafts/<slug>,POST /drafts/<slug>/publish,POST /drafts/<slug>/discard,GET /api/genjobs/<id>. Plus a “Generál” button per cluster on/semantics. Generator:claude -p --model opus— Claude Code natively installed at/home/levander/.local/bin/claude, logged in aslevander(subscription).--model opusis explicit (never Sonnet, per house rule); empty/error output → job fails loudly, nothing published. Artifacts: drafts indrafts/<slug>/(draft.md,meta.json, images); published articles indocs/consolidated/<slug>/(browsable, vectorizable on nextkbvec index). NO new always-on service — routes on the already-running KB app; single serialized background worker like the OCR ingest.
The flow
/semantics cluster ──"Generál"──▶ POST /generate/<id> (enqueue, returns job id)
│
single serialized background worker:
gather cluster member section .md + images
→ rewrite image refs to SOURCE-UNIQUE names (see gotcha ⚠)
→ build never-invent prompt (genprompt.py)
→ claude -p --model opus (prompt on stdin)
→ write DRAFT: drafts/<slug>/ {draft.md, meta.json, images}
→ run speccheck (advisory number-token flags → meta.json)
│
GET /drafts/<slug> ◀── AI-provenance banner + speccheck warnings + article
│
┌───────────┴───────────┐
Közzététel (publish) Elvetés (discard)
move → docs/consolidated/<slug>/ delete drafts/<slug>/
→ build_site() (atomic swap)
→ browsable + vectorizable
What was built
genprompt.py(pure): builds theclaude -pprompt from a cluster + its member section markdown. Each source delimited/labelled (=== SOURCE [<manual_id>] — <heading> ===); enforces the cite-everything / never-invent contract (see the tip).speccheck.py(pure): extracts number tokens from the draft and flags any not string-present in the concatenated source text — an advisory flag surfaced in the review UI, never a publish gate.gen.py(background job): loads a cluster fromclusters.json, gathers member.md+ images, rewrites image refs to source-unique names, runsclaude -p --model opus, writes the draft +meta.json(cluster id, sources, state, speccheck warnings, created), runs speccheck. Single serialized worker (idempotentstart_worker; the box is shared/power-flaky).kbgen.py(CLI):cluster <id>(generate a draft),list-drafts,publish <slug>,discard <slug>.- KB app routes + “Generál” button on
/semantics— draft rendered with a prominent AI-provenance banner, the speccheck warnings, and Közzététel / Elvetés buttons; publish moves the draft intodocs/consolidated/<slug>/and rebuilds the site atomically; discard deletes the draft dir. - Claude Code on the box: native installer to
~/.local/bin,claude login(interactive, subscription), then headlessclaude -p --model opuswith the prompt on stdin.
Result (the real proof — two live articles)
Two real cross-source clusters were generated and read against their sources:
- Engine-cooling cluster — PUBLISHED LIVE at
https://knowledgebase.taild4189d.ts.net/consolidated/engine-cooling/. 3 real source tags (chevy-tracker/geo-tracker-repair,suzuki-vitara/5door-supplement,suzuki-vitara/workshop-1988-1998), 49 images rendering, AI-provenance header present. - Parking-brake cluster — generated as a draft (still pending review). 40 real source citations;
specs verbatim-traceable to their sources (e.g.
20 to 25 kg (44 to 55 lbs),7 to 9 notches); nothing invented; it honestly noted a source that contributed nothing to the topic, and flagged a genuine both-ways disagreement between sources with a ⚠ marker rather than picking one.
Pending review artifact
The parking-brake draft is still unreviewed — it lives in
drafts/<slug>/and is a normal review artifact the user may publish (Közzététel) or discard (Elvetés) from/drafts/<slug>. It is not lost work; it’s the human gate doing its job.
Read-only discipline held: the source manuals and Qdrant were untouched; the only additions to the
live KB were under docs/consolidated/.
Key design decisions
- Cite-everything / never-invent trust model — every spec/step/number/torque/wire-color tagged with
its source
[manual_id]; numbers/units reproduced verbatim (no rounding/converting); conflicts shown both-ways with a ⚠ warning, never resolved by the model. - On-demand, one cluster at a time — no batch, no auto-generation.
- Draft → human review → publish, never auto-publish — publish is the only step that changes the live
KB, and it’s human-gated + atomic (reuses
build_site’s existing atomic swap). --model opus, explicit — never Sonnet; unavailable/empty/error → fail loudly, nothing published.- speccheck is advisory, ON TOP OF human review — an automated number-token spec-check that flags, but never blocks; the human is the backstop.
- Published articles carry an AI-provenance banner and live in a separate
consolidated/folder — they are never presented as a manual. - Modules co-located in
knowledgebase/(notkb-vectors/) — avoids cross-venv imports; reuses the KB app’s helpers directly. (This is the one deliberate deviation from the design doc, which proposedkb-vectors/+generate.py; the built file isgen.pyinknowledgebase/.)
Hard-won gotchas
Image basename collision across manuals — every OCR'd manual reuses names like
_page_62_Figure_16.jpegA consolidated article pulls images from MULTIPLE manuals, but every marker-OCR’d manual names its images the same way (
_page_62_Figure_16.jpeg, etc.). Copying them naively means a draft would silently get the WRONG manual’s diagram — a real accuracy failure for a repair document. Fix: before generation, rewrite each source’s image refs to SOURCE-UNIQUE names (safe_slug(manual_id) + "__" + basename) and build animage_map(unique-name → real file). The draft carries the unique refs, so copies resolve 1:1 to the correct source image. Verified: 4 refs → 4 correct copies, all prefixed.
The never-invent contract +
claude -pon the boxPrompt contract (genprompt.py): use ONLY facts in the provided sources; tag every spec/step/number with
[<manual_id>]; reproduce numbers/units/torque/wire-colors verbatim (never round/convert/infer); where sources disagree, present both with tags + a ⚠ (don’t pick one); carry through source images with their tags; if the sources don’t cover something, omit it — don’t fill gaps. Output is clean markdown,# <label>, a short source-naming intro, then the consolidated procedure. Claude on the box: native installer →~/.local/bin/claude(add toPATH),claude login(interactive, subscription), then headlessclaude -p --model opuswith the prompt on stdin works. It shares the box with Frigate / OCR / vectorize → serialize, one at a time.
- speccheck “false flags” are mostly REAL verbatim-deviations. The number-token check flags any draft
number not string-present in the sources. These fire mainly when the model reformats a number
(
1,000→1000,40→40.0) — which the never-invent prompt forbids — so a “false flag” is usually a real deviation worth surfacing. Keep it advisory (never a publish gate); human review is the backstop. It also flags year-digits from citation slugs (e.g.1988/1998from[...workshop-1988-1998]) — harmless. Deferred minor: strip thousands-commas + ignore digits inside[citation]brackets. start_workermust be idempotent (guard with a module flag) or a second call spawns a concurrent worker, breaking the one-at-a-time serialization that the power-flaky box depends on.publish()safety:safe_slugguarantees a non-empty single path component (resolve_withinalone returnsbasefor empty parts), so a degenerate label can’t makermtreewipe the wholeconsolidated/folder;build_site’s atomic swap keeps the served site consistent even if a publish build fails.
Ops / failure modes
- Single serialized background worker (shares the box with Frigate/OCR/vector); a job lost to a reboot is just re-run (idempotent by slug — re-generating overwrites the draft).
claudefailure / empty output → jobfailedwith the stderr tail; no draft published.- speccheck warnings never block; they inform the human gate.
- Publish is the only step that mutates the live KB; human-gated + atomic build swap.
- Read-only over the source manuals + Qdrant; only writes
drafts/and (on publish)docs/consolidated/. - Runs on telep-mainframe → down whenever the box is (unstable mains — see 2026-07-23-mains-power-shutdowns).
Roadmap position — full system COMPLETE
- Phase 1 (vectorize) — DONE (2026-07-24-kb-vectorize-complete).
- Phase 2 (cluster + explore) — DONE (2026-07-24-kb-semantics-phase2-complete).
- Phase 3 (consolidated generation) — DONE (this note).
The knowledge system is now complete end-to-end: OCR ingest → vectorize → semantic clustering →
on-demand cited consolidation. Possible future work: an optional claude -p relabel of clusters (behind
the make_label seam from phase 2); diagram understanding (hybrid CV path-tracing + a vision model,
accuracy-guarded) as a later phase; and the deferred speccheck normalization minors above.
Related
- 2026-07-25-kb-consolidated-gen-phase3-design — the design this implements (trust model, workflow, prompt contract, risks)
- 2026-07-25-kb-consolidated-gen-phase3-plan — the task-by-task implementation plan
- 2026-07-24-kb-semantics-phase2-complete — phase 2: the clustering whose
clusters.jsonis this phase’s retrieval bundle - 2026-07-24-kb-vectorize-complete — phase 1: the vectors the clustering is built on
- 2026-07-24-knowledgebase — the manual library + Flask app these routes fold into; the
build_site/safe_slughelpers reused - telep-mainframe — the host
- 2026-07-23-mains-power-shutdowns — why it’s down when the box is
- SESSION-HANDOVER
- homelab
- LOG
- TOPICS