Skip to content

Commit 3f9cd32

Browse files
bloveclaude
andauthored
feat(deploy): add canonical demo chat graph to shared cockpit-dev deployment (Phase 1) (#300)
* docs: spec for canonical-demo deployment to demo.cacheplane.ai 5-phase plan, each PR independently shippable except Phase 2 (depends on Phase 1 backend graph addition). Backend reuses the shared cockpit-dev LangGraph Cloud assistant. Frontend is a new independent Vercel project. Proxy code refactored out of scripts/examples-middleware.ts into a shared scripts/langgraph-proxy.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: Phase 1 plan — add canonical demo graph to shared deployment Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(deploy): include examples/chat/python in shared cockpit-dev deployment Adds the canonical demo's Python graph to the aggregated manifest generator. After CI redeploys, the shared cockpit-dev LangGraph Cloud assistant will expose 'chat' alongside the existing cockpit capability graphs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(deploy): wire 'chat' assistant into deploy + verify pipeline - Watch examples/chat/python/** so changes there retrigger deploy - Register chat in deployment-urls.json (single shared URL) - Include chat in production-smoke SMOKE_ASSISTANT_IDS Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ca0211b commit 3f9cd32

8 files changed

Lines changed: 650 additions & 2 deletions

.github/workflows/deploy-langgraph.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
paths:
77
- 'cockpit/langgraph/**/python/**'
88
- 'cockpit/deep-agents/**/python/**'
9+
- 'examples/chat/python/**'
910
- 'apps/cockpit/scripts/capability-registry.ts'
1011
- 'scripts/generate-shared-deployment-config.ts'
1112
- 'deployments/shared-dev/langgraph.json'

deployment-urls.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
1212
"subagents": "https://cockpit-dev-219a15942c545a00a03a9a41905d7fc2.us.langgraph.app",
1313
"da-memory": "https://cockpit-dev-219a15942c545a00a03a9a41905d7fc2.us.langgraph.app",
1414
"skills": "https://cockpit-dev-219a15942c545a00a03a9a41905d7fc2.us.langgraph.app",
15-
"sandboxes": "https://cockpit-dev-219a15942c545a00a03a9a41905d7fc2.us.langgraph.app"
15+
"sandboxes": "https://cockpit-dev-219a15942c545a00a03a9a41905d7fc2.us.langgraph.app",
16+
"chat": "https://cockpit-dev-219a15942c545a00a03a9a41905d7fc2.us.langgraph.app"
1617
}

deployments/shared-dev/langgraph.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@
2424
"subagents": "./deps/da-subagents/src/graph.py:graph",
2525
"da-memory": "./deps/da-memory/src/graph.py:graph",
2626
"skills": "./deps/skills/src/graph.py:graph",
27-
"sandboxes": "./deps/sandboxes/src/graph.py:graph"
27+
"sandboxes": "./deps/sandboxes/src/graph.py:graph",
28+
"chat": "./deps/examples-chat/src/graph.py:graph"
2829
},
2930
"dependencies": [
3031
"./deps/da-memory",
3132
"./deps/da-subagents",
3233
"./deps/deployment-runtime",
3334
"./deps/durable-execution",
35+
"./deps/examples-chat",
3436
"./deps/filesystem",
3537
"./deps/interrupts",
3638
"./deps/memory",
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
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

Comments
 (0)