Skip to content

AI CLI timeout knobs are env-var-only, unlike the sibling model/effort knobs (config-as-code gap) #8364

Description

@JSONbored

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

  • Four new optional .loopover.yml fields under review.ai_model.* (or the correct current
    section — verify the exact YAML path against claude_model's existing parsing before writing
    the patch): claude_timeout_ms, codex_timeout_ms, claude_first_output_timeout_ms,
    codex_first_output_timeout_ms.
  • AiRunOptions and the four resolve*TimeoutMs functions in src/selfhost/ai.ts accept and
    prefer the per-repo override over the env-var default.
  • config-lint.ts recognizes the four new fields (no false "unknown field" warning).
  • Unit tests covering: override present → override value used; override absent → existing
    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/** 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:featureGittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions