Skip to content

fix(claude): preserve generated subagent effort - #705

Merged
lidge-jun merged 3 commits into
lidge-jun:devfrom
jhste102lab:feat/claude-subagent-effort
Jul 29, 2026
Merged

fix(claude): preserve generated subagent effort#705
lidge-jun merged 3 commits into
lidge-jun:devfrom
jhste102lab:feat/claude-subagent-effort

Conversation

@jhste102lab

@jhste102lab jhste102lab commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add the validated optional setting claudeCode.subagentEffort (low | medium | high | xhigh | max) and render it into every generated ocx-* Claude Code agent definition.
  • Preserve exact routed-agent effort through an owned ocx-effort system directive. Claude Code 2.1.220 collapses custom-agent xhigh and max frontmatter into thinking.budget_tokens=31999; the routed inbound boundary restores the configured output_config.effort before translation.
  • Keep unset behavior backward-compatible: no frontmatter/directive is emitted and the agent inherits the parent session effort.
  • Reject invalid values at config write/import boundaries, while safely ignoring only an invalid persisted optional value so providers, account pools, keys, and listen settings remain available.
  • Document the configuration in English and Russian reference pages.

Closes #704.

Live verification

A fresh Claude Code 2.1.220 session was launched through the patched source and dispatched the generated ocx-deepseek-v4-pro agent with a deterministic arithmetic prompt.

Sanitized observed result:

Generated model: claude-ocx-opencode-go--deepseek-v4-pro
Generated effort: max
Subagent response: 42
Provider/model: opencode-go/deepseek-v4-pro
Requested effort: max
Effective effort: max
Outbound wire: reasoning_effort=max
HTTP status: 200

Before restoring the owned effort directive, the same live path produced requestedEffort=high and effectiveEffort=high, confirming that frontmatter generation alone was insufficient on Claude Code 2.1.220.

No credentials, account identifiers, request IDs, absolute user paths, or raw payloads are included.

Verification

  • bun test --cwd <worktree> ./tests/claude-agents-inject.test.ts ./tests/config.test.ts ./tests/claude-inbound.test.ts ./tests/claude-messages-endpoint.test.ts — 160 passed, 0 failed
  • bun run test — 5964 passed, 1 skipped, 0 failed across 429 files
  • bun run typecheck — passed
  • bun run privacy:scan — passed
  • bun run --cwd gui lint — passed
  • bun run --cwd docs-site build — 146 pages built
  • git diff --check — passed
  • Fresh Claude Code → OpenCodex → opencode-go/deepseek-v4-pro live smoke — HTTP 200 with requested/effective max
  • GUI production build and dashboard HTML smoke — passed

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added claudeCode.subagentEffort (low, medium, high, xhigh, max) to control effort for generated Claude Code subagents.
    • Generated agent definitions now include effort metadata, and inbound requests can apply it.
  • Bug Fixes
    • Invalid subagentEffort values are rejected/ignored with warnings and sanitized in saved configuration.
    • Effort directives are handled reliably so effort levels aren’t lost after budget collapsing.
  • Documentation
    • Updated configuration references (English/Russian) with regeneration guidance.
  • Tests
    • Added coverage for effort directive extraction/behavior and config validation/sanitization.

Add a validated claudeCode.subagentEffort setting and render it into generated ocx agent frontmatter. Claude Code 2.1.220 collapses xhigh and max custom-agent effort to a legacy thinking budget, so carry the exact value through an owned system directive and restore it at the routed inbound boundary. Invalid persisted values degrade without discarding unrelated provider or account configuration.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the bug Something isn't working label Jul 29, 2026
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds validated claudeCode.subagentEffort configuration, renders it into generated Claude Code agents, extracts the corresponding directive from inbound requests, and restores the exact effort in routed Anthropic output configuration.

Changes

Claude Code subagent effort

