From 3960c2bd46adf2150e1f9d637f77be2ad0f171cc Mon Sep 17 00:00:00 2001 From: ciregenz Date: Thu, 25 Jun 2026 02:11:05 -0700 Subject: [PATCH] [eric] conventions: one-line-comment + no-gratuitous-blank rules in CLAUDE.md; apply to configure_provider_env (271->223) --- CLAUDE.md | 12 +++ backend/CLAUDE.md | 4 + .../agents/manager/configure_provider_env.py | 81 ++++--------------- frontend/CLAUDE.md | 3 + 4 files changed, 36 insertions(+), 64 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index b46bfbdb..aada0fe6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -42,6 +42,18 @@ Personal / per-machine notes go in `CLAUDE.local.md` (gitignored), **not** here. are dynamic-key maps (a registry keyed by a runtime id) and external protocol shapes (the Claude Agent SDK hook returns, `model_dump` output). +## Comments +- **Every comment is ONE physical line. No exceptions.** Never wrap a comment across multiple `#` + lines — collapse it into a single line (long is fine; multi-line is not). +- **Delete comments that aren't pulling weight.** Keep only the WHY (a non-obvious reason, gotcha, or + ambiguity); delete anything that restates the code, and delete dead/commented-out code outright. +- A module/function docstring is exempt (it's a docstring, not a `#` comment). + +## Whitespace +- **No gratuitous blank lines.** One blank line separates logical units; never stack 2+ blank lines. +- **Tight imports** — no blank lines inside an import block beyond the single separator between + stdlib / third-party / local groups. Delete any blank line that isn't doing real readability work. + When you add or change code, the files you touch must be clean under these rules. Pre-existing debt in files you are not otherwise editing is grandfathered via the linter's exception lists — do not mass-migrate untouched files. diff --git a/backend/CLAUDE.md b/backend/CLAUDE.md index 04d81f60..291abc5d 100644 --- a/backend/CLAUDE.md +++ b/backend/CLAUDE.md @@ -13,6 +13,10 @@ All conventions in the root `CLAUDE.md` apply here. Python-specific emphasis: structured data — model it. Legitimate dicts: dynamic-key registries and external protocol shapes (SDK hook returns, `model_dump` output). - **Single-purpose file naming** — a one-export file is named after its export. +- **Comments are ONE line each, no exceptions** — never wrap across multiple `#` lines; keep only + WHY/gotcha comments, delete restating or dead-code comments. Docstrings are exempt. +- **No gratuitous blank lines** — never stack 2+ blanks; keep imports tight (only the single + stdlib/third-party/local group separators). Touch a file → it must be clean under these rules. Pre-existing debt is grandfathered in `linter/config/config.json`; don't mass-migrate untouched files. diff --git a/backend/apps/agents/manager/configure_provider_env.py b/backend/apps/agents/manager/configure_provider_env.py index ee7fc177..ee5e3cec 100644 --- a/backend/apps/agents/manager/configure_provider_env.py +++ b/backend/apps/agents/manager/configure_provider_env.py @@ -1,8 +1,7 @@ """Configure the SDK environment for the run's provider route: set ANTHROPIC/OPENAI/GOOGLE auth env vars (direct key, OpenSwarm Pro proxy, OpenRouter, or 9Router) and pin subagent models, -ensuring 9Router is up where the route needs it. Lifted out of the agent loop; mutates -options_kwargs[\"env\"] in place exactly as inline. sub_conns is the active-connection list used -for subagent-model fallback (empty today).""" +ensuring 9Router is up where the route needs it. sub_conns is the active-connection list for +subagent-model fallback (empty today).""" import os from typing import Dict, List, Optional @@ -41,25 +40,14 @@ async def configure_provider_env( options_kwargs["env"] = { "ANTHROPIC_API_KEY": global_settings.anthropic_api_key, "ANTHROPIC_BASE_URL": "https://api.anthropic.com", - # Pin subagent envs so they don't drift back to the proxy. + # Pin subagents so they don't drift back to the proxy. "CLAUDE_CODE_SUBAGENT_MODEL": "claude-sonnet-4-6", "ANTHROPIC_SMALL_FAST_MODEL": "claude-haiku-4-5", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5", } logger.info(f"[MCP-DEBUG] Using direct Anthropic API key (route=api) for {session.model}") elif is_pinned_api_route and api_route_provider == "openai" and getattr(global_settings, "openai_api_key", None): - # Goes through 9Router's Anthropic→OpenAI translator like - # other own-key routes, but we point OPENAI_BASE_URL at a - # tiny local pass-through (/api/openai-passthrough/v1) that - # renames max_tokens → max_completion_tokens before relaying - # to api.openai.com. OpenAI's GPT-5 family rejects max_tokens - # with HTTP 400, and 9Router 0.3.60 doesn't know about - # max_completion_tokens yet (its CLI<->OpenAI translator - # emits the legacy field). The pin on 0.3.60 is intentional - # (newer 9Router versions regress WebSearch, see - # nine_router.py comment) so we patch the boundary instead - # of bumping. Pre-fix: every gpt-5.* / gpt-5.* own-key - # session 400'd silently. + # openai-passthrough renames max_tokens->max_completion_tokens before relaying (GPT-5 400s on max_tokens; 9Router 0.3.60, pinned for WebSearch, emits the legacy field). passthrough_url = f"http://127.0.0.1:{os.environ.get('OPENSWARM_PORT', '8324')}/api/openai-passthrough/v1" options_kwargs["env"] = { "OPENAI_API_KEY": global_settings.openai_api_key, @@ -69,9 +57,7 @@ async def configure_provider_env( } logger.info(f"[MCP-DEBUG] Using direct OpenAI API key (route=api) for {session.model} via openai-passthrough") elif is_pinned_api_route and api_route_provider == "custom": - # User-configured OpenAI-compatible endpoint (Ollama Cloud, - # Together, local Ollama, etc.). Routes through 9Router's - # openai-compatible provider node we synced from settings. + # User OpenAI-compatible endpoint (Ollama/Together/LM Studio) via 9Router's synced provider node. from backend.apps.nine_router import ensure_running as p_9r_ensure_c if not nine_router_running(): logger.info(f"[MCP-DEBUG] custom provider selected but 9Router not running; waiting for startup") @@ -90,34 +76,24 @@ async def configure_provider_env( "ENABLE_TOOL_SEARCH": "auto", } if cp: - # Local OpenAI-compatible servers (LM Studio, Ollama, ...) - # often run with auth disabled, the user leaves api_key - # blank in Settings. The OpenAI-style SDK insists on a - # non-empty key; substitute a harmless placeholder so the - # CLI can issue requests. Servers that DO check auth always - # have a real key configured. + # Local servers often run auth-disabled; placeholder key since the OpenAI SDK requires non-empty. env["OPENAI_API_KEY"] = (cp.api_key or "").strip() or "no-auth-required" from backend.apps.nine_router import normalize_openai_compat_base_url as norm_cp_url env["OPENAI_BASE_URL"] = norm_cp_url(cp.base_url or "") - # Pin subagent ids, without these, CLI's default Haiku 4.5 - # gets sent to the custom provider and 404s. + # Pin subagents or CLI's default Haiku 4.5 404s on the custom provider. if global_settings.anthropic_api_key: env["CLAUDE_CODE_SUBAGENT_MODEL"] = "claude-sonnet-4-6" env["ANTHROPIC_SMALL_FAST_MODEL"] = "claude-haiku-4-5-20251001" env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = "claude-haiku-4-5-20251001" else: - # Pin to the same custom-provider model so subagents stay - # within the user's configured endpoint instead of hitting - # an unconfigured Anthropic lane. + # No Anthropic key: pin subagents to the custom model so they stay on the user's endpoint. env["CLAUDE_CODE_SUBAGENT_MODEL"] = resolved_model env["ANTHROPIC_SMALL_FAST_MODEL"] = resolved_model env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = resolved_model options_kwargs["env"] = env logger.info(f"[MCP-DEBUG] Using custom provider for {session.model} → {resolved_model}") elif is_pinned_api_route and api_route_provider == "gemini" and getattr(global_settings, "google_api_key", None): - # Routed through the local anthropic-proxy so it can scrub the - # JSON-Schema fields Gemini's API rejects ($schema, additionalProperties, - # propertyNames, exclusiveMinimum, nested const) that 9Router 0.3.60 misses. + # Local anthropic-proxy scrubs JSON-Schema fields Gemini rejects ($schema, additionalProperties, propertyNames, exclusiveMinimum, nested const) that 9Router 0.3.60 misses. proxy_url = f"http://127.0.0.1:{os.environ.get('OPENSWARM_PORT', '8324')}/api/anthropic-proxy" options_kwargs["env"] = { "GEMINI_API_KEY": global_settings.google_api_key, @@ -127,12 +103,7 @@ async def configure_provider_env( } logger.info(f"[MCP-DEBUG] Using direct Google API key (route=api) for {session.model} via local proxy") elif api_type == "openrouter" and getattr(global_settings, "openrouter_api_key", None): - # OpenRouter primary. The route="openrouter" entry's - # router_model_id is `openrouter//` so - # 9Router routes via the apikey connection synced from - # CLI's WebSearch delegation needs an Anthropic-shaped lane; - # if the user has no Anthropic key/sub/Pro, fall back to OR's - # resold Claude so subagents stay on the same OR billing. + # OpenRouter via 9Router; with no Anthropic key/sub, fall back to OR's resold Claude for subagents (incl. WebSearch delegation) so they stay on the same OR billing. if not nine_router_running(): from backend.apps.nine_router import ensure_running as nine_router_ensure logger.info(f"[MCP-DEBUG] OpenRouter selected but 9Router not running; waiting for startup") @@ -166,33 +137,24 @@ async def configure_provider_env( options_kwargs["env"] = { "ANTHROPIC_AUTH_TOKEN": bearer, "ANTHROPIC_BASE_URL": proxy_url, - # Pin subagent ids; CLI default 'claude-haiku-4-5-20251001' - # gets rejected by Pro's surface as "No credentials for provider: anthropic". - # (Free-trial clamps to its allowed Claude set + weights credits server-side.) + # Pin subagents; Pro rejects CLI's default haiku as "No credentials for provider: anthropic". "CLAUDE_CODE_SUBAGENT_MODEL": "claude-sonnet-4-6", "ANTHROPIC_SMALL_FAST_MODEL": "claude-haiku-4-5-20251001", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5-20251001", - # auto, never the bare default: tengu_defer_all_bn4 marks every tool - # defer_loading=true, which collides with our cache_control and 400s the - # first tool-laden request (the other anthropic branches all set this). + # auto: tengu_defer_all_bn4 defers every tool, which collides with cache_control and 400s the first tool-laden request. "ENABLE_TOOL_SEARCH": "auto", } - # Free lane meters one run per agent task: tag every call of this task (and its - # subagents, which inherit the env) AND its aux calls (title-gen, see generate_title) - # with the session id, so a query plus its title generation is ONE run, not two. - # The base goes straight to the cloud (no 9Router), so the header rides through. + # Tag every call (subagents inherit env) + aux calls with the task id so a query plus its title-gen meter as ONE free run; base goes straight to cloud (no 9Router) so the header rides. if getattr(global_settings, "connection_mode", "own_key") == "free-trial": options_kwargs["env"]["ANTHROPIC_CUSTOM_HEADERS"] = f"X-Openswarm-Task-Id: {session.id}" - # The cloud serves every free run as Haiku, so keep the subagent on Haiku too: - # a sonnet subagent makes the CLI attach `effort`, which Haiku 400s on. + # Cloud serves every free run as Haiku; keep the subagent on Haiku too (sonnet adds `effort`, Haiku 400s). options_kwargs["env"]["CLAUDE_CODE_SUBAGENT_MODEL"] = "claude-haiku-4-5-20251001" logger.info(f"[MCP-DEBUG] Using OpenSwarm cloud proxy at {proxy_url}") elif api_type == "anthropic" and not resolved_is_9router and global_settings.anthropic_api_key: options_kwargs["env"] = {"ANTHROPIC_API_KEY": global_settings.anthropic_api_key} logger.info("[MCP-DEBUG] Using direct Anthropic API key") elif nine_router_running(): - # Gemini-bound ids go through the local proxy for schema scrubbing; - # everything else hits 9Router directly. + # Gemini-bound ids go through the local proxy for schema scrubbing; everything else hits 9Router directly. is_gemini_bound = ( isinstance(resolved_model, str) and resolved_model.startswith(("gemini/", "gc/", "ag/")) @@ -208,12 +170,7 @@ async def configure_provider_env( "ANTHROPIC_API_KEY": "9router", "ANTHROPIC_BASE_URL": "http://localhost:20128", } - # Pin subagent ids to whichever lane the user has, else CLI's - # default Haiku 4.5 hits 9Router with no Claude route and 401s. - # NOTE: sub_conns is the connection list passed in. Callers currently pass [] - # (see RunOptions), so `active` is empty and this pinning is inert until the - # real connection list is wired through — a latent regression from the run/ split, - # surfaced by pyright (the old inline `_conns` reference was left dangling here). + # Pin subagents to whichever lane the user has, else CLI's default Haiku 4.5 hits 9Router with no Claude route and 401s. NOTE: callers pass sub_conns=[] today so this is inert (latent regression from the run/ split; pyright caught the dangling _conns ref). active = {c.get("provider") for c in sub_conns if isinstance(c, dict) and c.get("isActive")} sub_model = None @@ -241,11 +198,7 @@ async def configure_provider_env( logger.info( f"[MCP-DEBUG] 9Router direct, subagent_model={sub_model}, small_fast={small_model}" ) - # ENABLE_TOOL_SEARCH=auto: without it, CLI's tengu_defer_all_bn4 - # Statsig flag defers 16 tools with no way to load them on non- - # Anthropic networks. "auto" eagerly loads tools when schema - # budget fits in ~10% of context. Don't pass --bare, sets - # CLAUDE_CODE_SIMPLE=1 which strips the system prompt scaffolding. + # auto eagerly loads tools when the schema budget fits; without it tengu_defer_all_bn4 defers 16 tools unloadable off Anthropic networks. Don't use --bare (strips system prompt). env["ENABLE_TOOL_SEARCH"] = "auto" options_kwargs["env"] = env logger.info(f"[MCP-DEBUG] Using 9Router (api_type={api_type})") diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md index 83ea5edc..c8230a10 100644 --- a/frontend/CLAUDE.md +++ b/frontend/CLAUDE.md @@ -7,6 +7,9 @@ Python-only rules do not. Specifically: - **Single-purpose file naming** — a one-export file is named after its export. - **Type everything** under strict `tsconfig`; model data with typed interfaces, not untyped objects (the TS equivalent of "no bare dicts"). +- **Comments are ONE line each, no exceptions** (`//` — never stack into a multi-line block); keep + only WHY/gotcha, delete restating or dead-code comments. JSDoc blocks are exempt. +- **No gratuitous blank lines** — never stack 2+ blanks; keep imports tight. - **Does not apply (Python-only):** `@typechecked`, pydantic `BaseModel`, the no-relative-imports rule (the frontend uses the `@/` path alias and local relative imports), and `p-private`.