Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions ADOPTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,22 @@ What you do **not** copy: HANDBOOK.md, the `methodology` domain, `examples/`, `t
## One-Command Install

```bash
curl -fsSL https://raw.githubusercontent.com/emafriedrich/knowledge-driven-engineering/main/install.sh | bash -s -- <your-first-domain>
curl -fsSL https://raw.githubusercontent.com/emafriedrich/knowledge-driven-engineering/v0.8.0/install.sh | bash -s -- <your-first-domain>
```

`install.sh` performs the manual steps below, is idempotent, and never overwrites an existing file (it skips and tells you). It detects your package manager (npm, pnpm — workspaces included, yarn, bun) for the `yaml` dependency, and a dependency failure warns instead of aborting the install. Run it from the root of your repository; pass your first domain name as the argument. It refuses to run in a subdirectory of a repository that already runs KDE — a second catalog nothing reads — and names the directory to run it from. The argument only seeds a fresh install: on a repository that already has `knowledge/index.yaml` the installer creates no domain and points you to `npm run knowledge -- domain add <name>`. Installing, upgrading (`--upgrade`) and adding a domain are three separate actions. Offline installs work from a local clone: `KDE_SOURCE=/path/to/clone bash install.sh <domain>`. To pin a release instead of tracking `main`, set `KDE_REF=v0.1.0`.
`install.sh` performs the manual steps below, is idempotent, and never overwrites an existing file (it skips and tells you). It detects your package manager (npm, pnpm — workspaces included, yarn, bun) for the `yaml` dependency, and a dependency failure warns instead of aborting the install. Run it from the root of your repository; pass your first domain name as the argument. It refuses to run in a subdirectory of a repository that already runs Knowledge-Driven Engineering — a second catalog nothing reads — and names the directory to run it from. The argument only seeds a fresh install: on a repository that already has `knowledge/index.yaml` the installer creates no domain and points you to `npm run knowledge -- domain add <name>`. Installing, upgrading (`--upgrade`) and adding a domain are three separate actions. Offline installs work from a local clone: `KDE_SOURCE=/path/to/clone bash install.sh <domain>`. The script installs the release it belongs to (the tag in its own URL); to install another release, a branch or a commit, set `KDE_VERSION=main` (or `KDE_VERSION=v0.7.2`) in front of the command.

## Upgrading

The installer classifies what it writes by owner (DR-011):

- **Framework-owned** — `tools/knowledge-check.mts`, `tools/knowledge-context.mts`, `tools/knowledge.mts`, `tools/drift-gate.mts`, `tools/knowledge-hook.mts`, `.github/workflows/kde.yml`, the `<!-- kde:begin -->` … `<!-- kde:end -->` section of `AGENTS.md` (DR-017), and the KDE hook entries in `.claude/settings.json`. Every copy carries a `kde-version: X.Y.Z` marker on its first line.
- **Adopter-owned** — everything under `knowledge/`, `templates/`, `AGENTS.md` outside the kde markers, and your `package.json` beyond the two KDE scripts. Never touched, on any run. Templates are yours to shape to your team's conventions; the validator, not the template text, enforces KDE-SPEC-001.
- **Framework-owned** — `tools/knowledge-check.mts`, `tools/knowledge-context.mts`, `tools/knowledge.mts`, `tools/drift-gate.mts`, `tools/knowledge-hook.mts`, `.github/workflows/kde.yml`, the `<!-- kde:begin -->` … `<!-- kde:end -->` section of `AGENTS.md` (DR-017), and the Knowledge-Driven Engineering hook entries in `.claude/settings.json`. Every copy carries a `kde-version: X.Y.Z` marker on its first line.
- **Adopter-owned** — everything under `knowledge/`, `templates/`, `AGENTS.md` outside the kde markers, and your `package.json` beyond the `knowledge`, `knowledge:check` and `knowledge:context` scripts. Never touched, on any run. Templates are yours to shape to your team's conventions; the validator, not the template text, enforces KDE-SPEC-001.

Every run compares the installed markers with the fetched version and warns when a framework-owned file differs — whether from a newer release upstream or a local edit. To refresh them:

```bash
curl -fsSL https://raw.githubusercontent.com/emafriedrich/knowledge-driven-engineering/main/install.sh | bash -s -- --upgrade
curl -fsSL https://raw.githubusercontent.com/emafriedrich/knowledge-driven-engineering/v0.8.0/install.sh | bash -s -- --upgrade
```

`--upgrade` replaces framework-owned files that differ and reports each one (`upgrade tools/knowledge-check.mts (0.1.0 -> 0.2.0)`). A framework-owned file you edited locally is overwritten too, with an explicit `WARN` line naming it. Framework-owned files are not an extension point: if you need different validation behaviour, fork this repository and install from your fork; if the change would help everyone, contributions are welcome. Adopter-owned files are not touched by `--upgrade`. Templates are never overwritten; the installer prints a `note` line for each one that differs from the release, with the upstream URL, so you can decide whether to adopt the newer shape. Without branch protection and CODEOWNERS, human-only promotion is a procedural guarantee: the validator sees that an approver is recorded, not who typed it. New template files that a release adds (as `templates/model.md` and `templates/contract.md` were) arrive on a plain run, because the installer adds any file that does not exist yet.
Expand All @@ -54,7 +54,7 @@ curl -fsSL https://raw.githubusercontent.com/emafriedrich/knowledge-driven-engin
```

