@desplega.ai/agent-swarm 1.85.0 → 1.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +1 -0
  2. package/openapi.json +72 -1
  3. package/package.json +10 -6
  4. package/src/be/db-queries/tracker.ts +21 -0
  5. package/src/be/db.ts +279 -14
  6. package/src/be/migrations/078_backfill_gpt_5_5_pricing.sql +15 -0
  7. package/src/be/migrations/079_task_followup_config.sql +1 -0
  8. package/src/be/modelsdev-cache.json +155618 -0
  9. package/src/be/modelsdev-cache.ts +46 -0
  10. package/src/be/seed-pricing.ts +7 -44
  11. package/src/cli.tsx +38 -2
  12. package/src/commands/codex-session-runner.ts +132 -0
  13. package/src/commands/context-preamble.ts +272 -0
  14. package/src/commands/credential-wait.ts +2 -2
  15. package/src/commands/e2b.ts +728 -0
  16. package/src/commands/provider-credentials.ts +10 -5
  17. package/src/commands/resume-session.ts +35 -78
  18. package/src/commands/runner.ts +128 -16
  19. package/src/e2b/dispatch.ts +429 -0
  20. package/src/e2b/env.ts +206 -0
  21. package/src/heartbeat/heartbeat.ts +145 -30
  22. package/src/heartbeat/templates.ts +11 -7
  23. package/src/http/session-data.ts +8 -1
  24. package/src/http/tasks.ts +152 -3
  25. package/src/jira/sync.ts +4 -4
  26. package/src/linear/sync.ts +6 -5
  27. package/src/prompts/base-prompt.ts +49 -3
  28. package/src/providers/claude-adapter.ts +76 -61
  29. package/src/providers/claude-managed-adapter.ts +61 -75
  30. package/src/providers/claude-managed-models.ts +18 -2
  31. package/src/providers/codex-adapter.ts +429 -112
  32. package/src/providers/codex-models.ts +9 -2
  33. package/src/providers/codex-oauth/auth-json.ts +18 -1
  34. package/src/providers/codex-oauth/flow.ts +24 -1
  35. package/src/providers/index.ts +28 -19
  36. package/src/providers/pricing-sources.md +7 -4
  37. package/src/providers/swarm-events-shared.ts +14 -0
  38. package/src/providers/types.ts +6 -0
  39. package/src/slack/HEURISTICS.md +5 -1
  40. package/src/slack/handlers.test.ts +35 -0
  41. package/src/slack/handlers.ts +79 -2
  42. package/src/tasks/worker-follow-up.ts +162 -2
  43. package/src/telemetry.ts +11 -1
  44. package/src/tests/base-prompt.test.ts +46 -8
  45. package/src/tests/claude-adapter.test.ts +5 -27
  46. package/src/tests/claude-managed-adapter.test.ts +42 -56
  47. package/src/tests/codex-adapter-otel.test.ts +4 -4
  48. package/src/tests/codex-adapter.test.ts +25 -37
  49. package/src/tests/codex-oauth.test.ts +149 -3
  50. package/src/tests/codex-pool.test.ts +14 -3
  51. package/src/tests/codex-swarm-events.test.ts +35 -0
  52. package/src/tests/context-window.test.ts +1 -0
  53. package/src/tests/credential-check.test.ts +48 -29
  54. package/src/tests/e2b-dispatch.test.ts +330 -0
  55. package/src/tests/entrypoint-config-env-export.test.ts +81 -0
  56. package/src/tests/follow-up-redelivery-guard.test.ts +165 -0
  57. package/src/tests/heartbeat-supersede-resume.test.ts +285 -0
  58. package/src/tests/heartbeat.test.ts +26 -16
  59. package/src/tests/migration-046-budgets.test.ts +6 -5
  60. package/src/tests/pricing-routes.test.ts +6 -5
  61. package/src/tests/prompt-template-remaining.test.ts +4 -0
  62. package/src/tests/provider-adapter.test.ts +10 -10
  63. package/src/tests/provider-command-format.test.ts +4 -4
  64. package/src/tests/resume-session.test.ts +42 -50
  65. package/src/tests/session-costs-codex-recompute.test.ts +25 -0
  66. package/src/tests/structured-output.test.ts +69 -0
  67. package/src/tests/task-completion-idempotency.test.ts +185 -2
  68. package/src/tests/task-supersede-resume.test.ts +722 -0
  69. package/src/tests/telemetry-init.test.ts +69 -0
  70. package/src/tests/vcs-tracking.test.ts +39 -0
  71. package/src/tools/send-task.ts +42 -10
  72. package/src/tools/store-progress.ts +2 -2
  73. package/src/tools/templates.ts +14 -2
  74. package/src/types.ts +46 -1
  75. package/src/utils/context-window.ts +1 -0
  76. package/src/workflows/executors/agent-task.ts +3 -0
  77. package/templates/schedules/daily-blocker-digest/config.json +13 -0
  78. package/templates/schedules/daily-blocker-digest/content.md +150 -0
  79. package/templates/schedules/daily-compounding-reflection/config.json +21 -0
  80. package/templates/schedules/daily-compounding-reflection/content.md +210 -0
  81. package/templates/schedules/daily-hn-briefing/config.json +13 -0
  82. package/templates/schedules/daily-hn-briefing/content.md +97 -0
  83. package/templates/schedules/daily-workflow-health-audit/config.json +13 -0
  84. package/templates/schedules/daily-workflow-health-audit/content.md +189 -0
  85. package/templates/schedules/gtm-weekly-review/config.json +13 -0
  86. package/templates/schedules/gtm-weekly-review/content.md +58 -0
  87. package/templates/schedules/weekly-dependabot-triage/config.json +13 -0
  88. package/templates/schedules/weekly-dependabot-triage/content.md +45 -0
  89. package/templates/schema.ts +26 -0
  90. package/templates/skills/agentmail-sending/config.json +13 -0
  91. package/templates/skills/agentmail-sending/content.md +48 -0
  92. package/templates/skills/artifacts/config.json +13 -0
  93. package/templates/skills/artifacts/content.md +87 -0
  94. package/templates/skills/browser-use-cloud/config.json +13 -0
  95. package/templates/skills/browser-use-cloud/content.md +155 -0
  96. package/templates/skills/desloppify/config.json +13 -0
  97. package/templates/skills/desloppify/content.md +201 -0
  98. package/templates/skills/exa-search/config.json +13 -0
  99. package/templates/skills/exa-search/content.md +106 -0
  100. package/templates/skills/jira-interaction/config.json +13 -0
  101. package/templates/skills/jira-interaction/content.md +252 -0
  102. package/templates/skills/kapso-whatsapp/config.json +13 -0
  103. package/templates/skills/kapso-whatsapp/content.md +369 -0
  104. package/templates/skills/kv-storage/config.json +13 -0
  105. package/templates/skills/kv-storage/content.md +111 -0
  106. package/templates/skills/linear-interaction/config.json +20 -0
  107. package/templates/skills/linear-interaction/content.md +230 -0
  108. package/templates/skills/pages/config.json +18 -0
  109. package/templates/skills/pages/content.md +85 -0
  110. package/templates/skills/profile-corruption-escalation/config.json +13 -0
  111. package/templates/skills/profile-corruption-escalation/content.md +105 -0
  112. package/templates/skills/scheduled-task-resilience/config.json +13 -0
  113. package/templates/skills/scheduled-task-resilience/content.md +95 -0
  114. package/templates/skills/sprite-cli/config.json +13 -0
  115. package/templates/skills/sprite-cli/content.md +133 -0
  116. package/templates/skills/turso-interaction/config.json +13 -0
  117. package/templates/skills/turso-interaction/content.md +192 -0
  118. package/templates/skills/workflow-iterate/config.json +18 -0
  119. package/templates/skills/workflow-iterate/content.md +399 -0
  120. package/templates/skills/workflow-structured-output/config.json +13 -0
  121. package/templates/skills/workflow-structured-output/content.md +101 -0
  122. package/templates/skills/x-api-interactions/config.json +13 -0
  123. package/templates/skills/x-api-interactions/content.md +109 -0
  124. package/templates/workflows/autopilot/config.json +13 -0
  125. package/templates/workflows/autopilot/content.md +58 -0
  126. package/templates/workflows/linear-drain-loop/config.json +21 -0
  127. package/templates/workflows/linear-drain-loop/content.md +72 -0
  128. package/templates/workflows/ralph-loop/config.json +13 -0
  129. package/templates/workflows/ralph-loop/content.md +75 -0
