Skip to content

Conversation

@mathpirate
Copy link
Contributor

@mathpirate mathpirate commented Sep 11, 2025

Addresses/fixes Robin's feedback in https://linear.app/common-tools/issue/CT-884/named-types-should-get-entry-in-dollardefs.

Still does not auto-promote the root. Can easily change this, reviewers let me know your thoughts!


Summary by cubic

Switches the schema generator to hoist all named non-container types into definitions and use $ref for non-root uses. This reduces duplication, improves readability, and satisfies Linear CT-884.

  • Refactors
    • Emit $ref for every named type except wrappers: Array, ReadonlyArray, Cell, Stream, Default, Date.
    • Root types remain inline (no auto-promotion yet).
    • $ref can appear with Common Tools extensions as siblings (e.g., asStream: true).
    • Updated fixtures/tests to expect definitions and $ref; added README notes.

Copy link
Contributor

@ubik2 ubik2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if you're done, since I am seeing updates, but this LGTM.
Edit: maybe I should wait until tests are passing, though I'm surprised they aren't.
Edit2: Here's the issue from the integration test:

Unresolved $ref in schema:  #/definitions/Item {"type":"object","properties":{"$TYPE":{"type":"string"},"argument":{"$schema":"https://json-schema.org/draft-07/schema#","type":"object","properties":{"title":{"type":"string","default":"My List"},"items":{"type":"array","items":{"$ref":"#/definitions/Item"},"default":[]}},"required":["title","items"],"definitions":{"Item":{"type":"object","properties":{"title":{"type":"string"}},"required":["title"]}}}},"required":["$TYPE"]}

Formatted:

{
  "type": "object",
  "properties": {
    "$TYPE": { "type": "string" },
    "argument": {
      "$schema": "https://json-schema.org/draft-07/schema#",
      "type": "object",
      "properties": {
        "title": { "type": "string", "default": "My List" },
        "items": {
          "type": "array",
          "items": { "$ref": "#/definitions/Item" },
          "default": []
        }
      },
      "required": ["title", "items"],
      "definitions": {
        "Item": {
          "type": "object",
          "properties": { "title": { "type": "string" } },
          "required": ["title"]
        }
      }
    }
  },
  "required": ["$TYPE"]
}

This looks like you've exposed a bug somewhere else where we combine schemas improperly.
schema.ts has a generateHandlerSchema, which does a similar thing (and doesn't handle the $schema property), but this isn't that path.
This looks like ListInput from ct-list.tsx, but this is specifically for the process cell (with the $TYPE).
Ah, probably syncCellsForRunningRecipe.

`$ref` is emitted.
- Anonymous/type‑literal shapes (including aliases that resolve to anonymous
types) are inlined.
- `$ref` may appear with Common Tools extensions as siblings (e.g.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we change the emitted $schema to https://json-schema.org/draft/2020-12/schema, we won't need this as a disclaimer (since it would be legal), but I think we should also switch to $defs instead of definitions if we do that, and I'd rather see that in a later PR so this one lands earlier.
Both $defs and definitions are legal in 2020-12, but $defs is preferred.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- Hybrid policy is more complex and less predictable for readers.
- Loses some reuse within fragments.

Option C — Patch CT runner to resolve `$ref` from parent/root (long-term)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point, I intend to stop passing around schema and rootSchema, and instead make sure all the definitions used for the child schema get copied into that object from the rootSchema. This may include hoisting Root to be a named node (if your children have a "$ref": "#").

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, sounds good. Let me know if/when I should make corresponding changes on this side!

return rootSchema === false
// Handle boolean schemas (rare, but supported by JSON Schema)
if (typeof base === "boolean") {
const filtered = this.collectReferencedDefinitions(base, definitions);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if typeof base is boolean, we don't need to scan for references.
collectReferencedDefnitions will just return an empty object.

You may still want to convert it to an object form so you can convey the $schema tag, but we don't need definitions. No personal preference on whether it gets converted to object with $schema tag or just boolean.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point thanks, i went ahead and did the object form with $schema tag, PTAL

Copy link
Contributor

@ubik2 ubik2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logs good!

@mathpirate mathpirate merged commit 79bffce into main Sep 19, 2025
7 checks passed
@mathpirate mathpirate deleted the feat/all-named-refs branch September 19, 2025 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants