Files
gnoma/internal
vikingowl f321dabce3 feat(config): Phase 3 — gnoma doctor diagnostic command
Phase 3 of the 2026-05-24 config-migration plan. Read-only
diagnostic over config files. Pairs with `gnoma upgrade-config`
from the previous slice: doctor finds things upgrade-config
can't fix, upgrade-config fixes the things it can.

What doctor surfaces (severity-ranked):

  error — file unreadable, file unparseable
  warn  — unknown top-level keys (decoder silently
          ignores them today)
        — invalid enum values (permission.mode,
          router.prefer, slm.backend)
        — explicit-zero pointer fields whose resolved
          value diverges from the default (e.g.
          max_tokens = 0 when default is 8192)
  info  — (reserved; current diagnostics are warn+)

What doctor does NOT yet surface:

  - Per-field zero-spam inside a partially-set section
    (e.g. user wrote [provider] default = "anthropic" with
    no other fields — those are at Go zero but the
    encoder's omitempty handles them on the next write).
    Catching this requires per-key source-tracking that
    BurntSushi's MetaData doesn't expose for nested
    fields; tracked as a follow-up.
  - Cross-file layering bugs (e.g. project file's
    prefer = "" silently shadows global's prefer = "cloud").
    That requires loading the full layered config and
    diffing per-section — could be a follow-up to doctor,
    or the per-project upgrade-config --all flow.

CLI surface (`cmd/gnoma doctor`):

  gnoma doctor                  scan the project config
                                (default — cwd's .gnoma/config.toml)
  gnoma doctor <path>           scan a specific file
  gnoma doctor --all-projects   walk the registry, scan
                                global + every known project
  gnoma doctor --json           structured JSON to stdout
                                (severity as string, suitable
                                for CI/scripts)
  exit code:                    0 = clean, 1 = any warn/error

Help text: `gnoma -h` now lists `doctor` alongside the
other subcommands.

Implementation:

  internal/config/doctor.go    Severity, Finding, Doctor,
                                DiagnoseFile, DiagnoseFiles
                                (~150 lines).
  internal/config/doctor_test.go   11 tests covering each
                                finding type + Severity.String.
  cmd/gnoma/doctor_cmd.go      CLI dispatch + JSON / text
                                rendering + exit code.
  cmd/gnoma/doctor_cmd_test.go 5 tests for the CLI surface.
  internal/config/load.go      new ProjectConfigPathFor
                                helper for --all-projects
                                (constructs a project config
                                path from an arbitrary root
                                without chdir).
  cmd/gnoma/main.go             dispatch case + -h help text.

Severity.MarshalJSON is custom: encodes the int as its
lower-case name string ("warn" not 1) for stable CI
consumption. Tests assert on the string form.

End-to-end check on a synthetic config with multiple
findings:

  $ gnoma doctor
  warn    ...:permission.mode      invalid permission.mode "yes" ...
                                       → fix the value, or remove the line
  warn    ...:provider.max_tokens  explicit zero for provider.max_tokens
                                    (resolved to 0); the default is 8192. ...
  warn    ...:unknown_section      unknown top-level key "unknown_section" ...
  warn    ...:unknown_section.foo  unknown top-level key "unknown_section.foo" ...
  exit: 1

  $ gnoma doctor --json
  [
    { "severity": "warn", "path": "...",
      "key": "permission.mode", "message": "..." },
    ...
  ]

Quality pipeline:
  gofmt -l .                  clean
  go vet ./...                clean
  golangci-lint run ...       0 issues on touched packages
  go test ./...               all pass (only the pre-existing
                              TestStartBackend_Auto_NothingReachable
                              environmental failure remains)

Refs: docs/superpowers/plans/2026-05-24-config-migration.md
       § Phase 3.
2026-06-04 18:05:14 +02:00
..