@@ -0,0 +1,133 @@
1
+ # sprite-cli
2
+
3
+ `sprite` is a CLI for [sprites.dev](https://sprites.dev) — ephemeral Linux sandboxes backed by Fly.io firecracker microVMs. Use them when you need to run docker, docker-compose, postgres, redis, or anything else the host swarm container won't let you do.
4
+
5
+ ## When to use this
6
+
7
+ - You need to test a `docker run …` or `docker compose up …` flow.
8
+ - You need a real postgres / redis / rabbitmq for an integration test.
9
+ - You want to try an `apt-get install` of something you don't want polluting the swarm container.
10
+ - You need to fetch + execute untrusted code (sandboxed network egress is OK; egress to swarm internals is not).
11
+
12
+ If you can do it directly in the swarm container (lint, type-check, unit test, code search), don't reach for sprite — it's slower and uses paid resources.
13
+
14
+ ## Installation
15
+
16
+ The CLI is auto-installed at container start for agents whose setup script includes the install snippet (see "Setup script snippet" below).
17
+
18
+ Manual install:
19
+ ```bash
20
+ curl -fsSL https://sprites.dev/install.sh | sh
21
+ export PATH="$HOME/.local/bin:$PATH" # CLI lands in ~/.local/bin
22
+ ```
23
+
24
+ ## Authentication
25
+
26
+ The org token is stored in swarm config as `SPRITES_API_KEY` (global, secret). Setup scripts read `$SPRITES_API_KEY` from the env.
27
+
28
+ ```bash
29
+ sprite auth setup --token "$SPRITES_API_KEY"
30
+ ```
31
+
32
+ Token format is `<org>/<account_id>/<token_id>/<secret>`. The first segment is the org (`desplega`).
33
+
34
+ ## Core commands
35
+
36
+ | Command | What it does |
37
+ |---|---|
38
+ | `sprite create <name>` | Create a sprite (~1s). |
39
+ | `sprite list` | List your sprites. |
40
+ | `sprite exec -s <name> -- <cmd>` | Run a command in the sprite. |
41
+ | `sprite exec -s <name> -- bash -c "…"` | Run a multi-statement shell snippet. |
42
+ | `sprite console -s <name>` | Open an interactive shell. |
43
+ | `sprite destroy <name> --force` | Tear down. **Always do this when done.** |
44
+ | `sprite proxy <port>` | Forward a remote sprite port to your local machine (rarely useful from within a swarm container; prefer curl from inside the sprite). |
45
+
46
+ Flags: every command takes `-s <sprite>` (or sets a default via `sprite use`). `-o <org>` overrides the org if you have multiple.
47
+
48
+ ## Sandbox baseline (as of 2026-05-07)
49
+
50
+ - Ubuntu 25.10 (Questing Quokka), kernel 6.12.x-fly (firecracker microVM)
51
+ - Non-root `sprite` user (uid 1001), passwordless `sudo`
52
+ - No docker, no `/var/run/docker.sock`, no container runtime preinstalled
53
+ - No systemd / init — `service` and `systemctl` won't work; start daemons manually with `sudo <daemon> &` or `nohup`
54
+ - Kernel sysctls are mostly read-only (e.g. `vm.mmap_min_addr` writes fail — these warnings are harmless)
55
+
56
+ ## Recipe: docker inside a sprite
57
+
58
+ ```bash
59
+ sprite create dock
60
+ sprite exec -s dock -- bash -c '
61
+ set -e
62
+ sudo apt-get update -qq
63
+ sudo apt-get install -y docker.io docker-compose-v2
64
+ sudo dockerd > /tmp/dockerd.log 2>&1 &
65
+ # wait for daemon
66
+ for i in {1..15}; do sudo docker ps >/dev/null 2>&1 && break; sleep 1; done
67
+ sudo docker run --rm hello-world
68
+ '
69
+ ```
70
+
71
+ Notes:
72
+ - `invoke-rc.d` and `policy-rc.d denied` warnings during `apt-get install` are harmless — they come from missing systemd; the binaries install fine.
73
+ - The `sprite` user is **not** in the docker group by default, so prefix every docker call with `sudo`. (You can `sudo usermod -aG docker sprite` and start a new shell, but for one-off scripts `sudo` is simpler.)
74
+ - `dockerd` binds to `/var/run/docker.sock`. If you need it on a TCP port, pass `-H tcp://0.0.0.0:2375` (only inside the sprite — this is not exposed to the public internet).
75
+
76
+ ## Recipe: docker compose
77
+
78
+ ```bash
79
+ sprite exec -s dock -- bash -c '
80
+ cat > /tmp/compose.yml <<YAML
81
+ services:
82
+ pg:
83
+ image: postgres:16-alpine
84
+ environment:
85
+ POSTGRES_PASSWORD: dev
86
+ ports: ["5432:5432"]
87
+ YAML
88
+ sudo docker compose -f /tmp/compose.yml up -d
89
+ # wait for healthy
90
+ for i in {1..30}; do sudo docker exec $(sudo docker compose -f /tmp/compose.yml ps -q pg) pg_isready -U postgres >/dev/null 2>&1 && break; sleep 1; done
91
+ sudo docker exec $(sudo docker compose -f /tmp/compose.yml ps -q pg) psql -U postgres -c "select version();"
92
+ sudo docker compose -f /tmp/compose.yml down -v
93
+ '
94
+ ```
95
+
96
+ Use `docker compose` (v2 plugin) — `docker-compose` (legacy v1) is not installed.
97
+
98
+ ## Cleanup discipline (mandatory)
99
+
100
+ Sprites are paid resources. **Always destroy them.** Pattern:
101
+
102
+ ```bash
103
+ sprite create test-$$
104
+ trap "sprite destroy test-$$ --force" EXIT INT TERM
105
+ # … work …
106
+ ```
107
+
108
+ If a script crashes, `sprite list` shows what's still up. Sweep with `sprite list -o desplega` and destroy anything you don't recognize.
109
+
110
+ ## Setup script snippet
111
+
112
+ Drop this into an agent's setup script to auto-install + auth on container boot:
113
+
114
+ ```bash
115
+ # Sprite CLI — sandboxes for docker/postgres/etc.
116
+ if [ ! -x "$HOME/.local/bin/sprite" ]; then
117
+ curl -fsSL https://sprites.dev/install.sh | sh -s -- 2>/dev/null || true
118
+ fi
119
+ if [ -n "$SPRITES_API_KEY" ] && [ -x "$HOME/.local/bin/sprite" ]; then
120
+ "$HOME/.local/bin/sprite" auth setup --token "$SPRITES_API_KEY" 2>/dev/null || true
121
+ fi
122
+ # Make sprite findable in non-login shells
123
+ grep -q '/.local/bin' "$HOME/.bashrc" 2>/dev/null || echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.bashrc"
124
+ ```
125
+
126
+ ## Common gotchas
127
+
128
+ - **PATH:** the installer drops the binary in `~/.local/bin`. Non-login bash shells often miss it. Always export PATH or use the absolute path.
129
+ - **No keyring:** the CLI warns "No system keyring available. Storing secrets unencrypted in ~/.sprites/keyring/". This is expected in a container; the file is mode 600 and only readable by the agent user.
130
+ - **Daemon survival:** `sprite exec` runs in a fresh subshell. A daemon backgrounded with `&` survives the exec call (sprites use a shared init), but if you want to be safe use `nohup`. To stop it, `sprite exec -s … -- sudo pkill <daemon>`.
131
+ - **Port forwarding:** ports inside the sprite are not exposed to your swarm container. Curl from *inside* the sprite (`sprite exec -s … -- curl localhost:5432`) — not from your container.
132
+ - **Don't leak secrets:** treat the sprite as an untrusted host. Don't put production tokens in there.
133
+
@@ -0,0 +1,13 @@
1
+ {
2
+ "kind": "skill",
3
+ "name": "turso-interaction",
4
+ "displayName": "Turso / LibSQL Interaction",
5
+ "slug": "turso-interaction",
6
+ "title": "Turso / LibSQL Interaction",
7
+ "description": "Safely inspect and update LibSQL databases from agent workflows.",
8
+ "version": "1.0.0",
9
+ "category": "skills",
10
+ "placeholders": ["TURSO_DATABASE_URL"],
11
+ "runAllSeedersCandidate": false,
12
+ "tags": ["database", "libsql", "turso"]
13
+ }
@@ -0,0 +1,192 @@
1
+ # Turso Interaction
2
+
3
+ ## The two-token model (READ THIS FIRST)
4
+
5
+ Turso has two separate auth planes — they do NOT cross over.
6
+
7
+ | Token type | Where it works | Stored in swarm config as | Signature | Expiry |
8
+ |---|---|---|---|---|
9
+ | **Platform JWT** (management plane) | `api.turso.tech/v1/*` — list orgs, list DBs, mint DB tokens, group/db CRUD; also what the `turso` CLI uses | `TURSO_API_TOKEN` | Clerk-issued RS256 | ~7 days — Clerk rotates it |
10
+ | **DB token** (data plane) | `https://<db-host>/v2/pipeline` — SELECT/INSERT/etc. against a specific DB | `TURSO_DB_TOKEN` (content-state), `TURSO_X_POSTS_DB_TOKEN` (x-posts), etc. | EdDSA | non-expiring (mint with `--expiration none`) or per-token TTL |
11
+
12
+ Using the **platform JWT against `/v2/pipeline` returns `HTTP 401 "invalid JWT token: can't be decoded with any of the existing keys"`** on every DB — that's by design, not a bug. If you see that error, you reached for the wrong token. Use the DB-specific one.
13
+
14
+ The platform JWT *can*, however, **mint** a DB token for any DB (see "Mint a DB token via API" below) — that's how you bootstrap access to a DB whose token isn't stored in config.
15
+
16
+ ## Swarm config inventory (current)
17
+
18
+ Always fetch with `get-config includeSecrets=true`. As of 2026-05-12:
19
+
20
+ | Key | Plane | Scope | Notes |
21
+ |---|---|---|---|
22
+ | `TURSO_API_TOKEN` | Platform | global | Clerk JWT. Expires ~weekly. When expired, daily-blocker-digest surfaces it and Taras refreshes via Turso dashboard. |
23
+ | `TURSO_DB_TOKEN` | Data (content-state) | global | EdDSA, non-expiring. Used with `TURSO_DB_URL` for `/v2/pipeline`. |
24
+ | `TURSO_DB_URL` | Data (content-state) | global | `https://content-state-desplega.aws-eu-west-1.turso.io` (HTTPS form — required for HTTP API). |
25
+ | `TURSO_X_POSTS_DB_TOKEN` | Data (x-posts) | global | EdDSA, non-expiring. |
26
+ | `TURSO_X_POSTS_DB_URL` | Data (x-posts) | global | `libsql://x-posts-desplega.aws-eu-west-1.turso.io` — **swap `libsql://` → `https://` before hitting `/v2/pipeline`**. |
27
+
28
+ `dummy-test-db` has no stored DB token. Mint one via the API on demand (recipe below).
29
+
30
+ ## Querying via HTTP API `/v2/pipeline` (the workflow path)
31
+
32
+ Workflow script nodes hit the DB over HTTP. This is the pattern to use anywhere outside the CLI.
33
+
34
+ ```bash
35
+ curl -s -X POST "$DB_URL/v2/pipeline" \
36
+ -H "Authorization: Bearer $DB_TOKEN" \
37
+ -H "Content-Type: application/json" \
38
+ -d '{
39
+ "requests": [
40
+ {"type":"execute","stmt":{"sql":"SELECT name FROM sqlite_master WHERE type='\''table'\''"}},
41
+ {"type":"close"}
42
+ ]
43
+ }'
44
+ ```
45
+
46
+ Response shape (success):
47
+ ```json
48
+ {"results":[{"type":"ok","response":{"type":"execute","result":{"cols":[{"name":"name","decltype":"TEXT"}],"rows":[[{"type":"text","value":"posts"}]]}}}, {"type":"ok","response":{"type":"close"}}]}
49
+ ```
50
+
51
+ Always include `{"type":"close"}` as the last request. Use parameterized statements (`stmt.args`) for user-supplied values, not string-built SQL.
52
+
53
+ **URL form**: `/v2/pipeline` only accepts `https://`. If a config key holds the `libsql://` form, rewrite the scheme:
54
+ ```bash
55
+ URL="${TURSO_X_POSTS_DB_URL/libsql:\/\//https:\/\/}"
56
+ ```
57
+
58
+ ## Mint a DB token via the platform API
59
+
60
+ When a DB has no stored token (e.g., `dummy-test-db`), mint one with the platform JWT:
61
+
62
+ ```bash
63
+ DB=dummy-test-db
64
+ DB_TOKEN=$(curl -s -X POST \
65
+ "https://api.turso.tech/v1/organizations/desplega/databases/$DB/auth/tokens?authorization=read-only" \
66
+ -H "Authorization: Bearer $TURSO_API_TOKEN" | jq -r '.jwt')
67
+ ```
68
+
69
+ `authorization` can be `read-only` or `full-access`. Add `?expiration=1d` (or `7d`, `never`) to control TTL.
70
+
71
+ ## CLI installation
72
+
73
+ ```bash
74
+ curl -sSfL https://get.tur.so/install.sh | bash
75
+ export PATH="$HOME/.turso:$PATH"
76
+ ```
77
+
78
+ The binary lives at `~/.turso/turso`. PATH must be exported in the same session.
79
+
80
+ ## CLI authentication
81
+
82
+ The CLI uses the **platform JWT**, not a DB token:
83
+
84
+ ```bash
85
+ turso config set token "$TURSO_API_TOKEN"
86
+ turso org switch desplega
87
+ turso db list # verify
88
+ ```
89
+
90
+ Do NOT use `turso auth login` — it needs a browser. Always feed the config token in.
91
+
92
+ If `turso db list` returns 401, the platform JWT has expired — refresh `TURSO_API_TOKEN` in swarm config (Taras owns this; surface via blocker digest).
93
+
94
+ ## CLI database operations
95
+
96
+ ```bash
97
+ turso db create <name> # default group, aws-eu-west-1
98
+ turso db list
99
+ turso db show <name> # URL, region, size
100
+ turso db shell <name> # interactive
101
+ turso db shell <name> "SELECT * FROM t;" # one-shot
102
+ turso db shell <name> < dump.sql # pipe file
103
+ turso db destroy <name> # !!!
104
+ ```
105
+
106
+ ## CLI DB-token generation
107
+
108
+ ```bash
109
+ turso db tokens create <name> # default TTL
110
+ turso db tokens create <name> --expiration none # non-expiring (what we store in config)
111
+ turso db tokens create <name> --read-only # SELECT-only
112
+ ```
113
+
114
+ After generating, write back to swarm config with `set-config` (mark `isSecret=true`).
115
+
116
+ ## Seeding a Turso DB from local SQLite
117
+
118
+ ```bash
119
+ sqlite3 local.db .dump > dump.sql
120
+ turso db create <name>
121
+ turso db shell <name> < dump.sql
122
+ ```
123
+
124
+ ## Connection-URL pattern
125
+
126
+ ```
127
+ libsql://<db-name>-<org>.aws-eu-west-1.turso.io # for libsql:// clients
128
+ https://<db-name>-<org>.aws-eu-west-1.turso.io # for HTTP API /v2/pipeline
129
+ ```
130
+
131
+ Same host, two schemes. Some config keys store the libsql form, some the https form — normalize before use.
132
+
133
+ ## Key databases (org = desplega)
134
+
135
+ | Database | HTTPS URL | Token config key | Used by |
136
+ |---|---|---|---|
137
+ | `content-state` | `https://content-state-desplega.aws-eu-west-1.turso.io` | `TURSO_DB_TOKEN` | Content workflows (`content_history`, `image_prompt_history`, `refresh_history`, `repo_patterns`, `workflow_executions`) |
138
+ | `x-posts` | `https://x-posts-desplega.aws-eu-west-1.turso.io` | `TURSO_X_POSTS_DB_TOKEN` | X/Twitter post tracking + meme cooldown (`posts` table) |
139
+ | `dummy-test-db` | `https://dummy-test-db-desplega.aws-eu-west-1.turso.io` | — (mint via API) | Test fixture (`users` table) |
140
+
141
+ ## Groups
142
+
143
+ ```bash
144
+ turso group list
145
+ turso group create <name> --location <location>
146
+ ```
147
+
148
+ Default group: `default` in `aws-eu-west-1`.
149
+
150
+ ## Local development
151
+
152
+ ```bash
153
+ turso dev # starts a local LibSQL server
154
+ ```
155
+
156
+ ## Full bootstrap from scratch
157
+
158
+ ```bash
159
+ curl -sSfL https://get.tur.so/install.sh | bash
160
+ export PATH="$HOME/.turso:$PATH"
161
+ # Fetch TURSO_API_TOKEN via get-config includeSecrets=true
162
+ turso config set token "$TURSO_API_TOKEN"
163
+ turso org switch desplega
164
+ turso db list
165
+ ```
166
+
167
+ ## Health-check recipe (verify all 3 DBs in <30s)
168
+
169
+ ```bash
170
+ # Platform plane
171
+ curl -s -H "Authorization: Bearer $TURSO_API_TOKEN" \
172
+ https://api.turso.tech/v1/organizations/desplega/databases | jq '[.databases[].Name]'
173
+
174
+ # Data plane — one /v2/pipeline call per DB
175
+ for pair in "$TURSO_DB_URL|$TURSO_DB_TOKEN" "https://x-posts-desplega.aws-eu-west-1.turso.io|$TURSO_X_POSTS_DB_TOKEN"; do
176
+ url="${pair%|*}"; tok="${pair#*|}"
177
+ curl -s -X POST "$url/v2/pipeline" -H "Authorization: Bearer $tok" \
178
+ -H "Content-Type: application/json" \
179
+ -d '{"requests":[{"type":"execute","stmt":{"sql":"SELECT 1"}},{"type":"close"}]}' \
180
+ | jq -c '.results[0]'
181
+ done
182
+ ```
183
+
184
+ If either plane returns 401, treat as a blocker — surface in HEARTBEAT.md, do not retry silently.
185
+
186
+ ## When tokens expire / get rotated
187
+
188
+ - **`TURSO_API_TOKEN` expired** → CLI breaks, can't mint new DB tokens, `api.turso.tech` returns 401. Existing DB tokens keep working (data plane is independent). Action: Taras rotates via Turso dashboard, updates config.
189
+ - **A DB token expired/revoked** → that specific DB returns 401 on `/v2/pipeline`. Other DBs unaffected. Action: mint a new one (CLI or platform API), update the corresponding config key.
190
+
191
+ Don't conflate the two failure modes. The blocker-digest writer should name the exact key that needs rotation.
192
+
@@ -0,0 +1,18 @@
1
+ {
2
+ "kind": "skill",
3
+ "name": "workflow-iterate",
4
+ "displayName": "Workflow Iteration",
5
+ "slug": "workflow-iterate",
6
+ "title": "Workflow Iteration",
7
+ "description": "A procedure for improving workflows through small tested revisions.",
8
+ "version": "1.0.0",
9
+ "category": "skills",
10
+ "placeholders": [],
11
+ "runAllSeedersCandidate": true,
12
+ "tags": [
13
+ "workflows",
14
+ "authoring",
15
+ "qa"
16
+ ],
17
+ "must": true
18
+ }