Planning job

7cd72804submittedscores queued
the whole request

Build and launch a token-gated membership project on Sepolia, end to end: contracts, token and pool, a public GitHub repository, and a website hosted on IPFS.

Contracts (Foundry, with tests and an independent adversarial review):

  • The project token: a fixed-supply ERC-20 whose name and symbol you choose to fit a membership club.
  • A membership badge contract: an ERC-721 where any address holding at least a threshold balance of the token can mint exactly one badge for itself. The threshold is set at deployment. A badge is soulbound (non-transferable). Anyone can check whether an address is currently eligible and whether it holds a badge. If a holder later falls below the threshold the badge stays, but a view reports it as lapsed.
  • Emit events for mints so the site can list members.

Launch: deploy the token and the badge contract as an evm_project on Sepolia and seed the token pool, so the token can be bought to qualify.

Website (static, hosted on IPFS, reading the deployed addresses from the launch handoff): connect a wallet on Sepolia, show the token balance against the threshold, a link to buy the token on the pool, a mint button when eligible, the badge once minted, and a public member list built from mint events with lapsed members marked.

Context and requirements

Sepolia only. Keep contracts small and well tested. The site must work from the IPFS gateway with no backend.

planner instructions · round 1

Read .imd/reads/planning.json and propose the requested workflow in artifacts/plan.json. Do not implement or dispatch the proposed work.

Workflow checks passed and execution was submitted. This page tracks the planning assignment.

Proposed workflow

3 step(s) · round 1

This proposal must pass workflow and requirement checks before it can become an execution job.

Handoff from contracts to review, launch and frontend (Sepolia, chainId 11155111).

Token: fixed-supply ERC-20 (worker picks a membership-club name/symbol), whole supply minted at construction, no mint function afterwards.

Badge: soulbound ERC-721 with constructor(address token, uint256 threshold). The threshold is immutable.

Functions: mint() mints exactly one badge to msg.sender when token.balanceOf(msg.sender) >= threshold. isEligible(address) view. hasBadge(address) view. isLapsed(address) view, true when the address holds a badge but its balance is now below threshold. badgeOf(address) view returns the tokenId, or 0 if none. token() and threshold() views. Every transfer or approval path reverts except the mint.

Event: BadgeMinted(address indexed member, uint256 indexed tokenId), alongside the standard ERC-721 Transfer from address(0). ABI JSON goes in docs/abi/MembershipToken.json and docs/abi/MembershipBadge.json. The frontend reads the deployed addresses, pool reference and ABI hashes from the deployment handoff at .imd/reads/deployment.json, never from hardcoded values.

1. contractsbuild contract project

Can start without another proposed step.

Start from the empty tree and create the complete Foundry project. Commit its dependencies (forge-std, OpenZeppelin) as ordinary vendored files with no git submodules. Implement the fixed-supply ERC-20 project token and the soulbound ERC-721 membership badge exactly as described in sharedInterfaces, keeping both contracts small.

Write thorough Foundry tests. Export ABI JSON to docs/abi/ and write docs/contracts.md, which covers constructor parameters, the eligibility and lapsed semantics, and the event the site uses to list members. Deployment, pool seeding and publication belong to the control plane's evm_project launch.

Do not deploy.

Acceptance criteria

  • forge build and forge test pass offline using only committed files, and there are no submodules

  • The token has a fixed supply that is minted once at construction and cannot be minted again

  • The badge allows one mint per address and only when balance >= the threshold set at deployment. A second mint and a mint below the threshold both revert

  • Every transfer and approval path on the badge reverts. Tests prove the badge stays after the holder's balance falls below the threshold and isLapsed then reports true

  • isEligible, hasBadge and isLapsed are public views, and each mint emits BadgeMinted(member, tokenId)

  • docs/abi/MembershipToken.json, docs/abi/MembershipBadge.json and docs/contracts.md match the compiled contracts

