Skip to content

client: a refused memory read says why instead of Loading… - #2397

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-memory-count-failure
Nov 4, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-memory-count-failure

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2395

Owner: Dev-2
Reviewer: Birfy
Claim-Id: 9bbd0858d6564667b2c2b5c54d85db74
Branch: dev/2-memory-count-failure
Tip: e133e02
Tree: 6f6afb53a0855679729ebd6693a80ee65c50bd16
Base: be10ea2
Diff: 11 files, +1978/-1502 (6 hand-written, 195 changed lines, +182/-13; 5 generated)
Tier: T1
why: [] — every hand-written path is client source, its test and mirror, a changelog fragment or the subsystem doc; lines 195, files 6, generated 5
basis: changelog.d/2395-memory-count-read-failure.fixed.md=T1, core/client.js=T1, core/src/client/bot-settings.js=T1, core/src/client/i18n.js=T1, core/src/client/memory-panel.js=T1, core/test/memory-count-read-failure.test.js=T1, patches/ds-bot/0001-core-fixes.patch=T1, patches/ds-bot/0003-flybot-api-client.patch=T1, patches/ds-bot/0008-client-bundle.patch=T1, patches/ds-bot/files/test/memory-count-read-failure.test.js=T1, docs/subsystems/core-client.md=T0
Docs: docs/subsystems/core-client.md — the memory-panel.js row now says what a refused count read draws, in the same sentence that already lists the dialog's own states.

What changed

A refused memory-view had nowhere to go. useMemoryCount (core/src/client/memory-panel.js) attached an empty .catch, so the rejection disappeared and count stayed null; both consumers then drew count === null ? t('Loading…'), which made "the host said no" and "the read has not answered yet" the same picture — for as long as the window stayed open, with nothing to retry.

  • The hook now keeps the failure beside the count (failures next to counts, both module-level like the count cache they share) and returns { count, failure }.
  • memoryCountText({ count, failure }) is the single wording: the failure sentence with the host's own reason, else Loading… only while nothing has answered, else 0/1/{count} entries. Both consumers — the details pane's MemoryRow and the settings card's MemoryCard (core/src/client/bot-settings.js) — call it, so the two surfaces cannot drift.
  • A refusal is a sentence, never an exception out of the effect: hostText(error?.message ?? String(error)) goes through the existing i18n key 'The memory could not be read: {reason}' → '记忆读不出来:{reason}'.
  • A client that does not offer the dialog has no actions.memoryView. The base did not throw there either — ?.() short-circuits the whole chain, so .then was never reached — but the hook now reads const pending = actions.memoryView?.(botId) and wires .then only when something came back: a missing action asks nothing, and the row keeps Loading… because no read ever started, rather than being handed a made-up failure sentence. The new test pins that as a control, green on the base.
  • A read that arrives takes the failure back (noteCount clears the entry), so a retry or a recovered host does not leave a stale sentence over a live count.

The count read is a read, so a read-only window behaves exactly as before: the lock does not eat the sentence, and Loading… is now only the genuine in-flight state.

Per-file

file delta
core/src/client/memory-panel.js +36/−9
core/test/memory-count-read-failure.test.js +140/−0 (new)
core/src/client/bot-settings.js +3/−3
core/src/client/i18n.js +1/−0
changelog.d/2395-memory-count-read-failure.fixed.md +1/−0 (new)
docs/subsystems/core-client.md +1/−1
core/client.js +43/−21 (generated)
patches/ds-bot/0008-client-bundle.patch +1533/−1462 (generated)
patches/ds-bot/0001-core-fixes.patch +67/−5 (generated)
patches/ds-bot/0003-flybot-api-client.patch +13/−1 (generated)
patches/ds-bot/files/test/memory-count-read-failure.test.js +140/−0 (new, generated)

Hand-written: 36+140+3+1+1+1 = +182, 9+0+3+0+0+1 = −13 → 195 changed lines. Generated: 43+1533+67+13+140 = +1796, 21+1462+5+1+0 = −1489. Total +1978/−1502.

0001 and 0003 move because the patch chain carries these sources itself, not because generate rewrote anything else: src/client/i18n.js (line 662) and src/client/memory-panel.js (line 1987) live in 0001, and src/client/bot-settings.js (line 360) lives in 0003, so the edits re-cut those two segments' hunks — 0001's i18n hunk header moves @@ -557,6 +557,7 @@ → @@ -555,8 +555,10 @@ for the new ZH key, its memory-panel hunk widens to @@ -5,38 +5,66 @@, and 0003 re-cuts three bot-settings hunks (the import line, MemoryCard's count/label, one context block). git diff -- patches/ds-bot/0001 patches/ds-bot/0003 touches no other file.

Verification at the tip

Tip SHA: e133e02

Red first, on a clean worktree at be10ea26 carrying only core/test/memory-count-read-failure.test.js from this branch: 5 tests, 3 pass, 2 fail, 0 cancelled. The two reds are the defect:

  • a refused memory read says why instead of "Loading…" — expected 'The memory could not be read: The host did not answer.', actual 'Loading…'
  • the row keeps the reason the host gave — expected 'The memory could not be read: Unknown Bot', actual 'Loading…'

The three controls pass on the base and stay green: an empty memory reads as empty, a count that arrived is still a count, and a client without memoryView asks nothing and throws nothing.

After the fix: the new file 5/5; with six neighbouring files (memory-panel-readonly, memory-empty, i18n, details-bot-settings-readonly, settings-readonly-head, details-copy) 31/31.

Ran:

  • TZ=UTC ARCH_DOCS_BASE=be10ea26 npm run verify — exit 0: 2827 + 570 + 132 + 140 + 411 + 671 + 37 + 26 = 4814 pass, 0 fail, 0 cancelled; lint 0 errors (core 76 warnings, plugins 36/9/2/16); arch-docs: OK (11 changed file(s)).
  • node scripts/ds-bot-patches.mjs generate — second run a no-op (git status --porcelain empty).
  • node scripts/ds-bot-patches.mjs check — green (patches apply in order; core/ equals ds-bot/ + patches/ds-bot/).
  • node scripts/ds-bot-patches.mjs canonical — generate is a no-op.
  • node core/scripts/build-client.mjs --check — exit 0.
  • node .github/scripts/changelog.mjs check — 25 fragments in changelog.d/ well formed.
  • node .github/scripts/check-empty-catch.mjs — exit 0.
  • node .github/scripts/check-mojibake.mjs --check — 0 occurrences of U+FFFD in 1455 text files (22 skipped as binary).
  • the live rules gate on the real diff: risk-tier: T1 (lines 195, files 6, generated 5), why [].

Not run (and why): no walkthrough against a running host — none is installed on this box, and the row is measured through the fake React renderer the memory tests already use. No smoke test: core/index.js and core/src/host/** have zero diff. Nothing in npm run verify was skipped.

Blast radius: two rows that only read. core/src/host/** is untouched — the host answers Unknown Bot as before. A writable and a read-only window draw the same sentence (the lock decides writes, and this is a read), and a client without memoryView still shows Loading… because it never asked.

Checkpoint

{
  "pr": 2397, "claim": "Dev-2", "issue": 2395,
  "summary": "The Memory row in a Bot's details pane and in its settings card said Loading… for as long as the window stayed open when the host refused memory-view, because the hook attached an empty catch and the row had no failure state. The hook now keeps the refusal and memoryCountText draws one sentence with the host's own reason, identically on both surfaces. A refused read is a sentence, never an exception out of the effect, and a client without the action asks nothing and keeps Loading… because no read started. A count that arrives still shows and an empty memory still reads as empty.",
  "files": ["changelog.d/2395-memory-count-read-failure.fixed.md", "core/client.js", "core/src/client/bot-settings.js", "core/src/client/i18n.js", "core/src/client/memory-panel.js", "core/test/memory-count-read-failure.test.js", "docs/subsystems/core-client.md", "patches/ds-bot/0001-core-fixes.patch", "patches/ds-bot/0003-flybot-api-client.patch", "patches/ds-bot/0008-client-bundle.patch", "patches/ds-bot/files/test/memory-count-read-failure.test.js"],
  "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=be10ea26 npm run verify", "result": "green", "at": "e133e02ce1615c6933d70179f862135bdb8c17db" },
  "next": "none — ready for review; stays a draft until FlyBot Test's advisory PASS"
}

`useMemoryCount` dropped the `memory-view` rejection, so a Bot's memory row in the
details pane and in its settings said "Loading…" for as long as the window stayed
open. The hook now keeps the failure, `memoryCountText` draws it as one sentence
with the host's reason, and a count that arrives from the dialog takes the failure
back. An empty memory still reads as empty and a count that arrived is still a count.
@HugoXK

HugoXK commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy — T1 at the tip below: tests-ok is green, no PASS needed. Please enqueue when there is a slot.

Tip: e133e02ce1615c6933d70179f862135bdb8c17db

  • Fixes #2395, base main at be10ea26, 11 files +1978/-1502, maintainer_can_modify: true, ready (not a draft).
  • Checks at this exact tip (re-read after the ready flip): 16 success + 1 cancelled + 0 failure; tests-ok, enqueue-guard and merge-gate all success on this tip; mergeable_state: clean.
  • checkpointReasons(body, {pr: 2397, files}) = [] against the live be10ea26 rules.
  • FlyBot Test advisory (advisory, not the gate): PASS at e133e02ce1615c6933d70179f862135bdb8c17db — independent red-first 5 = 3 pass + 2 fail on a clean be10ea26, tip 5/5, neighbouring memory/usage specs 31/31, verify 4814/4814, generated files reproducible (TREE_STABLE).

Scope: a refused memory read in the Bot Details drawer and in Bot settings now says The memory could not be read: <reason> instead of standing on Loading…; the two consumers share one sentence source, successes and pending are unchanged, and a missing memoryView no longer throws.

Note on the generated delta: the 0001 and 0003 hunks are re-cuts of src/client/i18n.js, src/client/memory-panel.js and src/client/bot-settings.js, which those two patches already carried on be10ea26 — the regenerated files list is identical to base, so nothing unrelated was swept in.

@Birfy
Birfy added this pull request to the merge queue Nov 4, 2026
Merged via the queue into ThinkFlowLab:main with commit eae22b6 Nov 4, 2026
17 of 18 checks passed
@Birfy
Birfy deleted the dev/2-memory-count-failure branch November 4, 2026 13:36
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.

A refused memory read leaves the Memory row on "Loading…" forever (details pane and settings card)

2 participants