Skip to content

Derive the command reference from the binary that ships - #8

Merged
t3chn merged 2 commits into
mainfrom
generate-the-reference-from-the-binary
Jul 31, 2026
Merged

Derive the command reference from the binary that ships#8
t3chn merged 2 commits into
mainfrom
generate-the-reference-from-the-binary

Conversation

@t3chn

@t3chn t3chn commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Stacked on #7 — its base is watch-for-drift-on-a-clock, because it extends the workflow that adds. GitHub retargets this to main once #7 merges.

Second of two slices. #7 gave the checks a clock; this gives them the half that notices what was never written down.

The gap this closes

Every check here so far compares something written with something true. That cannot notice a command nobody wrote about — and a product under active development grows commands. Coverage needs a different mechanism from correctness.

How it works

  • One release is described at a time. product/pin.json names it. Bumping the tag and running npm run sync:product downloads that release, verifies it against its published checksums, asks the binary what it accepts, and rewrites the <!-- generated --> blocks in docs/commands.md from the answer. Prose outside the markers is never touched — the reference is derived, the explanation stays written.
  • The pin bump is the review moment. Its diff is the product's changelog as far as this site is concerned, and therefore the list of prose worth re-reading. Generating at build time from whatever is newest would let the deployed page change without a diff anyone reviewed, which is the opposite of what this repository is for.
  • Coverage is enforced. Every command the binary accepts must have a section on the page or an entry in the pin saying why not. An entry that outlives its command fails too, so the exclusions cannot rot.

What deriving it found immediately

The hand-written flag table paraphrased all four of its descriptions. "override detection" is really "codex or claude-code; omit to detect"; "adopt a configuration that names another custom schema" is really "switch an OpenSpec configuration…". And it documented flags for one command where five take them — --json, --state-dir, --discard-local-edits and others were simply absent.

Two things went differently than planned

  • The first run reported that every command takes no flags. Go's flag package writes usage to standard error, and reading stdout on success returns an empty string — no error, no output, eleven commands with nothing. Exactly the silent-pass failure this repository exists to be unhappy about; it now reads both streams and refuses an empty result.
  • --check was going to run only on the daily schedule, to keep a binary download off every pull request. That was wrong: the pull request bumping the pin is the one place the full check matters, and that plan would have verified it the next morning. It runs in CI too.

Test plan

  • check:claims is now 12 assertions, all passing. Each new one was exercised against a seeded failure: a command appearing in the surface with no section and no excuse; an excuse for a command that no longer exists; a generated block edited by hand; the pin one release behind.
  • sync:product -- --check fails on a hand-edited block and on a hand-edited surface, and passes clean.
  • The accumulate-then-fail shell in the daily job was checked directly — both checks run even when the first fails, and the job still exits non-zero.
  • Rendered locally: five generated tables on /docs/commands, markers invisible in the rendered page and present in the raw markdown an agent fetches.
  • npm run lint and npm run build pass; llms-full.txt regenerated by the build.

Comparing what is written with what is true cannot notice a command nobody
wrote about, and the product is adding them. This adds the half that can.

One release is described at a time and product/pin.json names it. Bumping the
tag and running `npm run sync:product` downloads that release, verifies it
against its published checksums, asks the binary what it accepts, and rewrites
the marked blocks in the commands page from the answer. Prose outside the
markers is never touched: the reference is derived, the explanation stays
written. The diff of a pin bump is the product's changelog as far as this site
is concerned, which makes it the list of prose worth re-reading.

Every command the binary accepts must now have a section on that page or an
entry in the pin saying why not, and an entry that outlives its command fails
too. A command added to the product cannot pass through here unnoticed; leaving
one out stays allowed, leaving one out silently does not.

Deriving it immediately paid for itself. The hand-written flag table paraphrased
all four of its descriptions — "override detection" for "codex or claude-code;
omit to detect", among others — and documented one command's flags where the
binary has five commands that take them. The page now carries what the binary
prints, with one normalization: Go writes `-repo` and every other page here,
including the product's own install prompt, writes `--repo`.

