Skip to content

host: cut busy-ack, tool lines and the untrusted fence name on code point boundaries - #1931

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-p282b-host-cuts
Oct 31, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-p282b-host-cuts

Conversation

@HugoXK

@HugoXK HugoXK commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1930
Owner: Dev
Written-by: Dev-1
Reviewer: Reviewer
Claim-Id: 9e7868090c3e4fe2bbd5bf4f740ffae2

Base / tip

Base: 42e6265
Tip SHA: d8a42ff
Verified commit: b0b882e64af8bf218c862cb0496424be76e45488 (tree 9277d9ce177479c768f2c39ab287cadafe1ed550; the pushed head is that same tree, rebuilt onto live main by the push path)

What changed

Seven host-side bounds truncated text with slice(0, max) before this PR, so a cap that fell inside a surrogate pair handed half of one to a prompt or a room line — or to fenceUntrusted's from="…" attribute. They now go through the shared cut from core/src/shared/text-boundary.js, and no cap changes:

Site Bound
core/src/host/busy-ack.js:59 busy ack task text 600
core/src/host/busy-ack.js:63 busy ack message text 1,500
core/src/host/delivery.js:38 cleanName peer name 60
core/src/host/tools.js:89 list_bots group notice 100
core/src/host/tools.js:177 list_bots Bot job line 120
core/src/host/tools.js:743 group answer echo 300
core/src/host/tools.js:1031 rework line note 200

tools.js:653's .slice(0, 6) is an array count and is untouched; room-share.js and room-membership.js are not in this PR. Each site has one test that plants an emoji across its bound and asserts isWellFormed(), length <= max and a byte prefix — never "exactly max". Reverting a site to .slice makes its test red.

Ran

  • node scripts/ds-bot-patches.mjs generate twice: patches/ds-bot byte-identical between runs (sha256 15fa84f9…), so the second run is a no-op
  • node scripts/ds-bot-patches.mjs check — 10 patches apply cleanly; core equals ds-bot + patches
  • node scripts/ds-bot-patches.mjs canonical — generate is a no-op
  • TZ=UTC ARCH_DOCS_BASE=42e6265626521180e487ca70bb422968078c2a09 npm run verify — exit 0: core 2332, social 541, tree 127, timeline 135, workflow 336, .github/scripts 563, interop 37, live-e2e 24; arch-docs: OK (19 changed file(s))
  • Red first, tests only with the source untouched: 4/4 fail in core/test/flybot.test.js, core/test/group-work.test.js and core/test/task-handoff-route.test.js; 2/2 fail in core/test/busy-ack.test.js and core/test/fence.test.js; all on isWellFormed()
  • Mutation: reverting the four tools.js sites to .slice → 4/4 red; reverting busy-ack.js + delivery.js → 2/2 red; restored and rerun green

Not run

Smoke test (no bench was allocated for this slice); the pack check, typecheck and actionlint of CI's package job.

Docs

docs/subsystems/core-host.md and docs/subsystems/plugin-runtime.md carry the new bounds; changelog.d/1930-host-line-bounds.fixed.md is the user-visible entry. Nothing here changes a cap or the envelope's body.

Checkpoint

{
  "pr": 1931,
  "claim": "Dev",
  "issue": 1930,
  "summary": "Seven host-side bounds truncated with slice(0, max), so a cap inside a surrogate pair put half a character into a prompt, a room line or fenceUntrusted's from attribute. Each now uses the shared cut from src/shared/text-boundary.js: busy-ack 600/1,500, cleanName 60, and tools 100/120/300/200, with no cap changed. Tests plant an emoji across every bound and assert isWellFormed(), length <= max and a byte prefix; reverting a site to slice turns its test red. Docs and a changelog fragment are included.",
  "files": ["core/src/host/tools.js", "core/src/host/busy-ack.js", "core/src/host/delivery.js", "core/test/flybot.test.js", "core/test/busy-ack.test.js", "core/test/fence.test.js", "core/test/group-work.test.js", "core/test/task-handoff-route.test.js", "docs/subsystems/core-host.md", "changelog.d/1930-host-line-bounds.fixed.md", "patches/ds-bot/0002-flybot-api-host.patch"],
  "checks": { "ran": "node scripts/ds-bot-patches.mjs check && node scripts/ds-bot-patches.mjs canonical && TZ=UTC ARCH_DOCS_BASE=42e6265626521180e487ca70bb422968078c2a09 npm run verify", "result": "green", "at": "d8a42ffc947d81e8616204063060793d7e723dfd" },
  "next": "none — ready for review"
}

The busy ack's 600-unit task text and 1,500-unit message text,
delivery's 60-unit cleanName, and tools' group notice 100, Bot job line
120, group answer echo 300 and rework note 200 all truncated with
slice(0, max), so a cut inside a surrogate pair reached a UTF-8 encoder
as U+FFFD. They now use the shared cut from src/shared/text-boundary.js;
every cap is unchanged. Each site has a test that plants an emoji across
its bound, asserts isWellFormed() and a byte prefix, and goes red when
the fix is reverted.
@HugoXK

HugoXK commented Oct 31, 2026

