|
| 1 | +# Canonical Demo Deploy — Phase 1 Implementation Plan |
| 2 | + |
| 3 | +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
| 4 | +
|
| 5 | +**Goal:** Add the canonical demo's LangGraph (`examples/chat/python`, graph name `chat`) to the existing shared `cockpit-dev` LangGraph Cloud deployment, so it's reachable on the LangGraph side before the frontend ships in Phase 2. |
| 6 | + |
| 7 | +**Architecture:** Extend the generator `scripts/generate-shared-deployment-config.ts` to include a small hardcoded list of "non-cockpit" Python dependencies alongside the existing capability-registry-driven cockpit graphs. Add `examples/chat/python/**` to the deploy-langgraph workflow's watched paths so changes there retrigger redeployment. Register `chat` in `deployment-urls.json` (so the shared-URL coherence check still passes) and in `scripts/verify-shared-deployment.ts`'s smoke list (so post-deploy verification confirms the graph is reachable). |
| 8 | + |
| 9 | +**Tech Stack:** TypeScript (Nx), vitest, GitHub Actions workflow YAML, LangGraph CLI. |
| 10 | + |
| 11 | +**Reference spec:** `docs/superpowers/specs/2026-05-13-canonical-demo-deploy-design.md` — see "Phase 1 — backend graph addition". |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## Background for the implementer |
| 16 | + |
| 17 | +The deploy pipeline today: |
| 18 | + |
| 19 | +1. `apps/cockpit/scripts/capability-registry.ts` exports an array of capability descriptors. Each has `pythonDir` and `graphName` fields. |
| 20 | +2. `scripts/generate-shared-deployment-config.ts` reads that array, filters to `product === 'langgraph' || product === 'deep-agents'`, reads each capability's `langgraph.json` manifest, and aggregates all graphs into `deployments/shared-dev/langgraph.json`. |
| 21 | +3. `.github/workflows/deploy-langgraph.yml` runs on pushes to main that touch any cockpit Python graph (or the generator, or the registry) and deploys `deployments/shared-dev/` to the shared `cockpit-dev` LangGraph Cloud assistant. |
| 22 | +4. After CI deploy, `scripts/verify-shared-deployment.ts` runs in production smoke and asserts each entry in `SMOKE_ASSISTANT_IDS` is reachable. |
| 23 | +5. `deployment-urls.json` is consulted by `verify-shared-deployment.ts` — every capability that targets the shared deployment is listed there, all pointing to the same URL. |
| 24 | + |
| 25 | +The canonical demo (`examples/chat/python`, graph `chat`) is **not** in any of these. After this phase it will be. |
| 26 | + |
| 27 | +We deliberately don't add `examples/chat` to `capability-registry.ts`. That registry's entries describe Angular SPAs with ports and project names — adding a phantom `chat-canonical` row would force fake values into multiple fields. Cleaner: put a single dedicated array of "extra Python deployments" in the generator itself. |
| 28 | + |
| 29 | +--- |
| 30 | + |
| 31 | +### Task 1: Extend generator to include `examples/chat/python` |
| 32 | + |
| 33 | +**Files:** |
| 34 | +- Modify: `scripts/generate-shared-deployment-config.ts` |
| 35 | +- Create: `scripts/generate-shared-deployment-config.spec.ts` |
| 36 | + |
| 37 | +**Context:** The generator is a top-level script today — no exported functions. We'll refactor lightly to extract the build into a callable function, then test it by running the generator end-to-end and reading the output. Refactoring is mild because the script is 86 lines and self-contained. |
| 38 | + |
| 39 | +--- |
| 40 | + |
| 41 | +- [ ] **Step 1: Create the failing test** |
| 42 | + |
| 43 | +Create `scripts/generate-shared-deployment-config.spec.ts`: |
| 44 | + |
| 45 | +```ts |
| 46 | +// scripts/generate-shared-deployment-config.spec.ts |
| 47 | +// SPDX-License-Identifier: MIT |
| 48 | +import { execSync } from 'child_process'; |
| 49 | +import { readFileSync } from 'fs'; |
| 50 | +import { resolve } from 'path'; |
| 51 | +import { describe, expect, it } from 'vitest'; |
| 52 | + |
| 53 | +describe('generate-shared-deployment-config', () => { |
| 54 | + it('includes the canonical-demo chat graph in the aggregated manifest', () => { |
| 55 | + const root = resolve(__dirname, '..'); |
| 56 | + execSync('npx tsx scripts/generate-shared-deployment-config.ts', { |
| 57 | + cwd: root, |
| 58 | + stdio: 'pipe', |
| 59 | + }); |
| 60 | + const manifestPath = resolve(root, 'deployments/shared-dev/langgraph.json'); |
| 61 | + const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as { |
| 62 | + graphs: Record<string, string>; |
| 63 | + dependencies: string[]; |
| 64 | + }; |
| 65 | + expect(manifest.graphs).toHaveProperty('chat'); |
| 66 | + expect(manifest.graphs.chat).toMatch(/examples-chat\/.+\.py:graph$/); |
| 67 | + expect(manifest.dependencies.some((d) => d.includes('examples-chat'))).toBe(true); |
| 68 | + }); |
| 69 | +}); |
| 70 | +``` |
| 71 | + |
| 72 | +- [ ] **Step 2: Run test to verify failure** |
| 73 | + |
| 74 | +``` |
| 75 | +npx vitest run scripts/generate-shared-deployment-config.spec.ts |
| 76 | +``` |
| 77 | + |
| 78 | +Expected: FAIL with `expected { ... } to have property "chat"`. |
| 79 | + |
| 80 | +- [ ] **Step 3: Modify the generator to stage `examples/chat/python` as an extra dependency** |
| 81 | + |
| 82 | +In `scripts/generate-shared-deployment-config.ts`, locate the loop that ends on line 68 (`}` closing the `for (const capability of capabilities)` block). Immediately after that closing `}`, before the line `const streamingManifestPath = resolve(rootDir, 'cockpit/langgraph/streaming/python/langgraph.json');`, insert: |
| 83 | + |
| 84 | +```ts |
| 85 | +// Extra Python deployments NOT in the cockpit capability registry. |
| 86 | +// These have no Angular project / port — only a backend graph aggregated |
| 87 | +// into the shared cockpit-dev assistant. |
| 88 | +const extraPythonDeployments: ReadonlyArray<{ pythonDir: string; alias: string }> = [ |
| 89 | + { pythonDir: 'examples/chat/python', alias: 'examples-chat' }, |
| 90 | +]; |
| 91 | + |
| 92 | +for (const extra of extraPythonDeployments) { |
| 93 | + const manifestPath = resolve(rootDir, extra.pythonDir, 'langgraph.json'); |
| 94 | + const extraManifest = readManifest(manifestPath); |
| 95 | + if (!extraManifest.graphs) { |
| 96 | + throw new Error(`Missing graphs in ${manifestPath}`); |
| 97 | + } |
| 98 | + const stagedDependencyRoot = stageDependency(extra.pythonDir, extra.alias); |
| 99 | + for (const [graphName, entrypoint] of Object.entries(extraManifest.graphs)) { |
| 100 | + addGraph(graphName, toDeploymentPath(stagedDependencyRoot, entrypoint)); |
| 101 | + } |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +- [ ] **Step 4: Run test to verify pass** |
| 106 | + |
| 107 | +``` |
| 108 | +npx vitest run scripts/generate-shared-deployment-config.spec.ts |
| 109 | +``` |
| 110 | + |
| 111 | +Expected: PASS. |
| 112 | + |
| 113 | +- [ ] **Step 5: Inspect the generated manifest manually** |
| 114 | + |
| 115 | +``` |
| 116 | +cat deployments/shared-dev/langgraph.json | head -30 |
| 117 | +``` |
| 118 | + |
| 119 | +Expected: `"chat": "./deps/examples-chat/src/graph.py:graph"` should appear in the `graphs` object. `"./deps/examples-chat"` should appear in `dependencies`. |
| 120 | + |
| 121 | +- [ ] **Step 6: Commit** |
| 122 | + |
| 123 | +```bash |
| 124 | +git add scripts/generate-shared-deployment-config.ts \ |
| 125 | + scripts/generate-shared-deployment-config.spec.ts \ |
| 126 | + deployments/shared-dev/langgraph.json |
| 127 | +git commit -m "feat(deploy): include examples/chat/python in shared cockpit-dev deployment |
| 128 | +
|
| 129 | +Adds the canonical demo's Python graph to the aggregated manifest |
| 130 | +generator. After CI redeploys, the shared cockpit-dev LangGraph |
| 131 | +Cloud assistant will expose 'chat' alongside the existing cockpit |
| 132 | +capability graphs. |
| 133 | +
|
| 134 | +Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>" |
| 135 | +``` |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +### Task 2: Wire the new graph into the deploy + verify pipeline |
| 140 | + |
| 141 | +**Files:** |
| 142 | +- Modify: `.github/workflows/deploy-langgraph.yml` (watched paths) |
| 143 | +- Modify: `deployment-urls.json` (add `chat` entry) |
| 144 | +- Modify: `scripts/verify-shared-deployment.ts` (`SMOKE_ASSISTANT_IDS` array) |
| 145 | + |
| 146 | +**Context:** Three small config edits. No tests — config-only edits don't benefit from unit coverage; the integration check is the production smoke that runs after deploy. |
| 147 | + |
| 148 | +--- |
| 149 | + |
| 150 | +- [ ] **Step 1: Extend the deploy-langgraph workflow's watched paths** |
| 151 | + |
| 152 | +In `.github/workflows/deploy-langgraph.yml`, locate the `paths:` block at the top (lines 5–10): |
| 153 | + |
| 154 | +```yaml |
| 155 | + paths: |
| 156 | + - 'cockpit/langgraph/**/python/**' |
| 157 | + - 'cockpit/deep-agents/**/python/**' |
| 158 | + - 'apps/cockpit/scripts/capability-registry.ts' |
| 159 | + - 'scripts/generate-shared-deployment-config.ts' |
| 160 | + - 'deployments/shared-dev/langgraph.json' |
| 161 | +``` |
| 162 | +
|
| 163 | +Add a new entry for the canonical demo's Python dir. The final block should read: |
| 164 | +
|
| 165 | +```yaml |
| 166 | + paths: |
| 167 | + - 'cockpit/langgraph/**/python/**' |
| 168 | + - 'cockpit/deep-agents/**/python/**' |
| 169 | + - 'examples/chat/python/**' |
| 170 | + - 'apps/cockpit/scripts/capability-registry.ts' |
| 171 | + - 'scripts/generate-shared-deployment-config.ts' |
| 172 | + - 'deployments/shared-dev/langgraph.json' |
| 173 | +``` |
| 174 | +
|
| 175 | +- [ ] **Step 2: Register `chat` in `deployment-urls.json`** |
| 176 | + |
| 177 | +`verify-shared-deployment.ts`'s `getSharedUrl()` function requires every entry in `deployment-urls.json` to resolve to the same URL. Append `chat` with the existing shared URL. |
| 178 | +
|
| 179 | +In `deployment-urls.json`, after the existing `"sandboxes"` line, add a `"chat"` entry. The shared URL value to use is the same one every other capability uses today (read the current file before editing — copy that value verbatim): |
| 180 | +
|
| 181 | +```json |
| 182 | + "chat": "https://cockpit-dev-219a15942c545a00a03a9a41905d7fc2.us.langgraph.app" |
| 183 | +``` |
| 184 | +
|
| 185 | +Result: every capability + `chat` all point to the same `cockpit-dev` URL. |
| 186 | +
|
| 187 | +- [ ] **Step 3: Add `chat` to the production-smoke assistant list** |
| 188 | +
|
| 189 | +In `scripts/verify-shared-deployment.ts`, locate `SMOKE_ASSISTANT_IDS` (around line 21): |
| 190 | +
|
| 191 | +```ts |
| 192 | +const SMOKE_ASSISTANT_IDS = [ |
| 193 | + 'streaming', |
| 194 | + 'deployment-runtime', |
| 195 | + 'planning', |
| 196 | + 'filesystem', |
| 197 | + 'c-generative-ui', |
| 198 | + 'c-a2ui', |
| 199 | +] as const; |
| 200 | +``` |
| 201 | + |
| 202 | +Append `'chat'`: |
| 203 | + |
| 204 | +```ts |
| 205 | +const SMOKE_ASSISTANT_IDS = [ |
| 206 | + 'streaming', |
| 207 | + 'deployment-runtime', |
| 208 | + 'planning', |
| 209 | + 'filesystem', |
| 210 | + 'c-generative-ui', |
| 211 | + 'c-a2ui', |
| 212 | + 'chat', |
| 213 | +] as const; |
| 214 | +``` |
| 215 | + |
| 216 | +- [ ] **Step 4: Re-run the generator test to confirm nothing broke** |
| 217 | + |
| 218 | +``` |
| 219 | +npx vitest run scripts/generate-shared-deployment-config.spec.ts |
| 220 | +``` |
| 221 | + |
| 222 | +Expected: PASS — the test from Task 1 still passes. |
| 223 | + |
| 224 | +- [ ] **Step 5: Run the verify script in dry-run mode locally** |
| 225 | + |
| 226 | +``` |
| 227 | +npx tsx scripts/verify-shared-deployment.ts --dry-run |
| 228 | +``` |
| 229 | + |
| 230 | +Expected: succeeds, prints a summary listing `chat` as one of the smoke assistants. (The script's full mode requires `LANGSMITH_API_KEY` and live network; `--dry-run` validates config only.) |
| 231 | + |
| 232 | +- [ ] **Step 6: Commit** |
| 233 | + |
| 234 | +```bash |
| 235 | +git add .github/workflows/deploy-langgraph.yml \ |
| 236 | + deployment-urls.json \ |
| 237 | + scripts/verify-shared-deployment.ts |
| 238 | +git commit -m "feat(deploy): wire 'chat' assistant into deploy + verify pipeline |
| 239 | +
|
| 240 | +- Watch examples/chat/python/** so changes there retrigger deploy |
| 241 | +- Register chat in deployment-urls.json (single shared URL) |
| 242 | +- Include chat in production-smoke SMOKE_ASSISTANT_IDS |
| 243 | +
|
| 244 | +Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>" |
| 245 | +``` |
| 246 | + |
| 247 | +--- |
| 248 | + |
| 249 | +### Task 3: Open PR and wait for the first deploy run |
| 250 | + |
| 251 | +**Context:** After merging to main, the deploy-langgraph workflow runs, redeploys `cockpit-dev` with the chat graph included, and the production-smoke job (in ci.yml) calls `verify-shared-deployment.ts`. If the chat assistant is reachable, smoke passes and Phase 1 is done. No frontend exists yet — the chat graph is dormant until Phase 2. |
| 252 | + |
| 253 | +--- |
| 254 | + |
| 255 | +- [ ] **Step 1: Push branch + open PR** |
| 256 | + |
| 257 | +```bash |
| 258 | +git push -u origin claude/canonical-demo-deploy |
| 259 | +gh pr create --title "feat(deploy): add canonical demo chat graph to shared cockpit-dev deployment (Phase 1)" --body "$(cat <<'EOF' |
| 260 | +## Summary |
| 261 | +
|
| 262 | +Phase 1 of the canonical-demo deployment plan. Adds the canonical demo's Python graph (`examples/chat/python`, graph name `chat`) to the aggregated `cockpit-dev` LangGraph Cloud deployment. |
| 263 | +
|
| 264 | +No frontend yet — Phase 2 stands up `demo.cacheplane.ai`. This phase makes the backend graph reachable so Phase 2 has something to consume. |
| 265 | +
|
| 266 | +## Changes |
| 267 | +
|
| 268 | +- `scripts/generate-shared-deployment-config.ts` — stages `examples/chat/python` as an extra Python dependency, aggregating its graphs alongside the cockpit registry. |
| 269 | +- `.github/workflows/deploy-langgraph.yml` — watches `examples/chat/python/**` so changes there retrigger redeploy. |
| 270 | +- `deployment-urls.json` — `chat` entry pointing at the shared URL. |
| 271 | +- `scripts/verify-shared-deployment.ts` — `chat` added to `SMOKE_ASSISTANT_IDS` for post-deploy smoke. |
| 272 | +- `scripts/generate-shared-deployment-config.spec.ts` — vitest spec asserting the generator includes the chat graph. |
| 273 | +
|
| 274 | +## Spec & Plan |
| 275 | +
|
| 276 | +- `docs/superpowers/specs/2026-05-13-canonical-demo-deploy-design.md` |
| 277 | +- `docs/superpowers/plans/2026-05-13-canonical-demo-deploy-phase-1.md` |
| 278 | +
|
| 279 | +## Test plan |
| 280 | +
|
| 281 | +- [x] vitest generator spec passes (asserts `chat` in manifest + `examples-chat` in dependencies) |
| 282 | +- [x] `npx tsx scripts/verify-shared-deployment.ts --dry-run` succeeds |
| 283 | +- [ ] After merge: deploy-langgraph workflow runs successfully |
| 284 | +- [ ] After deploy: production-smoke job passes (verify-shared-deployment.ts confirms `chat` assistant is reachable) |
| 285 | +EOF |
| 286 | +)" |
| 287 | +``` |
| 288 | + |
| 289 | +- [ ] **Step 2: Wait for CI to be green on the PR** |
| 290 | + |
| 291 | +Required green checks before merge: |
| 292 | +- `Library — lint / test / build` (the new vitest spec runs in this job) |
| 293 | +- `Website — lint / build` |
| 294 | +- `Cockpit — build / test`, `e2e`, `representative capability smoke`, `build all examples`, `deploy smoke dry-run`, `secret-gated integration` |
| 295 | +- `examples/chat — python smoke` |
| 296 | + |
| 297 | +The deploy-langgraph workflow does NOT run on PRs — only on pushes to main with the watched paths touched. So the first real-world test of this phase is the post-merge deploy. |
| 298 | + |
| 299 | +- [ ] **Step 3: After merge, verify deploy + smoke** |
| 300 | + |
| 301 | +Within ~10 minutes of merge: |
| 302 | +1. Confirm `Deploy LangGraph` workflow ran (Actions tab; should be triggered by the changes to `examples/chat/python/**` watched path entry → no, that path doesn't change on first ship, but the changes to `generate-shared-deployment-config.ts` + `deployments/shared-dev/langgraph.json` DO match — confirm the workflow triggered). |
| 303 | +2. Confirm it succeeded (`Deploy cockpit-dev` step). |
| 304 | +3. Confirm the subsequent CI `production-smoke` job passed (`verify-shared-deployment.ts` reports `chat` reachable). |
| 305 | + |
| 306 | +If `production-smoke` fails because the chat assistant isn't reachable, the deploy succeeded but the graph didn't register properly. Check the LangGraph Cloud dashboard for the cockpit-dev assistant and look for the `chat` entry. If absent, the manifest needs investigation; if present but unreachable via the SDK, the assistant ID may need to be configured separately. |
| 307 | + |
| 308 | +--- |
| 309 | + |
| 310 | +## Self-review notes |
| 311 | + |
| 312 | +- **Spec coverage:** every Phase 1 requirement from the spec maps to a task. (a) generator extension → Task 1. (b) workflow paths → Task 2 Step 1. (c) deployment-urls → Task 2 Step 2. (d) verify smoke → Task 2 Step 3. (e) deploy run → Task 3. |
| 313 | +- **No placeholders:** every code block is final content the implementer pastes verbatim. |
| 314 | +- **Type consistency:** all references to `chat`, `examples-chat`, `examples/chat/python` are spelled identically across tasks. |
| 315 | +- **Test coverage proportional to value:** the generator gets a vitest spec because it's pure code; the YAML / JSON / TS config edits don't (low-value tests would just duplicate `grep`). |
0 commit comments