Skip to content

Commit 065acdf

Browse files
dmealingclaude
andcommitted
docs(roadmap): record the #212 ruling and the @ROLE sub-decision it surfaced
Ratified: a stream is a channel, not a source. Drop source.event, model emission at the surface (api.eventing / operation.event / binding.messaging) with a payload->projection ref, keep the "becomes a source when treated as addressable state" escape clause. Three amendments. The first matters most, because #212 does not merely delete a catalog entry — it ratifies a TEST, and a test applies to everything. As proposed the test was a two-prong AND (addressable by id AND drift-inspectable as a schema), and swept across all twelve of ADR-0007's paradigms it fails in both directions: prong (b) would KEEP a schema-registry-backed Kafka topic and KILL source.memory, and it wrongly maims document, keyValue (Redis), objectStore (CSV), timeSeries (Prometheus) and graph. Prong (a) alone gives exactly the intended result, so the ratified doctrine is addressable-state-at-rest, with drift-inspectability demoted to a per-paradigm verify capability rather than an admission criterion. Only event's flow kinds fall; eventStore/changelog are the escape clause; vector and search survive (both have fetch-by-id — similarity is the query path, not the only read). Second: removing @ROLE: publish is not docs-only. It is live registered vocabulary in expected-registry.json, spec/metamodel/db.json and five ports' embedded definitions, so it rides the #210 consolidation batch rather than landing standalone. Third: FR-024 §7's channel row references a projection, not a value. Investigating the second amendment surfaced a sub-decision worth its own ruling. @ROLE is load-bearing for exactly one invariant — exactly one primary, and a primary may not be a read-only @kind. Nothing downstream reads the value: write-through codegen keys on @kind (isProjection/isWriteThrough), and across every fixture only primary and replica appear, leaving index/cache/publish/mirror with zero uses and zero readers. So removing publish is not removing a capability, it is deleting one of four dead enum members — which leaves no principle distinguishing the three that stay. Recommended: shrink to primary | replica and document the rest as reserved-not-registered, the same treatment ADR-0040 gave index.fulltext/vector/spatial. Recorded as an explicit option set rather than carried along implicitly with publish. The doc amendment (ADR-0007/0028/FR-024) lands now — ADR-0035 §1 excludes reserved-but-unregistered vocabulary from the compat surface, so no quiet-clock reset. The eventing surface rides FR-024 to 1.1; api.*, operation.* and binding.* are all unregistered today, so it is additive by construction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3msoGxjRMwx94PhKSLDuE
1 parent a052f7b commit 065acdf

1 file changed

Lines changed: 1 addition & 1 deletion

File tree

