Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 80 additions & 24 deletions .claude/skills/initial-setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,69 @@ If any check fails, tell the user what to install/configure and STOP.

---

## Phase 2 — Databricks App Configuration (app.yaml)
## Phase 2 — Personalization (name_prefix)

### 2a. Check if app.yaml exists
All deployed resource names (app, database, volume) are parameterized with the `name_prefix` bundle variable. This phase auto-detects a good prefix to avoid collisions in shared workspaces.

### 2a. Detect the current user

Extract the short username from the authenticated identity:

```bash
SHORT_USER=$(databricks auth describe 2>&1 | awk '/^User:/{print $2; exit}' | sed 's/@.*//')
echo "Detected user: $SHORT_USER"
```

### 2b. Check for existing app collisions

Check whether an app named `<username>-claw` already exists:

```bash
databricks apps list 2>/dev/null | grep "${SHORT_USER}-claw" || echo "No collision found"
```

If there IS a collision, warn the user and ask them to choose a different prefix.

### 2c. Write the local bundle override

Suggest `name_prefix = <short_user>` (e.g. `tanner` from `tanner@company.com`). Confirm with the user, then write:

```bash
mkdir -p .databricks/bundle
cat > .databricks/bundle/dev.yml <<EOF
variables:
name_prefix: ${SHORT_USER}
EOF
```

Show the user what was written:

```bash
echo "Wrote .databricks/bundle/dev.yml:"
cat .databricks/bundle/dev.yml
echo ""
echo "Your deployed resources will be named:"
echo " App: ${SHORT_USER}-claw"
echo " Database: ${SHORT_USER}-claw-db"
echo " Volume: ${SHORT_USER}_claw_memory"
```

This file is gitignored (`.databricks/` is in `.gitignore`), so it stays local.

If the user prefers a different prefix, update the file accordingly.

---

## Phase 3 — Databricks App Configuration (app.yaml)

### 3a. Check if app.yaml exists

If `app.yaml` does NOT exist, copy from the template:
```
cp app.example.yml app.yaml
```

### 2b. Walk through required env vars
### 3b. Walk through required env vars

Read `app.yaml` and guide the user through each field. The required env vars are:

Expand Down Expand Up @@ -97,7 +150,7 @@ For each required field:
If they don't match, update `app.yaml` with the correct workspace ID and STOP until fixed.
- For `CLAW_MEMORY_VOLUME_PATH`, confirm the catalog name matches `var.catalog_name` in `databricks.yml`.

### 2c. Check databricks.yml variables
### 3c. Check databricks.yml variables

Read `databricks.yml` and confirm the variable defaults make sense for the user's workspace:
- `ai_gateway_endpoint` — must match the `ANTHROPIC_MODEL` value in `app.yaml`
Expand All @@ -109,12 +162,12 @@ Ask the user to confirm or update these.

---

## Phase 3 — Slack UC Connection Verification
## Phase 4 — Slack UC Connection Verification

The person running this setup may not have admin access to the Slack app itself.
Instead of verifying Slack scopes manually, we validate that the UC connection is configured and can authenticate with Slack.

### 3a. Verify the Slack UC Connection exists
### 4a. Verify the Slack UC Connection exists

Run:
```
Expand All @@ -130,7 +183,7 @@ If the connection does not exist, guide the user to create it in the Databricks
4. Auth type: Bearer Token
5. Token: their Slack Bot User OAuth Token (from the Slack app settings)

### 3b. Test the Slack UC Connection
### 4b. Test the Slack UC Connection

Test the connection by calling `auth.test` through the Databricks external-function endpoint.
The Slack client in this codebase uses `POST /api/2.0/external-function` with the connection name.
Expand Down Expand Up @@ -159,15 +212,15 @@ STOP and confirm the test passes before proceeding.

---

## Phase 4 — Secrets Verification
## Phase 5 — Secrets Verification

The app needs these secrets. The secret scope is defined in `databricks.yml` variable `secret_scope` (default: `claw`).

| Secret Key | Purpose |
|---|---|
| `user-pat` | Databricks Personal Access Token — used for AI Gateway auth and UC connection proxy calls |

### 4a. Ensure the secret scope exists
### 5a. Ensure the secret scope exists

```
databricks secrets list-scopes | grep <scope>
Expand All @@ -178,14 +231,14 @@ If the scope does not exist:
databricks secrets create-scope <scope>
```

### 4b. Check each secret exists
### 5b. Check each secret exists

For each secret key, check if it exists (this returns metadata only, NOT the value):
```
databricks secrets list-secrets <scope>
```

### 4c. Set missing secrets
### 5c. Set missing secrets

For any missing secret, instruct the user to run:
```
Expand All @@ -202,9 +255,9 @@ Always use `databricks secrets put-secret` which handles input securely.

