Semantic Systems / Language / Glyphs

Developer Platform Blueprint for Embedded Semantics

Report summary

Embedded Semantics already has the right conceptual boundary for a developer platform: stable ConceptCodes are identity; reviewed expressions and provenance are governed evidence; vectors are evidence rather than authority . Its published architecture says that production resolution is currently det

Status
Research archive item
Category
Semantic Systems / Language / Glyphs
Length
6,480 words
Reading time
30 minutes
Report type
evaluation

Key topics

  • Semantic Systems / Language / Glyphs
  • Semantic Systems
  • Language
  • Glyphs
  • AI
  • C#
  • TypeScript
  • Python
  • Runtime

Research provenance

Archive status
Research archive item
Content identity
sha256:e9444cf60874242a114795b02b23b7c593b50652a840601178c2c5f5b5fec53e

For citation, use the report title and canonical URL. Archival presence does not establish authorship or promote report statements into portfolio evidence.

Source availability: 85 citation markers in the source export have no recoverable source links. Those markers are omitted from this reader; any supplied bibliography and ordinary links remain. Check the original sources before relying on the cited claims.

This page renders the archived Markdown as safe, formatted HTML. It is background research and does not become a portfolio claim without evidence review.

Full report

On this page

Executive findings, personas, and product principles

Embedded Semantics already has the right conceptual boundary for a developer platform: stable ConceptCodes are identity; reviewed expressions and provenance are governed evidence; vectors are evidence rather than authority. Its published architecture says that production resolution is currently deterministic reviewed-exact lookup, that the resolver should abstain when exact authority is absent, and that arbitrary-query embedding retrieval remains experimental.

That boundary should become the developer experience.

The platform should not ask ordinary application developers to learn ontologies, embedding geometry, equivalence classes, registry governance mechanics, or semantic-research terminology before they can use a stable identifier. The target mental model should be:

“Give me text. Tell me whether it maps to a known stable ID. Let me use that ID like any other typed identifier.”

There is one immediate prerequisite before a public developer-platform launch. As of August 23, 2026, the live site reports zero published concepts and zero reviewed exact expressions, while its deterministic prerelease bundle manifest contains 11 concepts and 55 expressions. The public concept endpoint likewise returns an empty collection. In other words, the project has a governed bootstrap artifact but not a populated live production registry. That makes a compelling five-minute quickstart impossible today: a correctly written application cannot successfully resolve even the site's own semantic_registry.stable_concept_identity example against the deployed registry. Fixing that deployment gap is more important than adding any sophisticated API feature.

Platform thesis. The ideal product is a small REST terminology service backed by immutable, downloadable registry releases. Remote and offline resolution must implement the same contract. SDKs should turn ConceptCodes into open-world value types, not closed enums. Registry changes should arrive as reviewable dependency upgrades, not silent semantic mutation. Provenance should always be inspectable but usually stay out of the application's happy path.

Developer personas

PersonaWhat they are trying to doWhat must stay invisible by defaultPlatform feature that wins them
Application developerPut stable semantics into records, business rules, APIs, and databasesOntology modeling, embeddings, governance workflowresolve(), validate(), typed ConceptCode
API/schema designerSay “this property contains a ConceptCode” and sometimes restrict it to a business-approved subsetRegistry internalsJSON Schema annotations, generated ConceptSets
Backend/platform engineerMake semantic lookups reliable and cheapManual cache/index implementationPinned packages, offline resolver, ETag-aware cache
Data engineerValidate stored ConceptCodes and migrate datasetsApplication SDK detailsCLI batch validation, registry diff/replay
Enterprise architectCombine public and organization-private conceptsNamespace collision and federation complexityNamespaced registries, signed packages, federation rules
Compliance/audit engineerExplain why a value resolved as it didModel internals unless relevantProvenance/evidence endpoint and immutable digest
Library/framework authorExpose ConceptCodes in reusable components without breaking future usersSpecific current registry membershipOpen-world code types and stable wire contracts
Research/semantic engineerInspect evidence and eventually experimental candidate retrievalNothing; this persona wants depthExplicit evidence API and separately versioned research surface

The distinction between ordinary application integration and semantic research is particularly important because Embedded Semantics itself says the production objective is concept resolution rather than generic similarity and explicitly treats “unknown” as a valid outcome.

Core use cases

The public platform should optimize for these operations, in this order:

  1. Resolve an expression to either one Concept, unknown, or ambiguity.
  2. Retrieve a Concept by stable ConceptCode.
  3. Validate a ConceptCode against a particular registry release.
  4. Pin and download the registry release used by an application.
  5. Inspect provenance/evidence for a resolution or Concept.
  6. Update a pinned registry through a reviewable diff.
  7. Run exactly the same resolution offline.
  8. Bind ConceptCodes into application schemas and language type systems.
  9. Test behavior across registry upgrades.
  10. Combine public and private registries without silently changing identity.

The design should resist expanding beyond those operations until real usage demands it.

API principles

Outcome is different from failure. unknown_expression, ambiguous_expression, and “this ConceptCode is not in this release” are expected semantic outcomes. Network failure, malformed JSON, authentication failure, unsupported API versions, and rate limiting are transport/service errors. Mixing the two makes ordinary application logic painful.

Identity is open-world. A ConceptCode that an SDK released six months ago has never heard of can still be perfectly legitimate. This single rule has major consequences: no global generated enum, no exhaustive registry-membership validation in constructors, and no assumption that a package upgrade requires an SDK upgrade.

Every result is reproducible. Responses should tell the caller exactly which registry identity, human-facing release version, and immutable digest produced the answer.

Offline and hosted behavior are equivalent. A pinned registry package and the hosted resolver must pass the same conformance suite.

Evidence is inspectable, not compulsory. Because Embedded Semantics distinguishes authoritative Concept identity from expressions, scores, vectors, prototypes, and other evidence, the API should preserve that distinction.

No client-defined normalization semantics. The site describes expressions as Unicode-preserving. Unicode defines multiple normalization forms and canonical-versus-compatibility equivalence, so “just lowercase and normalize the string” is not a sufficiently precise interoperability rule. The registry must own and version its matching algorithm, or distribute the precomputed lookup key used by the authoritative resolver.

Language is explicit. Use BCP 47 language tags where language constraints matter; BCP 47 defines the standardized syntax and matching framework for language tags.

Answers to the key architectural questions

QuestionRecommendation
Smallest useful API surface?Four primitives: Resolve, Get Concept, Validate ConceptCode, Get Registry Release. Package download and change diff are the essential operational extension.
Error semantics?Semantic outcomes are typed 200 responses. HTTP/service failures use RFC 9457 Problem Details.
Unknown and ambiguous?Explicit discriminated results: status: "unknown" and status: "ambiguous". Never fabricate a ConceptCode, never encode ambiguity as an exception.
Expose evidence?Yes, but opt-in. A tiny provenance summary belongs in normal responses; full evidence is requested with include=evidence or a subresource.
Pin registry versions?Lock by immutable digest, accompanied by an opaque release label for humans.
Update registry versions?Explicit check → diff → replay tests → update lockfile → review PR. Never silently mutate production semantics.
Offline SDK?Yes. For today's deterministic reviewed-exact model it should be a first-class mode, not an emergency fallback.
What should SDKs generate?Constants/value objects for selected concepts, closed ConceptSets when explicitly requested, JSON Schema fragments, and test fixtures.
ConceptCodes in typed languages?Open-world string-backed value types/newtypes.
Generate enums?Only for an explicitly closed, pinned ConceptSet—not the global registry. JSON Schema enum is by definition a fixed finite set.
Testing?Shared conformance pack, in-memory registries, local server, remote sandbox, upgrade replay tests.
Migration tooling?Registry diff, source/data scan, deprecated-code report, resolution replay, generated-code freshness checks.
Private registries?Namespace them, pin them independently, never let “priority” silently override a public Concept identity, and federate ambiguity explicitly.

A useful precedent comes from mature terminology systems rather than general-purpose knowledge graphs. FHIR distinguishes a code system defining codes from a ValueSet selecting codes for a particular use context, and exposes a specific $validate-code operation rather than requiring clients to download or reason through the entire terminology system. That separation maps well to Concept registry versus application ConceptSet.

Public REST API, JSON contracts, and protocol semantics

Use REST plus JSON as the normative public interface.

Do not launch GraphQL in the MVP. The core workload consists of point reads and small deterministic commands, not arbitrary client-shaped graph traversal. REST gives straightforward HTTP caching, immutable resource URLs, CDN distribution, observability, and high-quality OpenAPI generation. A read-only GraphQL explorer could be justified later if relationship traversal across large concept graphs becomes a dominant workflow; SDK correctness should never depend on it.

The smallest coherent API is:

MethodResourcePurpose
POST/v1/resolveResolve one expression
POST/v1/validateValidate one ConceptCode
GET/v1/concepts/{conceptCode}Retrieve authoritative Concept
GET/v1/registries/{registryId}Discover current stable release
GET/v1/registries/{registryId}/versions/{version}Retrieve immutable release metadata
GET/v1/registries/{registryId}/versions/{version}/packageDownload registry package
GET/v1/registries/{registryId}/changesDiff two releases

