Skip to content

feat(config): typed Config model + validation for .vouch/config.yaml #243

Description

@dripsmvcp

What you're trying to do

.vouch/config.yaml is currently parsed as an untyped dict (yaml.safe_load)
and read defensively at each call site — e.g. storage.py writes the starter
config as a literal dict, and proposals.py re-reads it with nested .get() +
isinstance guards and a silent except Exception fallback to {} for
review.approver_role. As more features grow config keys (retrieval.backends,
retrieval.reflex, mcp.publish_skills, page_kinds, …) this pattern spreads
the schema across the codebase, swallows typos (reveiw: is silently ignored),
and gives no single source of truth for what a valid config looks like.

A typed Config pydantic model parsed once at store-open turns config into a
load-bearing, validated shape — consistent with the project's "prefer pydantic
models for any persisted shape" rule.

Suggested shape

  • New Config (and nested ReviewConfig, RetrievalConfig, …) pydantic models
    in models.py, with defaults matching today's starter config.
  • KBStore.config parses config.yaml into Config once (cached), raising a
    clear ConfigError with the offending key/path on malformed input.
  • Unknown top-level keys produce a warning surfaced by vouch doctor rather than
    silent drop (so typos are visible, not lost).
  • All current ad-hoc readers (proposals.py, retrieval backend selection, etc.)
    switch to store.config.<field>.

Acceptance

  • A config.yaml with a mistyped key surfaces a warning in vouch doctor.
  • A malformed value (e.g. retrieval.default_limit: "ten") fails fast with a
    per-field error instead of a silent fallback.
  • An existing .vouch/ with no mcp:/retrieval: blocks loads with documented
    defaults (no behavior change for current KBs) — covered by a round-trip test.

Out of scope

  • A config-migration tool (separate from vouch migrate for the on-disk KB).
  • Env-var overlays on top of the file (follow-up).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions