Repository navigation
Description
id(web): terminalBytesForKey treats Object.prototype members as special keys — 'toString'/'constructor' send native-function source bytes as world terminal input instead of null
Evidence (verified this run, main 321dc21)
pkgs/id/web/src/world-keyboard.ts:20-35 — specialKeys is a plain object literal (Record<string, string>), and membership is tested with the in operator, which walks the prototype chain:
if (event.key in specialKeys) {
...
return new TextEncoder().encode(specialKeys[event.key]);
}The function's own contract (docstring, :23-25) is: "Returns null for browser/OS shortcuts and keys the Roc Key module does not recognize yet." Prototype-member names are not in the table, but in says they are, and the lookup returns the inherited Object.prototype member — a function or object — which TextEncoder.encode string-coerces.
Probe (verbatim copy of the file at this HEAD, md5-verified; laptop ~/git/fleet/probe-2026-10-30-laneA-keyboard/, bun run probe.ts):
| key | result |
|---|---|
Enter |
"\r" (correct) |
F5 |
null (correct) |
toString |
"function toString() { [native code] }" |
constructor |
"function Object() { [native code] }" |
hasOwnProperty |
"function hasOwnProperty() { [native code] }" |
valueOf |
"function valueOf() { [native code] }" |
__proto__ |
"[object Object]" |
isPrototypeOf |
"function isPrototypeOf() { [native code] }" |
Why it matters
The only production caller is pkgs/id/web/src/world.ts:258, which forwards the returned bytes as terminal input to the running world (Roc) program. Any key event whose key is a prototype-member name — synthetic/programmatic events, IME or virtual-keyboard oddities, future callers feeding non-DOM key sources into this exported pure function — injects the JS engine's native-function source text into the world program as if the user typed it, instead of being dropped as unrecognized. This is the same plain-object-lookup class fixed for detectLanguage in #2787 and for LANG_IMPORTS in highlight.ts:284-286 (own-key check with an explanatory comment); world-keyboard.ts was missed, and it uses the even broader in operator.
Acceptance criteria
terminalBytesForKeyreturnsnullfor everyObject.prototypemember name (toString,constructor,hasOwnProperty,valueOf,isPrototypeOf,__proto__, …) with no modifiers, matching the documented unrecognized-key behavior.- All twelve real special keys still encode exactly as before (existing
world-keyboard.test.tscases unchanged and green). - New tests in
pkgs/id/web/src/world-keyboard.test.tspin the prototype-name cases. - Fix uses an own-key check (
Object.hasOwn) or an equivalent prototype-free table (Object.create(null)/Map), consistent with the id(web): detectLanguage returns Object.prototype members as languages — plain-object lookups on EXT_TO_LANG/FILENAME_TO_LANG trust arbitrary filenames ('notes.constructor' returns the Object constructor, '__proto__' returns a prototype object) #2787 precedent in the same package.
Dedup: searched open + closed issues for world-keyboard, specialKeys, terminalBytesForKey this run — no hits. #2787 covers highlight.ts only. Sibling filed the same run for mimeToExtension in image-upload.ts (same class, different file/function/caller — independently fixable).
Activity
dezren39 commentedon Oct 31, 2026
🤖 Agent note · filed
terminalBytesForKeyinpkgs/id/web/src/world-keyboard.tsusesinon a plain object, so Object.prototype member names encode as native-function source bytes instead of returning null; reproduced with a verbatim-copy probe on the laptop at main 321dc21.dezren39 commentedon Oct 31, 2026
Cross-link / dedup correction from the filing run (full dedup results arrived after filing): #732 also covers
terminalBytesForKeybut is a different bug — modified special keys (Alt+Backspace, Alt+Arrow) being dropped/degraded; this issue is only the prototype-member lookup. #2792 is the open design issue for one own-key rule across dynamic lookups; this filing is a concrete verified instance inworld-keyboard.tsfor its tracker. Sibling instance: #2920.54 remaining items
dezren39 commentedon Nov 2, 2026
agent-note:v1 — holder: repo-worker-code (OpenChamber scheduled lane), at 2026-11-02T13:51:29Z. event: picked-up; task: 5472; queue: repo-health-code-pilot.
dezren39 commentedon Nov 2, 2026
agent-note:v1 — holder: repo-worker-code (OpenChamber scheduled lane), at 2026-11-02T13:51:33Z. event: parked; task: 5472; queue: repo-health-code-pilot. Canonical PR #4265 remains open and Drew-owned; hard stop. Resume after explicit handoff/ownership change or PR disposition, then revalidate fresh. PR contents, branch, worktree and source were not inspected or touched; no competing work.
dezren39 commentedon Nov 2, 2026
🤖 Agent note · picked up
dezren39 commentedon Nov 2, 2026
🤖 Agent note · done