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: 033a673560cc461c8eb0c9a870d5a912
Branch: dev/5-steward-shorten-chars
Base: 940815a
At: 2026-11-06T00:07:16Z
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.
Written-by: FlyBot Lead
What happens
shorten() in core/src/host/steward.js claims to cut a long message on a 1500-character budget, and #2546/#2548 moved the test of that budget to code points (fits()), but the two cut points still mix two units:
const head = codePointEnd(text, Math.ceil((STEWARD_LINE_MAX * 2) / 3))
const tail = codePointStart(text, text.length - (STEWARD_LINE_MAX - head))codePointEnd() returns a UTF-16 index (it only steps back off a surrogate pair), so head is an index, yet STEWARD_LINE_MAX - head subtracts it from a character budget and then subtracts that from text.length, also an index. slice() therefore cuts at the right place, but the budget arithmetic is off whenever the message holds astral characters: the view keeps fewer characters than 1500 and the marker overstates what was left out.
Measured with a probe built only on stewardRequest. steward.js is blob fca6229d27f0 on both 3d81d8b47f0b and the newer 940815ab184e, so the readings hold on both:
| message (1500-character budget) | characters kept | marker says |
|---|---|---|
'a'.repeat(1600) |
1500 | 100 |
'🚀'.repeat(1800) |
1500 | 300 |
'🚀'.repeat(800) + 'a'.repeat(800) (1600 chars) |
1000 | 600 |
'🚀'.repeat(500) + 'a'.repeat(1100) (1600 chars) |
1000 | 600 |
| 1500 characters exactly | 1500 | 0 (fits) |
A message dense in astral characters (🚀, most emoji, CJK extension B) loses a third of the view the budget allows, and such a message is exactly a long pasted report.
Not a regression from #2548: before it, the same two lines were on UTF-16 units and the view was 1000/750 characters. This is the half of #2546/#2548 that did not move.
Reproduction
No source change, live main:
import { stewardRequest } from './core/src/host/steward.js'
const body = '🚀'.repeat(800) + 'a'.repeat(800) // 1600 code points
const [line] = stewardRequest({ lines: [{ who: 'user', text: body }], midTurn: false, checkpoints: [], group: null }).split('\n\n')
console.log([...line].length, line.includes('characters left out'))
// today: 1000 characters kept, marker reports 600 left outExpected
For a message over the 1500-character budget, the view keeps 1500 characters: the head is the first 1000 code points, the tail is the last 500 code points, and the marker reports characters − 1500 left out (100 for a 1600-character message). A message at or under 1500 characters is returned untouched.
Scope
core/src/host/steward.js:shorten()takes both cut points from the code-point count (head= UTF-16 index of the 1000th code point,tail= index of the 500th code point from the end), so an index is never subtracted from a character budget.fits(),linesFor(),STEWARD_TOKENS,STEWARD_LINE_MAXand the marker wording stay as they are.core/test/steward.test.js: assertions added to the existing case at:120('a long message shortened for the steward cuts on characters, never inside one', whose:123comment already states the rule), not a new file —'🚀'.repeat(800) + 'a'.repeat(800)throughstewardRequestkeeps 1500 characters and a marker of 100; red on today's tree (1000/600).patches/ds-bot/0001-core-fixes.patchregenerated bynode scripts/ds-bot-patches.mjs generate— bothcore/src/host/steward.jsandcore/test/steward.test.jslive in that patch, socanonicalfails without it; onechangelog.d/fragment.- Not the client bundle, not
core/client.js; no plugin or docs change needed.
Duplicate check
Searched steward.js (blob fca6229d27f0, unchanged on 3d81d8b4 and 940815ab) for STEWARD_LINE_MAX, codePointEnd, shorten( and the closed issue list for "steward shorten": the only hits are #2546 (closed, merged) and #2548 (closed, merged), which fixed the fits() test but left these two lines; no open issue covers the remaining half. Tier: T2 (core/src/host/**).
Activity
HugoXK commentedon Nov 6, 2026
Release
Written-by: FlyBot Lead
Released: 033a673560cc461c8eb0c9a870d5a912
Reason: merged and issue closed
At: 2026-11-06T02:13:30Z