New docs/quickstart.md and docs/quickstart.zh-CN.md walk a human operator from
zero to the first notification: create a Telegram bot via BotFather, find the
chat id, get a Stalwart app password, provision Redis, deploy the container,
sign in with CONFIG_ENCRYPTION_KEY, fill the business config, register
callbacks, enable processing, verify end-to-end, and schedule the cron drain.
Every claim was verified against the code:
- healthz is static 200 'ok'; /ready returns 503 until all upstreams
reachable, 200 with per-component booleans
- admin session TTL = 1800s (30 min) in state.rs
- first save must be complete -> 422 (is_complete in config.rs)
- 409 conflict on revision mismatch
- secrets are write-only (Option<SecretString>, never echoed)
- /worker and /reconcile return 204 on success, 503 when disabled
- webhook secret mismatch returns 401 (not 403)
- search triggers: 搜索/查找/检索 + /search command only
- cron-drain.sh uses MW_APP_URL (req), MW_WORKER_TOKEN (req),
MW_RECONCILE_TOKEN (opt)
Registered both files in all 5 validator DOCS lists (audit_paths globs
automatically). Added rows to README §6 and charter §7 in both languages.
Added discoverable links from README §3 to the quickstart.
GATE-DOCS 6/6 PASS (71 anchors, 735 path-refs).
Root README gains §2 "Two ways to run it": a side-by-side table of
Form A (backend origin only) against Form B (behind the Cloudflare Worker
gateway) — what answers, failover, the callback URL to register, the
reachable route surface, which health endpoint to watch, and the docs to
read. Late in the switch: registration is idempotent and re-adding retires
the previous subscription, so the switch is one SPA field, not a migration;
nothing secret moves to the gateway; /api/bootstrap and /debug/* stay
origin-only either way. Later sections renumbered, and the monitoring
paragraph now points at the gateway aggregate GET /healthz-worker instead
of the origin-passthrough /healthz.
cloudflare-worker/README.md rewritten as the canonical English version and
cloudflare-worker/README.zh-CN.md added as its translation, covering the
route safelist (19 paths with per-path methods), the timeout/retry budget
with per-route overrides, bounded failover, the two health endpoints,
callback registration, config, local verification, deploy, and the design
invariants.
Both fixes along the way:
- README.zh-CN said the startup env block has 2 required + 2 optional
variables; the table has 3 rows, 2 required + 1 optional.
- docs/charter.md §8 / registry list now names the zh-CN twin next to the
gateway README.
scripts/docs_check gains the new file in validate_docs, check_tables,
audit_anchors and audit_anchor_semantics so it is checked like every other
document. check_sec_refs resolves §-refs against the document being scanned
when a basename is shared: README.md exists both at the repo root and under
cloudflare-worker/, and the basename-keyed heading map silently dropped one
of them. The gate was passing for the wrong reason before.
GATE-DOCS 6/6 clean: 72 anchors, 454 section refs, 727 path refs.
audit_anchor_semantics still reports the same 7 pre-existing candidates,
none in the new or edited files.
`docs/reference.md` is the single source of truth and gets touched on every API
change, yet 40 of its 56 `notify.rs` anchors had drifted onto a closing brace,
a doc comment, or the wrong symbol - including one whole line pointing at
`revoke_admin_session` where a jmap dedup key is claimed - while
`scripts/docs_check/audit_anchors.py` reported every one of them clean. Its own
docstring says it only checks that the target line exists and is non-blank.
Remove the anchors rather than correcting them: fixing 40 numbers creates a
treadmill that re-drifts on the next unrelated edit to the same file, and the
file's own convention already called the line number "not an identity". Symbol
names are kept everywhere, which is what the gates and the readers need.
`docs/deployment.md` keeps its line numbers - it is an operator runbook read
mid-incident - and the five anchors that were wrong there are corrected.
docs/reference.md
- Drop the false "Verified against commit b2dbe7c" header claim; the anchors
never matched that commit. Replace it with the design decision and the
reason, and drop the "Only this file carries line numbers" convention that
`deployment.md` was already violating.
- Remove all `*.rs:NNN` and bare `:NNN` anchors, keeping symbol names.
- Fix a wrong fact the anchors were hiding: the session issued by
`POST /api/admin/session` is a fresh CSPRNG token generated per call, not a
"fixed 32-byte hex secret derived from admin_token". The anchor pointed at
`bootstrap`'s body.
docs/deployment.md
- `retry_or_dlq` call site 481 -> 479; `lock:reconcile` acquire 289 -> 290
(twice); heartbeat `renew_lock` 302 -> 303 (twice).
AGENTS.md
- Record the convention: symbol names in docs, line numbers only in
`docs/deployment.md`, plus the audit script's limits so nobody reads its
output as "the anchors are correct".
scripts/docs_check/audit_anchor_semantics.py (new)
- Independent script, not wired into `run_all.sh`, always `exit 0`, prints
candidates only. It flags an anchor whose target line has no relation to the
symbol the doc claims. Candidates go 20 -> 5 and `reference.md` disappears
from the list entirely. It cannot catch a one- or two-line off-by-one - that
still needs a human - and a bare `:NNN` shorthand is not a matchable anchor.
Gates: GATE-DOCS 6/6 clean (ANCHOR AUDIT 324 -> 129 refs, 0 errors).
`src/` is untouched, so the test baseline stands at 86 passed / 0 failed /
2 ignored.