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:
@@ -2,10 +2,10 @@
|
|||||||
description: Record an architectural decision in six lines
|
description: Record an architectural decision in six lines
|
||||||
---
|
---
|
||||||
|
|
||||||
Append an ADR to `docs/decisions.md` using the exact format at the top of that file.
|
Append an ADR to `harness/decisions.md` using the exact format at the top of that file.
|
||||||
|
|
||||||
First, before writing anything: find the doc that owns this topic via the ownership table in
|
First, before writing anything: find the doc that owns this topic via the ownership table in
|
||||||
`docs/README.md` and read it. If it already carries the rule, do not write an ADR — amend that doc
|
`harness/README.md` and read it. If it already carries the rule, do not write an ADR — amend that doc
|
||||||
and say that is what you did. An ADR that restates an existing doc is a duplicate, not a decision.
|
and say that is what you did. An ADR that restates an existing doc is a duplicate, not a decision.
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Look for:
|
|||||||
- Tests that assert on private helpers, or that need a mock to exist.
|
- Tests that assert on private helpers, or that need a mock to exist.
|
||||||
- Dead code, commented-out code, `TODO`s older than the feature that introduced them.
|
- Dead code, commented-out code, `TODO`s older than the feature that introduced them.
|
||||||
- Comments that narrate control flow.
|
- Comments that narrate control flow.
|
||||||
- Anything in `docs/` describing code that no longer exists — excluding `ideas/` and
|
- Anything in `harness/` describing code that no longer exists — excluding `ideas/` and
|
||||||
`reference/`, which are never audited against the code and must not be opened here.
|
`reference/`, which are never audited against the code and must not be opened here.
|
||||||
|
|
||||||
Report as a table: item, location, why it has not earned its place, LOC recovered if deleted,
|
Report as a table: item, location, why it has not earned its place, LOC recovered if deleted,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ description: Check the architecture invariants against the code, not against the
|
|||||||
---
|
---
|
||||||
|
|
||||||
`verify.sh` checks what a grep can check. This pass checks the nine invariants in
|
`verify.sh` checks what a grep can check. This pass checks the nine invariants in
|
||||||
`docs/architecture.md`, which need reading the code. Run at arc boundaries, before a freeze, and
|
`harness/architecture.md`, which need reading the code. Run at arc boundaries, before a freeze, and
|
||||||
before the first deploy.
|
before the first deploy.
|
||||||
|
|
||||||
For each invariant, answer **held / violated / not yet applicable**, with a file and line when
|
For each invariant, answer **held / violated / not yet applicable**, with a file and line when
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ description: Reconcile the docs against the actual code and report drift
|
|||||||
Reconcile documentation with reality. The code is the truth; the docs are the suspects.
|
Reconcile documentation with reality. The code is the truth; the docs are the suspects.
|
||||||
|
|
||||||
1. Inventory the actual Go files, their line counts, and the non-stdlib dependencies in `go.mod`.
|
1. Inventory the actual Go files, their line counts, and the non-stdlib dependencies in `go.mod`.
|
||||||
2. Compare against `docs/state.md`: inventory rows, counters, latent items.
|
2. Compare against `harness/state.md`: inventory rows, counters, latent items.
|
||||||
Recount the counters **from the code** — number of render transforms, routing cases, views,
|
Recount the counters **from the code** — number of render transforms, routing cases, views,
|
||||||
output formats, extensions — rather than trusting the recorded numbers.
|
output formats, extensions — rather than trusting the recorded numbers.
|
||||||
3. Check `docs/architecture.md` STATUS lines: has a primitive become real, or is one described
|
3. Check `harness/architecture.md` STATUS lines: has a primitive become real, or is one described
|
||||||
as live when it is not built?
|
as live when it is not built?
|
||||||
4. Check `docs/content-model.md` against what the parser actually accepts. It describes today's
|
4. Check `harness/content-model.md` against what the parser actually accepts. It describes today's
|
||||||
behaviour and carries no markers, so every sentence in it is a claim to test. Frontmatter fields
|
behaviour and carries no markers, so every sentence in it is a claim to test. Frontmatter fields
|
||||||
the code reads but the doc omits are drift; fields the doc promises but the code ignores are worse
|
the code reads but the doc omits are drift; fields the doc promises but the code ignores are worse
|
||||||
drift. Worst, and the kind that has actually shipped: a subcommand, flag or gate the doc describes
|
drift. Worst, and the kind that has actually shipped: a subcommand, flag or gate the doc describes
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ are the first and the last.
|
|||||||
| the site root | Out of reach: it lives in its own repo (ADR-0011). A disk-contract change ships a **written migration step** the author runs, plus a note on whether any bundle key or URL moves. Never claim to have migrated files you cannot see. |
|
| the site root | Out of reach: it lives in its own repo (ADR-0011). A disk-contract change ships a **written migration step** the author runs, plus a note on whether any bundle key or URL moves. Never claim to have migrated files you cannot see. |
|
||||||
| code | The engine, plus a test for the new behaviour. |
|
| code | The engine, plus a test for the new behaviour. |
|
||||||
| the theme | Out of reach, like the site root (ADR-0023). This repo ships a **contract extension** in
|
| the theme | Out of reach, like the site root (ADR-0023). This repo ships a **contract extension** in
|
||||||
`docs/theme-contract.md` plus a written note of what a theme must do — never the theme itself, and never
|
`harness/theme-contract.md` plus a written note of what a theme must do — never the theme itself, and never
|
||||||
a layout or markup decision dressed as an engine feature. |
|
a layout or markup decision dressed as an engine feature. |
|
||||||
| fixtures and emitted output | Fixture sites in `testdata/`, the embedded default templates (a reference implementation of the contract), and anything reading a field or path shape you altered. |
|
| fixtures and emitted output | Fixture sites in `testdata/`, the embedded default templates (a reference implementation of the contract), and anything reading a field or path shape you altered. |
|
||||||
| harness docs | Every doc, ADR, marker or example that assumes the old form. An ADR that mandates what you just made optional is not stale, it is **contradicted** — supersede it, do not quietly reword it. |
|
| harness docs | Every doc, ADR, marker or example that assumes the old form. An ADR that mandates what you just made optional is not stale, it is **contradicted** — supersede it, do not quietly reword it. |
|
||||||
@@ -28,15 +28,15 @@ a layout or markup decision dressed as an engine feature. |
|
|||||||
|
|
||||||
## 1. Clarify
|
## 1. Clarify
|
||||||
|
|
||||||
Read `docs/state.md`, then `docs/README.md` — the map, always, before deciding what else to open.
|
Read `harness/state.md`, then `harness/README.md` — the map, always, before deciding what else to open.
|
||||||
|
|
||||||
**A handoff is not evidence.** If `.scratch/continue.md` says something is pending, waiting, or unfinished,
|
**A handoff is not evidence.** If `.scratch/continue.md` says something is pending, waiting, or unfinished,
|
||||||
run `git log --oneline -- <the path it names>` before planning from it. The file is uncommitted, so no gate
|
run `git log --oneline -- <the path it names>` before planning from it. The file is uncommitted, so no gate
|
||||||
compares it to anything, and it is written *during* a change whose commit can still move underneath it. The
|
compares it to anything, and it is written *during* a change whose commit can still move underneath it. The
|
||||||
log is the only record of what landed. Offering the human work that is already in `HEAD` wastes the turn
|
log is the only record of what landed. Offering the human work that is already in `HEAD` wastes the turn
|
||||||
they asked the question in.
|
they asked the question in.
|
||||||
For code, read the compressed form before the source: `docs/surface.md` locates every declaration,
|
For code, read the compressed form before the source: `harness/surface.md` locates every declaration,
|
||||||
`go doc` gives a package's surface, a test states its contract. Disciplines: `docs/context-economy.md`.
|
`go doc` gives a package's surface, a test states its contract. Disciplines: `harness/context-economy.md`.
|
||||||
Use its topic-ownership table to list the docs owning anything this change asserts a rule about,
|
Use its topic-ownership table to list the docs owning anything this change asserts a rule about,
|
||||||
and read those. That list is a floor: skipping it is how a rule gets written twice and the second
|
and read those. That list is a floor: skipping it is how a rule gets written twice and the second
|
||||||
copy contradicts the first.
|
copy contradicts the first.
|
||||||
@@ -96,7 +96,7 @@ If the plan reveals a trunk, say so instead of planning and offer the leaf.
|
|||||||
- Smallest code satisfying the success criteria. Nothing for the version after next.
|
- Smallest code satisfying the success criteria. Nothing for the version after next.
|
||||||
- Only the planned files. No renames, reordering, reformatting beyond `gofmt`, or tidying of
|
- Only the planned files. No renames, reordering, reformatting beyond `gofmt`, or tidying of
|
||||||
neighbouring code.
|
neighbouring code.
|
||||||
- Something wrong outside the plan? One line on the Latent list in `docs/state.md`, keep moving.
|
- Something wrong outside the plan? One line on the Latent list in `harness/state.md`, keep moving.
|
||||||
That is the whole mechanism; use it instead of a drive-by fix.
|
That is the whole mechanism; use it instead of a drive-by fix.
|
||||||
- Reuse the existing shape before inventing one. Copying eight lines twice beats an abstraction at
|
- Reuse the existing shape before inventing one. Copying eight lines twice beats an abstraction at
|
||||||
use two; the third use is when it becomes a design.
|
use two; the third use is when it becomes a design.
|
||||||
@@ -123,12 +123,12 @@ not run something, say which and why.
|
|||||||
|
|
||||||
## 5. Document
|
## 5. Document
|
||||||
|
|
||||||
Same change, not later. Triggers: `docs/README.md`. Walk the propagation table above — every surface
|
Same change, not later. Triggers: `harness/README.md`. Walk the propagation table above — every surface
|
||||||
done or explicitly n/a. Minimum:
|
done or explicitly n/a. Minimum:
|
||||||
|
|
||||||
- `docs/state.md`: inventory rows, counters, latent items added/removed — in *this* commit, never a
|
- `harness/state.md`: inventory rows, counters, latent items added/removed — in *this* commit, never a
|
||||||
trailing one, which is what `verify.sh` compares (ADR-0057).
|
trailing one, which is what `verify.sh` compares (ADR-0057).
|
||||||
- `docs/decisions.md`: an ADR if the choice is expensive to reverse. Six lines. First open the doc
|
- `harness/decisions.md`: an ADR if the choice is expensive to reverse. Six lines. First open the doc
|
||||||
that owns the topic — if it already carries the rule, amend it there; an ADR restating an
|
that owns the topic — if it already carries the rule, amend it there; an ADR restating an
|
||||||
existing doc is a duplicate, not a decision.
|
existing doc is a duplicate, not a decision.
|
||||||
- Any other doc **only if the change made it wrong.** Never state one fact in two docs.
|
- Any other doc **only if the change made it wrong.** Never state one fact in two docs.
|
||||||
@@ -139,7 +139,7 @@ Doc edits are surgical too. Prefer deleting a stale line to appending a correcte
|
|||||||
elsewhere in the old terms:
|
elsewhere in the old terms:
|
||||||
|
|
||||||
```
|
```
|
||||||
grep -rn '<old form>' docs CLAUDE.md HARNESS.md ideas reference .claude scripts
|
grep -rn '<old form>' harness CLAUDE.md HARNESS.md ideas reference .claude scripts
|
||||||
```
|
```
|
||||||
|
|
||||||
`verify.sh` fails on dangling file paths, ADR numbers and `CLAUDE.md` section refs. It cannot detect
|
`verify.sh` fails on dangling file paths, ADR numbers and `CLAUDE.md` section refs. It cannot detect
|
||||||
@@ -216,7 +216,7 @@ Swept: the old form you grepped for after a rename, or "n/a"
|
|||||||
| Renaming "for clarity" mid-feature | Drive-by refactor | Latent list |
|
| Renaming "for clarity" mid-feature | Drive-by refactor | Latent list |
|
||||||
| Explaining why the boundary can bend here | It cannot (ADR-0003) | Stop and ask |
|
| Explaining why the boundary can bend here | It cannot (ADR-0003) | Stop and ask |
|
||||||
| Plan grew while implementing | Scope drift | Stop, re-plan, continue |
|
| Plan grew while implementing | Scope drift | Stop, re-plan, continue |
|
||||||
| Writing a rule without reading its owning doc | Guessing where you could look | `docs/README.md` topic table, then amend that doc |
|
| Writing a rule without reading its owning doc | Guessing where you could look | `harness/README.md` topic table, then amend that doc |
|
||||||
| Quietly doing what was asked against a recorded decision | The conflict was real and you hid it | Quote the line, give both paths, wait |
|
| Quietly doing what was asked against a recorded decision | The conflict was real and you hid it | Quote the line, give both paths, wait |
|
||||||
| Changed the rule in code, left the docs describing the old one | Propagation stopped at the first surface | Walk all four surfaces |
|
| Changed the rule in code, left the docs describing the old one | Propagation stopped at the first surface | Walk all four surfaces |
|
||||||
| Made a field optional, left the ADR mandating it | Contradicted, not stale | Supersede the ADR |
|
| Made a field optional, left the ADR mandating it | Contradicted, not stale | Supersede the ADR |
|
||||||
@@ -225,7 +225,7 @@ Swept: the old form you grepped for after a rename, or "n/a"
|
|||||||
| Ending a turn with the work only in the working tree | The one copy is the one that gets lost | Commit before reporting |
|
| Ending a turn with the work only in the working tree | The one copy is the one that gets lost | Commit before reporting |
|
||||||
| Offering work a handoff calls pending | The file states intent; only the log states what landed | `git log -- <path>` before trusting it |
|
| Offering work a handoff calls pending | The file states intent; only the log states what landed | `git log -- <path>` before trusting it |
|
||||||
| Ticking the handoff during Document | The commit can still move under it | Reconcile it after the commit exists |
|
| Ticking the handoff during Document | The commit can still move under it | Reconcile it after the commit exists |
|
||||||
| Opening a file to find out what is in it | The generated surface already answers it | `docs/surface.md`, then read the range |
|
| Opening a file to find out what is in it | The generated surface already answers it | `harness/surface.md`, then read the range |
|
||||||
| Grepping for callers before changing a signature | The compiler enumerates them exactly | Change it, then `go build ./...` |
|
| Grepping for callers before changing a signature | The compiler enumerates them exactly | Change it, then `go build ./...` |
|
||||||
| A shorter report that dropped a caveat, case or number | Truncation wearing compression's clothes | Restore it; cut words, never findings |
|
| A shorter report that dropped a caveat, case or number | Truncation wearing compression's clothes | Restore it; cut words, never findings |
|
||||||
| One commit per file, or per doc touched | Shredding a single revertible unit | Bundle what would be undone together |
|
| One commit per file, or per doc touched | Shredding a single revertible unit | Bundle what would be undone together |
|
||||||
|
|||||||
@@ -12,27 +12,27 @@ as aesthetic, comprehensibility by one person.
|
|||||||
## 1. Read order (do not skip, do not exceed)
|
## 1. Read order (do not skip, do not exceed)
|
||||||
|
|
||||||
1. This file.
|
1. This file.
|
||||||
2. `docs/state.md` — what exists **right now**, plus the earn-it counters.
|
2. `harness/state.md` — what exists **right now**, plus the earn-it counters.
|
||||||
3. `docs/README.md` — always. The map: how you find which doc owns your topic. Not "exceeding".
|
3. `harness/README.md` — always. The map: how you find which doc owns your topic. Not "exceeding".
|
||||||
4. `.scratch/continue.md` — **after** the harness above, never instead of it, and only when picking work up
|
4. `.scratch/continue.md` — **after** the harness above, never instead of it, and only when picking work up
|
||||||
rather than answering a named request. A *temporary* handoff: uncommitted, ungated, discardable, holding
|
rather than answering a named request. A *temporary* handoff: uncommitted, ungated, discardable, holding
|
||||||
the continuation point and nothing else. Anything it calls pending is a claim about the past — check it
|
the continuation point and nothing else. Anything it calls pending is a claim about the past — check it
|
||||||
against `git log` first, and where they differ the log wins the way the code wins over `state.md`.
|
against `git log` first, and where they differ the log wins the way the code wins over `state.md`.
|
||||||
5. Every doc owning a topic your change asserts a rule about (ownership table in `docs/README.md`).
|
5. Every doc owning a topic your change asserts a rule about (ownership table in `harness/README.md`).
|
||||||
6. Only the source files you will edit, plus their direct callers.
|
6. Only the source files you will edit, plus their direct callers.
|
||||||
|
|
||||||
No reading the repo "for context", no speculative greps. Where `docs/state.md` and the code
|
No reading the repo "for context", no speculative greps. Where `harness/state.md` and the code
|
||||||
disagree, the code wins — say so, fix the doc in Document.
|
disagree, the code wins — say so, fix the doc in Document.
|
||||||
|
|
||||||
**The ceiling has a floor.** "Read less" governs breadth, never the doc that owns what you are
|
**The ceiling has a floor.** "Read less" governs breadth, never the doc that owns what you are
|
||||||
writing. Before stating a rule, contract, threshold, or gate, read its owning doc; if it already
|
writing. Before stating a rule, contract, threshold, or gate, read its owning doc; if it already
|
||||||
says it, amend there instead of restating elsewhere.
|
says it, amend there instead of restating elsewhere.
|
||||||
|
|
||||||
**Read the compressed form first.** `docs/surface.md` (generated, gated) says where every declaration
|
**Read the compressed form first.** `harness/surface.md` (generated, gated) says where every declaration
|
||||||
lives; `go doc ./internal/<pkg>` gives a package's surface; a test states a contract in a fifth of the
|
lives; `go doc ./internal/<pkg>` gives a package's surface; a test states a contract in a fifth of the
|
||||||
lines that implement it. Locate with `grep -n`, then read that range — a whole file is for the doc that
|
lines that implement it. Locate with `grep -n`, then read that range — a whole file is for the doc that
|
||||||
owns a rule you are asserting, or code you are about to rewrite. Every discipline, and the floor none of
|
owns a rule you are asserting, or code you are about to rewrite. Every discipline, and the floor none of
|
||||||
them may cross: `docs/context-economy.md`. A saving that buys a guess is not a saving.
|
them may cross: `harness/context-economy.md`. A saving that buys a guess is not a saving.
|
||||||
|
|
||||||
`ideas/` and `reference/` are out of context by default, indexes included. Open one only when the
|
`ideas/` and `reference/` are out of context by default, indexes included. Open one only when the
|
||||||
human names it. Never sweep, never list, never cite unasked. Storage, not background.
|
human names it. Never sweep, never list, never cite unasked. Storage, not background.
|
||||||
@@ -40,7 +40,7 @@ human names it. Never sweep, never list, never cite unasked. Storage, not backgr
|
|||||||
## 2. The primitives — everything reduces to one
|
## 2. The primitives — everything reduces to one
|
||||||
|
|
||||||
**Bundle · Stage · Query · View · Interaction · Effect** (+ bundle-as-program).
|
**Bundle · Stage · Query · View · Interaction · Effect** (+ bundle-as-program).
|
||||||
Definitions and STATUS: `docs/architecture.md`.
|
Definitions and STATUS: `harness/architecture.md`.
|
||||||
|
|
||||||
Name the primitive before writing code. If it reduces to none it is a **trunk** (wants a permanent
|
Name the primitive before writing code. If it reduces to none it is a **trunk** (wants a permanent
|
||||||
service or a core-model change): stop, say so in a paragraph, propose the leaf, wait.
|
service or a core-model change): stop, say so in a paragraph, propose the leaf, wait.
|
||||||
@@ -49,21 +49,21 @@ service or a core-model change): stop, say so in a paragraph, propose the leaf,
|
|||||||
browser (CSS, and only then JS). Build it at the outermost layer that can do the job: a line-breaking or
|
browser (CSS, and only then JS). Build it at the outermost layer that can do the job: a line-breaking or
|
||||||
spacing problem CSS solves is not an engine feature, and code that edits an author's text to fix how it
|
spacing problem CSS solves is not an engine feature, and code that edits an author's text to fix how it
|
||||||
*looks* is at the wrong layer by definition (ADR-0045 — a whole feature was deleted for this). Layers and
|
*looks* is at the wrong layer by definition (ADR-0045 — a whole feature was deleted for this). Layers and
|
||||||
the test: `docs/architecture.md`.
|
the test: `harness/architecture.md`.
|
||||||
|
|
||||||
## 3. Hard rules
|
## 3. Hard rules
|
||||||
|
|
||||||
1. **No abstraction before its second concrete use** — pipeline, resolver, interface, generic, config
|
1. **No abstraction before its second concrete use** — pipeline, resolver, interface, generic, config
|
||||||
knob, registry. The counters table in `docs/state.md` holds every threshold and is the only place
|
knob, registry. The counters table in `harness/state.md` holds every threshold and is the only place
|
||||||
they are written down: read them, increment them, never anticipate them.
|
they are written down: read them, increment them, never anticipate them.
|
||||||
2. **No new dependency** without an ADR and human approval. Allowlist:
|
2. **No new dependency** without an ADR and human approval. Allowlist:
|
||||||
`scripts/allowed-deps.txt`. Stdlib first, always.
|
`scripts/allowed-deps.txt`. Stdlib first, always.
|
||||||
3. **Surgical diffs.** Only the lines the feature needs. No renames, no reformatting beyond
|
3. **Surgical diffs.** Only the lines the feature needs. No renames, no reformatting beyond
|
||||||
`gofmt`, no "while I was in there". Spotted something bad? Latent list in `docs/state.md`.
|
`gofmt`, no "while I was in there". Spotted something bad? Latent list in `harness/state.md`.
|
||||||
4. **The untrusted boundary is absolute.** Anything not from the site root (comments, webmentions,
|
4. **The untrusted boundary is absolute.** Anything not from the site root (comments, webmentions,
|
||||||
form input) never reaches shortcode or template evaluation. Crossing it needs a plan callout.
|
form input) never reaches shortcode or template evaluation. Crossing it needs a plan callout.
|
||||||
5. **Permalinks are permanent.** A published URL never changes meaning; renames add aliases and
|
5. **Permalinks are permanent.** A published URL never changes meaning; renames add aliases and
|
||||||
permanent redirects. The path shape is decided (ADR-0008) and written in `docs/content-model.md` —
|
permanent redirects. The path shape is decided (ADR-0008) and written in `harness/content-model.md` —
|
||||||
read it before emitting a URL, and never invent a second shape.
|
read it before emitting a URL, and never invent a second shape.
|
||||||
6. **No speculative anything**: no unused parameters, no `interface{}` for flexibility, no "we
|
6. **No speculative anything**: no unused parameters, no `interface{}` for flexibility, no "we
|
||||||
might want to" comments, no one-field options structs, no plugin registry before its counter is due, no
|
might want to" comments, no one-field options structs, no plugin registry before its counter is due, no
|
||||||
@@ -96,14 +96,14 @@ Procedure: `.claude/skills/feature-loop/SKILL.md`. The gates people skip:
|
|||||||
those. Batched, up front, each with a **bold** default so silence answers. There is no cap: a request
|
those. Batched, up front, each with a **bold** default so silence answers. There is no cap: a request
|
||||||
carrying six real forks gets six questions, and splitting them across turns to look brisk wastes more of
|
carrying six real forks gets six questions, and splitting them across turns to look brisk wastes more of
|
||||||
the human's time than asking once. Never about naming, formatting,
|
the human's time than asking once. Never about naming, formatting,
|
||||||
or anything `docs/conventions.md` decides. None to ask? State assumptions in one line and move on.
|
or anything `harness/conventions.md` decides. None to ask? State assumptions in one line and move on.
|
||||||
|
|
||||||
**Verify.** `./scripts/verify.sh --quiet` green, plus one piece of feature-specific evidence you actually
|
**Verify.** `./scripts/verify.sh --quiet` green, plus one piece of feature-specific evidence you actually
|
||||||
ran (golden file, `curl`, test name, benchmark). Never report success from reading your own diff.
|
ran (golden file, `curl`, test name, benchmark). Never report success from reading your own diff.
|
||||||
"Should work" is not a result.
|
"Should work" is not a result.
|
||||||
|
|
||||||
**Commit.** Work lands in git without being asked, every time, before you report. What one commit
|
**Commit.** Work lands in git without being asked, every time, before you report. What one commit
|
||||||
contains is `docs/conventions.md` "Git": the changes that would be reverted together, bundled
|
contains is `harness/conventions.md` "Git": the changes that would be reverted together, bundled
|
||||||
together. Never `git push`, and never `--no-verify` without saying why in the body. Asymmetry is the
|
together. Never `git push`, and never `--no-verify` without saying why in the body. Asymmetry is the
|
||||||
whole reason — a commit is one `git revert` away from undone, while work that only ever existed in
|
whole reason — a commit is one `git revert` away from undone, while work that only ever existed in
|
||||||
the working tree is not recoverable at all.
|
the working tree is not recoverable at all.
|
||||||
@@ -119,8 +119,8 @@ exists; before it, an unannounced commit is a surprise in someone else's reposit
|
|||||||
- [ ] Plan's success criteria demonstrated with real output.
|
- [ ] Plan's success criteria demonstrated with real output.
|
||||||
- [ ] `./scripts/verify.sh` green.
|
- [ ] `./scripts/verify.sh` green.
|
||||||
- [ ] Diff contains nothing outside the planned files.
|
- [ ] Diff contains nothing outside the planned files.
|
||||||
- [ ] `docs/state.md` updated (inventory, counters, latent items, verified-at line).
|
- [ ] `harness/state.md` updated (inventory, counters, latent items, verified-at line).
|
||||||
- [ ] ADR in `docs/decisions.md` if a load-bearing choice was made.
|
- [ ] ADR in `harness/decisions.md` if a load-bearing choice was made.
|
||||||
- [ ] Committed — one revertible unit per commit, announced if this session did not ask for it.
|
- [ ] Committed — one revertible unit per commit, announced if this session did not ask for it.
|
||||||
- [ ] Report states LOC delta, what is now earned, what you deliberately did not do.
|
- [ ] Report states LOC delta, what is now earned, what you deliberately did not do.
|
||||||
|
|
||||||
@@ -138,5 +138,5 @@ Stopping early costs one message. Guessing costs a refactor.
|
|||||||
|
|
||||||
Go stdlib idiom, `net/http` + `html/template`, no framework. `%w` wrapping at package boundaries
|
Go stdlib idiom, `net/http` + `html/template`, no framework. `%w` wrapping at package boundaries
|
||||||
only. `log/slog`. Table-driven tests, golden files in `testdata/`. Explicit wiring in one file, no
|
only. `log/slog`. Table-driven tests, golden files in `testdata/`. Explicit wiring in one file, no
|
||||||
`init()`. Size thresholds live in `scripts/budgets.env` only. Full rules: `docs/conventions.md` —
|
`init()`. Size thresholds live in `scripts/budgets.env` only. Full rules: `harness/conventions.md` —
|
||||||
read them, do not ask.
|
read them, do not ask.
|
||||||
|
|||||||
+25
-22
@@ -4,9 +4,12 @@ Scaffolding that makes an agent build `khosra` the way you want: minimally, surg
|
|||||||
before code, docs that stay true.
|
before code, docs that stay true.
|
||||||
|
|
||||||
`README.md` is a two-line signpost — human here, agent to `CLAUDE.md`. `CLAUDE.md` is the
|
`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
|
constitution (always loaded). `harness/` is what the agent needs to build the
|
||||||
engine — anything in it may be pulled into context on demand. `ideas/` and `reference/` sit outside
|
engine — anything in it may be pulled into context on demand. It is named for its audience, not its
|
||||||
`docs/` deliberately: storage, opened only when you name a file, swept by nothing.
|
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`.
|
`.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,
|
`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
|
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
|
- "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
|
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.
|
working set.
|
||||||
- `/audit` every ~5 features — finds abstractions that never earned their keep.
|
- `/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
|
- `/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
|
**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
|
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
|
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,
|
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).
|
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.
|
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
|
**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
|
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
|
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
|
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.
|
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
|
**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
|
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
|
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
|
the code cannot mislead — an order of magnitude smaller than the source it stands for — and no one has to
|
||||||
remember to run it;
|
remember to run it;
|
||||||
@@ -125,23 +128,23 @@ wanting to emit HTML still renders a theme template instead (ADR-0036).
|
|||||||
In order, cheapest first:
|
In order, cheapest first:
|
||||||
|
|
||||||
1. `./scripts/verify.sh` — one command, tells you whether the thing is still coherent and whether
|
1. `./scripts/verify.sh` — one command, tells you whether the thing is still coherent and whether
|
||||||
`docs/state.md` has fallen behind the code.
|
`harness/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
|
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
|
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.
|
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
|
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.
|
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
|
4. `/refresh-docs` — reconciles every doc against the actual code and reports drift, which is exactly the
|
||||||
question you have after a year.
|
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
|
Each entry names the observation that would overturn it, so you can tell a stale decision from a
|
||||||
deliberate one.
|
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
|
built against and which agent-tooling contracts it assumes, so a tooling change is diagnosable instead
|
||||||
of looking like a harness bug.
|
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
|
## 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
|
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.
|
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
|
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
|
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`
|
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
|
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
|
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
|
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
|
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 —
|
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.
|
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,
|
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.
|
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:
|
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"
|
`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
|
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
|
`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
|
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.
|
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 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
|
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
|
`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`.
|
`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
|
- 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
|
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
|
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.
|
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.
|
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
|
- `./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
|
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
|
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.
|
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.
|
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
|
A new mechanism adds a row; nothing outside a value's home may restate it. A number written twice
|
||||||
eventually disagrees with itself.
|
eventually disagrees with itself.
|
||||||
|
|
||||||
## Open
|
## 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.
|
content file.
|
||||||
- `go.mod` does not exist yet — `go mod init` belongs to the first feature, and the module path is
|
- `go.mod` does not exist yet — `go mod init` belongs to the first feature, and the module path is
|
||||||
still unchosen.
|
still unchosen.
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ verify: ## everything the harness enforces; must be green before a commit
|
|||||||
quiet: ## the same gates, printing only what needs acting on
|
quiet: ## the same gates, printing only what needs acting on
|
||||||
./scripts/verify.sh --quiet
|
./scripts/verify.sh --quiet
|
||||||
|
|
||||||
surface: ## regenerate docs/surface.md, the compressed map of the code
|
surface: ## regenerate harness/surface.md, the compressed map of the code
|
||||||
./scripts/surface.sh --write
|
./scripts/surface.sh --write
|
||||||
|
|
||||||
fmt: ## format all Go source
|
fmt: ## format all Go source
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
# Doc map
|
# Harness doc map
|
||||||
|
|
||||||
Read the one you need. Do not read them all.
|
The docs an agent reads to build the engine. Read the one you need. Do not read them all.
|
||||||
|
|
||||||
|
This directory was `docs/` until ADR-0082 renamed it: `docs/` is reserved for documentation written for
|
||||||
|
whoever *uses* khosra, which does not exist yet. Everything here is engine- and agent-facing — a
|
||||||
|
specification, a rule, or a record — and none of it is written for an author.
|
||||||
|
|
||||||
| Doc | Contains | Mutability |
|
| Doc | Contains | Mutability |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -16,7 +20,7 @@ Read the one you need. Do not read them all.
|
|||||||
| `context-economy.md` | How the agent spends context: what to read, in what form, and the floor no saving may cross | When a technique or a gate for one changes |
|
| `context-economy.md` | How the agent spends context: what to read, in what form, and the floor no saving may cross | When a technique or a gate for one changes |
|
||||||
| `surface.md` | **Generated.** Every top-level declaration with its line — read before opening source | Never by hand; `make surface` |
|
| `surface.md` | **Generated.** Every top-level declaration with its line — read before opening source | Never by hand; `make surface` |
|
||||||
|
|
||||||
Sibling folders sit **outside** `docs/` because they are storage, not working material:
|
Sibling folders sit **outside** `harness/` because they are storage, not working material:
|
||||||
`../ideas/` (parked ideas, any topic, and `exploration.md` — the catalog of engine features nobody has asked
|
`../ideas/` (parked ideas, any topic, and `exploration.md` — the catalog of engine features nobody has asked
|
||||||
for yet) and `../reference/` (durable facts from conversation). Write there when asked to park something;
|
for yet) and `../reference/` (durable facts from conversation). Write there when asked to park something;
|
||||||
open a file only when the human names it. Nothing sweeps them.
|
open a file only when the human names it. Nothing sweeps them.
|
||||||
@@ -24,7 +24,7 @@ In order of cost, stop at the first that answers the question:
|
|||||||
|
|
||||||
| Question | Cheapest answer |
|
| Question | Cheapest answer |
|
||||||
|---|---|
|
|---|---|
|
||||||
| where does X live, what is in this package | `docs/surface.md` (generated; `make surface`) |
|
| where does X live, what is in this package | `harness/surface.md` (generated; `make surface`) |
|
||||||
| what does this package offer callers | `go doc ./internal/<pkg>` |
|
| what does this package offer callers | `go doc ./internal/<pkg>` |
|
||||||
| what does this behave like | its `_test.go` — a table states in 20 lines what 200 implement |
|
| what does this behave like | its `_test.go` — a table states in 20 lines what 200 implement |
|
||||||
| why is it like this | `decisions.md`, `state.md` — never the code |
|
| why is it like this | `decisions.md`, `state.md` — never the code |
|
||||||
@@ -77,7 +77,7 @@ assumption, or a caveat — a report that omits the caveat is not shorter, it is
|
|||||||
|
|
||||||
## What is enforced, and what is not
|
## What is enforced, and what is not
|
||||||
|
|
||||||
Three of these are mechanical. `scripts/hooks/pre-commit` regenerates `docs/surface.md` and stages it,
|
Three of these are mechanical. `scripts/hooks/pre-commit` regenerates `harness/surface.md` and stages it,
|
||||||
so no commit can carry a stale one, and `verify.sh` compares independently for the clone that never set
|
so no commit can carry a stale one, and `verify.sh` compares independently for the clone that never set
|
||||||
`core.hooksPath`. `CLAUDE_LOC_MAX` bounds the one file billed on every turn. `--quiet` exists to be
|
`core.hooksPath`. `CLAUDE_LOC_MAX` bounds the one file billed on every turn. `--quiet` exists to be
|
||||||
used, and the hook uses it.
|
used, and the hook uses it.
|
||||||
@@ -244,7 +244,7 @@ Date: 2026-07-28 · Status: accepted
|
|||||||
Decision: engine source, content, and theme are three separate surfaces with three separate owners. This
|
Decision: engine source, content, and theme are three separate surfaces with three separate owners. This
|
||||||
repository holds the engine and is bound to the **theme contract** — the data available to templates, the
|
repository holds the engine and is bound to the **theme contract** — the data available to templates, the
|
||||||
template and block names it looks for, the helpers it provides, the URLs it emits — recorded in
|
template and block names it looks for, the helpers it provides, the URLs it emits — recorded in
|
||||||
`docs/theme-contract.md`. It is not bound to any theme's markup, layout, or styling. Where a theme comes
|
`harness/theme-contract.md`. It is not bound to any theme's markup, layout, or styling. Where a theme comes
|
||||||
from (a directory in the site root, its own repo checked out into place) is deliberately outside this
|
from (a directory in the site root, its own repo checked out into place) is deliberately outside this
|
||||||
repo's concern: the engine knows a path and a contract.
|
repo's concern: the engine knows a path and a contract.
|
||||||
Why: a request that reads as one feature is often split — "supporting files listed in a sidebar" is a
|
Why: a request that reads as one feature is often split — "supporting files listed in a sidebar" is a
|
||||||
@@ -287,7 +287,7 @@ contract executable: a bare site root renders, and a golden-file test through it
|
|||||||
regressions. It is deliberately not a design: legibility only, no branding, no visual opinions, and it
|
regressions. It is deliberately not a design: legibility only, no branding, no visual opinions, and it
|
||||||
demonstrates every contract feature and nothing more.
|
demonstrates every contract feature and nothing more.
|
||||||
Why: a contract nobody implements is a contract nobody has tested. Without a reference theme, the first
|
Why: a contract nobody implements is a contract nobody has tested. Without a reference theme, the first
|
||||||
real theme discovers the contract's gaps, and `docs/theme-contract.md` stays aspirational. It also means
|
real theme discovers the contract's gaps, and `harness/theme-contract.md` stays aspirational. It also means
|
||||||
someone can point the binary at a folder of Markdown and see a site, which is the whole promise.
|
someone can point the binary at a folder of Markdown and see a site, which is the whole promise.
|
||||||
Consequence: cheap — templates and CSS are not Go, so they cost nothing against `CORE_LOC_MAX`, which is
|
Consequence: cheap — templates and CSS are not Go, so they cost nothing against `CORE_LOC_MAX`, which is
|
||||||
the same incentive that pushes presentation out of the core. Expensive — the reference theme is a
|
the same incentive that pushes presentation out of the core. Expensive — the reference theme is a
|
||||||
@@ -794,10 +794,10 @@ tweak to this one.
|
|||||||
|
|
||||||
## ADR-0053 — Context economy is a doc with a floor, plus three gates
|
## ADR-0053 — Context economy is a doc with a floor, plus three gates
|
||||||
Date: 2026-08-01 · Status: accepted (adopts `ideas/token-conservation.md`)
|
Date: 2026-08-01 · Status: accepted (adopts `ideas/token-conservation.md`)
|
||||||
Decision: `docs/context-economy.md` owns how the agent spends context — read the compressed form first,
|
Decision: `harness/context-economy.md` owns how the agent spends context — read the compressed form first,
|
||||||
fewer turns before fewer bytes, script anything repeatable, shrink output at the source, never pay twice
|
fewer turns before fewer bytes, script anything repeatable, shrink output at the source, never pay twice
|
||||||
for the same bytes. Three parts are mechanical rather than remembered: `scripts/surface.sh` generates
|
for the same bytes. Three parts are mechanical rather than remembered: `scripts/surface.sh` generates
|
||||||
`docs/surface.md`, the pre-commit hook regenerates and stages it while `verify.sh` compares it
|
`harness/surface.md`, the pre-commit hook regenerates and stages it while `verify.sh` compares it
|
||||||
independently, `CLAUDE_LOC_MAX=150` bounds the file re-sent every turn, and `verify.sh --quiet` prints
|
independently, `CLAUDE_LOC_MAX=150` bounds the file re-sent every turn, and `verify.sh --quiet` prints
|
||||||
only what needs acting on (the hook uses it). Generated artifacts are produced by the hook, never by
|
only what needs acting on (the hook uses it). Generated artifacts are produced by the hook, never by
|
||||||
memory; the hook refuses a commit with unstaged `.go` changes, since what it generated describes the
|
memory; the hook refuses a commit with unstaged `.go` changes, since what it generated describes the
|
||||||
@@ -887,7 +887,7 @@ one pointer, and `web.Handler` takes an accessor for it instead of two arguments
|
|||||||
|
|
||||||
## ADR-0057 — `state.md`'s currency is compared, not declared
|
## ADR-0057 — `state.md`'s currency is compared, not declared
|
||||||
Date: 2026-08-01 · Status: accepted (retires the `verified against` sha the harness carried from day one)
|
Date: 2026-08-01 · Status: accepted (retires the `verified against` sha the harness carried from day one)
|
||||||
Decision: `verify.sh` decides whether `docs/state.md` is current by comparing the last commit that touched
|
Decision: `verify.sh` decides whether `harness/state.md` is current by comparing the last commit that touched
|
||||||
it against the last commit that touched a `.go` file — current when the doc's commit is the same or newer.
|
it against the last commit that touched a `.go` file — current when the doc's commit is the same or newer.
|
||||||
The hand-written `**Verified against:** <sha>` line is deleted, and no commit exists solely to write one.
|
The hand-written `**Verified against:** <sha>` line is deleted, and no commit exists solely to write one.
|
||||||
Why: the sha could only ever be wrong. A commit cannot name itself, so the line had to be written *after*
|
Why: the sha could only ever be wrong. A commit cannot name itself, so the line had to be written *after*
|
||||||
@@ -1412,3 +1412,32 @@ A broken template serves its own source rather than 404ing, because a promised a
|
|||||||
worse than one answering unrendered (ADR-0029).
|
worse than one answering unrendered (ADR-0029).
|
||||||
Revisit if: a feature needs a path *prefix* rather than exact paths, or two features claim the same path —
|
Revisit if: a feature needs a path *prefix* rather than exact paths, or two features claim the same path —
|
||||||
neither is expressible today, and both would want the resolver rather than the mux.
|
neither is expressible today, and both would want the resolver rather than the mux.
|
||||||
|
|
||||||
|
## ADR-0082 — `docs/` becomes `harness/`, and `docs/` is reserved for the reader
|
||||||
|
Date: 2026-08-02 · Status: accepted
|
||||||
|
Decision: the twelve engine- and agent-facing documents move from `docs/` to `harness/`. `docs/` is
|
||||||
|
reserved for documentation written for whoever *uses* khosra and is deliberately left absent until it
|
||||||
|
exists. `HARNESS.md` stays at the repository 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 collide with it for no gain.
|
||||||
|
Why: `docs/content-model.md` opens with "Engine specification" and is where the rule that a leading
|
||||||
|
underscore makes a file unaddressable is written. 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, and the name is the part
|
||||||
|
that misleads: an author looking for how to write a post opens `docs/` and finds a specification for the
|
||||||
|
person implementing it.
|
||||||
|
Naming the directory for its audience makes the gap visible instead of hiding it. An empty `docs/` is an
|
||||||
|
honest statement that end-user documentation does not exist; `docs/` full of engine specs was a claim that
|
||||||
|
it did.
|
||||||
|
Consequence: mechanical and wide — 100 path references across 24 files, every gate in `verify.sh` that
|
||||||
|
names a doc by path, `surface.sh`'s output target, the pre-commit hook, `CLAUDE.md`'s read order, and the
|
||||||
|
directory lists the dangling-path and ADR-number gates scan. No rule, threshold, gate or obligation
|
||||||
|
changed: this is a rename, and the gates proved it by staying green with the new paths and by catching this
|
||||||
|
very ADR's number before it existed.
|
||||||
|
An end-user documentation site is planned and deliberately not built here: it wants its own decision about
|
||||||
|
where it lives and whether its claims are gated, and the human deferred both. `examples/demo-site` was
|
||||||
|
also considered for a move under `docs/` and deferred with it, since it is a live site root that
|
||||||
|
`verify.sh`, the coverage test and `make demo` all point at — moving it would couple a rename to a design
|
||||||
|
nobody has made.
|
||||||
|
Revisit if: `docs/` is still empty when the first person other than its author tries to use this engine, at
|
||||||
|
which point the absence has stopped being honest and become neglect.
|
||||||
@@ -7,7 +7,11 @@ If this file disagrees with the code, the code is right and this file is a bug.
|
|||||||
|
|
||||||
## Inventory
|
## Inventory
|
||||||
|
|
||||||
Line counts are **not** here: `docs/surface.md` is generated from the code and carries per-file sizes
|
The engine- and agent-facing docs live in `harness/`, not `docs/` (ADR-0082). `docs/` is reserved for
|
||||||
|
documentation aimed at whoever uses khosra and does not exist yet — an end-user documentation site is
|
||||||
|
planned, with its home and whether its claims are gated both undecided.
|
||||||
|
|
||||||
|
Line counts are **not** here: `harness/surface.md` is generated from the code and carries per-file sizes
|
||||||
plus every declaration's location (ADR-0053). This column drifted on six files before it was removed,
|
plus every declaration's location (ADR-0053). This column drifted on six files before it was removed,
|
||||||
which is what a number written in two places always does. What each file is *for* is the fact this
|
which is what a number written in two places always does. What each file is *for* is the fact this
|
||||||
table owns.
|
table owns.
|
||||||
@@ -166,7 +170,7 @@ Every ADR in `decisions.md` is accepted; none is open or proposed.
|
|||||||
|
|
||||||
Working plan lives in `.scratch/continue.md`, which is deliberately not committed — `git log` is the
|
Working plan lives in `.scratch/continue.md`, which is deliberately not committed — `git log` is the
|
||||||
record of what actually landed, and the handoff is read *against* the log rather than instead of it
|
record of what actually landed, and the handoff is read *against* the log rather than instead of it
|
||||||
(`docs/README.md`). If that file is absent, read the log and rebuild the plan from it.
|
(`harness/README.md`). If that file is absent, read the log and rebuild the plan from it.
|
||||||
|
|
||||||
## Arc retro log
|
## Arc retro log
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ Only with a declared `base`: without one there is no feed to point at.
|
|||||||
|
|
||||||
The engine supplies facts it alone can produce — which bundles exist, what a picture measures, where a
|
The engine supplies facts it alone can produce — which bundles exist, what a picture measures, where a
|
||||||
permalink points, what a month is called in Bengali. Everything about how those facts *look* is yours, and the
|
permalink points, what a month is called in Bengali. Everything about how those facts *look* is yours, and the
|
||||||
engine is audited against that line (ADR-0046, `docs/architecture.md`). Two consequences worth knowing:
|
engine is audited against that line (ADR-0046, `harness/architecture.md`). Two consequences worth knowing:
|
||||||
|
|
||||||
- Where a listing offers more than one shape of the same data — `.Items` and `.Groups` — pick one. The engine
|
- Where a listing offers more than one shape of the same data — `.Items` and `.Groups` — pick one. The engine
|
||||||
is not telling you which.
|
is not telling you which.
|
||||||
+5
-5
@@ -11,8 +11,8 @@ re-derivation** — the whole justification for writing it while the context is
|
|||||||
| an engine feature you might build (webmentions, gemtext, galleries) | `ideas/exploration.md` catalog line, `/leaf` for a verdict |
|
| an engine feature you might build (webmentions, gemtext, galleries) | `ideas/exploration.md` catalog line, `/leaf` for a verdict |
|
||||||
| that same feature, but with a discussion worth not repeating | an idea file here, linked from the catalog line |
|
| that same feature, but with a discussion worth not repeating | an idea file here, linked from the catalog line |
|
||||||
| the harness, agent workflow, token cost, tooling, process | here — `ideas/exploration.md` is engine-only |
|
| the harness, agent workflow, token cost, tooling, process | here — `ideas/exploration.md` is engine-only |
|
||||||
| a decision already made | `docs/decisions.md` |
|
| a decision already made | `harness/decisions.md` |
|
||||||
| a known code flaw deliberately unfixed | Latent list in `docs/state.md` |
|
| a known code flaw deliberately unfixed | Latent list in `harness/state.md` |
|
||||||
| a durable fact, number, or link | `reference/` |
|
| a durable fact, number, or link | `reference/` |
|
||||||
|
|
||||||
## Format
|
## Format
|
||||||
@@ -43,7 +43,7 @@ you, not the agent; `verify.sh` keeps it honest without reading it into context.
|
|||||||
- [specs-as-secondary-artifacts.md](specs-as-secondary-artifacts.md) — optional per-feature specs, derived by default, plus the named-test convention. **parked**
|
- [specs-as-secondary-artifacts.md](specs-as-secondary-artifacts.md) — optional per-feature specs, derived by default, plus the named-test convention. **parked**
|
||||||
- [engine-design-review.md](engine-design-review.md) — open design decisions for a multi-type site; items graduate to ADRs one at a time. **parked**
|
- [engine-design-review.md](engine-design-review.md) — open design decisions for a multi-type site; items graduate to ADRs one at a time. **parked**
|
||||||
- [exploration.md](exploration.md) — the catalog of engine features nobody has asked for, with leaf/trunk verdicts. **catalog**
|
- [exploration.md](exploration.md) — the catalog of engine features nobody has asked for, with leaf/trunk verdicts. **catalog**
|
||||||
- [token-conservation.md](token-conservation.md) — cut agent token cost without losing output quality. **adopted → `docs/context-economy.md`**, except the subagent question
|
- [token-conservation.md](token-conservation.md) — cut agent token cost without losing output quality. **adopted → `harness/context-economy.md`**, except the subagent question
|
||||||
|
|
||||||
It lives here rather than in `docs/` because a catalogue of things nobody has asked for is storage, not
|
It lives here rather than in `harness/` because a catalogue of things nobody has asked for is storage, not
|
||||||
working material: keeping it out of `docs/` keeps it out of the set an agent may pull in on demand.
|
working material: keeping it out of `harness/` keeps it out of the set an agent may pull in on demand.
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ sneakernet and QR offline distribution.
|
|||||||
|
|
||||||
## Shapes recorded before anything asked for them
|
## Shapes recorded before anything asked for them
|
||||||
|
|
||||||
Moved out of `docs/content-model.md`, which now describes only what the parser accepts. Each is
|
Moved out of `harness/content-model.md`, which now describes only what the parser accepts. Each is
|
||||||
intent, not a plan: the shape is decided by whatever eventually builds it, and being written here is not
|
intent, not a plan: the shape is decided by whatever eventually builds it, and being written here is not
|
||||||
permission.
|
permission.
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ implemented — flowing upward into the harness where it touches anything the ha
|
|||||||
- Tests are mandatory for `cmd/` and `internal/` changes but nothing ties a test to the rule it verifies.
|
- Tests are mandatory for `cmd/` and `internal/` changes but nothing ties a test to the rule it verifies.
|
||||||
`content-model.md` says a bundle key excludes language, and that a bundle supplying both `about.md`
|
`content-model.md` says a bundle key excludes language, and that a bundle supplying both `about.md`
|
||||||
and `about.en.md` is rejected — nothing checks a test exists for either.
|
and `about.en.md` is rejected — nothing checks a test exists for either.
|
||||||
- One fact, one place is enforced throughout (`docs/README.md` authority table). Specs must not become a
|
- One fact, one place is enforced throughout (`harness/README.md` authority table). Specs must not become a
|
||||||
fourth copy of decisions.
|
fourth copy of decisions.
|
||||||
|
|
||||||
## The non-overlapping slot
|
## The non-overlapping slot
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Token conservation
|
# Token conservation
|
||||||
|
|
||||||
Status: adopted → `docs/context-economy.md` + ADR-0053 (2026-08-01). All six mechanisms landed, plus
|
Status: adopted → `harness/context-economy.md` + ADR-0053 (2026-08-01). All six mechanisms landed, plus
|
||||||
the compressed-form, machine-discovery and quiet-output disciplines the human added. The open question
|
the compressed-form, machine-discovery and quiet-output disciplines the human added. The open question
|
||||||
below is the only part still undecided; it is also recorded in `state.md` "Open questions".
|
below is the only part still undecided; it is also recorded in `state.md` "Open questions".
|
||||||
Raised: 2026-07-28
|
Raised: 2026-07-28
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// nothing about HTTP.
|
// nothing about HTTP.
|
||||||
//
|
//
|
||||||
// The embedded templates and stylesheet are the reference theme (ADR-0026) — a demonstration of
|
// The embedded templates and stylesheet are the reference theme (ADR-0026) — a demonstration of
|
||||||
// docs/theme-contract.md, not a design. Fields a template may rely on are listed there.
|
// harness/theme-contract.md, not a design. Fields a template may rely on are listed there.
|
||||||
package render
|
package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// The types in this file are the theme contract in Go: what a template receives, nothing about how it is
|
// The types in this file are the theme contract in Go: what a template receives, nothing about how it is
|
||||||
// produced. Split from render.go when that file crossed the size warning — the seam was already here, since
|
// produced. Split from render.go when that file crossed the size warning — the seam was already here, since
|
||||||
// docs/theme-contract.md describes exactly this and nothing else.
|
// harness/theme-contract.md describes exactly this and nothing else.
|
||||||
|
|
||||||
package render
|
package render
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -3,8 +3,8 @@
|
|||||||
Durable information surfaced in conversation that would otherwise be lost: measured numbers, external
|
Durable information surfaced in conversation that would otherwise be lost: measured numbers, external
|
||||||
constraints, how a tool actually behaves, links worth having. One file per subject.
|
constraints, how a tool actually behaves, links worth having. One file per subject.
|
||||||
|
|
||||||
Not decisions (`docs/decisions.md`), not proposals (`ideas/`), not the present state of the code
|
Not decisions (`harness/decisions.md`), not proposals (`ideas/`), not the present state of the code
|
||||||
(`docs/state.md`). A fact only true today belongs in `state.md`; a rule belongs in the doc owning the
|
(`harness/state.md`). A fact only true today belongs in `state.md`; a rule belongs in the doc owning the
|
||||||
topic.
|
topic.
|
||||||
|
|
||||||
Each file states **how the fact was established** — measured, read from a spec, or asserted — so a
|
Each file states **how the fact was established** — measured, read from a spec, or asserted — so a
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Non-stdlib dependency allowlist. One module path per line; # starts a comment.
|
# Non-stdlib dependency allowlist. One module path per line; # starts a comment.
|
||||||
# Adding a line requires an ADR in docs/decisions.md. Stdlib first, always.
|
# Adding a line requires an ADR in harness/decisions.md. Stdlib first, always.
|
||||||
# Only direct requirements are checked here; the total module count is capped by DEPS_MAX.
|
# Only direct requirements are checked here; the total module count is capped by DEPS_MAX.
|
||||||
# Infrastructure clients (Redis, S3, search) are dependencies like any other and get no exemption.
|
# Infrastructure clients (Redis, S3, search) are dependencies like any other and get no exemption.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -35,4 +35,4 @@ CLAUDE_LOC_MAX=150 # CLAUDE.md only — the one file billed on every turn (A
|
|||||||
# CLAUDE_LOC_MAX is a different kind of budget: every other ceiling here bills once, when someone reads
|
# CLAUDE_LOC_MAX is a different kind of budget: every other ceiling here bills once, when someone reads
|
||||||
# the code, while CLAUDE.md is re-sent on every turn of every session. 150 leaves ~20 lines of headroom
|
# the code, while CLAUDE.md is re-sent on every turn of every session. 150 leaves ~20 lines of headroom
|
||||||
# over the current file. Reaching it means moving detail to the doc that owns the topic, never deleting
|
# over the current file. Reaching it means moving detail to the doc that owns the topic, never deleting
|
||||||
# a rule to fit — `docs/README.md`'s compression contract governs which of the two you are doing.
|
# a rule to fit — `harness/README.md`'s compression contract governs which of the two you are doing.
|
||||||
|
|||||||
+4
-4
@@ -3,7 +3,7 @@
|
|||||||
# with the line to jump to.
|
# with the line to jump to.
|
||||||
#
|
#
|
||||||
# Usage: scripts/surface.sh print it
|
# Usage: scripts/surface.sh print it
|
||||||
# scripts/surface.sh --write regenerate docs/surface.md (`make surface`)
|
# scripts/surface.sh --write regenerate harness/surface.md (`make surface`)
|
||||||
#
|
#
|
||||||
# Why this exists: reading a 400-line file to learn what is in it costs forty times what reading its
|
# Why this exists: reading a 400-line file to learn what is in it costs forty times what reading its
|
||||||
# surface costs, and the answer to "where does X live" is almost never in the bodies. `go doc` already
|
# surface costs, and the answer to "where does X live" is almost never in the bodies. `go doc` already
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
# lives — nothing outside cmd/ has many exported names by design.
|
# lives — nothing outside cmd/ has many exported names by design.
|
||||||
#
|
#
|
||||||
# Generated, never hand-edited, and gated: verify.sh regenerates and compares, so a stale surface fails
|
# Generated, never hand-edited, and gated: verify.sh regenerates and compares, so a stale surface fails
|
||||||
# the build rather than misleading a reader. Purposes and intent stay in docs/state.md — this file is
|
# the build rather than misleading a reader. Purposes and intent stay in harness/state.md — this file is
|
||||||
# mechanical and says only what is there.
|
# mechanical and says only what is there.
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
cd "$(dirname "$0")/.." || exit 1
|
cd "$(dirname "$0")/.." || exit 1
|
||||||
@@ -48,8 +48,8 @@ emit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if [ "${1:-}" = "--write" ]; then
|
if [ "${1:-}" = "--write" ]; then
|
||||||
emit >docs/surface.md
|
emit >harness/surface.md
|
||||||
printf 'wrote docs/surface.md (%s lines)\n' "$(wc -l <docs/surface.md | tr -d ' ')"
|
printf 'wrote harness/surface.md (%s lines)\n' "$(wc -l <harness/surface.md | tr -d ' ')"
|
||||||
else
|
else
|
||||||
emit
|
emit
|
||||||
fi
|
fi
|
||||||
|
|||||||
+18
-18
@@ -51,8 +51,8 @@ if [ -d .git ] && command -v git >/dev/null 2>&1; then
|
|||||||
if [ -z "$changed" ]; then
|
if [ -z "$changed" ]; then
|
||||||
pass "working tree clean — nothing to couple"
|
pass "working tree clean — nothing to couple"
|
||||||
else
|
else
|
||||||
if echo "$changed" | grep -qE '\.go$' && ! echo "$changed" | grep -qx 'docs/state.md'; then
|
if echo "$changed" | grep -qE '\.go$' && ! echo "$changed" | grep -qx 'harness/state.md'; then
|
||||||
bad "*.go changed but docs/state.md did not — inventory, counters and latent items move with the code"
|
bad "*.go changed but harness/state.md did not — inventory, counters and latent items move with the code"
|
||||||
else
|
else
|
||||||
pass "code/state.md coupling"
|
pass "code/state.md coupling"
|
||||||
fi
|
fi
|
||||||
@@ -77,8 +77,8 @@ if [ -d .git ] && command -v git >/dev/null 2>&1; then
|
|||||||
pass "code/test coupling"
|
pass "code/test coupling"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if echo "$changed" | grep -qE '^internal/.*templates/.*\.html$' && ! echo "$changed" | grep -qx 'docs/theme-contract.md'; then
|
if echo "$changed" | grep -qE '^internal/.*templates/.*\.html$' && ! echo "$changed" | grep -qx 'harness/theme-contract.md'; then
|
||||||
bad "embedded templates changed but docs/theme-contract.md did not — they drift together (ADR-0023)"
|
bad "embedded templates changed but harness/theme-contract.md did not — they drift together (ADR-0023)"
|
||||||
else
|
else
|
||||||
pass "templates/theme-contract coupling"
|
pass "templates/theme-contract coupling"
|
||||||
fi
|
fi
|
||||||
@@ -133,28 +133,28 @@ if [ -d .git ] && command -v git >/dev/null 2>&1; then
|
|||||||
|
|
||||||
# Dangling references. Every one of these found a real stale pointer when run by hand.
|
# Dangling references. Every one of these found a real stale pointer when run by hand.
|
||||||
# `.scratch/` is included only when it exists: it is gitignored, so on a fresh clone the handoff is
|
# `.scratch/` is included only when it exists: it is gitignored, so on a fresh clone the handoff is
|
||||||
# legitimately absent (docs/README.md says "if present") and checking it there would fail correct
|
# legitimately absent (harness/README.md says "if present") and checking it there would fail correct
|
||||||
# work. Once the directory is present, a named file missing from it is a stale pointer like any
|
# work. Once the directory is present, a named file missing from it is a stale pointer like any
|
||||||
# other — which is how state.md's pointer to a deleted build-queue outlived it by two commits. That
|
# other — which is how state.md's pointer to a deleted build-queue outlived it by two commits. That
|
||||||
# path is named without backticks on purpose: this gate reads its own file, so the example would be a
|
# path is named without backticks on purpose: this gate reads its own file, so the example would be a
|
||||||
# reference, and the gate would flag the comment explaining it.
|
# reference, and the gate would flag the comment explaining it.
|
||||||
scratchpat=""
|
scratchpat=""
|
||||||
[ -d .scratch ] && scratchpat='|\.scratch'
|
[ -d .scratch ] && scratchpat='|\.scratch'
|
||||||
refs=$(grep -rhoE "\`(docs|scripts|ideas|reference|\.claude$scratchpat)/[A-Za-z0-9_./-]+\`" \
|
refs=$(grep -rhoE "\`(harness|scripts|ideas|reference|\.claude$scratchpat)/[A-Za-z0-9_./-]+\`" \
|
||||||
docs CLAUDE.md HARNESS.md ideas reference .claude scripts 2>/dev/null | tr -d '`' | sort -u)
|
harness CLAUDE.md HARNESS.md ideas reference .claude scripts 2>/dev/null | tr -d '`' | sort -u)
|
||||||
dangling=""
|
dangling=""
|
||||||
for f in $refs; do [ -e "$f" ] || dangling="$dangling $f"; done
|
for f in $refs; do [ -e "$f" ] || dangling="$dangling $f"; done
|
||||||
[ -n "$dangling" ] && bad "reference to a path that does not exist:$dangling"
|
[ -n "$dangling" ] && bad "reference to a path that does not exist:$dangling"
|
||||||
|
|
||||||
adrs=$(grep -rhoE 'ADR-[0-9]{4}' docs CLAUDE.md HARNESS.md ideas reference .claude scripts cmd internal 2>/dev/null | sort -u)
|
adrs=$(grep -rhoE 'ADR-[0-9]{4}' harness CLAUDE.md HARNESS.md ideas reference .claude scripts cmd internal 2>/dev/null | sort -u)
|
||||||
missingadr=""
|
missingadr=""
|
||||||
for a in $adrs; do
|
for a in $adrs; do
|
||||||
# the log registers every number ever used — as an entry, or in the withdrawn line
|
# the log registers every number ever used — as an entry, or in the withdrawn line
|
||||||
grep -q "$a" docs/decisions.md 2>/dev/null || missingadr="$missingadr $a"
|
grep -q "$a" harness/decisions.md 2>/dev/null || missingadr="$missingadr $a"
|
||||||
done
|
done
|
||||||
[ -n "$missingadr" ] && bad "reference to an ADR with no entry in decisions.md:$missingadr"
|
[ -n "$missingadr" ] && bad "reference to an ADR with no entry in decisions.md:$missingadr"
|
||||||
|
|
||||||
secs=$(grep -rhoE 'CLAUDE\.md`? §[0-9]+' docs HARNESS.md ideas reference .claude scripts 2>/dev/null |
|
secs=$(grep -rhoE 'CLAUDE\.md`? §[0-9]+' harness HARNESS.md ideas reference .claude scripts 2>/dev/null |
|
||||||
grep -oE '§[0-9]+' | tr -d '§' | sort -u)
|
grep -oE '§[0-9]+' | tr -d '§' | sort -u)
|
||||||
missingsec=""
|
missingsec=""
|
||||||
for s in $secs; do
|
for s in $secs; do
|
||||||
@@ -183,12 +183,12 @@ if [ -d .git ] && command -v git >/dev/null 2>&1; then
|
|||||||
# doc update had to trail the code in a commit of its own, and folding the two together then left the
|
# doc update had to trail the code in a commit of its own, and folding the two together then left the
|
||||||
# sha naming a commit that no longer existed (ADR-0057).
|
# sha naming a commit that no longer existed (ADR-0057).
|
||||||
lastgo=$(git log -1 --format=%H -- '*.go' 2>/dev/null)
|
lastgo=$(git log -1 --format=%H -- '*.go' 2>/dev/null)
|
||||||
laststate=$(git log -1 --format=%H -- docs/state.md 2>/dev/null)
|
laststate=$(git log -1 --format=%H -- harness/state.md 2>/dev/null)
|
||||||
if [ -n "$lastgo" ]; then
|
if [ -n "$lastgo" ]; then
|
||||||
if [ -n "$laststate" ] && git merge-base --is-ancestor "$lastgo" "$laststate" 2>/dev/null; then
|
if [ -n "$laststate" ] && git merge-base --is-ancestor "$lastgo" "$laststate" 2>/dev/null; then
|
||||||
pass "docs/state.md is current with the code"
|
pass "harness/state.md is current with the code"
|
||||||
else
|
else
|
||||||
note "docs/state.md was last updated in ${laststate:-no commit}, older than the .go change in $(git log -1 --format=%h -- '*.go')"
|
note "harness/state.md was last updated in ${laststate:-no commit}, older than the .go change in $(git log -1 --format=%h -- '*.go')"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
# Every counter row says what does *not* count (ADR-0070). Four counters were re-scoped on first contact,
|
# Every counter row says what does *not* count (ADR-0070). Four counters were re-scoped on first contact,
|
||||||
@@ -199,7 +199,7 @@ if [ -d .git ] && command -v git >/dev/null 2>&1; then
|
|||||||
name=$2; gsub(/^[ \t]+|[ \t]+$/, "", name);
|
name=$2; gsub(/^[ \t]+|[ \t]+$/, "", name);
|
||||||
last=$6; gsub(/^[ \t]+|[ \t]+$/, "", last);
|
last=$6; gsub(/^[ \t]+|[ \t]+$/, "", last);
|
||||||
if (last == "") print name
|
if (last == "") print name
|
||||||
} on && !/^\|/{on=0}' docs/state.md)
|
} on && !/^\|/{on=0}' harness/state.md)
|
||||||
if [ -n "$counters" ]; then
|
if [ -n "$counters" ]; then
|
||||||
bad "counter row with no 'does not count' (ADR-0070): $(echo "$counters" | tr '\n' ';')"
|
bad "counter row with no 'does not count' (ADR-0070): $(echo "$counters" | tr '\n' ';')"
|
||||||
else
|
else
|
||||||
@@ -209,10 +209,10 @@ if [ -d .git ] && command -v git >/dev/null 2>&1; then
|
|||||||
# The generated surface is only worth reading if it cannot be wrong. Regenerate and compare rather
|
# The generated surface is only worth reading if it cannot be wrong. Regenerate and compare rather
|
||||||
# than trusting that whoever added a function also ran the script.
|
# than trusting that whoever added a function also ran the script.
|
||||||
if [ -x scripts/surface.sh ]; then
|
if [ -x scripts/surface.sh ]; then
|
||||||
if ! scripts/surface.sh | cmp -s - docs/surface.md; then
|
if ! scripts/surface.sh | cmp -s - harness/surface.md; then
|
||||||
bad "docs/surface.md is stale — run 'make surface'"
|
bad "harness/surface.md is stale — run 'make surface'"
|
||||||
else
|
else
|
||||||
pass "docs/surface.md matches the code"
|
pass "harness/surface.md matches the code"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -489,6 +489,6 @@ deadexp=$(echo "$gofiles" | grep '^\./internal/' | grep -v '_test\.go$' | xargs
|
|||||||
done)
|
done)
|
||||||
[ -n "$deadexp" ] && note "exported but referenced once — unexport or delete: $deadexp"
|
[ -n "$deadexp" ] && note "exported but referenced once — unexport or delete: $deadexp"
|
||||||
todos=$(echo "$gofiles" | xargs grep -c 'TODO\|FIXME\|XXX' 2>/dev/null | awk -F: '{s+=$2} END{print s+0}')
|
todos=$(echo "$gofiles" | xargs grep -c 'TODO\|FIXME\|XXX' 2>/dev/null | awk -F: '{s+=$2} END{print s+0}')
|
||||||
[ "${todos:-0}" -gt 0 ] && note "$todos TODO/FIXME markers (latent items belong in docs/state.md)"
|
[ "${todos:-0}" -gt 0 ] && note "$todos TODO/FIXME markers (latent items belong in harness/state.md)"
|
||||||
|
|
||||||
result_and_exit
|
result_and_exit
|
||||||
|
|||||||
Reference in New Issue
Block a user