Scaffold the EFS SDK monorepo#1
Conversation
pnpm + Turborepo + Changesets workspace with two packages: - @efs-project/sdk — TypeScript SDK (tsup dual ESM/CJS, viem-native, vitest) - @efs-project/solidity — compile-in Solidity library (Foundry, ships .sol source) Includes the ADR system (mirrored from contracts, lighter — ADR-0001 layout, 0002 viem-only, 0003 compile-in Solidity), top-level docs (README, CONTRIBUTING, AGENTS.md), CI, and scaffolded public API shapes reflecting the planning design (identity seam, static-vs-dynamic refs). TS build/typecheck/test/lint verified green; Solidity is CI-verified (forge not local). Best-practice review applied (changeset config, publishConfig, forge-std CI install, .nvmrc/engines). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- Rename npm scope @efs-project/* -> @efs/* (scope confirmed unpublished;
GitHub org stays efs-project). Pending `npm org create efs` before publish.
- ADR system: cut the contracts-inherited Etched/Durable/Ephemeral "permanence
framing" section + the cargo-culted `Permanence:` template field (validation
flagged as residual freeze-rigor); fold the one real point (published npm API
is the only near-permanent surface) into Discipline.
- Add the missing procedural bits agents need: next-number mechanic, an
ADR-worthiness threshold ("a choice with alternatives worth preserving"), and
a worked boundary example (vault design -> SDK ADR slice).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Establishes the three-layer doc model so nothing duplicates: specs (how it works now) vs ADRs (why we chose it) vs planning vault (cross-cutting design). Seeds specs/overview.md (the SDK model at a glance) and wires it into AGENTS.md as start-here reading. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c8e5a19b20
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- BLOCKER fix: .github/workflows/ci.yml still used @efs-project/sdk (the rename grep missed .yml) — would have failed every CI run. Now @efs/sdk. - Add release.yml using npm Trusted Publishing (OIDC) — no stored NPM_TOKEN, provenance automatic. ADR-0004 records the decision (OIDC over long-lived token) + the one-time per-package trusted-publisher setup. - Mark docs/api row "(later)"; bump recommended-next ADR numbers. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7994df5334
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- Solidity job failed: `forge install --no-commit` flag was removed in current Foundry. Clone forge-std (test-only dep) via git instead — version-stable. - Changeset job failed: no changeset on the PR. Add an empty changeset (the designed no-release escape hatch) for the scaffold. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- CI: minimal `permissions: contents: read`, cancel-in-progress concurrency, `timeout-minutes` on all jobs, pin forge-std to v1.9.4 (no master drift). - Release: pin global npm to @11; explicit `publishConfig.provenance: true` on both packages (belt-and-suspenders around the changesets/pnpm publish path). - ADR-0004 reconciled with the explicit-provenance choice. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
forge fmt --check was the last red CI job. Reformatted EFSLib/EFSWriter to Foundry style (verified locally with forge 1.7.1), simplified the stub signatures to unnamed params/returns so there are no unused-variable warnings, and removed the unreachable emit in _efsPinFile. Build + test pass locally. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eadb898eab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- P1: release workflow ran `pnpm build` (turbo) which builds @efs/solidity via `forge build`, but never installs Foundry → release job would fail on main. Scope the release build to @efs/sdk only; the Solidity package publishes .sol source and needs no build artifact (CI compile-checks it per-PR). - P2: remove the hardcoded `version = '0.0.0'` export — it would go stale after the first Changesets bump. Re-add derived from the manifest when implementing. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… deployer
The SDK ships a maintained chainId -> {addresses, schema UIDs} registry and
resolves EFS by chain (with a custom-deployment override). It deploys nothing;
integration tests fork a registry chain (anvil --fork-url) like the contracts
repo, rather than reimplementing EFS's CREATE3/proxy deploy. Reflected in the
overview spec.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- DX-7/ENG-14: lower Solidity pragma floor ^0.8.28 -> ^0.8.26 so the library is compilable alongside the contracts repo (pinned 0.8.26). Real compat bug. - DX-11: fix wrong ADR pointer in index.ts (cited ADR-0004 = OIDC publishing for the error model; now points at "Recommended next"). - DX-10: repoint READMEs from the private planning vault to the public in-repo docs/specs/overview.md as the primary "how it works" reference. Larger API-shape findings (resume/onProgress in pinFile, fetch+verify, resolvedBy, branded UIDs, pagination iterator, deployments codegen) tracked for the beta-slice spec. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d1e88d32fa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
First functional-slice plan for @efs/sdk: write/read real files with the API shapes frozen correctly before publish. Grounded in the freeze-branch contracts (contentHash now lives as a lens-scoped PROPERTY string; no chain deployed but a local fork exists) and two review passes. Adversarial review caught: (1) the spec/scaffold drifted to a FLAT API but the validated design is NAMESPACED (efs.fs.write) — realigned, surfaced as Decision F; (2) content verification is trust-relative not absolute (attacker can attest a contentHash under their own lens) — honest semantics baked in; (3) honest click count (~3 with metadata, not 2); richer WriteReceipt/preview; per-step idempotent resume; pinned local-fork artifact boundary; declared the large-file/update/delete seams. Decisions A/C/D/E/F surfaced for James. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…pping - P1: the "Changeset present" gate would block Changesets' own Version Packages PR (it consumes the changeset files). Skip the gate for head_ref changeset-release/main so the version PR can pass and publish. - P2: `pnpm test` / `pnpm --filter @efs/solidity test` failed on a clean checkout (forge-std absent, lib/ gitignored). Add a `setup` script + prebuild/pretest hooks that bootstrap forge-std, and DRY the CI solidity job to run the package scripts so CI and a contributor's local run are identical. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…odule Decision A closed after two expert passes: contentHash is a bare SHA-256 digest (lowercase hex, sha256sum-identical); the PROPERTY key is the algorithm tag — no multihash/CID/keccak (its future-proofing is illusory; the IPFS-CID rationale is false). Built it (it's pure, chain-independent): - src/content/hash.ts: hashContent() + trust-relative verifyContent() (matches-author/mismatch/no-claim; no-claim != success; malformed claim guarded) - tests against known SHA-256 vectors; exported from index.ts - ADR-0006 + docs/specs/content-hash.md (incl. fetch-safety: lens-scoped mirrors, size-cap streaming, ignore transport Content-Type) - updated beta-slice (Decision A done) + overview + ADR index (next: 0007/0008) Verified by subagent (impl correct, vectors confirmed, cross-docs consistent: GO). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 39653f6167
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
changesets/action with `publish: pnpm release` would publish the unpublished 0.0.0 scaffold packages on the first no-changeset main run — premature, and the @efs org doesn't exist yet. Drop the `publish:` input so the action only manages the Version Packages PR and never publishes. Re-enable at launch with a one-line `with: { publish: pnpm release }` (documented inline + in ADR-0004). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…rors Realign to the namespaced client (Decision F): efs.fs.* / efs.lenses.* / efs.eas.* / efs.raw.*. Built the foundational, chain-independent layers: - eas/: viem-native EAS layer (vendored ABIs, SchemaEncoder, attest/multiAttest builders, UID re-derivation) — no ethers/eas-sdk (ADR-0002). 15 tests. - lenses/: opaque Lens, lens()/identity()/resolveLens; throws MaxLensesExceeded rather than truncating (review S2); ENS resolution; MAX_LENSES=20. - chain/: deployments registry + resolveDeployment + construct-time integrity gate (checks view/router bytecode, review S1) (ADR-0005). - errors/: typed EfsError tree (NotImplemented/LensRequired/MaxLensesExceeded/ SchemaMismatch/DeploymentNotFound). - types/: branded DataUID, DataRef vs PathRef, WriteReceipt/ReadResult/EfsFile. - fs.* verbs are NotImplemented stubs with their final signatures. format script now runs `biome check --write` (organizes imports too, matching CI). tsc/test(24)/build/lint all green. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Integration review (GO) flagged two: - B1/B2: fs.* stubs were async-typed but threw synchronously — `.catch()`/ `.rejects`/`for await` would misbehave. Make them reject / throw-on-iterate so the async contract is locked before fs.* is implemented; tests use `.rejects`. - S1: assertDeploymentIntegrity only checks bytecode presence — softened the doc to say so (sanity gate, not "is the right EFS contract"); the real trust gate (schema-UID match) is noted as landing with the read layer. EAS layer, lenses, deployments, errors all verified by the review. tsc/test(24)/ build/lint green. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e92a8d0f0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The quickstart still used the pre-F flat API (efs.read/efs.pinFile); the client is namespaced (efs.fs.read/write/fetch). Updated to the accurate shape — read returns a ref + resolvedBy, fetch gets verified bytes — and added a status note that fs.* verbs are the target shape (currently NotImplemented). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 145e590697
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ex P2) buildAttest/buildMultiAttest returned no tx-level `value`, so a non-zero per-attestation resolver value would revert (no ETH sent). Compute and include `value` on the ContractCall: buildAttest forwards data.value; buildMultiAttest sums every entry across all requests. Added tests (26 total). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
From the 3-agent foundation review. The additive-now/breaking-later items: - Type-level write gating: createEfsClient overloads return EfsReadClient (no write verbs) without a walletClient, EfsClient with one (viem's read/write split). Runtime WalletRequired backstop. - EfsError -> viem-BaseError-grade: shortMessage, open-union `code`, cause, walk(). Added WalletRequired/CursorInvalid/PartialBatchFailure. - Named, exported option/return types (ReadOptions/ListOptions/FetchOptions/ WriteOptions) — no inline literals; param renamed `as` -> `lens`. - Pagination seam: Page<T> + EfsList<T> (AsyncIterable + .page()). - Batch seam: efs.batch() + BatchReceipt/OperationResult/WriteMechanism; fs.write documented as sugar over it. - Wallet-agnostic seam (your point): EfsReader/EfsWriter aliases instead of raw viem types in the public config, so widening to an ethers adapter later is non-breaking. viem-core stays; any wallet already works via EIP-1193. - FileStat (was Stat), branded steps[].uid, dropped root `export *`. Deferred (additive, non-breaking later): full graph/props/lists/sorts namespace skeleton — designed in a dedicated pass. tsc/test(27)/build/lint green. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4017c175b9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Renamed the read option `as` -> `lens` in the hardening batch; the README
quickstart still showed `{ as: ... }`. Fixed.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…t chain drift Three more drift windows in a multi-step write: writeFileTier1 now re-asserts the chain before its planning reads (parent/visibility/transport) so a wrong-chain UID can't enter the plan; submitLayeredTier1 re-asserts before each receipt wait (inside the wait try) so a post-send drift surfaces as outcome-unknown carrying the txHash, not a false partial failure; storeOnchain re-asserts before each deploy's receipt wait so a drift fails closed with WrongChain instead of a misleading no-address abort. Updates/adds regressions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e9adb0f964
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
JamesCarnley
left a comment
There was a problem hiding this comment.
Re-reviewed latest head e9adb0f.
The newest write-guard commits look good: standalone planning reads are guarded before they feed plans, file-write planning reads are guarded before parent/tag/transport reads, and receipt waits are guarded after broadcast while preserving the outcome-unknown recovery shape. Local verification passed on this head: git diff --check origin/main...HEAD, pnpm --filter @efs/sdk typecheck, pnpm --filter @efs/sdk test (563 passed, 3 skipped), pnpm --filter @efs/sdk build, pnpm lint, and pnpm --filter @efs/solidity test (53 passed). GitHub CI is green and merge state is clean.
One non-blocking coverage note: lists.remove(entryUID, { listUID }) now guards the advisory config read, but I only see tests for the existing append-only/list-not-found/revocable behavior, not the new WrongChain/no-read/no-revoke case. I would not block on that because the path is advisory and the revoke itself is already chain-guarded.
The merge-blocking release/docs issue is still present: .changeset/escape-hatches-raw-eas-decode.md and packages/sdk/src/raw/contracts.ts still say efs.raw.* instances re-resolve deployment lazily so chain changes are reflected. Current code and the later raw-surface changeset describe the actual contract: raw instances are bound to construction-chain deployment addresses and fail closed with WrongChain on live chain drift. Please update those stale docs before merge so the release notes/source docs do not promise raw chain-switch re-resolution.
… chain drift setOverview now re-asserts the chain before the /tags/system resolution (its UID becomes the Overview marker TAG, baked into the plan before writeFileTier1's own re-assert); mirrors.add re-asserts before resolveMirrorTransport's on-chain /transports fallback, reusing the submit context. Completes the standalone-write planning-read sweep (mirrors.add was the one missed). Adds regressions for both. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ADR-0014/0015) Additive foundational scaffolding, no behavior change. Adds the ReadSource interface + capabilities, the ViemReadSource live adapter (chain-as-data) and snapshot/indexer stubs, the TrustDescriptor provenance shape, and reserved fetch/verifier client-config slots that throw NotImplemented rather than silently no-op. Two ADRs capture the decisions (pluggable read source + chain-as-data; read-trust provenance / Fork 1 = inline hybrid). The rename, read-path wiring, trust stamping, and chainless construction are the gated behavioral phases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The additive ReadSource scaffolding (viem/snapshot/indexer adapters + config guards) put the gzipped bundle at 36.02 kB, 21 B over the 36 kB budget (the types are erased; the adapters are real runtime). Raise to 38 kB — headroom for this slice plus the upcoming read-path wiring/trust phases — consistent with the prior surface-growth bumps (10 → 36 kB). Revisit size after launch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e budget 69 kB
Two independent expert reviews of the trust field names converged. Reshape the reserved
TrustDescriptor (ADR-0015) from a flat { source, existence, revocation, asOf } record to a
discriminated union on `freshness` ('current' | 'as-of' | 'stale'): the dangerous state is
now spelled `stale` and can't hide behind a reassuring `existence:'confirmed'`, incoherent
combinations are unrepresentable, and asOf exists only on the 'as-of' variant. `source`
literals reconciled with ReadSourceCapabilities.kind ('cache' → 'snapshot'); the 'live' value
collision (a source AND a revocation value) removed; the require→requireTrust sugar option
moved onto the same freshness lattice (default 'as-of'). ADR-0015 updated with the review.
Bundle budget raised to 69 kB (loose pre-launch headroom). Reserved type, no behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 040b374b73
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…rift (TOCTOU) liveDeployment() resolves the deployment at time T, but the read engines' readContract/getCode run at T+1; a provider that drifts in between used the resolved chain's addresses on the new chain. readContext now hands the engines a chainGuardedPublicClient pinned to the resolved deployment.chainId, re-asserting live==resolved before each read (fail closed, WrongChain). The guard proxy now covers getCode too (web3:// reads); getEnsAddress stays unguarded (ENS is cross-chain). Adds a TOCTOU regression (eth_chainId reports seeded chain at resolve, drifts at read). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
JamesCarnley
left a comment
There was a problem hiding this comment.
Re-reviewed latest head dd5de95.
Local verification passed on this head: git diff --check origin/main...HEAD, pnpm --filter @efs/sdk typecheck, pnpm --filter @efs/sdk test (573 passed, 3 skipped), pnpm --filter @efs/sdk build, pnpm lint, and pnpm --filter @efs/solidity test (53 passed). GitHub CI is green and merge state is clean.
The newest write-planning guards look good: setOverview and mirrors.add now guard before the planning reads that feed their plans, and the tests cover the important no-read/no-submit ordering. I did not find a code blocker there.
I do still see merge-blocking docs/API-truth issues:
-
The prior raw docs/release-note blocker remains.
.changeset/escape-hatches-raw-eas-decode.mdandpackages/sdk/src/raw/contracts.tsstill say raw contract instances re-resolve deployment lazily so chain changes are reflected. Current code and the later raw-surface changeset describe the real contract: raw instances are bound to construction-chain deployment addresses and fail closed withWrongChainon live-chain drift. -
docs/adr/0015-read-trust-provenance.mdreads like the trust field lands now: it saysEfsFile,FileInfo, andReadResultcarry requiredtrust, and that reads stamp{ freshness:'current', source:'live' }. But the current types do not includetrust; the changeset andTrustDescriptorcomment correctly describe it as reserved for a later behavioral slice. Please soften the ADR language so it does not claim current result shapes already includetrust. -
docs/adr/0014-pluggable-read-source-and-chain-as-data.mdsays chainless clients work / the chainless-client rejection is superseded, butcreateEfsClientstill rejects chainlessViemConfig.publicClientat construction.viemReadSource(client, { chainId })supports chain-as-data, but it is not wired intocreateEfsClientyet. Please make the ADR match this phase. -
snapshotReadSourceadvertisessupportsGetCode/supportsEnsfromsnap.code/snap.ens, butgetCodecurrently returnsundefinedandgetEnsAddressis absent even when ENS data is present. Either implement those optional lookups or advertise the capabilities as false until the stub can actually serve them.
Once those docs/capability-truth issues are corrected, the verified code paths look very close.
JamesCarnley
left a comment
There was a problem hiding this comment.
Re-reviewed latest head 0fc48eb.
The new read-path TOCTOU guard looks good: readContext now resolves the live deployment, then gives the read engines a public client guarded against that resolved deployment.chainId; the guard also covers getCode for web3:// reads while leaving ENS unguarded as documented. Local verification passed on this head: git diff --check origin/main...HEAD, pnpm --filter @efs/sdk typecheck, pnpm --filter @efs/sdk test (574 passed, 3 skipped), pnpm --filter @efs/sdk build, pnpm lint, and pnpm --filter @efs/solidity test (53 passed). GitHub CI is green and merge state is clean.
The merge-blocking docs/capability issues from the previous review still appear unresolved:
.changeset/escape-hatches-raw-eas-decode.mdandpackages/sdk/src/raw/contracts.tsstill claim raw contract instances re-resolve deployment lazily so chain changes are reflected, while the actual contract is fail-closedWrongChainon live-chain drift.docs/adr/0015-read-trust-provenance.mdstill reads as thoughEfsFile,FileInfo, andReadResultalready carry requiredtrust, but this PR only reservesTrustDescriptor; current result types do not includetrustyet.docs/adr/0014-pluggable-read-source-and-chain-as-data.mdstill says chainless clients work / the chainless-client rejection is superseded, butcreateEfsClientstill rejects chainlessViemConfig.publicClient; onlyviemReadSource(client, { chainId })supports chain-as-data today.snapshotReadSourcestill advertisessupportsGetCode/supportsEnsbased on supplied snapshot data, but the stub does not actually serve those lookups (getCodereturnsundefined;getEnsAddressis absent).
Code-path hardening continues to look solid; the remaining blockers are docs/release/capability truth issues that would mislead callers if this merged as-is.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0fc48eb632
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…mespace read TOCTOU fetchVerified (public) now rejects non-finite/non-positive maxBytes (NaN slips the `>` cap checks, Infinity disables the ceiling). submitLayeredTier1 swallows a throwing onLayer/onProgress callback so a reporting bug can't abort the remaining irreversible layers (partial write). The standalone namespaces (graph.tags/pins, props, mirrors) now route their reads through a chainGuardedPublicClient pinned to the resolved deployment chain — closing the same resolve-then-read TOCTOU just fixed in readContext (optional dep; falls back unguarded in tests). Adds 7 regressions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5d44417a23
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… ADDR width props.list previously guarded only the anchor-page read — the per-anchor getAttestation used the unguarded publicClient and the value reads went through readContext() (a second liveDeployment resolution). A mid-list chain switch decoded chain-A anchors against chain B. All props.list reads now route through the guarded client pinned to the single resolved deployment. Separately, lists.has now rejects a non-20-byte ADDR-mode target (a 32-byte UID colliding on its trailing 20 bytes returned a false true), mirroring validateAddTarget. Adds regressions; updates the props.list tests to a single production-faithful read client. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
JamesCarnley
left a comment
There was a problem hiding this comment.
Reviewed current head 5d44417a23 in a clean detached worktree. CI is green and local verification passed (git diff --check origin/main...HEAD, pnpm --filter @efs/sdk typecheck, pnpm --filter @efs/sdk test, pnpm --filter @efs/sdk build, pnpm lint, pnpm --filter @efs/solidity test). Still not merge-ready; these blockers remain:
-
props.liststill escapes the new pinned-chain read guard. It resolvesdepand creates guardedpc, but the anchor-name decode pass still reads with unguardeddeps.publicClient(packages/sdk/src/writes/props.ts:191) and the value pass callsdeps.readContext()(packages/sdk/src/writes/props.ts:203), which can re-resolve a different live chain mid-method. A provider switch after the first guarded page read can still mix chains or avoid the intendedWrongChainfail-closed behavior. Please keep the later reads pinned to the samedep/guarded client and add a directprops.listregression; the new test name mentions props but currently exercises onlymirrors.list. -
ADR-0015 still promises required read-trust fields that the public API does not carry. The ADR says
EfsFile,FileInfo, andReadResultcarry requiredtrust: TrustDescriptorplusrequireTrust/StaleTrustsugar gating, but the current exported types omittrust(packages/sdk/src/types.ts:543,:749,:773) and returned files are built without it. Either land the behavioral slice now or revise the ADR/changeset language so PR #1 honestly reserves the shape instead of claiming it is present. -
ADR-0014 still says chainless
createEfsClientworks in PR #1, butcreateEfsClientcontinues to throwInvalidArgumentwhenpublicClient.chain?.idis absent (packages/sdk/src/index.ts:633), and the tests assert that rejection. Please make the docs match the shipped behavior, or actually route read-only chainless clients through theviemReadSource(client, { chainId })path. -
The raw-contract docs and release note still overclaim live chain rebinding.
packages/sdk/src/raw/contracts.tsand.changeset/escape-hatches-raw-eas-decode.mdsay lazy raw access reflects a chain change, while the implementation is construction-chain bound and guarded to fail closed withWrongChainon live-chain drift. The docs should say that exactly. -
snapshotReadSourcestill advertises capabilities it does not implement. Withsnap.code, it setssupportsGetCode: truebutgetCodealways returnsundefined; withsnap.ens, it setssupportsEns: truebut installs nogetEnsAddress. BecauseReadSourceCapabilitiesare meant to be branch-before-call flags, these need to be honest (implement the map lookups or advertisefalseuntil the stub is real). -
Minor docs drift:
.changeset/foundational-read-source-scaffolding.mdstill describes the oldTrustDescriptorshape assource/existence/revocation; the exported type is now thefreshnessdiscriminated union.
JamesCarnley
left a comment
There was a problem hiding this comment.
Re-reviewed current head 4363d015b6b71d4dc1b0b106c156c51b38601ec1 in a clean detached worktree.
The newest code fixes look good:
- The previous
props.listTOCTOU blocker is addressed.props.listnow resolves one deployment, derives one guarded read client, and uses that same pinned client for anchor enumeration, anchor-namegetAttestation, and per-key value reads. Focused subagent review found no issue in this slice. - The new
lists.hasADDR-width validation also looks correct. The false-positive was real for 32-byte values that already matched the right-aligned address membership key; valid 20-byte addresses remain accepted. Focused subagent review found no issue in this slice.
Fresh verification passed on this head:
git diff --check origin/main...HEADpnpm --filter @efs/sdk typecheckpnpm --filter @efs/sdk test(583 passed, 3 skipped)pnpm --filter @efs/sdk buildpnpm lintpnpm --filter @efs/solidity test(53 passed)
GitHub CI is green, merge state is clean, and there are no unresolved review threads. This head is still not merge-ready because the remaining docs/capability-truth blockers from the previous review are still current:
-
ADR-0015 still reads as though
EfsFile,FileInfo, andReadResultalready carry requiredtrust: TrustDescriptorplusrequireTrust/StaleTrustgating, but the current exported result types do not includetrustyet. Please make the ADR/changeset language explicit that this PR reserves the shape rather than shipping the behavioral result fields. -
ADR-0014 still says chainless
createEfsClientworks / the chainless-client rejection is superseded in PR #1, whilecreateEfsClientstill rejects a chainlessViemConfig.publicClient.viemReadSource(client, { chainId })supports chain-as-data, but that is not wired throughcreateEfsClientyet. Please make the ADR match this phase. -
The raw-contract docs/release note still overclaim live chain rebinding.
.changeset/escape-hatches-raw-eas-decode.md,packages/sdk/src/raw/contracts.ts, and the matchingpackages/sdk/src/index.tscomments still say raw instances re-resolve deployment lazily so chain changes are reflected. The actual contract is construction-chain-bound raw instances guarded withWrongChainon live-chain drift. Please update the stale wording. -
snapshotReadSourcestill advertises capabilities it does not implement. Withsnap.code, it setssupportsGetCode: truebutgetCodereturnsundefined; withsnap.ens, it setssupportsEns: truebut nogetEnsAddressis installed. Since these are branch-before-call capability flags, either implement those lookups or advertisefalseuntil the stub can serve them. -
Minor release-note drift:
.changeset/foundational-read-source-scaffolding.mdstill describes the oldTrustDescriptorshape assource/existence/revocation; the exported type is now thefreshnessdiscriminated union.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4363d015b6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… attestationsFor
fetchRef clamped the fetch cap to the declared size; for a size-0 file that clamped to 0, which
fetchVerified rejects as a non-positive cap — empty files failed. The clamp now applies only for
positive declared sizes (empty bodies verify vs the empty-SHA-256 under the default cap).
attestationsFor now flattens the top-level ref.uid/dataUID/anchorUID that HasSourceUIDs accepts
(was only the sourceUIDs bag), so DirEntry/DataRef DTOs hydrate instead of returning {}. Adds 2 regressions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 16bf2c92fa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
verifyDeployment resolved the deployment from the live chain but probed getCode + schema readContract via the unguarded publicClient; a post-resolution chain switch verified the resolved chain's addresses against the new chain. It now probes through a chainGuardedPublicClient pinned to the resolved dep.chainId (the guard already covers both readContract and getCode). Adds a TOCTOU regression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
JamesCarnley
left a comment
There was a problem hiding this comment.
Re-reviewed current head e1d1de67be7541ae0827c6df9b37c9b25c787993 in a clean detached worktree.
The two newest code commits look good:
16bf2c92fixes empty-file reads (sizeattested0) without weakening the positive fetch cap behavior, andattestationsFornow hydrates DTOs that carry only top-leveldataUID/anchorUID/ref.uid. Focused review found no issue.e1d1de6fixesefs.raw.verifyDeployment()TOCTOU by resolving the deployment once and probing through a client guarded to that resolved chain. Deployment overrides still flow through the same resolver, and the new drift regression covers the intendedWrongChainfail-closed path. Focused review found no issue.
Fresh verification passed on this head:
git diff --check origin/main...HEADpnpm --filter @efs/sdk typecheckpnpm --filter @efs/sdk test(586 passed, 3 skipped)pnpm --filter @efs/sdk buildpnpm lintpnpm --filter @efs/solidity test(53 passed)
GitHub CI is green, merge state is clean, and there are no unresolved review threads. Still not merge-ready because the docs/capability-truth blockers remain in the current tree:
-
ADR-0015 still reads as though
EfsFile,FileInfo, andReadResultalready carry requiredtrust: TrustDescriptorplusrequireTrust/StaleTrustgating, but current exported result types do not includetrustyet. Please make the ADR/changeset language explicit that this PR reserves the shape rather than shipping those behavioral fields. -
ADR-0014 still says chainless
createEfsClientworks / the chainless-client rejection is superseded in PR #1, whilecreateEfsClientstill rejects a chainlessViemConfig.publicClient.viemReadSource(client, { chainId })supports chain-as-data, but that is not wired throughcreateEfsClientyet. Please make the ADR match this phase. -
The raw-contract docs/release note still overclaim live chain rebinding.
.changeset/escape-hatches-raw-eas-decode.md,packages/sdk/src/raw/contracts.ts, and matching comments inpackages/sdk/src/index.tsstill say raw instances re-resolve deployment lazily so chain changes are reflected. The actual contract is construction-chain-bound raw instances guarded withWrongChainon live-chain drift. Please update the stale wording. -
snapshotReadSourcestill advertises capabilities it does not implement. Withsnap.code, it setssupportsGetCode: truebutgetCodereturnsundefined; withsnap.ens, it setssupportsEns: truebut nogetEnsAddressis installed. Since these are branch-before-call capability flags, either implement those lookups or advertisefalseuntil the stub can serve them. -
Minor release-note drift:
.changeset/foundational-read-source-scaffolding.mdstill describes the oldTrustDescriptorshape assource/existence/revocation; the exported type is now thefreshnessdiscriminated union.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e1d1de67be
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…scape hydration submitLayeredTier1's pre-send chain guard now folds a drift into WriteNotSentError (carrying the landed-UID map + WrongChain cause) ONCE an earlier layer has landed — a true partial write — while a first/only-layer drift still escapes as raw WrongChain. attestationsForUIDs now re-throws a systemic WrongChain rejection instead of swallowing it to undefined (which looked like genuine absence); per-UID degradation still maps to undefined. Updates the per-layer guard test to the partial-error contract; adds a hydration regression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 95c8c956db
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ity cache per connector submitLayeredTier1 now builds the layer refs before the cancellation check and, once an earlier layer has landed, folds an abort into WriteNotSentError (landed map + AbortError cause) instead of letting a raw AbortError escape and drop the partial-write context; a first-layer abort still escapes raw. detectAccount's profile cache is now scoped by the connector (wallet client) via a WeakMap, since getCapabilities (gasless/batch) is connector-dependent — a connector switch no longer reuses a stale capability profile. Adds 4 regressions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
JamesCarnley
left a comment
There was a problem hiding this comment.
Re-reviewed current head 87e4ca84f166b201dab0c573ba65925cb53e8058.
The newest fixes look good: the mid-write abort path now preserves partial-write recovery context, and the wallet-scoped capability cache avoids cross-connector reuse. I also had two focused subagent passes check those slices independently, and both came back clean.
Local verification passed on the current head:
git diff --check origin/main...HEADpnpm --filter @efs/sdk typecheckpnpm --filter @efs/sdk test(591 passed,3 skipped)pnpm --filter @efs/sdk buildpnpm lintpnpm --filter @efs/solidity test(53 passed)
GitHub status is also green right now, with clean merge state and no unresolved review threads. I still do not think this PR is merge-ready yet, because these blockers remain:
-
expand: ["attestations.schema"]/withSchemacan still swallow systemic chain drift.attestationFor()callsschemaRecordFor(), andschemaRecordFor()catches everygetSchema()failure and returnsundefined. That means aWrongChainraised while hydrating schema data fulfills as an attestation missingschemaRecord, so the new rejection scan inattestationsForUIDs()never sees it. Please let systemicWrongChainescape fromschemaRecordFor()while preserving ordinary per-schema degradation if that is intentional. Relevant area:packages/sdk/src/reads/attestations.ts:81-107. -
ADR-0015 still says
EfsFile,FileInfo, andReadResultcarry requiredtrustdescriptors and documentsrequireTrust/StaleTrust, but the current public SDK types do not expose that contract yet. The docs are ahead of the implementation. -
ADR-0014 still says chainless SDK construction lands in PR #1 and that rejecting chainless
ViemConfig.publicClientwas superseded, butcreateEfsClient()still rejects chainless public clients. Either the behavior or the ADR needs to change before merge. -
The raw contract escape-hatch docs/release note still claim raw instances re-resolve deployment info and reflect wallet chain changes, while the implementation is construction-chain-bound and reports
WrongChainon drift. That mismatch will mislead downstream callers. -
snapshotReadSource()still advertisessupportsGetCode/supportsEnsfrom snapshot contents even thoughgetCode()returnsundefinedand no ENS resolver is installed. Capability metadata should describe callable behavior, not stored snapshot presence. -
.changeset/foundational-read-source-scaffolding.mdstill describes the oldTrustDescriptorshape assource/existence/revocation, but the exported SDK type uses the currentfreshnessunion shape.
Once those are corrected, the current write-path fixes themselves look ready from my pass.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 87e4ca84f1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…spaces on read clients followRedirectChain no longer throws RedirectHopLimit when the chain terminates exactly at the cap — it does a final terminal check on the last hop's destination, failing closed only if a genuine further followable hop (or a cycle) exists. EfsReadClient now carries read-only views of graph/props/mirrors/redirects (Pick-derived TagsReadNs/PinsReadNs/PropsReadNs/MirrorsReadNs/ RedirectsReadNs + EfsGraphReadNs), so the lens-scoped read verbs are type-reachable on a no-wallet client without a cast; mutators stay gated to EfsClient. Adds 2 regressions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
JamesCarnley
left a comment
There was a problem hiding this comment.
Re-reviewed current head 5e6d64dfaad90e65e29c783d139f7884840ec333.
The newest commit looks good. The redirect hop-cap fix now accepts a chain that terminates exactly at the cap while still failing closed on a genuine extra followable hop/cycle, and the read-only client type now exposes only the wallet-free read namespaces. I checked those locally and had two focused subagent passes review the same delta; both came back clean.
Local verification on this head passed:
git diff --check origin/main...HEADpnpm --filter @efs/sdk typecheckpnpm --filter @efs/sdk test -- redirects.test.ts index.test.ts(45 passed)pnpm --filter @efs/sdk test(593 passed,3 skipped)pnpm --filter @efs/sdk buildpnpm lintpnpm --filter @efs/solidity test(53 passed)
GitHub CI is green, merge state is clean, and there are no unresolved review threads. I still do not think this PR is merge-ready, because the previous merge blockers remain on this head:
-
expand: ["attestations.schema"]/withSchemacan still swallow systemic chain drift.schemaRecordFor()catches everygetSchema()failure and returnsundefined, so aWrongChainduring schema hydration fulfills as an attestation missingschemaRecordinstead of escaping as a systemic failure. Relevant area:packages/sdk/src/reads/attestations.ts:81-107. -
ADR-0015 still says
EfsFile,FileInfo, andReadResultcarry requiredtrustdescriptors plusrequireTrust/StaleTrust, but the current exported SDK surface does not implement that contract. -
ADR-0014 still says chainless SDK construction lands in PR #1 and that the chainless
ViemConfig.publicClientrejection was superseded, whilecreateEfsClient()still rejects chainless public clients. -
The raw contract escape-hatch docs/release note still claim raw instances re-resolve deployment info and reflect wallet/provider chain changes, while the implementation is construction-chain-bound and reports
WrongChainon drift. -
snapshotReadSource()still advertisessupportsGetCode/supportsEnsfrom snapshot contents even thoughgetCode()returnsundefinedand no ENS resolver is installed. -
.changeset/foundational-read-source-scaffolding.mdstill describes the oldTrustDescriptorshape (source/existence/revocation) instead of the exportedfreshnessunion shape.
So: the latest addressed areas are verified, but these blockers still need one more pass before merge.
Initial scaffold of the EFS SDK monorepo, set up from best-practice research + multiple expert-subagent review passes.
What's here
@efs/sdk— TypeScript SDK (tsup dual ESM/CJS, viem-native, vitest)@efs/solidity— compile-in Solidity library (Foundry, ships.solsource)Verified
build(dual ESM/CJS + types),typecheck,test(2 passing),lint— all green.forge build/test/fmt, CI-verified.Notes for review
@efs(org needs claiming on npm before first publish; one-line revert to@efs-projectif taken).NotImplemented) — this is structure + decisions, not implementation.sdk-architecture.md); ADRs here implement slices of it.🤖 Generated with Claude Code