Skip to content

fix(cli): wire the role-effort catalog into the CLI/server on-ramp - #958

Merged
seonghobae merged 10 commits into
mainfrom
fix/hollow-path-role-effort-catalog-cli
Sep 1, 2026
Merged

fix(cli): wire the role-effort catalog into the CLI/server on-ramp#958
seonghobae merged 10 commits into
mainfrom
fix/hollow-path-role-effort-catalog-cli

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • TaskOrchestrator has accepted role_effort_catalog (ADR 0021 / issue [Product Gap] Provider-neutral role reasoning-effort profiles with equal-budget ablation #568) since it was added, but no caller in contextual_orchestrator/__main__.py ever passed a non-None value — the CLI/server entrypoint had no --role-effort-catalog flag and its TaskOrchestrator(...) construction never set the kwarg. apply_effort_profile therefore always ran its profile=None no-op branch in production, and the whole reasoning_effort_profile subsystem (temperature/top_p/seed/reasoning_effort injection, fail-closed provider-support handling, the replayable reasoning_effort_snapshot) was correct, unit-tested, and Atheris-fuzzed but structurally unreachable from the shipped CLI/server.
  • Adds a real, explicit opt-in on-ramp: --role-effort-catalog default loads default_role_effort_catalog() and passes it into the TaskOrchestrator constructed by main(). Omitting the flag (the default) keeps role_effort_catalog=None, so every existing payload is byte-for-byte unchanged and the locked route/conduct selection defaults (production_default_change_allowed) are untouched — this only wires the catalog's own additive per-role sampling/effort/snapshot fields, exactly the "opt-in" ADR 0021 already claims exists.

Why this direction over the alternative

The finding's other suggested direction — downgrading ADR 0021 / the orchestrator.py:3427 "Operator next action" comment to admit the subsystem is unreachable — would have been the wrong fix here: docs/doctoring/reasoning-effort-profile.md already frames default_role_effort_catalog() as usable "for an evaluation or an explicitly configured deployment." A CLI flag is that explicitly-configured-deployment on-ramp, so adding it makes the existing docs/ADR true instead of rewriting them to describe a permanent gap.

Changes

  • contextual_orchestrator/__main__.py: import default_role_effort_catalog; add --role-effort-catalog {default} (default None); pass role_effort_catalog=default_role_effort_catalog() if args.role_effort_catalog == "default" else None into the TaskOrchestrator(...) construction in main().
  • tests/test_cli_role_effort_catalog.py (new): exercises main() end-to-end against the real TaskOrchestrator and examples/agents.mock.json — confirms --role-effort-catalog default reaches the constructor and produces a reasoning_effort_snapshot in the CLI's JSON output; confirms omitting the flag keeps role_effort_catalog=None and the payload snapshot-free; confirms an unknown catalog name fails closed (SystemExit(2)).
  • CHANGELOG.md: ### Fixed entry under [0.2.0] - Unreleased.

Developer experience

python -m contextual_orchestrator "hi" --role-effort-catalog default now returns a reasoning_effort_snapshot (profile_version + snapshot_hash + role_profiles) alongside the normal route/conduct payload; omitting the flag reproduces today's exact output.

User experience

No default-path behavior change for existing operators. Operators who want the issue #568 per-role reasoning-effort catalog (temperature/top_p/seed/max_tokens per workflow role, native reasoning_effort only where a provider proves support, fail-closed on unproven support) now have a documented, testable way to turn it on, instead of a constructor kwarg nothing ever set.

Test plan

  • ruff check contextual_orchestrator/__main__.py tests/test_cli_role_effort_catalog.py
  • python -m pytest -q tests/test_cli_role_effort_catalog.py tests/test_cli_auth.py (new tests + existing __main__.py CLI coverage)
  • python -m interrogate -v . (100%, unchanged)
  • Full python -m pytest -q (Python 3.12, matching CI): 2834 passed, 1 skipped, 1 failed. The
    one failure, tests/test_psychometric_routing.py::test_fast_mlsirm_fit_uses_judge_acceptance_item_for_context_score,
    is the pre-existing ModuleNotFoundError: No module named 'fast_mlsirm' sandbox gap
    documented in docs/product-technical-gap-baseline.md ("unreachable in this sandbox, same
    documented blocker as PR fix(rater): preserve generic v1 and add criterion-bound wrapper #917, not a regression") — the private fast-mlsirm archive isn't
    fetchable from this sandbox; unrelated to this change.

Generated by Claude Code

Summary by CodeRabbit

  • 새 기능

    • CLI에 --role-effort-catalog default 옵션을 추가했습니다.
    • 옵션을 사용하면 역할별 reasoning 설정과 재현 가능한 reasoning 스냅샷이 응답에 반영됩니다.
  • 버그 수정

    • 기존 CLI에서 역할별 reasoning 카탈로그가 적용되지 않던 문제를 해결했습니다.
    • 옵션을 생략하면 기존 응답 형식과 동작을 그대로 유지합니다.
    • 지원하지 않는 옵션 값은 명확한 오류로 거부됩니다.
  • 문서

    • 변경 사항을 변경 로그에 추가했습니다.

TaskOrchestrator has accepted role_effort_catalog (ADR 0021 / issue #568)
since it was added, but __main__.py never passed a non-None value: there was
no --role-effort-catalog flag, and the TaskOrchestrator(...) constructed in
main() never set the kwarg. apply_effort_profile therefore always ran its
profile=None no-op branch in the shipped CLI/server, leaving the whole
reasoning_effort_profile subsystem (temperature/top_p/seed/reasoning_effort
injection, fail-closed provider-support handling, the replayable
reasoning_effort_snapshot) unreachable in production despite being correct,
unit-tested, and fuzzed.

Add --role-effort-catalog default, which loads default_role_effort_catalog()
and passes it into the constructed TaskOrchestrator. Omitting the flag (the
default) keeps role_effort_catalog=None, so every existing payload is
unchanged and the locked route/conduct selection defaults stay locked -- this
only wires the catalog's own additive, already-opt-in fields.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Gs7KmNvH75nxz1sL8mKjw
@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 48 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 982fa327-51fb-475a-8705-2e597563e2b8

📥 Commits

Reviewing files that changed from the base of the PR and between 8e0c53f and 2ce2a4a.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • contextual_orchestrator/__main__.py
  • contextual_orchestrator/orchestrator.py
  • tests/test_cli_role_effort_catalog.py
  • tests/test_generated_workflow.py
  • tests/test_mixed_pool_role_effort_selection.py
  • tests/test_model_discovery.py
  • tests/test_openai_passthrough.py
  • tests/test_passthrough_provider_failover.py
  • tests/test_tool_loop_role_effort_catalog_http.py
📝 Walkthrough

Walkthrough

--role-effort-catalog default CLI 옵션을 추가했습니다. 옵션을 사용하면 기본 role-effort catalog를 TaskOrchestrator에 전달하고 reasoning-effort snapshot을 생성합니다. 옵션을 생략하면 기존 동작을 유지합니다.

Changes

역할별 effort catalog CLI

Layer / File(s) Summary
CLI 옵션 및 catalog 연결
contextual_orchestrator/__main__.py
--role-effort-catalog default 옵션을 추가했습니다. 옵션이 설정되면 default_role_effort_catalog()TaskOrchestrator에 전달합니다.
CLI 동작 검증 및 변경 기록
tests/test_cli_role_effort_catalog.py, CHANGELOG.md
기본 catalog 전달, 옵션 생략 시 기존 응답 유지, 잘못된 값 거부를 테스트합니다. 변경 내용을 changelog에 기록합니다.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: 🔵 Low · up to 8e0c5

The change preserves existing behavior unless operators explicitly enable the built-in catalog, but enabled server processes may reject affected requests when provider capability metadata is missing; the CLI help also needs a minor field-name correction. The PR is mergeable with explicit owner awareness.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 CLI에 role-effort catalog를 연결하는 핵심 변경을 정확히 설명합니다. 간결하고 구체적입니다.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. (1 skipped: 1 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/hollow-path-role-effort-catalog-cli

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@seonghobae
seonghobae marked this pull request as ready for review August 31, 2026 08:19
devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

…r support

Devin Review flagged that default_role_effort_catalog() uses
unsupported_provider_fallback="abstain" for every role, while ordinary
real-provider agent configs (examples/agents.openai.json) and every
auto-discovered agent never set reasoning_effort_supported. Confirmed by
direct repro against ModelClient.apply_effort_profile: with such a pool,
--role-effort-catalog default constructed successfully and then raised
EffortProfileError on every subsequent request -- the CLI's own new test
only exercised the mock:// pool, which auto-passes the support check.

Per ADR 0021's fail-closed design and the doctoring note that the catalog
is for "an evaluation or an explicitly configured deployment," reject the
flag at startup instead of loosening the catalog's default: extract the
duplicated support check into agent_proves_reasoning_effort_support() and
use it in a new _require_eligible_role_effort_agents() gate, run right
after the agent pool (including --auto-discover-model-agents) is
finalized, before any request or server bind. Covers both the one-shot
CLI prompt and --serve.

Also fixes a CodeRabbit nit: the flag's help text said "max_tokens" where
it meant ReasoningEffortProfile's actual field name, max_output_tokens.

Adds three regression tests (non-mock unknown-support pool rejected in
CLI and --serve mode; non-mock pool with explicit
reasoning_effort_supported: true allowed) and points the CHANGELOG's
existing entry at docs/architecture.md's existing Fugu/TRINITY/Conductor
citation for reasoning_effort_profile.

Full suite: 2837 passed, 1 skipped (pre-existing fast_mlsirm sandbox gap,
same as PR #958 baseline), 100% interrogate, ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Gs7KmNvH75nxz1sL8mKjw
devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

noema-review failed on this head (f82fa28) with the same class of infra failure already seen on PR #965 in this repo — a raw network TimeoutError connecting to the local orchestrator sidecar during the LLM call, not related to this PR's diff:

File "scripts/ci/noema_review_gate.py", line 656, in call_llm
    with opener.open(request, timeout=120) as response:
...
TimeoutError: timed out

Likely load contention from the burst of PRs across this org marked ready for review in the last ~45 minutes. Re-running the failed job once.


Generated by Claude Code

Devin's round-2 finding on PR #958: _require_eligible_role_effort_agents
only proves *some* agent in the pool supports reasoning_effort. Ordinary
role-based selection (route_once, conduct, stream_route, batch_route,
structured-synthesis passthrough) could still rank or select an
*unsupported* agent from a mixed pool ahead of a supported one.
route_once/conduct recovered by accident via _invoke's generic
tool-failure failover; stream_route/batch_route call the provider
directly with no such recovery and would raise EffortProfileError
outright.

TaskOrchestrator._ranked_agents now narrows role-based candidates to
agents that prove reasoning_effort support whenever the role's
role_effort_catalog entry fails closed, via a new shared
_eligible_role_effort_candidates helper (falls back to the unfiltered
set when none prove support, so that edge case still gets a clear
attempt instead of a silently emptied list). This mirrors, and now
shares code with, the equivalent filter proxy_completion already
applied for its own caller-supplied effort profile.

Also documents (CHANGELOG + docstrings on remove_agent/patch_agent)
Devin's informational finding: runtime pool mutations are real
(POST/PATCH/DELETE .../worker_agents) and are not revalidated against
an active catalog -- confirmed real, left as a documented known
limitation per this PR's own narrow scope rather than a full
runtime-revalidation fix.

Adds tests/test_mixed_pool_role_effort_selection.py: a mixed pool
(one unsupported agent ranked first by priority, one supported) never
dispatches to the unsupported agent across route, conduct, stream, and
batch, plus a control test proving an "omit"-fallback role is left
unfiltered.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Gs7KmNvH75nxz1sL8mKjw
devin-ai-integration[bot]

This comment was marked as resolved.

…effort profile

PR #958 round-3 Devin findings:

- _require_eligible_role_effort_agents now proves eligibility per active
  fail-closed role using the same general-chat (_is_general_chat_agent) and
  provider_exclusions rules TaskOrchestrator._ranked_agents/_select_agent
  already apply at request time, reusing agent_proves_reasoning_effort_support
  and _eligible_role_effort_candidates from round 2 rather than new logic. A
  pool whose only proving agent is non-chat (e.g. embedding-only) or excluded
  from every active role is now rejected at startup instead of passing and
  failing every request later.
- The startup rejection path now closes the already-constructed
  TaskOrchestrator before calling parser.error() (which raises SystemExit),
  so an embedded caller of main() gets clean resource teardown instead of
  relying on process exit.
- TaskOrchestrator.proxy_completion's single-agent passthrough (the server's
  tool_loop call site, and any other caller that omits effort_profile) now
  defaults an unset effort_profile to the opted-in role_effort_catalog's
  "worker" entry -- the role every selection/failover call in that method
  already uses -- mirroring the existing
  `effort_profile or self._role_effort_profile("synthesizer")` pattern
  _orchestrated_provider_completion already applies for its own role. A
  caller that passes its own effort_profile, or runs with no catalog
  configured, is unaffected.

Adds two startup regression tests (non-chat-only prover; sole prover
excluded from every role) and an end-to-end HTTP test driving a real
tool-loop request through the server and asserting the resolved upstream
payload carries the catalog's worker-role sampling/token/seed/reasoning
fields, plus a companion test proving an unconfigured catalog stays a no-op.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Gs7KmNvH75nxz1sL8mKjw

Copy link
Copy Markdown
Contributor Author

Round-3 Devin findings addressed in 160e8540 (pushed to this branch). GitHub's review-thread GraphQL API stayed rate-limited for this account throughout this session (used across concurrent sessions on this org), so I couldn't fetch thread IDs to reply/resolve the three inline threads directly — posting the fix summary here instead; happy to reply-in-thread and resolve once the quota clears.

1. 🔴 "Startup accepts unusable provider pools" (__main__.py lines 99-100) — Fixed. _require_eligible_role_effort_agents previously only checked "does any enabled agent anywhere prove reasoning_effort support" — too weak, since TaskOrchestrator._ranked_agents/_select_agent only ever offer a role a general-chat agent (_is_general_chat_agent) that isn't excluded from that role via provider_exclusions. It now reapplies those same two rules per active fail-closed role, reusing round 2's agent_proves_reasoning_effort_support/_eligible_role_effort_candidates rather than new logic (deliberately does not reapply _zdr_agent_allowed, which is per-request state, not a static pool property). A pool whose only proving agent is non-chat (e.g. embedding-only) or excluded from every active role is now rejected at startup instead of passing and failing every request later. Added the two startup tests you asked for: test_role_effort_catalog_default_rejects_pool_whose_only_prover_is_non_chat and test_role_effort_catalog_default_rejects_pool_whose_sole_prover_is_role_excluded in tests/test_cli_role_effort_catalog.py.

2. 🔍 "Rejected startup relies on process cleanup" (lines 683-684) — Fixed. _require_eligible_role_effort_agents now calls the already-constructed TaskOrchestrator.close() right before parser.error() (which raises SystemExit), so an embedded caller of main() that catches that SystemExit gets clean resource teardown instead of relying on process exit.

3. 🟡 "Tool requests bypass effort profiles" (lines 676-678) — Investigated first, per your framing: confirmed this is the local fix you suggested, not a multi-call-site change. _orchestrated_provider_completion already had effort_profile or self._role_effort_profile("synthesizer") as its own fallback; proxy_completion's single-agent passthrough (the server's tool_loop call site, and every other caller that omits effort_profile, e.g. cost_router.py) had no equivalent. It now defaults an unset effort_profile to self._role_effort_profile("worker") — the role every selection/failover call in that method already uses — mirroring the existing pattern exactly. A caller that passes its own effort_profile, or runs with no role_effort_catalog configured, is unaffected. Added the end-to-end server test you asked for: tests/test_tool_loop_role_effort_catalog_http.py, driving a real tool-loop HTTP request through the running server and asserting the resolved upstream payload carries the catalog's worker-role temperature/top_p/seed/max_tokens/reasoning_effort (plus a companion test proving an unconfigured catalog stays a no-op).

Validation: full python -m pytest tests -q2846 passed, 2 failed (both pre-existing sandbox gaps unrelated to this diff — fast_mlsirm not installed here, documented in docs/product-technical-gap-baseline.md; and an mcp.Client attribute mismatch in test_privacy_policy_analysis.py, unrelated to role-effort/proxy_completion code). ruff check and interrogate (100%) clean on the changed files; tests/test_conventions.py passes.


Generated by Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Pausing further fixes on this PR here — this is round 4 of Devin review findings in the same widening area (role-effort-catalog integration across startup validation, routing, failover, and now passthrough/synthesis), and each round has kept surfacing new issues rather than converging (round 1: 1 bug; round 2: 1 bug; round 3: 2 bugs; round 4: 1 bug + 2 informational). That's the standing signal to stop iterating and report rather than keep expanding this PR's scope.

Status of round 4's findings:

  • 🟡 Real, confirmed by inspection: "Explicit omit profile is ignored." _ranked_agents filters out effort-unsupported candidates against the catalog's configured fallback before proxy_completion gets a chance to apply a caller-supplied explicit effort_profile override (e.g. unsupported_provider_fallback='omit'). A caller who explicitly opts out of the fail-closed behavior for one call still can't reach an unsupported agent, because ranking already dropped it. This is a real edge case, but only affects callers that pass an explicit override — the default (catalog-driven) behavior this PR is actually about is correct.
  • 🔍 Informational: "Planner advertises filtered agents" — the generated workflow's plan can still name an effort-unsupported/role-excluded agent even though it will actually fail over past it. Cosmetic (assignment succeeds via failover), not a correctness bug.
  • 🔍 Informational: "Tests encode review history" — fair nit; the mixed-pool test docstrings added across rounds 2-3 reference "PR round" context instead of describing durable behavior. Noted for whoever does the follow-up.

What's already fixed and validated across rounds 1-3 (all confirmed resolved by Devin's own follow-up passes): the original hollow-path finding (CLI wiring for --role-effort-catalog default), the default-catalog-rejects-every-request bug, the mixed-pool routing bug (route/conduct/stream/batch/synthesis all now correctly filter to eligible agents), the startup guard's chat/exclusion eligibility gap, and the tool-loop profile-propagation gap. Full suite is green (2842+ passed) with 100% interrogate coverage.

Recommend a narrowly-scoped follow-up PR for the explicit-override-bypass edge case rather than extending this one further, since it requires threading the per-call profile into candidate ranking — a small but distinct change from what this PR is about. Leaving this PR as-is for review/merge on its actual scope (the CLI wiring + the request-time bugs that would affect every default-catalog user, not just explicit-override callers).


Generated by Claude Code

seonghobae and others added 3 commits September 1, 2026 01:06
… selection

Devin review on PR #958 (contextual_orchestrator/orchestrator.py:5883-5884):
_orchestrated_provider_completion resolved its caller-supplied
effort_profile only for the final apply_effort_profile payload call, not
for the synthesizer's own selection (_select_agent), replica lookup
(_ranked_agents), or failover list (_failover_candidates) -- those three
call sites omitted effort_profile entirely and silently fell back to the
raw role_effort_catalog entry inside _ranked_agents. A fail-closed override
(unsupported_provider_fallback other than "omit") could rank/select an
unproven-support agent ahead of a proven one, and apply_effort_profile then
raised EffortProfileError outright with no failover -- unlike the identical
scenario on the plain passthrough path, which already threads its override
through every selection call site.

Resolve effort_profile or self._role_effort_profile("synthesizer") once, up
front, and pass it to all three call sites, matching the passthrough path's
existing pattern. Adds a regression test
(test_structured_synthesis_honors_explicit_effort_profile_override_for_selection)
that reproduces the bug against the pre-fix code (EffortProfileError instead
of failover) and passes against the fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Gs7KmNvH75nxz1sL8mKjw
devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Required noema-review check failure — not this PR's

Same root cause as reported on #960 and #965: the required noema-review check materializes scripts/ci/noema_review_gate.py from ContextualWisdomLab/.github's trusted main branch (still at commit 1cbb6aaf as of this writing), which still calls the LLM gateway with a raw timeout=120 on the JSON-repair retry path. When a repair retry runs slow, the hard 120s socket timeout fires before the response completes:

TimeoutError: timed out

Fix is in flight, not yet merged: ContextualWisdomLab/.github#1507 replaces the raw timeout with a deadline-bounded budget computed fresh on every attempt including the repair retry, plus a watchdog that fails closed instead of hanging. It's had 28 commits of iteration and CI is green on its current head; still finishing its own required-review cycle before the merge scheduler can pick it up.

Nothing to change in this PR itself. Holding off on a re-run since it would deterministically hit the same timeout again with the base script unchanged — will pick back up once .github#1507 merges.


Generated by Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Cross-PR integration contract: routing identity is provider-neutral model_group only; do not add or preserve a provider-family abstraction. OpenRouter discovery must retain concrete free model IDs, while the aggregate openrouter/free router is not a serving candidate. OpenCode, Noema, and Strix must call contextual-orchestrator. Do not impose fixed wall-clock deadlines on inference, initial ping, readiness/health, provider discovery, or OpenRouter ZDR-list retrieval; use explicit cancellation and evidence-backed transport failure instead. Reconcile this PR with #971 and central .github #1508 before merge.

Copy link
Copy Markdown
Contributor Author

Re: the cross-PR model_group/OpenRouter contract comment above — checked this PR's diff against #971. Real semantic overlap: both PRs restructure the identical block of TaskOrchestrator.sync_discovered_agents in contextual_orchestrator/orchestrator.py (right after updated_candidates = list(self.candidates), through the if self._pool_store is not None: save logic), each adding a differently-named new list variable and reshaping the loop differently. Both also edit test_sync_discovered_agents_adds_and_updates_idempotently in the same place.

Not a doc/text collision like the other flagged PRs — this is the same method being reshaped two different ways by two in-flight PRs. Whoever lands second will need to manually reconcile both reshapes rather than take a mechanical rebase; flagging rather than resolving it myself since it's a real design choice (which reshape of sync_discovered_agents is kept, or whether both survive in combination).


Generated by Claude Code

@seonghobae

Copy link
Copy Markdown
Contributor Author

Contextual-Orchestrator를 같이 손보든 어쩌든 해결하세요. Bypass merge 필요하면 가능 (chicken and eggs 상황이라면) + NVIDIA NIM 만 쓰는 건 허용하지 않아요. Contextual-Orchestrator를 쓰세요. Timeout은 적어도 3시간으로 잡으세요. 120초 같은 건 당황스럽군요.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Contextual-Orchestrator와 관계한 것들을 같이 손보든 어쩌든 해결하세요. Bypass merge 필요하면 가능 (chicken and eggs 상황이라면) + NVIDIA NIM 만 쓰는 건 허용하지 않아요. Contextual-Orchestrator를 쓰세요. Timeout은 적어도 3시간으로 잡으세요. 120초 같은 건 당황스럽군요. Strix가 6시간 이상 동작해서 취약점 잡는 것도 본 일이 있습니다. Opencode와 Noema 는 Coderabbitai 및 Devin 수준으로 실제로 리뷰를 하게 하시오. Strix도 보안 리뷰를 꼼꼼하게 하도록 하시오. 특히 보안 리뷰는 전체 코드로 수행하는 것입니다. Contextual-Orchestrator는 실시간으로 빠르면서 능력이 좋은 모델에 요청을 보내어 시간을 당기시오. @opencode-agent 라고 부르면 호출되는 기능도 인터넷 가이드에는 /oc 라고 나와있기 때문에 이 점도 확인해 보는 게 좋겠습니다.

Copy link
Copy Markdown
Contributor Author

noema-review failed on this head with the identical failure signature as #963: scripts/ci/noema_review_gate.py:656's opener.open(request, timeout=120) hit a raw, unhandled TimeoutError calling the LLM. That file is canonical only in ContextualWisdomLab/.githubnoema-review.yml materializes it fresh into every target repo's runner, so it can't be patched from this PR.

A fix already exists but hasn't merged yet: ContextualWisdomLab/.github#1507 removes this exact 120-second hard stop (raising the Noema LLM call budget to 4 hours) specifically because it was causing this class of crash. Once #1507 merges, this repo's next noema-review run will pick it up automatically. Watching #1507 to green; will re-check this PR once it lands.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

opencode-review also failed here with the same 90-minute dispatch-wait timeout as #967 (no APPROVED/CHANGES_REQUESTED verdict arrived from opencode-agent) — same class of review-dispatch/wake bug ContextualWisdomLab/.github#1507 fixed. Queued a re-run now that the fix is in main.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

noema-review failed on the current head (236b15e0) with:

Noema reviewed line 2 is not an exact changed-side line

This is scripts/ci/noema_review_gate.py's validate_substantive_verdict anti-hallucination guard (confirmed intentional/correct fail-closed design when it fired the same way on contextual-orchestrator#960): it rejects any formal verdict whose cited (path, line, side) doesn't exactly match a real changed-diff line, rather than trust the LLM's citation. It's not a bug in this PR's diff — the earlier failure on this head was the pre-.github#1507 raw TimeoutError crash at 120s, which is now fixed (the review ran to completion this time and reached substantive verdict validation), and this rejection is orchestrator/free's occasional line-citation imprecision, not a defect this PR introduced or can fix from inside its own diff.

Re-ran the failed job once (run 33440177537) to get a fresh sampled review pass, per the standing playbook for a legitimate-but-stochastic rejection. Will keep watching for green.


Generated by Claude Code


Generated by Claude Code

@seonghobae
seonghobae merged commit 47a0cf7 into main Sep 1, 2026
21 of 25 checks passed
@seonghobae
seonghobae deleted the fix/hollow-path-role-effort-catalog-cli branch September 1, 2026 07:19

@devin-ai-integration devin-ai-integration Bot left a comment

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.

Devin Review found 2 new potential issues.

Devin Review

updated_agents = [agent for agent in updated_candidates if not agent.disabled]
if not updated_agents:
raise ValueError("cannot disable the last enabled agent")
self._require_role_effort_pool(updated_candidates)

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.

🟡 Live supporting-provider rotation is blocked

Admin-managed providers cannot declare reasoning_effort_supported because the request allowlists reject it. patch_agent then blocks disabling the final seeded supporter, preventing live provider rotation.

Prompt for agents
Complete the runtime agent-management contract for reasoning-effort support. The HTTP create and patch allowlists in contextual_orchestrator/server.py currently reject reasoning_effort_supported, patch_agent in contextual_orchestrator/orchestrator.py does not apply it, and the API schema omits it. Allow a validated true/false/null value through create and patch operations, persist it through the existing agent-pool storage, and retain the new role-effort invariant check so an operator can add or update a replacement supporter before disabling or removing the old one. Add API-level tests for this rotation sequence.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread CHANGELOG.md
Comment on lines +285 to +304
<<<<<<< HEAD
- (Devin review on #958) `_orchestrated_provider_completion`'s structured/
Responses synthesis path (the `single_agent=False` branch of
`proxy_completion`) resolved its caller-supplied `effort_profile` override
only for the final `apply_effort_profile` payload call, not for the
synthesizer's own selection (`_select_agent`), replica lookup
(`_ranked_agents`), or failover list (`_failover_candidates`) — those three
call sites omitted `effort_profile` entirely and so silently fell back to
the raw `role_effort_catalog` entry inside `_ranked_agents`. A fail-closed
override (`unsupported_provider_fallback` other than `"omit"`) could still
rank/select an unproven-support agent ahead of a proven one, and
`apply_effort_profile` then raised `EffortProfileError` outright — with no
failover, unlike the identical scenario on the plain passthrough path,
which already threads its override through every selection call site. Now
resolves `effort_profile or self._role_effort_profile("synthesizer")` once,
up front, and passes it to all three call sites, matching the passthrough
path's existing pattern and `_ranked_agents`' own documented intent that
every role-based selection path -- "structured synthesis" included -- stay
consistent with the effort catalog's eligibility guard.
=======

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.

🔍 Unresolved changelog conflict

The release notes still contain <<<<<<<, =======, and >>>>>>> markers. Resolve the conflict before merging.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

3 participants