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>
56 lines
2.8 KiB
Bash
Executable File
56 lines
2.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Generates the compressed form of the code: every package, its size, and every top-level declaration
|
|
# with the line to jump to.
|
|
#
|
|
# Usage: scripts/surface.sh print it
|
|
# 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
|
|
# surface costs, and the answer to "where does X live" is almost never in the bodies. `go doc` already
|
|
# does this for *exported* identifiers; this also covers unexported ones, which is where most of khosra
|
|
# 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
|
|
# 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.
|
|
set -uo pipefail
|
|
cd "$(dirname "$0")/.." || exit 1
|
|
|
|
emit() {
|
|
printf '# Surface\n\n'
|
|
printf 'Generated by `scripts/surface.sh` — do not edit. Regenerate with `make surface`.\n\n'
|
|
printf 'Every top-level declaration in the engine, with its line. Read this before opening a source\n'
|
|
printf 'file: it answers "where does X live" and "what is in this package" without the bodies. What each\n'
|
|
printf 'file is *for* lives in `state.md`; why it is that way lives in `decisions.md`.\n'
|
|
|
|
git ls-files '*.go' | grep -v '_test\.go$' | xargs -n1 dirname | sort -u | while read -r dir; do
|
|
files=$(git ls-files "$dir/*.go" | grep -v '_test\.go$' | sort)
|
|
[ -z "$files" ] && continue
|
|
tests=$(git ls-files "$dir/*_test.go" | sort)
|
|
code=$(echo "$files" | xargs wc -l | awk '$2!="total"{t+=$1} END{print t+0}')
|
|
testloc=0
|
|
[ -n "$tests" ] && testloc=$(echo "$tests" | xargs wc -l | awk '$2!="total"{t+=$1} END{print t+0}')
|
|
printf '\n## %s — %s lines' "$dir" "$code"
|
|
[ "$testloc" -gt 0 ] && printf ' + %s test' "$testloc"
|
|
printf '\n\n'
|
|
# One manifest line per package, so the cost of opening any file is visible before you open it.
|
|
echo "$files" | while read -r f; do printf '%s %s · ' "$(basename "$f")" "$(wc -l <"$f" | tr -d ' ')"; done |
|
|
sed 's/ · $//'
|
|
printf '\n\n'
|
|
for f in $files; do
|
|
# Top-level declarations only: in gofmt'd Go those are exactly the lines starting in column one.
|
|
# The trailing brace and any body opener go, so a signature reads as one line.
|
|
grep -nE '^(func|type|const|var) ' "$f" |
|
|
sed -e 's/ {$//' -e 's/ ($//' -e 's/[[:space:]]*$//' |
|
|
awk -F: -v n="$(basename "$f")" '{ line = $1; $1 = ""; sub(/^:/, ""); sub(/^ +/, ""); printf "- %s:%s %s\n", n, line, $0 }'
|
|
done
|
|
done
|
|
}
|
|
|
|
if [ "${1:-}" = "--write" ]; then
|
|
emit >harness/surface.md
|
|
printf 'wrote harness/surface.md (%s lines)\n' "$(wc -l <harness/surface.md | tr -d ' ')"
|
|
else
|
|
emit
|
|
fi
|