File scope: src/**, test/**, script/**, lib/**, foundry.toml, remappings.txt, .gitignore, README.md, docs/abi/**, docs/contracts.md

2. reviewadversarial review

After: contracts

Review the token and badge contracts and their tests as an attacker would, independently of the writer. Look for mint bypasses, double mints, soulbound bypasses (transferFrom, safeTransferFrom, approve, setApprovalForAll), wrong eligibility or lapsed logic, and supply inflation. Check that the ABI files and integration docs match the source and the generated launch manifest.

Write nothing.

Acceptance criteria

  • Findings cite the specific functions and the tests that cover them, or note the tests that are missing

  • The review confirms that the ABI JSON, docs/contracts.md and the generated launch manifest are consistent with the source

  • It gives a clear blocking/non-blocking verdict for the launch

3. frontendfrontend for contract

After: review

Build a static Vite and React site under web/ with its export at the repository-root dist/. The site must work from an IPFS gateway with no backend, so use relative asset paths and hash routing. Read the contract addresses, the pool reference and the ABIs from the deployment handoff and docs/abi/.

Features: connect a wallet and require Sepolia; show the connected token balance against the threshold; link to buy the token on the seeded pool; show a mint button only when the connected address is eligible and has no badge; show the badge once minted; show a public member list built from BadgeMinted events read over RPC, with each lapsed member marked through isLapsed.

Acceptance criteria

  • The site builds to dist/ and loads from a subpath with no backend or server routes

  • Addresses and ABIs come from the deployment handoff and docs/abi, with nothing hardcoded that deviates from them

  • It prompts a switch when the wallet is not on chainId 11155111

  • Tests cover the eligibility and mint-button states, the badge display, and building the member list from mint events with lapsed marking

File scope: web/**, web/.gitignore, dist/**, docs/web/**

Work

  1. postedunder a minuteto the first attempt
  2. plan proposedWorkflow planner
    Workflow planneraccepted
    writes toartifacts/plan.json
    #1943

    I wrote the plan to artifacts/plan.json and it validates against the schema in planning.json. Nothing was implemented or dispatched, and the file is not committed.

    The plan has three worker steps, one after another:

    1. contracts (build-contract-project): starts from the empty repository and sets up the whole Foundry project. It writes both contracts and their tests, commits its dependencies as plain files (no git submodules), and exports ABI files to docs/abi/ plus integration notes in docs/contracts.md. It does not deploy anything.

    2. review (adversarial-review): depends directly on contracts and writes nothing. It hunts for:

      • ways around the one-badge mint rule
      • ways to transfer the badge despite it being soulbound
      • mistakes in the eligibility or lapsed checks
      • extra token supply

      It also checks that the ABI files, the docs and the launch manifest the control plane generates all match the source.

    3. frontend (frontend-for-contract): the final step, after the review and the deployment handoff. It writes only web/**, web/.gitignore, dist/** and docs/web/**. The site is static and works from an IPFS gateway with no backend. It reads addresses and ABIs from the handoff and covers:

      • Sepolia wallet connection
      • token balance against the threshold
      • a buy link to the pool
      • a mint button when eligible
      • the badge once minted
      • a member list built from mint events, with lapsed members marked

    Why only three steps:

    • Contracts kept together. The token and badge share one project setup and the badge reads the token's balance, so they stay with a single writer rather than being split into parallel branches.
    • Nothing overlaps. No two steps run at the same time, and the write paths don't overlap anyway.
    • Shared interface is written down. sharedInterfaces spells out what the later steps rely on: the badge constructor, its view functions and the mint event, the ABI file paths, and the handoff location.
    • Launch left to the control plane. Its generated manifest takes the integrator slot, so there is no integrate-project step. It also handles deployment, seeding the pool, the GitHub repository and IPFS hosting, and none of that is in the worker steps.

    The request's constraints are carried through: Sepolia only (chain 11155111), fixed token supply, threshold set at deployment, badge that can't be transferred and stays when a holder falls below the threshold, and the existing permissions. I added no questions, because the token name and symbol are the worker's to choose and the deployment values are handled at launch.

    One gap: the plan has no fix step after the review. If the review turns up blocking findings, they would need a separate correction pass before launch.

    ran onclaude · claude-opus-5-5 · 3 turns · 50s · 6 in · 4.8K out · 68.2K cached
    submission918fcbca517703ca66753d2a65354ee6dc4a69d44a1c7429c1dfb6ade429798d
    device00d95fd75f8b224cbade9810011b337d6448b20f22483c8826ccb3772627a4eb
    started from0243d7da4a4337ae8b16bcdf15bb4ead736fd68f
    bundle31488cf006db2a8c4b5d1b068b33fd1aaaab21607e63eefb933b63a3d10bab74 · 358 bytes
    made · 1 file
    artifacts/plan.json · 5.3 KB
  3. onchain
    1 receipt, 1 score queuedon Ethereum mainnet
    receipt
    work accepted · record queued
    scores
    1 score for built on structural · all 1 passed#1943

Outputs

1 file
planaccepted
fileartifacts/plan.json
typeapplication/json
size5.3 KB

File integrity and allowed paths were checked. Content accuracy and quality were not evaluated.