Skip to content

perf: scanMultipleRepositories の git worktree list を並列実行 (#711)#712

Merged
Kewton merged 1 commit into
developfrom
feature/711-worktree
May 15, 2026
Merged

perf: scanMultipleRepositories の git worktree list を並列実行 (#711)#712
Kewton merged 1 commit into
developfrom
feature/711-worktree

Conversation

@Kewton
Copy link
Copy Markdown
Owner

@Kewton Kewton commented May 15, 2026

Summary

scanMultipleRepositories の逐次スキャン (for ... await) を Promise.allSettled で並列化し、POST /api/repositories/sync の体感遅延を短縮します。
リポジトリ間の git worktree list は独立した子プロセスなので干渉せず、合計時間が「リポジトリ数 × 単一スキャン時間」から「最遅 1 件の時間」に圧縮されます。

Closes #711

Changes

Changed

  • src/lib/git/worktrees.ts: scanMultipleRepositoriesPromise.allSettled(repositoryPaths.map(...)) に書き換え。
  • 個別失敗は logger.error でログ出力するのみとし、全体は失敗させない既存挙動を維持。
  • ログには repoPath を付与(並列実行で順序が不定になるため後追い相関を可能にする)。

Tests

  • tests/unit/worktrees.test.ts: vi.mock('child_process')auto-mock → factory mock に変更。
    vitest の auto-mock は child_process.executil.promisify.custom シンボルを引き継いでしまい、promisify(exec)mockImplementation をバイパスして実装側を呼んでしまうため、vi.fn() 単体を返す factory に変更して回避。
  • scanMultipleRepositories の単体テストを 4 件追加:
    • 各リポジトリで git worktree list が 1 回ずつ呼ばれる
    • 1 件が reject しても残りの結果は返る
    • 空配列で [] を返す
    • 並列実行検証: deferred-callback パターンで、最初の await setImmediate 後に全リポジトリの exec が起動済みであることを確認(逐次実行であれば 1 件しか観測されない)

Test Results

Unit Tests

npm run test:unit
Test Files  343 passed (343)
Tests       6495 passed | 7 skipped (6502)

ベースライン (本変更前): 6491 passed | 7 skipped+4 新規テスト、0 リグレッション

Lint & Type Check

  • ESLint: 0 errors / 0 warnings
  • TypeScript (npx tsc --noEmit): 0 errors

Build

npm run build
Build successful (Next.js 14)

Performance Impact (試算)

Before (逐次) After (並列)
12 リポジトリ × 50〜100ms/件 600〜1200ms 100〜200ms (最遅 1 件分)

実機ベンチは PR マージ後にコメントで報告予定です。

Checklist

  • Unit tests pass (+4 new, 0 regressions)
  • Lint check passes
  • Type check passes
  • Build succeeds
  • No console.log in production code
  • 個別失敗時の既存挙動 (1 件失敗で全体は継続) を維持
  • ログ出力に repoPath を付与

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

…led (#711)

Each `git worktree list` spawns an independent child process, so running
them sequentially via `for ... await` made the sync endpoint scale linearly
in the number of repositories. Switch to `Promise.allSettled(...map(...))`
so all repository scans run concurrently while preserving the prior "one
failure does not abort the rest" semantics. Logs continue to include
`repoPath` so the (now unordered) output can still be correlated.

The unit test mock for `child_process` had to be switched from auto-mock to
a factory mock: vitest's auto-mock preserves `util.promisify.custom` from
the real `exec`, which made `promisify(exec)` bypass `mockImplementation`
entirely. The factory returns a fresh `vi.fn()` without that symbol so
callback-style mocking works.

Closes #711

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Kewton Kewton added bug Something isn't working enhancement New feature or request labels May 15, 2026
@Kewton Kewton merged commit 6e11795 into develop May 15, 2026
5 checks passed
@Kewton Kewton deleted the feature/711-worktree branch May 15, 2026 12:32
Kewton added a commit that referenced this pull request May 17, 2026
… (#718)

* feat: 履歴(History)表示件数を50〜250件で選択可能にする (#701) (#702)

* feat(history): allow selecting history display limit (50-250)

Issue #701

- Add HistoryDisplayLimit selector (50/100/150/200/250) to HistoryPane header
- Persist selection in localStorage (commandmate:historyDisplayLimit)
- Raise messages API upper bound from 100 to MAX_MESSAGES_LIMIT (250)
- Centralize options/MAX/DEFAULT in src/config/history-display-config.ts
- Propagate selection through WorktreeDetailRefactored (PC + Mobile)
- worktreeApi.getMessages: optional limit argument
- useInfiniteMessages: default pageSize references DEFAULT_MESSAGES_LIMIT
- Tests: boundary (1/250/251/0/abc), DB getMessages limit=250,
  useInfiniteMessages hasMore at pageSize=250, config Single Source of Truth

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(history-display-config): derive MAX_MESSAGES_LIMIT from union type

- Replace brittle `as 250` literal cast with `HistoryDisplayLimit` type
  derivation so extending HISTORY_DISPLAY_LIMIT_OPTIONS no longer
  requires touching MAX_MESSAGES_LIMIT.
- Tighten comments and adjust wording for clarity.

Issue #701

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(claude-md): add history-display-config.ts to module reference (#701)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(detection): handle Claude v2.1.142 skill approval prompt summary line (#704) (#705)

Claude Code v2.1.142 renders trailing summary lines like "… +1 pending"
below the real options in skill approval prompts ("Use skill ...?").
NORMAL_OPTION_PATTERN was matching these as option N with label="pending",
which poisoned isValidPrecedingOption() and caused every real option
above (1./2./3.) to be rejected — collectedOptions ended up with length
< 2, returning no_prompt. As a result Yes/No UI never appeared and
Auto-Yes did not fire.

Defense-in-depth fix:
- S1: SUMMARY_LINE_PATTERN early-continue in Pass 2 loop (tight
  anchored pattern, no free `(.+)` capture, keyword whitelist
  pending|more) so legitimate labels mentioning these words are
  preserved.
- S2: CLAUDE_PROMPT_FOOTER_PATTERN trims effectiveEnd to the
  "Esc to cancel · Tab to amend" footer, putting the summary line
  outside the scan window entirely. Fallback-safe: no footer found =>
  effectiveEnd untouched, so Codex/Gemini/OpenCode/Copilot detection
  paths are unaffected.
- S3: 3-layer regression tests across prompt-detector, status-detector,
  and auto-yes-resolver covering the real Claude v2.1.142 fixture plus
  FP-suppression for option labels containing "pending"/"more".

Quality gates: lint 0/0, tsc 0, unit 6486 passed / 7 skipped, build OK.

* fix(files): preserve scroll position during tree refetch (#706) (#707)

- FileTreeView: limit full-screen loading/error to initial mount (rootItems empty)
- FileTreeView: add non-destructive refetch indicator (aria-live=polite) and error banner with retry button
- FileTreeView: extract reloadTreeWithExpandedDirs via useCallback + mountedRef guard
- WorktreeDetailRefactored: skip first-poll false-positive refresh by treating null prevTreeHashRef as baseline-only
- tests: add 5 new tests for refetch indicator / non-destructive error / retry path

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* perf(sync): parallelize scanMultipleRepositories with Promise.allSettled (#711) (#712)

Each `git worktree list` spawns an independent child process, so running
them sequentially via `for ... await` made the sync endpoint scale linearly
in the number of repositories. Switch to `Promise.allSettled(...map(...))`
so all repository scans run concurrently while preserving the prior "one
failure does not abort the rest" semantics. Logs continue to include
`repoPath` so the (now unordered) output can still be correlated.

The unit test mock for `child_process` had to be switched from auto-mock to
a factory mock: vitest's auto-mock preserves `util.promisify.custom` from
the real `exec`, which made `promisify(exec)` bypass `mockImplementation`
entirely. The factory returns a fresh `vi.fn()` without that symbol so
callback-style mocking works.

Closes #711

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* perf(sidebar): adaptive polling interval for active/idle transitions (#710) (#713)

useWorktreesCache のポーリング間隔(active=5s/idle=30s)が startPolling() 呼び出し時点で固定され、worktrees の active/idle 状態が遷移しても interval が更新されない問題を修正。

- currentIntervalRef: useRef<number | null> を追加し活動中の interval を追跡
- startPolling/stopPolling/hasActiveSession を useCallback でフック直下にリフトアップ
- worktrees-change useEffect で desired interval を再計算し差分時のみ startPolling を再実行
- document.hidden 中・初期化前(ref === null)はガードして no-op
- 既存の visibilitychange ハンドラの責務は維持

Closes #710

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* perf(sidebar): consolidate useWorktreesCache via Provider Context (#709) (#714)

Sessions ページが useWorktreesCache() を直接呼び出していたため、
WorktreesCacheProvider と合わせて /api/worktrees ポーリングが2系統
並行で動いていた問題を解消。

- WorktreesCacheProvider に WorktreesCacheContext を新設し
  useWorktreesCacheContext() フックを export
- src/app/sessions/page.tsx を Context 経由参照に変更
- useWorktreesCache フック自体は不変 (テスト/単体利用は維持)
- 新規テスト tests/unit/components/providers/WorktreesCacheProvider.test.tsx

Closes #709

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* perf(db): add composite index including role column on chat_messages (#708) (#715)

chat_messages の相関サブクエリ (role='assistant'/'user' AND archived=0 の
MAX(timestamp)/ORDER BY timestamp DESC LIMIT 1) が、既存
idx_messages_archived(worktree_id, archived, timestamp DESC) では role 列を
含まないため行スキャンに陥り、件数増加に伴い線形劣化していた。

Migration v32 で複合インデックス
  idx_messages_worktree_role_archived_time(worktree_id, role, archived, timestamp DESC)
を新設し、既存 idx_messages_archived を DROP。
他の role なしクエリ (getMessages, getLastMessage, deleteAllMessages) は
idx_messages_worktree_time(worktree_id, timestamp DESC) で従来同等に動作する。

- src/lib/db/migrations/v32-add-messages-role-composite-index.ts: 新規
- src/lib/db/migrations/index.ts / runner.ts: v32 登録、CURRENT_SCHEMA_VERSION = 32
- src/lib/db/init-db.ts: 初期化パスを新インデックスへ置換
- tests/unit/lib/db-migrations.test.ts: v32 describe ブロック追加、
  v22 旧インデックスアサーションを rollback to 31 経由に修正
- tests/unit/lib/db/chat-db-explain-plan.test.ts: EXPLAIN QUERY PLAN
  で対象4クエリが新インデックスを使うことを検証

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat: Worktree詳細 HistoryPaneにメッセージテキスト検索機能を追加 (#716) (#717)

* feat(history): add text search to HistoryPane (#716)

Adds a per-pane text search bar to Worktree HistoryPane with namespace-isolated
CSS Custom Highlight API rendering so it can coexist with the TerminalPane
search (Issue #47).

Key design choices (3-stage reviewed):
- Preserve OCP: existing applyTerminalHighlights / clearTerminalHighlights
  signatures unchanged. New applyHistoryHighlights / clearHistoryHighlights
  share the internal engine via HighlightNamespace.
- ConversationPairCard receives no new props; only data-message-id is added so
  memo is preserved. useConversationHistory is untouched.
- HistoryPane manages autoExpandedIds internally to force-expand truncated
  assistant messages that contain hits, then applies highlights with a strict
  useLayoutEffect order (save scroll → restore scroll (skipped during search)
  → autoExpandedIds → applyHistoryHighlights) so textContent stays aligned
  with message.content.
- Debounce (300ms), min-query length (2), and max-matches (500) are shared
  via newly exported SEARCH_DEBOUNCE_MS / SEARCH_MIN_QUERY_LENGTH /
  TERMINAL_SEARCH_MAX_MATCHES from useTerminalSearch.

Tests: 47 new cases across useHistorySearch, HistorySearchBar, terminal-
highlight, and HistoryPane. Full unit suite: 6558 passed / 0 failed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(history-search): DRY debounce timer + unify selector escape (#716)

Small post-implementation cleanups identified during the refactoring phase.
No behavior changes; all 6558 unit tests still pass.

useHistorySearch.ts:
- Extract clearDebounceTimer() helper (DRY: clearTimeout/null pattern was
  duplicated across scheduleSearch / closeSearch / onCompositionStart /
  unmount cleanup).

HistoryPane.tsx:
- Replace custom escapeAttrValue() with native CSS.escape() via a small
  findMessageElement() helper. Removes a homemade escape function that
  duplicated standards-track functionality and unifies the two
  inconsistent selector-building sites (one used CSS.escape, the other
  the custom helper).
- Capture the current match element during the highlight loop so the
  scrollIntoView path no longer issues a second querySelector for the
  same node.

Constraints preserved:
- terminal-highlight.ts public API untouched (OCP).
- ConversationPairCard props unchanged (memo preserved).
- Effect declaration order (1)->(2)->(3)->(4) and HISTORY_SEARCH_NAMESPACE
  isolation unchanged (design policy v1.2 core).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(history): update CLAUDE.md and implementation-history for #716

Add HistoryPane text search feature entry and document new modules:
useHistorySearch hook, HistorySearchBar component, terminal-highlight
namespace separation (HISTORY_SEARCH_NAMESPACE), and data-message-id
attribution in ConversationPairCard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Kewton added a commit that referenced this pull request May 27, 2026
* feat: 履歴(History)表示件数を50〜250件で選択可能にする (#701) (#702)

* feat(history): allow selecting history display limit (50-250)

Issue #701

- Add HistoryDisplayLimit selector (50/100/150/200/250) to HistoryPane header
- Persist selection in localStorage (commandmate:historyDisplayLimit)
- Raise messages API upper bound from 100 to MAX_MESSAGES_LIMIT (250)
- Centralize options/MAX/DEFAULT in src/config/history-display-config.ts
- Propagate selection through WorktreeDetailRefactored (PC + Mobile)
- worktreeApi.getMessages: optional limit argument
- useInfiniteMessages: default pageSize references DEFAULT_MESSAGES_LIMIT
- Tests: boundary (1/250/251/0/abc), DB getMessages limit=250,
  useInfiniteMessages hasMore at pageSize=250, config Single Source of Truth

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(history-display-config): derive MAX_MESSAGES_LIMIT from union type

- Replace brittle `as 250` literal cast with `HistoryDisplayLimit` type
  derivation so extending HISTORY_DISPLAY_LIMIT_OPTIONS no longer
  requires touching MAX_MESSAGES_LIMIT.
- Tighten comments and adjust wording for clarity.

Issue #701

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(claude-md): add history-display-config.ts to module reference (#701)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(detection): handle Claude v2.1.142 skill approval prompt summary line (#704) (#705)

Claude Code v2.1.142 renders trailing summary lines like "… +1 pending"
below the real options in skill approval prompts ("Use skill ...?").
NORMAL_OPTION_PATTERN was matching these as option N with label="pending",
which poisoned isValidPrecedingOption() and caused every real option
above (1./2./3.) to be rejected — collectedOptions ended up with length
< 2, returning no_prompt. As a result Yes/No UI never appeared and
Auto-Yes did not fire.

Defense-in-depth fix:
- S1: SUMMARY_LINE_PATTERN early-continue in Pass 2 loop (tight
  anchored pattern, no free `(.+)` capture, keyword whitelist
  pending|more) so legitimate labels mentioning these words are
  preserved.
- S2: CLAUDE_PROMPT_FOOTER_PATTERN trims effectiveEnd to the
  "Esc to cancel · Tab to amend" footer, putting the summary line
  outside the scan window entirely. Fallback-safe: no footer found =>
  effectiveEnd untouched, so Codex/Gemini/OpenCode/Copilot detection
  paths are unaffected.
- S3: 3-layer regression tests across prompt-detector, status-detector,
  and auto-yes-resolver covering the real Claude v2.1.142 fixture plus
  FP-suppression for option labels containing "pending"/"more".

Quality gates: lint 0/0, tsc 0, unit 6486 passed / 7 skipped, build OK.

* fix(files): preserve scroll position during tree refetch (#706) (#707)

- FileTreeView: limit full-screen loading/error to initial mount (rootItems empty)
- FileTreeView: add non-destructive refetch indicator (aria-live=polite) and error banner with retry button
- FileTreeView: extract reloadTreeWithExpandedDirs via useCallback + mountedRef guard
- WorktreeDetailRefactored: skip first-poll false-positive refresh by treating null prevTreeHashRef as baseline-only
- tests: add 5 new tests for refetch indicator / non-destructive error / retry path

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* perf(sync): parallelize scanMultipleRepositories with Promise.allSettled (#711) (#712)

Each `git worktree list` spawns an independent child process, so running
them sequentially via `for ... await` made the sync endpoint scale linearly
in the number of repositories. Switch to `Promise.allSettled(...map(...))`
so all repository scans run concurrently while preserving the prior "one
failure does not abort the rest" semantics. Logs continue to include
`repoPath` so the (now unordered) output can still be correlated.

The unit test mock for `child_process` had to be switched from auto-mock to
a factory mock: vitest's auto-mock preserves `util.promisify.custom` from
the real `exec`, which made `promisify(exec)` bypass `mockImplementation`
entirely. The factory returns a fresh `vi.fn()` without that symbol so
callback-style mocking works.

Closes #711

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* perf(sidebar): adaptive polling interval for active/idle transitions (#710) (#713)

useWorktreesCache のポーリング間隔(active=5s/idle=30s)が startPolling() 呼び出し時点で固定され、worktrees の active/idle 状態が遷移しても interval が更新されない問題を修正。

- currentIntervalRef: useRef<number | null> を追加し活動中の interval を追跡
- startPolling/stopPolling/hasActiveSession を useCallback でフック直下にリフトアップ
- worktrees-change useEffect で desired interval を再計算し差分時のみ startPolling を再実行
- document.hidden 中・初期化前(ref === null)はガードして no-op
- 既存の visibilitychange ハンドラの責務は維持

Closes #710

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* perf(sidebar): consolidate useWorktreesCache via Provider Context (#709) (#714)

Sessions ページが useWorktreesCache() を直接呼び出していたため、
WorktreesCacheProvider と合わせて /api/worktrees ポーリングが2系統
並行で動いていた問題を解消。

- WorktreesCacheProvider に WorktreesCacheContext を新設し
  useWorktreesCacheContext() フックを export
- src/app/sessions/page.tsx を Context 経由参照に変更
- useWorktreesCache フック自体は不変 (テスト/単体利用は維持)
- 新規テスト tests/unit/components/providers/WorktreesCacheProvider.test.tsx

Closes #709

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* perf(db): add composite index including role column on chat_messages (#708) (#715)

chat_messages の相関サブクエリ (role='assistant'/'user' AND archived=0 の
MAX(timestamp)/ORDER BY timestamp DESC LIMIT 1) が、既存
idx_messages_archived(worktree_id, archived, timestamp DESC) では role 列を
含まないため行スキャンに陥り、件数増加に伴い線形劣化していた。

Migration v32 で複合インデックス
  idx_messages_worktree_role_archived_time(worktree_id, role, archived, timestamp DESC)
を新設し、既存 idx_messages_archived を DROP。
他の role なしクエリ (getMessages, getLastMessage, deleteAllMessages) は
idx_messages_worktree_time(worktree_id, timestamp DESC) で従来同等に動作する。

- src/lib/db/migrations/v32-add-messages-role-composite-index.ts: 新規
- src/lib/db/migrations/index.ts / runner.ts: v32 登録、CURRENT_SCHEMA_VERSION = 32
- src/lib/db/init-db.ts: 初期化パスを新インデックスへ置換
- tests/unit/lib/db-migrations.test.ts: v32 describe ブロック追加、
  v22 旧インデックスアサーションを rollback to 31 経由に修正
- tests/unit/lib/db/chat-db-explain-plan.test.ts: EXPLAIN QUERY PLAN
  で対象4クエリが新インデックスを使うことを検証

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat: Worktree詳細 HistoryPaneにメッセージテキスト検索機能を追加 (#716) (#717)

* feat(history): add text search to HistoryPane (#716)

Adds a per-pane text search bar to Worktree HistoryPane with namespace-isolated
CSS Custom Highlight API rendering so it can coexist with the TerminalPane
search (Issue #47).

Key design choices (3-stage reviewed):
- Preserve OCP: existing applyTerminalHighlights / clearTerminalHighlights
  signatures unchanged. New applyHistoryHighlights / clearHistoryHighlights
  share the internal engine via HighlightNamespace.
- ConversationPairCard receives no new props; only data-message-id is added so
  memo is preserved. useConversationHistory is untouched.
- HistoryPane manages autoExpandedIds internally to force-expand truncated
  assistant messages that contain hits, then applies highlights with a strict
  useLayoutEffect order (save scroll → restore scroll (skipped during search)
  → autoExpandedIds → applyHistoryHighlights) so textContent stays aligned
  with message.content.
- Debounce (300ms), min-query length (2), and max-matches (500) are shared
  via newly exported SEARCH_DEBOUNCE_MS / SEARCH_MIN_QUERY_LENGTH /
  TERMINAL_SEARCH_MAX_MATCHES from useTerminalSearch.

Tests: 47 new cases across useHistorySearch, HistorySearchBar, terminal-
highlight, and HistoryPane. Full unit suite: 6558 passed / 0 failed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(history-search): DRY debounce timer + unify selector escape (#716)

Small post-implementation cleanups identified during the refactoring phase.
No behavior changes; all 6558 unit tests still pass.

useHistorySearch.ts:
- Extract clearDebounceTimer() helper (DRY: clearTimeout/null pattern was
  duplicated across scheduleSearch / closeSearch / onCompositionStart /
  unmount cleanup).

HistoryPane.tsx:
- Replace custom escapeAttrValue() with native CSS.escape() via a small
  findMessageElement() helper. Removes a homemade escape function that
  duplicated standards-track functionality and unifies the two
  inconsistent selector-building sites (one used CSS.escape, the other
  the custom helper).
- Capture the current match element during the highlight loop so the
  scrollIntoView path no longer issues a second querySelector for the
  same node.

Constraints preserved:
- terminal-highlight.ts public API untouched (OCP).
- ConversationPairCard props unchanged (memo preserved).
- Effect declaration order (1)->(2)->(3)->(4) and HISTORY_SEARCH_NAMESPACE
  isolation unchanged (design policy v1.2 core).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(history): update CLAUDE.md and implementation-history for #716

Add HistoryPane text search feature entry and document new modules:
useHistorySearch hook, HistorySearchBar component, terminal-highlight
namespace separation (HISTORY_SEARCH_NAMESPACE), and data-message-id
attribution in ConversationPairCard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(executor): diagnose execFile maxBuffer errors and bump limit to 10MB (#719) (#720)

Node v24 で execFile の `ERR_CHILD_PROCESS_STDIO_MAXBUFFER` が
`failed/exit_code=null` のまま原因不明で終了していた問題を修正。

- error.code が文字列のとき parseInt で NaN → null になっていたのを
  `typeof errCode === 'number'` での厳密判定に変更(型安全)。
- `stdout || stderr || error.message` の優先順位で巨大 stdout が
  error.message を呑み込んでいたため、必ず先頭に Error/Code/Signal/Reason
  サマリを置き、続けて --- stdout --- / --- stderr --- セクションで
  両方を保存するように変更。
- ERR_CHILD_PROCESS_STDIO_MAXBUFFER の場合は専用 Reason 行を付加し、
  timeout ではなく failed として扱う(時間切れと出力過多は意味が異なる)。
- MAX_OUTPUT_SIZE を 1MB → 10MB に暫定緩和(spawn + rolling buffer 化は
  別 Issue で根本対策)。

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant