Skip to content

spike: doc-only perch apply_doc in the smart account (sole write path, doc-hash + JSON + event) - #201

Closed
willemneal wants to merge 9 commits into
fm/nido-perch-sdk-n1from
fm/nido-applydoc-spike-n4
Closed

willemneal wants to merge 9 commits into
fm/nido-perch-sdk-n1from
fm/nido-applydoc-spike-n4

Conversation

@willemneal

@willemneal willemneal commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

SPIKE — DRAFT, DO NOT MERGE. Stacked on #200 (fm/nido-perch-sdk-n1). This PR exists so the true diff shape and cost of adopting perch's apply_doc in nido's smart account can be eyeballed before deciding on full adoption. It will not merge.

Now DOC-ONLY (captain's ruling, superseding this PR's original hybrid cut): apply_doc is the sole policy write path, including for the dapp. The hybrid framing below is gone; earlier commits on the branch preserve it for comparison.

What this demonstrates

The doc-only perch model on nido's smart account, end to end:

  • contracts/smart-account: apply_doc(doc_json) -> doc_hash atomically replaces every context rule except the protected zk-recovery rule with the document's compiled rules. OZ's mutation surface (add_signer, remove_signer, remove_context_rule, add_policy, remove_policy, update_context_rule_*, add_multisig_recovery) is not implemented or exported at all — doc-only is structural, as in upstream perch. The account cross-calls perch's shared stateless doc-compiler contract (parse → validate → network-bind → canonical doc_hash → lower); compiler and interpreter addresses are derived from pins — the same testnet pins the SDK's deployment.ts carries.
  • Anti-brick, re-imported (DocAdminLockout): since the apply replaces the constructor's default passkey rule too, a document must carry at least one policy-free, cap-free self-admin rule with a signer or it is refused before anything changes — upstream perch's AdminLockout check, exactly as the hybrid cut's description predicted this decision would re-import.
  • Caps, folded in: a capped doc rule installs the interpreter AND nido's stock spending-limit policy on the same context rule (OZ enforces both). The policy is resolved in-contract from its pinned deployed address (CCJMCPGA…, DEPLOYED.md) — the same audited-pin pattern as the compiler/interpreter; nido-owned deploys aren't content-addressed, so this is an address pin rather than a wasm-hash derivation. DocCapUnsupported is retired.
  • Doc persistence: canonical doc_hash (32 bytes) in instance storage; the full canonical doc JSON in a persistent entry (get_applied_doc — the lossless, no-indexer read the showcase uses); the same JSON emitted as a DocApplied event (the eventual recovery method). apply_doc refuses non-canonical bytes (DocNotCanonical), so stored == emitted == canonical and either copy verifies with a bare sha256.
  • contracts/factory: still zero code change — it embeds the account wasm at build time; rebuild + republish is the whole deploy story for new accounts.
  • SDK (packages/passkey-sdk): buildApplyDocTx is the only apply routebuildDocInstallTxs (per-rule installs) is deleted with the entry points it drove. readPolicy is now two tiers: doc-verified (view-first from get_applied_doc, event recovery as fallback, hash-verified) / decompiled (pre-doc or foreign accounts). The drift tier disappears by construction — no mutators, no drift.

The recovery-completion collision (the interesting find)