The first four operations are the conceptual API. The package and change endpoints make it operationally trustworthy.

Later, add:

POST /v1/resolve:batch
POST /v1/validate:batch
GET  /v1/concepts
GET  /v1/concept-sets/{id}
POST /v1/webhook-subscriptions

Do not make browse/search endpoints prerequisites for normal runtime integration.

Resolution request

Zero-configuration development should be extremely small:

POST /v1/resolve
Content-Type: application/json
{
  "expression": "stable concept identity",
  "language": "en"
}

The server may use the public registry's current stable release when no pin is supplied, but the response must state exactly which release was used.

Production applications can pin explicitly:

{
  "expression": "stable concept identity",
  "language": "en",
  "registry": {
    "id": "public",
    "version": "2026-08-23.1",
    "digest": "sha256:8a42..."
  }
}

The example release is illustrative; the currently published Embedded Semantics manifest calls the combined bundle prerelease, while its constituent packs use both 1.0 and 2026-08-22.1. That heterogeneity is a strong reason not to force SemVer interpretation onto registry labels. Treat version as an opaque release identifier and digest as the reproducibility lock.

Resolved response

{
  "status": "resolved",
  "input": {
    "expression": "stable concept identity",
    "language": "en"
  },
  "concept": {
    "code": "semantic_registry.stable_concept_identity",
    "display": "Stable concept identity",
    "definition": "A persistent semantic identity whose meaning is governed independently of language-specific expressions.",
    "lifecycle": "active"
  },
  "match": {
    "method": "reviewed_exact",
    "evidenceId": "expr_01K4W...",
    "reviewed": true
  },
  "provenance": {
    "registryId": "public",
    "evidenceCount": 1
  },
  "registry": {
    "id": "public",
    "version": "2026-08-23.1",
    "digest": "sha256:8a42..."
  }
}

The API should not manufacture a confidence: 1.0 for exact matches. Exact governed authority and probabilistic model confidence are different concepts. Embedded Semantics explicitly separates reviewed exact production resolution from future model-backed confidence calibration.

Unknown response

Unknown is not a 404 and not an exception:

{
  "status": "unknown",
  "reason": "unknown_expression",
  "input": {
    "expression": "an unseen phrase",
    "language": "en"
  },
  "registry": {
    "id": "public",
    "version": "2026-08-23.1",
    "digest": "sha256:8a42..."
  }
}

HTTP status: 200 OK.

This mirrors the project's documented behavior: production should abstain when no governed exact evidence exists.

Do not include “maybe this concept?” candidates in this production response while candidate retrieval remains experimental. Doing so would blur the project's most important semantic boundary. When experimental retrieval is exposed, give it a visibly separate operation or namespace such as:

POST /experimental/v1/candidates

An ordinary resolve() call should remain authoritative and conservative.

Ambiguous response

{
  "status": "ambiguous",
  "reason": "ambiguous_expression",
  "input": {
    "expression": "bank",
    "language": "en"
  },
  "candidates": [
    {
      "conceptCode": "example.financial.bank",
      "display": "Financial institution"
    },
    {
      "conceptCode": "example.geography.river_bank",
      "display": "River bank"
    }
  ],
  "registry": {
    "id": "public",
    "version": "2026-08-23.1",
    "digest": "sha256:8a42..."
  }
}

Again: 200 OK.

An exact expression being governed to more than one concept is already a documented Embedded Semantics outcome.

Ambiguity candidates should be ordered deterministically, but not represented as a ranked “winner plus alternatives” unless the registry itself provides authoritative context-specific precedence. In an authoritative resolver, ambiguity means the caller must supply more context or choose explicitly.

A future context-aware request could be:

{
  "expression": "bank",
  "language": "en",
  "context": {
    "conceptSet": "finance-v3"
  }
}

Do not introduce arbitrary free-form context objects in v1; doing so invites hidden application-specific inference.

Concept retrieval

GET /v1/concepts/semantic_registry.stable_concept_identity
{
  "code": "semantic_registry.stable_concept_identity",
  "display": "Stable concept identity",
  "definition": "...",
  "lifecycle": "active",
  "expressions": [
    {
      "language": "en",
      "value": "stable concept identity",
      "kind": "reviewed_exact"
    }
  ],
  "relationships": [],
  "provenance": {
    "createdAt": "2026-08-20T16:02:10Z",
    "modifiedAt": "2026-08-22T10:12:03Z"
  },
  "registry": {
    "id": "public",
    "version": "2026-08-23.1",
    "digest": "sha256:8a42..."
  }
}

The public Concept representation should make code, definition, lifecycle, and registry coordinate immediately visible. Evidence-heavy structures should be behind:

GET /v1/concepts/{conceptCode}?include=evidence

or, once evidence becomes sufficiently large:

GET /v1/concepts/{conceptCode}/evidence

This follows the project's own provenance model, under which definitions, expressions, renderings, relationships, model profiles, prototypes, and evaluation outcomes can change independently without rewriting ConceptCode identity.

Evidence model

Yes, APIs should expose evidence. They should do so with a type system that prevents developers from confusing evidence with authority.

A useful record is:

{
  "id": "evidence_01K4...",
  "kind": "reviewed_expression",
  "role": "authoritative_resolution_evidence",
  "expression": {
    "value": "stable concept identity",
    "language": "en"
  },
  "review": {
    "status": "approved",
    "reviewedAt": "2026-08-22T10:10:00Z"
  },
  "source": {
    "registryPack": "embedded-semantics.starter-registry",
    "registryPackVersion": "1.0"
  }
}

Future model evidence should look visibly different:

{
  "id": "evidence_01K5...",
  "kind": "embedding_candidate",
  "role": "experimental_supporting_evidence",
  "modelProfile": "example-model-v4",
  "score": 0.84
}

The field role is more important than score: it tells a developer whether this evidence is semantically authoritative, supporting, or experimental. That encodes Embedded Semantics' published distinction between ConceptCode/registry definition as authority and models/scores/prototypes as evidence.

ConceptCode validation

A validation operation should answer a business question, not throw an HTTP error for an ordinary negative result:

POST /v1/validate
{
  "conceptCode": "semantic_registry.stable_concept_identity",
  "registry": {
    "id": "public",
    "digest": "sha256:8a42..."
  }
}

Valid:

{
  "valid": true,
  "conceptCode": "semantic_registry.stable_concept_identity",
  "lifecycle": "active",
  "registry": {
    "id": "public",
    "version": "2026-08-23.1",
    "digest": "sha256:8a42..."
  }
}

Unknown code:

{
  "valid": false,
  "reason": "unknown_concept_code",
  "conceptCode": "example.nonexistent",
  "registry": {
    "id": "public",
    "version": "2026-08-23.1",
    "digest": "sha256:8a42..."
  }
}

Malformed code:

{
  "valid": false,
  "reason": "invalid_concept_code_syntax",
  "conceptCode": "not a valid code"
}

All three are valid POST /validate transactions and should normally be 200.

This design is analogous to terminology APIs such as FHIR's $validate-code: the validation operation returns a Boolean/result rather than requiring the client to interpret retrieval failure itself.

Transport error model

Use application/problem+json and RFC 9457 for protocol/service errors; RFC 9457 exists specifically to provide machine-readable error detail without every API inventing its own envelope.

Example:

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
  "type": "https://embeddedsemantics.com/problems/invalid-request",
  "title": "Invalid request",
  "status": 400,
  "detail": "The language property is not a valid BCP 47 language tag.",
  "code": "invalid_request",
  "requestId": "req_01K4Y7...",
  "errors": [
    {
      "path": "/language",
      "code": "invalid_format",
      "message": "Expected a BCP 47 language tag."
    }
  ]
}

Recommended status semantics:

HTTP statusMachine codeMeaning
400invalid_requestMalformed JSON or request contract
401authentication_requiredMissing/invalid credentials
403forbiddenAuthenticated but unauthorized
404resource_not_foundRequested Concept/release endpoint does not exist
409registry_pin_conflictRequested version and digest identify different releases
413batch_too_largeBatch exceeds service limit
429rate_limitedRequest quota exceeded
503service_unavailableTemporary service failure

Do not use 404 for unknown_expression. The /resolve resource existed and executed successfully.

SDK exception hierarchies should correspond only to this error plane:

EmbeddedSemanticsError
  ApiError
    AuthenticationError
    AuthorizationError
    RateLimitError
    RegistryPinConflictError
  TransportError
  PackageIntegrityError

UnknownResolution and AmbiguousResolution do not belong in that hierarchy.

API and schema versioning

Keep independent version axes:

AxisMeaningExample
HTTP contractBreaking wire/API semantics/v1
Registry releaseHuman-readable governed release2026-08-23.1
Registry digestImmutable content identitysha256:...
Registry-package schemaFile-layout compatibilityregistry-package.v1
SDKLanguage package evolution2.3.0
Concept identityStable semantic identifiersemantic_registry...
Concept metadata revisionDefinition/evidence changecarried through registry release

A new Concept must not require /v2. A new registry release must not require a new SDK. A new SDK must not silently change the registry used by a pinned application.

JSON Schema strategy

Use JSON Schema Draft 2020-12 for application-schema integration. Its default format vocabulary treats format as an annotation unless the assertion vocabulary is explicitly implemented, so an SDK must not pretend that:

{
  "type": "string",
  "format": "concept-code"
}

will universally perform semantic registry validation.

The baseline schema should therefore remain structurally interoperable:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "ConceptCode": {
      "type": "string",
      "x-embedded-semantics": {
        "registry": "public",
        "lockFile": "./embedded-semantics.lock"
      }
    }
  }
}

Then the Embedded Semantics CLI/schema plugin interprets the annotation:

es schema validate order.json --schema order.schema.json

Do not introduce a custom JSON Schema vocabulary in the MVP. It would require every validator to understand project-specific semantic keywords.

For genuinely closed business domains, generate standard JSON Schema enum:

{
  "$defs": {
    "ReturnReasonConcept": {
      "type": "string",
      "enum": [
        "acme.returns.damaged",
        "acme.returns.wrong_item",
        "acme.returns.changed_mind"
      ],
      "x-embedded-semantics": {
        "conceptSet": "acme.return-reasons",
        "registryDigest": "sha256:..."
      }
    }
  }
}

This is safe specifically because JSON Schema enum is defined to restrict values to a fixed set.

OpenAPI strategy

OpenAPI should be the canonical HTTP contract, not generated after the implementation.

Use OpenAPI 3.2.0 as the source specification. OAS 3.2 is a language-agnostic HTTP API description format and includes first-class webhook descriptions; it also retains JSON Schema-aligned schema capabilities.

Every operation must have:

  • a permanent operationId;
  • examples for all expected result variants;
  • RFC 9457 problems;
  • explicit registry-version fields;
  • stable discriminator fields such as status;
  • auth requirements;
  • rate-limit documentation;
  • caching semantics.

The resolution response should be a oneOf contract around status:

Resolution:
  oneOf:
    - $ref: '#/components/schemas/ResolvedResolution'
    - $ref: '#/components/schemas/UnknownResolution'
    - $ref: '#/components/schemas/AmbiguousResolution'
  discriminator:
    propertyName: status

Maintain an automatically tested 3.1-compatible derivative only if important generators still lag OAS 3.2 support. Do not weaken the canonical contract to the oldest generator in the ecosystem.

GraphQL decision

Recommendation: no public GraphQL in MVP or early v2.

Add it only if evidence shows developers routinely need queries like:

Concept
  -> relationships
  -> target Concepts
  -> localized expressions
  -> evidence
  -> provenance

with radically different shapes per consumer.

Even then, make GraphQL a read/exploration surface over the same registry model. Keep resolve, validate, package distribution, pinning, and webhook delivery REST-native.

Registry distribution, offline operation, and change management

Registry distribution is where Embedded Semantics can become unusually easy to adopt. The current architecture is especially amenable to offline operation because the production resolver is governed exact lookup rather than a mandatory remote model call.

Registry lockfile

Introduce one boring, commit-friendly file:

embedded-semantics.lock

{
  "lockVersion": 1,
  "registries": {
    "public": {
      "version": "2026-08-23.1",
      "digest": "sha256:8a42b8...",
      "packageSchema": "embedded-semantics.registry-package.v1"
    }
  }
}

Developers should rarely hand-edit it.

es registry pin public

writes it.

es registry verify

confirms the local package matches the digest.

es registry status

shows what the application is actually using.

The digest is authoritative for reproducibility. The version label is for humans and release policy.

This design builds naturally on the project's existing manifest, which already publishes SHA-256 values for the prerelease bundle and its source packs.

Registry-package design

The site already advertises a Reviewed Registry Pack v1 JSON Schema and two downloadable packs, so the developer platform should evolve that artifact rather than inventing a separate “SDK database” format.

For the initial scale, a single canonical JSON package is ideal:

{
  "schema": "embedded-semantics.registry-package.v1",
  "registry": {
    "id": "public",
    "version": "2026-08-23.1"
  },
  "createdAt": "2026-08-23T14:00:00Z",
  "parentDigest": "sha256:77a...",
  "matching": {
    "mode": "reviewed_exact",
    "algorithm": "reviewed-exact-key-v1"
  },
  "concepts": [],
  "expressions": [],
  "ambiguities": [],
  "provenance": []
}

The HTTP representation should supply the immutable digest through release metadata and ideally an HTTP Content-Digest as well; RFC 9530 defines HTTP digest fields specifically for representation/content integrity.

At large scale, package v2 can become a manifest plus sharded canonical files:

manifest.json
concepts.ndjson
expressions.ndjson
ambiguities.ndjson
provenance.ndjson

An optional compiled local index may be distributed separately:

index.sqlite

but it should never be the sole authoritative representation. SDK implementations must be able to rebuild it from the canonical registry package.

Matching semantics in the package

The package must define matching behavior explicitly:

{
  "matching": {
    "mode": "reviewed_exact",
    "algorithm": "expression-key-v1",
    "unicodePolicy": "registry-computed",
    "languageMatching": "exact-bcp47"
  }
}

Each expression record can preserve original Unicode and carry a registry-computed key:

{
  "id": "expr_123",
  "value": "identidad conceptual estable",
  "language": "es",
  "matchKey": "identidad conceptual estable",
  "conceptCode": "semantic_registry.stable_concept_identity",
  "authority": "reviewed_exact"
}

That prevents four SDK teams from producing four subtly different normalizers. Unicode normalization is a real specification problem with multiple normalization forms and equivalence rules, so it belongs in the registry contract rather than application folklore.

Offline SDK

There should absolutely be an offline SDK.

The interface should be deliberately isomorphic:

const online = new EmbeddedSemantics({ baseUrl: "..." });
const offline = await EmbeddedSemantics.offline({
  lockFile: "./embedded-semantics.lock"
});

await online.resolve("stable concept identity", { language: "en" });
await offline.resolve("stable concept identity", { language: "en" });

For the same registry digest, both must produce equivalent domain results.

An application should also be able to use a hybrid strategy:

const es = await EmbeddedSemantics.create({
  registry: {
    lockFile: "./embedded-semantics.lock",
    prefer: "local",
    fallback: "remote"
  }
});

But the default for a pinned production application should be deterministic: do not silently switch registry versions or semantic modes because the network happens to be available.

Cache architecture

Use three cache levels.

Immutable package cache

~/.cache/embedded-semantics/
  sha256/
    8a/
      8a42.../
        registry.json
        verified.json

Content-addressed packages never need invalidation. A new release produces a new digest.

Compiled lookup cache

registry digest
   -> language
      -> registry matching key
         -> 0, 1, or N ConceptCodes

The index may use memory maps, SQLite, a language-native immutable map, IndexedDB in browsers, or another local representation. That is an SDK implementation detail.

Resolution-result cache

A bounded in-memory cache keyed by:

(registry digest, language constraint, expression)

This makes cache correctness trivial: registry changes create a new namespace.

For mutable discovery endpoints such as “current stable public release,” use normal HTTP validators. HTTP caching supports conditional revalidation and reuse after 304 Not Modified; ETags/conditional requests are the right mechanism.

Registry update workflow

Never make production registry updates implicit.

The expected workflow should look like dependency management:

$ es registry check
public  2026-08-23.1 -> 2026-09-04.1 available

$ es registry diff public
+ 18 concepts
+ 74 reviewed expressions
~  3 definitions
!  2 expressions become ambiguous
!  1 expression reassigned
-  0 concepts removed

$ es migrate replay ./tests/semantic-corpus.jsonl --to 2026-09-04.1
1203 unchanged
  14 unknown -> resolved
   2 resolved -> ambiguous
   0 resolved -> different concept

$ es registry update public --to 2026-09-04.1
Updated embedded-semantics.lock

The lockfile change belongs in version control.

Change taxonomy

Registry diffs should be semantic, not merely JSON diffs.

ChangeDefault risk
New ConceptLow
New expression resolving to existing ConceptLow to medium
Definition clarification preserving identityMedium
New ambiguityHigh
Ambiguity resolvedMedium
Expression changes target ConceptCritical
Concept deprecatedHigh
Replacement/supersession relation addedHigh
Concept identity withdrawnCritical
Matching algorithm changesCritical

The CLI and API should report those classifications.

Example machine-readable diff:

{
  "from": {
    "version": "2026-08-23.1",
    "digest": "sha256:old..."
  },
  "to": {
    "version": "2026-09-04.1",
    "digest": "sha256:new..."
  },
  "changes": [
    {
      "kind": "expression_resolution_changed",
      "risk": "critical",
      "expression": "example expression",
      "language": "en",
      "before": ["example.a"],
      "after": ["example.b"]
    }
  ]
}

