Fix twelve findings from the post-publication UX pass - #4
Merged
Conversation
Every one was reproduced before it was fixed and re-measured after. Two of them were introduced by the pre-tag link fix in this release. Twenty broken links across the Markdown mirrors. The pre-tag fix added `../` so relative links resolved correctly on the HTML pages, which are served from a DIRECTORY URL (`/operations/shared-store/`). The mirrors are not: they are served from `/operations/shared-store.md`, one level shallower — and also from `/operations/shared-store/index.md`, which is not. No single relative link can be correct at two depths, so `../gateway/` resolved to `/gateway/`. The mirror generator now resolves each relative link once against the page's canonical URL and emits it absolute, the only form that survives being served from more than one place. Links inside code fences are untouched: they are samples, not navigation. 139 mirror links, 0 broken. The light theme did not exist for a first-time visitor. Starlight stamps `data-theme` only for an explicit choice; on "auto" — the default — the attribute is absent and Starlight falls back to its own `prefers-color-scheme: light` rules. This stylesheet had none, so the chrome went light while the tokens stayed dark: sidebar text at #99a2b6 on #ffffff, 2.56:1 against a 4.5:1 floor. The light tokens are now also emitted under `@media (prefers-color-scheme: light)`, scoped so an explicit dark choice still wins on a light-preferring OS. Measured in a real browser at 375px across all four theme states, 806 text nodes each: 0 failures, worst 4.89:1. The `--json` contract was documented as absolute and is not. Both CLIs, identically, report a usage error (exit 2) as human help on stderr with stdout empty, even under `--json` — defensible, since the invocation itself was wrong, but "exactly one JSON object. Nothing else, ever." promised otherwise. The page now scopes the promise and tells a script to branch on the exit code first. Making exit 2 emit JSON is a behaviour change in two published packages and belongs in a patch release, not a docs pass. `checkSpendStore` was named without its import path, which appeared nowhere: `tx402/spend-store-contract` and `tx402.spend_store_contract` are now shown. The release gate claimed more than it proves. It called the tag "contained in protected main"; `main` is not protected, and the check is reachability from `origin/main`. Reachability is not protection — with protection off, ancestry can be satisfied by history nobody reviewed and the gate still passes. Renamed, with the limit stated plainly. `SECURITY.md` listed 0.1.x as supported and omitted the current 0.2.x. The Durable Object snippet called `createTx402Client` without importing it. `--max-per-hour` and `--max-total` were documented as `<MONEY>`; they take atomic integers, and `"1.00 USDC"` is rejected. `--max-spend` really is `<MONEY>` — only the two budget flags were wrong. `AGENTS.md` said dry-run touches no key. It does: planning reads the payer address and balance, because a route cannot be scored without knowing whether it is fundable. What dry-run guarantees is that no signature is produced. The accepted challenge timestamp was undocumented. `extra.timestamp` must be an RFC 3339 UTC string ending in `Z`; a numeric epoch is rejected rather than interpreted, because seconds-or-milliseconds is a thousand-fold ambiguity inside a freshness check. Documented with the emit recipe for both languages. Three fences labelled `json` carried `//` comments and, in one case, two documents. Split into a `bash` fence for the command and a valid `json` fence for the output; every `json` fence in the docs now parses. Documentation, styling and CI wording only. No package, source or behaviour change, so 0.2.0 on both registries stays correct. docs.tx402.io already serves these fixes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Twelve findings from the post-publication UX pass. Every one was reproduced before it was fixed and re-measured after. Two of them I introduced myself, in the pre-tag link fix that shipped with this release.
P2
Twenty broken links across the Markdown mirrors — self-inflicted
The pre-tag fix added
../so relative links resolved correctly on the HTML pages, which are served from a directory URL (/operations/shared-store/). The mirrors are not. They are served from/operations/shared-store.md— one level shallower — and from/operations/shared-store/index.md, which is not. No single relative link can be correct at two depths, so../gateway/resolved to/gateway/and 404'd.The mirror generator now resolves each relative link once against the page's canonical URL and emits it absolute — the only form that survives being served from more than one place. Links inside code fences are left alone: they are samples, not navigation.
139 mirror links checked, 0 broken. Verified again against the deployed site: 20 absolute links across 4 mirrors, all 200.
The
--jsoncontract was documented as absolute and is notBoth CLIs, identically, report a usage error (exit 2) as human-readable help on stderr with stdout empty — even under
--json. That is defensible: the invocation itself was wrong. But the page promised "exactly one JSON object. Nothing else, ever."The promise is now scoped, and a script is told to branch on the exit code first. Making exit 2 emit JSON is a behaviour change in two published packages — that belongs in 0.2.1, not a docs pass.
checkSpendStorewas named without its import pathtx402/spend-store-contract/tx402.spend_store_contractappeared nowhere in the docs. Both are now shown as code.The release gate claimed more than it proves
It called the tag "contained in protected main".
mainis not protected, and the check is reachability fromorigin/main. Reachability is not protection: with protection off, ancestry can be satisfied by history nobody reviewed and the gate still passes. Renamed to "reachable from origin/main", with the limit stated. Enabling branch protection is a repository setting and remains open.SECURITY.mdomitted the current versionIt listed 0.1.x as supported and not 0.2.x.
P3
The light theme did not exist for a first-time visitor. Starlight stamps
data-themeonly for an explicit choice; on "auto" — the default — the attribute is absent and Starlight falls back to its ownprefers-color-scheme: lightrules. This stylesheet had zero such rules, so the chrome went light while the tokens stayed dark: sidebar text at#99a2b6on#ffffff, 2.56:1 against a 4.5:1 floor.The light tokens are now also emitted under
@media (prefers-color-scheme: light), scoped:not([data-theme="dark"])so an explicit dark choice still wins on a light-preferring OS. Measured in a real browser at 375px, 806 text nodes per state:Also: the Durable Object snippet called
createTx402Clientwithout importing it.--max-per-hourand--max-totalwere documented as<MONEY>but take atomic integers —"1.00 USDC"is rejected,1000000accepted (--max-spendreally is<MONEY>; only the two budget flags were wrong).AGENTS.mdclaimed dry-run touches no key — it does: planning reads the payer address and balance, because a route cannot be scored without knowing whether it is fundable. What dry-run guarantees is that no signature is produced.The accepted challenge timestamp was undocumented:
extra.timestampmust be an RFC 3339 UTC string ending inZ, and a numeric epoch is rejected rather than interpreted — seconds-or-milliseconds is a thousand-fold ambiguity inside a freshness check. Now documented with the emit recipe for both languages, in the generator rather than the generated page.P4
Three fences labelled
jsoncarried//comments and, in one case, two documents. Split into abashfence for the command and a validjsonfence for the output — everyjsonfence in the docs now parses. Thetriagelabel both issue forms request did not exist, so GitHub silently dropped it; it has been created.Scope and verification
Documentation, styling and CI wording only. No package, source or behaviour change, so
0.2.0on both registries stays correct and there is nothing to re-release. docs.tx402.io already serves these fixes.Gate green: docs-gen (regenerated),
docs:build25 pages, every internal site link and all 139 mirror links resolve, workflow-lint, install-contract, conformance 88, format:check, nul-check, lint, TypeScript 836 passed / 11 skipped with Redis + Cluster + AOF-restart, Python 783 passed / 3 skipped at 92.10%.