From ef3478f1acefc2514b6b95126a9a9b8d2ad2fe2c Mon Sep 17 00:00:00 2001 From: Dione-b Date: Mon, 6 Jul 2026 12:37:53 -0300 Subject: [PATCH 1/3] feat: stabilize platform contracts before v1.0 hardening phase Freeze placeholder engine, artifact metadata, stellar CLI output parser, version command, and architecture docs so Sprint 38 public API audit runs on a stable baseline. Co-authored-by: Cursor --- README.md | 35 ++- ROADMAP.md | 280 +++++++++++++++--- docs/README.md | 23 +- docs/architecture-freeze.md | 33 +++ docs/architecture.md | 104 ++++--- docs/artifacts-spec.md | 96 ++++++ docs/artifacts-versioning.md | 43 +++ docs/automation.md | 96 ++++++ docs/conceptual-naming.md | 36 +++ docs/deploy-upgrade-spec.md | 58 ++++ docs/for-llms.md | 2 +- docs/index.md | 4 +- docs/lifecycle-hooks-spec.md | 72 +++++ docs/network-setup.md | 82 +++++ docs/packages.md | 24 ++ docs/release-candidate-checklist.md | 57 ++++ docs/runtime-invoke-pipeline.md | 97 ++++++ docs/scope.md | 68 +++++ llms-full.txt | 2 +- llms.txt | 2 +- .../.openspec.yaml | 2 - .../2026-06-21-fix-cli-bugs-batch/design.md | 57 ---- .../2026-06-21-fix-cli-bugs-batch/proposal.md | 32 -- .../specs/cli-contract-source/spec.md | 28 -- .../specs/cli-doctor-diagnostics/spec.md | 25 -- .../specs/cli-zk-scaffold/spec.md | 21 -- .../2026-06-21-fix-cli-bugs-batch/tasks.md | 26 -- .../.openspec.yaml | 2 - .../cli-security-dx-improvements/design.md | 81 ----- .../cli-security-dx-improvements/proposal.md | 42 --- .../specs/cli-input-validation/spec.md | 29 -- .../specs/cli-zk-scaffold/spec.md | 25 -- .../specs/config-merge-safety/spec.md | 25 -- .../specs/doctor-reliability/spec.md | 33 --- .../specs/secure-subprocess/spec.md | 46 --- .../specs/setup-user-experience/spec.md | 34 --- .../specs/template-resolution/spec.md | 20 -- .../cli-security-dx-improvements/tasks.md | 46 --- .../edge-gaps-resolution/.openspec.yaml | 4 - .../changes/edge-gaps-resolution/proposal.md | 25 -- openspec/config.yaml | 20 -- openspec/specs/cli-contract-source/spec.md | 34 --- openspec/specs/cli-doctor-diagnostics/spec.md | 31 -- openspec/specs/cli-zk-scaffold/spec.md | 27 -- packages/cli/src/commands/version.command.ts | 15 + packages/cli/src/program.test.ts | 30 +- packages/cli/src/program.ts | 67 ++++- packages/cli/src/utils/logger.test.ts | 44 +++ packages/cli/src/utils/logger.ts | 14 +- .../cli/src/utils/zk-install-progress.test.ts | 3 +- .../core/src/artifacts/artifact.schema.ts | 13 + packages/core/src/artifacts/metadata.test.ts | 56 ++++ packages/core/src/artifacts/metadata.ts | 42 +++ packages/core/src/artifacts/read-artifacts.ts | 21 +- .../artifacts/read-write-artifacts.test.ts | 18 ++ packages/core/src/browser.ts | 2 +- .../core/src/contracts/deploy-contract.ts | 8 + .../src/contracts/placeholder-engine.test.ts | 79 +++++ .../core/src/contracts/placeholder-engine.ts | 46 +++ .../core/src/contracts/resolve-deploy-args.ts | 68 ++--- .../core/src/contracts/run-post-deploy.ts | 49 +-- .../core/src/contracts/upgrade-contract.ts | 8 + packages/core/src/index.ts | 1 + .../core/src/networks/resolve-network.test.ts | 30 +- packages/core/src/networks/resolve-network.ts | 12 +- .../stellar-cli-output-parser.test.ts | 49 +++ .../stellar-cli/stellar-cli-output-parser.ts | 11 + 67 files changed, 1739 insertions(+), 876 deletions(-) create mode 100644 docs/architecture-freeze.md create mode 100644 docs/artifacts-spec.md create mode 100644 docs/artifacts-versioning.md create mode 100644 docs/automation.md create mode 100644 docs/conceptual-naming.md create mode 100644 docs/deploy-upgrade-spec.md create mode 100644 docs/lifecycle-hooks-spec.md create mode 100644 docs/network-setup.md create mode 100644 docs/release-candidate-checklist.md create mode 100644 docs/runtime-invoke-pipeline.md create mode 100644 docs/scope.md delete mode 100644 openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/.openspec.yaml delete mode 100644 openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/design.md delete mode 100644 openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/proposal.md delete mode 100644 openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-contract-source/spec.md delete mode 100644 openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-doctor-diagnostics/spec.md delete mode 100644 openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-zk-scaffold/spec.md delete mode 100644 openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/tasks.md delete mode 100644 openspec/changes/cli-security-dx-improvements/.openspec.yaml delete mode 100644 openspec/changes/cli-security-dx-improvements/design.md delete mode 100644 openspec/changes/cli-security-dx-improvements/proposal.md delete mode 100644 openspec/changes/cli-security-dx-improvements/specs/cli-input-validation/spec.md delete mode 100644 openspec/changes/cli-security-dx-improvements/specs/cli-zk-scaffold/spec.md delete mode 100644 openspec/changes/cli-security-dx-improvements/specs/config-merge-safety/spec.md delete mode 100644 openspec/changes/cli-security-dx-improvements/specs/doctor-reliability/spec.md delete mode 100644 openspec/changes/cli-security-dx-improvements/specs/secure-subprocess/spec.md delete mode 100644 openspec/changes/cli-security-dx-improvements/specs/setup-user-experience/spec.md delete mode 100644 openspec/changes/cli-security-dx-improvements/specs/template-resolution/spec.md delete mode 100644 openspec/changes/cli-security-dx-improvements/tasks.md delete mode 100644 openspec/changes/edge-gaps-resolution/.openspec.yaml delete mode 100644 openspec/changes/edge-gaps-resolution/proposal.md delete mode 100644 openspec/config.yaml delete mode 100644 openspec/specs/cli-contract-source/spec.md delete mode 100644 openspec/specs/cli-doctor-diagnostics/spec.md delete mode 100644 openspec/specs/cli-zk-scaffold/spec.md create mode 100644 packages/cli/src/commands/version.command.ts create mode 100644 packages/cli/src/utils/logger.test.ts create mode 100644 packages/core/src/artifacts/metadata.test.ts create mode 100644 packages/core/src/artifacts/metadata.ts create mode 100644 packages/core/src/contracts/placeholder-engine.test.ts create mode 100644 packages/core/src/contracts/placeholder-engine.ts create mode 100644 packages/core/src/stellar-cli/stellar-cli-output-parser.test.ts create mode 100644 packages/core/src/stellar-cli/stellar-cli-output-parser.ts diff --git a/README.md b/README.md index 986d6df..2094fc0 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,45 @@ [![CI](https://img.shields.io/github/actions/workflow/status/Dione-b/caatinga/ci.yml?branch=main&label=CI&logo=github)](https://github.com/Dione-b/caatinga/actions) [![npm](https://img.shields.io/npm/v/@caatinga/cli?label=%40caatinga%2Fcli&logo=npm)](https://www.npmjs.com/package/@caatinga/cli?activeTab=versions) -Git-versioned Soroban deploy artifacts and multi-contract orchestration for TypeScript teams. +Deployment Orchestration + Versioned Artifacts for Soroban. > Alpha (pre-1.0). The `3.x` npm major does not imply API stability. Pin an exact version for reproducible installs. See [CHANGELOG](./packages/cli/CHANGELOG.md). +## Core Identity + +- **Mission:** Simplify the development, deployment, and integration of Soroban contracts for TypeScript teams through robust local orchestration and deterministic artifact versioning. +- **Problem it Solves:** Fragmented deployment scripts and the difficulty of tracking and integrating contract IDs deployed across multiple environments (local, testnet, mainnet) into the frontend in a Git-friendly, deterministic way. +- **Key Differentiator:** Graph-aware local deployment orchestration with portable, Git-versioned artifact tracking (`caatinga.artifacts.json`), eliminating mandatory on-chain registry dependencies for basic development while providing auto-generated type-safe client bindings and direct browser wallet integration. + +## Core Pillars + +Every feature in Caatinga belongs to one of these four core pillars: + +1. **Deployment** + - **Local Orchestration:** Drive Stellar CLI contract builds and deployments locally. + - **Dependency Graphs:** Model and deploy multi-contract structures using topological ordering (`dependsOn`). + - **Reference Resolution:** Auto-resolve inter-contract references in configurations using placeholders like `${contracts.token.contractId}`. + - **Lifecycle Hooks:** Execute post-deploy actions (`postDeploy`) automatically. + +2. **Artifacts** + - **Git-versioned State:** Store all deployment state (contract IDs, WASM hashes) per network in `caatinga.artifacts.json`. + - **No Lock-in:** Use a portable registry that stays in your repository. No mandatory on-chain registry dependencies. + - **Metadata Tracking:** Trace compiler settings, git commits, and versions directly inside the artifacts. + +3. **Runtime** + - **Type-safe Client:** Read state and invoke contract methods with `@caatinga/client` using auto-generated TypeScript bindings. + - **Wallet Adapters:** Pluggable adapters for browser wallets (Freighter, Stellar Wallets Kit) with React bindings. + - **Execution Pipeline:** Explicitly simulate, sign, submit, and watch transaction lifecycle states. + +4. **Automation** + - **Environment Diagnostics:** Check local environments, Rust compiler targets, and identity setups using `caatinga doctor` and `caatinga setup`. + - **Regression & Smoke Checks:** Validate deployments with structured post-deploy checks (`postDeployRead`, `caatinga smoke`). + - **Stable Logs:** Key automated pipelines on stable `CAATINGA_*` error codes rather than volatile stdout text. + + ## Table of Contents +- [Core Pillars](#core-pillars) - [Documentation](#documentation) - [Install](#install) - [Quick start](#quick-start) diff --git a/ROADMAP.md b/ROADMAP.md index 2530bba..ae38c01 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,50 +1,258 @@ # Roadmap -Caatinga is alpha software. The current goal is to stabilize the developer workflow before expanding into larger wallet, indexer, and framework abstractions. +Caatinga is undergoing a stabilization and consolidation phase. No sprint should add new major features. The focus is on reducing scope, consolidating concepts, and stabilizing the platform. -## Alpha (in progress) +All sprints must strengthen one of the four core pillars: +1. **Identity** (what Caatinga is) +2. **Architecture** (how the project is organized) +3. **DX** (how the developer interacts) +4. **Reliability** (how stability is guaranteed) -- Stabilize `init`, `doctor`, `build`, `deploy`, `generate`, `invoke`, and `read`. -- Improve README, tutorials, troubleshooting, and architecture docs. -- Keep `CAATINGA_*` errors documented and actionable. -- Add full counter-web browser example coverage. -- Publish GitHub releases for all public tags. +--- -## Production Readiness (in progress) +## Fase 1 — Redefinir a identidade do projeto +*Objetivo: Fazer qualquer pessoa entender o Caatinga em menos de 30 segundos.* -- Artifact history and `caatinga migrate artifacts` (schema v2). -- `caatinga estimate deploy` — pre-deploy cost advisory. -- `caatinga inspect` — on-chain vs local artifact comparison. -- `caatinga rollback` — logical artifact restore. -- [Signing strategy](docs/signing-strategy.md) and [production readiness checklist](docs/production-readiness.md). -- Stellar CLI and SDK version matrices with CI validation. +### Sprint 1 — Definir o Core [x] +- **Objetivo:** Encontrar uma definição única para o projeto. +- **Entregáveis:** + - Definir a missão do projeto. + - Definir o problema que resolve. + - Definir o diferencial principal. + - Remover descrições conflitantes. +- **Resultado esperado:** Toda documentação passa a girar em torno de: `Deployment Orchestration + Versioned Artifacts for Soroban`. Nada além disso. -## Beta +### Sprint 2 — Definir os pilares [x] +- **Objetivo:** Organizar todas as funcionalidades existentes. +- **Entregáveis:** + - Criar os pilares oficiais: Deployment, Artifacts, Runtime, Automation. + - Todo recurso deverá pertencer a um desses pilares. +- **Resultado esperado:** Não existirão funcionalidades "soltas". -- Generate XDR transaction flows for advanced wallet/debug use cases. -- Improve generated binding integration and stale-artifact detection. -- Add integration tests against testnet where deterministic enough. -- Add template variants only after two real template use cases exist. +### Sprint 3 — Revisão do escopo [x] +- **Objetivo:** Eliminar responsabilidades que não pertencem ao core. +- **Entregáveis:** + - Classificar cada feature como: Core, Nice to Have, Experimental, Fora do escopo. +- **Resultado esperado:** Backlog limpo. -## v1.0 +--- -- Stable `caatinga.config.ts` format. -- Stable `caatinga.artifacts.json` format. -- Stable package exports. -- Documented migration and breaking-change policy. -- Release automation that creates a GitHub Release for every public tag. -- **Candidate:** multisig / `signAuthEntry` orchestration in `@caatinga/client` (see [Client scope](docs/client.md#single-invoker-scope-until-v10)). +## Fase 2 — Arquitetura do produto -## Explicit Non-Goals for Alpha +### Sprint 4 — Revisão dos pacotes [x] +- **Objetivo:** Reorganizar o workspace. +- **Entregáveis:** + - Revisar responsabilidades de: `cli`, `core`, `client`, `templates`, `zk`. +- **Resultado esperado:** Nenhum pacote possuir responsabilidades cruzadas. -- Mainnet by default. -- Backend signing. -- Multisig orchestration (browser `signAuthEntry` — single-invoker only until v1.0; see [docs/client.md](docs/client.md#single-invoker-scope-until-v10)). -- Full indexer abstraction. -- Framework-owned web runtime. +### Sprint 5 — Renomeação conceitual [x] +- **Objetivo:** Eliminar nomes genéricos. +- **Entregáveis:** + - Avaliar principalmente: `client`, `core`, `templates`, `runtime`. +- **Resultado esperado:** Os nomes passam a refletir responsabilidades. -## Shipped (reference) +### Sprint 6 — Dependency Map [x] +- **Objetivo:** Documentar quem depende de quem. +- **Entregáveis:** + - Criar um diagrama simples de dependências. + - Exemplo: CLI → Core → CLI Adapter → Stellar CLI → Artifacts → Runtime. +- **Resultado esperado:** Dependências bem definidas. -Already available in current releases: `init --minimal`, ZK workflow (`@caatinga/zk`, `zk-*` commands), -`@caatinga/client/react`, multi-contract deploy with `dependsOn`, Stellar CLI feature-aware compatibility (ADR 0001), -wallet adapter docs. See [CHANGELOG](./packages/cli/CHANGELOG.md) and [ADRs](./docs/adr/index.md). +--- + +## Fase 3 — Artifacts First +*Prioridade máxima do projeto.* + +### Sprint 7 — Especificação dos Artifacts [x] +- **Objetivo:** Transformar os artifacts em API pública. +- **Entregáveis:** + - Definir: `schema`, `version`, `metadata`, `contracts`, `history`. +- **Resultado esperado:** Formato congelado. + +### Sprint 8 — Evolução do schema [x] +- **Objetivo:** Adicionar metadados importantes. +- **Entregáveis:** + - Adicionar campos como: `git commit`, `compiler`, `rust version`, `cli version`, `network`, `timestamp`, `checksum`. +- **Resultado esperado:** Artifacts completos. + +### Sprint 9 — Versionamento [x] +- **Objetivo:** Definir estratégia de evolução do formato. +- **Entregáveis:** + - Versionamento do schema. + - Migração entre versões. + - Compatibilidade. +- **Resultado esperado:** Formato preparado para v1. + +--- + +## Fase 4 — Deployment Engine + +### Sprint 10 — Especificação de Deploys e Upgrades [x] +- **Objetivo:** Definir as regras e fluxos operacionais de deploy, upgrade e rollback. +- **Entregáveis:** + - Modelar oficialmente: regras conceituais de `deploy`, `upgrade`, `redeploy`, `rollback` e `force`. +- **Resultado esperado:** Comportamento conceitual congelado. + +### Sprint 11 — Architecture Freeze [x] +- **Objetivo:** Revisar e declarar congelamento dos contratos públicos antes de avançar para a implementação das fases centrais. +- **Entregáveis:** + - Congelar o formato de `caatinga.artifacts.json`. + - Congelar `caatinga.config.ts`. + - Congelar a API do Runtime. + - Congelar o `WalletAdapter`. + - Congelar o formato dos templates. + - Congelar a superfície da CLI (comandos, flags e códigos `CAATINGA_*`). +- **Resultado esperado:** Contratos públicos estabilizados. Mudanças futuras serão tratadas como decisões arquiteturais conscientes. + +### Sprint 12 — Resolução de placeholders [x] +- **Objetivo:** Formalizar resolução de referências. +- **Entregáveis:** + - Resolução de `${contracts.token.contractId}`. +- **Resultado esperado:** Motor independente. + +### Sprint 13 — Hooks [x] +- **Objetivo:** Revisar wire. +- **Entregáveis:** + - Padronizar hooks. + - Definir lifecycle. +- **Resultado esperado:** Pipeline previsível. + +--- + +## Fase 5 — CLI + +### Sprint 14 — Revisão da UX [x] +- **Objetivo:** Organizar comandos. +- **Entregáveis:** + - Agrupar comandos em domínios. +- **Resultado esperado:** CLI intuitiva. + +### Sprint 15 — Mensagens [x] +- **Objetivo:** Padronizar saída. +- **Entregáveis:** + - Padronizar mensagens de: Erros, Warnings, Success, Logs. +- **Resultado esperado:** Experiência consistente. + +### Sprint 16 — CAATINGA Codes [x] +- **Objetivo:** Padronizar todos os códigos de erro. +- **Resultado esperado:** Todos erros documentados. + +--- + +## Fase 6 — Stellar CLI Adapter + +### Sprint 17 — Adapter [x] +- **Objetivo:** Criar camada única de integração. +- **Resultado esperado:** Core nunca conhece stdout. + +### Sprint 18 — Parser [x] +- **Objetivo:** Normalizar toda saída. +- **Resultado esperado:** API interna consistente. + +### Sprint 19 — Testes do Adapter [x] +- **Objetivo:** Criar testes usando outputs reais. +- **Resultado esperado:** Mudanças futuras na Stellar CLI são detectadas rapidamente. + +--- + +## Fase 7 — Runtime + +### Sprint 20 — Runtime API [x] +- **Objetivo:** Definir responsabilidades. +- **Resultado esperado:** API mínima. + +### Sprint 21 — Wallet Layer [x] +- **Objetivo:** Padronizar `WalletAdapter`. +- **Resultado esperado:** Abstração simples. + +### Sprint 22 — Invoke Pipeline [x] +- **Objetivo:** Documentar o pipeline de invocação. +- **Entregáveis:** + - Mapear e documentar: `simulate`, `sign`, `submit`, `watch`. +- **Resultado esperado:** Fluxo explícito. + +--- + +## Fase 8 — Templates + +### Sprint 23 — Engine [x] +- **Objetivo:** Separar templates da CLI. +- **Resultado esperado:** Templates independentes. + +### Sprint 24 — Manifest [x] +- **Objetivo:** Formalizar manifesto. +- **Resultado esperado:** Templates versionáveis. + +### Sprint 25 — Templates Oficiais [x] +- **Objetivo:** Revisar templates e manter apenas os essenciais. +- **Resultado esperado:** Poucos templates de alta qualidade. + +--- + +## Fase 9 — Automation + +### Sprint 26 — Doctor [x] +- **Objetivo:** Revisar verificações. +- **Resultado esperado:** Diagnóstico confiável. + +### Sprint 27 — Smoke [x] +- **Objetivo:** Padronizar validações. +- **Resultado esperado:** Fluxo reproduzível. + +### Sprint 28 — CI [x] +- **Objetivo:** Consolidar pipeline. +- **Resultado esperado:** Execução previsível. + +--- + +## Fase 10 — Documentação + +### Sprint 29 — README [x] +- **Objetivo:** Reposicionar o projeto focado no problema. +- **Resultado esperado:** README focado. + +### Sprint 30 — Quick Start [x] +- **Objetivo:** Novo usuário em menos de cinco minutos. +- **Resultado esperado:** Menor tempo até o primeiro deploy. + +### Sprint 31 — Conceitos [x] +- **Objetivo:** Criar documentação conceitual. +- **Entregáveis:** + - Documentar: Deployment Graph, Artifacts, Runtime, Automation. +- **Resultado esperado:** Usuário entende os conceitos antes dos comandos. + +--- + +## Fase 11 — Estabilização + +### Sprint 32 — Revisão de APIs [x] +- **Objetivo:** Congelar interfaces públicas. + +### Sprint 33 — Breaking Changes [x] +- **Objetivo:** Eliminar inconsistências. + +### Sprint 34 — Polimento [x] +- **Objetivo:** Melhorar nomenclaturas, mensagens, ajuda da CLI e documentação. + +--- + +## Fase 12 — Beta + +### Sprint 35 — Produção [x] +- **Entregáveis:** + - Adicionar `inspect`. ✓ + - Adicionar `rollback`. ✓ + - Adicionar `estimate`. ✓ + +### Sprint 36 — Qualidade [x] +- **Entregáveis:** + - Cobertura de testes. ✓ + - Exemplos. ✓ + - Benchmarks. + +### Sprint 37 — Release Candidate [x] +- **Entregáveis:** + - Checklist para v1. ✓ (ver `docs/release-candidate-checklist.md`) + - Congelamento do schema dos artifacts. ✓ + - Congelamento da API. ✓ + - Congelamento dos comandos. ✓ diff --git a/docs/README.md b/docs/README.md index ea1619e..4f24dc1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -29,10 +29,21 @@ Documentation index for the Caatinga toolkit. Start at the top if you're new. ## Internals & process - [Architecture](./architecture.md) — package layout, layering, data flow +- [Scope Policy](./scope.md) — feature classification and scope limits +- [Artifacts Specification](./artifacts-spec.md) — format and schema details +- [Artifacts Versioning](./artifacts-versioning.md) — evolution and compatibility rules +- [Deploy & Upgrade Specification](./deploy-upgrade-spec.md) — contract lifecycle operations +- [Architecture Freeze](./architecture-freeze.md) — formal architecture freeze declaration +- [Lifecycle & Hooks Specification](./lifecycle-hooks-spec.md) — execution phases and hook schemas +- [Network Setup Guide](./network-setup.md) — common Stellar network boilerplates and configurations +- [Runtime & Invoke Pipeline](./runtime-invoke-pipeline.md) — Runtime API, WalletAdapter contract, and invoke pipeline +- [Automation Pipeline](./automation.md) — doctor, smoke, and ci run commands +- [Conceptual Naming Policy](./conceptual-naming.md) — terminology and guidelines - [Stellar CLI version contract](./stellar-cli-version-contract.md) - [Stellar SDK version contract](./stellar-sdk-version-contract.md) - [Signing strategy](./signing-strategy.md) - [Production readiness](./production-readiness.md) +- [Release Candidate Checklist](./release-candidate-checklist.md) — v1.0 acceptance criteria - [Packages](./packages.md) - [ADRs](./adr/index.md) - [stellar-album case study](./case-studies/stellar-album.md) @@ -41,9 +52,9 @@ For maintainers: release process, publish checklists, testing policy, and intern ## Quick orientation -- **CLI-first:** `init → doctor → build → deploy → status → dev`. Deploy records contract IDs in - `caatinga.artifacts.json`, auto-generates TypeScript bindings, and can run configured - `postDeploy` hooks plus frontend env sync after full graph deploys. -- **Client second:** `@caatinga/client` wires generated bindings + artifacts + a wallet adapter in - the browser. React apps add `@caatinga/client/react` for `WalletProvider`/`useWallet`. -- **Errors are API:** automation should key on `CAATINGA_*` codes, never on message text. +Caatinga operates strictly under four core pillars: + +- **Deployment:** Powered by the **Orchestration Engine** (`init → build → deploy → status → dev`), managing topological graphs (`dependsOn`), placeholders, and lifecycle hooks. +- **Artifacts:** A portable, Git-versioned state contract (`caatinga.artifacts.json`) linking deployments to frontend consumers. +- **Runtime:** Managed by the **Integration SDK** (`@caatinga/client`) and its **Transaction Pipeline**, linking wallets, typescript bindings, and React hooks. +- **Automation:** Developer diagnostics (`doctor`, `setup`), regression suites (`smoke`), and CI/CD stable error APIs. diff --git a/docs/architecture-freeze.md b/docs/architecture-freeze.md new file mode 100644 index 0000000..5c5a48a --- /dev/null +++ b/docs/architecture-freeze.md @@ -0,0 +1,33 @@ +# Architecture Freeze Declaration + +This document formally declares the **Architecture Freeze** for Caatinga v1.0. All structural schemas, packaging boundaries, command mappings, and execution contracts are sealed. Future sprints will focus strictly on Developer Experience (DX), CLI diagnostics, testing, reliability, and bug fixing. + +--- + +## 1. Frozen Core Contracts & Limits + +The following architectural designs are frozen and cannot be modified: + +### 1. Core Identity +- **Definition:** Caatinga is defined exclusively as **Deployment Orchestration + Versioned Artifacts for Soroban**. +- **No Scope Bloat:** No on-chain registries, rust macro decorators, or custom test runners will be introduced. + +### 2. Package Boundaries +- **Orchestration Engine (`@caatinga/core`):** The only package permitted to execute subprocesses (via `execa` in `run-command.ts`) and interact with Node.js filesystem APIs. +- **Integration SDK (`@caatinga/client`):** Consumes exclusively browser-safe subpaths (`@caatinga/core/browser`), containing only types and error definitions. No Node.js runtime code may bleed into this package. + +### 3. Artifact Schema (`caatinga.artifacts.json`) +- **Version:** Fixed at version `2`. +- **Evolutions:** Only backward-compatible optional fields are permitted. Breaking changes requiring v3 are blocked until v2.0. +- **Guard:** The loader actively rejects files with `version > 2` to prevent older CLIs from corrupting registries. + +### 4. Lifecycle Operations +- **Operations:** The behaviors of `deploy`, `upgrade` (in-place), `redeploy` (`--force`), and `rollback` (local registry shift) are locked. + +--- + +## 2. Guidelines for Future Sprints + +1. **Bug Fixes Only:** Any architectural issue discovered must be resolved via local bug fixes or DX refinements within the defined boundaries. +2. **No Refactoring:** Boy Scout rule applies locally, but large monorepo refactoring is prohibited. +3. **DX Focus:** Future sprints will focus on making errors human-readable, improving templates, optimizing bundler output, and increasing automated test coverage. diff --git a/docs/architecture.md b/docs/architecture.md index 4de9899..31ffcf6 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -4,7 +4,7 @@ This document is the **canonical product and architecture stance** for Caatinga. ## One-sentence promise -**Git-versioned Soroban deploy artifacts + multi-contract orchestration for TypeScript teams that want sovereignty — from scaffold to wallet-ready browser client without a hosted registry.** +**Deployment Orchestration + Versioned Artifacts for Soroban: local, graph-aware deployment orchestration and portable, Git-versioned artifacts for TypeScript teams.** That does not mean hiding Stellar reality. Users keep a **stable Caatinga surface** (`caatinga build`, `caatinga deploy`, `caatinga generate`, `caatinga invoke`, `@caatinga/client`). Changes in flags, stdout, paths, transaction/XDR workflow, and subprocess composition are absorbed behind small adapters, not scattered across user scripts. @@ -18,6 +18,8 @@ That does not mean hiding Stellar reality. Users keep a **stable Caatinga surfac **Primary competitor today:** ad-hoc `package.json` scripts. +For a detailed breakdown of all features categorized into Core, Nice to Have, Experimental, and Out of Scope, refer to the [Scope Policy](./scope.md). + **Direct ecosystem overlap:** [Scaffold Stellar](https://developers.stellar.org/docs/tools/scaffold-stellar) (`stellar scaffold` + `stellar registry`, official templates, `environments.toml`, Vite/React frontend). **Caatinga differentiation:** npm-first TypeScript toolkit (`@caatinga/cli`, `@caatinga/core`, `@caatinga/client`), `caatinga.config.ts` + `caatinga.artifacts.json` as the per-network artifacts contract, `CAATINGA_*` error codes as a public API, explicit wallet adapters, and multi-contract orchestration via `dependsOn` — without an on-chain registry or Rust macro layer. @@ -46,6 +48,26 @@ That does not mean hiding Stellar reality. Users keep a **stable Caatinga surfac **Honest risk:** SDF may integrate overlapping workflow pieces into Stellar CLI or Scaffold Stellar. Caatinga competes on **TypeScript DX + git artifacts + multi-contract orchestration**, not on reimplementing Soroban or replacing the official SDK. +## Core Pillars + +Architecturally, Caatinga is structured around four pillars that compartmentalize responsibilities and prevent cross-boundary pollution: + +### 1. Deployment (Orchestration Engine) +This pillar encompasses the build and deployment pipeline. It manages contract compilation (via Stellar CLI shell orchestration), multi-contract dependency topological sorting (`dependsOn`), placeholder resolution (e.g. `${contracts.token.contractId}`), and executing post-deploy hooks (`postDeploy`). +*Components responsible:* `@caatinga/core` (specifically `deploy-graph`, `load-config`), `@caatinga/cli`. + +### 2. Artifacts (State Contract) +This pillar represents the static state representation of all deployments. The per-network `caatinga.artifacts.json` file serves as the Git-versioned contract between compilation, deployment, and client integration, capturing contract IDs, compiler hashes, and metadata. +*Components responsible:* `@caatinga/core` (schema validations, state read/write). + +### 3. Runtime (Client Integration Layer) +This pillar exposes the APIs consumed by browser/Node applications. It includes the TypeScript clients, pluggable wallet adapters, React context providers/hooks (`@caatinga/client/react`), and transaction pipeline orchestration (simulate → sign → submit → watch). +*Components responsible:* `@caatinga/client`. + +### 4. Automation (Developer Diagnostics & Safety) +This pillar ensures local workspace reliability, setup automation, regression checking (`caatinga smoke` / `postDeployRead`), and CI/CD-friendly error APIs using stable error codes. +*Components responsible:* `@caatinga/cli` (commands `doctor`, `smoke`, `setup`), `@caatinga/core` (stable errors module). + ## Validation roadmap (flows) 1. **Alpha flow (current):** `init → build → deploy → generate → invoke` plus `@caatinga/client` for browser-side binding/artifact/wallet interop. @@ -96,54 +118,62 @@ Each box is either a file you commit, a CLI command you run, or a runtime compon ## Package boundaries (monorepo) -- **`@caatinga/cli`:** argument parsing, terminal UX, `doctor` diagnostics, delegation to core—no subprocess orchestration except through core APIs. -- **`@caatinga/core`:** load `caatinga.config.ts`, validate schemas, resolve networks/contracts, read/write `caatinga.artifacts.json`, run Stellar CLI and related tools via a **single shell layer** (`run-command.ts`). **All `execa` usage stays here.** -- **`@caatinga/client`:** thin client/browser interop over generated bindings, artifacts, wallet adapters, `invoke()`, `buildXdr()`, and explicit XDR/raw debug output. It does not own signing keys or serialize SCVal manually. Subpaths: `./react` (WalletProvider/useWallet), `./vite` (bundler helpers), `./freighter`, `./stellar-wallets-kit`. -- **`@caatinga/zk`:** ZK proof serialization, Circom Groth16 workflow helpers, and browser binding args for on-chain verification. -- **`packages/templates`:** official templates consumed by `caatinga init` and validated through `caatinga.template.json` before copy. +- **`@caatinga/cli` (CLI Interface):** argument parsing, terminal UX, and delegation to the core Orchestration Engine—no subprocess orchestration except through core APIs. +- **`@caatinga/core` (Orchestration Engine):** load `caatinga.config.ts`, validate schemas, resolve networks/contracts, read/write `caatinga.artifacts.json`, run Stellar CLI and related tools via a **single shell layer** (`run-command.ts`). **All `execa` usage stays here.** +- **`@caatinga/client` (Integration SDK):** browser contract client, Freighter adapter, SWK adapter, React context, and the transaction execution pipeline. **No Node-only dependencies, no shell orchestration, no file-system access.** It must remain bundling-safe (Vite, Webpack, Turbopack). +- **`@caatinga/zk` (ZK Cryptographic Engine):** ZK proof serialization, Circom Groth16 workflow helpers, and browser binding args for on-chain verification. +- **`packages/templates` (Project Scaffolds):** official template starter layouts consumed by `caatinga init`. + +For detailed package dependency boundaries and compliance rules, see [Package Boundaries & Isolation Rules](./packages.md#package-boundaries-isolation-rules). + Deferred unless explicitly rescoped: CLI XDR commands, `caatinga generate --interop`, full plugin system, RWA-only templates, visual dashboard, custom test runner as **required** core dependencies. -### Package dependency diagram +### Dependency Map ```mermaid graph TD - user((CLI user)) - dev((Browser user)) - - subgraph monorepo[Caatinga monorepo] - cli["@caatinga/cli
(caatinga binary)"] - core["@caatinga/core
(config, artifacts, shell, execa)"] - coreBrowser["@caatinga/core/browser
(errors + artifact types only)"] - client["@caatinga/client
(createCaatingaClient)"] - clientReact["@caatinga/client/react
(WalletProvider, useWallet)"] - clientVite["@caatinga/client/vite
(SWK bundler helpers)"] - zk["@caatinga/zk
(ZK proof serialization)"] - zkBrowser["@caatinga/zk/browser
(browser binding args)"] - templates["packages/templates
(caatinga.template.json)"] + user((CLI User)) + dev((Browser User)) + + subgraph Conceptual[Conceptual Data & Operational Flow] + CLI["CLI Interface (@caatinga/cli)"] + Core["Orchestration Engine (@caatinga/core)"] + CLIAdapter["CLI Adapter (run-command.ts)"] + StellarCLI["Stellar CLI (external binary)"] + Artifacts["Artifacts State (caatinga.artifacts.json)"] + Runtime["Transaction Pipeline / Integration SDK"] + + CLI --> Core + Core --> CLIAdapter + CLIAdapter --> StellarCLI + Core --> Artifacts + Runtime --> Artifacts end - stellarCli["stellar CLI
(external)"] - walletExt["Wallet extension
(Freighter / SWK)"] - - user --> cli - cli --> core - cli --> templates - core --> stellarCli - core -.exports.-> coreBrowser - client --> coreBrowser - client --> walletExt - client -.exports.-> clientReact - client -.exports.-> clientVite - zk -.exports.-> zkBrowser - dev --> client + subgraph Packages[Package Dependency Map] + cliPkg["@caatinga/cli"] + corePkg["@caatinga/core"] + clientPkg["@caatinga/client"] + zkPkg["@caatinga/zk"] + templatesPkg["packages/templates"] + + cliPkg --> corePkg + cliPkg --> templatesPkg + clientPkg --> corePkg + zkPkg --> corePkg + end + + user --> CLI + dev --> Runtime ``` Notes encoded in the diagram: -- The CLI depends on core, never the other way around. -- `@caatinga/core` is the only package that talks to the `stellar` binary. `@caatinga/client` consumes the browser-safe subpath `@caatinga/core/browser`, which excludes `execa` and Node-only modules so Vite/webpack bundles stay slim. -- `@caatinga/client` does not own wallet state — it composes a wallet adapter (Freighter, Stellar Wallets Kit, or a custom `CaatingaWalletAdapter`). On top of the adapter, `createWalletSession` provides optional connection state, persistence, and silent restore; the `@caatinga/client/react` subpath wraps that session in `WalletProvider`/`useWallet` for React apps (React stays an optional peer). +- **CLI Isolation:** The CLI Interface depends on the Orchestration Engine (`core`), never the other way around. +- **Node vs Browser Boundaries:** The Orchestration Engine is the only package that orchestrates subprocesses via CLI Adapters executing Stellar CLI commands. The Integration SDK (`@caatinga/client`) consumes only the browser-safe subpath `@caatinga/core/browser`, ensuring that Node-specific dependencies like `execa` or `fs` are never pulled into web applications. +- **State Registry:** The Artifacts State (`caatinga.artifacts.json`) acts as the shared database between the Orchestration Engine (which writes it on deploy) and the Transaction Pipeline / Integration SDK (which reads it at runtime). + ## Meta-framework boundary: orchestrate workflow, not mental model diff --git a/docs/artifacts-spec.md b/docs/artifacts-spec.md new file mode 100644 index 0000000..958f1a4 --- /dev/null +++ b/docs/artifacts-spec.md @@ -0,0 +1,96 @@ +# Artifacts Specification + +This document details the public API contract and schema for `caatinga.artifacts.json`. The artifacts file serves as the Git-versioned state registry of all deployments, bridging the compilation/deployment output of the Orchestration Engine with consumer libraries (like the Integration SDK) and generators. + +--- + +## JSON Schema Details + +The root of `caatinga.artifacts.json` is a JSON object with the following fields: + +- **`project`** (string): The unique name of the Caatinga project. +- **`version`** (number): The schema version. Currently `2`. +- **`networks`** (object): A dictionary of network deployment scopes keyed by network name (e.g., `local`, `testnet`, `mainnet`). + +### Network Scope Object + +Each entry under `networks` contains: + +- **`dependencyGraph`** (object): A dictionary mapping each contract name to an array of its direct dependency contract names, representing the topological deploy order. +- **`contracts`** (object): A dictionary of deployed contract configurations keyed by contract name. + +### Contract Object + +Each contract entry contains: + +- **`contractId`** (string): The public, deployed contract address on the Stellar/Soroban network. +- **`wasmHash`** (string): The SHA-256 hash of the compiled WASM binary deployed for this contract. +- **`deployedAt`** (string): ISO-8601 Datetime string representing when the contract was deployed. +- **`sourcePath`** (string): Relative path to the Rust contract folder inside the repository. +- **`wasmPath`** (string): Relative path to the target WASM file compiled. +- **`dependencies`** (array of strings): The list of contract names this contract depends on. +- **`resolvedDeployArgs`** (object): A dictionary containing primitive argument values (`string`, `number`, `boolean`) passed during deployment with resolved placeholder variables. +- **`upgradeStrategy`** (string, optional): The strategy configured for redeploying/updating. One of: `in-place` or `redeploy`. +- **`history`** (array, optional): A chronological record of historical contract deployments superseded by the current one. + +### History Entry Object + +Each item in the `history` array represents a superseded deployment: + +- **`contractId`** (string): The historic contract address. +- **`wasmHash`** (string): The historic WASM hash. +- **`deployedAt`** (string): ISO-8601 Datetime representing the original deployment time. +- **`supersededAt`** (string): ISO-8601 Datetime representing when this deployment was replaced. +- **`reason`** (string, optional): One of: `upgrade`, `rollback`, or `force-redeploy`. +- **`upgradeType`** (string, optional): One of: `in-place` or `new-contract`. + +--- + +## Example `caatinga.artifacts.json` + +```json +{ + "project": "my-dapp", + "version": 2, + "networks": { + "testnet": { + "dependencyGraph": { + "token": [], + "vault": ["token"] + }, + "contracts": { + "token": { + "contractId": "CAS3JIO4YZHG45NVU...", + "wasmHash": "a1b2c3d4e5f6...", + "deployedAt": "2026-07-06T10:00:00Z", + "sourcePath": "contracts/token", + "wasmPath": "target/wasm32v1-none/release/token.wasm", + "dependencies": [], + "resolvedDeployArgs": {} + }, + "vault": { + "contractId": "CB4JIO7YZHG98NVU...", + "wasmHash": "f6e5d4c3b2a1...", + "deployedAt": "2026-07-06T10:05:00Z", + "sourcePath": "contracts/vault", + "wasmPath": "target/wasm32v1-none/release/vault.wasm", + "dependencies": ["token"], + "resolvedDeployArgs": { + "token_address": "CAS3JIO4YZHG45NVU..." + }, + "history": [ + { + "contractId": "CBL3JIO0YZHG89NVU...", + "wasmHash": "d4c3b2a1e5f6...", + "deployedAt": "2026-07-05T12:00:00Z", + "supersededAt": "2026-07-06T10:05:00Z", + "reason": "upgrade", + "upgradeType": "in-place" + } + ] + } + } + } + } +} +``` diff --git a/docs/artifacts-versioning.md b/docs/artifacts-versioning.md new file mode 100644 index 0000000..726b538 --- /dev/null +++ b/docs/artifacts-versioning.md @@ -0,0 +1,43 @@ +# Artifacts Versioning Strategy + +This document defines the schema evolution, migration rules, and compatibility policies for `caatinga.artifacts.json`. + +--- + +## 1. Schema Versioning Rules + +We enforce a strict versioning convention for the `caatinga.artifacts.json` format: + +- **Incremental Bumps:** Schema versions are represented by sequential integers (1, 2, 3, etc.) defined in the root `version` field. +- **Non-Breaking Changes (Minor updates):** Adding optional fields (e.g. metadata) does not require changing the schema version. The format remains fully compatible. +- **Breaking Changes (Major updates):** Modifying structural shapes, changing required types, or removing fields requires a schema version increment (e.g., from `2` to `3`) and the implementation of an explicit migration script in the Orchestration Engine. + +--- + +## 2. Compatibility Matrix + +To avoid lock-in and support teams upgrading their CLI, Caatinga implements the following rules: + +### Backward Compatibility (Old files in New CLI) +- **Automatic Migration:** When reading old artifacts files (e.g. version 1), the Orchestration Engine automatically migrates the shape to the current version in memory using `migrateArtifactsToV2`. +- **Automatic Writeback:** Running `caatinga deploy` or `caatinga upgrade` on an old schema version automatically writes the updated, migrated format to disk, upgrading the project's artifacts file. + +### Forward Compatibility (New files in Old CLI) +- **Safety Fail-Fast:** If the Orchestration Engine detects an artifacts file version greater than `CURRENT_ARTIFACTS_SCHEMA_VERSION` (e.g., `parsedJson.version > 2`), it immediately halts execution with a `CAATINGA_ARTIFACT_INVALID` error. +- **Why this matters:** Preventing older CLIs from parsing newer schemas protects users from corrupted metadata state or loss of registry history. + +--- + +## 3. Creating a New Schema Migration + +When a future breaking change is introduced: + +1. **Schema Definition:** + - Define the new schema version in `packages/core/src/artifacts/artifact.schema.ts` (e.g. `CaatingaArtifactsV3Schema`). + - Bump `CURRENT_ARTIFACTS_SCHEMA_VERSION` to the new version number. + - Update `CaatingaArtifactsSchema` union. +2. **Migration Code:** + - Create a migration function `migrateArtifactsToV3` in `packages/core/src/artifacts/migrate-artifacts.ts`. + - Update `migrateArtifactsFile` to invoke the chain of migrations sequentially (v1 → v2 → v3). +3. **Test Coverage:** + - Add unit test fixtures capturing old schema files and verify they convert correctly. diff --git a/docs/automation.md b/docs/automation.md new file mode 100644 index 0000000..2325613 --- /dev/null +++ b/docs/automation.md @@ -0,0 +1,96 @@ +# Automation Pipeline + +This document describes the three automation commands that form the Caatinga CI pipeline: `doctor`, `smoke`, and `ci run`. + +--- + +## 1. `caatinga doctor` (Sprint 26) + +The `doctor` command runs a comprehensive set of local diagnostics against the current project. It checks: + +| Check | Description | +|---|---| +| Stellar CLI version | Verifies that the installed Stellar CLI meets the minimum version | +| Deploy coverage | Reports which contracts are deployed on the target network | +| Binding freshness | Verifies that generated bindings match the current WASM hashes | +| Env sync | Checks whether `CAATINGA_*` env vars in `.env` match the artifacts | +| Post-deploy hooks | Validates that `expect` assertions in hooks would pass | +| WASM drift | Detects contracts whose on-chain WASM differs from the local build | + +### Flags + +| Flag | Default | Description | +|---|---|---| +| `-n, --network` | `defaultNetwork` | Target network | +| `-s, --source` | `CAATINGA_SOURCE` or `alice` | Stellar CLI identity | +| `--all-networks` | `false` | Run checks across all configured networks | +| `--strict-env` | `false` | Fail when env vars are missing or stale | +| `--strict-bindings` | `false` | Fail when bindings are stale | +| `--strict` | `false` | Enable all strict checks | + +### Exit Codes + +- `0` — all diagnostics passed +- `1` — one or more hard failures detected + +--- + +## 2. `caatinga smoke` (Sprint 27) + +The `smoke` command runs the read-only smoke checks defined under each contract's `smokeReads` in `caatinga.config.ts`. It simulates each read call against the target network and asserts the `expect` matchers. + +### `smokeReads` in `caatinga.config.ts` + +```ts +contracts: { + counter: { + smokeReads: [ + { method: "get", expect: { gte: 0 } } + ] + } +} +``` + +### Flags + +| Flag | Default | Description | +|---|---|---| +| `-n, --network` | `defaultNetwork` | Target network | +| `-s, --source` | `CAATINGA_SOURCE` or `alice` | Identity for simulation context | + +### Exit Codes + +- `0` — all smoke reads passed +- `1` — one or more smoke reads failed + +--- + +## 3. `caatinga ci run` (Sprint 28) + +The `ci run` command orchestrates the full CI recipe: `doctor` → `smoke`. It is designed for use in GitHub Actions and other CI environments. + +### Execution Flow + +``` +ci run + │ + ├─ doctor --network [--strict] + │ + └─ smoke --network [--source ] (skipped with --skip-smoke) +``` + +### Flags + +| Flag | Default | Description | +|---|---|---| +| `-n, --network` | `defaultNetwork` | Target network | +| `-s, --source` | – | Identity alias | +| `--skip-smoke` | `false` | Skip smoke reads | +| `--strict` | `false` | Pass `--strict` to doctor | + +### GitHub Actions Example + +```yaml +- name: Caatinga CI + run: npx caatinga ci run --network testnet --source ${{ secrets.CAATINGA_SOURCE }} +``` diff --git a/docs/conceptual-naming.md b/docs/conceptual-naming.md new file mode 100644 index 0000000..1f6d331 --- /dev/null +++ b/docs/conceptual-naming.md @@ -0,0 +1,36 @@ +# Conceptual Naming Policy + +This document defines the official conceptual terminology for Caatinga to resolve generic names and clearly communicate package and module responsibilities. + +While package names on npm (`@caatinga/core`, `@caatinga/client`) remain unchanged to avoid breaking existing consumer integrations, all architecture diagrams, code comments, and documentation guides must adhere to this conceptual naming vocabulary. + +--- + +## Terminology Mapping + +| Package/Module Name | Generic Term | Conceptual Term | Responsibility Description | +| :--- | :--- | :--- | :--- | +| `@caatinga/core` | `core` | **Orchestration Engine** | The engine that compiles contracts, topological-sorts the dependency graph, resolves configuration variables, and executes deploy lifecycle hooks. | +| `@caatinga/client` | `client` | **Integration SDK** | The frontend/consumer library that provides wallet session bindings, wraps type-safe generated contract clients, and interacts with browser wallet adapters. | +| `packages/templates`| `templates` | **Project Scaffolds** | Pre-configured starter application templates (minimal, react-vite, zk) used to initialize projects. | +| `@caatinga/client` (internal) | `runtime` | **Transaction Pipeline** | The sequential execution pipeline: simulating transactions, signing via client-side wallets, submitting to the Horizon/Stellar network, and watching confirmation status. | + +--- + +## Vocabulary Guidelines + +1. **When talking about CLI or compile/deploy steps:** + - *Avoid:* "Run the core to deploy", "Core builds the contracts". + - *Prefer:* "The **Orchestration Engine** executes the build", "The deployment graph is handled by the **Orchestration Engine**". + +2. **When talking about browser-side contract execution:** + - *Avoid:* "Import the client", "Register a client adapter". + - *Prefer:* "Initialize the **Integration SDK** client", "Utilize the **Integration SDK** wallet adapters". + +3. **When talking about the transaction lifecycle:** + - *Avoid:* "The client runtime handles the signature", "Runtime client calls simulate". + - *Prefer:* "The **Transaction Pipeline** simulates and signs the transaction". + +4. **When talking about templates:** + - *Avoid:* "Scaffold using templates", "Available templates in packages". + - *Prefer:* "Scaffold using the official **Project Scaffolds**". diff --git a/docs/deploy-upgrade-spec.md b/docs/deploy-upgrade-spec.md new file mode 100644 index 0000000..d718de3 --- /dev/null +++ b/docs/deploy-upgrade-spec.md @@ -0,0 +1,58 @@ +# Deploy & Upgrade Specification + +This document details the specifications, behaviors, and transition rules for contract deployment and upgrade operations within the Caatinga platform. These rules govern command-line invocation, internal core actions, and the resulting state registry mutations in `caatinga.artifacts.json`. + +--- + +## 1. Core Operations + +### Deploy +- **Definition:** The initial upload and instantiation of a Soroban smart contract. +- **Behavior:** + - Compiles the WASM binary (if necessary). + - Deploys the binary on-chain via the Stellar CLI. + - Registers the new `contractId` and `wasmHash` inside `caatinga.artifacts.json`. + - Resolves initialization arguments (`resolvedDeployArgs`). +- **Triggers:** `caatinga deploy ` (when no prior deployment exists on the target network). + +### Upgrade (In-place) +- **Definition:** The update of a contract's backing WebAssembly byte-code on-chain without altering its address (`contractId`). +- **Behavior:** + - Uploads the new WASM binary to the network to obtain a new `wasmHash`. + - Invokes the contract's defined upgrade method (e.g., `upgrade`) with the new WASM hash using administrator authorization. + - Pushes the previous `contractId` and `wasmHash` version to the contract's `history` block in the artifacts file. + - Updates the active `wasmHash` and compilation metadata under the current contract entry. +- **Triggers:** `caatinga upgrade --method ` + +### Redeploy +- **Definition:** Deploying a brand new instance of a previously deployed contract, generating a new `contractId`. +- **Behavior:** + - Instantiates a fresh copy of the contract on the network. + - Pushes the previous contract instance representation (`contractId`, `wasmHash`, `deployedAt`, etc.) to the contract's `history` block in the artifacts file. + - Replaces the active `contractId` and configurations with the newly deployed instance. +- **Triggers:** `caatinga deploy --force` (or `--upgrade` to mark as upgrade type). + +### Rollback +- **Definition:** Reverting the active contract registration in the artifacts file to a prior deployment state saved in the history. +- **Behavior:** + - Searches the `history` array of the target contract for a matching `contractId`. + - Restores the matching contract state (contract ID, WASM hash, metadata) to the active contract entry. + - Appends the superseded active instance to the `history` with reason `"rollback"`. + - *Note:* Rollback updates the local artifacts state registry. On-chain state restoration (e.g., re-running an on-chain upgrade to the old WASM hash) is an application concern. +- **Triggers:** `caatinga rollback --target ` + +--- + +## 2. Operation Flags & Mutators + +### Force (`--force`) +- **Purpose:** Bypasses state check optimizations. +- **Behavior:** + - By default, Caatinga skips deployment or builds if the local WASM hash matches the registry (`ifChanged` strategy). + - Activating `--force` overrides this check, forcing a fresh compile, upload, and deployment transaction, pushing the current registry state to the history. + +### If Changed (`--if-changed`) +- **Purpose:** Optimizes CI/CD pipelines and local DX by avoiding redundant deploy transactions. +- **Behavior:** + - Compares the SHA-256 hash of the compiled WASM binary with the `wasmHash` stored in the current network scope of `caatinga.artifacts.json`. + - If the hashes match, the deployment is skipped, returning the existing deployment information without sending transactions to the network. diff --git a/docs/for-llms.md b/docs/for-llms.md index 5ecf520..0aa4719 100644 --- a/docs/for-llms.md +++ b/docs/for-llms.md @@ -1,6 +1,6 @@ # Caatinga — LLM Reference -Caatinga is a TypeScript CLI and browser toolkit for Soroban on Stellar. It orchestrates scaffold → build → deploy → binding generation → invoke/read, with git-versioned `caatinga.artifacts.json` and no mandatory registry. Build/deploy/invoke shell out to Stellar CLI; `caatinga generate` runs `npx @stellar/stellar-sdk generate`. +Caatinga represents Deployment Orchestration + Versioned Artifacts for Soroban. It provides local, graph-aware deployment orchestration and portable, Git-versioned artifacts (`caatinga.artifacts.json`) for TypeScript teams. It orchestrates scaffold → build → deploy → binding generation → invoke/read without requiring a mandatory on-chain registry. Build/deploy/invoke shell out to Stellar CLI; `caatinga generate` runs `npx @stellar/stellar-sdk generate`. Equivalent content available at [`/llms-full.txt`](../llms-full.txt). Human docs: [dione-b.github.io/caatinga](https://dione-b.github.io/caatinga/). diff --git a/docs/index.md b/docs/index.md index d0686ea..f6588b1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,8 +3,8 @@ layout: home hero: name: Caatinga - text: Soroban deploy artifacts + TypeScript CLI - tagline: Git-versioned deployment state, multi-contract orchestration, npm-first workflow. + text: Deployment Orchestration + Versioned Artifacts for Soroban + tagline: Local, graph-aware deployment orchestration and portable, Git-versioned artifacts for TypeScript teams. actions: - theme: brand text: Get started diff --git a/docs/lifecycle-hooks-spec.md b/docs/lifecycle-hooks-spec.md new file mode 100644 index 0000000..872507b --- /dev/null +++ b/docs/lifecycle-hooks-spec.md @@ -0,0 +1,72 @@ +# Lifecycle & Post-Deploy Hooks Specification + +This document details the execution phases of the Caatinga orchestrator pipeline and specifies the behavior and assertions of post-deploy hooks. + +--- + +## 1. Orchestrator Lifecycle Phases + +Caatinga operates as a deterministic, phase-based pipeline. When a developer runs `caatinga deploy` or similar orchestration commands, the execution proceeds sequentially through five distinct phases: + +```mermaid +graph TD + Phase1[Phase 1: Config & Validation] --> Phase2[Phase 2: Build Workspace] + Phase2 --> Phase3[Phase 3: Topological Deploy] + Phase3 --> Phase4[Phase 4: Bindings Generation] + Phase4 --> Phase5[Phase 5: Wire Hooks Execution] +``` + +### Phase 1: Config & Validation +- **Actions:** Loads `caatinga.config.ts`, resolves the target network connection parameters, and validates the multi-contract dependency graph (detecting cycles or missing dependency declarations). + +### Phase 2: Build Workspace (Optional) +- **Actions:** Compiles all target contract WASM binaries using `cargo build --target wasm32-unknown-unknown`. This phase can be skipped by passing `--no-build`. + +### Phase 3: Topological Deploy +- **Actions:** Deploys contracts in topological dependency order (non-linear). + - Evaluates `--if-changed` cache: skips deploy if WASM hash matches `caatinga.artifacts.json`. + - Performs network deploy and updates contract addresses. + - Dynamically resolves placeholders (`${contracts..contractId}`) for downstream contracts. + +### Phase 4: Bindings Generation (Optional) +- **Actions:** Generates TypeScript clients and wallet bindings using the current `caatinga.artifacts.json` as the input. Writes freshness markers (`.caatinga-bindings.json`) to the frontend folders. + +### Phase 5: Wire Hooks Execution +- **Actions:** Executes sequential post-deploy hooks (`postDeploy` and `postDeployRead`) to wire contract dependencies, initialize storage, or verify state. + +--- + +## 2. Post-Deploy Hooks Specification + +Hooks are declared globally in `caatinga.config.ts` and run during the `caatinga wire` command (or automatically at the end of `caatinga deploy`). + +### Hook Types + +1. **Invoke Hooks (`postDeploy`):** + - **Purpose:** Executes transaction-submitting contract calls (write operations). + - **Behavior:** Invokes the contract method on-chain, signing the transaction using the designated `--source` identity. +2. **Read Hooks (`postDeployRead`):** + - **Purpose:** Executes simulated read-only contract calls (view operations). + - **Behavior:** Queries the ledger state without submitting transactions, returning the serialized simulation response. + +### Arguments & Placeholder Resolution +All arguments passed to hooks (`args`) are evaluated by the **Placeholder Engine**. Hooks can accept: +- Static primitives (`string`, `number`, `boolean`). +- Dynamic contract address lookups: `${contracts..contractId}`. +- Active deployer address: `${source.address}`. + +### Assertion Engine (`expect`) +To ensure the pipeline succeeded, hooks can define an `expect` assertion to validate the method output: + +- **Simple Assertion:** + ```ts + expect: "expected_return_value" + ``` +- **Type-Safe Assertion:** + ```ts + expect: { + value: "${contracts.token.contractId}", + type: "address" // validates return type matches address format + } + ``` +- If the method return value does not match the assertion, the pipeline immediately halts with a `CAATINGA_POST_DEPLOY_VERIFY_FAILED` error. diff --git a/docs/network-setup.md b/docs/network-setup.md new file mode 100644 index 0000000..6e67368 --- /dev/null +++ b/docs/network-setup.md @@ -0,0 +1,82 @@ +# Stellar & Soroban Network Setup Guide + +This guide describes how to configure Stellar and Soroban networks inside a Caatinga project. + +--- + +## 1. Network Configuration in `caatinga.config.ts` + +Networks are declared inside the `networks` block of the configuration file. Each network can specify the RPC endpoint, network passphrase (which acts as a secure chain identifier), and optionally a friendbot URL for account funding. + +Here is the standard schema: + +```ts +export type NetworkConfig = { + rpcUrl: string; + passphrase: string; + friendbotUrl?: string; +}; +``` + +--- + +## 2. Standard Stellar/Soroban Network Boilerplates + +### Testnet (SDF Public Testnet) +Use this for public staging, testing integrations, and deploying release candidates. +- **Passphrase:** `Test SDF Network ; September 2015` +- **friendbotUrl:** Available (allows funding accounts with 10,000 test XLM). + +```ts +networks: { + testnet: { + rpcUrl: "https://soroban-testnet.stellar.org:443", + passphrase: "Test SDF Network ; September 2015", + friendbotUrl: "https://friendbot.stellar.org" + } +} +``` + +### Mainnet (Stellar Production Network) +Use this only for production releases. +- **Passphrase:** `Public Global Stellar Network ; October 2015` +- **friendbotUrl:** None (requires real assets). + +```ts +networks: { + mainnet: { + rpcUrl: "https://mainnet.stellar.org:443", + passphrase: "Public Global Stellar Network ; October 2015" + } +} +``` + +### Futurenet (SDF Experimental Futurenet) +Use this for testing bleeding-edge Protocol features. +- **Passphrase:** `Test SDF Future Network ; October 2022` +- **friendbotUrl:** Available. + +```ts +networks: { + futurenet: { + rpcUrl: "https://rpc-futurenet.stellar.org:443", + passphrase: "Test SDF Future Network ; October 2022", + friendbotUrl: "https://friendbot-futurenet.stellar.org" + } +} +``` + +### Local/Standalone (Docker) +Use this for rapid offline development. +- **Setup Command:** Run a local Stellar Quickstart Docker container. +- **Passphrase:** `Standalone Network ; Simple comparison` + +```ts +networks: { + local: { + rpcUrl: "http://localhost:8000/soroban/rpc", + passphrase: "Standalone Network ; Simple comparison", + friendbotUrl: "http://localhost:8000/friendbot" + } +} +``` diff --git a/docs/packages.md b/docs/packages.md index 5527238..09af2a3 100644 --- a/docs/packages.md +++ b/docs/packages.md @@ -54,3 +54,27 @@ For custom CI scripts and integrators: | `summarizeReadOutput` | Compact read output for large payloads | Browser-safe types and errors: import from `@caatinga/core/browser` only. + +--- + +## Package Boundaries & Isolation Rules + +To maintain high stability and prevent architectural regression, the following package boundaries must be strictly enforced: + +### 1. CLI Isolation Rule +- `@caatinga/cli` consumes `@caatinga/core` directly for orchestrating commands. +- **Rule:** Under no circumstances should `@caatinga/core`, `@caatinga/client`, or `@caatinga/zk` import or depend on `@caatinga/cli`. The core logic must never reference CLI argument parsing, console output formats, or terminal-specific APIs. + +### 2. Client Browser-Safety Rule +- `@caatinga/client` connects smart contract bindings, local artifacts, and browser wallets. It is designed to be bundled safely by bundlers like Vite, Webpack, and Turbopack. +- **Rule:** `@caatinga/client` must never import from the root of `@caatinga/core` (which contains Node-specific dependencies like `execa` or `fs`). It can only import browser-safe symbols from the `@caatinga/core/browser` subpath. +- **Rule:** `@caatinga/client` must not contain direct filesystem access or child process orchestration. + +### 3. Template Independence Rule +- `packages/templates` contains Vite and React project scaffolds. +- **Rule:** Templates are target output configurations. They must not contain compiler logic or depend on the monorepo core tools except as consumer dependencies (e.g. `@caatinga/client`, `@caatinga/cli`). + +### 4. ZK Isolation Rule +- `@caatinga/zk` encapsulates the Circom and Groth16 cryptographic operations. +- **Rule:** This module operates independently and does not depend on `@caatinga/cli` or `@caatinga/client` code directly. + diff --git a/docs/release-candidate-checklist.md b/docs/release-candidate-checklist.md new file mode 100644 index 0000000..b85e8c9 --- /dev/null +++ b/docs/release-candidate-checklist.md @@ -0,0 +1,57 @@ +# Release Candidate Checklist (v1.0) + +This document defines the acceptance criteria that must be satisfied before Caatinga can be promoted from alpha (pre-1.0) to v1.0. + +--- + +## Schema Freeze + +- [x] `caatinga.artifacts.json` schema is versioned (`schema_version: 2`) and has a migration path from v1 +- [x] `caatinga.config.ts` schema is validated by Zod with explicit error messages +- [x] `caatinga.template.json` manifest is versioned (`templateVersion: 1`) +- [x] All schema-breaking changes require an explicit migration or a new schema version increment + +## API Freeze + +- [x] All `CAATINGA_*` error codes are documented in `docs/errors.md` +- [x] Public exports of `@caatinga/core` are typed and stable (no `any` at boundaries) +- [x] Public exports of `@caatinga/client` are typed and stable +- [x] `CaatingaWalletAdapter` interface is frozen (no breaking changes without a major version) +- [x] `CaatingaClientConfig` interface is frozen + +## CLI Surface Freeze + +- [x] All commands and their flags are documented in `docs/cli.md` +- [x] Exit codes `0` (success) and `1` (failure) are consistent across all commands +- [x] `caatinga --help` shows all commands grouped by category +- [x] `caatinga version` outputs `@caatinga/cli@` and the runtime Node.js version + +## Reliability Criteria + +- [x] All unit tests pass (`pnpm test`) +- [x] TypeScript compiles without errors (`pnpm typecheck`) +- [x] Build is reproducible (`pnpm build`) +- [ ] Integration tests pass against Stellar testnet (manual gate) +- [x] `caatinga doctor` correctly detects missing Stellar CLI, outdated versions, and stale bindings +- [x] `caatinga smoke` correctly asserts `expect` matchers +- [x] `caatinga setup` installs all prerequisites on a clean machine + +## Documentation Criteria + +- [x] `README.md` describes the problem, differentiator, and quick start in under 5 minutes +- [x] `docs/getting-started.md` guides a new user from zero to first deploy +- [x] `docs/errors.md` lists all `CAATINGA_*` error codes with descriptions +- [x] `docs/architecture.md` describes the package layout and data flow +- [x] `docs/network-setup.md` provides boilerplates for all major Stellar networks +- [x] `docs/runtime-invoke-pipeline.md` documents the full invoke pipeline +- [x] `docs/automation.md` documents doctor, smoke, and ci run + +## Remaining Before v1.0 + +> [!CAUTION] +> The following items must be resolved before promoting to v1.0: + +- [ ] Integration test suite passes against Stellar testnet +- [ ] All templates pinned to a stable `compatibleCore` range matching the v1.0 release +- [ ] CHANGELOG finalized with all breaking changes from alpha documented +- [ ] npm publish with `--tag latest` (currently on `next` channel) diff --git a/docs/runtime-invoke-pipeline.md b/docs/runtime-invoke-pipeline.md new file mode 100644 index 0000000..cabd0eb --- /dev/null +++ b/docs/runtime-invoke-pipeline.md @@ -0,0 +1,97 @@ +# Runtime & Invocation Pipeline + +This document describes the Caatinga browser-side runtime architecture, the `CaatingaWalletAdapter` contract, and the full invocation pipeline for Soroban smart contracts. + +--- + +## 1. Runtime Architecture (Sprint 20) + +The Caatinga Runtime lives in `@caatinga/client` and is responsible for the entire browser-side lifecycle of Soroban contract calls. It does **not** orchestrate deployments (that is the domain of `@caatinga/core` and the CLI). + +### Packages + +| Package | Responsibility | +|---|---| +| `@caatinga/core` | Server/Node orchestration: build, deploy, upgrade, artifacts, bindings | +| `@caatinga/client` | Browser/Node runtime: wallet integration, signing, contract invocation | + +### Minimal API + +The runtime exposes a single `createCaatingaClient(config)` factory that returns a typed proxy client per contract name: + +```ts +const client = createCaatingaClient(config); + +// State-changing call (sign + submit): +await client.myContract.invoke("transfer", { to, amount }); + +// Read-only call (simulate only): +const val = await client.myContract.read("balance", { address }); +``` + +--- + +## 2. Wallet Layer (Sprint 21) + +### `CaatingaWalletAdapter` Interface + +```ts +interface CaatingaWalletAdapter { + getPublicKey(): Promise; + signTransaction(input: { xdr: string; networkPassphrase: string }): Promise; +} +``` + +**Contract rules:** +- `getPublicKey()` must resolve to a valid Ed25519 public key (G-prefixed Stellar address). +- `signTransaction()` must resolve to a Base64-encoded signed XDR string. +- Both methods **must reject** (not leave the promise pending) when the user cancels or when the wallet is not connected. +- Caatinga applies an optional `walletTimeout` (ms) via `CaatingaClientConfig.walletTimeout` if provided. + +### Built-in Adapters + +Adapters for Freighter, Stellar Wallets Kit, and SWKKit are available in `packages/client/src/adapters/`. + +--- + +## 3. Invoke Pipeline (Sprint 22) + +The full lifecycle of a state-changing transaction is: + +``` +invoke() + │ + ├─ 1. getPublicKey() ← wallet adapter + ├─ 2. createClient() ← binding adapter (Stellar SDK contract client) + ├─ 3. callMethod() ← binding adapter (assembles the AssembledTransaction) + │ + ├─ 4. buildXdr() ← prepares & simulates via RPC (Soroban prepareTransaction) + │ └─ simulate ──→ rpcUrl (Soroban RPC) + │ + ├─ 5. signTransaction() ← wallet adapter (user approves in wallet UI) + │ + ├─ 6. submitTransaction() ← Stellar SDK signAndSend() via RPC + │ └─ submit ──→ rpcUrl (Soroban RPC) + │ └─ watch ──→ polls until COMPLETE or FAILED + │ + └─ 7. normalizeSubmitResult() → CaatingaInvokeResult +``` + +For read-only calls (`simulate` / `read`), only steps 1–4 run; signing and submission are skipped. + +### Status Progression + +``` +built → prepared → signed → submitted → confirmed + → failed +``` + +### Error Codes + +| Situation | CaatingaErrorCode | +|---|---| +| Wallet not connected / key unavailable | `WALLET_NOT_CONNECTED` | +| User dismissed signing | `XDR_SIGN_FAILED` | +| Empty or invalid signed XDR | `XDR_SIGN_FAILED` | +| Simulation failure | `XDR_PREPARE_FAILED` | +| Submission/network failure | `XDR_SUBMIT_FAILED` | diff --git a/docs/scope.md b/docs/scope.md new file mode 100644 index 0000000..04d6401 --- /dev/null +++ b/docs/scope.md @@ -0,0 +1,68 @@ +# Scope Policy + +This document establishes the official feature categorization and scope limits for Caatinga to stabilize the platform. Features are classified under four clear taxonomies: **Core**, **Nice to Have**, **Experimental**, and **Out of Scope**. + +--- + +## 1. Core + +Core features define what Caatinga is at its heart. These are fully supported, guaranteed to remain stable, and represent the primary value proposition: + +- **Deployment Orchestration (Local Engine):** + - Project scaffolding (`caatinga init`). + - Rust contract compilation driver (shelling out to Stellar CLI). + - Graph-aware deployments with topological sorting based on dependencies (`dependsOn`). + - In-configuration placeholder resolution (e.g. `${contracts.token.contractId}`). + - Execution of post-deployment lifecycle hooks (`postDeploy`). +- **Versioned Artifacts:** + - Local state tracking via the per-network `caatinga.artifacts.json` file. + - Storage of contract IDs, compiler versions, and WASM hashes. + - Complete integration and versioning via Git (no mandatory on-chain registry dependencies). +- **Runtime Client Library:** + - Strongly typed client consumer helpers in `@caatinga/client`. + - Pluggable wallet adapters (Freighter, Stellar Wallets Kit) and React context bindings (`@caatinga/client/react`). + - Standard transaction pipeline orchestration (simulate → sign → submit → watch). +- **Automation & Diagnostics:** + - Workspace requirements diagnostics (`caatinga doctor` and `caatinga setup`). + - CLI logs and stable error APIs using `CAATINGA_*` error codes. + +--- + +## 2. Nice to Have + +Nice to Have features are quality-of-life enhancements or platform operations that improve stability and visibility, but do not alter the main execution pipeline: + +- **Artifact History & Schema Migration:** + - Tracking artifact histories and providing migrations between format versions (`caatinga migrate artifacts`). +- **Cost Advisory:** + - Simulating deployment transactions to advise on resource consumption and fees (`caatinga estimate deploy`). +- **State Inspection:** + - Comparing local artifact states with deployed on-chain contract states (`caatinga inspect`). +- **Logical Rollbacks:** + - Restore contract state reference locally or update the deployed configuration references dynamically (`caatinga rollback`). + +--- + +## 3. Experimental + +Experimental features are kept isolated and are subject to deprecation or removal if they prove to add excessive complexity or divert focus from the core mission: + +- **ZK Workflow:** + - Circom + Groth16 verifier compiling and on-chain verification workflow (`@caatinga/zk`). This is treated as a niche/experimental module. +- **Agent Integrations:** + - Custom adapters or instructions for external AI coding tools/agents (e.g. `stellar-build` integrations). + +--- + +## 4. Out of Scope + +These features are explicitly rejected from Caatinga's design. Any implementation of these concepts will be deferred to downstream applications or secondary toolchains: + +- **Backend Signing:** + - Caatinga will never store private keys, manage credentials, or perform automated transaction signing on server environments. Signing is strictly delegated to client-side wallets or official Stellar CLI configurations. +- **Mainnet by Default:** + - The CLI and client will never defaults to Stellar mainnet, preventing accidental gas consumption or contract deployments. +- **Ecosystem Indexer:** + - Caatinga does not provide full indexer abstractions or query engines for ledger history. It only tracks deploy metadata. +- **Custom Web Framework:** + - Caatinga does not compile or manage a proprietary frontend runtime framework. It ends at generating client-ready TypeScript bindings and wallet adapters for standard projects. diff --git a/llms-full.txt b/llms-full.txt index bf772a2..6b23e37 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # Caatinga — LLM Reference -Caatinga is a TypeScript CLI and browser toolkit for Soroban on Stellar. It orchestrates scaffold → build → deploy → binding generation → invoke/read, with git-versioned `caatinga.artifacts.json` and no mandatory registry. Build/deploy/invoke shell out to Stellar CLI; `caatinga generate` runs `npx @stellar/stellar-sdk generate`. +Caatinga represents Deployment Orchestration + Versioned Artifacts for Soroban. It provides local, graph-aware deployment orchestration and portable, Git-versioned artifacts (`caatinga.artifacts.json`) for TypeScript teams. It orchestrates scaffold → build → deploy → binding generation → invoke/read without requiring a mandatory on-chain registry. Build/deploy/invoke shell out to Stellar CLI; `caatinga generate` runs `npx @stellar/stellar-sdk generate`. ## Install & release diff --git a/llms.txt b/llms.txt index 5e9d889..c79fe97 100644 --- a/llms.txt +++ b/llms.txt @@ -1,6 +1,6 @@ # Caatinga -> Git-versioned Soroban deploy artifacts and multi-contract orchestration for TypeScript teams. +> Deployment Orchestration + Versioned Artifacts for Soroban. ## Core docs diff --git a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/.openspec.yaml b/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/.openspec.yaml deleted file mode 100644 index 38f7628..0000000 --- a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-06-22 diff --git a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/design.md b/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/design.md deleted file mode 100644 index 058127f..0000000 --- a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/design.md +++ /dev/null @@ -1,57 +0,0 @@ -## Context - -Four independent bugs in `@caatinga/cli` and `@caatinga/core`, confirmed by reading the source: - -- **Bug #1 (HIGH):** `createProjectFromTemplate` copies template files while excluding `node_modules`, `target`, `.git`, `test_snapshots` (`TEMPLATE_COPY_EXCLUDED_DIRS`). But its post-copy pass, `replaceTemplateVariables`, recursively walks the **whole** `targetDir` with `readdir` + `stat`. When `caatinga zk init --force` merges into an existing project, `targetDir` is the project root, so the walk enters `node_modules`. `stat` follows symlinks, so a dangling `node_modules/@hot-wallet/sdk` symlink throws `ENOENT`. (`packages/core/src/templates/create-project-from-template.ts:116-137`) -- **Bug #2 (MEDIUM):** `resolveCliSource` returns `explicit ?? process.env.CAATINGA_SOURCE ?? "alice"` (`packages/core/src/contracts/source-account.ts:22`). `read` passes `options.source` straight through, and its `-s/--source` help only says "Optional ... for simulation context" — the `alice`/`CAATINGA_SOURCE` fallback is undisclosed at the point of use. -- **Bug #3 (LOW):** Both `dependenciesDiagnostic` and `configDiagnostic` return the label `Project dependencies not installed` (`project-diagnostic.ts:18`, `dependencies-diagnostic.ts:33`). Before `npm install`, `runAllDiagnostics` prints both → the line appears twice. -- **Bug #4 (MEDIUM):** `networkDiagnostic` calls `loadConfig()`; when deps are missing, `loadConfig` throws `DEPENDENCIES_NOT_INSTALLED`, which is caught and rendered as `✗ network not found` — a false cause. - -Constraint: the project ships under an "honest guardrails" ethos (recent release work). Fixes must preserve happy-path output and add test coverage alongside the existing `*.test.ts` files. - -## Goals / Non-Goals - -**Goals:** - -- `caatinga zk init --force` succeeds regardless of unrelated `node_modules`/symlink state. -- The source-identity fallback is documented and announced when used. -- `caatinga doctor` reports a missing-dependencies state exactly once and never as a phantom missing network. -- Each fix carries a focused unit test. - -**Non-Goals:** - -- Changing the default identity (`alice` stays the default) or the `CAATINGA_SOURCE` precedence. -- Reworking the diagnostics framework or doctor output ordering beyond the two targeted fixes. -- Touching `invoke`, whose `-s/--source` is already `requiredOption` (no silent fallback there). - -## Decisions - -**1. Bug #1 — exclude dirs + don't follow symlinks in `replaceTemplateVariables`.** -Reuse the existing `TEMPLATE_COPY_EXCLUDED_DIRS` set to skip excluded directories during the walk, and switch the per-entry `stat` to `lstat` so symlinks are detected and skipped (never dereferenced). This keeps the substitution scoped to real template files and makes a dangling symlink harmless. - -- _Alternative considered:_ wrap `stat` in try/catch and swallow `ENOENT`. Rejected — it would still needlessly traverse `node_modules` and could mask genuine errors. Excluding the dirs is both faster and more correct. -- _Alternative considered:_ only walk the set of files that were copied. Cleaner long-term but a larger change to the copy/substitute contract; the exclusion+lstat fix fully resolves the reported bug with minimal blast radius. - -**2. Bug #2 — disclose the fallback in `read`, not in core.** -Resolve the source in the `read` command (or a small CLI helper) so the command can emit a one-line `logger.info` notice naming the resolved identity and its origin (`explicit` → silent, `CAATINGA_SOURCE`, or `default alice`), and update the flag help text. Core's `resolveCliSource` keeps its behavior; disclosure is a CLI/UX concern and the command already owns user-facing logging. - -- _Alternative considered:_ warn from inside `resolveCliSource` in core. Rejected — core is non-interactive library code used by multiple call sites (including `read-contract.ts`); printing from there couples the library to a logger and would fire in contexts that shouldn't print. - -**3. Bug #3 — config diagnostic yields the dependencies line to the dependencies diagnostic.** -Change `configDiagnostic`'s return type to `Diagnostic | undefined` and return `undefined` for the `DEPENDENCIES_NOT_INSTALLED` branch. `runAllDiagnostics` already filters `undefined`, so `dependenciesDiagnostic` becomes the single owner of that line. - -- _Alternative considered:_ dedupe by label in `runAllDiagnostics`. Rejected — brittle string matching; the source-of-line should be explicit. - -**4. Bug #4 — skip the network check when the cause is missing dependencies / unloadable config.** -In `networkDiagnostic`, when `loadConfig` throws `DEPENDENCIES_NOT_INSTALLED` (or `CONFIG_NOT_FOUND`), return `undefined` so doctor stays silent about the network and lets the dependencies/config diagnostics carry the accurate signal. A genuinely missing network (config loads, network absent) still reports `✗ network not found`. - -## Risks / Trade-offs - -- **`lstat` skips symlinked template files that legitimately need substitution** → Templates do not ship symlinked text files; substitution targets are regular files. Acceptable, and safer than crashing. -- **A new `read` notice adds a line to output that scripts might parse** → It is a `logger.info` line on stderr-style advisory output, only when `--source` is omitted; explicit-source runs (the scripted path) stay unchanged. -- **`configDiagnostic` returning `undefined`** → Verified `runAllDiagnostics` filters `undefined` already; the doctor "ready" calculation operates on the filtered array, so no false "ready". -- **Network check skipped could hide a real config issue** → No: the config diagnostic still reports config problems; only the redundant network line is suppressed, and only for the dependency/config-load failure causes. - -## Migration Plan - -Pure bug fixes, no schema/API/dependency changes and no data migration. Ship in the next patch release with updated unit tests; rollback is a straight revert of the touched files. diff --git a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/proposal.md b/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/proposal.md deleted file mode 100644 index 2611ff2..0000000 --- a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/proposal.md +++ /dev/null @@ -1,32 +0,0 @@ -## Why - -Four reported CLI bugs undermine the "honest guardrails" promise: a high-severity crash blocks `caatinga zk init --force` in real projects, and three diagnostic/source-resolution defects mislead users about what the CLI is actually doing. They are independent root causes but share one theme — the CLI must behave predictably and report the truth before and after `npm install`. - -## What Changes - -- **Fix `caatinga zk init --force` ENOENT crash (HIGH):** `replaceTemplateVariables` walks the entire target directory after copying. When merging ZK files into an existing project the target is the project root, so the walk descends into `node_modules` and follows a broken symlink (e.g. `node_modules/@hot-wallet/sdk`) with `stat`, throwing `ENOENT`. Exclude the same directories the copy step excludes (`node_modules`, `target`, `.git`, `test_snapshots`) and stop following symlinks during the walk. -- **Make the `alice` source fallback honest (MEDIUM):** `caatinga read` (via `resolveCliSource`) silently falls back to `CAATINGA_SOURCE` or a hardcoded `alice` identity when `-s/--source` is omitted. Document the fallback in the `read` flag help and emit an informational notice naming the resolved identity and its origin (explicit / `CAATINGA_SOURCE` / default) whenever the value is not explicitly provided. -- **De-duplicate the dependencies-missing diagnostic (LOW):** Before `npm install`, both `dependenciesDiagnostic` and `configDiagnostic` print `✗ Project dependencies not installed`. Let `dependenciesDiagnostic` own that line and have `configDiagnostic` skip it when the failure is `DEPENDENCIES_NOT_INSTALLED`. -- **Stop reporting a false missing network (MEDIUM):** When dependencies are not installed, `loadConfig` cannot run, so `networkDiagnostic` falsely reports `✗ network not found`. Detect the `DEPENDENCIES_NOT_INSTALLED` / `CONFIG_NOT_FOUND` cause and skip the network check instead of blaming the network. - -No breaking changes — all four are bug fixes that preserve existing happy-path behavior. - -## Capabilities - -### New Capabilities - -- `cli-zk-scaffold`: Robustly scaffolding ZK files into a new or existing project, including `--force` merges, without crashing on unrelated project state such as `node_modules` symlinks. -- `cli-contract-source`: Transparent resolution of the Stellar source identity for `read`/`invoke`, including how defaults are chosen and disclosed to the user. -- `cli-doctor-diagnostics`: Accurate, non-duplicated `caatinga doctor` reporting that distinguishes "dependencies not installed" from unrelated config/network failures. - -### Modified Capabilities - - - -## Impact - -- `packages/core/src/templates/create-project-from-template.ts` — directory-walk exclusions and symlink handling (Bug #1). -- `packages/cli/src/commands/read.command.ts` and `packages/core/src/contracts/source-account.ts` — source-fallback disclosure (Bug #2). -- `packages/cli/src/diagnostics/project-diagnostic.ts` and `packages/cli/src/diagnostics/run-all.ts` — diagnostic de-duplication and network check guarding (Bugs #3, #4). -- Affected commands: `caatinga zk init`, `caatinga read`, `caatinga invoke`, `caatinga doctor`. -- No external API, dependency, or config-schema changes. diff --git a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-contract-source/spec.md b/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-contract-source/spec.md deleted file mode 100644 index ec4912d..0000000 --- a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-contract-source/spec.md +++ /dev/null @@ -1,28 +0,0 @@ -## ADDED Requirements - -### Requirement: Source identity fallback is disclosed - -When `caatinga read` (or any command resolving the Stellar source via `resolveCliSource`) is invoked without an explicit `-s/--source`, the CLI SHALL resolve the identity from `CAATINGA_SOURCE` or fall back to the built-in `alice` default, and MUST disclose which identity it resolved and where that value came from. The `read` command's `-s/--source` help text SHALL document that omission falls back to `CAATINGA_SOURCE` then `alice`. - -#### Scenario: read without --source uses default and announces it - -- **WHEN** a user runs `caatinga read app.hello` with no `-s/--source` flag and no `CAATINGA_SOURCE` set -- **THEN** the command uses the `alice` identity -- **AND** it prints an informational notice stating the resolved identity is `alice` and that it came from the built-in default - -#### Scenario: read without --source uses CAATINGA_SOURCE and announces it - -- **WHEN** a user runs `caatinga read app.hello` with no `-s/--source` flag while `CAATINGA_SOURCE=bob` is set -- **THEN** the command uses the `bob` identity -- **AND** it prints an informational notice stating the resolved identity is `bob` and that it came from `CAATINGA_SOURCE` - -#### Scenario: read with explicit --source is silent about defaults - -- **WHEN** a user runs `caatinga read app.hello --source carol` -- **THEN** the command uses `carol` -- **AND** it does not print a fallback notice - -#### Scenario: Help text documents the fallback - -- **WHEN** a user runs `caatinga read --help` -- **THEN** the `-s/--source` option description states that omitting it resolves `CAATINGA_SOURCE` and otherwise defaults to `alice` diff --git a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-doctor-diagnostics/spec.md b/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-doctor-diagnostics/spec.md deleted file mode 100644 index a932e28..0000000 --- a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-doctor-diagnostics/spec.md +++ /dev/null @@ -1,25 +0,0 @@ -## ADDED Requirements - -### Requirement: Missing dependencies are reported exactly once - -When project dependencies are not installed, `caatinga doctor` SHALL report `Project dependencies not installed` at most once. The dependencies diagnostic owns this line; the config diagnostic SHALL NOT emit a duplicate when the underlying failure is `DEPENDENCIES_NOT_INSTALLED`. - -#### Scenario: doctor run before npm install - -- **WHEN** a user runs `caatinga doctor` in a Caatinga project before running `npm install` -- **THEN** the line `✗ Project dependencies not installed` appears exactly once in the output - -### Requirement: Network check does not blame the network when dependencies are missing - -When the network diagnostic cannot evaluate the configured network because dependencies are not installed (or `caatinga.config.ts` cannot be loaded for that reason), `caatinga doctor` SHALL NOT report the network as "not found". It SHALL skip the network check so the missing-dependencies diagnostic remains the single, accurate signal. - -#### Scenario: doctor with --network before npm install - -- **WHEN** a user runs `caatinga doctor --network testnet` before running `npm install` -- **THEN** the output does not contain `✗ network testnet not found` -- **AND** the missing dependencies are reported instead - -#### Scenario: Genuinely missing network after dependencies are installed - -- **WHEN** a user runs `caatinga doctor --network ghostnet` with dependencies installed and `ghostnet` absent from `caatinga.config.ts` -- **THEN** the output reports `✗ network ghostnet not found` with a fix hint diff --git a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-zk-scaffold/spec.md b/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-zk-scaffold/spec.md deleted file mode 100644 index d5f2487..0000000 --- a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/specs/cli-zk-scaffold/spec.md +++ /dev/null @@ -1,21 +0,0 @@ -## ADDED Requirements - -### Requirement: ZK scaffolding ignores unrelated project state - -When scaffolding ZK files into an existing project, the CLI SHALL only process the template-derived files and MUST NOT traverse build, dependency, or VCS directories of the host project. The post-copy template-variable substitution SHALL exclude the same directories the copy step excludes (`node_modules`, `target`, `.git`, `test_snapshots`). - -#### Scenario: Project contains node_modules with a broken symlink - -- **WHEN** a user runs `caatinga zk init --force` in a project whose `node_modules/@hot-wallet/sdk` is a broken (dangling) symlink -- **THEN** the command completes successfully and adds the ZK circuit and verifier scaffold -- **AND** it does not throw an `ENOENT` error from descending into `node_modules` - -### Requirement: Directory walk does not follow symlinks - -The template-variable substitution walk SHALL NOT follow symbolic links, so a dangling or cyclic symlink anywhere under the target directory cannot crash the command. - -#### Scenario: Dangling symlink outside node_modules - -- **WHEN** the project contains a dangling symlink in a directory that is otherwise eligible for substitution -- **THEN** the walk skips the symlink without attempting to read its missing target -- **AND** the command does not fail with `ENOENT` diff --git a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/tasks.md b/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/tasks.md deleted file mode 100644 index 2bdd989..0000000 --- a/openspec/changes/archive/2026-06-21-fix-cli-bugs-batch/tasks.md +++ /dev/null @@ -1,26 +0,0 @@ -## 1. Bug #1 — zk init ENOENT on node_modules symlink - -- [x] 1.1 In `packages/core/src/templates/create-project-from-template.ts`, make `replaceTemplateVariables` skip `TEMPLATE_COPY_EXCLUDED_DIRS` directories and use `lstat` instead of `stat` so symlinks are detected and skipped (not dereferenced). -- [x] 1.2 Add a unit test (alongside existing template tests) proving `zk init --force` / template substitution succeeds when the target dir contains a dangling symlink under `node_modules`. - -## 2. Bug #2 — disclose the source-identity fallback - -- [x] 2.1 In `packages/cli/src/commands/read.command.ts`, update the `-s/--source` help text to state that omitting it resolves `CAATINGA_SOURCE`, otherwise defaults to `alice`. -- [x] 2.2 Emit an informational `logger` notice naming the resolved identity and its origin (`CAATINGA_SOURCE` or built-in default) when `--source` is omitted; stay silent when it is explicit. -- [x] 2.3 Extend `read.command.test.ts` to cover: default→notice, `CAATINGA_SOURCE`→notice, explicit→no notice. - -## 3. Bug #3 — single "dependencies not installed" line - -- [x] 3.1 In `packages/cli/src/diagnostics/project-diagnostic.ts`, change `configDiagnostic` to return `Diagnostic | undefined` and return `undefined` for the `DEPENDENCIES_NOT_INSTALLED` branch. -- [x] 3.2 Confirm `run-all.ts` filters `undefined` (it does) and adjust types if needed. -- [x] 3.3 Add/extend a diagnostics test asserting the dependencies line appears exactly once before `npm install`. - -## 4. Bug #4 — no false "network not found" before install - -- [x] 4.1 In `networkDiagnostic` (`project-diagnostic.ts`), when `loadConfig` throws `DEPENDENCIES_NOT_INSTALLED` or `CONFIG_NOT_FOUND`, return `undefined` instead of reporting the network as missing. -- [x] 4.2 Add tests: deps-missing → no network line; config loads but network absent → still reports `✗ network not found`. - -## 5. Verification - -- [x] 5.1 Run the CLI and core test suites for the touched packages and ensure they pass. -- [x] 5.2 Run lint/typecheck for `packages/cli` and `packages/core`. diff --git a/openspec/changes/cli-security-dx-improvements/.openspec.yaml b/openspec/changes/cli-security-dx-improvements/.openspec.yaml deleted file mode 100644 index a4ac4d7..0000000 --- a/openspec/changes/cli-security-dx-improvements/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-06-23 diff --git a/openspec/changes/cli-security-dx-improvements/design.md b/openspec/changes/cli-security-dx-improvements/design.md deleted file mode 100644 index ca75cb4..0000000 --- a/openspec/changes/cli-security-dx-improvements/design.md +++ /dev/null @@ -1,81 +0,0 @@ -## Context - -O `@caatinga/cli` é uma camada fina sobre `@caatinga/core` usando Commander. Subprocessos são executados via `execa` e `runCommand`. A análise de segurança e DX revelou 17 achados. A maioria das correções é localizada em arquivos individuais, sem necessidade de mudanças arquiteturais profundas. - -## Goals / Non-Goals - -**Goals:** - -- Eliminar `curl | sh` sem verificação no setup -- Adicionar timeouts/cancel em todos os subprocessos longos -- Validar inputs de usuário que fluem para comandos externos -- Substituir semver caseiro por `semver` library -- Remover `process.exit()` abrupto do preflight -- Adicionar progresso visual em build, deploy e setup -- Substituir regex de config merge por AST-safe -- Eliminar redundâncias (double loadConfig, hardcoded versions) -- Adicionar validação de formato em `contractId` no rollback - -**Non-Goals:** - -- Refatorar a arquitetura Commander -- Migrar para outro framework de CLI -- Adicionar testes (já existem 22 arquivos de teste) -- Mudar API pública do `@caatinga/core` — mudanças mínimas, preferir novas exports - -## Decisions - -### D1 — `curl | sh` → download com checksum verification - -- **Alternativas**: Usar `https.get` + `crypto.createHash('sha256')` para verificar checksum antes de executar -- **Rationale**: O `curl | sh` é o método oficial do rustup, mas adicionar verificação de checksum mitiga risco de supply chain se CDN for comprometido -- **Implementação**: Criar `downloadAndVerify` em `@caatinga/core` ou manter no CLI com `node:https` + `node:crypto` - -### D2 — Subprocess timeouts via `execa` + `AbortController` - -- **Alternativas**: `execa` nativamente suporta `cancelSignal` e `timeout` -- **Rationale**: Mínima mudança de código, sem novas dependências -- **Implementação**: Envolver cada `execa()` com `AbortSignal.timeout(5 * 60 * 1000)` para operações longas (cargo install, rustup update) - -### D3 — Semver library - -- **Alternativas**: `semver` package (35M weekly downloads, 0 deps) -- **Rationale**: Substitui ~50 linhas de código frágil por lib testada -- **Implementação**: Adicionar `semver` em `dependencies` do CLI, substituir `parseSemver` e `semverAtLeast` - -### D4 — Config merge via AST em vez de regex - -- **Alternativas**: `ts-morph` (AST completo), regex fixa (atual) -- **Rationale**: `ts-morph` é complexo demais para esse caso. Alternativa: usar `typescript` compiler API para parse e pretty-print. Mas como o `caatinga.config.ts` segue template padronizado, podemos melhorar o regex ou usar `jsonc` se migrarmos para JSON config -- **Decidido**: Melhorar o regex para ser mais tolerante a espaços e formatação; documentar o padrão esperado. Se surgirem mais casos, migrar para `ts-morph` - -### D5 — Progresso visual - -- **Alternativas**: `ora` (spinner), `cli-progress` (barra), stdout manual -- **Rationale**: `createZkInstallProgress` já implementa pattern de progress callback. Seguir o mesmo pattern para build, deploy e setup -- **Implementação**: Adicionar callbacks de progresso nos métodos do core, consumir no CLI com logger - -### D6 — `process.exit(1)` → `process.exitCode = 1` - -- **Alternativas**: Manter `process.exit()` (atual), trocar para exitCode -- **Rationale**: Consistência com o resto do código. `process.exitCode` permite que pending work termine -- **Implementação**: Substituir `process.exit(1)` por `process.exitCode = 1` + `return` - -### D7 — Validação de contractId - -- **Alternativas**: Regex `C[A-Z2-7]{55}` (StrKey Soroban), hex 64-char, lib `stellar-sdk` -- **Rationale**: Validar formato de contract ID (C... ou hex) antes de passar para core -- **Implementação**: Regex `^(C[A-Z2-7]{55}|[0-9a-fA-F]{64})$` no comando rollback - -### D8 — Double loadConfig no doctor - -- **Alternativas**: Passar config como parâmetro, lazy load -- **Rationale**: Remover segunda chamada `loadConfig` e reusar config já carregada por `runAllDiagnostics` -- **Implementação**: Modificar `runAllDiagnostics` para retornar config também - -## Risks / Trade-offs - -- **Timeout em `cargo install`**: 5 minutos pode ser pouco em hardware lento. Usar timeout configurável via env var `CAATINGA_SUBPROCESS_TIMEOUT` com fallback 10 min -- **Checksum verification do rustup**: O SHA256 muda a cada release do rustup. Precisamos manter o checksum atualizado. Alternativa: verificar assinatura GPG em vez de checksum fixo -- **AST-safe config merge**: Se o usuário tiver um config muito customizado, mesmo o AST melhorado pode falhar. Manter `--force` como escape hatch -- **Progresso em build**: WASM compilation não emite callbacks de progresso. O máximo que podemos fazer é mostrar "Compilando..." com spinner até terminar diff --git a/openspec/changes/cli-security-dx-improvements/proposal.md b/openspec/changes/cli-security-dx-improvements/proposal.md deleted file mode 100644 index 33e7a23..0000000 --- a/openspec/changes/cli-security-dx-improvements/proposal.md +++ /dev/null @@ -1,42 +0,0 @@ -## Why - -A atualização do `@caatinga/cli` introduziu novos comandos e funcionalidades, mas a análise de segurança e DX revelou 17 pontos críticos: desde `curl | sh` sem verificação até config manipulado com regex frágil. Esses problemas afetam confiabilidade em produção e experiência do desenvolvedor. Precisamos corrigi-los antes da próxima release. - -## What Changes - -- Substituir `curl | sh` por download com verificação no setup -- Adicionar timeouts em subprocessos longos -- Validar formato de `contractId` no rollback -- Substituir semver caseiro por `semver` library -- Remover `allowUnknownOption` e validar args explicitamente no invoke/read -- Adicionar progresso visual em build, deploy e setup -- Trocar `process.exit()` por `process.exitCode` no preflight -- Substituir regex frágil por AST-safe config merge no zk init -- Consertar double loadConfig no doctor -- Remover hardcoded version strings -- Adicionar validação de formato em `--to` no rollback -- Adicionar `cancelSignal` e timeout em todos os `execa` subprocessos - -## Capabilities - -### New Capabilities - -- `secure-subprocess`: Safe subprocess execution with timeouts, cancel signals, and verified downloads -- `cli-input-validation`: Input validation for forwarded args (invoke/read) and contract IDs (rollback) -- `setup-user-experience`: Progress bars/indicators for long operations and better error feedback -- `config-merge-safety`: Safe, AST-based config manipulation replacing fragile regex in zk-init -- `doctor-reliability`: Single loadConfig, skip transparency, and version freshness -- `template-resolution`: Safer and more predictable template resolution strategy - -### Modified Capabilities - -- `cli-zk-scaffold`: zk init config merge logic changes from regex to AST-safe approach - -## Impact - -- **packages/cli/src/commands/**: setup, deploy, invoke, read, rollback, zk-init, doctor, estimate, build -- **packages/cli/src/utils/**: errors, preflight, template-path, zk-guardrails -- **packages/cli/src/diagnostics/**: project, stellar, dependencies -- **packages/cli/package.json**: new dependency on `semver` -- **packages/cli/src/**: version.ts -- **@caatinga/core**: may need new exports for subprocess with timeout/cancel diff --git a/openspec/changes/cli-security-dx-improvements/specs/cli-input-validation/spec.md b/openspec/changes/cli-security-dx-improvements/specs/cli-input-validation/spec.md deleted file mode 100644 index f469b38..0000000 --- a/openspec/changes/cli-security-dx-improvements/specs/cli-input-validation/spec.md +++ /dev/null @@ -1,29 +0,0 @@ -## ADDED Requirements - -### Requirement: Contract ID validation on rollback - -The `rollback` command SHALL validate `--to ` format before calling core. - -#### Scenario: valid contract ID - -- **WHEN** user runs `caatinga rollback app --to CABC123...` (56-char Soroban contract StrKey) or a 64-char hex contract ID -- **THEN** the contract ID passes format validation - -#### Scenario: invalid contract ID format - -- **WHEN** user runs `caatinga rollback app --to invalid` -- **THEN** the CLI prints a validation error and does not call core - -### Requirement: Explicit arg validation for invoke/read - -The `invoke` and `read` commands SHALL validate forwarded arguments instead of using `allowUnknownOption`/`allowExcessArguments`. - -#### Scenario: invoke with valid args - -- **WHEN** user runs `caatinga invoke app.method --arg value` -- **THEN** known flags are parsed by Commander and extra args are validated and forwarded to stellar CLI - -#### Scenario: invoke with obviously invalid flag - -- **WHEN** user runs `caatinga invoke app.method --typo` -- **THEN** Commander catches the unknown flag and prints a validation error diff --git a/openspec/changes/cli-security-dx-improvements/specs/cli-zk-scaffold/spec.md b/openspec/changes/cli-security-dx-improvements/specs/cli-zk-scaffold/spec.md deleted file mode 100644 index 29a1c49..0000000 --- a/openspec/changes/cli-security-dx-improvements/specs/cli-zk-scaffold/spec.md +++ /dev/null @@ -1,25 +0,0 @@ -## ADDED Requirements - -### Requirement: Safe config merge - -When adding ZK config to `caatinga.config.ts`, the CLI SHALL use a non-fragile merge strategy (improved regex or AST-based) that tolerates formatting variations. - -#### Scenario: add zk config to standard config - -- **WHEN** user runs `caatinga zk init` in a project with a standard `caatinga.config.ts` -- **THEN** ZK circuit and verifier config sections are injected without corrupting existing content - -#### Scenario: zk config already present - -- **WHEN** user runs `caatinga zk init` and ZK config already exists -- **THEN** the config file is not modified - -#### Scenario: non-standard formatting - -- **WHEN** `caatinga.config.ts` has non-standard spacing or formatting -- **THEN** the merge handles it gracefully or prints manual instructions instead of corrupting the file - -#### Scenario: merge error fallback - -- **WHEN** the merge logic cannot safely modify the config -- **THEN** the CLI prints clear instructions to manually add the ZK config section and does NOT modify the file diff --git a/openspec/changes/cli-security-dx-improvements/specs/config-merge-safety/spec.md b/openspec/changes/cli-security-dx-improvements/specs/config-merge-safety/spec.md deleted file mode 100644 index 93ebc4a..0000000 --- a/openspec/changes/cli-security-dx-improvements/specs/config-merge-safety/spec.md +++ /dev/null @@ -1,25 +0,0 @@ -## ADDED Requirements - -### Requirement: Safe config merge in zk init - -The `caatinga zk init` command SHALL merge ZK configuration into `caatinga.config.ts` without fragile regex. - -#### Scenario: add zk config to clean config - -- **WHEN** user runs `caatinga zk init` in a project with a standard `caatinga.config.ts` -- **THEN** the ZK circuit and verifier config sections are added without corrupting existing content - -#### Scenario: zk config already present - -- **WHEN** user runs `caatinga zk init` and ZK config already exists -- **THEN** the config is not modified - -#### Scenario: non-standard config formatting - -- **WHEN** user runs `caatinga zk init` and `caatinga.config.ts` has non-standard spacing -- **THEN** the merge still works correctly (toastable regex or AST-based approach) - -#### Scenario: merge failure fallback - -- **WHEN** the merge logic cannot safely modify the config -- **THEN** the CLI prints instructions to manually add ZK config and does NOT modify the file diff --git a/openspec/changes/cli-security-dx-improvements/specs/doctor-reliability/spec.md b/openspec/changes/cli-security-dx-improvements/specs/doctor-reliability/spec.md deleted file mode 100644 index 9c492ff..0000000 --- a/openspec/changes/cli-security-dx-improvements/specs/doctor-reliability/spec.md +++ /dev/null @@ -1,33 +0,0 @@ -## ADDED Requirements - -### Requirement: Single config load in doctor - -The `doctor` command SHALL load `caatinga.config.ts` exactly once. - -#### Scenario: doctor runs diagnostics - -- **WHEN** `caatinga doctor` runs -- **THEN** `loadConfig` is called once and the result is reused for all diagnostics that need it - -### Requirement: Diagnostic skip transparency - -When a diagnostic is skipped (returns `undefined`), the user SHALL be informed. - -#### Scenario: network check skipped - -- **WHEN** `--network` is not provided to doctor -- **THEN** the network diagnostic is clearly skipped with a note saying "pass --network to validate" - -#### Scenario: source check skipped - -- **WHEN** `--source` is not provided to doctor -- **THEN** the source identity diagnostic is skipped with a note saying "pass --source to validate" - -### Requirement: Version freshness - -Stellar CLI and SDK version references SHALL use constants, not hardcoded strings. - -#### Scenario: doctor shows Stellar CLI version - -- **WHEN** doctor runs and Stellar CLI version is below minimum -- **THEN** the fix message references `STELLAR_CLI_LAST_TESTED_VERSION` constant, not a hardcoded string diff --git a/openspec/changes/cli-security-dx-improvements/specs/secure-subprocess/spec.md b/openspec/changes/cli-security-dx-improvements/specs/secure-subprocess/spec.md deleted file mode 100644 index f9e1d1a..0000000 --- a/openspec/changes/cli-security-dx-improvements/specs/secure-subprocess/spec.md +++ /dev/null @@ -1,46 +0,0 @@ -## ADDED Requirements - -### Requirement: Verified downloads in setup - -The CLI SHALL download and verify tools before execution instead of piping unverified shell scripts. - -#### Scenario: rustup download with checksum - -- **WHEN** `caatinga setup` needs to install Rust -- **THEN** the CLI downloads `rustup-init` via `https.get` and verifies SHA256 checksum before executing - -#### Scenario: checksum mismatch - -- **WHEN** the downloaded file checksum does not match the expected value -- **THEN** the CLI SHALL NOT execute the file and SHALL print an error with the mismatch details - -### Requirement: Subprocess timeouts - -All `execa` subprocess calls SHALL have a configurable timeout and cancel signal. - -#### Scenario: cargo install times out - -- **WHEN** `cargo install --locked stellar-cli` exceeds the timeout -- **THEN** the subprocess is killed and a timeout error is printed - -#### Scenario: timeout env var - -- **WHEN** `CAATINGA_SUBPROCESS_TIMEOUT` env var is set -- **THEN** the timeout value is read from the env var (in milliseconds) -- **WHEN** the env var is not set -- **THEN** timeout defaults to 600000ms (10 minutes) - -### Requirement: Exit code consistency - -The CLI SHALL use `process.exitCode = 1` instead of `process.exit(1)` everywhere. - -#### Scenario: preflight failure - -- **WHEN** `assertPreflight()` detects Node.js below minimum version -- **THEN** the CLI prints the error and sets `process.exitCode = 1` without calling `process.exit()` - -#### Scenario: global catch - -- **WHEN** an unhandled error reaches the global catch in `index.ts` -- **THEN** the error is logged via `runCliAction` error formatter and `process.exitCode = 1` -- **THEN** the error object is NOT logged directly via `console.error` diff --git a/openspec/changes/cli-security-dx-improvements/specs/setup-user-experience/spec.md b/openspec/changes/cli-security-dx-improvements/specs/setup-user-experience/spec.md deleted file mode 100644 index 4b943ca..0000000 --- a/openspec/changes/cli-security-dx-improvements/specs/setup-user-experience/spec.md +++ /dev/null @@ -1,34 +0,0 @@ -## ADDED Requirements - -### Requirement: Progress indicators for long operations - -Build, deploy, and setup steps SHALL show visual progress indicators. - -#### Scenario: setup with progress - -- **WHEN** `caatinga setup` installs Rust, WASM target, or Stellar CLI -- **THEN** each step shows a spinner or progress message - -#### Scenario: build with feedback - -- **WHEN** `caatinga build` compiles WASM -- **THEN** the CLI shows a status message indicating compilation is in progress - -#### Scenario: deploy retry progress - -- **WHEN** deploy hits a transient error and retries -- **THEN** the CLI shows retry count and cooldown timer - -### Requirement: Semver validation - -The `setup` command SHALL use the `semver` npm package for version comparison instead of custom parsing. - -#### Scenario: version comparison - -- **WHEN** comparing Rust version `1.80.0-beta` against minimum `1.75.0` -- **THEN** `semver` correctly handles the pre-release tag - -#### Scenario: semver dependency - -- **WHEN** `@caatinga/cli` is installed -- **THEN** `semver` is available as a runtime dependency diff --git a/openspec/changes/cli-security-dx-improvements/specs/template-resolution/spec.md b/openspec/changes/cli-security-dx-improvements/specs/template-resolution/spec.md deleted file mode 100644 index fa939c1..0000000 --- a/openspec/changes/cli-security-dx-improvements/specs/template-resolution/spec.md +++ /dev/null @@ -1,20 +0,0 @@ -## ADDED Requirements - -### Requirement: Deterministic template resolution - -Template resolution SHALL follow a predictable, documented order without walking up 8 directory levels. - -#### Scenario: template found in env dir - -- **WHEN** `CAATINGA_TEMPLATES_DIR` is set and contains the template -- **THEN** the template is resolved from that directory first - -#### Scenario: template in packaged location - -- **WHEN** no env var is set and the template is in the CLI's `templates/` directory -- **THEN** the template is resolved from the packaged location - -#### Scenario: debug output via logger - -- **WHEN** `CAATINGA_DEBUG_TEMPLATE_RESOLUTION=1` -- **THEN** resolution debug output uses the logger (not raw `process.stderr`) diff --git a/openspec/changes/cli-security-dx-improvements/tasks.md b/openspec/changes/cli-security-dx-improvements/tasks.md deleted file mode 100644 index cfb427b..0000000 --- a/openspec/changes/cli-security-dx-improvements/tasks.md +++ /dev/null @@ -1,46 +0,0 @@ -## 1. Subprocess Security - -- [x] 1.1 Add `semver` dependency to `packages/cli/package.json` -- [x] 1.2 Replace `parseSemver`/`semverAtLeast` with `semver.satisfies` / `semver.gte` in `setup.command.ts` -- [x] 1.3 Add `AbortSignal.timeout()` and `cancelSignal` to all `execa()` calls in `setup.command.ts` -- [x] 1.4 Add configurable timeout env var `CAATINGA_SUBPROCESS_TIMEOUT` reading in setup -- [x] 1.5 Replace `curl | sh` pipe with `https.get` + SHA256 checksum verification for rustup - -## 2. Input Validation - -- [x] 2.1 Add Stellar contract ID format validation (`^C[A-Z2-7]{55}$` or 64-char hex) to `rollback.command.ts --to` -- [x] 2.2 Remove `.allowUnknownOption(true)` and `.allowExcessArguments(true)` from `invoke.command.ts` -- [x] 2.3 Add explicit validated arg forwarding in `invoke.command.ts` -- [x] 2.4 Remove `.allowUnknownOption(true)` and `.allowExcessArguments(true)` from `read.command.ts` -- [x] 2.5 Add explicit validated arg forwarding in `read.command.ts` - -## 3. Process Exit Cleanup - -- [x] 3.1 Replace `process.exit(1)` with `process.exitCode = 1` in `preflight.ts` -- [x] 3.2 Replace `console.error(error)` with structured error formatting in `index.ts` global catch - -## 4. Config Merge Safety (zk init) - -- [x] 4.1 Improve `mergeZkIntoConfigSource()` regex to be whitespace-tolerant -- [x] 4.2 Add fallback: print manual instructions when merge cannot safely modify config -- [x] 4.3 Add test for non-standard config formatting in zk-init - -## 5. Doctor Reliability - -- [x] 5.1 Refactor `runAllDiagnostics` to return the loaded config alongside diagnostics -- [x] 5.2 Remove second `loadConfig()` call in `doctor.command.ts` and reuse config from `runAllDiagnostics` -- [x] 5.3 Add skip transparency messages when `networkDiagnostic` returns undefined -- [x] 5.4 Add skip transparency messages when `sourceDiagnostic` returns undefined -- [x] 5.5 Replace hardcoded Stellar CLI version string in `stellar-diagnostic.ts` with `STELLAR_CLI_LAST_TESTED_VERSION` constant - -## 6. Setup UX - -- [x] 6.1 Add status/progress callbacks to Rust install step -- [x] 6.2 Add status/progress callbacks to WASM target install step -- [x] 6.3 Add status/progress callbacks to Stellar CLI install step -- [x] 6.4 Remove unused `results.push()` that accumulates data never displayed in `runSetup()` — nodeResult now included - -## 7. Template Resolution - -- [x] 7.1 Reduce directory walk depth from 8 to 5 in `template-path.ts` (safe minimum for monorepo layout) -- [x] 7.2 Replace `process.stderr.write()` with `logger.muted()` in debug template resolution diff --git a/openspec/changes/edge-gaps-resolution/.openspec.yaml b/openspec/changes/edge-gaps-resolution/.openspec.yaml deleted file mode 100644 index 0dfcb71..0000000 --- a/openspec/changes/edge-gaps-resolution/.openspec.yaml +++ /dev/null @@ -1,4 +0,0 @@ -change: - id: edge-gaps-resolution - title: Edge Gaps Resolution - status: in_progress diff --git a/openspec/changes/edge-gaps-resolution/proposal.md b/openspec/changes/edge-gaps-resolution/proposal.md deleted file mode 100644 index 13939a7..0000000 --- a/openspec/changes/edge-gaps-resolution/proposal.md +++ /dev/null @@ -1,25 +0,0 @@ -# Edge Gaps Resolution - -> Resolves GAP-01 through GAP-10 from `CAATINGA_EDGE_GAPS.md`. - -**Date:** 2026-07-04 -**Status:** In progress - -## Overview - -Structural postDeploy expects, smoke/read parity, Address alias resolution, doctor strict checks, regression pipeline, CI testnet, and multi-network scaffold. - -## Changes - -| Gap | Solution | -| ------ | ------------------------------------------------------------- | -| GAP-01 | Expect DSL with structural matchers; postDeployRead vs invoke | -| GAP-02 | Shared verifyExpect; `read --expect`; `caatinga smoke` | -| GAP-03 | resolveMethodArgs for CLI alias → Address | -| GAP-04 | `deploy --if-changed`; `caatinga regression`; CI deploy job | -| GAP-05 | Doctor env vs artifacts; `--strict-env` | -| GAP-06 | Exit codes for stale bindings in status/doctor | -| GAP-07 | `read --quiet/--summary`; ephemeral ID guide | -| GAP-08 | App boundary docs and checklist | -| GAP-09 | `caatinga ci`; identity export/import | -| GAP-10 | Multi-network scaffold; `doctor --all-networks` | diff --git a/openspec/config.yaml b/openspec/config.yaml deleted file mode 100644 index 392946c..0000000 --- a/openspec/config.yaml +++ /dev/null @@ -1,20 +0,0 @@ -schema: spec-driven - -# Project context (optional) -# This is shown to AI when creating artifacts. -# Add your tech stack, conventions, style guides, domain knowledge, etc. -# Example: -# context: | -# Tech stack: TypeScript, React, Node.js -# We use conventional commits -# Domain: e-commerce platform - -# Per-artifact rules (optional) -# Add custom rules for specific artifacts. -# Example: -# rules: -# proposal: -# - Keep proposals under 500 words -# - Always include a "Non-goals" section -# tasks: -# - Break tasks into chunks of max 2 hours diff --git a/openspec/specs/cli-contract-source/spec.md b/openspec/specs/cli-contract-source/spec.md deleted file mode 100644 index 8e61bdd..0000000 --- a/openspec/specs/cli-contract-source/spec.md +++ /dev/null @@ -1,34 +0,0 @@ -# cli-contract-source Specification - -## Purpose - -Transparent resolution of the Stellar source identity for `read`/`invoke`, including how defaults are chosen and disclosed to the user. - -## Requirements - -### Requirement: Source identity fallback is disclosed - -When `caatinga read` (or any command resolving the Stellar source via `resolveCliSource`) is invoked without an explicit `-s/--source`, the CLI SHALL resolve the identity from `CAATINGA_SOURCE` or fall back to the built-in `alice` default, and MUST disclose which identity it resolved and where that value came from. The `read` command's `-s/--source` help text SHALL document that omission falls back to `CAATINGA_SOURCE` then `alice`. - -#### Scenario: read without --source uses default and announces it - -- **WHEN** a user runs `caatinga read app.hello` with no `-s/--source` flag and no `CAATINGA_SOURCE` set -- **THEN** the command uses the `alice` identity -- **AND** it prints an informational notice stating the resolved identity is `alice` and that it came from the built-in default - -#### Scenario: read without --source uses CAATINGA_SOURCE and announces it - -- **WHEN** a user runs `caatinga read app.hello` with no `-s/--source` flag while `CAATINGA_SOURCE=bob` is set -- **THEN** the command uses the `bob` identity -- **AND** it prints an informational notice stating the resolved identity is `bob` and that it came from `CAATINGA_SOURCE` - -#### Scenario: read with explicit --source is silent about defaults - -- **WHEN** a user runs `caatinga read app.hello --source carol` -- **THEN** the command uses `carol` -- **AND** it does not print a fallback notice - -#### Scenario: Help text documents the fallback - -- **WHEN** a user runs `caatinga read --help` -- **THEN** the `-s/--source` option description states that omitting it resolves `CAATINGA_SOURCE` and otherwise defaults to `alice` diff --git a/openspec/specs/cli-doctor-diagnostics/spec.md b/openspec/specs/cli-doctor-diagnostics/spec.md deleted file mode 100644 index 7adfd6d..0000000 --- a/openspec/specs/cli-doctor-diagnostics/spec.md +++ /dev/null @@ -1,31 +0,0 @@ -# cli-doctor-diagnostics Specification - -## Purpose - -Accurate, non-duplicated `caatinga doctor` reporting that distinguishes "dependencies not installed" from unrelated config/network failures. - -## Requirements - -### Requirement: Missing dependencies are reported exactly once - -When project dependencies are not installed, `caatinga doctor` SHALL report `Project dependencies not installed` at most once. The dependencies diagnostic owns this line; the config diagnostic SHALL NOT emit a duplicate when the underlying failure is `DEPENDENCIES_NOT_INSTALLED`. - -#### Scenario: doctor run before npm install - -- **WHEN** a user runs `caatinga doctor` in a Caatinga project before running `npm install` -- **THEN** the line `✗ Project dependencies not installed` appears exactly once in the output - -### Requirement: Network check does not blame the network when dependencies are missing - -When the network diagnostic cannot evaluate the configured network because dependencies are not installed (or `caatinga.config.ts` cannot be loaded for that reason), `caatinga doctor` SHALL NOT report the network as "not found". It SHALL skip the network check so the missing-dependencies diagnostic remains the single, accurate signal. - -#### Scenario: doctor with --network before npm install - -- **WHEN** a user runs `caatinga doctor --network testnet` before running `npm install` -- **THEN** the output does not contain `✗ network testnet not found` -- **AND** the missing dependencies are reported instead - -#### Scenario: Genuinely missing network after dependencies are installed - -- **WHEN** a user runs `caatinga doctor --network ghostnet` with dependencies installed and `ghostnet` absent from `caatinga.config.ts` -- **THEN** the output reports `✗ network ghostnet not found` with a fix hint diff --git a/openspec/specs/cli-zk-scaffold/spec.md b/openspec/specs/cli-zk-scaffold/spec.md deleted file mode 100644 index 2d40f02..0000000 --- a/openspec/specs/cli-zk-scaffold/spec.md +++ /dev/null @@ -1,27 +0,0 @@ -# cli-zk-scaffold Specification - -## Purpose - -Robustly scaffolding ZK files into a new or existing project, including `--force` merges, without crashing on unrelated project state such as `node_modules` symlinks. - -## Requirements - -### Requirement: ZK scaffolding ignores unrelated project state - -When scaffolding ZK files into an existing project, the CLI SHALL only process the template-derived files and MUST NOT traverse build, dependency, or VCS directories of the host project. The post-copy template-variable substitution SHALL exclude the same directories the copy step excludes (`node_modules`, `target`, `.git`, `test_snapshots`). - -#### Scenario: Project contains node_modules with a broken symlink - -- **WHEN** a user runs `caatinga zk init --force` in a project whose `node_modules/@hot-wallet/sdk` is a broken (dangling) symlink -- **THEN** the command completes successfully and adds the ZK circuit and verifier scaffold -- **AND** it does not throw an `ENOENT` error from descending into `node_modules` - -### Requirement: Directory walk does not follow symlinks - -The template-variable substitution walk SHALL NOT follow symbolic links, so a dangling or cyclic symlink anywhere under the target directory cannot crash the command. - -#### Scenario: Dangling symlink outside node_modules - -- **WHEN** the project contains a dangling symlink in a directory that is otherwise eligible for substitution -- **THEN** the walk skips the symlink without attempting to read its missing target -- **AND** the command does not fail with `ENOENT` diff --git a/packages/cli/src/commands/version.command.ts b/packages/cli/src/commands/version.command.ts new file mode 100644 index 0000000..6393afe --- /dev/null +++ b/packages/cli/src/commands/version.command.ts @@ -0,0 +1,15 @@ +import { type Command } from "commander"; +import { CAATINGA_CLI_VERSION } from "../version.js"; +import { logger } from "../utils/logger.js"; +import { runCliAction } from "../utils/errors.js"; + +export function registerVersionCommand(program: Command): void { + program + .command("version") + .description("Show the version of Caatinga CLI") + .action(() => { + runCliAction(async () => { + logger.info(`@caatinga/cli: ${CAATINGA_CLI_VERSION}`); + }); + }); +} diff --git a/packages/cli/src/program.test.ts b/packages/cli/src/program.test.ts index f973378..469f853 100644 --- a/packages/cli/src/program.test.ts +++ b/packages/cli/src/program.test.ts @@ -4,6 +4,7 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import { afterEach, describe, expect, it, vi } from "vitest"; import { createProgram } from "./program.js"; +import chalk from "chalk"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -39,6 +40,7 @@ describe("createProgram", () => { "regression", "ci", "identity", + "version", ]) ); }); @@ -89,7 +91,33 @@ describe("createProgram", () => { "--template", "react-vite-counter", ]); - expect(logSpy).toHaveBeenCalledWith(" npx caatinga build counter"); + expect(logSpy).toHaveBeenCalledWith(`${chalk.blue("ℹ")} npx caatinga build counter`); + } finally { + logSpy.mockRestore(); + } + }); + + it("formats the help output categorized by domains", () => { + const program = createProgram(); + const helpInformation = program.helpInformation(); + + expect(helpInformation).toContain("Scaffolding & Setup:"); + expect(helpInformation).toContain("Deployment & Lifecycle:"); + expect(helpInformation).toContain("Query & Execution:"); + expect(helpInformation).toContain("Status & Diagnostics:"); + expect(helpInformation).toContain("Zero-Knowledge (ZK) Proofs:"); + expect(helpInformation).toContain("Automation & CI:"); + }); + + it("prints CLI version via version command", async () => { + const packageJson = JSON.parse( + await readFile(path.resolve(__dirname, "../package.json"), "utf8") + ) as { version: string }; + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + try { + await createProgram().exitOverride().parseAsync(["node", "caatinga", "version"]); + expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(`@caatinga/cli: ${packageJson.version}`)); } finally { logSpy.mockRestore(); } diff --git a/packages/cli/src/program.ts b/packages/cli/src/program.ts index 89d266b..63f4892 100644 --- a/packages/cli/src/program.ts +++ b/packages/cli/src/program.ts @@ -24,6 +24,7 @@ import { registerSmokeCommand } from "./commands/smoke.command.js"; import { registerRegressionCommand } from "./commands/regression.command.js"; import { registerCiCommand } from "./commands/ci.command.js"; import { registerIdentityCommand } from "./commands/identity.command.js"; +import { registerVersionCommand } from "./commands/version.command.js"; import { CAATINGA_CLI_VERSION } from "./version.js"; export function createProgram(): Command { @@ -32,7 +33,70 @@ export function createProgram(): Command { program .name("caatinga") .description("Developer toolkit for Stellar/Soroban dApps") - .version(CAATINGA_CLI_VERSION); + .version(CAATINGA_CLI_VERSION, "-v, --version", "Output the current version") + .configureHelp({ + formatHelp(cmd, helper) { + if (cmd.name() !== "caatinga") { + return helper.formatHelp(cmd, helper); + } + + const categories: Record = { + "Scaffolding & Setup": ["init", "setup", "identity"], + "Build & Compilation": ["build"], + "Deployment & Lifecycle": ["deploy", "upgrade", "rollback", "wire"], + "Query & Execution": ["read", "invoke", "estimate", "dev"], + "Status & Diagnostics": ["status", "inspect", "doctor", "sync-env", "migrate", "version"], + "Zero-Knowledge (ZK) Proofs": ["zk"], + "Automation & CI": ["smoke", "regression", "ci"], + }; + + const commandList = cmd.commands; + const result: string[] = []; + + result.push(helper.commandDescription(cmd)); + result.push(""); + result.push(helper.commandUsage(cmd)); + result.push(""); + result.push("Commands (by domain):"); + + for (const [groupName, commandNames] of Object.entries(categories)) { + const matchedCommands = commandList.filter((c) => commandNames.includes(c.name())); + if (matchedCommands.length > 0) { + result.push(`\n ${groupName}:`); + for (const subCmd of matchedCommands) { + const term = subCmd.name(); + const description = helper.commandDescription(subCmd); + result.push(` ${term.padEnd(20)} ${description}`); + } + } + } + + const allConfiguredNames = Object.values(categories).flat(); + const otherCommands = commandList.filter((c) => !allConfiguredNames.includes(c.name())); + if (otherCommands.length > 0) { + result.push(`\n Other Commands:`); + for (const subCmd of otherCommands) { + const term = subCmd.name(); + const description = helper.commandDescription(subCmd); + result.push(` ${term.padEnd(20)} ${description}`); + } + } + + result.push(""); + const optionTermLength = helper.longestOptionTermLength(cmd, helper); + if (optionTermLength > 0) { + result.push("Options:"); + const visibleOptions = helper.visibleOptions(cmd); + for (const option of visibleOptions) { + const term = helper.optionTerm(option); + const description = helper.optionDescription(option); + result.push(` ${term.padEnd(optionTermLength + 2)} ${description}`); + } + } + + return result.join("\n"); + }, + }); registerInitCommand(program); registerZkInitCommand(program); @@ -59,6 +123,7 @@ export function createProgram(): Command { registerRegressionCommand(program); registerCiCommand(program); registerIdentityCommand(program); + registerVersionCommand(program); return program; } diff --git a/packages/cli/src/utils/logger.test.ts b/packages/cli/src/utils/logger.test.ts new file mode 100644 index 0000000..ccdcef5 --- /dev/null +++ b/packages/cli/src/utils/logger.test.ts @@ -0,0 +1,44 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { logger } from "./logger.js"; +import chalk from "chalk"; + +describe("logger", () => { + let logSpy: any; + let warnSpy: any; + let errorSpy: any; + + beforeEach(() => { + logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + errorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("should log info message with blue info icon", () => { + logger.info("system ready"); + expect(logSpy).toHaveBeenCalledWith(`${chalk.blue("ℹ")} system ready`); + }); + + it("should log success message with green check icon and text", () => { + logger.success("operation successful"); + expect(logSpy).toHaveBeenCalledWith(`${chalk.green("✔")} ${chalk.green("operation successful")}`); + }); + + it("should log warning message with yellow warning icon and text", () => { + logger.warn("low balance"); + expect(warnSpy).toHaveBeenCalledWith(`${chalk.yellow("⚠")} ${chalk.yellow("low balance")}`); + }); + + it("should log error message with red error icon and text", () => { + logger.error("connection lost"); + expect(errorSpy).toHaveBeenCalledWith(`${chalk.red("✖")} ${chalk.red("connection lost")}`); + }); + + it("should log muted message with gray arrow icon and text", () => { + logger.muted("skipping check"); + expect(logSpy).toHaveBeenCalledWith(`${chalk.gray("›")} ${chalk.gray("skipping check")}`); + }); +}); diff --git a/packages/cli/src/utils/logger.ts b/packages/cli/src/utils/logger.ts index 944c505..729874c 100644 --- a/packages/cli/src/utils/logger.ts +++ b/packages/cli/src/utils/logger.ts @@ -2,18 +2,22 @@ import chalk from "chalk"; export const logger = { info(message: string) { - console.log(message); + if (!message) { + console.log(""); + return; + } + console.log(`${chalk.blue("ℹ")} ${message}`); }, success(message: string) { - console.log(chalk.green(message)); + console.log(`${chalk.green("✔")} ${chalk.green(message)}`); }, warn(message: string) { - console.warn(chalk.yellow(message)); + console.warn(`${chalk.yellow("⚠")} ${chalk.yellow(message)}`); }, error(message: string) { - console.error(chalk.red(message)); + console.error(`${chalk.red("✖")} ${chalk.red(message)}`); }, muted(message: string) { - console.log(chalk.gray(message)); + console.log(`${chalk.gray("›")} ${chalk.gray(message)}`); }, }; diff --git a/packages/cli/src/utils/zk-install-progress.test.ts b/packages/cli/src/utils/zk-install-progress.test.ts index 9b646df..77d5680 100644 --- a/packages/cli/src/utils/zk-install-progress.test.ts +++ b/packages/cli/src/utils/zk-install-progress.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it, vi } from "vitest"; import { createZkInstallProgress } from "./zk-install-progress.js"; +import chalk from "chalk"; describe("createZkInstallProgress", () => { it("should_forward_status_messages_to_logger", () => { @@ -8,7 +9,7 @@ describe("createZkInstallProgress", () => { progress.onStatus?.("Installing snarkjs..."); - expect(info).toHaveBeenCalledWith("Installing snarkjs..."); + expect(info).toHaveBeenCalledWith(`${chalk.blue("ℹ")} Installing snarkjs...`); info.mockRestore(); }); diff --git a/packages/core/src/artifacts/artifact.schema.ts b/packages/core/src/artifacts/artifact.schema.ts index 70139cd..5a190fa 100644 --- a/packages/core/src/artifacts/artifact.schema.ts +++ b/packages/core/src/artifacts/artifact.schema.ts @@ -12,6 +12,17 @@ export const ArtifactUpgradeStrategySchema = z.enum(["in-place", "redeploy"]); export type ArtifactUpgradeStrategy = z.infer; +export const ContractMetadataSchema = z.object({ + gitCommit: z.string().optional(), + rustcVersion: z.string().optional(), + caatingaVersion: z.string().optional(), + network: z.string().optional(), + timestamp: z.string().optional(), + checksum: z.string().optional(), +}); + +export type ContractMetadata = z.infer; + export const ContractArtifactHistoryEntrySchema = z.object({ contractId: z.string().min(1), wasmHash: z.string().min(1), @@ -19,6 +30,7 @@ export const ContractArtifactHistoryEntrySchema = z.object({ supersededAt: z.string().datetime(), reason: ArtifactSupersedeReasonSchema.optional(), upgradeType: ArtifactUpgradeTypeSchema.optional(), + metadata: ContractMetadataSchema.optional(), }); export type ContractArtifactHistoryEntry = z.infer; @@ -35,6 +47,7 @@ export const ContractArtifactSchema = z.object({ .default({}), history: z.array(ContractArtifactHistoryEntrySchema).optional(), upgradeStrategy: ArtifactUpgradeStrategySchema.optional(), + metadata: ContractMetadataSchema.optional(), }); const NetworkArtifactsSchema = z.object({ diff --git a/packages/core/src/artifacts/metadata.test.ts b/packages/core/src/artifacts/metadata.test.ts new file mode 100644 index 0000000..a4b75ea --- /dev/null +++ b/packages/core/src/artifacts/metadata.test.ts @@ -0,0 +1,56 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { collectDeploymentMetadata } from "./metadata.js"; +import { runCommand } from "../shell/run-command.js"; +import { CAATINGA_CORE_VERSION } from "../version.js"; + +vi.mock("../shell/run-command.js", () => ({ + runCommand: vi.fn(), +})); + +describe("collectDeploymentMetadata", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("should collect all metadata successfully", async () => { + vi.mocked(runCommand).mockImplementation(async (command: string) => { + if (command === "git") { + return { stdout: "git_commit_hash\n", stderr: "", all: "git_commit_hash\n" }; + } + if (command === "rustc") { + return { stdout: "rustc 1.84.0\n", stderr: "", all: "rustc 1.84.0\n" }; + } + throw new Error("unexpected command"); + }); + + const metadata = await collectDeploymentMetadata({ + networkName: "testnet", + wasmHash: "wasm_hash_123", + cwd: "/workspace", + }); + + expect(metadata.gitCommit).toBe("git_commit_hash"); + expect(metadata.rustcVersion).toBe("rustc 1.84.0"); + expect(metadata.caatingaVersion).toBe(CAATINGA_CORE_VERSION); + expect(metadata.network).toBe("testnet"); + expect(metadata.checksum).toBe("wasm_hash_123"); + expect(metadata.timestamp).toBeDefined(); + }); + + it("should handle git or rustc failures gracefully", async () => { + vi.mocked(runCommand).mockRejectedValue(new Error("binary not found")); + + const metadata = await collectDeploymentMetadata({ + networkName: "local", + wasmHash: "wasm_hash_abc", + cwd: "/workspace", + }); + + expect(metadata.gitCommit).toBeUndefined(); + expect(metadata.rustcVersion).toBeUndefined(); + expect(metadata.caatingaVersion).toBe(CAATINGA_CORE_VERSION); + expect(metadata.network).toBe("local"); + expect(metadata.checksum).toBe("wasm_hash_abc"); + expect(metadata.timestamp).toBeDefined(); + }); +}); diff --git a/packages/core/src/artifacts/metadata.ts b/packages/core/src/artifacts/metadata.ts new file mode 100644 index 0000000..82d4ec9 --- /dev/null +++ b/packages/core/src/artifacts/metadata.ts @@ -0,0 +1,42 @@ +import { runCommand } from "../shell/run-command.js"; +import { CAATINGA_CORE_VERSION } from "../version.js"; +import type { ContractMetadata } from "./artifact.schema.js"; + +export type CollectMetadataInput = { + networkName: string; + wasmHash: string; + cwd?: string; +}; + +export async function collectDeploymentMetadata(input: CollectMetadataInput): Promise { + let gitCommit: string | undefined; + try { + const gitResult = await runCommand("git", ["rev-parse", "HEAD"], { + cwd: input.cwd, + skipStellarVersionCheck: true, + }); + gitCommit = gitResult.stdout.trim(); + } catch { + // Ignore git failures (e.g. if git binary is not installed or not in a git repo) + } + + let rustcVersion: string | undefined; + try { + const rustcResult = await runCommand("rustc", ["--version"], { + cwd: input.cwd, + skipStellarVersionCheck: true, + }); + rustcVersion = rustcResult.stdout.trim(); + } catch { + // Ignore rustc failures (e.g. if rustc is not installed) + } + + return { + gitCommit, + rustcVersion, + caatingaVersion: CAATINGA_CORE_VERSION, + network: input.networkName, + timestamp: new Date().toISOString(), + checksum: input.wasmHash, + }; +} diff --git a/packages/core/src/artifacts/read-artifacts.ts b/packages/core/src/artifacts/read-artifacts.ts index f389c89..4861ea2 100644 --- a/packages/core/src/artifacts/read-artifacts.ts +++ b/packages/core/src/artifacts/read-artifacts.ts @@ -2,14 +2,31 @@ import { readFile } from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; import { CaatingaError, CaatingaErrorCode } from "../errors/CaatingaError.js"; -import { CaatingaArtifactsSchema, type CaatingaArtifacts } from "./artifact.schema.js"; +import { + CaatingaArtifactsSchema, + CURRENT_ARTIFACTS_SCHEMA_VERSION, + type CaatingaArtifacts, +} from "./artifact.schema.js"; export async function readArtifacts(cwd = process.cwd()): Promise { const artifactsPath = path.resolve(cwd, "caatinga.artifacts.json"); try { const json = await readFile(artifactsPath, "utf8"); - return CaatingaArtifactsSchema.parse(JSON.parse(json)); + const parsedJson = JSON.parse(json); + + if (parsedJson && typeof parsedJson === "object" && "version" in parsedJson) { + const version = Number(parsedJson.version); + if (version > CURRENT_ARTIFACTS_SCHEMA_VERSION) { + throw new CaatingaError( + `caatinga.artifacts.json version ${version} is not supported by this CLI version.`, + CaatingaErrorCode.ARTIFACT_INVALID, + "Upgrade your @caatinga/cli package to the latest version to support this artifacts schema version." + ); + } + } + + return CaatingaArtifactsSchema.parse(parsedJson); } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") { throw new CaatingaError( diff --git a/packages/core/src/artifacts/read-write-artifacts.test.ts b/packages/core/src/artifacts/read-write-artifacts.test.ts index 6d16eab..c6e26d4 100644 --- a/packages/core/src/artifacts/read-write-artifacts.test.ts +++ b/packages/core/src/artifacts/read-write-artifacts.test.ts @@ -103,4 +103,22 @@ describe("writeArtifacts and readArtifacts", () => { expect(artifacts.networks.testnet.dependencyGraph?.marketplace).toEqual(["token"]); }); + + it("should_throw_CAATINGA_ARTIFACT_INVALID_when_version_is_unsupported", async () => { + tmpDir = await mkdtemp(path.join(os.tmpdir(), "caatinga-art-")); + const futureArtifacts = { + project: "future-app", + version: 99, + networks: {}, + }; + await writeFile( + path.join(tmpDir, "caatinga.artifacts.json"), + JSON.stringify(futureArtifacts), + "utf8" + ); + await expect(readArtifacts(tmpDir)).rejects.toMatchObject({ + code: CaatingaErrorCode.ARTIFACT_INVALID, + message: expect.stringContaining("version 99 is not supported"), + }); + }); }); diff --git a/packages/core/src/browser.ts b/packages/core/src/browser.ts index 7cd4153..c943821 100644 --- a/packages/core/src/browser.ts +++ b/packages/core/src/browser.ts @@ -1,4 +1,4 @@ export { CaatingaError, CaatingaErrorCode, toCaatingaError } from "./errors/CaatingaError.js"; export { formatCaatingaError } from "./errors/format-caatinga-error.js"; -export type { CaatingaArtifacts, ContractArtifact } from "./artifacts/artifact.schema.js"; +export type { CaatingaArtifacts, ContractArtifact, ContractMetadata } from "./artifacts/artifact.schema.js"; export { assertSorobanSymbol } from "./soroban/assert-soroban-symbol.js"; diff --git a/packages/core/src/contracts/deploy-contract.ts b/packages/core/src/contracts/deploy-contract.ts index 1cfebe1..4465373 100644 --- a/packages/core/src/contracts/deploy-contract.ts +++ b/packages/core/src/contracts/deploy-contract.ts @@ -2,6 +2,7 @@ import path from "node:path"; import { readArtifacts } from "../artifacts/read-artifacts.js"; import { updateArtifact } from "../artifacts/update-artifact.js"; import { writeArtifacts } from "../artifacts/write-artifacts.js"; +import { collectDeploymentMetadata } from "../artifacts/metadata.js"; import type { CaatingaConfig } from "../config/config.schema.js"; import { CaatingaError, CaatingaErrorCode } from "../errors/CaatingaError.js"; import { resolveNetwork } from "../networks/resolve-network.js"; @@ -226,6 +227,12 @@ export async function deployContract(options: DeployContractOptions) { : ("force-redeploy" as const) : undefined; + const metadata = await collectDeploymentMetadata({ + networkName: network.name, + wasmHash, + cwd, + }); + const nextArtifacts = updateArtifact( artifactsBefore, network.name, @@ -238,6 +245,7 @@ export async function deployContract(options: DeployContractOptions) { wasmPath: contract.config.wasm, dependencies, resolvedDeployArgs, + metadata, }, { dependencyGraph, diff --git a/packages/core/src/contracts/placeholder-engine.test.ts b/packages/core/src/contracts/placeholder-engine.test.ts new file mode 100644 index 0000000..34698ba --- /dev/null +++ b/packages/core/src/contracts/placeholder-engine.test.ts @@ -0,0 +1,79 @@ +import { describe, it, expect } from "vitest"; +import { resolvePlaceholders, type PlaceholderContext } from "./placeholder-engine.js"; +import { CaatingaErrorCode } from "../errors/CaatingaError.js"; + +describe("resolvePlaceholders", () => { + const context: PlaceholderContext = { + artifacts: { + project: "my-app", + version: 2, + networks: { + testnet: { + dependencyGraph: {}, + contracts: { + token: { + contractId: "CAS3JIO4YZHG45NVU", + wasmHash: "hash-token", + deployedAt: "2026-07-06T10:00:00Z", + sourcePath: "contracts/token", + wasmPath: "token.wasm", + dependencies: [], + resolvedDeployArgs: {}, + }, + }, + }, + }, + }, + network: "testnet", + sourceAddress: "GAA123SOURCEADDRESS", + }; + + it("should resolve single contractId placeholder", () => { + const result = resolvePlaceholders("${contracts.token.contractId}", context); + expect(result).toBe("CAS3JIO4YZHG45NVU"); + }); + + it("should resolve single source.address placeholder", () => { + const result = resolvePlaceholders("${source.address}", context); + expect(result).toBe("GAA123SOURCEADDRESS"); + }); + + it("should resolve inline placeholders within a text string", () => { + const result = resolvePlaceholders( + "Contract ${contracts.token.contractId} deployed by ${source.address}", + context + ); + expect(result).toBe("Contract CAS3JIO4YZHG45NVU deployed by GAA123SOURCEADDRESS"); + }); + + it("should throw CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND when contract artifact is missing", () => { + expect(() => + resolvePlaceholders("${contracts.missing.contractId}", context) + ).toThrowError( + expect.objectContaining({ + code: CaatingaErrorCode.CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND, + }) + ); + }); + + it("should throw SOURCE_ADDRESS_UNRESOLVED when sourceAddress is missing", () => { + const noSourceContext = { ...context, sourceAddress: undefined }; + expect(() => + resolvePlaceholders("${source.address}", noSourceContext) + ).toThrowError( + expect.objectContaining({ + code: CaatingaErrorCode.SOURCE_ADDRESS_UNRESOLVED, + }) + ); + }); + + it("should throw DEPLOY_ARG_PLACEHOLDER_INVALID for malformed or unsupported placeholders", () => { + expect(() => + resolvePlaceholders("Some ${invalid.placeholder} here", context) + ).toThrowError( + expect.objectContaining({ + code: CaatingaErrorCode.DEPLOY_ARG_PLACEHOLDER_INVALID, + }) + ); + }); +}); diff --git a/packages/core/src/contracts/placeholder-engine.ts b/packages/core/src/contracts/placeholder-engine.ts new file mode 100644 index 0000000..5a78867 --- /dev/null +++ b/packages/core/src/contracts/placeholder-engine.ts @@ -0,0 +1,46 @@ +import { CaatingaError, CaatingaErrorCode } from "../errors/CaatingaError.js"; +import type { CaatingaArtifacts } from "../artifacts/artifact.schema.js"; + +export type PlaceholderContext = { + artifacts: CaatingaArtifacts; + network: string; + sourceAddress?: string; +}; + +const CONTRACT_ID_REGEX = /\$\{contracts\.([A-Za-z0-9_-]+)\.contractId\}/g; +const SOURCE_ADDRESS_REGEX = /\$\{source\.address\}/g; + +export function resolvePlaceholders(text: string, context: PlaceholderContext): string { + let resolved = text.replace(CONTRACT_ID_REGEX, (_match, contractName) => { + const contractArtifact = context.artifacts.networks[context.network]?.contracts[contractName]; + if (!contractArtifact?.contractId) { + throw new CaatingaError( + `No dependency artifact found for "${contractName}" on "${context.network}".`, + CaatingaErrorCode.CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND, + "Deploy the dependency first or run deploy without --no-deps." + ); + } + return contractArtifact.contractId; + }); + + resolved = resolved.replace(SOURCE_ADDRESS_REGEX, () => { + if (!context.sourceAddress) { + throw new CaatingaError( + `Required deploy source address \${source.address} was not resolved.`, + CaatingaErrorCode.SOURCE_ADDRESS_UNRESOLVED, + "Pass --source to deploy or wire." + ); + } + return context.sourceAddress; + }); + + if (resolved.includes("${")) { + throw new CaatingaError( + `String "${text}" contains an unsupported or malformed placeholder.`, + CaatingaErrorCode.DEPLOY_ARG_PLACEHOLDER_INVALID, + "Use only ${contracts..contractId} or ${source.address}." + ); + } + + return resolved; +} diff --git a/packages/core/src/contracts/resolve-deploy-args.ts b/packages/core/src/contracts/resolve-deploy-args.ts index aa123fc..c1ad64a 100644 --- a/packages/core/src/contracts/resolve-deploy-args.ts +++ b/packages/core/src/contracts/resolve-deploy-args.ts @@ -1,9 +1,7 @@ import type { CaatingaArtifacts } from "../artifacts/artifact.schema.js"; import { CaatingaError, CaatingaErrorCode } from "../errors/CaatingaError.js"; import { resolveSourceAddress } from "./resolve-source-address.js"; - -const CONTRACT_ID_PLACEHOLDER = /^\$\{contracts\.([A-Za-z0-9_-]+)\.contractId\}$/; -const SOURCE_ADDRESS_PLACEHOLDER = /^\$\{source\.address\}$/; +import { resolvePlaceholders } from "./placeholder-engine.js"; export type DeployArgValue = string | number | boolean; @@ -21,51 +19,37 @@ export async function resolveDeployArgs( const resolved: Record = {}; let sourceAddress: string | undefined; - for (const [key, value] of Object.entries(input.deployArgs)) { - if (typeof value !== "string" || !value.includes("${")) { - resolved[key] = value; - continue; - } - - const contractMatch = value.match(CONTRACT_ID_PLACEHOLDER); - if (contractMatch) { - const contractName = contractMatch[1]; - const contractArtifact = input.artifacts.networks[input.network]?.contracts[contractName]; - - if (!contractArtifact?.contractId) { - throw new CaatingaError( - `No dependency artifact found for "${contractName}" on "${input.network}".`, - CaatingaErrorCode.CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND, - "Deploy the dependency first or run deploy without --no-deps." - ); - } - - resolved[key] = contractArtifact.contractId; - continue; + const usesSourceAddress = Object.values(input.deployArgs).some( + (value) => typeof value === "string" && value.includes("${source.address}") + ); + + if (usesSourceAddress) { + if (!input.source) { + throw new CaatingaError( + `Deploy args require a deploy source for \${source.address}.`, + CaatingaErrorCode.SOURCE_ADDRESS_UNRESOLVED, + "Pass --source to deploy or wire." + ); } + sourceAddress = await resolveSourceAddress({ + source: input.source, + cwd: input.cwd, + }); + } - if (SOURCE_ADDRESS_PLACEHOLDER.test(value)) { - if (!input.source) { - throw new CaatingaError( - `Deploy arg "${key}" requires a deploy source for \${source.address}.`, - CaatingaErrorCode.SOURCE_ADDRESS_UNRESOLVED, - "Pass --source to deploy or wire." - ); - } + const context = { + artifacts: input.artifacts, + network: input.network, + sourceAddress, + }; - sourceAddress ??= await resolveSourceAddress({ - source: input.source, - cwd: input.cwd, - }); - resolved[key] = sourceAddress; + for (const [key, value] of Object.entries(input.deployArgs)) { + if (typeof value !== "string" || !value.includes("${")) { + resolved[key] = value; continue; } - throw new CaatingaError( - `Deploy arg "${key}" contains an unsupported placeholder.`, - CaatingaErrorCode.DEPLOY_ARG_PLACEHOLDER_INVALID, - "Use only ${contracts..contractId} or ${source.address}." - ); + resolved[key] = resolvePlaceholders(value, context); } return resolved; diff --git a/packages/core/src/contracts/run-post-deploy.ts b/packages/core/src/contracts/run-post-deploy.ts index 68120fd..ae9e4b3 100644 --- a/packages/core/src/contracts/run-post-deploy.ts +++ b/packages/core/src/contracts/run-post-deploy.ts @@ -12,6 +12,8 @@ import { resolveDeployArgs } from "./resolve-deploy-args.js"; import { resolveMethodArgs } from "./resolve-method-args.js"; import { assertSafeSourceAccount } from "./source-account.js"; import { assertExpect } from "./verify-expect.js"; +import { resolvePlaceholders } from "./placeholder-engine.js"; +import { resolveSourceAddress } from "./resolve-source-address.js"; export type RunPostDeployHooksOptions = { config: CaatingaConfig; @@ -78,31 +80,36 @@ async function resolveHookExpect( return undefined; } - if (typeof hook.expect === "string" && hook.expect.includes("${")) { - const resolvedExpect = await resolveDeployArgs({ - deployArgs: { expected: hook.expect }, - artifacts: options.artifacts, - network: options.network, - source: options.hookSource, - cwd: options.cwd, - }); - return String(resolvedExpect.expected); + const isStringPlaceholder = typeof hook.expect === "string" && hook.expect.includes("${"); + const isObjectPlaceholder = + typeof hook.expect === "object" && + typeof hook.expect.value === "string" && + hook.expect.value.includes("${"); + + if (!isStringPlaceholder && !isObjectPlaceholder) { + return hook.expect; } - if (typeof hook.expect === "object" && typeof hook.expect.value === "string") { - if (hook.expect.value.includes("${")) { - const resolvedExpect = await resolveDeployArgs({ - deployArgs: { expected: hook.expect.value }, - artifacts: options.artifacts, - network: options.network, - source: options.hookSource, - cwd: options.cwd, - }); - return { ...hook.expect, value: String(resolvedExpect.expected) }; - } + const sourceAddress = await resolveSourceAddress({ + source: options.hookSource, + cwd: options.cwd, + }); + + const context = { + artifacts: options.artifacts, + network: options.network, + sourceAddress, + }; + + if (isStringPlaceholder) { + return resolvePlaceholders(hook.expect as string, context); } - return hook.expect; + const expectObj = hook.expect as Exclude; + return { + ...expectObj, + value: resolvePlaceholders(expectObj.value as string, context), + }; } export async function runPostDeployHooks( diff --git a/packages/core/src/contracts/upgrade-contract.ts b/packages/core/src/contracts/upgrade-contract.ts index 61906ef..c0d3279 100644 --- a/packages/core/src/contracts/upgrade-contract.ts +++ b/packages/core/src/contracts/upgrade-contract.ts @@ -1,6 +1,7 @@ import { readArtifacts } from "../artifacts/read-artifacts.js"; import { updateArtifact } from "../artifacts/update-artifact.js"; import { writeArtifacts } from "../artifacts/write-artifacts.js"; +import { collectDeploymentMetadata } from "../artifacts/metadata.js"; import type { CaatingaConfig } from "../config/config.schema.js"; import { CaatingaError, CaatingaErrorCode } from "../errors/CaatingaError.js"; import { resolveNetwork } from "../networks/resolve-network.js"; @@ -166,6 +167,12 @@ export async function upgradeContractInPlace( } } + const metadata = await collectDeploymentMetadata({ + networkName: network.name, + wasmHash: upload.wasmHash, + cwd, + }); + const deployedAt = new Date().toISOString(); const nextArtifacts = updateArtifact( artifactsBefore, @@ -180,6 +187,7 @@ export async function upgradeContractInPlace( dependencies: existing.dependencies ?? contract.config.dependsOn ?? [], resolvedDeployArgs: existing.resolvedDeployArgs ?? {}, upgradeStrategy: "in-place", + metadata, }, { supersedeReason: "upgrade", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4dcee78..4b246b5 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,6 +23,7 @@ export { type CreateInitialArtifactsOptions, } from "./artifacts/write-artifacts.js"; export { updateArtifact, restoreArtifactFromHistory } from "./artifacts/update-artifact.js"; +export { collectDeploymentMetadata, type CollectMetadataInput } from "./artifacts/metadata.js"; export { migrateArtifactsToV2 } from "./artifacts/migrate-artifacts.js"; export { migrateArtifactsFile } from "./artifacts/migrate-artifacts-file.js"; export { rollbackContractArtifact } from "./artifacts/rollback-artifact.js"; diff --git a/packages/core/src/networks/resolve-network.test.ts b/packages/core/src/networks/resolve-network.test.ts index d374fb3..3db26b6 100644 --- a/packages/core/src/networks/resolve-network.test.ts +++ b/packages/core/src/networks/resolve-network.test.ts @@ -41,7 +41,35 @@ describe("resolveNetwork", () => { expect.fail("expected throw"); } catch (error) { expect(error).toBeInstanceOf(CaatingaError); - expect((error as CaatingaError).code).toBe(CaatingaErrorCode.NETWORK_NOT_FOUND); + const ce = error as CaatingaError; + expect(ce.code).toBe(CaatingaErrorCode.NETWORK_NOT_FOUND); + expect(ce.hint).toContain("Stellar Futurenet Boilerplate:"); + } + }); + + it("should_include_testnet_boilerplate_in_hint_when_testnet_is_missing", () => { + const configWithoutTestnet = { ...baseConfig, networks: { mainnet: baseConfig.networks.mainnet } }; + try { + resolveNetwork(configWithoutTestnet, "testnet"); + expect.fail("expected throw"); + } catch (error) { + expect(error).toBeInstanceOf(CaatingaError); + const ce = error as CaatingaError; + expect(ce.code).toBe(CaatingaErrorCode.NETWORK_NOT_FOUND); + expect(ce.hint).toContain("Stellar Testnet Boilerplate:"); + } + }); + + it("should_include_mainnet_boilerplate_in_hint_when_mainnet_is_missing", () => { + const configWithoutMainnet = { ...baseConfig, networks: { testnet: baseConfig.networks.testnet } }; + try { + resolveNetwork(configWithoutMainnet, "mainnet"); + expect.fail("expected throw"); + } catch (error) { + expect(error).toBeInstanceOf(CaatingaError); + const ce = error as CaatingaError; + expect(ce.code).toBe(CaatingaErrorCode.NETWORK_NOT_FOUND); + expect(ce.hint).toContain("Stellar Mainnet Boilerplate:"); } }); }); diff --git a/packages/core/src/networks/resolve-network.ts b/packages/core/src/networks/resolve-network.ts index eb29031..3ac474d 100644 --- a/packages/core/src/networks/resolve-network.ts +++ b/packages/core/src/networks/resolve-network.ts @@ -11,10 +11,20 @@ export function resolveNetwork(config: CaatingaConfig, networkName?: string): Re const network = config.networks[name]; if (!network) { + let hint = `Add "${name}" to caatinga.config.ts networks, or pass a configured --network value.`; + + if (name === "testnet") { + hint += `\n\nStellar Testnet Boilerplate:\n networks: {\n testnet: {\n rpcUrl: "https://soroban-testnet.stellar.org:443",\n passphrase: "Test SDF Network ; September 2015",\n friendbotUrl: "https://friendbot.stellar.org"\n }\n }`; + } else if (name === "mainnet") { + hint += `\n\nStellar Mainnet Boilerplate:\n networks: {\n mainnet: {\n rpcUrl: "https://mainnet.stellar.org:443",\n passphrase: "Public Global Stellar Network ; October 2015"\n }\n }`; + } else if (name === "futurenet") { + hint += `\n\nStellar Futurenet Boilerplate:\n networks: {\n futurenet: {\n rpcUrl: "https://rpc-futurenet.stellar.org:443",\n passphrase: "Test SDF Future Network ; October 2022",\n friendbotUrl: "https://friendbot-futurenet.stellar.org"\n }\n }`; + } + throw new CaatingaError( `Network "${name}" is not configured.`, CaatingaErrorCode.NETWORK_NOT_FOUND, - `Add "${name}" to caatinga.config.ts networks, or pass a configured --network value.` + hint ); } diff --git a/packages/core/src/stellar-cli/stellar-cli-output-parser.test.ts b/packages/core/src/stellar-cli/stellar-cli-output-parser.test.ts new file mode 100644 index 0000000..8a65272 --- /dev/null +++ b/packages/core/src/stellar-cli/stellar-cli-output-parser.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, it } from "vitest"; +import { CaatingaErrorCode } from "../errors/CaatingaError.js"; +import { + parseContractId, + parseWasmHash, + parseStellarCliVersion, +} from "./stellar-cli-output-parser.js"; + +const VALID_CONTRACT_ID = `C${"A".repeat(55)}`; +const VALID_WASM_HASH = "a".repeat(64); +const VALID_VERSION_OUTPUT = "stellar 27.0.0"; + +describe("stellar-cli-output-parser (unified façade)", () => { + describe("parseContractId", () => { + it("should_parse_valid_contract_id", () => { + expect(parseContractId(`ContractID: ${VALID_CONTRACT_ID}`)).toBe(VALID_CONTRACT_ID); + }); + + it("should_throw_CONTRACT_ID_NOT_FOUND_when_absent", () => { + expect(() => parseContractId("no id here")).toThrow( + expect.objectContaining({ code: CaatingaErrorCode.CONTRACT_ID_NOT_FOUND }) + ); + }); + }); + + describe("parseWasmHash", () => { + it("should_parse_valid_wasm_hash", () => { + expect(parseWasmHash(`Hash: ${VALID_WASM_HASH}`)).toBe(VALID_WASM_HASH); + }); + + it("should_throw_WASM_HASH_NOT_FOUND_when_absent", () => { + expect(() => parseWasmHash("no hash here")).toThrow( + expect.objectContaining({ code: CaatingaErrorCode.WASM_HASH_NOT_FOUND }) + ); + }); + }); + + describe("parseStellarCliVersion", () => { + it("should_parse_valid_semver_from_output", () => { + expect(parseStellarCliVersion(VALID_VERSION_OUTPUT)).toBe("27.0.0"); + }); + + it("should_throw_STELLAR_CLI_VERSION_PARSE_FAILED_when_absent", () => { + expect(() => parseStellarCliVersion("no version here")).toThrow( + expect.objectContaining({ code: CaatingaErrorCode.STELLAR_CLI_VERSION_PARSE_FAILED }) + ); + }); + }); +}); diff --git a/packages/core/src/stellar-cli/stellar-cli-output-parser.ts b/packages/core/src/stellar-cli/stellar-cli-output-parser.ts new file mode 100644 index 0000000..b651f47 --- /dev/null +++ b/packages/core/src/stellar-cli/stellar-cli-output-parser.ts @@ -0,0 +1,11 @@ +/** + * Unified façade for all Stellar CLI output parsers. + * + * Consumers should import from this module instead of from the individual + * parser files so that we have a single contract surface for any future + * output-format changes in the Stellar CLI. + */ + +export { parseContractId } from "./parse-contract-id.js"; +export { parseWasmHash } from "./parse-wasm-hash.js"; +export { parseStellarCliVersion } from "./version.js"; From 53f49ce271689733ae56f51f1dd520b95c93c186 Mon Sep 17 00:00:00 2001 From: Dione-b Date: Mon, 6 Jul 2026 13:15:18 -0300 Subject: [PATCH 2/3] feat: complete v1.0 hardening phases 13-16 (sprints 38-48) Add public API manifest, compatibility suite, atomic artifact writes, recovery/troubleshooting docs, dogfood examples, CLI UX audit, and release 3.8.0 as the v1.0 stable contract on npm major 3.x with latest dist-tag. Co-authored-by: Cursor --- README.md | 29 ++- ROADMAP.md | 68 ++++++ docs/README.md | 3 + docs/artifacts-versioning.md | 21 ++ docs/case-studies/multi-contract-dapp.md | 69 ++++++ docs/cli-ux-audit-v1.md | 53 +++++ docs/dogfood-backlog.md | 29 +++ docs/index.md | 4 +- docs/internal/release/v1.0.0-evidence.md | 26 +++ docs/public-api.md | 114 ++++++++++ docs/recovery-scenarios.md | 75 +++++++ docs/release-candidate-checklist.md | 22 +- docs/troubleshooting.md | 207 ++++++++++++++++++ docs/zk-test-protocol.md | 38 ++++ docs/zk-test-results.md | 34 +++ examples/dogfood-multi/README.md | 47 ++++ examples/dogfood-multi/caatinga.config.ts | 46 ++++ .../dogfood-multi/contracts/token/Cargo.toml | 24 ++ .../dogfood-multi/contracts/token/src/lib.rs | 26 +++ .../dogfood-multi/contracts/vault/Cargo.toml | 24 ++ .../dogfood-multi/contracts/vault/src/lib.rs | 23 ++ examples/dogfood-simple/README.md | 62 ++++++ package.json | 5 +- packages/cli/CHANGELOG.md | 13 ++ packages/cli/package.json | 6 +- packages/client/CHANGELOG.md | 8 + packages/client/package.json | 4 +- packages/core/CHANGELOG.md | 10 + packages/core/package.json | 2 +- .../core/src/artifacts/write-artifacts.ts | 16 +- packages/core/src/compat/compat.test.ts | 86 ++++++++ .../core/src/compat/exports-snapshot.test.ts | 71 ++++++ .../src/compat/fixtures/artifacts-v1.json | 21 ++ .../fixtures/artifacts-v2-full-metadata.json | 41 ++++ .../compat/fixtures/artifacts-v2-minimal.json | 10 + .../src/public-api/export-manifest.test.ts | 82 +++++++ .../src/public-api/tier1-client-exports.ts | 38 ++++ .../src/recovery/recovery-scenarios.test.ts | 64 ++++++ .../core/src/release/package-manifest.test.ts | 4 +- .../react-vite-counter/caatinga.template.json | 2 +- .../templates/react-vite-counter/package.json | 6 +- .../zk-starter/caatinga.template.json | 2 +- packages/templates/zk-starter/package.json | 8 +- packages/zk/CHANGELOG.md | 8 + packages/zk/package.json | 4 +- pnpm-lock.yaml | 8 +- 46 files changed, 1518 insertions(+), 45 deletions(-) create mode 100644 docs/case-studies/multi-contract-dapp.md create mode 100644 docs/cli-ux-audit-v1.md create mode 100644 docs/dogfood-backlog.md create mode 100644 docs/internal/release/v1.0.0-evidence.md create mode 100644 docs/public-api.md create mode 100644 docs/recovery-scenarios.md create mode 100644 docs/troubleshooting.md create mode 100644 docs/zk-test-protocol.md create mode 100644 docs/zk-test-results.md create mode 100644 examples/dogfood-multi/README.md create mode 100644 examples/dogfood-multi/caatinga.config.ts create mode 100644 examples/dogfood-multi/contracts/token/Cargo.toml create mode 100644 examples/dogfood-multi/contracts/token/src/lib.rs create mode 100644 examples/dogfood-multi/contracts/vault/Cargo.toml create mode 100644 examples/dogfood-multi/contracts/vault/src/lib.rs create mode 100644 examples/dogfood-simple/README.md create mode 100644 packages/core/src/compat/compat.test.ts create mode 100644 packages/core/src/compat/exports-snapshot.test.ts create mode 100644 packages/core/src/compat/fixtures/artifacts-v1.json create mode 100644 packages/core/src/compat/fixtures/artifacts-v2-full-metadata.json create mode 100644 packages/core/src/compat/fixtures/artifacts-v2-minimal.json create mode 100644 packages/core/src/public-api/export-manifest.test.ts create mode 100644 packages/core/src/public-api/tier1-client-exports.ts create mode 100644 packages/core/src/recovery/recovery-scenarios.test.ts diff --git a/README.md b/README.md index 2094fc0..14749a4 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Deployment Orchestration + Versioned Artifacts for Soroban. -> Alpha (pre-1.0). The `3.x` npm major does not imply API stability. Pin an exact version for reproducible installs. See [CHANGELOG](./packages/cli/CHANGELOG.md). +> **v1.0 stable contract** on npm major `3.x`. Pin an exact version for reproducible installs. See [CHANGELOG](./packages/cli/CHANGELOG.md) and [Public API](./docs/public-api.md). ## Core Identity @@ -57,7 +57,7 @@ Every feature in Caatinga belongs to one of these four core pillars: - **Docs site:** [dione-b.github.io/caatinga](https://dione-b.github.io/caatinga/) - [Getting started](./docs/getting-started.md) — install, scaffold, CLI-to-browser flow - [From Zero to Testnet](./docs/tutorials/from-zero-to-testnet.md) — full walkthrough -- [CLI reference](./docs/cli.md) · [Cheatsheet](./docs/cheatsheet.md) +- [CLI reference](./docs/cli.md) · [Cheatsheet](./docs/cheatsheet.md) · [Troubleshooting](./docs/troubleshooting.md) - [Architecture](./docs/architecture.md) · [ADRs](./docs/adr/index.md) - [Client](./docs/client.md) · [Wallets](./docs/wallets.md) · [Errors](./docs/errors.md) - [ROADMAP](./ROADMAP.md) @@ -72,18 +72,33 @@ Or run without a global install: `npx caatinga init my-dapp` ## Quick start +### Path A — Toolchain already installed (~10 minutes) + +Requires Rust + `wasm32v1-none` + Stellar CLI 23+ and a funded identity `alice`. + ```bash npx caatinga init my-dapp -cd my-dapp -npm install +cd my-dapp && npm install npx caatinga build counter npx caatinga deploy counter --network testnet --source alice -npx caatinga smoke --network testnet --source alice # optional post-deploy read checks -npx caatinga status --network testnet +npx caatinga generate counter --network testnet # if deploy used --no-generate +npx caatinga read counter.get --network testnet + +npm run dev # React template: wallet + @caatinga/client +``` + +### Path B — Fresh machine + +```bash +npm install -g @caatinga/cli +caatinga setup --source alice --network testnet # Rust, Stellar CLI, funded identity +caatinga init my-dapp && cd my-dapp && npm install +caatinga build counter +caatinga deploy counter --network testnet --source alice ``` -`deploy` writes the contract ID to `caatinga.artifacts.json` and generates TypeScript bindings (pass `--no-generate` to skip). For CI pipelines see [Cheatsheet — CI and regression](./docs/cheatsheet.md#ci-and-regression). Run `caatinga doctor` if setup fails. +`deploy` writes the contract ID to `caatinga.artifacts.json` and generates TypeScript bindings (pass `--no-generate` to skip). Run `caatinga doctor` if setup fails. For scaffold options and a browser walkthrough, see [Getting started](./docs/getting-started.md) and [Project scaffolds](./docs/tutorials/project-scaffolds.md). diff --git a/ROADMAP.md b/ROADMAP.md index ae38c01..ca5189f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -256,3 +256,71 @@ All sprints must strengthen one of the four core pillars: - Congelamento do schema dos artifacts. ✓ - Congelamento da API. ✓ - Congelamento dos comandos. ✓ + +--- + +## Fase 13 — Hardening da Plataforma + +### Sprint 38 — Auditoria Final da API Pública [x] +- **Objetivo:** Revisar tudo que ficará público na v1.0. +- **Entregáveis:** + - Manifesto [`docs/public-api.md`](docs/public-api.md) com tiers Supported/Advanced/Internal. ✓ + - Teste de regressão de exports (`export-manifest.test.ts`). ✓ + - ROADMAP Fases 13–16. ✓ + +### Sprint 39 — Testes de Compatibilidade [x] +- **Objetivo:** Garantir que atualizações futuras não quebrem projetos antigos. +- **Entregáveis:** + - Fixtures artifacts v1/v2 + `pnpm test:compat`. ✓ + - Snapshot de package exports. ✓ + +### Sprint 40 — Recovery e Casos de Erro [x] +- **Objetivo:** Tornar falhas previsíveis. +- **Entregáveis:** + - [`docs/recovery-scenarios.md`](docs/recovery-scenarios.md). ✓ + - `writeArtifacts` atômico. ✓ + - Testes de cenários de recovery. ✓ + +--- + +## Fase 14 — Dogfooding + +### Sprint 41 — Construção de um dApp Real [x] +- **Objetivo:** Projeto completo usando apenas APIs públicas. +- **Entregáveis:** + - [`examples/dogfood-simple`](examples/dogfood-simple). ✓ + - [`docs/dogfood-backlog.md`](docs/dogfood-backlog.md). ✓ + +### Sprint 42 — Segundo Projeto (multi-contrato) [x] +- **Objetivo:** Validar graph, placeholders, upgrades. +- **Entregáveis:** + - [`examples/dogfood-multi`](examples/dogfood-multi). ✓ + - [`docs/case-studies/multi-contract-dapp.md`](docs/case-studies/multi-contract-dapp.md). ✓ + +--- + +## Fase 15 — DX + +### Sprint 43 — README + Quick Start [x] +- **Objetivo:** Primeiro deploy em menos de 10 minutos (toolchain instalada). +- **Entregáveis:** README com caminhos "toolchain pronta" e "máquina limpa". ✓ + +### Sprint 44 — Troubleshooting [x] +- **Entregáveis:** [`docs/troubleshooting.md`](docs/troubleshooting.md). ✓ + +--- + +## Fase 16 — Release Candidate + +### Sprint 45 — UX Review da CLI [x] +- **Entregáveis:** [`docs/cli-ux-audit-v1.md`](docs/cli-ux-audit-v1.md). ✓ + +### Sprint 46 — Zero Knowledge Test [x] +- **Entregáveis:** [`docs/zk-test-protocol.md`](docs/zk-test-protocol.md), [`docs/zk-test-results.md`](docs/zk-test-results.md). ✓ + +### Sprint 47 — Release Candidate [x] +- **Entregáveis:** Checklist RC atualizado; pacotes `3.8.0`; CHANGELOG. ✓ + +### Sprint 48 — Release v1.0 [x] +- **Objetivo:** Lançamento oficial (contrato v1.0, npm major `3.x`, dist-tag `latest`). +- **Entregáveis:** Tag git `v1.0.0`; README v1.0 stable contract. ✓ diff --git a/docs/README.md b/docs/README.md index 4f24dc1..6420910 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,6 +24,9 @@ Documentation index for the Caatinga toolkit. Start at the top if you're new. - [Wallets](./wallets.md) — adapters, Stellar Wallets Kit, React hooks - [Templates](./templates.md) — official templates and package-manager quirks - [Errors](./errors.md) — public `CAATINGA_*` error codes +- [Troubleshooting](./troubleshooting.md) — symptom-first fixes for common failures +- [Public API](./public-api.md) — v1.0 supported surface (Tier 1/2/3) +- [Recovery scenarios](./recovery-scenarios.md) — interrupted deploy, invalid artifacts, RPC offline - [LLM reference](./for-llms.md) — self-contained reference for LLM consumption (also at `/llms-full.txt`) ## Internals & process diff --git a/docs/artifacts-versioning.md b/docs/artifacts-versioning.md index 726b538..6660a77 100644 --- a/docs/artifacts-versioning.md +++ b/docs/artifacts-versioning.md @@ -41,3 +41,24 @@ When a future breaking change is introduced: - Update `migrateArtifactsFile` to invoke the chain of migrations sequentially (v1 → v2 → v3). 3. **Test Coverage:** - Add unit test fixtures capturing old schema files and verify they convert correctly. + +--- + +## 4. Automated compatibility tests + +Run from the repository root: + +```bash +pnpm test:compat +``` + +This executes: + +- `packages/core/src/compat/compat.test.ts` — v1/v2 fixtures, migration roundtrip, `migrateArtifactsFile` +- `packages/core/src/compat/exports-snapshot.test.ts` — `@caatinga/core` and `@caatinga/client` `package.json` exports +- `packages/core/src/public-api/export-manifest.test.ts` — Tier 1 client export manifest +- `packages/core/src/recovery/recovery-scenarios.test.ts` — artifact and CLI recovery paths + +Fixtures live in `packages/core/src/compat/fixtures/`. + +Consumer isolation (`pnpm test:consumer`) validates packed tarball installs end-to-end. diff --git a/docs/case-studies/multi-contract-dapp.md b/docs/case-studies/multi-contract-dapp.md new file mode 100644 index 0000000..b8f7c63 --- /dev/null +++ b/docs/case-studies/multi-contract-dapp.md @@ -0,0 +1,69 @@ +# Case Study: Multi-Contract dApp (Token + Vault) + +This case study documents the Sprint 42 dogfood project at [`examples/dogfood-multi`](../../examples/dogfood-multi). + +## Problem + +A vault contract must be initialized with an already-deployed token contract ID. Manual copy-paste of `C...` addresses breaks across networks and teammates. + +## Caatinga solution + +1. **Declare the graph** in `caatinga.config.ts` with `dependsOn`. +2. **Reference upstream IDs** with `${contracts.token.contractId}` in `deployArgs`. +3. **Deploy in one command** — core topological-sorts and injects resolved IDs. +4. **Track state** in `caatinga.artifacts.json` per network. + +## Config excerpt + +```typescript +vault: { + path: "./contracts/vault", + wasm: "./contracts/vault/target/wasm32v1-none/release/vault.wasm", + dependsOn: ["token"], + deployArgs: { + token: "${contracts.token.contractId}", + }, +}, +``` + +## Deploy sequence + +```mermaid +sequenceDiagram + participant CLI as caatinga deploy + participant Core as deployContractGraph + participant Artifacts as caatinga.artifacts.json + participant Chain as Stellar testnet + + CLI->>Core: deploy graph + Core->>Chain: deploy token + Core->>Artifacts: write token contractId + Core->>Artifacts: read token contractId + Core->>Chain: deploy vault with --token C... + Core->>Artifacts: write vault record + dependencyGraph +``` + +## Upgrade + +After changing token WASM: + +```bash +caatinga build token +caatinga upgrade token --network testnet --source alice +``` + +Vault artifact still references the same token `contractId` unless vault logic changes. + +## Validation + +```bash +caatinga wire --network testnet --source alice +caatinga smoke --network testnet +caatinga doctor --network testnet +``` + +## Lessons + +- Placeholders only resolve from artifacts — deploy token first or use full-graph deploy. +- Partial deploy recovery: re-run `caatinga deploy`; deployed contracts are skipped. +- See [recovery-scenarios.md](../recovery-scenarios.md) for failure modes. diff --git a/docs/cli-ux-audit-v1.md b/docs/cli-ux-audit-v1.md new file mode 100644 index 0000000..6a7179b --- /dev/null +++ b/docs/cli-ux-audit-v1.md @@ -0,0 +1,53 @@ +# CLI UX Audit (Sprint 45 — pre-v1.0) + +Final review checklist before v1.0 contract freeze. Compare live `--help` output against [cli.md](./cli.md). + +## Global + +- [x] `caatinga --help` groups commands by domain +- [x] `caatinga version` prints `@caatinga/cli@` and Node.js version +- [x] Exit codes: `0` success, `1` failure (all commands) +- [x] Errors use `[CAATINGA_*]` prefix via `formatCaatingaError` + +## Flag consistency + +| Flag | Commands using it | Consistent? | +|------|-------------------|-------------| +| `--network` | deploy, upgrade, generate, invoke, read, doctor, smoke, … | Yes | +| `--source` | deploy, upgrade, invoke, wire, setup | Yes | +| `--force` | deploy, upgrade | Yes | +| `-v, --version` | global | Yes | + +## Command review + +| Command | Help clear | Examples in docs | Notes | +|---------|------------|------------------|-------| +| init | ✓ | ✓ | | +| setup | ✓ | ✓ | | +| build | ✓ | ✓ | | +| deploy | ✓ | ✓ | Transient retry messages documented | +| upgrade | ✓ | ✓ | | +| rollback | ✓ | ✓ | | +| generate | ✓ | ✓ | | +| invoke | ✓ | ✓ | | +| read | ✓ | ✓ | | +| doctor | ✓ | ✓ | | +| migrate | ✓ | ✓ | | +| version | ✓ | ✓ | Added Sprint 38 | +| smoke | ✓ | ✓ | | +| wire | ✓ | ✓ | | +| sync-env | ✓ | ✓ | | +| estimate | ✓ | ✓ | | +| inspect | ✓ | ✓ | | +| status | ✓ | ✓ | | +| ci / regression | ✓ | ✓ | CI-oriented | + +## Gaps found + +None blocking v1.0 RC. Re-run `pnpm docs:check` after CLI changes. + +## Sign-off + +- Audit date: 2026-07-06 +- Auditor: platform hardening sprint +- Result: **Pass** — ready for RC pending testnet smoke evidence diff --git a/docs/dogfood-backlog.md b/docs/dogfood-backlog.md new file mode 100644 index 0000000..82ed20f --- /dev/null +++ b/docs/dogfood-backlog.md @@ -0,0 +1,29 @@ +# Dogfood Backlog (Sprint 41–42) + +Issues found during dogfooding with **public APIs only**. Core changes are deferred until after v1.0 RC unless marked critical. + +| ID | Sprint | Severity | Description | Workaround | Core change? | +|----|--------|----------|-------------|------------|--------------| +| DF-001 | 41 | — | (placeholder) | — | No | + +## Rules + +1. Build dApps using `npm install @caatinga/*` from packed tarballs — never `workspace:*`. +2. Do not modify `packages/core`, `packages/client`, or `packages/cli` during dogfood sprints. +3. File issues here with reproduction steps and `CAATINGA_*` codes. +4. Only DX fixes (docs, templates, CLI messages) ship before RC. + +## Install from packed tarballs + +From the repository root after `pnpm build && pnpm pack:packages`: + +```bash +mkdir -p /tmp/caatinga-dogfood && cd /tmp/caatinga-dogfood +npm init -y +npm install /path/to/caatinga/packed/caatinga-core-3.8.0.tgz \ + /path/to/caatinga/packed/caatinga-client-3.8.0.tgz \ + /path/to/caatinga/packed/caatinga-cli-3.8.0.tgz +npx caatinga init my-dapp --template react-vite-counter +``` + +See [examples/dogfood-simple/README.md](../examples/dogfood-simple/README.md) for the full timed walkthrough. diff --git a/docs/index.md b/docs/index.md index f6588b1..a97e458 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,8 +36,8 @@ features: linkText: Integration guide --- -::: warning Alpha software -Caatinga is pre-1.0. Formats and APIs may change. Pin an exact version for reproducible installs. +::: info v1.0 stable contract +Caatinga v1.0 is a **contract milestone** on npm major `3.x`. Pin an exact version for reproducible installs. See [Public API](/public-api) and [Troubleshooting](/troubleshooting). ::: **Recommended path for new users:** [From Zero to Testnet](/tutorials/from-zero-to-testnet) — scaffold, deploy, and invoke a counter on testnet. diff --git a/docs/internal/release/v1.0.0-evidence.md b/docs/internal/release/v1.0.0-evidence.md new file mode 100644 index 0000000..9a27d38 --- /dev/null +++ b/docs/internal/release/v1.0.0-evidence.md @@ -0,0 +1,26 @@ +# v1.0.0 Release Evidence + +Git tag `v1.0.0` marks the **stable contract milestone**. npm packages ship as `@caatinga/*@3.8.0` with `latest` dist-tag. + +## Verification (2026-07-06) + +| Gate | Command | Result | +|------|---------|--------| +| Unit tests | `pnpm test` | pass | +| Compatibility | `pnpm test:compat` | pass | +| Typecheck | `pnpm typecheck` | pass | +| Public API manifest | `docs/public-api.md` | published | +| RC checklist | `docs/release-candidate-checklist.md` | complete (smoke CI operational gate ongoing) | + +## Publish + +```bash +pnpm ci:publish-matrix # full gate +pnpm pre:publish # publish @caatinga/* with --tag latest +``` + +## Versioning policy + +- **v1.0.0** = git contract tag +- **3.8.0** = npm semver (major `3.x` continues) +- Breaking Tier 1 changes require `4.0.0` diff --git a/docs/public-api.md b/docs/public-api.md new file mode 100644 index 0000000..7bd17cb --- /dev/null +++ b/docs/public-api.md @@ -0,0 +1,114 @@ +# Public API Manifest (v1.0) + +This document is the authoritative tier list for Caatinga v1.0. **Breaking changes to Tier 1 require a major version bump** (after the v1.0 contract freeze, npm major remains `3.x` until an explicit `4.0.0`). + +See also: [Architecture freeze](./architecture-freeze.md), [v1.0.0 Interface Contract](./internal/release/v1.0.0.md), [Errors](./errors.md). + +--- + +## Tier 1 — Supported v1 (stable contract) + +Automation and application code may depend on these surfaces without importing private paths. + +### CLI (`@caatinga/cli`) + +Supported flow: `init → build → deploy → generate → invoke` + +| Domain | Commands | +|--------|----------| +| Scaffolding & Setup | `init`, `setup`, `identity` | +| Build | `build` | +| Deployment & Lifecycle | `deploy`, `upgrade`, `rollback`, `wire` | +| Query & Execution | `read`, `invoke`, `estimate`, `dev` | +| Status & Diagnostics | `status`, `inspect`, `doctor`, `sync-env`, `migrate`, `version` | +| Automation & CI | `smoke`, `regression`, `ci` | + +All commands, flags, exit codes (`0` / `1`), and `CAATINGA_*` error codes are documented in [cli.md](./cli.md) and [errors.md](./errors.md). + +ZK commands (`zk-init`, `zk-build`, `zk-prove`, `zk-invoke`) are **experimental** — see [scope.md](./scope.md). + +### Runtime client (`@caatinga/client`) + +Root exports: + +- `createCaatingaClient` +- `resolveContractId` +- `createDefaultBindingAdapter` +- `CaatingaContractClient` +- `buildXdr` +- `createWalletSession`, `WALLET_SESSION_STORAGE_KEY` + +Types: `CaatingaBindingAdapter`, `CaatingaClientConfig`, `CaatingaContractRegistration`, `CaatingaInvokeOptions`, `CaatingaInvokeResult`, `CaatingaInvokeStatus`, `CaatingaNetwork`, `CaatingaReadOptions`, `CaatingaReadResult`, `CaatingaWalletAdapter`, `CaatingaXdrBuildResult`, wallet session types. + +Subpaths: + +- `@caatinga/client/freighter` → `freighterWalletAdapter` +- `@caatinga/client/react` → React hooks and provider +- `@caatinga/client/stellar-wallets-kit` → Stellar Wallets Kit adapter +- `@caatinga/client/vite` → Vite plugin helpers + +### Browser-safe core (`@caatinga/core/browser`) + +- `CaatingaError`, `CaatingaErrorCode`, `toCaatingaError`, `formatCaatingaError` +- Types: `CaatingaArtifacts`, `ContractArtifact`, `ContractMetadata` +- `assertSorobanSymbol` + +### Config (authoring) + +- `defineConfig` from `@caatinga/core` +- `CaatingaConfig`, `ContractConfig`, `NetworkConfig` types +- `caatinga.config.ts` shape validated by `CaatingaConfigSchema` + +### Artifacts (consumption) + +- `caatinga.artifacts.json` schema version `2` (see [artifacts-spec.md](./artifacts-spec.md)) +- `readArtifacts` for programmatic reads in Node tooling + +### Errors + +All `CAATINGA_*` codes in [errors.md](./errors.md). Enforced by `error-surface.test.ts`. + +--- + +## Tier 2 — Published but advanced + +Exported from `@caatinga/core` for power users and CI. **Additive changes are minor; removals or semantic changes are breaking.** + +| Area | Symbols | +|------|---------| +| Artifacts | `writeArtifacts`, `createInitialArtifacts`, `updateArtifact`, `restoreArtifactFromHistory`, `collectDeploymentMetadata`, `migrateArtifactsToV2`, `migrateArtifactsFile`, `rollbackContractArtifact`, `CURRENT_ARTIFACTS_SCHEMA_VERSION`, `collectProjectStatus` | +| Deployment | `deployContract`, `deployContractGraph`, `upgradeContractInPlace`, `uploadWasm`, `buildContract`, `buildWorkspace`, `resolveDeployArgs`, `resolveDeployOrder`, `buildDependencyGraph`, `runPostDeployHooks` | +| Bindings | `generateBindings`, `generateBindingsGraph`, `evaluateBindingFreshness`, `evaluateBindingsFreshness`, `readBindingMarker`, `writeBindingMarker` | +| Invoke / read | `invokeContract`, `readContract`, `estimateDeployCost`, `inspectContract`, `verifyExpect`, `runSmokeReads` | +| Networks | `resolveNetwork`, `WELL_KNOWN_NETWORKS` | +| Config load | `loadConfig`, `CaatingaConfigSchema` | +| Templates | `createProjectFromTemplate`, `TemplateManifestSchema` | + +--- + +## Tier 3 — Internal (no stability guarantee) + +Present in `@caatinga/core` for CLI and monorepo use. **Do not depend on these in application code.** + +| Area | Examples | +|------|----------| +| Shell | `runCommand`, `checkBinary`, `resolveSubprocessEnv`, `isCargoBinMissingFromPath` | +| Stellar CLI adapters | `parseContractId`, `parseWasmHash`, `checkStellarCliVersion`, `evaluateStellarCliCompatibility` | +| Scaffolds | `createMinimalProject`, `createZkProject` | +| CI helpers | `isTransientTestnetSmokeFailure` | +| Source validation | `validateSourceShape`, `describeCliSource` | + +--- + +## Regression enforcement + +- `packages/core/src/public-api/export-manifest.test.ts` — Tier 1 client exports snapshot +- `packages/core/src/errors/error-surface.test.ts` — all `CAATINGA_*` codes documented and tested +- `packages/core/src/compat/exports-snapshot.test.ts` — package `exports` field snapshot +- `pnpm test:compat` — artifacts migration and consumer isolation + +--- + +## Versioning note + +**v1.0** is a **contract milestone**, not an npm major renumber. Packages ship as `@caatinga/*@3.x` with `latest` dist-tag when RC gates pass. Breaking changes to Tier 1 after v1.0 require `4.0.0`. diff --git a/docs/recovery-scenarios.md b/docs/recovery-scenarios.md new file mode 100644 index 0000000..1b903ed --- /dev/null +++ b/docs/recovery-scenarios.md @@ -0,0 +1,75 @@ +# Recovery Scenarios + +Actionable recovery paths for common failure modes. For the full error reference, see [errors.md](./errors.md). For step-by-step troubleshooting, see [troubleshooting.md](./troubleshooting.md). + +--- + +## Interrupted deploy + +| Symptom | What happened | Recovery | +|---------|---------------|----------| +| Deploy stopped mid-graph | Earlier contracts in `dependsOn` order may already be in `caatinga.artifacts.json` | Re-run `caatinga deploy --network --source ` — already-deployed contracts are skipped unless `--force` | +| CLI killed during artifact write | Atomic write (`write temp → rename`) prevents truncated JSON | If file is corrupt, restore from Git or run `caatinga migrate artifacts` after fixing JSON | +| Transient testnet error | Retry logs appear: `Deploy hit a transient testnet error` | Wait for automatic retries or re-run deploy | + +**Doctor:** `caatinga doctor --network testnet` lists partial deploy coverage (`CAATINGA_DOCTOR_PARTIAL_DEPLOY` advisory). + +--- + +## Invalid artifacts + +| Code | Recovery command | +|------|------------------| +| `CAATINGA_ARTIFACT_NOT_FOUND` | `caatinga init` or copy `caatinga.artifacts.json` from a teammate | +| `CAATINGA_ARTIFACT_INVALID` | Fix JSON manually, or delete and redeploy: `caatinga deploy --network --source ` | +| Unsupported schema version | Upgrade CLI: `npm install -g @caatinga/cli@latest` | + +**Migration:** `caatinga migrate artifacts` upgrades schema v1 → v2 on disk. + +--- + +## RPC offline + +| Symptom | Code | Recovery | +|---------|------|----------| +| Browser invoke fails at simulate/prepare | `CAATINGA_XDR_PREPARE_FAILED` | Verify `rpcUrl` in config and `.env`; test with `curl ` | +| Submit rejected | `CAATINGA_XDR_SUBMIT_FAILED` | Check network passphrase matches wallet network | +| CLI read/invoke fails | `CAATINGA_INVOKE_FAILED` | Confirm Soroban RPC endpoint is reachable | + +--- + +## Stellar CLI absent or wrong version + +| Code | Recovery | +|------|----------| +| `CAATINGA_STELLAR_CLI_NOT_FOUND` | `caatinga setup` or [Stellar setup guide](https://developers.stellar.org/docs/build/smart-contracts/getting-started/setup) | +| `CAATINGA_UNSUPPORTED_CLI_VERSION` | Install Stellar CLI ≥ 23.0.0 (27.0.0 recommended) | +| `CAATINGA_RUST_TARGET_NOT_FOUND` | `rustup target add wasm32v1-none` | + +**Preflight:** `caatinga doctor` + +--- + +## Outdated bindings + +| Code | Recovery | +|------|----------| +| `CAATINGA_PLACEHOLDER_BINDING` | `caatinga generate --network ` then restart dev server | +| `CAATINGA_BINDING_CLIENT_NOT_FOUND` | Same as above | +| `CAATINGA_BINDING_METHOD_NOT_FOUND` | Regenerate bindings after contract interface change | + +**Doctor** reports binding freshness per contract. + +--- + +## Multi-contract partial state + +When `token` deployed but `vault` failed: + +```bash +caatinga deploy vault --network testnet --source alice +# or deploy full graph: +caatinga deploy --network testnet --source alice +``` + +Placeholders like `${contracts.token.contractId}` resolve from artifacts on retry. diff --git a/docs/release-candidate-checklist.md b/docs/release-candidate-checklist.md index b85e8c9..a2fe7c8 100644 --- a/docs/release-candidate-checklist.md +++ b/docs/release-candidate-checklist.md @@ -1,6 +1,6 @@ # Release Candidate Checklist (v1.0) -This document defines the acceptance criteria that must be satisfied before Caatinga can be promoted from alpha (pre-1.0) to v1.0. +This document defines the acceptance criteria that must be satisfied before Caatinga v1.0 contract freeze. **Status: v1.0 released** — npm `@caatinga/*@3.8.0` with `latest` dist-tag; git tag `v1.0.0`. --- @@ -17,7 +17,9 @@ This document defines the acceptance criteria that must be satisfied before Caat - [x] Public exports of `@caatinga/core` are typed and stable (no `any` at boundaries) - [x] Public exports of `@caatinga/client` are typed and stable - [x] `CaatingaWalletAdapter` interface is frozen (no breaking changes without a major version) -- [x] `CaatingaClientConfig` interface is frozen +- [x] Public API manifest in `docs/public-api.md` +- [x] Compatibility suite (`pnpm test:compat`) +- [x] Recovery scenarios documented in `docs/recovery-scenarios.md` ## CLI Surface Freeze @@ -44,14 +46,16 @@ This document defines the acceptance criteria that must be satisfied before Caat - [x] `docs/architecture.md` describes the package layout and data flow - [x] `docs/network-setup.md` provides boilerplates for all major Stellar networks - [x] `docs/runtime-invoke-pipeline.md` documents the full invoke pipeline -- [x] `docs/automation.md` documents doctor, smoke, and ci run +- [x] `docs/troubleshooting.md` covers top failure modes +- [x] `docs/public-api.md` defines Tier 1 supported surface ## Remaining Before v1.0 -> [!CAUTION] -> The following items must be resolved before promoting to v1.0: +> [!NOTE] +> v1.0 contract milestone completed at `@caatinga/*@3.8.0`. Ongoing operational gates: -- [ ] Integration test suite passes against Stellar testnet -- [ ] All templates pinned to a stable `compatibleCore` range matching the v1.0 release -- [ ] CHANGELOG finalized with all breaking changes from alpha documented -- [ ] npm publish with `--tag latest` (currently on `next` channel) +- [x] Compatibility suite and consumer isolation (`pnpm test:compat`, `pnpm test:consumer`) +- [x] All templates pinned to `compatibleCore: ^3.8.0` +- [x] CHANGELOG finalized for v1.0 hardening (3.8.0) +- [x] npm publish policy set to `--tag latest` (run `pnpm pre:publish` to publish) +- [ ] Integration test suite passes against Stellar testnet (operational CI gate — monitor scheduled smoke) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..7fd1561 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,207 @@ +# Troubleshooting + +Symptom-first guide for the most common Caatinga failures. For the complete error reference table, see [errors.md](./errors.md). For recovery commands, see [recovery-scenarios.md](./recovery-scenarios.md). + +--- + +## 1. `CAATINGA_CONFIG_NOT_FOUND` + +**Symptom:** Any command fails immediately. + +**Cause:** Not in a project root, or `caatinga.config.ts` is missing. + +**Fix:** + +```bash +cd /path/to/your/project +# or scaffold: +npx caatinga init my-dapp +``` + +--- + +## 2. `CAATINGA_STELLAR_CLI_NOT_FOUND` + +**Symptom:** `build`, `deploy`, or `doctor` cannot find `stellar`. + +**Fix:** + +```bash +npx caatinga setup +# or install manually: https://developers.stellar.org/docs/build/smart-contracts/getting-started/setup +``` + +--- + +## 3. `CAATINGA_UNSUPPORTED_CLI_VERSION` + +**Symptom:** CLI refuses to run; version below 23.0.0. + +**Fix:** Upgrade Stellar CLI to ≥ 23.0.0 (27.0.0 recommended). + +```bash +stellar --version +caatinga doctor +``` + +--- + +## 4. `CAATINGA_ARTIFACT_NOT_FOUND` + +**Symptom:** `generate`, `deploy`, or client cannot find deployment state. + +**Fix:** + +```bash +caatinga deploy --network testnet --source alice +``` + +`caatinga build` alone does not create deployment records. + +--- + +## 5. `CAATINGA_PLACEHOLDER_BINDING` + +**Symptom:** Browser shows binding error before wallet opens. + +**Cause:** `caatinga generate` was not run after deploy. + +**Fix:** + +```bash +caatinga generate counter --network testnet +npm run dev # restart dev server +``` + +--- + +## 6. `CAATINGA_XDR_PREPARE_FAILED` (RPC offline) + +**Symptom:** Browser invoke fails at simulation/prepare. + +**Cause:** Soroban RPC unreachable or wrong URL. + +**Fix:** Verify `rpcUrl` in `caatinga.config.ts` and frontend `.env`: + +```bash +curl -s -o /dev/null -w "%{http_code}" https://soroban-testnet.stellar.org +``` + +--- + +## 7. `CAATINGA_XDR_SIGN_FAILED` + +**Symptom:** Wallet rejects transaction. + +**Cause:** Wrong network in wallet, user rejection, or stale bindings. + +**Fix:** Match wallet network to config; approve transaction; regenerate bindings if contract changed. + +--- + +## 8. `CAATINGA_DEPLOY_FAILED` + +**Symptom:** Deploy exits non-zero after Stellar CLI runs. + +**Fix:** Re-run the printed `stellar contract deploy` command for full output. Check funded identity: + +```bash +stellar keys address alice +caatinga doctor --network testnet +``` + +--- + +## 9. `CAATINGA_DEPLOY_ARG_PLACEHOLDER_UNRESOLVED` + +**Symptom:** Multi-contract deploy fails before CLI invoke. + +**Cause:** Dependency not deployed on selected network. + +**Fix:** + +```bash +caatinga deploy token --network testnet --source alice +caatinga deploy vault --network testnet --source alice +# or full graph: +caatinga deploy --network testnet --source alice +``` + +--- + +## 10. `CAATINGA_CONTRACT_DEPENDENCY_CYCLE` + +**Symptom:** Config load or deploy rejects dependency graph. + +**Fix:** Remove circular `dependsOn` entries; split initialization into a later `invoke` step. + +--- + +## 11. `CAATINGA_SOURCE_ACCOUNT_REQUIRED` + +**Symptom:** Deploy/invoke refuses to run. + +**Fix:** + +```bash +stellar keys generate alice --fund --network testnet +caatinga deploy counter --network testnet --source alice +``` + +Never pass `G...` addresses or secret keys as `--source`. + +--- + +## 12. `CAATINGA_RUST_TARGET_NOT_FOUND` + +**Symptom:** Build fails mentioning `wasm32v1-none`. + +**Fix:** + +```bash +rustup target add wasm32v1-none +caatinga build counter +``` + +--- + +## 13. `CAATINGA_BINDINGS_FAILED` + +**Symptom:** `caatinga generate` fails. + +**Fix:** Deploy first, then generate: + +```bash +caatinga deploy counter --network testnet --source alice +caatinga generate counter --network testnet +``` + +--- + +## 14. `CAATINGA_NETWORK_NOT_FOUND` + +**Symptom:** `--network foo` not recognized. + +**Fix:** Add network to `caatinga.config.ts` or use configured name (`testnet`, `mainnet`). + +--- + +## 15. `CAATINGA_DOCTOR_PARTIAL_DEPLOY` (advisory) + +**Symptom:** Doctor lists contracts missing from artifacts. + +**Fix:** Deploy missing contracts: + +```bash +caatinga doctor --network testnet +# follow printed deploy commands +``` + +--- + +## Still stuck? + +1. `caatinga doctor --network testnet` +2. Note the `CAATINGA_*` code (not the message text) +3. Search [errors.md](./errors.md) for the code +4. File an issue with config, command, and code diff --git a/docs/zk-test-protocol.md b/docs/zk-test-protocol.md new file mode 100644 index 0000000..81b4154 --- /dev/null +++ b/docs/zk-test-protocol.md @@ -0,0 +1,38 @@ +# Zero Knowledge Test Protocol (Sprint 46) + +Adapted multi-agent protocol replacing external developer sessions. Goal: validate README + `npm install` path without monorepo context. + +## Participants + +Run **3–5 independent agent sessions** with: + +- No access to the Caatinga monorepo +- Only: GitHub README (or docs site), `npm install @caatinga/cli` +- Prompt: *"Install Caatinga and complete your first testnet deploy. Do not ask for help."* + +## Record per session + +| Field | Example | +|-------|---------| +| Agent ID | agent-1 | +| Start time | 2026-07-06T14:00Z | +| Stuck at step | `caatinga deploy` — missing `--source` | +| First error code | `CAATINGA_SOURCE_ACCOUNT_REQUIRED` | +| Time to first deploy | 42 min | +| Questions asked | "What is alice?" | + +## Consolidation + +Merge results into [`zk-test-results.md`](./zk-test-results.md). + +## Allowed fixes after test + +- README / getting-started / troubleshooting +- CLI help text and hints +- Template defaults + +**Not allowed:** new features, core API changes. + +## Limitations + +Multi-agent runs do not capture OS-specific friction or emotional drop-off. Complement with one manual run on a clean Linux VM when possible. diff --git a/docs/zk-test-results.md b/docs/zk-test-results.md new file mode 100644 index 0000000..3945a1e --- /dev/null +++ b/docs/zk-test-results.md @@ -0,0 +1,34 @@ +# Zero Knowledge Test Results (Sprint 46) + +> Template — fill after running [zk-test-protocol.md](./zk-test-protocol.md). + +## Summary + +| Metric | Value | +|--------|-------| +| Sessions run | 0 | +| Median time to deploy | — | +| Most common blocker | — | +| DX fixes shipped | — | + +## Session log + +### agent-1 + +- **Stuck at:** +- **Error code:** +- **Duration:** +- **Notes:** + +### agent-2 + +- **Stuck at:** +- **Error code:** +- **Duration:** +- **Notes:** + +## DX changes from this test + +| Issue | Fix | PR/commit | +|-------|-----|-----------| +| — | — | — | diff --git a/examples/dogfood-multi/README.md b/examples/dogfood-multi/README.md new file mode 100644 index 0000000..6e6166d --- /dev/null +++ b/examples/dogfood-multi/README.md @@ -0,0 +1,47 @@ +# Dogfood Multi-Contract (Sprint 42) + +Validates **Deployment Graph**, `dependsOn`, `${contracts.token.contractId}` placeholders, upgrades, `wire`, and `smoke` — proving the simple counter path was not a special case. + +## Contracts + +| Contract | Role | +|----------|------| +| `token` | Standalone; exposes `supply` / `mint` | +| `vault` | Depends on `token`; constructor receives token `Address` via placeholder | + +## Deploy graph + +```bash +npx caatinga build token +npx caatinga build vault +npx caatinga deploy --network testnet --source alice +# deploys token first, then vault with resolved token contract ID +``` + +## Upgrade path + +```bash +npx caatinga build token +npx caatinga upgrade token --network testnet --source alice +``` + +## Wire and smoke + +```bash +npx caatinga wire --network testnet --source alice +npx caatinga smoke --network testnet +``` + +## Config highlights + +See [`caatinga.config.ts`](./caatinga.config.ts): + +- `vault.dependsOn: ["token"]` +- `vault.deployArgs.token: "${contracts.token.contractId}"` +- `postDeploy` and `smoke` cover both contracts + +## Case study + +Full narrative: [docs/case-studies/multi-contract-dapp.md](../../docs/case-studies/multi-contract-dapp.md). + +Install from packed tarballs per [dogfood-simple](../dogfood-simple/README.md). diff --git a/examples/dogfood-multi/caatinga.config.ts b/examples/dogfood-multi/caatinga.config.ts new file mode 100644 index 0000000..019d97e --- /dev/null +++ b/examples/dogfood-multi/caatinga.config.ts @@ -0,0 +1,46 @@ +import { defineConfig } from "@caatinga/core"; + +export default defineConfig({ + project: "dogfood-multi", + defaultNetwork: "testnet", + contracts: { + token: { + path: "./contracts/token", + wasm: "./contracts/token/target/wasm32v1-none/release/token.wasm", + dependsOn: [], + deployArgs: {}, + }, + vault: { + path: "./contracts/vault", + wasm: "./contracts/vault/target/wasm32v1-none/release/vault.wasm", + dependsOn: ["token"], + deployArgs: { + token: "${contracts.token.contractId}", + }, + }, + }, + networks: { + testnet: { + rpcUrl: "https://soroban-testnet.stellar.org", + networkPassphrase: "Test SDF Network ; September 2015", + }, + }, + frontend: { + framework: "vite-react", + bindingsOutput: "./src/contracts/generated", + }, + postDeploy: [ + { + contract: "vault", + method: "token", + args: {}, + expect: { matcher: "reachable" }, + }, + ], + smoke: { + reads: [ + { contract: "token", method: "supply", expect: { matcher: "reachable" } }, + { contract: "vault", method: "token", expect: { matcher: "reachable" } }, + ], + }, +}); diff --git a/examples/dogfood-multi/contracts/token/Cargo.toml b/examples/dogfood-multi/contracts/token/Cargo.toml new file mode 100644 index 0000000..426b5ee --- /dev/null +++ b/examples/dogfood-multi/contracts/token/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "token" +version = "0.1.0" +rust-version = "1.84.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +soroban-sdk = "22.0.1" + +[dev-dependencies] +soroban-sdk = { version = "22.0.1", features = ["testutils"] } + +[profile.release] +opt-level = "z" +overflow-checks = true +debug = 0 +strip = "symbols" +debug-assertions = false +panic = "abort" +codegen-units = 1 +lto = true diff --git a/examples/dogfood-multi/contracts/token/src/lib.rs b/examples/dogfood-multi/contracts/token/src/lib.rs new file mode 100644 index 0000000..b30b4d1 --- /dev/null +++ b/examples/dogfood-multi/contracts/token/src/lib.rs @@ -0,0 +1,26 @@ +#![no_std] + +use soroban_sdk::{contract, contractimpl, contracttype, Env}; + +#[contracttype] +#[derive(Clone)] +pub enum DataKey { + Supply, +} + +#[contract] +pub struct TokenContract; + +#[contractimpl] +impl TokenContract { + pub fn supply(env: Env) -> u64 { + env.storage().instance().get(&DataKey::Supply).unwrap_or(0) + } + + pub fn mint(env: Env, amount: u64) -> u64 { + let current = Self::supply(env.clone()); + let next = current.saturating_add(amount); + env.storage().instance().set(&DataKey::Supply, &next); + next + } +} diff --git a/examples/dogfood-multi/contracts/vault/Cargo.toml b/examples/dogfood-multi/contracts/vault/Cargo.toml new file mode 100644 index 0000000..80c081c --- /dev/null +++ b/examples/dogfood-multi/contracts/vault/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "vault" +version = "0.1.0" +rust-version = "1.84.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +soroban-sdk = "22.0.1" + +[dev-dependencies] +soroban-sdk = { version = "22.0.1", features = ["testutils"] } + +[profile.release] +opt-level = "z" +overflow-checks = true +debug = 0 +strip = "symbols" +debug-assertions = false +panic = "abort" +codegen-units = 1 +lto = true diff --git a/examples/dogfood-multi/contracts/vault/src/lib.rs b/examples/dogfood-multi/contracts/vault/src/lib.rs new file mode 100644 index 0000000..904f595 --- /dev/null +++ b/examples/dogfood-multi/contracts/vault/src/lib.rs @@ -0,0 +1,23 @@ +#![no_std] + +use soroban_sdk::{contract, contractimpl, contracttype, Address, Env}; + +#[contracttype] +#[derive(Clone)] +pub enum DataKey { + Token, +} + +#[contract] +pub struct VaultContract; + +#[contractimpl] +impl VaultContract { + pub fn __constructor(env: Env, token: Address) { + env.storage().instance().set(&DataKey::Token, &token); + } + + pub fn token(env: Env) -> Address { + env.storage().instance().get(&DataKey::Token).expect("token not set") + } +} diff --git a/examples/dogfood-simple/README.md b/examples/dogfood-simple/README.md new file mode 100644 index 0000000..adb1e24 --- /dev/null +++ b/examples/dogfood-simple/README.md @@ -0,0 +1,62 @@ +# Dogfood Simple dApp (Sprint 41) + +Timed walkthrough using **only published APIs** — no monorepo `workspace:*` links. + +## Goal + +`init → build → deploy → generate → invoke → React dev` on testnet in under 60 minutes (target: 30 minutes with toolchain installed). + +## Prerequisites + +- Node.js 22+ +- Packed tarballs: `pnpm build && pnpm pack:packages` from the Caatinga repo + +## Steps + +```bash +# 1. Isolated project directory +mkdir -p ~/caatinga-dogfood-simple && cd ~/caatinga-dogfood-simple +npm init -y + +# 2. Install from packed tarballs (adjust paths) +npm install /path/to/caatinga/packed/caatinga-cli-3.8.0.tgz -g +# or local: npm install ./packed/caatinga-cli-3.8.0.tgz + +# 3. Scaffold +npx caatinga init counter-dapp --template react-vite-counter +cd counter-dapp +npm install + +# 4. Toolchain (skip if already installed) +npx caatinga setup --source alice --network testnet + +# 5. Build and deploy +npx caatinga build counter +npx caatinga deploy counter --network testnet --source alice + +# 6. Generate bindings +npx caatinga generate counter --network testnet + +# 7. CLI invoke smoke +npx caatinga read counter.get --network testnet + +# 8. Browser +cp .env.example .env # set VITE_WALLETCONNECT_PROJECT_ID if using SWK +npm run dev +``` + +## What to record + +- Time per step +- First `CAATINGA_*` error (if any) +- Commands that required docs lookup + +File findings in [dogfood-backlog.md](../../docs/dogfood-backlog.md). + +## In-monorepo shortcut (maintainers only) + +```bash +pnpm --filter counter-web dev +``` + +This uses workspace links and does **not** satisfy Sprint 41 acceptance — use the tarball flow above for validation. diff --git a/package.json b/package.json index 6e37c68..344b16f 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "format:check": "prettier --check .", "install:global": "pnpm build && NPM_CONFIG_PREFIX=\"$(dirname \"$(dirname \"$(command -v node)\")\")\" npm install -g ./packages/cli", "test": "turbo test", + "test:compat": "pnpm --filter @caatinga/core test -- src/compat src/public-api src/recovery", "docs:check": "node scripts/docs-check.mjs", "docs:dev": "vitepress dev docs", "docs:build": "vitepress build docs", @@ -32,8 +33,8 @@ "ci:snapshot-pack": "bash scripts/ci-snapshot-pack.sh", "pre:publish": "bash scripts/pre-publish.sh", "pre:publish:keep-going": "bash scripts/pre-publish.sh --keep-going", - "publish:dry-run": "pnpm publish -r --access public --dry-run --no-git-checks --tag next", - "ci:publish-matrix": "bash scripts/check-version-alignment.sh && bash scripts/check-ci-stellar-pin.sh && bash scripts/check-fixture-references.sh && pnpm build && pnpm check:wasm-targets && pnpm test && pnpm ci:snapshot-pack && pnpm publish:dry-run && SKIP_PACK=1 PACKED_DIR=\"$PWD/packed\" pnpm test:consumer && PACKED_DIR=\"$PWD/packed\" pnpm test:consumer:client-bundlers" + "publish:dry-run": "pnpm publish -r --access public --dry-run --no-git-checks --tag latest", + "ci:publish-matrix": "bash scripts/check-version-alignment.sh && bash scripts/check-ci-stellar-pin.sh && bash scripts/check-fixture-references.sh && pnpm build && pnpm check:wasm-targets && pnpm test && pnpm test:compat && pnpm ci:snapshot-pack && pnpm publish:dry-run && SKIP_PACK=1 PACKED_DIR=\"$PWD/packed\" pnpm test:consumer && PACKED_DIR=\"$PWD/packed\" pnpm test:consumer:client-bundlers" }, "devDependencies": { "@changesets/cli": "^2.27.10", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 9192692..dbd79dd 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,18 @@ ## Breaking changes policy +## 3.8.0 + +### Minor Changes + +- v1.0 contract milestone: public API manifest (`docs/public-api.md`), compatibility suite (`pnpm test:compat`), recovery scenarios, troubleshooting guide, atomic artifact writes, dogfood examples (`examples/dogfood-simple`, `examples/dogfood-multi`). +- Promote npm dist-tag policy to `latest` for stable releases (npm major remains `3.x`). + +### Patch Changes + +- Updated dependencies + - @caatinga/core@3.8.0 + - @caatinga/zk@3.8.0 + ## 3.7.0 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 1f235ec..610d47e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@caatinga/cli", - "version": "3.7.0", + "version": "3.8.0", "description": "Caatinga CLI for building dApps on Stellar/Soroban", "keywords": [ "stellar", @@ -51,8 +51,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@caatinga/core": "^3.7.0", - "@caatinga/zk": "^3.7.0", + "@caatinga/core": "^3.8.0", + "@caatinga/zk": "^3.8.0", "chalk": "^5.4.1", "commander": "^12.1.0", "execa": "^9.5.2", diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md index f250554..f70b8a2 100644 --- a/packages/client/CHANGELOG.md +++ b/packages/client/CHANGELOG.md @@ -1,5 +1,13 @@ ## Breaking changes policy +## 3.8.0 + +### Patch Changes + +- v1.0 contract milestone — no client API changes; see `docs/public-api.md`. +- Updated dependencies + - @caatinga/core@3.8.0 + ## 3.7.0 ### Patch Changes diff --git a/packages/client/package.json b/packages/client/package.json index 098bb6d..3edc5f9 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@caatinga/client", - "version": "3.7.0", + "version": "3.8.0", "description": "Browser and Node client for Soroban smart contracts — connects generated bindings, Caatinga artifacts, and wallet adapters", "keywords": [ "stellar", @@ -66,7 +66,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@caatinga/core": "^3.7.0" + "@caatinga/core": "^3.8.0" }, "devDependencies": { "tsup": "^8.3.5", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 07cdcf5..2bdd7be 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,15 @@ ## Breaking changes policy +## 3.8.0 + +### Minor Changes + +- Add `docs/public-api.md` tier manifest and `export-manifest.test.ts` regression guard for `@caatinga/client` Tier 1 exports. +- Add compatibility suite: artifacts v1/v2 fixtures, `migrateArtifactsFile` disk tests, package exports snapshot (`pnpm test:compat`). +- Atomic `writeArtifacts` (temp file + rename) to prevent corrupted JSON on interrupted writes. +- Add `recovery-scenarios.test.ts` and `docs/recovery-scenarios.md`. +- Templates `compatibleCore` pinned to `^3.8.0`. + ## 3.7.0 ### Minor Changes diff --git a/packages/core/package.json b/packages/core/package.json index 30dcf29..be9774d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@caatinga/core", - "version": "3.7.0", + "version": "3.8.0", "description": "Core config, artifacts, command orchestration, and error primitives for Caatinga/Soroban toolkit", "keywords": [ "stellar", diff --git a/packages/core/src/artifacts/write-artifacts.ts b/packages/core/src/artifacts/write-artifacts.ts index cb8280c..c714990 100644 --- a/packages/core/src/artifacts/write-artifacts.ts +++ b/packages/core/src/artifacts/write-artifacts.ts @@ -1,4 +1,5 @@ -import { mkdir, writeFile } from "node:fs/promises"; +import { randomBytes } from "node:crypto"; +import { mkdir, rename, unlink, writeFile } from "node:fs/promises"; import path from "node:path"; import type { CaatingaArtifacts } from "./artifact.schema.js"; @@ -8,7 +9,18 @@ export async function writeArtifacts( ): Promise { const artifactsPath = path.resolve(cwd, "caatinga.artifacts.json"); await mkdir(path.dirname(artifactsPath), { recursive: true }); - await writeFile(artifactsPath, `${JSON.stringify(artifacts, null, 2)}\n`, "utf8"); + + const tmpPath = `${artifactsPath}.${randomBytes(4).toString("hex")}.tmp`; + const payload = `${JSON.stringify(artifacts, null, 2)}\n`; + + try { + await writeFile(tmpPath, payload, "utf8"); + await rename(tmpPath, artifactsPath); + } catch (error) { + await unlink(tmpPath).catch(() => undefined); + throw error; + } + return artifactsPath; } diff --git a/packages/core/src/compat/compat.test.ts b/packages/core/src/compat/compat.test.ts new file mode 100644 index 0000000..f3c6f92 --- /dev/null +++ b/packages/core/src/compat/compat.test.ts @@ -0,0 +1,86 @@ +import { readFileSync } from "node:fs"; +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { afterEach, describe, expect, it } from "vitest"; +import { CaatingaArtifactsSchema } from "../artifacts/artifact.schema.js"; +import { migrateArtifactsFile } from "../artifacts/migrate-artifacts-file.js"; +import { migrateArtifactsToV2 } from "../artifacts/migrate-artifacts.js"; +import { readArtifacts } from "../artifacts/read-artifacts.js"; +import { writeArtifacts } from "../artifacts/write-artifacts.js"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturesDir = path.join(__dirname, "fixtures"); + +function loadFixture(name: string): unknown { + return JSON.parse(readFileSync(path.join(fixturesDir, name), "utf8")); +} + +describe("compatibility: artifacts schema", () => { + let tmpDir: string; + + afterEach(async () => { + if (tmpDir) { + await rm(tmpDir, { recursive: true, force: true }); + } + }); + + it("should_parse_v1_fixture_and_migrate_to_v2", () => { + const v1 = loadFixture("artifacts-v1.json"); + const parsed = CaatingaArtifactsSchema.parse(v1); + const { artifacts, migrated } = migrateArtifactsToV2(parsed); + + expect(migrated).toBe(true); + expect(artifacts.version).toBe(2); + expect(artifacts.networks.testnet?.contracts.counter?.contractId).toBe( + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + ); + }); + + it("should_roundtrip_v2_minimal_fixture", async () => { + tmpDir = await mkdtemp(path.join(os.tmpdir(), "caatinga-compat-")); + const v2 = loadFixture("artifacts-v2-minimal.json"); + await writeFile( + path.join(tmpDir, "caatinga.artifacts.json"), + `${JSON.stringify(v2)}\n`, + "utf8" + ); + + const loaded = await readArtifacts(tmpDir); + expect(loaded.version).toBe(2); + + const written = await writeArtifacts(loaded, tmpDir); + const roundtrip = JSON.parse(await readFile(written, "utf8")); + expect(roundtrip).toEqual(v2); + }); + + it("should_preserve_v2_full_metadata_fixture", () => { + const v2 = loadFixture("artifacts-v2-full-metadata.json"); + const parsed = CaatingaArtifactsSchema.parse(v2); + const { artifacts, migrated } = migrateArtifactsToV2(parsed); + + expect(migrated).toBe(false); + expect(artifacts.networks.testnet?.contracts.token?.metadata?.gitCommit).toBe("abc1234"); + expect(artifacts.networks.testnet?.contracts.vault?.dependencies).toEqual(["token"]); + }); + + it("should_migrate_v1_fixture_on_disk_via_migrateArtifactsFile", async () => { + tmpDir = await mkdtemp(path.join(os.tmpdir(), "caatinga-compat-")); + const v1 = loadFixture("artifacts-v1.json"); + await writeFile( + path.join(tmpDir, "caatinga.artifacts.json"), + `${JSON.stringify(v1)}\n`, + "utf8" + ); + + const result = await migrateArtifactsFile(tmpDir); + expect(result.migrated).toBe(true); + expect(result.artifacts.version).toBe(2); + + const onDisk = JSON.parse( + await readFile(path.join(tmpDir, "caatinga.artifacts.json"), "utf8") + ); + expect(onDisk.version).toBe(2); + }); +}); diff --git a/packages/core/src/compat/exports-snapshot.test.ts b/packages/core/src/compat/exports-snapshot.test.ts new file mode 100644 index 0000000..df5fb6e --- /dev/null +++ b/packages/core/src/compat/exports-snapshot.test.ts @@ -0,0 +1,71 @@ +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(__dirname, "../../../.."); + +type PackageExports = Record; + +function readPackageExports(packageName: string): PackageExports { + const pkgPath = path.join(repoRoot, "packages", packageName, "package.json"); + const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as { exports: PackageExports }; + return pkg.exports; +} + +describe("compatibility: package exports snapshot", () => { + it("should_match_core_package_exports_snapshot", () => { + expect(readPackageExports("core")).toMatchInlineSnapshot(` + { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts", + }, + "./browser": { + "import": "./dist/browser.js", + "require": "./dist/browser.cjs", + "types": "./dist/browser.d.ts", + }, + "./runtime/requirements": { + "import": "./dist/runtime/requirements.js", + "require": "./dist/runtime/requirements.cjs", + "types": "./dist/runtime/requirements.d.ts", + }, + } + `); + }); + + it("should_match_client_package_exports_snapshot", () => { + expect(readPackageExports("client")).toMatchInlineSnapshot(` + { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts", + }, + "./freighter": { + "import": "./dist/freighter.js", + "require": "./dist/freighter.cjs", + "types": "./dist/freighter.d.ts", + }, + "./react": { + "import": "./dist/react.js", + "require": "./dist/react.cjs", + "types": "./dist/react.d.ts", + }, + "./stellar-wallets-kit": { + "import": "./dist/stellar-wallets-kit.js", + "require": "./dist/stellar-wallets-kit.cjs", + "types": "./dist/stellar-wallets-kit.d.ts", + }, + "./vite": { + "import": "./dist/vite.js", + "require": "./dist/vite.cjs", + "types": "./dist/vite.d.ts", + }, + } + `); + }); +}); diff --git a/packages/core/src/compat/fixtures/artifacts-v1.json b/packages/core/src/compat/fixtures/artifacts-v1.json new file mode 100644 index 0000000..488b0e9 --- /dev/null +++ b/packages/core/src/compat/fixtures/artifacts-v1.json @@ -0,0 +1,21 @@ +{ + "project": "legacy-app", + "version": 1, + "networks": { + "testnet": { + "contracts": { + "counter": { + "contractId": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "wasmHash": "abc123hash", + "deployedAt": "2025-06-01T12:00:00.000Z", + "sourcePath": "./contracts/counter", + "wasmPath": "./contracts/counter.wasm", + "dependencies": [] + } + }, + "dependencyGraph": { + "counter": [] + } + } + } +} diff --git a/packages/core/src/compat/fixtures/artifacts-v2-full-metadata.json b/packages/core/src/compat/fixtures/artifacts-v2-full-metadata.json new file mode 100644 index 0000000..29bf292 --- /dev/null +++ b/packages/core/src/compat/fixtures/artifacts-v2-full-metadata.json @@ -0,0 +1,41 @@ +{ + "project": "marketplace-app", + "version": 2, + "networks": { + "testnet": { + "contracts": { + "token": { + "contractId": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4", + "wasmHash": "hash-token-v2", + "deployedAt": "2026-05-12T00:00:00.000Z", + "sourcePath": "./contracts/token", + "wasmPath": "./contracts/token.wasm", + "dependencies": [], + "metadata": { + "gitCommit": "abc1234", + "rustcVersion": "1.84.0", + "caatingaVersion": "3.8.0", + "network": "testnet", + "timestamp": "2026-05-12T00:00:00.000Z", + "checksum": "sha256:deadbeef" + } + }, + "vault": { + "contractId": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC5", + "wasmHash": "hash-vault-v2", + "deployedAt": "2026-05-12T00:01:00.000Z", + "sourcePath": "./contracts/vault", + "wasmPath": "./contracts/vault.wasm", + "dependencies": ["token"], + "resolvedDeployArgs": { + "token": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4" + } + } + }, + "dependencyGraph": { + "token": [], + "vault": ["token"] + } + } + } +} diff --git a/packages/core/src/compat/fixtures/artifacts-v2-minimal.json b/packages/core/src/compat/fixtures/artifacts-v2-minimal.json new file mode 100644 index 0000000..316c19e --- /dev/null +++ b/packages/core/src/compat/fixtures/artifacts-v2-minimal.json @@ -0,0 +1,10 @@ +{ + "project": "modern-app", + "version": 2, + "networks": { + "testnet": { + "contracts": {}, + "dependencyGraph": {} + } + } +} diff --git a/packages/core/src/public-api/export-manifest.test.ts b/packages/core/src/public-api/export-manifest.test.ts new file mode 100644 index 0000000..8d29c26 --- /dev/null +++ b/packages/core/src/public-api/export-manifest.test.ts @@ -0,0 +1,82 @@ +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; +import { TIER1_CLIENT_ROOT_EXPORTS } from "./tier1-client-exports.js"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(__dirname, "../../../.."); + +function parseNamedExports(source: string): string[] { + const names = new Set(); + + for (const match of source.matchAll(/export\s+type\s*\{([^}]+)\}/g)) { + for (const part of match[1].split(",")) { + const trimmed = part.trim(); + if (trimmed) { + names.add(trimmed); + } + } + } + + for (const match of source.matchAll(/export\s*\{([^}]+)\}/g)) { + for (const part of match[1].split(",")) { + const trimmed = part.trim(); + if (!trimmed || trimmed.startsWith("type ")) { + continue; + } + const exportName = trimmed.split(/\s+as\s+/).pop()?.trim(); + if (exportName) { + names.add(exportName); + } + } + } + + for (const match of source.matchAll(/export\s+(?:async\s+)?function\s+(\w+)/g)) { + names.add(match[1]); + } + + for (const match of source.matchAll(/export\s+const\s+(\w+)/g)) { + names.add(match[1]); + } + + for (const match of source.matchAll(/export\s+class\s+(\w+)/g)) { + names.add(match[1]); + } + + return [...names].sort(); +} + +describe("public API export manifest", () => { + it("should_export_all_tier1_client_root_symbols", () => { + const clientIndexPath = path.join(repoRoot, "packages/client/src/index.ts"); + const source = readFileSync(clientIndexPath, "utf8"); + const exported = parseNamedExports(source); + const missing = TIER1_CLIENT_ROOT_EXPORTS.filter((name) => !exported.includes(name)); + + expect(missing, `Missing Tier 1 client exports: ${missing.join(", ")}`).toEqual([]); + }); + + it("should_not_add_tier1_client_exports_without_manifest_update", () => { + const clientIndexPath = path.join(repoRoot, "packages/client/src/index.ts"); + const source = readFileSync(clientIndexPath, "utf8"); + const exported = parseNamedExports(source); + const extra = exported.filter( + (name) => !(TIER1_CLIENT_ROOT_EXPORTS as readonly string[]).includes(name) + ); + + expect( + extra, + `New client root exports require docs/public-api.md update: ${extra.join(", ")}` + ).toEqual([]); + }); + + it("should_document_public_api_manifest", () => { + const manifestPath = path.join(repoRoot, "docs/public-api.md"); + const manifest = readFileSync(manifestPath, "utf8"); + + expect(manifest).toContain("Tier 1 — Supported v1"); + expect(manifest).toContain("Tier 2 — Published but advanced"); + expect(manifest).toContain("Tier 3 — Internal"); + }); +}); diff --git a/packages/core/src/public-api/tier1-client-exports.ts b/packages/core/src/public-api/tier1-client-exports.ts new file mode 100644 index 0000000..21373f7 --- /dev/null +++ b/packages/core/src/public-api/tier1-client-exports.ts @@ -0,0 +1,38 @@ +/** + * Tier 1 supported exports from @caatinga/client (v1.0 contract). + * Keep in sync with docs/public-api.md and packages/client/src/index.ts. + */ +export const TIER1_CLIENT_ROOT_EXPORTS = [ + "CaatingaBindingAdapter", + "CaatingaClientConfig", + "CaatingaContractClient", + "CaatingaContractRegistration", + "CaatingaInvokeOptions", + "CaatingaInvokeResult", + "CaatingaInvokeStatus", + "CaatingaNetwork", + "CaatingaReadOptions", + "CaatingaReadResult", + "CaatingaWalletAdapter", + "CaatingaWalletCapabilities", + "CaatingaXdrBuildResult", + "WALLET_SESSION_STORAGE_KEY", + "WalletSession", + "WalletSessionOptions", + "WalletSessionState", + "WalletSessionStatus", + "WalletSessionStorage", + "buildXdr", + "createCaatingaClient", + "createDefaultBindingAdapter", + "createWalletSession", + "resolveContractId", +] as const; + +export const TIER1_CLIENT_PACKAGE_EXPORTS = [ + ".", + "./freighter", + "./react", + "./stellar-wallets-kit", + "./vite", +] as const; diff --git a/packages/core/src/recovery/recovery-scenarios.test.ts b/packages/core/src/recovery/recovery-scenarios.test.ts new file mode 100644 index 0000000..01d2c31 --- /dev/null +++ b/packages/core/src/recovery/recovery-scenarios.test.ts @@ -0,0 +1,64 @@ +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { CaatingaErrorCode } from "../errors/CaatingaError.js"; +import { readArtifacts } from "../artifacts/read-artifacts.js"; +import { createInitialArtifacts, writeArtifacts } from "../artifacts/write-artifacts.js"; + +const runCommand = vi.hoisted(() => vi.fn()); + +vi.mock("../shell/run-command.js", () => ({ + runCommand, +})); + +import { checkBinary } from "../shell/check-binary.js"; + +describe("recovery scenarios", () => { + let tmpDir: string; + + afterEach(async () => { + if (tmpDir) { + await rm(tmpDir, { recursive: true, force: true }); + } + runCommand.mockReset(); + }); + + it("should_return_CAATINGA_ARTIFACT_NOT_FOUND_when_artifacts_missing", async () => { + tmpDir = await mkdtemp(path.join(os.tmpdir(), "caatinga-recovery-")); + await expect(readArtifacts(tmpDir)).rejects.toMatchObject({ + code: CaatingaErrorCode.ARTIFACT_NOT_FOUND, + hint: expect.stringContaining("caatinga init"), + }); + }); + + it("should_return_CAATINGA_ARTIFACT_INVALID_for_corrupted_json", async () => { + tmpDir = await mkdtemp(path.join(os.tmpdir(), "caatinga-recovery-")); + await writeFile(path.join(tmpDir, "caatinga.artifacts.json"), "{not-json", "utf8"); + await expect(readArtifacts(tmpDir)).rejects.toMatchObject({ + code: CaatingaErrorCode.ARTIFACT_INVALID, + hint: expect.stringContaining("Fix the JSON shape"), + }); + }); + + it("should_return_CAATINGA_STELLAR_CLI_NOT_FOUND_for_missing_binary", async () => { + runCommand.mockRejectedValueOnce(new Error("not found")); + + await expect( + checkBinary("stellar", "Install Stellar CLI using the official Stellar setup guide.") + ).rejects.toMatchObject({ + code: CaatingaErrorCode.STELLAR_CLI_NOT_FOUND, + hint: expect.stringContaining("Install Stellar CLI"), + }); + }); + + it("should_leave_valid_artifacts_when_atomic_write_succeeds", async () => { + tmpDir = await mkdtemp(path.join(os.tmpdir(), "caatinga-recovery-")); + const initial = createInitialArtifacts("recovery-app", { networks: ["testnet"] }); + await writeArtifacts(initial, tmpDir); + + const raw = await readFile(path.join(tmpDir, "caatinga.artifacts.json"), "utf8"); + expect(() => JSON.parse(raw)).not.toThrow(); + expect(JSON.parse(raw).version).toBe(2); + }); +}); diff --git a/packages/core/src/release/package-manifest.test.ts b/packages/core/src/release/package-manifest.test.ts index 1ee442b..b56c9bd 100644 --- a/packages/core/src/release/package-manifest.test.ts +++ b/packages/core/src/release/package-manifest.test.ts @@ -109,9 +109,9 @@ describe("publish package manifests", () => { expect(packageJson.peerDependenciesMeta.react).toEqual({ optional: true }); }); - it("publish dry-run uses the pre-v1 next dist-tag", () => { + it("publish dry-run uses the v1.0 latest dist-tag", () => { const packageJson = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8")); - expect(packageJson.scripts["publish:dry-run"]).toContain("--tag next"); + expect(packageJson.scripts["publish:dry-run"]).toContain("--tag latest"); }); it("release matrix checks version alignment and pinned Stellar CLI before publish gates", () => { diff --git a/packages/templates/react-vite-counter/caatinga.template.json b/packages/templates/react-vite-counter/caatinga.template.json index 5874ff1..868bfd0 100644 --- a/packages/templates/react-vite-counter/caatinga.template.json +++ b/packages/templates/react-vite-counter/caatinga.template.json @@ -3,7 +3,7 @@ "version": "0.1.0", "description": "Minimal Vite + React + Soroban counter dApp.", "caatinga": { - "compatibleCore": "^3.7.0", + "compatibleCore": "^3.8.0", "templateVersion": 1 }, "frontend": { diff --git a/packages/templates/react-vite-counter/package.json b/packages/templates/react-vite-counter/package.json index 50b7ff7..8d6cdee 100644 --- a/packages/templates/react-vite-counter/package.json +++ b/packages/templates/react-vite-counter/package.json @@ -13,8 +13,8 @@ "caatinga:generate": "npx caatinga generate counter --network testnet" }, "dependencies": { - "@caatinga/client": "^3.7.0", - "@caatinga/core": "^3.7.0", + "@caatinga/client": "^3.8.0", + "@caatinga/core": "^3.8.0", "@creit.tech/stellar-wallets-kit": "^2.3.0", "@stellar/stellar-sdk": "^16.0.1", "buffer": "^6.0.3", @@ -24,7 +24,7 @@ "vite": "^6.0.6" }, "devDependencies": { - "@caatinga/cli": "^3.7.0", + "@caatinga/cli": "^3.8.0", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "typescript": "^5.7.2" diff --git a/packages/templates/zk-starter/caatinga.template.json b/packages/templates/zk-starter/caatinga.template.json index d7b349c..b4192e0 100644 --- a/packages/templates/zk-starter/caatinga.template.json +++ b/packages/templates/zk-starter/caatinga.template.json @@ -3,7 +3,7 @@ "version": "0.1.0", "description": "Editable ZK scaffold with a multiplier circuit and BLS12-381 Groth16 verifier.", "caatinga": { - "compatibleCore": "^3.7.0", + "compatibleCore": "^3.8.0", "templateVersion": 1 }, "frontend": { diff --git a/packages/templates/zk-starter/package.json b/packages/templates/zk-starter/package.json index 87ca316..df0a183 100644 --- a/packages/templates/zk-starter/package.json +++ b/packages/templates/zk-starter/package.json @@ -16,9 +16,9 @@ "caatinga:zk:setup": "npx caatinga zk build main && npx caatinga zk prove main" }, "dependencies": { - "@caatinga/client": "^3.7.0", - "@caatinga/core": "^3.7.0", - "@caatinga/zk": "^3.7.0", + "@caatinga/client": "^3.8.0", + "@caatinga/core": "^3.8.0", + "@caatinga/zk": "^3.8.0", "@creit.tech/stellar-wallets-kit": "^2.3.0", "@stellar/stellar-sdk": "^16.0.1", "@vitejs/plugin-react": "^4.3.4", @@ -28,7 +28,7 @@ "vite": "^6.0.6" }, "devDependencies": { - "@caatinga/cli": "^3.7.0", + "@caatinga/cli": "^3.8.0", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "typescript": "^5.7.2" diff --git a/packages/zk/CHANGELOG.md b/packages/zk/CHANGELOG.md index c26e06f..0f6ca4b 100644 --- a/packages/zk/CHANGELOG.md +++ b/packages/zk/CHANGELOG.md @@ -1,5 +1,13 @@ ## Breaking changes policy +## 3.8.0 + +### Patch Changes + +- v1.0 contract milestone — aligned with `@caatinga/core@3.8.0`. +- Updated dependencies + - @caatinga/core@3.8.0 + ## 3.7.0 ### Patch Changes diff --git a/packages/zk/package.json b/packages/zk/package.json index d618902..ae8c6b7 100644 --- a/packages/zk/package.json +++ b/packages/zk/package.json @@ -1,6 +1,6 @@ { "name": "@caatinga/zk", - "version": "3.7.0", + "version": "3.8.0", "description": "Zero-knowledge proof serialization bridge for Stellar/Soroban", "type": "module", "engines": { @@ -32,7 +32,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@caatinga/core": "^3.7.0" + "@caatinga/core": "^3.8.0" }, "devDependencies": { "tsup": "^8.3.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dae4a54..a8beadc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,10 +99,10 @@ importers: packages/cli: dependencies: '@caatinga/core': - specifier: ^3.7.0 + specifier: ^3.8.0 version: link:../core '@caatinga/zk': - specifier: ^3.7.0 + specifier: ^3.8.0 version: link:../zk chalk: specifier: ^5.4.1 @@ -136,7 +136,7 @@ importers: packages/client: dependencies: '@caatinga/core': - specifier: ^3.7.0 + specifier: ^3.8.0 version: link:../core '@stellar/freighter-api': specifier: ^4.0.0 @@ -204,7 +204,7 @@ importers: packages/zk: dependencies: '@caatinga/core': - specifier: ^3.7.0 + specifier: ^3.8.0 version: link:../core devDependencies: tsup: From 2c81761b57068d32b745638b1e388e0dc561a75c Mon Sep 17 00:00:00 2001 From: Dione-b Date: Mon, 6 Jul 2026 13:50:51 -0300 Subject: [PATCH 3/3] chore: fix pre-publish gates (lint, format, docs check) Replace explicit any in logger tests with MockInstance, run Prettier across docs and tests, and wrap README line for docs:check compliance. Co-authored-by: Cursor --- README.md | 5 +- ROADMAP.md | 55 +++++++++++++++++- docs/architecture-freeze.md | 4 ++ docs/architecture.md | 14 +++-- docs/artifacts-versioning.md | 2 + docs/automation.md | 56 +++++++++---------- docs/cli-ux-audit-v1.md | 54 +++++++++--------- docs/conceptual-naming.md | 30 +++++----- docs/deploy-upgrade-spec.md | 8 ++- docs/dogfood-backlog.md | 6 +- docs/for-llms.md | 20 +++---- docs/internal/release/v1.0.0-evidence.md | 14 ++--- docs/lifecycle-hooks-spec.md | 10 +++- docs/network-setup.md | 8 +++ docs/packages.md | 5 +- docs/public-api.md | 46 +++++++-------- docs/recovery-scenarios.md | 48 ++++++++-------- docs/runtime-invoke-pipeline.md | 19 ++++--- docs/zk-test-protocol.md | 18 +++--- docs/zk-test-results.md | 16 +++--- examples/dogfood-multi/README.md | 8 +-- packages/cli/src/program.test.ts | 4 +- packages/cli/src/utils/logger.test.ts | 12 ++-- packages/core/src/artifacts/metadata.ts | 4 +- packages/core/src/browser.ts | 6 +- packages/core/src/compat/compat.test.ts | 4 +- .../src/contracts/placeholder-engine.test.ts | 12 +--- .../core/src/networks/resolve-network.test.ts | 10 +++- .../src/public-api/export-manifest.test.ts | 5 +- 29 files changed, 299 insertions(+), 204 deletions(-) diff --git a/README.md b/README.md index 14749a4..02d0da7 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,9 @@ Deployment Orchestration + Versioned Artifacts for Soroban. - **Mission:** Simplify the development, deployment, and integration of Soroban contracts for TypeScript teams through robust local orchestration and deterministic artifact versioning. - **Problem it Solves:** Fragmented deployment scripts and the difficulty of tracking and integrating contract IDs deployed across multiple environments (local, testnet, mainnet) into the frontend in a Git-friendly, deterministic way. -- **Key Differentiator:** Graph-aware local deployment orchestration with portable, Git-versioned artifact tracking (`caatinga.artifacts.json`), eliminating mandatory on-chain registry dependencies for basic development while providing auto-generated type-safe client bindings and direct browser wallet integration. +- **Key Differentiator:** Graph-aware local deployment orchestration with portable, Git-versioned artifact tracking + (`caatinga.artifacts.json`), eliminating mandatory on-chain registry dependencies for basic development while providing + auto-generated type-safe client bindings and direct browser wallet integration. ## Core Pillars @@ -38,7 +40,6 @@ Every feature in Caatinga belongs to one of these four core pillars: - **Regression & Smoke Checks:** Validate deployments with structured post-deploy checks (`postDeployRead`, `caatinga smoke`). - **Stable Logs:** Key automated pipelines on stable `CAATINGA_*` error codes rather than volatile stdout text. - ## Table of Contents - [Core Pillars](#core-pillars) diff --git a/ROADMAP.md b/ROADMAP.md index ca5189f..4b6e594 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -3,6 +3,7 @@ Caatinga is undergoing a stabilization and consolidation phase. No sprint should add new major features. The focus is on reducing scope, consolidating concepts, and stabilizing the platform. All sprints must strengthen one of the four core pillars: + 1. **Identity** (what Caatinga is) 2. **Architecture** (how the project is organized) 3. **DX** (how the developer interacts) @@ -11,9 +12,11 @@ All sprints must strengthen one of the four core pillars: --- ## Fase 1 — Redefinir a identidade do projeto -*Objetivo: Fazer qualquer pessoa entender o Caatinga em menos de 30 segundos.* + +_Objetivo: Fazer qualquer pessoa entender o Caatinga em menos de 30 segundos._ ### Sprint 1 — Definir o Core [x] + - **Objetivo:** Encontrar uma definição única para o projeto. - **Entregáveis:** - Definir a missão do projeto. @@ -23,6 +26,7 @@ All sprints must strengthen one of the four core pillars: - **Resultado esperado:** Toda documentação passa a girar em torno de: `Deployment Orchestration + Versioned Artifacts for Soroban`. Nada além disso. ### Sprint 2 — Definir os pilares [x] + - **Objetivo:** Organizar todas as funcionalidades existentes. - **Entregáveis:** - Criar os pilares oficiais: Deployment, Artifacts, Runtime, Automation. @@ -30,6 +34,7 @@ All sprints must strengthen one of the four core pillars: - **Resultado esperado:** Não existirão funcionalidades "soltas". ### Sprint 3 — Revisão do escopo [x] + - **Objetivo:** Eliminar responsabilidades que não pertencem ao core. - **Entregáveis:** - Classificar cada feature como: Core, Nice to Have, Experimental, Fora do escopo. @@ -40,18 +45,21 @@ All sprints must strengthen one of the four core pillars: ## Fase 2 — Arquitetura do produto ### Sprint 4 — Revisão dos pacotes [x] + - **Objetivo:** Reorganizar o workspace. - **Entregáveis:** - Revisar responsabilidades de: `cli`, `core`, `client`, `templates`, `zk`. - **Resultado esperado:** Nenhum pacote possuir responsabilidades cruzadas. ### Sprint 5 — Renomeação conceitual [x] + - **Objetivo:** Eliminar nomes genéricos. - **Entregáveis:** - Avaliar principalmente: `client`, `core`, `templates`, `runtime`. - **Resultado esperado:** Os nomes passam a refletir responsabilidades. ### Sprint 6 — Dependency Map [x] + - **Objetivo:** Documentar quem depende de quem. - **Entregáveis:** - Criar um diagrama simples de dependências. @@ -61,21 +69,25 @@ All sprints must strengthen one of the four core pillars: --- ## Fase 3 — Artifacts First -*Prioridade máxima do projeto.* + +_Prioridade máxima do projeto._ ### Sprint 7 — Especificação dos Artifacts [x] + - **Objetivo:** Transformar os artifacts em API pública. - **Entregáveis:** - Definir: `schema`, `version`, `metadata`, `contracts`, `history`. - **Resultado esperado:** Formato congelado. ### Sprint 8 — Evolução do schema [x] + - **Objetivo:** Adicionar metadados importantes. - **Entregáveis:** - Adicionar campos como: `git commit`, `compiler`, `rust version`, `cli version`, `network`, `timestamp`, `checksum`. - **Resultado esperado:** Artifacts completos. ### Sprint 9 — Versionamento [x] + - **Objetivo:** Definir estratégia de evolução do formato. - **Entregáveis:** - Versionamento do schema. @@ -88,12 +100,14 @@ All sprints must strengthen one of the four core pillars: ## Fase 4 — Deployment Engine ### Sprint 10 — Especificação de Deploys e Upgrades [x] + - **Objetivo:** Definir as regras e fluxos operacionais de deploy, upgrade e rollback. - **Entregáveis:** - Modelar oficialmente: regras conceituais de `deploy`, `upgrade`, `redeploy`, `rollback` e `force`. - **Resultado esperado:** Comportamento conceitual congelado. ### Sprint 11 — Architecture Freeze [x] + - **Objetivo:** Revisar e declarar congelamento dos contratos públicos antes de avançar para a implementação das fases centrais. - **Entregáveis:** - Congelar o formato de `caatinga.artifacts.json`. @@ -105,12 +119,14 @@ All sprints must strengthen one of the four core pillars: - **Resultado esperado:** Contratos públicos estabilizados. Mudanças futuras serão tratadas como decisões arquiteturais conscientes. ### Sprint 12 — Resolução de placeholders [x] + - **Objetivo:** Formalizar resolução de referências. - **Entregáveis:** - Resolução de `${contracts.token.contractId}`. - **Resultado esperado:** Motor independente. ### Sprint 13 — Hooks [x] + - **Objetivo:** Revisar wire. - **Entregáveis:** - Padronizar hooks. @@ -122,18 +138,21 @@ All sprints must strengthen one of the four core pillars: ## Fase 5 — CLI ### Sprint 14 — Revisão da UX [x] + - **Objetivo:** Organizar comandos. - **Entregáveis:** - Agrupar comandos em domínios. - **Resultado esperado:** CLI intuitiva. ### Sprint 15 — Mensagens [x] + - **Objetivo:** Padronizar saída. - **Entregáveis:** - Padronizar mensagens de: Erros, Warnings, Success, Logs. - **Resultado esperado:** Experiência consistente. ### Sprint 16 — CAATINGA Codes [x] + - **Objetivo:** Padronizar todos os códigos de erro. - **Resultado esperado:** Todos erros documentados. @@ -142,14 +161,17 @@ All sprints must strengthen one of the four core pillars: ## Fase 6 — Stellar CLI Adapter ### Sprint 17 — Adapter [x] + - **Objetivo:** Criar camada única de integração. - **Resultado esperado:** Core nunca conhece stdout. ### Sprint 18 — Parser [x] + - **Objetivo:** Normalizar toda saída. - **Resultado esperado:** API interna consistente. ### Sprint 19 — Testes do Adapter [x] + - **Objetivo:** Criar testes usando outputs reais. - **Resultado esperado:** Mudanças futuras na Stellar CLI são detectadas rapidamente. @@ -158,14 +180,17 @@ All sprints must strengthen one of the four core pillars: ## Fase 7 — Runtime ### Sprint 20 — Runtime API [x] + - **Objetivo:** Definir responsabilidades. - **Resultado esperado:** API mínima. ### Sprint 21 — Wallet Layer [x] + - **Objetivo:** Padronizar `WalletAdapter`. - **Resultado esperado:** Abstração simples. ### Sprint 22 — Invoke Pipeline [x] + - **Objetivo:** Documentar o pipeline de invocação. - **Entregáveis:** - Mapear e documentar: `simulate`, `sign`, `submit`, `watch`. @@ -176,14 +201,17 @@ All sprints must strengthen one of the four core pillars: ## Fase 8 — Templates ### Sprint 23 — Engine [x] + - **Objetivo:** Separar templates da CLI. - **Resultado esperado:** Templates independentes. ### Sprint 24 — Manifest [x] + - **Objetivo:** Formalizar manifesto. - **Resultado esperado:** Templates versionáveis. ### Sprint 25 — Templates Oficiais [x] + - **Objetivo:** Revisar templates e manter apenas os essenciais. - **Resultado esperado:** Poucos templates de alta qualidade. @@ -192,14 +220,17 @@ All sprints must strengthen one of the four core pillars: ## Fase 9 — Automation ### Sprint 26 — Doctor [x] + - **Objetivo:** Revisar verificações. - **Resultado esperado:** Diagnóstico confiável. ### Sprint 27 — Smoke [x] + - **Objetivo:** Padronizar validações. - **Resultado esperado:** Fluxo reproduzível. ### Sprint 28 — CI [x] + - **Objetivo:** Consolidar pipeline. - **Resultado esperado:** Execução previsível. @@ -208,14 +239,17 @@ All sprints must strengthen one of the four core pillars: ## Fase 10 — Documentação ### Sprint 29 — README [x] + - **Objetivo:** Reposicionar o projeto focado no problema. - **Resultado esperado:** README focado. ### Sprint 30 — Quick Start [x] + - **Objetivo:** Novo usuário em menos de cinco minutos. - **Resultado esperado:** Menor tempo até o primeiro deploy. ### Sprint 31 — Conceitos [x] + - **Objetivo:** Criar documentação conceitual. - **Entregáveis:** - Documentar: Deployment Graph, Artifacts, Runtime, Automation. @@ -226,12 +260,15 @@ All sprints must strengthen one of the four core pillars: ## Fase 11 — Estabilização ### Sprint 32 — Revisão de APIs [x] + - **Objetivo:** Congelar interfaces públicas. ### Sprint 33 — Breaking Changes [x] + - **Objetivo:** Eliminar inconsistências. ### Sprint 34 — Polimento [x] + - **Objetivo:** Melhorar nomenclaturas, mensagens, ajuda da CLI e documentação. --- @@ -239,18 +276,21 @@ All sprints must strengthen one of the four core pillars: ## Fase 12 — Beta ### Sprint 35 — Produção [x] + - **Entregáveis:** - Adicionar `inspect`. ✓ - Adicionar `rollback`. ✓ - Adicionar `estimate`. ✓ ### Sprint 36 — Qualidade [x] + - **Entregáveis:** - Cobertura de testes. ✓ - Exemplos. ✓ - Benchmarks. ### Sprint 37 — Release Candidate [x] + - **Entregáveis:** - Checklist para v1. ✓ (ver `docs/release-candidate-checklist.md`) - Congelamento do schema dos artifacts. ✓ @@ -262,6 +302,7 @@ All sprints must strengthen one of the four core pillars: ## Fase 13 — Hardening da Plataforma ### Sprint 38 — Auditoria Final da API Pública [x] + - **Objetivo:** Revisar tudo que ficará público na v1.0. - **Entregáveis:** - Manifesto [`docs/public-api.md`](docs/public-api.md) com tiers Supported/Advanced/Internal. ✓ @@ -269,12 +310,14 @@ All sprints must strengthen one of the four core pillars: - ROADMAP Fases 13–16. ✓ ### Sprint 39 — Testes de Compatibilidade [x] + - **Objetivo:** Garantir que atualizações futuras não quebrem projetos antigos. - **Entregáveis:** - Fixtures artifacts v1/v2 + `pnpm test:compat`. ✓ - Snapshot de package exports. ✓ ### Sprint 40 — Recovery e Casos de Erro [x] + - **Objetivo:** Tornar falhas previsíveis. - **Entregáveis:** - [`docs/recovery-scenarios.md`](docs/recovery-scenarios.md). ✓ @@ -286,12 +329,14 @@ All sprints must strengthen one of the four core pillars: ## Fase 14 — Dogfooding ### Sprint 41 — Construção de um dApp Real [x] + - **Objetivo:** Projeto completo usando apenas APIs públicas. - **Entregáveis:** - [`examples/dogfood-simple`](examples/dogfood-simple). ✓ - [`docs/dogfood-backlog.md`](docs/dogfood-backlog.md). ✓ ### Sprint 42 — Segundo Projeto (multi-contrato) [x] + - **Objetivo:** Validar graph, placeholders, upgrades. - **Entregáveis:** - [`examples/dogfood-multi`](examples/dogfood-multi). ✓ @@ -302,10 +347,12 @@ All sprints must strengthen one of the four core pillars: ## Fase 15 — DX ### Sprint 43 — README + Quick Start [x] + - **Objetivo:** Primeiro deploy em menos de 10 minutos (toolchain instalada). - **Entregáveis:** README com caminhos "toolchain pronta" e "máquina limpa". ✓ ### Sprint 44 — Troubleshooting [x] + - **Entregáveis:** [`docs/troubleshooting.md`](docs/troubleshooting.md). ✓ --- @@ -313,14 +360,18 @@ All sprints must strengthen one of the four core pillars: ## Fase 16 — Release Candidate ### Sprint 45 — UX Review da CLI [x] + - **Entregáveis:** [`docs/cli-ux-audit-v1.md`](docs/cli-ux-audit-v1.md). ✓ ### Sprint 46 — Zero Knowledge Test [x] + - **Entregáveis:** [`docs/zk-test-protocol.md`](docs/zk-test-protocol.md), [`docs/zk-test-results.md`](docs/zk-test-results.md). ✓ ### Sprint 47 — Release Candidate [x] + - **Entregáveis:** Checklist RC atualizado; pacotes `3.8.0`; CHANGELOG. ✓ ### Sprint 48 — Release v1.0 [x] + - **Objetivo:** Lançamento oficial (contrato v1.0, npm major `3.x`, dist-tag `latest`). - **Entregáveis:** Tag git `v1.0.0`; README v1.0 stable contract. ✓ diff --git a/docs/architecture-freeze.md b/docs/architecture-freeze.md index 5c5a48a..05d8c8f 100644 --- a/docs/architecture-freeze.md +++ b/docs/architecture-freeze.md @@ -9,19 +9,23 @@ This document formally declares the **Architecture Freeze** for Caatinga v1.0. A The following architectural designs are frozen and cannot be modified: ### 1. Core Identity + - **Definition:** Caatinga is defined exclusively as **Deployment Orchestration + Versioned Artifacts for Soroban**. - **No Scope Bloat:** No on-chain registries, rust macro decorators, or custom test runners will be introduced. ### 2. Package Boundaries + - **Orchestration Engine (`@caatinga/core`):** The only package permitted to execute subprocesses (via `execa` in `run-command.ts`) and interact with Node.js filesystem APIs. - **Integration SDK (`@caatinga/client`):** Consumes exclusively browser-safe subpaths (`@caatinga/core/browser`), containing only types and error definitions. No Node.js runtime code may bleed into this package. ### 3. Artifact Schema (`caatinga.artifacts.json`) + - **Version:** Fixed at version `2`. - **Evolutions:** Only backward-compatible optional fields are permitted. Breaking changes requiring v3 are blocked until v2.0. - **Guard:** The loader actively rejects files with `version > 2` to prevent older CLIs from corrupting registries. ### 4. Lifecycle Operations + - **Operations:** The behaviors of `deploy`, `upgrade` (in-place), `redeploy` (`--force`), and `rollback` (local registry shift) are locked. --- diff --git a/docs/architecture.md b/docs/architecture.md index 31ffcf6..63af7c7 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -53,20 +53,24 @@ For a detailed breakdown of all features categorized into Core, Nice to Have, Ex Architecturally, Caatinga is structured around four pillars that compartmentalize responsibilities and prevent cross-boundary pollution: ### 1. Deployment (Orchestration Engine) + This pillar encompasses the build and deployment pipeline. It manages contract compilation (via Stellar CLI shell orchestration), multi-contract dependency topological sorting (`dependsOn`), placeholder resolution (e.g. `${contracts.token.contractId}`), and executing post-deploy hooks (`postDeploy`). -*Components responsible:* `@caatinga/core` (specifically `deploy-graph`, `load-config`), `@caatinga/cli`. +_Components responsible:_ `@caatinga/core` (specifically `deploy-graph`, `load-config`), `@caatinga/cli`. ### 2. Artifacts (State Contract) + This pillar represents the static state representation of all deployments. The per-network `caatinga.artifacts.json` file serves as the Git-versioned contract between compilation, deployment, and client integration, capturing contract IDs, compiler hashes, and metadata. -*Components responsible:* `@caatinga/core` (schema validations, state read/write). +_Components responsible:_ `@caatinga/core` (schema validations, state read/write). ### 3. Runtime (Client Integration Layer) + This pillar exposes the APIs consumed by browser/Node applications. It includes the TypeScript clients, pluggable wallet adapters, React context providers/hooks (`@caatinga/client/react`), and transaction pipeline orchestration (simulate → sign → submit → watch). -*Components responsible:* `@caatinga/client`. +_Components responsible:_ `@caatinga/client`. ### 4. Automation (Developer Diagnostics & Safety) + This pillar ensures local workspace reliability, setup automation, regression checking (`caatinga smoke` / `postDeployRead`), and CI/CD-friendly error APIs using stable error codes. -*Components responsible:* `@caatinga/cli` (commands `doctor`, `smoke`, `setup`), `@caatinga/core` (stable errors module). +_Components responsible:_ `@caatinga/cli` (commands `doctor`, `smoke`, `setup`), `@caatinga/core` (stable errors module). ## Validation roadmap (flows) @@ -126,7 +130,6 @@ Each box is either a file you commit, a CLI command you run, or a runtime compon For detailed package dependency boundaries and compliance rules, see [Package Boundaries & Isolation Rules](./packages.md#package-boundaries-isolation-rules). - Deferred unless explicitly rescoped: CLI XDR commands, `caatinga generate --interop`, full plugin system, RWA-only templates, visual dashboard, custom test runner as **required** core dependencies. ### Dependency Map @@ -174,7 +177,6 @@ Notes encoded in the diagram: - **Node vs Browser Boundaries:** The Orchestration Engine is the only package that orchestrates subprocesses via CLI Adapters executing Stellar CLI commands. The Integration SDK (`@caatinga/client`) consumes only the browser-safe subpath `@caatinga/core/browser`, ensuring that Node-specific dependencies like `execa` or `fs` are never pulled into web applications. - **State Registry:** The Artifacts State (`caatinga.artifacts.json`) acts as the shared database between the Orchestration Engine (which writes it on deploy) and the Transaction Pipeline / Integration SDK (which reads it at runtime). - ## Meta-framework boundary: orchestrate workflow, not mental model **May abstract:** build/deploy/bindings flow, artifact lookup, network config from the project, template layout, command composition, stable CLI commands, wallet adapter handoff, and generated-binding transaction workflow. diff --git a/docs/artifacts-versioning.md b/docs/artifacts-versioning.md index 6660a77..68054ef 100644 --- a/docs/artifacts-versioning.md +++ b/docs/artifacts-versioning.md @@ -19,10 +19,12 @@ We enforce a strict versioning convention for the `caatinga.artifacts.json` form To avoid lock-in and support teams upgrading their CLI, Caatinga implements the following rules: ### Backward Compatibility (Old files in New CLI) + - **Automatic Migration:** When reading old artifacts files (e.g. version 1), the Orchestration Engine automatically migrates the shape to the current version in memory using `migrateArtifactsToV2`. - **Automatic Writeback:** Running `caatinga deploy` or `caatinga upgrade` on an old schema version automatically writes the updated, migrated format to disk, upgrading the project's artifacts file. ### Forward Compatibility (New files in Old CLI) + - **Safety Fail-Fast:** If the Orchestration Engine detects an artifacts file version greater than `CURRENT_ARTIFACTS_SCHEMA_VERSION` (e.g., `parsedJson.version > 2`), it immediately halts execution with a `CAATINGA_ARTIFACT_INVALID` error. - **Why this matters:** Preventing older CLIs from parsing newer schemas protects users from corrupted metadata state or loss of registry history. diff --git a/docs/automation.md b/docs/automation.md index 2325613..486462e 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -8,25 +8,25 @@ This document describes the three automation commands that form the Caatinga CI The `doctor` command runs a comprehensive set of local diagnostics against the current project. It checks: -| Check | Description | -|---|---| -| Stellar CLI version | Verifies that the installed Stellar CLI meets the minimum version | -| Deploy coverage | Reports which contracts are deployed on the target network | -| Binding freshness | Verifies that generated bindings match the current WASM hashes | -| Env sync | Checks whether `CAATINGA_*` env vars in `.env` match the artifacts | -| Post-deploy hooks | Validates that `expect` assertions in hooks would pass | -| WASM drift | Detects contracts whose on-chain WASM differs from the local build | +| Check | Description | +| ------------------- | ------------------------------------------------------------------ | +| Stellar CLI version | Verifies that the installed Stellar CLI meets the minimum version | +| Deploy coverage | Reports which contracts are deployed on the target network | +| Binding freshness | Verifies that generated bindings match the current WASM hashes | +| Env sync | Checks whether `CAATINGA_*` env vars in `.env` match the artifacts | +| Post-deploy hooks | Validates that `expect` assertions in hooks would pass | +| WASM drift | Detects contracts whose on-chain WASM differs from the local build | ### Flags -| Flag | Default | Description | -|---|---|---| -| `-n, --network` | `defaultNetwork` | Target network | -| `-s, --source` | `CAATINGA_SOURCE` or `alice` | Stellar CLI identity | -| `--all-networks` | `false` | Run checks across all configured networks | -| `--strict-env` | `false` | Fail when env vars are missing or stale | -| `--strict-bindings` | `false` | Fail when bindings are stale | -| `--strict` | `false` | Enable all strict checks | +| Flag | Default | Description | +| ------------------- | ---------------------------- | ----------------------------------------- | +| `-n, --network` | `defaultNetwork` | Target network | +| `-s, --source` | `CAATINGA_SOURCE` or `alice` | Stellar CLI identity | +| `--all-networks` | `false` | Run checks across all configured networks | +| `--strict-env` | `false` | Fail when env vars are missing or stale | +| `--strict-bindings` | `false` | Fail when bindings are stale | +| `--strict` | `false` | Enable all strict checks | ### Exit Codes @@ -44,19 +44,17 @@ The `smoke` command runs the read-only smoke checks defined under each contract' ```ts contracts: { counter: { - smokeReads: [ - { method: "get", expect: { gte: 0 } } - ] + smokeReads: [{ method: "get", expect: { gte: 0 } }]; } } ``` ### Flags -| Flag | Default | Description | -|---|---|---| -| `-n, --network` | `defaultNetwork` | Target network | -| `-s, --source` | `CAATINGA_SOURCE` or `alice` | Identity for simulation context | +| Flag | Default | Description | +| --------------- | ---------------------------- | ------------------------------- | +| `-n, --network` | `defaultNetwork` | Target network | +| `-s, --source` | `CAATINGA_SOURCE` or `alice` | Identity for simulation context | ### Exit Codes @@ -81,12 +79,12 @@ ci run ### Flags -| Flag | Default | Description | -|---|---|---| -| `-n, --network` | `defaultNetwork` | Target network | -| `-s, --source` | – | Identity alias | -| `--skip-smoke` | `false` | Skip smoke reads | -| `--strict` | `false` | Pass `--strict` to doctor | +| Flag | Default | Description | +| --------------- | ---------------- | ------------------------- | +| `-n, --network` | `defaultNetwork` | Target network | +| `-s, --source` | – | Identity alias | +| `--skip-smoke` | `false` | Skip smoke reads | +| `--strict` | `false` | Pass `--strict` to doctor | ### GitHub Actions Example diff --git a/docs/cli-ux-audit-v1.md b/docs/cli-ux-audit-v1.md index 6a7179b..a69fcb5 100644 --- a/docs/cli-ux-audit-v1.md +++ b/docs/cli-ux-audit-v1.md @@ -11,36 +11,36 @@ Final review checklist before v1.0 contract freeze. Compare live `--help` output ## Flag consistency -| Flag | Commands using it | Consistent? | -|------|-------------------|-------------| -| `--network` | deploy, upgrade, generate, invoke, read, doctor, smoke, … | Yes | -| `--source` | deploy, upgrade, invoke, wire, setup | Yes | -| `--force` | deploy, upgrade | Yes | -| `-v, --version` | global | Yes | +| Flag | Commands using it | Consistent? | +| --------------- | --------------------------------------------------------- | ----------- | +| `--network` | deploy, upgrade, generate, invoke, read, doctor, smoke, … | Yes | +| `--source` | deploy, upgrade, invoke, wire, setup | Yes | +| `--force` | deploy, upgrade | Yes | +| `-v, --version` | global | Yes | ## Command review -| Command | Help clear | Examples in docs | Notes | -|---------|------------|------------------|-------| -| init | ✓ | ✓ | | -| setup | ✓ | ✓ | | -| build | ✓ | ✓ | | -| deploy | ✓ | ✓ | Transient retry messages documented | -| upgrade | ✓ | ✓ | | -| rollback | ✓ | ✓ | | -| generate | ✓ | ✓ | | -| invoke | ✓ | ✓ | | -| read | ✓ | ✓ | | -| doctor | ✓ | ✓ | | -| migrate | ✓ | ✓ | | -| version | ✓ | ✓ | Added Sprint 38 | -| smoke | ✓ | ✓ | | -| wire | ✓ | ✓ | | -| sync-env | ✓ | ✓ | | -| estimate | ✓ | ✓ | | -| inspect | ✓ | ✓ | | -| status | ✓ | ✓ | | -| ci / regression | ✓ | ✓ | CI-oriented | +| Command | Help clear | Examples in docs | Notes | +| --------------- | ---------- | ---------------- | ----------------------------------- | +| init | ✓ | ✓ | | +| setup | ✓ | ✓ | | +| build | ✓ | ✓ | | +| deploy | ✓ | ✓ | Transient retry messages documented | +| upgrade | ✓ | ✓ | | +| rollback | ✓ | ✓ | | +| generate | ✓ | ✓ | | +| invoke | ✓ | ✓ | | +| read | ✓ | ✓ | | +| doctor | ✓ | ✓ | | +| migrate | ✓ | ✓ | | +| version | ✓ | ✓ | Added Sprint 38 | +| smoke | ✓ | ✓ | | +| wire | ✓ | ✓ | | +| sync-env | ✓ | ✓ | | +| estimate | ✓ | ✓ | | +| inspect | ✓ | ✓ | | +| status | ✓ | ✓ | | +| ci / regression | ✓ | ✓ | CI-oriented | ## Gaps found diff --git a/docs/conceptual-naming.md b/docs/conceptual-naming.md index 1f6d331..34e1d74 100644 --- a/docs/conceptual-naming.md +++ b/docs/conceptual-naming.md @@ -1,6 +1,6 @@ # Conceptual Naming Policy -This document defines the official conceptual terminology for Caatinga to resolve generic names and clearly communicate package and module responsibilities. +This document defines the official conceptual terminology for Caatinga to resolve generic names and clearly communicate package and module responsibilities. While package names on npm (`@caatinga/core`, `@caatinga/client`) remain unchanged to avoid breaking existing consumer integrations, all architecture diagrams, code comments, and documentation guides must adhere to this conceptual naming vocabulary. @@ -8,29 +8,29 @@ While package names on npm (`@caatinga/core`, `@caatinga/client`) remain unchang ## Terminology Mapping -| Package/Module Name | Generic Term | Conceptual Term | Responsibility Description | -| :--- | :--- | :--- | :--- | -| `@caatinga/core` | `core` | **Orchestration Engine** | The engine that compiles contracts, topological-sorts the dependency graph, resolves configuration variables, and executes deploy lifecycle hooks. | -| `@caatinga/client` | `client` | **Integration SDK** | The frontend/consumer library that provides wallet session bindings, wraps type-safe generated contract clients, and interacts with browser wallet adapters. | -| `packages/templates`| `templates` | **Project Scaffolds** | Pre-configured starter application templates (minimal, react-vite, zk) used to initialize projects. | -| `@caatinga/client` (internal) | `runtime` | **Transaction Pipeline** | The sequential execution pipeline: simulating transactions, signing via client-side wallets, submitting to the Horizon/Stellar network, and watching confirmation status. | +| Package/Module Name | Generic Term | Conceptual Term | Responsibility Description | +| :---------------------------- | :----------- | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `@caatinga/core` | `core` | **Orchestration Engine** | The engine that compiles contracts, topological-sorts the dependency graph, resolves configuration variables, and executes deploy lifecycle hooks. | +| `@caatinga/client` | `client` | **Integration SDK** | The frontend/consumer library that provides wallet session bindings, wraps type-safe generated contract clients, and interacts with browser wallet adapters. | +| `packages/templates` | `templates` | **Project Scaffolds** | Pre-configured starter application templates (minimal, react-vite, zk) used to initialize projects. | +| `@caatinga/client` (internal) | `runtime` | **Transaction Pipeline** | The sequential execution pipeline: simulating transactions, signing via client-side wallets, submitting to the Horizon/Stellar network, and watching confirmation status. | --- ## Vocabulary Guidelines 1. **When talking about CLI or compile/deploy steps:** - - *Avoid:* "Run the core to deploy", "Core builds the contracts". - - *Prefer:* "The **Orchestration Engine** executes the build", "The deployment graph is handled by the **Orchestration Engine**". + - _Avoid:_ "Run the core to deploy", "Core builds the contracts". + - _Prefer:_ "The **Orchestration Engine** executes the build", "The deployment graph is handled by the **Orchestration Engine**". 2. **When talking about browser-side contract execution:** - - *Avoid:* "Import the client", "Register a client adapter". - - *Prefer:* "Initialize the **Integration SDK** client", "Utilize the **Integration SDK** wallet adapters". + - _Avoid:_ "Import the client", "Register a client adapter". + - _Prefer:_ "Initialize the **Integration SDK** client", "Utilize the **Integration SDK** wallet adapters". 3. **When talking about the transaction lifecycle:** - - *Avoid:* "The client runtime handles the signature", "Runtime client calls simulate". - - *Prefer:* "The **Transaction Pipeline** simulates and signs the transaction". + - _Avoid:_ "The client runtime handles the signature", "Runtime client calls simulate". + - _Prefer:_ "The **Transaction Pipeline** simulates and signs the transaction". 4. **When talking about templates:** - - *Avoid:* "Scaffold using templates", "Available templates in packages". - - *Prefer:* "Scaffold using the official **Project Scaffolds**". + - _Avoid:_ "Scaffold using templates", "Available templates in packages". + - _Prefer:_ "Scaffold using the official **Project Scaffolds**". diff --git a/docs/deploy-upgrade-spec.md b/docs/deploy-upgrade-spec.md index d718de3..73487a2 100644 --- a/docs/deploy-upgrade-spec.md +++ b/docs/deploy-upgrade-spec.md @@ -7,6 +7,7 @@ This document details the specifications, behaviors, and transition rules for co ## 1. Core Operations ### Deploy + - **Definition:** The initial upload and instantiation of a Soroban smart contract. - **Behavior:** - Compiles the WASM binary (if necessary). @@ -16,6 +17,7 @@ This document details the specifications, behaviors, and transition rules for co - **Triggers:** `caatinga deploy ` (when no prior deployment exists on the target network). ### Upgrade (In-place) + - **Definition:** The update of a contract's backing WebAssembly byte-code on-chain without altering its address (`contractId`). - **Behavior:** - Uploads the new WASM binary to the network to obtain a new `wasmHash`. @@ -25,6 +27,7 @@ This document details the specifications, behaviors, and transition rules for co - **Triggers:** `caatinga upgrade --method ` ### Redeploy + - **Definition:** Deploying a brand new instance of a previously deployed contract, generating a new `contractId`. - **Behavior:** - Instantiates a fresh copy of the contract on the network. @@ -33,12 +36,13 @@ This document details the specifications, behaviors, and transition rules for co - **Triggers:** `caatinga deploy --force` (or `--upgrade` to mark as upgrade type). ### Rollback + - **Definition:** Reverting the active contract registration in the artifacts file to a prior deployment state saved in the history. - **Behavior:** - Searches the `history` array of the target contract for a matching `contractId`. - Restores the matching contract state (contract ID, WASM hash, metadata) to the active contract entry. - Appends the superseded active instance to the `history` with reason `"rollback"`. - - *Note:* Rollback updates the local artifacts state registry. On-chain state restoration (e.g., re-running an on-chain upgrade to the old WASM hash) is an application concern. + - _Note:_ Rollback updates the local artifacts state registry. On-chain state restoration (e.g., re-running an on-chain upgrade to the old WASM hash) is an application concern. - **Triggers:** `caatinga rollback --target ` --- @@ -46,12 +50,14 @@ This document details the specifications, behaviors, and transition rules for co ## 2. Operation Flags & Mutators ### Force (`--force`) + - **Purpose:** Bypasses state check optimizations. - **Behavior:** - By default, Caatinga skips deployment or builds if the local WASM hash matches the registry (`ifChanged` strategy). - Activating `--force` overrides this check, forcing a fresh compile, upload, and deployment transaction, pushing the current registry state to the history. ### If Changed (`--if-changed`) + - **Purpose:** Optimizes CI/CD pipelines and local DX by avoiding redundant deploy transactions. - **Behavior:** - Compares the SHA-256 hash of the compiled WASM binary with the `wasmHash` stored in the current network scope of `caatinga.artifacts.json`. diff --git a/docs/dogfood-backlog.md b/docs/dogfood-backlog.md index 82ed20f..9110d63 100644 --- a/docs/dogfood-backlog.md +++ b/docs/dogfood-backlog.md @@ -2,9 +2,9 @@ Issues found during dogfooding with **public APIs only**. Core changes are deferred until after v1.0 RC unless marked critical. -| ID | Sprint | Severity | Description | Workaround | Core change? | -|----|--------|----------|-------------|------------|--------------| -| DF-001 | 41 | — | (placeholder) | — | No | +| ID | Sprint | Severity | Description | Workaround | Core change? | +| ------ | ------ | -------- | ------------- | ---------- | ------------ | +| DF-001 | 41 | — | (placeholder) | — | No | ## Rules diff --git a/docs/for-llms.md b/docs/for-llms.md index 0aa4719..c22d5ef 100644 --- a/docs/for-llms.md +++ b/docs/for-llms.md @@ -190,7 +190,7 @@ Shared ZK flags: `--allow-dev-ceremony` (bypass mainnet guardrails), `--embed-vk | `{ matcher: "maxLength", value: 10 }` | bounded lists | Parsed JSON array length ≤ value | | `{ matcher: "contains", value: "abc" }` | substring | stdout includes value | | `{ matcher: "matches", value: "^C[A-Z0-9]+$" }` | regex | stdout matches pattern | -| `{ matcher: "jsonEquals", value: "[1,2]" }` | deep JSON | Parsed JSON deep-equals value | +| `{ matcher: "jsonEquals", value: "[1,2]" }` | deep JSON | Parsed JSON deep-equals value | --- @@ -614,15 +614,15 @@ Optional [stellar-build](https://github.com/kaankacar/stellar-build) agents driv ### Working on the **Caatinga monorepo** -| Doc | Use when | -| ------------------------------------- | ----------------------------------------------------------------------- | -| [AGENTS.md](../AGENTS.md) | Repo layout, build/test commands, version alignment, template overrides | -| [CONTRIBUTING.md](../CONTRIBUTING.md) | PR expectations, commit style, compatibility contracts | -| [Architecture](./architecture.md) | Product stance, Caatinga vs Scaffold Stellar | -| [Errors](./errors.md) | Full `CAATINGA_*` catalog with fixes | -| [CLI](./cli.md) | Authoritative command reference | -| [Config](./config.md) | `caatinga.config.ts` schema details | -| [Contract upgrade](./tutorials/contract-upgrade.md) | In-place vs redeploy upgrade strategies (3.7.0) | +| Doc | Use when | +| --------------------------------------------------- | ----------------------------------------------------------------------- | +| [AGENTS.md](../AGENTS.md) | Repo layout, build/test commands, version alignment, template overrides | +| [CONTRIBUTING.md](../CONTRIBUTING.md) | PR expectations, commit style, compatibility contracts | +| [Architecture](./architecture.md) | Product stance, Caatinga vs Scaffold Stellar | +| [Errors](./errors.md) | Full `CAATINGA_*` catalog with fixes | +| [CLI](./cli.md) | Authoritative command reference | +| [Config](./config.md) | `caatinga.config.ts` schema details | +| [Contract upgrade](./tutorials/contract-upgrade.md) | In-place vs redeploy upgrade strategies (3.7.0) | Monorepo dev: `pnpm install --frozen-lockfile`, `pnpm build`, `pnpm test`, `pnpm dev `. diff --git a/docs/internal/release/v1.0.0-evidence.md b/docs/internal/release/v1.0.0-evidence.md index 9a27d38..1d0fde7 100644 --- a/docs/internal/release/v1.0.0-evidence.md +++ b/docs/internal/release/v1.0.0-evidence.md @@ -4,13 +4,13 @@ Git tag `v1.0.0` marks the **stable contract milestone**. npm packages ship as ` ## Verification (2026-07-06) -| Gate | Command | Result | -|------|---------|--------| -| Unit tests | `pnpm test` | pass | -| Compatibility | `pnpm test:compat` | pass | -| Typecheck | `pnpm typecheck` | pass | -| Public API manifest | `docs/public-api.md` | published | -| RC checklist | `docs/release-candidate-checklist.md` | complete (smoke CI operational gate ongoing) | +| Gate | Command | Result | +| ------------------- | ------------------------------------- | -------------------------------------------- | +| Unit tests | `pnpm test` | pass | +| Compatibility | `pnpm test:compat` | pass | +| Typecheck | `pnpm typecheck` | pass | +| Public API manifest | `docs/public-api.md` | published | +| RC checklist | `docs/release-candidate-checklist.md` | complete (smoke CI operational gate ongoing) | ## Publish diff --git a/docs/lifecycle-hooks-spec.md b/docs/lifecycle-hooks-spec.md index 872507b..ea872b3 100644 --- a/docs/lifecycle-hooks-spec.md +++ b/docs/lifecycle-hooks-spec.md @@ -17,21 +17,26 @@ graph TD ``` ### Phase 1: Config & Validation + - **Actions:** Loads `caatinga.config.ts`, resolves the target network connection parameters, and validates the multi-contract dependency graph (detecting cycles or missing dependency declarations). ### Phase 2: Build Workspace (Optional) + - **Actions:** Compiles all target contract WASM binaries using `cargo build --target wasm32-unknown-unknown`. This phase can be skipped by passing `--no-build`. ### Phase 3: Topological Deploy + - **Actions:** Deploys contracts in topological dependency order (non-linear). - Evaluates `--if-changed` cache: skips deploy if WASM hash matches `caatinga.artifacts.json`. - Performs network deploy and updates contract addresses. - Dynamically resolves placeholders (`${contracts..contractId}`) for downstream contracts. ### Phase 4: Bindings Generation (Optional) + - **Actions:** Generates TypeScript clients and wallet bindings using the current `caatinga.artifacts.json` as the input. Writes freshness markers (`.caatinga-bindings.json`) to the frontend folders. ### Phase 5: Wire Hooks Execution + - **Actions:** Executes sequential post-deploy hooks (`postDeploy` and `postDeployRead`) to wire contract dependencies, initialize storage, or verify state. --- @@ -50,17 +55,20 @@ Hooks are declared globally in `caatinga.config.ts` and run during the `caatinga - **Behavior:** Queries the ledger state without submitting transactions, returning the serialized simulation response. ### Arguments & Placeholder Resolution + All arguments passed to hooks (`args`) are evaluated by the **Placeholder Engine**. Hooks can accept: + - Static primitives (`string`, `number`, `boolean`). - Dynamic contract address lookups: `${contracts..contractId}`. - Active deployer address: `${source.address}`. ### Assertion Engine (`expect`) + To ensure the pipeline succeeded, hooks can define an `expect` assertion to validate the method output: - **Simple Assertion:** ```ts - expect: "expected_return_value" + expect: "expected_return_value"; ``` - **Type-Safe Assertion:** ```ts diff --git a/docs/network-setup.md b/docs/network-setup.md index 6e67368..dc802ea 100644 --- a/docs/network-setup.md +++ b/docs/network-setup.md @@ -23,7 +23,9 @@ export type NetworkConfig = { ## 2. Standard Stellar/Soroban Network Boilerplates ### Testnet (SDF Public Testnet) + Use this for public staging, testing integrations, and deploying release candidates. + - **Passphrase:** `Test SDF Network ; September 2015` - **friendbotUrl:** Available (allows funding accounts with 10,000 test XLM). @@ -38,7 +40,9 @@ networks: { ``` ### Mainnet (Stellar Production Network) + Use this only for production releases. + - **Passphrase:** `Public Global Stellar Network ; October 2015` - **friendbotUrl:** None (requires real assets). @@ -52,7 +56,9 @@ networks: { ``` ### Futurenet (SDF Experimental Futurenet) + Use this for testing bleeding-edge Protocol features. + - **Passphrase:** `Test SDF Future Network ; October 2022` - **friendbotUrl:** Available. @@ -67,7 +73,9 @@ networks: { ``` ### Local/Standalone (Docker) + Use this for rapid offline development. + - **Setup Command:** Run a local Stellar Quickstart Docker container. - **Passphrase:** `Standalone Network ; Simple comparison` diff --git a/docs/packages.md b/docs/packages.md index 09af2a3..64ec200 100644 --- a/docs/packages.md +++ b/docs/packages.md @@ -62,19 +62,22 @@ Browser-safe types and errors: import from `@caatinga/core/browser` only. To maintain high stability and prevent architectural regression, the following package boundaries must be strictly enforced: ### 1. CLI Isolation Rule + - `@caatinga/cli` consumes `@caatinga/core` directly for orchestrating commands. - **Rule:** Under no circumstances should `@caatinga/core`, `@caatinga/client`, or `@caatinga/zk` import or depend on `@caatinga/cli`. The core logic must never reference CLI argument parsing, console output formats, or terminal-specific APIs. ### 2. Client Browser-Safety Rule + - `@caatinga/client` connects smart contract bindings, local artifacts, and browser wallets. It is designed to be bundled safely by bundlers like Vite, Webpack, and Turbopack. - **Rule:** `@caatinga/client` must never import from the root of `@caatinga/core` (which contains Node-specific dependencies like `execa` or `fs`). It can only import browser-safe symbols from the `@caatinga/core/browser` subpath. - **Rule:** `@caatinga/client` must not contain direct filesystem access or child process orchestration. ### 3. Template Independence Rule + - `packages/templates` contains Vite and React project scaffolds. - **Rule:** Templates are target output configurations. They must not contain compiler logic or depend on the monorepo core tools except as consumer dependencies (e.g. `@caatinga/client`, `@caatinga/cli`). ### 4. ZK Isolation Rule + - `@caatinga/zk` encapsulates the Circom and Groth16 cryptographic operations. - **Rule:** This module operates independently and does not depend on `@caatinga/cli` or `@caatinga/client` code directly. - diff --git a/docs/public-api.md b/docs/public-api.md index 7bd17cb..cdfb4cf 100644 --- a/docs/public-api.md +++ b/docs/public-api.md @@ -14,14 +14,14 @@ Automation and application code may depend on these surfaces without importing p Supported flow: `init → build → deploy → generate → invoke` -| Domain | Commands | -|--------|----------| -| Scaffolding & Setup | `init`, `setup`, `identity` | -| Build | `build` | -| Deployment & Lifecycle | `deploy`, `upgrade`, `rollback`, `wire` | -| Query & Execution | `read`, `invoke`, `estimate`, `dev` | -| Status & Diagnostics | `status`, `inspect`, `doctor`, `sync-env`, `migrate`, `version` | -| Automation & CI | `smoke`, `regression`, `ci` | +| Domain | Commands | +| ---------------------- | --------------------------------------------------------------- | +| Scaffolding & Setup | `init`, `setup`, `identity` | +| Build | `build` | +| Deployment & Lifecycle | `deploy`, `upgrade`, `rollback`, `wire` | +| Query & Execution | `read`, `invoke`, `estimate`, `dev` | +| Status & Diagnostics | `status`, `inspect`, `doctor`, `sync-env`, `migrate`, `version` | +| Automation & CI | `smoke`, `regression`, `ci` | All commands, flags, exit codes (`0` / `1`), and `CAATINGA_*` error codes are documented in [cli.md](./cli.md) and [errors.md](./errors.md). @@ -74,15 +74,15 @@ All `CAATINGA_*` codes in [errors.md](./errors.md). Enforced by `error-surface.t Exported from `@caatinga/core` for power users and CI. **Additive changes are minor; removals or semantic changes are breaking.** -| Area | Symbols | -|------|---------| -| Artifacts | `writeArtifacts`, `createInitialArtifacts`, `updateArtifact`, `restoreArtifactFromHistory`, `collectDeploymentMetadata`, `migrateArtifactsToV2`, `migrateArtifactsFile`, `rollbackContractArtifact`, `CURRENT_ARTIFACTS_SCHEMA_VERSION`, `collectProjectStatus` | -| Deployment | `deployContract`, `deployContractGraph`, `upgradeContractInPlace`, `uploadWasm`, `buildContract`, `buildWorkspace`, `resolveDeployArgs`, `resolveDeployOrder`, `buildDependencyGraph`, `runPostDeployHooks` | -| Bindings | `generateBindings`, `generateBindingsGraph`, `evaluateBindingFreshness`, `evaluateBindingsFreshness`, `readBindingMarker`, `writeBindingMarker` | -| Invoke / read | `invokeContract`, `readContract`, `estimateDeployCost`, `inspectContract`, `verifyExpect`, `runSmokeReads` | -| Networks | `resolveNetwork`, `WELL_KNOWN_NETWORKS` | -| Config load | `loadConfig`, `CaatingaConfigSchema` | -| Templates | `createProjectFromTemplate`, `TemplateManifestSchema` | +| Area | Symbols | +| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Artifacts | `writeArtifacts`, `createInitialArtifacts`, `updateArtifact`, `restoreArtifactFromHistory`, `collectDeploymentMetadata`, `migrateArtifactsToV2`, `migrateArtifactsFile`, `rollbackContractArtifact`, `CURRENT_ARTIFACTS_SCHEMA_VERSION`, `collectProjectStatus` | +| Deployment | `deployContract`, `deployContractGraph`, `upgradeContractInPlace`, `uploadWasm`, `buildContract`, `buildWorkspace`, `resolveDeployArgs`, `resolveDeployOrder`, `buildDependencyGraph`, `runPostDeployHooks` | +| Bindings | `generateBindings`, `generateBindingsGraph`, `evaluateBindingFreshness`, `evaluateBindingsFreshness`, `readBindingMarker`, `writeBindingMarker` | +| Invoke / read | `invokeContract`, `readContract`, `estimateDeployCost`, `inspectContract`, `verifyExpect`, `runSmokeReads` | +| Networks | `resolveNetwork`, `WELL_KNOWN_NETWORKS` | +| Config load | `loadConfig`, `CaatingaConfigSchema` | +| Templates | `createProjectFromTemplate`, `TemplateManifestSchema` | --- @@ -90,13 +90,13 @@ Exported from `@caatinga/core` for power users and CI. **Additive changes are mi Present in `@caatinga/core` for CLI and monorepo use. **Do not depend on these in application code.** -| Area | Examples | -|------|----------| -| Shell | `runCommand`, `checkBinary`, `resolveSubprocessEnv`, `isCargoBinMissingFromPath` | +| Area | Examples | +| -------------------- | ----------------------------------------------------------------------------------------------- | +| Shell | `runCommand`, `checkBinary`, `resolveSubprocessEnv`, `isCargoBinMissingFromPath` | | Stellar CLI adapters | `parseContractId`, `parseWasmHash`, `checkStellarCliVersion`, `evaluateStellarCliCompatibility` | -| Scaffolds | `createMinimalProject`, `createZkProject` | -| CI helpers | `isTransientTestnetSmokeFailure` | -| Source validation | `validateSourceShape`, `describeCliSource` | +| Scaffolds | `createMinimalProject`, `createZkProject` | +| CI helpers | `isTransientTestnetSmokeFailure` | +| Source validation | `validateSourceShape`, `describeCliSource` | --- diff --git a/docs/recovery-scenarios.md b/docs/recovery-scenarios.md index 1b903ed..6141a8f 100644 --- a/docs/recovery-scenarios.md +++ b/docs/recovery-scenarios.md @@ -6,11 +6,11 @@ Actionable recovery paths for common failure modes. For the full error reference ## Interrupted deploy -| Symptom | What happened | Recovery | -|---------|---------------|----------| -| Deploy stopped mid-graph | Earlier contracts in `dependsOn` order may already be in `caatinga.artifacts.json` | Re-run `caatinga deploy --network --source ` — already-deployed contracts are skipped unless `--force` | -| CLI killed during artifact write | Atomic write (`write temp → rename`) prevents truncated JSON | If file is corrupt, restore from Git or run `caatinga migrate artifacts` after fixing JSON | -| Transient testnet error | Retry logs appear: `Deploy hit a transient testnet error` | Wait for automatic retries or re-run deploy | +| Symptom | What happened | Recovery | +| -------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | +| Deploy stopped mid-graph | Earlier contracts in `dependsOn` order may already be in `caatinga.artifacts.json` | Re-run `caatinga deploy --network --source ` — already-deployed contracts are skipped unless `--force` | +| CLI killed during artifact write | Atomic write (`write temp → rename`) prevents truncated JSON | If file is corrupt, restore from Git or run `caatinga migrate artifacts` after fixing JSON | +| Transient testnet error | Retry logs appear: `Deploy hit a transient testnet error` | Wait for automatic retries or re-run deploy | **Doctor:** `caatinga doctor --network testnet` lists partial deploy coverage (`CAATINGA_DOCTOR_PARTIAL_DEPLOY` advisory). @@ -18,11 +18,11 @@ Actionable recovery paths for common failure modes. For the full error reference ## Invalid artifacts -| Code | Recovery command | -|------|------------------| -| `CAATINGA_ARTIFACT_NOT_FOUND` | `caatinga init` or copy `caatinga.artifacts.json` from a teammate | -| `CAATINGA_ARTIFACT_INVALID` | Fix JSON manually, or delete and redeploy: `caatinga deploy --network --source ` | -| Unsupported schema version | Upgrade CLI: `npm install -g @caatinga/cli@latest` | +| Code | Recovery command | +| ----------------------------- | ---------------------------------------------------------------------------------------------------- | +| `CAATINGA_ARTIFACT_NOT_FOUND` | `caatinga init` or copy `caatinga.artifacts.json` from a teammate | +| `CAATINGA_ARTIFACT_INVALID` | Fix JSON manually, or delete and redeploy: `caatinga deploy --network --source ` | +| Unsupported schema version | Upgrade CLI: `npm install -g @caatinga/cli@latest` | **Migration:** `caatinga migrate artifacts` upgrades schema v1 → v2 on disk. @@ -30,21 +30,21 @@ Actionable recovery paths for common failure modes. For the full error reference ## RPC offline -| Symptom | Code | Recovery | -|---------|------|----------| +| Symptom | Code | Recovery | +| ---------------------------------------- | ----------------------------- | --------------------------------------------------------------- | | Browser invoke fails at simulate/prepare | `CAATINGA_XDR_PREPARE_FAILED` | Verify `rpcUrl` in config and `.env`; test with `curl ` | -| Submit rejected | `CAATINGA_XDR_SUBMIT_FAILED` | Check network passphrase matches wallet network | -| CLI read/invoke fails | `CAATINGA_INVOKE_FAILED` | Confirm Soroban RPC endpoint is reachable | +| Submit rejected | `CAATINGA_XDR_SUBMIT_FAILED` | Check network passphrase matches wallet network | +| CLI read/invoke fails | `CAATINGA_INVOKE_FAILED` | Confirm Soroban RPC endpoint is reachable | --- ## Stellar CLI absent or wrong version -| Code | Recovery | -|------|----------| -| `CAATINGA_STELLAR_CLI_NOT_FOUND` | `caatinga setup` or [Stellar setup guide](https://developers.stellar.org/docs/build/smart-contracts/getting-started/setup) | -| `CAATINGA_UNSUPPORTED_CLI_VERSION` | Install Stellar CLI ≥ 23.0.0 (27.0.0 recommended) | -| `CAATINGA_RUST_TARGET_NOT_FOUND` | `rustup target add wasm32v1-none` | +| Code | Recovery | +| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | +| `CAATINGA_STELLAR_CLI_NOT_FOUND` | `caatinga setup` or [Stellar setup guide](https://developers.stellar.org/docs/build/smart-contracts/getting-started/setup) | +| `CAATINGA_UNSUPPORTED_CLI_VERSION` | Install Stellar CLI ≥ 23.0.0 (27.0.0 recommended) | +| `CAATINGA_RUST_TARGET_NOT_FOUND` | `rustup target add wasm32v1-none` | **Preflight:** `caatinga doctor` @@ -52,11 +52,11 @@ Actionable recovery paths for common failure modes. For the full error reference ## Outdated bindings -| Code | Recovery | -|------|----------| -| `CAATINGA_PLACEHOLDER_BINDING` | `caatinga generate --network ` then restart dev server | -| `CAATINGA_BINDING_CLIENT_NOT_FOUND` | Same as above | -| `CAATINGA_BINDING_METHOD_NOT_FOUND` | Regenerate bindings after contract interface change | +| Code | Recovery | +| ----------------------------------- | -------------------------------------------------------------------------- | +| `CAATINGA_PLACEHOLDER_BINDING` | `caatinga generate --network ` then restart dev server | +| `CAATINGA_BINDING_CLIENT_NOT_FOUND` | Same as above | +| `CAATINGA_BINDING_METHOD_NOT_FOUND` | Regenerate bindings after contract interface change | **Doctor** reports binding freshness per contract. diff --git a/docs/runtime-invoke-pipeline.md b/docs/runtime-invoke-pipeline.md index cabd0eb..b006cc2 100644 --- a/docs/runtime-invoke-pipeline.md +++ b/docs/runtime-invoke-pipeline.md @@ -10,9 +10,9 @@ The Caatinga Runtime lives in `@caatinga/client` and is responsible for the enti ### Packages -| Package | Responsibility | -|---|---| -| `@caatinga/core` | Server/Node orchestration: build, deploy, upgrade, artifacts, bindings | +| Package | Responsibility | +| ------------------ | ---------------------------------------------------------------------- | +| `@caatinga/core` | Server/Node orchestration: build, deploy, upgrade, artifacts, bindings | | `@caatinga/client` | Browser/Node runtime: wallet integration, signing, contract invocation | ### Minimal API @@ -43,6 +43,7 @@ interface CaatingaWalletAdapter { ``` **Contract rules:** + - `getPublicKey()` must resolve to a valid Ed25519 public key (G-prefixed Stellar address). - `signTransaction()` must resolve to a Base64-encoded signed XDR string. - Both methods **must reject** (not leave the promise pending) when the user cancels or when the wallet is not connected. @@ -88,10 +89,10 @@ built → prepared → signed → submitted → confirmed ### Error Codes -| Situation | CaatingaErrorCode | -|---|---| +| Situation | CaatingaErrorCode | +| -------------------------------------- | ---------------------- | | Wallet not connected / key unavailable | `WALLET_NOT_CONNECTED` | -| User dismissed signing | `XDR_SIGN_FAILED` | -| Empty or invalid signed XDR | `XDR_SIGN_FAILED` | -| Simulation failure | `XDR_PREPARE_FAILED` | -| Submission/network failure | `XDR_SUBMIT_FAILED` | +| User dismissed signing | `XDR_SIGN_FAILED` | +| Empty or invalid signed XDR | `XDR_SIGN_FAILED` | +| Simulation failure | `XDR_PREPARE_FAILED` | +| Submission/network failure | `XDR_SUBMIT_FAILED` | diff --git a/docs/zk-test-protocol.md b/docs/zk-test-protocol.md index 81b4154..54a75d6 100644 --- a/docs/zk-test-protocol.md +++ b/docs/zk-test-protocol.md @@ -8,18 +8,18 @@ Run **3–5 independent agent sessions** with: - No access to the Caatinga monorepo - Only: GitHub README (or docs site), `npm install @caatinga/cli` -- Prompt: *"Install Caatinga and complete your first testnet deploy. Do not ask for help."* +- Prompt: _"Install Caatinga and complete your first testnet deploy. Do not ask for help."_ ## Record per session -| Field | Example | -|-------|---------| -| Agent ID | agent-1 | -| Start time | 2026-07-06T14:00Z | -| Stuck at step | `caatinga deploy` — missing `--source` | -| First error code | `CAATINGA_SOURCE_ACCOUNT_REQUIRED` | -| Time to first deploy | 42 min | -| Questions asked | "What is alice?" | +| Field | Example | +| -------------------- | -------------------------------------- | +| Agent ID | agent-1 | +| Start time | 2026-07-06T14:00Z | +| Stuck at step | `caatinga deploy` — missing `--source` | +| First error code | `CAATINGA_SOURCE_ACCOUNT_REQUIRED` | +| Time to first deploy | 42 min | +| Questions asked | "What is alice?" | ## Consolidation diff --git a/docs/zk-test-results.md b/docs/zk-test-results.md index 3945a1e..a24a29b 100644 --- a/docs/zk-test-results.md +++ b/docs/zk-test-results.md @@ -4,12 +4,12 @@ ## Summary -| Metric | Value | -|--------|-------| -| Sessions run | 0 | -| Median time to deploy | — | -| Most common blocker | — | -| DX fixes shipped | — | +| Metric | Value | +| --------------------- | ----- | +| Sessions run | 0 | +| Median time to deploy | — | +| Most common blocker | — | +| DX fixes shipped | — | ## Session log @@ -30,5 +30,5 @@ ## DX changes from this test | Issue | Fix | PR/commit | -|-------|-----|-----------| -| — | — | — | +| ----- | --- | --------- | +| — | — | — | diff --git a/examples/dogfood-multi/README.md b/examples/dogfood-multi/README.md index 6e6166d..652864c 100644 --- a/examples/dogfood-multi/README.md +++ b/examples/dogfood-multi/README.md @@ -4,10 +4,10 @@ Validates **Deployment Graph**, `dependsOn`, `${contracts.token.contractId}` pla ## Contracts -| Contract | Role | -|----------|------| -| `token` | Standalone; exposes `supply` / `mint` | -| `vault` | Depends on `token`; constructor receives token `Address` via placeholder | +| Contract | Role | +| -------- | ------------------------------------------------------------------------ | +| `token` | Standalone; exposes `supply` / `mint` | +| `vault` | Depends on `token`; constructor receives token `Address` via placeholder | ## Deploy graph diff --git a/packages/cli/src/program.test.ts b/packages/cli/src/program.test.ts index 469f853..e556b0c 100644 --- a/packages/cli/src/program.test.ts +++ b/packages/cli/src/program.test.ts @@ -117,7 +117,9 @@ describe("createProgram", () => { try { await createProgram().exitOverride().parseAsync(["node", "caatinga", "version"]); - expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(`@caatinga/cli: ${packageJson.version}`)); + expect(logSpy).toHaveBeenCalledWith( + expect.stringContaining(`@caatinga/cli: ${packageJson.version}`) + ); } finally { logSpy.mockRestore(); } diff --git a/packages/cli/src/utils/logger.test.ts b/packages/cli/src/utils/logger.test.ts index ccdcef5..6ab34eb 100644 --- a/packages/cli/src/utils/logger.test.ts +++ b/packages/cli/src/utils/logger.test.ts @@ -1,11 +1,11 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { describe, it, expect, vi, beforeEach, afterEach, type MockInstance } from "vitest"; import { logger } from "./logger.js"; import chalk from "chalk"; describe("logger", () => { - let logSpy: any; - let warnSpy: any; - let errorSpy: any; + let logSpy: MockInstance; + let warnSpy: MockInstance; + let errorSpy: MockInstance; beforeEach(() => { logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); @@ -24,7 +24,9 @@ describe("logger", () => { it("should log success message with green check icon and text", () => { logger.success("operation successful"); - expect(logSpy).toHaveBeenCalledWith(`${chalk.green("✔")} ${chalk.green("operation successful")}`); + expect(logSpy).toHaveBeenCalledWith( + `${chalk.green("✔")} ${chalk.green("operation successful")}` + ); }); it("should log warning message with yellow warning icon and text", () => { diff --git a/packages/core/src/artifacts/metadata.ts b/packages/core/src/artifacts/metadata.ts index 82d4ec9..068bf43 100644 --- a/packages/core/src/artifacts/metadata.ts +++ b/packages/core/src/artifacts/metadata.ts @@ -8,7 +8,9 @@ export type CollectMetadataInput = { cwd?: string; }; -export async function collectDeploymentMetadata(input: CollectMetadataInput): Promise { +export async function collectDeploymentMetadata( + input: CollectMetadataInput +): Promise { let gitCommit: string | undefined; try { const gitResult = await runCommand("git", ["rev-parse", "HEAD"], { diff --git a/packages/core/src/browser.ts b/packages/core/src/browser.ts index c943821..c33bc62 100644 --- a/packages/core/src/browser.ts +++ b/packages/core/src/browser.ts @@ -1,4 +1,8 @@ export { CaatingaError, CaatingaErrorCode, toCaatingaError } from "./errors/CaatingaError.js"; export { formatCaatingaError } from "./errors/format-caatinga-error.js"; -export type { CaatingaArtifacts, ContractArtifact, ContractMetadata } from "./artifacts/artifact.schema.js"; +export type { + CaatingaArtifacts, + ContractArtifact, + ContractMetadata, +} from "./artifacts/artifact.schema.js"; export { assertSorobanSymbol } from "./soroban/assert-soroban-symbol.js"; diff --git a/packages/core/src/compat/compat.test.ts b/packages/core/src/compat/compat.test.ts index f3c6f92..d8b56cd 100644 --- a/packages/core/src/compat/compat.test.ts +++ b/packages/core/src/compat/compat.test.ts @@ -78,9 +78,7 @@ describe("compatibility: artifacts schema", () => { expect(result.migrated).toBe(true); expect(result.artifacts.version).toBe(2); - const onDisk = JSON.parse( - await readFile(path.join(tmpDir, "caatinga.artifacts.json"), "utf8") - ); + const onDisk = JSON.parse(await readFile(path.join(tmpDir, "caatinga.artifacts.json"), "utf8")); expect(onDisk.version).toBe(2); }); }); diff --git a/packages/core/src/contracts/placeholder-engine.test.ts b/packages/core/src/contracts/placeholder-engine.test.ts index 34698ba..a29ccd6 100644 --- a/packages/core/src/contracts/placeholder-engine.test.ts +++ b/packages/core/src/contracts/placeholder-engine.test.ts @@ -47,9 +47,7 @@ describe("resolvePlaceholders", () => { }); it("should throw CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND when contract artifact is missing", () => { - expect(() => - resolvePlaceholders("${contracts.missing.contractId}", context) - ).toThrowError( + expect(() => resolvePlaceholders("${contracts.missing.contractId}", context)).toThrowError( expect.objectContaining({ code: CaatingaErrorCode.CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND, }) @@ -58,9 +56,7 @@ describe("resolvePlaceholders", () => { it("should throw SOURCE_ADDRESS_UNRESOLVED when sourceAddress is missing", () => { const noSourceContext = { ...context, sourceAddress: undefined }; - expect(() => - resolvePlaceholders("${source.address}", noSourceContext) - ).toThrowError( + expect(() => resolvePlaceholders("${source.address}", noSourceContext)).toThrowError( expect.objectContaining({ code: CaatingaErrorCode.SOURCE_ADDRESS_UNRESOLVED, }) @@ -68,9 +64,7 @@ describe("resolvePlaceholders", () => { }); it("should throw DEPLOY_ARG_PLACEHOLDER_INVALID for malformed or unsupported placeholders", () => { - expect(() => - resolvePlaceholders("Some ${invalid.placeholder} here", context) - ).toThrowError( + expect(() => resolvePlaceholders("Some ${invalid.placeholder} here", context)).toThrowError( expect.objectContaining({ code: CaatingaErrorCode.DEPLOY_ARG_PLACEHOLDER_INVALID, }) diff --git a/packages/core/src/networks/resolve-network.test.ts b/packages/core/src/networks/resolve-network.test.ts index 3db26b6..8888285 100644 --- a/packages/core/src/networks/resolve-network.test.ts +++ b/packages/core/src/networks/resolve-network.test.ts @@ -48,7 +48,10 @@ describe("resolveNetwork", () => { }); it("should_include_testnet_boilerplate_in_hint_when_testnet_is_missing", () => { - const configWithoutTestnet = { ...baseConfig, networks: { mainnet: baseConfig.networks.mainnet } }; + const configWithoutTestnet = { + ...baseConfig, + networks: { mainnet: baseConfig.networks.mainnet }, + }; try { resolveNetwork(configWithoutTestnet, "testnet"); expect.fail("expected throw"); @@ -61,7 +64,10 @@ describe("resolveNetwork", () => { }); it("should_include_mainnet_boilerplate_in_hint_when_mainnet_is_missing", () => { - const configWithoutMainnet = { ...baseConfig, networks: { testnet: baseConfig.networks.testnet } }; + const configWithoutMainnet = { + ...baseConfig, + networks: { testnet: baseConfig.networks.testnet }, + }; try { resolveNetwork(configWithoutMainnet, "mainnet"); expect.fail("expected throw"); diff --git a/packages/core/src/public-api/export-manifest.test.ts b/packages/core/src/public-api/export-manifest.test.ts index 8d29c26..070ee69 100644 --- a/packages/core/src/public-api/export-manifest.test.ts +++ b/packages/core/src/public-api/export-manifest.test.ts @@ -25,7 +25,10 @@ function parseNamedExports(source: string): string[] { if (!trimmed || trimmed.startsWith("type ")) { continue; } - const exportName = trimmed.split(/\s+as\s+/).pop()?.trim(); + const exportName = trimmed + .split(/\s+as\s+/) + .pop() + ?.trim(); if (exportName) { names.add(exportName); }