fix(admin): state the refusal a deleted model now meets, not the old zero bill - #312
Conversation
…zero bill
`admin.models.sub` (both packs + the `index.html` static fallback) told admins
that "calls for a deleted model bill at 0", and four code comments said the same
thing (`admin_models`' module doc and `remove` doc, `db::seed_models`,
`sharing::create` + its C2065 test). The sentence was true when it was written
(`98a7a9b`, v0.6.4) but R156 (`acb8815`) closed that state: `forward` and
`forward_stream` price every candidate key before picking one
(`src/gateway.rs:278-296` / `:512-523`), so a `status='on'` key whose
`(provider, model)` left the catalog is dropped from the pick set and the call
is refused with 503 ("provider 与 model 不在模型目录中,无法计价") *before
anything reaches upstream*. The settlement fallback arm is now a loud
`log::error!` instead of a silent zero.
R156 touched three files (`db.rs`, `gateway.rs`, `state_gate.rs`) and left every
carrier of the old sentence in place -- including `db.rs`, where it rewrote the
first half of that comment into the invariant and kept the stale half. This is
the same one-fact-two-answers shape this repository keeps closing: the copy is
the only place an admin meets the rule, and it described a behaviour the code no
longer has.
Change the words, not the behaviour. `ui/js/i18n.js` (zh + en) and the
`index.html` static fallback now say a key still pointing at a deleted model
cannot be priced and its calls are refused before they reach upstream, reusing
the wording the refusal itself already carries (`err.modelNotInCatalog`, the
`ERR_MAP` entry derived from `routes::{gateway,sharing}`). `i18n.js?v=` is
bumped per `ui/README.md`. The four comments now point at the authority (the
pricing gate at the routing entry) instead of restating a rule that lives in
code.
No new i18n keys, no new literals, no attribute changes: the i18n counters
(ZH/EN_KEY_COUNT, T_LITERAL_*, STATIC_ATTR_*, ERR_MAP_ENTRY_COUNT) are untouched.
No new gate. This is the doc-comment-claims axis -- a claim that stopped being
true -- where this repository's settled answer is to fix the data rather than add
a lexical guard (C2024, C2025, C2059; more recently R89 and R91). A lexical gate
can pin the shape of a sentence, not its truth, and both mechanical alternatives
are worse: copying the refusal sentence into the copy would mint a second
carrier of one fact, and a hand-written list of forbidden words is a wordlist,
not an invariant.
`cargo test` 413 passed / 0 failed (baseline 413); `cargo fmt --check` and
`cargo clippy --all-targets -- -D warnings` both clean.
|
Self-review (this task holds Does the diff say what the base commit does? Yes. Is the new wording ours or newly minted? Ours. It reuses the Did anything shift? No key, no literal, no attribute: Deliberately absent: a new gate. This is the doc-comment-claims axis and the repository's settled answer is to fix the data (C2024 / C2025 / C2059, plus R89 Verified on the branch: |
`ui/README.md`'s "设置 / 管理 / 运营布局约定(v1.22)" section still calls the operator overview's key card "上游 key 健康" and lists its three pills as "健康 / N 个异常 / 全部失败". That was true when `7d9b56e` (PR #174) wrote the line; C2158 (#269, `e5ee178`) then renamed the card and rewrote the pills, because the data has no health signal at all — `/api/ops/runtime` only returns `total` / `on` / `off`. #269 updated only the section it added itself further down this same file (`:1080`, which says the opposite), leaving this line behind, and its edit sheet (4 files / 7 edits) never listed it. Correct the wording to what the code ships: the title is `ops.keys.title` ("上游 key 状态") and the pills are `ops.keys.allOn` / `someOff` / `allOff` ("全部启用 / N 个停用 / 全部停用"). No gate: this is prose restating an implementation name, and the repo's standing decision for that axis is to fix the data, not to guard the prose (see PRs #309, #310, #312–#315). Verified on the branch tree: `cargo test` 417 passed / 0 failed (unchanged baseline), `cargo fmt --check` and `cargo clippy --all-targets -D warnings` both clean. The other claims on the same line were re-checked and are still true (`version` ← `env!("CARGO_PKG_VERSION")`, the five `uptime_*` fields, `split_uptime`, `fmtUptime`'s top-two-nonzero units, `ops.uptime.*`, `today_hours` zero-filled 0–23). The one remaining occurrence of the old wording (`:1084`) is the C2158 section quoting it as history and is left untouched.
Summary
admin.models.sub— the card subtitle on the admin models page, in both packs and theindex.htmlstatic fallback — tells admins that "calls for a deleted model bill at 0". So do four code comments (admin_models' module doc and itsremovedoc,db::seed_models,sharing::createand its C2065 test).That was true when the sentence was written (
98a7a9b, v0.6.4). R156 (acb8815) closed that state:forwardandforward_streamnow price every candidate key before picking one (src/gateway.rs:278-296/:512-523), so astatus='on'key whose(provider, model)has left the catalog is dropped from the pick set and the call is refused with 503 ("provider 与 model 不在模型目录中,无法计价") before anything reaches upstream. The settlement fallback arm is a loudlog::error!now, not a silent zero.R156 touched three files (
db.rs,gateway.rs,state_gate.rs) and left every carrier of the old sentence in place — includingdb.rs, where it rewrote the first half of that comment into the invariant and kept the stale half.One consequence worth naming: the card subtitle is the only place an admin meets this rule. The catalog editor is exactly where the state is created (deleting or renaming a catalog row does not touch
keys—admin_models::removeis a bareDELETE FROM models WHERE id = ?1), so the page that creates the condition is also the page that misdescribes it.Related Issue
None — found during this task's own reconnaissance. No open issue covers it (
gh issue list --state openis empty).Changes
admin.models.sub(zh + en inui/js/i18n.js, and theui/index.htmlstatic fallback, kept byte-identical to the zh value) now states the refusal instead of the zero bill.err.modelNotInCatalog, theERR_MAPentry derived from the backend literals inroutes::{gateway,sharing}), so the copy and the error the user actually gets speak with one voice.ui/js/i18n.js?v=20260927-2→?v=20260927-3per the cache-bust convention inui/README.md.admin_models' module doc, which already claimed not to restate it.What is deliberately not changed
CHANGELOG.md:243(the v0.6.4 entry, "deleted models billed at 0"). It was true when written; this repository does not rewrite history entries (unlike the v0.7.28 correction, where the token was wrong at the moment it was typed).src/gateway.rsin full, and the R156 gate header insrc/state_gate.rs. Those passages describe why the gate exists — their subject is the defect that was closed, not today's behaviour. Rewriting them would erase the reasoning that justifies the code, and the gate'sR2fixture (state_gate.rs:17111) quotes the fallback arm'slog::error!literal verbatim.Why no new gate
This is the doc-comment-claims axis — a claim that stopped being true — where this repository's settled answer is to fix the data, not add a lexical guard (C2024
d2c7431, C20259db4872, C205981df12d; most recently the R8945510baand R91194cc5frounds). A lexical gate can pin the shape of a sentence, never its truth, and both mechanical alternatives are worse than the defect they would guard: making the copy quote the refusal sentence would mint a second carrier of one fact (the thing this repository spends its gates removing), and a hand-written list of forbidden words is a wordlist, not an invariant.Tests
cargo test: 413 passed / 0 failed (baseline on11aaba5is 413;413 filtered out-style counts unchanged).cargo fmt --check— clean.cargo clippy --all-targets -- -D warnings— clean.ZH/EN_KEY_COUNT,T_LITERAL_*,STATIC_ATTR_*,ERR_MAP_ENTRY_COUNT) are untouched by this change and keep passing unchanged, which is the evidence that the edit added no key, no literal and no attribute.Checklist
fix/…)