Skip to content

Commit 7abb2b8

Browse files
dmealingclaude
andcommitted
docs(adr): reconcile ADR-0006 to D1 — AI-first sigil-free YAML; canonical JSON keeps @ (unchanged)
Walk back the Option-1 'purge @ everywhere' detour. Canonical JSON is unchanged (reserved bare + @-attrs; @<reserved> -> ERR_RESERVED_ATTR). YAML authoring is sigil-free (desugar re-adds @) + type-coercion guard (D2) + house style (D3) + conformance, TS-only (D4). Retitle/rename the ADR; revert wire-format/CLAUDE; the @-notation in source-v2/persistence specs is the canonical form (YAML drops it). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9fe361e commit 7abb2b8

7 files changed

Lines changed: 84 additions & 86 deletions

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ Preserve the following contracts exactly across all language ports:
404404
- Dotted-path syntax for `@via`: `"Program.weeks"` or `"Program.weeks.workouts"`.
405405
- Dotted-path syntax for `@of`: `"Week.id"`.
406406
- Package segments: `::` separator — `acme::common::id`.
407-
- Metadata keys are **bare — no `@` sigil** (ADR-0006). Reserved structural keywords are a closed set (`name`/`package`/`extends`/`abstract`/`overlay`/`isArray`/`children`/`value`); any other bare key is an inline attribute (classified by reserved-set membership). An `@`-prefixed key is rejected (`ERR_AT_PREFIX_KEY`) — write `schema`, not `@schema`. Array-ness is the bare reserved `isArray` (YAML: the `[]` key-suffix sugar). See [ADR-0006](spec/decisions/ADR-0006-reserved-keywords-vs-inline-attributes.md).
407+
- **Canonical JSON:** reserved structural keywords are bare (`name`/`package`/`extends`/`abstract`/`overlay`/`isArray`/`children`/`value`); inline attributes are `@`-prefixed. `@`-prefixing a reserved word (e.g. `@isArray`) is invalid (`ERR_RESERVED_ATTR`). **YAML authoring is sigil-free** — bare attrs; the desugar re-adds `@` when lowering to canonical JSON; canonical JSON is the sole cross-language interchange (YAML is a TS-only front-end). See [ADR-0006](spec/decisions/ADR-0006-ai-first-yaml-authoring.md).
408408

409409
**Loader pipeline:**
410410
- `extends:` resolution happens after all files are loaded (deferred, not eager).

docs/superpowers/specs/2026-05-23-persistence-attributes-cross-language-design.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
- **Companion to:** [source v2 spec](2026-05-23-source-v2-paradigm-subtypes-multisource-design.md) + [ADR-0007](../../../spec/decisions/ADR-0007-source-v2-paradigm-subtypes-multisource.md). Obeys [ADR-0006](../../../spec/decisions/ADR-0006-reserved-keywords-vs-inline-attributes.md) (reserved words bare; everything here is an `@`-attr).
66
- **Goal:** pull together every persistence concept already in TS (Drizzle/migrate/runtime), Java (ObjectManagerDB), and C# (EF Core) into **one normalized vocabulary**, fill the gaps (explicit referential actions, soft-delete, concurrency), and pin which attrs apply per source subtype.
77

8-
> **Notation (per revised [ADR-0006](../../../spec/decisions/ADR-0006-reserved-keywords-vs-inline-attributes.md)):** metadata keys are **bare — no `@` sigil**. Every `@`-prefixed name in this doc (`@column`, `@onDelete`, `@storage`, …) is the bare key (`column`, `onDelete`, `storage`); the `@` is dropped metamodel-wide and shown here only until normalization.
8+
> **Notation:** attribute names are shown in their **canonical JSON** spelling (`@`-prefixed: `@column`, `@onDelete`, `@storage`). In **YAML authoring** they're written sigil-free (`column:`, `onDelete:`, ); the desugar re-adds the `@` ([ADR-0006](../../../spec/decisions/ADR-0006-ai-first-yaml-authoring.md)). Reserved structural keys stay bare in both.
99
1010
## 1. Archaeology — what already exists (and diverges)
1111

docs/superpowers/specs/2026-05-23-source-v2-paradigm-subtypes-multisource-design.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
- **Related:** [ADR-0006](../../../spec/decisions/ADR-0006-reserved-keywords-vs-inline-attributes.md) (reserved keywords vs `@`-attrs — resolved here for `source`), ADR-0002 (subtype behavior), ADR-0004 (per-subtype attr schemas).
88
- **Companion:** [persistence attributes spec](2026-05-23-persistence-attributes-cross-language-design.md) — the full per-level, per-subtype attribute vocabulary (field/identity/relationship/source), including the new explicit `@onDelete`/`@onUpdate` referential actions, `@storage`, `@softDelete`, `@version`.
99

