@ai-matrx/content-ir 0.7.0 → 0.9.0

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,46 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.0 — 2026-08-29
4
+
5
+ - **A nested `__kind` object now registers the CHILD's schema, not just a ref
6
+ to it.** Arrays of `__kind` items have always drafted their item kind; an
7
+ object-VALUED field declaring a `__kind` returned
8
+ `{type:"object", kind}` and dropped the child's fields on the floor.
9
+
10
+ The consequence was not a missing child — it was a broken PARENT. The parser
11
+ had a field predicting `mx_child` with no schema registered for it, so the
12
+ child degraded, the parent failed validation, and the parent fell to
13
+ `broken-instance` and lost its component over a child the very same schema
14
+ had fully described one line above.
15
+
16
+ Found by matrx-frontend's new render matrix (every recognition path × every
17
+ shape archetype) on its first run.
18
+
19
+ ## 0.8.0 — 2026-08-29
20
+
21
+ - **A field is never DROPPED — it widens to `json`.** Every field-map loop in
22
+ the JSON Schema → KindSchema direction now goes through
23
+ `convertFieldOrWiden`. A construct the vocabulary cannot name precisely — a
24
+ 2-D array (`rows: [[...]]`), an array of untyped objects, a mixed `anyOf`, an
25
+ unresolvable `$ref` — becomes `json` with a warning instead of disappearing
26
+ from the kind.
27
+
28
+ WHY THIS IS NOT A DETAIL: a dropped field does not merely lose type
29
+ information, it inverts the outcome. The field is absent from the field map,
30
+ so a real payload's value for it becomes unknown-key residue; if the schema
31
+ marked it required, validation FAILS; the node goes `raw`; and the render
32
+ route correctly refuses it a component. Dropping one field could cost a kind
33
+ its entire component. `json` is always a true statement about such a field.
34
+
35
+ A recovery also downgrades whatever the failed attempt logged at `error`
36
+ severity to `warning`, so a host that screams on errors does not scream
37
+ about kinds that render perfectly.
38
+
39
+ - Live-corpus result: all **502** active kinds now derive a complete field map
40
+ from `emitted_json_schema` alone (was 62 with a stored field list, and 466
41
+ before this change). 28 carry at least one widened field, and each one names
42
+ itself in the problems list so the schema can be tightened later.
43
+
3
44
  ## 0.7.0 — 2026-08-29
4
45
 
5
46
  - **`kindSchemaFromJsonSchema` — the registry-facing door for deriving a
package/dist/index.cjs CHANGED
@@ -3213,14 +3213,13 @@ function registerDeclaredKindDraft(declaredKind, objectNode, path, ctx) {
3213
3213
  for (const [propName, propNode] of Object.entries(objectNode.properties)) {
3214
3214
  if (!isRecord4(propNode)) continue;
3215
3215
  if (propName === KIND_KEY) continue;
3216
- const converted = convertProperty(
3216
+ fields[propName] = convertFieldOrWiden(
3217
3217
  propName,
3218
3218
  propNode,
3219
3219
  memberRequired.has(propName),
3220
3220
  `${path}.${propName}`,
3221
3221
  ctx
3222
3222
  );
3223
- if (converted) fields[propName] = converted;
3224
3223
  }
3225
3224
  ctx.blockSchemas.push({
3226
3225
  slug: declaredKind,
@@ -3228,6 +3227,21 @@ function registerDeclaredKindDraft(declaredKind, objectNode, path, ctx) {
3228
3227
  fields
3229
3228
  });
3230
3229
  }
3230
+ function convertFieldOrWiden(fieldName, node, required, path, ctx) {
3231
+ const before = ctx.problems.length;
3232
+ const converted = convertProperty(fieldName, node, required, path, ctx);
3233
+ if (converted) return converted;
3234
+ for (let i = before; i < ctx.problems.length; i++) {
3235
+ const problem = ctx.problems[i];
3236
+ if (problem && problem.severity === "error") problem.severity = "warning";
3237
+ }
3238
+ ctx.problems.push({
3239
+ severity: "warning",
3240
+ path,
3241
+ message: `Field "${fieldName}" has no exact field type \u2014 carried as any JSON value (json) rather than dropped from the kind.`
3242
+ });
3243
+ return { ...requiredNullableFlags(required), type: "json" };
3244
+ }
3231
3245
  function convertProperty(fieldName, node, required, path, ctx) {
3232
3246
  const core = convertPropertyCore(fieldName, node, required, path, ctx);
3233
3247
  const field = core === null ? null : attachNodeMetadata(node, core);
@@ -3618,16 +3632,13 @@ function convertPropertyCore(fieldName, node, required, path, ctx) {
3618
3632
  for (const [propName, propNode] of Object.entries(itemNode.properties)) {
3619
3633
  if (!isRecord4(propNode)) continue;
3620
3634
  if (propName === KIND_KEY) continue;
3621
- const converted = convertProperty(
3635
+ itemFields[propName] = convertFieldOrWiden(
3622
3636
  propName,
3623
3637
  propNode,
3624
3638
  itemRequired.has(propName),
3625
3639
  `${path}[].${propName}`,
3626
3640
  ctx
3627
3641
  );
3628
- if (converted) {
3629
- itemFields[propName] = converted;
3630
- }
3631
3642
  }
3632
3643
  const alreadyHasBlockKind = propNameIsBlockKind(itemNode.properties) || itemRequired.has(KIND_KEY);
3633
3644
  ctx.arrayBindings.push({
@@ -3672,19 +3683,24 @@ function convertPropertyCore(fieldName, node, required, path, ctx) {
3672
3683
  for (const [propName, propNode] of Object.entries(node.properties)) {
3673
3684
  if (!isRecord4(propNode)) continue;
3674
3685
  if (propName === KIND_KEY) continue;
3675
- const converted = convertProperty(
3686
+ nestedFields[propName] = convertFieldOrWiden(
3676
3687
  propName,
3677
3688
  propNode,
3678
3689
  nestedRequired.has(propName),
3679
3690
  `${path}.${propName}`,
3680
3691
  ctx
3681
3692
  );
3682
- if (converted) {
3683
- nestedFields[propName] = converted;
3684
- }
3685
3693
  }
3686
3694
  const referencedKind = readBlockKindFromProperties(node.properties);
3687
3695
  if (referencedKind) {
3696
+ if (!ctx.refsInProgress.has(referencedKind)) {
3697
+ ctx.refsInProgress.add(referencedKind);
3698
+ try {
3699
+ registerDeclaredKindDraft(referencedKind, node, path, ctx);
3700
+ } finally {
3701
+ ctx.refsInProgress.delete(referencedKind);
3702
+ }
3703
+ }
3688
3704
  return {
3689
3705
  ...requiredNullableFlags(required, nullable),
3690
3706
  type: "object",
@@ -3866,16 +3882,13 @@ function convertAiSchemaToBlockFields(schemaName, rootSchema, strict) {
3866
3882
  for (const [fieldName, fieldNode] of Object.entries(properties)) {
3867
3883
  if (!isRecord4(fieldNode)) continue;
3868
3884
  if (fieldName === KIND_KEY) continue;
3869
- const converted = convertProperty(
3885
+ convertedFields[fieldName] = convertFieldOrWiden(
3870
3886
  fieldName,
3871
3887
  fieldNode,
3872
3888
  required.has(fieldName),
3873
3889
  fieldName,
3874
3890
  ctx
3875
3891
  );
3876
- if (converted) {
3877
- convertedFields[fieldName] = converted;
3878
- }
3879
3892
  }
3880
3893
  const rootHasKind = propNameIsBlockKind(properties) || required.has(KIND_KEY);
3881
3894
  if (!rootHasKind) {