refactor: remove legacy global contracts + migration scaffold (shards only)#33
Merged
Merged
Conversation
… only) Nothing is deployed yet, so there is no legacy data to migrate — drop the global posts/follows/likes contracts and the migration scaffold entirely. The app now talks ONLY to the parameterized shards (user/thread/inbox) + the identity delegate. Deleted: - contracts/posts, contracts/follows, contracts/likes (the global contracts; superseded by user-shard posts/profile/follows + thread-shard likes). The Cargo `contracts/*` glob drops them automatically; Cargo.lock regenerated. - web/src/migrations/ (run/state-store/candidates/legacy-hashes + tests) — only ever imported by freenet-api.ts; the candidate-chain migration was for global contract-hash bumps that no longer exist. freenet-api.ts: removed the global contract path entirely — contractKey field, deriveContractKey, runStartupMigrations/migrationGetState/migrationReinject, the `migrating` flag, subscribeToUpdates, PostsFeedState, and every `?? this.contractKey` fallback. connect() no longer gates on __MODEL_CONTRACT__ (offline is gated by __OFFLINE_MODE__ in index.ts); startup() just resolves the barrier; loadState / handleGetResponse / handleUpdateNotification / publishPost / completePublish now target and parse only the user shard (ShardDelta::Posts form). Removed the onMigration callback + index.ts migration-toast. Build/config: dropped build-posts/follows/likes, test-posts/follows/likes, publish-posts/follows/likes tasks + their deps in build-contracts/publish-all; removed __MODEL_CONTRACT__/__FOLLOWS_CONTRACT__/__LIKES_CONTRACT__ from vite.config + vite-env.d.ts; cleaned the legacy txt files from the clean task and .gitignore; removed the web/freenet.toml webapp dependency on contracts/posts (shards are PUT at runtime, not a build-time dep). Docs: README + AGENTS updated to describe the shard architecture (was still documenting the global contracts + Ed25519). Gate: cargo fmt/clippy -D warnings/test workspace green; tsc + vitest (11, the migration suites are gone) green; offline web build clean.
Skeptical review SHIP'd; only nits were two stale strings referencing a 'legacy feed' / 'startup migration loop' that no longer exist after the teardown. Reworded the no-code-hash warning and the startupDone-await comment to match the shards-only reality. No behavior change.
iduartgomez
added a commit
that referenced
this pull request
May 27, 2026
…gaps (#35) * fix: holistic-review follow-ups — GET serialization regression + test/doc gaps Whole-system review of the merged ADR-0001 shard stack (PRs #24–#33), reading all three shards + delegate + web client together rather than one PR diff, surfaced fixes the per-slice reviews could not see. Code: - H-1 (regression from #33): the stdlib resolves GET promises off a single uncorrelated FIFO queue. The slice-1 `startupDone` barrier only covered startup-time traffic, which the teardown removed — so post-cutover, with multiple shard flows live (user-shard probe/load, per-thread probe/like- refresh), two in-flight GETs could swap responses: a thread GET resolving the user-shard probe (feed never instantiates) or a like-refresh popping a thread probe (that thread never PUT → like silently lost). Replace the vestigial barrier with `serializedGet`: every api.get() chains behind the previous one's settle, so at most one GET is outstanding. - M-3: ensureThreadShard now awaits its PUT before the caller sends the like UPDATE (an UPDATE to a never-instantiated contract is dropped), and completeLike re-GETs the authoritative aggregate on UPDATE failure too, so the optimistic toggle is always reconciled rather than left stuck. - L-1: strip all NUL padding bytes from user-shard deltas, not just the first. Tests: - identity delegate: first unit tests (was zero) — export→import seed round- trip, and SignPost/SignLike outputs verify under the same `common` code the contracts run (thread-context binding, vk_hex == owner param). Extracted two pure signing helpers so tests exercise the real assembly code (host target can't drive process(): WASM-only secret store). - StateAndDelta update arm exercised in all three contracts (was untested). - web client: new freenet-api.test.ts pins the serializedGet single-in-flight property (the H-1 anti-misroute guarantee), optimistic-like revert, and nonce matching. - golden signing-payload byte vectors in common (tripwire vs consistent encoding drift that passes injectivity); inbox-shard key derivation vector. Docs / tracking: - finish the #33 teardown doc sync: AGENTS.md migration section + delta-format; README Ed25519→ML-DSA-65, shard roadmap, SignLike + deferred reply/inbox wiring. Close obsolete issue #23 (migration runtime deleted); open #34 for the WASM-in-node e2e tier (the load-bearing GET-rejects-on-uninstantiated assumption is still unverified against a live node). * fix: serializedGet must chain on raw GET settle, not the soft timeout (review H-A) Skeptical review caught that the H-1 fix was incomplete: the chain advanced on the 8s app-level withTimeout, but the stdlib keeps a GET registered in its pendingGets FIFO until a real response/NotFound or its OWN 30s REQUEST_TIMEOUT_MS. With 8s < 30s and no request cancellation, a timed-out GET stayed queued while serializedGet advanced and issued the next api.get() → two entries in the FIFO → a late response could pop the wrong awaited promise. That reopens the exact misroute the fix exists to close (wrong userShardExists bool → shard never PUT; wrong ensureThreadShard "exists" → like UPDATE silently dropped). Fix: chain getChain on the RAW api.get() promise, so the chain only advances once the stdlib pendingGets entry is truly drained (real settle or stdlib's 30s reject). The caller's 8s `ms` becomes a SOFT view (early resolve/reject for the caller) that does NOT release the next GET. A genuinely hung GET still drains via the stdlib 30s timeout, so the chain can't wedge. Adds a regression guard (fake timers) asserting the chain stays blocked through the 8s soft timeout and advances only on the real GET settle — fails against the prior run-chaining implementation. Also confirmed (no change needed): thread-shard UpdateData::State merges into the current shard (not replace), so a spurious re-PUT of empty initial state is a no-op against existing likes (review M-B). Documented the timeout subtlety in 0001-implementation-notes.md.
This was referenced May 27, 2026
iduartgomez
added a commit
that referenced
this pull request
May 30, 2026
* feat: plain repost (retweet) E2E on thread shard (#14) Wire the plain-repost ("retweet") half of the engagement surface end to end, mirroring the existing likes path. The repost button in the post card was previously a local-only toggle (`// TODO: wire to reposts contract`); it now signs a `RepostRecord` and folds it into the post's thread shard, with the authoritative count reconciled back to the UI. Quote-repost (repost-with-comment) is the remaining half of #14 and lands in a follow-up PR. Architecture — `RepostRecord` is a direct mirror of `LikeRecord`: - common: new `RepostRecord` (signer, seq, reposted, writer_cert, signature) under `REPOST_DOMAIN_TAG = b"raven:thread-repost:v1"`, with a thread-bound signing payload and a golden-vector tripwire test. - thread-shard: new per-reposter `reposts` surface with the same join semilattice as likes (higher seq wins; un-repost wins an equal-seq tie), post-merge cap, full re-verification on every merge path (delta / full-state / sync), validate_state agreement, and summarize/get_state_delta coverage so reposts converge over the sync protocol. - delegate: `SignRepost` request + `SignedRepost` response + `build_signed_repost`, mirroring `sign_like`. - web: `repostPost`/`completeRepost`/`dropPendingRepost`/`emitRepostState` in freenet-api, `signRepost` in identity, response routing in index, and the post-card button wired through feed/app with optimistic toggle + authoritative reconcile. Tests: common golden + roundtrip/tamper/thread-bind; thread-shard convergence + forged-record rejection + sync-protocol convergence; delegate sign/verify + thread-binding; vitest completeRepost success/failure + drop-pending + thread-GET aggregate. Also removes three orphaned `contracts/{follows,likes,posts}/build/` artifact dirs left by #33 (the crates were deleted but their untracked build output remained, breaking the local `contracts/*` workspace glob). * chore: refresh published-contract snapshot (repost UI changed web bundle) The web_container_contract.wasm embeds the built web bundle; the plain-repost UI wiring changed the bundle, so the committed snapshot drifted. Refreshed from the CI 'published-contract-rebuilt' artifact (byte-exact match for verify-published-contract).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Supersedes #32 (auto-closed when its base branch #31 was deleted on merge). Rebased onto main; diff is teardown-only. Skeptical-reviewed and SHIP'd on the prior head (
144c49e), nits fixed.What
Nothing deployed yet → no legacy data to migrate. Delete the global contracts + migration scaffold; the app talks ONLY to parameterized shards (user/thread/inbox) + identity delegate.
Deleted
contracts/{posts,follows,likes}/(superseded by user-shard posts/profile/follows + thread-shard likes). Cargocontracts/*glob drops them.web/src/migrations/(run/state-store/candidates/legacy-hashes + tests) — only imported by freenet-api.ts.freenet-api.ts
Removed the whole global path:
contractKey,deriveContractKey,runStartupMigrations/migrationGetState/migrationReinject,migratingflag,subscribeToUpdates,PostsFeedState, every?? this.contractKeyfallback,onMigration.connect()no longer gates on__MODEL_CONTRACT__(offline gated by__OFFLINE_MODE__in index.ts); load/get/notification/publish target & parse only the user shard (ShardDelta::Posts). Thread-likes routing (slice 2) preserved.Build/config
Dropped
build-/test-/publish-{posts,follows,likes}+ deps; removed__MODEL_CONTRACT__/__FOLLOWS_CONTRACT__/__LIKES_CONTRACT__from vite; cleaned clean-task + .gitignore; removedweb/freenet.tomlposts dependency. README + AGENTS updated to shard architecture.Gate
fmt/clippy -D warnings/test workspace; tsc + vitest (11); offline build — all green. Skeptical review verified zero dangling refs, offline/DEV/shard/thread paths intact (tsc+vitest on a fresh copy).