ZK recovery completes by installing a post-recovery rule via add_context_rule, authorized by the zero-signer recovery rule — so a strict "no mutators" surface bricks recovery. The spike's resolution:

  • add_context_rule survives as the completion vehicle only, hard-gated (DocOnlyWritePath) to the completion window: has_pending || completion_granted on the account's recovery controller.
  • completion_granted is new, and load-bearing: the controller's Policy::enforce consumes the pending during the completing call's __check_auth, before the entry-point body runs, so the body can never see the pending it was authorized under. The controller now records a same-ledger TEMPORARY CompletionGrant at consume time and exposes it as a view. Cost: a nido-zk-recovery change rides along with any doc-only adoption.
  • Friend-multisig recovery is dead under doc-only. Install (add_multisig_recovery) and completion (buildRotation's add_signer/remove_signer/add_policy) both rode the removed mutators, and doc v1 cannot express M-of-N principals — so the flow has no route at all until doc v1 grows quorum principals plus a recovery-scoped apply, or a rotation-shaped completion vehicle is added. The SDK builders now throw with that explanation. This is the headline product cost of the ruling.

Deferred decision remaining

Migration for existing accounts stays out of scope (new accounts only; deployed account wasm is immutable). The other deferred decision — sole write path — is what this revision implements.

Cost & observations

  • Wasm size tells the story: ~33 KB (pre-spike) → 51 KB (hybrid cut) → 45 KB doc-only — deleting the mutation surface and its per-op recovery guards pays back 6 KB of the doc layer's cost.
  • What doc-only eliminates: the four-op pending guard, the RecoveryRuleProtected per-op checks (the recovery rule is now protected structurally — no entry point can touch it, and apply_doc skips it), the drift read-tier, and the whole class of "legacy path edits a doc rule" states. apply_doc keeps the pending guard (it removes rules), so a live recovery still blocks re-docing while completion runs through the gated vehicle.
  • What doc-only newly requires: the anti-brick admin check; in-contract cap resolution (the spending-limit address pin); the controller CompletionGrant machinery; and the dapp must rebuild policy editing as document editing — session-key add/revoke become "compose the current doc (from readPolicy) with/without the rule and apply" (scopedSessionKeyDoc + buildApplyDocTx exist; the UI flows are stubbed to throw doc-only guidance, not yet rebuilt).
  • The dapp/SDK surface reshapes materially: buildDocInstallTxs deleted; per-rule policyBlock builders (scoped-session-key, multisig-recovery) and the friend-rotation executor now throw; the frontend's remove-context-rule op builder throws. Read/display paths (fromChain, summarize, decompiler) untouched. Frontend builds and its 408 tests pass — the fast e2e suite is UI-only, so CI is expected green while the throwing flows await their doc-route rebuilds.
  • Test-harness cost: policy-mechanics suites (session-key scoping, threshold, spending-limit, sweep) still need arbitrary rule shapes, now staged through env.as_contract library-call backdoors (test-only; unreachable on a real network). The guard e2e checks moved onto the surviving guarded ops (initiate_upgrade exercises the same controller cross-call). The old per-op guard/protection unit tests are deleted with their entry points.
  • The perch-smart-account trait crate is still not consumable (unpublished; git-unbuildable — it bakes git-ignored fetched wasm pins at build time; private storage/helpers). The account now matches its doc-only SEMANTICS — wipe-and-replace, anti-brick, sole write path — while still consuming only the wire protocol (perch-doc-compiler client) and mirroring the trait surface. Remaining deltas from upstream: the preserved recovery rule + completion gate, nido's spending-limit pin instead of perch's content-addressed one, and canonical-bytes enforcement (upstream's compiler is format-agnostic; here stored == emitted == canonical so verification is a bare sha256 — trivial to relax).
  • stellar-accounts fork divergence (unchanged): perch pins theahaco's CAP-0071 fork; nido pins upstream OZ; a workspace [patch] redirect (both v0.7.1) unifies the wire types and breaks if either pin moves.
  • On-chain doc storage (unchanged from the previous revision): persistent entry (never instance — instance loads on every __check_auth), ~doc-size rent, overwritten per apply, TTL bumping left as a shortcut; the event remains the eventual method (indexer needed for event-only reading and for historical docs).
  • Event/entry size bounds the doc; fine for nido-sized docs, verify limits before real adoption.
  • Pins in account code: perch stateless-registry id + compiler/interpreter wasm hashes + nido spending-limit address, all testnet; a perch infra republish or a spending-limit redeploy ⇒ account rebuild; mainnet needs its own pin story.

Spike shortcuts (deliberate)

  • Happy-path-lean tests; the throwing SDK/UI flows are stubs, not doc-route rebuilds; readPolicy stays a pure classifier (caller fetches the view/event inputs).
  • Compiler errors relayed as flat codes 11–15 (+10 offset from perch's DocCompilerError); 17 DocNotCanonical, 18 DocAdminLockout, 19 DocOnlyWritePath; 10 (DocCapUnsupported) retired.
  • No DEPLOYED.md/README refresh, no bindings republish.

The deployed-artifact wire-type trap (live find #2)

The first doc-only deploy trapped every real apply_doc with Error(Object, UnexpectedSize) decoding compile_doc's return. Root cause: the account's wire types came from perch source rev f5676a6 (6-field CompiledRule, with cap), but the canonical deployed compiler — still the registry's latest publish; perch tagged doc-compiler-v0.2.0 but never published a newer wasm — is an older build: 5-field CompiledRule, no cap lowering, CapUnsupported = 6 in its error enum. The pins were right; the source-vs-artifact skew was the bug, and the e2e masked it by registering the native source-rev compiler at the derived address.

The fix goes to ground truth: the account's wire types are now transcribed from the deployed wasm's own contract spec (fetched via stellar contract fetch; install decodes as raw Vec<Val> passthrough, so no interpreter type surface needs mirroring), the e2e registers the fetched on-chain bytes at the derived addresses, and a pin-fixture guard asserts sha256(fixture) == pin. Fallout worth noting:

  • The perch crate dependency surface collapsed to perch-ir (test-side canonicalization). The theahaco stellar-accounts fork [patch] — previously a headline adoption cost — is gone: nothing pulls perch's Rust workspace anymore.
  • Caps went through refused → live within the spike: the deployed compiler initially refused them (CapUnsupported), and when perch's CI published the cap-capable 0.2.1 (new registry CDX2DMYM…; publish receipt on the perch-doc-compiler-v0.2.1 release), the pin bump + wire re-transcription re-enabled the lowering onto nido's pinned stock spending-limit — proving the artifact-pinned model absorbs an upstream publish as a contained, testable bump.
  • Factory in-place upgrades had a stale-cache bug: account_wasm_hash caches the embedded hash in instance storage, so an upgrade would keep deploying the OLD account wasm. The Upgradable::upgrade override now clears it, plus an admin-gated refresh_account_wasm_hash for factories upgraded from pre-override code.

Live on testnet (2026-09-10)

The spike is clickable end to end — fresh accounts through the repointed factory carry the doc surface:

Factory (doc-only, unverified/factory repointed) CCJFOM6UGOH7JSAX22C3FAECG5657HKIUYDBTCMUMILKDA6LOA2J2EGG
Smart-account wasm (uploaded, embed==installed; artifact-aligned wire types, caps live) fe3b1878…8e59
ZK recovery pool (first testnet deploy; spike params 60s/7d/2/0; via set_recovery_pool) CAUZ6WFUTTZCJQNNL5D3BNZSG7FYYGX46BDJE6G2XVVCGN76RKE5ESAR
UltraHonk zk-verifier (committed depth-24 vk) CDMNKDMPSBUUOHCP6QKFLRP76TLYFCYBM7SICE77BQGFJTRL7MXOSIRD
Live probe account (create_account, genesis leaf 0) CBPE5AJY3ZKQBJFZ7Q3WVK3ANR6ZROCIXGAELRRVKWBOVKJPX2SH2SV2 — interface exposes apply_doc/get_applied_doc/applied_doc_hash/doc_rule_ids, no legacy mutators; both doc views null
Live apply_doc, end to end Account CD5X4AWMGW3KYNDUHDXQGTWAEXIV37ENFM6UP5BDZPAAII4OODKI7JQI: an add-admin-key document (owner passkey + second admin key on the self-admin rule) applied via the wallet signing path (recorded auth entry + WebAuthn assertion over digest(payload ‖ [0])). Live state: applied_doc_hash 4fb7ae94…, get_applied_doc returns the full canonical JSON, doc_rule_ids [2], recovery rule preserved, default rule replaced
Live CAPPED apply (compiler 0.2.1) Account CCVIDH5JM6T5QQVZ7C6223Q55SYNRJLKNYGRD537BVC7IA6HHIWIOH4G: doc with admin + daily-pay (XLM SAC transfer, cap 5 XLM/day) applied in tx 642f4950…. Rule 3 carries BOTH the pinned stock spending-limit CCJMCPGA… and the 0.2.1 interpreter CDR2OTZI… — the whole cap chain, including the real spending-limit install cross-call, live
Perch 0.2.1 pins (NEW registry) registry CDX2DMYM…, compiler CDWBJPDM… (wasm 35f248f0…), interpreter CDR2OTZI… (wasm f63cae53…) — receipt-verified, derivation-cross-checked

Deploy notes (details in DEPLOYED.md on the branch): the unverified/smart-account registry label is owned by a different author key than theahaco, so the version-label publish was refused — the functional path (direct wasm upload) is the deploy script's own fallback. The zk stack had to ship because the doc-only factory genesis-inserts unconditionally; the pool is reached via the factory's admin set_recovery_pool override, not a registry name.

Test evidence

  • just check (fmt + clippy -Dpedantic) clean; just test (cargo workspace) green.
  • Smart-account unit suite rewritten for the doc-only surface (16 green), incl. entry-points-don't-exist, completion-window gating, and anti-brick coverage.
  • 89 integration tests green: apply_doc e2e proves whole-rule-set replacement (default rule replaced by the doc's rules), cap install (interpreter + pinned spending-limit on one rule), anti-brick refusal, canonical/get_applied_doc/event round-trip against the real perch compiler + interpreter at derived addresses; the full zk-recovery suite (initiate → timelock → completion through the gated vehicle → guard release) passes against the real controller with the new grant.
  • passkey-sdk vitest 256 green; frontend build + 408 tests green.

Perch consumed at git rev f5676a6 — the same rev PR #200's SDK pins mirror.

Adds apply_doc/applied_doc_hash/doc_rule_ids to nido-smart-account,
consuming perch's stateless doc-compiler protocol (git-pinned
perch-doc-compiler at rev f5676a6, client mode) with the compiler and
interpreter addresses derived from the same testnet pins the SDK already
carries. HYBRID: apply_doc diffs only its own tracked rules; the default
passkey rule, recovery rule and all legacy mutators are untouched. Stores
the canonical doc_hash, emits the full doc JSON as a DocApplied event,
refuses capped docs (DocCapUnsupported) rather than installing weaker
than reviewed.

A [patch] redirects perch's theahaco stellar-accounts fork pin to nido's
upstream OZ rev so the wire types unify — a flagged adoption cost.

e2e (real wasm account + real compiler/interpreter at derived
addresses): install/hash/event + doc recovery parity, hybrid re-apply
diff, cap refusal, network binding. Factory: no code change needed
(embedded wasm); probe test proves new accounts expose the doc surface.
- Regenerate @nidohq/smart-account bindings from the apply_doc wasm
  (just bindings smart-account; only src/index.ts changes).
- buildApplyDocTx: one-tx apply mode beside buildDocInstallTxs — submits
  the CANONICAL doc JSON to apply_doc, so the DocApplied event carries
  the canonical form; refuses wrong-network and capped docs before any
  network round-trip (capped docs keep the per-rule install path).
- readPolicy: pure three-tier classifier. (a) doc-verified: event-
  recovered doc matches the stored applied_doc_hash AND its lowering
  matches the live doc-managed rules (scope/name/signers/expiry/
  interpreter attachment, plus each fetched program's committed doc_hash);
  (b) doc-drift: hash verifies but live rules drifted — doc returned with
  the drift list; (c) decompiled: existing decompileRules fallback.
- Tests: tier a/b/c coverage incl. the spec'd legacy-mutation drift case;
  suite: 260 passing.
@github-actions

Copy link
Copy Markdown

Example dApp preview deployed!

https://example-pr-201.mysoroban.pages.dev

The status-message example (testnet), wallet = THIS PR's preview (https://201.nido.fyi). The live home is https://nidohq.github.io/nido/ once merged.

@github-actions

Copy link
Copy Markdown

Preview deployed!

https://201.nido.fyi

Account URLs use numeric preview suffixes, for example <contract-address>--201.nido.fyi.

…eadPolicy

Captain extension to the spike: keep a lossless on-chain copy of the
applied policy document so the showcase can read it without event
history or an indexer (the DocApplied event stays as-is and remains the
eventual recovery method).

- apply_doc persists the full canonical doc JSON in a PERSISTENT entry
  (DOC_JSON) — persistent, not instance, so the KB-scale blob is not
  loaded on every __check_auth; only doc_hash + rule ids stay in
  instance storage. New get_applied_doc view returns it.
- apply_doc now REFUSES non-canonical byte submissions
  (DocNotCanonical, code 17): stored == emitted == canonical by
  construction, so either copy verifies against the stored hash with a
  bare sha256. A flagged divergence from perch's format-agnostic
  compiler; buildApplyDocTx already always submits canonicalJson(doc).
- SDK readPolicy: tier-a doc source is now view-first (storedDocJson
  from get_applied_doc), event recovery is the in-tier fallback; drift
  tier unchanged. Candidates are trusted only if they parse AND hash to
  the stored identity.
- Tests: round-trip through the view (bare sha256 of chain-returned
  bytes == stored hash), view tracks latest apply, non-canonical
  refusal, view-first + corrupt-view-falls-back-to-event SDK cases.
  Bindings regenerated. Suites: cargo workspace green, SDK 262 green.
Captain ruling: drop hybrid. The OZ mutation surface (add_signer,
remove_signer, remove_context_rule, add_policy, remove_policy,
update_context_rule_*, add_multisig_recovery) is no longer implemented
or exported; apply_doc atomically replaces EVERY rule except the
protected zk-recovery rule. The constructor's default passkey rule
lasts until the first apply; the re-imported anti-brick check
(DocAdminLockout) refuses docs without a policy-free self-admin rule.
Caps now install in-contract: CompiledCap lowers onto nido's stock
spending-limit policy at its pinned deployed address (same audited-pin
pattern as the compiler/interpreter); DocCapUnsupported retired.

add_context_rule survives solely as the zk-recovery COMPLETION vehicle,
hard-gated (DocOnlyWritePath) to the completion window: has_pending ||
completion_granted. The grant is new — Policy::enforce consumes the
pending during the completing call's __check_auth, BEFORE the entry
point body runs, so the controller now records a same-ledger TEMPORARY
CompletionGrant at consume time and exposes a completion_granted view.

Tests: unit suite rewritten for the doc-only surface (16 green);
integration tests ported — policy-mechanics suites stage rule shapes
through as_contract library helpers (test-only backdoor), guard e2e
checks moved onto the surviving guarded ops (initiate_upgrade), apply_doc
e2e now proves whole-rule-set replacement, cap install, anti-brick; 89
green; factory 28 green; clippy pedantic clean.
- Regenerate @nidohq/smart-account bindings from the doc-only wasm: the
  rule mutators are gone from the client; add_context_rule (completion
  vehicle), the doc surface, reads, and recovery machinery remain.
- Remove buildDocInstallTxs (the per-rule install route) and its
  DocInstallStep types/tests — apply_doc is the sole apply route.
- buildApplyDocTx: capped docs now ride the single apply_doc tx (the
  contract lowers caps onto its pinned stock spending-limit policy);
  the client-side cap refusal is gone.
- readPolicy: two tiers — doc-verified (view-first, event fallback,
  hash-verified) / decompiled fallback. The drift tier disappears by
  construction: no mutators, no drift.
- policyBlocks: scoped-session-key install/revoke and multisig-recovery
  install/revoke/rotation now THROW doc-only guidance (session keys have
  the doc route: compose + buildApplyDocTx; M-of-N friend recovery has
  NO doc route in doc v1 — headline cost, in the PR description). The
  read-side (fromChain/summarize) still renders existing rules. The
  frontend's remove-context-rule op builder throws the same guidance.

SDK vitest 256 green; frontend build + 408 tests green; cargo gates
green.
@willemneal willemneal changed the title spike: hybrid perch apply_doc in the smart account (doc-hash + event folded in) spike: doc-only perch apply_doc in the smart account (sole write path, doc-hash + JSON + event) Sep 10, 2026
Testnet deploy (identity: theahaco, the existing factory admin & registry
name owner):
- smart-account wasm f962dc8e… uploaded on-chain (embed==installed
  verified). The unverified/smart-account registry LABEL is owned by a
  different author key, so the 0.2.0 label publish was refused
  (WasmNameAlreadyTaken) — functional path is the direct upload, exactly
  the deploy script's own fallback; noted in DEPLOYED.md.
- Fresh doc-only factory CCJFOM6UGOH7JSAX22C3FAECG5657HKIUYDBTCMUMILKDA6LOA2J2EGG
  (admin theahaco); registry name 'factory' repointed to it (direct
  registry-contract invoke — the newer scaffold-registry CLI defaults to
  a different root registry than nido's unverified one).
- First zk-recovery testnet deploy, spike params (delay 60s, window 7d,
  max-cancels 2, floor 0): pool CAUZ6WFU… (factory-wired, reached via the
  factory's set_recovery_pool override; not registry-named), UltraHonk
  zk-verifier CDMNKDMP… with the committed depth-24 vk. Deployed via
  plain stellar CLI — the deploy-zk-recovery.mjs DEPLOY_SECRET path can't
  read a Secure-Store identity, and the CLI ctor-args bug it worked
  around is fixed.
- Live probe: create_account minted CBPE5AJY… (genesis leaf 0 inserted);
  the account's interface exposes apply_doc/get_applied_doc/
  applied_doc_hash/doc_rule_ids and none of the legacy mutators; both doc
  views return null (no doc applied yet).
- DEPLOYED.md rewritten for the new topology; passkey-sdk
  REGISTRY_FALLBACKS.factory updated (registry name already repointed;
  fallback fires only when the registry is unreachable). SDK 256 +
  frontend 408 tests green.
…re-bytes e2e

Live testnet failure (captain repro): every apply_doc trapped with
Error(Object, UnexpectedSize) decoding compile_doc's return. Root cause:
the account's wire types came from perch SOURCE rev f5676a6 (6-field
CompiledRule, with cap), but the canonical DEPLOYED compiler — hash
3645bd0d, still the registry's LATEST publish; perch tagged
doc-compiler-v0.2.0 but never published a newer wasm — is an older build:
5-field CompiledRule, no cap lowering, and CapUnsupported=6 in its error
enum. The pins were right; the source-vs-artifact skew was the bug, and
the e2e masked it by registering the NATIVE source-rev compiler at the
derived address.

Fix, aligned to ground truth:
- contracts/smart-account: drop the perch-doc-compiler crate entirely;
  mirror the DEPLOYED wasm's spec locally (CompiledDoc/CompiledRule/
  RuleScope/DocCompilerError incl. CapUnsupported; install as raw
  Vec<Val> passthrough — no interpreter type surface to mirror).
  Cap install branch removed (the deployed compiler refuses capped docs);
  DocCapUnsupported=10 returns as the relay of CapUnsupported. Cap
  support resumes when perch publishes the cap-capable compiler (the
  lowering branch is in this branch's history).
- Workspace: perch git deps reduced to perch-ir only; the theahaco
  stellar-accounts [patch] is GONE (nothing pulls the fork anymore) —
  that adoption cost disappears with artifact-aligned wire types.
- e2e now registers the FETCHED ON-CHAIN BYTES of the compiler +
  interpreter (fixtures/perch/*.wasm) at the derived addresses, plus a
  live-shape guard test: sha256(fixture) must equal the account's pins.
  The capped-doc test now proves the CapUnsupported refusal against the
  real deployed build. This configuration reproduces the live trap class
  locally.
- factory: Upgradable::upgrade override clears the cached embedded-wasm
  hash (stale-cache-after-upgrade bug found preparing the in-place
  testnet upgrade) + admin-gated refresh_account_wasm_hash for factories
  upgraded from pre-override code; two unit tests.
- SDK: buildApplyDocTx pre-refuses capped docs with the deployed-compiler
  reason; bindings regenerated (smart-account, factory).

Gates: just check/test green (7 apply_doc e2e vs deployed bytes, factory
30), SDK 257, frontend 408.
…ed end to end

- Uploaded account wasm b25e3287… (deployed-artifact-aligned wire types);
  in-place factory upgrade to f5374df1… on the SAME address
  CCJFOM6UGOH7JSAX22C3FAECG5657HKIUYDBTCMUMILKDA6LOA2J2EGG, then
  refresh_account_wasm_hash (the new admin entry) repaired the stale
  embed-hash cache the old code left behind. Registry name + pool wiring
  untouched.
- LIVE PROBE, the captain's exact failing flow: an add-admin-key policy
  document (owner passkey + second admin key on the self-admin rule)
  applied on-chain to fresh account CD5X4AWM…7JQI, signed via the
  wallet's own path (bindings-recorded auth entry + synthetic WebAuthn
  assertion over digest(payload || [0])). Verified live:
  applied_doc_hash 4fb7ae94…, get_applied_doc returns the full canonical
  JSON, doc_rule_ids [2], recovery rule preserved, default rule replaced.
  The previous wasm's UnexpectedSize trap is gone.
- DEPLOYED.md updated (new wasm hash, realignment note, live-probe
  evidence).
Perch's release CI published + deployed the cap-capable doc-compiler
0.2.1 (publish receipt on the perch-doc-compiler-v0.2.1 release):
registry moved to CDX2DMYM…, compiler CDWBJPDM… (wasm 35f248f0…),
interpreter CDR2OTZI… (wasm f63cae53…, same generation). Verified the
receipt's contract id equals the content-address derivation.

- contracts/smart-account: pins bumped to the new registry + both 0.2.1
  hashes; wire types mirror the 0.2.1 spec (6-field CompiledRule with
  cap; DocCompilerError back to 5 variants — CapUnsupported is gone);
  cap lowering restored onto the pinned stock spending-limit policy;
  anti-brick re-checks cap-free admin; DocCapUnsupported retired again.
- fixtures: both perch wasms refetched from the NEW on-chain instances;
  pin-fixture sha guard unchanged (and passing).
- e2e: capped doc installs interpreter + pinned spending-limit on one
  rule, proven against the fetched 0.2.1 build; 7 green.
- SDK: deployment.ts pins moved (new registry, interpreter f63cae53…,
  compiler hash added; golden derived addresses updated); the
  buildApplyDocTx cap refusal is gone.

Gates: just check/test green; SDK 256; frontend 408.
@willemneal

Copy link
Copy Markdown
Contributor Author

Superseded by #207, which merges this branch's work together with 200/201/202/204/205/206 into one reconciled, non-draft PR off main. This branch stays on origin for history.

@willemneal willemneal closed this Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant