From 6b6bcd2ef6ddd3fa9f0d57313b3f12b577b4bc1b Mon Sep 17 00:00:00 2001 From: Emanuel Friedrich Date: Wed, 30 Sep 2026 03:24:00 -0300 Subject: [PATCH 1/5] README rewrite: artifact-choice rule, LICENSE, field section moved, release 0.8.0 - AGENTS.md and the installer's framework-owned section gain "Choosing the artifact": decision when the request is clear, RFC with open questions when anything is ambiguous (stop, never assume), spec anchored to the promoted decision when behavior has many rules. KDE-RFC-014 and DR-019 drafted by the agent, pending promotion; KDE-PROMPT-001 carries the rule; install test asserts the section reaches adopters. - LICENSE (MIT). README "Honest limits" no longer claims per-author reporting of no-behavior-change. - examples/marketplace: catalog declares its two current specs, CONTEXT.md regenerated; README notes the in-place edit of MP-DR-034. - "In the field" moved from the old README to ADOPTING.md; HANDBOOK "Artifact Responsibilities" renamed "Artifacts" for the README anchor. - README "Minimal adoption" subsection; the KDE acronym replaced in user-facing prose of README, ADOPTING and HANDBOOK. - 0.8.0: install.sh downloads the release it belongs to (DEFAULT_REF), overridable with KDE_VERSION (KDE_REF still honoured); curl one-liners point at v0.8.0. --- ADOPTING.md | 20 +- AGENTS.md | 10 + HANDBOOK.md | 12 +- LICENSE | 21 ++ README.md | 218 +++++++++--------- examples/marketplace/README.md | 2 + .../knowledge/businesses/CONTEXT.md | 5 +- examples/marketplace/knowledge/index.yaml | 3 + install.sh | 57 +++-- knowledge/methodology/CONTEXT.md | 4 +- ...on-when-the-request-is-clear-an-rfc-whe.md | 42 ++++ knowledge/methodology/prompts/coding-agent.md | 3 +- ...the-artifact-and-never-assume-an-answer.md | 56 +++++ package.json | 2 +- tests/install.test.ts | 20 ++ 15 files changed, 330 insertions(+), 145 deletions(-) create mode 100644 LICENSE create mode 100644 knowledge/methodology/decisions/DR-019-agents-draft-a-decision-when-the-request-is-clear-an-rfc-whe.md create mode 100644 knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md diff --git a/ADOPTING.md b/ADOPTING.md index 68f5f91..05fafd9 100644 --- a/ADOPTING.md +++ b/ADOPTING.md @@ -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 -- +curl -fsSL https://raw.githubusercontent.com/emafriedrich/knowledge-driven-engineering/v0.8.0/install.sh | bash -s -- ``` -`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 `. 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 `. 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 `. 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 `. 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 `` … `` 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 `` … `` 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. @@ -67,7 +67,7 @@ Step 6 assumes you remember your decisions. On a codebase that has been shipping npm run knowledge -- backfill ``` -The first run scaffolds `knowledge//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//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. @@ -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: diff --git a/AGENTS.md b/AGENTS.md index fbac3ef..56c081d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -67,6 +67,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. diff --git a/HANDBOOK.md b/HANDBOOK.md index b973abe..d88f558 100644 --- a/HANDBOOK.md +++ b/HANDBOOK.md @@ -39,7 +39,7 @@ knowledge/ Do not create empty artifact folders. Add them when the domain has real knowledge of that type. -## Artifact Responsibilities +## Artifacts ### Product Vision @@ -232,7 +232,7 @@ Agents create and renumber; only humans promote and supersede. ### Brownfield Backfill -When a domain's behavior already exists in code but not in `knowledge/` — the default situation when KDE arrives mid-project — recover it with a backfill session (DR-018) instead of an ad-hoc report. `knowledge backfill ` starts or resumes the session; an agent following the protocol the installer ships as `tools/backfill-protocol.md` (KDE-PROMPT-002) audits the code and presents each recovered rule with its evidence, and the human approves, rejects, or edits it on sight. +When a domain's behavior already exists in code but not in `knowledge/` — the default situation when the method arrives mid-project — recover it with a backfill session (DR-018) instead of an ad-hoc report. `knowledge backfill ` starts or resumes the session; an agent following the protocol the installer ships as `tools/backfill-protocol.md` (KDE-PROMPT-002) audits the code and presents each recovered rule with its evidence, and the human approves, rejects, or edits it on sight. What lands where: approved behavior becomes **one spec per feature**, anchored to a per-domain **baseline Decision Record** that states honestly that observed behavior was adopted as current truth and the historical rationale was not recovered. Architectural stances get their own Decision Records instead of specs. Never a Decision Record per rule — backfill recovers the what, not the why, and reconstructed rationale is fabrication — and never a monolithic snapshot document, which has no per-rule lifecycle and competes with the catalog as a second source of truth. @@ -302,9 +302,9 @@ Create a task when someone needs to execute bounded work. ## Relation to OKF -Google Cloud's [Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md) shares KDE's substrate: markdown with YAML frontmatter, cross-linked, versioned in git. It addresses a different layer. This comparison is as of OKF v0.2; the spec is young and changes between minor versions. +Google Cloud's [Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md) shares the method's substrate: markdown with YAML frontmatter, cross-linked, versioned in git. It addresses a different layer. This comparison is as of OKF v0.2; the spec is young and changes between minor versions. -| | OKF | KDE | +| | OKF | Knowledge-Driven Engineering | | --- | --- | --- | | What it is | An interchange format | A governance process for knowledge | | On errors | Tolerant consumers: best effort, never reject a bundle for broken links or unknown fields | A strict gate: the validator fails CI | @@ -316,6 +316,6 @@ Google Cloud's [Open Knowledge Format](https://github.com/GoogleCloudPlatform/kn | Conflicting sources | Not addressed | Explicit precedence; conflicts are reported, never resolved silently | | Relation to code | None | Drift gate on pull requests; acceptance blocks run the project's tests | -OKF has what KDE does not: per-claim provenance (`sources` with footnote ids), an explicit expiry (`stale_after`), several independent verifications per document, and attested computations — sanctioned calculations an agent may parameterize but not modify, checked by deterministic code. +OKF has what the method does not: per-claim provenance (`sources` with footnote ids), an explicit expiry (`stale_after`), several independent verifications per document, and attested computations — sanctioned calculations an agent may parameterize but not modify, checked by deterministic code. -A KDE repository is not an OKF bundle today: OKF requires a non-empty `type` in the frontmatter of every markdown file other than `index.md` and `log.md`, and KDE derives the type from the folder and the id prefix, and generates `CONTEXT.md` without frontmatter. +A Knowledge-Driven Engineering repository is not an OKF bundle today: OKF requires a non-empty `type` in the frontmatter of every markdown file other than `index.md` and `log.md`, and the method derives the type from the folder and the id prefix, and generates `CONTEXT.md` without frontmatter. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c13c812 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Emanuel Friedrich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index defbb9c..54a4fad 100644 --- a/README.md +++ b/README.md @@ -2,169 +2,171 @@ **Your coding agents write good code fast. They don't know your rules.** -Knowledge-Driven Engineering (KDE) is a lightweight system for keeping a project's decisions and behavior rules in the repository — versioned, validated in CI, and retrievable by humans and agents before they change anything. Markdown files, a validator, and a handful of commands. No server, no database, no new place to look. +Knowledge-Driven Engineering keeps a project's decisions and behavior rules in the repository as markdown — versioned, validated in CI, and read by agents before they touch code. Agents draft; humans promote; a validator and a drift gate keep the knowledge and the code from disagreeing silently. + +No server, no database. Markdown files, one validator, a handful of commands. + +> **Status:** early. One production adoption, API may change between commits. Pin a commit if you depend on it. +> **License:** [MIT](LICENSE) + +## What it looks like + +A real decision record, from the `businesses` domain of the first production adoption ([full file](examples/marketplace/knowledge/businesses/decisions/MP-DR-034-orders-are-only-accepted-while-the-business-is-open-by-sched.md)): + +```markdown +--- +id: MP-DR-034 +title: Orders are only accepted while the business is open by schedule and by its manual switch +status: accepted +drafted_by: agent +approved_by: [emafriedrich] +scope: [businesses] +depends_on: [MP-DR-033] +related: [MP-DR-030] +--- + +## Context +Today "open/closed" is only the manual `isOpen` flag; the weekly schedule +never blocks anything, and checkout doesn't check `isOpen` either. A customer +can order at 3 a.m. from a place that closed at midnight. + +## Decision +1. Whether the business accepts orders is computed, not stored, as one + `availability` value: `open`, `closed_by_schedule` or `closed_by_merchant`. + The manual switch can only close the business, never open it outside + the schedule. +2. The API enforces it at checkout: `orders.business_closed`, HTTP 409, + with `reason` and `nextOpensAt`. The storefront check is convenience only. +4. Only creation is blocked. A cart opened at 23:58 and submitted at 00:01 + is rejected. +(rules 3, 5, 6 and consequences in the full record) +``` -It works best when a project starts with it: every decision is recorded as it is made, and there is no knowledge to recover from existing code. But no worries: it works well in an existing project too — see [Quick Start](#quick-start). +The agent drafted it; a human approved it; the validator refuses any record with `drafted_by: agent` and no approver. Before touching checkout, an agent reads the domain's generated `CONTEXT.md` — the decisions in force, and what is explicitly *not* truth yet: -To install, run this from the root of your repository, with the name of your first domain: +```markdown +## Active decisions +| Topic | ID | Title | Updated | +| schedule | MP-DR-033 | Merchants edit their weekly schedule from settings | 2026-09-25 | +| availability | MP-DR-034 | Orders are only accepted while the business is open ... | 2026-09-25 | +| visibility | MP-DR-030 | Business visibility states: active, open and published | 2026-09-25 | +(ten in total) -```bash -curl -fsSL https://raw.githubusercontent.com/emafriedrich/knowledge-driven-engineering/main/install.sh | bash -s -- +## Pending — NOT current truth, do not obey +- `MP-DR-053` (draft, agent-drafted): Backfill baseline for businesses +- `MP-RFC-005` (draft, agent-drafted): Multi-branch businesses ``` -## The Problem - -An agent implementing a change has two sources of truth: your prompt and your code. Everything else — why orders can't be edited after payment, which role may deactivate an admin, what a 2% price tolerance protects — lives in chat threads, tickets, and people's heads. So the agent does the only thing it can: - -- **It infers product rules from implementation.** Code shows what the system does, not what it must do. A bug looks exactly like a rule. -- **It invents what is missing.** Asked for behavior nobody wrote down, it picks something plausible, and plausible is not the same as decided. -- **It cannot tell current from historical.** An old design doc, a superseded decision, and today's rule look equally authoritative in a repository search. +That domain reads in ~8k tokens. Reconstructing the same rules from its ~50 files of code took an agent ~130k (n=1, one-time extraction — see [in the field](ADOPTING.md#in-the-field)). -Humans have the same problem six months later, including the person who made the decisions. Documentation is the usual answer, and documentation rots: nothing checks it against the code, nothing says which document wins, and nobody knows it is wrong until someone acts on it. +## Three documents, and who writes which -## What KDE Does About It +You don't need to know what an RFC is to use this. There are three kinds of document that matter, and the agent picks the right one for you. -KDE treats knowledge like code: it lives in the repository, it has a lifecycle, and CI checks it. +**Decision Record (DR) — a settled rule, and why.** One decision, written once, never edited: if it changes, a new record supersedes it. Read it when you need to know *what is true*. +*Example:* [MP-DR-034](examples/marketplace/knowledge/businesses/decisions/MP-DR-034-orders-are-only-accepted-while-the-business-is-open-by-sched.md) — "orders are only accepted while the business is open". Context (customers ordering at 3 a.m.), the rule, its consequences. -- **Current truth is computable.** Decision Records keep history; a small index per domain answers "which decisions apply *today*?" in a form an agent consumes directly. -- **Sources have an explicit precedence.** When a decision, a spec, and the code disagree, the order is written down — and the rule is to *report* the conflict, never resolve it silently. -- **Agents get a bounded retrieval path.** Not "read the docs": identify the domain, read its generated `CONTEXT.md`, its active decisions and current spec, and only then the code. It also costs less: a rule that takes one sentence in a spec is spread across several files in code, and an agent without memory re-derives it on every task that needs it. In the field, reconstructing the rules of one entire domain from its code took an agent about 130,000 tokens; all the knowledge that came out of it reads in about 8,000. A single task needs only a few of those rules, and the agent still reads the code it changes, so the saving per task is smaller — but it recurs on every task, and so does the risk of deducing a rule wrong. -- **Agents propose; humans promote.** An agent drafts anything; nothing it drafts becomes current truth until a human runs the promotion command. -- **Knowledge integrity runs in CI.** The validator checks the knowledge graph the way a linter checks code; a drift gate fails a pull request that changes a domain's behavior without touching its knowledge; a spec's acceptance block proves it with your own tests. -- **Existing code is not a dead end.** A backfill session recovers the rules that live only in your code into specs you approve one by one. +**RFC — a proposal with open questions.** Nothing in it is true yet. It lays out a problem, options, and the questions a human has to answer before anything gets decided. When it's accepted, it becomes one or more Decision Records. +*Example:* [MP-RFC-005](examples/marketplace/knowledge/businesses/rfcs/MP-RFC-005-multi-branch-businesses.md) — "multi-branch businesses". Should a brand with two locations be one tenant with two branches, or two businesses? Draft, unanswered, explicitly *not current truth*. -## What It Found In The Field +**Spec — how a decision behaves in detail.** Only for decisions with enough rules, edge cases and invariants that reconstructing them from code would be a risk. It carries an acceptance block (normally your tests), so "implemented" is proven, not declared. +*Example:* [MP-SPEC-004](examples/marketplace/knowledge/businesses/specs/MP-SPEC-004-business-deactivation-and-publishing.md) — what deactivating a business does to the database, the API and the events, rule by rule, with what was removed and what replaces it. -The first production adoption was a multi-tenant marketplace, four months into development, built largely by coding agents. KDE arrived mid-project. Some of what happened: +**The agent decides which one.** When you ask for a change, the agent reads the domain's knowledge and: -- **A rules snapshot rotted in six days.** Before KDE 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. +- If the request is clear and the rule is settled, it drafts a **Decision Record**. +- If anything is ambiguous — two reasonable readings, a rule it isn't sure applies, a trade-off you haven't stated — it drafts an **RFC with the open questions** and hands them to you. It does not pick an answer on your behalf, ever. +- Once a decision is promoted, if the behavior is more than a couple of rules, it drafts a **Spec** anchored to that decision before implementing. -That marketplace's code is private, but you can read the knowledge of one of its domains as it stood in production: [examples/marketplace](examples/marketplace/README.md) holds its merchant domain, with ten accepted decisions, two specs, two RFCs, and the baseline of its backfill. +In practice this means the agent asks more than you're used to, and every question is one it would otherwise have answered silently in code. -## Quick Start +## Quick start -### An existing codebase +Requires Node 22.6+ (only for the tools; your project can be any stack). -From the root of your repository: +**Existing codebase** — from the repo root: ```bash -curl -fsSL https://raw.githubusercontent.com/emafriedrich/knowledge-driven-engineering/main/install.sh | bash -s -- orders +curl -fsSL https://raw.githubusercontent.com/emafriedrich/knowledge-driven-engineering/v0.8.0/install.sh | bash -s -- orders npm run knowledge -- backfill orders ``` -Then ask your coding agent to **"backfill the orders domain"**. There is nothing to paste: the installer ships the protocol as `tools/backfill-protocol.md`, and the KDE section it writes into your `AGENTS.md` tells agents to follow it. The agent audits the domain's code, compares it against any knowledge you already have, and presents each recovered rule with its evidence. You approve, reject, or edit each one; approved behavior lands as one spec per feature, and you promote it with the command the agent hands you. +Then tell your agent: *"backfill the orders domain"*. It audits the domain's code, presents each recovered rule with its evidence, and you approve, reject or edit them one by one. Approved rules land as specs; you promote them with the command the agent hands you. -Add more domains as you go (`npm run knowledge -- domain add payments --description "..." --code-paths src/payments/`) and backfill each one when you need it. Adopting in a single domain is fully supported. - -### A new project - -This is where KDE gives the most. Knowledge is written down as decisions are made, so current truth is complete from the first commit: the retrieval path never falls back to guessing from code, and the drift gate guards every domain from day one. - -Seed current truth with one decision your team already made: +**New project** — same install, then record the first decision your team already made: ```bash npm run knowledge -- new decision orders "Orders are immutable after payment" --author npm run knowledge -- promote DR-001 --by ``` -`promote` sets the status, records you as the approver, and lists the decision in the domain's index. - -The installer is idempotent, never overwrites your knowledge, and refuses to run in a subdirectory of a repository that already uses KDE. It needs Node 22.6 or later — the tools run on any stack. Re-run it with `--upgrade` to refresh the framework's tools after a release. Details in [ADOPTING.md](ADOPTING.md). +The installer is idempotent, never overwrites your knowledge, and adds a Knowledge-Driven Engineering section to `AGENTS.md` plus Claude Code hooks that run the validator on every knowledge edit. Other harnesses with post-edit hooks can be wired the same way. Details in [ADOPTING.md](ADOPTING.md). -## How It Works +### Minimal adoption -### Domains and artifacts +You can run decision records alone. Install as above, skip specs, and leave `code_paths` unset. Each decision your team makes is one `new decision` plus one `promote`; the domain's `decisions/index.yaml` and generated `CONTEXT.md` tell agents which decisions are in force, and the Precedence section in `AGENTS.md` tells them what wins on conflict. The drift gate never fires: it only gates domains that have specs and declared `code_paths`. Add specs and code paths later, one domain at a time, when a decision's behavior outgrows a code comment. Details in [ADOPTING.md](ADOPTING.md#minimal-adoption). -Knowledge is organized by **domain** — a product or system area such as `orders` or `payments` — and inside a domain by **artifact type**, each answering one question: +## How it works -| Artifact | Answers | -| --- | --- | -| Decision Record | What did we decide, and why? | -| Specification | What must the implementation do? | -| RFC | What change are we proposing? | -| Model | What states and transitions exist? | -| Contract | What interface do clients depend on? | -| User Flow, Information Architecture, Design System | How do users move, what lives where, which visual rules repeat? | -| Task | What bounded work remains? | -| Prompt | What context should an agent get for repeated work? | +**Domains.** Knowledge is split by product area (`orders`, `payments`). Each domain has decision records, optionally specs and RFCs, and a generated `CONTEXT.md`. Other artifact types (models, contracts, flows, tasks, prompts) exist for when you need them — see the [handbook](HANDBOOK.md#artifacts). -Domains optimize retrieval; artifact types protect meaning. Folders exist only when a domain has real content of that type. +**Precedence.** When sources disagree, the order is fixed and agents must report the conflict, never resolve it: -The size of a change picks the path: a simple decision goes straight to code; complex behavior gets a spec with tests; large uncertainty starts as an RFC. Not every decision needs a spec — architecture, caching or infrastructure choices usually have no product behavior for a spec to promise. +1. Active decision record +2. Current spec +3. Flows, IA, design system, playbooks +4. The code +5. Historical knowledge (superseded decisions, old RFCs) +6. Tickets, wikis, chat — cite, never obey -### Current truth and precedence - -`knowledge/index.yaml` lists the domains. Each domain's `decisions/index.yaml` maps topics to the Decision Record in force, and a generated `CONTEXT.md` gives agents the domain at a glance. When sources disagree: - -1. An active Decision Record in the domain index -2. A current specification that does not contradict it -3. Information architecture, flows, design system, playbooks -4. The implementation -5. Historical knowledge — old RFCs, superseded decisions -6. Raw signals — tickets, wikis, chat. Cite them; never obey them. - -### Lifecycle - -Every transition is one command that validates the repository and refreshes the manifests, or refuses and says why: +**Lifecycle.** One command per transition; each validates and refreshes the indexes or refuses and says why. ```bash npm run knowledge -- new "" [--by agent] -npm run knowledge -- promote <ID> --by <human> # humans only -npm run knowledge -- supersede <OLD-ID> --by <NEW-ID> # humans only -npm run knowledge -- backfill <domain> # opens or resumes a backfill session -npm run knowledge -- domain add <name> --description "..." [--code-paths ...] -npm run knowledge -- accept <SPEC-ID> # runs a spec's acceptance block -npm run knowledge:check # the validator +npm run knowledge -- promote <ID> --by <human> +npm run knowledge -- supersede <OLD-ID> --by <NEW-ID> +npm run knowledge -- accept <SPEC-ID> # runs the spec's acceptance block +npm run knowledge:check # the validator ``` -Decision Records are superseded, not rewritten: a new record replaces the old one, and the command lists every document that depended on it. +Decisions are superseded, never rewritten. -### What CI enforces +**CI.** +- The **validator** checks ids, references, statuses, supersession links, stale indexes, frontmatter — and that nothing an agent drafted enters current truth without a recorded human approver. +- The **drift gate** fails a PR that changes code mapped to a domain with a current spec unless the PR also changes that domain's knowledge or declares `no-behavior-change`. +- **Acceptance**: a spec marked `implemented` must pass its acceptance block, normally your own tests. -- **The validator**: duplicate ids, broken references and dependencies, invalid statuses, supersession links, stale index entries, required frontmatter — and the promotion gate: an agent-drafted document cannot enter current truth without a recorded human approver, and a spec cannot enter it without an active decision behind it. -- **The drift gate**: a pull request that changes code mapped to a domain with a current spec must change that domain's knowledge, or declare `no-behavior-change` (or `implements-draft: <SPEC-ID>` for work against a draft). -- **Acceptance**: a spec promoted to `implemented` must pass its acceptance block — normally your own test suite — in CI. +## When to use it — and when not -The installer also wires Claude Code hooks that run the validator the moment an agent edits a knowledge file; any harness with post-edit hooks can do the same. +Use it for products with business rules, multi-tenant or permission-heavy systems, or any codebase where agents do most of the implementation. -## When To Use It +Skip it for prototypes, scripts, and code whose behavior fits in its comments. -Use KDE when contributors — human or agent — need context before changing behavior: products with business rules, multi-tenant or permission-heavy systems, cross-functional trade-offs, or any codebase where agents do a large share of the implementation. +## Honest limits -Skip it for throwaway prototypes, single-purpose scripts, and code whose meaningful behavior fits in its comments. +- **Promotion is procedural.** The validator sees that an approver is recorded, not who typed it. Branch protection and CODEOWNERS make it real. +- **The drift gate can be rubber-stamped.** `no-behavior-change` will get pasted by reflex the same way `skip-changelog` does. Mitigation so far: the declaration sits in the PR description where reviewers see it, and the gate prints it in the CI log. Nothing counts how often each author reaches for it. +- **One approver is a bottleneck.** The method makes approval explicit; it cannot make it careful. -## Honest Limits +## How it compares -- **Promotion is a procedural guarantee.** The validator sees that an approver is recorded, not who typed it. Branch protection and CODEOWNERS make it a technical one. -- **One approver is a bottleneck.** When one person approves every agent draft, review thins out. KDE makes approval explicit and visible; it cannot make it careful. -- **Knowledge still takes attention.** The drift gate forces the question on every pull request; it cannot answer it for you. +Versus **ADRs + AGENTS.md** alone: those record decisions and instruct agents, but nothing computes which decisions are current, nothing fails a PR when code and decisions diverge, and nothing gates agent-written knowledge behind a human. -## Prior Art +Versus **spec-driven tools** (Spec Kit, Kiro specs, OpenSpec, Cursor rules): those drive one task from a spec. Knowledge-Driven Engineering is the persistent layer underneath — decisions with history, precedence between sources, and a backfill path for rules that only exist in code. -The pieces are deliberately familiar: Decision Records (Nygard's ADRs), RFC processes, spec-driven development, the `AGENTS.md` convention, and domain partitioning from DDD. KDE is an operational synthesis for teams where agents implement, and its delta is what that tradition leaves out: a computable projection of current truth, explicit precedence between sources, consumption rules for agents, knowledge integrity as CI, and a governed path for knowledge that so far exists only in code. +Versus **OKF** (Google Cloud): same raw material, different half of the problem. OKF standardizes how knowledge is written so any tool can read it; the method governs whether you can trust it. [Detailed comparison](HANDBOOK.md#relation-to-okf). -The raw material — markdown files with a short header, linked to each other, versioned in git — is the same one Google Cloud's [Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog/tree/main/okf) (OKF) uses, but the two solve different halves of the problem. OKF is a shared way to write knowledge down so any tool can read it, and it is deliberately forgiving: a reader takes whatever it finds. KDE is about knowledge you can rely on while you work: +## This repository -- **Trustworthy.** Nothing an agent writes counts until a person approves it, and a pull request fails when the code and the knowledge stop agreeing. -- **Checkable after the fact.** Decisions are replaced, never rewritten, so you can see who approved what, when, and what it replaced; a spec's tests can be rerun at any time to show it still holds. -- **Quick to find.** An agent starting a task does not search the whole repository: it goes to the area it is changing and reads the decisions that apply today, then the code — in the field, a domain's rules read in about 8,000 tokens instead of the 130,000 it took to work them out from the code. +Both the definition of the method and a working instance of it: the method's own decisions live in `knowledge/methodology/` under the same lifecycle. -OKF does a few things KDE does not, such as setting a date when a document should be checked again and recording more than one reviewer. The [handbook](HANDBOOK.md#relation-to-okf) has the detailed comparison. - -## This Repository - -It is two things: the definition of the method and a working instance of it. The method's own decisions live in `knowledge/methodology/` and follow the same lifecycle, so every rule above traces to a Decision Record. - -- [ADOPTING.md](ADOPTING.md) — installing, upgrading, backfilling, minimal adoption +- [ADOPTING.md](ADOPTING.md) — install, upgrade, backfill, what happened in the first production adoption - [HANDBOOK.md](HANDBOOK.md) — the method in full - [AGENTS.md](AGENTS.md) — the rules agents follow -- [examples/marketplace](examples/marketplace/README.md) — one domain excerpted from the first production adoption -- [examples/food-delivery](examples/food-delivery/README.md) — a small worked example -- [CONTRIBUTING.md](CONTRIBUTING.md) and [REVIEW.md](REVIEW.md) — changing the method +- [examples/marketplace](examples/marketplace/README.md) — one real domain: ten decisions, two specs, two RFCs, backfill baseline +- [CONTRIBUTING.md](CONTRIBUTING.md) · [REVIEW.md](REVIEW.md) ```bash npm test # the tools' test suite -npm run knowledge:check # validate this repository's own knowledge -``` +npm run knowledge:check # validate this repo's own knowledge +``` \ No newline at end of file diff --git a/examples/marketplace/README.md b/examples/marketplace/README.md index 5f13889..0cce795 100644 --- a/examples/marketplace/README.md +++ b/examples/marketplace/README.md @@ -20,5 +20,7 @@ The records are unedited except for these changes, which let the excerpt stand a - Frontmatter references (`depends_on`, `related`, …) and `scope` values that point outside this domain were removed, because the validator rejects targets that do not exist. Body text still cites them, for example `MP-DR-008` or `MP-SPEC-005`, which live in domains not included here. - The product's name and city were removed. - `CONTEXT.md` was regenerated for the new paths. +- `knowledge/index.yaml` declares the two current specs under `current`, so the generated `CONTEXT.md` lists them as current truth. The original catalog had not been updated after the specs were promoted. +- Not a change, but worth knowing: `MP-DR-034` carries a "Revision note: edited in place after acceptance". That happened before launch, while the record was days old and its dependents were still being written. The method says decisions are superseded, never rewritten; in a project in production the same change would have been a new record promoted with `knowledge supersede`, leaving the original as history. The application code is not included. The paths in the domain README's code map and the `file:line` citations in the records point to the private codebase. diff --git a/examples/marketplace/knowledge/businesses/CONTEXT.md b/examples/marketplace/knowledge/businesses/CONTEXT.md index 89c790f..5f914e9 100644 --- a/examples/marketplace/knowledge/businesses/CONTEXT.md +++ b/examples/marketplace/knowledge/businesses/CONTEXT.md @@ -7,7 +7,10 @@ Merchant entity - profile, schedules, location, publication and settings. ## Current truth -None yet. +| Role | ID | Title | Updated | +| --- | --- | --- | --- | +| deactivation_and_publishing_backend | MP-SPEC-004 | Business deactivation and marketplace publishing (backend) | 2026-09-25 | +| deactivation_and_publishing_admin_ui | MP-SPEC-007 | Admin UI for business deactivation and marketplace publishing (frontend) | 2026-09-25 | ## Active decisions diff --git a/examples/marketplace/knowledge/index.yaml b/examples/marketplace/knowledge/index.yaml index 65039dc..2a620f8 100644 --- a/examples/marketplace/knowledge/index.yaml +++ b/examples/marketplace/knowledge/index.yaml @@ -4,3 +4,6 @@ domains: path: examples/marketplace/knowledge/businesses description: Merchant entity - profile, schedules, location, publication and settings. decision_index: examples/marketplace/knowledge/businesses/decisions/index.yaml + current: + deactivation_and_publishing_backend: MP-SPEC-004 + deactivation_and_publishing_admin_ui: MP-SPEC-007 diff --git a/install.sh b/install.sh index c169730..39ea808 100755 --- a/install.sh +++ b/install.sh @@ -2,7 +2,7 @@ # Installs Knowledge-Driven Engineering scaffolding into the current repository. # # Usage, from the root of your repository: -# curl -fsSL https://raw.githubusercontent.com/emafriedrich/knowledge-driven-engineering/main/install.sh | bash -s -- <first-domain> +# curl -fsSL https://raw.githubusercontent.com/emafriedrich/knowledge-driven-engineering/v0.8.0/install.sh | bash -s -- <first-domain> # # From a local clone (offline / development): # KDE_SOURCE=/path/to/knowledge-driven-engineering bash install.sh <first-domain> @@ -10,8 +10,10 @@ # <first-domain> only seeds a fresh install. On a repository that already has # knowledge/index.yaml, add domains with `npm run knowledge -- domain add <name>`. # -# Pin a release instead of main: -# KDE_REF=v0.1.0 curl -fsSL .../install.sh | bash -s -- <first-domain> +# The installer downloads the release named on line 1 of the script (the tag that +# matches its own version). Another release, a branch or a commit: +# KDE_VERSION=main curl -fsSL .../install.sh | bash -s -- <first-domain> +# (KDE_REF is honoured as an older name for the same variable.) # # Refresh framework-owned files after a release (DR-011): # curl -fsSL .../install.sh | bash -s -- --upgrade @@ -33,7 +35,10 @@ for arg in "$@"; do esac done REPO_URL="https://github.com/emafriedrich/knowledge-driven-engineering" -REF="${KDE_REF:-main}" +# Release this copy of the installer belongs to; bumped with package.json on +# every release so a curl of the script installs the matching tools. +DEFAULT_REF="v0.8.0" +REF="${KDE_VERSION:-${KDE_REF:-$DEFAULT_REF}}" say() { printf '%s\n' "$*"; } add() { say " add $1"; } @@ -74,19 +79,19 @@ STALE="" framework_file() { local prefix="$1" src="$2" dest="$3" suffix="${4:-}" tmp tmp="$(mktemp)" - { printf '%s kde-version: %s%s\n' "$prefix" "$KDE_VERSION" "$suffix"; cat "$src"; } > "$tmp" + { printf '%s kde-version: %s%s\n' "$prefix" "$FRAMEWORK_VERSION" "$suffix"; cat "$src"; } > "$tmp" if [ ! -e "$dest" ]; then cat "$tmp" > "$dest"; add "$dest" elif cmp -s "$tmp" "$dest"; then - say " ok $dest (kde ${KDE_VERSION})" + say " ok $dest (kde ${FRAMEWORK_VERSION})" elif [ "$UPGRADE" -eq 1 ]; then local from; from="$(installed_version "$dest")" - if [ "$from" = "$KDE_VERSION" ]; then + if [ "$from" = "$FRAMEWORK_VERSION" ]; then # Same version, different content: the adopter edited the file. Overwrite, # but say so — framework-owned files are not an extension point. - say " WARN $dest had local edits; overwritten with kde ${KDE_VERSION}. Framework-owned files are not meant to be edited: fork the framework if you need different tooling." + say " WARN $dest had local edits; overwritten with kde ${FRAMEWORK_VERSION}. Framework-owned files are not meant to be edited: fork the framework if you need different tooling." else - say " upgrade $dest (${from} -> ${KDE_VERSION})" + say " upgrade $dest (${from} -> ${FRAMEWORK_VERSION})" fi cat "$tmp" > "$dest" else @@ -104,24 +109,24 @@ else TMP="$(mktemp -d)" CLEANUP="$TMP" say "Downloading ${REPO_URL}@${REF} ..." - # /archive/<ref>.tar.gz resolves branches, tags and commits alike, so KDE_REF + # /archive/<ref>.tar.gz resolves branches, tags and commits alike, so KDE_VERSION # can pin a release (v0.1.0) as well as track main. curl -fsSL "${REPO_URL}/archive/${REF}.tar.gz" | tar -xz -C "$TMP" SRC="$(find "$TMP" -maxdepth 1 -mindepth 1 -type d | head -1)" fi trap '[ -n "$CLEANUP" ] && rm -rf "$CLEANUP"' EXIT -# The framework version is package.json's version in this repository (DR-011). -KDE_VERSION="$(sed -n 's/^[[:space:]]*"version":[[:space:]]*"\([^"]*\)".*/\1/p' "$SRC/package.json" | head -1)" -if [ -z "$KDE_VERSION" ]; then +# The framework version is package.json's version in the downloaded source (DR-011). +FRAMEWORK_VERSION="$(sed -n 's/^[[:space:]]*"version":[[:space:]]*"\([^"]*\)".*/\1/p' "$SRC/package.json" | head -1)" +if [ -z "$FRAMEWORK_VERSION" ]; then say "install.sh: could not read version from ${SRC}/package.json" >&2 exit 1 fi if [ "$UPGRADE" -eq 1 ]; then - say "Upgrading Knowledge-Driven Engineering framework files in $(pwd) to ${KDE_VERSION}" + say "Upgrading Knowledge-Driven Engineering framework files in $(pwd) to ${FRAMEWORK_VERSION}" else - say "Installing Knowledge-Driven Engineering ${KDE_VERSION} into $(pwd)" + say "Installing Knowledge-Driven Engineering ${FRAMEWORK_VERSION} into $(pwd)" fi # --- Tools and templates ----------------------------------------------------- @@ -141,7 +146,7 @@ for src in "$SRC"/templates/*.md; do else # Templates are adopter-owned and never overwritten (DR-011); a note, not a # WARN, so a team that customised them on purpose is not trained to ignore warnings. - say " note templates/$base differs from kde ${KDE_VERSION} (yours; not touched) — compare: ${REPO_URL}/blob/${REF}/templates/$base" + say " note templates/$base differs from kde ${FRAMEWORK_VERSION} (yours; not touched) — compare: ${REPO_URL}/blob/${REF}/templates/$base" fi done @@ -297,7 +302,7 @@ if [ -e .claude/settings.json ]; then if [ "$status" -eq 3 ]; then skip ".claude/settings.json KDE hooks" else - say " WARN could not merge hooks into .claude/settings.json; add them manually from ${REPO_URL}/blob/main/.claude/settings.json" + say " WARN could not merge hooks into .claude/settings.json; add them manually from ${REPO_URL}/blob/${REF}/.claude/settings.json" fi fi else @@ -336,6 +341,16 @@ When sources disagree: active Decision Record > current Specification > other do RFC proposes. Decision Record decides. Spec promises. Tests prove. A simple decision may go straight from a Decision Record to code; citing it in a comment (\`// DR-017 rule 3\`) is desirable and does not by itself call for a spec. Create or update a spec when behavior needs an explicit, independently testable contract: multiple rules, interactions, invariants, edge cases, or acceptance criteria that should not be reconstructed from code and decisions — or rules expected to change while the decision stays. Draft an RFC first when the change is uncertain or cross-domain. +#### 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. @@ -360,7 +375,7 @@ if [ -e AGENTS.md ] && grep -q '<!-- kde:begin -->' AGENTS.md; then AGENTS_CUR="$(mktemp)" awk '/<!-- kde:begin -->/ { on = 1 } on { print } /<!-- kde:end -->/ { on = 0 }' AGENTS.md > "$AGENTS_CUR" if cmp -s "$AGENTS_SRC" "$AGENTS_CUR"; then - say " ok AGENTS.md KDE section (kde ${KDE_VERSION})" + say " ok AGENTS.md KDE section (kde ${FRAMEWORK_VERSION})" elif ! grep -q '<!-- kde:end -->' AGENTS.md; then say " WARN AGENTS.md has a kde:begin marker without kde:end; section left alone. Restore the end marker and re-run." elif [ "$UPGRADE" -eq 1 ]; then @@ -372,7 +387,7 @@ if [ -e AGENTS.md ] && grep -q '<!-- kde:begin -->' AGENTS.md; then ' AGENTS.md > "$AGENTS_NEW" cat "$AGENTS_NEW" > AGENTS.md rm -f "$AGENTS_NEW" - say " upgrade AGENTS.md KDE section (-> ${KDE_VERSION}); text outside the markers untouched. Rules of your own belong outside the markers." + say " upgrade AGENTS.md KDE section (-> ${FRAMEWORK_VERSION}); text outside the markers untouched. Rules of your own belong outside the markers." else skip "AGENTS.md KDE section" STALE="${STALE} AGENTS.md:KDE-section" @@ -411,7 +426,7 @@ fi # through the warning (DR-011). if [ -n "$STALE" ]; then say "" - say " WARN framework-owned files differ from kde ${KDE_VERSION} (installed: $(installed_version tools/knowledge-check.mts)):" + say " WARN framework-owned files differ from kde ${FRAMEWORK_VERSION} (installed: $(installed_version tools/knowledge-check.mts)):" for f in $STALE; do say " - $f"; done say " Re-run with --upgrade to refresh them. Adopter-owned files (knowledge/, templates/, AGENTS.md outside the kde markers) are never touched." fi @@ -428,4 +443,4 @@ say " npm run knowledge -- new decision ${DOMAIN:-<domain>} \"<title>\" -- say " npm run knowledge -- promote <ID> --by <you>" say " 3. Enable branch protection with code-owner review so knowledge promotion needs a human." say "" -say "Full guide: ${REPO_URL}/blob/main/ADOPTING.md" +say "Full guide: ${REPO_URL}/blob/${REF}/ADOPTING.md" diff --git a/knowledge/methodology/CONTEXT.md b/knowledge/methodology/CONTEXT.md index a9af346..2be255c 100644 --- a/knowledge/methodology/CONTEXT.md +++ b/knowledge/methodology/CONTEXT.md @@ -14,7 +14,7 @@ Knowledge-Driven Engineering methodology rules, examples, and agent guidance. | product_vision | KDE-PV-001 | Knowledge-Driven Engineering product vision | 2026-08-30 | | lifecycle_flow | KDE-FLOW-001 | Knowledge evolution lifecycle | 2026-08-30 | | artifact_spec | KDE-SPEC-001 | Knowledge artifact and metadata rules | 2026-09-21 | -| agent_context | KDE-PROMPT-001 | Coding agent context template | 2026-09-03 | +| agent_context | KDE-PROMPT-001 | Coding agent context template | 2026-09-30 | | signals_playbook | KDE-PLAYBOOK-001 | Drafting knowledge from external signals | 2026-09-09 | ## Active decisions @@ -41,7 +41,9 @@ Knowledge-Driven Engineering methodology rules, examples, and agent guidance. ## Pending — NOT current truth, do not obey +- `DR-019` (draft, agent-drafted): Agents draft a decision when the request is clear, an RFC when it is ambiguous, and a spec when behavior has many rules - `KDE-RFC-010` (draft, agent-drafted): Distribute framework tooling as an npm package +- `KDE-RFC-014` (draft, agent-drafted): Agents choose the artifact and never assume an answer ## Rules diff --git a/knowledge/methodology/decisions/DR-019-agents-draft-a-decision-when-the-request-is-clear-an-rfc-whe.md b/knowledge/methodology/decisions/DR-019-agents-draft-a-decision-when-the-request-is-clear-an-rfc-whe.md new file mode 100644 index 0000000..bc60783 --- /dev/null +++ b/knowledge/methodology/decisions/DR-019-agents-draft-a-decision-when-the-request-is-clear-an-rfc-whe.md @@ -0,0 +1,42 @@ +--- +id: DR-019 +title: Agents draft a decision when the request is clear, an RFC when it is ambiguous, and a spec when behavior has many rules +status: draft +created: 2026-09-30 +updated: 2026-09-30 +drafted_by: agent +approved_by: [] +scope: [methodology] +tags: [decision, agents] +depends_on: [] +related: [KDE-RFC-014, DR-007, DR-016, DR-017, KDE-PROMPT-001] +supersedes: [] +superseded_by: [] +--- + +# DR-019: Agents Draft A Decision When The Request Is Clear, An RFC When It Is Ambiguous, And A Spec When Behavior Has Many Rules + +## Context + +AGENTS.md said what each artifact is for but not how an agent facing a request picks one, so ambiguous requests were resolved silently in code and the question a human should have answered never surfaced. KDE-RFC-014 proposed an explicit decision rule with a stop condition, matching the promise the README makes to adopters. + +## Decision + +When asked for a change, an agent reads the domain's knowledge first, then picks one artifact: + +1. **Clear request, settled rule: Decision Record.** The agent drafts the record and stops there; a simple decision goes from the record to code (DR-016). +2. **Any ambiguity: RFC, then stop.** If a request admits more than one reasonable reading, or a rule's applicability is uncertain, the agent drafts an RFC listing the open questions and stops. It does not choose an answer on the human's behalf, and it does not implement against an assumption. The accepted RFC becomes one or more Decision Records. +3. **Promoted decision with rich behavior: Spec before code.** If the behavior a promoted decision implies is more than a couple of rules — interactions, invariants, edge cases — the agent drafts a Spec anchored to that decision before implementing. Otherwise it implements against the decision and cites it in code. + +The rule lives in the **Choosing the artifact** subsection of AGENTS.md, inside the framework-owned section the installer writes and upgrades (DR-017), and in the coding-agent context template (KDE-PROMPT-001). + +## Consequences + +- Agents ask more, in the form of RFC drafts rather than chat questions; every question in an RFC is one that would otherwise have been answered silently in code. +- The installer's test asserts the installed section carries the rule verbatim, so an adopter on `--upgrade` receives it. +- The choice itself is not machine-checkable; the existing gates still apply around it (`motivated_by` on agent RFCs, DR-007; `implements-draft` when implementing against a draft spec, DR-015). +- Lifecycle and artifact semantics are unchanged: agents draft everything and promote nothing (DR-007). + +## Supersession + +None. diff --git a/knowledge/methodology/prompts/coding-agent.md b/knowledge/methodology/prompts/coding-agent.md index 9de80a0..891a88e 100644 --- a/knowledge/methodology/prompts/coding-agent.md +++ b/knowledge/methodology/prompts/coding-agent.md @@ -3,7 +3,7 @@ id: KDE-PROMPT-001 title: Coding agent context template status: current created: 2026-08-30 -updated: 2026-09-03 +updated: 2026-09-30 authors: [engineering] scope: [methodology] tags: [prompt, agents] @@ -32,6 +32,7 @@ Canonical knowledge: Rules: - Report conflicts between docs and implementation. - Do not invent product behavior. +- Pick the artifact before writing: a Decision Record when the request is clear and its rule settled; an RFC listing the open questions when a request admits more than one reasonable reading or a rule's applicability is uncertain — then stop, never choose for the human or implement against an assumption; a Spec anchored to the promoted decision when the behavior is more than a couple of rules. - Declare drafted_by: agent on knowledge documents you draft; never promote them. - Update affected specs or decisions when behavior changes. ``` diff --git a/knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md b/knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md new file mode 100644 index 0000000..1fdb354 --- /dev/null +++ b/knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md @@ -0,0 +1,56 @@ +--- +id: KDE-RFC-014 +title: Agents choose the artifact and never assume an answer +status: draft +created: 2026-09-30 +updated: 2026-09-30 +drafted_by: agent +approved_by: [] +motivated_by: The README rewrite (2026-09-30) promises adopters that the agent picks the artifact for them and never answers an open question on their behalf; AGENTS.md described what each artifact is for but gave the agent no rule for choosing one, or for stopping when a request is ambiguous +scope: [methodology] +tags: [rfc, agents] +depends_on: [] +related: [DR-007, DR-016, KDE-PROMPT-001, KDE-FLOW-001] +--- + +# RFC: Agents choose the artifact and never assume an answer + +## Summary + +Give agents one explicit decision rule for which knowledge artifact to draft when a human asks for a change: a Decision Record when the request is clear and its rule is settled; an RFC listing the open questions when anything is ambiguous, after which the agent stops; a Spec anchored to the promoted decision when the behavior is more than a couple of rules. The rule that matters most is the middle one: an agent never chooses an answer on the human's behalf and never implements against an assumption. + +## Problem + +The method already says what each artifact is for — "RFC proposes. Decision Record decides. Spec promises. Tests prove." — and the Knowledge Changes section of AGENTS.md lists creation criteria per artifact. What it never says is how an agent facing a concrete request picks one, and what it does when the request does not fit cleanly. + +The gap shows in practice as silent assumptions. A request that admits two readings gets implemented under one of them; a rule whose applicability the agent is not sure of gets applied, or skipped, without anyone being asked. The result looks like a finished change and hides the question that a human should have answered. Field evidence from the first production adoption: decisions were drafted well when the request was clear, but ambiguous requests produced code first and knowledge second, and the questions surfaced only in review, if at all. + +The new README (section "Three documents, and who writes which") promises adopters the opposite: that the agent picks the right document, asks instead of guessing, and drafts a spec before implementing rich behavior. That promise is not written anywhere agents read. + +## Proposal + +Add a subsection **Choosing the artifact** to the Knowledge Changes section of AGENTS.md — and to the framework-owned section `install.sh` writes into adopting repositories (DR-017) — with this rule: + +1. **Clear request, settled rule: Decision Record.** If the request is unambiguous and the rule it needs is settled, or the request itself is the decision, the agent drafts a Decision Record and stops there. A simple decision goes from the record to code (DR-016). +2. **Any ambiguity: RFC with open questions, then stop.** If a request admits more than one reasonable reading, or a rule's applicability is uncertain, the agent drafts an RFC listing the open questions and stops. It does not choose an answer on the human's behalf, and it does not implement against an assumption. When the human resolves the questions, the RFC becomes one or more Decision Records. +3. **Promoted decision with rich behavior: Spec first.** Once a decision is promoted, if the behavior it implies is more than a couple of rules — interactions, invariants, edge cases — the agent drafts a Spec anchored to that decision (`depends_on`) before implementing. Otherwise it implements against the decision and cites it in code. + +The same rule is reflected in the coding-agent context template (KDE-PROMPT-001), so scoped prompts carry it even when AGENTS.md is not loaded. + +Nothing here changes the lifecycle: agents still draft everything and promote nothing (DR-007), and the artifact semantics are unchanged (DR-016). The change is a decision procedure over existing artifacts, plus a stop condition. + +## Alternatives + +- **Leave it to judgment.** Status quo. Models under context pressure resolve ambiguity by picking the reading that lets them finish; the question disappears into the diff. +- **Always draft an RFC.** Rejected: an RFC for every clear request buries the real questions in ceremony, and the human stops reading them. +- **Ask in chat instead of drafting an RFC.** Rejected: a question in chat has no lifecycle, no scope and no place in precedence. The RFC is the question's canonical form, and its acceptance produces the decision. +- **Enforce mechanically.** Not possible for the choice itself — no validator can tell a clear request from an ambiguous one. What can be enforced already is: an agent RFC must carry `motivated_by` (DR-007), and a draft spec implemented against must be declared in the PR (DR-015). + +## Open Questions + +- Should the stop condition also apply during a backfill session (KDE-PROMPT-002), where the agent presents recovered rules with evidence and the human disposes of each one? The session protocol already has the human in the loop per rule; the proposal is that it does not change, and that a recovered rule whose reading is unclear is presented as a question rather than a rule. +- "More than a couple of rules" is deliberately a judgment call, as it is in DR-016. Should the subsection give a number? The proposal is no: the creation criteria in HANDBOOK.md already list the signals (interactions, invariants, edge cases, acceptance criteria), and a number invites gaming. + +## Outcome + +<!-- Fill after review: accepted, rejected, or deferred, with links to resulting decisions/specs. --> diff --git a/package.json b/package.json index dbe7af6..51acd86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "knowledge-driven-engineering", - "version": "0.7.2", + "version": "0.8.0", "private": true, "type": "module", "scripts": { diff --git a/tests/install.test.ts b/tests/install.test.ts index 0c21f59..44e8be9 100644 --- a/tests/install.test.ts +++ b/tests/install.test.ts @@ -140,6 +140,13 @@ test('the installed kde section carries every hard rule of the framework AGENTS. // The tools enforce these rules in adopting repositories too; a rule missing here is lost on --upgrade. for (const rule of rules) assert.ok(installed.includes(rule), `install.sh kde section is missing the hard rule: ${rule}`); assert.match(installed, /Read the domain `CONTEXT\.md` when present/); + // The artifact-choice rule (DR-019) is read every session too: the installed section carries it verbatim. + const choosing = own.split('### Choosing the artifact')[1].split('\n## ')[0].split('\n').filter((line) => line.startsWith('- ')); + assert.equal(choosing.length, 3); + const section = installed.split('<!-- kde:begin -->')[1].split('<!-- kde:end -->')[0]; + assert.match(section, /#### Choosing the artifact/); + for (const rule of choosing) assert.ok(section.includes(rule), `install.sh kde section is missing the artifact-choice rule: ${rule}`); + assert.ok(section.includes('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.')); } finally { rmSync(root, { recursive: true, force: true }); } @@ -174,3 +181,16 @@ test('the kde section of AGENTS.md is framework-owned: reported when stale, repl rmSync(root, { recursive: true, force: true }); } }); + +test('the installer downloads the release that matches its own version unless KDE_VERSION says otherwise', () => { + const script = readFileSync(join(repo, 'install.sh'), 'utf8'); + const version = JSON.parse(readFileSync(join(repo, 'package.json'), 'utf8')).version as string; + // The curl one-liner in the docs names the tag; a release bumps both together. + assert.match(script, new RegExp(`^DEFAULT_REF="v${version.replace(/\./g, '\\.')}"$`, 'm')); + assert.match(script, /^REF="\$\{KDE_VERSION:-\$\{KDE_REF:-\$DEFAULT_REF\}\}"$/m); + for (const doc of ['README.md', 'ADOPTING.md']) { + const text = readFileSync(join(repo, doc), 'utf8'); + assert.doesNotMatch(text, /knowledge-driven-engineering\/main\/install\.sh/, `${doc} still installs from main`); + assert.match(text, new RegExp(`knowledge-driven-engineering/v${version.replace(/\./g, '\\.')}/install\\.sh`), `${doc} does not install from v${version}`); + } +}); From 4c30f8c9d65a746e5cff97c949d9e317e51b0490 Mon Sep 17 00:00:00 2001 From: Emanuel Friedrich <aemanuelfriedrich@gmail.com> Date: Wed, 30 Sep 2026 03:33:52 -0300 Subject: [PATCH 2/5] DRIFT.md: name the problem the drift gate exists for, in both directions and both speeds README gains a "Why knowledge drifts" section and links the CI bullet to it; ADOPTING, HANDBOOK and the AGENTS.md method map point at DRIFT.md, which states what the gate catches and what it does not (reflex declarations, unmapped code, spec-only domains, wrong-direction edits, drift inside a session before the PR exists). --- ADOPTING.md | 2 +- AGENTS.md | 1 + DRIFT.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ HANDBOOK.md | 2 ++ README.md | 6 ++++- 5 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 DRIFT.md diff --git a/ADOPTING.md b/ADOPTING.md index 05fafd9..41a4006 100644 --- a/ADOPTING.md +++ b/ADOPTING.md @@ -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. diff --git a/AGENTS.md b/AGENTS.md index 56c081d..099b08a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 diff --git a/DRIFT.md b/DRIFT.md new file mode 100644 index 0000000..edfe2f3 --- /dev/null +++ b/DRIFT.md @@ -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). diff --git a/HANDBOOK.md b/HANDBOOK.md index d88f558..11a6cc9 100644 --- a/HANDBOOK.md +++ b/HANDBOOK.md @@ -253,6 +253,8 @@ When a decision changes: When a spec changes, review implementation and tests. Create a Decision Record only if the change records an important product, UX, design, architecture, security, infrastructure, or business decision. +When code changes without its knowledge, the drift gate fails the pull request. The problem it exists for, its rules, and its blind spots are in [DRIFT.md](DRIFT.md). + ## Agent Consumption Agents should retrieve the minimum canonical knowledge needed for the task: diff --git a/README.md b/README.md index 54a4fad..b8f17d5 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,10 @@ The agent drafted it; a human approved it; the validator refuses any record with That domain reads in ~8k tokens. Reconstructing the same rules from its ~50 files of code took an agent ~130k (n=1, one-time extraction — see [in the field](ADOPTING.md#in-the-field)). +## Why knowledge drifts + +Writing the rules down is the easy part. The hard part is that they stop being true: someone changes checkout and nobody touches the decision that governs it, or an agent reads the rules at the start of a session and, a hundred thousand tokens later, implements something else. A human discounts a stale document. An agent obeys it literally, and a rule that no longer holds looks exactly like one that does. That is drift, and discipline does not fix it. What has held up is mechanical: a change to governed code cannot merge without touching the knowledge that governs it, or saying out loud that it does not need to. [More on drift, and what the gate does not catch](DRIFT.md). + ## Three documents, and who writes which You don't need to know what an RFC is to use this. There are three kinds of document that matter, and the agent picks the right one for you. @@ -133,7 +137,7 @@ Decisions are superseded, never rewritten. **CI.** - The **validator** checks ids, references, statuses, supersession links, stale indexes, frontmatter — and that nothing an agent drafted enters current truth without a recorded human approver. -- The **drift gate** fails a PR that changes code mapped to a domain with a current spec unless the PR also changes that domain's knowledge or declares `no-behavior-change`. +- The **drift gate** is the answer to [drift](DRIFT.md): a PR that changes code mapped to a domain with a current spec fails unless it also changes that domain's knowledge or declares `no-behavior-change`. It checks the result, not the process, so it catches a divergence that took a week and one that took twenty minutes of a saturated session alike. - **Acceptance**: a spec marked `implemented` must pass its acceptance block, normally your own tests. ## When to use it — and when not From 2215a02d75b976a0af0c2feeff952f8aaf135e88 Mon Sep 17 00:00:00 2001 From: Emanuel Friedrich <aemanuelfriedrich@gmail.com> Date: Wed, 30 Sep 2026 03:37:37 -0300 Subject: [PATCH 3/5] KDE-RFC-015: re-anchor agents in-session by returning the domain manifest on code edits (draft) --- knowledge/methodology/CONTEXT.md | 1 + ...ession-the-hook-returns-the-domain-mani.md | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 knowledge/methodology/rfcs/KDE-RFC-015-re-anchor-agents-in-session-the-hook-returns-the-domain-mani.md diff --git a/knowledge/methodology/CONTEXT.md b/knowledge/methodology/CONTEXT.md index 2be255c..49babfc 100644 --- a/knowledge/methodology/CONTEXT.md +++ b/knowledge/methodology/CONTEXT.md @@ -44,6 +44,7 @@ Knowledge-Driven Engineering methodology rules, examples, and agent guidance. - `DR-019` (draft, agent-drafted): Agents draft a decision when the request is clear, an RFC when it is ambiguous, and a spec when behavior has many rules - `KDE-RFC-010` (draft, agent-drafted): Distribute framework tooling as an npm package - `KDE-RFC-014` (draft, agent-drafted): Agents choose the artifact and never assume an answer +- `KDE-RFC-015` (draft, agent-drafted): Re-anchor agents in-session: the hook returns the domain manifest on code edits ## Rules diff --git a/knowledge/methodology/rfcs/KDE-RFC-015-re-anchor-agents-in-session-the-hook-returns-the-domain-mani.md b/knowledge/methodology/rfcs/KDE-RFC-015-re-anchor-agents-in-session-the-hook-returns-the-domain-mani.md new file mode 100644 index 0000000..ee7be89 --- /dev/null +++ b/knowledge/methodology/rfcs/KDE-RFC-015-re-anchor-agents-in-session-the-hook-returns-the-domain-mani.md @@ -0,0 +1,56 @@ +--- +id: KDE-RFC-015 +title: "Re-anchor agents in-session: the hook returns the domain manifest on code edits" +status: draft +created: 2026-09-30 +updated: 2026-09-30 +drafted_by: agent +approved_by: [] +motivated_by: DRIFT.md (2026-09-30) names fast drift — an agent that read the rules at the start of a session and implements against a degraded reading or an unsurfaced assumption a hundred thousand tokens later — and admits the method has no mechanical defense against it before the pull request exists; every in-session defense today is procedural +scope: [methodology] +tags: [rfc, agents, tooling] +depends_on: [] +related: [DR-008, DR-009, DR-015, DR-019, KDE-RFC-014] +--- + +# RFC: Re-anchor agents in-session: the hook returns the domain manifest on code edits + +## Summary + +When an agent edits a file under a domain's `code_paths`, the harness hook that already runs on every edit returns that domain's generated `CONTEXT.md` to the agent, so the rules it must follow re-enter its context at the moment it is changing the code they govern. One mechanical defense against fast drift, built from two pieces that already exist: the manifest (DR-009) and the post-edit hook. + +## Problem + +The drift gate checks the result at pull-request time and is indifferent to how the divergence happened (DR-008). That is right for the gate and leaves a hole before it: inside a session, an agent reads `CONTEXT.md` once at the start, and by the time it edits checkout its context is saturated with build output, test logs and its own reasoning. What it implements answers a degraded reading of the rules, or an assumption it made along the way. Nothing in the repository changed; the agent stopped following it. DRIFT.md calls this fast drift and says plainly that the defenses against it are procedural: the context receipt, re-reading the manifest, drafting an RFC instead of assuming (DR-019). + +Procedural defenses are the ones models drop first under context pressure. Anything enforceable by machine should not be left as prose (KDE-RFC-001's principle). + +The hook already looks at code edits: `tools/knowledge-hook.mts` resolves the edited file to a domain through `code_paths` and warns when that domain's only specs are drafts (DR-015). The lookup exists; it just says nothing when the domain has current truth. + +## Proposal + +- **On an edit under a domain's `code_paths`, the hook returns the domain's `CONTEXT.md`** to the agent as context, not as an error. The manifest is the right payload: it is the retrieval bundle by construction, a map and never rationale, deterministic, and already committed (DR-009). Nothing new is rendered. +- **Once per domain per session, then on a cadence.** The first edit in a domain re-anchors unconditionally. Later edits in the same domain re-anchor again after N further edits in that domain (N calibrable, default in the open questions), because saturation is the problem and one injection at the first edit does not survive it. Session state is a small file keyed by the harness's session id, outside the repository. +- **Cheap on the happy path.** The hook reads the root catalog directly, as it does today, and touches the validator only when it has to. A file outside every `code_paths` costs one catalog read and nothing else. +- **Harness-specific wiring, harness-agnostic principle.** This repository ships the Claude Code wiring in `.claude/settings.json`; the mechanism is "a post-edit hook can return text the agent sees", which other harnesses provide too. Where a harness can only return errors, the manifest goes on the error channel with a clear prefix, as the draft-spec warning does today. +- **No new obligation on the agent.** The receipt, the manifest re-read and the RFC-and-stop rule stay as they are. This adds a mechanical floor under them. + +## Alternatives + +- **Status quo: procedural only.** Field evidence says agents implement against assumptions within a session; the gate then catches the symptom at PR time, after the work is done and the reasoning is gone. +- **Re-inject on every edit.** Simplest, and it floods the context with the same manifest, which is a different way to saturate it. Cadence is the compromise. +- **Inject before the edit instead of after.** Better in principle: the agent sees the rules before it writes. Whether a pre-edit hook can return context, and not only allow or deny, depends on the harness; left as an open question. +- **A Stop hook that checks the agent read the manifest.** Not verifiable: reading is not observable, and a check the agent can satisfy by echoing a line is theater. +- **Make the drift gate stricter.** Orthogonal. The gate cannot act before the branch exists. + +## Open Questions + +- **Cadence.** Once per domain per session, then every N edits in that domain. N = 10? The right number depends on how fast a session saturates, which no one has measured; propose 10 and calibrate on the first adoption that runs it. +- **Payload size.** The whole manifest, or only the current-truth and active-decisions tables, without the pending section and the rules footer? The marketplace example's manifest is about thirty lines; a domain with forty decisions is not. Propose the whole manifest up to a line budget, then tables only. +- **Before or after the edit.** If the harness supports returning context from a pre-edit hook, prefer it. Otherwise post-edit, where the agent can still revert. +- **Multi-domain edits.** A file under two domains' `code_paths` (nested prefixes) gets both manifests, or the innermost? Propose the innermost, matching how the drift gate reports. +- **Should the injection count as the receipt?** No: the receipt is what the agent declares it read, in the PR; the injection is what the harness showed it. Keeping them separate keeps Gate 5 honest. + +## Outcome + +<!-- Fill after review: accepted, rejected, or deferred, with links to resulting decisions/specs. --> From b1e1a2f1d61f9046d3887b4108ecf6fda51d6ec8 Mon Sep 17 00:00:00 2001 From: Emanuel Friedrich <aemanuelfriedrich@gmail.com> Date: Wed, 30 Sep 2026 03:39:11 -0300 Subject: [PATCH 4/5] Promote KDE-RFC-014 and DR-019 (approved by emafriedrich); index DR-019 under artifact-choice --- knowledge/methodology/CONTEXT.md | 3 +-- ...-draft-a-decision-when-the-request-is-clear-an-rfc-whe.md | 5 +++-- knowledge/methodology/decisions/index.yaml | 1 + ...-agents-choose-the-artifact-and-never-assume-an-answer.md | 5 +++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/knowledge/methodology/CONTEXT.md b/knowledge/methodology/CONTEXT.md index 49babfc..fc40134 100644 --- a/knowledge/methodology/CONTEXT.md +++ b/knowledge/methodology/CONTEXT.md @@ -38,12 +38,11 @@ Knowledge-Driven Engineering methodology rules, examples, and agent guidance. | artifact-lifecycle | DR-016 | Implemented belongs to specs and a spec is warranted by a testable contract | 2026-09-21 | | agent-rules-ownership | DR-017 | The agent rules section is framework-owned and stale templates are reported | 2026-09-21 | | backfill | DR-018 | Backfill recovers observed behavior into feature specs anchored to a baseline decision | 2026-09-29 | +| artifact-choice | DR-019 | Agents draft a decision when the request is clear, an RFC when it is ambiguous, and a spec when behavior has many rules | 2026-09-30 | ## Pending — NOT current truth, do not obey -- `DR-019` (draft, agent-drafted): Agents draft a decision when the request is clear, an RFC when it is ambiguous, and a spec when behavior has many rules - `KDE-RFC-010` (draft, agent-drafted): Distribute framework tooling as an npm package -- `KDE-RFC-014` (draft, agent-drafted): Agents choose the artifact and never assume an answer - `KDE-RFC-015` (draft, agent-drafted): Re-anchor agents in-session: the hook returns the domain manifest on code edits ## Rules diff --git a/knowledge/methodology/decisions/DR-019-agents-draft-a-decision-when-the-request-is-clear-an-rfc-whe.md b/knowledge/methodology/decisions/DR-019-agents-draft-a-decision-when-the-request-is-clear-an-rfc-whe.md index bc60783..173b454 100644 --- a/knowledge/methodology/decisions/DR-019-agents-draft-a-decision-when-the-request-is-clear-an-rfc-whe.md +++ b/knowledge/methodology/decisions/DR-019-agents-draft-a-decision-when-the-request-is-clear-an-rfc-whe.md @@ -1,11 +1,12 @@ --- id: DR-019 title: Agents draft a decision when the request is clear, an RFC when it is ambiguous, and a spec when behavior has many rules -status: draft +status: accepted created: 2026-09-30 updated: 2026-09-30 +authors: [emafriedrich] drafted_by: agent -approved_by: [] +approved_by: [emafriedrich] scope: [methodology] tags: [decision, agents] depends_on: [] diff --git a/knowledge/methodology/decisions/index.yaml b/knowledge/methodology/decisions/index.yaml index cb30b57..a6461d2 100644 --- a/knowledge/methodology/decisions/index.yaml +++ b/knowledge/methodology/decisions/index.yaml @@ -18,3 +18,4 @@ current: artifact-lifecycle: DR-016 agent-rules-ownership: DR-017 backfill: DR-018 + artifact-choice: DR-019 diff --git a/knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md b/knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md index 1fdb354..a64807d 100644 --- a/knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md +++ b/knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md @@ -1,11 +1,12 @@ --- id: KDE-RFC-014 title: Agents choose the artifact and never assume an answer -status: draft +status: accepted created: 2026-09-30 updated: 2026-09-30 +authors: [emafriedrich] drafted_by: agent -approved_by: [] +approved_by: [emafriedrich] motivated_by: The README rewrite (2026-09-30) promises adopters that the agent picks the artifact for them and never answers an open question on their behalf; AGENTS.md described what each artifact is for but gave the agent no rule for choosing one, or for stopping when a request is ambiguous scope: [methodology] tags: [rfc, agents] From f94fc52d5876548132a1afabddcf2fa3a41fbc10 Mon Sep 17 00:00:00 2001 From: Emanuel Friedrich <aemanuelfriedrich@gmail.com> Date: Wed, 30 Sep 2026 03:40:05 -0300 Subject: [PATCH 5/5] KDE-RFC-014: resolve open questions and record the outcome --- ...ents-choose-the-artifact-and-never-assume-an-answer.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md b/knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md index a64807d..a84a74c 100644 --- a/knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md +++ b/knowledge/methodology/rfcs/KDE-RFC-014-agents-choose-the-artifact-and-never-assume-an-answer.md @@ -49,9 +49,11 @@ Nothing here changes the lifecycle: agents still draft everything and promote no ## Open Questions -- Should the stop condition also apply during a backfill session (KDE-PROMPT-002), where the agent presents recovered rules with evidence and the human disposes of each one? The session protocol already has the human in the loop per rule; the proposal is that it does not change, and that a recovered rule whose reading is unclear is presented as a question rather than a rule. -- "More than a couple of rules" is deliberately a judgment call, as it is in DR-016. Should the subsection give a number? The proposal is no: the creation criteria in HANDBOOK.md already list the signals (interactions, invariants, edge cases, acceptance criteria), and a number invites gaming. +Resolved at review (2026-09-30): + +- The stop condition does not change the backfill session (KDE-PROMPT-002): the human already disposes of each recovered rule on sight. A recovered rule whose reading is unclear is presented as a question, not as a rule. +- "More than a couple of rules" stays a judgment call, as in DR-016. No number: the creation criteria in HANDBOOK.md already list the signals (interactions, invariants, edge cases, acceptance criteria), and a threshold invites gaming. ## Outcome -<!-- Fill after review: accepted, rejected, or deferred, with links to resulting decisions/specs. --> +Accepted on 2026-09-30. Decision recorded in DR-019. Shipped: the "Choosing the artifact" subsection in AGENTS.md and in the framework-owned section `install.sh` writes, the installer test that asserts the section reaches adopters verbatim, and the rule in the coding-agent context template (KDE-PROMPT-001).