# Why knowledge and commitments are two layers Chemenu compiles knowledge into `kb/`, and it also tracks what its operator has committed to do. Those look like one subject - both are "things about my projects" - and the stack deliberately keeps them apart: `kb/gtd/` holds one page per initiative, an external task tracker holds the open items, and the only thing that crosses between them is a name. This page is about why that line was drawn there. The rules that follow from it live in [kb/CONTRACT.md](../kb/CONTRACT.md) and the `review`, `new project` and `task new` rows of [tools/CONTRACT.md](../tools/CONTRACT.md). ## Contents - [Different half-lives want different machinery](#different-half-lives-want-different-machinery) - [Pattern 4: separate ownership, no synchronization](#pattern-4-separate-ownership-no-synchronization) - [The join happens at read time, and stores nothing](#the-join-happens-at-read-time-and-stores-nothing) - [One name, carrying the duties of an identifier](#one-name-carrying-the-duties-of-an-identifier) - [Status has exactly one home](#status-has-exactly-one-home) - [A finished initiative is a state, not a location](#a-finished-initiative-is-a-state-not-a-location) - [Which tracker is a decision the stack does not make](#which-tracker-is-a-decision-the-stack-does-not-make) ## Different half-lives want different machinery `kb/` is a compiler for durable things, and every mechanism in it assumes durability: `raw/` is immutable, a claim has to trace back to a source, a page's title is its identity, the indexes are generated, and a large change stops at a gate so a human can look at it. All of that is the right amount of ceremony for something that will still be true next year. A next action is the opposite kind of fact. It is unsourced - nobody cites a reason for "call the plumber". It changes several times a week. It is state, not knowledge: the interesting thing about it is whether it is still open. And it is only correct *now*. Running both through one layer does not produce a richer wiki; it produces a worse one. Every task-shaped page carries `provenance: general` because there is no source to bind it to, which drains that field of meaning for the pages where it matters. `kb/log.md` fills with "task checked off" entries until the audit trail of what the *wiki* learned is unreadable. Lint findings about orphans and stale claims start firing on pages that are supposed to be short-lived. And a weekly pass over the task list trips the Mass-Update Gate every single time, which is how a gate stops being read and starts being cleared reflexively. The GTD method this borrows from draws the same line for its own reasons: of its horizons, `kb/` covers the two slowest - project support material and reference - and nothing faster. ## Pattern 4: separate ownership, no synchronization Four arrangements were on the table, and three of them fail in ways worth naming. **One layer** is the case above. **Export** - the wiki writes a task list the tracker imports - means a checkbox ticked in the tracker is a tick in a view, while the truth sits in a file the operator was not editing; the two disagree immediately and silently. **Bidirectional sync** works, at the cost of an id mapping to maintain, a conflict-resolution rule to design, and a deletion semantics to decide - all of it machinery whose only job is to repair a split nobody needed. What is left is **separate ownership with no sync at all**: the tracker owns the tasks, `kb/` owns the project memory, and the single point of contact is the project's name. Nothing is mirrored, so nothing can drift out of mirror. ## The join happens at read time, and stores nothing Because there is no shared state, the connection between the two sides has to be made when somebody actually asks - which is what `wikitool review` does: it reads both sides, matches them on the case-normalized project name, prints what it found, and saves nothing. Not a cache, not a mapping file, not even a `reports/` artifact. That is the same posture `search` takes, and for the same reason: anything it wrote down would be a third copy of a state the two sides already hold, stale the moment either side moved, and the first thing to distrust in a report. A read-time join can be wrong about the present, but it cannot be wrong about the past, because it does not remember one. ## One name, carrying the duties of an identifier Reducing the coupling to a name is cheap, and it is not free. A name that joins two systems is an identifier, whether or not anything enforces it, so the design had to pick up an identifier's obligations explicitly: uniqueness is checked before a project is created rather than discovered later; a rename is a deliberate, infrequent operation that touches both sides in one pass; and nothing tries to re-match automatically behind the operator's back. The last one is what makes the review's *both-directional* report matter. A tracker project with no page and a page with no tracker project are reported separately, as two findings. They are usually the two halves of one rename - and reporting them separately is exactly what turns a silent decoupling into a visible event, at the cost of the review occasionally saying the same thing twice. ## Status has exactly one home The sharpest consequence of the split is a rule that feels like a restriction: a `kb/` page never summarizes its own task list. No "3 open items", no "next: call the supplier". Two places claiming to know the current status is the failure mode the whole arrangement exists to avoid, and a summary is a copy with a slower clock. The page says what an initiative *is* - its goal, its participants, its durable state, why it is worth doing. The tracker says what is open right now. Anyone wanting the second reads the tracker, or runs the review. This pays for itself somewhere unexpected: with the page carrying no task state, an agent has no reason to read the task list at all outside the weekly review. That is what keeps the command surface as small as it is - one read command and two creation commands - rather than growing a full CRUD tree over somebody's todo list. The second creation command exists because a single name is not always the whole story: a source can carry a piece of durable knowledge and a commitment to follow up on it at the same time - a complaint arriving by email is both something to file and something to chase - and the tracker-side half of that needs its own write path alongside `new project`'s pairing of a page with a tracker project. `task new` creates only the tracker item, never a page; a source that also carries knowledge gets that knowledge filed through the ordinary page-creation commands, as a separate step. The two are never one transaction the way `new project`'s tracker-then-page order is within a single command - they are two independent writes a skill sequences, tracker first, so a failure creating the item leaves no page and no promoted source material behind it, and a failure on the knowledge side afterwards is exactly the ordinary "a source without a page" state `lint` already reports. ## A finished initiative is a state, not a location Archiving moves nothing. A completed initiative's page stays where it is and changes its `state:` value, because the moment an initiative finishes is the moment its page is *most* valuable - what was decided, what it cost, who was involved - and filing it away is how that gets lost. The state field carries the distinction the review actually needs, which is not "open vs. done" but "does silence here mean something is wrong". An initiative that is deliberately paused looks identical, from the outside, to one that quietly stalled; only the operator knows which. Without a value for "paused on purpose", the review reports the same untouched initiatives every week, and a report that is mostly noise stops being read by the third week - which would cost more than the findings are worth. ## Which tracker is a decision the stack does not make The tracker is reached through a provider layer, and no instruction anywhere names which one it is. An instruction that said "open Super Productivity" would bake one instance's tool choice into the shared stack, and the next instance - a different context, a different employer, a different set of constraints - would have to edit prose to change a setting. So the provider lives in configuration (`.wikitool-tasks.json`), the adapters live behind one protocol, and a capability the provider lacks surfaces as an ordinary tool error rather than as a paragraph of instruction explaining what this particular tracker cannot do. A provider that cannot create a project, for instance, stops and asks the operator to do it - the same posture the gates take, and for the same reason: better a visible stop than an invented workaround.