diff --git a/README.md b/README.md index 296dd4e..54d7b32 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,8 @@ The site defaults to the host root; the docs are under `/docs/`. Pages CI sets `NEXT_PUBLIC_BASE_PATH=/agent-cli` for its deployment, builds and checks pull requests, and deploys main. Command schemas and the skill guide are generated from the CLI with `npm run docs:generate` and before docs builds. +Agents can fetch the same packaged skill as plain Markdown at +[SKILL.md](https://bloxwap.github.io/agent-cli/SKILL.md). [Docs index](docs/index.md), [security](docs/security.md), [error catalog](docs/errors.md), and [SKILL.md](packages/agent-cli/SKILL.md) remain diff --git a/apps/docs/README.md b/apps/docs/README.md index 7c39715..6328149 100644 --- a/apps/docs/README.md +++ b/apps/docs/README.md @@ -23,11 +23,17 @@ canonical and social URLs (default `https://bloxwap.github.io`). The static prev reads the base path from the built artifact and needs no Next.js server. Edit MDX in `content/docs/` and sidebar order in `meta.json`. `commands.mdx` and -`skill.mdx` are generated from the built CLI and packaged SKILL.md. Run +`skill.mdx`, plus the plain Markdown `public/SKILL.md`, are generated from the built +CLI and packaged SKILL.md. Run `npm run docs:generate` after CLI changes. Dev/build also synchronize them; `docs:check` rejects stale generated content, checks sidebar/local MDX links, verifies HIG provenance, and checks Next.js/TypeScript types. +The canonical skill is https://bloxwap.github.io/agent-cli/SKILL.md +(`http://localhost:3903/SKILL.md` during local development). It preserves the +packaged skill's frontmatter and is linked from the skill guide and `llms.txt`. +The export and browser checks verify that the served file matches the source. + Build output lives in `out/`. The postbuild checker validates local HTML links and assets, base-path containment, social metadata and 1200×630 PNG cards. Search uses a static `search.json` index in the browser. `llms.txt`, robots, and sitemap diff --git a/apps/docs/content/docs/skill.mdx b/apps/docs/content/docs/skill.mdx index a11116d..3adb274 100644 --- a/apps/docs/content/docs/skill.mdx +++ b/apps/docs/content/docs/skill.mdx @@ -3,6 +3,10 @@ title: Agent skill guide description: Install and use the packaged SKILL.md with Codex or Claude Code. --- +import Link from 'next/link'; + +Download SKILL.md as plain Markdown, including the skill frontmatter. + The npm package ships `SKILL.md` and embeds the same content in the CLI. Install it with: ```sh diff --git a/apps/docs/public/SKILL.md b/apps/docs/public/SKILL.md new file mode 100644 index 0000000..f608c5b --- /dev/null +++ b/apps/docs/public/SKILL.md @@ -0,0 +1,158 @@ +--- +name: agent-cli +description: Operate the agent-cli Hyperliquid trading CLI, including managed-agent onboarding, funding, orders, event monitoring, rotation, and normalized error recovery. +metadata: + version: "0.2.0" +--- + +Use `agent-cli` for the user's authorized Hyperliquid account operations. Obtain +command schemas with `agent-cli schema COMMAND`; arguments can be flags or +`--json '{...}'`, or a JSON object on stdin. Ordinary commands print exactly one +JSON object: `{ok:true,data}` or `{ok:false,code,message,retryable,details}`. +Logs/QR appear on stderr. `events --follow` and `logs` explicitly stream JSON lines. +Use `--network testnet` during testing; never silently change to mainnet. + +## Onboarding sequence + +1. The user creates and owns their own Privy account and app. Configure that + app ID and a local secret reference, e.g. `config set privyAppId APP` and + `config set privyAppSecretRef env:BLOXWAP_PRIVY_APP_SECRET`. The user sets the + environment secret outside the conversation on their own machine or runner. + Bloxwap does not host the app, receive credentials, or custody user keys. + Privy manages the trading-agent key in the user's app; master-wallet approval + always remains in the user's external wallet. The default Bloxwap builder is + `0x71b09A08257078A4d642F7Dd7315e656c837329a`, with `f=10` (1 bp = 0.01%). + Override with `config set builder.address` / `config set builder.fee` or + `BLOXWAP_BUILDER_ADDRESS` / `BLOXWAP_BUILDER_FEE`. +2. Run `init --master-address 0x... --network testnet`. Show the returned approval + payload/deeplink/QR. The user signs in their master wallet; submit only the + resulting signature with `init --signature 0x...`. Submit builder approval + separately with `init --builder-signature 0x...` for the configured 0.01% default. Continue only after `approved:true`. +3. Run `fund --amount 10 --origin-chain 42161`. Show `qrAscii`/deposit address, + origin chain/token, minimum, fees, expected received amount and expiry. On + testnet, do not treat the default Arbitrum mainnet chain/token as a test route: + use an explicitly supported test chain/token and `originDecimals`, or the + credential-gated CI harness with mocked Relay and pre-funded test collateral. + If no test route is known, stop funding and report the missing route. The user + transfers the quoted origin token. If `signatureSteps` require signatures, + show the exact payload and submit each via `deposit` in returned order. +4. Poll `status` until each active deposit is `settled`, for at most 12 polls + or 5 minutes, whichever comes first. Start with 1-second delays, double to a + 15-second cap, and stop with an explicit pending report if the budget expires. + Settlement requires Relay success and Hyperliquid balance receipt. Stop on + failed/refunded deposits and explain their status. Do not place orders while + any deposit is awaiting funds or pending. +5. Run `run --detach`, then monitor `events --follow --since SEQ`. Persist the + last handled sequence; replay after reconnect. An unfilled or rejected order + remains incomplete: do not claim the requested trade succeeded. A fill updates the trading + plan, an error follows the catalog below, and expiry warnings prompt rotation + using external master-wallet approval. + +## Completion and approval boundaries + +Read this skill and query schemas before acting. Keep a progress record with the +network, current stage, deposit IDs, last event sequence and order cloid/oid; +record public IDs only. A missing user-owned Privy app, wallet signature, funding +route or settled balance is a blocked stage. Report the exact next action without +requesting any secret in chat or moving to the next stage. Repeat `init` to resume +persisted approvals rather than creating another agent. An `ok:true` transport +envelope does not imply exchange acceptance: inspect `data.result` and reconcile +`orders`/`events` after uncertain mutations. Never retry an order with a new cloid +until the original intent has been reconciled. + +For rotation, verify `swapped:true` before asking the external wallet to sign old +agent revocation. Require `oldRevoked:true` before claiming rotation complete. +`revoke` immediately disables local signing; only `exchangeRevoked:true` confirms +that the signed exchange revocation was submitted. Inspect `status` to verify the +agent is absent from exchange approvals. Cancel outstanding orders before final +revocation, and report any cancellation or cleanup failure explicitly. + +## Commands and example envelopes + +Examples show representative `data`, not live balances or service promises. +Full inputs/outputs are discoverable with `schema`; amounts/prices are strings. + +| Input | Example output | +| --- | --- | +| `--help` | `{"ok":true,"data":{"commands":[{"name":"status","description":"Read account status"}],"globalFlags":["--json","--config","--network","--pretty"]}}` | +| `schema order` | `{"ok":true,"data":{"input":{"type":"object"},"output":{"oneOf":[]}}}` (actual schemas include properties) | +| `config get network` | `{"ok":true,"data":"testnet"}` | +| `config set limits.maxLeverage 5` | `{"ok":true,"data":{"version":1,"limits":{"maxLeverage":5}}}` | +| `config validate` | `{"ok":true,"data":{"valid":true,"version":1}}` | +| `init --master-address 0x...` | `{"ok":true,"data":{"approved":false,"requiresSignature":true,"approvals":{"agent":{"typedData":{}}},"qrAscii":"..."}}` | +| `fund --amount 10 --no-qr` | `{"ok":true,"data":{"depositId":"...","depositAddress":"0x...","expectedReceived":"100000000","builderFeeRate":"0.01%","builderAddress":"0x71b09A08257078A4d642F7Dd7315e656c837329a","state":"awaiting_funds","qrAscii":null}}` | +| `deposit --deposit-id ID` | `{"ok":true,"data":{"depositId":"ID","steps":[{"id":"step:0","requiresSignature":true,"payload":{}}]}}` | +| `deposit --deposit-id ID --step-id step:0 --signature 0x...` | `{"ok":true,"data":{"depositId":"ID","steps":[]}}` | +| `status` | `{"ok":true,"data":{"balance":{"accountValue":"100"},"agent":{"approved":true,"daysRemaining":"179"},"deposits":[],"policy":{"id":"...","version":"2"},"runner":{"pid":"123"}}}` | +| `balance` | `{"ok":true,"data":{"accountValue":"100","accountValueUnits":"10000000000","withdrawable":"96","marginUsed":"4"}}` | +| `market --coin BTC` | `{"ok":true,"data":{"coin":"BTC","asset":"0","szDecimals":"5","maxLeverage":"40","mid":"20000"}}` | +| `positions` | `{"ok":true,"data":[{"coin":"BTC","size":"0.001","entryPrice":"20000","leverage":"5","marginMode":"cross"}]}` | +| `orders` | `{"ok":true,"data":[{"coin":"BTC","oid":"123","sz":"0.001","limitPx":"20000","side":"B"}]}` | +| `order --json '{"coin":"BTC","side":"buy","size":"0.001","price":"20000","tif":"Gtc","cloid":"0x11111111111111111111111111111111"}'` | `{"ok":true,"data":{"result":{"status":"ok","response":{"data":{"statuses":[{"resting":{"oid":123}}]}}},"builderFeeRate":"0.01%","builderAddress":"0x71b09A08257078A4d642F7Dd7315e656c837329a","cloid":"0x..."}}` | +| `order --json '{"coin":"BTC","side":"sell","size":"0.001","type":"market","slippage":0.01,"reduceOnly":true}'` | `{"ok":true,"data":{"result":{"status":"ok","response":{"data":{"statuses":[{"filled":{"oid":123}}]}}}}}` | +| `cancel --oid 123` or `cancel --all` | `{"ok":true,"data":{"cancelled":["123"],"result":{"status":"ok"}}}` | +| `modify --json '{"modifications":[{"oid":123,"price":"20100","size":"0.001"}]}'` | `{"ok":true,"data":{"status":"ok"}}` | +| `leverage --coin BTC --leverage 5 --mode isolated` | `{"ok":true,"data":{"status":"ok"}}` | +| `run --detach` | `{"ok":true,"data":{"pid":123,"startedAt":1700000000000,"network":"testnet"}}` | +| `events --since 12 --types '["fill"]'` | `{"ok":true,"data":[{"seq":13,"timestamp":"2026-01-01T00:00:00.000Z","type":"fill","data":{"tid":1}}]}` | +| `events --follow` | `{"seq":13,"timestamp":"2026-01-01T00:00:00.000Z","type":"fill","data":{"tid":1}}` (one line per event) | +| `logs --follow --level warn` | `{"level":"warn","requestId":"...","message":"Risk rejected","time":"..."}` (JSON lines) | +| `rotate` then `rotate --signature 0x...` | `{"ok":true,"data":{"oldAgent":"0x...","newAgent":"0x...","swapped":true,"oldRevoked":false,"revokeApproval":{}}}` | +| `rotate --revoke-signature 0x...` | `{"ok":true,"data":{"swapped":true,"oldRevoked":true}}` | +| `revoke --cancel-orders` | `{"ok":true,"data":{"locallyRevoked":true,"exchangeRevoked":false,"requiresSignature":true,"approval":{}}}` | +| `revoke --signature 0x...` | `{"ok":true,"data":{"locallyRevoked":true,"exchangeRevoked":true}}` | +| `audit --intent ID` | `{"ok":true,"data":[{"intentId":"ID","stage":"decision","data":{"allowed":true},"previousHash":"...","hash":"..."}]}` | +| `skill install --target codex` | `{"ok":true,"data":{"target":"codex","path":".../agent-cli/SKILL.md","version":"0.2.0"}}` | + +Before an order, query `market --coin BTC` for `mid`, `szDecimals`, asset and +market leverage ceiling. Compute size/notional with decimal strings/bigints, +using the user-approved limit or the bounded IOC execution price. A valid schema +alone does not establish that an order is tradable or within the budget. + +Use `--intent-id ID --intent "reason"` on trading actions to connect the user's +reasoning to the audit. Keep cloid stable when retrying a known order intent. +For TP/SL use `type:"tp"|"sl"` and `triggerPrice` with size/price/reduceOnly; +market orders use an explicit slippage-bounded IOC, not an unbounded market order. + +## Error recovery + +| Code | Action | +| --- | --- | +| RATE_LIMIT, NETWORK_ERROR | At most 3 read retries with exponential backoff; for uncertain mutations, reconcile order state/cloid before retrying. Stop and report unresolved results | +| NONCE_COLLISION | Retry using a newly issued nonce; inspect a stale lock if persistent | +| DEPOSIT_PENDING | Poll status until settled; stop on terminal funding failure | +| RUNNER_NOT_RUNNING | Start runner or use configured direct fallback | +| INSUFFICIENT_MARGIN | Reduce exposure or request authorized funding | +| POLICY_REJECTED | Stop and revise intent within limits; do not bypass policy | +| AGENT_EXPIRED | Rotate with master-wallet approval | +| AGENT_NOT_APPROVED | Complete init/approval or explain revocation | +| INVALID_SIZE, INVALID_PRICE | Correct input using market precision | +| UNKNOWN_MARKET | Refresh metadata; use an allowed market | +| ORDER_NOT_FOUND | Refresh orders before changing intent | +| CONFIG_INVALID | Correct configuration/input; never insert secrets | +| UPSTREAM_ERROR, INTERNAL | Stop, inspect redacted details/logs and report failure | + +## Gotchas + +HyperCore perps USDC uses **8 decimals**; EVM USDC uses **6**. Preserve strings and +bigints. Some deposits require **two distinct signatures**; resume stored steps +rather than restarting the route. Agent expiry is at most **180 days**. Never +ask for, paste, log or import a master private key or seed. The managed agent is +trade-only; local limits include markets, size/notional, leverage, daily loss and +open orders. Privy sees an L1 action hash, so it cannot independently inspect +notional/action contents; Hyperliquid agent permissions and local checks enforce +those boundaries. State is always `~/.bloxwap`, env prefix `BLOXWAP_`. + + +## Verification boundaries + +Offline skill tests validate the documented command examples against CLI schemas. +Mocked lifecycle tests exercise resumable approvals, pending funding, events +and failure handling. These checks do **not** prove a live agent or +exchange run. The nightly testnet lifecycle uses only a company-owned disposable +CI master account and separate test Privy app, real Hyperliquid testnet and Privy, +and an explicitly mocked Relay route backed by already funded test collateral. +It never transfers real funds. Missing CI credentials fail the job; a skipped or +mocked test must never be reported as live validation. Record the workflow run, +commit, lifecycle report, event and audit artifacts before claiming a testnet pass. +See `docs/agent-workflow.md` and `docs/testnet.md` in the repository for setup. diff --git a/apps/docs/public/llms.txt b/apps/docs/public/llms.txt index 652aac2..23a5f7c 100644 --- a/apps/docs/public/llms.txt +++ b/apps/docs/public/llms.txt @@ -1,6 +1,6 @@ # Bloxwap Agent CLI -Hyperliquid trading with Privy custody and Relay funding. Master keys stay external. +Hyperliquid trading with user-owned Privy apps and Relay funding. Master keys stay external. - [Overview and installation](https://bloxwap.github.io/docs/) - [quickstart](https://bloxwap.github.io/docs/quickstart/) @@ -15,6 +15,7 @@ Hyperliquid trading with Privy custody and Relay funding. Master keys stay exter - [skill](https://bloxwap.github.io/docs/skill/) - [audit](https://bloxwap.github.io/docs/audit/) +- [SKILL.md — agent instructions](https://bloxwap.github.io/SKILL.md) - [Sitemap](https://bloxwap.github.io/sitemap.xml) - [Static search index](https://bloxwap.github.io/search.json) -- [Source and packaged SKILL.md](https://github.com/bloxwap/agent-cli/tree/main/packages/agent-cli) +- [SKILL.md source](https://github.com/bloxwap/agent-cli/blob/main/packages/agent-cli/SKILL.md) diff --git a/apps/docs/scripts/check-export.mjs b/apps/docs/scripts/check-export.mjs index 4597c40..ba29da1 100644 --- a/apps/docs/scripts/check-export.mjs +++ b/apps/docs/scripts/check-export.mjs @@ -14,13 +14,20 @@ async function walk(path) { } for (const required of [ 'index.html', '404.html', 'docs/index.html', 'search.json', 'icon.svg', - 'llms.txt', 'robots.txt', '404.md', + 'llms.txt', 'robots.txt', '404.md', 'SKILL.md', ]) { if (!await exists(join(root, required))) failures.push(`Missing ${required}`); } // Agent-readiness contract: every machine-readable file the site advertises must exist // and behave the way llms.txt and the 404 page claim it does. +const skillSource = await readFile(new URL('../../../packages/agent-cli/SKILL.md', import.meta.url)); +if (await exists(join(root, 'SKILL.md')) && !(await readFile(join(root, 'SKILL.md'))).equals(skillSource)) { + failures.push('SKILL.md must exactly match the packaged skill, including its frontmatter'); +} +const llms = await readFile(join(root, 'llms.txt'), 'utf8'); +const skillUrl = new URL(`${basePath}/SKILL.md`, siteUrl).href; +if (!llms.includes(`](${skillUrl})`)) failures.push(`llms.txt: missing canonical skill link ${skillUrl}`); const robots = await readFile(join(root, 'robots.txt'), 'utf8'); if (!/^sitemap:\s*https:\/\/\S+\/sitemap\.xml\s*$/im.test(robots)) failures.push('robots.txt: missing absolute Sitemap directive'); const notFoundMarkdown = await readFile(join(root, '404.md'), 'utf8'); diff --git a/apps/docs/scripts/generate-content.mjs b/apps/docs/scripts/generate-content.mjs index 755830c..10fa94d 100644 --- a/apps/docs/scripts/generate-content.mjs +++ b/apps/docs/scripts/generate-content.mjs @@ -14,11 +14,16 @@ for (const command of invoke('--help').commands) { } const skillSource = readFileSync(new URL('../../../packages/agent-cli/SKILL.md', import.meta.url), 'utf8'); const skill = '---\ntitle: Agent skill guide\ndescription: Install and use the packaged SKILL.md with Codex or Claude Code.\n---\n\n' + + 'import Link from \'next/link\';\n\n' + + 'Download SKILL.md as plain Markdown, including the skill frontmatter.\n\n' + 'The npm package ships `SKILL.md` and embeds the same content in the CLI. Install it with:\n\n```sh\nagent-cli skill install --target codex\nagent-cli skill install --target claude\n```\n\nCodex uses `$CODEX_HOME/skills/agent-cli/SKILL.md` (default `~/.codex/skills/agent-cli/SKILL.md`). Claude Code uses `~/.claude/skills/agent-cli/SKILL.md`. Reinstalling identical content is idempotent; replacing edited content requires `--force`.\n\nThe following guide comes directly from the packaged skill.\n\n' + skillSource.replace(/^---\n[\s\S]*?\n---\n/, ''); -for (const [name, text] of [['commands.mdx', commands], ['skill.mdx', skill]]) { - const target = new URL(name, pages); +for (const [name, target, text] of [ + ['commands.mdx', new URL('commands.mdx', pages), commands], + ['skill.mdx', new URL('skill.mdx', pages), skill], + ['public/SKILL.md', new URL('../public/SKILL.md', import.meta.url), skillSource], +]) { if (process.argv.includes('--check')) { if (readFileSync(target, 'utf8') !== text) throw new Error(`${name} is stale: run npm run docs:generate`); } else writeFileSync(target, text); } -console.log('Command schemas and packaged skill documentation synchronized.'); +console.log('Command schemas, skill guide and public SKILL.md synchronized.'); diff --git a/apps/docs/scripts/prepare-public.mjs b/apps/docs/scripts/prepare-public.mjs index 979480b..c00a7df 100644 --- a/apps/docs/scripts/prepare-public.mjs +++ b/apps/docs/scripts/prepare-public.mjs @@ -4,6 +4,6 @@ const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? 'https://bloxwap.github.io'; const absolute = (path) => new URL(`${basePath}${path}`, siteUrl).href; const pages = JSON.parse(readFileSync(new URL('../content/docs/meta.json', import.meta.url), 'utf8')).pages; writeFileSync(new URL('../public/robots.txt', import.meta.url), `User-agent: *\nAllow: /\nSitemap: ${absolute('/sitemap.xml')}\n`); -writeFileSync(new URL('../public/llms.txt', import.meta.url), '# Bloxwap Agent CLI\n\nHyperliquid trading with Privy custody and Relay funding. Master keys stay external.\n\n' + +writeFileSync(new URL('../public/llms.txt', import.meta.url), '# Bloxwap Agent CLI\n\nHyperliquid trading with user-owned Privy apps and Relay funding. Master keys stay external.\n\n' + pages.map((page) => `- [${page === 'index' ? 'Overview and installation' : page}](${absolute(page === 'index' ? '/docs/' : `/docs/${page}/`)})`).join('\n') + - `\n\n- [Sitemap](${absolute('/sitemap.xml')})\n- [Static search index](${absolute('/search.json')})\n- [Source and packaged SKILL.md](https://github.com/bloxwap/agent-cli/tree/main/packages/agent-cli)\n`); + `\n\n- [SKILL.md — agent instructions](${absolute('/SKILL.md')})\n- [Sitemap](${absolute('/sitemap.xml')})\n- [Static search index](${absolute('/search.json')})\n- [SKILL.md source](https://github.com/bloxwap/agent-cli/blob/main/packages/agent-cli/SKILL.md)\n`); diff --git a/apps/docs/scripts/preview.mjs b/apps/docs/scripts/preview.mjs index 1948615..8b88ab5 100644 --- a/apps/docs/scripts/preview.mjs +++ b/apps/docs/scripts/preview.mjs @@ -7,7 +7,7 @@ const root = fileURLToPath(new URL('../out/', import.meta.url)).replace(/\/$/, ' let basePath; try { ({ basePath } = JSON.parse(await readFile(join(root, '_docs-config.json'), 'utf8'))); } catch { throw new Error('Build the docs first with npm run docs:build.'); } -const mime = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.woff2': 'font/woff2', '.txt': 'text/plain; charset=utf-8', '.xml': 'application/xml' }; +const mime = { '.html': 'text/html; charset=utf-8', '.md': 'text/markdown; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.woff2': 'font/woff2', '.txt': 'text/plain; charset=utf-8', '.xml': 'application/xml' }; const server = createServer(async (request, response) => { try { const path = decodeURIComponent(new URL(request.url ?? '/', 'http://localhost').pathname); diff --git a/apps/docs/scripts/smoke.mjs b/apps/docs/scripts/smoke.mjs index 5909b03..ccdff0c 100644 --- a/apps/docs/scripts/smoke.mjs +++ b/apps/docs/scripts/smoke.mjs @@ -43,6 +43,22 @@ try { const errors = []; page.on('pageerror', (error) => errors.push(error.message)); + await check('canonical SKILL.md serves the packaged Markdown and is discoverable', async () => { + const response = await desktop.request.get(url('/SKILL.md')); + assert.equal(response.status(), 200); + assert.match(response.headers()['content-type'], /^text\/markdown\b/); + assert.deepEqual(await response.body(), await readFile(new URL('../../../packages/agent-cli/SKILL.md', import.meta.url))); + const llms = await desktop.request.get(url('/llms.txt')); + assert.equal(llms.status(), 200); + assert.ok((await llms.text()).includes(`${basePath}/SKILL.md`)); + await page.goto(url('/docs/skill/')); + const link = page.getByRole('link', { name: 'Download SKILL.md', exact: true }); + assert.equal(await link.getAttribute('href'), `${basePath}/SKILL.md`); + const download = page.waitForEvent('download'); + await link.click(); + assert.equal((await download).suggestedFilename(), 'SKILL.md'); + }); + await check('home renders with the base path and no runtime errors', async () => { const response = await page.goto(url('/')); assert.equal(response?.status(), 200); diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 9a590d4..e7142d7 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -39,7 +39,7 @@ | #34 | done | Optional cancel-all, resumable master revocation payload, immediate local blocking and config/session clearing after exchange submission. | | #35 | done | Cross-process locked fsynced hash-chain audit, intent/decision/signature digest/response/fill linkage, rotation/revoke records and filters; tamper/redaction tests. | | #36 | done | Pre-schema recursive CLI/RPC secret detector, BIP39-like seed rejection, private authorization refs and env/log/error scrubbing; threat model docs. | -| #37 | partial | User-owned Privy guidance, resumable approval/funding stages, exchange reconciliation and complete schema-validated command examples/error table. Offline forward tests pass; an actual coding-agent testnet session still needs dedicated QA credentials and evidence. | +| #37 | partial | User-owned Privy guidance, resumable approval/funding stages, exchange reconciliation and complete schema-validated command examples/error table. Canonical /SKILL.md is generated from the packaged source, linked from llms.txt/the skill guide and checked for byte parity. Offline tests pass; an actual coding-agent testnet session still needs dedicated QA credentials and evidence. | | #38 | done | Install/testnet README, custody architecture, schema-generated command reference and static docs site with gated Pages workflow. | | #39 | partial | Full mocked CLI/SDK/RPC lifecycle and fail-closed testnet harness, with builder approval, exchange outcome reconciliation, runner/events, rotation/revoke and audit assertions. Nightly live execution and journals need a disposable funded QA account and QA Privy app; missing credentials cannot count as a pass. | | #40 | partial | Script-free tarball global install and actual installed bunx help/schema smoke pass locally; executable/skill/license checks and Node 22.12/24 macOS/Linux CI. Public registry installation awaits publication. |