Layer / File(s) Summary
Configuration contract and validation
src/types.ts, src/config.ts, tests/config.test.ts, tests/config-user-edits.test.ts, docs-site/src/content/docs/reference/configuration.md, docs-site/src/content/docs/ru/reference/configuration.md
Adds supported effort values, validates candidate configurations, normalizes invalid persisted values without removing unrelated settings, emits diagnostics, protects config saves, and documents inheritance and regeneration behavior.
Generated agent effort metadata
src/claude/agents-inject.ts, tests/claude-agents-inject.test.ts
Propagates configured effort to roster and self-agent definitions, renders YAML frontmatter and an ocx-effort directive, and verifies unset and regeneration behavior.
Inbound effort restoration
src/claude/inbound.ts, src/server/claude-messages.ts, tests/claude-inbound.test.ts, tests/claude-messages-endpoint.test.ts
Extracts supported effort directives and applies them as output_config.effort, removing collapsed thinking configuration; tests cover malformed directives and exact xhigh/max restoration.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: lidge-jun, wibias, ingwannu

Sequence Diagram(s)

sequenceDiagram
  participant ClaudeCode
  participant GeneratedAgent
  participant OpenCodex
  participant Provider
  ClaudeCode->>GeneratedAgent: load model, effort, and ocx directives
  GeneratedAgent->>ClaudeCode: send routed subagent request
  ClaudeCode->>OpenCodex: send system ocx-route and ocx-effort directives
  OpenCodex->>OpenCodex: set output_config.effort and remove thinking
  OpenCodex->>Provider: forward exact reasoning_effort
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: preserving generated Claude Code subagent effort.
Linked Issues check ✅ Passed The code, docs, and tests implement #704 by validating subagent effort, preserving exact xhigh/max, and keeping unset inheritance.
Out of Scope Changes check ✅ Passed The changes stay focused on Claude Code subagent effort; no clearly unrelated behavior changes stand out from the summary.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/claude-messages-endpoint.test.ts`:
- Around line 785-805: Extend the endpoint regression coverage beside the
existing max-effort test to send an ocx-effort directive of xhigh through the
same request path, then assert the captured upstream request preserves
reasoning_effort as xhigh. Keep the existing max assertion and test setup
pattern unchanged so anthropicToResponsesTranslation is verified for both
distinct effort tiers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9a89848d-0c0f-46a0-94aa-49d3cb5ba134

📥 Commits

Reviewing files that changed from the base of the PR and between cfc50fb and 5635eda.

📒 Files selected for processing (11)
  • docs-site/src/content/docs/reference/configuration.md
  • docs-site/src/content/docs/ru/reference/configuration.md
  • src/claude/agents-inject.ts
  • src/claude/inbound.ts
  • src/config.ts
  • src/server/claude-messages.ts
  • src/types.ts
  • tests/claude-agents-inject.test.ts
  • tests/claude-inbound.test.ts
  • tests/claude-messages-endpoint.test.ts
  • tests/config.test.ts

Comment thread tests/claude-messages-endpoint.test.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5635eda700

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/claude-messages.ts Outdated
Comment on lines +544 to +545
anthropicBody.output_config = { effort: effortOverride };
delete anthropicBody.thinking;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve legacy thinking before native passthrough

When a generated ocx-self agent targets a raw model such as claude-haiku-4-5 or claude-sonnet-4-* and the request carries a native Anthropic credential, wantsNativePassthrough runs after this block. These lines unconditionally delete the valid legacy thinking: { type: "enabled", budget_tokens: ... } control and replace it with the adaptive-only effort field, even though src/adapters/anthropic.ts explicitly retains the legacy budget shape for these older families. Such native subagent turns therefore lose extended thinking or are rejected rather than preserving the configured effort; restrict this restoration to translated routed requests or compile the model-appropriate native thinking shape before passthrough.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 6d5bea1. Added focused regression coverage; 174 targeted tests and the full 5966-pass suite completed with 0 failures. Typecheck, privacy scan, and diff check also passed.

Comment thread src/config.ts
warnDegradedClaudeSubagentEffort(parsed);
warnDegradedNativeSubagentConfig(parsed, config);
return normalizeNativeSubagentSync(config, parsed);
return normalizeClaudeSubagentEffort(normalizeNativeSubagentSync(config, parsed), parsed);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep invalid effort normalized across live saves

When persisted config.json contains an invalid optional value such as claudeCode.subagentEffort: "ultra", this return removes it only from the loaded object while leaving the raw file unchanged. startServer then arms the Claude subtree baseline from that normalized object, so the first unrelated saveConfigPreservingClaudeCode call treats the raw invalid subtree as a later hand edit and copies it back into the live config; a subsequent management agent sync can consequently render unsupported effort frontmatter despite the promised safe-ignore behavior. Normalize the persisted subtree used by the hand-edit comparison or persist the cleaned value before arming the baseline.

AGENTS.md reference: src/AGENTS.md:L10-L10

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 6d5bea1. Added focused regression coverage; 174 targeted tests and the full 5966-pass suite completed with 0 failures. Typecheck, privacy scan, and diff check also passed.

Comment thread src/config.ts Outdated
function warnDegradedClaudeSubagentEffort(rawParsed: unknown): void {
const rawEffort = rawClaudeSubagentEffort(rawParsed);
if (rawEffort !== undefined && !isClaudeSubagentEffort(rawEffort)) {
console.warn(`⚠️ config.json claudeCode.subagentEffort ${JSON.stringify(rawEffort)} is invalid (expected ${CLAUDE_SUBAGENT_EFFORTS.join(", ")}) — ignoring it. Other settings were preserved.`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Do not log the invalid effort value

When a malformed persisted value is itself a credential—for example, an API key accidentally pasted into claudeCode.subagentEffort—this warning writes the complete secret to service logs via JSON.stringify(rawEffort). The value is unnecessary for diagnosing the allowed-value error and can also be an object containing credentials, so report only its type or omit it entirely rather than serializing untrusted configuration content.

AGENTS.md reference: AGENTS.md:L109-L115

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 6d5bea1. Added focused regression coverage; 174 targeted tests and the full 5966-pass suite completed with 0 failures. Typecheck, privacy scan, and diff check also passed.

jhste102lab and others added 2 commits July 29, 2026 22:32
Extend the routed endpoint regression to verify that the shared 31,999-token Claude Code budget preserves both xhigh and max as distinct outbound effort tiers.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/config.test.ts (1)

167-195: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Restore the warning spy in a finally block.

If any assertion fails, warnSpy.mockRestore() is skipped and later tests run with console.warn still mocked, masking diagnostics or causing order-dependent failures.

Proposed fix
   const invalidEffort = "credential-like-value";
   const warnSpy = spyOn(console, "warn").mockImplementation(() => {});
-  writeConfig({
+  try {
+    writeConfig({
       // ...
-  });
+    });
 
-  const config = loadConfig();
-  const diagnostics = readConfigDiagnostics();
-  // assertions...
-  warnSpy.mockRestore();
+    const config = loadConfig();
+    const diagnostics = readConfigDiagnostics();
+    // assertions...
+  } finally {
+    warnSpy.mockRestore();
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/config.test.ts` around lines 167 - 195, Wrap the assertions and
diagnostic checks following the warnSpy setup in a try/finally block, and move
warnSpy.mockRestore() into the finally clause. Preserve all existing
expectations while ensuring console.warn is restored even when a test assertion
fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/config.test.ts`:
- Around line 167-195: Wrap the assertions and diagnostic checks following the
warnSpy setup in a try/finally block, and move warnSpy.mockRestore() into the
finally clause. Preserve all existing expectations while ensuring console.warn
is restored even when a test assertion fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2052c03e-911b-4a3a-b81b-ffb6a76580ac

📥 Commits

Reviewing files that changed from the base of the PR and between b67ddb9 and 6d5bea1.

📒 Files selected for processing (5)
  • src/config.ts
  • src/server/claude-messages.ts
  • tests/claude-messages-endpoint.test.ts
  • tests/config-user-edits.test.ts
  • tests/config.test.ts

lidge-jun added a commit that referenced this pull request Jul 29, 2026
@lidge-jun
lidge-jun merged commit c0ad57a into lidge-jun:dev Jul 29, 2026
5 checks passed
lidge-jun added a commit that referenced this pull request Jul 29, 2026
…ion hardening, Windows scheduler fixes

Promotes dev to main for the v2.7.43 quick-fix release:
- fix(gui): renew loopback dashboard sessions silently instead of prompting
- PR #705 + follow-up: Claude effort restoration boundary hardening
- PRs #694/#698/#699 and Windows scheduler compare/denial fixes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants