# Editions

> Decide whether a PR's new or changed default needs edition-journal handling (pkg/edition, docs/prd/editions.md), and do the mechanical work if so: journal entries, the four-layer default check, snapshot/invariant regeneration, and the post-editions KindBehavior candidate note for behavior changes the journal can't gate yet. Invoke whenever a PR changes what a config key defaults to, or changes what an existing stored value effectively means.

- Skill: `cloudposse/editions` (Agent Skill)
- Install (CLI): `npx skillmds@latest add cloudposse/editions`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cloudposse/editions/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: cloudposse (https://skillmd.com/u/cloudposse)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cloudposse/editions

---


# Editions (Date-Anchored Defaults)

Source of truth: `docs/prd/editions.md`. This skill is the contributor-facing decision tree for
"does my change need edition handling", not a restatement of the architecture — read
`docs/prd/editions.md` for the four-layer model, `pkg/edition/journal.go`'s `Entry` schema, and the
guardrail tests before touching anything. This skill exists because the deciding question
("does this default change supersede prior behavior?") is easy to answer wrong by only checking
the literal rule ("new keys are never journal-gated") without checking what the rule is *for*.

## The decision tree

Ask these in order. Stop at the first "yes."

**1. Does this change a previously-shipped key's literal default value?**
(e.g. flipping `settings.terminal.pager`'s struct default from `"true"` to `"false"`.)

→ **Yes: `KindValue` journal entry required.** Follow `docs/prd/editions.md`'s "Contributor
workflow" exactly:
1. Change the literal in `setDefaultConfiguration` (`pkg/config/load.go`), and in
    `defaultCliConfig` (`pkg/config/default.go`) if that struct also carries the field. Confirm the
    key isn't set in the embedded `atmos.yaml` (layer (b) must stay journaled-key-free).
2. Append a dated `Entry{Date, Key, Kind: KindValue, Old, New, Description, Ref}` to
    `pkg/edition/journal.go` — `Ref` is the PR URL, `Old`/`New` typed as the field is typed today.
3. Regenerate the snapshot: `ATMOS_REGENERATE_DEFAULTS_SNAPSHOT=true go test ./pkg/config -run TestDefaultConfigurationSnapshot`.

Skipping this is not optional — `pkg/config/default_snapshot_test.go` fails the build on an
unjournaled value change.

**2. Is this a brand-new key whose default doesn't change what Atmos already, unconditionally did?**
(e.g. a new opt-in flag for genuinely new functionality with no prior equivalent — nothing before
this key ever did what it controls, not even as hardcoded behavior.)

→ **No action needed.** `docs/prd/editions.md`: "new defaults are never journal-gated." Only edit
the layer that introduces the key and regenerate the snapshot (new-key diffs there don't require a
journal entry, just committing the updated golden file).

**3. Is this a brand-new key whose default *does* change what Atmos already, unconditionally did
— just via a key that didn't exist before?**

This is the case rule 2's literal wording ("new key, so exempt") lets through even though the
*effect* on an upgrading user is identical to a changed default: before the key existed, behavior
was fixed; after it ships, the key's default silently changes that fixed behavior for every
existing project with zero action from them. Two sub-questions decide the safe default:

- **Is the new automatic behavior something Atmos flat-out never did before** (a new class of
  automatic side effect, e.g. mutating a lock file, deleting something, calling out to a new
  service)? → **Default to the value that preserves the old, fixed behavior**, even though that
  costs you the "just works" framing in the announcement. Ship the new capability as opt-in.
  Precedent: `components.terraform.init.upgrade` defaults to `never` (Atmos never passed
  `-upgrade` automatically before this setting existed), not `auto`, even though `auto` was the
  natural symmetric choice next to `init.mode`/`init.reconfigure`.
- **Is the new key only making an already-unconditional action conditional/smarter** (same work,
  done less redundantly — no new class of side effect)? → Defaulting to the smarter behavior is
  more defensible, but you're still choosing to ship a real behavior change. Call it out
  explicitly and prominently in the PRD's Goals/Migration section and the blog post — don't let a
  reader assume "new key = no impact." Precedent: `components.terraform.init.mode`/
  `init.reconfigure` default to `auto` (init already ran unconditionally every time; `auto` only
  skips/conditions it), with the PRD's Goals section stating outright which legacy-default
  combination is *not* behavior-preserving.

Either way: proceed to step 4.

**4. Does this PR reinterpret what an existing, already-shipped config value effectively means,
without changing the stored value itself?**
(e.g. `init_run_reconfigure: true` keeps meaning "true" in the YAML, but what `true` *causes*
changed from "always add `-reconfigure`" to "add it only when the backend changed.")

→ **This is `KindBehavior`, not `KindValue` — and `KindBehavior` resolution isn't implemented yet**
(`pkg/edition/journal.go`'s `Kind` field has the enum value reserved, zero entries use it; see
`docs/prd/editions.md`'s Roadmap). You cannot mechanically gate this today. What you **can** and
**must** do:
1. State the reinterpretation explicitly in the PR's PRD (Goals or a dedicated Migration section)
    — don't let it hide inside a generic "behavior change" bullet. Say precisely what the old value
    used to cause and what it causes now.
2. Add a bullet to `docs/prd/editions.md`'s Roadmap → "Behavior gating" seed-entry list, dated to
    this PR's merge date, with a PR link and one sentence describing the reinterpretation — same
    format as the existing 9 seed entries. If this is the first such entry to ship *after* editions
    itself existed (check the entry dates against `docs/prd/editions.md`'s own PRD changelog date),
    say so explicitly, the way the "First post-editions candidate" paragraph does — it's a
    different situation from the pre-editions historical sweep and worth flagging as such.
3. Bump `docs/prd/editions.md`'s own Changelog table with a new dated row noting the addition (no
    version-number bump needed for a docs-only Roadmap addition; use the next minor, e.g. 1.0 → 1.1).

## What NOT to do

- Don't invent a `KindBehavior` journal entry with a fake or partial `BehaviorChanged` predicate to
  "make it gated" — the resolution engine doesn't call it, so it would silently do nothing while
  looking like real protection. Wait for the actual v2 implementation.
- Don't skip the Roadmap note because "it's just a doc change" — the whole point of the seed-entry
  list is to make ungated behavior changes discoverable later, not to pretend editions covers
  something it doesn't.
- Don't default a new automatic-side-effect key to the "smart" value just because the sibling keys
  in the same feature default that way — check step 3's two sub-questions per key, not per
  feature. A single PR can legitimately ship one key defaulting to `auto` and another to `never`.

## Verifying before you commit

```bash
go build ./...
go test ./pkg/edition/... ./pkg/config/...          # anchor/resolve/journal + load_edition_test.go + invariants
ATMOS_REGENERATE_DEFAULTS_SNAPSHOT=true go test ./pkg/config -run TestDefaultConfigurationSnapshot  # only if a default changed
```

`pkg/config/edition_invariants_test.go` and `pkg/edition/journal_invariants_test.go` fail the build
on: non-chronological or malformed journal dates, a chain where `entry[n+1].Old != entry[n].New`, a
journaled key present in the embedded `atmos.yaml`, or `defaultCliConfig` disagreeing with the
journal's current value. Don't hand-edit `pkg/config/testdata/default-config-snapshot.yaml` —
always regenerate it.

## Related

- `docs/prd/editions.md` — architecture, semantics, full journal contents, testing, roadmap.
- `docs/prd/terraform-auto-init.md` — worked example of both a `KindBehavior` candidate
  (`init_run_reconfigure`'s reinterpretation) and a step-3 new-automatic-behavior default choice
  (`init.upgrade: never`) in the same PRD.
- `pull-request` skill — the broader PR-readiness checklist this feeds into; run this skill's
  decision tree before that one's pre-push checklist when a PR touches any default.

