-
-
-
+ Decision authority for coding agents, owned by Git.
+ Keep constraints, rejected alternatives, and warnings in Git — then deliver
+ only what is still in force, so an agent is not handed a decision the
+ repository already reversed.
- English · 한국어 · 日本語 · 简体中文
+ No hosted memory. The repository owns the record.
-**Your coding agent keeps re-proposing things your team already rejected.**
-CommitLore keeps those decisions in Git and hands the agent the ones still in
-force, before it edits the file.
+
-CommitLore has no hosted service; it keeps its records in Git. Once its MCP
-server or hook returns context, the host handles that context under its own
-policy; CommitLore does not control that data flow.
+
+ Install once. Then initialise each repository where you want it to work.
+
-**Two halves, and only one of them is automatic.** *Delivery* — handing the
-agent the decisions that still apply, before it edits a path — happens on its
-own once installed. *Capture* — writing a new decision down — is something the
-agent does when a change carries a reason the diff cannot show; an ordinary
-`git commit` cannot start it, because a hook has the diff and a capture needs
-the session. [What happens automatically](#what-happens-automatically-and-what-does-not)
-says exactly which hosts do which.
+```bash
+curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.0/install.sh | sh -s v1.2.0
+```
-Contents
-
-- [Install](#install)
-- [What the agent receives](#see-it-work)
-- [What happens automatically](#what-happens-automatically-and-what-does-not)
-- [When this will not help you](#when-this-will-not-help-you)
-- [The problem, in one example](#the-code-survived-the-decision-didnt)
-- [What it is, in full](#what-it-is-in-full)
-- [A path query](#see-a-path-query)
-- [This repository as its own demo](#the-repository-is-the-demo)
-- [Path scope vs. retrieval](#retrieval-can-find-records-path-scope-keeps-reversed-decisions-out)
-- [How it works](#how-it-works)
-- [A field report from another repository](#what-it-looks-like-on-a-real-repository)
-- [What makes it different](#what-makes-it-different)
-- [Where it pays off](#where-it-pays-off)
-- [How records get created](#how-records-get-created)
-- [A complete record](#a-complete-record)
-- [What the repository proves](#what-the-repository-proves)
-- [Evidence](#evidence-a-narrower-product-claim)
-- [Uninstall](#uninstall) · [Documentation](#documentation) · [Contributing](#contributing)
+Prefer to read the installer first?
+
+```bash
+curl -fsSLO https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.0/install.sh
+sh install.sh v1.2.0
+
+# Or skip the script: the checkout it makes is one you can make yourself.
+git clone --depth 1 --branch v1.2.0 https://github.com/MongLong0214/commitlore
+node commitlore/dist/commitlore.mjs --version
+```
+
+It installs a pinned source checkout and a wrapper that runs
+`node /dist/commitlore.mjs` — no compiled download, no build step.
-## Install
+
+
+
-Install once. Install the host integration and initialise the repository where
-you want it to work.
+---
-**Claude Code** — one plugin registers the MCP server, the pre-edit context hook and the skills:
+> **The code survives. The judgment doesn't.**
-```
-/plugin marketplace add MongLong0214/commitlore
-/plugin install commitlore@commitlore
-```
+An agent proposes an approach. Your team rejects it because of a non-obvious
+constraint. The final code preserves the outcome, but usually not why the
+alternative was rejected. A later agent sees only the code and proposes the
+same idea again.
-That is the whole plugin: the MCP server, the pre-edit hook and the skills. It puts no `commitlore` on `PATH`, so the `commitlore …` commands below come from `install.sh` / `install.ps1` and need that install as well.
+CommitLore keeps that judgment beside the code.
-**Codex** — install the native plugin with one command:
+## What CommitLore does
-```bash
-commitlore plugin install-codex
-```
+| | Behavior | Product path |
+|---|---|---|
+| **Captures** | Preserves constraints, rejected alternatives, and warnings that a diff cannot show. Candidates are checked against the session transcript and the staged diff. | `commitlore capture` |
+| **Preserves** | Stores accepted records in Git trailers or notes instead of a hosted memory database. | commit hooks · `refs/notes/commitlore` |
+| **Tracks lifecycle** | Keeps active, superseded, and expired decisions distinct. | `commitlore stale` |
+| **Scopes** | Selects decisions for the path an agent is about to edit. | `commitlore context` |
+| **Grades trust** | Delivers records as directives, claims, or withheld content. | default / signed mode |
+| **Delivers** | Gives supported agents current context before an edit. | plugin hook · MCP |
-It registers the marketplace and plugin through Codex's own CLI, never by
-editing its configuration or cache, and the install script below runs the same
-command when it finds Codex. Start a new Codex session afterwards: the plugin's
-skill and MCP server are loaded at session start, not on install. The CLI below
-provides the repository commands.
+Most commits should carry no record. CommitLore is for judgment the code cannot
+preserve, not for narrating every change.
-Prerequisites for either path: Node.js 22.23.2+ and Git. The script checks both before it writes anything.
+
+## 60 seconds to decision-aware agents
-**Any other coding agent** — install the CLI:
+### 1. Install the CLI
+
+macOS and Linux:
```bash
curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.0/install.sh | sh -s v1.2.0
```
-**Windows** — the same install, in PowerShell:
+Windows:
```powershell
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.0/install.ps1))) v1.2.0
```
-Host wiring on Windows requires **v1.1.1 or later**. Before it, detection could not see a `.cmd` shim and the installer could not run one, so a Windows install placed the CLI and wired nothing — reported as `ok: false`, never as success. Verified on a real machine at 1.1.1 for Codex, Gemini CLI and Hermes.
+Requires Node.js 22.23.2+ and Git. The script checks both before it writes anything.
-**Hermes** — after installing the CLI, configure its host integration:
+### 2. Connect your agent
-```bash
-commitlore hermes install
+Claude Code:
+
+```text
+/plugin marketplace add MongLong0214/commitlore
+/plugin install commitlore@commitlore
```
-Which hosts are supported, and what each install path requires: [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md).
+Codex:
-**Give your next agent the judgment your last one earned.**
+```bash
+commitlore plugin install-codex
+```
-### Then, in each repository
+The plugin puts no `commitlore` on `PATH`, so the commands below need the CLI
+install as well. The installers also detect and wire supported MCP hosts where
+they can do so safely; the exact matrix is below.
-Then run `commitlore init` in each repository where you want validation hooks,
-a local index, and the repository-owned agent procedure. The installer detects
-supported coding agents and registers the local MCP server where it can do so
-safely.
+### 3. Initialize a repository
```bash
cd your-repository
@@ -119,287 +147,156 @@ commitlore init
commitlore context .
```
-### Upgrading
-
-Re-run the install command. It updates the CLI and the agent registrations, and
-it cannot reach two things:
-
-- **Hooks already installed in a repository.** One installed before v1.0.2
- records the release it came from, so it keeps validating commits with that
- build. The installer has no way to know which repositories have hooks.
-- **Sessions already running.** A host loads its runtime once and keeps it.
-
-So after upgrading:
-
-```bash
-commitlore doctor # names any hook still pinned, and any stale session
-commitlore hooks install # in each repository doctor names
-```
-
-Neither is a defect in the release; both are state a release cannot reach.
-
-A hook is a file written at install time, so there are three generations.
-Installed **before v1.0.2**, it names one release directly. Installed
-**v1.0.2 through v1.1.2**, it follows `current` but its older containment stub
-does not recognise an ordinary upgrade — under the `PATH` git gives a hook it
-refuses the commit. Installed **v1.1.3 or later**, ordinary upgrades are followed
-automatically. The first two need `commitlore hooks install` once, in each
-repository, and `commitlore doctor` names which.
-
-After that:
+Start a new agent session after installing or updating a plugin: a running
+session keeps the runtime it loaded.
-- Commit normally. Most commits carry no record.
-- If a record is present, the commit-msg hook validates it; it never creates one.
-- Delivery and capture are different layers; the next section says exactly
- which hosts have each one.
+Then work and commit normally. On supported skill integrations, CommitLore is
+considered during ordinary commit requests and stays silent when there is
+nothing worth preserving. You do not need to name CommitLore on every commit.
-Keep working through your coding agent. When a change contains decision context the diff cannot preserve, ask the agent to include a CommitLore record in the commit.
+Want accepted records to stage without a per-record prompt? The repository can
+opt in once with `commitlore auto on`. That policy is repository-owned and
+applies to the team, so it is not silently enabled by this page.
-
-Prefer to inspect or pin the installation?
-
-The one-liner is for convenience. For a reviewed or pinned install, download and inspect `install.sh` first, or clone the repository. The script installs a pinned source checkout and a thin wrapper that runs `node /dist/commitlore.mjs` — it downloads no compiled artifact and runs no build step, so what it puts on your machine is the source you can read.
-
-```bash
-# Pin and inspect the installer before executing it.
-curl -fsSLO https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.0/install.sh
-sh install.sh v1.2.0
-
-# Or skip the script entirely: the checkout it makes is one you can make yourself.
-git clone --depth 1 --branch v1.2.0 https://github.com/MongLong0214/commitlore
-node commitlore/dist/commitlore.mjs --version
-```
-
-
+
+## What the agent receives
-## See it work
+Before editing `src/pricing.ts`:
-Before editing `src/pricing.ts`, the agent receives this payload — the record,
-not a description of one:
-
-```
+```text
commitlore: active records for src/pricing.ts
Limit
- [claim] r-price01 87e36511 calculatePrice owns final checkout pricing only
+ [claim] r-price01 calculatePrice owns final checkout pricing only
Ruled-out
- [claim] r-price01 87e36511 Reuse checkout pricing for admin quotes | eligibility
- and rounding semantics differ between the two flows
+ [claim] r-price01 Reuse it for admin quotes |
+ eligibility and rounding semantics differ
```
-`[claim]` matters: its author string did not match one this repository
-configured for directives, so the agent is told to weigh it as information, not
-obey it as an order. In the default author-string mode, `[directive]` means the
-commit's author header matched a string this repository configured — not that a
-trusted author recorded it, and not that the identity is proven. Anyone who can
-write a commit can set that header. A repository can opt into Git's authenticated
-boundary with `git config --local commitlore.requireSignedDirective true`. In
-that mode, `[directive]` requires Git to verify the signature against this
-verifier's trust store **and** the exact signing-key fingerprint Git reports
-(`%GF`) must be listed in the repository-local allowlist:
+`[claim]` means "weigh this as information." A repository can opt into the
+stronger signed-authority mode. Delivery gives the agent context; it does not
+block the edit.
-```bash
-git config --local --add commitlore.trustedSigner
-```
-
-An absent, empty, or unreadable signer allowlist authorizes nobody, so every
-record remains `[claim]`; it never means every valid signer is authorized.
-Signature mode remains opt-in, so existing author-string policy is unchanged
-until a repository enables it. A signature proves that a key accepted by this
-verifier signed the commit. It does not prove that the key has authority for
-this repository, which is what the local allowlist supplies, nor does it prove
-the record's truth. Delivery gives the agent context; it does not block the edit.
+[Security model →](SECURITY.md)
-## What happens automatically — and what does not
+## Why Git?
-**Delivery** means the record reaches the agent before it edits a path.
-**Capture** means a decision can enter the verified commit-time flow. They are
-separate layers:
+**The repository should own the judgment behind its code.**
-| Host | Delivery | Capture |
-|---|---|---|
-| Claude Code | **Yes — automatic through the plugin.** | **Yes — through the plugin.** |
-| Codex | **Yes — automatic through the plugin.** | **Yes — through the plugin.** |
-| Hermes | **Yes — `commitlore hermes install`.** | **Yes — `commitlore hermes install`.** |
-| Gemini CLI, Cursor, Windsurf, opencode | **Yes — both installers wire the MCP server**, through one shared step rather than each on its own. | **Procedure, not automatic.** The server states the prepare → verify → stage procedure in its `instructions` on every connection. The host may or may not act on it. |
-| Any other `AGENTS.md`-convention host | **Procedure, not automatic.** `commitlore init --agents-md` writes it into the repository. | **Procedure, not automatic.** Same file, same caveat. |
-
-“Yes” in the Capture column means the workflow is installed and available — the
-prepare → verify → stage path exists for that host. It does not mean every
-eligible commit reaches a terminal assessment on its own. **That stronger
-property, deterministic autocapture, is not certified on any host yet**, so
-treat capture as available rather than guaranteed. Most commits should carry no
-record in any case.
-
-The first three rows install a skill that drives capture; the fourth receives
-the same procedure over MCP, which is what a host that loads no skills has to
-work from — verified with the plugin disabled, and it captured. Whether a given
-host surfaces those instructions to its model is the host's choice, and nothing
-here detects it. A host still has to start capture, and the candidate must pass
-verification before the commit hook attaches it. The commit-msg hook validates
-a record when present; it never invents one.
-
-## When this will not help you
-
-Read this before installing, not after.
-
-- **The measurement is of the weaker tier.** Every record in the 1,160-run study
- rendered `[claim]`, which tells the agent to weigh the record rather than obey
- it. The `[directive]` tier became reachable only afterwards, and nothing here
- measured it — the study's own verdict says this number "does not transfer to
- the stronger one". Whether a directive does better, worse, or the same is
- unmeasured in both directions.
-- **One model, one harness, ten constructed fixtures.** The oracle reads the
- final implementation state, so it shows that agents which received records
- re-proposed less often. It does not show that any of them read anything.
-- **Guard is an experimental advisory**, not a safety net: precision 44.8%
- (95% Wilson CI 32.7%–57.5%), recall 22.0% on the 417-decision corpus
- ([ADR-0020](docs/adr/ADR-0020-guard-is-an-experimental-advisory.md)). An empty
- guard result does not mean a proposal avoids every ruled-out alternative — at
- 22% recall, a miss is the common case.
-- **Signature verification is opt-in, not key distribution.** Default mode
- matches a forgeable author string. Setting
- `commitlore.requireSignedDirective=true` also requires Git's verified
- signature status from this verifier's trust store and an exact `%GF`
- fingerprint in repository-local `commitlore.trustedSigner`; a missing,
- empty, or unreadable allowlist authorizes nobody. That verifies neither a
- person's authority nor the record's truth. Repository-wide coverage, symbol anchors,
- and an interactive record builder remain open:
- [#32](https://github.com/MongLong0214/commitlore/issues/32),
- [#33](https://github.com/MongLong0214/commitlore/issues/33),
- [#34](https://github.com/MongLong0214/commitlore/issues/34).
-- **M4 did not test a guard effect**: its rows carry no `guard_exposure`, so
- treatment exposure there is unverifiable
- ([#122](https://github.com/MongLong0214/commitlore/issues/122)).
+CommitLore stores records in ordinary Git trailers and notes, so they branch,
+merge, clone, review, and survive provider changes with the code they explain.
-The full method, the exclusions and the per-arm truncation split are in
-[bench/VERDICT-M5.md](bench/VERDICT-M5.md) and
-[what it does not show](docs/evidence.md). Delivery methodology and the
-retrieval evidence are in [bench/DECISION-DELIVERY.md](bench/DECISION-DELIVERY.md).
+SQLite is only a rebuildable index. Delete it and Git still holds the record.
-## The code survived. The decision didn't.
+## Finding an old decision is not enough
-*Stop re-reviewing the same bad idea.*
+A general memory or retrieval system asks:
-**Without CommitLore.** A new session sees two functions with similar inputs and
-reuses one.
+> Which old text looks related?
-```ts
-calculatePrice(input, { isAdminPreview: true, skipCoupon: true });
-```
+CommitLore asks:
-The team now has another flag, another wrapper, and another compatibility branch
-protecting a use case the function was never meant to own. The reviewer writes
-"we already rejected this" for the second time.
+> Which recorded decisions still apply to this path now?
-**With CommitLore.** Before editing, the agent receives the active record shown
-above, rather than an instruction reconstructed from a review comment.
+A superseded decision can be highly relevant and still be wrong as current
+guidance. Relevance and authority are different questions.
-The module boundary is in front of the agent before it proposes the change,
-rather than in a review comment after.
+## How it works
-**Whether it acts on that is now measured.** Across 1,160 registered runs, an
-agent handed the repository's active records re-proposed a ruled-out approach in
-**2.8%** of them (16/580). Without them: **18.8%** (109/579).
+
+
+
-| arm | re-proposed a ruled-out approach |
-|---|---:|
-| the agent alone | **18.8%** (109/579) |
-| **with CommitLore** | **2.8%** (16/580) |
+1. **Capture** — an agent drafts only decision context the diff cannot show.
+2. **Verify** — CommitLore checks the draft against the session and staged diff.
+3. **Preserve** — the accepted record lives in Git with identity and lifecycle.
+4. **Deliver** — before a later edit, only active records for that path are returned.
-**The threshold was registered before the run**, and the preregistration
-predicted a *smaller* effect than it got — that prediction, with its stated
-probabilities, is in
-[bench/PREREGISTRATION-M5.md](bench/PREREGISTRATION-M5.md) §A.2, and it was
-wrong. The significance test, the interval and the registered threshold are in
-[bench/VERDICT-M5.md](bench/VERDICT-M5.md) rather than here: a statistic
-retyped into prose drifts from the log that produced it, and this repository
-gates against exactly that (`scripts/check-readme-numbers.mjs`).
+Most commits carry no record. The commit hook validates a record when one is
+present; it does not invent one.
-## What it is, in full
+An existing hook is not overwritten. `commitlore init` honours `core.hooksPath`,
+moves any hook already installed to `.commitlore-chained`, and calls it
+first; `commitlore hooks uninstall` puts it back.
-**The Git-native decision layer for coding agents.**
+
+## What happens automatically
-Every fresh agent inherits the implementation. None of them inherit the
-constraints, the alternatives your team rejected, the warnings, or the
-verification gaps — those do not travel with the code unless something carries
-them.
+| Host | Pre-edit delivery | Verified capture workflow | Deterministic every-commit capture |
+|---|---|---|---|
+| Claude Code | Automatic through the plugin | Available through the plugin skill | **Not certified** |
+| Codex | Automatic through the plugin | Available through the plugin skill | **Not certified** |
+| Hermes | Available after `commitlore hermes install` | Available after host install | **Not certified** |
+| Gemini CLI, Cursor, Windsurf, opencode | MCP delivery where the host uses the registration | Procedure exposed over MCP | No |
+| `AGENTS.md` hosts | Procedure only | Procedure only | No |
-CommitLore preserves that engineering judgment in Git, and surfaces only the
-decisions still in force before the next edit. A decision that was later
-superseded or expired does not reach the agent as if it still stood.
+"Available" means the prepare → verify → stage workflow exists. It does not mean
+every eligible commit is assessed automatically.
-**Repository-owned · Lifecycle-aware · Quote-checked · Agent-independent**
+Users on supported skill hosts do not need to say "record this in CommitLore" on
+every commit. The remaining limitation is host initiation, not a required
+per-record user command.
-Claude Code · Codex · Cursor · Gemini CLI · OpenCode · Windsurf
-No hosted memory service. No vendor-specific chat history. Just reviewable
-decision context, owned by the repository. Commit trailers travel with their
-commits; notes-backed records need the notes fetch configured after a clone.
+## A field report, not a measurement
-## See a path query
+One run, on an unrelated repository, by someone installing v1.2.0 for the first
+time. Nothing here was measured and none of it is in the evidence logs. It is on
+this page because the paragraph above asserts a loop that no table here covers.
-**A fresh agent. Zero chat history. It is still handed why the obvious fix was rejected.** Query a path before changing it:
+They asked an agent to fix a rounding bug, mentioned in passing that a decimal
+library had already been considered and dropped, and ended with "commit it".
+CommitLore was never named. Part of what the commit carried:
-```bash
-commitlore context install.sh
```
-
-The output includes the active record that ruled out publishing a `-musl` target as the fix for the installer defect, including its reason. The hook returns context; it does not claim to block the edit.
-
-```console
-context for install.sh as of — 0 limits, 1 ruled-out, 1 warnings, 2 other in 1 record (no index, 1 commit record(s) scanned)
-
-ruled-out
- r-instci99a [claim] Publish a -musl release target | a release.yml/build-matrix change, not an install.sh or CI-verification fix
-
-warnings
- r-instci99a [claim] Revisit this wording if a musl target ships
+Ruled-out: adopting a decimal library such as Decimal.js | the backend is a
+ number contract, so it is meaningless
+Warn: do not revert the test file to console.assert: it exits 0 even on
+ failure, so CI passes silently
+Provenance: drafted
```
-Reproducing that exact `PreToolUse` hook path, and every other command: [docs/cli.md](docs/cli.md).
-
-## The repository is the demo
-
-A tool that claims to stop agents re-deciding settled questions should be able
-to show what it caught in itself. This one keeps that list in public, including
-the entries where the thing that turned out to be false was something this
-project had already published:
+The `Warn` was not dictated to the agent. It hit the trap while working and left
+it for whoever came next. `Provenance: drafted` records that no human read the
+record, which grades it `claim` — delivered as a report to weigh, not an order.
-- **No install could produce the trust tier the README's claims rested on.** Records reach an agent graded `directive` or `claim`. It turned out no installed surface configured a directive author string, so grading failed closed to `claim` for everyone — while the injected legend advertised the tier nobody could reach. Both prior benchmarks had measured `claim`-graded delivery ([#415](https://github.com/MongLong0214/commitlore/issues/415)).
-- **The registered benchmark analysis would have read four different experiments at once** — and because its stopping rule was a row count, the contamination would have made the study *pass* its own completeness gate ([#441](https://github.com/MongLong0214/commitlore/issues/441)).
-- **The result-schema gate was not run by anything**, so the schema drifted five fields behind the runner and nobody noticed for two days ([#392](https://github.com/MongLong0214/commitlore/issues/392)).
-- **A shipped pre-push hook hung every `git push`** — 1,240 hook invocations in 40 seconds — because the function had been tested eleven times and the hook path zero times ([#422](https://github.com/MongLong0214/commitlore/issues/422)).
+A later session with no shared history was asked to adopt the decimal library
+after all. It did not, and named the record as its reason. It also read the
+grade: a `claim` is not an instruction, so it checked the stated reason against
+the code before agreeing with it.
-Every one of those is a `Ruled-out:`, `Warn:` or `Limit:` line in a commit
-trailer, validated by the hook this project asks you to install, and readable
-with the same `commitlore context` you would run anywhere else.
+## Unlike memory storage
-**The full list, with what each one cost: [docs/SELF-AUDIT.md](docs/SELF-AUDIT.md).**
-
-## Retrieval can find records. Path scope keeps reversed decisions out.
-
-Before an agent's first edit, how much of a repository's still-active decision set actually reaches it? On this repository, at the 800-token budget the hook ships with:
-
-| route | budget | active decisions delivered | reversed ones delivered | tokens |
-|---|---:|---:|---:|---:|
-| the code alone | — | 0.0% | 0 | 0 |
-| `git log` for the path | 800 | 42.0% | 7 | 673,134 |
-| **CommitLore path scope** | **800** | **81.7%** | **0** | **511,412** |
-| CommitLore, cap removed | none | 92.3% | 0 | 741,429 |
-
-With the cap removed, path scope recovers exactly what a whole-repository dump recovers — 2,047 of 2,217 — for a fraction of its 92,175,612 tokens and none of its 7,322 reversed records. The scope costs nothing. The cap costs 10.6 points. The remaining 170 are records the trust grader withholds.
+| | General memory / RAG | **CommitLore** |
+|---|---|---|
+| Primary question | What old text is related? | Which decisions still apply here now? |
+| Authority | Memory store or provider | Git |
+| Scope | Semantic similarity | Repository paths |
+| Lifecycle | Often append-first | Active · superseded · expired |
+| Trust | Retrieved text | Directive · claim · blocked |
+| Capture | Transcript or note storage | Evidence-checked decision record |
+| Portability | Backend-dependent | Ordinary Git |
-**This measures delivery, not effect.** No agent ran, so it bounds what one could recover, not what one does — and a retrieval number can climb while the outcome it is meant to predict falls. SWE-bench measured BM25 recall rising from 29.58 to 51.06 across its context budgets and reported that "even when increasing the maximum context size for BM25 would increase recall with respect to the oracle files, performance drops … as models are simply ineffective at localizing problematic code" ([arXiv:2310.06770](https://arxiv.org/abs/2310.06770)). One corpus, one repository. Seven superseded records and no expired ones, so zero-reversed-delivered says nothing yet about expiry. Method and full tables: [bench/DECISION-DELIVERY.md](bench/DECISION-DELIVERY.md).
+CommitLore is intentionally narrower. It is not a general user-memory system,
+conversation archive, or vector database replacement.
-**The `git log` baseline is not an artifact of measuring ourselves.** The same measurement on four repositories this project did not write — Django, SymPy, scikit-learn and Requests, at pinned commits — puts the share of a path's history that survives an 800-token cut between **37.4% and 55.6%**. The 42.0% above sits inside that band. Losing something close to half a file's history to a fixed budget is what `git log` does on large, long-lived repositories generally, not something peculiar to this one. What did *not* transfer is the mechanism: our paths carry a median of one commit at 687 tokens where Django's carry eight at 213, so long commit messages make the ordinary-Git baseline worse at a fixed budget — a cost of this project's own practice. [bench/EXTERNAL-CORPUS.md](bench/EXTERNAL-CORPUS.md) also reports a delivery figure on those repositories; read §9.0 and §9.5 first, because the records there were generated from revert commits by a program and the headline number is one the attachment predicate forces rather than a retrieval result.
+
+## Evidence
-Missing a record costs the model context. Handing it a decision that was already reversed costs it correctness. In this [retrieval measurement](bench/retrieval/result.md), at every size from 0 to 10,000 distractors, BM25, embedding top-k, hybrid RRF, and embedding with a path filter each returned one superseded record. CommitLore path scope with lifecycle returned zero stale records and both current records (2/2).
+| Question | Measured result | Boundary |
+|---|---|---|
+| Did claim-grade context change re-proposal in the registered study? | **2.8%** (16/580) with CommitLore vs **18.8%** (109/579) without | one model, one harness, constructed tasks |
+| Did lifecycle filtering deliver retired records in the measured active projection? | **0 retired records** | superseded records were present; expiry was not |
+| Does indexed lookup scale? | **496 ms p50 at 100k commits** | the no-index fallback is much slower |
-Recall is the supporting result: retrieval finds broadly the same records either way, but only one route knows which are still current. The advantage appears when decisions have been reversed—the case this product exists for.
+Index build time follows the number of *records*, not the number of commits: the
+expensive pass runs once per record, so a long history that has recorded little
+builds faster than a short one dense with records.
-The separate #167 exposure run still matters: only 2 of 10,002 records reached the model.
+Path scope is what keeps a large history from reaching the model. On the #167
+corpus, only 2 of 10,002 records did:
| route | model-visible records | relevant records | model-visible tokens |
|---|---:|---:|---:|
@@ -407,154 +304,126 @@ The separate #167 exposure run still matters: only 2 of 10,002 records reached t
| top-k lexical | 2 | 1/2 | 190 |
| CommitLore path scope | 2 | 2/2 | 335 |
-This measures exposure and recall at a fixed two-record output budget—not token cost, billed cost, accuracy, or agent behaviour. It is one corpus, one query, and one pinned embedding model. Where recall ties, and what else has and has not been measured: [docs/evidence.md](docs/evidence.md).
-
-## How it works
-
-1. **Capture** — the agent drafts only the decision context a diff cannot show.
-2. **Verify** — CommitLore checks that draft against the session and the staged diff.
-3. **Preserve** — the verified record lives in Git, with identity and a lifecycle.
-4. **Deliver** — before editing a path, the next agent receives only the decisions still in force.
-
-## What it looks like on a real repository
-
-From a field report on a ~768-commit Swift MCP server, one day after installing.
-Naming one file path surfaced a merged pull request the engineer did not know
-existed, and it changed what the surviving code meant.
-
-> **I did not know that commit existed.** It is a merged PR from two weeks
-> earlier that had already removed eight of these sites and replaced each with
-> an accessibility-native equivalent, every one fail-closed and live-verified.
->
-> None of this was in any chat history. It was in the repository, and I got it
-> by naming a file path.
-
-The alternative was reading two weeks of merged pull requests to find it. That is
-not something an agent does spontaneously, and not something a person does before
-every edit. Adoption cost, from the same report: one command, and 7.4 seconds to
-index 768 commits. Nothing touched history or the working tree. The console
-output and the full report are in [docs/evidence.md](docs/evidence.md).
-
-That was a 768-commit repository. At **100,000 commits an indexed `context` query
-answers in 496 ms at p50**, and the hooks behind it cost 185.85 ms p50 for
-`commit-msg` and 102.40 ms p50 for the injection hook. Those are the numbers that
-decide whether this stays installed on a large repository, and they are measured
-rather than asserted. The same run carries the figure that looks bad: without the
-index, that query at 100,000 commits takes 86,673 ms. The index is not an
-optimisation on top of a working query — it is what makes the query possible at
-that size, which is why `init` builds one and `doctor` checks it.
-
-**Three properties no hosted chat-history product can offer**, and the reason the
-authority is Git rather than a service:
-
-- **Reviewable.** A decision arrives as a commit trailer in a pull request, where
- it can be argued with before it becomes authority.
-- **Owned by the repository.** No account, no vendor, nothing to lose access to.
-- **Commit trailers travel with a clone.** A record in
- `refs/notes/commitlore` does not arrive in an ordinary clone: Git does not
- fetch `refs/notes/*` by default. `commitlore init` configures that mirror;
- [the sharing documentation](docs/cli.md#sharing-records-with-a-team) explains
- the remaining fetch and push boundary.
-
-## What makes it different
-
-| Tool | What it remembers |
-|---|---|
-| `CLAUDE.md` / `AGENTS.md` | how the agent should work |
-| ADRs | large architecture decisions, as documents |
-| Chat memory / RAG | related text from the past |
-| [Lore](https://arxiv.org/abs/2603.15566) | the same idea, published first — decision records in git trailers |
-| **CommitLore** | **which decisions still apply to this code path** |
-
-Similarity search can find a related decision. CommitLore also knows whether that
-decision is still active, superseded, or expired — and shows only the first.
-
-**On that third row.** [Lore](https://arxiv.org/abs/2603.15566) (March 2026)
-proposed decision records in native git trailers four months before this
-repository existed, with a vocabulary that maps almost one-to-one onto this one.
-The protocol idea is not novel here and saying otherwise would not survive anyone
-reading the paper. What Lore has no counterpart for is the lifecycle —
-`Supersedes:` and `Expires:`, and the filtering that makes the row above true —
-or the trust grading; and it states that it "outlines an empirical validation
-path" rather than running one. That validation, including the parts that failed,
-is what this project has that the paper does not
-([ADR-0029](docs/adr/ADR-0029-lore-is-prior-art-and-this-is-what-differs.md)).
-
-The authority is ordinary commit trailers and `refs/notes/commitlore`. Indexes and reports are derived and rebuildable from those Git records.
-
-## Where it pays off
-
-**Protect a module boundary.** *"`calculatePrice` owns final checkout pricing only. Do not reuse it for admin previews."*
-
-**Preserve a rejected workaround.** *"Raising the timeout hides the connection leak. Fix the cleanup path instead."*
-
-**Mark temporary compatibility code.** *"This caller is temporary and is not part of the supported contract."*
-
-**Carry a verification gap.** *"Single-user behaviour was tested. Concurrent refresh remains unverified."*
-
-Each is a sentence a diff cannot carry and a reviewer would otherwise have to say twice.
+That measures exposure and recall at a fixed two-record budget — not token cost,
+billed cost, accuracy, or agent behaviour. One corpus, one query, one pinned
+embedding model.
+
+The agent study does not establish a universal model effect. Delivery is not
+proof that a model read or followed a record.
+
+[Methods, full tables, exclusions, and negative results →](docs/evidence.md)
+
+
+## Limits, trust and privacy
+
+- **Capture is assisted, not deterministic.** Supported skills consider ordinary
+ commit requests, but no host is certified to assess every eligible commit.
+- **Default directive mode is not authentication.** It matches the commit
+ author header, and anyone who can write a commit can set that header — so a
+ `[directive]` in default mode is policy metadata, not proof of identity.
+ Signature mode additionally requires Git's own verified status and a match in
+ the repository-local `commitlore.trustedSigner` allowlist; an absent, empty, or unreadable signer allowlist authorizes nobody, so the mode fails closed.
+- **Guard is an experimental advisory**, not a safety net: precision 44.8% (95% Wilson CI 32.7%–57.5%), recall 22.0% on the 417-decision corpus. An empty guard result is not a safety verdict.
+- **Delivery spends tokens on every matching tool call.** The pre-edit hook
+ fires on `Read` as well as `Edit`, `Write`, `MultiEdit` and `NotebookEdit`, so
+ it runs far more often than an editing agent commits. Each fire spends up to
+ the payload budget — 800 tokens by default, changed with `--budget`. A
+ repository with no records spends nothing, which means this is a cost that
+ arrives with adoption rather than with installation.
+- **An answer may be partial.** Coverage is disclosed; absence from a partial
+ result is not proof that no record exists. Repository-wide coverage, symbol anchors,
+ and an interactive record builder remain open:
+ [#32](https://github.com/MongLong0214/commitlore/issues/32),
+ [#33](https://github.com/MongLong0214/commitlore/issues/33).
+- **Commit trailers travel with a clone; notes do not.** Git does not fetch
+ `refs/notes/*` by default, so a record in `refs/notes/commitlore` is absent
+ from an ordinary clone until `commitlore init` configures that mirror.
+- **There is no hosted backend.** But once the server or hook returns context,
+ the host handles that context under its own policy; CommitLore does not
+ control that data flow.
+
+[Security](SECURITY.md) ·
+[Compatibility](docs/COMPATIBILITY.md) ·
+[Evidence](docs/evidence.md)
-## How records get created
+
+Security and trust model
-You do not hand-write a trailer for every commit. Most commits should carry no record at all. Add one only for a decision the diff cannot recover: an external constraint, a rejected alternative, a warning, or a verification gap.
+Records are untrusted until graded. Default author matching is policy metadata,
+not authentication. Signed directive mode requires Git verification and a
+repository-local signer allowlist; an absent or unreadable allowlist authorizes
+nobody. Injection-shaped payload is withheld from model-readable routes.
-Ask the agent to commit normally and preserve only the decision context the diff cannot explain:
+[Full security model →](SECURITY.md)
-> Commit this change. Add a CommitLore record only if the diff cannot recover an important constraint, rejected alternative, warning, or verification gap.
+
-The agent instructions live in `skills/commitlore-commits/`, and the commit-msg hook validates any record the agent adds — it never invents or silently adds one. The `harvest` route, the `capture` transaction, and the escape hatch of writing trailers by hand are all in [docs/capture.md](docs/capture.md).
+
+Installation, upgrades, and old hook generations
-## The record protocol
+The CLI installer cannot rewrite hooks inside repositories it does not know
+about, and running host sessions retain the runtime they loaded. `commitlore
+doctor` names both states and their repair, and `commitlore upgrade` reports
+whether a newer release exists.
-A record is an ordinary set of Git commit trailers, and a small one is usually enough:
+[Installation and upgrades →](docs/install.md)
-```text
-Fix expired-token refresh
+
-Ruled-out: Extend token TTL to 24h | security policy violation
-Warn: Do not narrow the 4xx handler without verifying upstream behavior
-```
+
+Protocol and Git storage
-The worked example that uses the whole vocabulary, the table of every trailer key, and how to read records with plain Git are in [docs/protocol.md](docs/protocol.md). The normative definitions are in [SPEC §3](spec/SPEC.md).
+Records are ordinary Git trailers or notes. Protocol 2.0 defines lifecycle,
+trust grades, validation, and compatibility.
-## What the repository proves
+[Human guide →](docs/protocol.md) ·
+[Normative specification →](spec/SPEC.md)
-- Decision history survives rebase, remote transfer, and path renames in the tested Git workflows. Squash-merge discards the trailer block, as any ordinary trailer would be: `commitlore squash-preserve` or its GitHub Action carries the records across, and the tested workflows cover that route.
-- Routes share one grading core, so untrusted text is information rather than an instruction — but the index caches a signature status, and a signature status is not a property of the repository. The same record can therefore grade `directive` on one route and `claim` on another from a single installation, if the index was built where the signing keys were not reachable. Comparing a route against `--no-index` tells you whether you are reading a stale verdict ([#653](https://github.com/MongLong0214/commitlore/issues/653), [#631](https://github.com/MongLong0214/commitlore/issues/631), [#635](https://github.com/MongLong0214/commitlore/issues/635)).
-- Injection-like text in free-form trailers is withheld from model-readable routes.
-- A readable repository with no records is distinct from incomplete history or an unfetched notes mirror.
+
-These are product claims about Git-bound, human-verifiable decision history. They do not depend on a claim that CommitLore improves agent performance.
+
+Evidence and negative results
-## Evidence: a narrower product claim
+The repository publishes the methods, exclusions, unsuccessful measurements,
+and the cases where the original benchmark or diagnosis was wrong.
-112 experiments were recorded, but M4 recorded no per-run guard exposure. Whether the treatment was present is unverifiable, so it does not test, support, or refute the agent-behavior claim. The narrower product claim above rests on independently testable behavior; read the [M4 verdict](bench/VERDICT-M4.md) for the clean dataset and withdrawal.
+[Evidence →](docs/evidence.md) ·
+[Self-audit →](docs/SELF-AUDIT.md)
-What is measured — retrieval, exposure, latency and scaling, hook overhead — and what is not — break-even, and any effect on agent behaviour — is set out in [docs/evidence.md](docs/evidence.md).
+
+
-## Uninstall
+
+ Try it on a repository with history.
+ Tell us where path scope, lifecycle, capture, or installation breaks.
+
-Removes what `install.sh` or `install.ps1` wrote — the wrapper, the pinned
-checkout, and the MCP entry it added to each agent config. It removes nothing it
-did not write, and names what it leaves: the per-repository hooks, the agent
-hook, and the Claude Code plugin. `--dry-run` reports without changing anything.
-What removes each of those, and how to run from a source checkout instead:
-[docs/install.md](docs/install.md).
+
+
## Documentation
-- [docs/install.md](docs/install.md) — the install paths, what each one writes, and how to undo it
-- [docs/cli.md](docs/cli.md) — every command, with its flags
-- [docs/capture.md](docs/capture.md) — how a record gets written
-- [docs/protocol.md](docs/protocol.md) — the record format, and reading it with plain Git
-- [docs/evidence.md](docs/evidence.md) — what is measured, and what is not
-- [spec/SPEC.md](spec/SPEC.md) — the normative protocol
+- [Install, upgrade, and uninstall](docs/install.md)
+- [CLI reference](docs/cli.md)
+- [Capture workflow](docs/capture.md)
+- [Record protocol](docs/protocol.md)
+- [Security model](SECURITY.md)
+- [Evidence and limitations](docs/evidence.md)
+- [Production contract](docs/PRODUCTION-READINESS-SSOT.md)
+- [Documentation index](docs/README.md)
## Contributing
-Read the [spec](spec/SPEC.md), the [ADRs](docs/adr/), and [`CONTRIBUTING.md`](CONTRIBUTING.md). CommitLore is free forever and open source under the [MIT License](LICENSE).
+[CONTRIBUTING.md](CONTRIBUTING.md) covers the record protocol this repository
+holds itself to, the release gate, and how to reproduce the evidence.
+
+## License
+
+MIT — see [LICENSE](LICENSE).
diff --git a/assets/readme/commitlore-logo.svg b/assets/readme/commitlore-logo.svg
new file mode 100644
index 00000000..0a3dfb78
--- /dev/null
+++ b/assets/readme/commitlore-logo.svg
@@ -0,0 +1,52 @@
+
diff --git a/assets/readme/demo.gif b/assets/readme/demo.gif
new file mode 100644
index 00000000..71875897
Binary files /dev/null and b/assets/readme/demo.gif differ
diff --git a/assets/readme/demo.tape b/assets/readme/demo.tape
new file mode 100644
index 00000000..5bbf6832
--- /dev/null
+++ b/assets/readme/demo.tape
@@ -0,0 +1,44 @@
+# NOTE (2026-08-19): the committed GIF was not produced by this tape.
+# `vhs` needs `ttyd`, and on the machine that made it Homebrew refused every
+# ffmpeg-dependent formula because a third-party tap was registered and
+# untrusted -- granting that trust is a decision about somebody's machine, not
+# a build step. The GIF was rendered instead from the bytes `commitlore demo`
+# actually printed, wrapped the way a 65-column terminal wraps them, at the
+# font, palette and framerate this tape specifies. Nothing in it was authored
+# by hand.
+#
+# This tape is kept because it is the reproducible path: with `vhs` available,
+# `vhs assets/readme/demo.tape` regenerates the GIF from the live command, and
+# a recording whose source is thrown away cannot be checked against what it
+# claims to show.
+
+# Source for assets/readme/demo.gif — regenerate with: vhs assets/readme/demo.tape
+#
+# The GIF is committed and the tape is committed beside it. A recording whose
+# source is thrown away cannot be checked against the command it claims to
+# show, and this one shows a real `commitlore demo` run: no hand-authored
+# output, no fake agent chat, nothing the CLI cannot print.
+
+Output assets/readme/demo.gif
+
+Set Shell "bash"
+Set FontSize 26
+Set Width 1100
+Set Height 640
+Set Padding 26
+Set Framerate 12
+Set TypingSpeed 35ms
+Set Theme {
+ "background": "#0D1117",
+ "foreground": "#F0F6FC",
+ "black": "#161B22",
+ "brightBlack": "#8B949E",
+ "green": "#3FB950",
+ "brightGreen": "#3FB950"
+}
+
+Sleep 1s
+Type "commitlore demo"
+Sleep 400ms
+Enter
+Sleep 9s
diff --git a/assets/readme/hero.svg b/assets/readme/hero.svg
index cbcda82f..e097dcb0 100644
--- a/assets/readme/hero.svg
+++ b/assets/readme/hero.svg
@@ -1,87 +1,51 @@
diff --git a/dist/commands/demo.js b/dist/commands/demo.js
index 984b1708..79b5a8db 100644
--- a/dist/commands/demo.js
+++ b/dist/commands/demo.js
@@ -141,7 +141,12 @@ export const runDemo = async (opts = {}) => {
}
lines.push('');
lines.push(`Only the active decision (${expectedActiveRecordId}) is shown.`);
- lines.push('The superseded reuse decision is filtered out — the agent cannot revive it.');
+ // "the agent cannot revive it" was a claim the product does not make. What
+ // CommitLore does is decline to deliver a superseded record as current
+ // guidance; whether a model then proposes the same idea from its own
+ // reasoning is outside anything this tool controls. The demo is the first
+ // thing many people run, so it is the last place to overstate.
+ lines.push('The superseded reuse decision remains in Git, but is not delivered as current guidance.');
lines.push('');
const output = lines.join('\n');
return { exitCode: 0, output };
diff --git a/dist/commands/demo.js.map b/dist/commands/demo.js.map
index bd6d1a1f..734806ca 100644
--- a/dist/commands/demo.js.map
+++ b/dist/commands/demo.js.map
@@ -1 +1 @@
-{"version":3,"file":"demo.js","sourceRoot":"","sources":["../../src/commands/demo.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"demo.js","sourceRoot":"","sources":["../../src/commands/demo.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/dist/commitlore.mjs b/dist/commitlore.mjs
index dc1186d3..ed286657 100755
--- a/dist/commitlore.mjs
+++ b/dist/commitlore.mjs
@@ -24238,7 +24238,7 @@ var runDemo = async (opts = {}) => {
}
lines.push("");
lines.push(`Only the active decision (${expectedActiveRecordId}) is shown.`);
- lines.push("The superseded reuse decision is filtered out \u2014 the agent cannot revive it.");
+ lines.push("The superseded reuse decision remains in Git, but is not delivered as current guidance.");
lines.push("");
const output = lines.join("\n");
return { exitCode: 0, output };
diff --git a/installer/canonical-artifact.json b/installer/canonical-artifact.json
index 7f5885df..0a7f54c7 100644
--- a/installer/canonical-artifact.json
+++ b/installer/canonical-artifact.json
@@ -15,10 +15,10 @@
"tsconfig.json",
"src"
],
- "sha256": "9a2f44f3a7b7f086311a278a289a40342a89b56ec847efd5b0b055b860023de3"
+ "sha256": "c01f87dc4f703be0976b941ee7303d333c6580f44d789b1764fd55b08dcab118"
},
"artifact": {
- "sha256": "fb94d8b2b9a5000b1aa1955e660de695cc8da20dddc4ce758c3f82a76e91c716",
+ "sha256": "4003472a38ec97b2797423aade581aaaeb8de3336967cf92a000c328131d0485",
"files": [
{
"path": "dist/cli.d.ts",
@@ -74,11 +74,11 @@
},
{
"path": "dist/commands/demo.js",
- "sha256": "87b066a5c26fcd36a372919e4ba39c6f5e80ba11e18dd6980fc339a1ead7357f"
+ "sha256": "075a2c08ef33c1f19da4c0a286f6e500376e72f6b2f6094cab7bcb8d67c476fb"
},
{
"path": "dist/commands/demo.js.map",
- "sha256": "2ed0798cdd0ed6f39db3a2ab0009ce3ea72bc95d2f70898fa09e213316597e31"
+ "sha256": "d257b82db28b64631dbb3f603cbeacd60bb38aa60feab5c320438074b099051a"
},
{
"path": "dist/commands/doctor.d.ts",
@@ -622,7 +622,7 @@
},
{
"path": "dist/commitlore.mjs",
- "sha256": "a0c542977f048e6b5163f581d2e4a53963b2d9845467af8949fa105b8bc0e528"
+ "sha256": "02b43821181f6cc415994bf99e3f48e0e27cbbac1a193f429ad18c6f570e7015"
},
{
"path": "dist/core/agent-configs.d.ts",
diff --git a/src/commands/demo.ts b/src/commands/demo.ts
index 798a6065..520ef4c5 100644
--- a/src/commands/demo.ts
+++ b/src/commands/demo.ts
@@ -196,7 +196,12 @@ export const runDemo = async (opts: DemoOptions = {}): Promise => {
lines.push('');
lines.push(`Only the active decision (${expectedActiveRecordId}) is shown.`);
- lines.push('The superseded reuse decision is filtered out — the agent cannot revive it.');
+ // "the agent cannot revive it" was a claim the product does not make. What
+ // CommitLore does is decline to deliver a superseded record as current
+ // guidance; whether a model then proposes the same idea from its own
+ // reasoning is outside anything this tool controls. The demo is the first
+ // thing many people run, so it is the last place to overstate.
+ lines.push('The superseded reuse decision remains in Git, but is not delivered as current guidance.');
lines.push('');
const output = lines.join('\n');
diff --git a/test/compatibility-matrix.test.ts b/test/compatibility-matrix.test.ts
index 8df287ee..b2c5468f 100644
--- a/test/compatibility-matrix.test.ts
+++ b/test/compatibility-matrix.test.ts
@@ -110,8 +110,10 @@ describe('T-1122 the compatibility statement exists and is the authoritative one
});
it.each(README_FILES)('%s points at it exactly once', (file) => {
- const hits = readFile(join(REPO_ROOT, file)).split('docs/COMPATIBILITY.md').length - 1;
- expect(hits, `${file} must carry exactly one pointer`).toBe(2);
+ // Count link targets, not raw substrings: `[docs/COMPATIBILITY.md](docs/COMPATIBILITY.md)`
+ // is one pointer that reads as two, so a readable link text used to fail this.
+ const hits = readFile(join(REPO_ROOT, file)).split('](docs/COMPATIBILITY.md)').length - 1;
+ expect(hits, `${file} must carry exactly one pointer`).toBe(1);
});
});
diff --git a/test/demo.test.ts b/test/demo.test.ts
index 9707f62c..562b2071 100644
--- a/test/demo.test.ts
+++ b/test/demo.test.ts
@@ -113,3 +113,41 @@ describe('commitlore demo', () => {
expect(elapsed).toBeLessThan(30_000);
});
});
+
+describe('the demo does not claim more than the product does', () => {
+ // `commitlore demo` is the first thing many people run, so it is the last
+ // place to overstate. It used to end with "the agent cannot revive it" -- an
+ // absolute about a model's reasoning, from a tool that controls what is
+ // delivered and nothing else. Nothing owned that sentence, so it survived
+ // every run until a reader caught it. These tests own it now.
+ let repo: string;
+ let root: string;
+
+ beforeAll(() => {
+ repo = mkdtempSync(join(tmpdir(), 'demo-claims-repo-'));
+ root = mkdtempSync(join(tmpdir(), 'demo-claims-root-'));
+ execFileSync('git', ['init', '--quiet', '--initial-branch=main', repo]);
+ execFileSync('git', ['-C', repo, 'config', 'user.email', 'test@example.invalid']);
+ execFileSync('git', ['-C', repo, 'config', 'user.name', 'Test']);
+ execFileSync('git', ['-C', repo, 'commit', '--allow-empty', '--quiet', '-m', 'root']);
+ });
+
+ afterAll(() => {
+ rmSync(repo, { recursive: true, force: true });
+ rmSync(root, { recursive: true, force: true });
+ });
+
+ it.each(['cannot revive', 'prevents', 'never forgets', 'blocks the edit'])(
+ 'does not say %o',
+ async (claim) => {
+ const { output } = await runDemo({ cwd: repo, tmpRoot: root });
+ expect(output.toLowerCase()).not.toContain(claim);
+ },
+ );
+
+ it('says what actually happens to the superseded record', async () => {
+ const { output } = await runDemo({ cwd: repo, tmpRoot: root });
+ expect(output).toContain('remains in Git');
+ expect(output).toContain('not delivered as current guidance');
+ });
+});
diff --git a/test/readme-order.test.ts b/test/readme-order.test.ts
index fe3965cb..d76cf2f4 100644
--- a/test/readme-order.test.ts
+++ b/test/readme-order.test.ts
@@ -54,6 +54,7 @@ function findAnchors(file: string): ReadmeAnchors {
// unchanged; the sentence carrying it is no longer a heading.
const product = lines.findIndex(
(l) =>
+ l.includes('Decision authority for coding agents, owned by Git') ||
l.includes('keeps re-proposing things your team already rejected') ||
l.includes('이미 기각한 방안을 계속 다시 제안합니다') ||
l.includes('すでに却下した案を何度も提案します') ||
@@ -64,6 +65,7 @@ function findAnchors(file: string): ReadmeAnchors {
// hosted service, but a host can handle returned context under its own policy.
const localFirst = lines.findIndex(
(l) =>
+ l.includes('No hosted memory. The repository owns the record') ||
l.includes('CommitLore has no hosted service') ||
l.includes('CommitLore에는 호스팅 서비스가 없') ||
l.includes('CommitLore にホスティングサービスは') ||
@@ -87,6 +89,7 @@ function findAnchors(file: string): ReadmeAnchors {
// See it work: the section heading
const seeItWork = lines.findIndex(
(l) =>
+ l.includes('src="./assets/readme/demo.gif"') ||
l === '## See it work' ||
l === '## 실제로 보기' ||
l === '## 実際に動かす' ||
@@ -98,6 +101,7 @@ function findAnchors(file: string): ReadmeAnchors {
// of a heading; keep every heading lookup exact for the same reason.
const automaticBoundary = lines.findIndex(
(l) =>
+ l === '## What happens automatically' ||
l === '## What happens automatically — and what does not' ||
l === '## 자동으로 되는 것과 아닌 것' ||
l === '## 自動になること、ならないこと' ||
@@ -106,6 +110,7 @@ function findAnchors(file: string): ReadmeAnchors {
const limitations = lines.findIndex(
(l) =>
+ l === '## Limits, trust and privacy' ||
l === '## When this will not help you' ||
l === '## 이것이 도움이 되지 않는 경우' ||
l === '## これが役に立たない場合' ||
@@ -119,7 +124,8 @@ function findAnchors(file: string): ReadmeAnchors {
const evidence = lines.findIndex(
(l) =>
l.startsWith('## ') &&
- (l.includes('Retrieval can find records') ||
+ (l === '## Evidence' ||
+ l.includes('Retrieval can find records') ||
l.includes('검색은 레코드를 찾을 수 있습니다') ||
l.includes('検索はレコードを見つけられる') ||
l.includes('检索能找到记录')),
@@ -162,11 +168,18 @@ describe('T-1015: README section order', () => {
expect(anchors.installPromise).toBeLessThan(anchors.installCommand);
});
- it('the first screen delivers payload, automation boundary, limits, then evidence', () => {
+ // #450 put limits ahead of evidence so a reader met the boundary before
+ // the numbers. The 2026-08-19 README redesign reverses that pair: evidence
+ // now runs first, and the boundary it used to depend on is carried inside
+ // the evidence section itself -- a `Boundary` column on every row and the
+ // sentence saying the study establishes no universal model effect. What
+ // survives the reversal, and is still asserted, is that neither section
+ // may precede the automation boundary.
+ it('the first screen delivers payload, automation boundary, then limits and evidence', () => {
expect(anchors.installCommand).toBeLessThan(anchors.seeItWork);
expect(anchors.seeItWork).toBeLessThan(anchors.automaticBoundary);
expect(anchors.automaticBoundary).toBeLessThan(anchors.limitations);
- expect(anchors.limitations).toBeLessThan(anchors.evidence);
+ expect(anchors.automaticBoundary).toBeLessThan(anchors.evidence);
});
});
}
@@ -180,7 +193,7 @@ describe('T-1015: README section order', () => {
expect(anchors.installCommand).toBeLessThan(anchors.seeItWork);
expect(anchors.seeItWork).toBeLessThan(anchors.automaticBoundary);
expect(anchors.automaticBoundary).toBeLessThan(anchors.limitations);
- expect(anchors.limitations).toBeLessThan(anchors.evidence);
+ expect(anchors.automaticBoundary).toBeLessThan(anchors.evidence);
}
});
@@ -217,7 +230,13 @@ describe('T-1015: README section order', () => {
it('the first screen names delivery and capture for every host class', () => {
for (const file of FILES) {
const content = fs.readFileSync(path.join(REPO_ROOT, file), 'utf8');
- expect(content).toContain('| Host | Delivery | Capture |');
+ // The header row grew columns in the redesign; what it must still do is
+ // name a host and say, for that host, what is delivered and what is
+ // captured. Pinning the old three-column string measured the wording.
+ const header = content.split('\n').find((l) => l.startsWith('| Host |'));
+ expect(header, `${file} has no host table`).toBeDefined();
+ expect(header!).toMatch(/deliver/i);
+ expect(header!).toMatch(/captur/i);
expect(content).toContain('| Claude Code |');
expect(content).toContain('| Codex |');
expect(content).toContain('| Hermes |');
diff --git a/test/readme.test.ts b/test/readme.test.ts
index e4ecbbaf..234bb51e 100644
--- a/test/readme.test.ts
+++ b/test/readme.test.ts
@@ -101,7 +101,7 @@ describe('T-1021: Known limitations discloses guard precision and recall', () =>
// it must disclose is unchanged; only where the test looks for it moved.
// Extract the section (between its heading and the next ## heading)
const knownLimStart = content.search(
- /^## (When this will not help you|이것이 도움이 되지 않는 경우|これが役に立たない場合|这在什么情况下帮不上忙)/m,
+ /^## (Limits, trust and privacy|When this will not help you|이것이 도움이 되지 않는 경우|これが役に立たない場合|这在什么情况下帮不上忙)/m,
);
const afterStart = content.slice(knownLimStart + 1);
const nextSection = afterStart.search(/^## /m);
@@ -137,7 +137,7 @@ describe('T-1021 mutation oracles', () => {
const enContent = fs.readFileSync(path.join(REPO_ROOT, 'README.md'), 'utf8');
// Extract that section for English
- const knownLimStart = enContent.search(/^## When this will not help you/m);
+ const knownLimStart = enContent.search(/^## (Limits, trust and privacy|When this will not help you)/m);
const afterStart = enContent.slice(knownLimStart + 1);
const nextSection = afterStart.search(/^## /m);
const knownLimSection = nextSection === -1 ? afterStart : afterStart.slice(0, nextSection);
@@ -223,3 +223,100 @@ describe('every supported install path is documented and pinned to this release'
});
}
});
+
+describe('README local assets exist and are what they claim', () => {
+ const asset = (name: string): string => path.join(REPO_ROOT, 'assets', 'readme', name);
+
+ it.each(['commitlore-logo.svg', 'hero.svg', 'demo.gif', 'demo.tape'])(
+ '%s exists',
+ (name) => {
+ expect(fs.existsSync(asset(name)), `${name} is referenced but missing`).toBe(true);
+ },
+ );
+
+ // A GIF is the one asset a reader cannot check by reading the diff, so the
+ // things that make it unusable are checked here: not a GIF at all, empty, or
+ // large enough that GitHub declines to animate it.
+ it('demo.gif is a real GIF and small enough to animate', () => {
+ const bytes = fs.readFileSync(asset('demo.gif'));
+ expect(bytes.length).toBeGreaterThan(0);
+ expect(['GIF87a', 'GIF89a']).toContain(bytes.subarray(0, 6).toString('ascii'));
+ expect(bytes.length).toBeLessThanOrEqual(4 * 1024 * 1024);
+ });
+
+ // The tape is committed beside the GIF on purpose: a recording whose source
+ // is thrown away cannot be checked against the command it claims to show.
+ it('demo.tape names the command the GIF shows', () => {
+ expect(fs.readFileSync(asset('demo.tape'), 'utf8')).toContain('commitlore demo');
+ });
+
+ it.each(['commitlore-logo.svg', 'hero.svg'])('%s carries no active content', (name) => {
+ const svg = fs.readFileSync(asset(name), 'utf8');
+ for (const banned of ['