You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
compat(ipc): ipcMain/ipcRenderer/contextBridge over host-routed el:<channel> frames, exact-literal per-window guard grants and a lifted renderer floor (draw.io send/on path first) #463
Generated by an AI agent (Claude Code) on behalf of @0monish during Wayfinder charting of the Electron compatibility program; rewritten 2026-10-07 by the doctrine-audit repair pass (change log at the end). Planning only — no implementation is authorized by this issue.
15 entities / 77 members of the Electron v44.4.5 API model: contextBridge (3), ipcMain (9), ipcRenderer (11), IpcMainEvent (8), IpcMainInvokeEvent (5), IpcRendererEvent (2), webUtils (1), MessageChannelMain (2), MessagePortMain (5), parentPort (2), utilityProcess (1), UtilityProcess (10), IpcMainServiceWorker (7), IpcMainServiceWorkerEvent (7), IpcMainServiceWorkerInvokeEvent (4). Two members of other families are consumed here as narrow subsets only: webContents.send (routing to one webview principal; the WebContents entity stays with F03) and WebFrameMain.url / WebFrameMain.origin as reached through event.senderFrame (the rest of WebFrameMain stays with F03).
Live today (FACT, Keld origin/main 8678c0d, read 2026-10-07): one macOS page-world window.keld.invoke (channel 1 only, 4096-byte payload, one pending invoke per WebView, host-minted correlation, isolated-world relay, KELD-WV-011 fail-closed); the renderer-bridge endpoint types live in keld-wv and are driven by a keld-core polling pump that admits one application call at a time; @keld/api has one echo channel handler; @keld/electron ships app lifecycle only (no ipcMain, no preload injection, no renderer shim); keld-guard's evaluate denies every non-AppProcess principal (KELD-GUARD006) before any grant lookup, the manifest loader ignores the top-level windows key, and DenyReason::ChannelForbidden (KELD-GUARD003) exists but is never returned; the Unix virtual-port registry (64-message queue, 4 KiB inline, one-shot transfer) has no renderer route.
Corpus demand (resolved call sites, corpus drawio-desktop@2edf9fb, zettlr@e6c7fd8; literal grep 2026-10-07)
draw.io (first proof): preload uses ipcRenderer.send / on / once and contextBridge.exposeInMainWorld twice (electron = {request, registerMsgListener, sendMessage, listenOnce}, all void-returning, page callbacks passed as arguments including one nested in an object; process = {type, versions}); main registers 27 ipcMain listeners on 25 distinct string literals (rendererReq multiplexer replying mainResp via event.reply, including an Error instance on the failure path), uses ipcMain.once and ipcMain.removeListener on the close path (isModified → isModified-result → saveAndClose / saveAndClose-result, removeDraft / draftRemoved), 9 webContents.send( sites, and checks event.senderFrame (null → reject, else url prefix match against the file:// code URL) before acting. 0 invoke, 0 handle, 0 sendSync. Open and save carry file contents through rendererReq / mainResp.
Zettlr (next milestone): 186 ipcRenderer.invoke( sites, 25 ipcMain.handle( sites by literal grep (another scan counted 26 — unpinned), 6 sendSync( sites behind synchronously-returning exposed functions, event.sender used as a WebContents handle, webUtils.getPathForFile, ipcRenderer.setMaxListeners(100), exposed functions that return functions.
Zero demand in all three corpus apps for MessageChannelMain, MessagePortMain, ipcRenderer.postMessage, utilityProcess, parentPort, IpcMainServiceWorker, exposeInIsolatedWorld, executeInMainWorld (refuter grep). Matrix token-scan counts (e.g. MessagePortMain.close 14/82) are token collisions, not demand.
Maturity ladder and pull order
first-proof (draw.io, macOS, explicit legacy profile): X01-T3/X01-T4 → F04-T1 conformance → F04-T6/F04-T7 specs → F04-T8 grants, F04-T9 navigation-generation mint, F04-T11 codec (sole codec owner; F03-T5 consumes it), F04-T2 send, F04-T10 on → F04-T12 payload bound → F04-T3 ipcMain facade → F04-T4 contextBridge/ipcRenderer facade (after F07-T1). Conditional on PANEL-P1 passing an arm: F04-T19 transport spec → F04-T18 blocking host CALL transport (consumed by F06-T7 for draw.io's close prompt).
L0 rows honest; L1 draw.io send/on/reply path on macOS; L2 pinned cells pass; L3 corpus workflow under keld dev with overhead only through registered X05 metric ids (no unregistered performance sentence, per PANEL-D11).
Linear owners consumed (live status, Linear, fetched 2026-10-07)
KEL-102 — In Progress, unassigned: owns the webview-principal channel-grant task unit and the navigation-generation contract (F04-A1 / F04-A4 decided). Consumed artifact: KEL-102/T3 terminal artifact comment cde25f5e (landed 66ccbbc), the live dispatch_privileged route.
KEL-142 — Done 2026-10-01: the landed renderer bridge seam (wv-link v1 envelope, document nonce, content-world isolation tests). No open Linear issue owns its revision.
KEL-80 — Backlog, unassigned. ASSUMPTION: intended owner of renderer floor v2. Its 2026-09-26 product-spine slice comment (54a36489) admits only bounded semantics for one renderer call; its 2026-08-20 owner comment says "do not pick up". No task-level artifact exists for floor v2.
KEL-74 — Done: evidence schema owner. KEL-237 — In Progress: lifecycle corpus; its non-goals exclude "compatibility-surface expansion", so it does NOT own F04 conformance entries.
KEL-136 — Done (generated app-link transport); KEL-98 — Done (echo-only codegen; scope amendment needed before it generates el control structs); KEL-58 — Done (MCP explain fails closed on channel); KEL-79 — Backlog (renderer origin / resource transport; consumed only through F05-T4 for the file:// origin draw.io's validateSender needs); KEL-127 — Todo (program parent; entry-reading packet X06-D1 governs when repo-landing facade work may start).
FACT (decided F04-A1, delegated): renderer→main authorization = exact-literal el:<channel> entries under windows.<w>.channels, KELD-GUARD003 on miss, evaluated only in dispatch_privileged; keld migrate refuses wildcards. Exact-content approval of the KEL-102 amendment is a separate owner step (F04-T7).
FACT (decided F04-A4): the existing per-WebView host navigation state is the single mint of the navigation generation; the guard webview principal is built from the generation stamped on the admitted request; rotation at cross-document commit, plus host-initiated load as earlier fail-closed invalidation, never on same-document navigation; u32 with checked increment. FACT (code): two counters exist today (bridge u64 rotating on commit-observed page-load start and on host navigate; guard principal u32 minted as 0).
FACT (decided F03-D4): the value codec is a host-opaque endpoint codec owned by @keld/electron, implemented once by F04-T11 (F03-T7 is a duplicate for F03 to supersede); v0 domain = JSON + undefined, Date, Error (name/message/stack), class instances flattened; Function/Promise/Symbol/WeakMap/WeakSet throw as Electron; Map/Set/RegExp/TypedArray/bulk refs throw typed KELD-COMPAT. Arch 02 §5's "postcard-encoded SCV" sentence must be amended in the F04 ipc spec PR (F04-T6 is the single amendment owner).
FACT (decided PANEL-D21, option A): the app preload and renderer compat runtime run in a named content world separate from the KEL-142 bridge world, with a world-scoped handler that accepts only compat envelopes (F07-T1 owns world placement and the isolation re-run).
FACT (design, research + refuters): carriage = one fixed generated kipc control channel per direction; Electron channel string, kind and codec bytes are payload fields; no runtime-minted channel ids. Arch 02 §2 "string names never travel per-call" must be amended explicitly (F04-T6).
FACT (pinned doc, ipc-main-event.md v44.4.5): senderFrame "May be null if accessed after the frame has either navigated or been destroyed"; reply "will send an IPC message to the renderer frame that sent the original message".
INFERENCE (source receipts by a refuter, not doc text): a reply after the frame is gone is dropped (sendToFrame returns false); a reply with unserializable args throws; replies survive same-document navigation because Electron keys on the frame host. Recorded in F04-T1 as uncited cells with expected status unknown (X01-T3 rule); compared with a recorded oracle in F04-T20 (next).
INFERENCE (no v44.4.5 doc sentence found): per-channel FIFO of ipcRenderer.send; unknown cell in F04-T1, BEHAVIOR_MATCH-capped oracle comparison in F04-T20.
FACT (pinned doc, ipc-renderer-event.md): IpcRendererEvent.sender is the IpcRenderer instance, ports the transferred MessagePorts.
FACT (decided PANEL-D8): sendSync stays a deadline-bounded blocking CALL in the contract and is SCAFFOLDED (typed throw, fix-it to invoke) until the F04-A5 experiment passes; draw.io has 0 sites. Main-side blocking: the showMessageBoxSync facade is F06-T7; the worker-owned blocking host CALL transport it needs is F04-T18 (spec F04-T19), both gated on PANEL-P1. FACT (park-probe receipt, not re-run): a parked Bun main thread stalls the link after 8 KiB.
UNKNOWN: which channels the populated draw.io webapp passes to sendMessage / registerMsgListener (webapp submodule empty in the clone; PANEL-P3 settles). UNKNOWN: draw.io open/save payload size distribution (PANEL-P3 / F04-T12).
Platform lanes
macOS WKWebView is the only lane with renderer bridge code and the only first-proof lane. WebKitGTK and WebView2 have no renderer bridge in-tree; their cells are recorded unknown with an explicit documented gap, never inferred from macOS (KEL-79: one engine's pass cannot stand in for another). Future WebView2 work: single world gives synchronous calls natively but isolation is ▲; preload injection must use the async completion form; chrome.webview.hostObjects.sync is the only documented synchronous page→host primitive among the three engines (refuter receipt, Microsoft docs fetched 2026-10-06).
contextBridge.exposeInIsolatedWorld — Chromium numeric world ids have no engine-neutral meaning; zero demand (documented-never row in F04-T5, pending the arch 04 §4 amendment)
contextBridge.executeInMainWorld — experimental; zero demand (F04-T5 row)
IpcMainServiceWorker.on/once/removeListener/removeAllListeners/handle/handleOnce/removeHandler — no Service Worker substrate on the app resource transport; zero demand (F04-T5 rows)
UtilityProcess 'login' event — network auth is the KEL-89 auth broker's; a role answering HTTP auth is ambient network authority (F04-T5 row)
utilityProcess.fork options as authority (env, cwd, execArgv, allowLoadingUnsignedLibraries, modulePath outside the declared bundle); PID as identity; raw child_process behind utilityProcess
Runtime-minted kipc channel ids for Electron string channels
Wildcard or prefix channel grants (el:*); renderer-side or facade-side channel allowlists in place of keld-guard
Exposing the whole ipcRenderer object over contextBridge
Silent async rewrite of sendSync
Codec decoding in keld-core or keld-ipc
Direct renderer-to-role transport for MessagePort transfer
process.env / process.argv copied into the main world under the strict profile (legacy-profile labelled cell only)
Out of scope
Implementation of MessageChannelMain.port1/port2 and MessagePortMain.postMessage/start/close/message/close — row only in F04-T5; facade owned by KEL-75 T5 after a pinned-oracle fixture
Implementation of parentPort.postMessage/message, utilityProcess.fork and UtilityProcess.postMessage/kill/spawn/error/exit/message/pid/stdout/stderr — row only in F04-T5; KEL-75 T4/T5
ipcRenderer.postMessage and IpcMainEvent.ports (MessagePort transfer) — row only in F04-T5
ipcRenderer.sendToHost (<webview> guest messaging; draw.io disables <webview>) — row only in F04-T5; F07 owns the webview tag
The dialog.showMessageBoxSync facade — F06-T7 (its blocking transport is F04-T18 in this unit)
WebContents members other than send, and WebFrameMain members other than url/origin — F03
Not yet specified (fog)
Bulk-reference lane and export payloads (draw.io ≥ 30-megapixel PNG base64 may exceed the 16 MiB app-link frame cap)
Codec support for Map, Set, RegExp, TypedArray/Buffer (typed KELD-COMPAT throw in v0)
WebKitGTK and WebView2 renderer bridge, worlds and preload injection (no in-tree code)
Subframe-originated IPC (window.keld is main-frame only); frame.ipc / webContents.ipc scoped IpcMain dispatch order
Real blocking sendSync mechanism (F04-A5 experiment)
keld migrate channel enumeration for computed channel names (Zettlr-class)
Whether audit logging of channel dispatch needs more than channel string and byte length
Decisions that govern this unit (closed decision tickets)
Rewrote design facts with FACT/INFERENCE/UNKNOWN labels: the event.reply drop and same-document survival are now INFERENCE from source receipts, send FIFO is INFERENCE with no doc sentence, and two generation counters exist today as FACT (critic ambiguity F04-EPIC; refuter invariants A4; my read of pinned v44.4.5 docs 2026-10-07)
Replaced the open questions with the closed resolutions of F04-A1, F04-A4, F03-D4, PANEL-D21 and PANEL-D8 (adopted decisions)
Owners now carry live Linear status fetched 2026-10-07. KEL-80 is an ASSUMPTION with its slice comment id, KEL-237 is excluded as conformance owner, and the missing-owner blocker has a resolution path (critic ambiguity KEL-80; X06-D3; X06-D6; program-wide predecessor finding)
Corrected the bridge endpoint owner: the types live in keld-wv and are driven by a keld-core pump (refuter invariants misattributed-owner finding)
Added pull order by milestone, with first-proof = draw.io send/on/reply/contextBridge and next = Zettlr invoke/sendSync/returns/webUtils (X06-D2 both lenses; critic yagni)
Scope now accounts for all 77 members, plus the webContents.send and WebFrameMain url/origin subsets (critic coverage_gaps; rule 9)
Corpus demand uses resolved literal-grep counts at pinned commits and states the 25/26 handle discrepancy (refuter low finding on unpinned counts)
Platform lanes state that only macOS has bridge code and record the WebView2 receipts as refuter claims (refuter low findings)
Maturity ladder L3 requires registered X05 metric ids, with no unregistered overhead claim (PANEL-D11; rule 5)
Pull order now starts at X01-T3/X01-T4, adds F04-T18/F04-T19 (conditional on PANEL-P1) to first-proof and F04-T20 to next (cross-checker finding 'first-proof slices gated on next-milestone X01-T1'; cross-checker finding 'blocking host CALL transport has zero owners')
Observed-only design facts point at F04-T1 unknown cells plus the F04-T20 oracle comparison, as X01-T3 requires (cross-checker finding 'first-proof slices gated on next-milestone X01-T1')
Codec fact names F04-T11 as the sole implementation owner and F04-T6 as the single arch 02 §5 amendment owner (cross-checker finding 'one atom, two owners' F03-T7/F04-T11)
Main-side blocking fact and out_of_scope line now name F04-T18/F04-T19 as the transport owner, citing the park-probe receipt (cross-checker finding 'blocking host CALL transport has zero owners')
Verified X05-T2 first-proof and X05-T1 non-gating against the repaired X05 unit (cross-checker finding 'X05 gates unverifiable')
Audit findings not applied (with the evidence-backed reason)
critic edge_problems X02-T4: add edges from F04-T3, F04-T4 and others; drop X01-T2 — X02 owns the scoring tickets, and X02-T6 is now the flipping ticket (not X02-T4). F04 side: the F04-T3/F04-T4 keys stay stable; X02-T6 must also add F04-T12 (save above 4096 bytes) and, conditional on PANEL-P1, F04-T18.
critic edge_problems X02-T3: edges ← F04-A1 and F09-A6; role-environment output — X02 owns X02-T3. F04-A1 is closed, so X02 should depend on F04-T7, which now carries the grant-source rule. The migrate criterion was removed from F04-T3.
critic edge_problems X05-T2: add as predecessor or drop 'step 0'; cut reserved family ranges — Partly applied: added F04-T3 ← X05-T2 for the generated control-channel constant only. Removing step 0 and family ranges is X05's edit; F04's design uses one fixed control channel per direction and needs no family range.
critic duplicates/edge_problems F07-T1: delete duplicated ACs, F07-T1 ← PANEL-D21 only, name the kel142 spec amendment owner — F07 owns F07-T1. F04 side applied: F04-T4 owns the round-trip criterion and has edge F04-T4 ← F07-T1. F04-T17 claims webUtils.getPathForFile.
critic duplicates F04-A1/X04-T1/MAP: delete the el:* fix claim from X04-T1 and MAP — Those texts belong to X04 and the MAP owner. F04 side applied: F04-T7 owns the fix.
critic ambiguity MAP census paragraph (draw.io , Zettlr 187 invoke) — The MAP owner must edit the MAP body. The F04 epic uses the resolved counts: 186 invoke by literal grep at zettlr@e6c7fd8 on 2026-10-07, and disabled.
critic quality disjunctive ACs and Zettlr ACs in F02-T5, F03-T2, F03-T3, F03-T4, F06-T3, F06-T4, F06-T6 — These are other units' tickets. The F04 instances (F04-T4 AC4, F04-T3 AC2) were applied.
critic quality: decision packets for F01-A4, F03-D4, X02-A1, F06-D1, X04-D4 — These belong to other units. F04-A1 and F04-A4 are now closed (adopted decisions).
critic hidden_coupling: AC in F05-T4 that the reported document URL equals the loaded file:// URL; F07-A6 constraint — F05 and F07 own those tickets. F04 side applied: edge F04-T3 ← F05-T4.
critic first_proof_frontier: F03-T2 ← F04-A4 and F03-T5 ← F03-D4 — F03 owns those edges. Since both decisions are closed, F03 should point at F04-T9 and F04-T11; this is noted in those tickets. The cross-checker also found F03-T7 duplicates F04-T11: F03 must supersede F03-T7 (replaced_by F04-T11) and move its executeJavaScript-envelope criterion into F03-T5.
X05-A4 recommendation rules 'one serialisation and one frame' and 'host-side fan-out' as conformance entries — These are implementation-shape rules justified by cost, and X05-A4 is still open. Making them acceptance criteria would add an unattributed performance criterion (rule 5). The correctness rules from the same list were applied: synchronous throw for non-cloneables in F04-T1/F04-T11, no reordering in F04-T2/F04-T10, and a typed error over the bound in F04-T12.
X06-D4 (both lenses): instruction-skill ticket shape — It concerns the agent-instruction skill ticket, not F04.
X06-D5 (both lenses): map structure and milestones — Map-level structure owned by the orchestrator. F04 side applied: milestone values and review_gates are set on every ticket so the gate labels can be derived.
X06-D1 invariants/semantics: KEL-127 entry-reading packet, KEL-140 state, compat main-role loss policy — These are program-level packets with no allowed F04 blocker key. The epic records that repo-landing facade work waits on the KEL-127 entry-reading packet. Item (d) was applied in F04-T3/F04-T4.
refuter invariants extra finding: add the 'no Electron-isms' invariant to the keld-core and keld-ipc crate AGENTS.md — This is an agent-instruction change and must go through .agents/instructions.md with a budget record, outside this map's planning scope. F04-T8 and F04-T6 assert it as a test and spec statement instead.
critic yagni X04-T1 milestone next — X04 owns X04-T1.
program-wide: remove KEL-209 and the KEL-89 secrets attribution — No F04 ticket cites KEL-209 or KEL-89 as secrets. The F04 never-list cites KEL-89 only as the auth broker, which matches its title. The task-level predecessor rule was applied to every F04 ticket.
cross-checker finding 'one atom, two owners' F03-T7/F04-T11: delete F03-T7, add F03-T5 ← F04-T11, move the executeJavaScript-envelope AC into F03-T5 — F03 owns F03-T7 and F03-T5, and this unit's supersede list may only close F04 keys. F04 side applied: F04-T11 is declared sole codec owner and absorbed F03-T7's extra criteria; F04-T6 is declared the single arch 02 §5 amendment owner; F04-T4 and F04-T11 no longer both flip the entry 7 Error-copy row.
cross-checker finding X06-D7 tracker-of-record contradiction: option (a) — correct adopted_decisions.json X06-D7 to option C — Wrong per evidence. Linear owner for repo-writing Electron-compat slices (owner decision: align to nearest Linear issue, else GitHub is the tracker of record) #517 is CLOSED (completed, 2026-10-06T20:32Z). Its owner-decision comment IC_kwDOSwkPJ88AAAABZxzzGA states 'This supersedes the earlier recommendation in the packet above (which rejected option D). Option D is adopted for unaligned work'. The body's option-C recommendation is superseded, so adopted_decisions.json is correct. Applied in F04: option (b)'s relabel (GitHub tracker of record, Linear reference link, conformance/spec tickets ready-for-agent).
cross-checker finding X06-D7 tracker-of-record contradiction: option (b) — add the workflow.md claim-protocol change as a named predecessor ticket — Not added as a gate. The same owner record calls the AGENTS.md/workflow.md amendment 'a separate, owner-authorized change' and says that until it lands 'this record and map Wayfinder map: KELD Electron Compatibility Program (Electron 44.x baseline) #391 are the documented exception that agents working this program must cite'. It is an agent-instruction change under .agents/instructions.md, so the map orchestrator should mint it once, not inside F04. The map-wide relabel of other units' needs-owner tickets is also the orchestrator's edit. Note: repaired X01-T3/X01-T4 still say 'claim on KEL-237 under Linear owner for repo-writing Electron-compat slices (owner decision: align to nearest Linear issue, else GitHub is the tracker of record) #517 option C', which the owner record supersedes.
cross-checker finding 'blocking host CALL transport has zero owners': add F04-T18 to blocked_by of F06-T7, F02-T11, F06-T14 and X02-T6 — Those tickets belong to F06, F02 and X02. The edges are named in the F04-T18 notes. X05-T3 was not used as the key because repaired X05 already uses X05-T3 (parked perf lanes).
cross-checker finding 'X02-T6 gate set incomplete': add F02-T4, F04-T12, F05-T7, F06-T7, F06-T8, X03-T4, X03-T7, X01-T4 to X02-T6 and rename 'X02-T4 consumes' notes — X02 owns X02-T6. The only F04 member of the set is F04-T12, and its notes name the edge. No F04 text says 'X02-T4 consumes'; the one F04 reference to X02-T4 (an unapplied reason) now points at X02-T6.
cross-checker finding 'X05 gates unverifiable': re-run the check with X05 included — Checked against repaired X05. X05-T2 is first-proof (ready_state needs-triage, no live Linear owner) and gates only F04-T3 in this unit. X05-T1 is parked and blocks no F04 ticket; F04-T2 out_of_scope cites it only as a non-gating owner of performance numbers. No F04 edge change is needed. F04-T6 item (b) now respects X05-T2's 'no Electron name in the table' criterion.
Generated by an AI agent (Claude Code, Fable 5.1) on behalf of @0monish, 2026-10-06, under the map's execution doctrine. Statements are labelled FACT / INFERENCE / UNKNOWN by their author; nothing here authorizes implementation.
changed the title [-]compat(ipc): ipcMain/ipcRenderer/contextBridge over host-routed el:<channel> frames, enumerated guard grants and a lifted renderer floor[/-][+]compat(ipc): ipcMain/ipcRenderer/contextBridge over host-routed el:<channel> frames, exact-literal per-window guard grants and a lifted renderer floor (draw.io send/on path first)[/+]on Oct 6, 2026
Parent map: #391 · Unit: F04
docs/agents/workflow.md§ Tracker issue (rule lands with docs(agents): record the #517 tracker-of-record rule in root AGENTS.md and the workflow claim protocol #520). Linear reference: KEL-80 (Backlog) carries a link to this issue.Scope
15 entities / 77 members of the Electron v44.4.5 API model:
contextBridge(3),ipcMain(9),ipcRenderer(11),IpcMainEvent(8),IpcMainInvokeEvent(5),IpcRendererEvent(2),webUtils(1),MessageChannelMain(2),MessagePortMain(5),parentPort(2),utilityProcess(1),UtilityProcess(10),IpcMainServiceWorker(7),IpcMainServiceWorkerEvent(7),IpcMainServiceWorkerInvokeEvent(4). Two members of other families are consumed here as narrow subsets only:webContents.send(routing to one webview principal; the WebContents entity stays with F03) andWebFrameMain.url/WebFrameMain.originas reached throughevent.senderFrame(the rest of WebFrameMain stays with F03).Live today (FACT, Keld origin/main 8678c0d, read 2026-10-07): one macOS page-world
window.keld.invoke(channel 1 only, 4096-byte payload, one pending invoke per WebView, host-minted correlation, isolated-world relay, KELD-WV-011 fail-closed); the renderer-bridge endpoint types live in keld-wv and are driven by a keld-core polling pump that admits one application call at a time;@keld/apihas one echo channel handler;@keld/electronships app lifecycle only (no ipcMain, no preload injection, no renderer shim); keld-guard'sevaluatedenies every non-AppProcess principal (KELD-GUARD006) before any grant lookup, the manifest loader ignores the top-levelwindowskey, andDenyReason::ChannelForbidden(KELD-GUARD003) exists but is never returned; the Unix virtual-port registry (64-message queue, 4 KiB inline, one-shot transfer) has no renderer route.Corpus demand (resolved call sites, corpus drawio-desktop@2edf9fb, zettlr@e6c7fd8; literal grep 2026-10-07)
ipcRenderer.send/on/onceandcontextBridge.exposeInMainWorldtwice (electron= {request, registerMsgListener, sendMessage, listenOnce}, all void-returning, page callbacks passed as arguments including one nested in an object;process= {type, versions}); main registers 27ipcMainlisteners on 25 distinct string literals (rendererReqmultiplexer replyingmainRespviaevent.reply, including an Error instance on the failure path), usesipcMain.onceandipcMain.removeListeneron the close path (isModified → isModified-result → saveAndClose / saveAndClose-result, removeDraft / draftRemoved), 9webContents.send(sites, and checksevent.senderFrame(null → reject, elseurlprefix match against the file:// code URL) before acting. 0 invoke, 0 handle, 0 sendSync. Open and save carry file contents throughrendererReq/mainResp.ipcRenderer.invoke(sites, 25ipcMain.handle(sites by literal grep (another scan counted 26 — unpinned), 6sendSync(sites behind synchronously-returning exposed functions,event.senderused as a WebContents handle,webUtils.getPathForFile,ipcRenderer.setMaxListeners(100), exposed functions that return functions.MessagePortMain.close14/82) are token collisions, not demand.Maturity ladder and pull order
keld devwith overhead only through registered X05 metric ids (no unregistered performance sentence, per PANEL-D11).Linear owners consumed (live status, Linear, fetched 2026-10-07)
dispatch_privilegedroute.channel); KEL-79 — Backlog (renderer origin / resource transport; consumed only through F05-T4 for the file:// origin draw.io'svalidateSenderneeds); KEL-127 — Todo (program parent; entry-reading packet X06-D1 governs when repo-landing facade work may start)..agents/instructions.mdamendment lands; agents cite the Linear owner for repo-writing Electron-compat slices (owner decision: align to nearest Linear issue, else GitHub is the tracker of record) #517 record as the documented exception. KEL-80 (Backlog, unassigned) and KEL-97 (Backlog, unassigned) re-fetched 2026-10-07.Design facts (labelled)
el:<channel>entries underwindows.<w>.channels, KELD-GUARD003 on miss, evaluated only indispatch_privileged;keld migraterefuses wildcards. Exact-content approval of the KEL-102 amendment is a separate owner step (F04-T7).@keld/electron, implemented once by F04-T11 (F03-T7 is a duplicate for F03 to supersede); v0 domain = JSON + undefined, Date, Error (name/message/stack), class instances flattened; Function/Promise/Symbol/WeakMap/WeakSet throw as Electron; Map/Set/RegExp/TypedArray/bulk refs throw typed KELD-COMPAT. Arch 02 §5's "postcard-encoded SCV" sentence must be amended in the F04 ipc spec PR (F04-T6 is the single amendment owner).senderFrame"May be null if accessed after the frame has either navigated or been destroyed";reply"will send an IPC message to the renderer frame that sent the original message".sendToFramereturns false); a reply with unserializable args throws; replies survive same-document navigation because Electron keys on the frame host. Recorded in F04-T1 as uncited cells with expected status unknown (X01-T3 rule); compared with a recorded oracle in F04-T20 (next).ipcRenderer.send; unknown cell in F04-T1, BEHAVIOR_MATCH-capped oracle comparison in F04-T20.IpcRendererEvent.senderis the IpcRenderer instance,portsthe transferred MessagePorts.sendMessage/registerMsgListener(webapp submodule empty in the clone; PANEL-P3 settles). UNKNOWN: draw.io open/save payload size distribution (PANEL-P3 / F04-T12).Platform lanes
macOS WKWebView is the only lane with renderer bridge code and the only first-proof lane. WebKitGTK and WebView2 have no renderer bridge in-tree; their cells are recorded
unknownwith an explicit documented gap, never inferred from macOS (KEL-79: one engine's pass cannot stand in for another). Future WebView2 work: single world gives synchronous calls natively but isolation is ▲; preload injection must use the async completion form;chrome.webview.hostObjects.syncis the only documented synchronous page→host primitive among the three engines (refuter receipt, Microsoft docs fetched 2026-10-06).Tracer-bullet tickets (sub-issues)
F04-T1F04-T2F04-T3F04-T4F04-T5F04-T6F04-T7F04-T8F04-T9F04-T10F04-T11F04-T12F04-T13F04-T14F04-T15F04-T16F04-T17F04-T18F04-T19F04-T20Never list (documented ✘)
el:*); renderer-side or facade-side channel allowlists in place of keld-guardOut of scope
<webview>guest messaging; draw.io disables<webview>) — row only in F04-T5; F07 owns the webview tagNot yet specified (fog)
Decisions that govern this unit (closed decision tickets)
F04-A4— Option A, recorded in the same KEL-102 amendment as F04-A1. Evidence determines both halves: the approved specs already name the host navigation state as the only minter, the code already has exactly one rotating counter, and commit is bothwindows.<w>.channelsgrant vocabulary and its evaluator (no live evaluator exists; KEL-102 and KEL-80 do not own it)? #398F04-A1— Option A: amend the KEL-102 spec with one scoped task unit for per-window channel grants; narrowest grammar = exact-literal el: entries, KELD-GUARD003 on miss, keld migrate refuses wildcards; fold the F04-A4 generation contract intChange log (doctrine-audit repair, 2026-10-07)
Audit findings not applied (with the evidence-backed reason)