ADR 0017: State-first targeted plan/apply (--target)
- Status: Proposed
- Date: 2026-05-29
- Decider(s): @davesade (David Kubec)
- Relates to: ADR 0006, ADR 0007, ADR 0014, ADR 0015
Context
For large states, vanilla Terraform targeting still pays heavy planning costs because planning starts from full configuration graph construction and provider planner behavior. Kilolock already has a state-native model (resources, lifecycles, dependency graph, reservations) that can drive narrower execution.
We want a workflow where operators can target specific addresses and get:
- fast plan latency
- safe dependency closure
- parallel apply semantics from ADR 0007
- normal Terraform/OpenTofu compatibility for later full runs
Decision
Kilolock will add first-class --target support for plan/apply, implemented as state-first targeted execution.
Command surface
kl plan --target module.db.aws_db_instance.primary
kl plan --target module.db --target random_id.release
kl apply --target module.db.aws_db_instance.primary
--target is repeatable on both plan and apply.
Design principles
D1. State-first closure, not naive narrowing
Given target addresses, Kilolock computes required closure from state metadata and dependency graph:
- write closure: target-owned write candidates
- read closure: transitive dependencies required for correct evaluation
- validation closure: objects needed so Terraform can validate references
If closure cannot be proven safely, command fails with explicit diagnostics.
D2. Safety over minimalism
Kilolock auto-widens scope when necessary. Narrow-but-unsafe execution is not default behavior.
Optional future escape hatch:
--unsafe-target: disables some widening checks and accepts higher risk.
D3. Keep Terraform/OpenTofu execution path
Kilolock still runs Terraform/OpenTofu for actual planning/apply. The speedup comes from a smaller workspace + sliced local state + reduced target set, not from replacing Terraform semantics.
D4. Preserve ADR 0007 concurrency model
Reservations are derived from scoped write/read sets. Surprise-write guard remains mandatory:
changed_addresses ⊆ reserved_write_set
Any unexpected write aborts commit.
Architecture
- Parse/normalize target selectors (
resource,module.*, optional instance keys). - Resolve ownership and dependency closure from graph + state.
- Build temporary backend-free workspace and sliced local state.
- Run Terraform/OpenTofu plan/apply with narrowed
-target=set. - Emit spec with scoped write/read/reservations.
- Commit through existing row-level apply path.
UX behavior
- Summary must separate:
- actual planned mutations
- targeted writable ownership scope
- Mutating
apply --targetruns require explicit scope acknowledgement(
--confirm-scope). Use--dry-runto inspect derived scope andobtain a copy/paste rerun command.
- No-op targeted plans should remain clearly no-op even with non-empty scope.
- Error messages should explain widening/failure reason and recommended fallback:
- add additional targets, or
- run full plan.
Implementation path (MVP)
- CLI flags
- add repeatable
--targettoplanandapply.
- add repeatable
- Target normalization
- canonical address handling for resources and module prefixes.
- State-first closure
- compute read/write/validation closure from existing depgraph + state.
- Scoped planning
- generate targeted plan spec with scoped reservations.
- Apply path
apply --targetconvenience path mirroringapply --file.
- Tests + demo
- no-op parity tests vs full plan
- dependency inclusion tests
- conflict/reservation behavior tests
- large-state timing comparison demo
Non-goals (initial)
- Perfect support for every dynamic expression shape on day one.
- Cross-state transactional targeting.
- Resource-level RBAC policy decisions (covered by future RBAC work, ADR 0016).
Consequences
Positive
- materially faster operator feedback for large states
- better multi-engineer throughput on shared state
- targeted changes remain auditable and safe
Negative
- more complexity in closure logic and diagnostics
- risk of drift from Terraform edge semantics if tests are weak
- additional maintenance surface for target canonicalization