fix(ui): let the login submit button restore its own label - #293
Merged
Merged
Conversation
`ui/index.html` ships the login submit button as `data-i18n="login.enter"`
(zh "进入平台" / en "Enter"), but the `#login-form` submit handler restored it to
`T("login.submit")` (zh "登 录" / en "Sign in") in its `finally` — a *foreign*
key. After any submit attempt (a wrong password is enough) the button says a
different thing, and the login screen has no language switcher, so it never
heals within the session.
The fix captures the label before the busy write and restores the captured
value — the repository's own idiom (`withLoading` reads `orig = btn.innerHTML`,
the forgot-password handler reads `const orig = btn.textContent`, plus three
more capture sites). `login.submit` had exactly one consumer in the whole repo
(that restore line) and is deleted from both packs; `login.enter` keeps its
markup consumer.
Gate: `state_gate::the_control_that_rests_owns_its_label`, three rules, all
derived from the artifacts under test (no key name is written into the gate):
(1) the last label write to a markup-declared control must derive from that
control's own source — a capture read of it, or `T(k)` with `k` equal to the
key `ui/index.html` declares; (2) every form whose submit button carries
`data-i18n` must resolve to exactly one submit handler and obey (1); (3) a
member that writes its label must write it at least twice (busy + rest), so
"delete the busy state" cannot pass. The axis test is red on the pre-fix tree
(`FOREIGN: T("login.submit")`) and green after; each rule has its own synthetic
mutant in `the_r169_rules_have_teeth`.
Tests: `cargo test` 376 passed / 0 failed (373 before). The jsdom probe
`r169_probe.js` reports 20/20 legs as declared on the landed bytes (its `C4`
leg samples before the first `await`, so the busy swap is still proven at
runtime).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
ui/index.htmlships the login submit button aszh "进入平台" / en "Enter", filled by
applyStatic()(innerHTML = t(key),called from
setLang()andDOMContentLoaded).The
#login-formsubmit handler rewrote that element's label twice: toT("login.logging")while the request was in flight, and — in itsfinally—to
T("login.submit")(zh "登 录" / en "Sign in").login.submithadexactly one consumer in the whole repository: that restore line.
So after any submit attempt — a wrong password is enough — the button says
something other than what it ships with, and the login screen has no language
switcher (that control lives in the settings view), so the element never heals
within the session. The screen shows the inline 401 error right next to the
freshly mis-labelled button.
Provenance: drift, not a tradeoff
8bd1063(the P2-A login wiring) introduced both strings in one commit: themarkup always said "进入平台", while the handler hand-wrote
btn.textContent = "登 录".68f9f70(the i18n packs) faithfully turned eachliteral into its own key, freezing the split into two keys. The design prototype
draws that button with "进入平台".
The repository's own convention is a capture:
withLoading()readsconst orig = btn.innerHTMLand writes it back, the forgot-password handlerreads
const orig = btn.textContent, and three more sites do the same. The loginrestore was the only one naming a key that is not the element's own.
The fix
The busy write is untouched, and
login.submitis deleted from both packs(net −1 key per pack;
login.enterkeeps its markup consumer).The gate
state_gate::the_control_that_rests_owns_its_label— three rules, each derivedfrom the artifacts under test; no key name is written into the gate (the
button's own key is read out of
ui/index.html):that control's own source: a capture read of it, or
T(k)wherekis thekey the markup declares;
data-i18n(five today)must resolve to exactly one submit handler and obey (1); "cannot be
resolved" is a loud failure, not a pass;
(busy + rest), so "delete the busy state" cannot pass.
Evidence
Instruments are run against the tree in this PR;
baselegs are fed the pre-fixtree (
git show HEAD:ui/js/app.js).Rust gate —
the_control_that_rests_owns_its_labelis red on thepre-fix
ui/js/app.jsand green here. The red reading names the defect:the_r169_rules_have_teethbuilds four synthetic mutants on the live tree(every anchor asserted unique), each flipping exactly the rule it targets:
a foreign restore → R1 + R2; deleting the busy write → R3; restoring with the
element's own key → all green (the other accepted shape,
fix_key); anunresolvable sibling binding → R2 only.
the_r169_roster_is_realis thepositive control: five forms derived from the markup, the login button's own
key read as
login.enter, all five handler bodies sliced correctly (each endson its closing brace and contains
preventDefault).jsdom probe (
r169_probe.js; realindex.html+ the four real scripts, notoken, a real
submitevent, once per language):---- 20 legs, 0 misdeclared ---- RESULT: ALL LEGS AS DECLAREDon the landedbytes; on the pre-fix tree exactly
B1/B2/D1are red in both packs.Its
C4leg samples before the firstawaitand still seesdisabled=true label="登录中…"/"Signing in…", so the busy state is provento have happened rather than assumed.
cargo test: 376 passed / 0 failed (373 before — the three new gatetests).
cargo fmt --checkclean;cargo clippy --all-targetsrc=0.src/i18n_pack.rspositive controls are re-read from the gates' own output,not recomputed:
ZH_KEY_COUNT/EN_KEY_COUNT792 → 791,T_LITERAL_COUNT545 → 544,T_LITERAL_DISTINCT434 → 433 (bothSTATIC_ATTR_*are unchanged —login.submitwas never adata-i18nattribute).
Scope (stated, not implied)
The gate is lexical: it proves that the resting expression and the markup key
are the same source; it does not prove the label rendered at that moment
(that half belongs to the jsdom probe). Handles are recognised by the
repository's dominant spelling
querySelector('button[type="submit"]')(login,register, verify, share today);
#forgot-formusesquerySelector("button[type=submit]")and is therefore not counted as alabel-writing member — recorded in
ui/README.mdrather than papered over.Relabelling the markup to
login.submit(self-consistent and gate-green) isrejected by the probe's
D2leg against the design prototype.Related Issue
None. No issue in this repository is open (all are closed); this defect comes
from this task's own recon. The
needs:issuebot check therefore cannot pass —by design, not by omission.
Checklist
fix/…)