[PR #5810] [MERGED] fix(javascript): count one IPC observation per site-resolved channel #7075

Closed
opened 2026-11-01 21:45:12 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/5810
Author: @kaoru0822-kitauji
Created: 10/21/2026
Status: ✅ Merged
Merged: 10/21/2026
Merged by: @kaoru0822-kitauji

Base: main ← Head: fix/js-ipc-const-channel-duplicate


📝 Commits (10+)

  • 59dda3c fix(javascript): count one IPC observation per site-resolved channel
  • 5430e98 fix(javascript): keep wrapper invocations distinct while removing repeats
  • 7d145b4 fix(javascript): compare site-determined IPC handlers by value
  • 2ad374a fix(javascript): keep closures over invocation values as distinct IPC handlers
  • 42f4cd6 Merge remote-tracking branch 'origin/main' into fix/js-ipc-const-channel-duplicate
  • d27d1e4 test(javascript): guard optional call paths in closure handler test
  • 8f88b60 fix(javascript): decide IPC handler identity from activation captures
  • 784eb39 fix(javascript): treat implicit captures and bundle modules precisely in IPC handler identity
  • f37215c fix(javascript): attribute an arrow handler's inherited context to its owner
  • 6e6c48b fix(javascript): skip suffix scans for per-invocation IPC handlers

📊 Changes

5 files changed (+735 additions, -13 deletions)

View changed files

📝 docs/javascript-artifact-reconstruction.md (+9 -1)
📝 src/domain/javascript/electronWrappedCallPaths.test.ts (+318 -0)
📝 src/domain/javascript/electronWrappedCallPaths.ts (+325 -12)
📝 src/domain/javascript/javascriptSymbolicBindings.ts (+7 -0)
➕ tests/acceptance/applications/electronIpcConstantChannelCli.test.ts (+76 -0)

📄 Description

What broke: analyze-javascript-application counted a send like ipcRenderer.send(PING), with const PING = "ping", as two renderer operations, and a wrapper call reached through a bridge member plus its own entry as two invocations (#5710).
Fix: wrapper substitution now resolves the syntax pass's unresolved observation in place when the call site itself determines the finding (channel, and for registrations the handler). A finding repeated through the same trailing local calls is recorded once.
Risk: low to medium. IPC counts drop only where one physical operation was recorded twice. A channel supplied through a wrapper argument stays an unresolved observation beside its resolved candidates, so unknowns are preserved.

Summary

Fixes #5710. One physical IPC operation could be recorded two or three times:

  • Site-determined constant channels. The syntax pass recorded send(PING) as dynamic (channel_expression: "PING"), and wrapper substitution added a separate literal "ping" record. Deduplication is keyed on the channel value, so the two never matched. The issue's fixture reported 3 operations and 2 transmissions instead of 2 and 1.
  • Nested entries. A wrapper call such as forward("forward-chan") is an entry seed (string-literal first argument) and is also reached through the enclosing exposeInMainWorld or local call. The finding's invocation identity was the walk's outermost seed, so the same call looked like two invocations. This duplicated literal sends and listener registrations (coygeek's helper-forwarding report: 4/3 and 4/0/3).

Problem and expected behavior

Expected, from the issue and the existing invariants in electronWrappedCallPaths.test.ts:

  • A directly observed operation stays one fact when its bridge path is traced.
  • Distinct wrapper invocations of a registration stay distinct.
  • One invocation reached twice is recorded once.

Separately, a channel that only a wrapper argument supplies must not erase the operation's unresolved observation. Callers outside the traced paths may pass other channels, and dropping the dynamic record would hide that, for example an exposed any: name => forward(name).

Change and scope

All in the wrapper-substitution producer, src/domain/javascript/electronWrappedCallPaths.ts:

  • Site resolution. The pass indexes the syntax pass's unresolved operations by site (side, operation, source range). When a substituted literal equals the channel the site determines on its own, the substituted record replaces the unresolved one. "Determines on its own" means the channel argument evaluated in the site's static lexical environment, which has no invocation bindings. The replaced record is indexed as directly observed, so later paths to it deduplicate exactly like a literal direct record. The syntax pass's unknown-coverage count is kept.
  • Argument-supplied channels (helper parameters) are added as candidates beside the unresolved observation, as before. They are no longer duplicated per entry.
  • Repeated paths. Invocation identity stays the walk's entry, as on main. A path is skipped only when an identical finding (apart from invocation) was already recorded through the same trailing local calls with at least as much enclosing context. When the recorded path is a strictly shorter nested entry, the fuller path takes over that record, so separate outer calls through one wrapper call stay distinct. Bundle module factories (recognized by the resolver) and factory calls made at module scope outside the trace run once per load, so closures they create are shared rather than per-invocation.
  • Registrations. In-place resolution also requires the handler to be site-determined: the handler argument, evaluated in the site's lexical environment, is the same function the substitution found (or both have none). A replaced observation removes the syntax pass's unknown-channel contribution, so a fully resolved file reports parse_status: complete. A handler supplied through a wrapper parameter keeps the generic source registration, with per-invocation candidates beside it. A handler closure that reads a binding of a function the trace activated (a wrapper parameter, a factory local or an outer call's parameter) is per-invocation: it never replaces the direct observation and is never skipped as a repeated path. An arrow handler's inherited this/arguments count as captures; bundle module factories (marked moduleFactory by the resolver) run once per module load, so their bindings do not.
  • docs/javascript-artifact-reconstruction.md documents the rule.
  • Not included: lexical resolution in the syntax pass itself, which has no symbolic resolver, and changes to summary counting. Counts follow the corrected records.
  • Scope decision: not applicable. This is a static-analysis correctness fix within the existing producer.

Contract and boundary impact

  • Semantic owner and earliest changed stage: wrapper substitution in electronWrappedCallPaths.ts, which owns symbolic channel resolution.
  • Contribution scope fit and linked maintainer decision: not applicable (ordinary fix).
  • CLI and MCP/tool-catalog contract: no schema change. IPC counts and observations change only where one operation was recorded more than once.
  • Provider, bridge, target-format, or platform compatibility: none.
  • Evidence, artifact, provenance, or reconstruction contract:
    • A site-resolved observation keeps its source range, bridge call path and invocation location, and carries resolution_basis: "wrapper-substitution".
    • Unresolved observations remain for argument-supplied channels.
    • The unknown coverage count is unchanged.
  • Target selection, authority, upstream behavior, and cleanup details: no execution. Electron is never run.
  • Generated metadata, package, or installation impact: none.

Expected effects:

  • 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:

    • src/domain/javascript/electronWrappedCallPaths.test.ts:
      • A site-determined constant channel resolves in place, with its bridge call path kept.
      • An argument-supplied channel keeps the unresolved observation beside its resolved candidates a and b, all at one site.
      • One wrapper call reached through an exposed member, or through an enclosing local call, is recorded once.
    • tests/acceptance/applications/electronIpcConstantChannelCli.test.ts checks the public CLI summary counts for one constant send (2/1, dynamic 0), two constant sends (3/2, dynamic 0), and a helper-forwarded channel (3/2, dynamic 1).
    • All 7 new tests fail against origin/main's producer and pass with this change (executed by restoring main's electronWrappedCallPaths.ts).
  • Base reproduction (executed, main d672cc239, built CLI, macOS arm64). Operations / transmissions (/ listeners) for the issue's fixtures:

    Fixture Before After Expected
    const 3/2 2/1 2/1
    inline literal (control) 2/1 2/1 2/1
    two const sites 5/4 3/2 3/2
    helper-forwarded send 4/3 3/2 see note
    helper-forwarded listener 4/0/3 3/0/2 see note

    Note on the helper-forwarded fixtures: the issue proposed 2/1 and 2/0/1. Instead, the parameter ch keeps its unresolved observation beside the resolved forward-chan candidate, and the duplicate literal is removed.

  • Related shapes (executed, after the change):

    • two exposed callers passing a and b: unresolved, a, b;
    • a resolvable plus an arbitrary caller: unresolved, a;
    • a nested local wrapper: one a plus unresolved, previously two a.
  • Real application (executed): Tabby's app.asar (29 MB, --integrity-policy record-and-continue) produces identical IPC summaries before and after (39 operations, 22 transmissions, 6 listeners, 11 handlers, 13 dynamic). Elapsed time was 116.15 s before and 116.50 s after. Peak RSS was 1.72 GB vs 1.87 GB, but that is one run each, so it is within run-to-run variance, not an established difference. Discord's ASAR has no IPC observations. Notion and Signal fail identically before and after (integrity mismatch; Signal also with record-and-continue after about 8.5 minutes).

  • Remaining proof gaps:

    • Real applications using the const-channel shape were not found among the locally installed apps.
    • Agent token usage was not measured; outputs only shrink where duplicates were removed.

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

  • npm run test:focused -- the 20 test files that reference Electron IPC, ipcRenderer, renderer_transmissions, electronWrappedCallPaths, ElectronBoundaryAnalysis or call_paths: 816 passed (before the new tests were added).

  • npm run test:focused -- src/domain/javascript/electronWrappedCallPaths.test.ts tests/acceptance/applications/electronIpcConstantChannelCli.test.ts tests/acceptance/applications/electronIpcListenerCallsiteCli.test.ts: 161 passed.

  • The same new tests against origin/main's producer: 7 failed, 152 passed (as expected).

  • npm run check:fast: passed. npm run format:check: passed. npm run docs:check: passed.

  • Review follow-up (5430e9892): restored entry-based invocation identity after Greptile showed that nearest-literal-call identity collapsed distinct outer handlers. Repeats are now detected by identical value plus a shared call suffix, and in-place resolution of registrations requires a site-determined handler (Codex). New regression tests cover both reviewer examples. Results: the 21 Electron/IPC test files pass (825 tests); check:fast, format:check and docs:check pass. Issue fixtures unchanged: 2/1, 2/1, 3/2, helper send 3/2 (1 dynamic), helper listener 3/0/2 (1 dynamic).

  • Review follow-up (7d145b456): handlers are compared by lexical value rather than by representation, so a named local handler resolves in place (Greptile). The stale unknown-channel contribution is removed on replacement (Codex). New tests cover both. Results: the 21 Electron/IPC test files pass (826 tests); check:fast and format:check pass.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. IPC counts decrease only for duplicated records.
  • Real Hopper/Ghidra, browser, or OS coverage: macOS arm64 only, locally. Static analysis only; Electron was not executed.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no new I/O, process or network behavior. Unresolved channels that could carry arbitrary renderer messages stay visible.

Author readiness

Human owner: @kaoru0822-kitauji

  • 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.
  • 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.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🤖 Generated with Claude Code


🔄 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/5810 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/21/2026 **Status:** ✅ Merged **Merged:** 10/21/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `fix/js-ipc-const-channel-duplicate` --- ### 📝 Commits (10+) - [`59dda3c`](https://github.com/morluto/rea/commit/59dda3c0631e2b4dea8813663369c842fc2ac0eb) fix(javascript): count one IPC observation per site-resolved channel - [`5430e98`](https://github.com/morluto/rea/commit/5430e989239a28fd317d969c95daae45dae2e05d) fix(javascript): keep wrapper invocations distinct while removing repeats - [`7d145b4`](https://github.com/morluto/rea/commit/7d145b4566b72ba2eb0793cc362e87f2c21ad079) fix(javascript): compare site-determined IPC handlers by value - [`2ad374a`](https://github.com/morluto/rea/commit/2ad374a040d0fc290c31d1005c523a5d7c45a437) fix(javascript): keep closures over invocation values as distinct IPC handlers - [`42f4cd6`](https://github.com/morluto/rea/commit/42f4cd6ac1fa81c64316d7a3cb1849c205fd5f82) Merge remote-tracking branch 'origin/main' into fix/js-ipc-const-channel-duplicate - [`d27d1e4`](https://github.com/morluto/rea/commit/d27d1e4b5a235d82e2d7b163ea09662df389bfb3) test(javascript): guard optional call paths in closure handler test - [`8f88b60`](https://github.com/morluto/rea/commit/8f88b60c0c5e8687473353de7fb14cbb9d4d3145) fix(javascript): decide IPC handler identity from activation captures - [`784eb39`](https://github.com/morluto/rea/commit/784eb39fe5e3aa2289f2a1b5eff86ec7f0b9809e) fix(javascript): treat implicit captures and bundle modules precisely in IPC handler identity - [`f37215c`](https://github.com/morluto/rea/commit/f37215c6f6411080c8e66de82b87f8e0a8d9f7b7) fix(javascript): attribute an arrow handler's inherited context to its owner - [`6e6c48b`](https://github.com/morluto/rea/commit/6e6c48b1ee28c0eaf8df77d85be8a9c1fb462ffe) fix(javascript): skip suffix scans for per-invocation IPC handlers ### 📊 Changes **5 files changed** (+735 additions, -13 deletions) <details> <summary>View changed files</summary> 📝 `docs/javascript-artifact-reconstruction.md` (+9 -1) 📝 `src/domain/javascript/electronWrappedCallPaths.test.ts` (+318 -0) 📝 `src/domain/javascript/electronWrappedCallPaths.ts` (+325 -12) 📝 `src/domain/javascript/javascriptSymbolicBindings.ts` (+7 -0) ➕ `tests/acceptance/applications/electronIpcConstantChannelCli.test.ts` (+76 -0) </details> ### 📄 Description **What broke:** `analyze-javascript-application` counted a send like `ipcRenderer.send(PING)`, with `const PING = "ping"`, as two renderer operations, and a wrapper call reached through a bridge member plus its own entry as two invocations (#5710). **Fix:** wrapper substitution now resolves the syntax pass's unresolved observation in place when the call site itself determines the finding (channel, and for registrations the handler). A finding repeated through the same trailing local calls is recorded once. **Risk:** low to medium. IPC counts drop only where one physical operation was recorded twice. A channel supplied through a wrapper argument stays an unresolved observation beside its resolved candidates, so unknowns are preserved. ## Summary Fixes #5710. One physical IPC operation could be recorded two or three times: - **Site-determined constant channels.** The syntax pass recorded `send(PING)` as `dynamic` (`channel_expression: "PING"`), and wrapper substitution added a separate `literal` `"ping"` record. Deduplication is keyed on the channel value, so the two never matched. The issue's fixture reported 3 operations and 2 transmissions instead of 2 and 1. - **Nested entries.** A wrapper call such as `forward("forward-chan")` is an entry seed (string-literal first argument) and is also reached through the enclosing `exposeInMainWorld` or local call. The finding's invocation identity was the walk's outermost seed, so the same call looked like two invocations. This duplicated literal sends and listener registrations (coygeek's helper-forwarding report: 4/3 and 4/0/3). ## Problem and expected behavior Expected, from the issue and the existing invariants in `electronWrappedCallPaths.test.ts`: - A directly observed operation stays one fact when its bridge path is traced. - Distinct wrapper invocations of a registration stay distinct. - One invocation reached twice is recorded once. Separately, a channel that only a wrapper argument supplies must not erase the operation's unresolved observation. Callers outside the traced paths may pass other channels, and dropping the dynamic record would hide that, for example an exposed `any: name => forward(name)`. ## Change and scope All in the wrapper-substitution producer, `src/domain/javascript/electronWrappedCallPaths.ts`: - **Site resolution.** The pass indexes the syntax pass's unresolved operations by site (side, operation, source range). When a substituted literal equals the channel the site determines on its own, the substituted record replaces the unresolved one. "Determines on its own" means the channel argument evaluated in the site's static lexical environment, which has no invocation bindings. The replaced record is indexed as directly observed, so later paths to it deduplicate exactly like a literal direct record. The syntax pass's unknown-coverage count is kept. - **Argument-supplied channels** (helper parameters) are added as candidates beside the unresolved observation, as before. They are no longer duplicated per entry. - **Repeated paths.** Invocation identity stays the walk's entry, as on main. A path is skipped only when an identical finding (apart from invocation) was already recorded through the same trailing local calls with at least as much enclosing context. When the recorded path is a strictly shorter nested entry, the fuller path takes over that record, so separate outer calls through one wrapper call stay distinct. Bundle module factories (recognized by the resolver) and factory calls made at module scope outside the trace run once per load, so closures they create are shared rather than per-invocation. - **Registrations.** In-place resolution also requires the handler to be site-determined: the handler argument, evaluated in the site's lexical environment, is the same function the substitution found (or both have none). A replaced observation removes the syntax pass's unknown-channel contribution, so a fully resolved file reports `parse_status: complete`. A handler supplied through a wrapper parameter keeps the generic source registration, with per-invocation candidates beside it. A handler closure that reads a binding of a function the trace activated (a wrapper parameter, a factory local or an outer call's parameter) is per-invocation: it never replaces the direct observation and is never skipped as a repeated path. An arrow handler's inherited `this`/`arguments` count as captures; bundle module factories (marked `moduleFactory` by the resolver) run once per module load, so their bindings do not. - `docs/javascript-artifact-reconstruction.md` documents the rule. - Not included: lexical resolution in the syntax pass itself, which has no symbolic resolver, and changes to summary counting. Counts follow the corrected records. - Scope decision: not applicable. This is a static-analysis correctness fix within the existing producer. ## Contract and boundary impact - Semantic owner and earliest changed stage: wrapper substitution in `electronWrappedCallPaths.ts`, which owns symbolic channel resolution. - Contribution scope fit and linked maintainer decision: not applicable (ordinary fix). - CLI and MCP/tool-catalog contract: no schema change. IPC counts and observations change only where one operation was recorded more than once. - Provider, bridge, target-format, or platform compatibility: none. - Evidence, artifact, provenance, or reconstruction contract: - A site-resolved observation keeps its source range, bridge call path and invocation location, and carries `resolution_basis: "wrapper-substitution"`. - Unresolved observations remain for argument-supplied channels. - The unknown coverage count is unchanged. - Target selection, authority, upstream behavior, and cleanup details: no execution. Electron is never run. - Generated metadata, package, or installation impact: none. Expected effects: - [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: - `src/domain/javascript/electronWrappedCallPaths.test.ts`: - A site-determined constant channel resolves in place, with its bridge call path kept. - An argument-supplied channel keeps the unresolved observation beside its resolved candidates `a` and `b`, all at one site. - One wrapper call reached through an exposed member, or through an enclosing local call, is recorded once. - `tests/acceptance/applications/electronIpcConstantChannelCli.test.ts` checks the public CLI summary counts for one constant send (2/1, dynamic 0), two constant sends (3/2, dynamic 0), and a helper-forwarded channel (3/2, dynamic 1). - All 7 new tests fail against `origin/main`'s producer and pass with this change (executed by restoring main's `electronWrappedCallPaths.ts`). - Base reproduction (executed, main `d672cc239`, built CLI, macOS arm64). Operations / transmissions (/ listeners) for the issue's fixtures: | Fixture | Before | After | Expected | |---|---|---|---| | const | 3/2 | 2/1 | 2/1 | | inline literal (control) | 2/1 | 2/1 | 2/1 | | two const sites | 5/4 | 3/2 | 3/2 | | helper-forwarded send | 4/3 | 3/2 | see note | | helper-forwarded listener | 4/0/3 | 3/0/2 | see note | Note on the helper-forwarded fixtures: the issue proposed 2/1 and 2/0/1. Instead, the parameter `ch` keeps its unresolved observation beside the resolved `forward-chan` candidate, and the duplicate literal is removed. - Related shapes (executed, after the change): - two exposed callers passing `a` and `b`: unresolved, `a`, `b`; - a resolvable plus an arbitrary caller: unresolved, `a`; - a nested local wrapper: one `a` plus unresolved, previously two `a`. - Real application (executed): Tabby's `app.asar` (29 MB, `--integrity-policy record-and-continue`) produces identical IPC summaries before and after (39 operations, 22 transmissions, 6 listeners, 11 handlers, 13 dynamic). Elapsed time was 116.15 s before and 116.50 s after. Peak RSS was 1.72 GB vs 1.87 GB, but that is one run each, so it is within run-to-run variance, not an established difference. Discord's ASAR has no IPC observations. Notion and Signal fail identically before and after (integrity mismatch; Signal also with record-and-continue after about 8.5 minutes). - Remaining proof gaps: - Real applications using the const-channel shape were not found among the locally installed apps. - Agent token usage was not measured; outputs only shrink where duplicates were removed. 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 - `npm run test:focused --` the 20 test files that reference Electron IPC, `ipcRenderer`, `renderer_transmissions`, `electronWrappedCallPaths`, `ElectronBoundaryAnalysis` or `call_paths`: 816 passed (before the new tests were added). - `npm run test:focused -- src/domain/javascript/electronWrappedCallPaths.test.ts tests/acceptance/applications/electronIpcConstantChannelCli.test.ts tests/acceptance/applications/electronIpcListenerCallsiteCli.test.ts`: 161 passed. - The same new tests against `origin/main`'s producer: 7 failed, 152 passed (as expected). - `npm run check:fast`: passed. `npm run format:check`: passed. `npm run docs:check`: passed. - Review follow-up (5430e9892): restored entry-based invocation identity after Greptile showed that nearest-literal-call identity collapsed distinct outer handlers. Repeats are now detected by identical value plus a shared call suffix, and in-place resolution of registrations requires a site-determined handler (Codex). New regression tests cover both reviewer examples. Results: the 21 Electron/IPC test files pass (825 tests); `check:fast`, `format:check` and `docs:check` pass. Issue fixtures unchanged: 2/1, 2/1, 3/2, helper send 3/2 (1 dynamic), helper listener 3/0/2 (1 dynamic). - Review follow-up (7d145b456): handlers are compared by lexical value rather than by representation, so a named local handler resolves in place (Greptile). The stale unknown-channel contribution is removed on replacement (Codex). New tests cover both. Results: the 21 Electron/IPC test files pass (826 tests); `check:fast` and `format:check` pass. ## Compatibility, safety, and release - Breaking changes or migration steps: none. IPC counts decrease only for duplicated records. - Real Hopper/Ghidra, browser, or OS coverage: macOS arm64 only, locally. Static analysis only; Electron was not executed. - Package or release metadata impact: none. - Security, privacy, process, or containment review: no new I/O, process or network behavior. Unresolved channels that could carry arbitrary renderer messages stay visible. ## Author readiness Human owner: @kaoru0822-kitauji - [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 - [ ] 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. - [ ] 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. - [ ] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-11-01 21:45:12 +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#7075
No description provided.