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
-ofor--output-ffor--force-nfor--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:
--filterhas no short form--rfc-idhas 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
--exactfor exact match - Use
--regexfor pattern match - Use
--at Nfor 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:
- Universal flags first (output, filter, dry-run)
- Resource-specific flags second
- Help/version last
4. Error Messages and Exit Codes
Exit codes:
0- Success1- 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
attyoris-terminalcrate - Respect
NO_COLORenvironment variable - Respect
--color <always|never|auto>flag (future)
Interactive prompts:
- Only show in TTY mode
- Skip if
--forceflag provided - Skip if stdin is not TTY
Progress indicators:
- Use
indicatifcrate for long operations (>2s expected) - Show progress for: render, check, bulk operations
- Suppress if
--quietor 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:
-
Consistent UX: All commands follow the same patterns. Users learn once, apply everywhere.
-
Agent-friendly: Predictable flags, stable error codes, machine-readable output defaults make automation reliable.
-
Maintainable: Implementation conventions documented, not scattered across code comments.
-
Discoverable: Distinct verbs (
set,add,remove,tick) are immediately understandable. Users can explore via--helpwithout reading documentation. -
Safe: Confirmation prompts prevent accidental destructive actions. Dry-run mode enables testing before committing.
-
Debuggable: Structured error messages with codes and locations make issues easy to diagnose.
Negative:
-
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. -
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
editcommand, but much easier to understand.addandremoveare clearer thanedit +valueandedit -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.