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
2 changes: 1 addition & 1 deletion .claude/skills/plan-parallel/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Phase 3 (parallel): issues whose dependencies are all in Phase 1-2
### 4. Estimate complexity per issue

For each issue, classify:
- **S** (small): ≤2 new files, mostly boilerplate/port from Cowork
- **S** (small): ≤2 new files, mostly boilerplate
- **M** (medium): 3-5 files, moderate logic
- **L** (large): 6+ files, significant new logic

Expand Down
29 changes: 13 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ Edit the source. No admin panel needed — it's your deployment.
```
claw/CLAUDE.md ← personality, tone, formatting rules, instructions
.claude/skills/<name>/ ← add skills (invocable via slash commands like /skill-name)
claw/core/system_prompt.py ← tune what context Claude sees on every turn
databricks.yml ← workspace, AI Gateway endpoint, UC connection names
```

Expand All @@ -68,7 +67,7 @@ databricks.yml ← workspace, AI Gateway endpoint, UC connection na

### Configure

1. Copy `.env.example` to `.env` and fill in values (or set DAB variables)
1. Copy `app.example.yml` to `app.yaml` and fill in your values
2. Add secrets to your workspace:
```bash
databricks secrets put-secret claw claw-client-id --string-value <your-client-id>
Expand Down Expand Up @@ -99,36 +98,34 @@ Push source code changes without redeploying infrastructure.
databricks-claw/
├── claw/
│ ├── main.py # FastAPI app entry point
│ ├── CLAUDE.md # ← edit this
│ ├── CLAUDE.md # ← edit this (personality, instructions)
│ ├── models.py # SQLModel tables (Session, Message)
│ └── core/
│ ├── agent.py # Claude Agent SDK loop
│ ├── auth.py # M2M token generation
│ ├── config.py # AppConfig
│ ├── db.py # Lakebase engine + token refresh
│ ├── mcp_mapper.py # UC connections → SDK MCP configs
│ ├── memory.py # MEMORY.md on UC Volume
│ ├── memory_mcp.py # Memory MCP server
│ ├── sessions.py # thread_ts → sdk_session_id
│ ├── slack_client.py # Slack API via UC connection proxy
│ └── slack_events.py # Event verification + dispatch
│ └── slack_poller.py # Slack polling + dispatch
├── .claude/skills/ # Local skills (plugins)
├── alembic/ # Alembic migrations
│ ├── env.py
│ └── versions/
├── resources/
│ ├── app.yml # Databricks App resource
│ ├── lakebase.yml # Lakebase CU_1 instance
│ └── volume.yml # UC Volume for memory files
├── scripts/
│ ├── deploy.sh # Full e2e deploy
│ └── migrate.py # Grant SP perms + Alembic
├── tests/ # Unit tests
├── alembic.ini
├── databricks.yml # DAB bundle
└── app.yaml # App runtime config
├── app.yaml # App runtime config (from app.example.yml)
└── pyproject.toml
```

## Key differences from Cowork

| | Cowork | databricks-claw |
|---|---|---|
| UI | React web app | Slack |
| Tenancy | Multi-user | Single user |
| Auth | Per-request OBO | M2M (Client ID + Secret) |
| Slack | Not integrated | Primary message bus |
| Admin panel | Yes | No — edit source code |
| Session storage | SQLite + SDK session IDs | Lakebase (same) |
| Memory | None | MEMORY.md on UC Volume |
2 changes: 1 addition & 1 deletion app.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# 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
# databricks bundle deploy --var name_prefix=yourname
#
# 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.
Expand Down
34 changes: 15 additions & 19 deletions design.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Ignore rules:

### Sending: UC Connection HTTP Proxy

Responses are sent back to Slack via the UC connection proxy — same pattern as Cowork's MCP server wiring but used as a direct HTTP client, not as an MCP server for Claude:
Responses are sent back to Slack via the UC connection proxy — used as a direct HTTP client, not as an MCP server for Claude:

```python
url = f"{workspace_host}/api/2.0/mcp/external/{config.slack_uc_connection}"
Expand Down Expand Up @@ -142,7 +142,7 @@ The agent also manages typed memory files in `/Volumes/{catalog}/{schema}/claw-m

---

## Lakebase: Same as Cowork
## Lakebase

**Resource** (`resources/lakebase.yml`):
```yaml
Expand All @@ -163,15 +163,15 @@ resources:
permission: CAN_CONNECT_AND_CREATE
```

**Alembic from day 1** — same `env.py` / `alembic.ini` / `scripts/migrate.py` pattern as Cowork. The deploy script runs migrations before starting the app.
**Alembic from day 1** — `env.py` / `alembic.ini` / `scripts/migrate.py`. The deploy script runs migrations before starting the app.

Initial tables:
- `sessions` — thread_ts → sdk_session_id map
- `messages` (optional) — conversation history log for debugging

---

## UC Volume: Same as Cowork
## UC Volume

**Resource** (`resources/volume.yml`):
```yaml
Expand Down Expand Up @@ -203,18 +203,15 @@ databricks-claw/
│ ├── CLAUDE.md # Personality + instructions (edit this)
│ ├── core/
│ │ ├── agent.py # stream_chat() — claude_agent_sdk.query() loop
│ │ ├── auth.py # M2M token generation
│ │ ├── config.py # AppConfig — env vars, secrets
│ │ ├── db.py # Lakebase engine (copied from Cowork)
│ │ ├── db.py # Lakebase engine + token refresh
│ │ ├── mcp_mapper.py # UC connection → McpHttpServerConfig
│ │ ├── memory.py # MEMORY.md read/write on UC Volume
│ │ ├── memory_mcp.py # Memory MCP server
│ │ ├── sessions.py # thread_ts → sdk_session_id CRUD
│ │ ├── slack_client.py # Slack API calls via UC connection proxy
│ │ └── slack_events.py # Event verification + dispatch
│ ├── alembic/
│ │ ├── env.py
│ │ ├── script.py.mako
│ │ └── versions/
│ ├── alembic.ini
│ │ └── slack_poller.py # Slack polling + dispatch
│ └── models.py # SQLModel tables (Session, Message)
├── .claude/
│ └── skills/ # Skills loaded as plugins into every session
Expand All @@ -224,7 +221,9 @@ databricks-claw/
│ └── volume.yml # UC Volume for memory
├── scripts/
│ ├── deploy.sh # Full e2e: build → validate → deploy → migrate → start → app-deploy
│ └── migrate.py # Grant SP perms + run Alembic (same as Cowork)
│ └── migrate.py # Grant SP perms + run Alembic
├── tests/ # Unit tests
├── alembic.ini
├── databricks.yml # DAB bundle — dev + prod targets + variables
├── app.yaml # Databricks App runtime config
└── pyproject.toml
Expand Down Expand Up @@ -262,7 +261,7 @@ targets:

## Deploy Script (e2e)

Same command structure as Cowork's `scripts/deploy.sh`:
Full e2e deploy via `scripts/deploy.sh`:

```
bash scripts/deploy.sh full [--target dev|prod]
Expand Down Expand Up @@ -303,14 +302,11 @@ user_api_scopes:

---

## Open Questions (resolved)
## Open Questions (all resolved)

- ~~Slack MCP or dedicated connector?~~ → Dedicated connector. Slack is transport, not a Claude tool.
- ~~OBO or M2M?~~ → M2M with user's Client ID + Secret stored as Databricks Secrets.
- ~~Memory or stateless?~~ → Both layers: SDK session IDs in Lakebase + MEMORY.md on UC Volume.
- ~~Streaming to Slack?~~ → Post final response. Can layer in edit-as-streaming later.

## Open Questions (unresolved)

- **Socket Mode vs Events API**: Socket Mode (WebSocket, no public URL) vs Events API (webhook). Socket Mode is simpler for Databricks Apps since no public URL routing needed — but adds a persistent WebSocket connection. Events API requires the app URL to be registered in Slack. Decision needed before implementing `main.py`.
- **Slack App OAuth scopes**: `chat:write`, `channels:read`, `im:write`, `app_mentions:read` at minimum. Full list depends on what Slack UC connection exposes.
- ~~Socket Mode vs Events API?~~ → Neither. Uses polling (`slack_poller.py`) — background asyncio task polling `conversations.history`. No public URL or persistent WebSocket needed.
- ~~Slack App OAuth scopes?~~ → Handled by UC connection configuration. The app never holds a Slack bot token directly.
Loading