[PR #4595] [CLOSED] fix(electron): complete wrapper removal through mutated raw-listener metadata #6328

Closed
opened 2026-10-31 01:13:36 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4595
Author: @jayinvers
Created: 10/16/2026
Status: ❌ Closed

Base: main ← Head: fix/electron-raw-listener-removal


📝 Commits (6)

  • bd94ca4 fix(electron): complete wrapper removal through mutated raw-listener metadata
  • ddb50a4 fix(electron): scope wrapper completion to its removal slot
  • 3f843e4 test(ci): fit the shared cli-boundary worker heap to current main
  • 852bcb4 fix(electron): make wrapper completion shift-proof and cheap
  • 5f569de fix(electron): observe removal forwarding instead of inferring it
  • 34fa335 fix(electron): scope removal flights and treat unknown getters conservatively

📊 Changes

4 files changed (+1509 additions, -6 deletions)

View changed files

📝 docs/electron-observation.md (+57 -0)
📝 scripts/electron-active-hook.cjs (+200 -4)
📝 tests/boundary/browser/electronActiveHook.test.ts (+1247 -0)
📝 vitest.config.ts (+5 -2)

📄 Description

Summary

Fixes #4513. An application that mutates the writable .listener metadata of an observed ipcMain entry (or installs a removal decorator) can still leave one listener registered after removeAllListeners: the merged #4389 traversal translates a wrapper to the caller's original function only while its public .listener data link still carries that function, so mutated metadata defeats the translation and the decorator receives and rejects the wrapper. This PR is rebased onto that merged architecture and completes removal through it.

Problem and expected behavior

Smallest trigger (the issue's probe): register a listener through the hook, mutate rawListeners("ch")[0].listener to a foreign function, decorate removeListener to reject anything but the original, then removeAllListeners("ch").

  • On current main (post-#4389): the decorator observes the wrapper, throws unexpected callback, and one listener remains. Executed: both new regression tests below fail on main's hook files (2 of 75).
  • Expected (#4513): removal completes; the application's removal decorator receives the original function; typed failures and plain EventEmitter parity (including for throwing accessor metadata) are preserved.

Change and scope

scripts/electron-active-hook.cjs, inside the merged traversal architecture:

  • The hook-maintained registry is authoritative for data-property metadata, whatever value mutation left on it: translation hands the original function to the application's current removal method.
  • When mutated metadata defeats the application's own native match, removal is completed afterwards by wrapper identity through the native removeListener captured at patch time. Public metadata is never temporarily restored or rewritten.
  • Accessor metadata stays untranslated, so the application's own native removal owns getter execution — preserving the plain-EventEmitter parity anchored by main's "preserves native removal before mutated raw listener metadata throws" test.

docs/electron-observation.md documents the policy and its visible limits (the recording wrapper remains visible to rawListeners()). Tests in tests/boundary/browser/electronActiveHook.test.ts.

Follow the contribution scope.
Not applicable: policy refinement inside the existing observation hook; no new capability or target interaction.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Electron active hook listener-removal policy (scripts/electron-active-hook.cjs); earliest stage is runtime observation behavior.
  • Contribution scope fit and linked maintainer decision: not applicable (ordinary fix within the existing hook boundary).
  • CLI and MCP/tool-catalog contract: none; the hook's recorded output shapes are unchanged.
  • Provider, bridge, target-format, or platform compatibility: none (hook script only).
  • Evidence, artifact, provenance, or reconstruction contract: none.
  • Target selection, authority, upstream behavior, and cleanup details (if applicable): not applicable; the hook's existing authority is unchanged.
  • Generated metadata, package, or installation impact: none.

Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added: tests/boundary/browser/electronActiveHook.test.ts
    • "removes observed ipcMain listeners by their original function through mutated raw-listener metadata" — the issue scenario verbatim against a plain EventEmitter control.
    • "completes observed ipcMain listener removal across decorators and metadata states" — an 8-scenario parity matrix (record-then-direct-removal, decorator + unmutated metadata, meta-listener + mutated data link, multi-listener LIFO, accessor metadata under main's untranslated contract, decorator-throws-then-retry, no-argument removeAllListeners, no-observer fast path).
  • Red/green (executed): checking out main's hook files over this branch makes exactly those two tests fail (seen: [false], remaining: 1, failure: "unexpected callback"); the merged hook here passes all 75 tests in the file, including every test anchored by #4389.
  • Executed reproduction (issue probe, Node 24.18.0, repo's stubbed electron): before → hooked: {seen: [false], remaining: 1, failure: "unexpected callback"} vs plain control {seen: [true], remaining: 0, failure: null}; after → hooked equals plain.
  • Remaining proof gaps: no real Electron application was run locally; validation is at the hook boundary (stubbed Electron) plus CI lanes. Real-Electron reproduction remains with the reporter's environment.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

Rebased onto main 61e68807 (includes #4389) as a single commit ab50a37f.

  • npx vitest run tests/boundary/browser/electronActiveHook.test.ts — 75 passed (75).
  • npm run check:fast — pass (typecheck + lint).
  • npx oxfmt --check on the three changed files — formatted.
  • npm run verify:test-discovery — "Discovered all 1442 test files exactly once."
  • npm run docs:check — pass.
  • Fork CI (jayinvers/rea, branch fix/electron-raw-listener-removal, same head commit):

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Removal behavior only becomes stricter about completing; recorded shapes are unchanged.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable; local validation was Windows with stubbed Electron, CI covers the Linux/macOS lanes.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: same hook authority as before; no new target interaction.

Review follow-up (2026-10-17)

Addressed both Greptile findings in commit ddb50a4e (rebased onto main a0405334):

  • P1 duplicate registrations: completion is now scoped to the removal slot observed before the translated call (lastIndexOf on the native raw list; a vacated slot stands down because native removal always removes the last match). One removal request removes exactly one entry; the traversal revisits the remaining duplicate as its own translated call. Parity tests cover a rejecting decorator (plain: seen [true,true], remaining 1, failure preserved) and a forwarding decorator (remaining 0).
  • P1 restored wrappers: patchedIpcMainOn marks the in-flight removal flight when the exact entry is re-registered during the translated call, and completion refuses to run for a marked wrapper — an application-restored listener survives exactly as in plain EventEmitter (one notification, remaining 1).

Commit 3f843e4a cherry-picks the sibling #4585 CI heap budget bump (384->512 MiB) so this branch's full CI can pass on current main; it elides automatically when #4585 merges.

New head after round 1: 3f843e4a. Fork CI: https://github.com/jayinvers/rea/actions/runs/39020750573 — success (79/79 hook tests in shard 2/6).

Review follow-up round 2 (2026-10-17)

Addressed the second Greptile round in commit 852bcb41:

  • P1 decorator-kept listeners: verified empirically what plain Node does — without a removeListener meta-listener, plain removeAllListeners bypasses the decorator (removes); with one, traversal dispatches through this.removeListener per entry and a swallowing decorator KEEPS the registration. Completion now also requires the wrapper data .listener to still defeat a native match for the translated target, so survival with matching metadata (a decorator that chose not to remove) is kept exactly like plain; the documented residual corner is a swallow with already-mutated metadata, which is state-identical to a forwarded native miss and completes like one.
  • P1 shift/cross-channel fragility: the pinned slot is gone — completion re-finds the wrapper by identity (lastIndexOf on the live list, prepend-proof), and the in-flight re-registration mark is channel-scoped, so registering a saved wrapper on another channel no longer blocks the original channel's completion while same-channel restores still survive.
  • P2 quadratic cleanup: completion is gated by an O(1) native listenerCount comparison (captured before, compared after). The ordinary path (native match succeeded) performs zero listener-list work; the rescan runs only on a miss. Instrumented test: 64 successful decorated removals -> 0 scans; one metadata miss -> exactly 1 scan.

New head: 852bcb41. Fork CI: https://github.com/jayinvers/rea/actions/runs/39022871896 — success (83/83 hook tests).

Review follow-up round 3 (2026-10-17)

The third round showed that inferring "did the application forward the removal" from end state (a listener-count gate, a post-call metadata recheck) is unsound by construction. Commit 5f569de0 replaces inference with observation:

  • ipcMain.removeListener is now patched at hook load like the other four methods — a pure pass-through for every direct call (unchanged arguments; one length check when no removal flight is active). Its only side effect: when a translated removal is in flight and the listener argument equals that flight's translated target, the flight is marked forwarded.
  • Completion fires iff forwarded && !satisfiedByNative && !reregistered && the wrapper is still registered by identity (prepend-proof re-find). satisfiedByNative is predicted before the native call inside the patched method by scanning from the end — native removal always removes its last match — for an entry equal to the target or whose data .listener (descriptor walk, getters never executed) names it; a predictable match proves the forwarded call removes one registration, so the request stands down (one removal per request for duplicates).
  • The count gate and the post-call metadata recheck are deleted. Direct-removal semantics remain byte-identical to plain EventEmitter; the throwing-getter parity and accessor-untranslated contracts are unchanged; trust boundary documented (pre-hook captures bypass the marker, same model as the other patched methods).

New head: 5f569de0. Fork CI: https://github.com/jayinvers/rea/actions/runs/39024920449 — success (86/86 hook tests).

Review follow-up round 4 (2026-10-18)

Addressed in commit 34fa3354:

  • P1 cross-emitter marking: removal flights now record their emitter, and the patched removeListener marks a flight only when called with the same receiver, so a decorator applying the captured patched method to another EventEmitter no longer marks an unrelated removal forwarded.
  • P1 unknown-getter double removal: in the satisfiedByNative end-first scan, an accessor (or opaque-chain) entry reached before a proven match is not a proven miss — native executes that getter inside the application's own forwarded call — so completion stands down and keeps the registration rather than double-removing.
  • P1 forwarding through off: verified empirically on Node 24.18.0 that EventEmitter.prototype.off === EventEmitter.prototype.removeListener and this.off(...) does not dispatch through the instance's own property; ipcMain.off now carries the identical pass-through and marker (direct off semantics byte-identical). Explicit prototype dispatch (EventEmitter.prototype.removeListener.call(...)) and pre-hook captures bypass instance-property observation by construction; patching the shared prototype would affect every emitter in the process, which this observation hook must not do — those paths defer to plain native behavior, documented in code and docs/electron-observation.md.

New head: 34fa3354. Fork CI: https://github.com/jayinvers/rea/actions/runs/39026784079 — success (89 hook tests).

Author readiness

Human owner: @jayinvers

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. (No scope decision required: fix within the existing hook.)
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output. (No user-visible output change; hook policy is documented in docs/electron-observation.md.)
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/4595 **Author:** [@jayinvers](https://github.com/jayinvers) **Created:** 10/16/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/electron-raw-listener-removal` --- ### 📝 Commits (6) - [`bd94ca4`](https://github.com/morluto/rea/commit/bd94ca417e1b6e15a5d80dd14e366dd2e71c3d45) fix(electron): complete wrapper removal through mutated raw-listener metadata - [`ddb50a4`](https://github.com/morluto/rea/commit/ddb50a4e89e745ef22a4bb481a86cb773d049d78) fix(electron): scope wrapper completion to its removal slot - [`3f843e4`](https://github.com/morluto/rea/commit/3f843e4a52aa8c510b161838dc6a147aefee6236) test(ci): fit the shared cli-boundary worker heap to current main - [`852bcb4`](https://github.com/morluto/rea/commit/852bcb415cde5097705ba49a48b296bd06666891) fix(electron): make wrapper completion shift-proof and cheap - [`5f569de`](https://github.com/morluto/rea/commit/5f569de06fb6674dffaeec622e0cdcbc1ef4f523) fix(electron): observe removal forwarding instead of inferring it - [`34fa335`](https://github.com/morluto/rea/commit/34fa3354e71fcb8f0dd89d775df108ad0e648d1d) fix(electron): scope removal flights and treat unknown getters conservatively ### 📊 Changes **4 files changed** (+1509 additions, -6 deletions) <details> <summary>View changed files</summary> 📝 `docs/electron-observation.md` (+57 -0) 📝 `scripts/electron-active-hook.cjs` (+200 -4) 📝 `tests/boundary/browser/electronActiveHook.test.ts` (+1247 -0) 📝 `vitest.config.ts` (+5 -2) </details> ### 📄 Description <!-- PR title: type(optional-scope): imperative outcome The title becomes the squash-merge commit subject and generated changelog entry. --> ## Summary Fixes #4513. An application that mutates the writable `.listener` metadata of an observed `ipcMain` entry (or installs a removal decorator) can still leave one listener registered after `removeAllListeners`: the merged #4389 traversal translates a wrapper to the caller's original function only while its public `.listener` data link still carries that function, so mutated metadata defeats the translation and the decorator receives and rejects the wrapper. This PR is rebased onto that merged architecture and completes removal through it. ## Problem and expected behavior Smallest trigger (the issue's probe): register a listener through the hook, mutate `rawListeners("ch")[0].listener` to a foreign function, decorate `removeListener` to reject anything but the original, then `removeAllListeners("ch")`. - On current main (post-#4389): the decorator observes the wrapper, throws `unexpected callback`, and one listener remains. Executed: both new regression tests below fail on main's hook files (2 of 75). - Expected (#4513): removal completes; the application's removal decorator receives the original function; typed failures and plain EventEmitter parity (including for throwing accessor metadata) are preserved. ## Change and scope `scripts/electron-active-hook.cjs`, inside the merged traversal architecture: - The hook-maintained registry is authoritative for **data-property** metadata, whatever value mutation left on it: translation hands the original function to the application's current removal method. - When mutated metadata defeats the application's own native match, removal is completed afterwards **by wrapper identity** through the native `removeListener` captured at patch time. Public metadata is never temporarily restored or rewritten. - Accessor metadata stays untranslated, so the application's own native removal owns getter execution — preserving the plain-EventEmitter parity anchored by main's "preserves native removal before mutated raw listener metadata throws" test. `docs/electron-observation.md` documents the policy and its visible limits (the recording wrapper remains visible to `rawListeners()`). Tests in `tests/boundary/browser/electronActiveHook.test.ts`. Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope). Not applicable: policy refinement inside the existing observation hook; no new capability or target interaction. ## Contract and boundary impact - Semantic owner and earliest changed stage: Electron active hook listener-removal policy (`scripts/electron-active-hook.cjs`); earliest stage is runtime observation behavior. - Contribution scope fit and linked maintainer decision: not applicable (ordinary fix within the existing hook boundary). - CLI and MCP/tool-catalog contract: none; the hook's recorded output shapes are unchanged. - Provider, bridge, target-format, or platform compatibility: none (hook script only). - Evidence, artifact, provenance, or reconstruction contract: none. - Target selection, authority, upstream behavior, and cleanup details (if applicable): not applicable; the hook's existing authority is unchanged. - Generated metadata, package, or installation impact: none. Expected effects (check known effects; use documentation-only on its own). Unchecked items do not establish absence; leave uncertainties visible. - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added: `tests/boundary/browser/electronActiveHook.test.ts` - "removes observed ipcMain listeners by their original function through mutated raw-listener metadata" — the issue scenario verbatim against a plain EventEmitter control. - "completes observed ipcMain listener removal across decorators and metadata states" — an 8-scenario parity matrix (record-then-direct-removal, decorator + unmutated metadata, meta-listener + mutated data link, multi-listener LIFO, accessor metadata under main's untranslated contract, decorator-throws-then-retry, no-argument removeAllListeners, no-observer fast path). - Red/green (executed): checking out main's hook files over this branch makes exactly those two tests fail (`seen: [false], remaining: 1, failure: "unexpected callback"`); the merged hook here passes all 75 tests in the file, including every test anchored by #4389. - Executed reproduction (issue probe, Node 24.18.0, repo's stubbed `electron`): before → `hooked: {seen: [false], remaining: 1, failure: "unexpected callback"}` vs `plain` control `{seen: [true], remaining: 0, failure: null}`; after → hooked equals plain. - Remaining proof gaps: no real Electron application was run locally; validation is at the hook boundary (stubbed Electron) plus CI lanes. Real-Electron reproduction remains with the reporter's environment. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed Rebased onto main `61e68807` (includes #4389) as a single commit `ab50a37f`. - `npx vitest run tests/boundary/browser/electronActiveHook.test.ts` — 75 passed (75). - `npm run check:fast` — pass (typecheck + lint). - `npx oxfmt --check` on the three changed files — formatted. - `npm run verify:test-discovery` — "Discovered all 1442 test files exactly once." - `npm run docs:check` — pass. - Fork CI (jayinvers/rea, branch `fix/electron-raw-listener-removal`, same head commit): - https://github.com/jayinvers/rea/actions/runs/38964763532 — conclusion: success. ## Compatibility, safety, and release - Breaking changes or migration steps: none. Removal behavior only becomes stricter about completing; recorded shapes are unchanged. - Real Hopper/Ghidra, browser, or OS coverage: not applicable; local validation was Windows with stubbed Electron, CI covers the Linux/macOS lanes. - Package or release metadata impact: none. - Security, privacy, process, or containment review: same hook authority as before; no new target interaction. ## Review follow-up (2026-10-17) Addressed both Greptile findings in commit `ddb50a4e` (rebased onto main `a0405334`): - P1 duplicate registrations: completion is now scoped to the removal slot observed before the translated call (lastIndexOf on the native raw list; a vacated slot stands down because native removal always removes the last match). One removal request removes exactly one entry; the traversal revisits the remaining duplicate as its own translated call. Parity tests cover a rejecting decorator (plain: seen [true,true], remaining 1, failure preserved) and a forwarding decorator (remaining 0). - P1 restored wrappers: patchedIpcMainOn marks the in-flight removal flight when the exact entry is re-registered during the translated call, and completion refuses to run for a marked wrapper — an application-restored listener survives exactly as in plain EventEmitter (one notification, remaining 1). Commit `3f843e4a` cherry-picks the sibling #4585 CI heap budget bump (384->512 MiB) so this branch's full CI can pass on current main; it elides automatically when #4585 merges. New head after round 1: `3f843e4a`. Fork CI: https://github.com/jayinvers/rea/actions/runs/39020750573 — success (79/79 hook tests in shard 2/6). ## Review follow-up round 2 (2026-10-17) Addressed the second Greptile round in commit `852bcb41`: - P1 decorator-kept listeners: verified empirically what plain Node does — without a `removeListener` meta-listener, plain `removeAllListeners` bypasses the decorator (removes); with one, traversal dispatches through `this.removeListener` per entry and a swallowing decorator KEEPS the registration. Completion now also requires the wrapper data `.listener` to still defeat a native match for the translated target, so survival with matching metadata (a decorator that chose not to remove) is kept exactly like plain; the documented residual corner is a swallow with already-mutated metadata, which is state-identical to a forwarded native miss and completes like one. - P1 shift/cross-channel fragility: the pinned slot is gone — completion re-finds the wrapper by identity (`lastIndexOf` on the live list, prepend-proof), and the in-flight re-registration mark is channel-scoped, so registering a saved wrapper on another channel no longer blocks the original channel's completion while same-channel restores still survive. - P2 quadratic cleanup: completion is gated by an O(1) native `listenerCount` comparison (captured before, compared after). The ordinary path (native match succeeded) performs zero listener-list work; the rescan runs only on a miss. Instrumented test: 64 successful decorated removals -> 0 scans; one metadata miss -> exactly 1 scan. New head: `852bcb41`. Fork CI: https://github.com/jayinvers/rea/actions/runs/39022871896 — success (83/83 hook tests). ## Review follow-up round 3 (2026-10-17) The third round showed that inferring "did the application forward the removal" from end state (a listener-count gate, a post-call metadata recheck) is unsound by construction. Commit `5f569de0` replaces inference with observation: - `ipcMain.removeListener` is now patched at hook load like the other four methods — a pure pass-through for every direct call (unchanged arguments; one length check when no removal flight is active). Its only side effect: when a translated removal is in flight and the listener argument equals that flight's translated target, the flight is marked `forwarded`. - Completion fires iff `forwarded && !satisfiedByNative && !reregistered &&` the wrapper is still registered by identity (prepend-proof re-find). `satisfiedByNative` is predicted before the native call inside the patched method by scanning from the end — native removal always removes its last match — for an entry equal to the target or whose data `.listener` (descriptor walk, getters never executed) names it; a predictable match proves the forwarded call removes one registration, so the request stands down (one removal per request for duplicates). - The count gate and the post-call metadata recheck are deleted. Direct-removal semantics remain byte-identical to plain EventEmitter; the throwing-getter parity and accessor-untranslated contracts are unchanged; trust boundary documented (pre-hook captures bypass the marker, same model as the other patched methods). New head: `5f569de0`. Fork CI: https://github.com/jayinvers/rea/actions/runs/39024920449 — success (86/86 hook tests). ## Review follow-up round 4 (2026-10-18) Addressed in commit `34fa3354`: - P1 cross-emitter marking: removal flights now record their emitter, and the patched removeListener marks a flight only when called with the same receiver, so a decorator applying the captured patched method to another EventEmitter no longer marks an unrelated removal forwarded. - P1 unknown-getter double removal: in the satisfiedByNative end-first scan, an accessor (or opaque-chain) entry reached before a proven match is not a proven miss — native executes that getter inside the application's own forwarded call — so completion stands down and keeps the registration rather than double-removing. - P1 forwarding through off: verified empirically on Node 24.18.0 that `EventEmitter.prototype.off === EventEmitter.prototype.removeListener` and `this.off(...)` does not dispatch through the instance's own property; `ipcMain.off` now carries the identical pass-through and marker (direct `off` semantics byte-identical). Explicit prototype dispatch (`EventEmitter.prototype.removeListener.call(...)`) and pre-hook captures bypass instance-property observation by construction; patching the shared prototype would affect every emitter in the process, which this observation hook must not do — those paths defer to plain native behavior, documented in code and docs/electron-observation.md. New head: `34fa3354`. Fork CI: https://github.com/jayinvers/rea/actions/runs/39026784079 — success (89 hook tests). ## Author readiness Human owner: @jayinvers - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. (No scope decision required: fix within the existing hook.) - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. (No user-visible output change; hook policy is documented in docs/electron-observation.md.) - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:13:36 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#6328
No description provided.