Cross-SDK Surface Alignment

Status: pending review for https://github.com/honua-io/honua-sdk-js/issues/44. Once the PR merges this becomes the canonical reference for downstream Python and .NET SDK alignment work; flip the status line to reviewed at that point.

The Honua SDKs should share one mental model even when each language keeps its own naming conventions. JavaScript may use queryAll(), Python may use query_all(), and .NET may use QueryAllAsync(). Those names are allowed to differ. The semantics behind them must not.

Canonical Semantics

Use the @honua/sdk-js/contract vocabulary as the current source of truth:

Concept Meaning
Dataset Logical grouping of one or more sources plus compatibility checks.
SourceDescriptor Serializable source identity: protocol, locator, capabilities, schema, and attribution.
Source Runtime handle for the common query, edit, related-record, attachment, and escape-hatch surface.
Protocol Stable source protocol identifier.
Capability Stable operation capability identifier.
Query Protocol-neutral request intent.
Result Protocol-neutral result envelope.
EditEnvelope / EditResult Protocol-neutral write envelope and result.
MapBinding Source → renderer-layer binding (used by MapPackage round-trips).
protocol(...) Explicit native protocol escape hatch.

These are the canonical nouns pinned in test/fixtures/sdk-contract/semantic-contract.v1.json and validated by the drift gate (test/contract/sdk-contract-fixtures.test.ts). Renaming a noun without updating both sites trips CI.

Language Bindings

Language bindings must be idiomatic. The binding table below is guidance for public API names, not a byte-for-byte requirement:

Concept TypeScript Python .NET
query all records queryAll() query_all() QueryAllAsync()
stream pages stream() stream() / iter_*() StreamAsync() / QueryPagesAsync()
query object ids queryObjectIds() query_object_ids() QueryObjectIdsAsync()
apply edits applyEdits() apply_edits() ApplyEditsAsync()
return geometry returnGeometry return_geometry ReturnGeometry
output fields outFields out_fields OutFields
pagination pagination pagination / limit / offset Pagination / Limit / Offset
native protocol escape hatch source.protocol(...) source.protocol(...) source.Protocol(...)

Existing SDK names can remain as aliases or facades. For example, Python's FeatureQuery and .NET's FeatureQueryRequest can continue to exist while new source-oriented facades converge on the same behavior.

Stable Protocol IDs

The canonical protocol identifiers are the values exported from PROTOCOLS in src/contract/types.ts. Serialized descriptors and docs should prefer the canonical identifiers; SDKs may accept short or legacy aliases for already-shipped public names by normalizing on read through the protocolAliases map in the fixture pack:

Stable Capability IDs

The canonical capability identifiers are the values exported from CAPABILITIES in src/contract/types.ts. SDKs should gate behavior on these semantic identifiers rather than inventing per-repo names for the same operation. Default capability sets per protocol live in PROTOCOL_DEFAULT_CAPABILITIES and are mirrored in docs/protocol-capability-matrix.md. Every identifier must also have exactly one reviewed entry in the support manifest's operationSurfaces registry. That registry binds the operation to either a compile-checked canonical Source member or a named typed adapter member, plus executable evidence; CI fails if vocabulary and surfaces drift. Top-level connect() discovery is tracked separately as a product support claim because it is not an operation on a Source.

Capability misses must be explicit. The required behavior is a typed SDK error that names the missing capability and protocol (HonuaCapabilityNotSupportedError in JS; equivalent typed errors in Python and .NET). Returning an empty result for an unsupported operation is not acceptable because it is indistinguishable from a valid query with no matches.

Query Semantics

All SDKs should preserve these query semantics even when the local request type uses language-specific names:

Result Semantics

All SDKs should expose the same result facts:

Backwards Compatibility And Deprecation Policy

The RFC aligns semantics — it does not delete shipped APIs.

Decisions Locked By This RFC

These are the alignment points the RFC nails down. Any future change to one of these requires a follow-up RFC plus coordinated multi-SDK updates.

Fixture Pack

The cross-SDK fixture pack lives under test/fixtures/sdk-contract/semantic-contract.v1.json. It is intentionally JSON-only so Python and .NET can consume the same payloads without depending on the JS test helpers.

Coverage today:

The JS drift gate in test/contract/sdk-contract-fixtures.test.ts validates that the fixture pack stays aligned with the exported JS contract registries and with the documented result/error/degraded semantics. Downstream SDK work should consume this fixture pack directly or mirror it with a clear checksum/update process.

Downstream Implementation Work

This RFC is JS-side and docs-only. The Python and .NET SDK alignment lands as separate downstream tickets:

This ticket explicitly does not edit either repository. Filing the downstream issues is part of merging this RFC; updating the placeholders above with the real issue links is the only follow-up edit allowed inside honua-sdk-js once the downstream tickets are open.

References