docs-compact ยท diff
git:20260930.a0a686d to git:20260930.2077a57
135 added, 51 removed. Audit A to A.
---
name: docs-compact
description: "Compact the tree's documents to what holds at the head of the main branch: rewrite each ADR to its present decision with a status of one date, remove the ADRs that constrain nothing, cut the changelog to the latest release, clean the rows of specs/architecture.md, and rewrite the comments that tell what the code did before. With --migrations, fold each role's migration chain into one revision under the head's revision id, shown equal by a schema dump. Works on a branch, pushes nothing, and reports what it removed, rewrote, and kept."
- allowed-tools: Read, Grep, Glob, Edit, Write, Bash(git status:*), Bash(git fetch:*), Bash(git symbolic-ref:*), Bash(git switch:*), Bash(git log:*), Bash(git diff:*), Bash(git rev-parse:*), Bash(git merge-base:*), Bash(git ls-files:*), Bash(git rm:*), Bash(git mv:*), Bash(git add:*), Bash(git commit:*), Bash(git restore:*), Bash(git clean:*), Bash(git grep:*), Bash(git tag:*), Bash(diff:*), Bash(mkdir:*), Bash(gh release:*), Bash(gh run:*), Bash(gh api:*), Bash(docker info:*), Bash(docker compose:*), Bash(uv run:*), Bash(make setup), Bash(make check), Bash(make infra-up), Bash(make migrate), Bash(make migrate-check), Bash(make test-integration)
+ allowed-tools: Read, Grep, Glob, Edit, Write, Bash(git status:*), Bash(git fetch:*), Bash(git symbolic-ref:*), Bash(git switch:*), Bash(git log:*), Bash(git diff:*), Bash(git rev-parse:*), Bash(git merge-base:*), Bash(git ls-files:*), Bash(git rm:*), Bash(git mv:*), Bash(git add:*), Bash(git commit:*), Bash(git restore:*), Bash(git clean:*), Bash(git grep:*), Bash(diff:*), Bash(mkdir:*), Bash(gh release:*), Bash(gh run:*), Bash(gh api:*), Bash(docker info:*), Bash(docker compose:*), Bash(uv run:*), Bash(make setup), Bash(make openapi), Bash(make check), Bash(make infra-up), Bash(make migrate)
---
# docs-compact
A document an agent reads says what holds at the head of the main
branch, in the present tense: a README, an ADR, a spec, a comment. What
was stays in git, the pull request, and the release notes (the
guideline's Documentation as Code). An agent acts on what it reads, so
text that also carries what was makes it sort the adopted from the
rejected before it acts. This skill cuts the tree's documents back to
what holds.
One test decides a rejected option. Could a reader at the head reach it
without knowing the past? A simpler design, a library's default, the
obvious other way: yes, so it stays as one sentence in the Decision, as
the rule's near miss with its reason ("a lock bound, never a statement
deadline: a long backfill would fail for running"). A former value, or
an option only the history explains: no, so it goes.
## Input
`[--migrations]`
Without the flag the skill rewrites text only, and never opens
- `om/migrations/`. With it, it also folds each role's migration chain
- into one revision, as step 7 says. It runs at the root of the checkout.
+ `om/migrations/`: a search lists lines of it, as step 6's does, and no
+ file there is read whole or changed. With it, it also folds each role's
+ migration chain into one revision, as step 7 says. It runs at the root
+ of the checkout.
## Role and credential
- None of the platform's: it reads no environment, and logs in to no
- database but the two the fold makes on the local stack and drops. It
- reads the repository host through `gh`, under the person's own sign-in:
- each release's notes, and for the fold each environment's last deploy.
+ None of the platform's: it reads no environment. On the local stack it
+ logs in to the databases it makes and drops, one for the gates and two
+ for the fold, and to the stack's own for one thing: the fold's
+ precondition brings it to its head (`make migrate`). It reads the
+ repository host through `gh`, under the person's own sign-in: each
+ release's notes, and for the fold each environment's last deploy.
## One-time preparations
Each is a person's to do, once. A part whose preparation is missing is
left as it is, and the report names it under Kept.
- **The changelog.** Every release the changelog lists below its latest
has its notes on the repository host (`gh release view v<x.y.z>` exits
0). A release with none keeps its section. The skill saves that
section as `release-v<x.y.z>.md` in the evidence folder, and the
report gives the command that publishes it: `gh release create
v<x.y.z> --verify-tag --notes-file <that file>`.
- **The fold.** Every environment has deployed the commit that holds the
newest migration. The local stack is up (`make infra-up`), with `make
migrate` run once.
## Procedure
1. **Start clean, on a branch.** `git status --porcelain` prints
nothing, or refuse. `git fetch origin`. The main branch is the one
`git symbolic-ref --short refs/remotes/origin/HEAD` names. On it, cut
the work branch, named for the day: `git switch --no-track -c
docs-compact-<yyyy-mm-dd> origin/<main>`. The name is taken when
`git rev-parse --verify --quiet <name>` or `git rev-parse --verify
--quiet origin/<name>` prints a commit: add a suffix then (`-2`). On
another branch, work there only when `git log
--oneline origin/<main>..HEAD` prints nothing; otherwise refuse.
- Make the evidence folder, `mkdir -p
- ~/Downloads/acme_docs_compact_<yyyy-mm-dd>/`.
+ The evidence folder is `~/Downloads/acme_docs_compact_<yyyy-mm-dd>/`
+ and the report `~/Downloads/acme_docs_compact_<yyyy-mm-dd>.md`. When
+ either exists, both take the next free suffix (`_2`), so a run never
+ overwrites another's. Make the folder (`mkdir -p`).
2. **Take the inventory**, and keep it for the report. Read nothing
under `node_modules`, a lock file, a generated file
(`clients/typescript/openapi.json` and the two schemas `make openapi`
writes), or a skill folder that is a link.
- Every `docs/adr/NNNN-*.md`: its status line, its `##` headings, and
- who cites its number (`git grep -n "NNNN"`: the code, the config,
- and the Markdown).
+ who cites it, in the code, the config, and the Markdown. A citation
+ takes one of four forms. `ADR NNNN`, `ADR-NNNN`, or `ADRNNNN`, in a
+ sentence or a comment: the checker reads all three. `adr/NNNN` in
+ a path. `NNNN-` where the file's name begins, as in a link from
+ the ADR beside it. And `NNNN` at a line's start, under a line that
+ ends in `ADR`: a citation the margin wrapped. The digits alone are
+ no citation.
+
+ ```bash
+ git grep -nE 'ADR[- ]?NNNN|adr/NNNN|NNNN-[a-z]' -- . ':!docs/adr/NNNN-*'
+ git grep -nE -B1 '^[^[:alnum:]]*NNNN([^0-9]|$)' -- . ':!docs/adr/NNNN-*'
+ ```
+
+ A hit of the second search is a citation when the line above it,
+ which the search prints, ends in `ADR`.
+
- `CHANGELOG.md`: its release sections, when the tree has one.
- The Substitutions and Deviations rows of `specs/architecture.md`.
- The sentences that tell a past, in the Markdown and in the code's
comments and docstrings. `references/phrases.txt`, in this skill's
folder, lists the phrases that mark one, a pattern a line, each
- matched as whole words:
+ matched as whole words. The pathspec leaves out what the skill
+ never edits: its own folder, `om/migrations/`, the three generated
+ files, and the lock files.
```bash
- git grep -nIiwE -f .agents/skills/docs-compact/references/phrases.txt -- . ':!.agents/skills/docs-compact'
+ git grep -nIiwE -f .agents/skills/docs-compact/references/phrases.txt -- . ':!.agents/skills/docs-compact' ':!om/migrations' ':!clients/typescript/openapi.json' ':!clients/typescript/src/schema.d.ts' ':!clients/python/src/acme/client/schema.py' ':!*.lock' ':!*.lock.hcl' ':!pnpm-lock.yaml'
```
The list is the sweep: the skill reads each hit in its paragraph,
and does not read every file.
3. **Compact the ADRs**, each once, then commit them alone.
- **Rewrite in place** an ADR whose decision holds. Its status is
- `**Status**: accepted (<date>)` and nothing more, with the first
- date the old status names, the day it was accepted. Every later
- date in it goes. It keeps Context, Decision, and Consequences, in
- that order, and any section that states a present fact, such as a
- measurement. A section named for a past (Amended, Closed,
- Alternatives, History) goes: what still holds in it moves into the
- Decision or the Consequences, and a rejected option goes through
- the test above. The text states the decision as it stands, with no
- "since", "no longer", or former value, and names no ticket, pull
- request, or person.
+ `**Status**: accepted (<date>)` and nothing more, with the date
+ the old status gives for its acceptance, wherever in the line it
+ stands. When it gives none, the date is the day the file was added
+ (`git log --follow --diff-filter=A --format=%as -- <file>`). Every
+ other date in the status goes. It keeps Context, Decision, and
+ Consequences, in that order, and any section that states a present
+ fact, such as a measurement. A section named for a past (Amended,
+ Closed, Alternatives, History) goes: what still holds in it moves
+ into the Decision or the Consequences, and a rejected option goes
+ through the test above. The text states the decision as it stands,
+ with no "since", "no longer", or former value, and names no
+ ticket, pull request, or person. A migration's revision id or file
+ name that it cites stays as it is: only a fold moves one, in
+ step 7. An ADR that records an expand and contract takes step 6's
+ test here, before its rewrite: in flight, it keeps every fact of
+ the contract, and with its piece dropped, what it says of the
+ contract's steps goes. That is its one rewrite, in this step's
+ commit.
- **Remove** an ADR that constrains nothing at the head: one replaced
whole, one that is closed, one that records what a release asked or
that a pin moved. A decision in it that still stands moves first,
- into the ADR that records that decision. When no ADR does, the
- decision most cited stays in this file, rewritten, under its
- number. The file then takes that decision's name (`git mv`, to
+ into the ADR that records that decision. A decision no other ADR
+ records stays in this file, rewritten, under its number. When
+ several do, one stays: the one most citations of the number mean,
+ or the first the file states when nothing cites the number or two
+ tie. The file then takes that decision's name (`git mv`, to
`NNNN-<the decision, as a slug>.md`), as any rewritten ADR does
whose name says another subject: the number stays, and every link
and every `adr =` path follows. Each other one gets a new ADR of
its own, numbered in turn: the first one above the highest the tree
held when the run began, the next one above that. A citation of the
kept number that meant a moved decision names the ADR that now
- holds it.
+ holds it. An ADR with no decision standing goes whole.
- **Fold** a substitution that has an ADR of its own into the one ADR
that lists the substitutions, as one row and the sentences that row
needs, and remove the ADR.
- **Re-point every citation** of a removed number before its file
goes (`git rm`): a link in Markdown, an `adr =` entry in
- `pyproject.toml`, an `ADR NNNN` in a comment. Each names the ADR
- that now holds the decision, or goes when what it explained is
+ `pyproject.toml`, and an `ADR NNNN` in a comment, a docstring, or
+ a sentence of prose, a README's or the changelog's. Each names the
+ ADR that now holds the decision, or goes when what it explained is
gone. `arch-check` fails on a number that code cites and no file
- has.
+ has. A citation of a rewritten ADR for a part its text leaves out
+ follows the same rule: it names the ADR or the file that states
+ the fact now, or it goes. A citation in a place the run never
+ edits stays as it is: a file under `om/migrations/`, or a string a
+ statement carries. The ADR it cites is never removed, and keeps
+ the part the citation is for: one this step would remove stays as
+ the run found it, and the report lists it under Kept with each
+ such place.
- An ADR that only restates a rule of the guideline, and decides
nothing of the tree's own, constrains nothing. It is removed when
no code or config cites its number, its links in Markdown going
with it. While one does, it is kept, and listed. An ADR the test
- leaves in doubt is kept and listed too.
+ leaves in doubt is kept and listed too. Either is rewritten in
+ place as this step's first item says, its status one date.
- A file under `docs/adr/` that is not an ADR, such as a picture,
goes when no ADR links it. An ADR never goes for want of a link.
4. **Cut the changelog** to its latest release: the lines above the
first release, that one section, and one line that says every
release's notes stay on the repository host. Each older section goes
- when its preparation holds. Commit.
+ when its preparation holds. A line above the first release that the
+ cut makes false, such as one that says each release has an entry
+ here, is rewritten to what the file holds after the cut. Commit.
5. **Clean the rows** of `specs/architecture.md`: each says what the
substitution or the deviation is, in the present tense, with its end
condition when it has one. A date, a release, and "ended with" go. A
row whose ADR step 3 removed goes, or names the ADR that holds it
now. The pin line stays as it is. Commit.
6. **Rewrite the sentences** of step 2's sweep, each hit judged once.
- A sentence about the code's past goes, or is rewritten to what the
code does: what a former release did, what changed, what a value
once held.
- A sentence about a running system stays: "raises `LeaseLost` when
the item is no longer this worker's" is state, not history. So does
a rule's near miss, and a lens's or a skill's own words for a
breach.
- A comment on code that serves the release before, in an expand and
contract still in flight, stays, in the present tense: what the
- code tolerates, and what ends it. The test: find the commit that
- added the piece (`git log -S'<its name>' --format=%H -- <file>`,
- the last line it prints), and count the release tags that hold it
- (`git tag --contains <commit>`). With fewer than two, an
- environment may still run, or roll back to, the release the piece
- serves: it is in flight. With two or more, it serves a release
- long gone. In doubt, or in a tree that tags no release, it is in
- flight.
- - Only text changes: a comment, a docstring, a document. A name in
- code is code. Code that seems to serve a release long gone is
- listed in the report and left as it is.
+ code tolerates, and what ends it. The test reads the tree alone,
+ and counts no release and no deploy. Name the piece the contract
+ keeps for the release before. While the head holds it, the step
+ that ends the contract has not landed, and it is in flight.
+ - A piece of the schema (a column, a default, a constraint, a
+ trigger, a function): the last up file that names it decides,
+ by the stamp the file's name begins with. The head holds the
+ piece unless that file drops it and does not make it again.
+ `git grep -nwF '<its name>' -- 'om/migrations/sql/*.up.sql'`
+ lists the lines to read.
+ - Any other piece (a field of a response, a setting, a branch of
+ the code) is the code the comment sits on: the head holds it.
+
+ In doubt, it is in flight.
+ - Code whose piece that file dropped serves a release long gone. It
+ is listed in the report and left as it is, its comment with it:
+ the comment says why the code is there, and whoever removes the
+ code removes both. Its ADR changed in step 3, by this test, and is
+ not edited here.
+ - Only text changes: a comment, a docstring, a document, and the
+ `reason` of a checker exception in `pyproject.toml`, which is prose
+ the checker only repeats. A name in code is code, a test's name
+ included: it is the test's identity. So is a string a statement
+ carries: an assert's message, and a field's `description`, which
+ is the wire's document. A client reads it in the API document, so
+ it changes with the API, never with a compaction.
Commit.
7. **With `--migrations`, fold each chain.** Read `references/fold.md`
first, and follow it: the precondition and how each part of it is
read, the two dumps that prove the fold, what else a fold moves, and
its commit. When the precondition does not hold, the chain stays as
it is and the report says which part failed.
8. **Run the gates.** `make setup` first, which installs the tree and
formats its Python; commit what it changes, apart. Then `make
- check`. When `docker info` exits 0, also
- `make infra-up`, `make migrate`, `make migrate-check`, and `make
- test-integration`; otherwise the report names each one skipped. A
- gate that fails is fixed in a commit of its own, and the gate runs
+ openapi`, which writes the API document and its two schemas from the
+ code, a docstring step 6 rewrote included; commit what it changes,
+ apart. Then `make check`. When `docker info` exits 0, also `make
+ infra-up`, and then `make migrate`, `make migrate-check`, and `make
+ test-integration` on a database the run makes and drops, never the
+ stack's own: the integration tests empty every table of the
+ database they run on.
+
+ ```bash
+ uv run python ops/audit/auditdb.py create audit_docs_compact_<yyyymmdd>
+ uv run python -c "import os, subprocess, sys; sys.path.insert(0, 'ops/audit'); import auditdb; os.environ.update(auditdb.urls('audit_docs_compact_<yyyymmdd>')); sys.exit(subprocess.call(['make', 'migrate', 'migrate-check', 'test-integration']))"
+ uv run python ops/audit/auditdb.py drop audit_docs_compact_<yyyymmdd>
+ ```
+
+ `<yyyymmdd>` is today's date in UTC. The name is chosen once,
+ before the run's first create: when `uv run python
+ ops/audit/auditdb.py list` shows it taken then, another run holds
+ it, so add a suffix (`_2`), and never drop a database this run did
+ not make. From its first create on, the name is this run's own. A
+ create that fails can leave the database behind: the targets do not
+ run then, and the drop still does. The second command runs the three
+ targets with the four `ACME_DATABASE_*` URLs set to that database,
+ which the Makefile takes over `.env`. The URLs live in that one
+ command: never export them in the shell, where the unit tests of
+ `make check` would read them. The drop runs whatever the create and
+ the targets answered, so a rerun finds the name free. When `docker
+ info` does not exit 0, the report names each of the four skipped.
+
+ A gate that fails is fixed in a commit of its own, and the gate runs
again from its first command: the first run plus at most 3 reruns,
then stop and say which gate fails and why.
9. **Write the report.**
## Bounds
- One pass: each document is rewritten once in a run. What a second
reading would change is a second run's.
- The gates, and the fold's proof, each take the first run plus at most
3 reruns.
- Its Next is the person's to run, never the session's: it pushes
nothing.
## What it never does
- Never drops a deviation's end condition: "until an operator task
needs a screen" is a trigger, not history.
- Never trims an expand and contract in flight, as step 6 tells one:
its comments, the column, the default, or the trigger it keeps stay
until the contract step lands. Its ADR is rewritten as any other is,
to the present tense and one date, and keeps every fact of the
contract: what the release before still reads or writes, the piece
that serves it, and the step that ends it.
- Never renumbers an ADR, and never gives a removed ADR's number to
another: a removed ADR leaves a gap.
- Never drops a reason that stops a plausible wrong change: it stays as
a near miss.
- Never changes a statement of code. A test changes only with
`--migrations`, and only one that pins a revision the fold removes,
tests a backfill it removes, or steps down from the head.
- Never opens `om/migrations/` without `--migrations`, and never edits
an applied migration file: a fold replaces a chain whole.
- - Never writes to a shared database or to an environment: it logs in
- only to the two databases it made on the local stack, and drops them.
+ - Never writes to a shared database or to an environment, and never
+ runs a test on the local stack's own database: its gates and its fold
+ run on databases it made on the local stack, and drops.
- Never pushes, never opens a pull request, never publishes or edits a
release. It moves no pin.
## Output
- The work branch, one commit per category, and
- `~/Downloads/acme_docs_compact_<yyyy-mm-dd>.md`:
+ The work branch, one commit per category, and the report, under the
+ name step 1 gave it:
```markdown
# Acme: documents compacted to what holds
Branch <name> at <commit>, cut from <main> at <commit>. Migrations: <folded | not asked | kept: which part of the precondition failed>.
| category | removed | rewritten | kept |
|---|---|---|---|
| ADRs | | | |
| Changelog | | | |
| Table rows | | | |
| Comments and documents | | | |
| Migrations | | | |
## Removed
- <ADR NNNN, or a section, a row, a file>: <what held it, and where its live part went>
## Rewritten
- <file>: <what it says now, in a line>
## Kept
- <what, and why: a trigger, an expand and contract in flight, a near miss, a preparation still missing, a doubt, with the question>
## Code that serves a release long gone
- <file:line>: <what it does, and what would remove it>
## The fold (with `--migrations`)
| role | head id | revisions folded | backfills left out | dump of the chain against the fold's |
|---|---|---|---|---|
The last commit before the fold: <sha>. A snapshot older than the fold is restored at that commit's release, which migrates it to the head, and then moves to the current one.
## Gates
- <gate>: <result, or skipped and why>
## Next
Push the branch and open the pull request: a person's to run, never the session's.
```