You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 91de3a8
Browse filesBrowse the repository at this point in the historyBrowse files
docs(adr): amend ADR-0007 per the #212 ruling — admission test + @ROLE shrink
ADR-0007 catalogued `source.event @topic @ROLE: publish`; ADR-0028 later ruled
that message topics are channels, not sources, "never in source.*". Both were
accepted and on the books, and nothing reconciled them. Read literally the later
decision foreclosed the earlier one's catalog entry. Nothing broke only because
`source.rdb` is the sole registered paradigm.
Resolved in favour of ADR-0028, and generalized. Amendment 1 removes `event` and
replaces the implicit admission rule with an explicit test: a source binds
ADDRESSABLE STATE AT REST — readable on demand by a declared key or address, not
merely observed as it flows past.
The second half of that test is the load-bearing part. #212 proposed a two-prong
AND (addressable AND drift-inspectable as a schema), and swept across all twelve
paradigms it fails in both directions: it would ADMIT a schema-registry-backed
Kafka topic and EXCLUDE source.memory, while wrongly maiming document ($jsonSchema
optional), keyValue (Redis has none), objectStore (CSV), timeSeries (Prometheus)
and graph. So drift-inspectability is demoted to a per-paradigm verify capability
and is explicitly NOT an admission criterion. Under the single prong only event's
flow kinds fall; get-by-id generalizes to get-by-declared-key/address, so
keyValue composite keys, objectStore paths and timeSeries series+time all
qualify, as do vector and search (both expose fetch-by-id — similarity is the
query path, not the only read). The escape clause is recorded as principled: a
stream becomes a source exactly when it is treated as addressable state, so a
compacted changelog read by key enters as an ordinary read-only-@kind source.
What stays out is only the flow itself.
Amendment 2 records that @ROLE is a designation mechanism, not a routing one.
The Consequences section promised codegen and runtime would "route by @ROLE
(primary = CRUD; index/cache/publish = derived)". No port ever built it: across
all five, every read of @ROLE is an equality test against primary — Java's OMDB
has zero role usage, and Kotlin's KotlinGenUtil and Python's write-through read
path are explicitly documented role-agnostic, finding the replica by @kind. So
the registered vocabulary shrinks to primary | replica, with index/cache/publish/
mirror reserved-not-registered (the ADR-0040 treatment) and a stated re-entry
bar: a role member enters the registry only when a shipping consumer dispatches
on it. Also recorded: replica is structurally required despite no consumer
reading the word, because @ROLE defaults to primary and the one-primary
invariant rejects two primaries — a second source must carry an explicit
non-primary role or the model fails to load.
Docs only. ADR-0035 §1 excludes reserved-but-unregistered vocabulary from the
compat surface, so this does not reset the quiet-period clock. The @ROLE registry
change itself rides the #210 consolidation batch; the eventing surface rides
FR-024 to 1.1.
Synced: ADR-0028's channel clause now cites the ratification; ADR-0018's
per-paradigm example no longer teaches a removed paradigm; the source-v2 design
doc strikes the event row, the CDC/outbox example and the four reserved roles;
FR-024 §7's channel row references a projection rather than a value, landing the
symmetry — queries return projections, commands take values, events emit
projections.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3msoGxjRMwx94PhKSLDuE
Amendments 1–2): the **`event` paradigm is removed** (a stream is a channel, not a source — a
7
+
`source.*` binds **addressable state at rest**; emission moves to the surface layer as
8
+
`api.eventing`/`operation.event`/`binding.messaging` with a payload→**projection** reference), and
9
+
**`@role`'s registered vocabulary shrinks to `primary | replica`** (`index`/`cache`/`publish`/`mirror`
10
+
become reserved-not-registered; no port ever built the anticipated role-routing). The rows below
11
+
marked ~~struck~~ are retained for provenance — read ADR-0007 for the governing contract.
5
12
-**Decision record:**[ADR-0007](../../../spec/decisions/ADR-0007-source-v2-paradigm-subtypes-multisource.md) (the durable contract); this spec is the detailed design + migration + rollout.
6
13
-**Supersedes:** FR-003 / Project E `source.dbTable` / `source.dbView`.
7
14
-**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).
@@ -13,7 +20,7 @@
13
20
14
21
A `source` declares **where an object's data physically lives**. Rules:
15
22
16
-
1.**Subtype = storage paradigm** (`rdb`, `document`, `event`, …). The paradigm selects the
23
+
1.**Subtype = storage paradigm** (`rdb`, `document`, `search`, …). The paradigm selects the
17
24
codegen/runtime driver, so it's the behavioral axis (ADR-0002). Each subtype owns its
18
25
attribute vocabulary (ADR-0004).
19
26
2.**`name` = logical name** (optional on sources), consistent with every node (ADR-0006).
@@ -31,7 +38,7 @@ A `source` declares **where an object's data physically lives**. Rules:
|~~**event**~~**REMOVED (#212)**|~~Kafka, Pulsar, Kinesis~~|~~`@topic`~~|~~topic* · stream · eventStore · changelog~~|A stream is a *channel*, not a source. Emission belongs to the surface layer (`binding.messaging` on an `operation.event`, payload → projection). A compacted changelog / event store read **by key** re-enters as an ordinary read-only-`@kind` paradigm source under the escape clause — it is addressable state at rest.|
|~~`index`~~**RESERVED (#212)**|~~search/vector index derived from primary~~| Not registered — no consumer dispatches on it |
123
+
|~~`cache`~~**RESERVED (#212)**|~~read-/write-through cache~~| Not registered — no consumer dispatches on it |
124
+
|~~`publish`~~**RESERVED, not registered (#212)**|~~event/stream sink (CDC, outbox, event-sourcing)~~| Emission is a surface concern; see ADR-0007 Amendment 2. `index`/`cache`/`mirror` are likewise reserved-not-registered — no consumer in any port dispatches on them. |
125
+
|~~`mirror`~~**RESERVED (#212)**|~~dual-write (migration)~~| Not registered — no consumer dispatches on it |
126
+
127
+
**Registered vocabulary is `primary | replica` only** (ADR-0007 Amendment 2). The struck rows are
128
+
reserved: documented here, not in the registry, re-entering only when a shipping consumer
129
+
dispatches on them. The example above is therefore illustrative of the *paradigm* axis; its
130
+
`@role: index` / `@role: cache` values are not currently loadable.
118
131
119
132
**Validation:** exactly one `primary` per object (`ERR_SOURCE_NO_PRIMARY` / `ERR_SOURCE_MULTIPLE_PRIMARY`). Single-source objects need no `@role` (defaults to `primary`).
Copy file name to clipboardExpand all lines: docs/superpowers/specs/2026-06-12-fr-024-entity-surfaces-projections-design.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -232,7 +232,7 @@ errors when it is **provably all-to-one** (every hop explicitly declares
232
232
| `rdb @kind:table` | **entity** only | own, required |
233
233
| `rdb @kind:view` / `materializedView` | **projection** (standalone) or **entity** (multi-source, read role) | borrowed / own |
234
234
| `rdb @kind:storedProc` / `tableFunction` | result shape = **projection** (today spelled entity — migration candidate, §10); args = **value** via `@parameterRef` (shipped) | optional — result sets are often keyless |
235
-
| *(future)* message topic / queue | **nobody** — a topic is a *channel*, not a population location; it belongs to the surface layer as `binding.messaging` on an operation referencing a value (AsyncAPI's model: channels reference message schemas) | n/a |
235
+
| *(future)* message topic / queue | **nobody** — a topic is a *channel*, not a population location; it belongs to the surface layer as `binding.messaging` on an operation referencing a **projection** (AsyncAPI's model: channels reference message schemas). Ratified 2026-08-05 (#212 / ADR-0007 Amendment 1): an emitted event's shape is system-produced derived output, so the CQRS symmetry is *queries return projections, commands take values, **events emit projections***. A topic that is treated as addressable state (compacted changelog / event store read by key) re-enters as an ordinary read-only-`@kind` paradigm source. | n/a |
Copy file name to clipboardExpand all lines: spec/decisions/ADR-0018-per-kind-physical-name-attrs.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@
7
7
8
8
## Context
9
9
10
-
ADR-0007 introduced the source-v2 paradigm and established the rule that the physical-address attribute is *per-paradigm-idiomatic*: `@table` for `source.rdb`, `@collection` for `source.document`, `@topic` for `source.event`, `@label`/`@edge` for `source.graph`. The Alternatives Considered section rejected a single generic `@locator` / `@map` attribute on the grounds that paradigms have genuinely different vocabularies and a multi-sourced field needs paradigm-specific addresses (`@column` for the rdb side, `@field` for the document side, simultaneously).
10
+
ADR-0007 introduced the source-v2 paradigm and established the rule that the physical-address attribute is *per-paradigm-idiomatic*: `@table` for `source.rdb`, `@collection` for `source.document`, `@index` for `source.search`, `@label`/`@edge` for `source.graph`. The Alternatives Considered section rejected a single generic `@locator` / `@map` attribute on the grounds that paradigms have genuinely different vocabularies and a multi-sourced field needs paradigm-specific addresses (`@column` for the rdb side, `@field` for the document side, simultaneously).
11
11
12
12
That per-paradigm principle is correct and is preserved here.
0 commit comments