with one click
dev-workflow
// End-to-end development workflow — build, deploy to Docker code-server, verify with Playwright, and iterate with hot-reload
// End-to-end development workflow — build, deploy to Docker code-server, verify with Playwright, and iterate with hot-reload
| name | dev-workflow |
| description | End-to-end development workflow — build, deploy to Docker code-server, verify with Playwright, and iterate with hot-reload |
| user_invocable | true |
You are helping the user develop and test the Datamates VS Code extension. The workflow involves making code changes in either the extension or the MCP engine repo, auto-building via rspack watch mode, deploying to a Docker code-server container, and verifying changes using Playwright MCP.
Two linked repositories:
The extension is volume-mounted into the container (no VSIX). The repo is mounted read-only at /home/coder/extension-src, and a symlink connects it to the code-server extensions directory.
Run the deploy script (runs in foreground with watch mode):
yarn docker:deploy
This will:
docker-setup/.env exists (prompts for DBT_PROJECT_PATH if not)yarn rspack buildyarn rspack build --watch for automatic recompilationSince deploy enters watch mode, run it in the background when using Claude:
cd /Users/saravananshanmugam/Desktop/projects/altimate/vscode-dbt-power-user && yarn docker:deploy
Use the Playwright MCP tools to verify the extension loads:
http://localhost:3001/?folder=/home/coder/projectTo check engine-side logs:
The watcher automatically recompiles when files change in either repo (~350-400ms). To pick up changes in code-server:
browser_navigate to http://localhost:3001/?folder=/home/coder/project — do NOT use the command palette approachIMPORTANT: Always reload by navigating to the URL again. Do NOT open the command palette to run "Developer: Reload Window" — it's slower and more fragile.
docker-compose.yml mounts the repo at /home/coder/extension-src:rostart-code-server.sh creates a symlink from /home/coder/.local/share/code-server/extensions/vscode-dbt-power-user → /home/coder/extension-srcdeploy.sh enters yarn rspack build --watch after the container is ready, so code changes in either repo are auto-compiledstart-code-server.sh sets altimate.onboardedMcpServer: true in user settings to skip onboardingChanges to vscode-dbt-power-user trigger a rebuild. No need to rebuild manually.
The engine link requires modifications to rspack.config.js and tsconfig.json. See docs/development.md → "Manual" section for the exact diff. If these files revert to their normal state, the engine will be pulled from node_modules instead of the local path.
When opening a folder in code-server for the first time, a "Do you trust the authors?" dialog appears. You must accept it before extensions activate fully. The --disable-workspace-trust flag should suppress this, but check if it appears.
The Datamates tab may appear directly in the bottom panel tabs, OR it may be hidden under the "Additional Views" overflow menu (the ... button). Check both.
code-server runs with --auth none. Do not expose port 3001 on public networks.
If port 3001 is already in use: yarn docker:stop first.
up --build, never restartAfter any rspack/webpack rebuild, always use docker compose up --build -d (or yarn docker:deploy), never docker compose restart. code-server caches extension state in memory — restart reuses the stale cache and the new bundle won't take effect. This causes tests to pass/fail inconsistently and wastes debugging time.
If changes don't appear after up --build, force a clean build:
cd docker-setup && docker compose build --no-cache && docker compose up -d
yarn docker:deploy enters watch mode and doesn't return. When running from Claude, use run_in_background: true on the Bash tool, then check output with TaskOutput.
When using Playwright MCP to verify the extension, follow this exact sequence to minimize tool calls:
1. Verify bundle (1 call):
grep -c "myKeyFunction" dist/extension.js
2. Deploy + seed (1 call — combine with &&):
docker compose -f docker-setup/docker-compose.yml up --build -d && \
for i in $(seq 1 15); do curl -sf http://localhost:3001/healthz > /dev/null 2>&1 && break; sleep 2; done && \
CID=$(docker ps -q --filter "name=docker-setup-code-server") && \
docker exec $CID bash -c 'cd /home/coder/project && dbt seed && dbt run'
3. Check activation via logs (1 call — NOT by clicking through the UI):
CID=$(docker ps -q --filter "name=docker-setup-code-server")
latest=$(docker exec $CID bash -c 'ls -td /home/coder/.local/share/code-server/logs/*/ | head -1')
docker exec $CID grep "innoverio\|altimateai\|removed" "${latest}remoteagent.log"
4. Navigate + wait (2 calls):
browser_navigate → http://localhost:3001/?folder=/home/coder/project
browser_wait_for → 20 seconds
5. Open file + execute + screenshot (5 calls):
Preferred: click file tree (Quick Open via Ctrl+P is unreliable in Playwright — sometimes the file doesn't open):
browser_evaluate →
const items = document.querySelectorAll('[role="treeitem"]');
const target = Array.from(items).find(i => i.getAttribute('aria-label') === 'test_query.sql');
if (target) { target.click(); target.click(); } // double-click to pin tab
browser_wait_for → 2 seconds
browser_press_key → Control+Enter
browser_wait_for → 12 seconds
browser_take_screenshot
Fallback: Quick Open (retry if document title doesn't change):
browser_press_key → Control+P
browser_snapshot → find textbox ref
browser_type → ref=<textbox>, text='test_query.sql', submit=true
browser_press_key → Control+Enter (after 2s wait)
browser_take_screenshot (after 12s wait)
Total: ~10 tool calls per test, not 30+.
When testing multiple branches simultaneously, each needs its own container:
# Use -p for unique project names and different ports in docker-compose.override.yml
docker compose -p feature-a-e2e up --build -d # port 3004
docker compose -p feature-b-e2e up --build -d # port 3005
Without -p, containers from different worktrees share the same project name and collide.
The symlink directory name must include the version suffix (e.g. innoverio.vscode-dbt-power-user-0.60.1). A bare name like innoverio.vscode-dbt-power-user gets re-marked as removed by code-server's obsolete scanner on every boot — even if extensions.json has the correct entry. The extensions.json registration is necessary but not sufficient without the versioned name.
.sql files may open as "MS SQL" instead of "Jinja SQL" if the jinjahtml extension hasn't activated yet when the file opens. This is a race condition — jinjahtml maps .sql → jinja-sql but activation order varies. Workaround: reopen the file or manually switch language mode after extension activation.
The Docker container installs @altimateai/altimate-code from npm. To test local changes from the altimate-code repo:
cd /Users/saravananshanmugam/Desktop/projects/altimate/altimate-code
# Requires bun >= 1.3.10 (upgrade with: bun upgrade)
npx turbo build --filter=@altimateai/altimate-code
This produces platform-specific binaries in packages/opencode/dist/@altimateai/.
The installed binary is a compiled ELF at /usr/lib/node_modules/@altimateai/altimate-code/bin/.altimate-code. To replace it:
# 1. Copy the linux-arm64 binary (Docker runs on Colima aarch64)
docker cp /Users/saravananshanmugam/Desktop/projects/altimate/altimate-code/packages/opencode/dist/@altimateai/altimate-code-linux-arm64/bin/altimate docker-setup-code-server-1:/tmp/altimate-new
# 2. Kill running altimate-code processes (binary is "text file busy" otherwise)
docker exec -u root docker-setup-code-server-1 pkill -f 'altimate-code serve'
# 3. Remove old binary and copy new one
docker exec -u root docker-setup-code-server-1 bash -c "rm /usr/lib/node_modules/@altimateai/altimate-code/bin/.altimate-code && cp /tmp/altimate-new /usr/lib/node_modules/@altimateai/altimate-code/bin/.altimate-code && chmod +x /usr/lib/node_modules/@altimateai/altimate-code/bin/.altimate-code && rm /tmp/altimate-new"
# 4. Reload browser to restart extension (spawns new altimate-code process)
Note: The built binary altimate-code is a symlink to altimate — copy the altimate file, not the symlink.
docker exec -u root docker-setup-code-server-1 npm install -g @altimateai/altimate-code@latest
After replacing the binary, you may need to clean stale state:
# Remove stale DB and config (fresh start)
docker exec docker-setup-code-server-1 rm -f \
/home/coder/.local/share/altimate-code/opencode.db \
/home/coder/.local/share/altimate-code/opencode.db-shm \
/home/coder/.local/share/altimate-code/opencode.db-wal \
/home/coder/project/config.json
| Command | Description |
|---|---|
yarn docker:deploy | Full build + deploy + watch mode |
yarn docker:logs | Stream container logs |
yarn docker:stop | Stop the container |
Stored in docker-setup/.env (gitignored):
| Variable | Description |
|---|---|
DBT_PROJECT_PATH | Absolute path to local dbt project directory, mounted at /home/coder/project. Leave unset to use the bundled test-fixtures/jaffle-shop-duckdb instead (see below). |
To change: edit docker-setup/.env or delete it and run yarn docker:deploy to be re-prompted.
Two ready-to-use DuckDB projects are bundled in the repo under test-fixtures/:
| Fixture | Profile | Notes |
|---|---|---|
test-fixtures/jaffle-shop-duckdb | jaffle_shop | Classic dbt-labs jaffle-shop (customers, orders, payments) |
test-fixtures/dbt-core-sample-duckdb | dbt_core_sample_duckdb | go-sales sample with Python + SQL models, macros, snapshots |
start-code-server.sh copies both fixtures from the read-only /home/coder/extension-src mount into writable home dirs on first boot and runs dbt deps in each:
/home/coder/extension-src/test-fixtures/jaffle-shop-duckdb → /home/coder/jaffle-shop-duckdb
/home/coder/extension-src/test-fixtures/dbt-core-sample-duckdb → /home/coder/dbt-core-sample-duckdb
The Dockerfile pre-seeds ~/.dbt/profiles.yml with both profiles pointing at DuckDB files, so the fixtures work without any local setup — no warehouse credentials required.
There are three common setups:
Fastest smoke test — use a bundled fixture in-container (recommended for throwaway verification).
Leave DBT_PROJECT_PATH unset and open the container copy directly:
http://localhost:3001/?folder=/home/coder/jaffle-shop-duckdb
http://localhost:3001/?folder=/home/coder/dbt-core-sample-duckdb
Changes stay inside the container and are wiped when the volume is rebuilt.
Point DBT_PROJECT_PATH at a fixture path on the host (when you want to edit the fixture and keep the change).
# docker-setup/.env
DBT_PROJECT_PATH=/absolute/path/to/vscode-dbt-power-user/test-fixtures/jaffle-shop-duckdb
The fixture is mounted at /home/coder/project, so http://localhost:3001/?folder=/home/coder/project opens it. Edits land in the repo — commit or git checkout to reset.
Point DBT_PROJECT_PATH at an unrelated real dbt project (for debugging user-reported issues). Same mechanism as #2 — the project is mounted at /home/coder/project.
target/ committed. test-fixtures/jaffle-shop-duckdb/target/manifest.json is checked in so the extension has something to parse before the first dbt run. Running dbt will overwrite it; don't commit the churn.dbt_core_sample_duckdb profile expects /home/coder/dbt-core-sample-duckdb/go_sales.db. That path only exists inside the container copy — running dbt against this fixture from the host mount (#2) will fail unless you also create go_sales.db locally.DBT_PROJECT_PATH is unset, compose mounts a host path that usually doesn't exist, and start-code-server.sh then opens /home/coder/jaffle_shop_duckdb (underscores), which also doesn't exist. Always either set DBT_PROJECT_PATH or navigate explicitly to /home/coder/jaffle-shop-duckdb (dashes).This section is the machine-readable contract for the harness sandbox system. The CLI parses the YAML block below to render the pod template.
| Phase | Marker file | What's available |
|---|---|---|
| Phase 1: code-ready | /workspace/.code-ready | Source code, node_modules, linters, tsc |
| Phase 2: server-ready | /workspace/.server-ready | code-server running on :3001, extension loaded |
harness spawn returns at phase 1 (~5s with base image). Agent reads codebase while code-server starts (~30-60s for yarn compile + code-server boot).
repo: vscode-dbt-power-user
repo_url: https://github.com/AltimateAI/vscode-dbt-power-user.git
base_image: altimateacr.azurecr.io/vscode-dbt-power-user-base:latest
working_dir: /workspace/vscode-dbt-power-user
port: 3001
health_path: /healthz
provides:
- name: CODE_SERVER_URL
value: "http://sandbox-{{ sandbox_id }}.{{ namespace }}.svc.cluster.local:3001"
needs: []
sidecars: []
setup_commands:
- name: enable-corepack
cmd: corepack enable
- name: install-root-deps
cmd: yarn install --immutable 2>&1 | tail -5 || yarn install 2>&1 | tail -5
- name: install-webview-deps
cmd: cd webview_panels && (yarn install --immutable 2>&1 | tail -5 || yarn install 2>&1 | tail -5)
- name: compile-extension
cmd: yarn compile
- name: setup-code-server-extension
cmd: |
EXT_DIR="$HOME/.local/share/code-server/extensions"
mkdir -p "$EXT_DIR"
ln -sf /workspace/vscode-dbt-power-user "$EXT_DIR/vscode-dbt-power-user"
- name: setup-code-server-settings
cmd: |
SETTINGS_DIR="$HOME/.local/share/code-server/User"
mkdir -p "$SETTINGS_DIR"
if [ ! -f "$SETTINGS_DIR/settings.json" ]; then
echo '{"altimate.onboardedMcpServer": true}' > "$SETTINGS_DIR/settings.json"
fi
start_command: >
code-server
--bind-addr 0.0.0.0:3001
--auth none
--disable-telemetry
--disable-workspace-trust
--log debug
~/.local/share/code-server/extensions/vscode-dbt-power-user and that dist/extension.js was built by yarn compile.az acr build --registry altimateacr --image vscode-dbt-power-user-base:latest -f Dockerfile.base .altimate.onboardedMcpServer: true in code-server settings.