- opengaps: drop the Telegram inbound callback and Stalwart PushSubscription
blockers, both verified live; 2 items remain (newState semantics, TTL
measurement). The public-callback mapping question is also resolved -- the
integration env has no Worker in front and Stalwart hits the backend origin
directly, while /push/jmap is already in the Worker safelist for production
- deployment 4.1 (new): full setWebhook and /api/push/register procedure,
expected replies, failure symptoms, and the setWebHook rotation trap (the
deployed secret must be re-registered after any TG_WEBHOOK_SECRET change)
- deployment 6.2: the worker is neither a background task nor a separate
container; POST /worker is the only consumer entry point
- deployment 6.3.1: add the /worker drain step to the cron and k8s CronJob
examples, plus a two-endpoint comparison table, ordering requirements, the
10-message batch cap, and the "204 is not a success signal" warning
- deployment 10.4: describe /worker as scheduler-driven, not manually invoked
- reference, retired: /worker anchor notify.rs:331 -> 380; the old value fell
inside the reconcile function body, so the anchor audit passed while being
semantically wrong
- charter 6.6: narrow the blocker list; code baseline b2dbe7c -> fc686ab
- design 11.2: PushSubscription permission now verified; the external Cron
must call both /reconcile and /worker
GATE-P0: cargo fmt / check / clippy clean, 69 passed, 1 ignored
GATE-DOCS: 6/6 clean (ANCHOR 299, SECTION-REF 234, PATH 428)
- remove the 真实 Redis TLS 连接 blocker: user confirms the managed
Redis is connected; the redis crate has tokio-rustls-comp enabled so
rediss:// is a supported scheme, and /debug/redis + /ready both report
reachable/true on the live deployment (checked just before this commit)
- the old item bundled three things: rediss:// handshake, password URL
encoding, ACL password. The URL-encoding half was only ever covered by
a parse-only unit test pointing at example.invalid, which never
connects, so the handshake itself was the unverified part; the live
connection covers all three
- blockers now 5 -> 4; no cross-reference lost: docs/deployment.md:231
already documents the rediss://default:<url-encoded-password>@ form,
and :220 already draws the startup-env vs business-config hot-reload
line, so deleting the item drops no surviving fact
- docs/charter.md 6.6: the blocker enumeration mirrored opengaps.md and
must move with it, and the credential preamble ("无 Stalwart /
Telegram / 托管 Redis 凭据") was already stale since the Stalwart and
Telegram creds both verified live, so it now states what is actually
unverified, which is the inbound callback direction
- 代码缺口 has nothing to remove: its only remaining item is
SAF-DEBUG-ALLOWLIST, which is deliberately unimplemented by product
decision and src/debug.rs:233-235 still shows the empty-allowlist
short-circuit
- GATE-DOCS 6/6 pass: ANCHOR 267 (was 269, the item carried 2 refs),
SECTION-REF 224, PATH 390 (was 392)
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.
Four doc headers reference the code commit whose line anchors they were
read against. The trait deletion moved src/channel.rs (361 -> 340) and
src/domain.rs (16 -> 10), so the anchors are re-based to that commit.
GATE-DOCS: PASS 5/5 (VALIDATION 0, TABLE 0, ANCHOR 248 line-refs / 0,
SECTION-REF 216 / 0, SIZE CHECK 0).
Line anchors in reference.md / retired.md were re-read against the compat-cleanup
commit, and the test baseline in charter.md and roadmap.md moves 62 -> 61 to match
the deleted from_env()-driven case.
GATE-DOCS: PASS 5/5, ANCHOR 247 line-refs / 0, SECTION-REF 216 / 0.
Code (src/config.rs 521->394, src/main.rs 232->200):
- Config::from_env() + required_secret / required_nonblank / env_bool deleted.
config.rs now performs no std::env read; all business fields come from Redis
business config (BusinessConfigWire -> validate_business_wire -> validate_nonblank).
- RUN_MODE deleted: variable, Config.run_mode field and the main.rs validation
block. The two modes never changed runtime behaviour -- webhook and reconcile
share one router, POST /reconcile is a standalone endpoint.
- jmap_password() accessor deleted; the field name is kept in
BusinessConfig / BusinessConfigWire because it is real data, not a stub.
- Config.port and Config.redis_url deleted: both had zero readers, and port was
hardcoded to 8080 in both constructors while the listener binds straight from
the PORT env var, so the field could silently disagree with the real port.
Config is now 6 fields: telegram / jmap / account_id / llm / auth / worker_token.
- redis_only() and from_business(value) signatures simplified accordingly.
- #[expect(dead_code)] count in src/ is now 0.
Test coverage: the from_env()-driven startup_rejects_blank_auth_secret case is
gone with the path it tested. The non-leak assertion it carried was reattached
to the real validation route (BusinessConfigWire -> TryFrom -> validate_nonblank)
with a stronger third assertion that the rejected value is not echoed either.
Docs: design.md 7.1 re-scoped to the Redis business config table; reference.md
5.1/5.3 rewritten (no legacy env path); deployment.md 5 and the runtime-mode
table rewritten; retired.md gains section 4 registering every deletion;
README x2, .env.example, charter.md updated.
GATE-P0: fmt / check / clippy -D warnings green, 61 passed / 0 failed / 1
ignored (was 62; the deleted case). GATE-DOCS: PASS 5/5, ANCHOR 247 line-refs /
0, SECTION-REF 216 / 0.
638c3a8 added one marker line to the top of six source files and re-based all
144 line anchors in the same commit, so that commit is the baseline for both
the code anchors and the document section refs. e0882c7 and b70d188 are no
longer valid baselines.
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.