The template repo now keeps three branches with fixed meanings — main is
stable, test is the release candidate, experimental is development — and
ckb-init/ckb-upgrade can source from any of them instead of only main.
Selection is per-invocation, in words the user already uses ("initialize
from the test branch", "check experimental for updates", "switch back to
stable"), and sticky: the resolved repo and branch are written to a
template: block in ckb.yaml. Without persistence, a KB bootstrapped from
experimental would be silently pulled back to main by its next upgrade.
A missing file or missing block both mean main, so every KB predating
this convention behaves exactly as before.
One consequence needed explicit handling. A KB tracking test or
experimental can sit on a VERSION main has not released yet, so comparing
it against main finds nothing newer — which the version check would have
reported as "up to date". That is true and misleading. ckb-upgrade now
reports it as "ahead", and treats a move back to main as a downgrade:
explicitly confirmed, with the specific losses named, and blocked
outright where kb_schema_version would drop below what local pages are
already written against.
ckb-module is told not to clobber the template: block — a module install
that silently reset a KB's channel would change what its next upgrade
pulls, which is not a module's business.
Documented in both READMEs, both MANUALs and both CHANGELOGs. VERSION
1.8.0 -> 1.9.0; kb_schema_version stays 1.5, since this is tooling rather
than a content contract.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
370 lines
18 KiB
Markdown
370 lines
18 KiB
Markdown
# Changelog & Schema Reference
|
||
|
||
*Read this in: **English** | [Polski](CHANGELOG.pl.md)*
|
||
|
||
This file tracks two things: the page schema exactly as it stands today, and
|
||
how this project arrived at its current version numbers.
|
||
|
||
There are **two independent version numbers**, and they are not the same
|
||
thing:
|
||
|
||
| Number | Lives in | Describes | Who bumps it |
|
||
|---|---|---|---|
|
||
| `kb_schema_version` | frontmatter of `wiki/index.md` | the **content contract** — what a page may contain and what those fields mean | `ckb-upgrade` (on a confirmed migration), `ckb-module` (additively, on install) |
|
||
| Template version | [`VERSION`](VERSION) | the **tooling layer** — `AGENTS.md`, the skills, the scripts, the docs | `ckb-upgrade`, when it pulls a newer template |
|
||
|
||
They move independently on purpose. You can take a newer set of skills
|
||
without touching a single wiki page, and a wiki authored against an older
|
||
schema keeps working — that is what the schema version is for.
|
||
|
||
**Versioning policy.** Bump the **minor** version for an additive change: a
|
||
new optional field, a new optional body section, a new optional scaffold
|
||
file. Bump the **major** version for a breaking one: changing or removing a
|
||
required field, or changing an existing reserved filename convention. There
|
||
has never been a major bump; every schema version to date has been additive,
|
||
so any page written since 2026-07-13 is still valid today.
|
||
|
||
---
|
||
|
||
## Contents
|
||
|
||
1. [Current page schema (1.5)](#current-page-schema-kb_schema_version-15)
|
||
2. [KB schema version history](#kb-schema-version-history)
|
||
3. [Template version history](#template-version-history)
|
||
4. [Migrating between versions](#migrating-between-versions)
|
||
|
||
---
|
||
|
||
## Current page schema (`kb_schema_version: "1.5"`)
|
||
|
||
### Frontmatter — every non-reserved page
|
||
|
||
Every `.md` file under `wiki/` except the reserved ones (`index.md`,
|
||
`log.md`) carries YAML frontmatter. Only `type` is required; the rest are
|
||
optional, and preferred where meaningful. The parser is deliberately simple —
|
||
flat `key: value` pairs only, no nesting.
|
||
|
||
| Field | Required | Meaning |
|
||
|---|---|---|
|
||
| `type` | **yes** | Open string: `person`, `project`, `concept`, `library`, `decision`, `playbook`, `repository`, `component`, … New values are always valid; readers tolerate unrecognised ones. |
|
||
| `resource` | no | Canonical URI of the authoritative external source this page describes, kept separate from the wiki's own commentary. |
|
||
| `tldr` | no | One-sentence summary optimised for LLM reading. This is what the index shows and what decides whether a page gets opened at all. |
|
||
| `confidence` | no | `0.0`–`1.0`. Source corroboration. Set on write, decays if nothing reinforces it, raised by a new source that agrees. |
|
||
| `quality` | no | `0.0`–`1.0`. Self-evaluation of the page's own structure and citations. Below `0.7` is flagged for review. |
|
||
| `supersedes` | no | Path to the older page this one replaces. |
|
||
| `superseded_by` | no | Path to the newer page that replaced this one. **Always set both sides.** |
|
||
| `last_updated` | no | `YYYY-MM-DD`. When the *page* last changed. |
|
||
| `freshness_window_days` | no | Days before lint flags the page as stale. Typical: 90 for a wiki page, 365 for a decision, 30 for a connector-indexed document. |
|
||
| `retention` | no | `high` / `medium` / `low`. A `low` page is archived (never deleted) after 2× its freshness window. |
|
||
| `source_fingerprint` | no | *(1.5)* Digest of the source this page was built from — `sha256:<8 hex>` for a local file, or `etag:<value>` / `mtime:<iso8601>` for a connector item. |
|
||
| `source_checked` | no | *(1.5)* `YYYY-MM-DD` — when that digest was last verified. Distinct from `last_updated`: a re-check that finds nothing changed advances this and leaves `last_updated` alone. |
|
||
|
||
`wiki/index.md` alone carries `kb_schema_version`. It is a bundle-level
|
||
declaration, not a per-page one — individual pages never carry it.
|
||
|
||
### Frontmatter — decision pages
|
||
|
||
Pages with `type: decision` live in `wiki/decisions/` as `NNNN-slug.md` and
|
||
add:
|
||
|
||
| Field | Required | Meaning |
|
||
|---|---|---|
|
||
| `status` | **yes** | `proposed` / `accepted` / `rejected` / `superseded` / `reversed`. The vocabulary is defined in `wiki/decisions/index.md` and validated by lint. |
|
||
| `decided_on` | for `accepted`/`rejected`/`reversed` | `YYYY-MM-DD`, the date the call was made. |
|
||
| `decided_by` | when known | Comma-separated names. Where genuinely unknown, write `unknown` rather than omitting the field — "we don't know who decided this" is itself worth recording. |
|
||
| `affects` | no | Comma-separated wiki paths this decision constrains. |
|
||
| `review_on` | no | `YYYY-MM-DD` to revisit. Lint reports these once the date passes. |
|
||
|
||
Decision records are **append-only**. A decision is never rewritten to match
|
||
a later change of mind: record a new one that supersedes it, and both stay on
|
||
the record.
|
||
|
||
### Reserved body sections
|
||
|
||
*(New in 1.5.)* Four `##` headings mean the same thing on every page in every
|
||
layer of the cascade. All are optional; where present, they mean exactly this
|
||
and nothing else.
|
||
|
||
#### `## Sources`
|
||
|
||
Where the page came from. One bullet per source, each carrying a fingerprint:
|
||
|
||
```markdown
|
||
## Sources
|
||
|
||
- `raw/archive/2026-09-21/kickoff-notes.md` — sha256:3f9a2c1e (checked 2026-09-21)
|
||
```
|
||
|
||
A freshness window is a guess that a source *might* have moved. A fingerprint
|
||
is a fact about whether it *did*. Lint recomputes local digests and flags what
|
||
actually changed, which is a different and more urgent finding than a page
|
||
that has merely aged.
|
||
|
||
#### `## Crux`
|
||
|
||
Verbatim excerpts from those sources — evidence, never paraphrase — attributed
|
||
to the source bullet they came from:
|
||
|
||
```markdown
|
||
## Crux
|
||
|
||
> FDEs need the VDI *and* a Jira account before day one; the VDI request
|
||
> alone takes ten working days.
|
||
— `raw/archive/2026-09-21/kickoff-notes.md`, under "Access"
|
||
```
|
||
|
||
Three to ten lines is the working range. A Crux approaching the length of the
|
||
summary above it has stopped being evidence and become a second copy of the
|
||
source. Two things follow from quoting rather than paraphrasing: a question
|
||
can often be answered from the page instead of the archive, and drift becomes
|
||
visible — a summary can wander from its source silently, a quote either still
|
||
matches or it does not.
|
||
|
||
A page with no quotable source simply has no `## Crux`. An empty or
|
||
paraphrased one is worse than none, because it looks like evidence.
|
||
|
||
#### `## Notes`
|
||
|
||
Human-authored, and **protected**. No skill may rewrite, reflow, summarise or
|
||
drop this section; regeneration preserves it byte-for-byte.
|
||
|
||
```markdown
|
||
## Notes
|
||
|
||
<!-- Yours. Never rewritten by any skill. -->
|
||
```
|
||
|
||
This matters most on pages the agent *regenerates* — connector indexes, code
|
||
maps — where everything else is discarded and rebuilt on the next run. It is
|
||
the only place an annotation survives. Decision pages deliberately have no
|
||
`## Notes`: nothing regenerates them, and an append-only record with a freely
|
||
editable annotation block invites exactly the retroactive revision the
|
||
append-only rule exists to prevent.
|
||
|
||
### Edge vocabulary
|
||
|
||
Relationships live in `wiki/graph/edges.json` (and each connector index's own
|
||
`graph/edges.json`). The vocabulary is closed, and each verb is defined by the
|
||
question it answers — if a proposed edge answers none of them, it belongs in
|
||
the page's prose instead.
|
||
|
||
| Verb | Question it answers | Since |
|
||
|---|---|---|
|
||
| `part_of` | Where does this live? What is it a piece of? | 1.5 |
|
||
| `uses` | What does this reach for at runtime? | 1.1 |
|
||
| `depends_on` | What breaks if I change this? | 1.1 |
|
||
| `produces` | Where does this output come from? | 1.5 |
|
||
| `configures` | What changes this thing's behaviour? | 1.5 |
|
||
| `validates` | What checks, tests, or judges this? | 1.5 |
|
||
| `implements` | What contract must this honour? | 1.5 |
|
||
| `caused` | Why did this happen? | 1.1 |
|
||
| `contradicts` | What disagrees with this, unresolved? | 1.1 |
|
||
| `supersedes` | What replaced this, and what did it replace? | 1.1 |
|
||
| `decided_by` | Who made this call? | 1.4 |
|
||
| `affects` | What does this decision constrain? | 1.4 |
|
||
| `has_expertise_in` | Who can answer questions on this? | 1.3 |
|
||
| `owns` | Who is responsible for this? | 1.3 |
|
||
| `mentioned_in` | Which source document discusses this? | 1.2 (lib indexes only) |
|
||
|
||
Record one direction per relationship — `part_of`, `supersedes`,
|
||
`depends_on` and `uses` are canonical, and the inverse is not stored as a
|
||
second edge. Record edges only from demonstrated evidence; an absent edge
|
||
beats a fabricated one, and a padded graph degrades retrieval rather than
|
||
improving it (in-degree is used as a ranking signal).
|
||
|
||
### Reserved scaffold
|
||
|
||
| Path | Since | Purpose |
|
||
|---|---|---|
|
||
| `wiki/index.md` | 1.1 | Routing table; the only page carrying `kb_schema_version` |
|
||
| `wiki/overview.md` | 1.1 | High-level map of the KB |
|
||
| `wiki/log.md` | 1.1 | Reverse-chronological change log for `wiki/` |
|
||
| `wiki/error-book.md` | 1.1 | Systemic issues with root cause and fix |
|
||
| `wiki/entities/` | 1.1 | Typed entity pages |
|
||
| `wiki/graph/` | 1.1 | `edges.json` plus the vocabulary in `index.md` |
|
||
| `wiki/query-gaps.md` | 1.2 | Questions the wiki could not answer, driving demand-driven ingest |
|
||
| `wiki/projects/` | 1.2 | Optional local query scopes |
|
||
| `wiki/decisions/` | 1.4 | Numbered, append-only decision records, with their own `log.md` |
|
||
|
||
Every subdirectory that groups pages carries its own `index.md`, so
|
||
navigation stays lazy.
|
||
|
||
---
|
||
|
||
## KB schema version history
|
||
|
||
### 1.5 — 2026-09-21 · evidence, fingerprints, and a completed edge vocabulary
|
||
|
||
Adopted from an analysis of [trailhq/Graft](https://github.com/trailhq/Graft),
|
||
a context layer for coding agents that keeps a derived code graph in sync via
|
||
content hashes rather than dates, and protects a user-authored block on every
|
||
regenerated node. Graft's store is disposable and regenerable; this one is not,
|
||
so most of its design does not transfer — but several mechanisms do, and two
|
||
of them closed real gaps here.
|
||
|
||
**Added:**
|
||
|
||
- **`## Crux`** — verbatim source excerpts alongside the synthesis. Lets
|
||
`ckb-retrieve` ground an answer without a round-trip to the archive (when
|
||
the fingerprint still matches), and makes drift detectable.
|
||
- **`## Notes`** — human-authored and protected everywhere. This closed a real
|
||
gap: `ckb-index-external` regenerates connector pages wholesale, so an
|
||
annotation written there was previously destroyed on the next refresh.
|
||
- **`## Sources`** — formalised as a reserved section with one fingerprinted
|
||
bullet per source. Previously an informal convention that `ckb-retrieve`
|
||
relied on but no skill actually specified.
|
||
- **`source_fingerprint` / `source_checked`** frontmatter.
|
||
- **Edge verbs** `part_of`, `produces`, `configures`, `validates`,
|
||
`implements`. `part_of` fixed a live inconsistency: `ckb-code-map` had been
|
||
writing it since template 1.7.0 while the schema never declared it.
|
||
|
||
**Changed:**
|
||
|
||
- `wiki/graph/index.md` rewritten as a question-per-verb table with
|
||
conventions on edge direction and evidence.
|
||
- `ckb-ingest` gained a blast-radius step: before writing, walk the graph
|
||
backwards from touched entities to find what the incoming material
|
||
confirms, extends or contradicts, and name the owners of affected pages.
|
||
Ingest had been additive-first, which is how a wiki accumulates two pages
|
||
that quietly disagree.
|
||
- `ckb-retrieve` fuses graph in-degree as one ranked list among several,
|
||
weighted below 1.0 — centrality is a prior, not evidence.
|
||
- Rule E (session start) now runs `lint_report.py --quick` alongside
|
||
`git status`: a deterministic one-line knowledge-rot signal costing no
|
||
model tokens.
|
||
- Lint gained checks 12 (fingerprint drift), 13 (crux verbatimness) and 14
|
||
(the protected-`## Notes` rule).
|
||
|
||
**Compatibility:** fully additive. A 1.4 page with none of the new sections or
|
||
fields is valid 1.5. A migration will never manufacture a `## Crux` —
|
||
inventing quotes is precisely the failure that section exists to prevent.
|
||
|
||
### 1.4 — 2026-09-01 · decision records
|
||
|
||
**Added:** `type: decision` pages under `wiki/decisions/` as `NNNN-slug.md`,
|
||
with `status`, `decided_on`, `decided_by`, `affects`, `review_on`; the
|
||
`decided_by` and `affects` edge verbs; `wiki/decisions/index.md` and its own
|
||
`log.md`; the append-only rule. Answers "why is it like this", "who decided",
|
||
and "what changed that decision" as direct lookups instead of a full-text
|
||
guess. Owned by the `ckb-decide` skill.
|
||
|
||
### 1.3 — 2026-08-06 · people-to-topic edges
|
||
|
||
**Added:** the `has_expertise_in` and `owns` edge verbs, making "who knows
|
||
about X" and "who owns X" a graph lookup rather than a full-text search.
|
||
Recorded only from demonstrated evidence — attending a meeting is not
|
||
expertise, and a job title is not ownership.
|
||
|
||
### 1.2 — 2026-07-29 · query scopes, gaps, and connector-backed libs
|
||
|
||
**Added:** `wiki/projects/` (optional local query scopes grouping related
|
||
pages, sources and graph areas); `wiki/query-gaps.md` (failed searches
|
||
recorded as future ingest targets); `raw/archive/<YYYY-MM-DD>/` as the
|
||
agent-maintained filing destination; connector-backed `libs/<name>/` with a
|
||
user-authored `source.yaml`, an agent-owned generated index, and the
|
||
`mentioned_in` edge verb used inside those indexes.
|
||
|
||
### 1.1 — 2026-07-13 · initial schema
|
||
|
||
The first versioned contract, shipped with the initial commit. Established
|
||
the frontmatter field set (`type`, `resource`, `tldr`, `confidence`,
|
||
`quality`, `supersedes`/`superseded_by`, `last_updated`,
|
||
`freshness_window_days`, `retention`), the core edge verbs (`uses`,
|
||
`depends_on`, `caused`, `contradicts`, `supersedes`), the `wiki/` scaffold,
|
||
the cascade priority rule, and the recursive index-and-log convention.
|
||
|
||
There was never a 1.0: versioning began with the first published schema.
|
||
|
||
---
|
||
|
||
## Template version history
|
||
|
||
The tooling layer — `AGENTS.md`/`CLAUDE.md`, the skills, the scripts, the
|
||
docs. Independent of the content schema above.
|
||
|
||
| Version | Date | What landed | Schema |
|
||
|---|---|---|---|
|
||
| **1.9.0** | 2026-09-22 | Release channels: `main`/`test`/`experimental` branches, branch-aware `ckb-init` and `ckb-upgrade`, `template:` block in `ckb.yaml` | 1.5 |
|
||
| 1.8.0 | 2026-09-21 | Seven ideas adopted from Graft: crux/notes/fingerprints, `--quick` lint mode, in-degree ranking, ingest blast radius, completed edge vocabulary | → 1.5 |
|
||
| 1.7.0 | 2026-09-20 | Opt-in modules (`.agents/modules/`, `ckb-module`, `ckb.yaml`); the `software` module with `ckb-code-map` and `ckb-spec`; `ckb-reset`; OpenSpec docs | 1.4 |
|
||
| 1.6.1 | 2026-09-01 | Fixed false-positive broken-edge findings in the lint graph check | 1.4 |
|
||
| 1.6.0 | 2026-09-01 | `ckb-decide`; detection half of lint moved into `lint_report.py`; OKF export moved into `export_okf.py` | → 1.4 |
|
||
| 1.3.0 | 2026-08-06 | Rank fusion, dedupe and rerank in `ckb-retrieve`; expertise and ownership lookups | → 1.3 |
|
||
| 1.2.1 | 2026-07-29 | `AGENTS.md` compressed — workflows moved out into skills, leaving a small always-loaded rule set | 1.2 |
|
||
| 1.2.0 | 2026-07-29 | Project scopes, query gaps, source-verified retrieval | → 1.2 |
|
||
| 1.1.0 | 2026-07-20 | Connector-backed `libs/` with self-contained external source indexing (`ckb-index-external`) | 1.1 \* |
|
||
| 1.0.0 | 2026-07-17 | First tagged template: full skill set, `LICENSE`, `MANUAL`, bilingual docs | 1.1 |
|
||
|
||
\* Connector-backed libs shipped as tooling in 1.1.0, but the schema only
|
||
recorded them — the `source.yaml` contract, the generated index shape, the
|
||
`mentioned_in` verb — at 1.2, nine days later. The two numbers catching up
|
||
with each other like this is normal, and is why the schema column shows what
|
||
was in effect *after* each template release rather than what the release was
|
||
about.
|
||
|
||
Versions 1.4.0 and 1.5.0 were never published — the template jumped from
|
||
1.3.0 to 1.6.0 on 2026-09-01.
|
||
|
||
---
|
||
|
||
## Release channels
|
||
|
||
The template repo keeps three branches, and the version histories above track
|
||
`main` only:
|
||
|
||
| Branch | What it is | Who should track it |
|
||
|---|---|---|
|
||
| `main` | **Stable** — the released template | Everyone, by default |
|
||
| `test` | **Release candidate** — validated before merging to `main` | Anyone helping validate a release |
|
||
| `experimental` | **Development** — active work, may be broken or reverted | People developing the template itself |
|
||
|
||
The branch a KB tracks lives in `ckb.yaml`:
|
||
|
||
```yaml
|
||
template:
|
||
repo: https://git.wierzbowa.cloud/michal/ckb.git
|
||
branch: main
|
||
```
|
||
|
||
`ckb-init` writes it, `ckb-upgrade` reads it as the default and updates it when
|
||
you switch. No `ckb.yaml` and no `template:` block both mean `main`.
|
||
|
||
One consequence worth knowing: a KB that took tooling from `test` or
|
||
`experimental` can sit on a `VERSION` that `main` has not released yet.
|
||
Comparing it against `main` then finds nothing newer — which is true, but is
|
||
**not** the same as "up to date", and `ckb-upgrade` reports it as "ahead"
|
||
rather than as current. Moving such a KB back to `main` is a *downgrade*: it
|
||
can remove skills and lower `kb_schema_version` below what local pages are
|
||
written against. It requires an explicit confirmation, and is blocked outright
|
||
where the schema would drop below the content's own declared contract.
|
||
|
||
---
|
||
|
||
## Migrating between versions
|
||
|
||
Say **"upgrade the wiki"** or **"check for a newer template version"**. To use
|
||
a different channel, name it: *"upgrade from the test branch"*, *"check
|
||
experimental"*, *"switch back to stable"*. The
|
||
`ckb-upgrade` skill checks the canonical template repo, updates the tooling
|
||
layer in place, and — separately, and only after you explicitly confirm —
|
||
migrates existing `wiki/` content up to the current schema while preserving
|
||
every fact already collected.
|
||
|
||
The two halves are deliberately separate. Taking newer skills never rewrites
|
||
your pages, and a content migration is never silent: it reports what it
|
||
intends to change, batches anything it had to infer (such as a missing `type`)
|
||
for your confirmation, and logs every touched page in `wiki/log.md` marked as
|
||
a schema-migration backfill rather than new knowledge.
|
||
|
||
Because every schema version so far has been additive, an older wiki keeps
|
||
working unmigrated. Migration is worth doing to make the newer checks
|
||
meaningful — backfilled fingerprints are what give lint anything to verify —
|
||
not because anything is broken without it.
|
||
|
||
---
|
||
|
||
## Version & License
|
||
|
||
Current template version: [VERSION](VERSION). Current schema version: the
|
||
`kb_schema_version` field in `wiki/index.md`. Licensed under the
|
||
[Apache License 2.0](LICENSE).
|