From 76683f74e00c4cbe0c6ac93515fd86a74e5da5b2 Mon Sep 17 00:00:00 2001 From: Tanner Date: Tue, 31 Mar 2026 21:37:31 -0400 Subject: [PATCH] =?UTF-8?q?chore:=20pre-public=20cleanup=20=E2=80=94=20fix?= =?UTF-8?q?=20README,=20remove=20Cowork=20refs,=20sanitize=20examples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix README project structure to match actual repo layout (#94) - Remove references to non-existent files: system_prompt.py, slack_events.py, .env.example (#94) - Remove "Key differences from Cowork" section from README (#94) - Remove all Cowork comparisons from design.md (#95) - Merge unresolved questions into resolved (Socket Mode → polling, OAuth scopes → UC connection) (#95) - Remove Cowork reference from plan-parallel skill (#95) - Replace personal name_prefix example with generic placeholder in app.example.yml (#96) Closes #94, closes #95, closes #96 Co-Authored-By: Claude Sonnet 4.6 --- .claude/skills/plan-parallel/SKILL.md | 2 +- README.md | 29 ++++++++++------------- app.example.yml | 2 +- design.md | 34 ++++++++++++--------------- 4 files changed, 30 insertions(+), 37 deletions(-) diff --git a/.claude/skills/plan-parallel/SKILL.md b/.claude/skills/plan-parallel/SKILL.md index fbf39eb..f4f61a4 100644 --- a/.claude/skills/plan-parallel/SKILL.md +++ b/.claude/skills/plan-parallel/SKILL.md @@ -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 diff --git a/README.md b/README.md index f1bf664..c0c9976 100644 --- a/README.md +++ b/README.md @@ -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// ← 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 ``` @@ -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 @@ -99,17 +98,23 @@ 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 @@ -117,18 +122,10 @@ databricks-claw/ ├── 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 | diff --git a/app.example.yml b/app.example.yml index fde654f..f2143de 100644 --- a/app.example.yml +++ b/app.example.yml @@ -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. diff --git a/design.md b/design.md index 53df975..1cf57d0 100644 --- a/design.md +++ b/design.md @@ -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}" @@ -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 @@ -163,7 +163,7 @@ 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 @@ -171,7 +171,7 @@ Initial tables: --- -## UC Volume: Same as Cowork +## UC Volume **Resource** (`resources/volume.yml`): ```yaml @@ -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 @@ -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 @@ -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] @@ -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.