@ai-matrx/content-ir 0.2.0 → 0.2.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.2 — 2026-08-24
4
+
5
+ - `rehydrateRunResult` now rejects nested entries whose `__kind` is not
6
+ `node_outcome`, even when the object carries plausible run/node identity.
7
+ This keeps a malformed sibling from masquerading as a valid terminal result.
8
+ - Workflow Studio now delegates runtime-wrapper field reading and output-ref
9
+ rehydration to the package APIs; its host adapter retains only deferred,
10
+ persistent diagnostics for present-but-broken wrappers.
11
+
12
+ ## 0.2.1 — 2026-08-24
13
+
14
+ - **FIX — `validateStructuralLeg` no longer rejects every current kind
15
+ instance.** The leg validated ONLY the `__kind`-stripped copy of the sample,
16
+ a leftover from the dead "marker is envelope framing" doctrine. Since the
17
+ 2026-08-23 ruling every `emitted_json_schema` DECLARES `__kind` (const +
18
+ required), so stripping turned a valid instance into
19
+ `(root) must have required property '__kind'` — 503 of the 842 live kind
20
+ definitions, i.e. every marker-declaring kind, failed the Shape Studio Test
21
+ tab, the gate tab, the example manager, and instance save. The leg now
22
+ validates the caller's value VERBATIM and only retries the marker-free
23
+ reduction if that fails (backward compatibility for a row still pinned to a
24
+ pre-2026-08-23 marker-free schema), and reports the errors from whichever
25
+ attempt matches the schema in hand.
26
+ - `JsonTokenizer` and `KindStreamParser` declare explicit fields instead of
27
+ constructor parameter properties. Consumers compile this SOURCE directly, and
28
+ a strict host (`apps/dashboard`) sets `erasableSyntaxOnly`, under which
29
+ parameter properties are a hard error — the flag had to be turned off in
30
+ Workflow Studio for exactly this reason, and is now back on. Behaviour is
31
+ unchanged; the published 0.2.0 tarball does not carry this edit.
32
+
33
+
3
34
  ## 0.2.0 — 2026-08-23
4
35
 
5
36
  - Added the `wire/` layer — the server→UI contracts every UI reads the same way:
package/dist/index.cjs CHANGED
@@ -404,16 +404,19 @@ var IrTree = class {
404
404
 
405
405
  // core/json-tokenizer.ts
406
406
  var JsonStreamTokenizer = class {
407
- constructor(onToken) {
408
- this.onToken = onToken;
409
- }
410
- onToken;
411
407
  mode = "normal";
412
408
  pos = 0;
413
409
  stringBuffer = "";
414
410
  primitiveBuffer = "";
415
411
  unicodeBuffer = "";
416
412
  tokenStart = 0;
413
+ onToken;
414
+ // Explicit field rather than a parameter property: consumers compile this
415
+ // source directly, and a strict host (the dashboard) sets
416
+ // `erasableSyntaxOnly`, under which parameter properties are a hard error.
417
+ constructor(onToken) {
418
+ this.onToken = onToken;
419
+ }
417
420
  get position() {
418
421
  return this.pos;
419
422
  }
@@ -636,16 +639,6 @@ function safeCopy(value) {
636
639
  }
637
640
  }
638
641
  var KindStreamParser = class {
639
- constructor(options) {
640
- this.options = options;
641
- this.resolver = isSchemaResolver(options.schemas) ? options.schemas : {
642
- get: (kind) => options.schemas[kind]
643
- };
644
- this.tokenizer = new JsonStreamTokenizer(
645
- (token) => this.handleToken(token)
646
- );
647
- }
648
- options;
649
642
  resolver;
650
643
  stack = [];
651
644
  objectKinds = /* @__PURE__ */ new Map();
@@ -680,6 +673,18 @@ var KindStreamParser = class {
680
673
  rootKind = "";
681
674
  rootDone = false;
682
675
  failed = false;
676
+ options;
677
+ // Explicit field, not a parameter property — see the note in
678
+ // `json-tokenizer.ts`: `erasableSyntaxOnly` hosts compile this source.
679
+ constructor(options) {
680
+ this.options = options;
681
+ this.resolver = isSchemaResolver(options.schemas) ? options.schemas : {
682
+ get: (kind) => options.schemas[kind]
683
+ };
684
+ this.tokenizer = new JsonStreamTokenizer(
685
+ (token) => this.handleToken(token)
686
+ );
687
+ }
683
688
  push(chunk) {
684
689
  if (this.failed || this.rootDone) return;
685
690
  try {
@@ -2773,6 +2778,12 @@ function storageToKindSchema(kind, shape) {
2773
2778
  }
2774
2779
  return { kind, fields };
2775
2780
  }
2781
+ var GENERIC_FALLBACK_COMPONENT_KEY = "generic_structured";
2782
+ function satisfies(resolved) {
2783
+ return Boolean(
2784
+ resolved?.isActive && resolved.componentKey !== GENERIC_FALLBACK_COMPONENT_KEY
2785
+ );
2786
+ }
2776
2787
  var ajv = new Ajv__default.default({ allErrors: true, strict: false });
2777
2788
  function stripKind(value) {
2778
2789
  if (Array.isArray(value)) return value.map(stripKind);
@@ -2786,6 +2797,14 @@ function stripKind(value) {
2786
2797
  }
2787
2798
  return value;
2788
2799
  }
2800
+ function describeAjvErrors(errors) {
2801
+ return (errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message ?? ""}`.trim()).slice(0, 8);
2802
+ }
2803
+ function schemaDeclaresKind(emittedJsonSchema) {
2804
+ if (!emittedJsonSchema || typeof emittedJsonSchema !== "object") return false;
2805
+ const properties = emittedJsonSchema.properties;
2806
+ return !!properties && typeof properties === "object" && KIND_KEY in properties;
2807
+ }
2789
2808
  function validateStructuralLeg(sample, emittedJsonSchema) {
2790
2809
  let validate;
2791
2810
  try {
@@ -2796,9 +2815,11 @@ function validateStructuralLeg(sample, emittedJsonSchema) {
2796
2815
  detail: `emitted_json_schema failed to compile: ${err instanceof Error ? err.message : String(err)}`
2797
2816
  };
2798
2817
  }
2799
- const ok = validate(stripKind(sample));
2800
- if (ok) return { ok: true };
2801
- const errors = (validate.errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message ?? ""}`.trim()).slice(0, 8);
2818
+ if (validate(sample)) return { ok: true };
2819
+ const markedErrors = describeAjvErrors(validate.errors);
2820
+ if (validate(stripKind(sample))) return { ok: true };
2821
+ const strippedErrors = describeAjvErrors(validate.errors);
2822
+ const errors = schemaDeclaresKind(emittedJsonSchema) ? markedErrors : strippedErrors;
2802
2823
  return { ok: false, detail: `sample failed schema: ${errors.join("; ")}` };
2803
2824
  }
2804
2825
  var SERVER_DATA_ANNOTATION_KEYS = /* @__PURE__ */ new Set(["language"]);
@@ -2848,16 +2869,18 @@ function validateRender(kind, sample, definition, resolvedComponent, dataOnly) {
2848
2869
  detail: "data-only contract kind \u2014 render leg is structurally inapplicable (n/a)"
2849
2870
  };
2850
2871
  }
2851
- if (!definition && !resolvedComponent?.isActive) {
2872
+ const onlyFallback = resolvedComponent?.isActive && resolvedComponent.componentKey === GENERIC_FALLBACK_COMPONENT_KEY;
2873
+ const noComponentDetail = onlyFallback ? `the only active role='output' component for kind "${kind}" is '${GENERIC_FALLBACK_COMPONENT_KEY}' \u2014 that IS the generic viewer, i.e. no component. A reader would get a key/value dump. Author a real source='db' component (or register a compiled one), then retire the generic row.` : null;
2874
+ if (!definition && !satisfies(resolvedComponent)) {
2852
2875
  return {
2853
2876
  ok: false,
2854
- detail: `kind "${kind}" has no component (not in the compiled registry, and no active role='output' kind_component row) \u2014 nothing to render`
2877
+ detail: noComponentDetail ?? `kind "${kind}" has no component (not in the compiled registry, and no active role='output' kind_component row) \u2014 nothing to render`
2855
2878
  };
2856
2879
  }
2857
- if (definition && !definition.legacyBlockType && !definition.component && !definition.toLegacyServerData && !resolvedComponent?.isActive) {
2880
+ if (definition && !definition.legacyBlockType && !definition.component && !definition.toLegacyServerData && !satisfies(resolvedComponent)) {
2858
2881
  return {
2859
2882
  ok: false,
2860
- detail: `kind "${kind}" has no component (no compiled legacyBlockType/component facet, and no active role='output' kind_component row) \u2014 nothing to render`
2883
+ detail: noComponentDetail ?? `kind "${kind}" has no component (no compiled legacyBlockType/component facet, and no active role='output' kind_component row) \u2014 nothing to render`
2861
2884
  };
2862
2885
  }
2863
2886
  if (definition?.toLegacyServerData) {
@@ -2881,7 +2904,7 @@ function validateRender(kind, sample, definition, resolvedComponent, dataOnly) {
2881
2904
  }
2882
2905
  return { ok: true };
2883
2906
  }
2884
- const satisfier = definition?.legacyBlockType ? `compiled component "${definition.legacyBlockType}"` : definition?.component ? "compiled component facet" : resolvedComponent ? `resolved ${resolvedComponent.source} component "${resolvedComponent.componentKey}"` : "component";
2907
+ const satisfier = definition?.legacyBlockType ? `compiled component "${definition.legacyBlockType}"` : definition?.component ? "compiled component facet" : satisfies(resolvedComponent) && resolvedComponent ? `resolved ${resolvedComponent.source} component "${resolvedComponent.componentKey}"` : "component";
2885
2908
  return {
2886
2909
  ok: true,
2887
2910
  detail: `bridgeless kind \u2014 ${satisfier} parses content itself; full DOM render check deferred to an RTL harness`
@@ -4353,7 +4376,7 @@ function rehydrateRunResult(raw, frame) {
4353
4376
  if (raw[KIND_KEY] !== RUN_RESULT_KIND) return null;
4354
4377
  const ref = str(raw, "output_ref");
4355
4378
  const resolved = ref === null ? void 0 : readOutputRef(frame, ref);
4356
- const outputs = Array.isArray(raw.outputs) ? raw.outputs.map((child) => rehydrateNodeOutcome(child, frame) ?? child) : [];
4379
+ const outputs = Array.isArray(raw.outputs) ? raw.outputs.map((child) => rehydrateNodeOutcome(child, frame)).filter((child) => child !== null) : [];
4357
4380
  return readRunResultValue({
4358
4381
  ...raw,
4359
4382
  ...resolved === void 0 ? {} : { output: resolved },