Migration rules

A stable semantic identity system must make an unusually strong promise: migration tooling never silently rewrites ConceptCodes based on fuzzy similarity.

A deprecation can publish:

{
  "code": "acme.old_code",
  "lifecycle": "deprecated",
  "replacements": [
    {
      "conceptCode": "acme.new_code",
      "relationship": "superseded_by"
    }
  ]
}

Then:

es migrate scan

can say:

src/orders.ts:88
  acme.old_code is deprecated
  Suggested governed replacement: acme.new_code
  No change applied.

The developer approves the identity change.

Change events and webhooks

For teams that need automatic notifications, publish CloudEvents. CloudEvents exists to standardize event metadata across event publishers and delivery systems.

Example:

{
  "specversion": "1.0",
  "id": "evt_01K5...",
  "source": "urn:embedded-semantics:registry:public",
  "type": "io.embeddedsemantics.registry.version.published",
  "time": "2026-09-04T14:00:00Z",
  "subject": "public/2026-09-04.1",
  "datacontenttype": "application/json",
  "data": {
    "registryId": "public",
    "version": "2026-09-04.1",
    "digest": "sha256:new...",
    "previousDigest": "sha256:old...",
    "changeSummary": {
      "conceptsAdded": 18,
      "expressionsAdded": 74,
      "ambiguitiesIntroduced": 2
    }
  }
}

Useful event types:

io.embeddedsemantics.registry.version.published
io.embeddedsemantics.registry.concept.changed
io.embeddedsemantics.registry.expression.changed
io.embeddedsemantics.registry.concept.deprecated

Do not emit one event per tiny record change to ordinary public subscribers unless they opt into that granularity. Most developers want “a new registry dependency is available.”

Webhook signatures can use a simple product HMAC scheme initially; an enterprise-grade option can use HTTP Message Signatures, for which RFC 9421 specifies authentication/signing of selected HTTP components.

OpenAPI 3.2 can document the outbound webhook directly, avoiding a second API-description system in the MVP.

OCI registry packaging

Do not require OCI infrastructure for the public MVP. Plain HTTPS package download is substantially simpler.

For enterprise and v2, however, OCI artifacts are an excellent optional distribution transport because organizations already mirror artifact registries into restricted environments. ORAS is expressly designed to push and pull non-container artifacts through OCI registries and supports packaging arbitrary files.

An enterprise could therefore mirror:

registry.acme.example/semantics/public@sha256:...
registry.acme.example/semantics/acme@sha256:...

and optionally verify signed OCI objects with Cosign; Sigstore documents signing and verification of OCI-hosted non-container objects.

SDKs, typed ConceptCodes, and code generation

The SDK is where semantic identity either becomes mundane—in the best sense—or remains specialist infrastructure.

The SDK should make this ordinary:

const result = await es.resolve(text);

if (result.status === "resolved") {
  order.reason = result.concept.code;
}

It should make this difficult:

// Bad architecture:
const vector = await es.embedding(text);
order.reason = vector; // not identity

SDK architecture

Each officially supported SDK should have five layers:

Developer facade
    ↓
Domain model
    ↓
Resolver / Registry abstractions
    ↓
Remote transport OR Local registry store
    ↓
OpenAPI-generated primitives + HTTP/filesystem implementation

Generated transport: request/response serialization, endpoint signatures, basic HTTP plumbing.

Hand-written domain facade: ConceptCode, resolution unions, lockfile behavior, retry rules, cache policies, offline resolver, helpful diagnostics.

Do not expose raw generated OpenAPI classes as the primary public API. Generator naming and ergonomics change too easily.

Core interfaces conceptually look like:

Resolver
  resolve(expression, options) -> Resolution

ConceptRepository
  get(ConceptCode) -> Concept
  validate(ConceptCode) -> ValidationResult

RegistryStore
  open(RegistryPin) -> Registry
  current(registryId) -> RegistryRelease
  update(...) -> RegistryDiff

ConceptCode type design

A ConceptCode is an open-world semantic identifier, similar in application architecture to a URI, UUID, currency code, or external resource ID—not to a compiler enum whose complete population is known at library build time.

The wire form should stay a simple JSON string:

{
  "reason": "semantic_registry.stable_concept_identity"
}

Developers should not have to persist:

{
  "reason": {
    "registryVersion": "...",
    "internalId": "...",
    "display": "...",
    "evidence": "..."
  }
}

unless their business domain genuinely needs those fields.

At runtime, SDKs wrap that string in a lightweight semantic type.

Why a global enum is unsafe

An enum captures a closed known set. JSON Schema explicitly defines enum as a fixed allowed set. C# enums are named constants over an integral underlying type, and Microsoft guidance describes them as appropriate for fixed sets of options.

A registry, by contrast, is designed to grow.

Suppose SDK 1.4 was generated when the registry contained:

concept.a
concept.b

A service then receives:

concept.c

from a newer producer.

With a closed generated enum, deserialization may fail before the application can even choose a forward-compatibility policy. With an open value type, the application safely carries the stable identifier and can validate it against its own pinned registry when appropriate.

Therefore:

Never generate the entire public registry as the canonical ConceptCode enum.

When enum generation is safe

Enums can be generated for a declared ConceptSet whose product semantics are intentionally closed:

conceptSet: acme.return-reasons
registry:
  digest: sha256:...
codes:
  - acme.returns.damaged
  - acme.returns.wrong_item
  - acme.returns.changed_mind
policy:
  closed: true

That is analogous to selecting a bounded set of terminology codes for a particular application context, an approach also seen in FHIR ValueSets.

Even then, generated code should carry the source registry digest so a maintainer knows what the type means.

What code generation should produce

A project config:

embedded-semantics.codegen.yaml

registry:
  lockFile: ./embedded-semantics.lock

generate:
  knownConcepts:
    - semantic_registry.stable_concept_identity
    - semantic_registry.semantic_embedding

  conceptSets:
    - acme.return-reasons

targets:
  - typescript
  - csharp

output:
  typescript: ./src/generated/semantics.ts
  csharp: ./Generated/Semantics.g.cs

should produce:

Open-world constants

export const Concepts = {
  stableConceptIdentity:
    conceptCode("semantic_registry.stable_concept_identity"),
  semanticEmbedding:
    conceptCode("semantic_registry.semantic_embedding"),
} as const;

Closed set only when requested

export const ReturnReasons = {
  damaged: conceptCode("acme.returns.damaged"),
  wrongItem: conceptCode("acme.returns.wrong_item"),
  changedMind: conceptCode("acme.returns.changed_mind"),
} as const;

export type ReturnReason =
  (typeof ReturnReasons)[keyof typeof ReturnReasons];

JSON Schema fragments, generated test fixtures, and provenance comments:

Generated from registry digest sha256:...
Do not edit manually.

The generator must have deterministic name mapping and collision handling. Once:

foo.bar-baz

becomes:

FooBarBaz

that generated symbol must not arbitrarily change because a second Concept later produces the same normalized language identifier. Persist collision decisions in generated metadata.

C# SDK proposal

C# should use a string-backed value type:

public readonly record struct ConceptCode
{
    public string Value { get; }

    public ConceptCode(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
            throw new ArgumentException("ConceptCode cannot be empty.", nameof(value));

        Value = value;
    }

    public override string ToString() => Value;
}

Do not validate registry membership in that constructor. Construction means “this is intended to be a ConceptCode,” not “the current network registry recognizes it.”

Generated constants:

public static class KnownConcepts
{
    public static readonly ConceptCode StableConceptIdentity =
        new("semantic_registry.stable_concept_identity");

    public static readonly ConceptCode SemanticEmbedding =
        new("semantic_registry.semantic_embedding");
}

Resolution should use sealed records:

public abstract record Resolution
{
    public sealed record Resolved(
        Concept Concept,
        MatchInfo Match,
        RegistryCoordinate Registry) : Resolution;

    public sealed record Unknown(
        string Reason,
        RegistryCoordinate Registry) : Resolution;

    public sealed record Ambiguous(
        IReadOnlyList<ConceptCandidate> Candidates,
        RegistryCoordinate Registry) : Resolution;
}

Usage:

Resolution result = await semantics.ResolveAsync(
    "stable concept identity",
    language: "en",
    cancellationToken);

switch (result)
{
    case Resolution.Resolved resolved:
        Console.WriteLine(resolved.Concept.Code);
        break;

    case Resolution.Unknown:
        // Ask user, store raw text, or follow product policy.
        break;

    case Resolution.Ambiguous ambiguous:
        // Present/contextualize candidates.
        break;
}

C#'s built-in enums are backed by integral types, making them particularly awkward as the canonical representation for externally defined string ConceptCodes.

Suggested packages:

EmbeddedSemantics
EmbeddedSemantics.Offline
EmbeddedSemantics.AspNetCore
EmbeddedSemantics.Testing
EmbeddedSemantics.CodeGen

Keep EmbeddedSemantics dependency-light. Offline/package support can be an optional package if its storage dependencies become substantial.

