From 0d7ca53fdf94b595955ce02eb13f872e6b67b2c4 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Tue, 20 Oct 2026 14:24:49 -0400 Subject: [PATCH 1/2] chore(docs): prevent ambiguous specification and API references Signed-off-by: Yordis Prieto --- docs/README.md | 2 + .../architecture-resource-roadmap.md | 30 +- .../specification-and-api-acceptance.md | 119 ++++++ .../specification-and-api-contracts.md | 367 ++++++++++++++++++ 4 files changed, 505 insertions(+), 13 deletions(-) create mode 100644 docs/reference/specification-and-api-acceptance.md create mode 100644 docs/reference/specification-and-api-contracts.md diff --git a/docs/README.md b/docs/README.md index 6e853657..da6a628c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -34,6 +34,8 @@ Resource contracts and acceptance: - [Channel and DataStore contracts](reference/channel-and-datastore-contracts.md) - [Channel and DataStore acceptance fixture](reference/channel-and-datastore-acceptance.md) +- [Specification and API contracts](reference/specification-and-api-contracts.md) +- [Specification and API acceptance criteria](reference/specification-and-api-acceptance.md) ## Explanation diff --git a/docs/explanation/architecture-resource-roadmap.md b/docs/explanation/architecture-resource-roadmap.md index 408469e7..d5eade1e 100644 --- a/docs/explanation/architecture-resource-roadmap.md +++ b/docs/explanation/architecture-resource-roadmap.md @@ -34,29 +34,33 @@ the fitness host's reference adapter still need their own supported representati Implementing a new association requires extending reference discovery, validation and lifecycle handling together. -## Deliver a connected resource slice +## Implemented transport and storage resources Channel and DataStore include typed values, identified associations and nested reference handling. The acceptance fixture covers repeated observations, exact versions, reference discovery, guarded mutations and branch behavior. -Implemented scope: - -- [x] Add entity kinds, typed references and generated bindings. -- [x] Admit writes and resolve associations through the authorized backend. -- [x] Index nested references for queries, reverse references and impact. -- [x] Preserve identity through edit, copy, retarget and branch operations. -- [x] Exercise the connected fixture through public service and CLI/MCP paths. -- [x] Expose the resources and diagnostics in Studio's review surfaces. - Runtime evidence comes from the linked executable tests. Component review coverage does not establish deployed-browser or production readiness. +## Next resource slice: Specification and API contracts + +The [Specification/API contract](../reference/specification-and-api-contracts.md) +defines the next native resource slice. Its schema and runtime implementation +are pending. The [acceptance fixture](../reference/specification-and-api-acceptance.md) +records required queries, identity changes, authorization and lifecycle cases; +it does not claim executable evidence. + +Implementation must add the entity/member representations, identified +associations, authorized admission, exact reference discovery and lifecycle +handling together. Native manifest, CLI/MCP and Studio support must preserve +those selections. No source connector or additional format compiler is required +by this slice. + ## Follow-on contracts -Specification/API resources need explicit operation, message and schema-member -identity, with request and response roles kept separate. Team and Person need -exact-version versus family responsibility, retained directory data and a clear +Team and Person need exact-version versus family responsibility, retained +directory data and a clear separation from authorization principals. DataProduct needs ReadModel output ports, lineage and selected access paths tied to the same output. diff --git a/docs/reference/specification-and-api-acceptance.md b/docs/reference/specification-and-api-acceptance.md new file mode 100644 index 00000000..4eb841c8 --- /dev/null +++ b/docs/reference/specification-and-api-acceptance.md @@ -0,0 +1,119 @@ +# Specification and API acceptance fixture + +Status: required acceptance criteria for the +[Specification/API contract](specification-and-api-contracts.md). The resource +schema, service implementation, CLI/MCP support and Studio surfaces are pending. +This fixture describes required outcomes; it is not executable test evidence. + +## Connected fixture + +Use a fresh namespace for each mutation case. Every model reference includes an +exact version. Names below are fixture labels; retained member and association +identities come from the authoritative write response. + +| Resource | Required declaration | +| --- | --- | +| `order-request@1` | Integration ReadModel containing a submitted order. | +| `order-receipt@1` | ReadModel containing the receipt exposed by the API response. | +| `order-adapter@1` | Processor observing `order-request@1` and issuing the local `place-order@1` Command through an AutomationSlice. | +| `order-intake@1` | The AutomationSlice containing that exact ReadModel observation and paired Processor; retain its occurrence ID for the Channel selection. | +| `place-order@1`, `order-placed@1` | Local Command and Event connected through the ordinary CommandSlice grammar. | +| `receipt-projection@1` | ReadModelSlice projecting `order-placed@1` into `order-receipt@1`. | +| `order-api@1` | Specification retaining a contract artifact and declaring schema, message and operation members. | +| Request and response messages | Distinct members mapped to `order-request@1` and `order-receipt@1`, respectively. Both may select the same schema member; schema reuse does not merge their roles or ReadModels. | +| Submit operation | Select the request member as REQUEST and the receipt member as RESPONSE, with separate occurrence IDs. Its binding describes the API transport. | +| Another operation | Reuse the receipt message in another RESPONSE occurrence with its own association identity. | +| `public-gateway@1`, `partner-gateway@1` | Components with separate explicit exposures of the submit operation. A provider list alone is insufficient. | +| `checkout@1` | Caller Component with a usage selecting the submit operation and only the public-gateway exposure. | +| Unspecified-provider usage | Another usage of the same operation without selecting an exposure. Its provider remains unspecified. | +| `order-types@1` | Existing TypeLibrary available for a separate schema binding selecting a fully qualified protobuf message name. This does not convert the retained artifact into a TypeLibrary. | +| `native-order-shape@1` | Named native Schema selected by another schema binding, independently of artifact-backed schemas. | +| `shared-contract@1` | Another Specification with a retained artifact and artifact-backed schema. An order-api message selects it through ContractTypeRef, including exact owner UID, schema/artifact member IDs, digest and expected selector. | +| `order-transport@1` | Channel selecting the real order-adapter AutomationSlice observation. Another operation uses a CHANNEL binding selecting this Channel without inventing another observation. | +| Foreign-namespace referrer | Another Specification has a message mapping to `order-receipt@1`; a restricted reader cannot inspect that Specification, its member names or artifact content. | + +Keep references to both provider exposures, every message and schema member, and +their association IDs for later lifecycle cases. Include an authorized anchor +outside the capped model snapshot so exact-resolution behavior is exercised. + +## Required queries + +| Query | Required result | +| --- | --- | +| Fetch `order-api@1` through service, CLI and MCP | Preserve retained bytes, digests, value presence, member IDs and association IDs. | +| Select a schema or operation member | Resolve the exact Specification incarnation, expected member kind and member identity. No matching by title, list position or schema shape. | +| Inspect the submit operation | Return separate REQUEST and RESPONSE message occurrences with independently selected schema and ReadModel mappings. | +| Follow the submit response | Reach `order-receipt@1`; the request's `order-request@1` is not response data. | +| Inspect checkout's provider | Resolve only the public-gateway exposure. Do not produce a caller-to-partner-gateway dependency from the shared operation. | +| Inspect the unspecified-provider usage | Retain a caller-to-operation dependency with no invented provider. | +| Resolve both response occurrences | Reusing the same message does not collapse the occurrences or their IDs. | +| Get incoming references and impact for a ReadModel, Channel, Schema, TypeLibrary, provider or referenced Specification | Discover references nested in messages, schema selections, transport bindings, exposures, usages and dependencies. Return only authorized details. | +| Select an association as a connection subject | Resolve its owning Specification, incarnation and ConnectionId. Member identity alone is not a ConnectionRef. | +| Traverse behavior from `order-request@1` | Reach the declared AutomationSlice, local Command/Event and receipt projection through the existing graph. Specification declarations introduce no extra behavioral edges. | +| Fetch an anchor outside the projection cap | Resolve it by exact authorized key in the selected branch. Omission from a projection is not absence from storage. | +| Export and reapply an unchanged native manifest | Preserve identities and exact bytes; do not parse or rewrite an opaque artifact during the round trip. | + +## Admission and identity cases + +Rejected writes leave persisted content and identities unchanged. Run each case +through standalone and batch mutation surfaces where applicable, including +preview. Invalid or forbidden target diagnostics must not expose hidden data. + +| Case | Required outcome | +| --- | --- | +| Create a Specification with omitted native member/association IDs | Assign and return the IDs under a fresh server-owned incarnation. A persisted selectable occurrence never has an absent ID. | +| Reorder members, schema bindings, operation messages, exposures or usages | Preserve the retained identities. | +| Rename display labels or edit documentation | Preserve the member or association identity when its semantic selection is unchanged. | +| Repeat a member ID or an association ID within its owner | Reject. Association IDs are unique across the whole Specification, not just one member list. | +| Select a member using the wrong member kind | Reject even if a member with that display name exists. | +| Change an association role, selected message, schema target, caller, operation or provider exposure while retaining its ID | Reject; the replacement needs a fresh occurrence identity. | +| Remove and later re-add a member | Previously retained member selections remain unresolved; a new member cannot silently inherit the removed identity. No unbounded retired-ID list is persisted. | +| Remove and later re-add an association with the same endpoints | Assign a new ConnectionId under the ordinary connection-identity contract. | +| Delete and recreate `order-api@1` | A new owner incarnation prevents old member and connection selections from resolving into the replacement. | +| Copy the Specification to a new key or version | The new owner has a distinct incarnation. Rewrite intra-owner selections deliberately; external references remain exact and do not follow unrelated copies. | +| Change retained bytes without updating the digest | Reject. A pin to former content must not resolve to changed bytes. | +| Change retained bytes with a valid new digest while retaining the artifact member ID | Reject. Add a new artifact member and explicitly retarget schema declarations instead. Old content pins must not resolve to the replacement. | +| Reference an unresolved local member or an undeclared provider exposure | Reject the new or edited declaration. Do not infer the selection from matching transport text. | +| Select an exposure belonging to another operation | Reject the usage. The operation and exposure must agree. | +| Omit the provider selection on a usage | Accept the explicit caller-to-operation dependency without inventing a provider. | +| Use the same schema for request and response | Accept independent roles and ReadModel mappings; schema equality is not evidence of response identity. | +| Supply a Command or Event as an integration message mapping | Reject; confirmed integration mappings select ReadModels. Local behavior is reached through its existing slices. | +| Record an unsupported or unknown contract format | Retain its declared format and opaque bytes under the first-release support boundary. Do not claim semantic validation or compatibility success. | +| Select an opaque artifact type | Report its selector as unverified, even when its declared syntax resembles a supported native type system. | +| Select a ContractTypeRef with the wrong digest, artifact member ID, selector value or selector presence | Reject without falling back to another member or a matching display name. | +| Retarget a schema definition to another artifact while a foreign ContractTypeRef pins its former definition | Assign a new definition ConnectionId. Preserve the unchanged foreign declaration as diagnostically unresolved; newly writing that stale type pin fails admission. | +| Substitute a URL for retained artifact content | Reject the remote-only declaration. No server fetch may turn a mutable URL into retained content or an immutable pin. | +| Retain UTF-8 text with different newline bytes | Preserve the exact text bytes and distinct digest; do not normalize them during authoring or export. | +| Submit non-UTF-8 binary artifact content | Reject under the first-release source-text boundary; a format label does not enable a binary codec. | +| Exceed an artifact, total-content, member, association or encoded-entity limit | Reject atomically under the contract's concrete ceilings and any lower transport/store limit. Do not truncate the contract or discard references. | +| Select a non-existent protobuf message from a TypeLibrary | Reject the new binding. Retained opaque artifact selectors do not gain protobuf compiler support by sharing a format label. | + +## Authorization and lifecycle cases + +| Case | Required outcome | +| --- | --- | +| Create or retarget a declaration across namespaces | Require write access to the Specification and authorized reference access to every selected external entity. | +| Query nested references as a restricted reader | Do not expose forbidden namespaces, member content, provider details or connection subjects through diagnostics or impact output. | +| Safely delete an entity with only a hidden Specification referrer | Refuse deletion, individually and in guarded batch preview/apply, without returning hidden referrer details. | +| Safely delete a selected Specification | Discover incoming member and connection selections as incoming references to their owner and refuse deletion. | +| Explicitly force-delete an external target | Preserve referring declarations as unresolved. Do not erase, retarget or fabricate replacement members. | +| Make an otherwise permitted target edit that invalidates a retained member selection | Preserve unchanged referring declarations for inspection and report the unresolved selection. A newly authored or edited invalid declaration still fails admission. | +| Add descriptive dependencies forming a Specification cycle | Apply the declared relation-specific cycle policy; traversal remains bounded and preserves the cycle. TypeLibrary import and supersedes rules do not change. | +| Preview and land a branch with unchanged declarations invalidated by a permitted target edit | Use the same admission policy as ordinary writes; do not globally reject all existing unresolved resources. | +| Edit or create a selected declaration on a branch | Resolve exact targets and local members in the post-write merged branch view. A branch tombstone must not fall back to baseline content. | +| Independently create the same Specification key on branch and baseline | Require explicit conflict resolution for distinct owner incarnations. | +| Resolve a member/association conflict or land after a concurrent removal | Preserve the chosen side's valid identities under the documented merge policy. A stale selection must not resurrect a retired identity automatically. | +| Exceed the visible resource reporting budget during guarded deletion | Reporting may be incomplete under its explicit policy; authorization must still use the complete incoming-reference guard. | +| Encounter a complete-guard scan error or timeout | Refuse deletion. An incomplete scan is not proof that a target is unreferenced. | + +## Implementation evidence required + +The implementation PR must replace this pending status with links to executable +public-service regressions, real store reference-guard tests, native manifest +round trips, CLI/MCP coverage and Studio review coverage. Assert absence of +invented provider cross-products and behavioral edges, not just the presence of +the intended references. + +Documentation checks establish link and formatting integrity only. They do not +establish a generated wire contract, backend enforcement, format compatibility, +runtime behavior or deployed-browser readiness. diff --git a/docs/reference/specification-and-api-contracts.md b/docs/reference/specification-and-api-contracts.md new file mode 100644 index 00000000..eed62e32 --- /dev/null +++ b/docs/reference/specification-and-api-contracts.md @@ -0,0 +1,367 @@ +# Specification and API contracts + +Status: native first-release contract; schema, backend, CLI/MCP and Studio +implementation is pending. Names below describe the required model, not +existing protobuf messages or allocated field numbers. The +[acceptance contract](specification-and-api-acceptance.md) defines the connected +fixture and failure cases required before implementation can be called complete. + +## Behavioral scope + +A Specification describes a versioned interface or schema contract. An API is +a Specification with operations and explicit provider exposures; it is not a +competing top-level behavioral primitive. Integration messages map individually +to ReadModels. Request, response and notification roles belong to an operation's +use of a message, independently of the message's ReadModel mapping. + +A request does not become a Command because it carries intent, and a +notification does not become an Event because it announces something. Existing +ReadModel-to-Processor observations, Processor-to-Command issuance, Command +handling and Event projections remain the behavioral paths. A Specification +creates none of those paths. Behavioral impact follows the mapped ReadModels +and existing slices. Editable operation-to-Command/Event intent shortcuts are +outside this release. + +Standalone schema contracts need no invented operation, provider or behavioral +anchor. A message may have no confirmed ReadModel mapping; queries report that +absence explicitly. A declared operation must select at least one message, but +does not prove a complete behavioral implementation. Source overlays, +connectors, source handles, directory ownership and data offerings are outside +this contract. + +## Entity and value rules + +| Specification field | Contract | +| --- | --- | +| `id` | Existing exact `Id`: namespace, slug and positive breaking-change version. | +| `title` | Required nonblank display text. | +| `doc` | Optional Markdown; empty and absent mean no documentation. | +| `metadata` | Existing registered typed annotations; unsupported arbitrary nested values do not become graph references. | +| `supersedes` | Optional exact prior same-kind, same-namespace, same-slug version; existing terminating single-parent rules apply. | +| `artifacts` | Collection of retained `ContractArtifact` members. | +| `schemas` | Collection of `ContractSchema` members. | +| `messages` | Collection of `ContractMessage` members. | +| `operations` | Collection of `ContractOperation` members. | +| `exposures` | Explicit `ProviderExposure` associations. | +| `usages` | Explicit `ApiUsage` associations. | +| `dependencies` | Explicit `ContractDependency` associations. | + +A Specification contains at most 1,024 members and 4,096 identified associations +across all nested collections. Exceeding either ceiling rejects admission. +A Specification must contain at least one artifact, schema or message. Member +and association collections may otherwise be empty unless a rule below requires +them. Collection order has no execution meaning; export preserves authored order. +Optional values preserve presence. Unknown enum numbers and missing required +oneof alternatives are rejected on native writes. + +| Value object | Contract | +| --- | --- | +| `ContractMemberId` | Non-nil lowercase hyphenated UUID, unique across all members in one Specification incarnation. Distinct from `ConnectionId`. | +| `MemberName` | Nonblank display name, unique within its member kind using case-sensitive exact comparison. Renaming preserves member identity. | +| `ContentDigest` | Algorithm fixed to SHA-256 with lowercase hexadecimal digest of the exact retained artifact bytes. No text, newline or encoding normalization. | +| `ArtifactPath` | Unique case-sensitive relative POSIX path within the Specification; no empty segment, absolute path, backslash, `.` or `..` segment. A label, never a host filesystem instruction. | +| `MediaType` | Nonblank ASCII media type declaration; no inferred validation behavior. | +| `ContractFormat` | Alternative `OPENAPI`, `ASYNCAPI`, `GRAPHQL`, `JSON_SCHEMA`, `AVRO`, `PROTOBUF`, or `OTHER` with a required nonblank name. Known alternatives reject custom names. | +| `FormatVersion` | Optional nonblank declared version text, preserved verbatim; no compatibility ordering inferred. | +| `ProtocolName` | Lowercase ASCII token matching `[a-z][a-z0-9.+-]*`, as in the Channel contract. | +| `QualifiedProtoName` | Fully qualified protobuf message name, without leading dot; resolves through the selected TypeLibrary's compiled descriptors. | + +## Members and exact selections + +Every artifact, schema, message and operation has required `member_id` and +`name`, plus optional `doc`. A member is contained data, not a top-level entity +with an independently assigned Atlas version. + +`SpecificationRef` contains an exact Specification `Id`. Like existing typed +entity references, it follows that key and does not freeze later nonbreaking +edits or pin an incarnation. + +`ContractMemberRef` contains required `specification: SpecificationRef`, +`owner_uid`, `kind: ARTIFACT | SCHEMA | MESSAGE | OPERATION`, and `member_id`. +Resolution checks the exact owner key, incarnation, kind and member identity. +No selection falls back to name, position, path or a matching digest. Local +member selections inside a Specification use typed member IDs rather than +self-referencing external entity references. + +Only artifact selections may additionally contain `expected_digest`. A matching +pin guarantees the retained bytes, not the rest of the Specification or its +mutable documentation. For a schema artifact, `ContractTypeRef` contains a +SCHEMA `ContractMemberRef`, required expected local artifact member ID and +artifact digest, and optional expected opaque selector. It accepts only an +artifact-backed schema. Resolution checks the artifact identity, digest and +selector including absence against the current schema definition. Changing any +of them invalidates that pin even when the schema member ID remains unchanged. An ARTIFACT +selection alone denotes a document, not a schema type. + +An ordinary exact version remains editable under Atlas concurrency guards. +Changing a breaking-change version is explicit; no semantic format parser +automatically determines compatibility. A historical proof must retain its +checked snapshot or revision and bytes. Neither an entity version nor a digest +alone freezes message mappings, provider declarations or behavioral edges. + +## Retained artifacts and schemas + +| ContractArtifact field | Contract | +| --- | --- | +| `path` | Required `ArtifactPath`. | +| `format` | Required `ContractFormat`. | +| `format_version` | Optional `FormatVersion`. | +| `media_type` | Required `MediaType`. | +| `content` | Required nonempty UTF-8 source text; its exact UTF-8 bytes are retained. | +| `digest` | Required `ContentDigest`; backend verifies against `content`. | + +Artifact content, format, format version and media type are immutable within a +member identity. Replacing any of them requires a new artifact member ID. Path, +name and documentation remain editable labels. Existing schemas stay on the old +artifact until explicitly retargeted; old byte pins remain valid while that +artifact is retained. Removing it leaves external selections unresolved. Binary +artifacts are not accepted in this release. + +Artifacts live inside the entity and its authorized export. No remote-only +artifact, automatic fetch, external credentials, executable documentation or +mutable download URL substitutes for retained bytes. Native manifest export +must retain every byte and digest. Limits are 256 KiB of UTF-8 bytes per artifact, 512 KiB of total artifact bytes, +and 1 MiB for the complete protobuf-encoded Specification. Existing lower +entity/RPC/storage limits still apply. Over-limit admission fails atomically, +without truncation or a reference-only fallback. External blob +storage and streaming large contracts require a separate release contract. + +| ContractSchema field | Contract | +| --- | --- | +| `definition` | Required `SchemaDefinition` association selecting a local artifact or exact native `SchemaRef`. | +| `definition.connection_id` | Required persisted association occurrence ID, selectable through `ConnectionRef`. | +| `definition.artifact` | Local ARTIFACT member ID and required expected digest. | +| `definition.selector` | Optional `OpaqueTypeSelector` only with the artifact alternative. | +| `definition.schema` | Exact named Atlas Schema identity; anonymous inline schemas are not a reference target. | + +`SchemaRef` is a typed exact reference introduced with this feature, not the +existing `FieldType.ref` union. An `OpaqueTypeSelector` contains required +nonblank `syntax` and `value` text. Native admission validates presence and +retains both strings; it does not claim that a JSON pointer, GraphQL type, Avro +name or protocol operation exists inside artifact bytes. Query results label +these selectors unverified. A whole-artifact schema omits the selector and is +also a declaration, not semantic proof that the bytes define a valid schema. + +A `SchemaBinding` contains `connection_id`, required role `PAYLOAD | HEADERS`, +required `type`, and optional `doc`. Its `type` is a typed alternative: +local SCHEMA member ID, external `ContractTypeRef`, exact `SchemaRef`, or +`TypeLibraryTypeRef`. The latter contains exact `TypeLibraryRef` and +`QualifiedProtoName`; existing compilation validates that the message exists. +This selection does not freeze future nonbreaking library edits. Native Schema +and TypeLibrary alternatives do not introduce artifact digests they cannot +verify. Repeated bindings to the same type are allowed as distinct declared +occurrences with distinct connection IDs. + +## Messages and operation roles + +| ContractMessage field | Contract | +| --- | --- | +| `schemas` | Collection of `SchemaBinding` occurrences. Empty means undeclared shape. | +| `mapping` | Optional `MessageReadModelMapping`: its own `connection_id` and required exact `ReadModelRef`. | + +Each message independently selects at most one exact ReadModel. Several +messages may map to the same ReadModel, and several Specifications may describe +it. Another ReadModel version is a distinct selection. Mapping does not assert +schema equality or infer a native Schema from an external artifact. Message +examples, schema compatibility checks and automatic model generation are +outside this release. + +| ContractOperation field | Contract | +| --- | --- | +| `binding` | Optional typed `OperationBinding` declaration. | +| `messages` | Nonempty collection of `OperationMessage` occurrences. | + +An `OperationMessage` contains `connection_id`, required local MESSAGE +`message_id`, required role `REQUEST | RESPONSE | NOTIFICATION`, and optional +`doc`. A message can be used repeatedly in one operation, including with +different roles. Each occurrence has its own connection ID. Unknown roles are +not accepted in native authoring. A notification-only operation is valid; +absence of request or response does not fabricate one. + +Request and response shapes come from the selected messages; there is no +competing operation-level request/response schema list. Response ReadModels +come only from RESPONSE message occurrences. REQUEST or NOTIFICATION mappings +do not silently count as response data. This release records no request-to- +response correlation or execution ordering among occurrences. + +| OperationBinding alternative | Required declaration | +| --- | --- | +| `HTTP` | Uppercase ASCII method token and nonblank path beginning `/`; optional distinct integer response status codes in `100..599`. Path text is retained without template interpretation. | +| `RPC` | Nonblank service and method names; no automatic protobuf service lookup. | +| `CHANNEL` | Association `connection_id` plus exact `ChannelRef`. | +| `GRAPHQL` | Kind `QUERY`, `MUTATION` or `SUBSCRIPTION` and nonblank field name; no document parsing or field lookup. | +| `OTHER` | Required `ProtocolName` and nonblank binding description. | + +An absent binding means undeclared transport. HTTP status declarations do not +pair particular message occurrences. A CHANNEL binding references descriptive +transport; it does not assert that the Channel observes every mapped message, +select a different Processor, or create a behavioral observation. Existing +Channel bindings remain the authority for those observations. + +## Providers and API usage + +`ProviderTarget` is a typed alternative containing exact `ComponentRef` or +`ExternalSystemRef`. It does not accept a Domain, Swimlane, directory entry, +URL or arbitrary entity kind. + +| ProviderExposure field | Contract | +| --- | --- | +| `connection_id` | Required persisted association occurrence ID. | +| `provider` | Required `ProviderTarget`. | +| `operation_id` | Required local OPERATION member ID. | +| `doc` | Optional declaration-specific Markdown. | + +An exposure declares that exactly that provider offers exactly that operation +with the operation's binding. The same provider-operation pair may appear only +once. Different providers can expose one operation; each declaration remains +separate. This release has no deployment endpoint or environment overrides. +A provider list never expands into an operation cross-product. + +| ApiUsage field | Contract | +| --- | --- | +| `connection_id` | Required persisted association occurrence ID. | +| `caller` | Required `ProviderTarget`; caller and provider may be the same entity. | +| `operation_id` | Required local OPERATION member ID. | +| `exposure_id` | Optional local ConnectionId selecting one ProviderExposure on this Specification. | +| `doc` | Optional declaration-specific Markdown. | + +A selected exposure must exist and select the same operation. Absence means +the caller uses the contract with provider undeclared. It does not select all +providers or allow a guessed provider. Usage and exposure are descriptive +dependencies, not proof of runtime traffic or Processor calls. Repeated usages +are valid separate declarations; callers, exposures and messages never form +an implicit Cartesian product. + +## Dependency graph + +A `ContractDependency` contains `connection_id`, required `target` alternative +of exact `SpecificationRef` or `TypeLibraryRef`, and optional `doc`. Duplicate +same-kind exact targets in this list are rejected. Dependencies describe +whole-contract prerequisites; external member/type references independently +declare their own dependencies and need no redundant entry here. + +Member resolution never follows dependency lists looking for a matching name. +Descriptive Specification dependencies and external schema bindings may form +cycles. Resolving a ContractTypeRef checks its selected schema and local artifact +without recursively expanding dependencies or interpreting opaque schemas. +A self-dependency in the explicit dependencies list is redundant and rejected; +mutual dependencies are allowed. Local selections remain typed: operations +select messages, messages select schemas, and schemas select artifacts or +native Schemas. Existing TypeLibrary import and supersession cycle rules still +apply independently. Caller/provider cycles are also descriptive and create no +behavioral execution cycles. Bounded graph queries use visited identities and +report incomplete traversal when their limits are reached; partial traversal +cannot authorize deletion. + +Reference enumeration, reverse lookup, impact, authorized closure, retargeting +and deletion protection must include every nested external reference above. +Local member/association selections are checked and reported with precise +member IDs; they do not create fictitious top-level EntityRef targets. Member +references index the Specification owner as well as their exact selection. +Incomplete diagnostics cannot authorize deletion; global safety checks must +remain complete even when visible reporting is capped. + +## Identity and lifecycle + +New member IDs are server-assigned when omitted, using UUIDv7. A new owner may +accept caller-supplied valid member IDs. On an existing owner, supplied IDs +must already be active in the same member kind. Additions omit their IDs; +removal followed by re-addition receives a fresh ID. No retired-member ledger +is accumulated. Returned persisted state is authoritative, and subsequent +edits preserve its member and association IDs. The same exact-content no-op +exception as [connection identity](connection-identity.md) applies to unchanged +id-less manifests; an edited manifest cannot strip retained identities. + +Local selections require known IDs. On new-owner creation, clients may supply +the member and association IDs needed for same-owner selections. On an existing +owner, first add id-less members or exposures, then use their authoritative +returned IDs in a guarded follow-up edit. This release introduces no temporary +member handles or client-selected IDs for additions to an existing owner. +Preview-assigned IDs do not become active or reserved. + +Members retain identity across rename, reorder, documentation, schema-definition, +mapping and transport-declaration edits. Immutable artifact content is the +exception described above. Changing member kind requires a new +member. Association IDs follow existing connection admission rules: changing +their role or selected endpoint requires a new connection ID. The member ID +is not that connection ID. A message mapping can therefore change while its +message member remains selected; the mapping occurrence changes identity. + +Association endpoints include their containing member identity. For a +SchemaDefinition they include the schema member, target alternative, selected +artifact or native Schema, digest and opaque selector including absence. For +OperationMessage they include operation, selected message and role; for schema +bindings, containing message, role and selected type; for usage, caller, +operation and selected exposure or explicit absence. Exposures include provider +and operation. A Channel binding includes its containing operation and Channel. +Changing an opaque selector or digest in a selected external type counts as +retargeting that schema-binding association. + +An OperationMessage selects the message member, not its derived ReadModel. +Changing that member's mapping renews only the mapping association. Likewise, +changing a schema member's definition preserves bindings that still select the +same local member. Immutable external type pins must still pass their explicit +definition checks. + +All associations participate in `ConnectionRef` resolution using the exact +Specification owner, incarnation UID and local ConnectionId. Member selectors +use `ContractMemberRef` instead. Branch/conflict logic applies recorded +base/current/selected identity checks to members as well as associations; +it must not resurrect identities deleted by a present opposing same-incarnation +owner. Explicit restoration creates fresh IDs for those members/associations +and consistently rewrites same-owner local selections, or rejects an ambiguous +restoration. It never rewrites external references silently. Whole-owner +deletion with explicit keep-ours follows the existing surviving-incarnation +rule. + +| Operation | Required behavior | +| --- | --- | +| Create/update Specification | Validate the complete proposed entity, member selections, external targets, digest checks, authorization and declared dependency rules in the operation's branch context. | +| Remove a locally selected member/exposure | Reject unless the same guarded final graph removes or explicitly replaces all local selections. No cascading deletion inferred from list order. | +| Safely delete a referenced Specification or other target | Refuse with authorized dependency diagnostics; inaccessible dependencies still protect deletion. | +| Force-delete an external target | Preserve declarations as inspectable unresolved references; no cascade or automatic replacement. | +| Replace artifact content | Add a fresh artifact member. Explicitly retarget local schemas and byte pins in the same guarded graph; retained external selections keep selecting the old member until its removal. | +| Remove a member selected by another Specification | Preserve the external declaration and diagnose a missing member, like removing a selected behavioral occurrence. | +| Delete/recreate a Specification at the same key | New UID invalidates old member and connection selections; plain SpecificationRef follows the key. | +| Clone/create another version | Fresh owner UID; local IDs may be retained. Rewrite same-owner local selections consistently. Preserve external exact targets unless an explicit mapping replaces them. | +| Guarded generic retarget | Rewrite supported exact entity targets and renew changed association IDs. ContractMemberRef and ContractTypeRef require explicit member selection; replacing only their owner key is not sufficient. | +| Preview, resolve and land a branch | Apply the same final-graph admission, concurrency, authorization and identity rules as writes. Preview identities are not reserved. | + +Unchanged declarations may remain unresolved after accepted target edits or +forced deletion. Unrelated branch landing does not globally reject those +pre-existing diagnostics. New or edited Specification declarations must pass +their own admission rules. Backend reads must not expose hidden targets, +artifact bytes, member names or diagnostic details through partial access. + +Cross-namespace references require authorized read/reference access to every +external target and write access to the owning Specification. A readable +Specification grants no additional access to referenced entities. Native +Schema and TypeLibrary targets may be selected across namespaces when the +caller has authorized reference access. A selected library compiles in its own +namespace, retaining the existing same-namespace TypeLibrary import rules. +This feature does not change entity Any validation, transcoding permissions or +library namespace ownership. Local member mutation requires owner write access, +not an independent member permission system. + +## First-release support boundary + +Native entity authoring, exact reads, reference/impact queries, validation, +guarded mutations, branch lifecycle and byte-preserving native manifest +import/export are required. CLI/MCP are authoring surfaces; Studio is a review +surface. Specification support must join existing connected graph and export +flows rather than rely on a separate registry. + +All listed contract formats can be retained as UTF-8 source text with declared labels. +`OTHER` preserves an explicit format name. There is no first-release protocol +importer, source sync, remote fetch, schema compatibility checker, format +conversion, generated model, API execution or OpenAPI/AsyncAPI/GraphQL semantic +parser. Opaque selectors remain explicitly unverified. Existing TypeLibrary +compilation and typed native Schema resolution remain available only through +their explicit binding alternatives. + +Malformed native members, wrong-kind selections, mismatched digests, duplicate +identities, missing required roles, stale UIDs, invalid local exposure selection, +invalid dependency declarations and unauthorized references fail admission atomically. +Unverified external document semantics do not masquerade as successful type +validation. Execution evidence belongs in the linked acceptance contract; +this reference alone does not establish implementation or release readiness. From ab22a5eca095c5903736c08be4df0227474cbb8e Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Tue, 20 Oct 2026 14:33:07 -0400 Subject: [PATCH 2/2] chore(docs): prevent ambiguous API classification and fixture references Signed-off-by: Yordis Prieto --- .../specification-and-api-acceptance.md | 12 +++++++++-- .../specification-and-api-contracts.md | 20 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/docs/reference/specification-and-api-acceptance.md b/docs/reference/specification-and-api-acceptance.md index 4eb841c8..bc5645f1 100644 --- a/docs/reference/specification-and-api-acceptance.md +++ b/docs/reference/specification-and-api-acceptance.md @@ -7,7 +7,9 @@ This fixture describes required outcomes; it is not executable test evidence. ## Connected fixture -Use a fresh namespace for each mutation case. Every model reference includes an +Allocate distinct fresh namespaces for each mutation case and retain their +returned IDs as `primary_namespace` and `foreign_namespace`. Unqualified model +labels below belong to `primary_namespace`. Every model reference includes an exact version. Names below are fixture labels; retained member and association identities come from the authoritative write response. @@ -30,7 +32,7 @@ identities come from the authoritative write response. | `native-order-shape@1` | Named native Schema selected by another schema binding, independently of artifact-backed schemas. | | `shared-contract@1` | Another Specification with a retained artifact and artifact-backed schema. An order-api message selects it through ContractTypeRef, including exact owner UID, schema/artifact member IDs, digest and expected selector. | | `order-transport@1` | Channel selecting the real order-adapter AutomationSlice observation. Another operation uses a CHANNEL binding selecting this Channel without inventing another observation. | -| Foreign-namespace referrer | Another Specification has a message mapping to `order-receipt@1`; a restricted reader cannot inspect that Specification, its member names or artifact content. | +| `foreign_namespace/foreign-order-contract@1` | Specification with a message mapping to `primary_namespace/order-receipt@1`; a restricted reader cannot inspect that Specification, its member names or artifact content. | Keep references to both provider exposures, every message and schema member, and their association IDs for later lifecycle cases. Include an authorized anchor @@ -41,6 +43,7 @@ outside the capped model snapshot so exact-resolution behavior is exercised. | Query | Required result | | --- | --- | | Fetch `order-api@1` through service, CLI and MCP | Preserve retained bytes, digests, value presence, member IDs and association IDs. | +| Classify `order-api@1` | Report API classification because it declares operations and provider exposures selecting local operations. A provider need not expose every operation. | | Select a schema or operation member | Resolve the exact Specification incarnation, expected member kind and member identity. No matching by title, list position or schema shape. | | Inspect the submit operation | Return separate REQUEST and RESPONSE message occurrences with independently selected schema and ReadModel mappings. | | Follow the submit response | Reach `order-receipt@1`; the request's `order-request@1` is not response data. | @@ -61,6 +64,10 @@ preview. Invalid or forbidden target diagnostics must not expose hidden data. | Case | Required outcome | | --- | --- | +| Create a schema/message Specification with no operations or exposures | Accept it as a standalone Specification, without API classification. | +| Create an operation-bearing Specification with no provider exposures | Accept it as provider-undeclared, without API classification or an invented provider. | +| Create a provider exposure without its selected local operation | Reject rather than classifying a dangling declaration as an API. | +| Remove an API's last exposure after repairing local usage selections | Preserve a valid provider-undeclared Specification and remove its derived API classification. | | Create a Specification with omitted native member/association IDs | Assign and return the IDs under a fresh server-owned incarnation. A persisted selectable occurrence never has an absent ID. | | Reorder members, schema bindings, operation messages, exposures or usages | Preserve the retained identities. | | Rename display labels or edit documentation | Preserve the member or association identity when its semantic selection is unchanged. | @@ -97,6 +104,7 @@ preview. Invalid or forbidden target diagnostics must not expose hidden data. | Safely delete an entity with only a hidden Specification referrer | Refuse deletion, individually and in guarded batch preview/apply, without returning hidden referrer details. | | Safely delete a selected Specification | Discover incoming member and connection selections as incoming references to their owner and refuse deletion. | | Explicitly force-delete an external target | Preserve referring declarations as unresolved. Do not erase, retarget or fabricate replacement members. | +| Force-delete or revoke reader access to a declared API provider | Preserve the exposure declaration and API classification while reporting only authorized target diagnostics. Classification does not depend on viewer access or current external-target resolution. | | Make an otherwise permitted target edit that invalidates a retained member selection | Preserve unchanged referring declarations for inspection and report the unresolved selection. A newly authored or edited invalid declaration still fails admission. | | Add descriptive dependencies forming a Specification cycle | Apply the declared relation-specific cycle policy; traversal remains bounded and preserves the cycle. TypeLibrary import and supersedes rules do not change. | | Preview and land a branch with unchanged declarations invalidated by a permitted target edit | Use the same admission policy as ordinary writes; do not globally reject all existing unresolved resources. | diff --git a/docs/reference/specification-and-api-contracts.md b/docs/reference/specification-and-api-contracts.md index eed62e32..987a9983 100644 --- a/docs/reference/specification-and-api-contracts.md +++ b/docs/reference/specification-and-api-contracts.md @@ -8,9 +8,13 @@ fixture and failure cases required before implementation can be called complete. ## Behavioral scope -A Specification describes a versioned interface or schema contract. An API is -a Specification with operations and explicit provider exposures; it is not a -competing top-level behavioral primitive. Integration messages map individually +A Specification describes a versioned interface or schema contract. It is +classified as an API exactly when it declares at least one operation and at +least one provider exposure selecting a local operation. This classification +is derived from the declared structure, not an editable discriminator or a +separate entity kind. A Specification with operations but no exposures remains +a valid provider-undeclared contract and is not classified as an API. +Integration messages map individually to ReadModels. Request, response and notification roles belong to an operation's use of a message, independently of the message's ReadModel mapping. @@ -51,6 +55,9 @@ across all nested collections. Exceeding either ceiling rejects admission. A Specification must contain at least one artifact, schema or message. Member and association collections may otherwise be empty unless a rule below requires them. Collection order has no execution meaning; export preserves authored order. +Empty operation or exposure collections therefore do not satisfy the API +classification rule. An exposure without its selected local operation fails +admission; the classification rule never fabricates a provider or operation. Optional values preserve presence. Unknown enum numbers and missing required oneof alternatives are rejected on native writes. @@ -217,6 +224,13 @@ once. Different providers can expose one operation; each declaration remains separate. This release has no deployment endpoint or environment overrides. A provider list never expands into an operation cross-product. +API classification requires a declared exposure for at least one operation, +not for every operation. Removing the last exposure leaves a valid +provider-undeclared Specification once its local usage selections are repaired. +An exposure whose external provider later becomes missing or unreadable remains +a declaration with an unresolved or unauthorized target. That target state +does not change classification or make classification viewer-dependent. + | ApiUsage field | Contract | | --- | --- | | `connection_id` | Required persisted association occurrence ID. |