| phase | P |
|---|---|
| title | Generated Go Bindings for sc-sha |
| status | complete |
| branch | sprint/p-3-release-prep-v1.5.0 |
| worktree | ../sc-compose-worktrees/sprint/p-3-release-prep-v1.5.0 |
| target | integrate/phase-p |
| planning_branch | plan/sha-go |
| planning_worktree | ../sc-compose-worktrees/plan/sha-go |
| related_issue | #510 |
Publish a generated, typed Go binding for the two existing sc-sha operations
so sc-dolt and atm-core can consume the canonical hash contract without
copying the algorithm. Establish the repository's reusable Go-binding policy
at the same time: UniFFI is the ordinary SDK generator; rust2go is an explicit
future opt-in for a proven async/shared-memory need.
This phase implements bindings/sc-sha-go only. It does not implement Go
bindings for sc-composer, introduce ATM code, change the two-operation
sc-sha core API, add graph discovery, or invent an async hash interface.
ADR-0020 is the governing
binding strategy and is Accepted. P.1 records the required amendment to
ADR-0018 / CLAUDE.md / sc-boundary inventories and its review evidence.
The P.1 gate pins a compatible UniFFI plus uniffi-bindgen-go pair and proves
the pair can generate and build the actual Go package. The Go generator is a
third-party 0.x tool, so this gate is a release-quality requirement, not a
best-effort developer setup check. Do not substitute handwritten CGo glue if
the generator proof fails; resolve the generator compatibility issue or revise
the plan through ADR review.
The initial approved compatibility pair is UniFFI 0.31.0 and
uniffi-bindgen-go v0.7.1+v0.31.0. P.1 records that pair in committed tool
configuration and may not replace either component with a newer release unless
the generation/conformance suite is deliberately re-approved.
| ID | Requirement | Closure sprint |
|---|---|---|
| P-R1 | A Go consumer calls the canonical Rust calculate_hash behavior through generated bindings and receives the same lowercase digest and stable failure identity. |
P.1 |
| P-R2 | A Go consumer supplies a typed, resolved composition manifest and receives the canonical Rust calculate_composition_hash behavior. |
P.1 |
| P-R3 | Go source is generated from a version-pinned interface contract, committed for the released module, and CI rejects generator drift. | P.1 |
| P-R4 | The Go module includes a reproducible native-library distribution contract and is consumable outside this Cargo workspace. | P.2 |
| P-R5 | sc-dolt and atm-core receive an external-consumer handoff with exact package/version, vectors, and migration steps; neither repository is modified by this phase. |
P.2 |
| P-N1 | sc-sha remains pure and receives no UniFFI, rust2go, CGo, Go, resolver, filesystem, CLI, or ATM dependency. |
P.1 |
| P-N2 | Normal Go safety checks remain enabled; no adapter, test, CI job, or consumer requires GODEBUG=cgocheck=0 or GODEBUG=invalidptr=0. |
P.1, P.2 |
| P-N3 | A future sc-composer Go binding is estimated and bounded, but not silently started. |
Phase plan only |
sc-dolt / atm-core (external Go consumers)
│
▼
generated Go package ── generated by uniffi-bindgen-go
│
▼
bindings/sc-sha-go ── UniFFI adapter only
│
▼
crates/sc-sha ── canonical two-operation Rust implementation
The Go-facing types are generated records, enums, and typed errors—not JSON
maps. The adapter performs the same input validation currently exposed by
bindings/sc-sha-python: strict UTF-8 handling, canonical tagged source
validation, digest parsing, schema-version validation, and stable error-code
mapping. It delegates every successful calculation to sc-sha.
The generated public contract is intentionally limited to:
CalculateHash(utf8FileBytes []byte) -> TemplateHash / ScShaError
CalculateCompositionHash(manifest ResolvedTemplateManifest)
-> CompositionHash / ScShaError
The exact generated Go spelling follows the selected pinned generator's output and is recorded in P.1's generated-artifact fixture. It must preserve the semantics of the existing Rust and Python contracts:
CanonicalSource = LocalPath(string) | Url(string)
ResolvedTemplateNode = { source, sha256 }
ResolvedIncludeEdge = { parent, child, occurrence }
ResolvedTemplateManifest = { schema, nodes, edges }
schema accepts only sc-sha/manifest/v1 / its explicitly documented V1
representation. Node order, edge order, and edge occurrence remain identity
inputs; graph discovery, sorting, deduplication, cycle detection, and source
canonicalization stay with callers.
P.1 is the sole foundation sprint. It creates the generated interface, adapter, boundary enforcement, source-generation check, and conformance suite. P.2 depends on P.1's merged public contract and owns native artifact/module release and external consumer handoff.
Therefore P.1 and P.2 are sequential and may not run in parallel. Each may
run in parallel with unrelated phases that do not modify sc-sha, the Go
adapter, ADR-0018, CLAUDE.md, or the same boundary inventories.
In scope:
- UniFFI and
uniffi-bindgen-gogenerated bindings forsc-sha; - a dedicated Go adapter package and native library distribution contract;
- Rust/Python/Go conformance vectors and generator-drift verification;
- ADR-0018 /
CLAUDE.md/ sc-boundary amendments required for the adapter; - release and consumer handoff documentation for
sc-doltand atm-core.
Out of scope:
- an atm-core or
sc-doltsource change; - a third
sc-shacore operation, raw-byte hashing, filesystem access, or template graph discovery; - a rust2go adapter or an async/shared-memory API;
- Go bindings for
sc-composer; - manually maintained public CGo glue;
- relaxing Go pointer checks or accepting
GODEBUGsafety bypasses.
This phase does not authorize bindings/sc-composer-go. The existing
bindings/python surface is roughly 2,500 Rust/Python test lines across
conversion, errors, functions, and enums; a Go equivalent is not a one-sprint
extension of the sc-sha adapter.
Rough estimate after a separate API inventory: three to five sprints.
- Specify the supported
sc-composerGo value/error/result contract and identify the intentionally unsupported Python-only convenience surface. - Build the generated UniFFI adapter and core conformance suite.
- Add render/resolve/extract fixtures and publication packaging.
- Optionally split complex callback/async behavior into a separately ADR'd rust2go adapter only if benchmarked workload evidence requires it.
- Adopt the module in external consumers and complete compatibility release validation.
The applicable boundaries are non-negotiable: sc-compose remains the CLI
consumer of sc-composer (Rule 2); the Go adapter is a new dedicated adapter,
not an expansion of bindings/python (Rules 3–4); and sc-composer must
never depend back on any Go adapter (Rule 5). A later phase must amend those
rules and add a dedicated sc-boundary contract before code begins.
- ADR-0020 and the P.1 ADR-0018/boundary amendments are accepted before adapter source is authored (PR #513).
- A generated Go package exposes exactly the two existing
sc-shaoperations and typed manifest/error values (PR #513). - Go, Rust, and Python agree on all committed success and failure vectors (PR #513).
- CI regenerates the Go source using the pinned toolchain and detects committed-artifact drift (PR #513).
- The released module has a documented target-bundle download/linking contract and passes an independent consumer-module smoke test (PR #515).
- Neither
sc-shanorsc-composeracquires a binding/runtime reverse dependency, and sc-boundary negative tests prove it (PR #513). -
sc-doltand atm-core receive the released package/version, vectors, and handoff instructions; their adoption remains separately owned work (PR #515). - All sprint and required
fix/worktrees are QA-approved, merged, and revalidated on the merged parent (PR #514).
Each implementation sprint must run its own exact validation list. The phase closeout reruns:
cargo fmt --all --check
cargo test --workspace
cargo clippy --all-targets --all-features -- -D warnings
just lint
go test ./... # from bindings/sc-sha-go
git diff --check
Run all applicable sc-lint targets after each sprint's implementation. Fix
minor findings within the sprint. Create a fix/ worktree from the sprint
commit for remaining findings, grouping independent rule classes together
(for example, one crate-local constant-string group, not one worktree per
literal; keep each length-refactor violation separate). Send team-lead the
parent commit, target paths, finding class, evidence, tests, and fix commit;
team-lead opens the PR and sends it to quality-mgr. The originating sprint is
not closed until required fix PRs are QA-approved, merged, and its merged
parent is revalidated.
Status: complete on sprint/p-3-release-prep-v1.5.0.
P.3 publishes the Phase P release documentation and metadata: the generated
sc-sha-go consumer section and integration guide use the 1.5.0 module tag,
the Rust and Python package versions are synchronized, and the Phase P
boundary and planning gates are closed. External adoption by sc-dolt and
atm-core remains consumer-owned work as documented in the integration guide.