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
editcommand 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
--atsemantics) - Aliases for ergonomics:
alt->alternativespro->proscon->consreason->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 readsset: replace scalar value at resolved pathadd: append into array resolved by pathremove: remove by explicit indexed path, or by existing matcher options when path resolves to an arrayremoveconflict 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 (alternativesfor ADR,acceptance_criteriafor work item). Nested paths fortickare 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-reasonremains 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/--conand 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