10-
> **Notation (per revised [ADR-0006](../../../spec/decisions/ADR-0006-reserved-keywords-vs-inline-attributes.md)):** metadata keys are **bare — no `@` sigil**. Where this doc writes `@table`, `@kind`, `@role`, `@onDelete`, etc., read them as the bare keys `table`, `kind`, `role`, `onDelete`; the `@` is dropped metamodel-wide. The tables retain the `@`-form only until this doc is normalized in implementation.
10+
> **Notation:** attribute names are shown in their **canonical JSON** spelling — `@`-prefixed (`@table`, `@kind`, `@role`, `@onDelete`). In **YAML authoring** the same attributes are written sigil-free (`table:`, `kind:`, …) and the desugar re-adds the `@` ([ADR-0006](../../../spec/decisions/ADR-0006-ai-first-yaml-authoring.md)). Reserved structural keys (`name`, `isArray`, `children`, …) are bare in both.
1111
1212
## 1. Model (the rules)
1313

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# ADR-0006 — AI-first YAML authoring (sigil-free YAML; JSON stays canonical)
2+
3+
**Status:** Proposed (2026-05-23)
4+
5+
**Related:** ADR-0004 (per-subtype attr schemas), ADR-0007 (source v2), the enum datatype design
6+
(`docs/superpowers/specs/2026-05-23-enum-datatype-design.md`, which defers its YAML spelling here).
7+
8+
## Context
9+
10+
MetaObjects has an optional **YAML authoring front-end** that lowers to canonical JSON metadata
11+
(`parser-yaml.ts``yaml-desugar.ts` → the shared `buildTree`). **Canonical JSON is the
12+
cross-language interchange + conformance form** and is *unchanged* by this ADR: reserved
13+
structural keys are bare; inline attributes are `@`-prefixed.
14+
15+
Configs are **AI-first** — Claude generates this metadata — so the authoring format must minimize
16+
silent footguns and "multiple ways to say the same thing." Four problems with YAML today:
17+
18+
1. **`@` is a YAML reserved indicator.** `@dbColumn:` is a hard parse failure; you must write
19+
`"@dbColumn":`. A frequent AI mistake.
20+
2. **YAML 1.2 core-schema coercion.** Unquoted `true/false`, `null/~`, and numeric tokens coerce
21+
to bool/null/number; the original text is unrecoverable after parse — dangerous for domain data
22+
(enum members, codes, names).
23+
3. **Too many equivalent forms** (bare-type defaults, scalar-vs-map body, scalar-vs-array values)
24+
raise AI error rate.
25+
4. **YAML is not conformance-tested**, and only the TS `FileMetaDataLoader` loads it.
26+
27+
## Decision
28+
29+
- **D1 — Sigil-free YAML authoring.** A **closed set of structural keys** (`name`, `package`,
30+
`extends`, `abstract`, `overlay`, `isArray`, `children`, `value`, plus the single
31+
`type.subType` wrapper). **Any other body key is an attribute**; the desugar re-adds the `@`
32+
when lowering to canonical JSON. **Canonical JSON is unchanged** — it keeps reserved-bare +
33+
`@`-attributes. This removes the `@`-quoting footgun entirely. *(Corollary for the JSON path:
34+
in canonical JSON, `@`-prefixing a reserved word — e.g. `@isArray` — is invalid →
35+
`ERR_RESERVED_ATTR`. Mostly a hand-edited-JSON guard; humans author YAML.)*
36+
- **D2 — Type-coercion guard.** (a) House rule: quote domain-data string scalars. (b)
37+
Deterministic backstop: when the schema declares a value's type, the loader **rejects** a value
38+
that parsed as a different JS type (bool/number/null) with a clear, located "quote this value"
39+
error. Never silently accept a coerced value. (Schema-guided parsing where feasible.)
40+
- **D3 — Reduce degrees of freedom.** Define the single AI **house style** Claude always emits:
41+
explicit `type.subType`; one consistent body form; quoted string values. The desugar may still
42+
*accept* human shorthand; optionally lint-warn toward house style.
43+
- **D4 — First-class + conformance-tested, TS-only scope.** YAML stays a **TS-only authoring
44+
front-end**; **canonical JSON remains the sole cross-language interchange** (only TS has a YAML
45+
loader). Add YAML conformance fixtures exercising sigil-free attributes + the coercion guard.
46+
47+
## Consequences
48+
49+
- Changes are **TS-only** (`core/parser-yaml.ts`, `core/yaml-desugar.ts`, the schema-validation
50+
pass for D2, constants, tests + new YAML fixtures). **Canonical JSON, the other ports, and the
51+
conformance oracle are unchanged.**
52+
- **Enum coordination:** `field.enum`'s `values:` (YAML) / `@values` (canonical JSON) follows D1.
53+
- **Sequencing:** built **last** in the source-v2 rollout — YAML authoring desugars to the
54+
canonical vocabulary, so source v2 + the persistence attrs must be final first.
55+
- Low blast radius: YAML is optional + currently unexercised; the canonical form doesn't move.
56+
57+
## Alternatives considered
58+
59+
- **Purge `@` from canonical JSON too** (bare everywhere). Maximally uniform, but migrates 4
60+
serializers + every `expected.json` for a cosmetic win, and removes the explicit attribute
61+
marker from the wire/oracle form. The canonical form staying stable + marked is worth more.
62+
Rejected — sigil-free *authoring* (YAML) achieves the AI-ergonomics goal without touching the
63+
canonical contract.
64+
- **Keep `@` in YAML (quoted).** The footgun being removed. Rejected.
65+
- **`_`-prefix on reserved words.** YAML-safe but marks the *frequent* set (`name`/`children`)
66+
and uglifies the canonical form. Rejected.
67+
68+
## Realization status
69+
70+
- **Spec:** `spec/wire-format.md` (canonical key model, unchanged) + this ADR (YAML layer).
71+
- **TypeScript:** _pending_`parser-yaml`/`yaml-desugar`/validation + YAML fixtures. **Other
72+
ports: out of scope** (canonical JSON is the cross-language interchange).

spec/decisions/ADR-0006-reserved-keywords-vs-inline-attributes.md

Lines changed: 0 additions & 73 deletions
This file was deleted.

spec/decisions/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ Write one when a decision is **cross-cutting** (affects more than one language p
2323
- [ADR-0003 — Metamodel constants colocation](ADR-0003-metamodel-constants-colocation.md)*Accepted*
2424
- [ADR-0004 — Provider-based type registration & composition](ADR-0004-provider-based-type-registration.md)*Accepted*
2525
- [ADR-0005 — Object representation: entity/value semantics + binding-resolved representation (OO ports)](ADR-0005-object-representation-binding.md)*Accepted*
26-
- [ADR-0006 — Reserved structural keywords vs `@`-prefixed inline attributes](ADR-0006-reserved-keywords-vs-inline-attributes.md)*Proposed*
26+
- [ADR-0006 — AI-first YAML authoring (sigil-free YAML; JSON stays canonical)](ADR-0006-ai-first-yaml-authoring.md)*Proposed*
2727
- [ADR-0007 — Source metatype v2: storage-paradigm subtypes, logical names, multi-source, per-subtype physical addresses](ADR-0007-source-v2-paradigm-subtypes-multisource.md)*Proposed*

spec/wire-format.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,16 @@ Within a node body, keys appear in this exact order in the canonical form:
3838
3. `extends` — when set
3939
4. `abstract` — when `true`
4040
5. `overlay` — when `true`
41-
6. `isArray` — when `true`
42-
7. inline attributes (bare) — alphabetical order within this section
43-
(e.g. `currency`, `default`, `fields`, `locale`, `objectRef`, `schema`)
41+
6. `isArray` — when `true` (structural, NOT an `@`-attr)
42+
7. `@`-prefixed attributes — alphabetical order within this section
43+
(e.g. `@currency`, `@dbColumn`, `@default`, `@fields`, `@locale`, `@objectRef`)
4444
8. `children` — when non-empty (declaration order, NOT alphabetized)
4545

46-
All keys are **bare — there is no `@` sigil** (ADR-0006). The reserved structural keywords are
47-
exactly the closed set listed above (1–6, 8); **any other bare key is an inline attribute**.
48-
Classification is reserved-set membership, so no marker is needed. An `@`-prefixed key is
49-
rejected (`ERR_AT_PREFIX_KEY`) in every language port — keys are bare; write `schema`, not
50-
`@schema`. Array-ness is the bare reserved `isArray` (or, in YAML, the `[]` key-suffix sugar);
51-
never a sigiled key.
46+
The reserved structural keys are exactly those listed above. In **canonical JSON**, everything
47+
else is an `@`-prefixed attribute; `@`-prefixing a reserved word (e.g. `@isArray`) is invalid
48+
(`ERR_RESERVED_ATTR`). **YAML authoring is sigil-free** (ADR-0006): the same closed reserved set
49+
is bare, every other key is a bare attribute, and the desugar re-adds the `@` when lowering to
50+
this canonical form. Array-ness is the bare reserved `isArray` (YAML: the `[]` key-suffix sugar).
5251

5352
## Package + file organization
5453

0 commit comments

Comments
 (0)