fix(workshop): keep approval target names UTF-16 safe#102963
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 9, 2026, 1:20 PM ET / 17:20 UTC. Summary PR surface: Source +1, Tests +23, Docs 0. Total +24 across 3 files. Reproducibility: yes. Current main still raw-slices the computed target-name prefix, and the PR includes an old-slice RED control showing the boundary failure as a replacement character. Review metrics: none identified. Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the focused formatter, regression-test, and docs patch after ordinary current-head merge and repository ancestry gates pass, keeping truncation centralized in Skill Workshop approval formatting. Do we have a high-confidence way to reproduce the issue? Yes. Current main still raw-slices the computed target-name prefix, and the PR includes an old-slice RED control showing the boundary failure as a replacement character. Is this the best way to solve the issue? Yes. The patch fixes the canonical approval-description formatter with the existing shared UTF-16 helper; downstream UI or Gateway sanitization would duplicate the policy and miss this source boundary. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 921b6c1e2d79. Label changesLabel justifications:
Evidence reviewedPR surface: Source +1, Tests +23, Docs 0. Total +24 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (4 earlier review cycles)
|
bb4a4c7 to
2fec48e
Compare
|
Land-ready evidence for frozen head
The code/proof is ready. Native landing remains intentionally held only on the repository PR-wrapper ancestry gate in #98765. @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Merged via squash.
|
* fix(workshop): keep approval target names UTF-16 safe * test(workshop): tighten UTF-16 approval regression * test(workshop): mirror approval budget exactly * docs(plugins): correct approval description limit --------- Co-authored-by: Peter Steinberger <[email protected]>
What Problem This Solves
Skill Workshop lifecycle approvals bound the
Target skill:line to the 512-UTF-16-code-unit approval-description limit with raw JavaScriptslice. If an astral character crossed that exact cut, the prompt contained a lone surrogate and could render�instead of a valid target name.Why This Change Was Made
buildLifecycleApprovalDescriptionis the source formatter for apply, reject, and quarantine approval payloads. This change replaces only its raw target-name slice with the existing sharedtruncateUtf16Safehelper from@openclaw/normalization-core/utf16-slice.That is the best owner boundary: proposal lookup, lifecycle policy, persistence, gateway limits, and renderers remain unchanged, while every affected approval payload becomes valid before it leaves the formatter. Downstream UI or gateway sanitization would be later, duplicated, and incomplete.
The regression exercises the real
proposeCreateSkill→ persisted proposal →resolveSkillWorkshopToolApprovalpath. It extends the existing metadata-bound test instead of adding a near-duplicate scenario, preserves every required approval fact, checks the shared 512-unit constant, and rejects lone high or low surrogates.The docs correction changes the generic plugin approval description limit from the stale 256 value to the runtime and gateway-protocol value of 512.
User Impact
Operators see a clean ellipsis-truncated target name for long Unicode skill names. Short names, approval policy, allowed decisions, timeout behavior, proposal lookup/state, and apply/reject/quarantine behavior do not change.
Evidence
Frozen reviewed head:
3e7224efcbcfb8f23f16e4ba44ee10c4f78b8c49.Untrusted contributor code was exercised only in sanitized AWS Crabbox leases with public networking, no Tailscale, no instance role, isolated
HOME, pinned Node/pnpm,--fresh-pr, and--no-hydrate.cbx_94657e95da1f: created persisted boundary and short-control proposals, resolved the exported approval payload, and checked the behavior contract without Vitest. Boundary output was at most 512 UTF-16 code units, kept the complete prefix plus one ellipsis, contained every approval fact, and had no unpaired surrogate; the short control remained unchanged.pnpm test src/skills/workshop/policy.test.ts— 1 file, 4/4 tests passed.slicemade exactly the boundary regression fail; the received target line contained�before the ellipsis.pnpm test src/skills/workshop— 2 files, 40/40 tests passed.node scripts/check-docs-mdx.mjs docs/plugins/plugin-permission-requests.md: passed.git diff --check: clean.No external service participates in this formatter path. Browser UI automation was not used; the live proof observes the exported approval payload consumed by the approval runtime/UI.
Root Cause and Provenance
The raw bounded slice was introduced in #100498 (
f53103de72da09185b27a58618ece136a5667c18). JavaScript string indices are UTF-16 code units, so the cut could land between a valid high/low surrogate pair. This PR follows the same shared-helper fix class as #102816 but covers the distinct Skill Workshop approval formatter. A live related-PR search found no same-point open fix.Risk / Compatibility
Risk is low and display-only. At a pair-splitting boundary, the visible prefix can be one code unit shorter so the astral character is omitted whole before the existing ellipsis. There are no config, schema, storage, protocol, dependency, provider, security-boundary, or operator-workflow changes.
Landed through the repository native wrapper as
b451d8b5e59539c21009947c23e203df7d438943after exact-head Testbox preparation and merge verification.