TypeScript SDK proposal

TypeScript should use a branded string:

declare const conceptCodeBrand: unique symbol;

export type ConceptCode = string & {
  readonly [conceptCodeBrand]: "ConceptCode";
};

export function conceptCode(value: string): ConceptCode {
  if (value.length === 0) {
    throw new Error("ConceptCode cannot be empty");
  }
  return value as ConceptCode;
}

Resolution is a discriminated union:

export type Resolution =
  | {
      status: "resolved";
      concept: Concept;
      match: MatchInfo;
      registry: RegistryCoordinate;
    }
  | {
      status: "unknown";
      reason: "unknown_expression";
      registry: RegistryCoordinate;
    }
  | {
      status: "ambiguous";
      reason: "ambiguous_expression";
      candidates: ConceptCandidate[];
      registry: RegistryCoordinate;
    };

Usage:

const result = await es.resolve("stable concept identity", {
  language: "en",
});

switch (result.status) {
  case "resolved":
    saveConcept(result.concept.code);
    break;

  case "unknown":
    saveUnresolvedText("stable concept identity");
    break;

  case "ambiguous":
    showDisambiguation(result.candidates);
    break;
}

For closed generated sets, favor as const objects over TypeScript enum. TypeScript itself describes enums as one of its few constructs that changes JavaScript runtime behavior, and its handbook notes that developers may want to hold off on them unless they are sure they need them; TypeScript explicitly supports enum-like as const object patterns.

Suggested packages:

@embeddedsemantics/sdk
@embeddedsemantics/node
@embeddedsemantics/testkit
@embeddedsemantics/codegen

Browser offline storage can use IndexedDB behind the same RegistryStore abstraction; Node can use a filesystem package cache.

Python SDK proposal

Python can use typing.NewType:

from typing import NewType

ConceptCode = NewType("ConceptCode", str)

Python documents NewType specifically as a low-overhead distinct type for static type checkers while leaving runtime representation essentially unchanged.

Domain models:

from dataclasses import dataclass
from typing import Literal

@dataclass(frozen=True)
class Concept:
    code: ConceptCode
    display: str | None
    definition: str | None

@dataclass(frozen=True)
class Resolved:
    status: Literal["resolved"]
    concept: Concept

@dataclass(frozen=True)
class Unknown:
    status: Literal["unknown"]
    reason: Literal["unknown_expression"]

@dataclass(frozen=True)
class Ambiguous:
    status: Literal["ambiguous"]
    candidates: tuple[Concept, ...]

Usage:

result = client.resolve("stable concept identity", language="en")

match result:
    case Resolved(concept=concept):
        print(concept.code)
    case Unknown():
        handle_unknown()
    case Ambiguous(candidates=candidates):
        choose_candidate(candidates)

Core package:

embedded-semantics

Optional extras:

embedded-semantics[offline]
embedded-semantics[otel]
embedded-semantics[test]

Avoid making Pydantic mandatory for the core client. Provide an optional integration module for organizations already using it.

Rust SDK proposal

Rust is an excellent fit for a newtype:

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[serde(transparent)]
pub struct ConceptCode(String);

Rust's official documentation describes the newtype idiom as providing compile-time guarantees that otherwise similar underlying values are not confused, while Serde's transparent representation allows a one-field newtype to serialize like its wrapped value.

