Resolve schema references through their Source#1044
Draft
malberts wants to merge 11 commits into
Draft
Conversation
A SchemaReference carries an optional source (null means local) and a
SchemaName, resolved through the Schema's own Source (ADR 23). The
serialized form is a bare name string for a local reference and a
{ source, name } object for a foreign one: Schema names are page titles
and may contain colons, so a string-concatenation grammar would be
ambiguous. The parser canonicalizes a reference that names the local
source to the bare local form, mirroring SubjectIdParser; source keys
compare byte-for-byte.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Subject now holds a SchemaReference internally instead of a bare SchemaName. getSchemaName() keeps returning the reference's name so existing consumers stay source-blind, and getSchemaReference() exposes the full reference. The createNew() and newSubject() factories still take a SchemaName and wrap it as a local reference. Serialization is unchanged: this only threads the type through the construction sites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The subject-slot `schema` field now serializes a SchemaReference: a
local reference stays the bare name string, byte-identical to before,
while a foreign reference becomes a { source, name } object. The
deserializer accepts either form through SchemaReferenceParser, so a
reference naming the local wiki canonicalizes to the bare local form.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A RelationProperty's targetSchema is now a SchemaReference. It
serializes as the bare name string for a local target, byte-identical
to before, and as a { source, name } object for a foreign one, matching
the Subject slot format. getTargetSchema() keeps returning the name so
existing consumers stay source-blind; getTargetSchemaReference() exposes
the source.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SchemaReferenceResolver resolves a SchemaReference to its Schema through the reference's own Source, independent of any subject's source (ADR 23). The source-routing implementation mirrors SourceRoutingSubjectLookup: a local reference routes through the local Source and behaves exactly as a name-keyed lookup, an unregistered source key returns null plus one logged warning, and a registered Source without the schema returns null without logging. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The query, validation, and projection paths that resolve a Subject's schema (ProposedSubjectValidator, ValidateSubjectUpdateQuery, ReplaceSubjectAction, RdfPageProjector, and the Neo4j subject updater) now resolve the Subject's SchemaReference through SchemaReferenceResolver instead of looking the name up directly. Local references route through the local Source unchanged; an unresolvable reference degrades to the existing schema-not-found handling. The name-parameterized paths (CreateSubjectAction, ValidateSubjectQuery, GetSchema, the frontend schema map) stay name-based. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The subject `schema` field and the relation `targetSchema` field now
document both reference forms: a bare name string for a local Schema and
a { source, name } object for a Schema from another Source, with the
local-source canonicalization rule and the colon-safety rationale. The
glossary Schema entry notes the (source, name) reference form.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The frontend gains a SchemaReference type mirroring the backend: a bare
name string is a local reference, an object { source, name } references
a Schema from another Source. SubjectDeserializer and the relation
property deserializer now accept either form and reduce it to the
schema name, so a local reference stays a plain string end to end while
a foreign one no longer leaks its object shape into a name field.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The object targetSchema form is defined by the reference format, but schema save validation still accepts only the bare string, and local-source normalization runs on the subject schema field only. The docs claimed both for targetSchema. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A foreign SchemaReference accepted any string as its source key, including empty and whitespace, while a SubjectId source key must match [A-Za-z0-9+_-]+. Apply that same grammar on the foreign-source construction path so the two agree byte-for-byte; the local bare form and the name stay unaffected. The check also covers deserialization, matching how a SubjectId rejects a malformed persisted id. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Graceful degradation of an unresolvable schema reference holds only on the resolution, validation, and projection paths, which return null with one logged warning and skip the Subject. The read and View path does not degrade the same way: a Subject whose schema reference is unresolvable is not presentable yet, though the surrounding page still renders. Scope the claim in the subject and schema format docs and the glossary so they no longer imply graceful presentation, and tie the gap to the deferred sourced-Schema rendering work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
For #993 (work order T3). Builds on the T2 Source
contract PR and targets its branch, so the diff shows only this track.
Schema references become
(source, name), resolved through the schema's own Source and degradinggracefully when unresolvable. Reference plumbing only; no non-local schema source exists yet.
Design calls recorded here:
SchemaReferencevalue object withSchemaNameintact underneath, per the workorder's own lean: locally the name IS the page title in the Schema namespace, and a
source-qualified reference is not a valid title.
{ "source": ..., "name": ... }, not asource:Namestring. Schema names are page titles and may legitimately contain colons, so astring-concatenation grammar is ambiguous; subject ids avoid that only because the bare nanoid
alphabet excludes the separator. Local references stay bare name strings, byte-identical to
before (regression-pinned on both persisted spots).
schemafield, an object naming the local Source normalizesto the bare form at parse time (mirroring
SubjectIdParser), so one Schema is never referencedunder two identities. A relation property's
targetSchemais deliberately not at full parityyet: schema save validation still accepts only the bare string form, so the object form has no
ingress there, and normalization is omitted where it can never run. Widening the schema-save
contract and canonicalizing that path belongs with sourced-schema authoring; flip it in review if
you prefer parity now.
SourceRoutingSchemaReferenceResolver,mirroring the subject routing semantics exactly: unknown source keys degrade to null plus one
logged warning, and unresolvable references land in the pre-existing
schema-not-found/null-schema handling in all five converted consumers (validators, replace action, RDF projector,
Neo4j updater), never fatally. Name-parameterized paths (REST schema APIs, new-subject creation)
stay name-based.
reduces string-or-object references to the bare name: sourced rendering is deferred by ADR 23, so
the TS domain keeps plain names end to end.
Docs:
docs/api/subject-format.mdgains the Schema References section,docs/api/schema-format.mddocuments the
targetSchemaforms and their current acceptance honestly, and the glossary Schemaentry notes the reference form.
Production notes
Design and review adjudication by
Fable 5 (max); implementation by anOpus 4.8 (max)subagent;two
Opus 4.8 (max)review lenses (runtime correctness with container probes, tests/conventions/docs) stood in for the usual pre-PR human review at @malberts' direction. The review confirmed the
targetSchema asymmetry is unreachable through every current ingress and found only docs overclaims,
which the final commit scopes to shipped behavior.