diff --git a/.agents/skills/ckb-index-external/SKILL.md b/.agents/skills/ckb-index-external/SKILL.md index 32a432a..2efcf95 100644 --- a/.agents/skills/ckb-index-external/SKILL.md +++ b/.agents/skills/ckb-index-external/SKILL.md @@ -94,6 +94,12 @@ connector: sharepoint # sharepoint | google_drive | web | custom — open location: "https://contoso.sharepoint.com/sites/Finance/Shared Documents/Reports" description: "Finance team's shared reports folder" # optional +refresh_interval_days: 7 # optional — how often a write-access run should rebuild this source, + # and the freshness_window_days stamped on its generated pages. + # Omit to use the default of 30. Tune per source: a busy folder + # that changes daily deserves a shorter window than a quarterly + # reports archive that barely moves. + index: # optional — omit entirely for the original default: the index lives only # here, in this repo, tracked by this repo's own git (nothing to fetch/publish) store: git # git | shared — open string, where the built index is published to / fetched from @@ -183,6 +189,15 @@ This is the one place access level actually changes behavior: from here on; Step 8 is what actually creates the remote copy either way. +Either way, compare the existing index's newest `last_updated` against +this source's `refresh_interval_days` (default 30) and say where it +stands. A read-only user needs to know they're reading a copy that's three +weeks past due so they can go ask the admin rather than quietly trusting +it; a write-access user is about to rebuild anyway, but "this was 40 days +stale" is worth saying, because a source that's routinely overdue is +either configured with too tight an interval or has nobody actually +maintaining it. Both are worth surfacing rather than silently correcting. + ### Step 5 — Resolve the connector and enumerate/summarize documents Resolve `connector` to whatever live tool is actually available this @@ -215,7 +230,8 @@ tldr: ... confidence: 0.3–0.9 # per the rule above quality: ... last_updated: YYYY-MM-DD -freshness_window_days: 30 # shorter than a typical wiki page — external sources change without notice +freshness_window_days: 30 # this source's refresh_interval_days, or 30 if unset — shorter than a + # typical wiki page, because external sources change without notice retention: medium --- ``` @@ -253,7 +269,7 @@ tldr: One sentence — who/what this is, and what these documents specifically s confidence: 0.3–0.9 quality: ... last_updated: YYYY-MM-DD -freshness_window_days: 30 +freshness_window_days: 30 # or this source's refresh_interval_days, if set retention: medium --- ``` diff --git a/.agents/skills/ckb-ingest/SKILL.md b/.agents/skills/ckb-ingest/SKILL.md index dce86ae..ead6656 100644 --- a/.agents/skills/ckb-ingest/SKILL.md +++ b/.agents/skills/ckb-ingest/SKILL.md @@ -63,6 +63,17 @@ libraries, concepts, systems. Create entity pages at relationships between entities — `uses`, `depends_on`, `caused`, `contradicts`, `supersedes` — as edge data in `wiki/graph/edges.json`. +Also record `has_expertise_in` and `owns` edges when the material actually +evidences them: `has_expertise_in` when a person demonstrably answers +questions or explains decisions on a topic, `owns` when they hold +declared responsibility for a system, area, or decision. These are what +make "who knows about X" and "who owns X" answerable as a direct graph +lookup instead of a full-text guess (see `ckb-retrieve` Step 3). Record +them only from demonstrated evidence — someone being present in a meeting +or cc'd on a thread is not expertise, and don't infer ownership from job +title alone. Both are optional, like every other edge type; an absent +edge is better than a fabricated one. + For recurring teams, clients, systems, or initiatives, consider whether a plain project scope page under `wiki/projects/.md` would make future queries easier to route. A scope page should list when to use it, included @@ -95,9 +106,30 @@ structured distillation before writing the final page: - `High-signal excerpts` for dense technical paragraphs or consecutive messages that would be lost in a single summary. -If a high-signal excerpt is substantial enough to answer future questions -on its own, keep it as its own section or linked page rather than burying -it inside a broad transcript summary. +"High-signal" needs an acceptance test, or every excerpt looks worth +keeping and the page becomes a second copy of the transcript. Promote a +run of text to its own section or linked page only when it clears all +three: +- **It carries a rare term.** Something specific enough that a future + search would use it — a config flag, an error string, a hostname, a + contract clause, a version number. Check with `rg -c` across `wiki/`: + if the term already appears on many pages it isn't a distinguishing + handle, and the excerpt adds no findability the summary doesn't have. +- **It's substantial.** Roughly 200 characters or more, or a few + consecutive paragraphs/messages from one author. A one-line "yes, do + that" is a resolution to fold into `Resolution`, not an excerpt. +- **Something corroborates it.** It was agreed with, acted on, corrected, + or referred back to later in the material. An unanswered assertion is a + claim, not a settled fact — keep it in the summary with that ambiguity + intact rather than promoting it. + +Fail any one of the three and the content still belongs in the page, just +inside `Summary`/`Resolution` rather than as its own retrievable unit. + +When you do promote an excerpt, carry its parent topic with it — the +thread question or section heading it sat under. An excerpt that reads +unambiguously on its own is the entire point; one that needs the +surrounding transcript to make sense hasn't been extracted, only moved. ### Step 5 — Link and cross-reference diff --git a/.agents/skills/ckb-lint/SKILL.md b/.agents/skills/ckb-lint/SKILL.md index ff2accf..852970d 100644 --- a/.agents/skills/ckb-lint/SKILL.md +++ b/.agents/skills/ckb-lint/SKILL.md @@ -124,9 +124,20 @@ doesn't need an Error Book entry — this is for patterns, not incidents. For each `libs//source.yaml`, verify it has a non-empty `connector` and `location` — report only, this file is never edited by any skill. If an `index:` block is present, verify it has a non-empty `store` and -`location` too. Also flag (report only) a `libs//` that ambiguously -has both real content files and a `source.yaml` — that's a configuration -conflict for the user to resolve, not something to guess at. +`location` too. If `refresh_interval_days` is present, verify it's a +positive integer. Also flag (report only) a `libs//` that +ambiguously has both real content files and a `source.yaml` — that's a +configuration conflict for the user to resolve, not something to guess at. + +Report any connector-backed source whose generated index is overdue: +newest `last_updated` in `libs//` older than its +`refresh_interval_days` (default 30). Report it the same way whether or +not this user has write access — a read-only user can't fix it, but +knowing which source to chase the admin about is the actionable part. Name +how overdue it is rather than just "stale," since a source two days past a +7-day interval is a different situation from one six months past a 30-day +one. Never re-index here; that's `ckb-index-external`'s job, and suggesting +it is as far as this check goes. Don't flag a missing `libs//source.local.yaml` as an issue — its absence is the correct, read-only default (see `ckb-index-external`), not diff --git a/.agents/skills/ckb-retrieve/SKILL.md b/.agents/skills/ckb-retrieve/SKILL.md index b817998..ac9d448 100644 --- a/.agents/skills/ckb-retrieve/SKILL.md +++ b/.agents/skills/ckb-retrieve/SKILL.md @@ -87,11 +87,20 @@ its Sources. Keep the shortlist to what's plausibly relevant; this isn't If index/TLDR matching is not enough, run a local hybrid pass before giving up: use `rg` for exact tokens across `wiki/`, `raw/archive/`, -`outputs/`, and readable upstream indexes; combine those hits with -project-scope matches, entity/title/TLDR matches, graph proximity, -freshness, confidence, and quality. Prefer exact text matches for error -strings, commands, flags, filenames, hostnames, IDs, and other literals; -prefer entity/semantic matches for paraphrased questions. +`outputs/`, `raw/inbox/`, and readable upstream indexes; combine those +hits with project-scope matches, entity/title/TLDR matches, graph +proximity, freshness, confidence, and quality. Prefer exact text matches +for error strings, commands, flags, filenames, hostnames, IDs, and other +literals; prefer entity/semantic matches for paraphrased questions. + +Sweep `raw/inbox/` even though nothing there has been ingested yet. +Material dropped an hour ago can already hold the answer, and finding it +there is also the clearest possible signal that an `ckb-ingest` run is +overdue — mention that. Say plainly when an answer rests on un-ingested +inbox material rather than on a synthesized page. + +Keep each signal's hits as its own ranked list rather than merging them by +eye as you go. Step 4 needs the separate orderings. ### Step 3 — Walk the graph for anything the shortlist missed @@ -100,22 +109,70 @@ connector's own `graph/edges.json`), follow `depends_on`/`uses`/`caused` edges one hop out to catch a connected page the index text match alone wouldn't have surfaced. -### Step 4 — Expand local context and build an evidence packet +For "who knows about X" and "who owns X" questions — both advertised in +the trigger phrases above — the graph is the primary lookup rather than a +fallback. Read the `has_expertise_in` and `owns` edges pointing at the +topic entity and answer from the people or teams on the other end, +ordered by how many distinct sources evidence each edge. Where no such +edge exists yet, fall back to authorship evidence: who the `## Sources` +material actually shows answering questions on that topic. Say which of +the two grounded the answer, since an inferred expert is a weaker claim +than a recorded one. -For every result that might ground the answer, keep a small evidence -packet with: +### Step 4 — Fuse the signals, dedupe, then rerank + +Signals disagree, and none of them is trustworthy alone: an exact `rg` hit +can sit in a page that answers a different question, and a confident TLDR +match can be a loose paraphrase of something stale. Don't settle that by +picking a favourite signal — fuse the ranked lists from Step 2. + +**Fuse.** For every candidate, sum `weight / (k + rank)` across each list +it appears in, where `rank` is its 1-based position in that list. Use +`k = 10` and a default `weight` of 1.0. A page ranked third in three +different lists beats one ranked first in a single list; consensus is the +whole point. + +`k = 10` is deliberately smaller than the `k = 60` rank fusion is usually +quoted with. 60 is tuned for retrievers returning hundreds of candidates, +and against the dozen-or-so a local wiki produces it flattens every score +into near-identical values. Raise a list's weight when the question +warrants it — for a pasted error string, command, flag, or ID, weight the +exact-match list around 2.0, because no amount of title/TLDR similarity +should outrank a literal match on the token the user actually pasted. + +**Dedupe.** Collapse candidates carrying the *same claim* into one entry +before ranking further. Step 6 actively manufactures these: a `wiki/` +page, the `raw/archive/` file it cites, and a connector-index page whose +`resource:` points back at that same wiki page are three hits for one +fact. Keep whichever sits closest to the primary material and record the +others as corroboration, not as independent evidence. Three views of one +claim are not three sources. + +**Rerank.** Score each surviving candidate 0–10 on how well it answers +*the literal question asked*, not on how well it matches the query's +vocabulary, then keep the best handful and drop the rest. This is the same +agent making a deliberate second pass, not a separate model. The point is +that relevance judgment happens explicitly, over the shortlist, in one +place — rather than being folded silently into how the final answer gets +drafted. + +### Step 5 — Expand local context and build an evidence packet + +For every result that survived Step 4, keep a small evidence packet with: - source path or connector resource - matched claim or short excerpt - source date or `last_updated` - freshness/confidence/quality signals, when available - project-scope or graph relationship hints, when relevant +- which signals it was fused from, its rerank score, and anything it + absorbed during dedupe — this is what Step 7 draws caveats from When a match is a section, heading, or snippet inside a larger Markdown file, include nearby headings/paragraphs before deciding what it means. Avoid answering from an isolated fragment when the neighboring context changes the interpretation. -### Step 5 — Follow every page on the shortlist to its source before answering from it +### Step 6 — Follow every page on the shortlist to its source before answering from it This is the step this skill exists to enforce. For each page on the shortlist that will actually ground part of the answer: @@ -148,7 +205,7 @@ deck doesn't need a full read every time, just enough (search for the name/topic, read the surrounding context) to confirm the point actually being used. -### Step 6 — Reconcile and answer +### Step 7 — Reconcile and answer If the source confirms the index, answer normally — but note what grounded it (e.g. "per the kickoff transcript cited in @@ -162,6 +219,17 @@ exist to eventually catch, so mention it's worth a lint pass if the gap looks like more than a one-off, but don't rewrite the wiki page yourself mid-answer unless the user asks for that separately. +State caveats in the answer itself, not only in the metadata you read to +build it. When a page grounding the answer is past its +`freshness_window_days`, carries a low `confidence` or `quality`, rests on +un-ingested `raw/inbox/` material, or was checked against a cached +connector index rather than a live re-fetch, say so in a short clause next +to the claim it qualifies. Surface a conflict between two live pages the +same way, even when neither is marked `superseded_by` yet. The metadata +already exists and Step 4 already put it in front of you; the failure mode +is answering confidently *from* a stale or contested page without passing +that on, which leaves the reader no way to weigh it. + If no page anywhere is even plausibly relevant, say so — don't verify against a source that has nothing to do with the question just to appear thorough, and don't fabricate an answer either. For durable missing @@ -172,6 +240,11 @@ that would close the gap. If you edit `wiki/query-gaps.md`, update ## Edge cases +- **Only one signal returned anything** — fusion is a no-op, so don't run + the arithmetic for show. The rerank still applies (it's the check that + the one list actually answers the question rather than merely matching + its wording), and dedupe still applies as soon as Step 6 pulls in a + page's own cited source. - **A cited source file no longer exists** (moved, renamed, or cleaned up) — say so plainly rather than silently falling back to the `tldr` as if it had been verified. Flag it as a likely `ckb-lint` finding (a diff --git a/AGENTS.md b/AGENTS.md index b8ed52e..51fbf58 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,7 +86,7 @@ retention: high|medium|low --- ``` -`wiki/index.md` alone also carries `kb_schema_version`, currently `"1.2"`. +`wiki/index.md` alone also carries `kb_schema_version`, currently `"1.3"`. Detailed schema migration and version-bump policy belongs in `ckb-upgrade`. @@ -112,7 +112,9 @@ file. Short routing rules: - For questions, use `ckb-retrieve`; it owns project scopes, hybrid local - search, evidence packets, source verification, and query-gap capture. + search, rank fusion across signals, dedupe/rerank, expertise and + ownership lookups, evidence packets, source verification, answer + caveats, and query-gap capture. - For "Ingest", "Sync the wiki", or "Update the wiki", use `ckb-ingest`. - For "Index external sources", "index libs", or "refresh the external index", use `ckb-index-external`. diff --git a/MANUAL.md b/MANUAL.md index 07cc9f1..9db5b3e 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -105,6 +105,13 @@ which stay entirely read-only. There are three ways to mount one: committed, never seen by anyone else). This is deliberate: it lets one or two people maintain a source for the whole team instead of everyone redundantly re-scanning it. + - **You can set how often it should be refreshed.** Add an optional + `refresh_interval_days: 7` to `source.yaml` (it defaults to 30). A + folder that churns daily wants a shorter window than a quarterly + archive nobody touches. Both "index external sources" and "Lint" then + tell you when a source is overdue and by how much — which matters most + if you're read-only for it, since knowing *which* source has gone + stale is what lets you go ask the person who maintains it. Either way, once it's mounted, just ask questions normally — the agent checks your local `wiki/` first, then walks `linked/`, then `libs/`, and @@ -143,6 +150,17 @@ Example: > reminding you to review the result and say "sync changes" once you're > happy with it. +For a long transcript, the agent doesn't just write one summary page. It +pulls out the searchable question, the summary, the resolution, the systems +and people involved — and promotes individual passages to their own +findable sections when they'd otherwise be lost inside a summary. That last +part has a deliberate bar: a passage has to contain a genuinely specific +term (a flag, an error string, a clause, a version), run to a couple of +sentences at least, and be corroborated by something later in the material. +Otherwise it stays folded into the summary. Without that bar every +paragraph looks quotable and the wiki page ends up being the transcript +again, which defeats the point of ingesting it. + If `raw/inbox/` is empty, the agent scans `raw/` directly instead (still skipping `raw/archive/`, which is already-processed history). @@ -206,6 +224,9 @@ This runs a health check across the whole wiki: - orphaned pages (nothing links to them) get backlinked or archived - broken graph edges get fixed or removed - missing/duplicate index and log entries get corrected +- connector-backed sources whose index is overdue for a refresh get + flagged, with how overdue they are — useful even if you're read-only for + that source, since it tells you who to chase - recurring systemic issues get written into `wiki/error-book.md` It fixes what it safely can on its own, and reports the rest for you to @@ -369,6 +390,32 @@ knowledge graph for connected facts, and falls back to `linked/`/`libs/` if the local wiki has nothing. You get an answer grounded in what's actually written down, not a guess. +Two things about that worth knowing as a user: + +- **It searches `raw/inbox/` too.** Something you dropped in this morning + and haven't ingested yet can still answer your question. The agent will + tell you when an answer came from un-ingested material, which doubles as + a nudge that "Ingest" is overdue. +- **Answers carry their own caveats.** If the page behind an answer is past + its freshness window, scored low on confidence, or was read from a cached + connector index instead of a live check, the answer says so next to the + claim. If two pages disagree and neither has been marked superseded yet, + you'll hear about that too. The point is that you never have to go read + the frontmatter yourself to know how much to trust what you just got. + +### Ask who knows something + +> "Who knows about the checkpoint restore path?" / "Who owns the billing +> service?" + +These are answered from the knowledge graph directly rather than by +keyword-searching for names. Ingest records an expertise or ownership edge +when the source material actually shows someone answering questions on a +topic or holding declared responsibility for it — not from having attended +a meeting or from a job title. If nobody has a recorded edge yet, the agent +falls back to who the archived sources show answering that kind of question +and tells you it's inferring rather than reporting. + When there is still no answer, the agent should tell you what is missing and either add/propose a short entry in `wiki/query-gaps.md` or suggest the smallest source to drop into `raw/inbox/`. @@ -471,6 +518,7 @@ Example: > connector: sharepoint > location: "https://contoso.sharepoint.com/sites/Finance/Shared Documents/Reports" > description: "Finance team's shared reports folder" +> refresh_interval_days: 7 > ``` > *then say "Index external sources."* > @@ -554,7 +602,7 @@ graph stay in sync with what you changed. | `raw/archive//` | Agent | Auto-filed copy of what you dropped in `raw/inbox/`, organised by ingestion date. Don't hand-file here — let Ingest do it, so the date and pairing with the log entry stay accurate. | | `linked//` | **You** (you create the symlink) | Points at another KB's real files, which live and get edited *in that other repo* — never here. The agent must never write inside `linked/`. | | `libs//` (git-copy, no `source.yaml`) | **You** (you `git clone`) | A frozen copy of an external KB. Update it by re-pulling that repo yourself, not by hand-editing files here. The agent must never write inside it. | -| `libs//source.yaml` (connector) | **You, only** | Declares the connector, location, and optionally where a shared/pre-built index lives (`index:`). The agent reads it but never writes it — same as anything else upstream. | +| `libs//source.yaml` (connector) | **You, only** | Declares the connector, location, optionally how often it should be refreshed (`refresh_interval_days:`), and optionally where a shared/pre-built index lives (`index:`). The agent reads it but never writes it — same as anything else upstream. | | `libs//source.local.yaml` (connector) | **You** (or the agent, only when you explicitly ask to become/stop being that source's admin) | Per-person, per-machine `access: write`/`read` setting — never committed, never seen by anyone else. Absent = read-only, the default. | | `libs//{index.md,entities/,graph/,log.md}` (connector) | Agent-generated, **you can freely edit** | The agent's own index of that one connector's source, built/refreshed by "Index external sources" — but only if you have `access: write` locally; read-only users just get a fetched copy. Structurally the same deal as the main `wiki/` row below — feel free to correct an entry by hand, then run "Lint" (it now also checks connector-backed indexes, respecting the same read/write split). Scoped entirely to that connector; never blended into `wiki/`. | | `wiki/` (pages, `index.md`, `overview.md`, `log.md`, `error-book.md`, `entities/`, `graph/`) | Agent-generated, **you can freely edit** | This is the one place the agent both writes and expects you might too. Feel free to correct a page by hand — just keep the frontmatter fields intact (or update `last_updated`), and run Lint afterward if you touched something the index/graph/log reference. | @@ -584,4 +632,5 @@ graph stay in sync with what you changed. | "Export the wiki to Starlight" | Human-readable docs site at `outputs/starlight/` | `ckb-export-starlight` | | "Upgrade the wiki" / "Check for a newer template version" | Checks template + wiki schema versions against the canonical repo, upgrades what you accept | `ckb-upgrade` | | "Index external sources" / "Index libs" | Builds/refreshes a self-contained index for each connector-backed `libs//` | `ckb-index-external` | -| Just ask a question | Answers from the wiki, using the index/TLDR/graph cascade | — (core query workflow) | +| Just ask a question | Answers from the wiki, using the index/TLDR/graph cascade, with caveats when a source is stale or contested | `ckb-retrieve` | +| "Who knows about X" / "Who owns X" | Answers from expertise/ownership edges in the graph | `ckb-retrieve` | diff --git a/MANUAL.pl.md b/MANUAL.pl.md index 9447120..554a10d 100644 --- a/MANUAL.pl.md +++ b/MANUAL.pl.md @@ -110,6 +110,14 @@ podpięcia: niewidoczny dla współpracowników). To celowe: pozwala jednej lub dwóm osobom utrzymywać źródło dla całego zespołu, zamiast żeby każdy redundantnie je skanował. + - **Możesz ustawić, jak często ma być odświeżane.** Dodaj opcjonalne + `refresh_interval_days: 7` do `source.yaml` (domyślnie 30). Folder + zmieniający się codziennie potrzebuje krótszego okna niż kwartalne + archiwum, którego nikt nie tyka. Wtedy zarówno „index external + sources", jak i „Lint" powiedzą ci, kiedy źródło jest zaległe i o ile — + co ma największe znaczenie, jeśli masz do niego dostęp tylko do + odczytu, bo wiedza o tym, *które* źródło się przedawniło, pozwala + zapytać osobę, która je utrzymuje. Niezależnie od sposobu, po podpięciu wystarczy normalnie zadawać pytania — agent sprawdza najpierw twoją lokalną `wiki/`, potem przechodzi przez @@ -149,6 +157,17 @@ Przykład: > `raw/archive/2026-07-10/`. Na koniec przypomina o przejrzeniu wyniku i > powiedzeniu „sync changes”, gdy będziesz zadowolony. +Przy długim transkrypcie agent nie pisze po prostu jednej strony +podsumowania. Wyciąga wyszukiwalne pytanie, podsumowanie, rozwiązanie oraz +zaangażowane systemy i osoby — a pojedyncze fragmenty awansuje do własnych +znajdowalnych sekcji, jeśli inaczej przepadłyby wewnątrz podsumowania. Ta +ostatnia część ma celowy próg: fragment musi zawierać naprawdę konkretny +termin (flagę, komunikat błędu, klauzulę, numer wersji), mieć co najmniej +kilka zdań i być potwierdzony przez coś dalej w materiale. W przeciwnym razie +zostaje wtopiony w podsumowanie. Bez tego progu każdy akapit wygląda na wart +zacytowania, a strona wiki znów staje się transkryptem — co przekreśla sens +jego zingestowania. + Jeśli `raw/inbox/` jest puste, agent skanuje bezpośrednio `raw/` (nadal pomijając `raw/archive/`, które zawiera już przetworzoną historię). @@ -179,6 +198,21 @@ ingest to wchłonie. Dzięki temu wiki pozostaje napędzana zapotrzebowaniem: rośnie wokół tego, o co faktycznie pytasz, a nie wokół wszystkiego, co teoretycznie dałoby się spisać. +Trwałe braki można też śledzić w `wiki/query-gaps.md`. Dobry wpis o luce jest +maleńki: pytanie, gdzie agent szukał i jakie najmniejsze źródło lub strona +sprawiłaby, że odpowiedź będzie dostępna następnym razem. + +### D. Utwórz lokalny zakres projektu + +Gdy jakiś temat, klient, system lub inicjatywa wraca często, poproś: + +> „Utwórz zakres projektu dla integracji płatności." + +Agent utworzy lub zaktualizuje zwykłą stronę Markdown pod `wiki/projects/`, +wymieniającą strony, encje, pliki z `raw/archive/`, indeksy konektorów i +obszary grafu, które należy przeszukać najpierw dla tego zakresu. Nadal masz +jedną lokalną wiki; to tylko daje powracającym pytaniom lepszy punkt startowy. + --- ## 3. Utrzymanie porządku @@ -201,6 +235,9 @@ To uruchamia przegląd kondycji całej wiki: albo są archiwizowane - uszkodzone krawędzie grafu są naprawiane lub usuwane - brakujące/podwójne wpisy w indeksie i dzienniku są poprawiane +- konektorowe źródła, których indeks jest zaległy do odświeżenia, zostają + oflagowane wraz z informacją o ile — przydatne nawet jeśli masz do tego + źródła dostęp tylko do odczytu, bo mówi ci, kogo dopytać - powtarzające się problemy systemowe trafiają do `wiki/error-book.md` Naprawia samodzielnie to, co może zrobić bezpiecznie, a resztę zgłasza do @@ -363,11 +400,46 @@ Po prostu zapytaj, zwykłym językiem: > „Co wiemy o ryzyku migracji w Q3?” -Agent najpierw czyta `wiki/index.md`, żeby znaleźć odpowiednie strony, -sprawdza ich jednolinijkowy `tldr` przed załadowaniem pełnej strony, -przechodzi po grafie wiedzy w poszukiwaniu powiązanych faktów i sięga do -`linked/`/`libs/`, jeśli lokalna wiki nic nie ma. Dostajesz odpowiedź -opartą na tym, co faktycznie zostało spisane, a nie na domysłach. +Agent najpierw czyta `wiki/index.md`, żeby znaleźć odpowiednie strony. Jeśli +istnieje pasujący zakres projektu pod `wiki/projects/`, przeszukuje najpierw +ten zakres. Potem sprawdza jednolinijkowe pola `tldr`, w razie potrzeby +uruchamia dokładne wyszukiwanie lokalne dla literalnych tokenów, rozszerza +kontekst wokół dopasowanych sekcji, przechodzi po grafie wiedzy w poszukiwaniu +powiązanych faktów i sięga do `linked/`/`libs/`, jeśli lokalna wiki nic nie ma. +Dostajesz odpowiedź opartą na tym, co faktycznie zostało spisane, a nie na +domysłach. + +Dwie rzeczy warte wiedzenia jako użytkownik: + +- **Przeszukuje też `raw/inbox/`.** Coś, co wrzuciłeś dziś rano i czego jeszcze + nie zingestowałeś, nadal może odpowiedzieć na twoje pytanie. Agent powie ci, + kiedy odpowiedź pochodzi z niezingestowanego materiału, co jednocześnie + sygnalizuje, że „Ingest" jest zaległy. +- **Odpowiedzi noszą własne zastrzeżenia.** Jeśli strona stojąca za odpowiedzią + przekroczyła okno świeżości, ma niską pewność albo została przeczytana z + zapisanego indeksu konektora zamiast z żywego źródła, odpowiedź mówi o tym + obok danego twierdzenia. Jeśli dwie strony są ze sobą sprzeczne, a żadna nie + została jeszcze oznaczona jako zastąpiona, też o tym usłyszysz. Chodzi o to, + żebyś nigdy nie musiał sam czytać frontmatteru, by wiedzieć, na ile zaufać + temu, co właśnie dostałeś. + +Gdy nadal nie ma odpowiedzi, agent powinien powiedzieć, czego brakuje, i albo +dodać/zaproponować krótki wpis w `wiki/query-gaps.md`, albo zasugerować +najmniejsze źródło do wrzucenia do `raw/inbox/`. + +### Pytanie, kto się na czymś zna + +> „Kto zna się na ścieżce przywracania checkpointów?" / „Kto jest właścicielem +> usługi billingowej?" + +Na te pytania odpowiada bezpośrednio graf wiedzy, a nie wyszukiwanie nazwisk po +słowach kluczowych. Ingest zapisuje krawędź eksperctwa lub własności, gdy +materiał źródłowy faktycznie pokazuje, że ktoś odpowiada na pytania w danym +temacie albo ma zadeklarowaną odpowiedzialność za niego — a nie na podstawie +obecności na spotkaniu czy nazwy stanowiska. Jeśli nikt nie ma jeszcze +zapisanej krawędzi, agent wraca do tego, kogo zarchiwizowane źródła pokazują +jako odpowiadającego na tego rodzaju pytania, i mówi ci, że wnioskuje, a nie +raportuje. ### Nauka z wiki @@ -472,6 +544,7 @@ Przykład: > connector: sharepoint > location: "https://contoso.sharepoint.com/sites/Finance/Shared Documents/Reports" > description: "Wspólny folder raportów zespołu finansowego" +> refresh_interval_days: 7 > ``` > *potem mówisz „Index external sources.”* > @@ -562,7 +635,7 @@ zmieniłeś. | `raw/archive//` | Agent | Automatycznie zarchiwizowana kopia tego, co wrzuciłeś do `raw/inbox/`, uporządkowana według daty ingestu. Nie umieszczaj tu plików ręcznie — pozwól, żeby zrobił to Ingest, tak by data i powiązanie z wpisem w dzienniku były poprawne. | | `linked//` | **Ty** (tworzysz dowiązanie symboliczne) | Wskazuje na rzeczywiste pliki innej KB, które żyją i są edytowane *w tamtym repozytorium* — nigdy tutaj. Agent nigdy nie może zapisywać wewnątrz `linked/`. | | `libs//` (kopia git, bez `source.yaml`) | **Ty** (robisz `git clone`) | Zamrożona kopia zewnętrznej KB. Aktualizujesz ją, ponownie pobierając to repozytorium samodzielnie, a nie ręcznie edytując pliki tutaj. Agent nigdy nie może zapisywać wewnątrz niej. | -| `libs//source.yaml` (konektor) | **Tylko ty** | Deklaruje konektor, lokalizację i opcjonalnie gdzie znajduje się współdzielony/wcześniej zbudowany indeks (`index:`). Agent go czyta, ale nigdy nie zapisuje — tak jak wszystko inne nadrzędne. | +| `libs//source.yaml` (konektor) | **Tylko ty** | Deklaruje konektor, lokalizację, opcjonalnie jak często ma być odświeżany (`refresh_interval_days:`) i opcjonalnie gdzie znajduje się współdzielony/wcześniej zbudowany indeks (`index:`). Agent go czyta, ale nigdy nie zapisuje — tak jak wszystko inne nadrzędne. | | `libs//source.local.yaml` (konektor) | **Ty** (albo agent, tylko gdy wyraźnie poprosisz o zostanie/przestanie bycia administratorem tego źródła) | Osobiste, per-komputer ustawienie `access: write`/`read` — nigdy niecommitowane, nigdy niewidoczne dla innych. Brak = tylko do odczytu, domyślnie. | | `libs//{index.md,entities/,graph/,log.md}` (konektor) | Generowane przez agenta, **możesz swobodnie edytować** | Własny indeks agenta dla tego jednego źródła konektora, budowany/odświeżany przez „Index external sources” — ale tylko jeśli masz lokalnie `access: write`; użytkownicy tylko-do-odczytu dostają po prostu pobraną kopię. Strukturalnie ta sama zasada jak przy wierszu `wiki/` poniżej — śmiało popraw wpis ręcznie, a potem uruchom „Lint” (teraz sprawdza też indeksy oparte na konektorach, respektując ten sam podział odczyt/zapis). Ograniczone wyłącznie do tego konektora; nigdy nie wmieszane w `wiki/`. | | `wiki/` (strony, `index.md`, `overview.md`, `log.md`, `error-book.md`, `entities/`, `graph/`) | Generowane przez agenta, **możesz swobodnie edytować** | To jedyne miejsce, w którym zarówno agent zapisuje, jak i spodziewa się, że ty też możesz. Śmiało popraw stronę ręcznie — zachowaj tylko pola frontmatteru (lub zaktualizuj `last_updated`) i uruchom potem Lint, jeśli dotknąłeś czegoś, do czego odwołuje się indeks/graf/dziennik. | @@ -592,4 +665,5 @@ zmieniłeś. | „Export the wiki to Starlight” | Czytelna dla człowieka strona dokumentacji w `outputs/starlight/` | `ckb-export-starlight` | | „Upgrade the wiki” / „Check for a newer template version” | Sprawdza wersje szablonu i schematu wiki względem kanonicznego repozytorium, aktualizuje to, co zaakceptujesz | `ckb-upgrade` | | „Index external sources” / „Index libs” | Buduje/odświeża samodzielny indeks dla każdego `libs//` opartego na konektorze | `ckb-index-external` | -| Po prostu zadaj pytanie | Odpowiedź z wiki, przy użyciu kaskady indeks/TLDR/graf | — (podstawowy przepływ zapytań) | +| Po prostu zadaj pytanie | Odpowiedź z wiki, przy użyciu kaskady indeks/TLDR/graf, z zastrzeżeniami gdy źródło jest nieaktualne lub sprzeczne | `ckb-retrieve` | +| „Kto wie o X" / „Kto jest właścicielem X" | Odpowiedź z krawędzi eksperctwa/własności w grafie | `ckb-retrieve` | diff --git a/README.md b/README.md index 25cb731..5797bfd 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,17 @@ undifferentiated blob: searchable question, short summary, resolution or decision, systems/code references, people involved, and high-signal excerpts that deserve to stay findable on their own. +"High-signal" is an explicit test, not a judgment call, or every excerpt +looks worth keeping and the page becomes a second copy of the transcript. +An excerpt earns its own retrievable section only if it carries a term rare +across the wiki (checked with `rg -c` — a distinguishing search handle, not +a word already on twenty pages), runs to roughly 200 characters or more, and +is corroborated by something later in the material agreeing with, acting on, +or correcting it. Fail any one and the content still lands in the page, just +inside the summary rather than as its own unit. Promoted excerpts carry +their parent heading or thread question with them, so they read +unambiguously alone. + ### Lazy-Loading Index with "Use When" Triggers `wiki/index.md` is a routing table. Each entry has a **Use when** column listing trigger keywords. The agent reads the index first (stays in context), @@ -106,7 +117,9 @@ question, the agent falls back to the full cascade. When index/TLDR routing is not enough, the agent can combine several local signals before answering: - exact text search with `rg` for error strings, commands, flags, filenames, - hostnames, ticket IDs, and other literal tokens + hostnames, ticket IDs, and other literal tokens — including across + `raw/inbox/`, so material dropped an hour ago and not yet ingested can + still answer a question (and flags that an ingest is overdue) - semantic/entity matches from page titles, TLDRs, project scopes, and graph relationships - freshness and confidence metadata, so stale or weak pages are treated with @@ -114,9 +127,48 @@ signals before answering: - context expansion around a matched section, so answers are grounded in the neighboring headings and paragraphs rather than a lone snippet +Each signal produces its own ranked list, and the lists are then fused +rather than resolved by picking a favourite: every candidate scores +`weight / (k + rank)` summed across the lists it appears in, so a page +ranked third by three signals beats one ranked first by a single signal. +`k` is 10, deliberately smaller than the 60 rank fusion is usually quoted +with — 60 is tuned for retrievers returning hundreds of candidates and +flattens all scores into near-identical values against the dozen a local +wiki produces. Literal-token queries up-weight the exact-match list, since +no amount of title similarity should outrank a match on the exact string +someone pasted. + +Fused candidates are then deduplicated by claim — a wiki page, the +`raw/archive/` file it cites, and a connector page pointing back at it are +three hits for one fact, not three sources — and reranked 0–10 on how well +each answers the literal question rather than how well it matches the +question's wording. Same agent, deliberate second pass, no separate model. + The result is normalized internally as an evidence packet: source path, -matched claim, date/freshness, confidence/quality, and relationship or scope -hints. No server, vector database, or dedicated client is required. +matched claim, date/freshness, confidence/quality, relationship or scope +hints, and which signals each candidate was fused from. No server, vector +database, or dedicated client is required. + +### Answer Caveats +Metadata the wiki already tracks is stated in the answer itself, not just +consulted while building it. When a page grounding an answer is past its +`freshness_window_days`, carries low `confidence`/`quality`, rests on +un-ingested `raw/inbox/` material, or was checked against a cached +connector index rather than a live re-fetch, the answer says so next to +the claim it qualifies. Conflicts between two live pages are surfaced the +same way even when neither carries `superseded_by` yet. The failure mode +this closes is answering confidently *from* a stale page without passing +that on. + +### Expertise & Ownership Lookups +"Who knows about X" and "who owns X" are direct graph lookups rather than +full-text guesses. Ingest records `has_expertise_in` edges when someone +demonstrably answers questions or explains decisions on a topic, and +`owns` edges for declared responsibility over a system, area, or decision +— both from demonstrated evidence only, never inferred from meeting +attendance or job title. Where no edge exists yet, retrieval falls back to +authorship evidence and says which of the two grounded the answer, since +an inferred expert is a weaker claim than a recorded one. ### Query Gaps If the cascade cannot answer a question, the agent records or proposes a @@ -151,16 +203,18 @@ retention: high|medium|low - **freshness_window_days** — pages older than this get flagged during lint - **retention** — low pages are archived after 2× freshness window -`wiki/index.md` alone also carries `kb_schema_version` (e.g. `"1.2"`), declaring +`wiki/index.md` alone also carries `kb_schema_version` (e.g. `"1.3"`), declaring which revision of this schema the wiki was authored against — bump minor for additive optional fields, major for breaking changes. ### Entity Extraction & Knowledge Graph During ingest, the agent extracts typed entities (people, projects, libraries, concepts, systems) and stores them as pages in `wiki/entities/`. Typed -relationships (`uses`, `depends_on`, `caused`, `contradicts`, `supersedes`) -are recorded in `wiki/graph/edges.json`. Queries can walk the graph to -discover connected pages (e.g. "what depends on Redis?"). +relationships are recorded in `wiki/graph/edges.json` — structural ones +(`uses`, `depends_on`, `caused`, `contradicts`, `supersedes`) plus +people-to-topic ones (`has_expertise_in`, `owns`). Queries can walk the +graph to discover connected pages (e.g. "what depends on Redis?") or to +answer "who knows about X" directly. ### Recursive Index & Log Convention Any `wiki/` subdirectory that groups multiple pages (`entities/`, `graph/`, @@ -180,7 +234,15 @@ mirror locally: connector: sharepoint location: "https://contoso.sharepoint.com/sites/Finance/Shared Documents/Reports" description: "Finance team's shared reports folder" +refresh_interval_days: 7 # optional, defaults to 30 ``` +`refresh_interval_days` tunes cadence per source — a folder that churns +daily deserves a shorter window than a quarterly archive that barely moves +— and sets the `freshness_window_days` stamped on that source's generated +pages. Both "index external sources" and "Lint" report a source that's +overdue against it, and say by how much, so a read-only user knows which +admin to chase rather than quietly trusting a copy three weeks past due. + Say "index external sources" and the agent walks it, resolving `connector` to whatever live tool is available that session (a connected Microsoft 365/Google Drive MCP tool, or `WebFetch` for a plain URL), and builds a @@ -240,6 +302,7 @@ Periodically (or on demand), the agent health-checks the wiki: - **Orphan detection** — finds pages with no inbound links - **Graph consistency** — verifies all edges point to existing entities - **Index/log consistency** — verifies every subdirectory has an index.md and no change is double-logged +- **Connector cadence** — flags a connector-backed source whose generated index is overdue against its `refresh_interval_days`, and by how much - **Error Book** — records systemic issues with root cause and fix Auto-fixes what it can (broken links, missing backlinks, stale flags), and diff --git a/README.pl.md b/README.pl.md index 9b624b8..ba2fc3e 100644 --- a/README.pl.md +++ b/README.pl.md @@ -75,6 +75,25 @@ się do kontekstu tylko wtedy, gdy jest faktycznie wywoływana. Odrębne od skilla `ckb-sync-changes`, który jest czysto operacją na poziomie gita, bez syntezy wiki. +Dla długich rozmów, notatek ze spotkań, transkryptów lub eksportów czatu +ingest używa strukturalnej destylacji, zamiast traktować cały plik jako jedną +niezróżnicowaną bryłę: wyszukiwalne pytanie, krótkie podsumowanie, +rozwiązanie lub decyzja, odniesienia do systemów/kodu, zaangażowane osoby +oraz fragmenty o wysokiej wartości, które zasługują na to, by pozostać +znajdowalne samodzielnie. + +„Wysoka wartość" to jawny test, a nie ocena uznaniowa — inaczej każdy +fragment wygląda na wart zachowania, a strona staje się drugą kopią +transkryptu. Fragment zasługuje na własną wyszukiwalną sekcję tylko wtedy, +gdy zawiera termin rzadki w całej wiki (sprawdzane przez `rg -c` — wyróżniający +uchwyt wyszukiwania, a nie słowo już obecne na dwudziestu stronach), ma około +200 znaków lub więcej i jest potwierdzony przez coś dalej w materiale, co się +z nim zgadza, działa na jego podstawie lub go koryguje. Niespełnienie choćby +jednego warunku oznacza, że treść nadal trafia na stronę, tylko wewnątrz +podsumowania, a nie jako osobna jednostka. Awansowane fragmenty zabierają ze +sobą nagłówek nadrzędny lub pytanie wątku, żeby dały się jednoznacznie +czytać samodzielnie. + ### Leniwie ładowany indeks z wyzwalaczami „Use When" `wiki/index.md` to tabela routingu. Każdy wpis ma kolumnę **Use when** z listą słów kluczowych wyzwalających. Agent najpierw czyta indeks (pozostaje @@ -87,6 +106,86 @@ Podczas zapytania agent najpierw czyta TLDR-y. Jeśli TLDR już odpowiada na pytanie, pełna treść nigdy nie jest ładowana. Łańcuch odwoławczy: TLDR → treść → surowe źródło. +### Lokalne zakresy projektów +Dla powracających zespołów, klientów, systemów lub inicjatyw wiki może +trzymać zwykłe strony zakresów w formacie Markdown pod `wiki/projects/`. +Strona zakresu wymienia strony wiki, encje, źródła z `raw/archive/`, +konektorowe `libs/`, wyjścia i obszary grafu, które należy przeszukać +najpierw dla danego projektu. Daje to tę samą praktyczną korzyść co +przestrzeń robocza projektu w większym systemie wyszukiwania, pozostając +lokalnym, przejrzystym i edytowalnym w dowolnym edytorze tekstu. + +Zakresy zawężają tylko pierwsze przejście. Jeśli wyszukiwanie w zakresie nie +odpowiada na pytanie, agent wraca do pełnej kaskady. + +### Lokalne wyszukiwanie hybrydowe +Gdy routing po indeksie/TLDR nie wystarcza, agent może połączyć kilka +lokalnych sygnałów przed odpowiedzią: +- dokładne wyszukiwanie tekstu przez `rg` dla komunikatów błędów, komend, + flag, nazw plików, nazw hostów, identyfikatorów zgłoszeń i innych + literalnych tokenów — również w `raw/inbox/`, więc materiał wrzucony + godzinę temu i jeszcze nie zingestowany nadal może odpowiedzieć na pytanie + (i sygnalizuje, że ingest jest zaległy) +- dopasowania semantyczne/encyjne z tytułów stron, TLDR-ów, zakresów + projektów i relacji w grafie +- metadane świeżości i pewności, dzięki którym nieaktualne lub słabe strony + są traktowane ostrożnie +- rozszerzenie kontekstu wokół dopasowanej sekcji, aby odpowiedzi były + osadzone w sąsiadujących nagłówkach i akapitach, a nie w samotnym urywku + +Każdy sygnał tworzy własną listę rankingową, a listy są następnie łączone, +zamiast rozstrzygania przez wybór ulubionego sygnału: każdy kandydat zbiera +`weight / (k + rank)` zsumowane po listach, na których występuje, więc strona +na trzecim miejscu w trzech listach wygrywa ze stroną pierwszą w jednej. +`k` wynosi 10, celowo mniej niż zwykle cytowane 60 — 60 jest dostrojone do +wyszukiwarek zwracających setki kandydatów i spłaszcza wszystkie wyniki do +niemal identycznych wartości przy kilkunastu, które daje lokalna wiki. +Zapytania o literalne tokeny podnoszą wagę listy dokładnych dopasowań, +ponieważ żadne podobieństwo tytułu nie powinno wyprzedzić trafienia w dokładny +ciąg, który ktoś wklejił. + +Połączeni kandydaci są następnie deduplikowani według twierdzenia — strona +wiki, plik z `raw/archive/`, który cytuje, i strona konektora wskazująca na +nią to trzy trafienia dla jednego faktu, nie trzy źródła — i przerankowani w +skali 0–10 według tego, jak dobrze odpowiadają na dosłownie zadane pytanie, a +nie jak dobrze pasują do jego sformułowania. Ten sam agent, świadome drugie +przejście, bez osobnego modelu. + +Wynik jest wewnętrznie normalizowany jako pakiet dowodowy: ścieżka źródła, +dopasowane twierdzenie, data/świeżość, pewność/jakość, wskazówki o relacjach +lub zakresie oraz informacja, z których sygnałów każdy kandydat został +połączony. Nie jest wymagany żaden serwer, baza wektorowa ani dedykowany +klient. + +### Zastrzeżenia w odpowiedziach +Metadane, które wiki już śledzi, są podawane w samej odpowiedzi, a nie tylko +sprawdzane przy jej budowaniu. Gdy strona stanowiąca podstawę odpowiedzi +przekroczyła `freshness_window_days`, ma niską `confidence`/`quality`, opiera +się na niezingestowanym materiale z `raw/inbox/` lub została sprawdzona +względem zapisanego w pamięci indeksu konektora, a nie żywego źródła — +odpowiedź mówi o tym obok twierdzenia, którego to dotyczy. Konflikty między +dwiema aktywnymi stronami są ujawniane tak samo, nawet jeśli żadna nie ma +jeszcze `superseded_by`. Zamyka to tryb awarii polegający na pewnej +odpowiedzi *z* nieaktualnej strony bez przekazania tej informacji dalej. + +### Wyszukiwanie ekspertów i właścicieli +„Kto wie o X" i „kto jest właścicielem X" to bezpośrednie zapytania do grafu, +a nie zgadywanie po pełnym tekście. Ingest zapisuje krawędzie +`has_expertise_in`, gdy ktoś wykazuje się odpowiadaniem na pytania lub +wyjaśnianiem decyzji w danym temacie, oraz krawędzie `owns` dla zadeklarowanej +odpowiedzialności za system, obszar lub decyzję — oba wyłącznie na podstawie +wykazanych dowodów, nigdy wnioskowane z obecności na spotkaniu czy ze +stanowiska. Gdy krawędzi jeszcze nie ma, wyszukiwanie wraca do dowodów +autorstwa i mówi, które z dwóch stanowiło podstawę odpowiedzi, bo domniemany +ekspert to słabsze twierdzenie niż zapisany. + +### Luki w zapytaniach +Jeśli kaskada nie potrafi odpowiedzieć na pytanie, agent zapisuje lub proponuje +krótki wpis w `wiki/query-gaps.md`: o co pytano, gdzie szukał i jakie +najmniejsze źródło lub strona zamknęłaby lukę. Dzięki temu nieudane +wyszukiwania stają się użytecznym sygnałem zapotrzebowania dla następnego +ingestu, zamiast przepadać w historii czatu. + ### Schemat frontmatteru strony Każda strona wiki używa frontmatteru YAML. Pole `type` jest wymagane; reszta jest opcjonalna: @@ -121,7 +220,7 @@ retention: high|medium|low - **retention** — strony o niskim priorytecie są archiwizowane po 2× oknie świeżości -Sam `wiki/index.md` dodatkowo zawiera `kb_schema_version` (np. `"1.1"`), +Sam `wiki/index.md` dodatkowo zawiera `kb_schema_version` (np. `"1.3"`), deklarujący, według której wersji tego schematu wiki została napisana — zwiększaj wersję pomniejszą dla dodatkowych opcjonalnych pól, główną dla zmian łamiących kompatybilność. @@ -129,9 +228,11 @@ zmian łamiących kompatybilność. ### Ekstrakcja encji i graf wiedzy Podczas ingestu agent wydobywa typowane encje (osoby, projekty, biblioteki, koncepcje, systemy) i zapisuje je jako strony w `wiki/entities/`. Typowane -relacje (`uses`, `depends_on`, `caused`, `contradicts`, `supersedes`) są -zapisywane w `wiki/graph/edges.json`. Zapytania mogą przechodzić po grafie, -aby odkrywać powiązane strony (np. „co zależy od Redis?"). +relacje są zapisywane w `wiki/graph/edges.json` — strukturalne (`uses`, +`depends_on`, `caused`, `contradicts`, `supersedes`) oraz łączące osoby z +tematami (`has_expertise_in`, `owns`). Zapytania mogą przechodzić po grafie, +aby odkrywać powiązane strony (np. „co zależy od Redis?") albo bezpośrednio +odpowiadać na „kto wie o X". ### Rekurencyjna konwencja indeksu i dziennika Każdy podkatalog `wiki/`, który grupuje wiele stron (`entities/`, `graph/`, @@ -151,7 +252,16 @@ URL albo inny konektor — którego nie chcesz w pełni kopiować lokalnie: connector: sharepoint location: "https://contoso.sharepoint.com/sites/Finance/Shared Documents/Reports" description: "Wspólny folder raportów zespołu finansowego" +refresh_interval_days: 7 # opcjonalne, domyślnie 30 ``` +`refresh_interval_days` dostraja częstotliwość per źródło — folder zmieniający +się codziennie zasługuje na krótsze okno niż kwartalne archiwum, które prawie +nie drgnie — i ustawia `freshness_window_days` nadawane generowanym stronom +tego źródła. Zarówno „index external sources", jak i „Lint" raportują źródło +zaległe względem tej wartości i mówią o ile, żeby użytkownik z dostępem tylko +do odczytu wiedział, kogo zapytać, zamiast po cichu polegać na kopii sprzed +trzech tygodni. + Powiedz „index external sources", a agent go przeskanuje, dopasowując `connector` do dowolnego żywego narzędzia dostępnego w danej sesji (połączonego narzędzia MCP do Microsoft 365/Google Drive, albo `WebFetch` @@ -219,6 +329,9 @@ Okresowo (lub na żądanie) agent sprawdza kondycję wiki: istniejące encje - **Spójność indeksu/dziennika** — weryfikuje, czy każdy podkatalog ma index.md i czy żadna zmiana nie jest podwójnie logowana +- **Częstotliwość konektorów** — oznacza konektorowe źródło, którego + generowany indeks jest zaległy względem `refresh_interval_days`, wraz z + informacją o ile - **Księga błędów (Error Book)** — zapisuje systemowe problemy wraz z przyczyną i naprawą diff --git a/VERSION b/VERSION index 6085e94..f0bb29e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.1 +1.3.0 diff --git a/wiki/graph/index.md b/wiki/graph/index.md index c2e7221..5449a1d 100644 --- a/wiki/graph/index.md +++ b/wiki/graph/index.md @@ -2,6 +2,8 @@ Edge list and relationship data for the knowledge graph, keyed by entity page. -* `edges.json` - Typed relationships (`uses`, `depends_on`, `caused`, `contradicts`, `supersedes`) between entity pages. Created on the first ingest that extracts entities. +* `edges.json` - Typed relationships between entity pages. Created on the first ingest that extracts entities. + * Structural: `uses`, `depends_on`, `caused`, `contradicts`, `supersedes`. + * People-to-topic: `has_expertise_in` (demonstrated ability to answer questions on a topic) and `owns` (declared responsibility for a system, area, or decision). These make "who knows about X" and "who owns X" a direct graph lookup rather than a full-text guess. Recorded only from demonstrated evidence, never inferred from attendance or job title. *(No edges recorded yet — populated on the next ingest.)* diff --git a/wiki/index.md b/wiki/index.md index 56dccfc..dd0a1a6 100644 --- a/wiki/index.md +++ b/wiki/index.md @@ -1,5 +1,5 @@ --- -kb_schema_version: "1.2" +kb_schema_version: "1.3" --- # Knowledge Base Index diff --git a/wiki/log.md b/wiki/log.md index 8349e2f..668ab20 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -1,5 +1,17 @@ # Wiki Change Log +## [2026-07-30 08:40] - [UPDATE] +- **File Affected:** `wiki/index.md` +- **Description:** Bumped `kb_schema_version` from `1.2` to `1.3` — a minor bump for the additive `has_expertise_in`/`owns` graph edge types. No existing field or convention changed meaning. +- **Source:** Chat conversation implementing eight ideas drawn from Cerebras' "How We Built Our Knowledge Base". +--- + +## [2026-07-30 08:38] - [UPDATE] +- **File Affected:** `wiki/graph/index.md` +- **Description:** Documented two new optional edge types, `has_expertise_in` and `owns`, alongside the existing structural ones, so "who knows about X" / "who owns X" become direct graph lookups. Both are evidence-only — never inferred from meeting attendance or job title. +- **Source:** Chat conversation implementing eight ideas drawn from Cerebras' "How We Built Our Knowledge Base". +--- + ## [2026-07-29 22:47] - [UPDATE] - **File Affected:** `wiki/index.md` - **Description:** Bumped `kb_schema_version` from `1.1` to `1.2` for the new optional project-scope and query-gap scaffold conventions. diff --git a/workload/2026-07-30_summary.md b/workload/2026-07-30_summary.md new file mode 100644 index 0000000..927e233 --- /dev/null +++ b/workload/2026-07-30_summary.md @@ -0,0 +1,29 @@ +## 2026-07-30 08:05 CEST + +- User pointed at a local copy of the Cerebras article saved to `tmp/How Cerebras Built Its Enterprise Knowledge Base.html` after two direct `WebFetch` attempts returned HTTP 500. +- Converted it to plain text via `lynx -dump` into the session scratchpad and re-derived the concept list from the primary source, correcting details taken from secondary write-ups in the previous turn. +- Re-listed the ideas not covered by this KB but compatible with a local, file-based, no-install design; excluded the pgvector/HNSW embeddings table, Socket Mode ingestion, CocoIndex chunking, and the hosted cross-encoder reranker as out of scope. + +## 2026-07-30 08:20 CEST + +- User asked which of those ideas could actually be implemented in the current wiki. +- Read `.agents/skills/ckb-retrieve/SKILL.md` and `.agents/skills/ckb-ingest/SKILL.md` in full, plus the `source.yaml` schema in `ckb-index-external`, the graph edge-type vocabulary, and `.gitignore`'s per-user local-state precedent. +- Found three items from the previous turn's list were already partly covered by the skills (high-signal excerpts in ingest Step 4, `rg` over `raw/archive/` in retrieve Step 2, contradiction-surfacing in retrieve Step 6) and narrowed those claims accordingly. +- Concluded all ten are implementable without dedicated software: seven as prose-only edits to existing skills, three needing one new convention each (an expertise edge type, a `source.yaml` cadence field, a gitignored default-scope file). No implementation performed — assessment only. + +## 2026-07-30 09:05 CEST + +- User asked to implement the list, then chose all ten minus #5 (planning pass) and #10 (per-user default scope) — the two I'd advised against on value grounds. Eight items implemented. +- `ckb-retrieve`: added `raw/inbox/` to the exact-token sweep, a new Step 4 (RRF-style fusion at `k = 10`, claim-level dedupe, explicit 0–10 rerank), an expertise/ownership graph lookup in Step 3, caveat surfacing in Step 7, and an edge case for the single-signal case. Renumbered old Steps 4–6 to 5–7. +- `ckb-ingest`: replaced the loose "high-signal excerpt" guidance with a three-part acceptance test (rare term via `rg -c`, ~200 chars, corroborated later in the material) plus a rule to carry the parent heading; added `has_expertise_in`/`owns` edge recording, evidence-only. +- `ckb-index-external`: added optional `refresh_interval_days` to `source.yaml` (default 30), wired it into the generated pages' `freshness_window_days` and into Step 4's overdue reporting for both access levels. +- `ckb-lint`: validates `refresh_interval_days` and reports overdue connector indexes with how overdue, regardless of the user's access level; never re-indexes. +- Chose `k = 10` rather than the usual `k = 60` for fusion, since 60 is tuned for hundreds of candidates and flattens scores at local-wiki scale. Recorded the reasoning inline in the skill so it isn't mistaken for a transcription error. +- Bumped `kb_schema_version` 1.2 → 1.3 (additive edge types) and `VERSION` 1.2.1 → 1.3.0; logged both wiki changes in `wiki/log.md` per Rule B. +- Found `README.pl.md`/`MANUAL.pl.md` were behind by more than the version string: five README sections and two MANUAL sections from the 2026-07-29 session had never been translated. Caught both up rather than layering today's changes onto an incoherent base. EN/PL section parity now 28/28 and 15/15. + +## 2026-07-30 09:30 CEST + +- User asked the reverse comparison: what this KB has that the Cerebras system does not. +- Verified claimed absences by grepping the saved article text rather than asserting from memory — no hits for version/history/supersede/contradict/retention/archive/quality/confidence/entity/ontology/curate/stale/conflict; "graph" only as the HNSW citation, "decay" only as query-time age decay, "audit" once with no detail. +- Framed the answer around the root asymmetry: Cerebras Knowledge is a read path over systems of record it doesn't own, while this KB is itself a system of record that authors and curates content. Noted honestly that the article is a blog post rather than a spec, and that several of this KB's curation features are only tractable at small scale.