Full diff
@@ -81,7 +81,7 @@ jobs:\n OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}\n WECHAT_API_KEY: ${{ secrets.DeepSeekV4Flash_API }}\n AVWIRE_WECHAT_MODEL: ${{ vars.AVWIRE_WECHAT_MODEL }}\n- AVWIRE_PROVIDER_ORDER: opencode:claude-sonnet-4-6,opencode:gpt-5.5,gemini:gemini-3.6-flash,opencode:gemini-3.1-pro,opencode:qwen3.6-plus,nvidia:z-ai/glm-5.2,opencode:glm-5.1,opencode:kimi-k2.6,gemini:gemini-3.5-flash,nvidia:deepseek-ai/deepseek-v4-pro,nvidia:nvidia/nemotron-3-ultra-550b-a55b,wechat:Deepseek-v4-flash,openrouter:nvidia/nemotron-3-ultra-550b-a55b:free,nvidia:qwen/qwen3.5-397b-a17b,nvidia:nvidia/nemotron-3-super-120b-a12b,openrouter:nvidia/nemotron-3-super-120b-a12b:free,nvidia:mistralai/mistral-medium-3.5-128b,openrouter:google/gemma-4-31b-it:free,openrouter:inclusionai/ling-3.0-flash:free,openrouter:google/gemma-4-26b-a4b-it:free,openrouter:nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free,openrouter:openai/gpt-oss-20b:free,openrouter:nvidia/nemotron-3-nano-30b-a3b:free,openrouter:nvidia/nemotron-nano-12b-v2-vl:free,openrouter:nvidia/nemotron-nano-9b-v2:free,openrouter:poolside/laguna-s-2.1:free,openrouter:cohere/north-mini-code:free,openrouter:poolside/laguna-xs-2.1:free\n+ AVWIRE_PROVIDER_ORDER: gemini:gemini-3.6-flash,gemini:gemini-3.5-flash,nvidia:nvidia/nemotron-3-ultra-550b-a55b,wechat:Deepseek-v4-flash,openrouter:nvidia/nemotron-3-ultra-550b-a55b:free,nvidia:nvidia/nemotron-3-super-120b-a12b\n BRIEFING_LLM_INTRO: ${{ vars.BRIEFING_LLM_INTRO }}\n BRIEFING_MAX_ITEMS: ${{ vars.BRIEFING_MAX_ITEMS }}\n BRIEFING_MAX_ITEMS_PER_SECTION: ${{ vars.BRIEFING_MAX_ITEMS_PER_SECTION }}\n@@ -66,7 +66,7 @@ jobs:\n AVWIRE_GEMINI_MODEL: ${{ vars.AVWIRE_GEMINI_MODEL }}\n AVWIRE_NVIDIA_MODEL: ${{ vars.AVWIRE_NVIDIA_MODEL }}\n AVWIRE_WECHAT_MODEL: ${{ vars.AVWIRE_WECHAT_MODEL }}\n- AVWIRE_PROVIDER_ORDER: opencode:claude-sonnet-4-6,opencode:gpt-5.5,gemini:gemini-3.6-flash,opencode:gemini-3.1-pro,opencode:qwen3.6-plus,nvidia:z-ai/glm-5.2,opencode:glm-5.1,opencode:kimi-k2.6,gemini:gemini-3.5-flash,nvidia:deepseek-ai/deepseek-v4-pro,nvidia:nvidia/nemotron-3-ultra-550b-a55b,wechat:Deepseek-v4-flash,openrouter:nvidia/nemotron-3-ultra-550b-a55b:free,nvidia:qwen/qwen3.5-397b-a17b,nvidia:nvidia/nemotron-3-super-120b-a12b,openrouter:nvidia/nemotron-3-super-120b-a12b:free,nvidia:mistralai/mistral-medium-3.5-128b,openrouter:google/gemma-4-31b-it:free,openrouter:inclusionai/ling-3.0-flash:free,openrouter:google/gemma-4-26b-a4b-it:free,openrouter:nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free,openrouter:openai/gpt-oss-20b:free,openrouter:nvidia/nemotron-3-nano-30b-a3b:free,openrouter:nvidia/nemotron-nano-12b-v2-vl:free,openrouter:nvidia/nemotron-nano-9b-v2:free,openrouter:poolside/laguna-s-2.1:free,openrouter:cohere/north-mini-code:free,openrouter:poolside/laguna-xs-2.1:free\n+ AVWIRE_PROVIDER_ORDER: gemini:gemini-3.6-flash,gemini:gemini-3.5-flash,nvidia:nvidia/nemotron-3-ultra-550b-a55b,wechat:Deepseek-v4-flash,openrouter:nvidia/nemotron-3-ultra-550b-a55b:free,nvidia:nvidia/nemotron-3-super-120b-a12b\n # freshness window (hours); code default 120, validated 24-336\n NEWS_MAX_AGE_HOURS: ${{ vars.NEWS_MAX_AGE_HOURS }}\n # private usage-dashboard URL token (page skipped when unset)\n@@ -44,10 +44,8 @@\n "openrouter:poolside/laguna-xs-2.1:free",\n )\n \n-# Rank by model capability first, then by provider for equivalent models.\n-# Primary provider routes remain ahead of OpenRouter's free fallbacks. The\n-# WeChat Flash route stays below the primary models because its gateway\n-# disables hidden reasoning for predictable output.\n+# Complete model catalog used by the manual drafting workbench and historical\n+# usage dashboard.\n MODEL_ORDER = (\n OPENCODE_MODEL_ORDER[0],\n OPENCODE_MODEL_ORDER[1],\n@@ -67,7 +65,19 @@\n *OPENROUTER_MODEL_ORDER[2:],\n )\n \n-DEFAULT_PROVIDER_ORDER = ",".join(MODEL_ORDER)\n+# Automated runs use only routes that completed at least one valid article in\n+# recent production telemetry. Retired/unavailable routes stay selectable in\n+# the manual workbench but no longer add doomed HTTP 410 calls to every story.\n+AUTOMATIC_MODEL_ORDER = (\n+ DIRECT_MODEL_ORDER[0],\n+ DIRECT_MODEL_ORDER[1],\n+ DIRECT_MODEL_ORDER[4],\n+ WECHAT_MODEL_ORDER[0],\n+ OPENROUTER_MODEL_ORDER[0],\n+ DIRECT_MODEL_ORDER[6],\n+)\n+\n+DEFAULT_PROVIDER_ORDER = ",".join(AUTOMATIC_MODEL_ORDER)\n \n MODEL_DISPLAY_NAMES = {\n "wechat:Deepseek-v4-flash":\n@@ -70,7 +70,7 @@\n # (JSON syntax only - content failures NEVER take this path)\n # Only fields the respective endpoint documents are sent; models without a\n # profile fall back to conservative generic settings.\n-NVIDIA_DEFAULT_MODEL = "z-ai/glm-5.2"\n+NVIDIA_DEFAULT_MODEL = "nvidia/nemotron-3-ultra-550b-a55b"\n GEMINI_DEFAULT_MODEL = "gemini-3.6-flash"\n OPENROUTER_DEFAULT_MODEL = "nvidia/nemotron-3-ultra-550b-a55b:free"\n OPENCODE_DEFAULT_MODEL = "claude-sonnet-4-6"\n@@ -16,6 +16,7 @@\n import manual_draft # noqa: E402\n import write # noqa: E402\n from model_config import ( # noqa: E402\n+ AUTOMATIC_MODEL_ORDER,\n MODEL_DISPLAY_NAMES,\n MODEL_ORDER,\n OPENCODE_MODEL_ORDER,\n@@ -817,9 +818,10 @@ def test_static_security_contract():\n and "encrypted envelopes only" in workflow\n and "data/manual-jobs/outbox 2>/dev/null || true" in workflow,\n "Actions uses Secrets, checks ciphertext and tolerates empty queues")\n- configured_order = f"AVWIRE_PROVIDER_ORDER: {','.join(MODEL_ORDER)}"\n+ configured_order = (\n+ f"AVWIRE_PROVIDER_ORDER: {','.join(AUTOMATIC_MODEL_ORDER)}")\n check(configured_order in hourly and configured_order in briefing,\n- "automatic workflows use the complete shared fallback order")\n+ "automatic workflows use the proven production fallback order")\n check("secrets.OPENROUTER_API_KEY" in hourly\n and "secrets.OPENROUTER_API_KEY" in briefing\n and "sk-or-v1-" in hourly and "sk-or-v1-" in briefing,\n@@ -25,6 +25,10 @@\n os.environ.setdefault("OPENCODE_API_KEY", "oc_sk_test-not-real")\n \n import providers # noqa: E402\n+from model_config import ( # noqa: E402\n+ AUTOMATIC_MODEL_ORDER,\n+ OPENROUTER_MODEL_ORDER,\n+)\n \n _pass = 0\n _fail = 0\n@@ -166,12 +170,22 @@ def test_model_priority_defaults():\n )\n check(providers.MODEL_ORDER == expected,\n "model priority order matches the configured product order")\n- check(providers.DEFAULT_ORDER == ",".join(expected),\n- "fallback default derives from model priority order")\n+ active = (\n+ "gemini:gemini-3.6-flash",\n+ "gemini:gemini-3.5-flash",\n+ "nvidia:nvidia/nemotron-3-ultra-550b-a55b",\n+ "wechat:Deepseek-v4-flash",\n+ "openrouter:nvidia/nemotron-3-ultra-550b-a55b:free",\n+ "nvidia:nvidia/nemotron-3-super-120b-a12b",\n+ )\n+ check(AUTOMATIC_MODEL_ORDER == active\n+ and providers.DEFAULT_ORDER == ",".join(active),\n+ "automatic fallback default contains only proven live routes")\n check(providers.GEMINI_DEFAULT_MODEL == "gemini-3.6-flash",\n "Gemini 3.6 Flash is the default model")\n- check(providers.NVIDIA_DEFAULT_MODEL == "z-ai/glm-5.2",\n- "GLM 5.2 is the highest-priority NVIDIA default")\n+ check(providers.NVIDIA_DEFAULT_MODEL\n+ == "nvidia/nemotron-3-ultra-550b-a55b",\n+ "Nemotron Ultra is the highest-priority NVIDIA default")\n check(providers.OPENROUTER_DEFAULT_MODEL\n == "nvidia/nemotron-3-ultra-550b-a55b:free",\n "Nemotron Ultra is the highest-priority OpenRouter default")\n@@ -186,7 +200,7 @@ def test_model_priority_defaults():\n ) and expected[14:16] == (\n "nvidia:nvidia/nemotron-3-super-120b-a12b",\n "openrouter:nvidia/nemotron-3-super-120b-a12b:free",\n- ), "primary routes stay before WeChat Flash and free fallbacks")\n+ ), "complete catalog preserves owner model order")\n print("test_model_priority_defaults: done")\n \n \n@@ -289,10 +303,9 @@ def fake_repair(url, json=None, timeout=None, headers=None):\n def test_openrouter_models_and_payloads():\n models = [\n token.split(":", 1)[1]\n- for token in providers.MODEL_ORDER\n- if token.startswith("openrouter:")\n+ for token in OPENROUTER_MODEL_ORDER\n ]\n- check(len(models) == 13, "all 13 OpenRouter models are configured")\n+ check(len(models) == 13, "all 13 OpenRouter profiles remain supported")\n for model in models:\n captured = []\n \n@@ -380,9 +393,9 @@ def fake_repair(url, json=None, timeout=None, headers=None):\n else:\n os.environ[key] = value\n check(automatic == [\n- token for token in providers.MODEL_ORDER\n+ token for token in AUTOMATIC_MODEL_ORDER\n if token.startswith("openrouter:")\n- ], "automatic writer exposes all OpenRouter fallbacks in owner order")\n+ ], "automatic writer exposes only proven OpenRouter fallbacks")\n \n try:\n provider._final_text(FakeResponse({"error": {"code": 401}}))\n@@ -513,9 +526,9 @@ def fake_post(url, json=None, timeout=None, headers=None):\n else:\n os.environ[key] = value\n check(automatic == [\n- token for token in providers.MODEL_ORDER\n+ token for token in AUTOMATIC_MODEL_ORDER\n if token.startswith("opencode:")\n- ], "automatic writer exposes all OpenCode fallbacks in owner order")\n+ ], "automatic writer excludes unavailable OpenCode routes")\n print("test_opencode_protocols_and_payloads: done")\n \n \n@@ -28,6 +28,7 @@\n import providers # noqa: E402\n import usage # noqa: E402\n from common import now_utc # noqa: E402\n+from model_config import AUTOMATIC_MODEL_ORDER # noqa: E402\n \n CHECKS = 0\n FAILED = 0\n@@ -300,9 +301,11 @@ def json(self):\n }\n }\n priority_rows, _ = build.usage_rows(priority_ledger, prices)\n+expected_priority = list(AUTOMATIC_MODEL_ORDER) + sorted(\n+ set(providers.MODEL_ORDER) - set(AUTOMATIC_MODEL_ORDER))\n check("usage dashboard follows the configured model priority",\n [row["label"] for row in priority_rows]\n- == list(providers.MODEL_ORDER))\n+ == expected_priority)\n ultra = next(r for r in rows if "ultra" in r["label"])\n expect_usd = (ultra["in"] / 1e6 * 0.423) + (ultra["out"] / 1e6 * 2.61)\n check("theoretical USD uses the reference price table",