Skip to content

feat: vouch new <kind> — scaffold a typed page/entity proposal from a template #330

Description

@plind-junior

config-declared page kinds (person, decision, system, plus anything a kb adds under page_kinds in config.yaml — see #234) carry required frontmatter fields, an optional frontmatter schema, and a required_citations flag. today an author has to know those fields by heart and hand-write the --meta key=value pairs on vouch propose-page, or discover the gaps only when propose_page rejects the proposal. vouch schema list already surfaces per-kind requirements via load_page_kind_registry(store).resolve(name), but nothing turns that into a filled starting point.

vouch new <kind> closes that gap: it reads the registry for <kind>, stubs (or interactively prompts for) every required field, and emits a proposal through the normal review gate — so authors get a correctly-shaped draft instead of a frontmatter guessing game, and the reviewer still sees exactly one pending proposal to approve or reject.

proposed surface

  • vouch new <kind> --title <t> — resolve <kind> against load_page_kind_registry(store); for each required field returned by .resolve() (which yields (required_fields, frontmatter_schema, required_citations)), stub an empty value into the frontmatter dict (or prompt when --interactive).
  • flags:
    • --title <t> (required for page kinds).
    • --field key=value (repeatable) — pre-fill a required/optional field; parsed as yaml, the same way _parse_meta handles --meta on propose-page.
    • --interactive / -i — prompt for each unfilled required field at the terminal; default off so it stays scriptable.
    • --body <text> / --body - — page body, mirroring propose-page (- reads stdin).
    • --dry-run — print the assembled draft (title, resolved kind, frontmatter, missing-field list) without creating a proposal; passes through to propose_page(..., dry_run=True).
    • --json — emit the resulting proposal id (or the dry-run draft) machine-readably.
  • entity kinds route to propose_entity instead of propose_page. entity kinds are the EntityType members (person, project, repo, company, concept, decision, workflow); vouch new person --name <n> stubs the entity shape.
  • dispatch and ambiguity: some names collide (decision and project are both an EntityType member and a possible page kind). resolve deterministically — a name present in the page-kind registry scaffolds a page; --entity forces the entity path; a name that is only an EntityType scaffolds an entity. an unknown name fails clearly.
  • config: no new config surface. the field set is derived entirely from the existing page_kinds registry; a required_citations kind gets a stubbed-but-empty --claim/--source reminder in the draft rather than a silent pass.

no new kb.* method. the scaffold is a client-side convenience that composes the existing propose_page / propose_entity proposal calls, so it needs no server/jsonl/capabilities registration — just the cli command in src/vouch/cli.py and a test at tests/test_new_scaffold.py (or tests/test_cli.py if that is where the propose-page cli tests live).

review gate & scope

the scaffold only produces a pending proposal. it calls propose_page(store, ..., proposed_by=_whoami()) (or propose_entity) exactly as the existing propose-page / propose-entity commands do — the same path that already runs the page_kinds validation and dangling-reference checks. a scaffolded draft is never written as an approved page or entity; a human still runs vouch approve <id> (kb.approve) to land it. filling required fields does not weaken validation: propose_page re-validates the kind, so a scaffold that leaves a required field empty is proposable but flagged the same way any other proposal is.

stays local-first and pure: field resolution reads the registry only; no business logic moves into storage.py; the command does string assembly plus one existing proposals.* call. --dry-run reuses propose_page's existing dry_run flag rather than a parallel write path.

acceptance criteria

  • vouch new decision --title "pick X" creates a pending page proposal whose frontmatter stubs every required field for the decision page kind, resolved from load_page_kind_registry(store).
  • vouch new person --name "alice-example" routes to propose_entity and creates a pending entity proposal.
  • a name present in the page-kind registry scaffolds a page; --entity forces the entity path; the collision cases (decision, project) resolve to exactly one target with no silent guess.
  • --field key=value pre-fills a field, parsed as yaml (so --field attendees=[a, b] arrives as a list), matching the --meta / _parse_meta behavior on propose-page.
  • --interactive prompts only for required fields left unfilled; non-interactive runs never block on a prompt.
  • --dry-run prints the assembled draft and its missing-required-field list and creates no proposal (propose_page(..., dry_run=True)).
  • the created artifact is a pending proposal only: it shows up in vouch pending, and does not become an approved page or entity until a human vouch approves it.
  • an unknown <kind> fails clearly, listing known kinds from registry.known(), instead of stubbing an invalid page.
  • required_citations kinds surface a citation reminder in the draft rather than silently proposing an uncited page.
  • test under tests/ covers page-kind scaffold, entity scaffold, dry-run (no write), collision dispatch, and unknown-kind error.

adjacent: #234 introduced config-declared typed page kinds and the vouch schema inspection commands; this issue does not add or change kinds — it consumes the existing registry to scaffold a proposal for one. distinct from vouch propose-page, which takes fully hand-authored --meta and does no field discovery.

Metadata

Metadata

Assignees

No one assigned

    Labels

    clicommand line interfaceenhancementNew feature or requestsize: S50-199 changed non-doc lines

    Type

    No type

    Fields

    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