Skip to content

Fix twelve findings from the post-publication UX pass - #4

Merged
jaykomarraju merged 1 commit into
mainfrom
fix/ux-pass-u36-u47
Aug 13, 2026
Merged

Fix twelve findings from the post-publication UX pass#4
jaykomarraju merged 1 commit into
mainfrom
fix/ux-pass-u36-u47

Conversation

@jaykomarraju

Copy link
Copy Markdown
Contributor

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 --json contract was documented as absolute and is not

Both 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.

checkSpendStore was named without its import path

tx402/spend-store-contract / tx402.spend_store_contract appeared 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". 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 to "reachable from origin/main", with the limit stated. Enabling branch protection is a repository setting and remains open.

SECURITY.md omitted the current version

It 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-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 zero such rules, 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 :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:

Theme state Failing Worst
auto, light OS 0 4.89:1
auto, dark OS 0 6.39:1
explicit light on dark OS 0 4.89:1
explicit dark 0 6.39:1

Also: the Durable Object snippet called createTx402Client without importing it. --max-per-hour and --max-total were documented as <MONEY> but take atomic integers"1.00 USDC" is rejected, 1000000 accepted (--max-spend really is <MONEY>; only the two budget flags were wrong). AGENTS.md claimed 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, 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 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. The triage label 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.0 on both registries stays correct and there is nothing to re-release. docs.tx402.io already serves these fixes.

Gate green: docs-gen (regenerated), docs:build 25 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%.

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.
@jaykomarraju
jaykomarraju merged commit 2c48d74 into main Aug 13, 2026
14 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