Skip to content

fix(ui): label a market row's availability from the row, not from a missing key count - #248

Merged
argszero merged 1 commit into
mainfrom
fix/market-availability-pill-row-flag
Sep 14, 2026
Merged

argszero merged 1 commit into
mainfrom
fix/market-availability-pill-row-flag

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

On the guest marketplace every row's availability cell reads "No key", while on the same row the availability dot is green and the 使用 / Use button is enabled — two pieces of copy on one row contradicting each other.

A market row's availability has exactly one fact: avail. It is what the dot (.dot), the disabled state of the 使用 button, the "Available only" filter and the expanded detail's "当前可用 / 当前繁忙" all render. keys is not a second fact — it only exists when a count is known: the logged-in path fills it from /api/modelsavailable_keys via modelsToView(), and the guest fallback table (data.js > MARKET) deliberately carries no count (rant 2026-08-19T15:54:06: multi / success were removed as fabricated data). The availability pill, however, read only keys, so m.keys || 0 was 0 and every guest row fell into the "no key" branch.

Measured in jsdom with the real ui/index.html + the four real scripts (only fetch stubbed):

  • guest leg — 7/7 rows read "No key"; on 6 of them the dot is live and the button is enabled
  • logged-in leg (positive control) — the three count states (keys=3/1/0) render correctly, so the pill itself is not broken; what is missing is the field it reads on guest rows

Related Issue

Changes

  • ui/js/app.js: availPill() keeps the count states (>=2 multi, ==1 tight) and falls back to the row's own avail when no count is present, so the cell can no longer contradict the rest of the row.
  • ui/js/i18n.js: new key mk.avail.on (zh 「可用」 / en "Available") — the honest label when only "usable" is known. mk.avail.multi / mk.avail.tight / mk.avail.none unchanged.
  • ui/index.html: cache-bust i18n.js / app.js.
  • src/catalog_gate.rs: new gate market_availability_pill_agrees_with_the_row_it_renders pinning both halves — the consumer must consult m.avail (with the fallback table's boolean avail as the positive control that the field name is real), and MARKET rows must not carry a hand-written keys count (that would put fabricated operational data in front of guests). js_function_body_stops_at_the_right_place proves the extractor stops at the right place and that the assertion has teeth via a synthetic input.
  • src/i18n_pack.rs: key-count constants follow the +1 key (the extractor's own output was used for calibration, not hand arithmetic).
  • ui/README.md: new convention section + key-count sentence kept in sync.

Not the fix: adding hand-written counts to MARKET. That is the same thing v1.22 cleaned up (multi / success); if guests are to see a real count it has to come from /api/models.

Tests

  • cargo test262 passed / 0 failed (was 260; +2 new gate tests)
  • cargo fmt --check — clean
  • cargo clippy — only the pre-existing warning at src/protocol.rs:662
  • New unit tests added (the gate above)

A/B evidence (tmp/c2128_*, all UI mutations injected from tmp/ through env vars — the work tree is never rewritten):

leg red set note
v0_orig (pinned ecc654c app.js/i18n.js) {B1, B2, B3} the axis
m_swap (pill reads only m.avail, count states dropped) {C2} kills the logged-in positive control ⇒ the count states must stay
m_fabricate (competing fix: hand-written keys in MARKET) {} honestly recorded: the probe alone does not pin the direction — the Rust gate does (its MARKET-must-not-carry-keys half goes red)
live {} 11/11

In-place gate legs (byte-identical restore verified by md5): m_nofallback → red at the consumer assertion; m_fabricate → red at the data assertion.

Checklist

  • 分支命名符合约定(fix/
  • Commit message 使用 Conventional Commits 格式
  • 单一职责,改动最小化

…issing key count

游客市场每一行的「可用性」都渲染成「无 key」,而同一行的绿点是亮的、「使用」按钮
是可点的 —— 同一格里两处文案互相打脸。

一个市场行的可用性只有**一个**事实:`avail`(绿点 / 「使用」按钮的 disabled /
「仅可用」筛选 / 展开详情的「当前可用」四处都读它)。`keys` 只是**有计数时才存在**
的补充说明:登录态由 `modelsToView()` 从 `/api/models` 的 `available_keys` 填,
游客兜底表 `data.js > MARKET` 按 rant 2026-08-19T15:54:06「虚构数据已移除」不带计数
(同源被删的还有 `multi` / `success`)。可用性 pill 却只认 `keys`,于是 `m.keys || 0`
恒为 0,游客 7/7 行都落进「无 key」档。

jsdom 实测(真 `ui/index.html` + 四个真脚本,只 stub `fetch`):游客腿 7 行中 6 行
圆点亮、按钮可点、pill 却写「无 key」;登录腿是阳性对照 —— 三档计数 pill 都正常,
说明 pill 本身没坏,坏的是游客数据里被读的那个字段不存在。

改法:pill 先看计数(≥2 / ==1),没有计数时回落到 `m.avail`(新键 `mk.avail.on`)。
**不给兜底表补手写计数** —— 那是把虚构的运营数据摆给游客看。

CI:`catalog_gate::market_availability_pill_agrees_with_the_row_it_renders` 同时钉住
消费侧(pill 必须读 `m.avail`)与数据侧(`MARKET` 行不得出现手写 `keys`),
`js_function_body_stops_at_the_right_place` 自证提取器并有合成输入对照。
@argszero
argszero merged commit 818ed88 into main Sep 14, 2026
1 check passed
@argszero
argszero deleted the fix/market-availability-pill-row-flag branch September 14, 2026 17:22
@argszero argszero mentioned this pull request Sep 15, 2026
10 tasks
argszero added a commit that referenced this pull request Sep 15, 2026
Ships the 18 PRs merged since v0.7.24 (#242-#259). Schema 14 -> 15 (two
covering indexes, applied at startup). No config change, so no deployment-side
config.toml edit is needed.

Two themes:

Perf on the NFS dev database
- #259: stop mapping the db (PRAGMA mmap_size 64MB -> 0) and stop a real write
  per request (dao::touch_api_key gains a 60s guard). Measured on the live dev
  db: mmap=64MB 1.7-3.1s per COUNT / 250 MiB read vs mmap=0 ~10.5ms / 80 KiB;
  mmap=0 alone still leaves ~1.2s behind any write, so the pair is required.
- #242: codify the two emergency indexes in a v15 migration and gate the
  conditional joins at the plan level.
- #243: read the sharing page's earn total from one batched aggregate.

Frontend: display must equal what it filters on, and one fact, one source
- #250 one writer for the transaction cache; #251 clear every session slot at
  the identity boundary and give the wallet view a loader; #253 one shared
  writer for the wallet/dashboard month-changes; #254 boot loads only the
  destination view; #255 a model row's identity is the model, not its index;
  #256 the marketplace source follows the session, not whether data arrived;
  #257 the sidebar advertises only digits that work; #258 the admin
  total-balance card sums the gift amount its caption names.

i18n
- #249 every backend error reaches the wordlist, and the comment stripper stops
  mangling UTF-8; #252 the backend stops inventing Chinese display labels in
  response data fields.

Forms and robustness
- #244 a non-auth boot failure no longer looks like being logged out; #245 a
  credential 401 is no longer read as a session expiry; #246 wire timestamps
  reach the renderer unsliced; #247 inline cards submit from every field; #248
  a market row's availability label comes from that row.

- Cargo.toml / Cargo.lock: 0.7.24 -> 0.7.25.
- CHANGELOG.md: v0.7.25 entry.
- ui/index.html: cache-bust left as-is; the UI PRs in this release already
  advanced it past the value deployed with v0.7.24 (app.js 20260915-13,
  i18n.js 20260915-3).

cargo test 288 passed; cargo fmt --check clean; clippy unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant