Context
This repo has a config-as-code mandate: env vars are bootstrap-only, and per-repo-overridable
settings belong in .loopover.yml's manifest, not env-var-only. src/selfhost/ai.ts already
follows this for the AI-review model and effort knobs: review.ai_model.claude_model,
claude_effort, codex_model, codex_effort are parsed by
packages/loopover-engine/src/focus-manifest.ts:1002-1032 and threaded through AiRunOptions
(claudeModel/claudeEffort/codexModel/codexEffort) into createClaudeCodeAi/createCodexAi.
The sibling timeout knobs never got the same treatment. resolveClaudeCliTimeoutMs,
resolveCodexCliTimeoutMs, resolveClaudeFirstOutputTimeoutMs, and
resolveCodexFirstOutputTimeoutMs (src/selfhost/ai.ts:204-258) read
CLAUDE_AI_TIMEOUT_MS/CODEX_AI_TIMEOUT_MS/CLAUDE_AI_FIRST_OUTPUT_TIMEOUT_MS/
CODEX_AI_FIRST_OUTPUT_TIMEOUT_MS directly from parentEnv with no per-repo override path — there
is no options.claudeTimeoutMs-style parameter anywhere in AiRunOptions, and
packages/loopover-engine/src/focus-manifest.ts has no timeout_ms-related parsing at all (verified
via a full-file grep).
This means every repo on a shared self-host instance is forced to the operator's single global
timeout, even though the model/effort choice — which materially affects how long a call should
reasonably take — is already per-repo-configurable.
Requirements
- Add four new optional fields to
SelfHostAiModelConfig (or wherever claude_model/claude_effort
are currently defined in packages/loopover-engine/src/focus-manifest.ts), matching the existing
naming convention: claude_timeout_ms, codex_timeout_ms, claude_first_output_timeout_ms,
codex_first_output_timeout_ms.
- Thread these through
AiRunOptions in src/selfhost/ai.ts (e.g. claudeTimeoutMs,
codexTimeoutMs, claudeFirstOutputTimeoutMs, codexFirstOutputTimeoutMs), exactly mirroring how
claudeModel/claudeEffort are already threaded through.
resolveClaudeCliTimeoutMs/resolveCodexCliTimeoutMs/resolveClaudeFirstOutputTimeoutMs/
resolveCodexFirstOutputTimeoutMs must accept the per-repo override and use it when present,
falling back to the existing env-var/effort-based resolution when absent — the env var stays the
global default, this is additive, not a replacement.
- Every new manifest field must be added to
config-lint.ts's recognized-fields validation (check
whether claude_model/claude_effort are already listed there and mirror that entry) so the new
fields don't trigger a spurious "unknown field" warning.
Deliverables
Test Coverage Requirements
This repo's Codecov patch gate is 99%+ of changed lines and branches (src/** and
packages/loopover-engine/src/** are both covered). Every new manifest-parsing branch and every new
"override present vs absent" branch in the four resolver functions must be covered by both true/false
cases.
Expected Outcome
A repo can set its own AI-call timeout budget via .loopover.yml, independent of the self-host
operator's global env-var default, matching the same per-repo-override pattern already available for
model and effort selection.
Links & Resources
src/selfhost/ai.ts:204-258 — the four resolver functions to extend
packages/loopover-engine/src/focus-manifest.ts:1002-1032 — the existing claude_model/
claude_effort parsing pattern to mirror
src/queue/ai-review-orchestration.ts:691 — an example of how AiRunOptions fields get consumed
end-to-end
Context
This repo has a config-as-code mandate: env vars are bootstrap-only, and per-repo-overridable
settings belong in
.loopover.yml's manifest, not env-var-only.src/selfhost/ai.tsalreadyfollows this for the AI-review model and effort knobs:
review.ai_model.claude_model,claude_effort,codex_model,codex_effortare parsed bypackages/loopover-engine/src/focus-manifest.ts:1002-1032and threaded throughAiRunOptions(
claudeModel/claudeEffort/codexModel/codexEffort) intocreateClaudeCodeAi/createCodexAi.The sibling timeout knobs never got the same treatment.
resolveClaudeCliTimeoutMs,resolveCodexCliTimeoutMs,resolveClaudeFirstOutputTimeoutMs, andresolveCodexFirstOutputTimeoutMs(src/selfhost/ai.ts:204-258) readCLAUDE_AI_TIMEOUT_MS/CODEX_AI_TIMEOUT_MS/CLAUDE_AI_FIRST_OUTPUT_TIMEOUT_MS/CODEX_AI_FIRST_OUTPUT_TIMEOUT_MSdirectly fromparentEnvwith no per-repo override path — thereis no
options.claudeTimeoutMs-style parameter anywhere inAiRunOptions, andpackages/loopover-engine/src/focus-manifest.tshas notimeout_ms-related parsing at all (verifiedvia a full-file grep).
This means every repo on a shared self-host instance is forced to the operator's single global
timeout, even though the model/effort choice — which materially affects how long a call should
reasonably take — is already per-repo-configurable.
Requirements
SelfHostAiModelConfig(or whereverclaude_model/claude_effortare currently defined in
packages/loopover-engine/src/focus-manifest.ts), matching the existingnaming convention:
claude_timeout_ms,codex_timeout_ms,claude_first_output_timeout_ms,codex_first_output_timeout_ms.AiRunOptionsinsrc/selfhost/ai.ts(e.g.claudeTimeoutMs,codexTimeoutMs,claudeFirstOutputTimeoutMs,codexFirstOutputTimeoutMs), exactly mirroring howclaudeModel/claudeEffortare already threaded through.resolveClaudeCliTimeoutMs/resolveCodexCliTimeoutMs/resolveClaudeFirstOutputTimeoutMs/resolveCodexFirstOutputTimeoutMsmust accept the per-repo override and use it when present,falling back to the existing env-var/effort-based resolution when absent — the env var stays the
global default, this is additive, not a replacement.
config-lint.ts's recognized-fields validation (checkwhether
claude_model/claude_effortare already listed there and mirror that entry) so the newfields don't trigger a spurious "unknown field" warning.
Deliverables
.loopover.ymlfields underreview.ai_model.*(or the correct currentsection — verify the exact YAML path against
claude_model's existing parsing before writingthe patch):
claude_timeout_ms,codex_timeout_ms,claude_first_output_timeout_ms,codex_first_output_timeout_ms.AiRunOptionsand the fourresolve*TimeoutMsfunctions insrc/selfhost/ai.tsaccept andprefer the per-repo override over the env-var default.
config-lint.tsrecognizes the four new fields (no false "unknown field" warning).env-var/effort-based resolution used (both branches must be covered per this repo's
branch-counted Codecov gate).
Test Coverage Requirements
This repo's Codecov patch gate is 99%+ of changed lines and branches (
src/**andpackages/loopover-engine/src/**are both covered). Every new manifest-parsing branch and every new"override present vs absent" branch in the four resolver functions must be covered by both true/false
cases.
Expected Outcome
A repo can set its own AI-call timeout budget via
.loopover.yml, independent of the self-hostoperator's global env-var default, matching the same per-repo-override pattern already available for
model and effort selection.
Links & Resources
src/selfhost/ai.ts:204-258— the four resolver functions to extendpackages/loopover-engine/src/focus-manifest.ts:1002-1032— the existingclaude_model/claude_effortparsing pattern to mirrorsrc/queue/ai-review-orchestration.ts:691— an example of howAiRunOptionsfields get consumedend-to-end