docs(encyclopedia): consolidation, hierarchy and rooms design
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
# House encyclopedia: consolidation, hierarchy and rooms
|
||||
|
||||
Extends `2026-10-26-encyclopedia-bounded-planning-design.md`. Supersedes its sections *Finding nearby articles* (assign candidates), *A run* (step list), *Links*, and the plan format; everything else there stays. The binding rule stays: **no model call may need the whole wiki in its context**.
|
||||
|
||||
## Why
|
||||
|
||||
The first production plan (299 articles) had two faults:
|
||||
|
||||
1. **One subject, many peers.** Heating became `heizungsanlage` (1 note), `waermepumpe`, `vitocal-heatpump`, `vitocal-heat-pump` (all the same heat pump), `open3e-integration`, `open3e-viessmann-integration`, … with overlapping scopes and no hierarchy. Assign candidates came only through the articles a note's neighbour notes were already assigned to, so early batches rarely saw existing articles; nothing compared articles with each other; fold only looks at thin articles.
|
||||
2. **No room articles**, although the wiki has room notes (`eg-schlafzimmer-nord-tur-wall-switch`, `eg-schlafzimmer-rollos-toggle-script`, …). The assign prompt sent a note only to "its parent system".
|
||||
|
||||
## Article vectors
|
||||
|
||||
Each run embeds every planned article's `titleDe | titleEn | scope` with the active local `IEmbeddingModel`, in batches of 32 texts. Vectors are cached by slug and text hash, so an unchanged article is embedded once; the cache holds only the current plan's articles. Search is cosine similarity in memory. No model call sees the vectors or the article list: this is local embedding and arithmetic only. An embedding failure ends the planning steps like any other unreachable service.
|
||||
|
||||
## Assign candidates
|
||||
|
||||
The candidates of a batch are two lists, interleaved and deduplicated, capped at `CandidateArticles × 2` as before:
|
||||
|
||||
- the note-neighbour articles (unchanged: articles the batch notes' nearest notes are assigned to);
|
||||
- the `CandidateArticles` articles nearest to each batch note's vector by article vector.
|
||||
|
||||
Interleaving keeps both kinds in the prompt when one alone would fill the cap. A batch now sees the articles earlier batches created even when none of its neighbour notes is assigned yet.
|
||||
|
||||
## A run
|
||||
|
||||
1. Diff. 2. Assign. 3. **Consolidate** (new). 4. Split. 5. Fold. 6. Write. Order and caps otherwise unchanged.
|
||||
|
||||
## Consolidate
|
||||
|
||||
**Groups.** A seed article and its nearest articles by article vector, `ConsolidateGroupSize` (default 8) articles in all, the seed included. Seeds are taken in slug order among the articles no group of this run has covered yet; every member of a group formed this run (asked or remembered) counts as covered. Slug order, not size: it does not depend on source counts, which change nightly, so an unchanged neighbourhood forms the same groups night after night and is not asked again.
|
||||
|
||||
**Call.** One per group, at most `MaxConsolidationsPerRun` (default 60) per run. 60 groups of 8 cover ~300+ articles in one bootstrap night even with overlapping groups (299 articles need at least 38). Input, per member: `slug | titleDe | category | scope | source count | current parent` — bounded by the group size. `ConsolidateMaxTokens` 8000 (the reply is short; thinking tokens count, as for fold).
|
||||
|
||||
**Reply** (JSON): `{"merges":[{"from","into"}],"parents":[{"child","parent"}]}`. A merge says both articles are about the same subject; a parent link says which article is the overview of which.
|
||||
|
||||
**Validation.** Both slugs of an entry must be members of the group and differ. A `from` merges once (first wins); an article merged away is never a merge target. Parent entries are resolved through the merges of the same reply (a merged child or parent stands for its target); a link that would close a cycle anywhere in the plan is dropped. Invalid entries are dropped one by one; a reply that is truncated, not JSON, rejected with 400 or has neither array is unusable.
|
||||
|
||||
**Merge** uses fold's mechanics (`ArticlePlan.Fold`): sources move into the target, `redirects[from] = into`, public when both language files of `from` were public at the run's start (`publicRedirects`), redirects to `from` move along. Additionally the children of `from` move to `into`; if `into` was a child of `from`, it takes `from`'s parent. A move that would close a cycle leaves the child without parent.
|
||||
|
||||
**Memory.** `_state.json` gains `ConsolidatedGroups` (fingerprint → when) for usable replies and `FailedGroups` (fingerprint → when) for unusable ones; a group with a remembered fingerprint is not asked again. Fingerprint: `Prompts.Version` and, per member in slug order, slug, titles, category, scope and parent. Source counts are left out: they change whenever a note is added and say nothing about whether two articles share a subject; with them, most groups would be asked again every night. Both maps keep only the fingerprints of groups formed in the latest run that reached the step, so they do not grow without bound. A call that fails with `HttpRequestException` skips that group only and is not remembered.
|
||||
|
||||
`ReconcileSummary` gains `Merged` (merges applied) and `Parented` (parent links set); the commit message, endpoint body and status panel report them.
|
||||
|
||||
## Hierarchy
|
||||
|
||||
`PlannedArticle` gets an optional `parent` slug. Sources of parent links: consolidation, and split (children of a split get the overview as parent). Fold and merge re-point children; an article deleted for losing its last source leaves its children without parent.
|
||||
|
||||
**Write prompt.** Link targets are the nearby articles (≤ `CandidateArticles`), the parent and up to 20 children (`MaxChildLinks`), marked `main article` / `sub-article` in the list. Split children are linked through this rule in every run, not only the splitting one.
|
||||
|
||||
**Not stale.** A change of parent or children does not make an article stale, like other link-target changes: staleness is still sources and title.
|
||||
|
||||
**Article page.** Deterministic navigation from the plan at the catalog's commit: a "Hauptartikel / Main article" link to the parent above the body, and a "Unterartikel / Sub-articles" list below it, by title. Both are filtered exactly like links: only articles this reader may see in this language (anonymous: both language files public). A private parent or child is invisible, title and slug alike.
|
||||
|
||||
## Rooms
|
||||
|
||||
Category `room` exists; the prompts now use it:
|
||||
|
||||
- **Assign:** a note about something in a specific room additionally feeds that room's article, titled floor + room (e.g. "EG Schlafzimmer"), created when none is among the candidates. The granularity rule still applies: a thin room article may be folded, e.g. into its floor.
|
||||
- **Write:** a room article describes what is in the room and what it does there, and links to the system articles for how things work.
|
||||
|
||||
## One-time re-bootstrap
|
||||
|
||||
Nothing has been written or published yet; only `_plan.json` and `_state.json` exist. The plan file gains `"version": 3`; a plan without it (the current v2) or with another version reads as "no plan", so the next run bootstraps with the new rules. When the stored plan does not read, the stored state is dropped as well (its subjects, failed batches and kept articles belong to the old plan). `Prompts.Version` goes to 8.
|
||||
|
||||
## Cost
|
||||
|
||||
Bootstrap: up to 60 consolidation calls of ~1k input tokens each on top of the assign calls. A normal night: the groups around changed articles.
|
||||
Reference in New Issue
Block a user