spec/roadmap.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ _(FR-032 was developed under the working number "FR-026" — see commit history;
134134
- **Group B — 1.0-freeze-window vocab decision (needs an owner ruling before the freeze):**
135135
- **#210 — [RULED 2026-08-05: DO IT, batched into the pre-1.0 consolidation, scope narrowed]** Retire the **assembly** origins (`aggregate`/`computed`/`collection`/`first`) on `object.value`, and widen `@payloadRef`/`@responseRef` to accept a **sourceless** `object.projection`. `origin.passthrough` **stays** — FR-015 parameter VOs are out of scope, because the loader already draws that line (FR-024 B5 in `validateOriginPaths`: a value's passthrough is *parameter lineage*, not an assembly path), and retiring it would silently drop the `ERR_PASSTHROUGH_TYPE_MISMATCH` check on proc args. The "total value purity" framing is dropped — ADR-0046's `@enforce: false` reference already made value rules conditional; the durable rule is **"passthrough on a value is lineage; assembly origins live on projections."** No new vocabulary (`origin.collection` is registered; a sourceless projection already loads), but `expected-registry.json` **does** change — `object.value`'s `rules` string says "by assembly" — so all five ports sync in lockstep. Timing: **not** during the Phase-1 window (the churn is in the payload tier, this repo's recent cross-port bug hotspot, and it would churn the very pillar the launch showcases); it rides the single ADR-0035 §3 breaking batch while 1.0 stays unscheduled. Blocked on two non-breaking preludes: **#270** (retire the redundant Kotlin/Python payload origin-typing — a live cross-port divergence, benign only because #185 made passthrough type-preserving — and close Java's `KNOWN_GAPS` entry as moot) and **#271** (port #248's persistability-from-source contract cross-port; prove every fan-out no-ops on a sourceless projection — four ports have never seen one, since #248 was npm-only). Reversal triggers: real adopters authoring assembly origins on payload VOs; #271 finding a port that needs new vocabulary for sourceless projections (ADR-0023 cost class ⇒ reopen); or 1.0 being scheduled imminently (then ship it and document the rule-of-thumb instead).
136136
- **Group C — post-1.0 backend generalization (design-now, build-later):**
137-
- **#212** — Events emit projections: resolve the **ADR-0007 vs ADR-0028 contradiction** (a stream is a channel, not a source). The ADR amendment (drop `source.event`; model emission at the surface as `api.eventing`/`operation.event`/`binding.messaging` with a payload→projection ref) is cheap to decide now; the eventing surface builds post-1.0. Lands the CQRS+events symmetry: queries return projections, commands take values, events emit projections.
137+
- **#212 — [RULED 2026-08-05: RATIFIED with three amendments]** Events emit projections: resolve the **ADR-0007 vs ADR-0028 contradiction** (a stream is a channel, not a source). Drop `source.event`; model emission at the surface as `api.eventing`/`operation.event`/`binding.messaging` with a payload→projection ref; keep the "a stream becomes a source exactly when it is treated as addressable state" escape clause. Lands the CQRS+events symmetry: queries return projections, commands take values, events emit projections. **Amendment 1 — the doctrine's test is single-prong.** The proposed two-prong AND (addressable *and* drift-inspectable) fails in both directions: it would KEEP a schema-registry-backed Kafka topic and KILL `source.memory`, and it wrongly maims `document`/`keyValue`/`objectStore`/`timeSeries`/`graph`. Ratified wording: a `source.*` binds an object to **addressable state at rest** — readable on demand by a declared key or address, not merely observed in flight; **drift-inspectability is a per-paradigm `verify` capability, never an admission criterion**. Only `event`'s flow kinds (topic/stream) fall; `eventStore`/`changelog` are the escape clause; `vector` and `search` survive (both have fetch-by-id). **Amendment 2 — removing `@role: publish` is NOT docs-only**: it is live registered vocabulary (`expected-registry.json`, `spec/metamodel/db.json`, five ports' embedded definitions), so it rides the #210 consolidation batch. **Amendment 3** — FR-024 §7's channel row references a **projection**, not a value. **Open sub-decision (needs a ruling):** `@role` is load-bearing for exactly one invariant (exactly-one-`primary`, and a `primary` may not be a read-only `@kind`); nothing downstream reads the *value* — write-through codegen keys on `@kind` (`isProjection`/`isWriteThrough`), and only `primary`/`replica` appear in any fixture, leaving `index`/`cache`/`publish`/`mirror` with zero uses and zero readers. Options: (A, recommended) shrink to `primary | replica` and document the rest as reserved-not-registered per the ADR-0040 precedent; (B) remove only `publish`; (C) keep all six and document that only `primary` is read. Splits: the ADR-0007/0028/FR-024 doc amendment lands **now** (ADR-0035 §1 excludes reserved-but-unregistered from the compat surface, so no quiet-clock reset); the `@role` registry change rides the #210 batch; the eventing surface rides FR-024 to 1.1 (`api.*`/`operation.*`/`binding.*` are all unregistered today, so it is additive by construction).
138138
- **#211****Backend-agnostic projection materialization** (one derivation spec, N lowerings): generalize `SelectSpec` from RDB-only to document/search/… with an origin × (paradigm, `@kind`) capability matrix + load-time errors. Large FR; its foundation is Group A landing with the "semantic, not RDB-specific" lens (#207/#208/#209 become the first lowerings). Post-1.0; composes with FR-034 (ecosystem tier).
139139
- **FR-019 — Shared + externally-provided enums.** Stop redeclaring a `field.enum` inline in every consuming entity: a package-level abstract `field.enum` materializes ONE standalone enum type per port (the existing D6 `extends` reuse vocabulary), and **`@provided: true`** — a provenance flag on the named-type *declaration* (not the field), shared cross-type with value objects — references an existing hand-written type instead of emitting one (per-port namespace via codegen config, never a metadata FQN — retires the C#-only `@csEnumType`). Decision in [ADR-0026](decisions/ADR-0026-shared-and-provided-named-types.md); implementation spec `docs/superpowers/specs/2026-06-06-fr-019-shared-and-provided-enums-design.md`. (Generators are now subclass-extensible across all 5 ports, so this lands on open seams.)
140140
- **FR-020 — `@inheritance` persistence strategy (single-table vs joined).** TPH (one physical table, nullable subtype columns) is the single implicit strategy today (FR-014/FR-017). FR-020 adds an `@inheritance` attr to choose **joined** (a base table + per-subtype tables, joined on read) as an alternative — **additive, default stays TPH** (no breaking change). Status: design (proposed, sequenced after FR-017, which shipped). Design: `docs/superpowers/specs/2026-06-07-fr-020-inheritance-strategy-design.md`. *(Was not previously surfaced in this roadmap.)*

0 commit comments

Comments
 (0)