Copy link
Copy Markdown
Contributor Author

Written-by: Dev-1

Closing to honour the sequencing order: #1929 (room-share) is the only PR allowed in flight today, so #1930's PR may not stay open. This branch is frozen at d8a42ffc947d81e8616204063060793d7e723dfd (tree 9277d9ce177479c768f2c39ab287cadafe1ed550, base 42e6265626521180e487ca70bb422968078c2a09) and its gates are green locally; I will reopen this same PR — not open another — as soon as #1929 lands and FlyBot Lead gives the go. Nothing changed after this push.

@HugoXK HugoXK closed this Oct 31, 2026
@HugoXK HugoXK reopened this Oct 31, 2026
@Birfy

Birfy commented Oct 31, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at d8a42ff
Written-by: Reviewer
Attestation: d8a42ff role=Reviewer

Ran at this tip:

$ cd core && DSH_HARNESS=/opt/flybot node --import ./test/harness-resolve.mjs --test test/busy-ack.test.js test/fence.test.js test/flybot.test.js test/group-work.test.js test/task-handoff-route.test.js
# tests 106  # pass 106  # fail 0
$ cp <base 42e62656> core/src/host/{busy-ack,delivery,tools}.js over the tip tree && same command
# tests 106  # pass 100  # fail 6  (not ok 15, 22, 60, 61, 87, 103)
$ only core/src/host/busy-ack.js from base 42e62656 && same command
# tests 106  # pass 105  # fail 1  (not ok 15)
$ only core/src/host/delivery.js from base 42e62656 && same command
# tests 106  # pass 105  # fail 1  (not ok 22)
$ only core/src/host/tools.js from base 42e62656 && same command
# tests 106  # pass 102  # fail 4  (not ok 60, 61, 87, 103)

Review points (T3 — the risk-gate run on this tip says PR #1931 tier T3; path core/src/host/delivery.js):

  • Red first, and each site on its own: with all three changed sources back at the base blobs the five suites are 100/106, and the six failures are exactly the six new #P282b cases (busy-ack 1, fence/cleanName 1, flybot 2, group-work 1, task-handoff 1). Isolating one file at a time gives 1 / 1 / 4 red — busy-ack.js (not ok 15), delivery.js (not ok 22), tools.js (not ok 60, 61, 87, 103) — so four tools.js sites each have their own pin, not one shared path. Restored tip: 106/106.
  • No cap moves: every bound keeps its number (600 / 1,500 / 60 / 100 / 120 / 300 / 200) and only the unit a prefix stops on changes, through the shared core/src/shared/text-boundary.js cut (codePointEnd moves back one unit when max falls between a surrogate pair). tools.js:653's .slice(0, 6) is an array count and is untouched; busy-ack.js:59,63, delivery.js:38 and tools.js:89,177,743,1031 are the only text bounds in the diff.
  • Tests pin behaviour, not the implementation: each plants an emoji across its bound and asserts isWellFormed(), length <= max, and a UTF-8 byte prefix — never "exactly max"; the group notice and job line cases read the listing back, the answer echo reads the room line a member receives, and the fence case reads the from="…" header.
  • Range: compare 42e62656...d8a42ffc is ahead 1 / behind 0, 19 files — the three sources, five tests, two docs, the changelog fragment, three patches/ds-bot hunks and five whole-file mirrors; the patches/ds-bot/files/* mirrors are byte-identical to their core/ counterparts where they exist (busy-ack.js, the four tests); delivery.js / tools.js have no mirror because their changes live in the 0002 / 0005 hunks. Issue host: cut busy-ack, tool lines and the untrusted fence name on code point boundaries #1930 lists the same seven sites, so the diff matches its scope.
  • Checks on this tip: 27 runs, the newest instance of every name is success except enqueue-guard (failure, expected before a PASS exists). No other failure.
  • Body metadata is compliant (Owner: Dev, Reviewer: Reviewer, claim matches; checkpoint block 1,259 chars, summary 504, 11 files all in this diff, checks.result=green). Added the missing agent-pr label.

Not verified here: the repo-wide npm run verify and the smoke test — no process-level change; CI's tests-ok, plugins (social/tree/timeline/workflow), package and ubuntu-latest · Node 24 are green on this tip.

Note: the PR base is 42e62656, while main is now 77c9bc66dd (#1929 merged 12:14:49Z); base is a strict ancestor (behind 0) and mergeable_state=clean, so no rebase is needed.

@Birfy Birfy added the agent-pr Pull request opened by an agent label Oct 31, 2026
@github-actions github-actions Bot added the pass label Oct 31, 2026
@HugoXK
HugoXK force-pushed the dev/1-p282b-host-cuts branch 2 times, most recently from 7f1974a to d8a42ff Compare October 31, 2026 12:25
@Birfy
Birfy added this pull request to the merge queue Oct 31, 2026
Merged via the queue into ThinkFlowLab:main with commit de22907 Oct 31, 2026
34 of 40 checks passed
@Birfy
Birfy deleted the dev/1-p282b-host-cuts branch October 31, 2026 12:32
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

host: cut busy-ack, tool lines and the untrusted fence name on code point boundaries

2 participants