---

## Phase 5 — Deploy
## Phase 6 — Deploy

### 5a. Full deploy
### 6a. Full deploy

Run the end-to-end deployment, takes around 5-10 minutes to start when the ap compute is off (install deps, validate bundle, deploy, run migrations, start app):

Expand All @@ -218,18 +271,21 @@ If it fails, read the error output and help the user fix the issue. Common probl
- Migration failure → Lakebase not yet provisioned (retry after a minute)
- Start failure → app configuration issue in `app.yaml`

### 5b. Verify deployment
### 6b. Verify deployment

Check that the app is running:
```
databricks apps get claw-app
Check that the app is running. First resolve the deployed app name from Phase 2 (it will be `<name_prefix>-claw`, e.g. `tanner-claw`):

```bash
APP_NAME=$(cat .databricks/bundle/dev.yml 2>/dev/null | python3 -c "import sys,yaml; d=yaml.safe_load(sys.stdin); print(d['variables']['name_prefix'])" 2>/dev/null || echo "claw")
APP_NAME="${APP_NAME}-claw"
databricks apps get "$APP_NAME"
```

The status should show the app is ACTIVE or RUNNING.

Then tail the logs briefly to confirm startup:
```
databricks apps get-logs claw-app --limit 50
```bash
databricks apps get-logs "$APP_NAME" --limit 50
```

Look for:
Expand All @@ -241,9 +297,9 @@ If there are errors, read them and help the user fix the issue before proceeding

---

## Phase 6 — Smoke Test
## Phase 7 — Smoke Test

### 6a. Identify the DM channel
### 7a. Identify the DM channel

From the deployment logs, the bot should have resolved its own user_id and opened a self-DM channel.
Tell the user:
Expand All @@ -254,12 +310,12 @@ Tell the user:
4. Wait 10-15 seconds (the poller runs every `POLL_INTERVAL_SECONDS`)
5. Claw should reply in a thread

### 6b. Confirm success
### 7b. Confirm success

Ask the user to confirm they received a reply. If they did — setup is complete!

If not, check:
- App logs for errors: `databricks apps get-logs claw-app --limit 100`
- App logs for errors: `databricks apps get-logs "$APP_NAME" --limit 100`
- That the Slack bot is in the workspace
- That the `SLACK_UC_CONNECTION` points to the right Slack bot token
- That the `ANTHROPIC_BASE_URL` and `ANTHROPIC_MODEL` are correct for their AI Gateway
Expand All @@ -270,7 +326,7 @@ If not, check:

When all phases pass, summarize:
- Workspace host
- App name: `claw-app`
- App name: `<name_prefix>-claw` (as configured in Phase 2)
- Slack UC connection name
- Memory volume path
- AI Gateway endpoint
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ __pycache__/
dist/
build/
.env
.databricks/
9 changes: 9 additions & 0 deletions app.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
# Copy this file to app.yaml and fill in your values.
# app.yaml is gitignored since it contains deployment-specific configuration.
#
# Resource names (app, database, volume) are parameterized via the `name_prefix`
# bundle variable in databricks.yml (default: "claw"). To deploy with a custom
# prefix (e.g. to avoid collisions in a shared workspace):
#
# databricks bundle deploy --var name_prefix=tanner
#
# Or run the /initial-setup skill which auto-detects your username and writes
# a local .databricks/bundle/dev.yml override so you never need --var manually.
#
# See https://docs.databricks.com/en/dev-tools/databricks-apps/app-runtime.html

command:
Expand Down
3 changes: 3 additions & 0 deletions databricks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ bundle:
name: claw

variables:
name_prefix:
description: Prefix for all deployed resource names (avoids collisions in shared workspaces)
default: claw
ai_gateway_endpoint:
description: AI Gateway serving endpoint name
default: databricks-claude-sonnet-4-6
Expand Down
128 changes: 128 additions & 0 deletions openspec/changes/name-prefix/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# OpenSpec: Parameterize deployed resource names with name_prefix

Issue: #89

## Current State

Hardcoded resource names that would collide in a shared workspace:

| File | Field | Current Value |
|---|---|---|
| `resources/app.yml` | `apps.claw_app.name` | `claw-app` |
| `resources/lakebase.yml` | `database_instances.claw_db.name` | `claw` |
| `resources/volume.yml` | `volumes.claw_memory.name` | `claw_memory` |
| `scripts/deploy.sh` | `cmd_start` | `databricks bundle run claw_app` (uses YAML key, OK) |
| `scripts/deploy.sh` | `cmd_app_deploy` | `databricks apps deploy claw-app` (hardcoded deployed name) |
| `scripts/deploy.sh` | `cmd_stop` | `databricks apps stop claw-app` (hardcoded deployed name) |
| `SKILL.md` (initial-setup) | Phase 5b | `databricks apps get claw-app` (hardcoded) |

## Changes

### 1. `databricks.yml` — add `name_prefix` variable

Add a new variable `name_prefix` with default `"claw"` to the `variables` block.

```yaml
variables:
name_prefix:
description: Prefix for all deployed resource names (avoids collisions in shared workspaces)
default: claw
```

### 2. `resources/app.yml` — parameterize app name

Change the `name` field (NOT the YAML key `claw_app`):

```yaml
resources:
apps:
claw_app:
name: ${var.name_prefix}-claw
```

### 3. `resources/lakebase.yml` — parameterize database instance name

Change the `name` field (NOT the YAML key `claw_db`):

```yaml
resources:
database_instances:
claw_db:
name: ${var.name_prefix}-claw-db
```

### 4. `resources/volume.yml` — parameterize volume name

Change the `name` field (NOT the YAML key `claw_memory`):

```yaml
resources:
volumes:
claw_memory:
name: ${var.name_prefix}_claw_memory
```

Note: Volume names use underscores (UC naming convention), not hyphens.

### 5. `.gitignore` — add `.databricks/`

Append `.databricks/` so local bundle overrides are not committed.

### 6. `scripts/deploy.sh` — remove hardcoded app names

The `cmd_app_deploy` and `cmd_stop` functions hardcode `claw-app`. Since the app name is now dynamic via `${var.name_prefix}`, these commands must resolve the name from the bundle.

- `cmd_start` already uses `databricks bundle run claw_app` which resolves via the bundle — no change needed.
- `cmd_app_deploy` and `cmd_stop` use `databricks apps deploy/stop claw-app` directly. Add a helper that reads the app name from the bundle output or derives it from the variable.

Approach: use `databricks bundle validate --target "$TARGET" -o json` to extract the resolved app name, or accept an `APP_NAME` env var with fallback to `claw-app`.

Simpler approach: use `databricks bundle run` for start (already done). For `app-deploy` and `stop`, derive the name:

```bash
resolve_app_name() {
databricks bundle validate --target "$TARGET" -o json 2>/dev/null \
| python3 -c "import sys,json; b=json.load(sys.stdin); print(list(b['resources']['apps'].values())[0]['name'])" \
2>/dev/null || echo "claw-app"
}
```

Alternatively, since `deploy.sh` is a convenience script and `bundle run`/`bundle deploy` handle resolution, we can note in comments that `app-deploy` and `stop` assume the default prefix. For now, use the resolve approach.

### 7. `.claude/skills/initial-setup/SKILL.md` — add Personalization phase

Insert a new "Phase 1.5" (renumber as Phase 2, shifting others) after Prerequisites:

Phase: Personalization
1. Run `databricks auth describe` to extract short username (part before @)
2. Run `databricks apps list` to check for `<username>-claw` collision
3. Suggest `name_prefix = <username>` (e.g. `tanner` from `tanner@company.com`)
4. Create `.databricks/bundle/dev.yml` with:
```yaml
variables:
name_prefix: <username>
```
5. Show the user what was written

Also update Phase 5b (now Phase 6b) to use the resolved app name instead of hardcoded `claw-app`.

### 8. `app.example.yml` — add documentation comment

Add a comment at the top documenting the `--var name_prefix=<value>` override.

### 9. `tests/test_dab_bundle.py` — update tests

- Add `test_variable_name_prefix` to `TestDatabricksYml`
- Update any tests in `TestAppYml` or `TestLakebaseYml` that check for hardcoded names (currently none do explicitly, but add tests that verify the `name` fields contain `var.name_prefix`)

## Files Modified

1. `databricks.yml`
2. `resources/app.yml`
3. `resources/lakebase.yml`
4. `resources/volume.yml`
5. `.gitignore`
6. `scripts/deploy.sh`
7. `.claude/skills/initial-setup/SKILL.md`
8. `app.example.yml`
9. `tests/test_dab_bundle.py`
2 changes: 1 addition & 1 deletion resources/app.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
resources:
apps:
claw_app:
name: claw-app
name: ${var.name_prefix}-claw
description: Claw — Slack-native AI assistant powered by Databricks
source_code_path: ..
config: {}
Expand Down
2 changes: 1 addition & 1 deletion resources/lakebase.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
resources:
database_instances:
claw_db:
name: claw
name: ${var.name_prefix}-claw-db
capacity: CU_1
Loading
Loading