Skip to main content

zendev-proposal

zendev-proposal is a stateless validator and indexer for repositories that store durable design proposals as Markdown. Git history remains the lifecycle record, YAML frontmatter remains structured proposal metadata, templates remain the required-section authority, and the committed JSON index remains a derived projection.

The tool owns repository mechanics:

  • safe YAML frontmatter parsing with duplicate-key rejection
  • JSON Schema validation
  • filename, number, title, H1, and Executive Summary consistency
  • required H2 sections derived from repository templates
  • frontmatter-bearing drafts with either the formal or a dedicated draft schema
  • proposal relation integrity and inverse index edges
  • optional Git-backed deletion, number-reuse, initial-state, and transition checks
  • deterministic index checking and explicit writes
  • stable human and JSON diagnostics

Project terminology, normative semantics, process authority, and acceptance decisions stay in each proposal repository.

Install this tool independently of the commit workflow:

$ uv add --dev zendev-proposal

For an ad hoc run, use uvx --from zendev-proposal zendev-proposal .... The complete zendev distribution exposes the same application through its unified command:

$ uvx --from zendev zendev proposal --help

Python API

from zendev.proposal import load_config, validate_repository

config = load_config("proposal.toml")
result = validate_repository(config)
if not result.ok:
    for diagnostic in result.diagnostics:
        print(diagnostic)

Repositories using prek can install the published hook directly:

[[repos]]
repo = "https://github.com/zendev-lab/zendev"
rev = "v0.2.0"
hooks = [
  { id = "zendev-proposal" },
  { id = "zendev-proposal-index" },
]

zendev-proposal is the read-only commit gate and also checks the committed index. It always runs, including deletion-only commits. The index writer is a manual-stage hook so CI cannot repair drift and pass with an uncommitted change:

$ uvx prek run --stage manual zendev-proposal-index

Commands

Run commands from the proposal repository root or pass an explicit config:

$ zendev-proposal check [--config proposal.toml] [--base-ref REF] [--json]
$ zendev-proposal index [--config proposal.toml] (--check | --write) [--json]

check uses PROPOSAL_BASE_REF when --base-ref is absent. History validation is disabled when neither is present. An explicitly requested ref must exist locally; the tool fails closed instead of silently skipping lifecycle checks.

Exit codes have stable meanings:

  • 0: repository is valid and the requested index operation succeeded
  • 1: proposal documents, graph, history, or committed index are invalid
  • 2: the tool could not load its configuration, schema, templates, or Git ref

check and index --check never write. index --write validates proposal documents before replacing the configured index.

Policy file

The root policy is versioned TOML. This example models numbered VEP documents, lightweight pre-VEP drafts, a proposal graph, and Git lifecycle transitions:

version = 1

[proposal]
prefix = "VEP"
number_field = "vep"
title_field = "title"
type_field = "type"
status_field = "status"
documents_dir = "veps"
schema = "schemas/vep.schema.json"
index = "veps-index.json"
number_width = 4
metadata_title = "plain"
filename_slug_pattern = "[a-z0-9]+(?:-[a-z0-9]+)*"

[drafts]
directory = "drafts"
schema = "schemas/draft.schema.json"
marker = "> Pre-VEP design draft. Non-normative."
pre_proposal = true

[templates]
Technical = "templates/technical.md"
Process = "templates/process.md"
Informational = "templates/informational.md"

[summary]
prefix = "**Executive Summary:**"
minimum_sentences = 2
maximum_sentences = 4

[graph]
fields = ["requires", "amends", "supersedes"]
requires_field = "requires"
amends_field = "amends"
supersedes_field = "supersedes"
accepted_status = "Accepted"
superseded_status = "Superseded"

[history]
initial_status = "Draft"
protect_records = true
bootstrap_numbers = [0]

[history.transitions]
Draft = ["Draft", "Review", "Withdrawn"]
Review = ["Review", "Draft", "Accepted", "Rejected", "Withdrawn"]
Accepted = ["Accepted", "Superseded"]
Rejected = ["Rejected"]
Withdrawn = ["Withdrawn"]
Superseded = ["Superseded"]

[index]
version = 1
entries_key = "veps"
include_drafts = false

[[index.fields]]
name = "vep"
source = "metadata"
key = "vep"

[[index.fields]]
name = "id"
source = "identifier"

[[index.fields]]
name = "path"
source = "path"

[[index.fields]]
name = "requires"
source = "metadata"
key = "requires"

[[index.fields]]
name = "required_by"
source = "inverse"
key = "requires"

All configured paths must remain under the repository root. The schema and template files must already exist. Unknown policy keys are rejected so a typo cannot silently disable a check. The index may be absent before the first explicit index --write.

Title modes

metadata_title = "plain" keeps the identifier out of metadata and constructs the formal H1 as # VEP-0001: Title. metadata_title = "prefixed" expects the metadata title itself to start with VEP-0001: and requires the H1 to equal that title. The second mode supports existing processes that treat the display title as a single frontmatter field.

Draft policy

Omit [drafts] when the repository does not use proposal drafts. When it is configured, drafts use YAML frontmatter. drafts.schema selects a dedicated schema; if omitted, drafts use the formal proposal schema. A draft must not assign a proposal number, its H1 must match its metadata title, and an optional marker must immediately follow that H1.

pre_proposal = true models non-normative exploration before the proposal lifecycle. It additionally forbids status metadata, status sections, and concrete proposal IDs in the body. A lifecycle draft instead uses pre_proposal = false and must have status: Draft.

require_summary = true applies the configured Executive Summary policy to drafts. Set index.include_drafts = true only when drafts belong in the committed index. These independent settings cover the actual VEP pre-proposal and SEP lifecycle-draft contracts without teaching the shared tool their repository-specific schemas.

Templates

Each key under [templates] is a value accepted by the configured proposal type field. Every H2 heading in that template is required in documents of that type. Headings inside fenced code blocks are ignored. This keeps the executable section policy in the template instead of duplicating it in Python.

Graph policy

Every configured graph field rejects duplicate, self, and missing-target edges. Integer references and canonical strings such as VEP-0001 are normalized to the same identifier.

The optional role fields add semantic checks:

  • requires_field: rejects cycles and prevents an accepted proposal from transitively requiring a non-accepted proposal
  • amends_field: requires amendment targets to remain accepted
  • supersedes_field: validates coordinated supersession and exactly one accepted forward superseder

Index fields with source = "inverse" derive reverse edges without storing two writable authorities in proposal frontmatter.

History policy and waivers

When a base ref is supplied, the tool verifies that formal records were not deleted, proposal numbers were not reused under another path, new proposals begin at initial_status, and existing statuses follow the transition table.

Bootstrap proposal numbers may bypass the initial-status rule. A repository can also preserve a narrow historical exception without hard-coding it into the shared tool:

[[history.waivers]]
path = "proposals/VEP-0001-example.md"
from_status = "Accepted"
to_status = "Draft"
reason = "One-time bootstrap correction recorded by Process VEP-0000."

Waivers apply only to the exact path and transition. Their required reason is a review record, not a second lifecycle state.

Index fields

Index fields are emitted in configuration order. Supported sources are:

  • metadata: copy the configured frontmatter key
  • identifier: emit the formatted prefix and number
  • path: emit the repository-relative Markdown path
  • inverse: derive sources that point to this proposal through relation key

Documents sort by number, followed by unnumbered frontmatter drafts sorted by path. JSON uses UTF-8, two-space indentation, and one trailing newline.

JSON diagnostics

--json emits one object to standard output:

{
  "command": "check",
  "diagnostics": [
    {
      "code": "proposal.index.drift",
      "hint": "Run `zendev-proposal index --write` and commit the result.",
      "line": null,
      "message": "committed proposal index is missing or out of date",
      "path": "veps-index.json"
    }
  ],
  "ok": false,
  "schema_version": 1,
  "summary": {
    "drafts": 3,
    "formal_proposals": 1,
    "index": "drifted"
  }
}

Diagnostic codes and JSON keys are the automation contract. Human messages may be made more actionable without requiring consumers to parse prose.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

zendev_proposal-0.2.1-py3-none-any.whl (23.8 kB view details)

Uploaded Python 3

File details

Details for the file zendev_proposal-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for zendev_proposal-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 92199e0ea7339cde58f3c36fd608763a93eac9467cceb4210627e244c85d157c
MD5 a450e1ecd04d7ce0aa1efcbde8e33407
BLAKE2b-256 f72a89efdb0d13211a253ed4e0121b55fb7bf92f0688364937ab70f37299ac01

See more details on using hashes here.

Provenance

The following attestation bundles were made for zendev_proposal-0.2.1-py3-none-any.whl:

Publisher: cd-release.yml on zendev-lab/zendev

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.0

1 file

0.3.0

1 file

0.2.2

1 file

This release

0.2.1 This release

1 file

0.2.0

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page