Repository navigation
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 152521891c4c4c7cb0043e3094633caf
Branch: dev/1-unit1-oneline-code-point
Base: 37e8162
At: 2026-11-01T00:54:57Z
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
What happens today
Two independent 300-unit helpers still cut with String.prototype.slice, which counts UTF-16 code units. When the bound lands between the halves of a surrogate pair (an emoji or any astral character), the cut keeps the high surrogate alone; that lone surrogate is stored and every renderer shows it as U+FFFD.
core/src/host/tasks.js:105—oneLine(value, max); the 300-budget call sites arereadNote(:124),:135,:161andreadTitle(:106). Base37e81623line numbers.core/src/host/work-dispatch.js:16— a separate local helper of the same shape,.slice(0, 300), used at:33to turn a group line into a dispatched card'stitleandnote. Base37e81623line numbers.
They are two copies, not one shared function: fixing one does not fix the other, and a single test may not stand for both.
Reproduction (base 37e8162)
With only the two new test files added to a pristine 37e81623:
core/test/tasks.test.js→a task note cut at the 300-unit bound keeps the pair wholefails withthe stored note is not well formed: "a\ud83d".core/test/work-dispatch.test.js→a dispatch title cut at the 300-unit bound keeps the pair wholefails withthe title is not well formed: "x\ud83d".
# pass 0 / # fail 2.
Expected
A bound that lands inside a pair drops the pair whole, so the stored string is always well formed, and the budget stays 300 units — it must not silently shrink to 299, and a pair that ends exactly on the bound must be kept.
core/src/shared/text-boundary.js already exports cut() for exactly this and core/src/host/tasks.js already imports it; no new helper is needed.
Scope
core/src/host/tasks.js+core/test/tasks.test.jscore/src/host/work-dispatch.js+core/test/work-dispatch.test.js- the four
patches/ds-bot/files/mirrors, regenerated bygenerateon37e81623(never hand-edited), thencmp-checked againstcore/ docs/subsystems/core-host.md: the sentence the arch-docs gate asks for, next to the existing 2,000-bound paragraph- one
changelog.d/fragment - one PR; each site has its own failing test first, and each of the two mutations (revert one site to
slice) turns only its own test red
Out of scope
- Text already stored ill-formed by hand or by import: that needs a load-time
toWellFormedin the store, a separate issue. - The other
.slice()sites in the tree (#1917,#1930,#1915and friends covered their own sets;#1956/#1957covered the 2,000-unit bounds insidetasks.jsonly).
Duplicate check (2026-11-01)
On live main 37e81623 both sites are still bare slice (read-only verification: 0 of the audited sites are already cut). Closed issues searched: code point, surrogate, oneLine, work-dispatch 300. The closest are #1917 (groups/browser/diagnostics/remote-label), #1930 (busy-ack/delivery/tools), #1956/#1957 (the 2,000-unit bounds in tasks.js); none touches tasks.js:105's 300 budget or work-dispatch.js:16. No duplicate, no follow-up issue pending.
Activity
HugoXK commentedon Nov 1, 2026
Advisory self-tests at the PR tip
1c31c817c6556cfdaf1c8270bab9b8eb39ca4c31(tree7818b2923eb39a8c414600ee45c19ee695722ae0): FlyBot Test PASS, FlyBot Security PASS. Both are advisory; the repository Verdict is Birfy'sVerdict: PASS at 1c31c817c655(comment 6551552491, 01:06:32Z), andenqueue-guardis green at that tip.Record-only notes from those runs, so a later change to the same helper does not have to rediscover them (the PR tip and body are frozen after the Verdict, so none of this was edited there):
core/src/host/tasks.jsare imprecise, the line numbers are right.oneLine(definition:105) capsreadTitle(:106call),readNote(:124), and also:115(links),:135(readTarget),:161(readOptionalLine— reason / ask / answer), plus:153/:154(write-back 200 / 500 / 1500),:234(a name at 200),:862,:894,:932, androle(24) elsewhere in the file. There is noflowfield in the repository. All of these sites can only change where the oldslicesplit a pair, so the direction is still "fixes", but whoever editsoneLinenext should not read it as a title-only helper.299 × 'a' + '😀'yields 299 units. What the case pins is that an input of exactly 300 units stays 300 (no gratuitous 299), not that the output can never be 299.Verified commit 19980663…in the PR body is a local pre-push sha (404 in both remotes); the verifiable identity is the tree7818b292with parent37e81623.state.json) passes through unchanged; this change only avoids cutting a good string in half. A store-loadtoWellFormed()would be a separate issue.