Skip to content

Docs 6: the launch audit - #88

Merged
arpanghoshal merged 2 commits into
mainfrom
docs/8-audit-and-launch
Sep 6, 2026
Merged

Docs 6: the launch audit#88
arpanghoshal merged 2 commits into
mainfrom
docs/8-audit-and-launch

Conversation

@arpanghoshal

@arpanghoshal arpanghoshal commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Session 6: read the site as a stranger, run the full audit, write the human steps down. Two halves — what a machine can check, and what only a first read finds.

The fresh read

A session that had not written any of it read the site the way a site is read, as a backend engineer with an agent that issues refunds and deploys services, and ran the commands. Thirty-odd findings. The three that would have cost the most trust:

  • reference/cli.mdx said Usage: main fourteen times, under a sentence promising each block is the command's own --help verbatim. Click takes the program name from sys.argv[0]. A reader who tried it got command-not-found from the reference page.
  • Two pages quoted ctrlrun verify transcripts that were written, not captured. Every PASS row named stripe.refund where the page's own policy makes verify exercise k8s.delete_namespace. That is the false-green problem this project warns about, in its own evidence.
  • All 71 API reference pages had signatures that raise. The renderer dropped the * and every default, so protect('stripe.refund', 'refund:{id}') copied off the page is a TypeError. Protocols rendered with none of their members, and no page had an import line or named its extra.

Also fixed: a stray fence in ARCHITECTURE.md that rendered everything from the module map to the end of the file inside a code block on the live site, and hid 88 lines from the lint. Five pages claiming every call leaves a receipt, when a call waiting on a human has none. Site-wide canonical and og:url, making all 180 pages declare themselves duplicates of the home page.

And a long tail of statements that were wrong: the Slack answer body and signature format, python -m ctrlrun.conformance.store, ctrlrun inspect given an approval id, the exit-2 case, a read-only Postgres role running --verify-chain, PendingApproval's keys, Subject's rule, five reserved subjects where there are ten, a receipt's action field called name, deny/blocked, ten guarantees where there are eleven, 10/10 and 5/5 in the roadmap, 1,625 test functions where there are 1,704.

Three sentences that were not true yet

  • The README said an unreleased version was on PyPI. The readiness block read "Version 0.6.0, on PyPI" off pyproject.toml while the changelog says ## [0.6.0] - unreleased and PyPI holds 0.5.0. It reads the changelog for the newest dated version now, and dating the entry flips all three copies on its own.
  • Every share card pointed at the wrong domain, because og:image was relative.
  • The home page shared as "CTRLRun - CTRLRun".

New guards, each mutation-tested

Guard Catches
Rendered signatures against inspect.signature a signature a reader cannot call
Quoted verify transcripts against a real run a written transcript
A measured receipt count "every call leaves a receipt"
A guarantee count against the catalogue ten where there are eleven
The suite size as a floor a page undercounting its own evidence
No site-wide canonical every page claiming to be the home page
No description cut mid-clause a truncated search snippet
Every API page names its import and extra a reference nobody can use

The three docs audits stop being advisory

Runnable snippets, the forbidden-words lint and the link check carried continue-on-error: true from session 0, so a job red on arrival would not teach everyone to ignore it. The baseline is clear, so the flags are gone. Adding docs to the required status checks is a settings click and is on the checklist.

Checked and green

Generators with --check 9, none drifted
Public names with a reference page 62 of 62
Runnable snippets · forbidden words · links 96 blocks, 0 failed · 0 · 0
mint validate · mint broken-links passed · none
Tags with a GitHub Release · badges 6 of 6 · 7 of 7
Community profile 100%

The new-user path, from a wheel built here into a fresh 3.12 virtualenv with no repository: pip install brings click and pyyaml and nothing else, ctrlrun init writes a policy, ctrlrun demo runs five scenarios in 0.12s with no network, receipts and verify work, and the quickstart's five runnable blocks pass against that install.

Left for a person

In internal/LAUNCH-CHECKLIST.md, which stays local. The social preview on the repository page is still the pre-branding grey image; docs is not yet a required status check; the MCP screenshot needs taking.

Not fixed, and listed rather than hidden

  • ctrlrun init writes a ctrlrun.policy/v1 policy with no effect: key, so a reader who starts where the install page points gets no reservation and no one-effect-once — the guarantee the whole site is about. That is a src/ behaviour change and needs your word.
  • Every Production page, the Postgres guide and eight capability cards describe 0.6, which is not on PyPI yet. They become true on the tag; the readiness block is the only place that currently says so.
  • The adapters pin ctrlrun>=0.5,<0.6, so pip install ctrlrun-langgraph on 0.6 downgrades the kernel. No site page states a supported kernel range.
  • Unanswered questions a reader has: what this costs per call, how to unit-test a @protected function, what happens to an abandoned approval request.

Verification

ruff format --check · ruff check · mypy --strict src · pytest
3908 passed, 45 skipped — all checks passed

@mintlify

mintlify Bot commented Sep 6, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
ctrlrun 🟢 Ready View Preview Sep 6, 2026, 4:16 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

Session 6 of the docs milestone. What a machine can check is checked; what a person
has to click is in `internal/LAUNCH-CHECKLIST.md`, which is local-only like the
other `internal/` files. Three findings needed code.

**The README said an unreleased version was on PyPI.** The readiness block read
*"Version 0.6.0, on PyPI"* off `pyproject.toml` alone, while `CHANGELOG.md` still
says `## [0.6.0] - unreleased` and PyPI holds 0.5.0 — a false sentence on the front
page, produced by a generator, which is the one failure a generator exists to
prevent. It now reads the changelog for the newest **dated** version and says
*"Version 0.6.0 is in development; PyPI has 0.5.0"* until the release lands, at
which point dating the entry flips all three copies on its own. Found by fetching
the badges and reading what they said.

**Every share card on the internet pointed at the wrong domain.** `og:image` and
`twitter:image` were `/images/social-preview.png`, and Mintlify resolved that
against the deployment origin rather than the custom domain, so the image URL was
`ctrlrun.mintlify.app`. Absolute now, on `docs.ctrlrun.dev`.

**The home page shared as "CTRLRun - CTRLRun".** Mintlify renders
`<title> - <site name>`, and both were `CTRLRun`. It carries its own `og:title` and
`twitter:title` now, and a test refuses one that is the site name again.

**The three docs audits stop being advisory.** Runnable snippets, the forbidden-words
lint and the link check carried `continue-on-error: true` from session 0, because a
job that was red on arrival would have taught everyone to ignore it. The baseline is
clear — 96 runnable blocks, 182 documents, 0 findings, 0 broken links — so the flags
are gone. Adding `docs` to the required status checks is a settings click and is on
the checklist; until it is required, a red docs job still merges.

What the audit checked and found green: nine generators with `--check`; `CLAIMS.md`
re-pointed with no drift and its line-number test passing; every one of the 62 public
names has a reference page; `mint validate` and `mint broken-links` both clean; all
six tags have a GitHub Release; all seven badges render; the community profile is at
100%; the recording still matches what the demo prints. And the new-user path, from a
wheel built here into a fresh 3.12 virtualenv with no repository: `pip install`
brings `click` and `pyyaml` and nothing else, `ctrlrun init` writes a policy,
`ctrlrun demo` runs five scenarios in 0.12s with no network, `receipts` and `verify`
both work, and the quickstart's five runnable blocks pass against that install.

Two things the audit found that only a person can fix, both on the checklist: the
social preview uploaded to the repository page is still the pre-branding grey image,
and `docs` is not yet a required status check.
A session that had not written any of it read the site the way a site is read —
home, Why, the navigation, the links the pages themselves offer — as a backend
engineer with an agent that issues refunds and deploys services. It ran the
commands. Thirty-odd findings; every one below is fixed and most now have a guard.

**The three that would have cost the most trust.**

`reference/cli.mdx` said `Usage: main [OPTIONS] COMMAND [ARGS]...` **fourteen times**,
under a sentence promising each block is "the command's own `--help`, verbatim".
Click takes the program name from `sys.argv[0]`, which under the generator is
`main`. A reader who tried it got command-not-found from the reference page.

