Skip to content

refactor: remove legacy global contracts + migration scaffold (shards only)#33

Merged
iduartgomez merged 2 commits into
mainfrom
feat/adr01-phase4-legacy-teardown
May 26, 2026
Merged

refactor: remove legacy global contracts + migration scaffold (shards only)#33
iduartgomez merged 2 commits into
mainfrom
feat/adr01-phase4-legacy-teardown

Conversation

@iduartgomez

Copy link
Copy Markdown
Contributor

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). Cargo contracts/* 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, migrating flag, subscribeToUpdates, PostsFeedState, every ?? this.contractKey fallback, 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; removed web/freenet.toml posts 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).

… 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
iduartgomez merged commit 542f469 into main May 26, 2026
4 checks passed
@iduartgomez
iduartgomez deleted the feat/adr01-phase4-legacy-teardown branch May 26, 2026 13:51
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.
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).
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