Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

This book contains the governance documentation for govctl, an opinionated CLI for RFC-driven software development.

How This Book Is Organized

Specifications

RFCs (Requests for Comments) are the normative specifications that define govctl’s behavior. They are constitutional law — implementation must conform to them.

  • RFC-0000: The governance framework itself. Start here to understand the core concepts: RFCs, Clauses, ADRs, and Work Items.
  • RFC-0001: Lifecycle state machines for all artifact types.

Decisions

ADRs (Architectural Decision Records) document significant design choices. They explain why things are built a certain way.

Work Items

Work Items track units of work from inception to completion. They provide an audit trail of what was done and when.

The Data Model

All governance artifacts have a Single Source of Truth (SSOT) in the gov/ directory:

gov/
├── config.toml           # govctl configuration
├── rfc/                  # RFC-NNNN/rfc.json + clauses/
├── adr/                  # ADR-NNNN-*.toml
└── work/                 # WI-YYYY-MM-DD-NNN-*.toml

The markdown files in this book are rendered projections — generated from the SSOT by govctl render. Each includes a SHA-256 signature for tampering detection.

Phase Discipline

govctl enforces a strict phase lifecycle:

spec → impl → test → stable
  • spec: Defining what will be built. No implementation work permitted.
  • impl: Building what was specified.
  • test: Verifying implementation matches specification.
  • stable: Released for production use.

Phases cannot be skipped. This discipline ensures specifications precede implementation.

Getting Started

  1. Read RFC-0000 to understand the governance model
  2. Follow the Getting Started guide to install and initialize
  3. Learn about RFCs, ADRs, and Work Items

Getting Started

This guide walks you through installing govctl and creating your first governed artifact.

Requirements

  • Rust 1.94+ (uses edition 2024)

Installation

# From crates.io (includes TUI by default)
cargo install govctl

# Without TUI
cargo install govctl --no-default-features

# Or build from source
git clone https://github.com/govctl-org/govctl
cd govctl
cargo build --release
# Binary at ./target/release/govctl

Features

FeatureDefaultDescriptionDependencies
tuiYesInteractive terminal dashboard (govctl tui)ratatui, crossterm

Shell Completion

Generate completion scripts for your shell:

# Bash
govctl completions bash > ~/.local/share/bash-completion/completions/govctl

# Zsh (add to your .zshrc or install to completion directory)
govctl completions zsh > ~/.zsh/completions/_govctl
# Then add to fpath: fpath=(~/.zsh/completions $fpath)

# Fish
govctl completions fish > ~/.config/fish/completions/govctl.fish

# PowerShell (add to your profile)
govctl completions powershell >> $PROFILE

Restart your shell or source the configuration to enable tab completion.

Initialize a Project

govctl init

This creates the governance directory structure:

gov/
├── config.toml       # Configuration (project name, schema version, guards)
├── rfc/              # RFC sources (TOML)
├── adr/              # ADR sources (TOML)
├── work/             # Work item sources (TOML)
├── guard/            # Verification guards (TOML)
├── schema/           # JSON schemas for validation
└── releases.toml     # Release history

All governance artifacts use TOML with #:schema comment headers for IDE discoverability:

#:schema ../schema/adr.schema.json

[govctl]
id = "ADR-0001"
title = "My Decision"
status = "proposed"
...

Create Your First RFC

govctl rfc new "Feature Title"

This creates gov/rfc/RFC-0000/rfc.toml with the RFC metadata.

Add a Clause

RFCs are composed of clauses — atomic units of specification:

govctl clause new RFC-0000:C-SCOPE "Scope" -s "Specification" -k normative

Edit Clause Content

govctl clause edit RFC-0000:C-SCOPE text --stdin <<'EOF'
The feature MUST do X.
The feature SHOULD do Y.
EOF

View Artifacts

# Styled markdown to stdout
govctl rfc show RFC-0000
govctl adr show ADR-0001
govctl work show WI-2026-01-17-001
govctl clause show RFC-0000:C-SCOPE

# Interactive TUI dashboard
govctl tui

Validate Everything

govctl check

This validates all governance artifacts against JSON schemas, phase rules, cross-references, and source code annotations.

Render to Markdown

govctl render

Generates human-readable markdown in docs/.

Interactive TUI

govctl includes an optional interactive terminal dashboard:

govctl tui

TUI Keyboard Shortcuts

KeyAction
1 / rView RFCs
2 / aView ADRs
3 / wView Work Items
j / Navigate down
k / Navigate up
EnterOpen detail view
EscGo back
/Filter list (type query, Enter to apply)
gJump to top
GJump to bottom
Ctrl+dScroll half page down (detail view)
Ctrl+uScroll half page up (detail view)
PageDownScroll page down (detail view)
PageUpScroll page up (detail view)
?Toggle help overlay
qQuit

Cutting a Release

When a set of work items is complete and ready for release:

# Collect all unreleased done work items into a version
govctl release 0.2.0

# Specify a custom date
govctl release 0.2.0 --date 2026-04-15

This records the release in gov/releases.toml and makes those work items available for changelog generation.

Adopting govctl in an Existing Project

govctl init is safe to run in existing repositories — it only creates the gov/ directory structure alongside existing files.

For AI-assisted migration, use the /migrate skill to systematically discover undocumented decisions, backfill ADRs, and annotate source code with [[...]] references.

govctl migrate vs the /migrate Skill

govctl migrate/migrate skill
WhatUpgrade existing govctl artifacts to current formatAdopt govctl in an existing project
WhenAfter updating govctl versionWhen starting governance in a brownfield repo
EffectRewrites TOML/JSON files in gov/Discovers decisions, backfills ADRs, annotates source
RiskLow — transactional, reversibleMedium — requires human review of generated ADRs

Run govctl migrate when govctl tells you a migration is needed (error E0505). Use the /migrate skill when bringing a legacy project under governance for the first time.

Canonical Edit Surface

All artifact fields are accessible through a unified path-based edit interface:

# Set a scalar value
govctl rfc edit RFC-0010 version --set 1.2.0

# Add to an array
govctl adr edit ADR-0003 refs --add RFC-0010

# Remove by index
govctl work edit WI-2026-01-17-001 acceptance_criteria --at 0 --remove

# Tick checklist items
govctl adr edit ADR-0003 alternatives --tick accepted --at 0
govctl work edit WI-2026-01-17-001 acceptance_criteria --tick done --at 0

Nested object fields use dot-delimited paths:

govctl adr edit ADR-0003 content.decision --set "We will use Redis"
govctl adr edit ADR-0003 "content.alternatives[0].pros" --add "Low latency"
govctl work edit WI-2026-01-17-001 "journal[0].scope" --set backend

CLI Self-Description

govctl provides a machine-readable command catalog:

govctl describe
govctl describe --context   # Includes project context
govctl describe --output json

This is designed for agent discoverability — agents can inspect available commands and their semantics without hardcoded knowledge.

Next Steps

Working with RFCs

RFCs (Requests for Comments) are the normative specifications in govctl. They define what will be built before implementation begins.

Creating RFCs

# Auto-assign next available ID
govctl rfc new "Feature Title"

# Specify ID manually
govctl rfc new "Feature Title" --id RFC-0010

RFC Structure

