Repository navigation
host: the memory review's two-ended cut spends its budget in characters - #2555
Conversation
|
Verdict: PASS at 2d05041 Ran at this tip: Red first — 1 red. With only Scope — exactly the 5 stated files: The fix — Tip checks — 15 runs, all terminal, one failure: Not run here — |
`ends()` judged the line with `fits()` (characters) but split it with `text.length` (UTF-16 units): the tail started `max / 3` units before the end and the head took `max - tail.length` units, so a line of characters outside the BMP kept about half of the 3,000-character budget. Spend the budget in characters at both ends through `charIndex`, which steps whole code points, so the head is the rendered line's first 2,000 characters and the tail its last 1,000 and neither cut can land between the halves of a surrogate pair. The text is flattened with `toWellFormed()` first, as `cut` did. The test extends the existing two-ended-cut case: a 3,500-emoji user line keeps 3,000 characters where it kept 1,503, with ASCII of the same length as the control. `docs/subsystems/core-host.md` carries the new sentence. Fixes ThinkFlowLab#2553
Fixes #2553
Owner: Dev-5
Written-by: Dev-5
Reviewer: Reviewer
Risk tier: T2 —
core/src/host/memory.js, its test, the subsystem page it owns, the generated patch and one changelog fragment.Base: 39bd520
Tip: 2d05041 (tree 47fdb1593ccb1f6de3830302065a7d7084f8d4de)
The defect
ends()incore/src/host/memory.jsgives the user's own line in the memory review a 3,000-character budget. #2339 moved its test tofits()(characters), but both cut points still spend the budget in UTF-16 units:text.lengthis a unit count, andmax - tail.lengthsubtracts a unit count from a character budget. With characters outside the BMP the tail starts too early, the head gives back units it never had, and the view keeps about half of its budget.Readings through the review's own render path on the base
39bd5201(rendered line =User:+ text):'🚀'.repeat(3500)— 3,506 rendered chars / 7,006 units'a'.repeat(3500)— same rendered length in charactersThe ASCII row is the control: it spent the budget correctly before the change too, so this is the mixed-unit arithmetic and not the budget or
fits().Red first
On the base with only the test file changed (
core/test/memory.test.js, the existing#1918two-ended-cut case at:570, no new file and no new test name):The two older assertions in that case (head and tail are byte prefixes of the input) still pass on the base; the new block asserts head 2,000 + tail 1,000 = 3,000 for the emoji line and for the ASCII control, and that both ends are the rendered line's real ends.
The fix
charIndex(text, chars)— the UTF-16 index the nth character starts at, stepping whole code points (the same helper and shape as host: the steward's long-message budget is characters at both ends #2554'ssteward.jsfix);tail = flat.slice(charIndex(flat, codePoints(flat) - tailChars))andhead = flat.slice(0, charIndex(flat, max - tailChars)), so both ends spend the budget in characters and neither cut can land inside a surrogate pair;toWellFormed()first, ascutdid, so an unpaired surrogate still becomes U+FFFD instead of half a character.fits(),REVIEW_USER_LINE_MAX, the exportedclipand every other bound are unchanged;codePointStartleaves the import because nothing in the file uses it now.Docs
docs/subsystems/core-host.mdownscore/src/host/and its text-bounds row stated the oldends()rule (moves the tail's start back with codePointStart), so it is updated in this PR rather than left saying something the code no longer does: the#1918sentence now says the budget is spent in characters throughcharIndex, that the head is the rendered line's first 2,000 characters and the tail its last 1,000, that the text is flattened first, and it links #2553.arch-docstherefore reports 5 changed files.Files (
git diff --numstat 39bd5201...HEAD)core/src/host/memory.jsis not inpatches/ds-bot/files/; it lives in0001-core-fixes.patch, whichnode scripts/ds-bot-patches.mjs generaterewrote once after the rebase onto39bd5201and a second run leaves byte-identical (checkclean,canonicalno-op).core/client.js,patches/ds-bot/0008*andcore/src/host/diagnostics.jsare untouched.Ran, at the tip
2d050418core/test/memory.test.json its own at the tip: 98 tests, 98 pass. Not run:dev/bench.sh smoke— nocore/index.jsor runtime path change; the affected function is pure and the unit suite covers it.Checkpoint
{ "pr": 2555, "claim": "Dev-5", "issue": 2553, "summary": "The memory review's two-ended cut judged its 3,000-character budget with fits() but split the line in UTF-16 units, so an emoji-dense user line kept 1,503 of 3,000 characters. ends() now spends the budget in characters through a local charIndex() and flattens with toWellFormed(), giving the rendered line's first 2,000 characters and its last 1,000. The case added to the existing #1918 test fails on the base, and the subsystem page's text-bounds row carries the new rule.", "files": ["core/src/host/memory.js", "core/test/memory.test.js", "docs/subsystems/core-host.md", "changelog.d/2553-memory-review-line-budget.fixed.md", "patches/ds-bot/0001-core-fixes.patch"], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=39bd5201950a0657399f726f14c0a22e3e1c47df npm run verify", "result": "green", "at": "2d0504185fdb3415d6f8f73c7129806cc8d2d505" }, "next": "none — ready for review" }