v2026.7.21: SPEC-236 Phase C — SBI PromiseId + Schema Fingerprints
v2026.7.21: SPEC-236 Phase C — SBI PromiseId + Schema Fingerprints
Section titled “v2026.7.21: SPEC-236 Phase C — SBI PromiseId + Schema Fingerprints”Phase C hardens the identity of a local Promise[T]. Phase A made the cell
safe; Phase B made request/response pleasant. Phase C makes the handle an
SBI-shaped PromiseId and fails closed on stale epoch or schema mismatch.
What’s new
Section titled “What’s new”Packed PromiseId (local form)
Section titled “Packed PromiseId (local form)”Promise[T] remains a single u64 SBI scalar in Janus source. The runtime
packs:
| Bits | Field | Notes |
|---|---|---|
[63:48] | authority_scope | Local default 1 (ID_SCOPE_LOCAL) |
[47:32] | epoch | Process runtime epoch |
[31:0] | slot | Map key; 0 is invalid |
Every bridge access unpacks the handle and rejects wrong scope, wrong epoch, or a destroyed/missing slot ([PROM:6.2]).
use std.cluster.promise as promise
let p: Promise[u64] = promise.new[u64]()let slot = promise.id_slot[u64](p)let epoch = promise.id_epoch[u64](p)let scope = promise.id_scope[u64](p)// scope == promise.ID_SCOPE_LOCALAfter promise.runtime_reset(), prior handles are stale: resolve / await /
destroy fail closed. New promises stamp the advanced epoch.
Schema fingerprint gate ([PROM:6.3])
Section titled “Schema fingerprint gate ([PROM:6.3])”Create a promise that expects a result schema fingerprint. On resolve, pass
the actual fingerprint. Mismatch transitions the promise to
Failed(SchemaMismatch) and returns 0.
let expected: u64 = 0xA11CE001 as u64let wrong: u64 = 0xB0B00002 as u64
let p = promise.new_with_schema[u64](expected)let r = promise.resolver[u64](p)
// Mismatch → fail closed_ = promise.resolve_with_schema[u64](r, 1 as u64, wrong)// state == STATE_FAILED, failure_kind == ERR_SCHEMA_MISMATCH
// Match → resolvelet p2 = promise.new_with_schema[u64](expected)let r2 = promise.resolver[u64](p2)_ = promise.resolve_with_schema[u64](r2, 55 as u64, expected)// await_or → 55promise.new[u64]() remains the unchecked path (expected fingerprint 0):
resolve_with_schema still accepts any actual fingerprint.
v0.1 fingerprints are caller-supplied u64 values. Full SBI BLAKE3/u128 type
CIDs (SPEC-039) will feed this gate when the compiler emits them.
API surface (std.cluster.promise)
Section titled “API surface (std.cluster.promise)”| API | Role |
|---|---|
new_with_schema[T](fp) | Create with expected fingerprint (0 = unchecked) |
resolve_with_schema[T](r, v, fp) | Resolve with actual fingerprint |
id_slot / id_epoch / id_scope | Decode packed PromiseId fields |
runtime_epoch / runtime_reset | Diagnostics + restart simulation |
ERR_SCHEMA_MISMATCH | Failure kind 2 |
Verification
Section titled “Verification”./scripts/zb test-cluster-promise-sbi-id./scripts/zb test-cluster-promise-sbi-fingerprint./scripts/zb test-cluster-promise-call-sugar./scripts/zb test-cluster-promise-request-response./scripts/zb test-cluster-promise-terminal-statesScope and what’s next
Section titled “Scope and what’s next”| SPEC-236 phase | Status |
|---|---|
| A — local Promise slots | ✅ Complete |
B — .call() sugar (local) | ✅ Complete |
| C — SBI PromiseId layout | ✅ Complete (this release) |
| D v0.1 — local pipeline graph | ✅ Complete — Phase D notes |
| D.1 — wire envelopes + Promise.call | Deferred (distributed + compiler) |
Full 16-byte on-wire PromiseId (slot:u64 | epoch:u32 | scope:u32) and
promise-on-promise pipeline segments remain Phase D.
Known compiler note
Section titled “Known compiler note”Full-width hex u64 literals (for example 0xA11CE00000000001) currently
collapse to 0 in AOT (Gap 107). Smokes use 32-bit-wide hex constants; the
runtime ABI is still u64.
Reference
Section titled “Reference”- std.cluster reference — PromiseId + fingerprint section
- Phase A · Phase B
- Monastery: Promise IDs and Schema Fingerprints (
07-promise-id-and-schema.md) - SPEC-039 §16 — PromiseId local handle form