rename docs/ to harness/, and reserve docs/ for the reader

docs/content-model.md opens with "Engine specification". It is also where the
rule lives that a leading underscore makes a file unaddressable — and the human
who owns this site did not know that rule, because nothing in this repository is
addressed to an author. Twelve documents named docs/ while being exclusively
about building the parser is a signpost pointing at the wrong room.

Naming the directory for its audience makes the gap visible instead of hiding it.
docs/ is now reserved and deliberately absent: an empty docs/ is an honest
statement that end-user documentation does not exist, where docs/ full of parser
specs was a claim that it did.

HARNESS.md stays at the root. Root holds the three entry points — README.md for a
human, CLAUDE.md for an agent, HARNESS.md for whoever maintains the machine — and
harness/README.md is the map of the directory, so moving the guide inside would
have collided with it for nothing.

Mechanical and wide: 100 path references across 24 files. Every verify.sh gate
that names a doc by path, the directory lists the dangling-path and ADR-number
gates scan, surface.sh's output target, the Makefile, CLAUDE.md's read order, the
skill, four commands, and two Go package comments. A first pass with a shell loop
silently edited only four files and the rest still said docs/; the fix was to
write the file list out and check the remaining count was zero rather than trust
the loop's exit status.

No rule, threshold, gate or obligation moved — this is a rename, and the gates
demonstrated it twice: they stayed green on the new paths, and the ADR-number gate
caught ADR-0082 before the entry existed.

Deferred, both on the human's call: the end-user documentation site itself, which
wants its own decision about where it lives and whether its claims are gated; and
moving examples/ under docs/, since demo-site is a live site root that verify.sh,
the coverage test and make demo all point at, and moving it would couple a rename
to a design nobody has made.

31 files, +146/-106.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 20:11:06 +06:00
co-authored by Claude Opus 5
parent 9349c54d2e
commit ec6e9838f0
31 changed files with 146 additions and 106 deletions
+25 -22
View File
@@ -4,9 +4,12 @@ Scaffolding that makes an agent build `khosra` the way you want: minimally, surg
before code, docs that stay true.
`README.md` is a two-line signpost — human here, agent to `CLAUDE.md`. `CLAUDE.md` is the
constitution (always loaded). `docs/` is what the agent needs to build the
engine — anything in it may be pulled into context on demand. `ideas/` and `reference/` sit outside
`docs/` deliberately: storage, opened only when you name a file, swept by nothing.
constitution (always loaded). `harness/` is what the agent needs to build the
engine — anything in it may be pulled into context on demand. It is named for its audience, not its
format: `docs/` is reserved for documentation written for whoever *uses* khosra, and is absent because
that does not exist yet (ADR-0082). An empty `docs/` says so honestly; `docs/` full of parser
specifications said the opposite, and cost its own author a rule he did not know was written down. `ideas/` and `reference/` sit outside
`harness/` deliberately: storage, opened only when you name a file, swept by nothing.
`.claude/` holds the feature-loop skill (`skills/feature-loop/`), the commands, and `launch.json`.
`scripts/` holds the gate. `.scratch/continue.md` is a **temporary** handoff — uncommitted, ungated,
discardable — holding the continuation point and the findings worth carrying that no doc owns. It is read
@@ -37,7 +40,7 @@ Occasional maintenance, by you:
- "Park this" → the agent writes `ideas/<slug>.md`, resumable cold, and indexes it. Name the file
later to pick the thread up; it reads these only when named. `ideas/exploration.md` is the same idea for
engine features nobody has asked for — a catalog, deliberately outside `docs/` so it adds no weight to the
engine features nobody has asked for — a catalog, deliberately outside `harness/` so it adds no weight to the
working set.
- `/audit` every ~5 features — finds abstractions that never earned their keep.
- `/invariants` at arc boundaries, before a freeze, before the first deploy — checks the nine
@@ -54,7 +57,7 @@ split the difference into a compromise nobody picked.
**Three surfaces, one of them yours to edit here.** Engine source lives in this repo; content lives in
the site root (ADR-0011); the theme is a third surface with its own owner (ADR-0023). A request that spans
engine and theme produces a *contract extension* in `docs/theme-contract.md` plus a note of what the theme
engine and theme produces a *contract extension* in `harness/theme-contract.md` plus a note of what the theme
must do — not the theme. `verify.sh` fails if the embedded reference theme changes without the contract doc
changing, because in practice those two drift together — and it fails on a `<script>` tag in that theme,
because a reference theme that grows taste stops being a reference (ADR-0026).
@@ -68,7 +71,7 @@ without a case there is a feature the demo does not show, and the build says so
a site root and there is nothing else to run.
**The agent names the layer as well as the primitive.** Content on disk, engine, theme, browser — and it builds
at the outermost layer that can do the job (`CLAUDE.md` §2, `docs/architecture.md`). This exists because a whole
at the outermost layer that can do the job (`CLAUDE.md` §2, `harness/architecture.md`). This exists because a whole
feature was built at the wrong one: widow prevention as a Markdown transform that inserted a non-breaking space
into an author's prose. It worked and it had tests; `text-wrap: pretty` does it better with no bytes in the
content, so the feature was deleted (ADR-0045). If a feature only rearranges how something looks, expect the
@@ -81,9 +84,9 @@ history belongs, not here. Read a core raise as evidence something belongs in `i
it as evidence the number was small.
**Context is a budget, and parts of it are mechanical.** The limit on this project is how much
work fits in a session, so `docs/context-economy.md` holds the reading, searching and reporting
work fits in a session, so `harness/context-economy.md` holds the reading, searching and reporting
disciplines — read the compressed form first, batch calls, script anything repeatable, never pay twice
for the same bytes (ADR-0053). These are enforced rather than trusted: `docs/surface.md` is
for the same bytes (ADR-0053). These are enforced rather than trusted: `harness/surface.md` is
regenerated and staged by the pre-commit hook and independently compared by `verify.sh`, so the map of
the code cannot mislead — an order of magnitude smaller than the source it stands for — and no one has to
remember to run it;
@@ -125,23 +128,23 @@ wanting to emit HTML still renders a theme template instead (ADR-0036).
In order, cheapest first:
1. `./scripts/verify.sh` — one command, tells you whether the thing is still coherent and whether
`docs/state.md` has fallen behind the code.
2. `docs/state.md` — what exists, the earn-it counters, the latent list. This is the only doc that
`harness/state.md` has fallen behind the code.
2. `harness/state.md` — what exists, the earn-it counters, the latent list. This is the only doc that
describes the present, and `verify.sh` compares its last commit against the last `.go` one rather than
trusting a sha written by hand (ADR-0057). Its companion `docs/surface.md` is generated:
trusting a sha written by hand (ADR-0057). Its companion `harness/surface.md` is generated:
every declaration and its line, so you can find your way around without opening anything.
3. `git log --oneline` — one revertible unit per commit, each body saying *why* (`conventions.md`). This is the
real map of how the code got here.
4. `/refresh-docs` — reconciles every doc against the actual code and reports drift, which is exactly the
question you have after a year.
5. `docs/decisions.md` — the ADR log, when you hit something and think "why on earth is it like this".
5. `harness/decisions.md` — the ADR log, when you hit something and think "why on earth is it like this".
Each entry names the observation that would overturn it, so you can tell a stale decision from a
deliberate one.
6. `docs/toolchain.md` — if something is broken rather than merely unfamiliar. It records what this was
6. `harness/toolchain.md` — if something is broken rather than merely unfamiliar. It records what this was
built against and which agent-tooling contracts it assumes, so a tooling change is diagnosable instead
of looking like a harness bug.
Then `docs/README.md` for whichever topic you are actually here for.
Then `harness/README.md` for whichever topic you are actually here for.
## Why the pieces exist
@@ -150,7 +153,7 @@ empty (ADR-0070). Four counters had to be re-scoped the first time anything test
sentence about what had been wrongly included — so the sentence is now required up front. A counter that
cannot name an exclusion is measuring a symptom.
**Counters in `docs/state.md`** turn "no abstraction before its second use" into arithmetic. Every
**Counters in `harness/state.md`** turn "no abstraction before its second use" into arithmetic. Every
threshold lives in that one table and nowhere else. The agent cannot argue a pipeline into existence
one transform early — it writes the next one inline and lets the count force the extraction. Most
load-bearing mechanism here, and the one with least machine enforcement, which is why `verify.sh`
@@ -183,7 +186,7 @@ is the engine's own job rather than the gate's. The gate skips `ideas/`, `refere
diffed, the theme contract and this file are coupled to what they describe. `.scratch/continue.md` can have
none of that: it is gitignored, so there is no commit to compare it against. Two things stand in for the
gate it cannot have — it holds the plan and the carried findings but never what landed, and the read order
puts `git log` first with the handoff read against it (`docs/README.md`). The one check that *is* mechanical
puts `git log` first with the handoff read against it (`harness/README.md`). The one check that *is* mechanical
now runs: a `.scratch/` path named in a doc must exist, guarded on the directory being present so a fresh
clone with no handoff still passes. A handoff that recorded what landed was believed for a whole session —
it said a doc trim awaited a yes while the commit being amended around it had already done the trim.
@@ -217,7 +220,7 @@ Softer signals stay advisory: `fmt.Errorf` without `%w`, nesting past 4, exporte
and `any` in an **exported** signature — only exported, because ADR-0002 mandates an open page object,
so unexported code reading frontmatter takes `any` legitimately and forever.
**STATUS markers in `docs/architecture.md`** give the target shape *and* what is legal today, so
**STATUS markers in `harness/architecture.md`** give the target shape *and* what is legal today, so
the agent can read the endgame without building toward it. They are the only markers of that kind left:
`content-model.md`'s `[spec]` sections were deleted rather than given a stricter "do not build from this"
rule, because a marker inside a doc the agent already has open still gets read. Those shapes moved to
@@ -243,30 +246,30 @@ that describe it, in the same change.**
`internal/ext/…`, and `verify.sh` states that as one rule rather than a list of the pairs that happen to
exist today (ADR-0069) — so a core package added next month cannot quietly import a feature. A feature
importing its sibling fails the same rule.
- `verify.sh` fails when a `.go` file changes without `docs/state.md` changing, warns when `state.md`'s last
- `verify.sh` fails when a `.go` file changes without `harness/state.md` changing, warns when `state.md`'s last
commit is older than the last `.go` one — the two together mean the doc ships inside the change, never in a
commit trailing it (ADR-0057) — and fails when `cmd/` or
`internal/` code changes without a `_test.go` changing — behaviour ships with a test. A comment-only or
`gofmt`-only diff is exempt: it ships no behaviour, and failing it would only teach you `--no-verify`.
- The gate is not optional. `scripts/hooks/pre-commit` runs it on every commit; enable once per clone
with `git config core.hooksPath scripts/hooks`. `--no-verify` bypasses it, and the commit body should
say why. The hook also regenerates `docs/surface.md` and stages it, so a generated file is never
say why. The hook also regenerates `harness/surface.md` and stages it, so a generated file is never
something you have to remember — and it refuses a commit with unstaged `.go` changes, because what it
generated describes the working tree rather than what you are committing.
- `docs/decisions.md` registers every ADR number ever used, entries and withdrawals alike, so a citation
- `harness/decisions.md` registers every ADR number ever used, entries and withdrawals alike, so a citation
can resolve to a decision or to a deferral but never to nothing.
- `./scripts/verify.sh --list` names every gate that exists. A doc claiming enforcement is checkable
against it in one command, and `/refresh-docs` checks it in both directions — a claimed gate that is
missing, and a real gate nothing explains. Asserting a mechanism before it exists is the drift that
reads as enforcement and is decoration; `CLAUDE.md` rule 8 forbids it.
- `docs/README.md` carries three tables: topic → doc to **read before asserting a rule**, change →
- `harness/README.md` carries three tables: topic → doc to **read before asserting a rule**, change →
doc to **update after making one**, and an authority table naming the one home of every value.
A new mechanism adds a row; nothing outside a value's home may restate it. A number written twice
eventually disagrees with itself.
## Open
- No ADR gate blocks Arc 1. One question remains in `docs/state.md`: the language suffix on the first
- No ADR gate blocks Arc 1. One question remains in `harness/state.md`: the language suffix on the first
content file.
- `go.mod` does not exist yet — `go mod init` belongs to the first feature, and the module path is
still unchosen.