Skip to content

fix(desktop): wire generationTimeoutSec preference to in-flight aborts#100

Merged
hqhq1025 merged 3 commits intomainfrom
wt/loop-fix-generation-timeout-pref
Apr 19, 2026
Merged

fix(desktop): wire generationTimeoutSec preference to in-flight aborts#100
hqhq1025 merged 3 commits intomainfrom
wt/loop-fix-generation-timeout-pref

Conversation

@hqhq1025
Copy link
Copy Markdown
Collaborator

Summary

  • The Settings → Advanced → "Generation timeout" preference was persisted and validated but never read by the generate IPC handler. Recent main.log shows generations running 267s against the configured 120s default — the abort timer was never scheduled.
  • Extracts armGenerationTimeout into apps/desktop/src/main/generation-ipc.ts and calls it from both codesign:v1:generate and the legacy codesign:generate shim. Reads prefs lazily per-request so Settings changes apply on the next generation without an app restart.
  • Aborts with CodesignError(code: 'GENERATION_TIMEOUT'). Failures to read prefs degrade to a no-op (warn only) so a corrupt prefs file cannot block generation. Non-finite / non-positive values are also no-op for safety.

Files

  • apps/desktop/src/main/generation-ipc.ts — new armGenerationTimeout(id, controller, readTimeoutSec, logger) helper.
  • apps/desktop/src/main/index.ts — wires armTimeout(id, controller) into both generate handlers + clears in finally.
  • apps/desktop/src/main/generation-ipc.test.ts — 4 new vitest cases (fire / clear / read-failure / non-finite) using fake timers.

Principles checks

  • Compatibility: green — no API surface change, prefs schema unchanged.
  • Upgradeability: green — pref is read per-request, no caching to invalidate.
  • No bloat: green — net +127 LOC, all in main + tests; zero deps added.
  • Elegance: green — single helper, signature-injectable for testing.

Test plan

  • pnpm --filter @open-codesign/desktop test — 300/300 pass.
  • pnpm exec biome check apps/desktop/src/main/{index,generation-ipc,generation-ipc.test}.ts — clean (only pre-existing complexity warning on apply-comment handler).
  • pnpm exec tsc --noEmit -p tsconfig.node.json — clean.
  • Manual: set generationTimeoutSec to 5 in Settings → Advanced, run a long generation, confirm abort + toast.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Major] Preferences read failure is silently downgraded to "no timeout" — this violates the "no silent fallbacks" constraint and can leave long-running LLM calls unbounded instead of surfacing a contextual error to users, evidence apps/desktop/src/main/generation-ipc.ts:48
    Suggested fix:
    try {
      timeoutSec = await readTimeoutSec();
    } catch (err) {
      throw new CodesignError(
        `Failed to read generation timeout preference: ${err instanceof Error ? err.message : String(err)}`,
        'PREFERENCES_READ_FAILED',
      );
    }

Summary

  • Review mode: initial
  • 1 issue found in the new timeout wiring: a silent fallback on preferences-read errors.
  • Not found in repo/docs: docs/VISION.md, docs/PRINCIPLES.md (missing in this checkout), so constraint validation used CLAUDE.md + changed code.

Testing

  • Not run (automation)
  • Suggested: add an IPC-level test asserting codesign:v1:generate rejects with contextual CodesignError when preferences read fails during timeout arming.

open-codesign Bot

Comment thread apps/desktop/src/main/generation-ipc.ts Outdated
id,
message: err instanceof Error ? err.message : String(err),
});
return () => {};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silent fallback here (return () => {}) masks preferences read failures and disables timeout enforcement. Please throw a contextual CodesignError instead so the UI surfaces the failure.

@hqhq1025 hqhq1025 force-pushed the wt/loop-fix-generation-timeout-pref branch from 8917adc to ce374b6 Compare April 19, 2026 08:47
@hqhq1025
Copy link
Copy Markdown
Collaborator Author

Addressed Codex [Major]: prefs read failure no longer silently degrades to unbounded LLM calls.

armGenerationTimeout now rethrows as CodesignError('PREFERENCES_READ_FAIL') when the prefs reader throws. The IPC handlers in apps/desktop/src/main/index.ts now invoke armTimeout inside the existing try block so generate.fail logging + inFlight cleanup still run, and the error propagates to the renderer toast surface. The vitest case for the failure path was flipped to assert the rethrow.

Commit: ce374b6

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Major] Timer overflow can turn a large timeout into near-immediate abort — setTimeout in Node caps delay to a 32-bit signed int; when generationTimeoutSec * 1000 exceeds that limit, the callback runs almost immediately, unexpectedly cancelling valid in-flight generations. Evidence apps/desktop/src/main/generation-ipc.ts:65
    Suggested fix:
    const timeoutMs = timeoutSec * 1000;
    const MAX_TIMEOUT_MS = 2_147_483_647;
    if (timeoutMs > MAX_TIMEOUT_MS) {
      throw new CodesignError(
        `generationTimeoutSec is too large (${timeoutSec}). Max supported is ${Math.floor(MAX_TIMEOUT_MS / 1000)} seconds.`,
        'PREFERENCES_READ_FAIL',
      );
    }
    
    const handle = setTimeout(() => {
      // existing timeout abort logic
    }, timeoutMs);

