Repository navigation
Conversation
efca818 to
daef1c5
Compare
|
Review after discussing the direction with Joep: please don't merge this as one PR. The parts are useful, but the front door duplicates the existing ontology model. Direction There is one schema model: Ontology, Class and Property, edited in the existing ontology editor. Every API (code, JSON Schema, LLM tools) writes ordinary ontologies. The building blocks already exist: Identity Every Ontology, Class and Property keeps its live ID (HTTP or
Old versions live in the Loro history, indexed by frozen ID. A frozen ID is shared together with its live ID, so it can be resolved. What to keep from this PR, each as its own PR
Drop: Later, and only if Audio or another app needs strict offline checks: carrying definitions with data and checking them on arrival. Validation should apply to writes, never block a drive's sync over one resource. Apps ship a bundle of their ontology with fixed live IDs, made once by the developer, and import it on startup. Each install making its own ontology wouldn't work, because device IDs would diverge. Generated by Claude Code |
… and lenses (#2151) * Plan class constraints and their effect on forms Allowed values, limits and linked classes move from Properties to a JSON Schema-style constraints map on the class, so properties can become immutable. Covers what changes for forms, tables and kanban, and why this lands separately from #2045. * Specify content-addressed property identity A Property's ID becomes atomic:prop:{blake3-hex}, the BLAKE3 derive-key hash (context "atomic property identity v1") of the JCS object {datatype, ontology, shortname}. Adds the spec page with test vectors, property_id / verify_property_id in atomic_lib, and the atomic:prop: identifier kind. Nothing creates or enforces these IDs yet. * Mint content-addressed property IDs in @tomic/lib Adds propertyId / verifyPropertyId (same vectors as atomic_lib), the property identifier kind, and a contentAddressedProperty option on Store.newResource that derives the subject from parent, shortname and datatype instead of minting a genesis cert. Property keys written as did:ad:prop: are stored under their atomic:prop: form. * Create properties under content IDs and keep their identity fixed Table columns, select columns, form fields, the ontology editor and ensureSchema now create Properties with contentAddressedProperty, only once parent, shortname and datatype are final. Renaming a column or form field changes its label, not its shortname. Changing the datatype of a content-addressed property creates a new property and swaps it into its classes (existing values move once lenses land). Re-parenting such a property is refused. Legacy properties keep editing in place. * Accept and protect content-addressed properties in commits A genesis commit for an atomic:prop: subject is valid when the ID is the hash of its parent, shortname and datatype; it needs no genesis cert and append rights on the parent. A repeat genesis of the same ID from another device merges. Later commits cannot change a property's parent, shortname or datatype. * Add class constraints: JSON Schema keywords on a Class, enforced on commit and save A Class gets a `constraints` map (property subject to enum, minimum, maximum, exclusive bounds, min/maxLength, min/maxItems, pattern, class). Rust and TS share one fixture. Enforced at commit under validate_schema and in Resource.save for loaded classes; peer commits and reconcile skip it. * Add ensureOntology and JSON Schema import/export ensureOntology finds or creates an Ontology with content-addressed properties, classes, requires/recommends and class constraints; running it twice writes nothing. ensureSchema becomes a deprecated alias. ontologyFromJsonSchema / ontologyToJsonSchema map draft 2020-12 onto that input and back; unsupported constructs fail with a JSON pointer. Rust twin in lib/src/schema/json_schema.rs (import + ensure_ontology), sharing one fixture with the TS tests. * Add schema push, lock and check to atomic-cli and @tomic/cli Both tools turn a JSON Schema or ontology file into a real ontology and write <file>.lock.json pinning the property IDs. lock works offline, check fails when a pinned property ID changed (a new shortname or datatype makes a new property), push refuses a stale lockfile unless --accept-changes. Rust and TS write byte-identical lockfiles, proven by a shared fixture. * Move tables, kanban and the ontology editor to class constraints Select options, single pick, number/text limits and linked classes are written to the table class's constraints map instead of the Property. Readers use getEffectiveConstraint: the class map wins per keyword, legacy Property allowsOnly/max/classtype is the fallback, so existing tables keep working. The ontology editor edits constraints per property in each class card; content-addressed Property cards no longer edit those fields. The form builder keeps its property-level writes until its own step. * Let an empty allowsOnly restrict nothing A SelectProperty must carry allowsOnly, but its options now live in the class constraints map, so createSelectProperty stores []. Resource::set read that as 'allows nothing' and every choice submission failed. * Move forms to class constraints The form builder reads and writes enum, maxItems and class on the form's data class. Form options use JSON Schema keywords (minimum/maximum, minItems/maxItems; old names still read) and may only tighten the class constraint. forms.rs and form-renderer validate submissions against the narrowed constraints, and describe_form returns the narrowed JSON Schema. * Save a recreated property even after its genesis is signed Signing the genesis clears `new` while the commit waits for save(), so a renamed form field's sibling property was never committed and the table could not load the column after a reload. Choice options in the class constraints now follow the field to its new property. * Give the assistant find_schema and ensure_ontology find_schema searches the drive's classes by words and returns each match's JSON Schema, replacing get_user_classes. ensure_ontology takes a JSON Schema, imports it with ontologyFromJsonSchema and runs ensureOntology; import errors come back verbatim with their JSON pointer, and a second call changes nothing. Skills tell the model to search first and reuse classes. * Add lenses: content-addressed mappings that keep old data readable A Lens (atomic:lens:{hash} of from, to, transform) maps one property onto another with rename, wrap, head, map or convert. atomic_lib and @tomic/lib derive the missing side when a resource materializes; the signed Loro doc never changes and real values always win. Lenses are owned by the target property's ontology, are backfilled into stored resources when they land, and never reject a commit. Changing a property's datatype or a form field's shortname now creates the matching lens. * Format lens.test.ts * Mount lib/tests/fixtures in the CI JS test container The class constraint, lens, JSON Schema and lockfile tests read fixtures shared with their Rust twins by repository-root path. * Fix select columns, linked classes and template tags on the new schema model - Tags for a hosted (atomicdata.dev) property get a DID subject under the table's row class; the server rightly refused path subjects there, which broke template drives. - A draft select column no longer saves its Property early; tags created in the draft reach the class enum. - Select cells wait for loading tag options before picking; first-row constraints include the table's row class. - The property dialog offers a Linked class field for link datatypes, writing the class's class constraint. - Lenses and recreated properties are always saved (signing the genesis already clears new); views swap old columns for the new property. - The generic edit page keeps a content-addressed property's identity read-only; the form builder says why a looser limit is refused. * Changelog entries for the schema change * Set validate_constraints in the sync ownership test from develop * Return the effective constraint from the store snapshot The React Compiler (data-browser compiles @tomic/react) cached the constraint computed from the hook's unchanging arguments, so the ontology constraint editor kept showing the value it first read after every save. The constraint is now the useSyncExternalStore snapshot, rebuilt after each change. Vitest compiles browser/react/src with the React Compiler so the regression test sees the same code the app runs. * Give the hosted MCP find_schema and ensure_ontology find_schema searches the classes on the drives the agent can read and returns each as JSON Schema; get_user_classes is now an alias for it. ensure_ontology imports a JSON Schema and creates the ontology with commits signed as the issued agent through the normal pipeline, offered only when editing was allowed. atomic_lib gains ontology_to_json_schema, checked against the same fixture as the TypeScript export. MCP class and property arguments accept atomic: subjects, and a class's own name property no longer clashes with the standard one. * Tidy the class constraints section and form bound fields The Constraints toggle is now a small text button with a summary when collapsed, and its inputs sit on a grid that shares the right edge of the add-property row (full width when the row wraps). Min/max pairs in the form builder align to the top, and a bound's error shows in flow, fully visible.
|
Complexity review (about 9.7k changed lines; I read
Separately, this adds a second schema API next to Generated by Claude Code |
|
Closing: #2151 (content-addressed properties, class constraints, JSON Schema and lenses, merged 9 Oct) replaced this PR and took over its JSON Schema conversion code. The Complexity Critic's review: the bindings, the dependency memo cache and the Rust/TS link-discovery mismatch ( Generated by Claude Code |
App schemas that travel with the data
Apps can define immutable Classes and Properties without hosting an ontology,
edit nested data through Rust/TypeScript APIs, and sync to a cold replica without
pre-registering its schema. Definitions use
atomic:frozen:<BLAKE3 of JCS JSON-AD>.They travel in the resource's
atomic:schema-definitionsLoro root over existingCOMMIT/UPDATE/SYNC_PUSH paths, including Iroh.
Implementation
persistence. Install reachable definitions with admitted data; missing
dependencies fail explicitly. Verify unused attachments too. No schema network
fetch or peer-supplied code execution is introduced.
Exact-body verification caches are bounded and cannot supply missing definitions
across stores. These are post-import limits, not a CRDT decompression DoS audit.
edits, enum/nullable/union shapes, generated Rust/Dart models, and bounded JSON
Schema 2020-12 import/export with verified identity sidecars.
resumable progress and retained originals. Receiving schemas never runs a
migration; concurrent old-version edits require explicit reconciliation.
failures. Errors identify the rejected resource.
Review fixes
assigned. Genesis creator attribution and distinct signed edit tokens are
covered by a regression that failed on the reviewed draft.
validation replica, freed after five idle seconds. Local changes refresh it;
rejected or abandoned candidates cannot affect later checks. A previously
ordinary resource is still checked when an incoming delta introduces a schema.
corrected path 14 ms, pre-PR path 14.5 ms. First imports, rendered UI
and Android are not covered by this benchmark. Details and reproduction:
review follow-up.
Compatibility and scope
This remains a draft. String tagging, preserved nested JSON nulls and stricter
Rust setters also affect resources without app schemas. Older readers may still
interpret JSON-like strings differently. Upgrade readers and writers together;
there is no negotiated schema-enforcement capability. Core HTTP ontology IDs stay
unchanged. Existing frozen definitions cannot be edited; semantic changes need a
new definition and migration.
The mutable plugin-schema API remains separate, and typed nested/list edits are
not exposed through plugin RPC. Plugin standardization requires coordination.
A standalone Dart transport/editing client, browser migration orchestration and
unrestricted JSON Schema support remain follow-up work. The draft retains its
review history; representation, protocol/admission and SDK layers still need
explicit review before merge.
Validation
integration tests. The 25 schema tests also passed after adding resource-subject
diagnostics. An initial run hit the existing macOS compaction test failure;
both its isolated rerun and the full suite rerun passed.
performance probe). Live-server integration tests excluded.
with warnings denied, and four Dagger/fixture setup checks pass. The commit
hook also checks the exact staged snapshot.
authorization, migration races, replica expiry/local edits/failed candidates,
signed attribution, and valid browser entries following rejected ones without
false sync completion.
Atomic Audio adoption (separate repository)
Audio backport: d13c280632c6842c792bbd328223b8936e4f5f00.
Validation and boundaries,
vendored signing regression,
Mac audio counters.
Audio passes 29 native tests, 43 Flutter tests, analysis, all-target Clippy and
formatting. Full Mac integration passes in 74 seconds with zero underruns,
deadline misses or device errors across 54,004 callbacks. The normal Mac app was
rebuilt afterwards. Android hardware and a deployed phone/browser round trip
were not retested. These results belong to the linked Audio commit, outside this diff.
CI status
Exact-head run for efca818a318147d37ec5325c25354fa91e151854
was dispatched on 2026-10-05. Main (Mancave) and downstream atomic-saas are queued
as of this update. This draft is not merge-ready without a concluded green run;
local checks do not replace CI or establish SaaS compatibility.