@githolon/dsl 0.80.0 → 0.80.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.80.0",
3
+ "version": "0.80.1",
4
4
  "type": "module",
5
5
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -197,12 +197,23 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
197
197
  const byId = new Map<string, AggregateHandle>();
198
198
  for (const a of [...aggregatesOf(merged), ...(spec.extraAggregates ?? [])]) byId.set(a.id, a);
199
199
  const directives = directivesOf(merged);
200
- // RELATIONS AUTO-ENSURE: a directive that `.ensures(RelationTuple)` (the relate-family shape — its plan
201
- // `writeTuple`s) needs the framework RelationTuple aggregate IN the schema, or the deploy gate never
202
- // learns the type and the tuple write silently fails admission. Inject it so the dev needn't re-export
203
- // the framework handle — the same "discoverable without re-exports" courtesy a `capability()` bundle
204
- // gets. Era-safe: only a domain that authors tuples gains the aggregate, so no existing manifest hash drifts.
205
- if (!byId.has(RelationTuple.id) && directives.some((d) => d.aggregateId === RelationTuple.id)) {
200
+ // RELATIONS AUTO-ENSURE + AUTO-COMPOSE (co2 recovery DX): a domain that USES relation law — declares a
201
+ // relation schema, a `.requires(...)`-gated directive, or relate-family/signer directives ensuring
202
+ // RelationTuple — needs the framework RelationTuple aggregate IN the schema on TWO counts: (1) the deploy
203
+ // gate must learn the type or a `writeTuple` silently fails admission, and (2) the auto-composed
204
+ // `relationTuplesByObject` query (appended below) `.returns(RelationTuple)`, so the read projection
205
+ // (`readProjectionForModule`/`readKeyFieldKind`) cannot resolve its return schema unless RelationTuple is
206
+ // one of the module's aggregates. Injecting it HERE (before the aggregate snapshot) makes auto-compose
207
+ // BYTE-EQUIVALENT to a tenant that explicitly `export { relationTuplesByObject, RelationTuple }` — the
208
+ // same aggregate + query enter the manifest, and the USD prim emission sorts aggregates by id, so
209
+ // insertion order does not perturb the hash. Era-safe: only a relation-using domain gains the aggregate,
210
+ // so no existing (relation-free) manifest hash drifts, and a tenant that already had the explicit export
211
+ // sees NO further move (identical bytes).
212
+ const usesRelations =
213
+ (spec.relationTypes ?? []).length > 0 ||
214
+ directives.some((d) => (d as { requiresRelationGate?: unknown }).requiresRelationGate !== undefined) ||
215
+ directives.some((d) => d.aggregateId === RelationTuple.id);
216
+ if (usesRelations && !byId.has(RelationTuple.id)) {
206
217
  byId.set(RelationTuple.id, RelationTuple);
207
218
  }
208
219
  // BIRTH-METER AUTO-ENSURE (metered birth, slice A — the same "discoverable without re-exports"
@@ -285,10 +296,9 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
285
296
  // workspace running the law — no tenant re-export to forget. ERA/OMIT RULE: a relation-free domain
286
297
  // appends nothing (byte-identical module/package); a relation-using one gains the query on its NEXT
287
298
  // recompile (a named, additive hash move the #58 evolve gate admits silently).
288
- const usesRelations =
289
- relationTypes.length > 0 ||
290
- directives.some((d) => (d as { requiresRelationGate?: unknown }).requiresRelationGate !== undefined) ||
291
- directives.some((d) => d.aggregateId === RelationTuple.id);
299
+ // `usesRelations` was computed above the aggregate snapshot (so RelationTuple could be injected into the
300
+ // schema); reuse it here to append the audit query. The two MUST agree — the aggregate and the query it
301
+ // returns have to enter the manifest together, or the read projection can't resolve the return schema.
292
302
  if (usesRelations && !queries.some((q) => q.id === relationTuplesByObject.id)) {
293
303
  queries.push(relationTuplesByObject);
294
304
  }