You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: AGENTS.md
+37-6Lines changed: 37 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,24 +19,55 @@ Read `CLAUDE.md` alongside this file when you need the full rationale for repo-s
19
19
20
20
## Repository Conventions
21
21
22
-
- Tests live next to the source as `*.test.ts`.
23
-
- Every user-facing PR needs a changeset in `.changeset/*.md`.
22
+
- Tests live next to the source as `*.test.ts`. `vitest.config.ts` only picks up `.test.ts`; update it before adding `.tsx` tests.
23
+
- Pure logic embedded in `.tsx` components should be lifted into sibling `.ts` files so tests can import it without pulling React and Ink into the runner.
24
+
- Every user-facing PR needs a changeset in `.changeset/*.md`. Refactors, tests, and internal tooling-only changes can skip one.
24
25
- Do not add AI/tool attribution, signatures, or provenance to commits, PRs, generated files, or repo content.
25
26
- Do not commit design docs, brainstorming notes, context dumps, or scratch files.
26
27
- Do not mock `polkadot-api` primitives such as `Enum` or encoders in tests.
27
-
- Long-lived resources such as session signers and chain clients must be explicitly destroyed in cleanups.
28
+
- Long-lived resources such as `TerminalAdapter`, session signers, and chain clients must be explicitly destroyed in cleanups.
28
29
29
-
## Architecture Boundaries
30
+
## Dependency Rules
30
31
31
-
- All chain URLs and contract addresses belong in `src/config.ts`.
32
-
-`src/bootstrap.ts` is the first import and owns the ambient Sentry handoff to `bulletin-deploy`. Keep `DOT_TELEMETRY` as the privacy gate for both CLI telemetry and `BULLETIN_DEPLOY_TELEMETRY`.
32
+
- Do not upgrade `polkadot-api` or `@polkadot-api/sdk-ink` past the current pins unless `@polkadot-apps/chain-client` is bumped and compatibility is verified.
33
33
- Keep `bulletin-deploy` pinned to an explicit version. Do not switch it to `latest`.
34
+
- When upgrading `bulletin-deploy`, check public API changes for `deploy()`, DotNS methods, `DeployOptions`, `jsMerkle`, signer options, RPC handling, and attributes.
35
+
36
+
## Architecture Boundaries
37
+
38
+
- All chain URLs and contract addresses belong in `src/config.ts`. Do not inline websocket URLs or `0x...` addresses elsewhere.
-`src/utils/deploy/*` and `src/utils/build/*` are SDK-facing surfaces for WebContainer consumers and must not import React or Ink.
35
41
- CLI/TUI code lives under `src/commands/*`.
42
+
-`src/bootstrap.ts` is the first import in `src/index.ts` and owns the ambient Sentry handoff to `bulletin-deploy`. Keep `DOT_TELEMETRY` as the privacy gate for both CLI telemetry and `BULLETIN_DEPLOY_TELEMETRY`.
36
43
- The Bun compiled-binary stdin warm-up in `src/index.ts` is intentional. Do not remove it until Bun compiled TTY stdin works reliably with Ink.
37
44
45
+
## Deploy And Storage Invariants
46
+
47
+
- Deploy delegates storage hardening to `bulletin-deploy`: chunking, retries, pool accounts, nonce fallback, DAG-PB, and DotNS commit-reveal stay there.
48
+
- The CLI owns `registry.publish()` because the registry contract must record the user as `env::caller()`.
49
+
- Do not call `bulletin-deploy.deploy()` just to store playground metadata JSON. Use `@polkadot-apps/bulletin::upload()` so it submits `TransactionStorage.store` directly and returns the CID.
50
+
- Metadata uploads need a dedicated Bulletin client with `heartbeatTimeout: 300_000`, destroyed immediately after upload.
51
+
-`dot deploy` currently relies on the Kubo binary path and must not pass `jsMerkle: true` until bulletin-deploy's pure-JS merkleizer preserves DAG-PB directory/file blocks correctly.
52
+
-`dot init` installs `ipfs`, so deploys can rely on the Kubo CLI after setup.
53
+
38
54
## Runtime Safety
39
55
56
+
-`getSessionSigner()` returns an adapter that keeps the Node event loop alive. Every caller must call the returned `destroy()`.
40
57
- New long-running commands should register cleanup through `onProcessShutdown()` and use the process guard where appropriate.
41
58
-`startMemoryWatchdog()` runs for both `dot deploy` and `dot mod`; add it to new top-level commands that do meaningful I/O.
42
59
- TUI info updates must be throttled or coalesced. Do not stream raw high-volume logs directly into Ink state.
60
+
-`DeployLogParser.feed()` must not emit catch-all events per log line. Keep events limited to meaningful phase/progress matches.
61
+
62
+
## Mod And GitHub Behavior
63
+
64
+
-`dot mod` is GitHub-tarball-only. Do not reintroduce `git clone`, `gh repo fork`, or tooling requirements for the public-repo path.
65
+
-`ensureGhAuthed()` must not shell out to `gh auth login` from inside Ink. Fail with the existing actionable message instead.
66
+
-`metadata.repository` is written only when `--modable` is explicitly opted in.
67
+
- When `--modable` needs a repository, deploy either pushes to existing `origin` or creates a public repo with `gh repo create --public --push`; failed pushes must fail the deploy.
68
+
69
+
## Sentry Telemetry
70
+
71
+
- Helpers in `src/telemetry.ts`, `src/utils/deploy/phase.ts`, `src/cli-runtime.ts`. Every command wraps with `runCliCommand`; every deploy phase wraps with `withDeployPhase`. Don't reimplement the boilerplate.
72
+
- Dashboards are JSON snapshots in `sentry/dashboards/`. Run `./sentry/backup-dashboards.sh` before any change. Use `./sentry/patch-dashboard.py` and `./sentry/create-dashboard.py` for edits and new dashboards.
73
+
- E2E test runs are tagged `cli.tag:e2e-ci` (CI) or `cli.tag:e2e-local` (helper default). Production widgets exclude `cli.tag:e2e-*`; the E2E Health dashboard (id 2216096) targets it.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+36Lines changed: 36 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,41 @@
1
1
# playground-cli
2
2
3
+
## 0.15.4
4
+
5
+
### Patch Changes
6
+
7
+
- 7151157: Avoid GitHub auth and `git push` during `dot deploy --modable` when the project already has an `origin`; the existing repository URL is recorded directly.
8
+
9
+
## 0.15.3
10
+
11
+
### Patch Changes
12
+
13
+
- 9d0a0ba: Load the logged-in account for `dot deploy --signer dev --playground` so Playground registry publishes can be signed by the app owner.
14
+
15
+
## 0.15.2
16
+
17
+
### Patch Changes
18
+
19
+
- f2f43c4: Suppress the non-fatal `ReviveApi_trace_call` compatibility stack during Playground registry contract dry-runs.
20
+
21
+
## 0.15.1
22
+
23
+
### Patch Changes
24
+
25
+
- 2de1408: Resolve the Playground registry address from the live CDM meta-registry before publishing or browsing apps.
26
+
27
+
## 0.15.0
28
+
29
+
### Minor Changes
30
+
31
+
- 86abd07: `dot --suri` now accepts a BIP-39 mnemonic in addition to the dev names (Alice, Bob, Charlie, Dave, Eve, Ferdie). An optional `//<path>` derivation suffix is supported, e.g. `dot deploy --suri "<12-word phrase>//0"`. The dev-name fast path is unchanged.
32
+
33
+
### Patch Changes
34
+
35
+
- 934c0db: Add E2E integration test suite covering install, build, init, session, deploy, mod, and diagnostic commands. Tests spawn the CLI as a child process via execa and assert on stdout/stderr/exit codes. Deploy tests verify contract detection for Foundry, Hardhat, and CDM backends. Includes CI workflow, fixture projects, and chain query helpers for Paseo testnet validation.
36
+
- 21481ba: Fix `dot deploy` exiting 1 after a successful deploy. polkadot-api's `client.destroy()` can fire a `DisjointError: ChainHead disjointed` from a still-in-flight chainHead operation after the WS has closed, which surfaces as an unhandled rejection and forced the process to exit 1 even though the deploy completed and printed "Deploy complete". Now suppressed alongside the existing benign-teardown filter for `UnsubscriptionError: Not connected`.
37
+
- ba63fec: Fix `dot deploy` and `dot mod` exiting 0 on failures. Previously the CLI's entry point unconditionally called `process.exit(0)` after the action returned, overwriting the non-zero `process.exitCode` set by `scheduleHardExit()` (deploy preflight, e.g. `SignerNotAvailableError` from a corrupt session) and never set by `dot mod` at all on `runSetup` failures (e.g. registry miss). Both paths now propagate a non-zero exit code so shell scripts and CI pipelines can rely on the result.
Copy file name to clipboardExpand all lines: CLAUDE.md
+16-2Lines changed: 16 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -24,8 +24,8 @@ These are things that aren't self-evident from reading the code and have bitten
24
24
- **Process-guard safety net** (`src/utils/process-guard.ts`) — deploy pipelines open several long-lived WebSockets + child processes and any one of them can keep the event loop alive after the TUI visibly finishes, turning `dot` into a zombie that accumulates retry buffers indefinitely (seen climbing past 25 GB). We defend in depth: (1) `installSignalHandlers()` catches SIGINT/TERM/HUP + `unhandledRejection` and forces cleanup + exit within 3 s; (2) `scheduleHardExit()` installs an `unref`'d timer that kills the process if the event loop doesn't drain within a grace period; (3) `startMemoryWatchdog()` aborts if RSS exceeds 4 GB — a generous cap because legit deploys on Bun SEA binaries routinely touch 1–1.5 GB from runtime-metadata decoding + Bun's JSC heap + Ink yoga. Do NOT re-add a per-window growth detector: we tried 300 MB / 3 s and it false-positived on the single-burst metadata-loading spike, aborting deploys that would have succeeded. Set `DOT_MEMORY_TRACE=1` to stream per-sample RSS/heap/external stats — useful when diagnosing a real leak report. **Telemetry bootstrap** (`src/bootstrap.ts`) is the FIRST import in `src/index.ts`. It sets `BULLETIN_DEPLOY_USE_AMBIENT_SENTRY=1` and `BULLETIN_DEPLOY_HOST_APP=playground-cli` before `bulletin-deploy` can evaluate, then maps `DOT_TELEMETRY`/internal-context detection to `BULLETIN_DEPLOY_TELEMETRY`. Do not leave `BULLETIN_DEPLOY_TELEMETRY` unset while setting the host app: `bulletin-deploy@0.7.6` treats `playground-cli` as an internal host, which would enable deploy telemetry for external users. `BULLETIN_DEPLOY_MEM_REPORT` is not forced off by default anymore because upstream guards the Bun-incompatible memory-report path. Any new long-running command should register a cleanup hook via `onProcessShutdown()`.
25
25
-**Parser MUST NOT emit an event per log line.**`DeployLogParser.feed()` is called for every console line bulletin-deploy prints — hundreds per deploy on the happy path, thousands if retries fire. We intentionally emit events ONLY for phase-banner matches and `[N/M]` chunk progress. Everything else returns `null`. Adding a catch-all `info` emit turns the parser into a firehose that allocates ~200 bytes × thousands of lines, and was a measurable contributor to chunk-upload memory pressure.
26
26
-**`dot mod` is GitHub-tarball-only and must stay that way.**`src/utils/mod/source.ts` downloads from `codeload.github.com` (no auth, no `git`/`gh` required for the public-repo case) and extracts via `node:zlib` + the pure-JS `tar` package. Do NOT re-introduce `git clone` or `gh repo fork` paths — both would re-add a hard tooling requirement and the fork path was specifically removed because GitHub caps you to one fork per source-repo per account, which broke "mod the same starter twice." A non-modable app (no `metadata.repository`) returns a hard error from `dot mod`; the interactive picker filters those out so the user never sees an unmoddable option.
27
-
-**`ensureGhAuthed()` does NOT shell out to `gh auth login`.** Even from the interactive deploy, Ink owns stdout + raw-mode stdin and a `stdio: "inherit"` child would race Ink's `useInput` for keystrokes — producing garbled output and dropped key events. Both interactive and non-interactive paths fail with the same actionable message: run `gh auth login` once outside `dot`, then retry. Properly suspending Ink to hand off stdio is possible but requires unmounting + re-rendering with state preservation, and we deemed that complexity not worth it for a one-time-per-machine speedbump. If you ever do implement it, the wiring point is `ModablePreflightStage` in `src/commands/deploy/DeployScreen.tsx`.
28
-
-**`metadata.repository` is set ONLY when `--modable` is opted in.** Older code in `publishToPlayground` would silently probe `git remote get-url origin` and stuff whatever it found into the metadata, which surprised users who didn't realise their fork was being advertised. The new contract: `runDeploy` takes an explicit `repositoryUrl: string | null`, and `publishToPlayground` writes the field iff that param is non-null. The CLI command is responsible for resolving the URL upstream via `src/utils/deploy/modable.ts::resolveRepositoryUrl()`, which either pushes to existing `origin`(failing the deploy on push error — stale URLs would mislead modders) or runs `gh repo create --public --push` to set up a fresh public repo. Re-deploys never delete user repos.
27
+
-**`ensureGhAuthed()` does NOT shell out to `gh auth login`.** Even from the interactive deploy, Ink owns stdout + raw-mode stdin and a `stdio: "inherit"` child would race Ink's `useInput` for keystrokes — producing garbled output and dropped key events. Both interactive and non-interactive paths that need GitHub repo creation fail with the same actionable message: run `gh auth login` once outside `dot`, then retry. Existing `origin` URLs do not require `gh` auth. Properly suspending Ink to hand off stdio is possible but requires unmounting + re-rendering with state preservation, and we deemed that complexity not worth it for a one-time-per-machine speedbump. If you ever do implement it, the wiring point is `ModablePreflightStage` in `src/commands/deploy/DeployScreen.tsx`.
28
+
-**`metadata.repository` is set ONLY when `--modable` is opted in.** Older code in `publishToPlayground` would silently probe `git remote get-url origin` and stuff whatever it found into the metadata, which surprised users who didn't realise their fork was being advertised. The new contract: `runDeploy` takes an explicit `repositoryUrl: string | null`, and `publishToPlayground` writes the field iff that param is non-null. The CLI command is responsible for resolving the URL upstream via `src/utils/deploy/modable.ts::resolveRepositoryUrl()`, which uses an existing `origin`URL without pushing, or runs `gh repo create --public --push` to set up a fresh public repo when no origin exists. Re-deploys never delete user repos.
29
29
-**`startMemoryWatchdog()` runs for both `dot deploy` and `dot mod`.** Mod's tarball download is a streaming pipe through `node:zlib` + `tar.extract()`, and a stuck IPFS gateway or a malformed tarball can leak buffers. Same 4 GB cap, same worker-thread sampler. Any new top-level command that does meaningful I/O should also call `startMemoryWatchdog()` and register `stopWatchdog` via `onProcessShutdown()`.
30
30
31
31
## Repo conventions
@@ -37,3 +37,17 @@ These are things that aren't self-evident from reading the code and have bitten
37
37
- Do NOT commit design docs, brainstorming notes, or context dumps (e.g. `context.md`) to the repo. They belong in tickets or scratch files outside the tree.
38
38
- Don't mock primitives from `polkadot-api` (`Enum`, encoders) in tests — doing so turns intended coverage into tautology.
39
39
- Long-lived resources (`TerminalAdapter`, `PaseoClient`) have explicit `destroy()` / `destroyConnection()` — always release them, especially from React `useEffect` cleanups. The WebSocket keeps the event loop alive; forgetting a destroy manifests as `dot <cmd>` hanging after its work is visibly finished.
40
+
41
+
## Sentry telemetry
42
+
43
+
- DSN: `src/telemetry-config.ts::PLAYGROUND_SENTRY_DSN`. Region: EU (`https://de.sentry.io`).
44
+
- Org slug: `paritytech`. API token: macOS keychain service `sentry-api-token` (member of paritytech org with `org:read` + `org:write`).
45
+
- Attribute prefix: `cli.` (see `getCliRootAttributes` in `src/telemetry-config.ts`). Spec: `sentry-instrumentation-spec.md` at the repo root (untracked — keep there).
46
+
-**Helpers (don't reimplement):**`src/telemetry.ts` exports `withCommandTelemetry`, `withRootSpan`, `withSpan` (2-arg + 4-arg overloads), `captureWarning`, `captureException`, `errorMessage`, `sanitizedErrorMessage`. `src/utils/deploy/phase.ts` exports `withDeployPhase` for deploy-phase orchestration. `src/cli-runtime.ts` exports `runCliCommand` for the standard CLI scaffolding (telemetry + watchdog + hard-exit). Every command's `.action()` body should be one `runCliCommand(name, options, async () => { ... })` call — do not re-add try/finally + `scheduleHardExit` boilerplate.
47
+
-**Dashboards** live as JSON snapshots under `sentry/dashboards/<id>.json`:
-**Workflow:** run `./sentry/backup-dashboards.sh` BEFORE any change. Use `./sentry/patch-dashboard.py <id> <patch.json>` for surgical edits (supports `replace`, `patch_query`, `set_description` ops) or full widget replacement. Use `./sentry/create-dashboard.py <payload.json>` for new dashboards. Per spec §15f, do NOT include a `projects` field in POST payloads. Per spec §15g, PUT replaces the whole widget list — backup first.
52
+
-**E2E tagging:** every spawn from `e2e/cli/helpers/dot.ts` injects `DOT_TAG=e2e-local` (default) and `DOT_TELEMETRY=1`. CI sets `DOT_TAG=e2e-ci` in `.github/workflows/e2e.yml` so production health widgets filter cleanly via `!cli.tag:e2e-*`.
53
+
-**SAD% propagation** is verified by a regression test in `src/telemetry.test.ts` ("SAD% propagation through transaction envelope"). It confirms `captureWarning` flips `cli.sad="true"` on the root transaction. If that test fails, the SAD% dashboard widget on Dashboard 1 will silently degrade to a duplicate of the unexpected-failure rate.
0 commit comments