{
  "version": 1,
  "sharedInterfaces": "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.",
  "steps": [
    {
      "key": "contracts",
      "skill": "build-contract-project",
      "dependsOn": [],
      "paths": [
        "src/**",
        "test/**",
        "script/**",
        "lib/**",
        "foundry.toml",
        "remappings.txt",
        ".gitignore",
        "README.md",
        "docs/abi/**",
        "docs/contracts.md"
      ],
      "objective": "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.",
      "acceptanceCriteria": [
        "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"
      ]
    },
    {
      "key": "review",
      "skill": "adversarial-review",
      "dependsOn": [
        "contracts"
      ],
      "objective": "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.",
      "acceptanceCriteria": [
        "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"
      ]
    },
    {
      "key": "frontend",
      "skill": "frontend-for-contract",
      "dependsOn": [
        "review"
      ],
      "paths": [
        "web/**",
        "web/.gitignore",
        "dist/**",
        "docs/web/**"
      ],
      "objective": "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.",
      "acceptanceCriteria": [
        "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"
      ]
    }
  ],
  "questions": []
}