Summary

  • Review mode: follow-up after new commits
  • 1 issue found in the newly added timeout arming path: oversized timeout values can overflow timer delay and cancel requests immediately.
  • Not found in repo/docs: docs/VISION.md, docs/PRINCIPLES.md.

Testing

  • Not run (automation)
  • Suggested: add a Vitest case where readTimeoutSec returns a value above 2_147_483 and assert a contextual CodesignError is thrown before scheduling.

open-codesign Bot

Comment thread apps/desktop/src/main/generation-ipc.ts Outdated
'GENERATION_TIMEOUT',
),
);
}, timeoutSec * 1000);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Major] timeoutSec * 1000 can exceed Node timer max (2_147_483_647ms) and fire almost immediately, aborting generations unexpectedly.

Suggested fix:

const timeoutMs = timeoutSec * 1000;
const MAX_TIMEOUT_MS = 2_147_483_647;
if (timeoutMs > MAX_TIMEOUT_MS) {
  throw new CodesignError(
    `generationTimeoutSec is too large (${timeoutSec}). Max supported is ${Math.floor(MAX_TIMEOUT_MS / 1000)} seconds.`,
    PREFERENCES_READ_FAIL,
  );
}
const handle = setTimeout(() => {
  // existing timeout abort logic
}, timeoutMs);

The Settings → Advanced → "Generation timeout" preference was persisted
and validated, but the value was never read by the generate IPC handler.
Logs show generations running >4 minutes against a configured 120s limit
because no AbortController timer was ever scheduled.

Extract `armGenerationTimeout` into `generation-ipc.ts` and call it from
both the `codesign:v1:generate` and legacy `codesign:generate` handlers.
Reads prefs lazily per-request so Settings changes apply on the next
generation without an app restart. Failures to read prefs degrade to a
no-op (warn only) so a corrupt prefs file cannot block generation.

Throws CodesignError with code GENERATION_TIMEOUT on abort so the
renderer can distinguish user-cancel from timeout in toast copy later.

Tests: 4 new vitest cases covering fire / clear / read-failure /
non-finite values, all using fake timers.

Signed-off-by: hqhq1025 <[email protected]>
When the preferences read throws, armGenerationTimeout previously
fell back to "no timeout" — long LLM calls would run unbounded with
no signal to the user. Rethrow as PREFERENCES_READ_FAIL so the IPC
caller logs generate.fail and the renderer surfaces a toast. Move
armTimeout inside the try block in index.ts so inFlight cleanup
still runs if it throws.
Node's setTimeout max delay is 2^31-1 ms (~24.8 days). Larger values
overflow and the timer fires immediately, aborting generation instantly
when the user picks a long timeout.

Clamp the millisecond value to TIMEOUT_MAX_MS before scheduling, and
reject NaN/Infinity/negative values up front with a CodesignError
('PREFERENCES_INVALID_TIMEOUT') instead of silently disabling the
timeout. A value of 0 still means 'disabled'.
@hqhq1025 hqhq1025 force-pushed the wt/loop-fix-generation-timeout-pref branch from ce374b6 to e891e93 Compare April 19, 2026 08:54
@hqhq1025
Copy link
Copy Markdown
Collaborator Author

Pushed e891e93: clamp generation timeout to Node's setTimeout int32 cap (2_147_483_647 ms ≈ 24.8 days). Without this, large generationTimeoutSec values overflow and the timer fires immediately, aborting generation instantly.

Also tightened input validation: NaN / Infinity / negative values now throw CodesignError('PREFERENCES_INVALID_TIMEOUT') rather than being silently dropped to a no-op. 0 still means "disabled".

Tests added in apps/desktop/src/main/generation-ipc.test.ts:

  • clamps very large timeout values to Node setTimeout int32 cap so the abort does not fire immediately
  • throws PREFERENCES_INVALID_TIMEOUT when the timeout value is NaN
  • throws PREFERENCES_INVALID_TIMEOUT when the timeout value is negative

pnpm test (334 desktop tests) and pnpm lint (no new errors) green. Rebased on origin/main.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • No actionable issues found in the added/modified lines of this update.

Summary

  • Review mode: follow-up after new commits
  • Re-reviewed the latest head diff and follow-up compare context; no new correctness/security/constraint violations attributable to this patch were found.
  • Residual risk/testing gap: timeout behavior is covered at helper-unit level, but there is still no end-to-end IPC/UI test asserting that GENERATION_TIMEOUT is surfaced to the user path.
  • Not found in repo/docs: docs/VISION.md, docs/PRINCIPLES.md.

Testing

  • Not run (automation)

open-codesign Bot

@hqhq1025 hqhq1025 merged commit 89a0c6d into main Apr 19, 2026
6 checks passed
@hqhq1025 hqhq1025 deleted the wt/loop-fix-generation-timeout-pref branch April 19, 2026 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant