Skip to content

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

@dripsmvcp

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions