From 8c633341b7d15d18c8ffd349ce5197653c431cec Mon Sep 17 00:00:00 2001 From: Faadil1 Date: Tue, 30 Jun 2026 21:09:53 +0000 Subject: [PATCH] Add solana launch readiness skill --- solana-launch-readiness-skill/CLAUDE.md | 1 + solana-launch-readiness-skill/LICENSE | 21 + solana-launch-readiness-skill/README.md | 152 +++++++ .../commands/fix-blockers.md | 80 ++++ .../commands/launch-check.md | 78 ++++ .../examples/launch-check-cases.md | 79 ++++ .../install-custom.sh | 22 + solana-launch-readiness-skill/install.sh | 39 ++ .../rules/no-shared-rpc.md | 52 +++ solana-launch-readiness-skill/skill/SKILL.md | 168 ++++++++ solana-launch-readiness-skill/skill/demo.md | 99 +++++ solana-launch-readiness-skill/skill/docs.md | 98 +++++ .../skill/remediation.md | 381 ++++++++++++++++++ solana-launch-readiness-skill/skill/rpc.md | 128 ++++++ .../skill/security.md | 168 ++++++++ .../skill/testing.md | 126 ++++++ .../tests/cloud-sanity.sh | 99 +++++ .../tests/manual-harness.md | 70 ++++ 18 files changed, 1861 insertions(+) create mode 100644 solana-launch-readiness-skill/CLAUDE.md create mode 100644 solana-launch-readiness-skill/LICENSE create mode 100644 solana-launch-readiness-skill/README.md create mode 100644 solana-launch-readiness-skill/commands/fix-blockers.md create mode 100644 solana-launch-readiness-skill/commands/launch-check.md create mode 100644 solana-launch-readiness-skill/examples/launch-check-cases.md create mode 100755 solana-launch-readiness-skill/install-custom.sh create mode 100755 solana-launch-readiness-skill/install.sh create mode 100644 solana-launch-readiness-skill/rules/no-shared-rpc.md create mode 100644 solana-launch-readiness-skill/skill/SKILL.md create mode 100644 solana-launch-readiness-skill/skill/demo.md create mode 100644 solana-launch-readiness-skill/skill/docs.md create mode 100644 solana-launch-readiness-skill/skill/remediation.md create mode 100644 solana-launch-readiness-skill/skill/rpc.md create mode 100644 solana-launch-readiness-skill/skill/security.md create mode 100644 solana-launch-readiness-skill/skill/testing.md create mode 100755 solana-launch-readiness-skill/tests/cloud-sanity.sh create mode 100644 solana-launch-readiness-skill/tests/manual-harness.md diff --git a/solana-launch-readiness-skill/CLAUDE.md b/solana-launch-readiness-skill/CLAUDE.md new file mode 100644 index 0000000..f304426 --- /dev/null +++ b/solana-launch-readiness-skill/CLAUDE.md @@ -0,0 +1 @@ +Read skill/SKILL.md to assess Solana launch readiness. diff --git a/solana-launch-readiness-skill/LICENSE b/solana-launch-readiness-skill/LICENSE new file mode 100644 index 0000000..5df2ef3 --- /dev/null +++ b/solana-launch-readiness-skill/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Faadil Boussari + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/solana-launch-readiness-skill/README.md b/solana-launch-readiness-skill/README.md new file mode 100644 index 0000000..f9ba92b --- /dev/null +++ b/solana-launch-readiness-skill/README.md @@ -0,0 +1,152 @@ +# solana-launch-readiness-skill + +solana-launch-readiness-skill doesn't help you ship faster — it tells your agent when NOT to ship yet. + +Detects launch context (demo, devnet, mainnet-no-funds, mainnet-with-funds) and applies Solana-specific severity accordingly — a hot wallet authority is a warning on devnet and a hard BLOCKER on mainnet with funds. + +Every BLOCKER comes with the exact fix command. No vague advice. + +## What This Is + +This is a default-deny Solana launch verdict engine, not a checklist. It assumes `NOT_LAUNCH_READY` until evidence proves otherwise, and it outputs exactly one verdict: + +- `LAUNCH_READY` +- `LAUNCH_READY_WITH_RISKS` +- `NOT_LAUNCH_READY` + +The verdict comes from 19 criteria across 5 domains: + +- **Security & Authority** — upgrade authority, build verifiability, IDL/interface evidence, vulnerability status, external review +- **Testing Evidence** — core instruction coverage, error-path tests, live integration evidence, test-keypair isolation +- **RPC & Runtime Reliability** — dedicated RPC, retry logic, transaction simulation, spending caps +- **Documentation & Discoverability** — correct program ID, error mapping, security disclosure process +- **Demo & Submission Confidence** — reachable demo, demo wallet funding, submission completeness + +Severity for every criterion depends on detected launch context — the same gap (e.g., a hot-wallet upgrade authority) can be a HIGH on devnet and a hard BLOCKER on mainnet with real funds. + +## Install + +```bash +bash <(curl -fsSL https://raw.githubusercontent.com/Faadil1/solana-launch-readiness-skill/main/install.sh) +``` + +Or clone and install locally: + +```bash +git clone https://github.com/Faadil1/solana-launch-readiness-skill.git +cd solana-launch-readiness-skill +./install.sh +``` + +## Usage + +### Example 1 — `MAINNET_WITH_FUNDS` → `NOT_LAUNCH_READY` + +Project has a hot-wallet upgrade authority and uses the shared public RPC endpoint. + +``` +> /launch-check +Context detected: MAINNET_WITH_FUNDS + +NOT_LAUNCH_READY + +BLOCKER A1 — Upgrade authority is a hot wallet. + Fix: solana program set-upgrade-authority + +BLOCKER C1 — Production RPC is api.mainnet-beta.solana.com. + Fix: set RPC_URL to a dedicated Helius/Triton endpoint before launch. +``` + +### Example 2 — `DEVNET` → `LAUNCH_READY_WITH_RISKS` + +Project is missing core instruction tests and has no recent integration evidence. + +``` +> /launch-check +Context detected: DEVNET + +LAUNCH_READY_WITH_RISKS + +HIGH B1 — Core instruction tests missing for 2 of 5 instructions. + Fix: add LiteSVM or cargo test coverage for the untested instructions. + +HIGH B3 — No integration test run found within the last 48 hours. + Fix: run the integration suite against devnet or Surfpool and retain the dated log. +``` + +### Example 3 — `DEMO_HACKATHON` → `LAUNCH_READY` + +Project has a working demo link, README and LICENSE present, and makes no real-funds claim. + +``` +> /launch-check +Context detected: DEMO_HACKATHON + +LAUNCH_READY + +No BLOCKERs. No unresolved HIGH risks. +``` + +## Signature Example + +```text +> /launch-check + +Detected launch context: MAINNET_WITH_FUNDS + +Verdict: NOT_LAUNCH_READY + +BLOCKER A1 — Upgrade authority is a hot wallet. +Fix: solana program set-upgrade-authority --new-upgrade-authority + +BLOCKER C1 — Production RPC is api.mainnet-beta.solana.com. +Fix: set RPC_URL to a dedicated Helius/Triton/QuickNode endpoint before launch. + +Next Fix: +Move upgrade authority away from a hot wallet before any funded mainnet launch. +``` + +## Commands + +- **`/launch-check`** — runs the full verdict flow: classifies launch context, loads only the relevant domain modules, evaluates applicable criteria, and returns one of the three verdicts above with named BLOCKERs and HIGH risks. +- **`/fix-blockers`** — takes the most recent `/launch-check` result and produces an ordered fix plan, pulling exact commands from the remediation guide. Requires a prior `/launch-check` run. + +## Verified + +Remote installer tested from GitHub raw: + +```bash +bash <(curl -fsSL https://raw.githubusercontent.com/Faadil1/solana-launch-readiness-skill/main/install.sh) +``` + +Verified result: + +```text +Installed solana-launch-readiness to ~/.claude/skills/solana-launch-readiness +Available commands: /launch-check, /fix-blockers +PASS_MAIN +``` + +Cloud sanity harness: + +```bash +./tests/cloud-sanity.sh +``` + +Verified result: + +```text +ALL CLOUD SANITY CHECKS PASSED +``` + +## Rule + +`no-shared-rpc` passively warns whenever `api.mainnet-beta.solana.com` (or `clusterApiUrl('mainnet-beta')`) appears in a project file, independent of running `/launch-check`. It does not assume launch context on its own — it points to `/launch-check` for the full context-aware verdict. + +## Safety Boundaries + +This skill never asks for private keys or seed phrases, never sends transactions, and never modifies wallet authorities or deployments directly. Every fix is presented as an exact command for the user to run themselves. + +## License + +MIT diff --git a/solana-launch-readiness-skill/commands/fix-blockers.md b/solana-launch-readiness-skill/commands/fix-blockers.md new file mode 100644 index 0000000..4d31ea4 --- /dev/null +++ b/solana-launch-readiness-skill/commands/fix-blockers.md @@ -0,0 +1,80 @@ +# /fix-blockers + +Convert a prior `/launch-check` result into an ordered remediation plan using `skill/remediation.md` as the source of truth. This command does not re-derive fixes — it pulls them from that module. It does not re-run the full launch check unless the user explicitly asks for that. + +## Procedure + +**STEP 1 — Get the source verdict.** +Use the most recent `/launch-check` result available in this conversation. If none exists, stop and instruct the user to run `/launch-check` first — do not guess at a verdict or context to proceed without one. + +**STEP 2 — Extract from that verdict:** +- detected launch context +- the verdict itself +- every named BLOCKER +- every named HIGH risk +- any criteria listed under missing evidence + +**STEP 3 — Load `skill/remediation.md`.** For each BLOCKER and HIGH extracted in STEP 2, find that criterion's entry in `remediation.md` and use its fix, verification step, and "what not to do" guidance exactly as written there. Do not invent a fix not present in `remediation.md`. + +**STEP 4 — Order the plan:** +1. BLOCKERs first, in the order they appeared in the source verdict +2. HIGH risks second +3. Missing evidence that is currently preventing a criterion from being classified at all + +**STEP 5 — Mark judgment calls.** Any fix that `remediation.md` labels as requiring a decision only the user can make (e.g., selecting multisig signers, engaging an external auditor, deciding where to move excess demo funds) must be marked `requires human approval: yes` with a one-line explanation of what the user needs to decide. All other fixes are `requires human approval: no`. + +**STEP 6 — Handle a clean verdict.** If the source verdict is `LAUNCH_READY`, do not produce a BLOCKER/HIGH fix plan — state that none is needed, and optionally list any MEDIUM advisories from the source verdict if present. + +## Safety Boundaries + +Never execute any command in the plan. Never request a private key, seed phrase, or keypair file content, under any phrasing. Never move funds or change an upgrade authority — only present the exact command for the user to run themselves. Never instruct the user to paste a secret into chat. + +## Required Output Format + +``` +# Solana Launch Fix Plan + +Source verdict: +Detected launch context: + +## Priority 1 — BLOCKERs +1. + Why it blocks: <reason> + Fix: + ```bash + <exact command if applicable> + ``` + Verify: + ```bash + <verification command/check> + ``` + Human approval: <yes/no, explain if yes> + +## Priority 2 — HIGH Risks +1. <ID> — <title> + Why it matters: <reason> + Fix: <command/action> + Verify: <verification check> + Human approval: <yes/no, explain if yes> + +## Missing Evidence +- <ID> — <what is needed to classify this criterion> + +## Do Not Do +- <safety boundary relevant to this plan's fixes> + +## Re-check Command +Run `/launch-check` again after completing the fixes above. +``` + +If the source verdict has zero BLOCKERs and zero HIGH risks, output only: +``` +No blocker fix plan needed. Re-run /launch-check only after material changes. +``` +followed by any MEDIUM advisories from the source verdict, if present. + +## Output Discipline + +- Do not invent a fix, command, or verification step that is not present in `skill/remediation.md` for that criterion ID. +- Do not reproduce the full contents of `remediation.md` — pull only the entries relevant to the BLOCKERs and HIGH risks named in the source verdict. +- Keep every fix command-oriented: a concrete command, file edit, or named action — not a general instruction to "improve" or "review" something. diff --git a/solana-launch-readiness-skill/commands/launch-check.md b/solana-launch-readiness-skill/commands/launch-check.md new file mode 100644 index 0000000..169adab --- /dev/null +++ b/solana-launch-readiness-skill/commands/launch-check.md @@ -0,0 +1,78 @@ +# /launch-check + +Run the full Solana launch readiness verdict flow using `skill/SKILL.md` as the source of truth for severity, routing, and verdict logic. This command does not duplicate that logic — it invokes it. + +## Default Assumption + +Start every run from `NOT_LAUNCH_READY`. Do not move off this default until evidence for the relevant criteria has actually been checked. + +## Procedure + +**STEP 0 — Classify launch context.** +Determine one of: `DEMO_HACKATHON`, `DEVNET`, `MAINNET_NO_FUNDS`, `MAINNET_WITH_FUNDS`, per `skill/SKILL.md` STEP 0. If the project files do not make this unambiguous, ask exactly one clarifying question and stop — do not proceed to STEP 1 on an assumed context. + +**STEP 1 — Classify project type.** +Determine one of: on-chain program, frontend-only dApp, documentation/submission review, full-stack Solana project. + +**STEP 2 — Load only relevant modules**, per the routing table in `skill/SKILL.md`: +- on-chain program → `security.md` + `testing.md` +- frontend-only dApp → `rpc.md` + `demo.md` +- documentation/submission review → `docs.md` + `demo.md` +- full-stack project → all five domain modules +- if any BLOCKER or HIGH is found in STEP 3 → also load `remediation.md` + +Do not load a module with no applicable criteria for the detected project type. + +**STEP 3 — Evaluate only the criteria in the loaded modules.** For each, gather evidence per that module's "what to check" guidance and classify as resolved, unresolved, or missing. Apply the context-aware severity for the STEP 0 context, as defined in that criterion's section of `SKILL.md` and the relevant domain module — do not re-derive severity from memory; read it from the table. + +**STEP 4 — Apply verdict logic** from `skill/SKILL.md`. Hard rule: never output `LAUNCH_READY` while any criterion that is BLOCKER-severity for the detected context remains unresolved. This rule cannot be overridden by narration, confidence, or stated intent to fix later. + +**STEP 5 — If any BLOCKER or HIGH is unresolved**, pull the exact fix for each from `skill/remediation.md` — do not invent a fix; use the one defined there for that criterion ID. + +## Safety Boundaries + +Do not send transactions, change upgrade authorities, query private wallet data, or request private keys, seed phrases, or keypair file content at any point in this command. All fixes are presented as commands or actions for the user to run themselves. + +## Required Output Format + +``` +# Solana Launch Readiness Verdict + +Detected launch context: <context> +Project type: <type> +Loaded modules: <modules> + +Verdict: <LAUNCH_READY | LAUNCH_READY_WITH_RISKS | NOT_LAUNCH_READY> + +## BLOCKERs +- <ID> — <title> + Evidence: <what was found/missing> + Fix: <exact command/action from remediation.md> + +## HIGH Risks +- <ID> — <title> + Evidence: <what was found/missing> + Fix: <recommended action> + +## MEDIUM Advisories +- <ID> — <summary> + +## Evidence Table +| ID | Status | Severity | Evidence | +|---|---|---|---| + +## Missing Evidence +- <ID> — <what is needed> + +## Next Fix +<single highest-priority next action> +``` + +If no BLOCKER, HIGH, or MEDIUM finding exists for the loaded modules, still output every section above — use `None` in place of list items rather than omitting the section. A clean result can legitimately produce `Verdict: LAUNCH_READY`. + +## Output Discipline + +- Verdict comes first, before any explanatory detail. +- Do not invent evidence — every line in the Evidence Table must trace to something actually checked, read, or stated by the user. If a criterion's evidence could not be gathered, list it under Missing Evidence instead of guessing. +- Do not reproduce the full 19-criteria severity table from `SKILL.md` in the output — reference the criterion ID and its severity for this run only. +- Keep BLOCKER and HIGH entries concrete: name the ID, the title, what was actually found or missing, and the fix — not a general statement that something needs attention. diff --git a/solana-launch-readiness-skill/examples/launch-check-cases.md b/solana-launch-readiness-skill/examples/launch-check-cases.md new file mode 100644 index 0000000..10b6f25 --- /dev/null +++ b/solana-launch-readiness-skill/examples/launch-check-cases.md @@ -0,0 +1,79 @@ +# /launch-check — Expected Cases + +Three reference cases for verifying `/launch-check` behavior. Each case states the launch context, the evidence setup that should be assumed or simulated, and the verdict and findings that output is expected to match. + +These are proof references for manual verification — not a fake Solana project. No actual program, repo, or deployment is created; the evidence below is described to Claude Code as the state of a hypothetical project when running `/launch-check`. + +--- + +## Case 1 — MAINNET_WITH_FUNDS → NOT_LAUNCH_READY + +**Context:** `MAINNET_WITH_FUNDS` + +**Evidence setup:** +- Upgrade authority is a single-keypair hot wallet (no Squads multisig, not revoked). +- RPC configuration contains `api.mainnet-beta.solana.com`. + +**Expected findings:** `BLOCKER A1`, `BLOCKER C1` + +**Expected output excerpt:** +``` +NOT_LAUNCH_READY + +BLOCKER A1 — Upgrade authority is a hot wallet. + Fix: solana program set-upgrade-authority <PROGRAM_ID> <SQUADS_MULTISIG_ADDRESS> + +BLOCKER C1 — Production RPC is api.mainnet-beta.solana.com. + Fix: set RPC_URL to a dedicated Helius/Triton endpoint before launch. +``` + +**Pass condition:** Verdict is exactly `NOT_LAUNCH_READY`. Both A1 and C1 appear under BLOCKERs, each with an exact fix command — not a general recommendation. + +--- + +## Case 2 — DEVNET → LAUNCH_READY_WITH_RISKS + +**Context:** `DEVNET` + +**Evidence setup:** +- Core instruction tests are missing for some instructions (not full B1 coverage). +- No recent Surfpool or devnet integration run is evidenced within the last 48 hours (B3 unresolved). +- No active BLOCKER-severity criteria are unresolved at this context. + +**Expected findings:** `HIGH B1`, `HIGH B3` + +**Expected output excerpt:** +``` +LAUNCH_READY_WITH_RISKS + +HIGH B1 — Core instruction tests missing for [N] instructions. + Fix: add LiteSVM or cargo test coverage for the untested instructions. + +HIGH B3 — No integration test run found within the last 48 hours. + Fix: run the integration suite against devnet or Surfpool and retain the dated log. +``` + +**Pass condition:** Verdict is exactly `LAUNCH_READY_WITH_RISKS`. Both B1 and B3 appear under HIGH Risks. No BLOCKER section is populated. + +--- + +## Case 3 — DEMO_HACKATHON → LAUNCH_READY + +**Context:** `DEMO_HACKATHON` + +**Evidence setup:** +- Demo link is reachable (no localhost-only access path). +- `README.md` and `LICENSE` are both present. +- Project makes no real-funds or mainnet-deployment claim. +- No unresolved BLOCKER, HIGH, or three-or-more MEDIUM findings exist for the modules loaded under this context. + +**Expected findings:** None + +**Expected output excerpt:** +``` +LAUNCH_READY + +No BLOCKERs. No unresolved HIGH risks. +``` + +**Pass condition:** Verdict is exactly `LAUNCH_READY`. BLOCKERs and HIGH Risks sections are empty (`None`), consistent with `skill/SKILL.md`'s required output format for a clean result. diff --git a/solana-launch-readiness-skill/install-custom.sh b/solana-launch-readiness-skill/install-custom.sh new file mode 100755 index 0000000..e1e72ee --- /dev/null +++ b/solana-launch-readiness-skill/install-custom.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +SKILL_NAME="solana-launch-readiness" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo "Install to (1) personal ~/.claude/skills/ or (2) project ./.claude/skills/? [1/2]" +read -r choice + +case "${choice}" in + 2) + INSTALL_DIR="./.claude/skills/${SKILL_NAME}" + ;; + *) + INSTALL_DIR="${HOME}/.claude/skills/${SKILL_NAME}" + ;; +esac + +mkdir -p "${INSTALL_DIR}" +cp -r "${SCRIPT_DIR}/skill/." "${INSTALL_DIR}/" + +echo "✓ solana-launch-readiness skill installed to ${INSTALL_DIR}" diff --git a/solana-launch-readiness-skill/install.sh b/solana-launch-readiness-skill/install.sh new file mode 100755 index 0000000..5f5aa98 --- /dev/null +++ b/solana-launch-readiness-skill/install.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -euo pipefail + +SKILL_NAME="solana-launch-readiness" +INSTALL_DIR="${HOME}/.claude/skills/${SKILL_NAME}" +REPO_URL="https://github.com/Faadil1/solana-launch-readiness-skill.git" + +TMP_DIR="" + +cleanup() { + if [ -n "${TMP_DIR}" ] && [ -d "${TMP_DIR}" ]; then + rm -rf "${TMP_DIR}" + fi +} +trap cleanup EXIT + +SCRIPT_PATH="${BASH_SOURCE[0]}" +SCRIPT_DIR="$(cd "$(dirname "${SCRIPT_PATH}")" 2>/dev/null && pwd || true)" + +if [ -n "${SCRIPT_DIR}" ] && [ -d "${SCRIPT_DIR}/skill" ]; then + SOURCE_DIR="${SCRIPT_DIR}" +else + TMP_DIR="$(mktemp -d)" + git clone --depth 1 "${REPO_URL}" "${TMP_DIR}/repo" >/dev/null + SOURCE_DIR="${TMP_DIR}/repo" +fi + +mkdir -p "${INSTALL_DIR}" + +cp -R "${SOURCE_DIR}/skill/." "${INSTALL_DIR}/" + +mkdir -p "${INSTALL_DIR}/commands" +cp -R "${SOURCE_DIR}/commands/." "${INSTALL_DIR}/commands/" + +mkdir -p "${INSTALL_DIR}/rules" +cp -R "${SOURCE_DIR}/rules/." "${INSTALL_DIR}/rules/" + +echo "Installed ${SKILL_NAME} to ${INSTALL_DIR}" +echo "Available commands: /launch-check, /fix-blockers" diff --git a/solana-launch-readiness-skill/rules/no-shared-rpc.md b/solana-launch-readiness-skill/rules/no-shared-rpc.md new file mode 100644 index 0000000..556d304 --- /dev/null +++ b/solana-launch-readiness-skill/rules/no-shared-rpc.md @@ -0,0 +1,52 @@ +# Rule: no-shared-rpc + +## Trigger + +Any of the following found in a project file: +- `api.mainnet-beta.solana.com` +- `clusterApiUrl('mainnet-beta')` +- `clusterApiUrl("mainnet-beta")` + +## Behavior + +This rule fires passively, independent of `/launch-check` — it does not run the full verdict flow and does not classify launch context on its own. It supports criterion C1 (`skill/rpc.md`). On match, insert the warning below before continuing whatever task is in progress. + +Severity depends entirely on launch context, which this rule does not assume from the trigger alone: + +| Context | Severity | +|---|---| +| DEMO_HACKATHON | LOW | +| DEVNET | MEDIUM if this pattern indicates accidental devnet config confusion; otherwise not applicable | +| MAINNET_NO_FUNDS | HIGH | +| MAINNET_WITH_FUNDS | BLOCKER | + +If context is not already known from earlier in the conversation, state that severity is unresolved until classified — do not guess. + +## Inline Warning + +```text +⚠️ Shared public Solana mainnet RPC detected + +Found: <matched pattern or file reference> +Criterion: C1 — Dedicated RPC endpoint in use +Context severity: unknown until /launch-check classifies launch context + +If this project is MAINNET_WITH_FUNDS, C1 is a BLOCKER. +Fix direction: configure a dedicated provider endpoint through RPC_URL / NEXT_PUBLIC_RPC_URL. +Do not hardcode private RPC API keys in public client-side code. + +Run /launch-check for the full context-aware verdict. +``` + +If the launch context is already established as `MAINNET_WITH_FUNDS` earlier in the conversation, state directly: "This is a BLOCKER under C1 until replaced with a dedicated RPC endpoint" — skip the "unknown until classified" line in that case. + +## Safety Boundaries + +- Never send a transaction or otherwise interact with the matched RPC endpoint. +- Never test, ping, or benchmark the endpoint to assess its reliability. +- Never edit the matched file or config automatically — this rule only warns. +- Never expose, request, or suggest pasting a private RPC API key. + +## References + +For full detail beyond this warning, see `skill/rpc.md` C1, `skill/remediation.md` C1, and the `/launch-check` command — this rule does not reproduce their content. diff --git a/solana-launch-readiness-skill/skill/SKILL.md b/solana-launch-readiness-skill/skill/SKILL.md new file mode 100644 index 0000000..426a813 --- /dev/null +++ b/solana-launch-readiness-skill/skill/SKILL.md @@ -0,0 +1,168 @@ +# Solana Launch Readiness Skill + +Default assumption: NOT_LAUNCH_READY until evidence proves otherwise. This skill does not advise — it gates. + +STEP 0 — classify launch context first. Severity for every criterion below depends on this classification; do not skip it. + +Output one verdict: LAUNCH_READY, LAUNCH_READY_WITH_RISKS, or NOT_LAUNCH_READY. Never soften a BLOCKER into a suggestion. + +## Purpose + +This is a launch verdict engine, not a checklist. It turns Solana-specific launch evidence — program authority, RPC configuration, test coverage, documentation state, demo readiness — into a single context-aware verdict. A checklist asks the founder to self-grade. This skill gates: it assumes the project is not ready until each criterion is evidenced, and it never lets a BLOCKER pass as a recommendation. + +## When to Use + +- The user asks whether a Solana project is ready to demo, submit, deploy, or launch. +- The user wants Claude Code to assess a repo before calling the work done. +- The user has a Solana program, dApp, or hackathon/bounty submission and needs hard blockers surfaced before going further. + +## When NOT to Use + +- The user wants help writing Solana code from scratch. (Use Solana dev/build skills instead.) +- The user wants a deep code audit or formal third-party security audit. (This skill checks audit *status*, not code correctness.) +- The user wants autonomous wallet or deployment changes made on their behalf. (This skill never executes — see Safety Boundaries.) + +--- + +## STEP 0 — Classify Launch Context + +Every criterion's severity depends on this classification. Ask or infer from project files before applying anything below. + +| Context | Definition | +|---|---| +| `DEMO_HACKATHON` | Building to show judges or demo to an audience. No real user funds at risk. | +| `DEVNET` | Live on devnet only. Real infrastructure, no real funds. | +| `MAINNET_NO_FUNDS` | Deployed on mainnet but the program does not custody or transfer user funds (read-only oracle, registry, non-financial dApp). | +| `MAINNET_WITH_FUNDS` | Deployed on mainnet and moves, holds, or controls real user funds or tokens. | + +If context is ambiguous, ask exactly one clarifying question before proceeding. Do not guess on this step — every downstream severity depends on it. Record the selected context; all evidence below is read relative to it. + +--- + +## STEP 1 — Classify Project Type and Route Progressively + +Load only the modules required for the detected project type. Do not load a module that has no applicable criteria for this project. + +| Project type | Load | +|---|---| +| On-chain program | `security.md` + `testing.md` | +| Frontend-only dApp | `rpc.md` + `demo.md` | +| Documentation / submission review | `docs.md` + `demo.md` | +| Full-stack Solana project | All domain modules | +| Unresolved BLOCKER or HIGH found | `remediation.md` | + +--- + +## Severity Table — 19 Criteria, Context-Aware + +Columns: `DEMO/HK` = DEMO_HACKATHON · `DEVNET` · `NO_FUNDS` = MAINNET_NO_FUNDS · `WITH_FUNDS` = MAINNET_WITH_FUNDS + +### Domain A — Program Security & Authority (`security.md`) + +| ID | Criterion | DEMO/HK | DEVNET | NO_FUNDS | WITH_FUNDS | +|---|---|---|---|---|---| +| A1 | Upgrade authority is not a hot wallet (Squads multisig, hardware wallet, or revoked) | HIGH | HIGH | HIGH | **BLOCKER** | +| A2 | Program build is verifiable (anchor build --verifiable, OtterSec, or Ellipsis verified) | LOW | MEDIUM | HIGH | HIGH | +| A3 | IDL published on-chain (Anchor only) — non-Anchor: equivalent client SDK docs or interface spec present | LOW | MEDIUM | HIGH | HIGH | +| A4 | No known critical/high vulnerabilities — at minimum `cargo audit` clean | MEDIUM | HIGH | HIGH | **BLOCKER** | +| A5 | External security review completed (Trail of Bits, OtterSec, Halborn, Neodyme, or Superteam Bug Bash) | LOW | LOW | MEDIUM | HIGH | + +### Domain B — Testing Evidence (`testing.md`) + +| ID | Criterion | DEMO/HK | DEVNET | NO_FUNDS | WITH_FUNDS | +|---|---|---|---|---|---| +| B1 | LiteSVM unit tests present covering core instructions — not happy-path only | MEDIUM | HIGH | HIGH | HIGH | +| B2 | Error paths tested — every `error_code` variant has a rejection test | LOW | MEDIUM | MEDIUM | HIGH | +| B3 | Surfpool or devnet integration test executed without failures in last 48h | LOW | HIGH | HIGH | HIGH | +| B4 | No `#[cfg(test)]`-only keypairs in production code paths | MEDIUM | HIGH | HIGH | **BLOCKER** | + +### Domain C — RPC & Runtime Reliability (`rpc.md`) + +| ID | Criterion | DEMO/HK | DEVNET | NO_FUNDS | WITH_FUNDS | +|---|---|---|---|---|---| +| C1 | Dedicated RPC endpoint in use (Helius, Triton, QuickNode) — not `api.mainnet-beta.solana.com` | LOW | MEDIUM | HIGH | **BLOCKER** | +| C2 | Retry logic for `TransactionExpiredBlockheightExceededError` and `SendTransactionError` | LOW | MEDIUM | HIGH | HIGH | +| C3 | Transaction simulation called before submission on all state-changing operations | LOW | HIGH | HIGH | HIGH | +| C4 | Spending circuit breaker or hard cap implemented (applies only if program transfers SOL or tokens) | LOW | MEDIUM | MEDIUM | **BLOCKER** | + +### Domain D — Documentation & Discoverability (`docs.md`) + +| ID | Criterion | DEMO/HK | DEVNET | NO_FUNDS | WITH_FUNDS | +|---|---|---|---|---|---| +| D1 | README shows correct network program ID — no devnet placeholder in production docs | MEDIUM | LOW | HIGH | **BLOCKER** | +| D2 | Anchor error codes mapped to user-facing messages in client — errors not silently swallowed | LOW | MEDIUM | MEDIUM | HIGH | +| D3 | Security contact or disclosure process documented (SECURITY.md or README section) | LOW | LOW | MEDIUM | HIGH | + +### Domain E — Demo & Submission Confidence (`demo.md`) + +| ID | Criterion | DEMO/HK | DEVNET | NO_FUNDS | WITH_FUNDS | +|---|---|---|---|---|---| +| E1 | Live demo runs on target network without localhost dependencies | HIGH | HIGH | HIGH | HIGH | +| E2 | Demo wallet funded only to minimum needed — no excess funds in hot demo wallet | HIGH | MEDIUM | HIGH | **BLOCKER** | +| E3 | Submission package complete: README, demo link/video, repo public, LICENSE present | HIGH | HIGH | HIGH | HIGH | + +**Conditional note:** C4 applies only when the program transfers or custodies SOL or tokens. If the program is read-only, C4 = LOW in every context. + +--- + +## Verdict Logic + +``` +PRE-CONDITION: STEP 0 must be complete before any verdict is computed. + +BLOCKER evaluation: + A criterion is an active BLOCKER only when both are true: + (1) its severity cell for the detected context = BLOCKER + (2) it is unresolved (no evidence found) + +NOT_LAUNCH_READY + → One or more active BLOCKERs. + Must name each BLOCKER by ID and title, explain why it disqualifies + for this specific context, and provide the exact fix command or action. + Never downgrade a BLOCKER to a suggestion. + +LAUNCH_READY_WITH_RISKS + → Zero active BLOCKERs + → AND (one or more unresolved HIGH, OR three or more unresolved MEDIUM + in the detected context column) + Must list each unresolved HIGH with a recommended fix, and summarize + MEDIUM count as advisory. + +LAUNCH_READY + → Zero active BLOCKERs + → Zero unresolved HIGH + → Fewer than three unresolved MEDIUM + Any remaining MEDIUM is advisory only and does not change the verdict. + +HARD RULE: Never output LAUNCH_READY while any context-mapped BLOCKER +is unresolved. This is enforced by the table above, not by judgment. +``` + +--- + +## Required Output Format + +Every `/launch-check` run must return, in this order: + +1. **Detected launch context** — the STEP 0 classification used +2. **Loaded modules** — which domain files were read for this run +3. **Evidence table** — criterion ID, status (resolved/unresolved), severity for this context +4. **Verdict** — exactly one of LAUNCH_READY / LAUNCH_READY_WITH_RISKS / NOT_LAUNCH_READY +5. **BLOCKERs** — named, with exact fix command (empty list if none) +6. **HIGH risks** — named, with recommended fix +7. **MEDIUM advisories** — summarized, does not change verdict +8. **Prioritized fixes** — ordered BLOCKER first, then HIGH, then MEDIUM +9. **Missing evidence** — criteria that could not be checked and why + +## Token Discipline + +- Load only the modules required by STEP 1 routing. Do not load a module with no applicable criteria for the detected project type. +- Do not repeat full module content in the output — reference the criterion ID and verdict-relevant detail only. +- Verdict comes first in the response. Evidence detail is concise and secondary. + +## Safety Boundaries + +- Never request private keys or seed phrases. +- Never send transactions on the user's behalf. +- Never modify wallet authorities or deployments directly. +- Always provide the exact command for the user to run manually — this skill diagnoses and prescribes, it does not execute. diff --git a/solana-launch-readiness-skill/skill/demo.md b/solana-launch-readiness-skill/skill/demo.md new file mode 100644 index 0000000..3cbc2e9 --- /dev/null +++ b/solana-launch-readiness-skill/skill/demo.md @@ -0,0 +1,99 @@ +# Demo & Submission Confidence — Domain E + +## Purpose + +This module evaluates whether the project can actually be demonstrated, reviewed, or submitted without launch-critical ambiguity — not whether the pitch is compelling. It does not write or polish a pitch. It checks whether the demo and submission evidence supports the launch verdict: a verdict claiming LAUNCH_READY is undermined if the "live demo" only runs on localhost, or if the submission is missing the artifacts a reviewer needs to evaluate it at all. + +Severity for every criterion below is read from the launch context classified in `SKILL.md` STEP 0. Do not apply these severities without that classification. + +--- + +## E1 — Live Demo Runs on Target Network Without Localhost Dependencies + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| HIGH | HIGH | HIGH | HIGH | + +**What to check:** Whether the demo a reviewer would actually access runs against a real, reachable deployment — not a local development server only the builder can reach. + +**Command:** +``` +grep -R "localhost\|127.0.0.1" README.md docs/ app/ src/ +``` +Treat a match inside README/docs as a stronger signal than a match in source — a `localhost` reference in a setup script for local development is expected; the same reference as the only access path for reviewers is the actual failure mode. + +**Evidence examples:** +- Resolved: README or submission links to a deployed, externally reachable URL (Vercel, Netlify, Cloud Run, Render, or a devnet/mainnet program address with a working frontend) that does not require the reviewer to run anything locally. +- Unresolved: the only documented way to see the demo is "clone the repo and run `npm run dev`," with no deployed URL provided. +- Missing: no demo access path documented at all — neither a URL nor instructions. + +**How to classify:** If both a deployed URL and local setup instructions exist, this criterion is resolved by the deployed URL alone — local setup instructions are a reasonable addition, not a problem, as long as they are not the *only* path. + +**What not to assume:** Do not assume a demo "probably works" because the code looks complete — this criterion is about reachability, not code quality. Do not assume a deployed link is currently live without checking that it resolves to something other than an error or placeholder page, if a check is possible. + +**Output wording:** "Demo reachability: [confirmed — live at <URL>, no localhost dependency / unresolved — only local setup instructions found, no deployed URL / no demo access path found]." + +--- + +## E2 — Demo Wallet Funded Only to Minimum Needed + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| HIGH | MEDIUM | HIGH | **BLOCKER** | + +**What to check:** Whether any wallet used for demo purposes holds only the minimum funds needed to run the demo — not idle excess that turns a public demo into an unnecessary funded-wallet exposure. + +**Evidence handling:** This module never queries a wallet balance autonomously. Wallet balance is accepted only as evidence the user directly provides (a stated balance, a screenshot description, or an explicit confirmation). Do not look up or expose a wallet's holdings on your own initiative. + +**Evidence examples:** +- Resolved: the user states the demo wallet balance and it is reasonably scoped to the demo's actual needs (e.g., a few dollars of SOL for transaction fees on a funds-moving demo, not an arbitrary large balance). +- Unresolved: the user states or implies the demo wallet holds significantly more than the demo requires, with no stated reason (e.g., it's the same wallet used for other production funds). +- Missing: no information about the demo wallet's funding has been provided — ask the user directly rather than assuming either a safe or unsafe state. + +**How to classify:** "Minimum needed" is contextual — a demo that only signs read-only or simulated transactions needs near-zero balance; a demo that actually moves tokens needs enough to cover that movement plus fees, and no meaningful excess beyond it. Judge against the demo's actual on-chain actions, not a fixed number. + +**What not to assume:** Do not assume a demo wallet is safe because "it's just for the hackathon" — a publicly shared demo wallet address with real funds is exploitable by anyone watching the demo or reading the repo, regardless of intent. Do not request the wallet's private key or seed phrase to check this yourself under any circumstance. + +**Output wording:** "Demo wallet funding: [user-confirmed — balance scoped to demo needs / user-confirmed — balance appears to exceed demo needs, recommend reducing / not disclosed — ask user before continuing]." + +**Fix when unresolved, MAINNET_WITH_FUNDS:** Move excess funds out of the demo wallet to a separate wallet not exposed in any public demo, repo, or video, leaving only the minimum balance required for the demo's actual on-chain actions plus transaction fees. + +--- + +## E3 — Submission Package Complete + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| HIGH | HIGH | HIGH | HIGH | + +**What to check:** Whether the basic artifacts a reviewer needs to evaluate the submission are actually present: README, a demo link or video, a public repository, and a license. + +**Commands:** +``` +ls README.md LICENSE +git remote -v +git status --short +grep -R "demo\|video\|youtube\|loom\|vercel\|netlify\|cloud run\|render" README.md docs/ +``` +`git remote -v` confirms whether the repo has a remote at all (a strong signal it could be public); it does not by itself confirm public visibility — ask the user to confirm the repo's visibility setting directly rather than inferring it from the remote URL alone. `git status --short` confirms there are no uncommitted changes that would make the submitted state different from what a reviewer actually checks out. + +**Evidence examples:** +- Resolved: `README.md` and `LICENSE` both exist, a demo link or video is referenced in the README or docs, the repo has a remote and the user confirms it is public, and `git status --short` shows no uncommitted changes at submission time. +- Unresolved: one or more of the above exists but is incomplete (e.g., README exists but has no demo link; LICENSE file is present but empty). +- Missing: `README.md` or `LICENSE` does not exist at all, or the user confirms the repository is still private. + +**How to classify:** Treat each artifact independently and report which specific ones are missing rather than a single pass/fail — a submission missing only a demo video has a different fix than one missing a LICENSE entirely. + +**What not to assume:** Do not assume a repository is public because it has a `git remote -v` entry pointing to GitHub — private repos also have remotes. Confirm visibility with the user directly. Do not assume uncommitted local changes will be included in what a reviewer sees — only committed, pushed state matters for submission completeness. + +**Output wording:** "Submission package: [complete — README, LICENSE, demo link, public repo all confirmed / incomplete — missing: <list> / repo visibility unconfirmed, ask user]." + +--- + +## Safety Boundaries (Domain E) + +- Never request a private key or seed phrase for any wallet, demo or otherwise. +- Never query or look up a wallet's balance or holdings autonomously — accept only balance information the user explicitly provides. +- Never encourage funding a demo wallet beyond what the demo's actual on-chain actions require. +- Never claim a demo is live or reachable without an actual URL or other concrete evidence — do not assume reachability from the presence of deployment-related code alone. +- This module does not publish, push, or submit anything — it reads current repository and documentation state and reports gaps for the user to act on. diff --git a/solana-launch-readiness-skill/skill/docs.md b/solana-launch-readiness-skill/skill/docs.md new file mode 100644 index 0000000..3093b51 --- /dev/null +++ b/solana-launch-readiness-skill/skill/docs.md @@ -0,0 +1,98 @@ +# Documentation & Discoverability — Domain D + +## Purpose + +This module evaluates whether the project's documentation contains the minimum evidence a user, judge, integrator, or reviewer needs to avoid launch-critical confusion. It is not a copywriting guide and does not assess writing quality, tone, or formatting. It checks whether documentation gaps create a real risk — a wrong program ID, a swallowed error, or no path to report a vulnerability — not whether the README reads well. + +Severity for every criterion below is read from the launch context classified in `SKILL.md` STEP 0. Do not apply these severities without that classification. + +--- + +## D1 — README Shows Correct Network Program ID + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| MEDIUM | LOW | HIGH | **BLOCKER** | + +**What to check:** Whether the program ID published in the README (or other primary docs) matches the network the project is actually claiming to be live on — specifically, that a mainnet-launch claim is not paired with a devnet program ID left over from earlier development. + +**Command:** +``` +grep -R "program id\|programId\|declare_id!\|mainnet\|devnet" README.md docs/ programs/ app/ src/ +``` +Cross-reference the program ID found in `declare_id!()` (source of truth in code) against the program ID published in README/docs, and against the network the project claims to be deployed on. + +**Evidence examples:** +- Resolved: README states the network (e.g., "deployed on mainnet-beta") and the listed program ID matches the `declare_id!()` value, and that ID is independently confirmed to exist on the claimed network (cross-check against the security module's `solana program show` evidence if already gathered). +- Unresolved: README claims mainnet deployment but the published program ID is the devnet program ID (a common leftover from development), or no network is stated at all alongside the program ID. +- Missing: no program ID published in any documentation — ask the user directly rather than guessing one. + +**How to classify:** A mismatch between the README's claimed network and the program ID's actual deployed network is the specific failure this criterion exists to catch. If the project has both a devnet and mainnet deployment with different IDs, confirm the README clearly labels which ID belongs to which network — an unlabeled dual listing is unresolved. + +**What not to assume:** Do not invent or guess a program ID under any circumstance. Do not infer which network a program ID belongs to from a transaction signature or address pattern alone — confirm against `declare_id!()` in source or ask the user directly. + +**Output wording:** "Program ID documentation: [confirmed — README program ID matches <network> deployment / mismatch — README claims <network> but program ID corresponds to <other network> / no program ID published]." + +**Fix when unresolved, MAINNET_WITH_FUNDS:** Update the README to show the actual mainnet program ID confirmed via `solana program show <PROGRAM_ID>` against the mainnet cluster, and remove or clearly separate any devnet ID so a user cannot mistakenly interact with the wrong network. + +--- + +## D2 — Error Codes Mapped to User-Facing Messages + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| LOW | MEDIUM | MEDIUM | HIGH | + +**What to check:** Whether the client surfaces a program's specific error to the end user, rather than swallowing it into a generic failure message or an unhandled exception. + +**Command:** +``` +grep -R "AnchorError\|errorCode\|error_code\|ProgramError\|catch" src/ app/ lib/ +``` + +**Evidence examples:** +- Resolved: client code catches the specific `AnchorError`/`ProgramError`, reads `error.errorCode.code` (or equivalent), and maps it to a user-facing message that reflects what actually went wrong (e.g., "Insufficient balance" rather than "Transaction failed"). +- Unresolved: errors are caught but the user sees only a generic message ("Something went wrong") regardless of which on-chain error fired, or the specific error code is logged to console but never surfaced to the UI. +- Missing: no error handling found around transaction calls — failures would propagate as unhandled exceptions or silent no-ops. + +**How to classify:** "Mapped" requires an actual branch or lookup from error code to message — not just that an error object exists somewhere in a catch block. A catch block that only does `console.error(e)` with no user-facing surface is unresolved, not partially resolved. + +**What not to assume:** Do not assume an error is handled because a `try/catch` wraps the transaction call — check whether the catch block does anything beyond logging. Do not assume a generic "transaction failed" toast satisfies this criterion; the requirement is that the *specific* error reaches the user in some readable form. + +**Output wording:** "Error surfacing: [confirmed — N of M known error codes mapped to user-facing messages / errors caught but only logged, not surfaced / no error handling found]." + +--- + +## D3 — Security Contact or Disclosure Process Documented + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| LOW | LOW | MEDIUM | HIGH | + +**What to check:** Whether there is a documented way for someone who finds a security issue to report it responsibly, rather than no path existing at all. + +**Commands:** +``` +ls SECURITY.md .github/SECURITY.md +grep -R "security@\|disclosure\|vulnerability\|bug bounty" README.md SECURITY.md .github/ docs/ +``` + +**Evidence examples:** +- Resolved: a `SECURITY.md` file exists (root or `.github/`), or the README contains a clearly labeled section with a contact email, disclosure process, or bug bounty link. +- Unresolved: a security-related mention exists but lacks an actual contact method (e.g., "please report responsibly" with no email or process named). +- Missing: no `SECURITY.md` exists and no relevant section appears in README or other docs. + +**How to classify:** A disclosure process requires a concrete point of contact or process step — an email address, a form link, or a named bug bounty platform. A vague statement of intent without a way to actually act on it is unresolved. + +**What not to assume:** Do not claim a disclosure process exists because the project has a general "Contact" section — that section must specifically address security reporting, not general support inquiries, to count as resolved here. + +**Output wording:** "Security disclosure process: [confirmed — <contact method/process found> / mentioned but no actionable contact found / not documented]." + +--- + +## Safety Boundaries (Domain D) + +- Never invent or guess a program ID under any circumstance — every program ID referenced in output must come from source code, on-chain query, or direct user statement. +- Never infer which network a program ID or transaction belongs to from a signature or address pattern alone without corroborating evidence. +- Never state that a security disclosure process exists unless it is actually documented somewhere checked. +- This module reads and reports on documentation only — it does not rewrite, edit, or publish any public-facing docs. diff --git a/solana-launch-readiness-skill/skill/remediation.md b/solana-launch-readiness-skill/skill/remediation.md new file mode 100644 index 0000000..db2a603 --- /dev/null +++ b/solana-launch-readiness-skill/skill/remediation.md @@ -0,0 +1,381 @@ +# Remediation Guide + +## Purpose + +This module converts unresolved BLOCKER and HIGH findings into exact, user-run commands or concrete code/config actions. It does not execute fixes, send transactions, modify wallet authorities, or deploy anything itself — every action below is something the user runs. Load this module only after a verdict has named unresolved findings in `SKILL.md`. + +## Remediation Priority + +Fix BLOCKERs first, then HIGH, in the order they appear in the verdict's prioritized fix list. MEDIUM findings are included only where needed to explain a HIGH/BLOCKER fix's context. LOW findings are omitted unless the user asks for them directly. + +--- + +## Signature Remediation Example + +``` +Context detected: MAINNET_WITH_FUNDS + +NOT_LAUNCH_READY + +BLOCKER A1 — Upgrade authority is a hot wallet. + Fix: solana program set-upgrade-authority <PROGRAM_ID> <SQUADS_MULTISIG_ADDRESS> + +BLOCKER C1 — Production RPC is api.mainnet-beta.solana.com. + Fix: set RPC_URL to a dedicated Helius/Triton endpoint before launch. +``` + +This is the standard output shape: verdict first, named BLOCKERs second, exact fix third. No setup narration precedes it. + +--- + +## A1 — Upgrade Authority Is a Hot Wallet + +**Trigger:** `solana program show <PROGRAM_ID>` returns a single-keypair address as Authority, with no multisig or hardware-wallet confirmation, on MAINNET_WITH_FUNDS (BLOCKER) or any other context (HIGH). + +**Why it matters:** Whoever holds that one keypair can push an arbitrary program upgrade at any time, including one that drains user funds. There is no second check. + +**Fix:** +``` +solana program set-upgrade-authority <PROGRAM_ID> --new-upgrade-authority <SQUADS_MULTISIG_ADDRESS> +``` +If the program is feature-complete and no further upgrades are intended: +``` +solana program set-upgrade-authority <PROGRAM_ID> --final +``` + +**Verify:** +``` +solana program show <PROGRAM_ID> +``` +Authority field should now show the multisig address or `none`. + +**What not to do:** Do not run this command on the user's behalf. Do not accept a hot wallet as a temporary fix on MAINNET_WITH_FUNDS — this is a BLOCKER with no acceptable interim state. Choosing the multisig signer set requires human approval — do not select signers for the user. + +--- + +## A2 — Build Not Verifiable + +**Trigger:** No record on otter.so/verify.ellipsis.fi, and `anchor verify` has not been run, on MAINNET_NO_FUNDS or MAINNET_WITH_FUNDS (HIGH). + +**Why it matters:** Without a verifiable build, no one can confirm the deployed bytecode matches the published source — a silent mismatch is undetectable. + +**Fix:** +``` +anchor build --verifiable +anchor verify -p <PROGRAM_NAME> <PROGRAM_ID> +``` + +**Verify:** `anchor verify` exits successfully, or the program ID appears on otter.so/verify.ellipsis.fi. + +**What not to do:** Do not claim verification succeeded without the command's actual output confirming it. + +--- + +## A3 — IDL or Interface Evidence Missing + +**Trigger:** Anchor program with `anchor idl fetch <PROGRAM_ID>` returning empty (HIGH on MAINNET_NO_FUNDS/WITH_FUNDS). Non-Anchor program with no published client SDK docs or interface spec. + +**Why it matters:** Without a published interface, integrators and reviewers cannot confirm how to call the program correctly, increasing the chance of malformed transactions or copy-pasted incorrect integrations. + +**Fix (Anchor):** +``` +anchor idl init -f target/idl/<program_name>.json <PROGRAM_ID> +``` +**Fix (non-Anchor):** Document every instruction's accounts and byte layout in README or a docs page — this is a documentation action, not a command; requires the project owner to write it. + +**Verify:** +``` +anchor idl fetch <PROGRAM_ID> +``` +Should return a non-empty IDL. + +**What not to do:** Do not require an on-chain IDL from a non-Anchor program — confirm the equivalent docs exist instead. + +--- + +## A4 — Vulnerabilities Present or `cargo audit` Not Run + +**Trigger:** `cargo audit` reports critical/high advisories, or has never been run, on MAINNET_WITH_FUNDS (BLOCKER) or other contexts (MEDIUM/HIGH). + +**Why it matters:** Known-vulnerable dependencies are a documented, exploitable attack surface — this is the lowest-effort check with the highest avoidable risk if skipped. + +**Fix:** +``` +cargo audit +``` +Upgrade every crate with a flagged advisory, then re-run until clean. + +**Verify:** `cargo audit` exits with no critical/high advisories. + +**What not to do:** Do not treat a clean `cargo audit` as proof the program logic itself is vulnerability-free — it only covers known dependency CVEs. This BLOCKER cannot be waived by stated intent to fix later; it must show clean. + +--- + +## A5 — External Security Review Missing + +**Trigger:** No published audit report or named reviewing entity found, on MAINNET_WITH_FUNDS (HIGH). + +**Why it matters:** Self-review and AI-assisted review both miss classes of bugs that an independent specialist reviewer is trained to catch. + +**Fix:** Requires human approval — engaging an auditor (Trail of Bits, OtterSec, Halborn, Neodyme) or submitting to a Superteam Bug Bash is a scoping and budget decision the project owner must make; this skill cannot select or engage a reviewer on the user's behalf. + +**Verify:** A published report exists and is linked from the README. + +**What not to do:** Do not state or imply a review has occurred without a named reviewer and report. + +--- + +## B1 — Core Instruction Tests Missing + +**Trigger:** Test suite has zero or partial coverage of defined instructions, confirmed via `anchor test` / `cargo test` and review of which instructions are actually invoked. + +**Why it matters:** An untested instruction is the most likely place for an unhandled edge case to reach production unnoticed. + +**Fix:** Write a test for each untested instruction using LiteSVM (preferred) or direct `cargo test` invocation, covering at least one non-trivial input per instruction, then run: +``` +anchor test +cargo test +``` + +**Verify:** Re-run the grep used in `testing.md` (`grep -R "LiteSVM\|litesvm" tests/ programs/`) and confirm every instruction now has a matching test call. + +**What not to do:** Do not report coverage as complete based on a passing suite alone — confirm which instructions are actually exercised. + +--- + +## B2 — Error Path Tests Missing + +**Trigger:** One or more `#[error_code]` variants have no test that deliberately triggers and asserts on them. + +**Why it matters:** Untested error paths can fail silently or behave unexpectedly under the exact conditions they exist to guard against. + +**Fix:** Add a test per missing error variant that triggers the specific failing condition and asserts on that exact error code, not a generic failure. + +**Verify:** +``` +grep -R "assert_err\|AnchorError\|error_code" tests/ programs/ +``` +Confirm count of tested variants matches count of defined variants. + +**What not to do:** Do not count a happy-path test that "would fail if something broke" as error-path coverage. + +--- + +## B3 — Recent Live Integration Evidence Missing + +**Trigger:** No CI log, Surfpool transcript, or dated devnet run within the last 48 hours. + +**Why it matters:** Unit tests do not catch network-layer or runtime failures that only appear when a transaction actually travels through a live cluster. + +**Fix:** Run the project's integration test suite against devnet or Surfpool now, and retain the log/output with a timestamp. + +**Verify:** Log or CI run shows a timestamp within 48 hours of the launch-readiness check, with a successful outcome. + +**What not to do:** Do not accept a stale or undated prior run as current evidence. + +--- + +## B4 — Test-Only Keypairs in Production Code Paths + +**Trigger:** `Keypair::new()` or `read_keypair_file()` found outside a `#[cfg(test)]` guard or `tests/` directory, in a file reachable by the production build, on MAINNET_WITH_FUNDS (BLOCKER). + +**Why it matters:** A test keypair reachable in production is a predictable, often hardcoded, signing key — an attacker who finds it gains unintended signing capability. + +**Fix:** Move the flagged keypair generation behind `#[cfg(test)]` or into `tests/`, excluded from the production build. + +**Verify:** +``` +grep -R "#\[cfg(test)\].*Keypair\|Keypair::new\|read_keypair_file" programs/ src/ app/ +``` +Re-run and confirm zero matches outside test-scoped files. + +**What not to do:** Do not accept "we'll fix it before mainnet" as resolution — this BLOCKER requires the grep to show zero matches before the verdict changes. + +--- + +## C1 — Shared Public RPC in Production + +**Trigger:** `api.mainnet-beta.solana.com` or `clusterApiUrl('mainnet-beta')` found in a production code path, on MAINNET_WITH_FUNDS (BLOCKER). + +**Why it matters:** The shared public endpoint rate-limits unpredictably under load — a funded production app can fail silently for users at the worst possible moment. + +**Fix:** Replace the shared endpoint with a dedicated provider URL. Set `RPC_URL` (or `NEXT_PUBLIC_RPC_URL`, depending on the project's config convention) to a Helius, Triton, or QuickNode mainnet endpoint in `.env`/`.env.production`, then confirm the running app actually loads that value rather than a hardcoded fallback. + +**Verify:** +``` +grep -R "api.mainnet-beta.solana.com\|clusterApiUrl" . +``` +Confirm zero matches in files that execute in production. + +**What not to do:** Do not embed the dedicated provider's API key directly in public client-side code if the URL contains one — route it through a server-side proxy or environment variable instead. + +--- + +## C2 — Missing Retry/Recovery Logic + +**Trigger:** `TransactionExpiredBlockheightExceededError` or `SendTransactionError` is caught but only logged, with no retry or blockhash refresh. + +**Why it matters:** Transient network conditions cause these errors routinely — without recovery, a normal hiccup becomes a failed user transaction with no automatic retry. + +**Fix:** Add a retry wrapper around transaction submission that re-fetches a fresh blockhash and resubmits on these specific error types, rather than surfacing a bare failure. + +**Verify:** +``` +grep -R "TransactionExpiredBlockheightExceededError\|SendTransactionError\|retry" src/ app/ lib/ +``` +Confirm the matched code path includes an actual retry attempt, not just a log statement. + +**What not to do:** Do not credit a dependency's built-in retry capability (e.g., an SDK's retry option) as resolved unless the project actually configures or invokes it. + +--- + +## C3 — Missing Pre-Submission Simulation + +**Trigger:** One or more state-changing transaction paths send without calling `simulateTransaction` first. + +**Why it matters:** Simulating first surfaces a failure before it costs a transaction fee or has any on-chain side effect — skipping it means failures are discovered the expensive way. + +**Fix:** Add a `simulateTransaction` call (or SDK-equivalent) before every `sendTransaction` call, and check the simulation result before proceeding. + +**Verify:** +``` +grep -R "simulateTransaction\|simulate" src/ app/ lib/ +``` +Confirm every state-changing send path has a matching simulation call upstream of it. + +**What not to do:** Do not assume wallet-adapter behavior simulates automatically — most do not. + +--- + +## C4 — Missing Spending Circuit Breaker or Hard Cap + +**Trigger:** Program or client transfers SOL/tokens with no enforced maximum amount, on MAINNET_WITH_FUNDS (BLOCKER) — only applicable if the program actually moves funds. + +**Why it matters:** An unbounded transfer path means a bug, misuse, or exploit can move an arbitrarily large amount in a single call. + +**Fix:** Add an enforced maximum transfer amount inside the program instruction itself (not only the client UI), so the cap holds even if the program is called directly. + +**Verify:** +``` +grep -R "transfer\|sendAndConfirmTransaction\|createTransferInstruction\|token" src/ app/ programs/ +``` +Confirm the cap is enforced at the program/server layer, not just a frontend input `max` attribute. + +**What not to do:** Do not apply this fix to a program confirmed read-only or non-financial — C4 is not applicable in that case. + +--- + +## D1 — Wrong or Missing Network Program ID in Docs + +**Trigger:** README claims mainnet deployment but the published program ID matches the devnet `declare_id!()` value, or no network is stated, on MAINNET_WITH_FUNDS (BLOCKER). + +**Why it matters:** A user who copies the wrong program ID interacts with the wrong network entirely — at best a wasted transaction, at worst confusion that enables a scam impersonating the real deployment. + +**Fix:** Update README to show the program ID confirmed via `solana program show <PROGRAM_ID>` against the correct cluster, clearly labeled by network. If both devnet and mainnet IDs are documented, separate them explicitly rather than listing one unlabeled. + +**Verify:** +``` +grep -R "program id\|programId\|declare_id!\|mainnet\|devnet" README.md docs/ programs/ app/ src/ +``` +Confirm the README's stated ID matches `declare_id!()` and the claimed network. + +**What not to do:** Never invent or guess a program ID to fill this gap — confirm the real value from source or on-chain query, or ask the user directly. + +--- + +## D2 — Missing User-Facing Error Mapping + +**Trigger:** Client catches `AnchorError`/`ProgramError` but only logs it; the user sees a generic failure message regardless of which on-chain error fired. + +**Why it matters:** A user who hits "insufficient balance" but sees "transaction failed" cannot self-correct — they retry the same failing action or abandon the flow. + +**Fix:** Add a mapping from `error.errorCode.code` (or equivalent) to a specific user-facing message for each known error variant. + +**Verify:** +``` +grep -R "AnchorError\|errorCode\|error_code\|ProgramError\|catch" src/ app/ lib/ +``` +Confirm the catch block branches on the specific error code rather than only logging. + +**What not to do:** Do not count a generic "transaction failed" toast as resolved — the specific error must reach the user in some readable form. + +--- + +## D3 — Missing Security Disclosure Process + +**Trigger:** No `SECURITY.md` and no README section with an actionable contact method, on MAINNET_WITH_FUNDS (HIGH). + +**Why it matters:** Without a clear reporting path, a researcher who finds a vulnerability has no responsible-disclosure option and may default to public disclosure or do nothing. + +**Fix:** Add a `SECURITY.md` (root or `.github/`) containing: a contact email or form, expected response time, and scope of what to report. A minimal version is sufficient — completeness matters more than length. + +**Verify:** +``` +ls SECURITY.md .github/SECURITY.md +grep -R "security@\|disclosure\|vulnerability\|bug bounty" README.md SECURITY.md .github/ docs/ +``` +Confirm a concrete contact method is present, not just a statement of intent. + +**What not to do:** Do not count a general "Contact" section as resolved unless it specifically addresses security reporting. + +--- + +## E1 — Localhost-Only Demo / No Reachable Demo + +**Trigger:** README's only documented access path is local setup instructions (`npm run dev`), with no deployed URL. + +**Why it matters:** A reviewer cannot evaluate a demo they cannot reach — this directly blocks the submission, independent of how good the underlying code is. + +**Fix:** Deploy the frontend to a reachable host (Vercel, Netlify, Cloud Run, Render) and add that URL to the README, in addition to (not instead of) any local setup instructions. + +**Verify:** +``` +grep -R "localhost\|127.0.0.1" README.md docs/ app/ src/ +``` +Confirm a deployed URL is documented as the primary access path. + +**What not to do:** Do not assume code completeness implies the demo is reachable — confirm an actual working URL exists. + +--- + +## E2 — Overfunded Demo Wallet + +**Trigger:** User states or implies the demo wallet holds significantly more than the demo's actual on-chain actions require, on MAINNET_WITH_FUNDS (BLOCKER). + +**Why it matters:** A publicly demoed wallet address with excess real funds is an exploitable target for anyone watching the demo or reading the repo. + +**Fix:** Move excess funds to a separate wallet not exposed in any public demo, repo, or video — requires human approval, since only the user can decide where remaining funds go and execute the transfer themselves. + +**Verify:** User confirms the demo wallet now holds only the minimum needed for the demo's actual actions plus transaction fees. + +**What not to do:** Never request the wallet's private key or seed phrase to check or move funds yourself. Never query the wallet balance autonomously — only act on balance information the user provides. + +--- + +## E3 — Incomplete Submission Package + +**Trigger:** One or more of README, LICENSE, demo link/video, or confirmed-public repo status is missing. + +**Why it matters:** A missing core artifact (especially LICENSE or a working demo link) can disqualify a submission before a reviewer even evaluates the technical work. + +**Fix:** Add whichever specific artifact is missing — create `README.md` or `LICENSE` if absent, add a demo link/video reference to the README, or confirm/change the repository's visibility to public. + +**Verify:** +``` +ls README.md LICENSE +git remote -v +git status --short +``` +Confirm all four artifacts (README, LICENSE, demo reference, public visibility) are present, and that `git status --short` shows no uncommitted changes that would differ from what a reviewer checks out. + +**What not to do:** Do not assume a `git remote -v` entry confirms public visibility — private repos have remotes too; confirm with the user directly. + +--- + +## Safety Boundaries (Remediation) + +- Never execute any command in this guide on the user's behalf — every fix above is presented for the user to run themselves. +- Never request a private key, seed phrase, or keypair file content, under any phrasing. +- Never instruct the user to paste a secret into chat. +- Authority changes, wallet fund movement, and deployment actions are described as exact user-run commands only — this module never performs them. +- Where a fix requires a judgment call this skill cannot make on the user's behalf (selecting multisig signers, engaging an external auditor, deciding where to move excess demo funds), it is explicitly labeled "requires human approval" above rather than silently assumed. diff --git a/solana-launch-readiness-skill/skill/rpc.md b/solana-launch-readiness-skill/skill/rpc.md new file mode 100644 index 0000000..a5d6373 --- /dev/null +++ b/solana-launch-readiness-skill/skill/rpc.md @@ -0,0 +1,128 @@ +# RPC & Runtime Reliability — Domain C + +## Purpose + +This module evaluates whether the application has launch-relevant evidence for RPC endpoint configuration, transaction retry behavior, pre-submission simulation, and fund-movement safeguards. It does not benchmark RPC providers and does not execute or send any transaction. It checks whether runtime failure risks — the kind that cause silent fund loss or a broken demo mid-launch — are surfaced and addressed before launch, not after. + +Severity for every criterion below is read from the launch context classified in `SKILL.md` STEP 0. Do not apply these severities without that classification. + +--- + +## C1 — Dedicated RPC Endpoint in Use + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| LOW | MEDIUM | HIGH | **BLOCKER** | + +**What to check:** Whether the application's production RPC configuration points to a dedicated provider (Helius, Triton, QuickNode) rather than the shared public endpoint. + +**Commands:** +``` +grep -R "api.mainnet-beta.solana.com\|clusterApiUrl" . +``` +Also inspect `.env`, `.env.production`, `NEXT_PUBLIC_RPC_URL`, `RPC_URL`, and any config files (`config.ts`, `constants.ts`, deployment manifests) for the actual endpoint used at runtime — a `grep` hit on the literal string is a starting signal, not the full picture, since the URL may be injected via an environment variable whose value isn't checked into the repo. + +**Evidence examples:** +- Resolved: the production RPC URL (confirmed in `.env`/config, or directly stated by the user) points to a named dedicated provider's endpoint. +- Unresolved: `grep` finds `api.mainnet-beta.solana.com` or `clusterApiUrl('mainnet-beta')` directly in a file that runs in production, or the RPC URL is an environment variable whose actual value the user has not disclosed. +- Missing: no RPC configuration found at all — ask the user where the app's RPC endpoint is set. + +**How to classify:** A reference to `clusterApiUrl('devnet')` or `clusterApiUrl('testnet')` is not the same finding as `clusterApiUrl('mainnet-beta')` — only flag the mainnet shared-endpoint case under this criterion. If the URL is environment-variable-driven, ask what the variable resolves to in production rather than assuming it's safe or unsafe. + +**What not to assume:** Do not assume an RPC URL is "probably fine" because the project demoed successfully once — shared public endpoints degrade under load and rate-limit unpredictably, which is exactly the failure mode this criterion exists to catch before it happens during a funded mainnet launch. + +**Output wording:** "Production RPC endpoint: [confirmed dedicated provider: <name> / shared public endpoint (api.mainnet-beta.solana.com) detected / cannot determine — RPC URL is environment-variable-driven, value not disclosed]." + +**Fix when unresolved, MAINNET_WITH_FUNDS:** Replace the shared endpoint with a dedicated provider URL (e.g., a Helius or Triton mainnet endpoint) in the relevant `.env`/config, and confirm the new value is what actually loads at runtime — not just what's written in a template file. + +--- + +## C2 — Retry Logic for Transaction Expiry and Send Failures + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| LOW | MEDIUM | HIGH | HIGH | + +**What to check:** Whether the client code handles `TransactionExpiredBlockheightExceededError` and `SendTransactionError` with a retry or recovery path, rather than letting the transaction fail silently or crash the calling code. + +**Command:** +``` +grep -R "TransactionExpiredBlockheightExceededError\|SendTransactionError\|retry" src/ app/ lib/ +``` + +**Evidence examples:** +- Resolved: a `grep` match shows the error type being caught and handled with a retry, a re-fetched blockhash, or an explicit user-facing recovery message — not just logged and dropped. +- Unresolved: the error type appears only in a generic `catch (e) { console.error(e) }` block with no retry or recovery logic, or transaction-sending code has no error handling at all. +- Missing: no transaction-sending code found in the inspected paths, or the project does not submit transactions client-side (e.g., it's a read-only viewer). + +**How to classify:** Catching the error and logging it is not equivalent to handling it. Resolved evidence requires an actual retry attempt, a blockhash refresh before resubmission, or a deliberate fallback — not just visibility into the failure. + +**What not to assume:** Do not assume retry logic exists because the library used (e.g., `@solana/web3.js`, Helius SDK) has retry options available — check that the project actually configures or invokes them, rather than crediting the dependency's capability to the project. + +**Output wording:** "Transaction retry handling: [confirmed — retry/recovery logic found for <error type> / errors caught but not retried / no transaction error handling found / not applicable — no client-side transaction submission]." + +--- + +## C3 — Transaction Simulation Before Submission + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| LOW | HIGH | HIGH | HIGH | + +**What to check:** Whether every state-changing transaction is simulated before being sent, so a failure surfaces before it costs a fee or has on-chain side effects. + +**Command:** +``` +grep -R "simulateTransaction\|simulate" src/ app/ lib/ +``` + +**Evidence examples:** +- Resolved: every code path that builds and sends a state-changing transaction calls `simulateTransaction` (or an SDK-equivalent wrapper) first, and the result is checked before proceeding to `sendTransaction`. +- Unresolved: simulation is called in some transaction paths but not others, or simulation results are fetched but never actually checked before sending. +- Missing: no simulation calls found anywhere in the transaction-sending code. + +**How to classify:** "All state-changing operations" means every instruction that writes on-chain state — not just the one shown in a demo. If the project has three transaction-sending flows and only one simulates first, this is unresolved, not resolved. + +**What not to assume:** Do not assume simulation happens implicitly through wallet adapter behavior — most wallet adapters do not simulate by default; the project's own code must call it explicitly. + +**Output wording:** "Pre-submission simulation: [confirmed for all N state-changing paths / confirmed for N of M paths — list unsimulated paths / no simulation found]." + +--- + +## C4 — Spending Circuit Breaker or Hard Cap + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| LOW | MEDIUM | MEDIUM | **BLOCKER** | + +**Conditional applicability:** This criterion applies only when the program or app actually transfers, custodies, or moves SOL or tokens. If the project is read-only or never transfers value (e.g., a registry, oracle, or display-only dApp), C4 = LOW in every context regardless of the table above — do not apply the BLOCKER severity to a non-financial app. + +**What to check:** Whether there is an enforced maximum amount the program or client will move in a single transaction or session, independent of what the caller requests. + +**Command:** +``` +grep -R "transfer\|sendAndConfirmTransaction\|createTransferInstruction\|token" src/ app/ programs/ +``` +Use this to first confirm whether fund movement exists in the codebase at all — if it doesn't, mark C4 LOW/not-applicable and move on. If it does, look for an explicit cap check (a constant, a config value, or an on-chain account constraint) that bounds the transferred amount before the instruction executes. + +**Evidence examples:** +- Resolved: a hardcoded or configurable maximum transfer amount is enforced in the program's instruction logic or in client-side pre-flight checks, and the cap cannot be bypassed by caller-supplied input alone. +- Unresolved: transfer logic exists with no enforced cap — the amount transferred is whatever the caller requests, with no upper bound. +- Missing / not applicable: no transfer, `sendAndConfirmTransaction`, `createTransferInstruction`, or token-movement logic found anywhere — record this explicitly rather than leaving the criterion ambiguous. + +**How to classify:** A cap enforced only in a frontend form (e.g., an input field `max` attribute) is not sufficient — it must be enforced in the program or in server-side logic the caller cannot bypass by calling the program directly. + +**What not to assume:** Do not assume the absence of a cap is acceptable because "the user controls their own funds" — for MAINNET_WITH_FUNDS, a missing cap on a transfer path is exactly the kind of unbounded-exposure risk this criterion exists to catch. + +**Output wording:** "Spending cap: [not applicable — no fund movement found / confirmed — capped at <amount/logic> enforced in <program/server> / unresolved — transfer logic found with no enforced cap]." + +**Fix when unresolved, MAINNET_WITH_FUNDS (and fund movement confirmed present):** Add an enforced maximum transfer amount inside the program instruction itself (not just the client UI), so the cap holds even if the program is called directly, bypassing the frontend. + +--- + +## Safety Boundaries (Domain C) + +- This module never sends, simulates for execution purposes, or broadcasts any transaction — all simulation references above describe what the *project's own code* should do, not an action this skill performs. +- Never test an RPC endpoint's reliability or rate limits by sending live requests against it. +- Never recommend embedding a private RPC API key directly in public client-side code — if a dedicated RPC URL contains an API key, flag that it belongs in a server-side proxy or environment variable, not a client bundle. +- Do not assume an RPC provider's quality, uptime, or rate limits without evidence — this module checks configuration and code, not live provider performance. diff --git a/solana-launch-readiness-skill/skill/security.md b/solana-launch-readiness-skill/skill/security.md new file mode 100644 index 0000000..8687b0d --- /dev/null +++ b/solana-launch-readiness-skill/skill/security.md @@ -0,0 +1,168 @@ +# Security — Domain A: Program Security & Authority + +## Purpose + +This module evaluates launch authority control, build verifiability, interface/IDL evidence, vulnerability status, and external security review status for a Solana program. It does not perform a formal audit and does not substitute for one. It checks whether the *evidence* that a launch-blocking security control exists is present — not whether the code is correct. + +Severity for every criterion below is read from the launch context classified in `SKILL.md` STEP 0. Do not apply these severities without that classification. + +--- + +## A1 — Upgrade Authority Is Not a Hot Wallet + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| HIGH | HIGH | HIGH | **BLOCKER** | + +**What to check:** Who or what can upgrade this program right now. + +**Command:** +``` +solana program show <PROGRAM_ID> +``` +Read the `Authority` field. Acceptable resolutions: +- A Squads multisig address +- A hardware wallet address (verify out of band — this skill cannot confirm wallet type from on-chain data alone) +- `none` (upgrade authority revoked — program is immutable) + +**Evidence examples:** +- Resolved: `solana program show` output shows authority = known Squads multisig address (cross-check against app.squads.so), or authority = `none`. +- Unresolved: authority is a single keypair address with no multisig or hardware-wallet confirmation, and the user has not stated otherwise. +- Missing: program ID not provided, or `solana program show` cannot be run (no RPC access, program not found). + +**How to classify:** +- Do not assume an address is a multisig because it "looks complex" or has many transactions. Confirm via Squads UI or ask the user directly. +- Do not assume a hot wallet is "probably fine for now" on MAINNET_WITH_FUNDS — this is the one criterion most directly tied to fund-drain risk. + +**What not to assume:** +- Do not assume revoked authority is always the right answer — a project still iterating on devnet/demo should keep authority, just not on a bare hot wallet once funds are real. +- Do not infer wallet custody type (hot vs hardware) from the address alone. + +**Output wording:** "Upgrade authority is `<address>`. This is [an unconfirmed single-signer keypair / a confirmed Squads multisig / revoked]." Never phrase this as "looks fine" without one of the three resolutions above. + +**Fix when unresolved, MAINNET_WITH_FUNDS:** +``` +solana program set-upgrade-authority <PROGRAM_ID> --new-upgrade-authority <SQUADS_MULTISIG_ADDRESS> +``` +Or, if the program is feature-complete and no further upgrades are intended: +``` +solana program set-upgrade-authority <PROGRAM_ID> --final +``` + +--- + +## A2 — Program Build Is Verifiable + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| LOW | MEDIUM | HIGH | HIGH | + +**What to check:** Whether the deployed on-chain binary can be independently verified to match the published source code. + +**Commands:** +``` +anchor build --verifiable +anchor verify -p <PROGRAM_NAME> <PROGRAM_ID> +``` + +**Evidence examples:** +- Resolved: `anchor verify` succeeds, or the program ID is listed as verified on otter.so or verify.ellipsis.fi. +- Unresolved: build was done with plain `anchor build` (non-verifiable), no verification record exists anywhere checked. +- Missing: user has not provided the program ID, or the project is not Anchor-based and no equivalent reproducible-build process exists. + +**How to classify:** A verifiable build claim requires either a passing `anchor verify` run or a third-party verification registry hit. A successful local `anchor build --verifiable` alone is not sufficient — it must be checked against the deployed program. + +**What not to assume:** Do not assume a program is verifiable just because it uses Anchor. Anchor makes verification *possible*, not automatic. + +**Output wording:** "Build verifiability: [confirmed via otter.so / confirmed via anchor verify / not verified — no record found]." + +--- + +## A3 — IDL or Interface Documentation Published + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| LOW | MEDIUM | HIGH | HIGH | + +**Anchor programs — what to check:** Whether the IDL is published on-chain. + +**Command:** +``` +anchor idl fetch <PROGRAM_ID> +``` +Resolved if this returns a non-empty IDL. Unresolved if it returns empty or errors. + +**Non-Anchor programs — what to check:** This criterion does not apply in its IDL form to native/Pinocchio/non-Anchor programs. Instead require equivalent evidence: +- A published client SDK with documented instruction encoding, or +- A README/docs section that fully specifies instruction layout, account ordering, and expected data formats. + +**Evidence examples:** +- Resolved (Anchor): `anchor idl fetch` returns the IDL. +- Resolved (non-Anchor): README or docs site documents every instruction's accounts and byte layout. +- Unresolved: program is Anchor-based but IDL was never published on-chain (common when `anchor idl init` was skipped after deploy). +- Missing: cannot determine whether the program uses Anchor at all from available files. + +**What not to assume:** Do not require an on-chain IDL from a non-Anchor program — this is a framework-specific artifact, not a universal requirement. Do not treat the *absence* of Anchor as a deficiency in itself. + +**Output wording:** "Interface evidence: [IDL published on-chain / IDL present locally but not published — fix below / non-Anchor program, client SDK docs found / non-Anchor program, no equivalent interface documentation found]." + +**Fix when unresolved (Anchor, IDL not published):** +``` +anchor idl init -f target/idl/<program_name>.json <PROGRAM_ID> +``` + +--- + +## A4 — No Known Critical/High Vulnerabilities + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| MEDIUM | HIGH | HIGH | **BLOCKER** | + +**What to check:** Whether known-vulnerable dependencies exist in the program's dependency tree. + +**Command:** +``` +cargo audit +``` + +**Evidence examples:** +- Resolved: `cargo audit` runs clean (no critical or high advisories), and the user confirms no known unpatched vulnerability reports exist for the program logic itself. +- Unresolved: `cargo audit` reports one or more critical/high advisories, or has not been run at all. +- Missing: `Cargo.lock` not present or not accessible to run the command against. + +**How to classify:** `cargo audit` clean is the floor, not the ceiling — it only catches known CVEs in dependencies, not logic bugs in the program's own instructions. State this limitation explicitly in output; do not let a clean `cargo audit` imply the program logic itself is vulnerability-free. + +**What not to assume:** Do not treat a clean `cargo audit` as equivalent to "no vulnerabilities." It is one input, not a substitute for A5. + +**Output wording:** "Dependency vulnerability scan: [clean — no critical/high advisories / N critical or high advisories found / not run]. Note: this does not cover custom program logic." + +**Fix when unresolved, MAINNET_WITH_FUNDS:** Run `cargo audit`, address every critical/high advisory by upgrading the affected crate, and re-run until clean before considering this criterion resolved. A BLOCKER here cannot be waived by intent to fix later — it must show clean before verdict changes. + +--- + +## A5 — External Security Review Completed + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| LOW | LOW | MEDIUM | HIGH | + +**What to check:** Whether an external party has reviewed the program for security issues — not whether the team believes the code is sound. + +**Evidence examples:** +- Resolved: a published audit report (Trail of Bits, OtterSec, Halborn, Neodyme) exists and is linked from the README, or the program passed a Superteam Bug Bash with a public report. +- Unresolved: no external review has occurred, or one is "in progress" with no completed report yet. +- Missing: cannot determine review status from available files — ask the user directly rather than assuming none exists. + +**What not to assume:** Do not claim an audit exists unless a report or named reviewing entity is actually evidenced. Do not accept "we reviewed it ourselves" or "the AI reviewed it" as satisfying this criterion — it requires an external party. + +**Output wording:** "External security review: [completed — <reviewer name>, report linked / not completed — no external review found]." Never state or imply that an audit has occurred without a named source. + +--- + +## Safety Boundaries (Domain A) + +- Never run, suggest running, or simulate an authority-change transaction on the user's behalf. Only provide the exact command for the user to run themselves. +- Never request a private key, seed phrase, or keypair file content. +- Never claim an external security review exists without a verifiable report or named reviewing entity as evidence. +- All commands above are read-only or are presented as copy-paste actions for the user — this module does not execute them. diff --git a/solana-launch-readiness-skill/skill/testing.md b/solana-launch-readiness-skill/skill/testing.md new file mode 100644 index 0000000..c99110a --- /dev/null +++ b/solana-launch-readiness-skill/skill/testing.md @@ -0,0 +1,126 @@ +# Testing — Domain B: Testing Evidence + +## Purpose + +This module evaluates whether launch-relevant test evidence exists for the program's core logic, error paths, and integration behavior on a live network. It does not measure full coverage and does not prove correctness. It checks whether the *evidence* needed to support a launch verdict is present — a passing test suite reduces launch risk; it does not eliminate it. + +Severity for every criterion below is read from the launch context classified in `SKILL.md` STEP 0. Do not apply these severities without that classification. + +--- + +## B1 — Core Instruction Tests Present, Not Happy-Path Only + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| MEDIUM | HIGH | HIGH | HIGH | + +**What to check:** Whether every core on-chain instruction has a test that exercises it beyond the single expected success path. + +**Commands:** +``` +anchor test +cargo test +grep -R "LiteSVM\|litesvm" tests/ programs/ +``` + +LiteSVM is the preferred evidence type for Solana-native test execution — it runs the actual program against a lightweight SVM rather than mocking it. If the project does not use LiteSVM, a plain `cargo test` suite that directly invokes program logic (without a full validator) is acceptable evidence as long as it exercises the same instructions. Do not require LiteSVM specifically — require that *some* test exercises each core instruction. + +**Evidence examples:** +- Resolved: `anchor test` or `cargo test` passes, and a `grep` for `LiteSVM`/`litesvm` or equivalent instruction-level test calls shows each core instruction is invoked at least once with a non-trivial input, not just the default success case. +- Unresolved: tests exist but only cover one instruction, or all tests use the same minimal "happy path" input with no edge cases. +- Missing: no `tests/` directory, or test commands fail to run (missing dependencies, build errors). + +**How to classify:** "Covering core instructions" means every instruction a user can call in production has at least one test. A program with five instructions and tests for only two has unresolved evidence for the other three, even if the two tested ones are thorough. + +**What not to assume:** Do not assume a single passing `anchor test` run means all instructions are tested — check which instructions are actually invoked in the test file, not just that the suite is green. Do not invent or assume test coverage percentages; report only what is directly observed. + +**Output wording:** "Core instruction test coverage: [N of M instructions have direct test invocation — list untested instructions / all instructions tested, single-path only / all instructions tested with multiple input cases / no test evidence found]." + +--- + +## B2 — Error Paths Tested + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| LOW | MEDIUM | MEDIUM | HIGH | + +**What to check:** Whether each custom error variant the program can return has a test that deliberately triggers it and asserts the rejection. + +**Command:** +``` +grep -R "assert_err\|AnchorError\|error_code" tests/ programs/ +``` + +**Evidence examples:** +- Resolved: every `#[error_code]` variant defined in the program has a corresponding test that triggers the failing condition and asserts on the specific error (not just "any error"). +- Unresolved: some error variants are defined but never deliberately triggered in tests, or tests assert generic failure without checking which error fired. +- Missing: no `error_code` enum found, or no error-path tests exist at all. + +**How to classify:** Count the defined error variants against the ones with a matching test assertion. Partial coverage (some but not all variants tested) is unresolved, not resolved — state the gap explicitly rather than rounding up. + +**What not to assume:** Do not assume an error path is tested because the happy-path test "would fail if something broke" — that is not the same as a deliberate test of the rejection condition. + +**Output wording:** "Error path coverage: [N of M error variants have a dedicated rejection test / no error-path tests found / unable to enumerate error variants]." + +--- + +## B3 — Live Integration Test Executed Recently + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| LOW | HIGH | HIGH | HIGH | + +**What to check:** Whether the program has been exercised against a live network (devnet, or Surfpool's local validator fork) recently, not just in isolated unit tests. + +**Evidence to request:** Surfpool run output, devnet integration test logs, or a CI run timestamp within the last 48 hours. Acceptable forms: +- A CI job log showing a devnet integration test run with a timestamp +- A Surfpool session transcript or log file +- A manually reported test run with timestamp and outcome, stated directly by the user + +**Evidence examples:** +- Resolved: a log, CI run, or direct user statement shows a successful integration run against devnet or Surfpool within the last 48 hours. +- Unresolved: integration tests exist in the repo but no evidence of an actual recent run is available (last known run is stale or undated). +- Missing: no integration test setup exists at all — only unit-level tests. + +**How to classify:** "Recent" means within 48 hours of the launch-readiness check. A test suite that exists but was last run a week ago is unresolved, not resolved — the program or its dependencies may have changed since. + +**What not to assume:** Do not treat the mere existence of an integration test file as evidence it was run. Do not assume a successful unit test suite substitutes for a live network run — they test different failure modes (logic errors vs. network/runtime behavior). + +**Output wording:** "Live integration evidence: [confirmed — <source>, run at <timestamp> / integration tests exist but no recent run evidence / no integration tests found]." + +--- + +## B4 — No Test-Only Keypairs in Production Code Paths + +| DEMO_HACKATHON | DEVNET | MAINNET_NO_FUNDS | MAINNET_WITH_FUNDS | +|---|---|---|---| +| MEDIUM | HIGH | HIGH | **BLOCKER** | + +**What to check:** Whether any keypair generated or loaded for testing purposes is reachable from a code path that runs in production. + +**Command:** +``` +grep -R "#\[cfg(test)\].*Keypair\|Keypair::new\|read_keypair_file" programs/ src/ app/ +``` + +**Evidence examples:** +- Resolved: every `Keypair::new()` or `read_keypair_file()` call is confined to a `#[cfg(test)]` block, a `tests/` directory, or an explicitly test-only binary/script that is never invoked by the production deployment. +- Unresolved: a `Keypair::new()` or hardcoded test keypair appears in a file under `programs/`, `src/`, or `app/` outside a `#[cfg(test)]` guard, with no clear separation from the production build path. +- Missing: cannot determine build/deploy boundaries from available files — ask the user which files are actually included in the production build. + +**How to classify:** The grep above is a starting signal, not a final verdict — review each match manually. A match inside a `#[cfg(test)] mod tests { ... }` block is resolved; the same call sitting in a shared utility module imported by both tests and the production binary is unresolved. + +**What not to assume:** Do not assume a keypair is test-only just because the variable is named `test_keypair` — check whether the code path that uses it is actually excluded from the production build. Do not assume the absence of a grep match means none exist — dynamically constructed keypair logic may not match the pattern. + +**Output wording:** "Test-keypair isolation: [confirmed — all test keypair generation is scoped to #[cfg(test)] or tests/ / unresolved — test keypair generation found in <file path>, reachable from production path]." + +**Fix when unresolved, MAINNET_WITH_FUNDS:** Move the flagged keypair generation behind a `#[cfg(test)]` attribute or into a file under `tests/` that is excluded from the production build, then re-run the grep to confirm no remaining matches outside test scope. This BLOCKER cannot be waived by stating intent to fix later — it must show zero matches before the verdict changes. + +--- + +## Safety Boundaries (Domain B) + +- Do not invent or assume passing test results — only report what `anchor test`, `cargo test`, or a provided log actually shows. +- Do not claim a coverage percentage unless a coverage tool actually produced one; describe coverage in terms of "N of M instructions/errors" instead. +- Do not treat a happy-path-only test suite as sufficient evidence for B1 or B2 regardless of launch context — happy-path coverage downgrades the resolution status, it does not satisfy it. +- This module never modifies production code or test files — it reads and reports only. diff --git a/solana-launch-readiness-skill/tests/cloud-sanity.sh b/solana-launch-readiness-skill/tests/cloud-sanity.sh new file mode 100755 index 0000000..6e03ac3 --- /dev/null +++ b/solana-launch-readiness-skill/tests/cloud-sanity.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -euo pipefail + +fail() { + echo "FAIL: $1" + exit 1 +} + +pass() { + echo "PASS: $1" +} + +# 1. Required files +for f in \ + README.md LICENSE CLAUDE.md install.sh install-custom.sh \ + skill/SKILL.md skill/security.md skill/testing.md skill/rpc.md skill/docs.md skill/demo.md skill/remediation.md \ + commands/launch-check.md commands/fix-blockers.md \ + rules/no-shared-rpc.md \ + examples/launch-check-cases.md tests/manual-harness.md +do + test -f "$f" || fail "missing $f" +done +pass "required files exist" + +# 2. 19 criteria IDs in core files +for id in A1 A2 A3 A4 A5 B1 B2 B3 B4 C1 C2 C3 C4 D1 D2 D3 E1 E2 E3 +do + grep -q "$id" skill/SKILL.md || fail "$id missing from skill/SKILL.md" + grep -q "$id" skill/remediation.md || fail "$id missing from skill/remediation.md" +done +pass "all 19 criteria present in SKILL.md and remediation.md" + +# 3. Domain module coverage +for id in A1 A2 A3 A4 A5; do grep -q "$id" skill/security.md || fail "$id missing from security.md"; done +for id in B1 B2 B3 B4; do grep -q "$id" skill/testing.md || fail "$id missing from testing.md"; done +for id in C1 C2 C3 C4; do grep -q "$id" skill/rpc.md || fail "$id missing from rpc.md"; done +for id in D1 D2 D3; do grep -q "$id" skill/docs.md || fail "$id missing from docs.md"; done +for id in E1 E2 E3; do grep -q "$id" skill/demo.md || fail "$id missing from demo.md"; done +pass "domain modules cover expected IDs" + +# 4. Verdict language +grep -q "NOT_LAUNCH_READY" skill/SKILL.md || fail "NOT_LAUNCH_READY missing" +grep -q "LAUNCH_READY_WITH_RISKS" skill/SKILL.md || fail "LAUNCH_READY_WITH_RISKS missing" +grep -q "LAUNCH_READY" skill/SKILL.md || fail "LAUNCH_READY missing" +grep -q "19 Criteria" skill/SKILL.md || fail "19 Criteria heading missing" +! grep -R --exclude-dir=.git --exclude=cloud-sanity.sh "18 Criteria\|18-criteria" . >/dev/null || fail "stale 18 criteria reference found" +pass "verdict language and 19 criteria count valid" + +# 5. Commands +grep -q "STEP 0" commands/launch-check.md || fail "STEP 0 missing from launch-check" +grep -q "remediation.md" commands/launch-check.md || fail "remediation.md missing from launch-check" +grep -q "/launch-check" commands/fix-blockers.md || fail "/launch-check missing from fix-blockers" +grep -q "remediation.md" commands/fix-blockers.md || fail "remediation.md missing from fix-blockers" +pass "commands reference expected flow" + +# 6. Rule triggers +grep -q "api.mainnet-beta.solana.com" rules/no-shared-rpc.md || fail "public RPC trigger missing" +grep -q "clusterApiUrl" rules/no-shared-rpc.md || fail "clusterApiUrl trigger missing" +grep -q "C1" rules/no-shared-rpc.md || fail "C1 missing from no-shared-rpc" +grep -q "/launch-check" rules/no-shared-rpc.md || fail "/launch-check missing from no-shared-rpc" +pass "no-shared-rpc rule triggers valid" + +# 7. README proof +grep -q "doesn't help you ship faster" README.md || fail "README positioning line missing" +grep -q "/launch-check" README.md || fail "README missing /launch-check" +grep -q "/fix-blockers" README.md || fail "README missing /fix-blockers" +grep -q "api.mainnet-beta.solana.com" README.md || fail "README missing rule example" +grep -q "Faadil1" README.md || fail "README install URL does not include Faadil1" +pass "README proof sections valid" + +# 8. Example cases +grep -q "MAINNET_WITH_FUNDS" examples/launch-check-cases.md || fail "Case 1 context missing" +grep -q "NOT_LAUNCH_READY" examples/launch-check-cases.md || fail "Case 1 verdict missing" +grep -q "LAUNCH_READY_WITH_RISKS" examples/launch-check-cases.md || fail "Case 2 verdict missing" +grep -q "DEMO_HACKATHON" examples/launch-check-cases.md || fail "Case 3 context missing" +pass "example cases present" + +# 9. Installer executable +test -x install.sh || fail "install.sh not executable" +test -x install-custom.sh || fail "install-custom.sh not executable" +pass "installers executable" + +# 10. Forbidden phrases +for phrase in \ + "comprehensive checklist" \ + "best practices" \ + "ensure your project is secure" \ + "helps you launch with confidence" \ + "AI-powered analysis" \ + "improve security" \ + "review your code" \ + "production-ready RPC" +do + ! grep -R -i --exclude-dir=.git --exclude=cloud-sanity.sh "$phrase" . >/dev/null || fail "forbidden phrase found: $phrase" +done +pass "forbidden phrase scan clean" + +echo +echo "ALL CLOUD SANITY CHECKS PASSED" diff --git a/solana-launch-readiness-skill/tests/manual-harness.md b/solana-launch-readiness-skill/tests/manual-harness.md new file mode 100644 index 0000000..bf53235 --- /dev/null +++ b/solana-launch-readiness-skill/tests/manual-harness.md @@ -0,0 +1,70 @@ +# Manual Verification Harness + +Manual steps to verify the skill installs correctly, the `no-shared-rpc` rule fires, and `/launch-check` / `/fix-blockers` behave as documented. No automated test framework — run these by hand and compare actual output to the pass criteria below. + +--- + +## 1. Install Test + +```bash +bash install.sh +test -f ~/.claude/skills/solana-launch-readiness/SKILL.md +``` + +**Pass:** `install.sh` exits without error and the second command exits `0` — `SKILL.md` exists at the install path. Re-running `bash install.sh` a second time should also exit cleanly (idempotent). + +--- + +## 2. Custom Install Test + +```bash +printf "1\n" | bash install-custom.sh +printf "2\n" | bash install-custom.sh +test -f ./.claude/skills/solana-launch-readiness/SKILL.md +``` + +**Pass:** Choice `1` installs to `~/.claude/skills/solana-launch-readiness/`. Choice `2` (run from a directory you intend as the project root) installs to `./.claude/skills/solana-launch-readiness/`, and the final `test` command confirms `SKILL.md` exists at that project-relative path. + +--- + +## 3. Rule Test + +```bash +mkdir -p /tmp/solana-launch-rule-test +echo 'RPC_URL=https://api.mainnet-beta.solana.com' > /tmp/solana-launch-rule-test/.env +``` + +Then, with Claude Code pointed at `/tmp/solana-launch-rule-test`, ask it to review or work with that project's RPC configuration. + +**Expected warning content:** +- "Shared public Solana mainnet RPC detected" +- Reference to Criterion C1 +- Statement that context severity is unknown until `/launch-check` classifies it + +**Pass:** The warning fires before Claude continues with the requested task, and includes all three elements above — not a generic "RPC looks fine" or no response at all. + +--- + +## 4. Command Behavior Tests + +Using the three cases in `examples/launch-check-cases.md`: + +1. For each case, describe the evidence setup to Claude Code (in this conversation or a fresh one) and run `/launch-check`. +2. Compare the actual verdict and named findings against that case's expected output excerpt. +3. For Case 1 (`NOT_LAUNCH_READY`), follow up with `/fix-blockers` and confirm the fixes returned match `skill/remediation.md`'s entries for A1 and C1 — not an invented fix. + +--- + +## 5. Pass Criteria Summary + +| # | Check | Pass condition | +|---|---|---| +| 1 | `install.sh` | Installs cleanly, idempotent on re-run | +| 2 | `install-custom.sh` | Both choice `1` and choice `2` install to the correct respective path | +| 3 | `no-shared-rpc` rule | Warning fires with all three required elements before Claude continues | +| 4 | Case 1 | `/launch-check` produces `NOT_LAUNCH_READY` with BLOCKER A1 and BLOCKER C1 | +| 5 | Case 2 | `/launch-check` produces `LAUNCH_READY_WITH_RISKS` with HIGH B1 and HIGH B3 | +| 6 | Case 3 | `/launch-check` produces `LAUNCH_READY` with no BLOCKERs or HIGH risks | +| 7 | `/fix-blockers` | For Case 1, returns fixes matching `skill/remediation.md` entries for A1 and C1, not invented ones | + +All seven must pass before considering the skill verified end-to-end.