Project Interface Change v1

September 10, 2026 ยท View on GitHub

Status: implemented bounded profile; HOSTED GREEN under the v0.4.0 release baseline. Historical local, authoring-time, ignored, or separately provisioned observations below retain their narrower scope; public promotion and broader product completion remain separately gated.

Audience: agent builders, compiler contributors, and reviewers.

implement_interface appends one actual source impl declaration that binds an explicit Project record to an explicit Project protocol's complete member table. The binding names existing Project functions by persistent identity. The declaration defaults to the receiver module; optional destination selects one other exact declared Project module. The compiler plans canonical dependency imports before mutation. Source and Project checkers validate the closed static sidecar before candidate application rebuilds the full Project. This does not generate function bodies, lower dynamic dispatch, introduce a runtime witness table, or add a runtime Graph edge for the protocol dependency.

Intention and discovery

{
  "kind": "implement_interface",
  "target": "example.counter",
  "protocol": "example.readable",
  "id": "example.counter.readable",
  "destination": "example.bindings",
  "members": [
    {"method": "example.readable.read", "implementation": "example.read"}
  ]
}

The five-field form without destination remains exact and places the sidecar in the receiver module. The six-field form shown above is also closed. target selects one explicit monomorphic Project record. protocol selects one explicit Project protocol. Each method is an explicit required member, and each implementation is an existing explicit ordinary non-main, monomorphic Project function. Every required member must be selected exactly once, and implementation functions must be distinct. The new implementation ID must be globally fresh. Selectors use the source static-conformance grammar: 1โ€“240 ASCII letters, digits, underscores, dots, colons, or hyphens. The new ID cannot use the auto: or semaprax. prefixes or collide with prelude identities.

ProjectCandidate::interface_catalog(expected_candidate, target) returns semaprax.project-interface-change-catalog.v1, bound to the exact candidate and Project revision. It lists Project protocols and provider modules, required signatures and modes, eligible Project function IDs, declared destination modules, and an existing implementation if present. complete_mapping_available requires an actual one-to-one matching, not merely one candidate per member. The ordinary change catalogue advertises implement_interface when a complete new mapping is available. Discovery never admits a proposed implementation or selects a preferred table automatically.

The compiler's static protocol owner resolves nominal parameter identities in each declaration's own module before comparison. The receiver position substitutes protocol Self or the protocol name with the record's exact stable identity. Remaining resolved type identities, parameter modes, arity, and return type must match exactly. Implementation functions cannot add effects or preconditions; ordinary postconditions are permitted. Retained checked HIR must contain the exact receiver record and every selected function before mutation. The ordinary verifier still owns body, ownership, cleanup, and backend admission.

Canonical source and persistent identities

The operation constructs a typed AST declaration, orders bindings by required method ID, and leaves projection to the canonical formatter. Cross-module dependencies are sorted by family and stable identity. Existing exact imports are reused; fresh aliases prefer the provider display name and then use the bounded _spx_impl_<n> namespace. A conflicting identity, kind, or provider rejects before mutation. The output has ordinary source form:

@id("example.counter.readable")
impl "example.readable" for "example.counter" {
    "example.readable.read" = "example.read";
}

A cross-module destination additionally carries exact use protocol, use type, and use function declarations. use protocol is a static sidecar dependency: Project validation authenticates it, while runtime HIR and runtime Graph projection deliberately omit it.

Before mutation, candidate admission reparses each selected authenticated source module and requires exact Program equality. The receiver's retained record HIR must match its source name, span, monomorphic shape, and every ordered field's stable identity, name, span, index, and recursively resolved type. Every selected function must match retained HIR in name and span, each parameter's name, span, ownership and recursively resolved type, return type, effects, and requires/ensures inventory. Bare source string parameters normalize only to the compiler's existing owning String mode. Nominal resolution is bounded to 64 levels and 65,536 authentication work items.

The source checker validates this complete table before candidate admission. Every subsequent candidate operation preserves the existing source-owned implementation identity, owner/module, protocol, and member-ID mapping. These facts are checked before formatting and again after source replay. Global source identity checks include protocol and implementation declarations even where the runtime graph does not index them. Renaming a function's display name can retain conformance because its ID is unchanged; an edit introducing a forbidden precondition or incompatible signature fails the normal source check.

