Skip to content

Require explicit main model configuration for degraded startup#1540

Merged
Aaronontheweb merged 16 commits into
netclaw-dev:devfrom
Aaronontheweb:review/pr-1214-explicit-unconfigured-model
Jul 2, 2026
Merged

Require explicit main model configuration for degraded startup#1540
Aaronontheweb merged 16 commits into
netclaw-dev:devfrom
Aaronontheweb:review/pr-1214-explicit-unconfigured-model

Conversation

@Aaronontheweb

@Aaronontheweb Aaronontheweb commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR supersedes #1214 and carries forward its No-Op chat client degraded-startup work on top of the current dev branch.

PR #1214 introduced a degraded startup path for missing or typoed provider configuration. During review, we found one remaining gap: bound object defaults such as local-ollama/qwen3:30b could still make a fresh or partially configured install look intentionally configured. This replacement PR makes explicit operator configuration the source of truth.

What changed

  • Rebased the PR Add a No-Op chat client fallback for degraded startup #1214 work onto current upstream/dev.
  • Added raw configuration-presence tracking via ProviderRuntimeConfiguration so bound defaults do not count as explicit model configuration.
  • Treat missing Models:Main, incomplete Models:Main, no providers, and typoed Models:Main:Provider as degraded NoProviderConfigured state.
  • Preserve fatal startup behavior for malformed explicit config, including missing provider Type, incomplete explicit Fallback / Compaction, and optional roles that reference unknown providers.
  • Keep provider descriptors and OAuth setup services registered in degraded mode so operators can recover through netclaw model / provider setup flows without a missing service graph.
  • Surface degraded model status consistently in daemon status and CLI status-bar model capability resolution.
  • Prevent warn-level wizard health checks from counting as a clean pass so setup does not auto-launch chat when the No-Op client is active.
  • Updated PRDs and the netclaw-operations provider guidance to document the degraded-vs-invalid split.

Relationship to #1214

This branch is intended to replace #1214 rather than stack on it. It includes the original degraded No-Op client work plus the explicit-model-configuration follow-up fixes and additional regression coverage. #1214 should be closed in favor of this PR.

close #1214

Validation

  • dotnet test Netclaw.slnx
  • openspec validate add-noop-chat-client-fallback --strict
  • dotnet slopwatch analyze
  • pwsh ./scripts/Add-FileHeaders.ps1 -Verify
  • ./scripts/smoke/run-smoke.sh init-wizard
  • git diff --check upstream/dev..HEAD

codymullins and others added 6 commits July 1, 2026 03:43
OpenSpec change scaffolding the No-Op IChatClient fallback path so the
daemon starts in a degraded-but-operational mode when no valid inference
provider/model configuration is detected, instead of failing host startup.

Includes proposal, design (tri-state validation outcome, provider-level
branch, fixed-message contract, doctor warn item, restart-required
recovery), spec deltas for netclaw-model-providers and netclaw-onboarding,
and the implementation task list.
When Netclaw starts without a valid inference provider/model configuration,
the daemon now launches in degraded mode with a No-Op IChatClient instead
of failing host startup. Chat turns return a fixed banner beginning with
"No valid model configuration detected." plus recovery steps (netclaw
doctor, netclaw model, edit netclaw.json) so operators can discover and
fix the configuration problem.

Validation is now tri-state (Valid / NoProviderConfigured / Invalid):
malformed configuration (declared provider missing credentials, schema
violations, model points to an unconfigured provider) still fails startup
loudly — only the genuinely-not-configured case selects the No-Op fallback.
The previous silent local-ollama default in Program.cs is removed.

- IChatClientProvider.IsDegraded default interface method (false on real
  providers, true on NoOpChatClientProvider) so doctor and diagnostics can
  query the state without sniffing concrete types
- netclaw doctor: new Chat Client check reporting pass/warn/fail
- Init wizard: provider health check emits warn-level item (not fail) when
  no provider is configured; daemon still starts in degraded mode
- netclaw-operations skill, PRD-004, PRD-005 updated; skill version 2.9.0
Operator typo in Models:Main.Provider (e.g. "ollama-local1" vs configured
"ollama-local") previously crashed the daemon with a raw
ProviderPluginFactory stack trace because the validation classified the
mismatch as Invalid but the DI extension fell through to lazy factory
construction instead of pre-throwing.

