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:
- Change the literal in
setDefaultConfiguration(pkg/config/load.go), and indefaultCliConfig(pkg/config/default.go) if that struct also carries the field. Confirm the key isn't set in the embeddedatmos.yaml(layer (b) must stay journaled-key-free). - Append a dated
Entry{Date, Key, Kind: KindValue, Old, New, Description, Ref}topkg/edition/journal.go—Refis the PR URL,Old/Newtyped as the field is typed today. - 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.upgradedefaults tonever(Atmos never passed-upgradeautomatically before this setting existed), notauto, even thoughautowas the natural symmetric choice next toinit.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.reconfiguredefault toauto(init already ran unconditionally every time;autoonly 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:
- 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.
- 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 againstdocs/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. - 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
KindBehaviorjournal entry with a fake or partialBehaviorChangedpredicate 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
autoand another tonever.
Verifying before you commit
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 aKindBehaviorcandidate (init_run_reconfigure's reinterpretation) and a step-3 new-automatic-behavior default choice (init.upgrade: never) in the same PRD.pull-requestskill — 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.