The new implementation remains in exact Semantic Change history, replay, and recovery. A stale or failed application cannot change the existing candidate or original files. Conservative rebase and merge compare a closed compiler-owned dependency fingerprint at each original and destination intermediate revision before replay. It binds the exact receiver, protocol, and destination identities and shapes, relevant import bindings, the ordered required-member signatures, the normalized method-to-function mapping, each selected function's conformance-relevant signature/effects/precondition facts, the vacant receiver/protocol pair, and the globally absent new implementation ID. Mapping input order is immaterial because the admitted table is normalized by method ID. Named receiver-field and selected-function signature types also bind retained checked-HIR identities, and protocol method source order remains part of the protocol shape. Thus unchanged type spelling or an import alias cannot hide a nominal identity substitution.

Selected-function body, postcondition, and display-name edits do not change static conformance and are excluded from this fingerprint. They may therefore survive conflict selection, as can an unrelated source edit, but the whole intention is still reconstructed and passed through ordinary Project candidate admission on the exact destination. Receiver or protocol display/shape drift, required-member drift, selected-function signature/effect/precondition drift, an occupied pair, and any new source identity collision reject with SPX-G235. There is no selector guessing, same-spelling recovery, behavioral implication, dynamic-dispatch compatibility, or dependency remapping. Dependencies created only by another not-yet-replayed history remain outside this conservative route.

Evidence and boundaries

The existing runtime graph does not gain a fabricated declaration or call edge. Candidate summaries carry the exact new source implementation fact. Its impact entry explicitly reports source_static_conformance_only and unavailable cross-file runtime impact. Semantic deltas can select source implementation, protocol, and protocol-method IDs using authenticated authored source spans. Related conformance facts also attach to receiver and implementing-function deltas. Existing ordinary targets without related conformance do not gain an empty new facet. These are recomputable static facts, not runtime dispatch, behavioral equivalence, dynamic impact, or test coverage evidence.

The intention admits at most 64 members, below the broader source checker limit. Discovery has at most 65,536 member/candidate entries and a 1 MiB output cap; matching paths have at most 64 member steps. Source identity and implementation inventories are bounded to 65,536 entries. Existing Semantic Change, candidate history, source, static-protocol, and Project limits remain active. These are structural/output bounds, not a total heap or latency promise.

SPX-G272 rejects malformed mappings, unavailable subjects, duplicate or incomplete selections, incompatible member signatures, and identity collisions. SPX-G273 reports candidate interface capacity. SPX-G274 rejects a changed source implementation inventory. SPX-G497 rejects an absent or ambiguous destination, SPX-G498 rejects import conflicts or alias exhaustion, and SPX-G499 rejects missing or ambiguous retained source/HIR bindings. SPX-G235 owns conservative rebase/merge dependency, pair-occupancy, identity, and unsupported-history conflicts. The source static-conformance SPX-Q1xx, Project, and ordinary candidate stale/replay diagnostics remain authoritative where delegated.

tests/project_candidate/interface.rs authors discovery, same-module and cross-module source additions, canonical dependency imports, selected delta verification, exact replay/recovery, no-write behavior, absent-destination and incompatible-function rejection, display rename preservation, precondition revalidation, and absence of the static protocol and implementation identities from the real candidate Semantic Graph. tests/project_candidate/interface_rebase.rs authors conservative rebase/merge success across unrelated body and selected display edits, exact replay equivalence, unchanged parents/files, the absence of a fabricated runtime-graph declaration, and fail-closed receiver, protocol, selected-function, pair, and implementation-identity conflicts. A focused unit regression checks one-to-one discovery matching. The implemented release regression corpus is HOSTED GREEN. A focused authored Workspace Graph unit regression independently checks that use protocol, the protocol and member identities, and the source implementation identity are absent from both runtime Graph declarations/edges and the operation-sidecar declaration/import inventories. Ordinary type and function imports remain present in the sidecar.