An RFC consists of:

  • Metadata (rfc.toml) — ID, title, status, phase, version, owners
  • Clauses (clauses/*.toml) — Atomic units of specification

The TOML files use #:schema headers and a [govctl] + [content] layout:

#:schema ../../schema/rfc.schema.json

[govctl]
id = "RFC-0010"
title = "Feature Title"
version = "0.1.0"
status = "draft"
phase = "spec"
owners = ["@you"]
created = "2026-03-17"
refs = []

[content]
summary = "Brief summary of this RFC."

Tagging RFCs

Once tags are registered in the project vocabulary, apply them to RFCs:

govctl rfc edit RFC-0010 tags --add caching
govctl rfc edit RFC-0010 tags --add api

Filter lists by tag:

govctl rfc list --tag caching
govctl rfc list --tag caching,api

Canonical Edit Surface

All RFC and clause fields are accessible through path-based editing:

# Lifecycle-managed fields use dedicated verbs
govctl rfc bump RFC-0010 --minor -m "Add new clause for edge case"
govctl rfc finalize RFC-0010 normative

# Add to array fields
govctl rfc edit RFC-0010 refs --add RFC-0001
govctl rfc edit RFC-0010 owners --add "@co-maintainer"

# Remove by index or pattern
govctl rfc edit RFC-0010 refs --at 0 --remove
govctl rfc edit RFC-0010 owners --remove "@old-owner" --exact

# Edit clause text
govctl clause edit RFC-0010:C-SCOPE text --stdin <<'EOF'
New clause text here
EOF

Working with Clauses

Create a Clause

govctl clause new RFC-0010:C-SCOPE "Scope" -s "Specification" -k normative

Options:

  • -s, --section — Section name (e.g., “Specification”, “Rationale”)
  • -k, --kindnormative (binding) or informative (explanatory)

Clause files use the same [govctl] + [content] layout:

#:schema ../../../schema/clause.schema.json

[govctl]
id = "C-SCOPE"
title = "Scope"
kind = "normative"
status = "active"
since = "0.1.0"

[content]
text = """
The system MUST validate all inputs."""

Edit Clause Text

# From stdin (recommended for multi-line)
govctl clause edit RFC-0010:C-SCOPE text --stdin <<'EOF'
The system MUST validate all inputs.
The system SHOULD log validation failures.
EOF

# Inline text
govctl clause edit RFC-0010:C-SCOPE text --set "The system MUST validate all inputs."

# From file
govctl clause edit RFC-0010:C-SCOPE text --stdin < clause-text.md

Delete a Clause

Accidentally created clauses can be deleted from draft RFCs only:

govctl clause delete RFC-0010:C-MISTAKE -f

Safety: Deletion is only allowed when:

  • The RFC status is draft (normative RFCs are immutable)
  • No other artifacts reference the clause

For normative RFCs, use govctl clause deprecate RFC-0010:C-OLD instead.

List Clauses

govctl clause list
govctl clause list RFC-0010
govctl clause list --tag core    # Filter by tag

View a Clause

govctl clause show RFC-0010:C-SCOPE

Status Lifecycle

RFCs have three statuses:

draft → normative → deprecated

Finalize to Normative

When the spec is complete and approved:

govctl rfc finalize RFC-0010 normative

This makes the RFC binding — implementation must conform to it.

Deprecate

When an RFC is superseded or obsolete:

govctl rfc finalize RFC-0010 deprecated

Phase Lifecycle

RFCs progress through four phases:

spec → impl → test → stable

Advance Phase

govctl rfc advance RFC-0010 impl    # Ready for implementation
govctl rfc advance RFC-0010 test    # Implementation complete, ready for testing
govctl rfc advance RFC-0010 stable  # Tested, ready for production

Phase transitions are gated:

  • spec → impl requires status = normative
  • Each phase has invariants that must be satisfied

Versioning

RFCs use semantic versioning:

# Bump version with changelog entry
govctl rfc bump RFC-0010 --patch -m "Fix typo in clause C-SCOPE"
govctl rfc bump RFC-0010 --minor -m "Add new clause for edge case"
govctl rfc bump RFC-0010 --major -m "Breaking change to API contract"

Listing and Viewing

govctl rfc list
govctl rfc list normative    # Filter by status
govctl rfc list impl         # Filter by phase
govctl rfc list --tag api    # Filter by tag
govctl rfc show RFC-0010     # Styled markdown to stdout

Working with ADRs

ADRs (Architectural Decision Records) document significant design choices. They explain why things are built a certain way.

Creating ADRs

govctl adr new "Use Redis for caching"

This creates a TOML file in gov/adr/ with the decision context.

ADR Structure

ADRs are TOML files with #:schema headers:

#:schema ../schema/adr.schema.json

[govctl]
id = "ADR-0003"
title = "Use Redis for caching"
status = "proposed"
date = "2026-03-17"
refs = ["RFC-0001"]

[content]
context = "We need a caching layer for..."
decision = "We will use Redis because..."
consequences = "Positive: faster reads. Negative: operational complexity."

[[content.alternatives]]
text = "Memcached"
pros = ["Simpler"]
cons = ["No persistence"]
rejection_reason = "Persistence is required for our use case"

ADRs contain:

  • Context — The situation requiring a decision
  • Decision — What was decided
  • Consequences — Expected outcomes (positive and negative)
  • Alternatives — Options considered with pros, cons, and rejection reasons (per [[ADR-0027]])
  • Statusproposed, accepted, rejected, or superseded

Editing ADRs

Use govctl to get/set fields:

# Get specific field
govctl adr get ADR-0003 status

# Set content field value
govctl adr set ADR-0003 decision "We will use Redis because..."

# Set multi-line content from stdin
govctl adr set ADR-0003 context --stdin <<'EOF'
We need a caching layer that can handle
10k requests per second with sub-millisecond latency.
EOF

Canonical Edit Paths

All ADR fields are accessible through a unified path-based edit interface:

# Scalar fields
govctl adr edit ADR-0003 content.decision --set "We will use Redis"
govctl adr edit ADR-0003 content.context --stdin < context.md

# Array fields — add, remove, tick
govctl adr edit ADR-0003 refs --add RFC-0010
govctl adr edit ADR-0003 refs --at 0 --remove

# Nested alternatives
govctl adr edit ADR-0003 content.alternatives --add "Option C: Use etcd"
govctl adr edit ADR-0003 "content.alternatives[0].pros" --add "Fast reads"
govctl adr edit ADR-0003 "content.alternatives[0].cons" --add "Operational cost"
govctl adr edit ADR-0003 "content.alternatives[0].status" --set accepted
govctl adr edit ADR-0003 "content.alternatives[0].rejection_reason" --set "Too complex"

# Tick alternative status
govctl adr edit ADR-0003 content.alternatives --tick accepted --at 0

Path aliases are available for common fields:

AliasResolves to
decisioncontent.decision
contextcontent.context
consequencescontent.consequences
altcontent.alternatives
procontent.alternatives[i].pros
concontent.alternatives[i].cons
reasoncontent.alternatives[i].rejection_reason

Legacy Set/Add/Remove Verbs

The original verbs remain available and compile into the same edit pipeline:

govctl adr set ADR-0003 decision "We will use Redis because..."
govctl adr add ADR-0003 alternatives "Option C"
govctl adr remove ADR-0003 refs RFC-0001

Tagging ADRs

Once tags are registered in the project vocabulary, apply them to ADRs:

govctl adr edit ADR-0003 tags --add caching
govctl adr edit ADR-0003 tags --add performance

Filter lists by tag:

govctl adr list --tag caching
govctl adr list --tag caching,performance

Status Lifecycle

proposed → accepted → superseded
         ↘ rejected

Accept a Decision

Before accepting, the ADR must have at least 2 alternatives with 1 accepted and 1 rejected per [[ADR-0042]]:

govctl adr edit ADR-0003 "content.alternatives[0].status" --set accepted
govctl adr edit ADR-0003 "content.alternatives[1].status" --set rejected

govctl adr accept ADR-0003

Use --force for historical backfills where alternatives cannot be reconstructed:

govctl adr accept ADR-0003 --force

When consensus is reached:

govctl adr accept ADR-0003

Deprecate

When a decision is no longer relevant:

govctl adr deprecate ADR-0003

Supersede

When a new decision replaces an old one:

govctl adr supersede ADR-0001 --by ADR-0005

This marks ADR-0001 as superseded and records ADR-0005 as its replacement.

Listing and Viewing

govctl adr list
govctl adr list accepted    # Filter by status
govctl adr show ADR-0003    # Styled markdown to stdout

Working with Work Items

Work Items track units of work from inception to completion. They provide an audit trail of what was done and when.

See also: Tags, TUI, Canonical Edit

Creating Work Items

# Create in queue (pending)
govctl work new "Implement caching layer"

# Create and activate immediately
govctl work new --active "Urgent bug fix"

Work items are automatically assigned IDs like WI-2026-01-17-001.

Work Item Structure

Work items are TOML files with #:schema headers:

#:schema ../schema/work.schema.json

[govctl]
id = "WI-2026-01-17-001"
title = "Implement caching layer"
status = "active"
started = "2026-01-17"
refs = ["RFC-0010", "ADR-0003"]

[content]
description = "Add Redis caching for the query endpoint."

[[content.acceptance_criteria]]
text = "add: Cache invalidation on write"
status = "pending"

[[content.acceptance_criteria]]
text = "chore: govctl check passes"
status = "pending"

[[content.journal]]
date = "2026-01-17"
text = "Implemented core logic"
scope = "backend"

[[content.notes]]
text = "Do not retry the old validation path"

Work items contain:

  • Title — Brief description
  • Description — Task scope declaration
  • Journal — Execution tracking entries with date and scope (per [[ADR-0026]])
  • Notes — Durable learnings and constraints
  • Acceptance Criteria — Checkable completion criteria with changelog category
  • Refs — Links to related RFCs, ADRs, or external resources

Status Lifecycle

queue → active → done
    ↘        ↘ cancelled

Move Between States

# By ID
govctl work move WI-2026-01-17-001 active
govctl work move WI-2026-01-17-001 done

# By filename (without path)
govctl work move implement-caching.toml active

Moving to done requires all verification guards to pass (see Validation).

Acceptance Criteria

Add Criteria

govctl work add WI-2026-01-17-001 acceptance_criteria "chore: Unit tests pass"
govctl work add WI-2026-01-17-001 acceptance_criteria "add: Documentation updated"

Category prefixes (add:, fix:, change:, chore:, etc.) are required and drive changelog generation. Conventional-commit aliases like feat:, refactor:, test:, docs: are also accepted.

Canonical changelog categories are still the preferred form in stored artifacts. The conventional-commit aliases are accepted as input sugar and normalized into the changelog model.

Mark Criteria Complete

govctl work tick WI-2026-01-17-001 acceptance_criteria "Unit tests" -s done

The pattern matches case-insensitively by substring.

Canonical Edit Paths

Most work item fields are accessible through the unified path-based edit interface. Lifecycle-managed fields (such as status) are excluded — use govctl work move for status transitions instead.

# Set scalar fields
govctl work edit WI-2026-01-17-001 content.description --stdin <<'EOF'
New description here
EOF

# Add to array fields
govctl work edit WI-2026-01-17-001 refs --add RFC-0010
govctl work edit WI-2026-01-17-001 acceptance_criteria --add "fix: Handle edge case"

# Remove by index
govctl work edit WI-2026-01-17-001 acceptance_criteria --at 0 --remove

# Tick checklist items
govctl work edit WI-2026-01-17-001 acceptance_criteria --tick done --at 0
govctl work edit WI-2026-01-17-001 acceptance_criteria --tick cancelled --at 1

# Nested journal fields
govctl work edit WI-2026-01-17-001 "journal[0].scope" --set backend
govctl work edit WI-2026-01-17-001 "journal[0].content" --stdin <<'EOF'
Detailed progress update here
EOF

Path aliases are available for common fields:

AliasResolves to
descriptioncontent.description
accontent.acceptance_criteria
journalcontent.journal
notescontent.notes
categorycontent.acceptance_criteria[i].category
scopecontent.journal[i].scope

Tagging Work Items

Once tags are registered in the project vocabulary, apply them to work items:

govctl work edit WI-2026-01-17-001 tags --add backend
govctl work edit WI-2026-01-17-001 tags --add performance

Filter lists by tag:

govctl work list --tag backend
govctl work list --tag backend,performance

Journal

Track execution progress with dated journal entries:

govctl work add WI-2026-01-17-001 journal "Implemented core logic"

# With scope tag
govctl work add WI-2026-01-17-001 journal "Fixed edge case" --scope parser

# Multi-line via stdin
govctl work add WI-2026-01-17-001 journal --scope backend --stdin <<'EOF'
Completed the API layer.
All integration tests passing.
EOF

Per-Work-Item Guards

Work items can require extra verification guards in addition to the project’s default guard set.

Example:

[verification]
required_guards = ["GUARD-CLIPPY"]

This means:

  • GUARD-CLIPPY is required for this work item even if it is not a project default
  • project defaults from gov/config.toml still apply when verification is enabled
  • the work item cannot move to done until the effective required guards pass or are explicitly waived

To run the effective guard set for a single work item:

govctl verify --work WI-2026-01-17-001

Waiving A Guard

If a specific guard must be waived for this work item, record that in the artifact with a reason:

[[verification.waivers]]
guard = "GUARD-CARGO-TEST"
reason = "Temporarily flaky on macOS runners; tracked in issue #123"

Waivers are per-work-item only. They do not disable the guard globally, and they should remain rare and justified.

Notes

Add durable notes for future steps:

govctl work add WI-2026-01-17-001 notes "Do not retry the old validation path; it fails on missing refs"

Nested path edits are also available for structured fields:

govctl work set WI-2026-01-17-001 "journal[0].scope" "parser"

Removing Items

Remove items from array fields using flexible matching:

# Substring match (default, case-insensitive)
govctl work remove WI-2026-01-17-001 notes "edge case"

# Exact match
govctl work remove WI-2026-01-17-001 notes "Discovered edge case in validation" --exact

# By index (0-based)
govctl work remove WI-2026-01-17-001 notes --at 0

# Negative index (from end)
govctl work remove WI-2026-01-17-001 notes --at -1

# Regex pattern
govctl work remove WI-2026-01-17-001 refs "RFC-.*" --regex

# Remove all matches
govctl work remove WI-2026-01-17-001 refs "obsolete" --all

Deleting Work Items

Accidentally created work items can be deleted if they’re still in queue status:

govctl work delete WI-2026-01-17-999 -f

Safety: Deletion is only allowed when:

  • The work item status is queue (never activated)
  • No other artifacts reference it

For work items that have been activated, use status transitions instead:

govctl work move WI-2026-01-17-001 cancelled

Listing and Viewing

govctl work list
govctl work list queue      # Pending items
govctl work list active     # In progress
govctl work list done       # Completed
govctl work show WI-2026-01-17-001  # Styled markdown to stdout

Validation & Rendering

govctl provides tools to validate governance artifacts, enforce completion gates, and render human-readable documentation.

Validation

Check All Artifacts

govctl check

This validates:

  • Schema conformance — All required fields present, correct types
  • Phase discipline — No invalid state transitions
  • Cross-referencesrefs and [[...]] annotations point to existing artifacts
  • Controlled-vocabulary tags — All artifact tags are registered in gov/config.toml [tags] allowed
  • Clause structure — Normative clauses in spec sections
  • Source code scanning[[RFC-0001]] annotations in source files are verified

Exit Codes

  • 0 — All validations passed
  • 1 — Validation errors found

Source Code Scanning

govctl scans source files for [[artifact-id]] annotations and verifies they reference existing, non-deprecated artifacts:

#![allow(unused)]
fn main() {
// Implements [[RFC-0001:C-VALIDATION]]
fn validate() { ... }

// Per [[ADR-0005]], we use semantic colors
}

Configure scanning in gov/config.toml:

[source_scan]
enabled = true
include = ["src/**/*.rs"]
exclude = []

Controlled-Vocabulary Tags

Tags provide cross-cutting categorization across all governance artifacts. Every tag must be registered in a project-level allow list before use.

Managing the Tag Registry

# List all registered tags with usage counts
govctl tag list

# Register a new tag
govctl tag new caching

# Remove a tag (fails if any artifact still uses it)
govctl tag delete caching

Tagging Artifacts

Once a tag is registered, apply it to any artifact via the standard edit command with --add:

govctl rfc edit RFC-0010 tags --add caching
govctl adr edit ADR-0003 tags --add caching
govctl work edit WI-2026-01-17-001 tags --add caching

Filtering by Tag

List commands support --tag to filter by one or more tags (comma-separated, AND logic):

govctl rfc list --tag caching
govctl adr list --tag caching,performance
govctl work list --tag breaking-change

Tags are validated at govctl check time — any tag not in the allow list produces error E1105.

Verification Guards

Guards are executable completion checks that run automatically when a work item moves to done. They prevent work items from closing unless all configured checks pass.

How Guards Work

When you run govctl work move <WI-ID> done, govctl executes each guard defined in gov/config.toml:

[verification]
enabled = true
default_guards = ["GUARD-GOVCTL-CHECK", "GUARD-CARGO-TEST"]

Each guard is a TOML file in gov/guard/:

#:schema ../schema/guard.schema.json

[govctl]
id = "GUARD-CARGO-TEST"
title = "cargo test passes"
refs = ["RFC-0000"]

[check]
command = "cargo test"
timeout_secs = 300

Guard Subcommands

Guards are first-class resources with their own CRUD verbs:

# Create a new guard
govctl guard new "My Lint Check"

# List all guards
govctl guard list

# Show guard definition
govctl guard show GUARD-MY-LINT

# Set guard fields
govctl guard edit GUARD-MY-LINT check.command --set "npm run lint"
govctl guard edit GUARD-MY-LINT check.timeout_secs --set 60

# Delete a guard (blocked if still referenced by work items or project defaults)
govctl guard delete GUARD-MY-LINT

Guard Fields

FieldRequiredDescription
idYesUnique guard identifier (e.g., GUARD-LINT)
titleYesHuman-readable description
refsNoRelated RFCs/ADRs
commandYesShell command to execute from project root
timeout_secsNoMax execution time (default: 300s)
patternNoRegex pattern that must match stdout+stderr

Guard Behavior

  • A guard passes when its command exits with code 0 (and matches pattern if specified)
  • A guard fails when the command exits non-zero, times out, or doesn’t match the pattern
  • All guards must pass before govctl work move <WI-ID> done succeeds

Running Guards Independently

Use govctl verify to run guards without moving a work item:

# Run all project default guards
govctl verify

# Run specific guards
govctl verify GUARD-CARGO-TEST GUARD-GOVCTL-CHECK

# Run guards required by a specific work item
govctl verify --work WI-2026-01-17-001

Per-Work-Item Guards

Project-level default_guards are only part of the picture. A work item can also require additional guards of its own:

[verification]
required_guards = ["GUARD-CLIPPY"]

This is useful when one work item needs an extra check that should not become a project-wide default.

The effective required guard set for a work item is:

  • the project-level default_guards when verification is enabled
  • plus the work item’s verification.required_guards
  • minus any explicitly waived guards

Guard Waivers

If a guard must be waived for a specific work item, record that explicitly with a reason:

[[verification.waivers]]
guard = "GUARD-CARGO-TEST"
reason = "Flaky on CI runner image; tracked in issue #123"

Waivers are scoped to a single work item. They do not disable verification globally, and they should be treated as an exception that must be explained.

Rendering

Render governance artifacts to markdown for documentation.

Render All

govctl render            # RFCs to docs/rfc/
govctl render adr        # ADRs to docs/adr/
govctl render work       # Work items to docs/work/
govctl render all        # Everything
govctl render changelog  # CHANGELOG.md

Render Single Items

govctl rfc render RFC-0010
govctl adr render ADR-0005
govctl work render WI-2026-01-17-001

View Without Writing Files

The show commands render styled markdown to stdout without writing files:

govctl rfc show RFC-0010
govctl adr show ADR-0005
govctl work show WI-2026-01-17-001
govctl clause show RFC-0010:C-SCOPE

Hash Signatures

Rendered markdown includes a SHA-256 signature for tampering detection:

<!-- SIGNATURE: sha256:abc123... -->

If the source changes, the signature won’t match — indicating the rendered doc is stale.

Project Status

govctl status

Shows RFC/ADR/work item counts by status, phase breakdown, and active work items.

CLI Self-Description

govctl provides a machine-readable command catalog for agent discoverability:

govctl describe
govctl describe --context   # Includes project context (RFCs, ADRs, active work items)
govctl describe --output json

Self-Update

Update govctl to the latest release:

govctl self-update          # Download and replace binary
govctl self-update --check  # Check for newer version without downloading

Supports GITHUB_TOKEN environment variable for authenticated API requests.

Agent Skill Installation

Install or update govctl’s agent skills and reviewer agents for your AI coding tool:

# Claude Code (default)
govctl init-skills

# Codex CLI
govctl init-skills --format codex

# Custom output directory
govctl init-skills --dir /path/to/agent-config

This writes workflow skills (/gov, /quick, /discuss, /commit) and reviewer agents (RFC/ADR/WI reviewer, compliance checker) to the configured agent directory.

Schema Migration

When the governance schema evolves between govctl versions, artifact files may need format upgrades:

govctl migrate

This upgrades artifact file formats (e.g., adding #:schema headers, converting JSON to TOML) with transactional safety — changes are staged, backed up, and committed atomically.

govctl migrate vs the /migrate Workflow

These are related but serve different purposes:

govctl migrate/migrate skill
WhatUpgrade existing govctl artifacts to current formatAdopt govctl in an existing project
WhenAfter updating govctl versionWhen starting governance in a brownfield repo
EffectRewrites TOML/JSON files in gov/Discovers decisions, backfills ADRs, annotates source
RiskLow — transactional, reversibleMedium — requires human review of generated ADRs

Run govctl migrate when govctl tells you a migration is needed (error E0505). Use the /migrate skill when bringing a legacy project under governance for the first time.

RFC-0000: govctl Governance Framework

Version: 1.1.1 | Status: normative | Phase: stable


1. Summary

[RFC-0000:C-SUMMARY] Framework Summary (Informative)

govctl is a governance CLI that manages six artifact types:

  • RFCs: Normative specifications that define intent and constraints
  • Clauses: Individual requirements within RFCs
  • ADRs: Architectural Decision Records documenting design choices
  • Work Items: Units of work tracking implementation progress
  • Releases: Published versions and the work items included in each version
  • Verification Guards: Reusable executable checks that turn completion requirements into auditable pass/fail gates

All artifacts follow explicit lifecycle states, phase gates, or immutability rules to ensure disciplined development.

Tags: core

Since: v1.0.0


2. RFC Specification

[RFC-0000:C-RFC-DEF] RFC Definition (Normative)

An RFC (Request for Comments) is a normative document that defines intent, constraints, or decisions governing implementation.

An RFC is not a suggestion. It is law.

The current canonical RFC storage format is TOML. New repositories and repositories that have completed the RFC/clause migration MUST store each RFC as a TOML file (rfc.toml) containing:

  • [govctl] section with: id, title, version, status, phase, owners, created
  • optional [govctl] fields: updated, supersedes, refs, signature
  • [[sections]] array with ordered section definitions and clause references
  • [[changelog]] array for version history

Format evolution is tracked by the project-level [schema] version in gov/config.toml, not per-artifact fields.

Entries in sections[].clauses MUST reference clause TOML files by relative path (for example clauses/C-EXAMPLE.toml) in migrated repositories.

Repositories that have not yet completed the RFC/clause migration MAY retain legacy JSON RFC files temporarily. After govctl migrate has completed for a repository, normal govctl operations MUST create and modify RFCs only in TOML form.

Rationale: This removes the last storage-format split from RFC handling without forcing a silent in-place break for repositories that still need an explicit migration step.

Tags: core, schema

Since: v1.0.0

[RFC-0000:C-STATUS-LIFECYCLE] RFC Status Lifecycle (Normative)

RFC status follows this lifecycle:

draft → normative → deprecated

draft: Under discussion. Implementation MUST NOT depend on draft RFCs.

normative: Frozen and ratified. Implementation MUST conform to normative RFCs.

deprecated: Superseded or obsolete. Implementation SHOULD migrate away.

Transition rules:

  • draft → normative: Requires explicit finalization
  • normative → deprecated: Requires a superseding RFC or explicit deprecation
  • Reverse transitions are FORBIDDEN

Tags: core, lifecycle

Since: v1.0.0

[RFC-0000:C-PHASE-LIFECYCLE] RFC Phase Lifecycle (Normative)

RFC phase follows this lifecycle:

spec → impl → test → stable

spec: Defining what will be built. No implementation work permitted.

impl: Building what was specified. Implementation proceeds per spec.

test: Verifying implementation matches specification.

stable: Released for production use.

Phase rules:

  • Phases MUST proceed in order; skipping is FORBIDDEN
  • Each phase transition requires the previous phase gate to pass
  • draft + stable is FORBIDDEN (cannot stabilize unratified work)
  • deprecated + impl/test is FORBIDDEN (no new work on deprecated specs)

Tags: core, lifecycle

Since: v1.0.0

[RFC-0000:C-REFERENCE-HIERARCHY] Artifact Reference Hierarchy (Normative)

[govctl] id = “C-REFERENCE-HIERARCHY” title = “Artifact Reference Hierarchy” kind = “normative” status = “active” since = “1.0.1”

[content] text = “”“ Governance artifacts follow a strict authority hierarchy. References between artifact types MUST respect this hierarchy.

Authority Order (highest to lowest):

  1. RFC — Constitutional law. Defines what the system does.
  2. ADR — Interpretation. Documents decisions implementing RFCs.
  3. Work Item — Execution. Tracks work implementing ADRs and RFCs.

Structured reference rules (refs fields and [[…]] link targets):

Implementations MUST validate these rules during project validation (for example govctl check).

  • RFC refs entries and [[...]] link targets MUST NOT identify an ADR or a Work Item. RFCs MUST link only to RFCs and RFC clause references (RFC-ID:C-CLAUSE).

  • ADR refs entries and [[...]] link targets MUST NOT identify a Work Item. ADRs MAY link to RFCs (including clause references) and to ADRs.

  • Work Item refs entries and [[...]] link targets MAY identify any artifact type.

Rationale:

This hierarchy prevents circular dependencies and maintains clear authority chains. An RFC that links to an ADR or Work Item in refs or [[...]] inverts the dependency direction — lower layers become authorities over the specification.

Prose outside refs and [[…]]:

Normative RFC clause text SHOULD remain self-contained. Plain prose (without [[...]]) MAY cite artifact identifiers for explanation; that form is not governed by the structured rules above.“”“

Tags: core, validation

Since: v1.0.1


3. Clause Specification

[RFC-0000:C-CLAUSE-DEF] Clause Definition (Normative)

A clause is an individual requirement or statement within an RFC.

The current canonical clause storage format is TOML. New repositories and repositories that have completed the RFC/clause migration MUST store each clause as a TOML file containing:

  • [govctl] section with: id, title, kind, status
  • optional [govctl] fields: since, superseded_by, anchors
  • [content] section with: text

Format evolution is tracked by the project-level [schema] version in gov/config.toml, not per-artifact fields.

After govctl migrate has completed for a repository, normal govctl operations MUST create and modify clauses only in TOML form.

Repositories that have not yet completed the RFC/clause migration MAY retain legacy JSON clause files temporarily.

Clause kinds:

  • normative: Defines a requirement. Implementations MUST comply.
  • informative: Provides context. No compliance requirement.

Deprecation is a lifecycle state (in status), not a document kind.

Rationale: This aligns clause storage with RFC, ADR, Work Item, and Release storage while preserving an explicit migration boundary for existing repositories.

Tags: core, schema

Since: v1.0.0


4. ADR Specification

[RFC-0000:C-ADR-DEF] ADR Definition (Normative)

An ADR (Architectural Decision Record) documents a significant design decision.

Every ADR MUST be stored as a TOML file containing:

  • [govctl] section with: id, title, status, date, refs
  • optional [govctl] field: superseded_by
  • [content] section with: context, decision, consequences

Format evolution is tracked by the project-level [schema] version in gov/config.toml, not per-artifact fields.

Implementations MUST define a machine-readable JSON Schema for the ADR file structure and MUST validate ADR files against that schema during project validation.

ADR status lifecycle:

proposed → accepted → superseded
         → rejected

proposed: Under consideration. Not yet binding.

accepted: Ratified. Design SHOULD follow this decision.

rejected: Declined after consideration. Reason documented in decision field.

superseded: Replaced by a newer ADR. Listed in superseded_by field.

Tags: core, schema

Since: v1.0.0


5. Work Item Specification

[RFC-0000:C-WORK-DEF] Work Item Definition (Normative)

A Work Item tracks a unit of work from inception to completion.

Every Work Item MUST be stored as a TOML file containing:

  • [govctl] section with: id, title, status, created, refs
  • optional [govctl] fields: started, completed
  • [content] section with: description, acceptance_criteria, journal, notes
  • optional [verification] section with: required_guards, waivers

Format evolution is tracked by the project-level [schema] version in gov/config.toml, not per-artifact fields.

Implementations MUST define a machine-readable JSON Schema for the Work Item file structure and MUST validate Work Item files against that schema during project validation.

When present, verification.required_guards MUST be an array of Verification Guard IDs defined by RFC-0000:C-GUARD-DEF. These guard IDs augment project-level default guard requirements when project verification is enabled.

When present, each verification.waivers entry MUST be an object with exactly two fields: guard and reason.

The guard field MUST name exactly one Verification Guard ID.

The reason field MUST be a non-empty string.

Waiver entries MUST reference only guards that appear in the work item’s effective required guard set. Duplicate waivers for the same guard MUST be rejected during validation.

A waiver MUST suppress only the named guard. A Work Item MUST NOT disable the verification system globally.

Work Item status lifecycle:

queue → active → done
    ↘        ↘ cancelled

queue: Planned but not started.

active: Currently in progress. Only one active item recommended per focus area.

done: Completed. All acceptance criteria met, and any required verification guards have passed or been explicitly waived.

cancelled: Abandoned from queue or active. Reason documented in notes.

A Work Item MUST NOT transition to done if any acceptance_criteria are pending.

Tags: core

Since: v1.0.0


6. Release Specification

[RFC-0000:C-RELEASE-DEF] Release Definition (Normative)

A Release tracks a published version and the set of work items included in that version.

Release data MUST be stored as a TOML file at gov/releases.toml containing:

  • [[releases]] array with entries containing: version, date, refs

Format evolution is tracked by the project-level [schema] version in gov/config.toml, not per-artifact fields.

Each releases[].version MUST be a valid semantic version.

Each releases[].date MUST use ISO 8601 calendar date format YYYY-MM-DD.

Each releases[].refs entry MUST reference an existing Work Item ID.

A release entry has no lifecycle and MUST be treated as immutable once created.

Implementations MUST define a machine-readable JSON Schema for the release file structure and MUST validate gov/releases.toml against that schema during project validation.

Rationale: Releases are first-class governance artifacts because changelog generation and release membership depend on them. Giving releases a defined TOML structure and required JSON Schema removes the remaining schema-less storage special case.

Tags: core, release

Since: v1.0.2


7. Verification Guard Specification

[RFC-0000:C-GUARD-DEF] Verification Guard Definition (Normative)

A Verification Guard defines a reusable executable completion check for a govctl project.

Every Verification Guard MUST be stored as a TOML file under gov/guard/ containing:

  • [govctl] section with: id, title
  • optional [govctl] field: refs
  • [check] section with: command
  • optional [check] fields: timeout_secs, pattern

Format evolution is tracked by the project-level [schema] version in gov/config.toml, not per-artifact fields.

Implementations MUST define a machine-readable JSON Schema for the Verification Guard file structure and MUST validate guard files during project validation.

Verification Guard IDs MUST be unique within a repository.

A guard check MUST execute its command non-interactively from the project root.

If timeout_secs is absent, implementations MUST use a default timeout of 300 seconds.

A guard check that exceeds its timeout MUST fail.

A guard check MUST evaluate pattern, when present, against the combined standard output and standard error streams using case-insensitive regular-expression matching.

A guard check MUST pass only when the command exits successfully. If pattern is provided, the combined output MUST also match that pattern.

Rationale: Verification Guards make completion rules explicit, reusable, and machine-executable so agents cannot satisfy them with checklist text alone.

Tags: core, validation

Since: v1.1.0


Changelog

v1.1.1 (2026-03-22)

Clarify machine-validated reference hierarchy for refs and [[…]]

Added

  • Structured rules: RFC must not link ADR/WI; ADR must not link WI

v1.1.0 (2026-03-17)

Add verification guards and work-item verification metadata

Added

  • Added Verification Guard artifact definition and work-item verification fields

v1.0.2 (2026-03-17)

Record release artifact clause and migration amendments

v1.0.1 (2026-01-26)

Add artifact reference hierarchy clause

Added

  • Add C-REFERENCE-HIERARCHY clause defining that RFCs must not reference ADRs

v1.0.0 (2026-01-17)

Initial stable release of the govctl governance framework.

Added

  • RFC specification with status and phase lifecycle
  • Clause specification for normative and informative requirements
  • ADR specification for architectural decisions
  • Work Item specification for task tracking

RFC-0001: Lifecycle State Machines

Version: 0.3.0 | Status: normative | Phase: stable


1. Summary

[RFC-0001:C-SUMMARY] Summary (Informative)

This RFC defines state machines governing RFC, ADR, Work Item, and Clause lifecycles, including the additional transition gates that control when work may be treated as complete.

Tags: lifecycle

Since: v0.1.0


2. Specification

[RFC-0001:C-RFC-STATUS] RFC Status Transitions (Normative)

An RFC MUST have exactly one of the following status values:

  1. draft — Initial state. The RFC is under development and not yet binding.
  2. normative — The RFC defines required behavior. Implementations MUST conform. Normative RFCs MAY be amended via version bumping; amendments MUST include a changelog entry documenting the change.
  3. deprecated — The RFC is no longer recommended. Existing conforming implementations MAY continue, but new implementations SHOULD NOT use this RFC.

Valid transitions:

  • draft → normative (via finalize command)
  • normative → deprecated (via deprecate command)

Invalid transitions (MUST be rejected):

  • normative → draft (no “un-finalize”)
  • deprecated → normative (no resurrection)
  • deprecated → draft (no resurrection)
  • Any skip (e.g., draft → deprecated directly)

Tags: lifecycle

Since: v0.1.0

[RFC-0001:C-RFC-PHASE] RFC Phase Transitions (Normative)

An RFC MUST have exactly one of the following phase values:

  1. spec — Specification phase. The RFC text is being written. No implementation work.
  2. impl — Implementation phase. Code is being written to conform to the RFC.
  3. test — Testing phase. Implementation is complete; tests are being written and validated.
  4. stable — Stable phase. Implementation and tests are complete. No further changes expected.

Valid transitions (forward only, via advance command):

  • spec → impl
  • impl → test
  • test → stable

Invalid transitions (MUST be rejected):

  • Any backward transition (e.g., impl → spec)
  • Any skip (e.g., spec → test, spec → stable)
  • stable → any (stable is terminal for phase)

Rationale: Phase discipline ensures specification precedes implementation, and testing validates implementation before stabilization.

Tags: lifecycle

Since: v0.1.0

[RFC-0001:C-WORK-STATUS] Work Item Status Transitions (Normative)

A Work Item MUST have exactly one of the following status values:

  1. queue — Initial state. The work item is defined but not yet started.
  2. active — The work item is currently being worked on.
  3. done — The work item is complete. All acceptance criteria are satisfied, and any required verification guards have passed or been explicitly waived.
  4. cancelled — The work item was abandoned. No further work will be done.

Valid transitions (via move command):

  • queue → active (start work)
  • queue → cancelled (abandon before starting)
  • active → done (complete work)
  • active → cancelled (abandon in progress)

Invalid transitions (MUST be rejected):

  • done → any (done is terminal)
  • cancelled → any (cancelled is terminal)
  • queue → done (cannot complete without being active)
  • active → queue (no “un-start”)

Timestamp behavior:

  • queue → active: Sets started date if not already set
  • active → done: Sets completed date
  • active → cancelled: Sets completed date

Tags: lifecycle

Since: v0.1.0

[RFC-0001:C-ADR-STATUS] ADR Status Transitions (Normative)

An ADR MUST have exactly one of the following status values:

  1. proposed — Initial state. The decision is under consideration.
  2. accepted — The decision has been accepted and is in effect.
  3. rejected — The decision was considered and explicitly declined.
  4. superseded — The decision has been replaced by another ADR.

Valid transitions:

  • proposed → accepted (via accept command)
  • proposed → rejected (via reject command)
  • accepted → superseded (via supersede command, requires --by to specify replacement)

Invalid transitions (MUST be rejected):

  • proposed → superseded (cannot supersede without first accepting)
  • accepted → proposed (no “un-accept”)
  • accepted → rejected (accepted decisions must be superseded, not retroactively rejected)
  • rejected → any (rejected is terminal)
  • superseded → any (superseded is terminal)

When an ADR is superseded:

  • The superseded_by field MUST be set to the ID of the replacing ADR
  • The replacing ADR SHOULD reference the superseded ADR

When an ADR is rejected:

  • The ADR MUST remain available as historical record
  • The superseded_by field MUST NOT be set

Tags: lifecycle

Since: v0.1.0

[RFC-0001:C-CLAUSE-STATUS] Clause Status Transitions (Normative)

A Clause MUST have exactly one of the following status values:

  1. active — Default state. The clause is in effect.
  2. deprecated — The clause is no longer recommended but still valid.
  3. superseded — The clause has been replaced by another clause.

Valid transitions:

  • active → deprecated (via deprecate command)
  • active → superseded (via supersede command, requires replacement clause)
  • deprecated → superseded (already deprecated, now replaced)

Invalid transitions (MUST be rejected):

  • deprecated → active (no resurrection)
  • superseded → any (superseded is terminal)

When a clause is superseded:

  • The superseded_by field MUST be set to the ID of the replacing clause
  • The replacing clause SHOULD have a since field indicating the version it was introduced

Tags: lifecycle

Since: v0.1.0

[RFC-0001:C-GATE-CONDITIONS] Transition Gate Conditions (Normative)

Certain transitions have additional gate conditions beyond the state machine rules.

Work Item → done:

  1. The work item MUST have at least one acceptance criterion defined
  2. All acceptance criteria MUST have status “done” or “cancelled” (no “pending”)
  3. Every guard named in the work item’s verification.required_guards MUST pass or be explicitly waived with a reason
  4. If project verification is enabled, every project-level default guard MUST also pass or be explicitly waived with a reason

The effective required verification guards are the union of the work item’s verification.required_guards and, only when project verification is enabled, the project’s configured default guards. Only guards covered by explicit waivers are removed from that set.

Rationale: Prevents marking work as complete without defined success criteria or executable completion proof.

RFC draft → normative:

  • No additional gates (policy decision, not structural)

RFC phase spec → impl:

  • RFC status SHOULD be normative (warning if draft)

Rationale: Implementation should follow finalized specification.

RFC phase impl → test:

  • No additional gates

RFC phase test → stable:

  • No additional gates (assumes tests are passing externally)

Future gates MAY be added via RFC amendment, but MUST NOT break existing valid workflows. Project verification MUST default to disabled when the project config does not opt into it.

Tags: lifecycle, validation

Since: v0.1.0


Changelog

v0.3.0 (2026-03-17)

Add executable verification gates to work completion

Added

  • Added required verification guard gate conditions for work completion

v0.2.1 (2026-01-26)

Remove ADR reference from C-RFC-STATUS; RFCs are self-contained and should not reference ADRs

Added

  • Inline amendment rule in C-RFC-STATUS clause (was referencing ADR-0016)

v0.2.0 (2026-01-19)

Clarify that normative RFCs may be amended via version bumping

v0.1.0 (2026-01-17)

Initial draft

RFC-0002: CLI Resource Model and Command Architecture

Version: 0.8.0 | Status: normative | Phase: test


1. Summary

[RFC-0002:C-SUMMARY] Summary (Informative)

This RFC defines the resource-first command architecture for the govctl CLI. It establishes normative requirements for command structure, resource types, verb semantics, and project-level verification commands to ensure consistency and discoverability.

The design follows established patterns from Docker, kubectl, and other modern CLIs where commands are grouped by resource type (noun-first) rather than operation type (verb-first).

Scope: This RFC specifies the structural contract (which commands exist, how they’re organized, what resources they operate on). Implementation details (flag syntax, help text formatting, terminal colors) are left to ADRs.

Rationale: A stable command structure enables:

  1. Predictable UX across all resource types
  2. Agent/script automation via consistent patterns
  3. Scoped help and command discovery
  4. Future extensibility without namespace pollution

Tags: cli

Since: v0.1.0


2. Specification

[RFC-0002:C-RESOURCE-MODEL] Resource-First Command Structure (Normative)

The govctl CLI MUST use a resource-first command structure of the form:

govctl <resource> <verb> [arguments] [flags]

Where:

  • <resource> is a governance artifact type (rfc, adr, work, clause, release, guard)
  • <verb> is an operation on that resource (new, list, get, edit, delete, or resource-specific lifecycle operations)
  • [arguments] are positional parameters (typically IDs, titles, or values)
  • [flags] are optional modifiers (–filter, –output, –stdin, etc.)

Rationale:

The resource-first structure (noun-first) provides several advantages over verb-first (operation-first):

  1. Scoped Discovery: govctl rfc --help shows only RFC operations, not all 30+ global commands
  2. Namespace Clarity: govctl rfc finalize is unambiguous; govctl finalize requires knowing it only applies to RFCs
  3. Mental Model Alignment: Users think “I want to work with RFCs” then “What can I do?”, not “I want to finalize something, what can be finalized?”
  4. Extensibility: Adding govctl rfc validate doesn’t pollute the global namespace
  5. Industry Convention: Docker, kubectl, git, cargo all use resource-scoping or similar patterns

Exceptions:

Commands that operate across all resource types (init, check, status, render) remain at the global level as they don’t belong to a specific resource namespace.

Implementation Note:

For backwards compatibility during migration, verb-first aliases MAY be supported but MUST emit deprecation warnings. Full verb-first syntax MUST be removed in a major version release.

Tags: cli

Since: v0.1.0

[RFC-0002:C-RESOURCES] Resource Types (Normative)

The following resource types MUST be supported as top-level command namespaces:

1. rfc - Request for Comments

Manages RFC specifications (normative documents defining system behavior).

  • ID Format: RFC-NNNN (e.g., RFC-0001)
  • Lifecycle: draft → normative → deprecated (per RFC-0001:C-RFC-STATUS)
  • Phase: spec → impl → test → stable (per RFC-0001:C-RFC-PHASE)

2. adr - Architecture Decision Record

Manages ADRs (records of architectural decisions).

  • ID Format: ADR-NNNN (e.g., ADR-0001)
  • Lifecycle: proposed → accepted → superseded, or proposed → rejected (per RFC-0001:C-ADR-STATUS)

3. work - Work Item

Manages work items (tasks, features, bugs).

  • ID Format: WI-YYYY-MM-DD-NNN (e.g., WI-2026-01-19-001)
  • Lifecycle: queue → active → done, with cancellation at any stage (per RFC-0001:C-WORK-STATUS)

4. clause - RFC Clause

Manages individual clauses within RFCs.

  • ID Format: RFC-NNNN:C-NAME (e.g., RFC-0001:C-SUMMARY)
  • Lifecycle: active → deprecated → superseded (per RFC-0001:C-CLAUSE-STATUS)

Clause Namespace vs Storage:

Clauses remain at the CLI top-level namespace despite being child resources of RFCs because their ID format (RFC-NNNN:C-NAME) is self-scoping and unambiguous.

The CLI namespace is independent of filesystem layout. Implementations MAY store clause files nested under RFC directories (e.g., gov/rfc/RFC-0001/clauses/C-NAME.toml) while maintaining the flat CLI command structure (govctl clause get RFC-0001:C-NAME).

5. guard - Verification Guard

Manages reusable executable completion checks defined by RFC-0000:C-GUARD-DEF.

  • ID Format: GUARD-NAME (e.g., GUARD-CARGO-TEST)
  • Storage: gov/guard/ as individual TOML files
  • No lifecycle (guards are either present or removed)

6. release - Release Version

Manages published versions and their included work item references.

  • ID Format: Semantic version (e.g., 1.0.0)
  • Storage: gov/releases.toml as defined by RFC-0000:C-RELEASE-DEF
  • No lifecycle (immutable once created)

Resource Identification:

Each resource type MUST have a unique, predictable ID format that:

  1. Is stable across filesystem operations
  2. Can be referenced in other artifacts
  3. Clearly identifies the resource type without context
  4. Supports lexicographic sorting where meaningful
  5. Is case-sensitive (RFC-0001 ≠ rfc-0001)

Date Format:

All date-valued resource metadata fields (such as created, updated, started, completed, and date) MUST use ISO 8601 calendar date format YYYY-MM-DD.

Tags:

RFCs, clauses, ADRs, work items, and guards MAY include an optional tags array in the [govctl] section. Each tag MUST be a string from the project’s controlled vocabulary defined in gov/config.toml under [tags] allowed. Tags MUST match the pattern [a-z][a-z0-9-]* (lowercase kebab-case). Releases do not carry tags. govctl check MUST reject any artifact that references a tag not present in the allowed set.

Future Extensions:

Additional resource types MAY be added via RFC amendment. New resource types MUST follow the same structural patterns defined in RFC-0002:C-CRUD-VERBS.

Tags: cli, schema

Since: v0.1.0

[RFC-0002:C-CRUD-VERBS] Universal CRUD Verbs (Normative)

All resource types MUST support the following CRUD (Create, Read, Update, Delete) verbs where applicable:

1. new - Create Resource

Syntax: govctl <resource> new <arguments>

Creates a new instance of the resource. Required arguments vary by resource type but typically include a title or primary identifier.

MUST support for: rfc, adr, work, clause, release, guard Behavior:

  • Generates unique ID if not provided
  • Initializes resource with default values
  • Creates necessary filesystem structure
  • Validates inputs per resource schema

Guard-specific: govctl guard new "<title>" scaffolds a new guard TOML file under gov/guard/. The generated ID is derived from the slugified title with a GUARD- prefix. The command SHOULD print a hint about adding the guard to verification.default_guards in gov/config.toml.

2. list - List Resources

Syntax: govctl <resource> list [filter] [--tag <tag>[,<tag>...]]

Lists all instances of the resource type. Optional filter narrows results.

MUST support for: rfc, adr, work, clause, release, guard Behavior:

  • Returns tabular output by default
  • Supports filtering (exact match or substring)
  • Sorted by ID (lexicographic order)
  • MUST respect RFC-0002:C-OUTPUT-FORMAT flags
  • With --tag: filters results to artifacts that carry ALL specified tags. MUST support comma-separated tag values. Applies to rfc, clause, adr, work, and guard (releases do not carry tags).

3. get - Read Resource

Syntax: govctl <resource> get <id> [field]

Retrieves a resource by ID. If field is specified, returns only that field value.

MUST support for: rfc, adr, work, clause, guard Behavior:

  • Returns full resource if no field specified
  • Returns field value if field specified
  • MUST respect RFC-0002:C-OUTPUT-FORMAT flags
  • MUST error if resource or field does not exist

Field Name Stability:

Field names exposed via get <id> <field> MUST be stable identifiers defined by the resource schema. Field names MAY NOT be renamed without a major version bump of govctl.

4. edit - Update Resource

Syntax: govctl <resource> edit <id> <field> [value]

Updates a single field on a resource. Value can be provided as argument or via stdin.

MUST support for: rfc, adr, work, clause, guard Behavior:

  • Simple fields: replace value
  • Array fields: special syntax (see ADR-0017 for implementation details)
  • MUST validate field names per resource schema
  • MUST validate values per field type
  • MUST support --stdin flag for multiline content

5. delete - Delete Resource

Syntax: govctl <resource> delete <id>

Removes a resource permanently.

MUST support for: work, clause, guard MUST NOT support for: rfc, adr (use lifecycle verbs instead)

Deletion Safety Constraints:

For guards:

  • MUST verify no work items reference the guard ID in verification.required_guards or verification.waivers[*].guard before deletion
  • MUST verify the guard is not listed in verification.default_guards in gov/config.toml before deletion
  • MUST error with list of referencing artifacts/config if any references exist

For clauses:

  • MUST only allow deletion if containing RFC is in draft status
  • MUST verify no other artifacts reference the clause ID before deletion
  • MUST error with list of referencing artifacts if references exist

For work items:

  • MUST only allow deletion if status is queue
  • MUST verify no other artifacts reference the work item ID before deletion
  • MUST error with list of referencing artifacts if references exist

General Deletion Behavior:

  • SHOULD require confirmation unless --force flag provided
  • MUST be atomic (either fully succeeds or fully fails)

Rationale: Permanent deletion breaks referential integrity. These constraints ensure deletions are safe and don’t leave dangling references in the governance graph.

Consistency Requirements:

  1. All CRUD verbs MUST use the same flag names across resource types
  2. Error messages MUST follow the same format across all verbs
  3. Exit codes MUST be consistent (0 = success, non-zero = error)
  4. Stdin handling MUST work identically across all verbs that accept it

Forbidden Variations:

  • MUST NOT use different verb names for the same operation (e.g., “show” vs “get”)
  • MUST NOT reorder arguments between resource types (ID always comes before field)
  • MUST NOT have resource-specific flags for universal operations. Flags that filter by a cross-resource metadata field (e.g., --tag) are permitted on resources that carry that field and silently ignored or unavailable on resources that do not.

Tags: cli, editing

Since: v0.1.0

[RFC-0002:C-LIFECYCLE-VERBS] Resource-Specific Lifecycle Verbs (Normative)

Resource-specific lifecycle verbs implement state transitions defined in RFC-0001. These verbs MUST be scoped to their resource namespace.

RFC Lifecycle Verbs:

  1. govctl rfc finalize <id> <normative|deprecated>

  2. govctl rfc advance <id> <spec|impl|test|stable>

  3. govctl rfc bump <id> <patch|minor|major> -m <message>

    • Version bumping per semantic versioning
    • Updates changelog automatically
    • Optional: --change <description> for additional changelog entries
  4. govctl rfc supersede <id> --by <replacement-id>

    • Marks RFC as superseded by another RFC
    • Both RFCs must exist

ADR Lifecycle Verbs:

  1. govctl adr accept <id>

  2. govctl adr reject <id>

  3. govctl adr supersede <id> --by <replacement-id>

Work Lifecycle Verbs:

  1. govctl work move <id> <queue|active|done|cancelled>
    • Implements RFC-0001:C-WORK-STATUS transitions
    • Validates gate conditions (e.g., acceptance criteria for done)
    • Updates timestamp fields automatically

Clause Lifecycle Verbs:

  1. govctl clause deprecate <id>

  2. govctl clause supersede <id> --by <replacement-id>

Consistency Requirements:

  1. All lifecycle verbs MUST validate transitions per RFC-0001
  2. All lifecycle verbs MUST update timestamp fields where applicable
  3. All lifecycle verbs MUST be atomic (no partial state changes)
  4. All lifecycle verbs MUST support global --dry-run flag
  5. Invalid transitions MUST error with clear explanation of valid transitions

Rationale:

Lifecycle verbs are resource-specific because:

  • RFCs have status AND phase (two dimensions of state)
  • ADRs can be rejected (not applicable to RFCs)
  • Work items have gate conditions (acceptance criteria)
  • Each resource has different valid transitions

Scoping these verbs to resources makes their applicability explicit and prevents confusion.

Tags: cli, lifecycle

Since: v0.1.0

[RFC-0002:C-OUTPUT-FORMAT] Output Format Control (Normative)

All commands that output resource data MUST support the --output (or -o) flag with the following format options:

Required Output Formats:

  1. table (default for human use)

    • Formatted tables with headers and aligned columns
    • MAY use colors when terminal supports them
    • MUST be readable in plain text (no control codes in non-TTY)
  2. json

    • Valid JSON output
    • Pretty-printed with 2-space indentation
    • MUST be parseable by standard JSON tools
  3. yaml

    • Valid YAML output
    • Formatted for readability
    • MUST be parseable by standard YAML tools
  4. toml

    • Valid TOML output (native format for governance artifacts)
    • MUST be parseable by standard TOML tools
  5. plain

    • Plain text values with no formatting
    • One value per line for lists
    • Single value (no newline) for scalar fields
    • Suitable for shell scripting and piping

Applicability:

Commands MUST support output formats as follows:

  • get with no field: table, json, yaml, toml
  • get with field: plain (default), json, yaml
  • list: table (default), json, yaml

Commands MAY add format-specific flags (e.g., --format-json-compact) but MUST NOT remove or change behavior of standard formats.

Format Selection:

  1. Explicit --output <format> takes precedence
  2. If not specified:
    • For TTY: default is table
    • For non-TTY: default MUST be json
  3. Invalid format names MUST error with list of valid formats

Exceptions:

govctl status is exempt from output format requirements and always produces human-readable tabular output intended for interactive use only.

Consistency Requirements:

  1. JSON/YAML/TOML output MUST match the internal schema exactly
  2. Table format MAY omit fields for readability but MUST show all critical fields
  3. Plain format MUST output stable, parseable text (no decorations)
  4. Output format MUST NOT affect command semantics (same data, different representation)

Rationale:

Universal output format control enables:

  • Human readability (table)
  • Script automation (json/yaml)
  • Integration with standard tools (jq, yq)
  • Native format inspection (toml for governance artifacts)

This pattern follows kubectl and Docker conventions where -o json works on all read operations.

Tags: cli

Since: v0.1.0

[RFC-0002:C-GLOBAL-COMMANDS] Global Commands (Normative)

The following commands operate across all resources and MUST remain at the global namespace level:

1. govctl init

Initializes a new govctl project in the current directory.

Syntax: govctl init [--force]

Behavior:

  • Creates gov/ directory structure
  • Generates gov/config.toml
  • Creates subdirectories for rfcs, adrs, work items, and verification guards
  • Installs bundled JSON Schema files under gov/schema/
  • MUST error if already initialized (unless --force)
  • MUST NOT install agent skills or agents (see init-skills)
  • SHOULD print a hint about govctl init-skills and plugin installation

2. govctl check

Validates all governance artifacts across the project.

Syntax: govctl check [--deny-warnings]

Behavior:

  • Validates RFCs, ADRs, clauses, work items, releases, and verification guards
  • Implementations MUST define a corresponding machine-readable JSON Schema for each governance artifact type: RFC, clause, ADR, work item, release, and verification guard
  • These schemas MUST be JSON files stored under gov/schema/
  • For TOML artifacts, MUST validate structure against the corresponding JSON Schema after parsing and normalization
  • Checks state machine invariants
  • Verifies cross-references
  • Validates the semantic correctness of the optional [verification] section in gov/config.toml, including that configured default guard IDs resolve
  • Scans source code for references (if enabled in config)
  • In repositories that have completed RFC/clause migration, if legacy JSON RFC or clause files are detected, MUST fail with a migration-required diagnostic that points to govctl migrate
  • Returns exit code 0 if valid, non-zero if errors
  • With --deny-warnings: treats warnings as errors

3. govctl status

Shows summary counts of all artifacts grouped by status.

Syntax: govctl status

Behavior:

  • Displays counts by status/phase for each resource type
  • Highlights active work items
  • Shows pending decisions (proposed ADRs, draft RFCs)
  • Uses colors in TTY mode for visual scanning
  • No output format flag (always human-readable table)

4. govctl render

Generates markdown documentation from source-of-truth governance artifacts.

Syntax: govctl render [targets...] [--output-dir PATH] [--dry-run]

Behavior:

  • In repositories that have completed RFC/clause migration, renders RFCs from TOML to markdown (published)
  • Renders ADRs from TOML to markdown (local only)
  • Renders work items from TOML to markdown (local only)
  • Generates CHANGELOG.md from releases
  • Default: renders RFCs only
  • With targets: rfc, adr, work, changelog, all
  • MUST validate before rendering

5. govctl describe

Outputs machine-readable CLI metadata for agent/tool integration.

Syntax: govctl describe [--context]

Behavior:

  • Outputs JSON with command catalog
  • Includes workflow information
  • With --context: adds current project state and suggested actions
  • Enables machine agents and automation to discover capabilities safely

Stability Contract:

The JSON schema of govctl describe output MUST be versioned. The schema version MUST be included in the output. Backward-incompatible changes to the schema require a major version bump of govctl.

This ensures agents and automation tools can rely on stable introspection.

6. govctl completions

Generates shell completion scripts.

Syntax: govctl completions <bash|zsh|fish|powershell>

Behavior:

  • Outputs completion script for specified shell
  • Can be sourced or installed per shell conventions

7. govctl migrate

Performs versioned repository-local format migration for governance artifacts.

Syntax: govctl migrate [--dry-run]

Behavior:

  • Reads the current schema version from gov/config.toml [schema] version
  • Runs all pending migration steps from the current version to the latest
  • Each step produces a set of file operations (writes and deletes) executed transactionally
  • Bumps [schema] version in gov/config.toml after successful migration
  • MUST leave the repository unchanged if any step fails
  • MUST be safe to run on an already-migrated repository and report a no-op result
  • MUST NOT perform heuristic project discovery or broad adoption tasks
  • MUST ensure all bundled JSON Schema files exist in gov/schema/, overwriting with the latest version regardless of schema version (per ADR-0035)

8. govctl verify

Executes reusable verification guards.

Syntax:

  • govctl verify [GUARD-ID ...]
  • govctl verify --work WI-ID

Behavior:

  • Loads Verification Guard artifacts from gov/guard/
  • Explicit GUARD-ID arguments and --work WI-ID MUST NOT be combined in the same invocation
  • With explicit GUARD-ID arguments: runs those guards
  • With --work WI-ID: runs the effective required guards for that work item after applying project defaults and work-item waivers
  • With no explicit guards and no --work: runs the project-level default guards from gov/config.toml
  • Reads project-level verification policy from RFC-0002:C-VERIFY-CONFIG
  • Executes each guard command non-interactively from the project root
  • Reports pass/fail per selected guard
  • Returns non-zero if any selected guard fails, if no guards are selected, or if a requested guard ID is unknown

9. govctl init-skills

Installs agent skills and agents into the project’s agent directory (per ADR-0035).

Syntax: govctl init-skills [--force] [--format <claude|codex>] [--dir PATH]

Behavior:

  • --dir overrides the output directory for this invocation. Resolution order: --dir flag > agent_dir from config > format-implied default (.claude for claude, .codex for codex)
  • --format selects the output format for agent definitions (default claude):
    • claude: skills as skills/*/SKILL.md, agents as agents/*.md with YAML frontmatter (compatible with Claude Code, Cursor, Windsurf, and similar editors)
    • codex: skills as skills/*/SKILL.md (same format), agents as agents/*.toml with developer_instructions field (compatible with Codex CLI)
  • Skips files that already exist unless --force is used
  • Reports created/updated/skipped counts
  • This command is separate from init because plugin users receive skills globally and do not need local copies

10. govctl tag

Manages the project’s controlled tag vocabulary.

Syntax:

  • govctl tag new <tag>
  • govctl tag delete <tag>
  • govctl tag list

Behavior:

  • new: registers a new tag in gov/config.toml under [tags] allowed. Tags MUST match [a-z][a-z0-9-]* (lowercase kebab-case). MUST error if the tag already exists.
  • delete: removes a tag from the allowed list. MUST error if any artifact still references the tag.
  • list: displays all registered tags with usage counts (how many artifacts reference each tag).

Artifact-level tagging uses existing resource verbs on taggable types (rfc, clause, adr, work, guard):

  • govctl {rfc|clause|adr|work|guard} add <ID> tags <tag> — assign a tag to an artifact
  • govctl {rfc|clause|adr|work|guard} remove <ID> tags <tag> — remove a tag from an artifact

11. govctl self-update

Updates the govctl binary to the latest release.

Syntax: govctl self-update [--check]

Behavior:

  • Downloads and replaces the running binary from GitHub Releases
  • With --check: prints version comparison without downloading
  • Full specification in RFC-0002:C-SELF-UPDATE

Rationale:

These commands are global because they:

  1. Operate on multiple resource types simultaneously
  2. Don’t fit the <resource> <verb> pattern semantically
  3. Are project-level operations, not resource-level
  4. Match user mental model of “project commands” vs “resource commands”

govctl migrate qualifies because it operates across the governance repository as a whole and changes multiple resource types in one coordinated step.

govctl verify qualifies because it executes project-level completion checks that may be required by multiple work items and by the project configuration.

govctl init-skills qualifies because it performs project-level initialization of agent assets (criterion 2).

govctl tag qualifies because it manages project-level configuration that applies across all resource types (criterion 1).

govctl self-update qualifies because it provides meta-information about the CLI itself and performs binary lifecycle management (criterion 3).

Future Additions:

New global commands MAY be added via RFC amendment. They MUST meet at least one criterion:

  1. Operate on multiple resource types
  2. Perform project-level initialization or cleanup
  3. Provide meta-information about the CLI itself

Tags: cli

Since: v0.1.0

[RFC-0002:C-VERIFY-CONFIG] Verification Configuration (Normative)

The project config file gov/config.toml MAY include an optional [verification] section.

When present, the section MUST support the following fields:

  • enabled — boolean, default false
  • default_guards — array of Verification Guard IDs, default []

If the section is absent, the implementation MUST behave as if enabled = false and default_guards = [].

Every guard ID listed in default_guards MUST resolve to an existing Verification Guard defined by RFC-0000:C-GUARD-DEF. Unknown IDs MUST cause validation failure.

When enabled = false, project-level default guards MUST NOT be applied automatically by govctl verify or by Work Item completion checks.

Work Item verification.required_guards remain effective regardless of the project-level enabled value.

Rationale: The project config controls whether shared default guard policy is active. Explicit Work Item requirements remain local and auditable instead of becoming inert metadata.

Tags: cli, validation

Since: v0.3.0

[RFC-0002:C-SELF-UPDATE] Self-Update Command (Normative)

11. govctl self-update

Updates the govctl binary to the latest release.

Syntax: govctl self-update [--check]

Behavior:

  • Queries the GitHub Releases API for the govctl-org/govctl repository to determine the latest published version
  • Compares the latest version against the running binary’s compiled version
  • Without --check: downloads the platform-appropriate binary asset, verifies integrity, and replaces the running executable. MUST print the old and new version on success. MUST exit with code 0 if already up to date, printing a message indicating no update is needed.
  • With --check: prints current version and latest available version without downloading. MUST exit with code 0 if up to date, exit with code 1 if a newer version is available.
  • MUST detect the current platform target at compile time and select the matching release asset
  • MUST display download progress when connected to a TTY
  • MUST error with a clear message if the binary lacks write permission to its install location
  • MUST error with a clear message if the GitHub API is unreachable or rate-limited
  • SHOULD support GITHUB_TOKEN environment variable for authenticated API requests to avoid rate limits

Rationale:

A self-update command provides a single canonical update path that works regardless of how govctl was originally installed (cargo install, cargo binstall, or direct binary download). This meets criterion 3 of RFC-0002:C-GLOBAL-COMMANDS (meta-information about the CLI itself).

Tags: cli, release

Since: v0.8.0


Changelog

v0.8.0 (2026-04-13)

Add self-update global command (ADR-0041)

Added

  • C-SELF-UPDATE clause for govctl self-update command

Changed

  • C-GLOBAL-COMMANDS updated with entry 11 and rationale for self-update

v0.7.0 (2026-04-09)

Add controlled-vocabulary tags: tags field on RFC/clause/ADR/work/guard, –tag filter on list, govctl tag new/delete/list for registry management (ADR-0040)

v0.6.1 (2026-04-08)

Add –dir flag to init-skills for one-step directory override without config editing

v0.6.0 (2026-04-08)

Add –format flag to init-skills for cross-platform agent format support (claude/codex)

v0.5.0 (2026-03-27)

Add guard as a resource type with CRUD verbs

Added

  • Added guard to C-RESOURCES as resource type 5 (renumbered release to 6)
  • Updated C-CRUD-VERBS with guard-specific verb applicability
  • Updated C-RESOURCE-MODEL to include guard in resource list

v0.4.0 (2026-03-17)

Add init-skills command, update init and migrate per ADR-0035

Added

  • init-skills global command for explicit agent asset installation

Changed

  • init no longer installs skills/agents
  • migrate now ensures schema JSON files are up to date

v0.3.0 (2026-03-17)

Add project-level verification command and config semantics

Added

  • Added global verify command and verification config contract

v0.2.0 (2026-01-19)

Incorporate review feedback: add deletion safety constraints, field name stability, output format defaults, describe schema versioning, and editorial clarifications

Added

  • Added deletion safety constraints requiring draft RFCs and reference checks
  • Added field name stability guarantee for get command
  • Specified json as default output format for non-TTY
  • Added status command exception to output format requirements
  • Added describe command schema versioning contract
  • Clarified clause namespace vs filesystem storage independence
  • Added case-sensitivity and timestamp format requirements

v0.1.0 (2026-01-19)

Initial draft

RFC-0003: TUI UX improvements

Version: 0.1.0 | Status: normative | Phase: stable


1. Summary

[RFC-0003:C-SUM] Summary (Informative)

  • Define a consistent TUI navigation frame with shared header and footer.
  • Add list filtering and quick-jump for faster browsing.
  • Improve detail view readability with consistent layout and scroll position.

Tags: tui

Since: v0.1.0


2. Specification

[RFC-0003:C-NAV] Shared header/footer navigation (Normative)

  • The TUI MUST render a persistent header and footer across all views.
  • The header MUST show the current view hierarchy (breadcrumb) and basic counts for the active view.
  • The footer MUST provide the current keymap for primary navigation, including quit and back.
  • Existing navigation keys MUST remain functional.

Tags: tui

Since: v0.1.0

[RFC-0003:C-FILTER] List filtering and quick-jump (Normative)

  • List views MUST support an inline filter mode that matches ID, title, or status.
  • Filter mode MUST be entered with a single key and exited without leaving the list view.
  • When a filter is active, the list MUST show only matching items and navigation MUST operate over the filtered set.
  • List views MUST support quick-jump to top and bottom and allow stepping through matches.

Tags: tui

Since: v0.1.0

[RFC-0003:C-DETAIL] Detail view readability (Normative)

  • Detail views MUST present metadata and content sections consistently across artifact types.
  • Detail views MUST show scroll position to indicate where the user is within the content.
  • Scrolling MUST not lose the current view context or selection.

Tags: tui

Since: v0.1.0


Changelog

v0.1.0 (2026-02-07)

Initial draft

RFC-0004: Concurrent write safety for governance artifacts

Version: 0.1.0 | Status: normative | Phase: stable


1. Summary

[RFC-0004:C-SUMMARY] Summary (Informative)

This RFC specifies that govctl MUST preserve integrity of governance artifacts when multiple processes invoke write operations concurrently (e.g. agent-triggered parallel tasks creating or editing RFCs, ADRs, or work items).

Scope: Applies to any command that modifies files under the gov root or writes rendered output under the docs root. The concrete mechanism to satisfy this requirement is an implementation detail.

Backward compatibility: Existing command invocations and arguments remain valid. This RFC adds only coordination and failure behaviour; it does not change command semantics.

Specification outline: The RFC defines terms (write command, read-only command, concurrent invocations, corrupted file); requires that every write command participate in a single global concurrency mechanism and that read-only commands never block on it; requires that concurrent writes never produce corrupted files, duplicate work item IDs, or lost read-modify-write updates; and requires predictable failure behaviour (bounded wait, then either proceed or fail with an actionable error) when the mechanism cannot be acquired.

Rationale: Concurrent writes without coordination cause file corruption, duplicate IDs, and lost updates. Agents and scripts often run multiple govctl invocations in parallel; the implementation must prevent observable corruption and provide clear behaviour on conflict.

Tags: safety

Since: v0.1.0


2. Specification

[RFC-0004:C-CONCURRENT-WRITE] Concurrent write safety (Normative)

Commands that modify the governance tree or rendered output MUST use a concurrency mechanism such that:

  1. No two concurrent invocations (across processes) produce corrupted artifact files. Corrupted means: invalid JSON/TOML, truncated content, or interleaved content from different writes.
  2. Work item creation MUST NOT assign the same ID to two items created by concurrent invocations when both use the same ID prefix (e.g. same date under sequential or author-hash strategy). The scenario is same repository, concurrent processes (distinct from branch-merge ID collision).
  3. Read-modify-write operations (e.g. edit, set, bump) MUST NOT lose updates from another concurrent invocation modifying the same artifact.

This RFC requires the observable behaviour: artifact integrity and no duplicate IDs under concurrent write load. The concrete mechanism (e.g. process-level filesystem locking) is an implementation detail.

Rationale: Agents and CI may run multiple govctl write commands in parallel. Without a defined concurrency strategy, races are observable in practice (file corruption, duplicate WI IDs). The normative requirement ensures implementors address this.

Tags: safety

Since: v0.1.0

[RFC-0004:C-DEFINITIONS] Definitions (Informative)

Write command: A govctl invocation that may create, modify, or delete files under the gov root, or write files under the docs root (e.g. render output). Any invocation that writes files under the docs root is a write command regardless of command name. Examples: rfc new, adr new, work new, rfc set, adr set, work set, rfc edit, adr edit, work edit, clause new, clause edit, rfc bump, rfc finalize, rfc advance, adr accept, work tick, work move, render.

Read-only command: A govctl invocation that does not modify gov or docs. Examples: rfc list, adr list, work list, rfc get, adr get, work get, check, status. The commands show and describe are read-only when they do not write to gov or docs (e.g. when they output only to stdout).

Concurrent invocations: Two or more govctl processes running at the same time, such that their execution may overlap (e.g. multiple agent tasks, or a script spawning parallel govctl calls).

Corrupted artifact file: A file under gov or docs that does not conform to the expected schema (invalid JSON or TOML), or that contains truncated or interleaved content from more than one logical write.

Rationale: These definitions make the scope and guarantees of this RFC testable and unambiguous.

Tags: safety

Since: v0.1.0

[RFC-0004:C-FAILURE-BEHAVIOUR] Behaviour when concurrency mechanism is unavailable (Normative)

When a write command cannot obtain exclusive access (e.g. because another write command is in progress), the implementation MUST either wait for a bounded time and then proceed, or wait for a bounded time and then fail. The maximum wait time is implementation-defined and MUST be documented. Implementations SHOULD use a default maximum wait of at least 30 seconds for interoperability and testability. The maximum wait time MAY be configurable.

If the implementation chooses to fail after waiting, it MUST exit with a non-zero status and MUST emit an actionable error message that indicates that another govctl write is in progress and that the user or agent should retry later. The message MUST NOT assume a specific concurrency mechanism (e.g. must not require the word “lock”).

If the implementation waits until access is granted, it MUST eventually proceed; it MUST NOT deadlock.

Implementations SHOULD ensure that exclusive access is released on process exit or that time-based expiry or stale-lock cleanup applies, so that a crashed process does not block writers indefinitely.

Rationale: Predictable failure behaviour allows agents and scripts to retry or serialise writes; clear errors avoid confusion. A documented, implementation-defined bound makes conformance testable; the 30-second SHOULD gives implementations and tests a shared expectation. The stale-lock note supports the “MUST NOT deadlock” requirement when a holder crashes.

Tags: safety

Since: v0.1.0

[RFC-0004:C-SCOPE] Scope of write commands (Normative)

Every write command MUST participate in the concurrency mechanism before performing any mutation of the gov tree or docs output. Participation means acquiring exclusive access (or equivalent) for the duration of the write operations performed by that invocation.

Read-only commands MUST NOT acquire exclusive access and MUST NOT block on the concurrency mechanism. They MAY run concurrently with each other and with at most one write command.

The concurrency mechanism SHALL apply to the entire gov root (and, when a command writes rendered output, to the docs root) as a single unit. Exclusive access MUST NOT be held by more than one write command at the same time.

Rationale: Defining scope ensures that all mutation paths are covered and that read-only usage is never blocked by writers.

Tags: safety

Since: v0.1.0


Changelog

v0.1.0 (2026-02-15)

Initial draft

ADR-0001: Use TOML for ADRs and Work Items

Status: superseded | Date: 2026-01-17 Superseded by: ADR-0034

Tags: schema

References: RFC-0000:C-ADR-DEF, RFC-0000:C-WORK-DEF

Context

ADRs and Work Items need a human-readable, machine-parseable format. JSON is verbose and hard to edit manually. YAML has implicit typing issues. TOML provides explicit typing, clean multiline strings, and good tooling support.

Decision

Use TOML for ADRs and Work Items instead of JSON. Structure follows [govctl] metadata section and [content] body section. This aligns with Rust ecosystem conventions (Cargo.toml).

Consequences

Positive: Easier manual editing, cleaner diffs, natural multiline support. Negative: Different format from RFC clauses (which remain JSON for richer structure). Migration: Existing JSON-based work items would need conversion.

ADR-0002: Fix artifact lifecycle design flaws

Status: accepted | Date: 2026-01-17

Tags: lifecycle

References: RFC-0000

Context

Analysis of the artifact lifecycle state machines revealed several design flaws:

  1. Clause kind vs status duplication: The kind enum includes deprecated alongside normative and informative, but status also has deprecated. This creates confusion about where deprecation state belongs.

  2. ADR missing rejected state: The ADR lifecycle is proposed → accepted → superseded. If a proposal is rejected rather than accepted, there is no state to record this outcome. The alternative is leaving it as proposed forever or deleting it, both of which lose information.

  3. Work Item forced activation before cancellation: The lifecycle queue → active → done|cancelled implies you must start work before abandoning it. This is illogical - planned work can be abandoned before starting.

  4. RFC status×phase constraint rules undocumented: The 3×4 matrix of valid/invalid status×phase combinations exists implicitly but is not formally documented in SCHEMA.md.

Decision

We will fix all four lifecycle design flaws:

  1. Remove deprecated from Clause kind: The kind field should only contain semantic categories (normative, informative). Deprecation is a lifecycle state that belongs in status.

  2. Add rejected to ADR lifecycle: The new lifecycle becomes:

    proposed → accepted → superseded
             → rejected
    

    This allows recording when a proposal was considered but declined.

  3. Allow Work Item queue → cancelled: The new lifecycle becomes:

    queue → active → done
        ↘        ↘ cancelled
    

    Work can be abandoned at any stage.

  4. Document RFC constraint rules: Add explicit invariant rules to SCHEMA.md stating when status×phase combinations are forbidden.

Consequences

Positive:

  • Cleaner separation of concerns (kind = category, status = lifecycle)
  • Complete lifecycle coverage for all artifact types
  • Explicit rules prevent confusion about valid state combinations

Negative:

  • Existing tools validating kind: deprecated will need updating
  • Schema version bump required for breaking changes

Migration:

  • Any clause with kind: deprecated should change to kind: normative, status: deprecated
  • Currently no such clauses exist in the codebase

ADR-0003: Deterministic hash signatures for rendered projections

Status: accepted | Date: 2026-01-17

Tags: validation

References: RFC-0000

Context

govctl renders markdown files from authoritative JSON/TOML sources (RFCs, ADRs, Work Items). These rendered markdown files are projections — read-only views intended for human consumption.

Problem: Without a mechanism to detect tampering, someone could edit docs/rfc/RFC-0000.md directly. This edit would:

  1. Be overwritten on next govctl render
  2. Create confusion about which version is authoritative
  3. Violate the Single Source of Truth (SSOT) principle

We need a mechanism to:

  • Mark rendered files as generated (not authoritative)
  • Detect when a rendered file has been edited directly
  • Ensure verification is deterministic (same source always produces same hash)

Decision

Implement deterministic hash signatures embedded in rendered markdown.

Signature Format:

<!-- GENERATED: do not edit. Source: RFC-0000 -->
<!-- SIGNATURE: sha256:<64-hex-chars> -->

Hash Computation:

  1. Collect all source JSON content (RFC metadata + clauses, or ADR/Work Item TOML)
  2. Canonicalize JSON/TOML: sort object keys recursively, normalize whitespace
  3. For RFCs: sort clauses by clause_id before hashing
  4. Compute SHA-256 of the canonical representation
  5. Include a signature version prefix for future-proofing

Determinism Requirements:

  • Object keys sorted alphabetically at all nesting levels
  • Arrays preserve order (only objects get key-sorted)
  • Consistent string escaping and number formatting
  • No dependency on file modification times or filesystem order

Verification:

  • govctl check extracts signature from rendered markdown
  • Recomputes hash from current source files
  • Reports mismatch as a diagnostic error

Consequences

Positive:

  • Enforces “edit the source, not the projection” discipline
  • Tampered markdown is detected automatically by govctl check
  • Deterministic hashes enable reproducible builds
  • Clear provenance: every rendered file traces back to its source

Negative:

  • Existing rendered markdown will fail verification until re-rendered
  • Minor source reformatting (whitespace) changes the hash
  • Adds complexity to the render pipeline

Migration:

  • Run govctl render to regenerate all markdown with signatures
  • Commit the updated files

ADR-0004: Adopt Keep a Changelog format for RFC changelogs

Status: accepted | Date: 2026-01-17

Tags: release

References: RFC-0000

Context

RFC changelogs use a flat changes array. This makes it difficult to categorize changes by type (additions, fixes, removals). The Keep a Changelog format (keepachangelog.com) is a widely-adopted standard that organizes changes into semantic categories aligned with semver.

Decision

Replace the flat changes array with categorized arrays: added, changed, deprecated, removed, fixed, security. Keep summary field as optional notes. Migrate existing RFC-0000 changelog (trivial: one entry).

Consequences

Positive: Standard format, better scanability, semver-aligned categories. Negative: More verbose schema, breaking change to ChangelogEntry model. Migration cost is minimal (RFC-0000 has only one entry).

ADR-0005: CLI output color scheme and formatting

Status: accepted | Date: 2026-01-17

Tags: cli

References: RFC-0000

Context

CLI output uses plain text with no visual hierarchy. Users cannot quickly distinguish success from failure, or identify important values. Status messages blend together making output hard to scan.

Decision

Adopt semantic color scheme: Green for success, Red for errors, Yellow for warnings, Cyan for paths/IDs, Bold for emphasis. Use owo-colors crate (zero-cost, no deps). Auto-detect terminal color support. All output via ui module for consistency.

Consequences

Positive: Better UX, faster scanning, clearer status. Negative: One new dependency (owo-colors). Migration: All eprintln! calls routed through ui module.

ADR-0006: Global dry-run support for content-modifying commands

Status: accepted | Date: 2026-01-17

Tags: cli

References: RFC-0000:C-WORK-DEF, ADR-0019

Context

All content-modifying commands (new, set, add, remove, edit, tick, bump, finalize, advance, accept, deprecate, supersede, move) write files immediately with no way to preview changes. Only render commands support –dry-run. Users and agents need a way to preview what changes will be made before committing to disk, especially for destructive or complex operations.

Decision

Add global -n/–dry-run flag at CLI level. Create WriteOp enum with Preview/Execute variants in write.rs. All write functions accept WriteOp. In Preview mode: serialize, display via ui::dry_run_preview, skip fs::write. Read-only commands ignore the flag.

Consequences

Positive: Unified dry-run UX, follows Unix -n convention, DRY write logic. Negative: Signature changes for write functions. Migration: Update all write callsites to use WriteOp.

ADR-0007: Ergonomic array field matching for remove and tick commands

Status: accepted | Date: 2026-01-17

Tags: editing

References: RFC-0000:C-WORK-DEF, RFC-0000:C-ADR-DEF

Context

The remove command requires exact string match, which is fragile for long strings like URLs. The tick command uses substring matching but remove does not. Checklist fields (acceptance_criteria, decisions, alternatives) require separate semantics from string fields, leading to confusion about when to use remove vs tick.

Decision

Unify matching semantics: (1) Default to case-insensitive substring matching for remove and tick. (2) Add –exact flag for exact matching. (3) Add --at <index> for positional removal. (4) Add –regex flag for pattern matching. (5) Add –all flag for bulk removal. (6) Single match proceeds; multiple matches error with guidance. (7) remove works on all array types including checklists; tick only changes status.

Consequences

Positive: Ergonomic default matching, safe bulk operations, unified semantics. Negative: Breaking change for scripts relying on exact match. Migration: Existing exact-match callers should add –exact flag.

ADR-0008: Add refs field to RfcSpec for artifact cross-referencing

Status: accepted | Date: 2026-01-17

Tags: schema

References: RFC-0000

Context

RFCs are the supreme governance documents in govctl, yet they cannot formally reference other artifacts (ADRs, other RFCs, work items). Meanwhile, ADRs and Work Items both have a refs field for cross-referencing.

This asymmetry creates problems:

  1. RFCs cannot declare dependencies on ADRs that informed their design
  2. No way to trace which ADRs led to an RFC’s creation
  3. Validation cannot check RFC references for consistency
  4. Impact analysis for deprecation is incomplete

The existing pattern in AdrMeta and WorkItemMeta is: refs: Vec<String> with validation against known artifact IDs.

Decision

Add refs field to RfcSpec following the same pattern as AdrMeta and WorkItemMeta:

  1. Add refs: Vec<String> to RfcSpec model (serde skip_serializing_if empty)
  2. Add diagnostic code E0105RfcRefNotFound for validation errors
  3. Extend validate_artifact_refs() to include RFC refs validation
  4. Support add/remove/get refs operations in edit commands for RFCs
  5. Validate RFC supersedes field against known RFCs (currently unvalidated)

This creates consistency across all artifact types and enables complete cross-reference tracking.

Consequences

Positive:

  • RFCs can now reference ADRs, other RFCs, and work items
  • Complete artifact graph for impact analysis
  • Consistent API across all artifact types
  • Existing RFCs remain valid (refs is optional, defaults to empty)

Negative:

  • Minor schema change to RfcSpec
  • Slightly more validation overhead on check

Neutral:

  • Existing RFCs do not need migration (empty default)

ADR-0009: Configurable source code reference scanning

Status: accepted | Date: 2026-01-17

Tags: validation

References: RFC-0000

Context

Source code often contains references to governance artifacts (RFCs, clauses, ADRs) in comments. These references can become stale when artifacts are deprecated, superseded, or renamed.

Currently govctl validates internal artifact references (refs fields) but has no mechanism to detect broken references in source code comments. This creates a gap where documentation in code can drift from the actual governance state.

neotex-spec implements this via a hardcoded comment scanning pattern, but govctl needs a generalized solution with configurable patterns to work across different projects and conventions.

Decision

Add configurable source code scanning with:

  1. New [source_scan] config section with:

    • enabled: bool (default false)
    • roots: Vec<PathBuf> (directories to scan)
    • exts: Vec<String> (file extensions to include)
    • pattern: String (regex with capture group for artifact ID)
  2. Default pattern matches RFC-0001:C-NAME and ADR-0001 style references

  3. Scanner walks configured directories, applies pattern, validates extracted IDs against ProjectIndex

  4. New diagnostics: E0107SourceRefUnknown (error), W0107SourceRefOutdated (warning for deprecated/superseded)

  5. Scanner runs during ‘govctl check’ when source_scan.enabled = true

Consequences

Positive:

  • Dead link detection in source code comments
  • Configurable pattern supports different project conventions
  • Disabled by default - opt-in for projects that want it
  • Catches outdated references to deprecated/superseded artifacts

Negative:

  • Adds walkdir dependency
  • Additional check time when scanning large codebases

Neutral:

  • Pattern configuration requires regex knowledge
  • Projects must explicitly enable and configure

ADR-0010: Validate work item descriptions for placeholder content

Status: accepted | Date: 2026-01-17

Tags: validation

References: RFC-0000:C-WORK-DEF

Context

Work items are created with a placeholder description template: “Describe the work to be done. What is the goal? What are the acceptance criteria?” This placeholder is meant to be replaced with actual content, but 21 of 24 existing work items still have this placeholder text.

Empty or placeholder descriptions provide no value for audit trails, make it hard to understand what was actually done, and defeat the purpose of structured governance.

Decision

Add a warning diagnostic (W0106) for work items with placeholder or empty descriptions.

Detection patterns:

  1. Description matches the exact template text
  2. Description is empty or whitespace-only
  3. Description contains only generic phrases like “TODO”, “TBD”, “Fill in later”

This is a warning (not error) because:

  • Existing work items should not block govctl check
  • It’s advisory during development, not a hard gate
  • Users can address warnings incrementally

Consequences

Positive:

  • Encourages meaningful documentation of completed work
  • Improves audit trail quality
  • Catches forgotten placeholders before they become permanent

Negative:

  • Existing 21 work items will trigger warnings until fixed
  • Minor noise during govctl check until addressed

Migration: Fix existing work items by adding meaningful descriptions based on their titles and acceptance criteria.

ADR-0011: Inline reference expansion in rendered content

Status: accepted | Date: 2026-01-17

Tags: editing

References: RFC-0000

Context

Content fields (description, context, decision, consequences) are rendered as-is with no link expansion. The refs field gets expanded to markdown links, but inline references like [RFC-0000](../rfc/RFC-0000.md) in text remain as literal strings.

This creates an inconsistency:

  1. The source_scan feature uses [[artifact-id]] pattern to detect references in source code
  2. Users might expect the same pattern to work in content fields
  3. Without expansion, content authors must write full markdown links manually

The source_scan config already defines a customizable pattern field for reference format.

Decision

Add inline reference expansion to content fields during rendering:

  1. Use the same pattern from source_scan.pattern config (defaults to [[RFC-NNNN]] format)
  2. Expand matched references using existing ref_link() function
  3. Apply to: description, context, decision, consequences, notes, acceptance_criteria text
  4. Pattern is evaluated at render time, so config changes apply on next render

This creates consistency between source code scanning and content rendering — the same reference format works everywhere.

Consequences

Positive:

  • Inline references in content become clickable links
  • Consistent with source_scan pattern format
  • No new config needed (reuses source_scan.pattern)
  • Works with custom patterns if configured

Negative:

  • Couples rendering to source_scan config (even if source_scan.enabled is false)
  • Literal [[text]] in content will be matched if it looks like an artifact ID

Migration: Existing content with literal [[...]] that happens to match artifact patterns will now become links. This is likely the desired behavior.

ADR-0012: Prefix-based changelog category parsing

Status: accepted | Date: 2026-01-17

Tags: release

References: ADR-0013

Context

The govctl bump command supports adding changelog entries via -c flags, but all changes are hardcoded to the added category. The ChangelogEntry model supports 6 categories (added, changed, deprecated, removed, fixed, security) per Keep a Changelog format, but users must manually edit JSON to use any category other than added.

Decision

Parse conventional-commit-style prefixes from change strings to route to the correct changelog category. Supported prefixes: add:, fix:, changed:, deprecated:, removed:, security:. Unknown prefixes produce a validation error. No prefix defaults to added for backward compatibility.

Example: govctl bump RFC-0001 --patch -m "Bug fixes" -c "fix: memory leak" -c "security: patched CVE"

Consequences

Users can populate all changelog categories via CLI without JSON editing. The prefix syntax follows conventional commits, reducing learning curve. Invalid prefixes are caught early with helpful error messages listing valid options.

ADR-0013: Add category field to acceptance criteria for changelog generation

Status: accepted | Date: 2026-01-17

Tags: schema

References: ADR-0012

Context

Work items track implementation work (features, fixes, refactoring), but lack categorization that maps to Keep a Changelog format. RFC changelog entries already use Keep a Changelog categories (Added, Changed, Deprecated, Removed, Fixed, Security). To generate a repo-level CHANGELOG.md from completed work items, we need the same categorization. Acceptance criteria are the natural atomic unit for changelog entries - each criterion represents one deliverable.

Decision

Add a category field to ChecklistItem (acceptance criteria) using the existing ChangelogCategory enum (added, changed, deprecated, removed, fixed, security). Default to added for backward compatibility. Reuse ADR-0012 prefix parsing: govctl add WI-xxx acceptance_criteria "fix: memory leak" parses to category=fixed, text="memory leak". This consolidates ChangelogCategory from write.rs into model.rs for reuse across RFC changelogs and work item criteria.

Consequences

Acceptance criteria can be grouped by category when rendering CHANGELOG.md. Each criterion becomes one changelog entry. Work items can span multiple categories without being split. Existing work items remain valid (criteria default to added). Single ChangelogCategory enum used for both RFC changelog entries and work item criteria.

Alternatives Considered

Add category field to WorkItemMeta (work-item level) (rejected)

Add category field to ChecklistItem (criterion level) with prefix parsing (accepted)

ADR-0014: Release management with releases.toml

Status: accepted | Date: 2026-01-17

Tags: release

References: ADR-0013, RFC-0000:C-WORK-DEF

Context

To generate a CHANGELOG.md from work items, we need version information. Work items have completed dates but no version. Version is a release-time concept, not a work-time concept. We need a way to track which work items belong to which release without mutating completed work items.

Decision

Store release history in a single gov/releases.toml file with explicit work item references:

[[releases]]
version = "0.2.0"
date = "2026-01-17"
refs = ["WI-2026-01-17-029", "WI-2026-01-17-008"]

The govctl release <version> command collects all done work items not yet in any release and adds them to a new release entry. The govctl render changelog command generates CHANGELOG.md grouped by release version and category.

Consequences

Completed work items remain immutable. Explicit refs list eliminates ambiguity about release membership. Single file is simpler than per-release artifacts. Unreleased work items appear under [Unreleased] section in changelog.

Alternatives Considered

Add version field to work items after release (rejected)

Store releases in single gov/releases.toml with explicit refs (accepted)

ADR-0015: Context-aware self-describing CLI for agent discoverability

Status: accepted | Date: 2026-01-18

Tags: cli

References: RFC-0000

Context

AI coding agents (Claude, Cursor, Codex, etc.) can invoke shell commands, making govctl immediately usable. However, agents lack semantic understanding of when to use which command and why.

User feedback: “I need to tell the agent in my prompt what this tool is and when to run it.”

Current solutions:

  1. MCP (Model Context Protocol) — adds structured tool definitions, but requires server setup, process management, and creates a parallel interface to maintain
  2. Agent guide files (.claude/CLAUDE.md) — static, requires manual updates, doesn’t reflect current project state
  3. --help output — describes syntax, not semantics or workflow context

The core problem: discovery and context-awareness, not invocation. Agents need to understand govctl’s philosophy, command purposes, and what actions are relevant given current project state.

Decision

Add a govctl describe command with two modes:

1. Static mode (default):

govctl describe --json

Outputs machine-readable JSON containing:

  • version: govctl version
  • purpose: one-line description of govctl’s role
  • philosophy: core principles (RFC supremacy, phase discipline)
  • commands[]: for each command:
    • name: command name (e.g., “new rfc”, “advance”)
    • purpose: what it does
    • when_to_use: semantic guidance on when to invoke
    • example: concrete usage
    • prerequisites: what must be true before running
  • workflow: typical command sequence for common tasks

2. Context-aware mode:

govctl describe --context --json

Reads current project state and outputs:

  • project_state: current RFCs, ADRs, work items with their statuses/phases
  • suggested_actions[]: contextually relevant commands with reasons
    • Example: “RFC-0001 is in impl phase. If implementation complete, run govctl advance RFC-0001 test
  • warnings[]: governance issues detected (stale items, blocked transitions)

Implementation:

  • Command metadata derived from existing clap #[command(about = "...")] attributes where possible
  • Semantic guidance (when_to_use) defined as static data
  • Context mode reuses status and list logic for project state
  • Output format follows JSON Schema for predictable parsing

Consequences

Positive:

  • Agents gain semantic understanding without MCP complexity
  • Single interface (CLI) — no parallel implementation to maintain
  • Context-aware mode reduces agent trial-and-error
  • Works with any shell-capable agent (not limited to MCP-compatible tools)
  • Self-documenting: govctl describe output stays in sync with actual commands
  • Distribution unchanged: cargo install govctl is all users need

Negative:

  • Additional ~200-300 lines of code for command metadata and describe logic
  • Semantic guidance (when_to_use) must be manually authored and maintained
  • JSON output format becomes a compatibility surface (changes need care)

Neutral:

  • Agents must call govctl describe once per session (cacheable)
  • Plain --help remains available for human users
  • Does not preclude future MCP integration if demand materializes

Comparison to MCP:

Aspectgovctl describeMCP
Distributioncargo install govctlServer config + process mgmt
Agent compatibilityAny shell-capable agentMCP-compatible agents only
MaintenanceSingle codebaseCLI + MCP server
Context awarenessBuilt-inSeparate implementation
Works offlineYesDepends

ADR-0016: Allow RFC amendments via versioning during implementation

Status: accepted | Date: 2026-01-19

Tags: lifecycle

References: RFC-0001, ADR-0004

Context

Real-world governance experience from the neotex-v2 project shows that RFCs often need amendments during implementation. This conflicts with the current govctl mental model that treats normative status as “frozen” — the documentation (CLAUDE.md:79) states “normative: Frozen. Implementation MUST conform.”

This creates a false dichotomy:

  • Theory: Spec everything perfectly, then implement
  • Practice: You discover spec bugs, ambiguities, and wrong assumptions during implementation

The problem is not with RFC-0001:C-RFC-STATUS (which only says “normative” means “binding”), but with the interpretation that “binding” implies “immutable.” This forces workarounds: draft ADRs, inline comments, or ignoring governance entirely.

The infrastructure for RFC evolution already exists: versioning (version field) and changelog (changelog array per ADR-0004). We’re just not using it for governance.

Decision

Clarify the semantics of “normative”:

  • normative means binding (code must conform to current version)
  • normative does NOT mean frozen (spec can evolve with version bumps)

Operational changes:

  1. Remove “normative = frozen” messaging from all documentation
  2. Document that normative RFCs MAY be amended via version bumping
  3. When amending a normative RFC:
    • Bump version according to semantic versioning
    • Add changelog entry documenting the change
    • Rationale and audit trail live in git/jj history

Precedent: Linux kernel APIs are both binding (drivers must conform) and evolving (with deprecation, versioning, compatibility) simultaneously.

Consequences

Easier:

  • Amending RFCs during implementation (matches real workflow)
  • Iterative spec refinement (discover-fix-document cycle)
  • Honest governance (no pretending specs are perfect)

More difficult:

  • Reviewers must check RFC changelog to see what changed
  • Multiple versions of an RFC might exist during development (but this is reality anyway)

No breaking changes:

  • Existing RFCs remain valid
  • No data model changes required
  • Validation rules simplified (remove frozen assumption)

ADR-0017: CLI Command Implementation Details

Status: accepted | Date: 2026-01-19

Tags: cli

References: RFC-0002, ADR-0037

Context

RFC-0002 defines the structural contract for govctl CLI commands: resource-first organization, universal CRUD verbs, lifecycle operations, and output format control. However, RFC-0002 intentionally defers implementation details to preserve flexibility while maintaining a stable interface contract.

Implementation requires decisions on:

  • Exact flag syntax and naming conventions
  • Array field editing notation (how to add/remove items)
  • Help text formatting and structure
  • Error message templates and exit codes
  • Terminal capability detection (colors, interactive prompts)
  • Confirmation prompt behavior
  • Progress indicators for long-running operations

Without documented conventions, implementers would make inconsistent choices, leading to:

  • Flags named differently across commands (–filter vs –query)
  • Array editing syntax that varies by resource type
  • Inconsistent help text structure
  • Error messages with different formats
  • Terminal behavior that breaks in non-interactive contexts

This ADR establishes implementation conventions that realize RFC-0002’s structural contract with consistent UX.

Decision

We adopt the following implementation conventions:

1. Flag Syntax and Naming

Long flags: Always use double-dash with kebab-case

  • --output, --filter, --dry-run, --force

Short flags: Single letter aliases for common flags only

  • -o for --output
  • -f for --force
  • -n for --limit (per ADR-0019, aligns with head/tail convention)

No short flag for --dry-run: Safety flags benefit from explicit long form.

No short flags for domain-specific options to avoid collision:

  • --filter has no short form
  • --rfc-id has no short form

Flag values:

  • Space-separated: --output json (preferred)
  • Equals accepted: --output=json (clap auto-supports)
  • No colons: avoid --output:json

2. Field Editing Commands

govctl uses distinct verbs for different mutation operations. Each verb is clear and discoverable via --help.

Scalar fields: Use set

govctl rfc set RFC-0001 title "New Title"
govctl work set WI-001 description "Updated description"
govctl adr set ADR-001 status accepted

Array fields - append: Use add

govctl work add WI-001 refs RFC-0001
govctl rfc add RFC-0001 owners @alice
govctl work add WI-001 acceptance_criteria "New criterion"

Array fields - remove: Use remove

govctl work remove WI-001 refs RFC-0001
govctl rfc remove RFC-0001 owners @bob

Checklist status: Use tick

govctl work tick WI-001 acceptance_criteria "Tests pass" -s done
govctl work tick WI-001 acceptance_criteria "Docs" -s pending

Pattern matching for array operations: Same semantics as ADR-0007

  • Default: case-insensitive substring match
  • Use --exact for exact match
  • Use --regex for pattern match
  • Use --at N for index-based access

Rationale: Distinct verbs (set, add, remove, tick) are more intuitive and discoverable than a unified edit command with prefix notation. Users immediately understand what each command does.

3. Help Text Structure

All commands follow this template:

USAGE:
    govctl <resource> <verb> [OPTIONS] <ARGS>

ARGS:
    <required>     Description
    [optional]     Description

OPTIONS:
    -o, --output <FORMAT>    Output format [default: table] [possible: json, yaml, toml, plain]
    --filter <EXPR>          Filter results (KEY=VALUE)
    --dry-run                Preview changes without writing
    -h, --help               Print help

EXAMPLES:
    govctl rfc list draft
    govctl rfc get RFC-0001 -o json
    govctl rfc set RFC-0001 title "New Title"

Ordering:

  1. Universal flags first (output, filter, dry-run)
  2. Resource-specific flags second
  3. Help/version last

4. Error Messages and Exit Codes

Exit codes:

  • 0 - Success
  • 1 - General error (validation, not found, invalid transition)
  • 2 - Usage error (wrong arguments, unknown flag)

Error format:

error[CODE]: <message>
  --> <location>
  |
  | <context>

Example:

error[E0102]: RFC not found: RFC-9999
  --> gov/rfc/RFC-9999
  |
  | Run 'govctl rfc list' to see available RFCs

Diagnostic codes: Use existing govctl error taxonomy (E0xxx, W0xxx).

5. Terminal Capability Detection

Color output:

  • Auto-detect TTY with atty or is-terminal crate
  • Respect NO_COLOR environment variable
  • Respect --color <always|never|auto> flag (future)

Interactive prompts:

  • Only show in TTY mode
  • Skip if --force flag provided
  • Skip if stdin is not TTY

Progress indicators:

  • Use indicatif crate for long operations (>2s expected)
  • Show progress for: render, check, bulk operations
  • Suppress if --quiet or non-TTY

6. Confirmation Prompts

Destructive operations require confirmation unless --force:

  • delete (clauses, work items)
  • deprecate (RFCs, clauses)
  • supersede (RFCs, ADRs, clauses)

Prompt format:

Delete work item WI-2026-01-19-001? [y/N]

Behavior:

  • Default to “no” (capital N)
  • Accept: y, Y, yes, Yes, YES
  • Reject: n, N, no, No, NO, empty, anything else
  • Timeout: none (wait indefinitely)

7. Filter Syntax

Per RFC-0002:C-CRUD-VERBS, list commands support filtering.

Simple filter (shorthand):

govctl rfc list draft          # filter by status
govctl work list active        # filter by status

Explicit filter (future extension):

govctl rfc list --filter status=draft
govctl rfc list --filter phase=impl,status=normative

Implementation: Start with simple substring matching, defer explicit expressions to future ADR.

8. Stdin Handling

When --stdin flag is present:

  • Read entire stdin to EOF
  • Trim trailing newline only (preserve internal newlines)
  • Empty stdin is valid (allows heredocs with empty content)

HEREDOC pattern (recommended for multi-line):

govctl clause set RFC-0001:C-SCOPE text --stdin <<'EOF'
This clause defines the scope.
EOF

9. Dry-Run Behavior

Global --dry-run flag:

  • Shows what would be written
  • Prints diff or file preview
  • Exits with 0 (success simulation)
  • No short flag (safety flags should be explicit)

Output format:

[DRY RUN] Would write: gov/rfc/RFC-0001/rfc.json
--- before
+++ after

10. Subcommand Organization (clap)

Structure:

Use nested Subcommand enums for resource-first organization.
Resource commands contain their own verb subcommands.
Global commands remain at top level.

Backward compatibility aliases: Emit deprecation warning when old verb-first commands are used.

Consequences

Positive:

  1. Consistent UX: All commands follow the same patterns. Users learn once, apply everywhere.

  2. Agent-friendly: Predictable flags, stable error codes, machine-readable output defaults make automation reliable.

  3. Maintainable: Implementation conventions documented, not scattered across code comments.

  4. Discoverable: Distinct verbs (set, add, remove, tick) are immediately understandable. Users can explore via --help without reading documentation.

  5. Safe: Confirmation prompts prevent accidental destructive actions. Dry-run mode enables testing before committing.

  6. Debuggable: Structured error messages with codes and locations make issues easy to diagnose.

Negative:

  1. Multiple verbs: Users must learn four verbs for mutation operations (set, add, remove, tick) instead of one. However, each verb’s meaning is immediately clear.

  2. Testing complexity: Need tests for TTY detection, confirmation prompts, dry-run mode, error formatting, etc.

Trade-offs Accepted:

  • Clarity over brevity: Distinct verbs are more verbose than a unified edit command, but much easier to understand. add and remove are clearer than edit +value and edit -value.

  • Discoverability over uniformity: Four clear verbs are better than one verb with special syntax that requires documentation.

  • Safety over speed: Confirmation prompts slow down destructive operations. This is intentional to prevent accidents.

ADR-0018: Global Command Shortcuts vs Strict Resource-First Syntax

Status: accepted | Date: 2026-01-19

Tags: cli

References: RFC-0002, ADR-0017

Context

During implementation of RFC-0002, we successfully built the resource-first command structure (govctl <resource> <verb>). However, a design question emerged about backward compatibility with existing verb-first shortcuts.

Current Implementation

Both syntaxes currently work:

# Resource-first (RFC-0002 canonical)
govctl rfc list
govctl work new "title"
govctl adr accept ADR-0001

# Verb-first shortcuts (legacy)
govctl list rfc
govctl new work "title"

The Tension

RFC-0002:C-GLOBAL-COMMANDS explicitly defines which commands should remain global:

  • init, check, status, render, describe, completions

Notably absent from this list: list, new, and all resource-specific operations like move, tick, accept, etc.

This suggests the RFC intentionally designed these as resource-scoped operations, not global commands.

Problem Statement

Should we:

  1. Keep list and new as convenient global shortcuts that delegate to resource-first implementations?
  2. Remove them entirely and enforce strict resource-first syntax per RFC-0002?
  3. Keep them with deprecation warnings for gradual migration?

User Impact

We have ~40 completed work items and governance workflows that use the old syntax. Scripts, documentation, and muscle memory are built around patterns like govctl list rfc and govctl new work.

However, we are pre-1.0 (currently 0.2.0), which traditionally allows breaking changes.

Implementation Complexity

Current implementation maintains both paths through the canonical command pattern, adding ~40 lines of delegation code. This works but violates the “one way to do it” principle.

Decision

Decision: Remove shortcuts immediately (Option 2)

We will remove govctl list and govctl new shortcuts entirely, enforcing strict resource-first syntax per RFC-0002.

Rationale

  1. RFC Compliance: RFC-0002:C-GLOBAL-COMMANDS deliberately defines the exhaustive list of global commands. list and new are not on that list, indicating they should be resource-scoped.

  2. Design Clarity: The resource-first pattern (govctl <resource> <verb>) provides a clear, predictable structure:

    • govctl rfc list - list RFCs
    • govctl work new - create work item
    • govctl adr accept - accept ADR

    Having two ways (govctl list rfc vs govctl rfc list) violates the “one obvious way” principle.

  3. Reduced Complexity: Maintaining both patterns adds ~40 lines of delegation code and increases cognitive load for users (“which way should I use?”).

  4. Pre-1.0 Window: We’re at version 0.2.0, which traditionally allows breaking changes before stabilizing at 1.0.

  5. Shell Aliases Sufficient: Users who want shortcuts can add personal aliases:

    alias gl='govctl rfc list'
    alias gw='govctl work'
    alias ga='govctl adr'
    

    This moves convenience to where it belongs—user preference—rather than mandating it in the tool.

  6. Consistency: If we keep list and new as globals, why not move, tick, accept, reject? The line becomes arbitrary. Resource-first is consistent across all operations.

Migration Plan

Since this is a breaking change:

  1. Update all documentation to use resource-first syntax ✅ (done)
  2. Update internal workflows (gov.md, .claude/CLAUDE.md) ✅ (done)
  3. Remove from Commands enum in main.rs
  4. Remove delegation logic from command_router.rs
  5. Update CHANGELOG.md noting the breaking change
  6. Consider: Add migration guide in docs showing the mapping

Non-breaking Alternative Considered

We could keep shortcuts with deprecation warnings (Option 3), but this:

  • Extends the transition period indefinitely
  • Keeps dual-path complexity in codebase
  • Delays the inevitable

Given pre-1.0 status, clean break is preferable.

Consequences

Positive

  • Single Canonical Syntax: Only one way to invoke commands reduces confusion
  • RFC Compliance: Aligns perfectly with RFC-0002:C-GLOBAL-COMMANDS
  • Simpler Codebase: ~40 fewer lines of delegation logic to maintain
  • Clearer Documentation: Examples show only one pattern, easier to teach
  • Predictable: govctl <resource> <verb> works for all operations without exceptions
  • Tab Completion: Clearer completion tree (type govctl → see resources, not mix of resources + verbs)

Negative

  • Breaking Change: Existing scripts using govctl list or govctl new will break
    • Mitigation: We’re pre-1.0, document in CHANGELOG
  • More Keystrokes: govctl rfc list is longer than govctl list rfc
    • Mitigation: Shell aliases for personal shortcuts
  • Learning Curve: Users familiar with old syntax need to adjust
    • Mitigation: Clear error messages suggesting correct syntax

Migration Required

Users need to update:

# Old → New
govctl list rfc        → govctl rfc list
govctl list adr        → govctl adr list
govctl list work       → govctl work list
govctl list clause     → govctl clause list
govctl new rfc "..."   → govctl rfc new "..."
govctl new adr "..."   → govctl adr new "..."
govctl new work "..."  → govctl work new "..."
govctl new clause ...  → govctl clause new ...

Future Extension

This decision does NOT prevent adding convenience aliases later if user demand warrants it. However, any such addition would require an RFC amendment to RFC-0002:C-GLOBAL-COMMANDS.

Recommendation for Users

Add personal shell aliases for frequently used commands:

# In ~/.bashrc or ~/.zshrc
alias gr='govctl rfc'
alias ga='govctl adr'
alias gw='govctl work'
alias gc='govctl clause'

# Usage becomes:
gr list        # govctl rfc list
gw new "task"  # govctl work new "task"

This provides the convenience without coupling it to the tool.

Alternatives Considered

Keep both syntaxes permanently

Remove shortcuts immediately (strict RFC-0002)

Phased deprecation (warnings now, remove in 1.0)

Expand shortcuts to all common operations

ADR-0019: Change -n from dry-run to limit for better Unix convention alignment

Status: accepted | Date: 2026-01-19

Tags: cli

References: ADR-0006, ADR-0017

Context

ADR-0006 and ADR-0017 assigned -n as the short flag for --dry-run, following rsync’s convention. However, this conflicts with the more universal Unix convention where -n means “number” or “limit”:

Standard Unix tools using -n for number/limit:

  • head -n 10 (limit to N lines)
  • tail -n 10 (limit to N lines)
  • grep -n (show line numbers)
  • sort -n (numeric sort)
  • ls -n (numeric IDs)

Tools using -n for dry-run:

  • rsync -n (dry-run) - notable exception
  • Very few other standard tools

Current problem: We need to add --limit flag to all list commands to control result count. The natural short flag would be -n, but it’s already taken by dry-run. This forces us to either:

  1. Use no short flag for limit (verbose, less ergonomic)
  2. Change -n to mean limit (breaks ADR-0006/ADR-0017)

Impact of current design:

  • govctl work list --limit 5 is verbose (no short option)
  • Inconsistent with head -n 5 / tail -n 5 muscle memory
  • Dry-run is a safety flag (verbose is acceptable)
  • Limit is used frequently in interactive sessions (short flag valuable)

Decision

Reassign -n from dry-run to limit:

  1. Remove -n short flag from --dry-run (keep long form only)

    • Dry-run remains accessible as --dry-run
    • This is a safety flag - verbosity is appropriate
  2. Add -n short flag to --limit on all list commands

    • govctl rfc list -n 10
    • govctl work list -n 5
    • Aligns with head -n / tail -n conventions
  3. Amend ADR-0006 and ADR-0017 to reflect this change

Rationale:

  • Unix convention alignment: -n for “number” is far more common
  • Ergonomics: Limit is used frequently, dry-run less so
  • Safety: Dry-run benefits from being explicit (--dry-run)
  • Pre-1.0 window: Breaking changes are acceptable now
  • Consistency: Follows head/tail patterns users know

Consequences

Positive:

  • Natural -n for limit matches Unix expectations
  • More ergonomic interactive use: govctl work list -n 5
  • Explicit --dry-run is clearer for safety-critical flag
  • Consistent with standard tooling (head, tail, etc.)

Negative:

  • Breaking change: Scripts using -n for dry-run will break
  • Requires updating ADR-0006 and ADR-0017
  • Requires updating any documentation mentioning -n

Migration:

  • All uses of -n must change to --dry-run
  • Update .claude/CLAUDE.md and other docs
  • Add to CHANGELOG as breaking change
  • Search codebase for any hardcoded -n usage

Affected ADRs:

  • ADR-0006: Originally defined -n for dry-run
  • ADR-0017: Documents -n as global dry-run flag

ADR-0020: Configurable work item ID strategies for multi-person collaboration

Status: accepted | Date: 2026-01-26

Tags: collaboration, work-items

References: RFC-0000

Context

The current work item ID format (WI-YYYY-MM-DD-NNN) uses local sequential numbering. The govctl work new command scans gov/work/ to find the max sequence number for today’s date, then increments by 1.

Problem: When multiple people work on parallel branches and both create work items on the same day, they get the same ID:

Alice: scans → max=002 → creates WI-2026-01-26-003
Bob:   scans → max=002 → creates WI-2026-01-26-003 (collision!)

On merge, these IDs collide. This blocks multi-person teams from adopting govctl.

Options considered:

StrategyFormatProsCons
Author namespaceWI-2026-01-26-alice-001Clear ownership, sequential per authorRequires config
Git identity hashWI-2026-01-26-a7f3-001Auto from git email, no configLess readable
Random suffixWI-2026-01-26-a7f3Simple, no coordinationNon-sequential
TimestampWI-2026-01-26-143257Natural orderingClock skew issues
Merge-time renumberKeep currentMinimal changeBreaks refs on rename

govctl is primarily single-contributor but must support multi-person teams as external adopters.

Decision

Add opt-in ID strategies via gov/config.toml, keeping current behavior as default.

  1. Default: sequential (current behavior)

    • Format: WI-YYYY-MM-DD-NNN
    • Solo projects keep simple, readable IDs
    • No breaking change for existing users
  2. Opt-in: author-hash (recommended for teams)

    • Format: WI-YYYY-MM-DD-{hash4}-NNN
    • {hash4} = first 4 chars of sha256(git config user.email)
    • Example: WI-2026-01-26-a7f3-001
    • Each contributor gets their own sequence namespace
    • Zero configuration (auto-derived from git identity)
  3. Opt-in: random (simple uniqueness)

    • Format: WI-YYYY-MM-DD-{rand4}
    • {rand4} = 4 random hex chars (65,536 possibilities per day)
    • Example: WI-2026-01-26-b2c9
    • No sequence number, just unique suffix

Configuration:

# gov/config.toml
[work_item]
id_strategy = "author-hash"  # or "sequential" (default), "random"

Rationale:

  • Backward compatible: default remains sequential
  • Teams explicitly opt-in to collision-safe strategy
  • author-hash is recommended because it preserves sequential numbering within author namespace
  • Uses git identity (already required for commits) — zero additional config

Consequences

Positive:

  • Multi-person teams can adopt govctl without ID collision risk
  • Existing single-contributor projects unchanged
  • author-hash provides both collision safety AND sequential ordering
  • Zero configuration for author-hash (uses git email)

Negative:

  • New ID formats (WI-...-a7f3-001) are less human-memorable
  • Cannot easily tell “who created this” without looking up the hash
  • Projects must explicitly configure for team use

Implementation:

  • Add id_strategy to Config struct
  • Modify src/cmd/new.rs::create_work_item() to dispatch based on strategy
  • Add validation that all work items follow configured strategy
  • Update gov/schema/SCHEMA.md to document new formats

Migration for teams:

  1. Set [work_item] id_strategy = "author-hash" in gov/config.toml
  2. Existing work items retain their IDs (no renaming required)
  3. New work items use the new format

ADR-0021: Resource-scoped render commands for single-item rendering

Status: accepted | Date: 2026-01-29

Tags: cli

References: RFC-0002, ADR-0018

Context

The global govctl render command currently has an --rfc-id flag for rendering a single RFC, but no equivalent flags for ADRs or work items. This creates an asymmetry in the CLI.

Problem Statement

When users want to render a single artifact, the current options are:

ArtifactCurrent SyntaxWorks?
RFCgovctl render rfc --rfc-id RFC-0001✅ Yes
ADR(none)❌ No
Work Item(none)❌ No

Options Considered

  1. Add --adr-id and --work-id flags — Proliferates flags on global command
  2. Add resource-scoped render verbs — Clean separation of bulk vs single
  3. Leave as-is — Accept asymmetry

Constraints

Decision

We will add resource-scoped render commands and remove the --rfc-id flag from the global render command.

New Commands

govctl rfc render <RFC-ID>    # Render single RFC
govctl adr render <ADR-ID>    # Render single ADR
govctl work render <WI-ID>    # Render single work item

Modified Command

# Global render now ONLY does bulk operations
govctl render [rfc|adr|work|changelog|all]  # No --rfc-id flag

Rationale

  1. Clean separation: Global render = bulk operations, resource-scoped = single-item
  2. No flag proliferation: Avoids --rfc-id, --adr-id, --work-id on global command
  3. Discoverable: govctl rfc --help shows render alongside other verbs
  4. Noun-first for single items: Consistent with govctl rfc get, govctl rfc list
  5. RFC-0002 compatible: render remains global for bulk; adding resource verbs doesn’t violate this

Implementation Notes

  • Resource-scoped render shares implementation with global render (just filters to single ID)
  • Error handling: if ID not found, return clear error message
  • Output: same format as bulk render, just for one item

Consequences

Positive

  • Symmetric API: All resource types support single-item render equally
  • Cleaner global command: No proliferation of --*-id flags
  • Intuitive mental model: “Work with RFC” → govctl rfc render RFC-0001
  • Better discoverability: govctl rfc --help shows all RFC operations including render

Negative

  • Breaking change: Scripts using govctl render --rfc-id must update
    • Mitigation: Pre-1.0, document in CHANGELOG
  • Two entry points for render: Global (bulk) and resource-scoped (single)
    • Mitigation: Clear purpose split makes this a feature, not a bug

Migration

# Old syntax (removed)
govctl render rfc --rfc-id RFC-0001

# New syntax
govctl rfc render RFC-0001

Alternatives Considered

Add –adr-id and –work-id flags to global render: Proliferates flags, violates ‘one way’ principle

Leave as-is with only –rfc-id: Asymmetric, inconsistent UX

ADR-0022: Add show command for stdout rendering

Status: accepted | Date: 2026-02-07

Tags: cli

References: RFC-0002, ADR-0021

Context

Agents (Claude, Cursor, etc.) intuitively try govctl rfc show RFC-0001 to read an RFC’s rendered content, but this command doesn’t exist. The error unrecognized subcommand 'show' is a recurring failure mode.

Problem Statement

The current CLI has an asymmetry in read operations:

VerbPurposeOutput
getRead individual field valuestdout
renderGenerate full markdownfile (side effect)
(missing)Read full rendered contentstdout

The gap: there’s no way to get the full rendered representation to stdout without writing a file.

Current Workarounds

  1. govctl rfc render RFC-0002 --dry-run — shows preview, but semantically wrong (dry-run is for previewing writes)
  2. Read docs/rfc/RFC-0002.md directly — requires knowing the configurable output path

Both are awkward for agents who just want to “see” an artifact.

Constraints

Mental Model

Unix convention: commands that read data print to stdout; commands that generate files write to filesystem. show fits the “read and display” pattern like cat, kubectl get -o yaml, or docker inspect.

Decision

We will add a show verb to all renderable resources that outputs content to stdout.

New Commands

govctl rfc show <RFC-ID>       # Print rendered RFC to stdout
govctl adr show <ADR-ID>       # Print rendered ADR to stdout
govctl work show <WI-ID>       # Print rendered work item to stdout
govctl clause show <CLAUSE-ID> # Print clause content to stdout

Output Format

  • Default: Markdown text (the human-readable rendered form)
  • With --output json: Structured JSON (equivalent to get with no field)
  • Respects RFC-0002:C-OUTPUT-FORMAT conventions

Semantic Distinction

CommandPurposeSide Effects
showRead and display to stdoutNone
renderGenerate and write to fileWrites file
getRead single field valueNone

Rationale

  1. Agent-friendly: Natural command that agents try first (show = “let me see this”)
  2. Unix convention: Read operations → stdout; write operations → filesystem
  3. Minimal implementation: Reuses existing render logic, just changes output target
  4. Symmetric with render: show is to render as cat is to cp
  5. Format flexibility: --output json gives structured access when needed

Consequences

Positive

  • Fixes agent failures: govctl rfc show RFC-0001 will work as expected
  • Cleaner semantics: Clear separation between read (show) and write (render)
  • Pipeable: govctl rfc show RFC-0002 | less or | grep pattern just works
  • Consistent UX: All renderable resources get the same show verb

Negative

  • New verb to learn: Users must understand show vs render distinction
    • Mitigation: Intuitive naming makes this self-evident
  • Slight command surface growth: 4 new subcommands
    • Mitigation: Natural extension of existing pattern, improves discoverability

Neutral

  • --output json on show is functionally equivalent to get with no field argument — this redundancy is acceptable for discoverability

Alternatives Considered

Extend get with –rendered flag: Overloads get semantics, less discoverable

Use view instead of show: Equally valid, but show is more common in CLIs (docker inspect, kubectl describe)

Use cat as the verb: Unix-y but loses semantic meaning (cat is for concatenation)

ADR-0023: Organize assets into commands, skills, and agents subdirectories

Status: accepted | Date: 2026-02-11

Tags: skills-agents

References: ADR-0024

Context

The assets/ directory currently contains a flat mix of files: four command workflow definitions (gov.md, quick.md, discuss.md, status.md) and four logo SVGs. As we prepare to add skill definitions (specialized AI agent capabilities) and agent definitions (agent role/behavior configurations), the flat structure becomes ambiguous — it conflates three distinct categories of assets.

Problem Statement

With new asset types incoming, a flat assets/ directory provides no semantic separation. Developers (and tools like sync-commands.sh) must rely on conventions or file extensions to distinguish command templates from skills from agents.

Constraints

  • govctl init copies command templates via include_str! in src/cmd/new.rs — paths are compile-time constants
  • build.rs tracks command files for rebuild — paths must stay in sync
  • scripts/sync-commands.sh and sync-commands.ps1 glob assets/*.md — pattern must be updated
  • Logo SVGs are referenced from README.md and are a fourth category (static images)

Decision

Organize assets/ into three subdirectories by category:

  • assets/commands/ — AI workflow command definitions (gov.md, quick.md, discuss.md, status.md)
  • assets/skills/ — Skill definitions (new, initially empty)
  • assets/agents/ — Agent definitions (new, initially empty)

Logo SVGs remain at the assets/ root since they are static images, not AI-consumable definitions.

All compile-time paths (include_str!), build system paths (build.rs), and script globs are updated to reference assets/commands/.

Rationale

  1. Semantic clarity — the directory name tells you what kind of asset it is
  2. Minimal blast radius — logos stay put, only command .md files move one level deeper
  3. Future-proof — skills and agents get their own home from day one

Consequences

Positive

  • Clear separation of concerns for three distinct asset categories
  • New skills/agents have a designated location from the start
  • Scripts and tooling can target specific subdirectories

Negative

  • One-time update to all paths referencing command assets (4 files)
  • Test snapshots that reference output paths may need updating

Neutral

  • Logo SVGs stay at assets/ root — no change to README references

Alternatives Considered

Flat with naming conventions: Keep flat assets/ and use prefixes like cmd-gov.md, skill-foo.md. Rejected: naming conventions are fragile and don’t scale.

ADR-0024: Writers as skills, reviewers as agents for governance artifacts

Status: accepted | Date: 2026-02-11

Tags: skills-agents

References: ADR-0023

Context

govctl manages three governance artifact types — RFCs, ADRs, and work items. We need to add AI-assisted capabilities for both creating (writing) and reviewing these artifacts.

Per ADR-0023, the assets/ directory is organized into commands/, skills/, and agents/ subdirectories. Skills and agents are synced to .claude/skills/ and .claude/agents/ respectively.

Problem Statement

Each artifact type needs writing guidance (structure, conventions, quality patterns) and review criteria (completeness checks, quality gates). The question: should each capability be a skill (augments the main agent inline) or an agent (runs as isolated subagent with its own system prompt)?

Key Distinction

  • Skills run in the main agent’s context. They have full access to the conversation and codebase. They augment what the main agent knows.
  • Agents run in isolated contexts with custom system prompts. They receive delegated tasks and return results. They have no access to the main conversation.

Constraints

  • Skills MUST be <250 lines per SKILL.md (progressive disclosure via references/)
  • Agents MUST have focused system prompts
  • Each capability should follow “one skill, one capability” principle
  • The existing /discuss and /gov commands orchestrate workflows that need writing knowledge

Decision

Writers are skills. Reviewers and auditors are agents. Seven items total:

Skills (in assets/skills/)

SkillPurpose
rfc-writerHow to write well-structured RFCs: normative language (MUST/SHOULD/MAY), clause structure, versioning, since fields
adr-writerHow to write effective ADRs: context/decision/consequences, trade-off analysis, alternatives documentation
wi-writerHow to write good work items: acceptance criteria with category prefixes, description quality

Agents (in assets/agents/)

AgentPurpose
rfc-reviewerReview RFC drafts for completeness, normative language quality, clause coverage, cross-references
adr-reviewerReview ADR drafts for context quality, decision clarity, alternatives, honest consequences
wi-reviewerReview work items for acceptance criteria structure, category correctness, description quality
compliance-checkerVerify code conforms to normative RFC clauses and ADR decisions — detect spec violations in implementation

Rationale

  1. Writers need conversation context. An RFC writer must know why the user is creating the RFC — what problem, what constraints, what existing artifacts relate. Skills run inline and have full context.

  2. Reviewers benefit from cognitive isolation. When the main agent writes an artifact and then reviews it, confirmation bias is unavoidable. An isolated review agent has no memory of authoring — it evaluates purely on quality criteria.

  3. Compliance checking is auditing, not writing. The compliance-checker agent cross-references source code against normative RFC clauses (MUST/MUST NOT) and ADR decisions. It is distinct from artifact reviewers — those check if an artifact is well-written, this checks if code follows what artifacts specify. Isolation ensures no “I wrote this code so it must conform” bias.

  4. Seven separate, not consolidated. Each capability is genuinely different. Each stays well under 250 lines. The ~15% shared knowledge (govctl commands, [[ref]] syntax) is 3-5 lines of duplication — not worth coupling distinct capabilities to eliminate.

Integration

  • /discuss and /gov commands will reference writer skills for quality guidance
  • Review agents can be invoked at quality gates (e.g., after drafting, before finalization)
  • compliance-checker can be invoked during /gov Phase 4 (testing) or on demand
  • sync-assets.sh already syncs both skills/ and agents/ directories

Consequences

Positive

  • Writers augment the main agent with domain knowledge while preserving conversation context
  • Reviewers provide unbiased quality checks through cognitive isolation
  • Compliance checker catches spec violations that govctl check cannot (it validates references exist; the agent validates semantic conformance)
  • Seven focused items follow “one skill, one capability” — each stays under 250 lines
  • Future artifact types get their own skill + agent pair without touching existing files
  • sync-assets.sh already supports the directory structure (ADR-0023)

Negative

  • 7 files to maintain (mitigation: each is small and focused, changes are rare)
  • ~15% knowledge duplication across items (mitigation: 3-5 lines per file, not worth abstracting)
  • Review/audit agents lack conversation context (mitigation: they evaluate on merit, which is the point)
  • Compliance checking is inherently imprecise — agent may flag false positives (mitigation: output is advisory, not blocking)

Neutral

  • Existing /discuss and /gov commands continue to work unchanged initially; skills/agents integration is additive
  • govctl check continues to handle structural validation; compliance-checker handles semantic validation — complementary, not competing

Alternatives Considered

All skills: Make both writers and reviewers skills. Rejected: reviewers lose cognitive isolation, confirmation bias when main agent reviews its own work.

All agents: Make both writers and reviewers agents. Rejected: writers lose conversation context, cannot access why the user is creating the artifact.

4 consolidated items (governance-writer + wi-writer, governance-reviewer + wi-reviewer): Rejected: RFC and ADR capabilities are genuinely different, merging creates a skill that branches on artifact type — special cases we want to eliminate.

2 consolidated items (one writer, one reviewer): Rejected: work items differ fundamentally from RFCs/ADRs, no shared domain knowledge worth coupling.

ADR-0025: Concurrent write safety for agent-driven parallel tasks

Status: accepted | Date: 2026-02-15

Tags: safety

References: RFC-0002, ADR-0020

Context

When an agent (e.g. Cursor, Claude Code) runs multiple tasks in parallel that each invoke govctl to create or modify RFCs, ADRs, or work items, concurrent writes to the same files or to the same directory can cause:

  1. File corruption — Two processes write to the same file; interleaved writes or truncate-then-write races produce partial or invalid content.
  2. ID collision — Work item creation uses find_max_sequence(work_dir, id_prefix) then writes a new file. Two processes can read the same max, both write the same ID or overwrite the same path (same date-slug).
  3. Lost updates — Read-modify-write (e.g. edit, set, bump) without coordination: one process overwrites the other’s write.

This is distinct from ADR-0020, which addresses ID collision across branches (merge-time). Here the scenario is same repository, multiple concurrent processes (e.g. multiple agent tasks in one workspace).

Constraints: govctl is a CLI; no daemon. Implementation must work across processes. No network or external services. Must remain portable (Unix/macOS/Windows where feasible).

Decision

Use process-level filesystem locking so that only one govctl process mutates the governance tree at a time.

  1. Scope of locking

    • Any command that modifies gov/ or writes to docs/ (render, new, set, add, edit, tick, bump, finalize, advance, accept, move, etc.) MUST acquire a lock before performing mutations and release it when done.
    • Read-only commands (list, get, check, status, show) do NOT need to hold the lock.
  2. Lock mechanism

    • A single gov-root lock file (e.g. gov/.govctl.lock or a lock in a well-known location under gov root). One lock for the entire gov tree.
    • Acquire: exclusive (write) lock on that file (e.g. flock(LOCK_EX) on Unix; equivalent on Windows).
    • Blocking: if lock is held by another process, wait with optional timeout; on timeout, fail with a clear error instructing the user to retry or avoid parallel govctl writes.
  3. Granularity

    • Coarse-grained (one lock per gov root) is chosen over per-artifact or per-directory locks to avoid deadlock and to keep implementation and behavior simple. Parallel agents serialize at the gov root; throughput is traded for correctness and simplicity.

Consequences

Positive

  • Prevents file corruption and ID collision under concurrent agent tasks.
  • Single lock file: no deadlock, no lock ordering, easy to reason about.
  • Portable: file locking is available on all supported platforms (flock/cfg with fallbacks).

Negative

  • Parallel govctl write commands serialize; one task may block until another finishes. Mitigation: agents can be designed to queue writes or run write commands sequentially; CLI documents the locking behavior.
  • Stale lock if process crashes without releasing. Mitigation: lock is process-scoped (OS releases on exit); optional timeout + clear error message for “stuck” waiters.
  • Slight complexity in CLI entrypoint: acquire lock early for write commands, release on all exit paths.

Neutral

  • Render (writing to docs/) is included in the lock scope so that render + new/edit from two processes do not interleave.

Alternatives Considered

File lock (gov-root): One exclusive lock for entire gov/ tree. Blocks concurrent writers until release. Chosen for simplicity and no deadlock.

Per-artifact lock: Lock only the file or directory being written. Rejected: deadlock risk (e.g. A holds rfc/ B holds adr/; A needs adr/ B needs rfc/), more complex lock ordering.

Write queue / single-writer daemon: One process accepts write requests over a socket or FIFO. Rejected: requires a long-running daemon, contradicts CLI-only design.

Documentation-only: Tell users/agents not to run write commands in parallel. Rejected: does not prevent races; agents and scripts often parallelize by default.

Atomic write + retry: Write to temp then rename; for work item ID, retry on collision. Rejected: avoids partial writes but does not fix read-modify-write races or deterministic ID collision from find_max_sequence.

ADR-0026: Add journal field to WorkItem for execution tracking

Status: accepted | Date: 2026-02-22

Tags: editing

References: RFC-0000

Context

During agent-driven governance workflows, agents naturally use work items as “working memory” to track execution progress. Currently, this working memory is stored in the description field, mixing multiple semantic purposes:

  1. Task declaration — “What needs to be done” (static scope definition)
  2. Execution tracking — Progress updates, bug fixes, verification results (dynamic, frequently updated)
  3. Planning adjustments — Next steps, design decisions (evolves during execution)

This mixing causes description to become very long (4000+ characters in observed cases) and conflates declarative “what” with imperative “how it’s going.”

Example: In WI-2026-02-21-004, the description field contains:

  • Initial implementation plan (Steps 1-6)
  • Multiple “Progress update (YYYY-MM-DD, scope)” sections with detailed execution notes
  • Bug fix records, verification results, and next-step planning

The notes field exists but is underutilized because its original intent (“observations and decisions made during work”) was not clear enough, and agents naturally reached for description as the primary writing surface.

Reference: RFC-0000:C-CONTENT defines the content model but does not distinguish between declaration and tracking semantics.

Decision

Add a new journal field to WorkItemContent as a structured array for execution tracking, distinct from description (task declaration) and notes (ad-hoc points).

TOML structure:

Each journal entry has three fields:

  • date (required): ISO date string “YYYY-MM-DD”
  • scope (optional): Topic/module identifier for this entry
  • content (required): Markdown text with progress details

Example work item with journal:

[content]
description = "Big-bang AST/IR v2 migration with 6 implementation steps..."

[[content.journal]]
date = "2026-02-21"
scope = "typub-html"
content = "v2 parse + serialize paths active; fixed footnote issues"

[[content.journal]]
date = "2026-02-21"
scope = "typub-markdown"
content = "Migrated renderer tests to v2 fixtures; fixed footnote refs"

Data structure (Rust):

pub struct JournalEntry {
    pub date: String,
    pub scope: Option<String>,
    pub content: String,
}

Semantic boundaries:

FieldPurposeUpdate Pattern
descriptionTask scope declarationDefine once, rarely change
journalExecution process trackingAppend on each progress
notesAd-hoc key pointsAdd anytime, concise
acceptance_criteriaCompletion criteriaDefine then tick

Backward compatibility: The journal field is optional with #[serde(default)]. Existing work items remain valid without migration. New work items can adopt the field incrementally.

Changes required:

  1. Update WorkItemContent struct in src/model.rs
  2. Update gov/schema/work.schema.toml
  3. Update render logic in src/render.rs to include journal section
  4. Update wi-writer skill documentation

Consequences

What becomes easier:

  • Clear separation of concerns: description stays focused on task scope; execution details go to journal
  • Better readability: Rendered markdown shows a clean structure with dedicated sections
  • Agent ergonomics: Agents have a designated place for execution tracking without polluting description
  • Historical traceability: journal entries preserve the execution timeline

What becomes more difficult:

  • None significant. The field is optional and additive.

Migration impact:

  • No migration required for existing work items
  • Agents may gradually adopt journal for new or active work items
  • Old work items with “progress updates” in description remain valid

Documentation updates:

  • wi-writer skill updated with field usage guidelines
  • work.schema.toml updated with journal field definition
  • Rendered markdown includes “## Journal” section after description

ADR-0027: Extend Alternative structure with pros, cons, and rejection_reason

Status: accepted | Date: 2026-02-22

Tags: schema

References: RFC-0000:C-ADR-DEF

Context

The current Alternative structure in ADRs only has two fields: text and status. This is insufficient for capturing structured decision rationale:

  1. No pros/cons tracking: When comparing options, the advantages and disadvantages are key decision factors, but they cannot be structured.

  2. No rejection reason: When an alternative is rejected, the reason is often scattered in the decision or context fields instead of being directly attached to the alternative.

  3. Manual workarounds: Current practice requires authors to write Markdown tables in the context field to compare options, which is not structured and cannot be processed programmatically.

Example of current limitation:

[[content.alternatives]]
text = "Option A: Sequential IDs"
status = "rejected"
# Where do I put the pros/cons? In context as markdown table?

Comparison with other fields:

  • WorkItem has structured acceptance_criteria with status and category
  • RFC has structured changelog with categorized entries
  • ADR alternatives should have similar structure for decision tracking.

Decision

Extend the Alternative structure with three new optional fields:

pub struct Alternative {
    pub text: String,
    pub status: AlternativeStatus,
    pub pros: Vec<String>,              // NEW: advantages
    pub cons: Vec<String>,              // NEW: disadvantages
    pub rejection_reason: Option<String>, // NEW: why rejected
}

Field semantics:

  • pros: List of advantages for this alternative
  • cons: List of disadvantages for this alternative
  • rejection_reason: If status is rejected, explains why

Backward compatibility: All new fields use #[serde(default)] and skip_serializing_if_empty, so existing ADRs remain valid without migration.

Consequences

What becomes easier:

  • Structured comparison: Each alternative has its own pros/cons attached
  • Clearer rendering: Rendered output can show options with their trade-offs
  • Better tooling: CLI can display alternatives in a structured format (e.g., comparison table)
  • Self-documenting decisions: The reason for rejection is directly linked to the alternative

What becomes more difficult:

  • None significant. The fields are optional and additive.

Migration impact:

  • No migration required
  • Existing ADRs with simple alternatives continue to work
  • New ADRs can opt into the extended structure

Rendering example:

## Alternatives Considered

### Sequential IDs (rejected)
- Pros: Simple, Readable
- Cons: Collisions in teams
- Rejected because: Does not solve the collision problem

### Author hash namespace (accepted)
- Pros: Auto isolation, Zero config
- Cons: Slightly less readable

ADR-0028: Migrate commands to skills format for cross-platform compatibility

Status: accepted | Date: 2026-02-22

Tags: skills-agents

References: ADR-0024, ADR-0023

Context

The project currently has two mechanisms for AI agent capabilities:

  1. Commands (.claude/commands/): Slash commands for workflow orchestration
  2. Skills (.claude/skills/): Knowledge augmentation for the main agent

Industry trend: Major AI coding platforms are converging on skills as the standard format:

  • Claude/Cursor: Skills can be triggered via slash commands (commands and skills are functionally equivalent)
  • Codex: Only supports skills, no commands

Current structure:

.claude/commands/
├── discuss.md    # Design discussion workflow
├── gov.md        # Governed implementation workflow
├── quick.md      # Fast path workflow
└── status.md     # Governance status (rarely used)

.claude/skills/
├── rfc-writer/
├── adr-writer/
└── wi-writer/

Problem:

  • Two similar concepts (commands vs skills) create confusion
  • Commands are not portable to Codex
  • status command has very low usage frequency

Decision

Migrate all workflow commands to skills format:

Migration:

SourceTargetAction
commands/discuss.mdskills/discuss/SKILL.mdMigrate
commands/gov.mdskills/gov/SKILL.mdMigrate
commands/quick.mdskills/quick/SKILL.mdMigrate
commands/status.mdDelete (low usage)

Format change:

Extend skill frontmatter to include command-specific fields:

---
name: gov
description: "Execute governed workflow — work item, RFC/ADR, implement, test, done"
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
argument-hint: <what-to-do>
---

Why keep current names:

  • discuss - Clear intent for design discussion phase
  • gov - Established shorthand for governed workflow
  • quick - Clear intent for fast path

Why delete status:

  • Very low usage frequency observed
  • Equivalent information available via govctl status CLI
  • Reduces maintenance burden

Consequences

Positive:

  • Single unified format for all agent capabilities
  • Portable across Claude, Cursor, and Codex
  • Reduces conceptual overhead (one concept instead of two)
  • Eliminates underutilized status command

Negative:

  • One-time migration effort (minimal: 3 files)
  • Breaking change for users who reference commands directly
  • Need to update any documentation referencing commands/

Migration steps:

  1. Create skill directories for discuss, gov, quick
  2. Convert command files to skill format (add name field, preserve allowed-tools/argument-hint)
  3. Delete commands/ directory
  4. Update ADR-0023 to reflect new structure

Future structure:

.claude/skills/
├── discuss/
│   └── SKILL.md
├── gov/
│   └── SKILL.md
├── quick/
│   └── SKILL.md
├── rfc-writer/
│   └── SKILL.md
├── adr-writer/
│   └── SKILL.md
└── wi-writer/
    └── SKILL.md

ADR-0029: Path-based nested field addressing for artifact edits

Status: accepted | Date: 2026-02-25

Tags: editing

References: RFC-0002, ADR-0007, ADR-0017, ADR-0027

Context

govctl currently edits artifacts with resource-scoped verbs (set, add, remove, tick), matching ADR-0017.

Problem Statement

The current CLI shape becomes verbose and awkward when users need to edit deeply nested fields, especially ADR alternatives extended by ADR-0027 (pros, cons, rejection_reason).

Concrete pain point today:

# Create or replace whole alternative entries
govctl adr add ADR-0001 alternatives "Option 3: Use Raft" --pro "Strong consistency"

# Remove by match or index
govctl adr remove ADR-0001 alternatives "Option 3" --exact

What is missing is direct nested addressing, for example: update only the second pro of the second alternative without rewriting the entire object.

Constraints

  • Preserve resource-first command architecture from RFC-0002.
  • Preserve verb semantics from ADR-0017 instead of replacing everything with a new universal verb.
  • Reuse existing array matching behavior from ADR-0007 where possible.
  • Keep backward compatibility for existing scripts and habits.

Options Considered

  • Option 1: Keep current field-only operations and rely on manual TOML edits for nested changes.
  • Option 2: Introduce a new universal edit command with JSONPath-like syntax.
  • Option 3: Keep current verbs, but allow a path expression in the field position for nested addressing.

Decision

We will introduce path-based nested field addressing as an additive capability, while keeping existing resource-scoped verbs (set, add, remove, tick) defined in ADR-0017.

This decision is primarily motivated by ADR-0027, which introduced structured ADR alternatives (pros, cons, rejection_reason) that need precise nested edits.

Path expressions will be accepted where a field name is currently accepted for get, set, add, and remove.

Path Syntax (proposed)

  • Grammar (strict): path := segment ('.' segment | "[" index "]")*
  • Segment token: segment := [a-z_][a-z0-9_]*
  • Index token: index := -?[0-9]+
  • Dot notation for object traversal: content.alternatives
  • Bracket notation for array indexing: alternatives[2].pros[2]
  • Index semantics: 0-based, with optional negative index for from-end addressing (aligned with ADR-0007 --at semantics)
  • Aliases for ergonomics:
    • alt -> alternatives
    • pro -> pros
    • con -> cons
    • reason -> rejection_reason

Alias Collision Rule

Canonical field names take precedence. Alias expansion applies only when the token is not an exact field name in the current object scope. This keeps future schema evolution safe and predictable.

Verb Semantics

  • get: existing read verb, now path-aware for scalar/object/array reads
  • set: replace scalar value at resolved path
  • add: append into array resolved by path
  • remove: remove by explicit indexed path, or by existing matcher options when path resolves to an array
  • remove conflict rule: explicit indexed paths and matcher flags (--exact, --regex, --all, pattern args) are mutually exclusive; the CLI MUST fail fast with a usage error when mixed.
  • tick: unchanged behavior; only checklist roots are supported (alternatives for ADR, acceptance_criteria for work item). Nested paths for tick are invalid.

Before and After Example

# Before: remove and re-add an entire alternative to change one nested value
govctl adr remove ADR-0001 alternatives "Option 3" --exact
govctl adr add ADR-0001 alternatives "Option 3: Use Raft" --pro "Updated pro"

# After: direct nested edit
govctl adr set ADR-0001 alt[2].pro[0] "Updated pro"

Validation Timing

  • Eager validation (parse time): path grammar, token class, alias normalization
  • Resolution-time validation: field existence, index bounds, target type compatibility for verb

Compatibility and Convergence

  • Existing top-level field syntax remains valid.
  • Existing adr add ... alternatives --pro/--con/--reject-reason remains supported during migration.
  • Convergence plan: docs and examples immediately prefer path syntax; legacy alternative-specific flags receive deprecation warnings after rollout stabilizes (target: two minor releases), then are removed only in a major release.

Consequences

Positive

  • Enables concise, direct nested edits such as alt[2].pro[2] without editing raw TOML.
  • Preserves current mental model (set/add/remove/tick) and command discoverability.
  • Reuses existing match semantics from ADR-0007, reducing conceptual fragmentation.
  • Creates a reusable abstraction for nested fields across ADRs and work items.

Negative

  • Adds parser and resolver complexity in the edit pipeline.
    • Mitigation: strict grammar, bounded depth, and dedicated parser tests (including fuzz/property tests).
  • Requires broader validation and diagnostics coverage.
    • Mitigation: explicit diagnostic matrix for parse/resolution/type errors and snapshot tests for representative failures.
  • Dual syntax during migration (--pro/--con and path syntax) can confuse users.
    • Mitigation: docs prefer path syntax immediately, deprecation warnings for legacy flags after rollout, and a published convergence timeline.
  • Documentation and examples require coordinated updates across guides and help text.
    • Mitigation: track doc updates in implementation work items and gate release on docs parity checks.

Neutral

  • No data migration is required for existing ADR files; the change is CLI-surface and parser behavior.
  • Alias set is intentionally small and closed (alt, pro, con, reason); future alias additions require explicit schema/governance update to avoid accidental drift.

Alternatives Considered

Option 1: Keep current field-only operations and use manual TOML edits for nested changes (rejected)

  • Pros: No new parser or resolver, Lowest immediate implementation risk
  • Cons: Poor CLI ergonomics for nested edits, Hard to script precise updates for ADR-0027 nested fields
  • Rejected because: Does not address the primary usability gap

Option 2: Add a universal edit verb with JSONPath-like expressions (rejected)

  • Pros: Single conceptual mutation entry point, Potentially expressive for advanced operations
  • Cons: Conflicts with verb separation rationale in ADR-0017, Higher migration and discoverability cost
  • Rejected because: Overlaps existing verbs and weakens current command architecture

Option 3: Keep verbs and add path-based field addressing (accepted)

  • Pros: Compatible with existing command model, Directly solves nested edit ergonomics, Supports incremental rollout with low script breakage
  • Cons: Introduces parser and resolver complexity, Creates temporary dual-syntax cognitive load during migration

ADR-0030: Parser strategy for path-based field expressions

Status: accepted | Date: 2026-02-25

Tags: editing

References: ADR-0029, ADR-0017, ADR-0007, RFC-0002

Context

govctl recently introduced path-based field addressing in ADR-0029 for commands like get/set/add/remove. During implementation review, we identified parser correctness risks in the current hand-written parser flow (for example, extra trailing segments being ignored, and some path-shape rules enforced inconsistently by verb).

Problem Statement

We need a parser design that guarantees strict grammar handling, full-input consumption, and stable diagnostics, while preserving existing CLI compatibility behavior for legacy field forms.

Scope

This ADR focuses on parser architecture for field-path expressions only:

  • grammar parsing and AST shape
  • parser technology choice
  • legacy compatibility policy (content.*, govctl.*, aliases)
  • validation layering (parse-time vs verb/resolution-time)

It does not decide broader CLI command architecture, which remains governed by RFC-0002 and ADR-0017.

Constraints

  • Maintain resource-first and verb-separated CLI semantics from RFC-0002 and ADR-0017.
  • Keep compatibility commitments from ADR-0029 for legacy dotted prefixes and short aliases.
  • Preserve deterministic diagnostic behavior (E0814 to E0818) for automation and tests.
  • Keep parser dependency and complexity proportionate to grammar size.

Options Considered

  • Option 1: Keep manual parser and harden it.
  • Option 2: Use a parser combinator library (winnow) with a typed AST.
  • Option 3: Use a PEG/grammar generator approach (pest/similar).

Decision

We will adopt winnow-based strict parsing with a typed FieldPath AST, replacing the ad-hoc character-walk parser for path expressions.

Technical Selection

winnow is selected because it gives us:

  • explicit grammar composition in Rust types
  • full-input consumption checks by default pattern (terminated(parser, eof))
  • precise, testable parse failures without introducing a full external DSL/toolchain
  • lower operational overhead than a separate grammar generator for this small grammar

Grammar Contract

  • path := segment (("." segment) | ("[" index "]"))*
  • segment := [a-z_][a-z0-9_]*
  • index := -?[0-9]+

Parsing MUST fail if any trailing tokens remain unconsumed.

Legacy Field Compatibility Policy

Compatibility is preserved as a normalization layer after parse, before resolution:

  • canonical field names always win over aliases in the same scope
  • supported aliases remain: ac, alt, desc, pro, con, reason
  • legacy two-segment prefixes remain: content.<field>, govctl.<field>
  • prefix collapse is limited to compatibility-allowed roots and known simple fields; invalid combinations fail with diagnostics

Validation Layering

  • Parse-time validation: lexical/grammar correctness, token class, full consumption
  • Normalization-time validation: alias/prefix compatibility rules
  • Resolution-time validation: existence, index bounds, verb/path shape constraints, type mismatch

Non-Goals

  • No expression language expansion (wildcards, slices, recursive descent) in this ADR
  • No change to command verbs or lifecycle semantics

Migration Plan

  1. Introduce winnow parser behind current parse_field_path API.
  2. Keep current diagnostics mapping (E0814/E0815/E0816/E0817/E0818).
  3. Add golden tests for legacy compatibility inputs.
  4. Add negative tests for over-deep/over-specified paths and verb-shape violations.
  5. Remove old parser implementation after parity tests pass.

Consequences

Positive

  • Eliminates a class of silent-acceptance bugs via full-input consumption.
  • Makes grammar behavior explicit and easier to reason about during review.
  • Provides a stable foundation for future extensions without reintroducing ad-hoc state logic.
  • Improves confidence in diagnostics by separating parse/normalize/resolve phases.

Negative

  • Adds a third-party parsing dependency.
    • Mitigation: keep dependency surface small and isolate parser module behind a narrow API.
  • Introduces a learning curve for maintainers unfamiliar with parser combinators.
    • Mitigation: document grammar and parser module invariants with examples and tests.
  • Migration requires careful parity testing to avoid compatibility regressions.
    • Mitigation: snapshot parity suite for legacy field forms and aliases before rollout.

Neutral

  • Runtime cost is expected to be negligible for CLI-scale path strings; this should be verified by micro-benchmarks in CI but is not expected to be user-visible.

Alternatives Considered

Option 1: Keep manual parser and harden it (rejected)

  • Pros: No new dependencies, Minimal refactor scope
  • Cons: Higher long-term maintenance risk for state-machine edge cases, Harder to prove full-consumption and grammar invariants
  • Rejected because: Recent review findings indicate brittle behavior under malformed or over-specified paths

Option 2: Use winnow parser combinators with typed AST (accepted)

  • Pros: Strict grammar with full-input consumption, Rust-native and testable composition, Balanced complexity for small grammar
  • Cons: Adds dependency and parser-combinator learning curve

Option 3: Use PEG/grammar generator approach (rejected)

  • Pros: Clear formal grammar artifacts, Good for larger language evolution
  • Cons: Heavier tooling/runtime surface for current grammar size, Additional integration overhead without near-term need
  • Rejected because: Overkill for current path grammar scope

ADR-0031: Unified Artifact Edit Engine with SSOT and Format Adapters

Status: accepted | Date: 2026-02-27

Tags: editing

References: ADR-0029, ADR-0030, ADR-0001, ADR-0017, ADR-0007, RFC-0002

Context

govctl artifact editing has evolved from simple field updates into nested path edits (per ADR-0029) with parser hardening work (per ADR-0030). The current implementation now mixes parser logic, alias/legacy normalization, verb-path validation, dispatch generation, and artifact-specific read/write behavior across large files.

Problem Statement

We currently pay complexity twice:

  1. We maintain substantial command semantics in hand-written Rust control flow.
  2. We effectively treat JSON and TOML as separate operational paths in parts of the stack, even though the underlying operation is the same: read structured document, modify addressed node, validate, write.

This increases code volume, review difficulty, and risk of behavioral drift between artifacts and formats.

Before/After (architecture intent)

  • Before: parser + routing + validation + handlers are coupled, with format concerns leaking into command logic.
  • After: one semantic edit engine (parse -> canonicalize -> resolve -> plan -> validate -> execute) with thin format adapters.

Constraints

  • Preserve resource-first and verb semantics from RFC-0002 and ADR-0017.
  • Preserve index and matcher semantics from ADR-0007 (0-based indexing, negative indices from end, existing conflict diagnostics).
  • Preserve user-facing path syntax and compatibility promises from ADR-0029 and ADR-0030.
  • Respect storage decisions from ADR-0001: ADR/Work artifacts remain TOML at rest unless separately decided.
  • Keep deterministic diagnostics and stable error codes for automation scripts.

Options Considered

  • Option A: Continue incremental cleanup of current architecture.
  • Option B: Standardize internal editing on JSON only (convert TOML at boundaries, no dedicated TOML adapter contract).
  • Option C: Introduce a single SSOT-driven semantic edit engine with explicit JSON/TOML adapters.

Decision

We will adopt Option C: a single SSOT-driven semantic edit engine with explicit format adapters.

Core Architecture

  1. Single semantic pipeline for all artifacts and formats:
    • parse -> canonicalize -> resolve -> plan -> validate -> execute
  2. Single SSOT model (edit-model.json + JSON Schema) defines:
    • field tree, indexability, verb capability matrix
    • alias/legacy mapping and conflict policy
    • validator bindings and handler IDs
  3. Single execution engine consumes typed EditPlan operations.
  4. Thin format adapters implement storage-specific read/write behavior:
    • JsonAdapter
    • TomlAdapter

Parser Selection

Per ADR-0030, parser implementation will use winnow (not a grammar generator), while grammar remains documented in PEG/EBNF style.

Field-token acceptance is strict:

  • parser accepts syntactic segments
  • resolver accepts only SSOT-known canonical fields/aliases
  • unknown fields fail with deterministic diagnostics

Compatibility and Convergence Policy

  • Existing path syntax and legacy forms from ADR-0029 remain supported during migration.
  • Canonical names take precedence over aliases on conflict.
  • Documentation will prefer canonical path syntax; legacy forms are compatibility-only.
  • Legacy form deprecation warnings begin after full V2 parity is reached.

Migration Plan

  1. Phase 1 (foundation): Introduce V2 SSOT, parser, typed EditPlan, and adapter interfaces behind feature-gated path.
  2. Phase 2 (shadow mode): Run V1 and V2 for ADR/Work operations; compare planned ops and rendered outcomes in tests.
  3. Phase 3 (cutover): Switch ADR/Work to V2, then RFC/Clause.
  4. Phase 4 (cleanup): Remove V1 dispatch branches, remove hand-written artifact/field/verb matches, tighten lint/test gates.

Completion Criteria

V2 is considered complete only when:

  • adding a new editable field requires SSOT changes only (no manual dispatch branching)
  • JSON and TOML share one semantic engine
  • compatibility test suite for legacy paths passes in V2 mode

Consequences

Positive

  • Reduces long-term maintenance cost by centralizing edit semantics in one engine.
  • Eliminates JSON/TOML behavioral drift risk by sharing the same plan/validation pipeline.
  • Makes parser and resolver behavior auditable through SSOT and generated tables.
  • Improves extensibility: new fields and verbs are primarily SSOT additions.

Negative

  • Migration complexity is significant because V1 and V2 must coexist temporarily.
    • Mitigation: enforce a bounded coexistence window (max two releases) with explicit phase exit criteria and deletion checklist.
  • Generator/SSOT errors can affect many paths at once.
    • Mitigation: schema validation at build-time, generated-table snapshot tests, and golden command fixtures per artifact/verb.
  • Temporary dual operation styles (legacy vs canonical path forms) can confuse users.
    • Mitigation: docs prefer canonical syntax immediately; legacy usage prints guidance warnings after V2 parity milestone.
  • Documentation and contributor onboarding work increases initially.
    • Mitigation: track docs updates as required migration work items and block V2 cutover completion until docs/tests are updated.

Neutral

  • Runtime overhead should remain negligible for CLI-scale paths, but this will be measured with parser and end-to-end micro-benchmarks before finalizing ADR status.

Alternatives Considered

Option A: Continue incremental cleanup on current architecture (rejected)

  • Pros: Lowest immediate implementation risk, No major migration event required
  • Cons: Continues structural duplication and code growth, Does not solve JSON/TOML semantic divergence risk
  • Rejected because: It optimizes short-term churn but leaves the root architecture problem unsolved.

Option B: Standardize internal editing on JSON only (rejected)

  • Pros: Maximizes reuse of mature JSON tooling and standards, Simplifies internal document mutation mechanics
  • Cons: Introduces conversion boundary risk for TOML-only semantics and formatting expectations, Weakens direct TOML-at-rest operational clarity established by ADR-0001
  • Rejected because: It shifts complexity to conversion boundaries and does not provide a first-class TOML contract.

Option C: SSOT-driven semantic engine with explicit JSON/TOML adapters (accepted)

  • Pros: Preserves storage-format independence while unifying semantic behavior, Enables SSOT-driven extensibility with lower long-term maintenance cost
  • Cons: Requires staged migration and temporary dual-path operation, Demands high-quality generator and parity tests to avoid systemic regressions

ADR-0032: Migration skill for adopting govctl in existing projects

Status: accepted | Date: 2026-03-02

Tags: migration

References: ADR-0023, ADR-0024, ADR-0028

Context

govctl currently assumes greenfield projects — govctl init creates the governance directory structure from scratch, and all workflows (skills, agents) assume artifacts exist from day zero.

Problem Statement

The majority of real-world projects that would benefit from govctl are existing codebases that lack formal governance. These projects have:

  • Undocumented architectural decisions embedded in code, comments, and tribal knowledge
  • Existing specifications scattered across markdown docs, wikis, or issue trackers
  • In-progress work tracked informally (GitHub Issues, Jira, sticky notes)
  • No artifact cross-references in source code

Adopting govctl today requires teams to either:

  1. Start fresh (losing existing context), or
  2. Manually create dozens of ADRs, RFCs, and work items — a tedious process that discourages adoption

Requirements

A migration skill should:

  • Guide an AI agent through systematically discovering and codifying existing decisions
  • Use only existing govctl CLI commands (no new CLI capabilities needed)
  • Support incremental migration (not all-or-nothing)
  • Produce well-structured artifacts that pass govctl check
  • Add [[...]] references to existing source code where decisions are implemented

Decision

We will create a migrate skill (.claude/skills/migrate/SKILL.md) that guides the agent through a multi-phase migration workflow using existing govctl commands only.

Migration Phases

Phase 0: Scaffold — Initialize govctl in the existing project.

  • Run govctl init (safe alongside existing files)
  • Read project structure (README, docs, config files) to understand the codebase

Phase 1: Discover — Systematically scan the project for implicit governance artifacts.

  • Decisions: Read architecture docs, README sections, config comments, and code patterns to identify undocumented architectural decisions
  • Specifications: Find existing specs, API contracts, or design docs that could become RFCs
  • Work in progress: Check issue trackers, TODO comments, and branch names for active work

Phase 2: Backfill ADRs — Create ADRs for discovered decisions.

  • For each significant decision found, create an ADR using govctl adr new
  • Populate context (what prompted the decision), decision (what was chosen), consequences
  • Add alternatives where the rejected options are known
  • Accept the ADR immediately (govctl adr accept) since these are historical records

Phase 3: Backfill RFCs (optional) — Create RFCs for existing specifications.

  • Only for projects that have existing specification documents
  • Create RFC + clauses from existing spec content
  • Finalize as normative and advance to stable (these specs are already implemented)

Phase 4: Annotate source — Add [[...]] references to existing code.

  • Scan source files for implementations of newly-created ADRs/RFCs
  • Insert // Implements [[ADR-NNNN]] or // Per [[RFC-NNNN:C-NAME]] comments
  • Run govctl check to verify references resolve

Phase 5: Establish baseline — Create work items for any in-progress work.

  • Create work items for known active tasks
  • Going forward, all new work uses the /gov workflow

Skill Properties

  • Interactive: The skill prompts the user at each phase to confirm discoveries and prioritize what to backfill
  • Incremental: Each phase can be run independently; partial migration is valid
  • Non-destructive: Never overwrites existing files; only adds governance artifacts alongside existing content
  • Idempotent: Running the skill again skips already-created artifacts

Consequences

Positive

  • Lowers adoption barrier — Existing projects can adopt govctl without starting from scratch.
  • Preserves institutional knowledge — Undocumented decisions get codified as searchable, cross-referenced ADRs.
  • Gradual onboarding — Teams can migrate incrementally, one module or decision at a time.
  • Agent-native — The skill leverages Claude Code’s ability to read codebases and synthesize decisions, making backfill practical.

Negative

  • Quality depends on agent understanding — Auto-discovered decisions may be incomplete or inaccurately described.
    • Mitigation: Interactive confirmation at each step; user reviews all generated artifacts.
  • Source annotation churn — Adding [[...]] references to existing code creates a large diff.
    • Mitigation: Phase 4 is optional and can be done incrementally per-module.
  • Historical ADRs may lack alternatives — Old decisions often don’t have documented rejected options.
    • Mitigation: The skill accepts “considered” as the only alternative when history is unclear.

Neutral

  • No new govctl CLI commands are needed — the skill composes existing commands.
  • The skill is bundled with govctl and installed via govctl sync, like other skills.

Alternatives Considered

Agent-assisted skill using existing govctl commands (accepted)

  • Pros: Zero new CLI code needed, Ships immediately as a bundled skill
  • Cons: Requires an AI agent to execute the workflow

New govctl migrate CLI command with auto-detection (rejected)

  • Pros: Works without AI agent, Deterministic output
  • Cons: Significant new CLI code to write and maintain, Auto-detection heuristics are brittle across project types
  • Rejected because: The discovery and synthesis tasks are inherently judgment-heavy — an AI agent handles ambiguity better than heuristic code. CLI commands can be added later if common patterns emerge.

ADR-0033: Distribute govctl agent integration as Claude Code plugin

Status: accepted | Date: 2026-03-04

Tags: plugin

References: ADR-0015, ADR-0024, ADR-0028

Context

govctl has a mature agent integration layer: 8 skills (gov, quick, discuss, commit, migrate, rfc-writer, adr-writer, wi-writer) and 4 agents (compliance-checker, adr-reviewer, wi-reviewer, rfc-reviewer) in .claude/. This works well for developing govctl itself, but creates a distribution problem for adopters.

Problem Statement

Every project that adopts govctl must manually recreate the agent configuration: copy skills, agents, and CLAUDE.md into their .claude/ directory. govctl init scaffolds a basic CLAUDE.md, but the full skill/agent suite requires manual setup and ongoing maintenance when govctl updates its workflows.

Claude Code’s plugin system (introduced in v1.0.33, documented at https://docs.claude.com/en/plugins) solves exactly this: a standardized package format with skills, agents, hooks, and MCP servers that can be installed once and used across all projects.

Constraints

  • ADR-0015 decided against MCP in favor of CLI-based agent discoverability — maintaining two interfaces (CLI + MCP) is a maintenance burden
  • ADR-0024 established writers as skills and reviewers as agents — this architecture maps directly to the plugin format
  • ADR-0028 migrated commands to skills for cross-platform compatibility — the unified skill format is already plugin-compatible
  • The plugin must work with any shell-capable agent, not just Claude Code (graceful degradation)
  • Plugin skills are namespaced (e.g., /govctl:gov instead of /gov)

Decision

We will package govctl’s agent integration as a Claude Code plugin with enforcement hooks, following the oh-my-claudecode pattern. The .claude/ directory IS the plugin content. The marketplace catalog at the repo root points to it.

Structure

govctl/
├── .claude-plugin/
│   └── marketplace.json      ← source: "./.claude"
├── .claude/                   ← plugin content (auto-discovered)
│   ├── skills/                ← already exists
│   ├── agents/                ← already exists
│   ├── hooks/
│   │   └── hooks.json
│   ├── scripts/
│   │   ├── post-edit-check.sh
│   │   ├── session-start.sh
│   │   └── pre-stop-check.sh
│   └── settings.local.json   ← gitignored, not copied
├── src/
├── gov/
└── Cargo.toml

No plugin.json inside .claude/ — the marketplace entry provides all metadata, and Claude Code auto-discovers skills/, agents/, hooks/ from the plugin directory.

Hook Enforcement

Three hooks provide automatic governance enforcement:

  1. PostToolUse (matcher: Write|Edit): Inspects the edited file path. If under gov/, runs govctl check and surfaces validation errors.

  2. SessionStart: Runs govctl status for context. Checks govctl binary availability.

  3. Stop: Runs govctl check. Warns about pending failures before session ends.

Distribution

cargo install govctl
/plugin marketplace add govctl-org/govctl
/plugin install govctl@govctl
/govctl:gov "implement feature X"
  • Zero duplication: .claude/ is the SSOT for skills, agents, and plugin content
  • One .claude-plugin/: marketplace.json at repo root, no nested manifest
  • Versioning: just stamp-plugin-version updates marketplace.json from Cargo.toml

Consequences

Positive

  • Zero duplication: .claude/ is both the local dev config and the plugin content
  • One-step installation for adopters: /plugin install govctl@govctl
  • Automatic governance enforcement via hooks
  • No build step, no sync task, no symlinks — just files in .claude/
  • No MCP server complexity — single codebase, single distribution, per ADR-0015
  • Plugin format is the Claude Code standard — compatible with marketplace distribution

Negative

  • Plugin skills are namespaced (/govctl:gov vs /gov) when installed as plugin (mitigation: only affects plugin users, not in-repo developers)
  • Hook scripts are shell-only — Windows users need WSL or Git Bash (mitigation: hooks are additive enforcement, not required functionality)
  • Binary dependency: plugin requires govctl installed separately (mitigation: SessionStart hook checks and provides install instructions)
  • Plugin format is Claude Code-specific — non-Claude-Code agents cannot use hooks or plugin metadata (mitigation: skills and agents are plain markdown, usable by any agent)
  • .claude/ directory gains additional files (hooks/, scripts/, .claude-plugin/) that are plugin-specific (mitigation: these are small, clearly organized, and don’t interfere with local dev)

Neutral

  • govctl init still works for non-Claude-Code users
  • Future MCP integration remains possible per ADR-0015 — the plugin format supports .mcp.json
  • Developers working on govctl see .claude/ as both their local config and the plugin source — this is a feature, not a bug

Alternatives Considered

Option A — Thin Plugin (skills + agents only, no hooks): Simplest approach, zero new code, but no automatic enforcement. Agent must remember to run govctl check manually. (rejected)

  • Pros: Zero new code, No shell scripts to maintain
  • Cons: No automatic governance enforcement, Agent must remember validation
  • Rejected because: Hooks are the high-leverage differentiator between ‘works with Claude Code’ and ‘great Claude Code plugin’. Without them, the plugin is just a file distribution mechanism.

Option C — Plugin + MCP Server: Full structured integration wrapping govctl CLI as MCP tools. Richest agent experience but doubles the API surface. (rejected)

  • Pros: Structured tool interface with typed parameters, Richer error handling
  • Cons: Two interfaces to maintain (CLI + MCP), MCP server is a separate process to manage, Significant new code (~1000+ lines)
  • Rejected because: Per ADR-0015, maintaining two interfaces is a maintenance nightmare for marginal parsing convenience. The CLI works. Shell-capable agents invoke it fine. The door remains open if demand materializes.

Option D — Separate repository (govctl-plugin): Independent repo with its own release cycle. (rejected)

  • Pros: Independent versioning, Smaller repo for plugin contributors
  • Cons: Sync between repos is error-prone, Skills and agents drift from main codebase
  • Rejected because: The skills and agents ARE the govctl agent layer. Splitting them into a separate repo creates a sync problem that doesn’t need to exist.
  • Cons: File duplication or symlink fragility, Requires build/sync step, Extra directory to maintain
  • Rejected because: The .claude/ directory already has the exact structure a Claude Code plugin expects (skills/, agents/). Adding a second directory is solving a problem that doesn’t exist.

ADR-0034: Use TOML as the canonical storage format for all governance artifacts

Status: accepted | Date: 2026-03-16

Tags: schema

References: ADR-0001, ADR-0014, ADR-0031, ADR-0032, RFC-0000, RFC-0002

Context

govctl currently stores ADRs, work items, and releases as TOML, while RFCs and RFC clauses remain JSON. In this ADR, “governance artifacts” means RFCs, clauses, ADRs, work items, and releases. Project config is out of scope. That split is no longer buying us anything useful.

Problem Statement

The format split leaks into loaders, writers, adapters, schema docs, help text, and migration behavior. ADR-0031 already had to introduce explicit JSON and TOML adapters just to keep one semantic edit engine honest. Keeping RFCs and clauses on JSON forever would preserve that special case in the exact place we are trying to simplify.

We also lack real schema validation for generated TOML artifacts. Today TOML artifacts are parsed structurally and then checked semantically, but they are not validated against machine-readable artifact schemas. That means malformed or drifted TOML can survive longer than it should. Before this change, release data was the worst special case: it lived in gov/releases.toml without a first-class artifact definition in RFC-0000 or an explicitly required JSON Schema contract.

Finally, existing repositories already contain JSON RFC and clause files. If we switch formats without an explicit migration boundary, we either keep dual-format support indefinitely or break existing repositories silently. Both are bad designs.

Constraints

  • Preserve the normative meaning of existing governance artifacts and references under RFC-0000 and RFC-0002.
  • Keep normal operation simple: after migration, the main load/edit/render paths should be TOML-only.
  • Respect ADR-0032 by keeping this migration command narrow and deterministic. This is not a heuristic project-adoption workflow.
  • Preserve auditability and deterministic failures for automation and agent workflows.

Options Considered

  • Keep the mixed JSON/TOML storage model and improve adapters further.
  • Standardize all governance artifacts on JSON instead.
  • Standardize all governance artifacts on TOML with an explicit repository migration step.

Decision

We will use TOML as the canonical on-disk source-of-truth format for governance artifacts because it removes a persistent storage-format special case, matches the existing direction of ADR/work/release storage, and gives humans a format they can read and edit without carrying JSON-only baggage forward.

  1. One storage format: RFCs, clauses, ADRs, work items, and releases will all be stored as TOML at rest.
  2. One normal code path: After explicit repository migration, steady-state govctl operations will treat TOML as the only supported artifact storage format.
  3. One compatibility boundary: Legacy JSON support and deterministic TOML shape upgrades will exist only inside a migration command that converts existing JSON RFC and clause files to TOML and applies govctl-managed structural upgrades such as release-file metadata normalization.
  4. One validation model: RFC, clause, ADR, work item, and release artifacts will each have a corresponding machine-readable JSON Schema, and generated or edited TOML will be validated against that schema after parsing and normalization.

Implementation Notes

  • RFC and clause definitions should converge on the same [govctl] metadata pattern already used by ADRs and work items, including a required schema field.
  • gov/releases.toml should stop being a schema-less special case. It should carry explicit file-level metadata, including a required schema field, alongside the [[releases]] collection.
  • The schema contract should be explicit rather than implied: one JSON Schema file per artifact type, stored under gov/schema/, with the TOML govctl.schema field selecting the corresponding version.
  • The migration command should be narrow by design: convert legacy JSON governance files already recognized by govctl. It must not become a general project-discovery tool or overlap with the broader migration skill from ADR-0032.
  • Post-migration normal operation should not keep a permanent dual-read compatibility layer. Legacy JSON handling belongs in the migration path, not in every loader and writer.
  • Before migration, normal commands should fail with an explicit migration-required diagnostic rather than guessing or partially operating on mixed-format repositories.
  • The migration command should rewrite only govctl-managed storage references. Arbitrary repo docs, tests, and scripts remain user-owned follow-up changes.
  • If accepted, this decision supersedes the storage split recorded in ADR-0001.

Consequences

Positive

  • Removes the last major on-disk format split in governance artifacts.
  • Makes storage expectations easier to explain: governance artifacts are TOML, rendered docs are projections.
  • Creates a clean place to contain legacy JSON support instead of spreading it across the whole codebase.
  • Enables real schema validation for all TOML governance artifacts, including releases, using one existing validation technology (jsonschema) after TOML parsing.

Negative

  • Existing repositories with JSON RFCs and clauses will require an explicit migration step before normal TOML-only operation. (mitigation: normal commands should emit a dedicated govctl migrate diagnostic instead of attempting mixed-format behavior.)
  • RFC and clause path conventions will change from .json to .toml, so docs, tests, and direct file-path tooling must be updated carefully. (mitigation: govctl migrate rewrites govctl-managed clause-path references, but user-maintained references remain explicit follow-up work.)
  • gov/releases.toml will need a small structural migration to add explicit metadata for schema validation. (mitigation: keep the release-entry payload shape stable and migrate the file automatically with deterministic rewrite rules.)
  • External scripts, CI glue, and older govctl versions that assume .json RFC paths will break after migration. (mitigation: this is an intentional compatibility drop that must be documented plainly in release notes and migration docs.)
  • A broad format migration touches foundational governance definitions, so review and rollout discipline matter. (mitigation: require draft review plus full govctl check validation before rollout.)

Neutral

  • This does not replace the broader project-adoption migration workflow in ADR-0032; it only adds a deterministic repository-format migration boundary.
  • The semantic meaning of RFCs, clauses, ADRs, and work items does not change. Only their canonical storage format and validation contract change.

Alternatives Considered

Keep mixed JSON/TOML storage with format adapters (rejected)

  • Pros: Lowest immediate disruption to existing RFC storage, Avoids a repo-wide format migration in the short term
  • Cons: Preserves storage-format branching in loaders, writers, docs, and tests, Keeps JSON/TOML drift as a permanent maintenance cost
  • Rejected because: This keeps the special case alive instead of deleting it.

Standardize all governance artifacts on JSON (rejected)

  • Pros: Would reuse the existing RFC and clause storage model, Could lean on mature JSON tooling and schemas
  • Cons: Moves ADRs, work items, and releases away from the format already chosen for human editing, Fights the existing TOML direction established across most governance artifacts
  • Rejected because: It optimizes around legacy RFC storage instead of the current human-facing workflow.

Standardize all governance artifacts on TOML with explicit migration (accepted)

  • Pros: Eliminates the on-disk format split in normal operation, Keeps compatibility code confined to a single migration boundary
  • Cons: Requires a carefully specified migration command, Creates one-time churn in paths, docs, and tests

ADR-0035: Decouple skill and agent installation from project initialization

Status: accepted | Date: 2026-03-17

Tags: skills-agents

References: RFC-0002, ADR-0033, ADR-0028

Context

govctl init currently bundles three concerns into one command: governance directory scaffolding (gov/), JSON Schema deployment, and agent asset installation (skills + agents to .claude/). ADR-0033 introduced plugin distribution, giving users a second path to receive skills and agents — globally, via the Claude Code plugin system.

Problem Statement

  1. Redundant local copies for plugin users. Users who install govctl as a Claude Code plugin receive skills and agents globally. govctl init also writes them locally, creating two copies with unclear authority.
  2. Missing schemas for old projects. Projects initialized with earlier govctl versions lack gov/schema/*.json files. The #:schema relative-path comments in TOML artifacts resolve to nonexistent files, breaking IDE validation. Neither govctl sync nor govctl migrate fills this gap.
  3. Unclear sync naming. govctl sync only syncs agent assets (skills + agents), but the name implies general synchronization.

Constraints

  • RFC-0002:C-GLOBAL-COMMANDS specifies init with “optionally creates .claude/commands/” — the skill dump was always optional.
  • RFC-0002:C-GLOBAL-COMMANDS requires new global commands to be added via RFC amendment and meet at least one criterion (multi-resource, project-level init/cleanup, or meta-information).
  • govctl sync is not specified in RFC-0002 — it exists only as an implementation convenience.

Decision

We will separate the three concerns as follows:

  1. govctl init creates governance structure (gov/ directories, config.toml, JSON Schemas). It no longer installs skills or agents. After completion, it prints a hint about govctl init-skills and plugin installation.

  2. govctl init-skills (replaces govctl sync) explicitly installs skills and agents into the configured agent_dir. This is the opt-in command for users who do not use the plugin. Supports -f to overwrite existing files.

  3. govctl migrate ensures all bundled JSON Schema files exist in gov/schema/, always overwriting with the latest version. This fills the gap for projects initialized with older govctl versions.

Implementation Notes

  • init removes the skill/agent writing loop and the hardcoded .claude path.
  • init-skills reuses the existing sync_commands() implementation unchanged.
  • migrate adds a schema-sync step that runs unconditionally (not gated by schema version), writing all ARTIFACT_SCHEMA_TEMPLATES entries to config.schema_dir().
  • RFC-0002:C-GLOBAL-COMMANDS is amended to add init-skills and update the init and migrate descriptions.

Consequences

Positive

  • Plugin users no longer get redundant local skill/agent copies from init
  • Old projects get working #:schema comments after running govctl migrate
  • Command names are self-documenting: init = governance, init-skills = agent assets
  • The hardcoded .claude path in init is eliminated; init-skills uses the configured agent_dir

Negative

  • Users who had govctl init in onboarding docs will find .claude/ empty after upgrading (mitigation: init prints a hint, and the changelog documents the change)
  • Two commands instead of one for full setup (mitigation: plugin users need zero commands for agent assets; CLI-only users run init then init-skills)

Neutral

  • govctl sync is removed as a command name; init-skills replaces it
  • Schema files are now overwritten on every migrate run, even if unchanged — this is safe since they are generated artifacts

Alternatives Considered

Keep init bundled: init continues to dump skills/agents alongside governance structure (rejected)

  • Cons: Redundant for plugin users, Hardcoded .claude path ignores agent_dir config
  • Rejected because: Plugin distribution per ADR-0033 makes unconditional local dumping obsolete

Add –skills flag to init instead of separate command: govctl init –skills dumps agent assets (rejected)

  • Cons: Discovery problem: users must know the flag exists, Couples governance init with agent concerns
  • Rejected because: Separate command is more discoverable and aligns with single-responsibility

ADR-0036: Restructure ADR chosen-option and migration semantics

Status: superseded | Date: 2026-04-06 Superseded by: ADR-0038

Tags: editing

References: ADR-0034, ADR-0031, ADR-0027, ADR-0032

Context

The current ADR model stores the chosen option as an accepted entry inside content.alternatives[]. That shape creates two related problems:

  1. It duplicates the decision itself. The chosen path is already described in decision, but tooling also expects an accepted alternative.
  2. It leaks checklist-style status semantics into decision options. govctl adr tick accepts done|pending|cancelled, which are internally mapped to accepted|considered|rejected for alternatives. This is implementation-centric rather than domain-centric and confuses both users and bundled skill examples.
  3. It makes migration and rendering harder. Historical ADRs may have incomplete alternative metadata, yet the current model has no explicit place to record migration gaps while keeping the artifact renderable.

We need a cleaner ADR model that:

  • makes the chosen option explicit without duplicating it as an alternative,
  • keeps alternatives focused on non-selected options,
  • structures consequences so mitigations attach to negative outcomes,
  • allows migration to produce schema-valid, renderable ADRs even when some historical intent cannot be fully recovered.

Decision

We will redesign ADR storage around four principles:

  1. Chosen option is first-class. ADR content gains an explicit selected_option field. The chosen path is no longer represented as an accepted alternative.
  2. Alternatives only model non-selected options. content.alternatives[] remains for options that were not chosen. Each alternative may keep pros, cons, and rejection_reason, but no longer carries a status field.
  3. Consequences become structured. content.consequences becomes a structured object with positive, neutral, and negative entries. Negative consequences may include mitigations.
  4. Migration state is explicit metadata. govctl.migration records whether an ADR needs post-migration review and carries warning entries describing unresolved historical gaps.

Migration semantics

  • Legacy accepted alternatives migrate into selected_option and are removed from alternatives.
  • Legacy rejected alternatives remain alternatives.
  • Legacy considered alternatives in accepted or superseded ADRs are migrated into alternatives with synthesized rejection rationale and a migration warning.
  • If migration cannot determine the chosen option, the migrated ADR remains renderable and schema-valid, but govctl.migration.state = "needs_review" and govctl check emits warnings until a human resolves it.

CLI semantics

govctl adr tick ... alternatives ... no longer participates in ADR option state. ADR editing uses direct field paths such as selected_option and alternatives[0].rejection_reason. tick remains checklist-oriented and continues to apply to work item acceptance criteria.

Recovered Selected-Option Advantages

  • Eliminates accepted-alternative duplication
  • Keeps unresolved migrations renderable

Consequences

Positive

  • The chosen option becomes explicit and no longer needs to be duplicated as an accepted alternative.
  • ADR CLI and skills can use domain language directly instead of checklist-style status remapping.
  • Migration gains a principled place to record unresolved historical gaps without breaking rendering.
  • Negative outcomes and mitigations become attachable data rather than prose hidden inside a markdown block.

Negative

  • This is a breaking schema change for ADR files, renderer output, and edit semantics.
  • Existing ADRs require a versioned migration and some will still need manual follow-up.
  • Bundled skills, reviewer guidance, and examples all need coordinated updates.
  • Requires a schema migration.

Neutral

  • decision remains a prose field; the redesign adds structure around it rather than replacing it with a fully object-shaped decision document.
  • The migration pipeline becomes responsible for one more schema step.

Alternatives Considered

Keep alternative status as considered/accepted/rejected (rejected)

  • Pros: Minimal data-model churn
  • Cons: Preserves confusing tick-to-status mapping, Continues duplicating the chosen option inside alternatives
  • Rejected because: Keeps the same semantic collision between checklist state and decision state.

Use a fully object-shaped decision document instead of keeping decision prose (rejected)

  • Pros: Captures rationale in more machine-readable form
  • Cons: Much larger writer, renderer, and documentation rewrite, Makes the redesign harder to adopt in one migration step
  • Rejected because: The immediate problem is chosen-option and migration semantics, not replacing ADR narrative writing with a new decision DSL.

Explicit selected_option field, structured consequences, and migration metadata (accepted)

  • Pros: Makes the chosen option explicit rather than implicit in prose and option status., Provides a dedicated place for migration-only review state.
  • Cons: Introduces a breaking schema change for ADRs., Requires coordinated migration and ecosystem updates.

ADR-0037: Canonical edit surface for nested artifact mutation

Status: accepted | Date: 2026-04-06

Tags: editing

References: ADR-0031, ADR-0029, ADR-0017, ADR-0030, ADR-0007, RFC-0002

Context

govctl currently exposes artifact mutation through resource-first verbs such as set, add, remove, and tick, with path-based field addressing layered in via ADR-0029 and strict parsing via ADR-0030. The semantic engine behind those commands is being unified per ADR-0031.

Problem Statement

The current CLI surface is still too shape-dependent for reliable agent use:

  1. Whether an operation uses set, add, remove, or tick depends on the target field’s storage shape rather than the user’s intent alone.
  2. Nested editing support is asymmetric. Some paths behave like true field paths, while others still rely on special-case command semantics.
  3. The CLI surface leaks artifact-specific implementation details, which increases agent failure rates and makes help text harder to generalize.
  4. Upcoming schema work, including richer ADR structures, will increase nested object/array combinations and amplify the problem if the edit surface remains verb-fragmented.

Constraints

  • Preserve resource-first command organization from RFC-0002.
  • Preserve the SSOT-driven engine direction from ADR-0031.
  • Keep a stable migration path for existing human users and scripts.
  • Make the canonical mutation interface regular enough that agents can synthesize commands from field paths without artifact-specific guessing.
  • Avoid introducing a new batch-specific input language unless single-operation ergonomics prove insufficient.

Options Considered

  • Option A: Keep set/add/remove/tick as the only mutation interface and continue expanding path support.
  • Option B: Introduce a canonical edit command with path-first operation flags, and keep existing verbs as compatibility sugar.
  • Option C: Replace existing verbs completely with a JSONPatch-like or DSL-heavy mutation language.

Decision

We will introduce a canonical path-oriented edit surface for governed artifact mutation:

govctl <resource> edit <ID> <path> --set <value>
govctl <resource> edit <ID> <path> --add <value>
govctl <resource> edit <ID> <path> --remove <pattern>
govctl <resource> edit <ID> <path> --tick <status>

where:

  • <path> is a canonical fully qualified field path
  • exactly one mutation flag is provided per invocation
  • existing resource-first verbs (set, add, remove, tick) remain available as human-friendly sugar that compile into the same semantic edit plan

Canonical Interface Rules

  1. Canonical path syntax is fully regular. It MUST support nested object and array traversal for arbitrary depth, subject to SSOT validation.
  2. Canonical paths prefer explicit field names. Aliases remain compatibility-only, but documentation and agent examples prefer full canonical paths.
  3. edit is the authoritative mutation interface. New nested-field capabilities are specified first against edit; shorthand verbs are layered on top.
  4. tick remains checklist-oriented. It stays available only where the schema marks a status-bearing checklist item. It is not the canonical mechanism for arbitrary state mutation.
  5. Path semantics are SSOT-defined. The engine resolves paths and operation legality from generated schema/rules, not from artifact-specific handwritten branching.
  6. This ADR standardizes single-operation editing only. Multi-step orchestration remains the responsibility of the calling agent or shell layer for now.

Compatibility Strategy

  • Existing commands such as govctl adr set ..., govctl adr add ..., and govctl work tick ... remain supported during migration.
  • Help text, docs, and agent-facing examples will progressively move to the canonical edit form.
  • Compatibility verbs are treated as sugar over the same EditPlan, not as separate semantic implementations.
  • No new YAML/JSON patch language is introduced in this phase.

Examples

govctl adr edit ADR-0001 content.decision --set "We will ..."
govctl adr edit ADR-0001 content.alternatives --add "Option A"
govctl adr edit ADR-0001 content.alternatives[0].pros --add "Readable"
govctl work edit WI-2026-04-06-001 content.acceptance_criteria[0] --tick done

This keeps resource-first organization intact while giving both humans and agents a single canonical grammar for mutation.

Recovered Selected-Option Advantages

  • Gives humans and agents one stable canonical mutation grammar
  • Lets existing verbs converge onto the same EditPlan without immediate breakage
  • Keeps the target path visually primary when reading or typing commands

Consequences

Positive

  • Agents can synthesize mutation commands from one regular shape instead of guessing between multiple top-level verbs.
  • The CLI surface aligns with the SSOT edit engine in ADR-0031, reducing semantic drift.
  • Nested object/array edits can be documented once and reused across ADRs, RFCs, work items, and future artifact types.
  • Existing human-friendly verbs can remain as convenience entrypoints without blocking engine regularization.
  • The design does not add a separate patch document format, so the CLI remains focused on a single mutation grammar.

Negative

  • The CLI surface grows: users must understand that edit is canonical even if shorthand verbs still exist.
  • Help text, docs, examples, and tests require a coordinated rewrite.
  • Compatibility layering adds temporary maintenance cost until sugar commands fully delegate to the canonical path.
  • Multi-step batch edits are not made atomic by this ADR; callers still need orchestration logic when applying a series of mutations.
  • Requires dual-surface docs during migration.

Neutral

  • Resource-first organization is unchanged; only the mutation entrypoint is normalized.
  • This ADR does not itself redesign artifact schemas such as ADR consequences or alternatives. It defines the edit surface that future schema work can rely on.
  • If transactional batch mutation becomes necessary later, it can be addressed in a separate ADR with clearer evidence of need.

Alternatives Considered

Keep set/add/remove/tick as the only mutation interface and continue expanding path support (rejected)

  • Pros: Lowest immediate CLI churn, Keeps current verb-first UX intact
  • Cons: Agents still need field-shape-specific verb selection, Does not establish a single canonical mutation grammar
  • Rejected because: It fixes capability gaps incrementally but leaves the core ergonomics problem unresolved for automation.

Replace current verbs with a JSONPatch-like or DSL-heavy universal mutation language (rejected)

  • Pros: Maximum expressiveness in one command family
  • Cons: Higher cognitive load for humans, Too large a break from current resource-first mutation ergonomics
  • Rejected because: Over-corrects toward a mini language and sacrifices discoverability for power we do not currently need.

Add a canonical edit command with path-first operation flags, while preserving current verbs as sugar (accepted)

  • Pros: Establishes one canonical mutation grammar without breaking existing workflows., Keeps nested mutation semantics centered on field paths instead of ad hoc verb choice.
  • Cons: Expands the CLI surface during the transition period., Requires documentation and help text to teach the canonical form clearly.

ADR-0038: Keep ADR schema discussion-oriented and avoid broad migration

Status: accepted | Date: 2026-04-06

Tags: schema

References: ADR-0027, ADR-0036, ADR-0037

Context

We have two goals that now need to be balanced more carefully.

Problem Statement

  1. The canonical edit surface from ADR-0037 is valuable and should remain.
  2. The broader ADR schema redesign from ADR-0036 proved too heavy for the value it provides.
  3. In practice, ADR authoring works best when alternatives are written first, discussed, and then one option is marked as selected before the final decision prose is written.
  4. The selected_option plus structured-consequences redesign pushed the model toward a final-state representation and away from the natural discussion flow.
  5. The migration burden is not justified while this work is still on a PR branch and has not landed on the main branch.

Constraints

  • Preserve the canonical edit-surface work already captured in ADR-0037.
  • Preserve the discussion-oriented alternative model from ADR-0027.
  • Avoid a repository-wide ADR migration for a change that is not yet merged.
  • Keep the ADR schema simple enough that humans and agents can both use it reliably.

Options Considered

We considered keeping the full ADR-0036 redesign, keeping the current ADR schema and workflow, and adopting a smaller additive field for the selected option only.

Decision

We will keep the current ADR schema discussion-oriented and avoid the broad schema/migration redesign from ADR-0036 because:

  1. Authoring flow matters more than final-shape normalization. ADRs are written by exploring alternatives first and only then recording the final decision. The current alternatives-with-status model supports that flow directly.
  2. The migration cost is disproportionate. A schema and repository-wide migration is not justified for a redesign that has not landed on the main branch.
  3. The canonical edit surface already solves the more valuable problem. ADR-0037 gives us the regular mutation interface we wanted without requiring the ADR artifact itself to become deeply restructured.
  4. Future ADR refinement can still happen incrementally. If we later need a machine-readable chosen-option field or richer consequence structure, that should be justified by a narrower problem and designed without coupling it to a broad migration.

Implementation Notes

  • Keep content.consequences as prose markdown.
  • Keep content.alternatives[] with status = considered|rejected|accepted.
  • Continue to model the selected option by marking one alternative as accepted and then writing the final decision prose.
  • Do not introduce selected_option, structured consequences, or migration-specific ADR metadata in this line of work.

Consequences

Positive

  • Preserves the natural ADR writing flow: alternatives first, decision last.
  • Avoids a repository-wide ADR migration for a redesign that has not merged.
  • Keeps canonical edit-surface gains from ADR-0037 without tying them to a broader artifact rewrite.
  • Keeps ADR authoring understandable for humans and agents using today’s schema.

Negative

  • The chosen option remains represented partly by alternative status and partly by decision prose.
  • ADR tooling will have less machine-readable structure than ADR-0036 proposed.
  • Some future refinement pressure is deferred rather than eliminated. (mitigation: revisit only when a narrower, clearly justified problem emerges.)

Neutral

  • This decision supersedes the schema-and-migration redesign from ADR-0036 but does not change the canonical edit-surface direction from ADR-0037.
  • Existing ADR files remain valid without conversion.

Alternatives Considered

Keep ADR-0036 full redesign with selected_option, structured consequences, and migration metadata (rejected)

  • Pros: Makes the chosen option explicit, Provides more machine-readable structure
  • Cons: Encourages premature final-state authoring, Requires schema and repository migration
  • Rejected because: The migration and authoring costs are too high for a change that has not landed on the main branch.

Keep the current ADR schema and reinforce the alternatives-first workflow (accepted)

  • Pros: Matches the natural authoring flow of ADR discussion, Avoids repository-wide migration, Works with the canonical edit surface from ADR-0037
  • Cons: Keeps chosen-option state partly in alternative status and partly in decision prose

Add only a lightweight selected_option field and leave other ADR fields unchanged (rejected)

  • Pros: Gives tooling a direct chosen-option field
  • Cons: Still encourages agents to write the conclusion too early, Adds schema surface without solving the broader authoring-flow issue
  • Rejected because: It keeps the premature-conclusion problem while still introducing schema churn.

ADR-0039: Use SQLite FTS5 as read-only search index for governance artifacts

Status: proposed | Date: 2026-04-09

Tags: cli

References: RFC-0002, RFC-0004

Context

govctl manages governance artifacts (RFCs, ADRs, clauses, work items, guards) as TOML files in gov/. As the corpus grows (currently 200+ artifacts, projected to reach 1000+ in active projects), finding artifacts by content becomes increasingly difficult.

Problem Statement

Users need to answer questions like “which ADR discussed caching?”, “which RFC clause mentions backward compatibility?”, or “which work items reference RFC-0002?”. Currently this requires:

  • grep over raw TOML files (poor UX, no ranking, no stemming)
  • govctl list + manual inspection (only searches titles)
  • Memorizing artifact IDs

None of these scale or provide relevance-ranked results.

Constraints

  • RFC-0002 establishes TOML files as the source of truth — any index must be derived, not authoritative
  • RFC-0004 governs concurrent write safety — the index must not interfere with the file locking protocol
  • The index must work offline with no external services
  • Rebuild must be fast enough to run transparently on every search query

Decision

We will use SQLite FTS5 with lazy incremental sync as the search backend for govctl search.

Design

  1. Index location: gov/.search.db (gitignored, along with WAL sidecars gov/.search.db-wal and gov/.search.db-shm). Disposable — can be deleted and rebuilt transparently.

  2. Indexed content: All artifact types (RFCs, clauses, ADRs, work items, guards). Each entry stores the artifact ID, type, title, and a concatenation of all human-readable text fields. Tokenized with Porter stemming for English morphological matching.

  3. Sync strategy — lazy incremental:

    • On every govctl search, compare content hashes of gov/ TOML files against an index-side manifest
    • New/changed files: parse and upsert into the search index
    • Deleted files: remove from index
    • Unchanged files: skip
    • Missing or corrupt index: full rebuild (no error, just slower first query)
  4. No write-through optimization. The lazy scan is correct in all cases and fast enough (~10ms at current scale). Adding write-through coupling between the artifact write path and the index is premature complexity.

  5. Concurrency: SQLite WAL mode handles concurrent search invocations safely. If two govctl search calls trigger a sync simultaneously, both will complete without corruption.

  6. Explicit escape hatch: govctl search --reindex forces a full rebuild.

Why This Design

  • Lazy sync avoids coupling between the write path and the index — files changed by manual editing, VCS operations, or govctl all sync identically
  • A single read-only cache file is simpler than a persistent daemon or event-driven index
  • The index is not covered by RFC-0004 file locking because it is a derived cache, not a governance artifact
  • govctl init should add gov/.search.db* to .gitignore to cover the database and WAL sidecars

Consequences

Positive

  • Users can find artifacts by content with relevance ranking — “which ADR discussed caching?” returns ranked results instantly
  • Porter stemming handles morphological variants (cache/caching/cached) without exact-match frustration
  • Index is disposable and self-healing — delete gov/.search.db and the next search rebuilds it
  • No daemon, no external service, no network — works fully offline
  • Lazy sync means zero ceremony — no separate index-build step, no cache invalidation protocol

Negative

  • Adds rusqlite (bundled) as a dependency, increasing binary size by ~3MB (mitigation: feature-gate search behind a default-on cargo feature if binary size becomes a concern)
  • First search after bulk file changes (e.g., git checkout switching branches, large merge) will be slower due to lazy-sync catch-up cost (mitigation: rebuild is still sub-second for 1000 artifacts; --reindex makes this explicit when needed)
  • CJK text requires additional tokenizer configuration beyond the default Porter stemmer (mitigation: defer to a follow-up if CJK projects adopt govctl)
  • The index file must be gitignored — if a user commits it accidentally, it will cause noisy diffs (mitigation: govctl init adds gov/.search.db to .gitignore by default)

Neutral

  • The search index introduces a second file format (SQLite) into the gov/ directory alongside TOML, but it is explicitly non-authoritative and disposable

Alternatives Considered

SQLite FTS5 with lazy incremental sync: single-file read-only index using rusqlite (bundled), Porter stemming, BM25 ranking, and content-hash-based incremental updates on each search query. (accepted)

  • Pros: Battle-tested BM25 ranking out of the box, Single-file index, no daemon or external service, Porter stemming handles English morphology (cache/caching/cached), rusqlite is mature with bundled compilation — no system SQLite dependency, Lazy sync means no separate build step or cache invalidation protocol
  • Cons: Adds ~3MB to binary size from bundled SQLite, CJK segmentation requires additional tokenizer configuration

Tantivy (Rust-native full-text search): Use the tantivy crate, a Lucene-inspired search engine written in Rust. Supports BM25, tokenizers, and schema-defined fields natively. (rejected)

  • Pros: Pure Rust, no C dependency, More powerful query language (boolean, phrase, fuzzy), Purpose-built for search — better performance at scale
  • Cons: Much heavier dependency (~50 crates in dependency tree), Index is a directory of segment files, not a single file, Overkill for <1000 documents
  • Rejected because: Dependency weight and complexity are disproportionate to the scale of govctl’s artifact corpus. SQLite FTS5 covers the requirements with a single well-understood dependency.

In-memory inverted index with no persistence: Build a simple inverted index on every search invocation by scanning all TOML files, tokenizing content, and ranking by term frequency. No disk cache. (rejected)

  • Pros: Zero dependencies — no SQLite, no new crates, No cache invalidation problem — always fresh
  • Cons: Full rebuild on every query (~100ms at 200 files, grows linearly), No stemming or advanced tokenization without additional code, No BM25 — would need a custom ranking implementation
  • Rejected because: Lacks stemming and BM25 ranking out of the box. Rebuild cost scales linearly and becomes noticeable beyond 500 artifacts. The UX gap versus FTS5 is significant for the marginal dependency savings.

ADR-0040: Controlled-vocabulary tags for governance artifacts

Status: accepted | Date: 2026-04-09

Tags: schema

References: RFC-0002, ADR-0039

Context

As the govctl artifact corpus grows (currently 200+ artifacts), finding related artifacts by domain becomes difficult. Users resort to grep or memorizing IDs.

Problem Statement

There is no structured way to answer “show me everything related to caching” or “which ADRs touch the parser”. Artifact titles provide some signal, but titles are inconsistent and not designed for cross-cutting categorization.

Constraints

  • RFC-0002:C-RESOURCES defines the field surface for each artifact type — adding tags requires a schema amendment
  • RFC-0002:C-CRUD-VERBS governs how fields are mutated — tags must follow existing add/remove verb semantics
  • Tags must be diffable and reviewable in PRs (no hidden state)
  • The system should prevent tag sprawl — typos and near-duplicates degrade signal

Options Considered

Two tagging models: controlled vocabulary (registry-first) vs. free-form (tag-on-use). See alternatives for analysis.

Decision

We will use a controlled-vocabulary tag system where tags must be registered in a project-level allowed list before any artifact can reference them.

Why Controlled Vocabulary

The core trade-off is between friction and signal quality. Free-form tags have zero friction but degrade rapidly — typos, case variants, and synonyms fragment the taxonomy. In a governed workflow where artifacts are meant to be auditable and cross-referenced, unreliable metadata defeats the purpose.

A controlled vocabulary enforces consistency at the cost of a one-time registration step for each new tag. This cost is intentional: introducing a new domain category is a project-level decision that should be visible and reviewable.

Design Outline

  • Registry: a [tags] allowed list in gov/config.toml — flat, lowercase kebab-case strings
  • Artifact field: an optional tags array in the [govctl] section of RFCs, clauses, ADRs, work items, and guards (releases do not carry tags)
  • Management: registry-level new/delete/list commands; artifact-level tagging via existing add/remove verbs
  • Filtering: --tag flag on existing list commands for taggable resource types
  • Validation: govctl check rejects tags not in the allowed set; add rejects unregistered tags immediately

Detailed command syntax, schema changes, and validation rules will be specified in an RFC-0002 amendment.

Constraints

  • No maximum tag count per artifact — signal quality is maintained by the controlled vocabulary, not by limiting labels
  • The initial seed list of allowed tags is a separate operational decision from the mechanism itself
  • Tags complement but do not replace potential future full-text search (see ADR-0039)

Consequences

Positive

  • Cross-cutting discovery becomes a first-class operation — “show me everything about caching” is a single command
  • Controlled vocabulary prevents tag sprawl — consistency is enforced, not hoped for
  • Tags are part of the TOML source — diffable, reviewable in PRs, greppable
  • Agents can enumerate available tags and use them programmatically
  • Extends existing add/remove/list verb model — minimal new CLI grammar

Negative

  • Friction to introduce a new tag — requires a config edit before first use (mitigation: this friction is intentional and the operation is a one-liner)
  • Retroactive tagging of existing artifacts requires effort (mitigation: incremental adoption — untagged artifacts simply don’t appear in filtered queries)
  • Schema change across all five taggable artifact types (mitigation: tags is optional with empty-array default — existing artifacts remain valid without modification)

Neutral

  • govctl tag becomes a new top-level command namespace for registry management
  • The tag vocabulary will need periodic curation as the project evolves — orphaned or overly broad tags should be pruned
  • Tags complement but do not replace full-text search; ADR-0039 remains a viable future option if content-level discovery is needed
  • An RFC-0002 amendment is a prerequisite before implementation — this ADR authorizes the design direction but not the schema change

Alternatives Considered

Controlled vocabulary: tags registered in gov/config.toml before use, enforced by govctl check. Lowercase kebab-case, flat list. (accepted)

  • Pros: Prevents tag sprawl — typos and near-duplicates are caught at check time, Registry is diffable and reviewable in PRs, Tag list is enumerable — agents and CLI completion can offer suggestions, Removing a tag from the registry is an explicit, auditable decision
  • Cons: Friction to add a new tag — requires a config edit before first use

Free-form tags: any string can be used as a tag on any artifact. No registry. Tags are created implicitly on first use. (rejected)

  • Pros: Zero friction — tag immediately without config changes
  • Cons: Tag sprawl is inevitable — cache vs caching vs Cache are all different tags, No way to enforce consistency across contributors, Removing a stale tag requires finding and editing every artifact that uses it
  • Rejected because: In a governed workflow, uncontrolled metadata defeats the purpose of structured artifacts. Tag sprawl would quickly make filtering unreliable.

No tags — improve search and filtering instead: rely on title grep, rendered markdown search tools (rg, qmd), or future FTS (ADR-0039) to find artifacts by content rather than adding structured metadata. (rejected)

  • Pros: Zero schema changes — no new fields, no config section, no validation rules, No tagging discipline burden on authors
  • Cons: Finding all artifacts related to a topic requires remembering the right search terms, No enumerable taxonomy — agents cannot discover what categories exist, Cross-cutting queries remain ad hoc and fragile
  • Rejected because: Search finds text matches, not intentional categorization. Tags express author intent about which domain an artifact belongs to — a dimension that free-text search cannot reliably recover.

ADR-0041: Self-update and cargo-binstall binary distribution

Status: accepted | Date: 2026-04-13

Tags: release

References: RFC-0002, ADR-0018, ADR-0033

Context

govctl is distributed via cargo install govctl and as prebuilt binaries on GitHub Releases. The release CI (.github/workflows/release.yml) already produces binaries for five platform targets: x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-apple-darwin, aarch64-apple-darwin, and x86_64-pc-windows-msvc.

Problem Statement

Two gaps exist in the binary distribution story:

  1. No in-place update. Users must remember to re-run cargo install govctl or manually download from GitHub Releases to get a new version. There is no built-in way to check for or apply updates.

  2. No cargo binstall support. cargo-binstall can install prebuilt binaries from GitHub Releases without compiling from source, but requires [package.metadata.binstall] in Cargo.toml to locate the correct asset. Without this metadata, cargo binstall govctl falls back to a full source build.

Constraints

  • RFC-0002:C-GLOBAL-COMMANDS requires new global commands to meet at least one criterion: (1) multi-resource, (2) project-level init/cleanup, or (3) meta-information about the CLI itself. A self-update command qualifies under criterion 3.
  • ADR-0018 established “one canonical way” — the update mechanism should be singular.
  • Release assets use the naming convention govctl-v{version}-{target}.{ext} (tar.gz for Unix, zip for Windows).
  • The project already depends on reqwest for HTTP (via other crates), so adding network capability is not a new dependency class.

Decision

We will use the self_update crate for a built-in govctl self-update command and add [package.metadata.binstall] to Cargo.toml for cargo-binstall support, because:

  1. Existing infrastructure fits perfectly. The release CI already produces platform binaries with naming that self_update expects (govctl-v{version}-{target}.{ext}). No CI changes needed.

  2. Minimal effort, maximum coverage. The self_update crate handles the hard parts (API queries, platform detection, archive extraction, binary replacement) in ~20 lines. cargo-binstall metadata is a 4-line addition to Cargo.toml.

  3. Two complementary install paths, one asset layout. Users who installed via cargo binstall can update via cargo binstall govctl. Users who installed via direct download or govctl self-update can update in place. Both paths consume the same GitHub Release assets.

Consequences

Positive

  • Users can update govctl with a single command (govctl self-update) regardless of how it was originally installed
  • cargo binstall govctl installs prebuilt binaries in seconds instead of compiling from source (~2 min)
  • Both update paths share the same GitHub Release assets — no additional CI or hosting required
  • Version check (govctl self-update --check) enables scripted staleness detection in CI or hooks

Negative

  • New runtime dependency on self_update crate and its transitive dependencies (mitigation: the crate is well-maintained with 8M+ downloads; feature-flag to compile only the GitHub backend + rustls)
  • Binary replacement requires write permission to the install directory (mitigation: clear error message when permission is denied, suggesting sudo or ownership fix)
  • GitHub API rate limits apply to unauthenticated requests — 60 requests/hour per IP (mitigation: self-update is infrequent; document GITHUB_TOKEN env var for authenticated requests if needed)

Neutral

  • cargo install govctl continues to work unchanged — this adds paths, does not replace existing ones
  • Plugin users (ADR-0033) are unaffected — plugin updates are managed by Claude Code’s plugin system

Alternatives Considered

self_update crate with GitHub Releases backend: Use the self_update crate (v0.44, ~8M downloads, actively maintained) to query GitHub Releases API, download platform-appropriate binary, and replace the running executable. Pair with cargo-binstall metadata in Cargo.toml so both self-update and cargo-binstall share the same release asset layout. (accepted)

  • Pros: Minimal code (~20 lines) — the crate handles API queries, platform detection, archive extraction, and binary replacement, Actively maintained with broad adoption (8M+ downloads), Reuses existing release CI assets without changes — asset naming already matches, cargo-binstall support is additive metadata only, zero code, Both update paths share one asset layout, reducing maintenance
  • Cons: Adds a runtime dependency (~5 transitive crates for HTTP, archive, self-replace)

Manual implementation with reqwest: Implement GitHub Releases API querying, asset download, archive extraction, and binary replacement manually using reqwest and flate2/tar crates. (rejected)

  • Pros: Full control over behavior and error messages, No dependency on third-party update crate
  • Cons: Significant implementation effort (~200+ lines) for a solved problem, Must handle platform detection, archive formats, binary replacement, and edge cases manually, Ongoing maintenance burden for update logic
  • Rejected because: The self_update crate already solves this reliably. Reimplementing is unnecessary complexity for marginal control benefit.

Shell out to cargo install or cargo binstall: Instead of a built-in self-update, invoke cargo install govctl or cargo binstall govctl as a subprocess. (rejected)

  • Pros: Zero new code for the update mechanism itself, Leverages existing package manager infrastructure
  • Cons: Requires Rust toolchain (cargo install) or cargo-binstall installed separately, Slow for source builds — full compilation on every update, Poor UX — error messages come from external tools, not govctl, Cannot work in environments where govctl was installed via direct binary download
  • Rejected because: Depends on external toolchain being present. Users who installed from GitHub Releases would not have cargo available. Per ADR-0018, prefer one canonical path.

ADR-0042: Enforce ADR writing order with structural gates

Status: accepted | Date: 2026-04-14

Tags: validation

References: ADR-0027, RFC-0001

Context

The adr-writer skill prescribes a specific writing order — context, alternatives, mark alternatives as accepted/rejected, decision, consequences — but nothing in the CLI enforces it. Today, an agent or user can write decision on a fresh ADR with zero alternatives, or accept an ADR with empty context and no rejected options.

Problem Statement

Without structural enforcement, ADRs drift toward “conclusion-first” writing: a decision is stated without evidence that alternatives were considered. This undermines the purpose of ADRs as justificatory artifacts per ADR-0027.

The threshold of “at least 2 alternatives, with at least 1 accepted and 1 rejected” comes from the adr-writer skill’s core principle: “Let the alternatives show the discussion.” A decision that evaluated zero alternatives is not a decision — it is an assertion. A decision with only one option (the chosen one) has no visible trade-off. Two alternatives (one chosen, one rejected) is the minimum structure that demonstrates deliberation.

Current State

  • govctl adr set <ID> decision has no precondition checks
  • govctl adr accept <ID> only validates the status transition (proposed -> accepted) per RFC-0001:C-ADR-STATUS, not content completeness
  • govctl check validates schema and cross-references but has no ADR completeness rules
  • The adr-writer skill documents the order but cannot enforce it

Constraints

  • Historical backfills are a legitimate use case where alternatives may not be recoverable — enforcement must have an escape hatch
  • Write-time gates should not block exploratory drafting of other fields (context, alternatives, consequences) — only decision is order-sensitive
  • The force flag is the established override pattern in govctl (used by init, init-skills, delete); it applies to the lifecycle gate (adr accept --force) but not to the write-time gate

Decision

We will enforce ADR writing order at two points: when the decision field is written, and when the ADR is accepted, because:

  1. Write-time gates catch conclusion-first thinking at the source. Blocking decision before alternatives are evaluated forces the author to consider options before committing to a conclusion. This is the critical moment — once a decision is written, the mental model shifts from exploration to defense. This gate is strict and not bypassable.

  2. Lifecycle gates provide a completeness checkpoint. Acceptance requires evaluated alternatives per the adr-writer skill’s prescribed order and the minimum-deliberation threshold described in the context.

  3. The force flag on adr accept preserves historical backfill workflows. When alternatives cannot be reconstructed, the lifecycle gate can be bypassed explicitly via adr accept --force rather than silently.

The specific validation rules (minimum alternative count, required statuses) are implementation details guided by the adr-writer skill’s prescribed order and the minimum-deliberation threshold described in the context.

Consequences

Positive

  • ADRs become structurally complete before decisions are recorded — alternatives-first thinking is enforced, not just recommended
  • Agents cannot shortcut the process by writing decision before evaluating options
  • Acceptance gate catches incomplete ADRs even when write-time gate was bypassed
  • Historical backfills remain possible via the force flag with explicit intent

Negative

  • Authors who prefer to draft decision first and refine alternatives later face friction on every ADR (mitigation: this friction is intentional — the force flag exists for genuinely exceptional cases like historical backfills, not as a routine workflow bypass; the expected frequency of force usage should be low)
  • Validation logic spans two code paths (edit and lifecycle), which adds maintenance surface as the ADR schema evolves (mitigation: the checks are field-presence and count checks with clear error messages; both paths share the same validation function)

Neutral

  • Existing accepted ADRs are unaffected — the gates only apply to future write and accept operations
  • The adr-writer skill documentation remains the same; the CLI now enforces what the skill recommends

Alternatives Considered

Both write-time and lifecycle gates: Gate the decision field behind alternatives completeness, and gate acceptance behind overall ADR completeness. Both gates bypassable with the force flag for historical backfills. (accepted)

  • Pros: Enforces alternatives-first thinking at the moment it matters most, Lifecycle gate provides a second checkpoint at acceptance time, Consistent with the adr-writer skill prescribed order
  • Cons: Adds validation logic to two code paths (edit and lifecycle)

Lifecycle gate only: Enforce completeness only at adr accept time, no write-time restrictions on setting decision. (rejected)

  • Pros: No new edit-path complexity, Allows flexible drafting order
  • Cons: Decision can be written without evidence of alternatives-first thinking, Quality check only at acceptance, not at authoring time
  • Rejected because: Lifecycle-only enforcement misses the critical moment: when the decision is being written. By then, the conclusion-first pattern is already established.

No enforcement: Keep the current behavior where the adr-writer skill documents the order but the CLI does not enforce it. (rejected)

  • Pros: Zero implementation effort
  • Cons: No enforcement at all — relies entirely on skill guidance and human discipline, Agents can and do skip alternatives when not enforced
  • Rejected because: The adr-writer skill already documents the order. The problem is that documentation alone does not prevent conclusion-first writing.