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.88+ (uses edition 2024)

Installation

# From crates.io
cargo install govctl

# With TUI dashboard feature
cargo install govctl --features tui

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

# Build with TUI
cargo build --release --features tui

Optional Features

FeatureDescriptionDependencies
tuiInteractive 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
├── rfc/              # RFC sources
├── adr/              # ADR sources
├── work/             # Work item sources
├── schema/           # JSON schemas
└── templates/        # New artifact templates

Create Your First RFC

govctl rfc new "Feature Title"

This creates gov/rfc/RFC-0000/rfc.json 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 --stdin <<'EOF'
The feature MUST do X.
The feature SHOULD do Y.
EOF

Validate Everything

govctl check

This validates all governance artifacts against the schema and phase rules.

Render to Markdown

govctl render

Generates human-readable markdown in docs/rfc/RFC-0000.md.

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.

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.json) — ID, title, status, phase, version
  • Clauses — Atomic units of specification

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)

Edit Clause Text

# From stdin (recommended for multi-line)
govctl clause edit RFC-0010:C-SCOPE --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 "The system MUST validate all inputs."

# From file
govctl clause edit RFC-0010:C-SCOPE --text-file 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

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 RFCs

govctl rfc list
govctl rfc list normative    # Filter by status
govctl rfc list impl         # Filter by phase

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 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

ADRs are TOML files — edit them directly in your editor or use govctl to get/set fields:

# Get specific field
govctl adr get ADR-0003 status

# Set field value
govctl adr set ADR-0003 status accepted

For alternatives (pros/cons/rejection reason), path-based edits are supported:

# Before: remove and re-add whole alternative to change one pro
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"

Status Lifecycle

proposed → accepted → superseded
         ↘ rejected

Accept a Decision

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 ADRs

govctl adr list
govctl adr list accepted    # Filter by status

Why TOML?

ADRs use TOML (not JSON or YAML) because:

  • Comments allowed — Humans can annotate inline
  • Multi-line strings — Clean """ blocks for prose
  • No YAML ambiguityNO stays NO, not false
  • Round-trip stable — Deterministic serialization

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.

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 contain:

  • Title — Brief description
  • Description — Task scope declaration
  • Journal — Execution tracking entries with date and scope (per [[ADR-0026]])
  • Notes — Ad-hoc key points (array of strings)
  • 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

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.

Mark Criteria Complete

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

The pattern matches case-insensitively by substring.

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

Notes

Add context or progress notes:

govctl work add WI-2026-01-17-001 notes "Discovered edge case in validation"

Nested path edits are also available for structured fields:

# Before: append a replacement journal entry to adjust one field
govctl work add WI-2026-01-17-001 journal --scope parser "Fixed parser edge case"

# After: direct nested update
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 Work Items

govctl work list
govctl work list queue      # Pending items
govctl work list active     # In progress
govctl work list done       # Completed

Why TOML?

Like ADRs, work items use TOML for human-friendly editing with comments and clean multi-line strings.

Validation & Rendering

govctl provides tools to validate governance artifacts and render them to human-readable formats.

Validation

Check All Artifacts

govctl check

This validates:

  • Schema conformance (all required fields present)
  • Phase discipline (no invalid state transitions)
  • Cross-references (refs point to existing artifacts)
  • Clause structure (normative clauses in spec sections)

Exit Codes

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

Rendering

Render governance artifacts to markdown for documentation.

Render RFCs

# All RFCs (committed to repo)
govctl render

# Single RFC
govctl rfc render RFC-0010

Output goes to docs/rfc/RFC-NNNN.md.

Render Other Artifacts

ADRs and work items render to .gitignored local files by default:

govctl render adr        # → docs/adr/
govctl render work       # → docs/work/
govctl render all        # Everything
govctl render changelog  # → CHANGELOG.md

Render Single Items

Each resource type supports single-item rendering:

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

Hash Signatures

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

<!-- govctl:signature sha256:abc123... -->

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

Statistics

Get a summary of your governance state:

govctl stat

Shows:

  • RFC counts by status and phase
  • ADR counts by status
  • Work item counts by status
  • Any validation warnings

Building Documentation

For mdbook integration:

./scripts/build-book.sh          # Build static site
./scripts/build-book.sh --serve  # Live preview

This renders all artifacts and generates the book structure.

RFC-0000: govctl Governance Framework

Version: 1.0.1 | Status: normative | Phase: stable


1. Summary

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

govctl is a governance CLI that manages four 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

All artifacts follow explicit lifecycle states and phase gates to ensure disciplined development.

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.

Every RFC MUST be stored as a JSON file (rfc.json) containing:

  • rfc_id: Unique identifier (e.g., RFC-0001)
  • title: Human-readable title
  • version: Semantic version (MAJOR.MINOR.PATCH)
  • status: Lifecycle state (draft | normative | deprecated)
  • phase: Work stage (spec | impl | test | stable)
  • owners: List of responsible parties
  • sections: Ordered list of sections with clause references
  • changelog: Version history

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

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)

Since: v1.0.0

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

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.

Reference Rules:

RFCs MUST NOT reference ADRs in normative clause text. RFCs are self-contained specifications. If an RFC needs to describe a decision, the decision MUST be stated directly in the RFC, not delegated to an ADR.

ADRs MAY reference RFCs to establish authority for decisions. ADRs SHOULD cite the RFC clause that the decision interprets or implements.

Work Items MAY reference any artifact type. Work Items typically reference the RFC or ADR they implement.

Rationale:

This hierarchy prevents circular dependencies and maintains clear authority chains. An RFC that references an ADR inverts the hierarchy — the ADR becomes the authority, which defeats the purpose of having RFCs as constitutional law.

Changelog entries and informative notes MAY mention ADRs for historical context, but normative clause text MUST remain self-contained.

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.

Every clause MUST be stored as a JSON file containing:

  • clause_id: Unique identifier within the RFC (e.g., C-PHASE-ORDER)
  • title: Human-readable title
  • kind: normative | informative
  • status: active | superseded | deprecated
  • text: The clause content
  • since: Version when the clause was introduced

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.

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
  • [content] section with: context, decision, consequences

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.

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, refs
  • [content] section with: description, acceptance_criteria, decisions, notes

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.

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

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

Since: v1.0.0


Changelog

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.2.1 | 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.

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)

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.

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.
  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

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. superseded — The decision has been replaced by another ADR.

Valid transitions:

  • proposed → accepted (via accept 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”)
  • 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

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

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”)

Rationale: Prevents marking work as complete without defined success criteria.

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.

Since: v0.1.0


Changelog

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.2.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, and verb semantics 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

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)
  • <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.

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).

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.json) while maintaining the flat CLI command structure (govctl clause get RFC-0001:C-NAME).

5. release - Release Version

Manages release versions and changelogs.

  • ID Format: Semantic version (e.g., 1.0.0)
  • 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)

Timestamp Format:

All timestamps in resource metadata (created, updated, started, completed) MUST use ISO 8601 date format (YYYY-MM-DD) in UTC timezone.

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.

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 Behavior:

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

2. list - List Resources

Syntax: govctl <resource> list [filter]

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

MUST support for: rfc, adr, work, clause, release 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

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 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 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 MUST NOT support for: rfc, adr (use lifecycle verbs instead)

Deletion Safety Constraints:

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

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.

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 ADRs and Work Items)
    • MUST be parseable by standard TOML tools
    • Only applicable where TOML is the source format
  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 editing (toml for ADRs/work items)

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

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
  • Optionally creates .claude/commands/ with workflow templates
  • MUST error if already initialized (unless --force)

2. govctl check

Validates all governance artifacts across the project.

Syntax: govctl check [--deny-warnings]

Behavior:

  • Validates RFCs, ADRs, clauses, work items
  • Checks state machine invariants
  • Verifies cross-references
  • Scans source code for references (if enabled in config)
  • 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 TOML/JSON.

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

Behavior:

  • Renders RFCs from JSON 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 AI agents to discover capabilities (per ADR-0015)

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

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”

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

Since: v0.1.0


Changelog

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.

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.

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.

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.

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.

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.

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.

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.

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.

Since: v0.1.0


Changelog

v0.1.0 (2026-02-15)

Initial draft

ADR-0001: Use TOML for ADRs and Work Items

Status: accepted | Date: 2026-01-17

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

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

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

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

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

References: RFC-0000:C-WORK-DEF

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

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

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

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

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

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

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

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

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

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

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

References: RFC-0002

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

References: RFC-0002, ADR-0017

Context

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

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

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

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

References: RFC-0002, ADR-0018

Context

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

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

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

References: RFC-0002, ADR-0021

Context

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

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

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

References: ADR-0024

Context

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

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

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

References: ADR-0023

Context

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

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

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

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

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

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

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, StrReplace, Shell, Glob, Grep, LS, SemanticSearch
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

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

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

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

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: proposed | Date: 2026-03-04

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.