WW-69 design forks — recorded during build¶
Genuine ambiguities in SPEC-WW69.md where I picked a defensible answer and moved on, plus one literal-reading deviation. Flagging all for review.
1. The 8th "call site" (LlmStudyContentVerifier) is NOT wired to the router¶
The spec lists LlmStudyContentVerifier among the "8 taskClass call sites" to switch from
selector.Default to router.SelectAsync(taskClass). On inspection, this class doesn't hold a
selector at all — it takes a plain ILlmHarness via constructor injection, and the harness is chosen
at DI-registration time in Program.cs (sp.GetRequiredService<ILlmHarnessSelector>().ById(verifierProvider),
config key WorkWingman:StudyContentVerification:Provider, default "codex"). This is a genuine
provider pin (config-driven, deliberately NOT the app default) — its whole purpose is
model-vs-model cross-checking, so routing it dynamically by cost class could put the SAME model on
both sides of the check. Per the spec's own exception ("If a call site currently pins a specific
provider for correctness ... keep that pin — add the router only where .Default was used"), I left
this one untouched. Only 7 of the 8 named sites actually used .Default; all 7 are wired.
Deviation, with reason: literal reading of the spec would wire all 8; I wired 7 and left
LlmStudyContentVerifier on its existing config-driven pin, because routing it would defeat its
cross-model-check purpose and it never used .Default to begin with.
2. RoutingDecision.RuleSource = "UserOverride" — precedence when it coexists with other signals¶
The spec defines four RuleSource values but doesn't fully specify precedence when more than one
applies at once (e.g., userOverride: true AND the rule was user-edited; or userOverride: true AND
routing is disabled for that taskClass). I implemented this precedence in LlmRouter.SelectAsync,
evaluated in order:
- GovernorForced — wins unconditionally whenever the governor's tier is Red/Blackout or
PreferCheapOrLocalis true, matching the binding "governor always wins" rule literally. - Default — routing disabled for this taskClass (
RoutingRule.Enabled == false); the configured default harness is used regardless of anyuserOverrideflag. - UserOverride — the caller passed
userOverride: trueand neither of the above applied. - UserRule vs Default — compares the stored rule's
PreferredCostClassagainstRoutingRuleDefaults.Seed[taskClass]; equal →Default(never edited), different →UserRule.
This is provisional — if the real UX never surfaces a caller-side userOverride flag independently of
the governor's own Blackout cost-warning flow, UserOverride may end up rarely/never observed in
practice. It's wired and tested (LlmRouterTests.SelectAsync_UserOverrideRequested_...) either way.
3. LlmCostClass.Cheap has no harness that classifies into it today¶
Per the spec's literal cost-ranking algorithm (Kind=="local" → Local; provider-id ordinal map →
Mid/Strong; unknown → Mid), nothing maps to Cheap — there's no cheap-key provider in the harness
fleet yet. A rule that prefers Cheap (JobFitNarrative, ResearchGrounding) always walks up the chain to
whatever's cheapest AVAILABLE (typically Mid today). This is intentional per the spec as written, not a
bug — Cheap exists as a slot for a future cheap-key harness (e.g. a "-mini" tier) to occupy without a
router change. Documented in LlmCostClassifier's XML doc and covered by
LlmRouterTests.SelectAsync_FallsBackUpTheChain_WhenPreferredClassUnavailable.
4. RoutingDecision.GovernorTier/.Reason record the ORIGINAL (pre-downgrade) governor decision¶
When the governor forces a downgrade, LlmRouter does NOT re-query the governor against the
newly-chosen (often local/unmetered) harness for the persisted record — it keeps the tier/reason from
the decision that TRIGGERED the downgrade. Re-querying against the new harness would usually report an
unrelated Green state (a fresh Ollama harness has no plan settings) and obscure why forcing happened.
This felt like the more honest provenance choice; flagging in case the intent was "the tier of whoever
ends up serving the call."
5. RoutingRulesGrid built as its own standalone Angular component¶
The spec names RoutingRulesGrid as a component deliverable; the existing AI-efficiency UI is otherwise
all inlined into the monolithic Metrics component. I built it as a genuinely separate, independently
unit-tested component (features/metrics/routing-rules-grid/) rather than inlining its logic into
Metrics, both to match the literal naming and to keep its PUT/error-state tests isolated. It's
imported into Metrics's imports array and slotted via <app-routing-rules-grid /> inside the
"Keys & Plans" card, in a new "Routing rules" subsection.
6. Live-Ollama smoke tests needed no CI exclusion after all¶
The gate instructions anticipated needing --filter "FullyQualifiedName!~LiveSmoke&FullyQualifiedName!~LocalRagLive"
for a flaky live-Ollama test. I ran the full suite with no filter on this box — LocalRagLiveSmokeTests
already self-skips cleanly when Ollama isn't reachable (its own early-return, not a build-time
exclusion), so dotnet test tests/WorkWingman.Tests with zero filter went green at 3339/3339. No
deviation was needed; noting it since the gate explicitly asked me to say so either way.