Two things went differently than planned. The first run reported that every
command takes no flags, because Go's flag package writes usage to standard error
and reading stdout on success returns nothing — silently, which is the failure
this repository exists to be unhappy about. And `--check` was going to run only
on the daily schedule; the pull request that bumps the pin is the one place it
matters most, so it runs in CI as well.
@t3chn
t3chn force-pushed the generate-the-reference-from-the-binary branch from 80aa0b8 to 59a7815 Compare July 31, 2026 14:17
@t3chn
t3chn changed the base branch from watch-for-drift-on-a-clock to main July 31, 2026 14:17

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 80aa0b8698

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/check-claims.mjs
Comment on lines +289 to +291
const unexplained = surface.commands
.map(({name}) => name)
.filter((name) => !teaches.has(name) && !(name in excused));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require generated blocks for documented commands

When a pinned release adds a command, adding only a ## gr new-command`` heading makes teaches.has(name) satisfy this coverage check even if the author forgets the generated marker. Because `rewrite()` only replaces markers that already exist, `sync:product -- --check` also passes, leaving that command's flags handwritten or absent—the same drift this change is intended to prevent. Require each documented, non-excused command to have a generated block (or explicitly exempt commands whose empty reference is intentionally omitted).

AGENTS.md reference: AGENTS.md:L53-L57

Useful? React with 👍 / 👎.

return [
'| Flag | Effect |',
'|---|---|',
...flags.map(({flag, effect}) => `| \`${flag}\` | ${effect} |`),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Escape help text before building Markdown tables

When a documented command's help text contains Markdown or HTML metacharacters, interpolating effect directly corrupts the generated reference. The already-recorded finish --result effect contains both unescaped | delimiters and placeholders such as <id>, so adding a generated block for that existing command would split the row into extra cells and allow the placeholders to be interpreted as raw HTML by marked. Escape table delimiters and HTML-sensitive characters while preserving the rendered product wording.

AGENTS.md reference: AGENTS.md:L53-L57

Useful? React with 👍 / 👎.

Comment thread scripts/check-claims.mjs
Comment on lines +302 to +304
const stale = Object.keys(excused).filter(
(name) => !surface.commands.some((entry) => entry.name === name),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject exemptions once their commands are documented

When an omitted command later gains a real section, leaving its old undocumented entry behind still passes this check because an exemption is considered stale only when the binary drops the command. That obsolete entry then silently masks any later accidental removal of the section: coverage continues to pass via name in excused. Treat exemptions for commands in teaches as stale too, so an exclusion cannot shadow documentation after its rationale has expired.

AGENTS.md reference: AGENTS.md:L53-L57

Useful? React with 👍 / 👎.

@t3chn t3chn closed this Jul 31, 2026
@t3chn t3chn reopened this Jul 31, 2026
The prompt was the one thing here still compared against the product's `main`,
and the reasoning for that — notice the day the product moves, not the day
someone bumps a pin — did not survive contact with a moving product. A change
landed on `main` that rewrote the prompt for behaviour no release carries, and
the check went red on content it had passed an hour earlier.

Copying the new wording would have made the site worse rather than better. The
prompt's own first instruction is to download `releases/latest`, which serves
v0.1.2. Both binaries were run in the same fresh repository to be sure of what
that means: the released one applies the registration and prints a notice naming
`--fix-gitignore`, which is exactly what the prompt on this site tells the agent
to watch for, and the one built from `main` writes that ignore rule itself and
prints no notice at all. The new prompt would have sent an agent to fetch one
binary and then look for a report only a different one prints.

So the prompt is read at the pinned tag, like the flag tables and the capture
already are, and the site describes one release consistently. Noticing that the
product moved is not this comparison's job and never should have been: the pin
failing to be the newest published release is what fires, and bumping it brings
the new prompt, the new flags and a new capture in one reviewable diff.

The rationale in the file header said the opposite, and a stale explanation
inside the check against stale claims is its own small joke. It now says what
the code does and why it changed.
@t3chn
t3chn merged commit 274fbd2 into main Jul 31, 2026
2 checks passed
@t3chn
t3chn deleted the generate-the-reference-from-the-binary branch July 31, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant