Skip to content

refactor(config): validate readConfig() output with zod (follow-up to #1394) #1395

@esengine

Description

@esengine

Follow-up to #1394 / #1295.

#1394 fixed the React #31 crash by adding 4 layers of defensive typeof checks downstream of readConfig(). The root cause is upstream: src/config.ts:330 does JSON.parse(raw) as ReasonixConfig with no runtime validation, so cfg.mcp (typed string[]) can contain objects, numbers, etc. from a hand-edited config.json.

Per repo conventions, boundary input should be validated with zod and downstream code should trust the types. Right now we have it backwards.

Scope

  1. Define a zod schema for ReasonixConfig (or at least the fields most exposed to hand-editing: mcp, mcpDisabled, skills.paths, pricingOverride, ...).
  2. Run it in readConfig(). On invalid item:
    • drop the item from the returned config
    • log a clear console.warn naming the file path, field, and bad value (so the user knows their config was partially ignored — not silently swallowed)
  3. Remove the now-redundant downstream defenses added in fix(desktop): prevent React #31 when MCP config contains non-string items (#1295) #1394:
    • the typeof s.summary === "string" ? ... : "[invalid]" ternaries in desktop/src/ui/context-panel.tsx and desktop/src/ui/settings.tsx
    • the .filter((raw): raw is string => typeof raw === "string") in emitMcpSpecs becomes unnecessary once the schema guarantees string[]
    • the catch-block stringification in summarizeMcpSpec can stay (it's defending against malformed strings, not non-strings)
  4. Keep the regression test in tests/mcp-desktop-react31.test.ts; add a new test that a malformed config.json mcp entry is dropped + warned at read time.

Why not just leave it

The current state has the validation in 4 places. Any new field added to config that's hand-editable will repeat the same pattern unless we move it to the boundary. Code review will keep hitting "where do we validate this" without a clear answer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions