Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
7aff995
spec: metamodel self-description for LLMs (design)
dmealing Jun 13, 2026
50b17ef
spec: revise — provider definitions as declarative data files (+ meta…
dmealing Jun 13, 2026
34cec5e
roadmap: register FR-033 (provider definitions as declarative data + …
dmealing Jun 13, 2026
803d9dd
plan: FR-033 — provider definitions as declarative data + metamodel docs
dmealing Jun 13, 2026
7319a76
spec+plan(fr-033): the constraint model (typesConfig recovered)
dmealing Jun 13, 2026
9859288
feat(fr-033): provider-data format + defineProviderFromData (TS found…
dmealing Jun 13, 2026
ee63d8b
feat(fr-033): constraint engine — additive merge + contradiction vali…
dmealing Jun 13, 2026
a72f8bb
feat(fr-033): field provider reads spec/metamodel/field.json (embedded)
dmealing Jun 13, 2026
2387ea1
feat(fr-033): registry manifest emits descriptions + constraint graph…
dmealing Jun 13, 2026
b1b0870
Merge remote-tracking branch 'origin/main'
dmealing Jun 13, 2026
e3e33c4
feat(fr-033): attr provider reads spec/metamodel/attr.json (embedded)
dmealing Jun 13, 2026
58c0eda
feat(fr-033): validator provider reads spec/metamodel/validator.json …
dmealing Jun 14, 2026
3eb610d
feat(fr-033): identity provider reads spec/metamodel/identity.json (e…
dmealing Jun 14, 2026
68b7607
feat(fr-033): relationship provider reads spec/metamodel/relationship…
dmealing Jun 14, 2026
a4caa31
feat(fr-033): object provider reads spec/metamodel/object.json (embed…
dmealing Jun 14, 2026
bed24df
feat(fr-033): origin provider reads spec/metamodel/origin.json (embed…
dmealing Jun 14, 2026
c7f0291
feat(fr-033): source provider reads spec/metamodel/source.json (embed…
dmealing Jun 14, 2026
6fb2e11
feat(fr-033): layout provider reads spec/metamodel/layout.json (embed…
dmealing Jun 14, 2026
c2424a8
feat(fr-033): view provider reads spec/metamodel/view.json (embedded)
dmealing Jun 14, 2026
d3f5d1a
feat(fr-033): template provider reads spec/metamodel/template.json (e…
dmealing Jun 14, 2026
e08218d
feat(fr-033): documentation provider reads spec/metamodel/documentati…
dmealing Jun 14, 2026
7ef9ff7
plan(fr-033): strict per-subtype constraint model + 5 concern-provide…
dmealing Jun 14, 2026
cb37224
feat(fr-033): S0 foundation — unified apply path + universal *.* comm…
dmealing Jun 14, 2026
c43f239
feat(fr-033): S1-field-A — re-home field attrs to concern providers (…
dmealing Jun 14, 2026
7712e5b
feat(fr-033): S1-field-B — strict per-subtype scoping of field
dmealing Jun 14, 2026
680b8bf
feat(fr-033): S1-simple — drop the any-attr wildcard from attr-only p…
dmealing Jun 14, 2026
b7fb2d1
feat(fr-033): S1-object — strict per-subtype scoping of object (last …
dmealing Jun 14, 2026
1d7d4c8
feat(fr-033): S1.5-A — data-driven extend capability + db provider re…
dmealing Jun 14, 2026
df20611
feat(fr-033): S1.5-B — ui + prompt providers read spec/metamodel/{ui,…
dmealing Jun 14, 2026
1dd63c6
feat(fr-033): S2 — re-home view/layout/template type attrs to ui/prom…
dmealing Jun 14, 2026
456b4ce
feat(fr-033): S3 — metamodel doc-gen (INDEX + per-type pages + provid…
dmealing Jun 14, 2026
b7f78d3
fix(fr-033): S3 — providers.md honors the same type exclusions as IND…
dmealing Jun 14, 2026
44529c4
plan(fr-033): Phase 4 cross-port fan-out — gap analysis + per-port re…
dmealing Jun 14, 2026
ace7949
feat(fr-033): Java port S-A — registry-manifest emitter shape + model…
dmealing Jun 14, 2026
cab4f83
feat(fr-033): Java port S-B1 — embed spec/metamodel JSON + reader + d…
dmealing Jun 14, 2026
49d5776
feat(fr-033): Java port S-B2a — strict children graph + cardinality f…
dmealing Jun 14, 2026
0d3af6c
feat(fr-033): Java port S-B2b — strict attr scoping + metadata.root →…
dmealing Jun 14, 2026
4d538ee
feat(fr-033): Python port S-A — registry-manifest emitter shape + mod…
dmealing Jun 14, 2026
34d3fc3
feat(fr-033): Python port S-B1 — embed spec/metamodel JSON + reader +…
dmealing Jun 14, 2026
84ab765
feat(fr-033): Python port S-B2 — strict children/scoping + metadata.r…
dmealing Jun 14, 2026
120a3d3
feat(fr-033): Python — metaobjects-ui/prompt providers (re-home to ma…
dmealing Jun 14, 2026
1d47a5f
feat(fr-033): Java — re-home ui/prompt attrs into metaobjects-ui/prom…
dmealing Jun 14, 2026
96f235e
feat(fr-033): C# port S-A — registry-manifest emitter shape + model s…
dmealing Jun 14, 2026
15d9c8a
feat(fr-033): C# port S-B1 — embed spec/metamodel JSON + reader + des…
dmealing Jun 14, 2026
19fce8b
feat(fr-033): C# port S-B2 — strict children/scoping + metadata.root …
dmealing Jun 14, 2026
8cee363
feat(fr-033): C# — re-home ui/prompt attrs into metaobjects-ui/prompt…
dmealing Jun 14, 2026
9b204a7
docs(fr-033): C# — fix 2 stale comments naming the deleted TemplateTy…
dmealing Jun 14, 2026
a70081c
test(fr-033): Java — gate structural-child placement enforcement
dmealing Jun 14, 2026
38f5226
feat(fr-033): Python — wire+gate structural-child placement enforcement
dmealing Jun 14, 2026
a74e189
feat(fr-033): C# — wire + gate structural-child placement enforcement
dmealing Jun 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

92 changes: 92 additions & 0 deletions docs/superpowers/plans/2026-06-14-fr-033-cross-port-fan-out.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# FR-033 Phase 4 — cross-port fan-out (Java → C# → Python; Kotlin via JVM)

_Status: PLANNED 2026-06-14. Follows the TS strict re-architecture (S0–S3, branch
`fr-033-strict-constraint-model`). Makes the 4 non-TS ports' `registry-conformance`
go RED→green against the strict, description-carrying, constraint-graph
`fixtures/registry-conformance/expected-registry.json`._

## The oracle rule (cross-language-porting skill)
`fixtures/registry-conformance/expected-registry.json` is the (correct, TS-produced)
golden. Port each language to MATCH it. NEVER edit the golden to match a port. If a
fixture looks wrong, escalate — don't silently regenerate. Update the
expected-failures ledger honestly for what isn't done.

## Gap analysis (measured 2026-06-14)
The golden grew (TS S0–S3) to carry, per `type.subType`: `description`, optional
`rules`/`example`/`whenToUse`, the `children` constraint graph (childRules with
cardinality, the `(attr,*,*)` wildcard REMOVED), `parents?`, and **strict
per-subtype attr scoping** (e.g. `@maxLength` only on `field.string`,
`@storage` only on `field.object`, `@autoSet` only on temporal fields,
`@discriminator` only on `object.entity`). Per attr: `description` (+ optional
rules/example/whenToUse) alongside `name`/`valueType`/`isArray`/`required`.

Each non-TS port currently emits only the **v1 manifest** (`types[{type, subType,
attrs[{name, valueType, isArray, required}]}]` + `commonAttrs` + `defaultSubTypes`)
and explicitly EXCLUDES description / children / rules. So all four are RED at the
first type. (Confirmed for Java: `RegistryManifestConformanceTest` fails at
`type: "base"`.)

**Two load-bearing facts that shrink the work:**
1. **The manifest is provider-agnostic.** It emits the COMPOSED per-type view with
NO `provider` field (provenance is derived at TS doc-gen time, not in the
manifest). So the **5-concern provider split is an internal Tier-3 detail** — a
port does NOT need the same provider structure, only the same composed
per-`type.subType` `{description, attrs, children, parents, rules}`.
2. **Descriptions must be byte-identical to TS.** Hand-maintaining identical prose
across ports is the exact duplication FR-033 kills. So the correct port reads the
**shared `spec/metamodel/*.json`** (embedded) for descriptions + the strict
constraint graph — single-sourced, byte-identical by construction. (Java's
`TypeDefinition` already has a `description` slot; its `ChildRequirement` likely
needs a per-attr `description` added. Verify per port.)

## Per-port recipe
For each of Java, C#, Python (Kotlin inherits via the shared JVM `metadata` module):
1. **Embed the shared definitions.** Bundle the repo-root `spec/metamodel/*.json`
(the 15 provider definition files) as build-time resources / embedded constants
(the port's existing asset-embed pattern). A byte-identity gate per port.
2. **Read them at registration.** A port-local `defineProviderFromData` equivalent:
parse the embedded JSON, register each `type.subType` with its strict
constraints + `description` (+ rules/example/whenToUse) + the `children` graph
(named attrs → the port's attr-requirement; structural entries → child rules;
`extendsBase` → additive inheritance; universal `*.*` → common attrs;
`extends` directives → the port's "extend an existing type" path). Keep the
port's factories / native bindings / validation passes as code (Tier 2/3).
- **Strict scoping reconciliation:** the registered per-subtype attr set must
equal TS's (move attrs off subtypes where TS scoped them away). Java already
has a per-subtype `ChildRequirement` model (pre-6.0 typesConfig) — reconcile it
to the JSON, don't reinvent.
- **Drop the any-attr wildcard** from the registered childRules (strict).
3. **Grow the manifest emitter** to output, in the TS key order: per type
`description`, optional `rules`/`example`/`whenToUse`, `attrs` (each now with
`description` + optional doc fields), `children` (the constraint graph: childType,
childSubType [value|list|`*`], childName, `min?`/`max?` emitted only when defined,
`named?`), `parents?` (sorted). Match `server/typescript/packages/metadata/src/
registry-manifest.ts` exactly (key order, sorting, `null` for polymorphic
valueType, single trailing newline).
4. **Run `registry-conformance`** → byte-match the golden. Iterate the diff.
Per-port: Java `mvn -q -pl metadata test -Dtest=RegistryManifestConformanceTest -o`;
C# `dotnet test MetaObjects.Conformance.Tests/...`; Python `uv run pytest tests/conformance/`.
Kotlin's `codegen-kotlin` RegistryManifestConformanceTest uses the shared JVM
`metadata` registry — it goes green when Java does (re-verify it after Java).
5. **Enforce placement in validate().** Port the `ERR_CHILD_NOT_ALLOWED`
structural-child check (Java already has `ValidationPhase` + `acceptsChild`;
wire the strict check). Misplaced attr → `ERR_UNKNOWN_ATTR` (the ports likely
already enforce per-type attrs). Fix any port-local fixture that placed an attr
on a now-disallowed subtype (fix the fixture, not the model — and confirm it's a
genuine misplacement first).
6. **Expected-failures ledger:** while a port is mid-port (RED), record it honestly.
The conformance byte-match is all-or-nothing, so intermediate commits stay RED
until that port completes — expected.

## Sequencing
**Java first** (the pre-6.0 strict typesConfig is the closest existing model; Kotlin
rides its `metadata` module → mostly free, re-verify `codegen-kotlin`), **then C#,
then Python** (mirrors the FR-032 fan-out). Each port is its own multi-step effort
(embed + reader + emitter + scoping reconcile + enforce) — comparable to a slice of
the TS arc. Budget a focused session per port; the byte-match diff is the guide.

## Scale note
This is the largest remaining FR-033 phase — effectively porting the strict model +
description/constraint-graph emission into 3 engines (Java/C#/Python; Kotlin free).
Do it port-by-port with the subagent-driven review discipline; keep the TS reference
(`registry-manifest.ts`, `provider-data.ts`, `constraint-merge.ts`) as the spec.
112 changes: 112 additions & 0 deletions docs/superpowers/plans/2026-06-14-fr-033-strict-constraint-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# FR-033 (revised) — Strict per-subtype constraint model + concern-provider split

_Status: DESIGNED (brainstormed + approved 2026-06-14). Revises the FR-033 plan
(`2026-06-13-fr-033-provider-definitions-as-data.md`) and spec
(`docs/superpowers/specs/2026-06-13-metamodel-self-description-design.md`)._

## Why this revision

The first Phase-2 pass (commits `a72f8bb2`..`e08218db`) made each provider's
metamodel definition *data*, but in a **loose** shape:

- every field attr was lumped into the **core** provider (`@maxLength`/`@objectRef`/
`@filterable`/`@example`/… all on *every* field subtype);
- the wildcard **"any attr"** child rule (`{type:"attr", subType:"*", name:"*"}`)
was registered on every type — a catch-all that accepts a misplaced/typo'd attr;
- structural child rules were **post-assigned in provider code**, not in the data;
- `documentation` got a **bespoke** `commonAttrs` field + `defineCommonAttrsFromData`.

This is the looseness of the post-6.0 Java model. We are moving to the **pre-6.0
strict** model: each type/subtype declares **exactly** which children it allows
(by type + subtype + name + cardinality), it is **fail-closed**, and the *checking*
runs in `validate()`. Goal triad: **strict** (kills drift) + **documentable** (the
rules ARE the docs) + **extensible** (concerns ride their own providers; additive
merge only — a provider/app can ADD rules, never loosen).

The committed data files + descriptions are **reusable**; the structure is reworked.

## Decisions

1. **Strict per-subtype rules.** Each `type.subType` declares its allowed children
explicitly. The **"any attr" wildcard is removed**. Genuinely-open *structural*
sets stay wildcard (an `object.entity` holds arbitrary `field.*`; a field takes
any `validator.*`/`view.*`/`origin.*`). Cardinality is pinned where there's
structure (`identity.primary` max 1; an object's primary `source` exactly 1).
2. **Attrs are named `attr` children.** A child rule with a concrete name = a
specific allowed attr (→ `AttrSchema`); there is no wildcard-attr rule anymore.
3. **Common attrs = attr-children of a universal `*.*` entry** in the data, routed
to the existing common-attr registration by the **one** apply path. Deletes the
bespoke `commonAttrs` field + `defineCommonAttrsFromData`.
4. **All child rules live in the JSON, none in provider code.** Shared rules declared
on `*.base` and inherited via the existing `constraint-merge.ts`; subtype
specializations on that subtype. The manifest emits **effective (merged)**
constraints so inheritance is DRY and the canonical does not churn for inherited
rules.
5. **Enforce in `validate()` now.** A child (attr or structural) not admitted by the
merged rules → load-time error (new code, e.g. `ERR_CHILD_NOT_ALLOWED`). Data stays
declarative; the strict checking is code — the pre-6.0 Java split. Complex
cross-node rules (M:N junction, `@symmetric` self-join, origin path) stay in the
existing loader validation passes.
6. **Concern-provider split (5).** Only **documentation** is universal (`*.*`); every
other concern attaches to the specific nodes it is about (DB/XML/etc. are NOT
universal).

## The 5 concern providers + attr ownership

| Provider | Concern | Owns | Scope |
|---|---|---|---|
| `metaobjects-core-types` *(exists)* | metamodel spine + intrinsic logical shape | type/subtype vocabulary; fields `@default`/`@readOnly`/`@required`, `@maxLength`→string, `@precision`/`@scale`→decimal, `@currency`→currency, `@objectRef`→object, `@values`/`@provided`→enum; objects `@discriminator`/`@discriminatorValue`; identity/relationship/origin structure; `source.rdb` subtype | per node type/subtype |
| `metaobjects-db` *(exists, gains)* | physical storage + DB constraints | fields `@column`/`@db.indexed`/`@dbColumnType` (today) **+ `@storage`→object, `@autoSet`→temporal, `@unique`**; sources `@table`/`@kind`/`@role`/`@schema` | fields + sources (NOT universal) |
| `metaobjects-documentation` *(exists)* | documentation | `@description`/`@title`/`@notes`/`@deprecated`/`@replacedBy`/`@seeAlso`/`@aliases`/`@summary` | **universal `*.*`** |
| `metaobjects-prompt` *(expand `templateProvider`)* | AI + serialization (FR-004/006/010/011) | `template.prompt`/`output`/`toolcall` **type attrs** (`@payloadRef`/`@textRef`/`@format`/`@kind`/…) **moved out of core**; field teaching/extract attrs `@example`/`@instruction`/`@enumAlias`/`@enumDoc`/`@coerceDefault`/`@normalize`/`@xmlText` | fields + `template.*` (NOT universal) |
| `metaobjects-ui` *(new)* | presentation + query surface | fields `@filterable`/`@sortable`/`@sortableDefaultOrder`; `view.*` + `layout.dataGrid` **type attrs** (`@locale`/`@pageSize`/`@columns`/…) **moved out of core** | fields + view/layout (NOT universal) |

Open per-attr calls confirmed: `@autoSet` → date/time/timestamp (all temporal);
`@maxLength` → `field.string` only. `@normalize` lives on `field.enum` (prompt
provider) AND remains the `object.value` default (prompt provider extends
`object.value`).

## Mechanics (foundation changes)

- **`provider-data.ts`**: one apply path. Route a `type:"attr"` child to an
`AttrSchema` (named) and DROP the wildcard-attr concept; recognize the universal
`type:"*", subType:"*"` TypeDef and register its attr children via
`registerCommonAttrs`. Remove `commonAttrs` field + `defineCommonAttrsFromData`.
- **manifest emitter** (`registry-manifest.ts`): emit **effective** (merged via
`mergeConstraints`) children/attrs per `type.subType` so base-inherited rules show
on subtypes — keeping the canonical byte-stable while the JSON is DRY. Common
attrs stay in the `commonAttrs` block (NOT folded into every type's children).
- **`validate()` enforcement**: a placement check against the merged effective
rules; misplaced/unknown child → new error code. Wire into the loader validation
phase. Expect to surface + fix existing corpus violations (fail-closed is stricter).

## Implementation sequence (provider-by-provider, reviewed)

- **Phase S0 — foundation.** provider-data routing + universal `*.*` + effective-
constraint manifest + the `validate()` placement check (enforcement). Prove on a
trivial provider; keep the canonical byte-stable for unchanged placements.
- **Phase S1 — re-scope core providers strictly.** `field` first (densest; bring the
per-subtype placement table back for review), then object/attr/validator/identity/
relationship/source/origin — move shared rules to `*.base`, scope subtype attrs,
pin cardinalities, drop "any attr". Each its own commit + review; regen canonical.
- **Phase S2 — re-home attrs to the 5 providers.** Expand `templateProvider` →
`metaobjects-prompt` (template type attrs + field teaching/extract attrs +
`@xmlText`); create `metaobjects-ui` (view/layout type attrs + filter/sort field
attrs); move `@storage`/`@autoSet`/`@unique` to `metaobjects-db`. Update
`coreProviders`. registry-conformance ownership reconciled.
- **Phase S3 — documentation as universal `*.*`.** Replace the bespoke path; canonical
`commonAttrs` block unchanged.
- **Phase S4 — corpus + enforcement green.** Fix any fixture that placed an attr on a
now-disallowed subtype; full TS suite + registry-conformance green.
- **Phase S5 — docs-gen + cross-port (Phase 4).** Tiered metamodel docs from the
strict graph; fan out the read-the-file + strict model to Java/C#/Python/Kotlin
(Java already has the pre-6.0 model to mirror).

## Invariants throughout

- TS reference goes green at each step; the other four ports stay RED on
registry-conformance until Phase S5 (documented intermediate state).
- The data files own declarative facts + rule prose; factories, the apply mechanism,
validation passes, and codegen stay code (ADR-0023 strict provenance preserved;
no DSL).
- Commit to local `main`; per-provider implementer + two-stage review.
Loading
Loading