Non-Node projects can run it with any Node >= 22.6 installed; the tool has one dependency.
4. **Wire CI.** Copy `.github/workflows/ci.yml` (or the equivalent in your CI) so every PR runs `knowledge:check`. Without CI the method is an honor system. Copy `tools/knowledge-context.mts` and `tools/drift-gate.mts` too, declare `code_paths` on your domains, and add a CODEOWNERS file plus branch protection so knowledge promotion requires owner approval.
4. **Wire CI.** Copy `.github/workflows/ci.yml` (or the equivalent in your CI) so every PR runs `knowledge:check`. Without CI the method is an honor system. Copy `tools/knowledge-context.mts` and `tools/drift-gate.mts` too, declare `code_paths` on your domains, and add a CODEOWNERS file plus branch protection so knowledge promotion requires owner approval. What the gate catches, and what it does not, is in [DRIFT.md](DRIFT.md).
5. **Add the agent rules.** Copy the section `install.sh` writes — from `<!-- kde:begin -->` to `<!-- kde:end -->`, markers included — into your project's `AGENTS.md`. The markers are what lets `install.sh --upgrade` refresh the rules later (DR-017); put rules of your own outside them.
6. **Seed current truth.** Write the first Decision Record for a decision your team already made, list it in the domain `decisions/index.yaml`, and anchor the domain in `knowledge/index.yaml`. One real decision beats ten empty folders.
7. **Grow on demand.** Add artifact folders (`specs/`, `flows/`, `rfcs/`) only when the domain has real content of that type, and new domains only when work needs a stable retrieval boundary.
Expand All @@ -67,7 +67,7 @@ Step 6 assumes you remember your decisions. On a codebase that has been shipping
npm run knowledge -- backfill <domain>
```

The first run scaffolds `knowledge/<domain>/backfill.yaml` (session state) and a draft **baseline Decision Record** stating that the rules about to be recovered describe observed behavior at adoption time, with no reconstructed rationale. The command audits nothing itself. Ask your coding agent to backfill the domain — there is nothing to paste: the installer ships the protocol as `tools/backfill-protocol.md` and the KDE section of your `AGENTS.md` tells agents to follow it. The agent audits the domain's code, diffs what it finds against any existing knowledge, and presents each rule one at a time — the rule, its evidence (paths, symbols, test names; never `file:line`), and any conflict. You approve, reject, or edit each rule as you see it; running the `promote` command the agent hands you is the approval.
The first run scaffolds `knowledge/<domain>/backfill.yaml` (session state) and a draft **baseline Decision Record** stating that the rules about to be recovered describe observed behavior at adoption time, with no reconstructed rationale. The command audits nothing itself. Ask your coding agent to backfill the domain — there is nothing to paste: the installer ships the protocol as `tools/backfill-protocol.md` and the framework-owned section of your `AGENTS.md` tells agents to follow it. The agent audits the domain's code, diffs what it finds against any existing knowledge, and presents each rule one at a time — the rule, its evidence (paths, symbols, test names; never `file:line`), and any conflict. You approve, reject, or edit each rule as you see it; running the `promote` command the agent hands you is the approval.

Approved behavior lands as **one spec per feature** anchored to the baseline record, born with its proving test named in Acceptance Checks. Architectural stances (infrastructure, caching, eventing) become their own Decision Records. Nothing lands in a standalone report: a snapshot document has no per-rule lifecycle and starts contradicting the code within days, while the generated `CONTEXT.md` already gives you the readable summary for free.

Expand Down Expand Up @@ -113,6 +113,14 @@ You can adopt the two highest-value pieces without the rest, today:

That already gives an agent what most repositories lack: which decisions are in force, and which source wins on conflict.

## In the field

The first production adoption was a multi-tenant marketplace, four months into development, built largely by coding agents. The method arrived mid-project. Some of what happened:

- **A rules snapshot rotted in six days.** Before the method had a backfill path, agents reconstructed the business rules into a standalone document. Within a week it contradicted an accepted decision and the code, and its line-number citations had drifted. That failure is why backfill writes into the catalog, rule by rule, and never into a report.
- **Diffing knowledge against code caught a wrong decision.** An *accepted* Decision Record turned out to describe behavior the code did not have. Because the decision was a canonical, indexed record, the contradiction was detectable, and it was fixed through the normal draft-and-promote flow instead of surfacing as a production bug.
- **One backfilled domain paid for itself.** Auditing a single domain — login, sessions, user management, about fifty files — recovered 22 behavior rules into three feature specs, each rule with its evidence and, where one exists, the test that proves it. The same pass reported two accepted decisions that were never implemented, one decision the code had outgrown, and three defects, two of them security issues. Nobody had asked about any of them.

## Distribution Roadmap

Copying files is the V1 adoption path on purpose: it keeps your knowledge and its validator versioned inside the repository they govern, which is where CI needs them. Two distribution improvements are candidates once the method stabilizes against a real product:
Expand Down
11 changes: 11 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This file is the only document loaded every session. Read the rest on demand, at
- `CONTRIBUTING.md`: the change process. Read before changing methodology rules, templates, or the validator.
- `templates/`: canonical artifact shapes. Copy the matching template when creating any knowledge document.
- `ADOPTING.md`: only for setting the method up in another repository.
- `DRIFT.md`: what the drift gate catches and does not. Read when a pull request fails the gate.

## Retrieval Order

Expand Down Expand Up @@ -67,6 +68,16 @@ Update an existing spec when intended behavior changes and the decision context

Create or update a task only after the canonical knowledge that justifies it exists.

### Choosing the artifact

When asked for a change, read the domain's knowledge first, then pick one artifact:

- The request is clear and the rule it needs is settled, or the request itself is the decision: draft a Decision Record and stop there. A simple decision goes from the record to code.
- If a request admits more than one reasonable reading, or a rule's applicability is uncertain, draft an RFC listing the open questions and stop. Do not choose an answer on the human's behalf, and do not implement against an assumption. When the human resolves the questions, the RFC becomes one or more Decision Records.
- Once a decision is promoted, if the behavior it implies is more than a couple of rules — interactions, invariants, edge cases — draft a Spec anchored to that decision (`depends_on`) before implementing. Otherwise implement against the decision and cite it in code.

Every question you would otherwise have answered silently in code belongs in the RFC.

## Hard Rules

- Do not invent product behavior.
Expand Down
65 changes: 65 additions & 0 deletions DRIFT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Drift

Writing a rule down is the easy part. The hard part is that the written rule and the running code stop agreeing, and nothing tells you. That gap is **drift**, and it is the problem most of this method's machinery exists for.

## Two directions

Drift runs both ways, and the first production adoption hit both within a week ([in the field](ADOPTING.md#in-the-field)):

- **Code moves, knowledge stays.** Someone changes checkout; the decision that governs checkout is not touched. The record still says `accepted`. A snapshot of the business rules written by agents contradicted the code six days after it was written.
- **Knowledge moves, code stays.** A Decision Record is accepted and never implemented, or implemented differently. An *accepted* record described behavior the code did not have. Nobody noticed until an audit diffed the two.

Either way the repository now holds two answers to "what does the system do", and whoever reads the wrong one acts on it.

## Why agents make it worse

A human who finds a stale document discounts it. Experience says "this is probably old", and they go read the code. An agent does the opposite: it obeys the document literally, because that is what it was told to do, and a rule that no longer holds looks exactly like one that does. The precedence order that makes canonical knowledge useful (active decision beats code) is the same order that makes stale knowledge dangerous.

Drift also runs faster with agents than the word suggests.

- **Slow drift** happens between tasks and people, over days: the classic case of documentation rot.
- **Fast drift** happens inside one session, after one prompt. The agent reads the domain's `CONTEXT.md` at the start; a hundred thousand tokens later its context is saturated, and what it implements answers a degraded reading of the rules, or an assumption it made along the way and never surfaced. Nothing in the repository changed. The agent stopped following it.

With agents doing most of the implementation, fast drift is the common case, not the edge case.

## What the gate does

The drift gate runs on every pull request and looks at the result, not the process. It does not know whether a divergence took a week or twenty minutes, and it does not need to.

For each domain that declares `code_paths` in `knowledge/index.yaml` and has a spec:

- If the PR changes code under those paths and the domain has a **current** spec, the PR must also change a document under the domain's knowledge path, or declare `no-behavior-change` in its description. Otherwise it fails.
- If the domain's only specs are **drafts**, the PR must declare `implements-draft: <SPEC-ID>` (or `no-behavior-change`). Implementing against a draft is allowed; hiding it is not. The spec still needs a human to promote it.
- If the code changed sits under a current **Contract**'s `implements` paths, the contract must change in the same PR, or the PR declares `no-behavior-change`.

The gate prints one line per domain it evaluated, so a reviewer sees which declaration carried the PR.

```text
drift-gate: domain businesses — code and knowledge changed together. OK.
drift-gate: domain orders — code changed, no-behavior-change declared. OK.
```

Decision Records and manifests are protected by different means: the validator refuses a decision index that points at a superseded record, and `knowledge:context --check` fails CI when a committed `CONTEXT.md` differs from what the generator produces.

## What the gate does not catch

Say this to your team before they trust it too much:

- **`no-behavior-change` by reflex.** The declaration is a sentence in a PR description. It will get pasted the way `skip-changelog` gets pasted. The gate makes the shortcut visible; it does not make it rare. Reviewers and CODEOWNERS do that.
- **Code outside `code_paths`.** A domain without declared code paths is not gated. Partial adoption is deliberate, so unmapped code is simply outside the contract.
- **A domain with decisions but no specs.** The `DR -> code` path has nothing mechanical to hang on; review and the project's tests verify it. A team that wants the mechanical link writes the spec.
- **Knowledge that changed in the wrong direction.** A PR that edits both the code and the spec passes, even if the spec now contradicts the decision it depends on. The validator checks structure, not meaning; a human reads the diff.
- **Fast drift before the PR.** The gate fires when the branch is pushed. Inside the session, the defenses are procedural: the context receipt from `knowledge:context` pasted in the PR, re-reading `CONTEXT.md` before touching governed code, and the rule that an agent facing an ambiguous request drafts an RFC and stops instead of implementing against an assumption (AGENTS.md, "Choosing the artifact"). The harness hooks run the validator in-session, but only on knowledge edits.

## Tuning it

- Declare `code_paths` per domain as soon as the domain has a spec. Prefix matching, one line in `knowledge/index.yaml`.
- Keep specs anchored: a spec entering current truth must depend on an active decision, so the chain from code to rule to rationale stays walkable.
- Treat `no-behavior-change` as a review signal. If a PR needs it, ask why the behavior did not change when the code did.
- Run the gate locally before pushing when in doubt:

```bash
PR_BODY="no-behavior-change" DRIFT_BASE_REF=origin/main node --experimental-strip-types tools/drift-gate.mts
```

The gate is `tools/drift-gate.mts`; the decisions behind it are DR-008 (mechanical drift verification), DR-010 (contract obligations) and DR-015 (declared drafts).
Loading
Loading