`guides/verify-in-ci.mdx` and `cookbook/verify-in-github-actions.mdx` presented
`ctrlrun verify` transcripts as real runs. They were **written, not captured**: every
PASS row named `stripe.refund` where the page's own policy makes verify exercise
`k8s.delete_namespace`, both dropped the stderr line G7's scenario logs, and one
misaligned a column. That is the false-green problem this project spends thousands
of words warning about, arriving in its own quoted evidence. Both are now compared
against a real run of the page's own policy, line by line.

**All seventy-one API reference pages had signatures that raise.** The renderer
emitted `name: annotation` and nothing else, so twenty-seven keyword-only signatures
read as positional and forty-four optional parameters read as required —
`protect('stripe.refund', 'refund:{id}')`, copied off the page, is a `TypeError`.
Protocols rendered as a bare `class X(Protocol)` with none of the members they ask
an implementer for. And not one page carried an import line or named the extra it
needs. A new test compares every rendered signature with `inspect.signature`.

**`docs/ARCHITECTURE.md` had a stray closing fence**, so everything from the module
map to the end of the file rendered inside a code block on the live site — and the
forbidden-words lint, which reads only outside fences, had stopped looking at 88
lines.

**Five pages said every call leaves a receipt, refused ones too.** A receipt is
written when an action reaches a terminal state and *waiting on a human* is not one:
three protected calls leave two receipts. Measured in the test rather than asserted.

**`canonical` and `og:url` were site-wide metatags**, so all 180 pages declared
themselves duplicates of the home page — an instruction to a search engine to index
one and drop the rest.

Wrong and now right: `--store-url`'s help text still said "Reserved; v0.4 accepts
'sqlite'" (the CLI reference rendered it faithfully); the Slack guide's answer body
omitted the two fields the endpoint requires and never gave the signature's wire
format, so the verifier it tells you to write could not be written; the conformance
suite was documented as `python -m ctrlrun.conformance.store`, which has no
`__main__`; `ctrlrun inspect` was passed an approval id; the exit-2 case was
described as a policy with no approve rule, which exits 0; a read-only Postgres role
was said to run `--verify-chain`, which it cannot, because opening the store
migrates it; `PendingApproval` was documented with `principal` and `hash` keys it
does not have; `Subject` was said to require `agent` and to refuse a wildcard, and
both are the opposite; five reserved condition subjects were named where there are
ten; a receipt's `action` field was called `name` and its approver `human:shell`,
a string in no source file; a policy `deny` was shown as `deny/blocked`; four pages
said ten guarantees where there are eleven; the roadmap quoted `10/10` and `5/5`;
`how-this-is-built.md` claimed 1,625 functions and 2,442 cases against 1,704 and
3,944; the threat model said it covers through v0.3 and named a `check` hook that
never shipped; the OWASP mapping listed `ASI06` as both covered and out of scope and
named four of the six chain-break names.

Also: four frontmatter descriptions were cut mid-clause, and they are the search
snippet; `mcp/use-the-docs-from-your-editor` published a production note to its own
author where a screenshot should be; two pages used `just` and `simply`.

**New guards, each mutation-tested:** rendered signatures against `inspect`; quoted
verify transcripts against a run; the receipt count against a measurement; a
guarantee count against the catalogue; the suite size as a floor; no site-wide
canonical; no truncated description; every API page names its import and its extra.

The lint allowlist records why `ROADMAP.md` stays excluded now that it is a site
page: every regulation it names is future work, and the packs section says in its
own words that no pack describes itself as compliant with anything.
@arpanghoshal
arpanghoshal force-pushed the docs/8-audit-and-launch branch from ab14d0d to 72ed75d Compare September 6, 2026 17:17
@arpanghoshal
arpanghoshal enabled auto-merge (squash) September 6, 2026 17:17
@arpanghoshal
arpanghoshal merged commit 87160bf into main Sep 6, 2026
8 checks passed
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