impl ConceptCode {
    pub fn new(value: impl Into<String>) -> Result<Self, ConceptCodeError> {
        let value = value.into();

        if value.is_empty() {
            return Err(ConceptCodeError::Empty);
        }

        Ok(Self(value))
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

Resolution:

pub enum Resolution {
    Resolved {
        concept: Concept,
        registry: RegistryCoordinate,
    },
    Unknown {
        reason: UnknownReason,
        registry: RegistryCoordinate,
    },
    Ambiguous {
        candidates: Vec<ConceptCandidate>,
        registry: RegistryCoordinate,
    },
}

Usage:

match client.resolve("stable concept identity", Some("en")).await? {
    Resolution::Resolved { concept, .. } => {
        println!("{}", concept.code.as_str());
    }
    Resolution::Unknown { .. } => handle_unknown(),
    Resolution::Ambiguous { candidates, .. } => choose(candidates),
}

Do not generate a Rust enum for the entire registry. The newtype preserves arbitrary future codes without a special “Unknown” enum variant that loses the original wire value.

Suggested crates:

embedded-semantics
embedded-semantics-offline
embedded-semantics-testkit

Generated API versus generated registry code

Keep these concepts separate:

OpenAPI generation
    changes when HTTP contract changes

Registry code generation
    changes when selected registry concepts change

A registry update should never force regeneration of unrelated HTTP transport code. Conversely, a new optional API endpoint should not churn every application's generated Concept constants.

CLI, testing, IDE support, documentation, and local development

A high-quality CLI is not secondary to the SDK. It is the bridge between registry governance and ordinary development workflows.

CLI design

Binary name:

es

Primary commands:

es resolve
es concept
es validate
es registry
es cache
es schema
es generate
es migrate
es test
es serve
es auth
es doctor

Examples:

es resolve "stable concept identity" --lang en
RESOLVED
ConceptCode  semantic_registry.stable_concept_identity
Registry     public @ 2026-08-23.1
Method       reviewed_exact
es resolve "bank" --lang en
AMBIGUOUS
2 governed concepts match the expression.

example.financial.bank       Financial institution
example.geography.river_bank River bank
es resolve "unseen wording" --lang en
UNKNOWN
No governed exact expression exists in the pinned registry.

--json must emit the exact machine contract:

es resolve "..." --json | jq '.status'

Other important commands:

es concept get semantic_registry.stable_concept_identity
es concept get ... --include evidence

es validate semantic_registry.stable_concept_identity

es registry list
es registry pull
es registry pin public
es registry verify
es registry check
es registry diff
es registry update
es registry vendor

es cache status
es cache prune

es schema validate payload.json --schema event.schema.json

es generate
es generate --check

es migrate scan .
es migrate replay testdata/semantic-corpus.jsonl --to VERSION

es test
es serve
es doctor

Suggested stable process exit codes:

0  operation successful / expression resolved / ConceptCode valid
1  operational failure
3  expression unknown
4  expression ambiguous
5  ConceptCode invalid

That lets shell users write deterministic pipelines without parsing prose.

Local development mode

es serve should be a major feature:

es serve --lock ./embedded-semantics.lock
Embedded Semantics local server
Registry: public
Version:  2026-08-23.1
Digest:   sha256:8a42...
API:      http://127.0.0.1:8765/v1

It exposes the same REST API from the downloaded package:

POST /v1/resolve
POST /v1/validate
GET  /v1/concepts/{code}

No credentials are required when bound to loopback.

Applications switch via one variable:

ES_BASE_URL=http://127.0.0.1:8765

This is crucial for:

  • deterministic tests;
  • air-gapped development;
  • CI;
  • reproducing customer failures;
  • development when the hosted API is unavailable;
  • comparing old and new registry releases.

Testing toolkit

Publish a language-neutral conformance corpus.

Example:

{
  "schema": "embedded-semantics.conformance.v1",
  "registryDigest": "sha256:...",
  "cases": [
    {
      "name": "known English expression",
      "request": {
        "expression": "stable concept identity",
        "language": "en"
      },
      "expect": {
        "status": "resolved",
        "conceptCode": "semantic_registry.stable_concept_identity"
      }
    },
    {
      "name": "unknown expression",
      "request": {
        "expression": "not in registry",
        "language": "en"
      },
      "expect": {
        "status": "unknown"
      }
    }
  ]
}

Every hosted release and every SDK must run exactly this corpus.

The corpus needs deliberate cases for:

CaseWhy
Reviewed exact matchHappy path
UnknownAbstention is part of correctness
Exact ambiguityPrevent accidental first-wins logic
Multiple languagesCore project promise
Wrong language constraintLanguage handling
Unicode composed/decomposed formsMatching-algorithm compatibility
Deprecated ConceptCodeLifecycle handling
Unknown ConceptCodeForward/open-world semantics
Registry digest mismatchPin correctness
Corrupt package digestSupply-chain correctness
HTTP 429Retry behavior
HTTP 503Transient failure behavior

Unicode cases are especially important because canonical-equivalence and normalization behavior can differ unless explicitly standardized.

SDK testkit

Each language package should provide an in-memory registry:

TypeScript:

const registry = TestRegistry.create({
  concepts: [
    concept("test.shipping.delayed", {
      definition: "A shipment is delayed",
    }),
  ],
  expressions: [
    expression("shipment late", "en", "test.shipping.delayed"),
  ],
});

const es = EmbeddedSemantics.fromRegistry(registry);

Then:

expect(
  await es.resolve("shipment late", { language: "en" })
).toMatchObject({
  status: "resolved",
  concept: {
    code: "test.shipping.delayed",
  },
});

No test should require a live public endpoint unless it is explicitly an integration test.

Upgrade replay testing

This is one of the most important platform features.

An application records representative production inputs without requiring telemetry upload:

{"expression":"shipment late","language":"en"}
{"expression":"damaged package","language":"en"}
{"expression":"entrega retrasada","language":"es"}

Then:

es migrate replay semantic-corpus.jsonl \
  --from ./old.lock \
  --to ./candidate.lock

Result:

50000 inputs compared

49280 unchanged
  410 unknown -> resolved
  180 unknown -> ambiguous
  120 ambiguous -> resolved
    8 resolved -> ambiguous
    2 resolved -> different ConceptCode

HIGH-RISK CHANGES: 10

This turns semantic-registry upgrades into testable software changes instead of governance guesswork.

Contract testing

Publish a small Docker/OCI test server eventually, but do not require containers.

Simpler MVP:

es serve --fixture ./registry.fixture.json

Then consumer contract tests can target localhost.

The hosted service should also have a non-production sandbox registry:

registry: sandbox

with intentionally stable fixtures for resolved, ambiguous, deprecated, and unknown behavior.

Never make examples depend exclusively on mutable production data.

IDE/editor tooling

The first layer of IDE support comes “for free” from generated language constants.

Typing:

Concepts.sta...

should autocomplete:

stableConceptIdentity

The second layer should be a small language server, es-lsp, reading the lockfile and local package.

Capabilities:

Hover

semantic_registry.stable_concept_identity

Stable concept identity

Persistent semantic identity...
Registry: public @ 2026-08-23.1
Status: active

Diagnostics

Unknown ConceptCode in pinned registry.
Deprecated ConceptCode; governed replacement is available.

Go to definition

Navigate to the local Concept JSON/virtual registry document.

Completion

Inside supported JSON/YAML schema fields:

{
  "conceptCode": "semantic_registry.sta..."
}

Code actions

Show provenance
Resolve selected expression
Replace deprecated ConceptCode
Compare against latest registry

Registry upgrade UI

A developer hovering a changed ConceptCode in a lockfile diff should see whether its definition, expression evidence, or lifecycle changed.

Initial editor support should target VS Code through LSP; JetBrains and other editors can reuse the protocol.

Documentation architecture

Documentation should be task-first:

Quickstart
Resolve text
Handle unknown and ambiguity
Use ConceptCodes
Validate stored codes
Use ConceptCodes in JSON Schema
Pin a registry
Work offline
Upgrade a registry safely
Inspect provenance
Test an integration
Enterprise/private registries
API reference
SDK reference
Registry governance
Research

“Ontology,” “embedding geometry,” “cross-lingual alignment,” and research methodology should not appear before a developer has completed the basic resolver flow unless specifically relevant.

Embedded Semantics already labels its FAQ “answer-first documentation” and clearly separates production behavior from research; the developer portal should extend that discipline.

Quick-start experience

The ideal TypeScript quickstart should fit on one screen.

Install:

npm install @embeddedsemantics/sdk

Resolve:

import { EmbeddedSemantics } from "@embeddedsemantics/sdk";

const es = new EmbeddedSemantics();

const result = await es.resolve("stable concept identity", {
  language: "en",
});

switch (result.status) {
  case "resolved":
    console.log(result.concept.code);
    break;

  case "unknown":
    console.log("No governed meaning is known.");
    break;

  case "ambiguous":
    console.log("More context is required.");
    break;
}

Then pin:

npx es registry pin public

And go offline:

const es = await EmbeddedSemantics.offline({
  lockFile: "./embedded-semantics.lock",
});

The key DX choice is that the first example includes all three domain outcomes. Unknown and ambiguity must not be buried in a later “error handling” chapter because they are part of the semantic contract, not exceptional failures. Embedded Semantics itself explicitly treats unknown/ambiguity as expected abstention states.

Before publishing that tutorial, however, the deployed public registry must actually contain the starter records. The site presently exposes zero published Concepts despite its 11-Concept prerelease bundle.

Enterprise integration, security, rate limits, and observability

Public authentication strategy

Public immutable registry packages and public Concept GETs should be anonymously accessible and CDN-cacheable.

A developer should be able to:

es registry pull public

before obtaining credentials.

For hosted resolution:

  • allow a modest anonymous quota;
  • support simple project API keys for server-side developer accounts;
  • reserve OAuth for organizations needing identity federation, scopes, and machine-to-machine policy.

Do not force an OAuth ceremony on someone completing the first quickstart.

Enterprise authentication

For private registries, standardize on OAuth 2.0-compatible bearer access with current security practices, rather than inventing a custom signed-token protocol. RFC 9700 is the current OAuth 2.0 security Best Current Practice and updates the older OAuth threat guidance.

Typical service-to-service flow:

CI/backend
   -> enterprise authorization server
   -> scoped access token
   -> private Embedded Semantics API

Suggested scopes:

semantics.resolve
semantics.concepts.read
semantics.registry.read
semantics.registry.admin
semantics.webhooks.manage

High-assurance enterprises may require mutual-TLS OAuth. RFC 8705 defines OAuth client authentication and certificate-bound tokens using mTLS.

Private deployments should publish OAuth protected-resource metadata to simplify client discovery where appropriate; RFC 9728 standardizes that metadata model.

Registry namespaces and private identity

Private registries create the most serious identity-design problem: two registries must not independently define the same bare ConceptCode and let runtime configuration determine what it means.

The platform therefore needs a canonical registry coordinate:

{
  "registry": "com.acme.semantics",
  "code": "orders.return_authorized"
}

or a globally namespace-owned code:

com.acme.orders.return_authorized

For developer simplicity, a single-registry application may usually manipulate just:

ConceptCode

but federation infrastructure must always know the identity namespace.

The recommended rule:

A registry may only author new ConceptCodes in namespaces it owns.

An enterprise may add private expressions for a public Concept without redefining that Concept:

{
  "registry": "com.acme.semantics",
  "expression": "green-lit for shipment",
  "language": "en",
  "mapsTo": {
    "registry": "public",
    "conceptCode": "example.shipping.approved"
  }
}

That is an extension of evidence, not an override of identity.

Federation semantics

Configuration:

registries:
  - id: public
    endpoint: https://api.example
    lock: ./public.lock

  - id: com.acme.semantics
    endpoint: https://semantics.acme.example
    lock: ./acme.lock

Do not resolve by “first registry wins.”

Instead:

expression
   ↓
exact evidence across active registries
   ↓
all matching canonical ConceptRefs

Outcomes:

no ConceptRefs                 -> unknown
one ConceptRef                 -> resolved
multiple evidence records
  for same ConceptRef          -> resolved + merged provenance
multiple different ConceptRefs -> ambiguous

This preserves the system's explicit abstention philosophy even across enterprise overlays.

Air-gapped operation

Private registries should support complete operation without the public hosted service:

Public release
   ↓ mirror approval
Enterprise artifact registry
   ↓
CI / production / developer machines

OCI/ORAS is a good optional v2 transport because ORAS supports non-container artifact push/pull through OCI infrastructure.

The package itself remains the same logical registry package regardless of whether it arrived by public HTTPS, an enterprise artifact manager, a filesystem share, or OCI.

Rate-limit strategy

Exact registry resolution should be cheap enough that the commercial model should encourage local/offline usage rather than monetizing lookup scarcity.

Separate limit buckets:

metadata/read operations
resolve operations
batch operations
administrative operations
webhook configuration

Package downloads should be CDN-friendly rather than charged as resolver calls.

On throttling:

HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/problem+json

RFC 6585 defines 429 Too Many Requests, while HTTP Retry-After expresses how long the client should wait.

As of August 23, 2026, the proposed standardized RateLimit and RateLimit-Policy headers are still an IETF Internet-Draft rather than a final RFC; the May 2026 draft defines both headers and is scheduled to expire in November 2026. They can be emitted opportunistically, but SDK correctness should not depend on them until standardized.

SDK retry policy:

429               retry, honor Retry-After
502 / 503 / 504   retry with exponential backoff + jitter
400 / 401 / 403   never automatic retry
409 pin conflict  never automatic retry

Because resolve is logically read-only, repeating it is safe from an application-state standpoint. Clients still need bounded retry limits.

Package integrity and signing

MVP:

HTTPS
+ published SHA-256 digest
+ local verification

V2/enterprise:

digest
+ publisher signature
+ enterprise trust policy

OCI mirrors can use existing artifact-signing ecosystems. Cosign supports signing and verification of OCI-stored artifacts.

A lockfile should optionally record signer policy:

{
  "registry": "com.acme.semantics",
  "digest": "sha256:...",
  "trust": {
    "requireSignature": true,
    "issuer": "https://id.acme.example",
    "subject": "semantic-registry-publisher"
  }
}

Telemetry and observability

SDKs should support OpenTelemetry hooks without taking a mandatory dependency on an exporter. OpenTelemetry defines instrumentation libraries and tracing attributes as the mechanism for attaching useful operation metadata.

Suggested span:

embedded_semantics.resolve

Safe default attributes:

embedded_semantics.operation = "resolve"
embedded_semantics.resolution.status = "resolved" | "unknown" | "ambiguous"
embedded_semantics.registry.id = "public"
embedded_semantics.registry.version = "2026-08-23.1"
embedded_semantics.cache.hit = true
embedded_semantics.mode = "offline"
server.address = ...
http.response.status_code = ...

Do not include by default:

expression
definition
full candidate list
private Concept labels
authorization token

Expressions may contain customer, medical, legal, financial, or otherwise sensitive business text. OpenTelemetry itself emphasizes that instrumenters are responsible for identifying and protecting sensitive data; the observability framework cannot decide what is sensitive for them.

Even hashing raw expressions should not be a default privacy measure: low-entropy or dictionary-like expressions can sometimes be guessed and rehashed. Prefer aggregate outcome counters.

Useful metrics:

semantic_resolutions_total{status,mode}
semantic_resolution_duration
semantic_registry_cache_hits_total
semantic_registry_package_load_duration
semantic_registry_integrity_failures_total
semantic_http_requests_total{status}

Avoid ConceptCode as a metric label in large registries; it can create unbounded/high-cardinality telemetry.

Every hosted response should return a request correlation identifier:

X-Request-Id: req_01K...

and propagate standard distributed tracing context where present.

Enterprise audit logs

Administrative actions—not ordinary expression text—should have strong audit records:

registry release published
registry release promoted
registry package mirrored
private Concept created
Concept deprecated
expression evidence approved/revoked
webhook created/deleted
trust policy changed

Audit records should identify:

actor
action
registry
previous digest
new digest
timestamp
request/trace ID

This provides enterprise governance without turning every semantic lookup into sensitive audit content.

Adoption obstacles, migration strategy, and platform roadmap

Principal adoption obstacles

The live registry is currently empty. This is the immediate blocker. The site advertises stable Concept resolution but currently reports 0/11 governed bootstrap Concept coverage and 0/55 expression coverage. A developer platform should not launch until the canonical quickstart works against production.

Semantic terminology can leak into everyday programming. Developers will disengage if routine integration asks them to understand “semantic residue,” vector calibration, equivalence evidence, or ontology governance. The solution is not better ontology tutorials; it is making those concepts unnecessary for ordinary API use.

Unknown feels like failure unless the SDK teaches otherwise. The API and quickstarts must normalize the three-way result from the beginning. This is aligned with the project's own “Unknown is a valid result” principle.

Registry upgrades can change behavior without changing code. Lockfiles, digests, diffing, and replay testing turn that hidden dependency into an explicit one.

Generated enums create false safety. A compile-time closed registry type looks attractive but harms forward compatibility. Generate open constants by default and closed sets only on explicit request.

A hosted-only resolver creates reliability and procurement friction. Offline packages eliminate network latency, service outages, data-egress concerns, and many enterprise security objections.

Provenance can overwhelm the core API. Keep a compact provenance pointer in normal resolution and full evidence behind opt-in expansion.

Private registries can destroy global identity if “override” semantics are allowed. Namespace ownership and ambiguity-preserving federation are mandatory.

Experimental semantic retrieval can contaminate the stable product. Preserve a hard namespace and contract boundary until retrieval quality/calibration is production-governed. Embedded Semantics currently makes exactly that production/research distinction.

Migration tooling

The full tool suite should include:

es registry diff

Understands semantic changes.

es migrate scan .

Finds ConceptCodes in source, JSON, YAML, and configured data formats, then checks them against a target release.

es migrate replay corpus.jsonl

Compares resolution behavior across releases.

es migrate deprecated

Produces governed replacements without applying them.

es generate --check

Fails CI when generated selected-Concept bindings are stale.

es registry verify

Fails CI when the package no longer matches the lock digest.

A CI job might be:

steps:
  - run: es registry verify
  - run: es generate --check
  - run: es schema validate fixtures/*.json
  - run: es test

A registry-update bot can submit a pull request containing:

embedded-semantics.lock
registry-diff.json
registry-diff.md
semantic-replay.json
generated bindings

That is the right interaction model: a semantic upgrade looks like a dependency upgrade with a domain-aware changelog.

The MVP should be aggressively constrained.

ComponentMVP recommendation
Deployed registryRequired first: publish the existing governed bootstrap so quickstarts resolve
REST/resolve, /validate, /concepts/{code}, registry-release metadata/package
Result model`resolved
Error modelRFC 9457 Problem Details
Registry identityregistryId + opaque version + SHA-256 digest
PackageExisting pack concept evolved into stable Registry Package v1
Lockfileembedded-semantics.lock
OfflineFirst-class exact resolver
CLIresolve, validate, registry pull/pin/verify/diff, serve, test
OpenAPICanonical OAS 3.2 contract
JSON SchemaDraft 2020-12 annotations; no custom vocabulary
TypeScriptGA SDK
PythonGA SDK
C#GA SDK
RustPreview or GA if maintenance capacity exists
TestingCross-language conformance package and in-memory fixtures
DocumentationQuickstart, handling unknown/ambiguity, pinning, offline
AuthAnonymous public reads; API key hosted resolver
GraphQLNo
WebhooksNot required for first MVP
OCINot required
IDE LSPNot required; generated-code autocomplete is enough initially
Experimental candidatesSeparate from stable API

The most important MVP acceptance test is:

fresh developer machine
   ↓
install one SDK
   ↓
copy 15-line quickstart
   ↓
resolve a published expression
   ↓
receive a stable ConceptCode
   ↓
pin the registry
   ↓
run the same call offline

If that cannot happen in roughly five minutes without semantic-domain knowledge, the platform is not ready.

V2 should deepen operations rather than widen semantic theory.

Add:

ConceptSets. First-class closed subsets for business schemas, generated validation, and carefully safe enum-like code generation.

Change subscriptions. CloudEvents webhooks plus subscription management.

Signed packages and OCI mirroring. Especially for regulated and air-gapped enterprises.

Federated public/private registries. With canonical registry coordinates and ambiguity-preserving resolution.

LSP/editor tooling. Registry-aware hover, completion, deprecation diagnostics, and upgrade previews.

Batch resolution/validation. For data pipelines.

Advanced migration replay. Production-corpus comparison, policy thresholds, machine-readable CI gates.

Provenance graph. Richer evidence browsing without bloating normal resolver responses.

Organization governance APIs. Publishing workflow, approval, promotion, audit.

Read-only GraphQL only if evidence supports it. Particularly for graph exploration; not as an SDK dependency.

Production model-backed candidate resolution only after it meets the project's own quality/calibration gates. When that happens, preserve exact-first authority and explicit abstention rather than retrofitting probabilistic guesses into the exact resolver. The current project documentation explicitly says unseen-query retrieval and confidence calibration remain experimental.

The overall system should converge on:

                           ┌────────────────────────┐
                           │ Registry governance    │
                           │ review / publish       │
                           └───────────┬────────────┘
                                       │
                              immutable release
                                       │
                    ┌──────────────────▼──────────────────┐
                    │ Canonical Registry Package         │
                    │ version + digest + provenance      │
                    └───────┬───────────────┬────────────┘
                            │               │
                      public HTTPS     enterprise mirror
                            │               │
              ┌─────────────▼───┐       ┌──▼──────────────┐
              │ Hosted REST API │       │ OCI/filesystem  │
              └──────┬──────────┘       └──┬──────────────┘
                     │                     │
          ┌──────────▼─────────────────────▼──────────┐
          │ Common SDK domain model                  │
          │ ConceptCode / Resolution / RegistryPin   │
          └───────┬──────────────────┬───────────────┘
                  │                  │
             RemoteResolver     OfflineResolver
                  │                  │
                  └────────┬─────────┘
                           │
                       Application

The architectural invariant is:

same registry digest
+ same resolver mode
+ same request
= same semantic result

That invariant should be tested across every language SDK.

Prototype-ready OpenAPI fragment

The following is intentionally compact but sufficient for another team to prototype the core API:

openapi: 3.2.0

info:
  title: Embedded Semantics API
  version: 1.0.0

servers:
  - url: https://api.embeddedsemantics.com

paths:
  /v1/resolve:
    post:
      operationId: resolveExpression
      summary: Resolve a governed expression
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveRequest'
      responses:
        '200':
          description: Resolution completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Resolution'
        '400':
          $ref: '#/components/responses/Problem'
        '429':
          $ref: '#/components/responses/Problem'
        '503':
          $ref: '#/components/responses/Problem'

  /v1/validate:
    post:
      operationId: validateConceptCode
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidateRequest'
      responses:
        '200':
          description: Validation completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '400':
          $ref: '#/components/responses/Problem'

  /v1/concepts/{conceptCode}:
    get:
      operationId: getConcept
      parameters:
        - name: conceptCode
          in: path
          required: true
          schema:
            type: string
        - name: registryVersion
          in: query
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Concept found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Concept'
        '404':
          $ref: '#/components/responses/Problem'

  /v1/registries/{registryId}:
    get:
      operationId: getRegistry
      parameters:
        - name: registryId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Registry metadata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegistryRelease'

components:
  schemas:
    RegistryPin:
      type: object
      properties:
        id:
          type: string
        version:
          type: string
        digest:
          type: string

    RegistryCoordinate:
      type: object
      required:
        - id
        - version
        - digest
      properties:
        id:
          type: string
        version:
          type: string
        digest:
          type: string

    ResolveRequest:
      type: object
      required:
        - expression
      properties:
        expression:
          type: string
          minLength: 1
        language:
          type: string
        registry:
          $ref: '#/components/schemas/RegistryPin'
        include:
          type: array
          items:
            enum:
              - provenance
              - evidence

    Concept:
      type: object
      required:
        - code
        - definition
        - lifecycle
      properties:
        code:
          type: string
        display:
          type:
            - string
            - 'null'
        definition:
          type: string
        lifecycle:
          enum:
            - active
            - deprecated
            - retired

    MatchInfo:
      type: object
      required:
        - method
      properties:
        method:
          enum:
            - reviewed_exact
        evidenceId:
          type: string

    ConceptCandidate:
      type: object
      required:
        - conceptCode
      properties:
        conceptCode:
          type: string
        display:
          type:
            - string
            - 'null'

    ResolvedResolution:
      type: object
      required:
        - status
        - concept
        - registry
      properties:
        status:
          const: resolved
        concept:
          $ref: '#/components/schemas/Concept'
        match:
          $ref: '#/components/schemas/MatchInfo'
        registry:
          $ref: '#/components/schemas/RegistryCoordinate'

    UnknownResolution:
      type: object
      required:
        - status
        - reason
        - registry
      properties:
        status:
          const: unknown
        reason:
          const: unknown_expression
        registry:
          $ref: '#/components/schemas/RegistryCoordinate'

    AmbiguousResolution:
      type: object
      required:
        - status
        - reason
        - candidates
        - registry
      properties:
        status:
          const: ambiguous
        reason:
          const: ambiguous_expression
        candidates:
          type: array
          minItems: 2
          items:
            $ref: '#/components/schemas/ConceptCandidate'
        registry:
          $ref: '#/components/schemas/RegistryCoordinate'

    Resolution:
      oneOf:
        - $ref: '#/components/schemas/ResolvedResolution'
        - $ref: '#/components/schemas/UnknownResolution'
        - $ref: '#/components/schemas/AmbiguousResolution'
      discriminator:
        propertyName: status

    ValidateRequest:
      type: object
      required:
        - conceptCode
      properties:
        conceptCode:
          type: string
        registry:
          $ref: '#/components/schemas/RegistryPin'

    ValidationResult:
      type: object
      required:
        - valid
      properties:
        valid:
          type: boolean
        reason:
          enum:
            - unknown_concept_code
            - invalid_concept_code_syntax
            - deprecated_concept
        conceptCode:
          type: string
        registry:
          $ref: '#/components/schemas/RegistryCoordinate'

    RegistryRelease:
      type: object
      required:
        - id
        - version
        - digest
      properties:
        id:
          type: string
        version:
          type: string
        digest:
          type: string
        packageSchema:
          type: string

    Problem:
      type: object
      required:
        - type
        - title
        - status
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        code:
          type: string
        requestId:
          type: string

  responses:
    Problem:
      description: API error
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'

OpenAPI is explicitly designed to describe HTTP interfaces in a machine-readable, language-neutral form, making this specification appropriate as the source for generated transport models and API documentation.

Prototype server behavior

A prototype implementation can use a tiny in-memory index:

key:
  (registryDigest, language, exactMatchKey)

value:
  [ConceptCode, ...]

Pseudocode:

resolve(expression, language, pin):
    registry = load_registry(pin)

    key = registry.matching.compute_key(
        expression,
        language
    )

    matches = registry.expression_index[key]

    if matches is empty:
        return Unknown(
            reason = "unknown_expression",
            registry = registry.coordinate
        )

    uniqueConcepts = distinct(matches.conceptCode)

    if uniqueConcepts.count > 1:
        return Ambiguous(
            candidates = lookup(uniqueConcepts),
            registry = registry.coordinate
        )

    concept = registry.concepts[uniqueConcepts[0]]

    return Resolved(
        concept = concept,
        match = reviewed_exact,
        registry = registry.coordinate
    )

Critically, there is no embedding call in this production algorithm. That matches the project's currently declared production boundary.

Definition of platform success

The public platform succeeds when an ordinary developer can treat semantic identity as:

a stable string ID
+ a resolver
+ a validator
+ a pinned data dependency

and can ignore everything else until they need it.

The final product experience should therefore feel closer to:

const result = await semantics.resolve(userText);

than to:

configure ontology
choose embedding model
select similarity metric
tune threshold
interpret nearest neighbors
decide whether score constitutes identity

That is not merely a DX simplification. It preserves Embedded Semantics' own architectural premise: model representations may change, while governed Concept identity remains stable.

Bibliography

Embedded Semantics. Stable concept identity across languages. Defines registry-backed multilingual identity, stable ConceptCodes, reviewed exact resolution, vectors as evidence rather than authority, and the current production architecture.

Embedded Semantics. Registry & Runtime Status and machine-readable status. Documents the August 23, 2026 deployment state: zero published Concepts, zero reviewed expressions, 0/11 bootstrap Concept coverage, 0/55 expression coverage, exact registry-first resolver mode, and inactive semantic runtime.

Embedded Semantics. Public Registry Bundle Manifest. Publishes the deterministic prerelease bundle containing 11 Concepts and 55 expressions, bundle/source-pack SHA-256 digests, and heterogeneous release labels.

Embedded Semantics. Research & Methodology. Defines exact production resolution, experimental unseen-query retrieval, abstention, evaluation boundaries, and independent provenance tracking.

Embedded Semantics. Frequently Asked Questions. Documents unknown_expression, ambiguous_expression, multilingual ConceptCode identity, and abstention rather than guessing.

Embedded Semantics. llms.txt. Lists the public Concept API, status API, Registry Pack JSON Schema, registry packs, resolver endpoint, and the project's authority/evidence distinction.

IETF, RFC 9457. Problem Details for HTTP APIs. Basis for the proposed transport error format.

OpenAPI Initiative. OpenAPI Specification 3.2.0. Basis for the canonical HTTP contract, generated clients, JSON Schema-aligned models, and webhook description.

JSON Schema. Draft 2020-12 Validation and enum documentation. Basis for schema integration, the distinction between format annotation/assertion, and the recommendation that enums represent only closed ConceptSets.

IETF, RFC 9110 and RFC 9111. HTTP Semantics and HTTP Caching. Basis for validators, conditional requests, cache revalidation, and Retry-After.

IETF, RFC 9530. Digest Fields. Basis for optional HTTP content/representation integrity metadata on registry packages.

IETF, RFC 6585. Additional HTTP Status Codes. Defines 429 Too Many Requests.

IETF HTTPAPI Working Group. RateLimit header fields for HTTP, draft revision 11, May 2026. Defines proposed RateLimit and RateLimit-Policy fields; still an Internet-Draft as of the report date.

IETF, RFC 9700. Best Current Practice for OAuth 2.0 Security. Basis for private/enterprise OAuth deployment recommendations.

IETF, RFC 8705. OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens. Basis for optional high-assurance enterprise authentication.

IETF, RFC 9728. OAuth 2.0 Protected Resource Metadata. Basis for OAuth client discovery around private registries.

IETF, RFC 9421. HTTP Message Signatures. Basis for an interoperable future webhook-signature option.

Cloud Native Computing Foundation. CloudEvents Specification. Basis for registry-update event envelopes.

Unicode Consortium. Unicode Standard Annex #15: Unicode Normalization Forms. Basis for keeping Unicode matching rules in the registry contract rather than allowing each SDK to improvise normalization.

IETF, BCP 47 / RFC 5646. Tags for Identifying Languages. Basis for standardized language-tag representation.

HL7 FHIR R5. Terminology Service, CodeSystem, and ValueSet. Provides mature terminology-system precedents for code validation and separating a code system from contextual sets of allowed codes.

ORAS Project. OCI Registry As Storage. Basis for optional enterprise distribution of registry packages as non-container OCI artifacts.

Sigstore. Cosign signing documentation. Basis for optional signed OCI registry packages.

Microsoft. C# enumeration types. Establishes C# enums as integral named-constant types intended for fixed sets, supporting the recommendation to use string-backed ConceptCode value types instead.

TypeScript. Handbook: Enums and as const enum-like patterns. Supports using branded strings and constant objects rather than global runtime enums.

Python Software Foundation. typing.NewType. Supports a low-overhead, statically distinct ConceptCode string type.

Rust Project and Serde. New Type Idiom and #[serde(transparent)]. Supports an open-world string-backed ConceptCode newtype with wire-compatible serialization.

OpenTelemetry. Overview, tracing, and handling sensitive data. Basis for optional SDK observability and the recommendation not to capture raw expressions in default telemetry.