Sync the stated-as-current gate numbers that the previous feature commit
(88f0923) shifted, per the same-round rule:
- Test baseline 104 -> 105 passed / 0 failed / 4 ignored across the
authoritative (charter §6.1) and deferring docs (design §Phase 0,
opengaps access-control, HANDOFF-push-401), with retest date 2026-10-14.
The new test `setup_status_reports_backend_label` is a normal (non-ignored)
test, so the ignored set is unchanged.
- opengaps anchor count 72 -> 49, matching what `run_all.sh` ANCHOR AUDIT now
reports (the design.md main.rs line-anchors were converted to symbol refs).
- AGENTS version baseline 3b36fe3 -> 88f0923 (gate-holder merged this batch;
88f0923 is the gate-green commit of the round). Charter Code Baseline
`fc686ab` left as-is — line-number anchor semantics were not bulk-reverified
this round, so claiming otherwise would overstate (§7.4).
GATE-DOCS 6/6 clean (49 line-refs, 0 errors); GATE-P0 stood at 105/0/4.
Gate-holder update per AGENTS.md version-baseline rule: after the
rate-limiter + stale-doc batch (3b36fe3) passed GATE-P0 (104/0/4) and
GATE-DOCS (6/6), the surviving line-anchors (21 in opengaps/retired/design)
are valid against 3b36fe3, so it becomes the new baseline.
Two changes requested by the user:
1. Fold the hand-holding quickstart tutorial into README §3 (both
languages). Delete docs/quickstart.md and docs/quickstart.zh-CN.md.
Remove them from all 5 validator DOCS lists, README §6 doc index,
and charter §7 boundary table. The tutorial now lives as
README §3.0–§3.11: prerequisites, create Telegram bot, find chat id,
get JMAP credentials, run backend, sign in, fill config, register
callbacks, enable processing, verify, schedule drain, troubleshoot.
2. Replace 'gateway'/'网关' with 'load balancer'/'负载均衡器' across
all markdown (README, docs/, AGENTS.md, cloudflare-worker/README).
Also fix 4 markdown anchor links whose heading slugs changed.
Also fix charter §8 '18 safe routes' → '19' (was missed in the
earlier reference.md fix).
GATE-DOCS 6/6 PASS (71 anchors, 727 path-refs, 455 sec-refs).
reference.md: new Section 6.5 explains the bimodal cursor, the two token
classes (Session.state is not an Email-collection token; newState is), why a
truncated walk persists as a walk, and why oldState and destroyed are dropped.
Section 6.2 budget table is renamed to the new constant names and gains
RECONCILE_INITIAL_CHANGES. state:jmap:since is listed as a pipeline key.
opengaps.md: the re-baseline fix is described with the new session_state()
name, and the position-walk note no longer claims a state argument is passed
as None - the None is folder_id, and the method has no state parameter at all.
retired.md: the dead-code removal no longer claims those were the last three
#[expect(dead_code)] attributes in src/; reload_async still has one.
deployment.md / design.md: the 54+14 anchors the code move had shifted by one
hunk are re-anchored, plus two manual corrections (the /reconcile route and the
unconditional 204 return). charter.md: baseline 89 -> 93 passed.
AGENTS.md: the two anchor counts it quotes (60, 44) were stale; they are 54
and 17.
check_sec_refs.py: two-part section refs attributed to an RFC (RFC 8620
Section 2.1) are external, not doc references - the trailing-dot test only
caught the three-part form.
Same work, two instruction versions in the queue: the executing agent follows
the stale one while the lead has already finished and committed the revised
one. Nothing in the queue records which instruction is current, so which
version runs is effectively random. That is what the mutual-overwrite round
was, not a communication failure.
AGENTS.md gains a 9th section with the five rules, including the one that
actually had to be enforced: withdrawing an instruction requires telling the
executor by name, because replacing it in the queue is not withdrawal.
Mechanical follow-up work that cannot collide with someone else (anchor
recalculation, wording, missing tests) should be done while waiting rather
than queued. Committing stays with the lead.
GATE-DOCS 6/6, ANCHOR AUDIT 129 refs / 0, PATH AUDIT 454 / 0. AGENTS.md
248 -> 267 lines, outside the 500-line soft cap and the size checker only
covers src/, web/ and cloudflare-worker/.
The convention added in 166b77e said deployment.md was the only exception, but
opengaps.md, retired.md and design.md still carry 44 anchors and reference.md is
the file that actually lost its 56. State the policy as it is applied: symbol
names for anything new, deployment.md's 60 anchors kept because it is an
operator runbook, reference.md stays line-number-free, and the 44 legacy anchors
are neither added to nor cleaned up because those files rarely change.
`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.
GET /api/business-config returns the stored values with presence-only secrets, and PUT now merges a patch against the stored config instead of replacing it, so the SPA can show what is already set and send back only the fields that changed.
The SPA marks secrets as set-but-unreadable, edits chat_allowlist as an unordered set, and omits fields it did not touch; blank required fields keep the stored value, blank optional fields clear it. The read-back is Serialize-only, so a client cannot inject secrets_present.
Forward both paths at the gateway: /api/business-config accepted PUT only, so the read-back returned 405, and the preflight path was missing from the safelist entirely, so it returned 404. The SPA is served from the Worker domain, so the whole edit experience was unreachable behind the production gateway. Route methods are now asserted per path, which the safelist test never checked.
Also: /debug/config reports timezone again, the timezone select is styled like the other fields, and the docs follow the new contract.
Not even the retired registry — there a fictional path reads the same as a
real one, so nobody can tell "we decided against this" from "this is wrong".
docs
design.md external-repo citations now carry their owning prefix
(stalwartlabs/jmap-client/src/client.rs), so a bare
src/client.rs can no longer read as a local file; the LLM
layer, the local-write red line and the missing-test-dir notes
stop quoting directories that were never created
retired.md header states the rule; invented entries are rewritten as named
proposals ("把渠道层建成目录模块") instead of as paths that
look like files; docs/todo.md stays, git history proves it
AGENTS.md §7.4 records the rule as a gate requirement
charter.md §6.3 validator count and scope
tooling
audit_paths, the sixth docs_check validator: every path-shaped token in
inline code in docs/*.md and AGENTS.md must resolve against a real root,
or carry an external owning prefix, or be provably deleted in git history,
or sit on a short commented allowlist. Non-path shapes are excluded
structurally — absolute paths and URIs, dotted hosts, media types, JMAP
method names, numeric pairs, glob fragments — and a seg/ token only counts
as a directory when it is the whole inline-code span, so Email/changes and
application/json are never mistaken for dirs. run_all.sh is now 6/6.
Verified, not assumed: a scratch doc with three invented paths yields 3
errors and exit 1, while a real file, a git-deleted file, an
external-prefixed citation and the conventional-name allowlist all pass.
Every member's audit is now read and each real finding fixed, not just reported.
AGENTS.md §2.1
- The 500-line soft cap now has a machine-checkable exception: an over-limit
file must carry a one-line `SPLIT-EVAL:` marker stating the split decision.
- Six files were over the cap with no justification: notify.rs 2036, state.rs
1586, worker.rs 1045, config.js 1079, client.rs 601, config.rs 521. All now
carry the marker. client.rs was missed by my own earlier line count, which
did not descend into src/domain/jmap/.
- New validator scripts/docs_check/check_file_size.py; GATE-DOCS 4/4 -> 5/5.
It proves the marker exists, not that the written reason is sound.
- Re-based 144 line anchors across six docs for the +1 line each file gained;
every shifted anchor verified against the code, not just for non-blankness.
audit-arch.md
- design.md §6.1 module matrix omitted the `debug` module entirely; added.
- Seven numeric duplications in design.md now defer to reference.md §6.1 as
the single authority instead of restating the values.
- reference.md §5.1 was missing DEBUG_TOKEN; §5.3 listed six recognised names
and guessed `JMAP_*`. It now lists all 20 exactly as config.rs reads them.
- Corrected two wrong claims in the audit itself: debug.rs is compiled
unconditionally, not under #[cfg(debug_assertions)] (there are no cargo
features at all), and /debug/* exposes 7 routes, not the invented session
list / resend / rebuild surface.
audit-fileplanner.md
- reference.md cited a nonexistent `jmap_service.rs`; replaced with the real
adapter chain worker.rs -> domain/jmap.rs -> domain/jmap/client.rs.
- cloudflare-worker/README.md was outside every validator's scope; all four
doc validators now include it (208 section refs, 250 line refs).
- README.zh-CN.md stated only half of what README.md says about RUN_MODE; the
forward-placeholder sentence is now bilingual.
- charter.md §7 boundary table was missing rows for itself and for web/.
audit-rust.md / audit-frontend.md
- Error envelope corrected: `error` is a bare machine code, no `message`.
- Status table, AI-consent labels and ~15 stale or phantom facts fixed against
notify.rs; `TelegramClient::SendMessage` and `web_config` phantoms removed.
- GET|PUT /api/enabled forwarded by the worker so the SPA kill switch works.
- Ten UI strings that called the admin credential the Redis ACL password now
say CONFIG_ENCRYPTION_KEY.
- POST /healthz returns 405; dead HTTP_STATUS removed.
- audit_anchors.py now flags identifiers that do not exist in the file they
are anchored to.
charter.md
- `SAF-ENABLE-FLAG` registered (85 IDs); `SAF-ADMIN-SESSION` row corrected;
- `REQ-PUSH-TYPES` registered; the stale `admin_session_digest` removed.
Gates: GATE-DOCS PASS 5/5 (250 line-refs, 208 section refs, 0 errors);
GATE-P0 62 passed / 0 failed / 1 ignored, baseline unchanged; worker and SPA
36 tests, 0 failures.
Real Stalwart / Telegram / managed Redis verification stays blocked on missing
credentials and remains labelled as unverified.
AGENTS.md (276 -> 239 lines) is now a language-agnostic engineering-norms
document: code style, config and secrets handling, build environment,
testing gates, document governance. It states no project fact and names no
language, framework, dependency, or file. Verified: zero hits for JMAP,
Telegram, Redis, axum, Cloudflare, LLM, *.rs, and every project env var.
All project-specific content moved to the new docs/charter.md (~276 lines):
project goals, locked technology choices, 23 numbered security invariants,
implementation stages, prohibitions, test/acceptance gates with the full
GATE-P0 command and baseline, document boundary rules, and the stable ID
registry.
docs/charter.md §8 is the single authority index for stable IDs: 83 rows,
no duplicates, ID | definition file | one-line statement | category. Four
IDs that were referenced across the docs but never registered are now
registered: ARCH-STATE-REDIS, C-REDIS-EXTERNAL, GATE-UPTIME-KUMA, GATE-DOCS.
All previously registered IDs are preserved.
Defect fixed while rebuilding the registry: SAF-ADMIN-SESSION cited
src/config.rs `admin_session_digest`, a name that has 0 hits in src/ -- a
phantom identifier that survived in the authoritative registry since before
this restructure. Corrected to `session_digest` (src/config.rs:7). All 8
distinct backticked symbols in the registry's definition-file column now
verify against src/.
Two false project facts removed from the docs (both claimed the SPA writes
through POST /api/bootstrap, which web/config.js never calls -- 0 hits; the
actual path is PUT /api/business-config): README.md, README.zh-CN.md,
docs/reference.md. The first successful write creates the configuration and
later writes hot-reload it; POST /api/bootstrap remains the one-shot
automation endpoint.
Cross-reference redirections in 9 files: README.md, README.zh-CN.md,
docs/deployment.md, docs/design.md, docs/reference.md,
cloudflare-worker/README.md, cloudflare-worker/src/index.js,
cloudflare-worker/wrangler.toml now point project facts at charter.md and
language-agnostic quality rules at AGENTS.md.
Four validators extended to cover docs/charter.md: DOCS lists in
audit_anchors.py, check_tables.py, validate_docs.py, check_sec_refs.py, plus
charter.md added to check_sec_refs.py's TARGET_BEFORE regex and to
validate_docs.py's index-file cross-doc exemption.
Gates: GATE-DOCS PASS 4/4 (VALIDATION 0 errors, TABLE STRUCTURE 0
mismatches, ANCHOR AUDIT 227 line-refs / 0 errors, SECTION-REF AUDIT 191
refs / 0 errors). GATE-P0 PASS, baseline unchanged 62 passed / 0 failed /
1 ignored. Anchor audit proves line reachability only, not that the line
contains what the prose claims.
Code (2 sites):
- main.rs logged bootstrap_config.port, which is always the hardcoded 8080 in
the two-variable production config; the listener actually binds the port
parsed at main.rs:31-34. Log the bound port.
- That exposed Config.port as genuinely unread (clippy -D warnings), so mark it
with #[expect(dead_code, reason=...)] instead of silently deleting a
documented env-parsing site.
Docs, phantom identifiers (names that do not exist in src/):
- RuntimeConfig x3 -> OutboundConfig via RuntimeConfigProvider (state.rs:34/41)
- LlmClient::noop() x2 -> None in Option<Arc<LlmClient>> (main.rs:125)
- main.rs::validate_env_or_exit x2 -> config.rs:338 read, main.rs:75 validation
- TelegramChannel::send_message -> TelegramClient::send_text (channel.rs:134)
Docs, stale facts:
- LLM table: ENABLED and ALLOW_NET defaulted true,true -> false,false;
API_KEY, BASE_URL, MODE were "required" -> only required when enabled. This
contradicted design.md:385-387, which already had the correct values.
- design.md:560 said RUN_MODE "is actually consumed"; it is only read and
validated and never affects behaviour (main.rs:73-74).
- RUN_MODE operator docs (README, reference, deployment, .env.example) now say
both accepted values produce identical behaviour.
- retired.md: 5 intents -> 6 (/search shipped, worker.rs:517); 4 LLM env vars
-> 6; main.rs tracing events 6 -> 7.
- design.md:494 tracing count 6 -> 7, debug-gate warning added to the list.
- design.md:350 code-gap count 2 -> 1 (multi-instance window done in 6c99ce5).
- design.md:554 marks the 45-paused stage-0 baseline explicitly as historical.
- .env.example said the Redis ACL password was the bootstrap trust root; it is
CONFIG_ENCRYPTION_KEY (main.rs:88-91).
Docs, re-anchored after the main.rs shrink (6 refs shifted by -2).
GATE-P0 62 passed / 0 failed / 1 ignored; GATE-DOCS 4/4 clean
(228 line anchors, 211 section refs, 0 errors).
- GET /ready: ready -> 200 + diagnostic report (unchanged success body);
not ready -> 503 + {"error":"service_unavailable","request_id":id}
+ Retry-After: 30, consistent with the other public entry points
- drop the legacy {"status":"not_ready"} diagnostic block from the 503 body
(backward compatibility not required)
- HTTP 200/503 semantics preserved, so Uptime Kuma HTTP(s) monitors
(status-code based) keep working
- sync docs: README.md, README.zh-CN.md, docs/design.md, docs/deployment.md,
AGENTS.md, docs/todo.md
- README.md is now English; add README.zh-CN.md with cross-links; both versions
cover the same 40 sections and the same key facts
- correct stale docs/design.md 10.5.1 and docs/todo.md to the current code
(XAUTOCLAIM threshold, malformed-entry ACK, error envelope now done)
- replace the temporary HANDOFF.md with docs/todo.md (unfinished work + blockers)
- adjust docs/deployment.md and AGENTS.md accordingly