Two fixes:

- ProviderRuntimeValidation: "model references unknown provider" now
  returns NoProviderConfigured. From the operator's standpoint this is
  the same remediation as genuinely-no-provider (fix the model section
  via `netclaw model` or edit netclaw.json); the No-Op banner's
  "Available providers:" line surfaces the configured names so the typo
  is obvious. The doctor check correspondingly reports warn instead of
  error for this case.

- DaemonProviderServiceExtensions: when validation does return Invalid
  (currently unreachable in practice, but a real path for future stricter
  validation), register an IChatClientProvider factory that throws
  immediately with the validation reason — no more raw factory stack
  traces deep in the DI graph.

Includes regression tests at the validation, DI-registration, and doctor
layers exercising the exact "ollama-local1" scenario from the user report.
…value

When the daemon is running in No-Op degraded mode, `ncl chat` previously
showed the stale/invalid model name from netclaw.json (e.g. "gemma4" even
though that model lives on an unconfigured provider). The CLI builds its
own ModelCapabilities locally from netclaw.json without consulting the
daemon's actual chat-client state, so the broken config leaked into the
status bar.

CLI's ModelCapabilities factory now runs ProviderRuntimeValidation against
the same config it just loaded; when the validation outcome is non-Valid
it substitutes a sentinel ModelId ('(no model — run \`netclaw model\`)')
and zeros the context-window probe — matching the daemon's No-Op selection
without a round-trip.
…ealth

ncl status previously reported overall=healthy and showed the configured
(broken) model values when the daemon was running with the No-Op chat
client — e.g. "model: gemma4 (provider: ollama-local1, ...)" — completely
hiding the degraded state from operators.

Wire-level: DaemonRuntimeStatus.Model gains Degraded + DegradedReason.
DaemonRuntimeStatusService populates them from the running
IChatClientProvider.IsDegraded and the ProviderRuntimeValidation singleton.
ResolveOverallStatus now flips to "degraded" whenever the chat client is
No-Op, regardless of other subsystem health (the daemon can't actually
serve model responses).

CLI formatter: when Degraded=true, the model section renders as
"model: (none — No-Op chat client active)" with the validation reason
and a recovery hint, instead of the stale ModelId/Provider line.

Status service constructor takes the new dependencies as optional
trailing parameters so the seven existing tests don't need touching.
@Aaronontheweb
Aaronontheweb marked this pull request as draft July 1, 2026 04:07
@Aaronontheweb Aaronontheweb added the reliability Retries, resilience, graceful degradation label Jul 2, 2026
@Aaronontheweb
Aaronontheweb marked this pull request as ready for review July 2, 2026 16:13
@Aaronontheweb
Aaronontheweb marked this pull request as draft July 2, 2026 16:33
@Aaronontheweb
Aaronontheweb marked this pull request as ready for review July 2, 2026 18:41

@Aaronontheweb Aaronontheweb left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

LGTM


if (status?.Model is { Degraded: true })
{
return new ModelRuntimeResolution(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Return explicit "model not configured" errors going forward if a user installs netclaw and launches the daemon without running netclaw init first - consistent theme you'll notice throughout all of these commands and APIs.

daemon);

Assert.True(result.Degraded);
Assert.Equal("(no model - run `netclaw init`)", result.ModelId);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

LGTM

@Aaronontheweb
Aaronontheweb merged commit 596a107 into netclaw-dev:dev Jul 2, 2026
15 checks passed
@Aaronontheweb
Aaronontheweb deleted the review/pr-1214-explicit-unconfigured-model branch July 2, 2026 18:43
Aaronontheweb added a commit that referenced this pull request Jul 3, 2026
…tly remote (#1567)

#1540's chat preflight blocks with 'daemon not configured' whenever the local
netclaw.json is missing — but NETCLAW_DAEMON_ENDPOINT and paired-client
(client.json) endpoints mean the daemon's config lives on the daemon host.
This regressed paired/remote clients and broke the containerized eval harness
(every case failed 0/5 with 'daemon not configured' from the host CLI).

Preflight now stands down when either endpoint source is present — the same
sources DaemonApi.ResolveEndpoint prefers over local daemon config.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

reliability Retries, resilience, graceful degradation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants