docs: split the root README into owned documents under docs/ and guard it with drift tests #263
Labels
No labels
bug
enhancement
needs-triage
ready-for-agent
ready-for-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#263
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Split the 3,182-line root
README.mdinto owned documents underdocs/, cut the README back to orientation, and add the guards (drift tests, a CLAUDE.md rule, a docs map) that stop it from inflating again. This issue is the pure move: content changes location, not wording. Correcting and condensing the moved prose is #264.Current behavior
README.mdis 3,182 lines / 185 KB. It began as a 114-line baseline in July, was rewritten to ~360 lines for v1 (ff7ea68), and has since been appended to by nearly every feature commit — 70 commits touch it, with single commits adding 200–490 lines (multilingual4e74536, host navigationc90b868, feature lifecycle0b5423a, bundle dependencies78e181e, categories629834b, editor modules640d8df). Only two commits ever removed more than they added.What it holds is not a README:
## How it works(707 lines) is the HTTP API's wire-shape reference — every JSON key of every endpoint — mixed with design rationale ("Rich text, never raw HTML", "Publish is a background job", "Branch mode", "Media gate").### Host navigation(~480 lines) is a versioned contract with the hosting layer, including a JSON schema "The shape, version 1".## Scaffolding/### Feature bundles(~800 lines) is the bundle author's manual: manifest, dependencies, receipt,harness add/remove, machine-readable output, the shipped bundles.## Configuration referencerepeats sidecar territory thatscaffold/site/AGENTS.mdowns (element sidecar, body policy, page-type, backend layout, editor module, sidecar language files)./api/...routes registered ininternal/server/server.goandinternal/server/mutation.go, 24 are never named (all of/api/media/*,/api/element/body,/api/element/duplicate,/api/element/refs,/api/page/create-root,/api/page/discard,/api/page/move,/api/page/refs,/api/page/reorder,/api/publish/status).Why it happened (the instructions, not the agents): CLAUDE.md has three "MUST update the docs in the same change" rules, all naming
scaffold/site/AGENTS.md, the theme README and the skills; the root README is never mentioned and nothing says what it is for or what belongs elsewhere.docs/holds only mockups; there is no API reference, noCONTEXT.md, nodocs/adr/— thetriageandgrill-with-docsskills route decisions to ADRs that do not exist, so rationale lands in the README. Issue briefs then point at "the README API section" (e.g. #257) because it already is one. The review rule is asymmetric: a change that leaves docs untouched is incomplete, but nothing penalises a doc that is too long or in the wrong file. Theupdate-docsskill has never run (nodocs/.docs-sync, nodocs:commit since July) and its "match surrounding depth, don't restructure" rule would preserve the inflation anyway.Nothing outside the repo links into README anchors (checked the
deckle-hostedimport), and the only in-repo anchor links are the README's own, so sections can move freely.Desired behavior
Every documented fact has exactly one owning document, and the README only points at them. Target layout:
README.md(≈300 lines)# deckleintro,## Quick start,### Releases,## Development,## v1 limitations,## Repository layoutdocs/api.md###-level heading per route (GET /api/page,POST /api/page/save, …) with request and response shape; a short preamble on coded error bodies andAccept-Language## How it works(server-described forms, coded messages, sidecar labels on the wire, translations on the wire, publish job/status/pending), the#### What the editor acceptsroute material,## Multilingual sitesAPI parts,#### Machine-readable outputwhere it describes HTTP rather than CLIdocs/harness.md--jsonoutput shapes, the receipt format,harness.yaml### The ten commands,### Serving the preview behind a reverse proxy,### harness lang,#### Machine-readable output(CLI parts),#### The receipt,### harness.yamldocs/host-navigation.md### Host navigationand all its####childrendocs/features.mdcategoriesandnewsbundles, "Not covered yet"## Scaffoldingand### Feature bundleswith childrendocs/architecture.mddocs/api.md, linked): canonical content engine, sidecars as constraint language, module shell, media, git backbone, branch mode, publish, link check## How it works,## Multilingual sites(model, not API)docs/adr/README.mdstating the format (one dated file per decision: context, decision, consequences). May be otherwise empty after this issue; #264 extracts the decisionsdocs/agents/docs-map.mdupdate-docsskill already looks for: which file owns which kind of fact, and the rule that the README gains no sectionsdocs/.docs-syncupdate-docsreads (line 1 full HEAD SHA, line 2YYYY-MM-DD), written at the end of this changeThe
## Configuration referencesidecar sections (### Element sidecar,### The categories field type,### Body policy,### Page-type sidecar,### Content-root authority,### Backend layout sidecar,### Slots, layout resolution, and inheritance,### Editor module sidecar,### Sidecar language files) are AGENTS.md's territory. Diff each againstscaffold/site/AGENTS.md: a fact AGENTS.md already states is dropped from the README in favour of a link; a fact AGENTS.md lacks is added to AGENTS.md (the CLAUDE.md "skills are procedures, AGENTS.md is the facts" rule) and then dropped. Do not create a third copy indocs/.Guards
Mechanical guards follow the pattern of
internal/element/agentsdoc_test.goandscaffold/skills_test.go:internal/server/server.go,internal/server/mutation.go— find the registration function rather than grepping string literals in tests) must appear as a heading indocs/api.md, and every/api/...heading indocs/api.mdmust be a registered route. Because 24 routes are undocumented today, this test fails untildocs/api.mdhas a heading for each; in this issue each missing route gets a heading with a one-line stub ("Documented in #264") so the test is green and the gap is visible. Writing the real sections is #264.harnessdispatcher (cmd/harness/main.gorun()switch, and thelangsubcommand switch) accepts must be named indocs/harness.md, and vice versa.versionis a command today and the README's "ten commands" block omits it while listing eleven lines — the test should catch exactly this class of drift. Preferred: the command table indocs/harness.mdis checked against theusage()text so the CLI has one owner; acceptable: a name-set comparison.README.mdstays under a line budget (300; pick the number and state it in the test's failure message), contains no/api/path and no fencedjson/yamlblock other thanharness.yaml's minimal example if kept. The failure message must say where the content belongs (the docs map), because the point of this test is to tell the next agent where to put things.[text](docs/...md#anchor)and[text](#anchor)inREADME.mdand indocs/*.mdresolves to an existing file and heading. Extend or mirrorTestSkillSectionReferencesExistinscaffold/skills_test.gorather than writing a second heading-slug implementation (CLAUDE.md "exactly one owner": if the slug rule is needed in two tests, move it to a package both import).CLAUDE.md rule
Add a fourth section to
CLAUDE.md, in the voice and structure of the existing three (## Every fact has one documentor similar), containing:scaffold/site/AGENTS.md, the theme README and the skills so it is the complete map.Skills and process alignment
.claude/skills/update-docs/SKILL.md: it already readsdocs/agents/docs-map.mdanddocs/.docs-sync; both now exist. Add one standing rule: a sync may remove or condense prose that the owning document now carries, and a doc that has grown past its scope is drift to report, not surrounding depth to match..claude/skills/triage/SKILL.md/AGENT-BRIEF.md: the brief's docs line names the owning document from the docs map. One sentence; do not restructure the skill..claude/skills/grill-with-docs/SKILL.mdandtriagealready route ADRs todocs/adr/; verify the path they name matches the directory this issue creates and fix the skill if not.scaffold/site/.claude/skills/*) do not reference the root README (checked: only the theme README and AGENTS.md). Confirm with the drift test and leave them alone.Decisions
git diff --color-movedand the reviewer can confirm nothing was lost or silently changed. Fixing wrong or stale prose, condensing, and extracting ADRs is #264. The only text written fresh in this issue is: the README's new "Documentation" section, each doc's one-paragraph preamble, the route stubs, the docs map, the CLAUDE.md section, the skill sentences.docs/plain Markdown, no docs-site generator — the repo has none and the audience is agents and the maintainer; files and headings are enough and are what the drift tests can check.docs/. The owner is the kind of fact (wire, CLI, host contract, bundle authoring, design), so a new feature touches existing files.agentsdoc_test.goandskills_test.gofor this; a CLAUDE.md sentence alone did not keep the README small and will not.docs/would be the exact "sibling rule" CLAUDE.md forbids.deckle-hosted; rewording it is a contract change and out of scope here.Key interfaces
internal/serverroute registration — the drift test needs the registered set as data; if it is only available asmux.HandleFunccalls inside a constructor, expose the route list (a function returning the patterns) so the test and the server share one owner.cmd/harness/main.gorun()andusage();cmd/harness/lang.gosubcommand switch.scaffold/skills_test.goheading-slug / section-reference helpers — reuse for the cross-link test.internal/element/agentsdoc_test.go— the model for "every X the engine accepts is named in doc Y"..claude/skills/update-docs/SKILL.mdsteps 1 and 3 (baseline file, docs map path).CLAUDE.md— new section after "Every rule has exactly one owner".Acceptance criteria
README.mdis under the budget the shape test states, contains no/api/path, and has a "Documentation" section linkingdocs/api.md,docs/harness.md,docs/host-navigation.md,docs/features.md,docs/architecture.md,docs/adr/,scaffold/site/AGENTS.mdandscaffold/themes/deckle/README.md.docs/*.mdfile, orscaffold/site/AGENTS.md— or is listed in the PR description as dropped because AGENTS.md already stated it (with the AGENTS.md section named).docs/api.mdhas a heading for all 67 registered routes; the 24 previously undocumented ones carry a stub pointing at #264.docs/harness.mdnames every dispatcher command includingversion.CLAUDE.mdhas the new section with the owner table and the planning/grilling sentence.docs/agents/docs-map.mdexists anddocs/.docs-syncholds the merge-ready HEAD and today's date..claude/skills/update-docs/SKILL.mdcarries the condense/remove rule; the triage brief guide names the docs map.go test ./...andscaffold/skills_test.goandinternal/element/agentsdoc_test.goare green;harness checkon a freshly scaffolded site is unchanged.How to verify
git diff --color-moved=dimmed-zebra main..HEAD -- README.md docs/shows moves, not rewrites.wc -l README.mdunder budget;grep -c '/api/' README.mdis 0.go test ./...green; then break each invariant (add a fake route heading todocs/api.md, add a/api/xline to README, remove a command fromdocs/harness.md, break a link) and confirm the matching test fails with a message naming the owning document.go test ./scaffold/ ./internal/element/green.harness initinto a scratch dir,harness checkgreen.Assumptions to check
server.goandmutation.goonly.README.mdanchors — checked against thedeckle-hostedsnapshot at spawn; re-check the live repo's default branch if reachable.scaffold/site/AGENTS.md's drift test does not depend on anything in the root README.## Configuration referencesidecar sections contain nothing AGENTS.md lacks; where they do, the fact moves into AGENTS.md (and its drift test may need the new vocabulary).Out of scope
docs/adr/directory and its format README are created here.Blocked by
None - can start immediately