|
| 1 | +# Onboard databricks-sql-python to the pinned databricks-bot-engine |
| 2 | + |
| 3 | +**Date:** 2026-07-14 |
| 4 | +**Status:** Approved (pending spec review) |
| 5 | +**Repo:** `databricks/databricks-sql-python` |
| 6 | + |
| 7 | +## Problem |
| 8 | + |
| 9 | +The repo currently runs the PR **reviewer bot** from *vendored* code under |
| 10 | +`scripts/reviewer_bot/` + `scripts/shared/`, invoked as |
| 11 | +`python -m scripts.reviewer_bot.*`. The upstream `databricks/databricks-bot-engine` |
| 12 | +is the documented source of truth: consumers `pip install` it pinned to an |
| 13 | +immutable `engine-ref` and call its **reusable workflows**, rather than copying |
| 14 | +its code. The vendored copy has already drifted from the engine and will not pick |
| 15 | +up engine fixes without hand-resync. |
| 16 | + |
| 17 | +This work converts the repo to the documented consumer model, matching the |
| 18 | +existing `databricks/databricks-driver-test` consumer. |
| 19 | + |
| 20 | +## Goal |
| 21 | + |
| 22 | +- Reviewer bot (initial review + follow-up) runs from the pinned engine via its |
| 23 | + reusable workflows, installed in REF mode. |
| 24 | +- All vendored bot code removed; engine is the single source of truth. |
| 25 | +- Trigger policy unchanged: every non-fork PR (no label gate). |
| 26 | +- Repo-specific review guidance preserved as engine *additive* prompt. |
| 27 | + |
| 28 | +Non-goals: adopting the **engineer bot** (bug-fix / coverage). Only the reviewer |
| 29 | +is in scope, matching what the repo runs today. |
| 30 | + |
| 31 | +## Engine pin |
| 32 | + |
| 33 | +- **Engine SHA:** `d780b2da60bb1ac68bb5cd1acb7cabf495b3ff2d` (current |
| 34 | + `databricks-bot-engine` `main` HEAD). |
| 35 | +- The SHA is used in BOTH the `uses:` ref AND the `engine-ref:` input of each |
| 36 | + caller, so the workflow definition and the installed engine match. Never |
| 37 | + `@main` (force-pushable; the job carries secrets). |
| 38 | + |
| 39 | +## Design |
| 40 | + |
| 41 | +### 1. Workflows — thin callers of the engine reusables |
| 42 | + |
| 43 | +**`.github/workflows/reviewer-bot.yml`** (replaces the current hand-rolled job): |
| 44 | +```yaml |
| 45 | +name: Reviewer Bot |
| 46 | +on: |
| 47 | + pull_request: |
| 48 | + types: [opened, synchronize, reopened, ready_for_review] |
| 49 | + workflow_dispatch: |
| 50 | + inputs: |
| 51 | + pr_number: { description: 'PR number to review', required: true, type: string } |
| 52 | + dry_run: { description: 'Print instead of posting', required: false, default: 'true', type: string } |
| 53 | +permissions: |
| 54 | + contents: read |
| 55 | + pull-requests: write |
| 56 | + id-token: write |
| 57 | +jobs: |
| 58 | + review: |
| 59 | + uses: databricks/databricks-bot-engine/.github/workflows/reviewer-bot.reusable.yml@d780b2da60bb1ac68bb5cd1acb7cabf495b3ff2d |
| 60 | + with: |
| 61 | + engine-ref: d780b2da60bb1ac68bb5cd1acb7cabf495b3ff2d |
| 62 | + pr-number: ${{ inputs.pr_number }} |
| 63 | + dry-run: ${{ inputs.dry_run }} |
| 64 | + secrets: |
| 65 | + review-bot-app-id: ${{ secrets.REVIEW_BOT_APP_ID }} |
| 66 | + review-bot-app-private-key: ${{ secrets.REVIEW_BOT_APP_PRIVATE_KEY }} |
| 67 | + databricks-token: ${{ secrets.DATABRICKS_TOKEN }} |
| 68 | + databricks-host: ${{ secrets.DATABRICKS_HOST }} |
| 69 | + engine-pat: ${{ secrets.BOT_ENGINE_PAT }} |
| 70 | +``` |
| 71 | +
|
| 72 | +**`.github/workflows/reviewer-bot-followup.yml`** (replaces the current job): |
| 73 | +```yaml |
| 74 | +name: Reviewer Bot — Follow-up |
| 75 | +on: |
| 76 | + pull_request_review_comment: { types: [created] } |
| 77 | + pull_request: { types: [synchronize] } |
| 78 | +permissions: |
| 79 | + contents: read |
| 80 | + pull-requests: write |
| 81 | + id-token: write |
| 82 | +jobs: |
| 83 | + followup: |
| 84 | + # No enablement `if:` — keep today's "every non-fork PR" policy. The engine |
| 85 | + # reusable's job `if:` still enforces the security floor (fork==false, PR |
| 86 | + # open) and loop-prevention lives in the engine's followup.py. |
| 87 | + uses: databricks/databricks-bot-engine/.github/workflows/reviewer-bot-followup.reusable.yml@d780b2da60bb1ac68bb5cd1acb7cabf495b3ff2d |
| 88 | + with: |
| 89 | + engine-ref: d780b2da60bb1ac68bb5cd1acb7cabf495b3ff2d |
| 90 | + secrets: |
| 91 | + review-bot-app-id: ${{ secrets.REVIEW_BOT_APP_ID }} |
| 92 | + review-bot-app-private-key: ${{ secrets.REVIEW_BOT_APP_PRIVATE_KEY }} |
| 93 | + databricks-token: ${{ secrets.DATABRICKS_TOKEN }} |
| 94 | + databricks-host: ${{ secrets.DATABRICKS_HOST }} |
| 95 | + engine-pat: ${{ secrets.BOT_ENGINE_PAT }} |
| 96 | +``` |
| 97 | +
|
| 98 | +The engine reusables own: the fork/open-PR security gate (job `if:`), App-token |
| 99 | +mint, Python setup, JFrog-OIDC engine install, `MODEL_ENDPOINT` construction |
| 100 | +(`https://$DATABRICKS_HOST/serving-endpoints/databricks-claude-opus-4-8/invocations`), |
| 101 | +`workflow_dispatch` input validation, and (for `reviewer-bot`) the PR-head |
| 102 | +content-root dispatch security. So the ~250 lines of hand-rolled gate / checkout / |
| 103 | +pre-filter logic in the current workflows are removed. |
| 104 | + |
| 105 | +### 2. `.bot/prompts/review/system.md` — repo-specific additive prompt |
| 106 | + |
| 107 | +Verified against the engine source at the pinned SHA |
| 108 | +(`reviewer_bot/run_review.py::_review_system_prompt`): the reviewer's **base** |
| 109 | +system prompt is **engine-owned** (`SYSTEM_PROMPT` — it owns the output contract, |
| 110 | +severity scale, anchoring, and dedup rules). The consumer file is read from the |
| 111 | +default path `.bot/prompts/review/system.md` and **appended** as *additive* repo |
| 112 | +guidance via `append_repo_guidance`. It is **optional** at the default path |
| 113 | +(missing ⇒ engine base alone — no abort). It is read from the TRUSTED checkout |
| 114 | +(REPO_ROOT), never the PR-head tree (fork prompt-injection guard). |
| 115 | + |
| 116 | +> Note: the engine README describes this file as "the reviewer's *entire* system |
| 117 | +> prompt (absent ⇒ runtime abort)." The **code** at this SHA disagrees — it is |
| 118 | +> additive and optional. We follow the code. |
| 119 | + |
| 120 | +We port only the *repo-specific* parts of the current vendored `SYSTEM_PROMPT` |
| 121 | +and drop the engine-owned parts (severity scheme, anchor rules, output structure, |
| 122 | +dedup, `finalize_review` contract), which the base already owns. Kept: |
| 123 | +- "senior code reviewer for databricks-sql-python (the Databricks SQL connector |
| 124 | + for Python)" framing + the review axes tuned for this repo. |
| 125 | +- Landmarks: `CONTRIBUTING.md` (PEP 8, 100-char limit, DCO sign-off), `README.md`, |
| 126 | + connector package under `src/databricks/`, tests under `tests/unit` (fast, |
| 127 | + mocked) + `tests/e2e` (warehouse integration). |
| 128 | + |
| 129 | +No `config.yaml`, engineer prompts, or `context-repos.yaml` — none are read by the |
| 130 | +reviewer path. |
| 131 | + |
| 132 | +### 3. Deletions (vendored removal) |
| 133 | + |
| 134 | +Delete: |
| 135 | +- `scripts/reviewer_bot/` (runtime + tests) |
| 136 | +- `scripts/shared/` (runtime + tests) |
| 137 | +- `scripts/__init__.py` (only present to package the vendored bot; `dependency_manager.py` runs as a path script) |
| 138 | +- `scripts/requirements-sdk.txt` |
| 139 | +- `.github/actions/setup-claude-sdk/` |
| 140 | +- `.github/workflows/sdk-smoke.yml` (the reviewer-bot *foundation* smoke test; the only consumer of `setup-claude-sdk`) |
| 141 | +- `.github/workflows/reviewer-bot-unit-tests.yml` (its tests import the deleted `scripts.reviewer_bot` / `scripts.shared`) |
| 142 | + |
| 143 | +**Keep** (used by unrelated CI — verified via grep): |
| 144 | +- `.github/actions/setup-jfrog/` — used by `kernel-e2e.yml` and `setup-poetry`. |
| 145 | +- `scripts/dependency_manager.py` — used by `code-quality-checks.yml`. |
| 146 | + |
| 147 | +### 4. Secrets (provisioned by the maintainer, documented in the PR) |
| 148 | + |
| 149 | +| Secret | Status | Purpose | |
| 150 | +|---|---|---| |
| 151 | +| `REVIEW_BOT_APP_ID` | exists | mint bot App token | |
| 152 | +| `REVIEW_BOT_APP_PRIVATE_KEY` | exists | " | |
| 153 | +| `DATABRICKS_TOKEN` | exists | model auth | |
| 154 | +| `DATABRICKS_HOST` | **NEW** — replaces `MODEL_ENDPOINT` | engine builds `MODEL_ENDPOINT` from it | |
| 155 | +| `BOT_ENGINE_PAT` | **NEW** | read access to the private engine repo (REF-mode install) | |
| 156 | + |
| 157 | +The old `MODEL_ENDPOINT` secret is no longer consumed after this change. |
| 158 | + |
| 159 | +## Testing / verification |
| 160 | + |
| 161 | +- Static: `actionlint` on the new workflow YAML if available, else a YAML parse |
| 162 | + check; confirm no dangling references to deleted paths remain (grep). |
| 163 | +- Runtime: the reusable `uses:` against the **private** engine repo cannot fully |
| 164 | + resolve in CI until `BOT_ENGINE_PAT` + `DATABRICKS_HOST` are added as secrets. |
| 165 | + End-to-end verification (a real PR review) happens after secrets are |
| 166 | + provisioned. This limitation is stated plainly in the PR description — the PR |
| 167 | + is not claimed "verified working end-to-end." |
| 168 | + |
| 169 | +## Rollout |
| 170 | + |
| 171 | +1. New branch off `main`; commit workflows + `.bot/` + deletions. |
| 172 | +2. Open PR to `databricks/databricks-sql-python` with the secrets checklist. |
| 173 | +3. Maintainer adds `DATABRICKS_HOST` + `BOT_ENGINE_PAT`. |
| 174 | +4. Merge; first non-fork PR exercises the reviewer end-to-end. |
| 175 | +5. Future engine updates: bump the SHA in both `uses:` and `engine-ref:` in |
| 176 | + lockstep across both workflows. |
| 177 | + |
| 178 | +## Risks |
| 179 | + |
| 180 | +- **Private-engine install:** REF mode fails without `BOT_ENGINE_PAT`; the engine |
| 181 | + action fails fast with a clear error. Documented. |
| 182 | +- **README vs. code drift** on the review prompt semantics: resolved by following |
| 183 | + the code (additive/optional), recorded above. |
| 184 | +- **Reusable `uses: ./…` cross-repo resolution:** the engine's reusables use local |
| 185 | + `./.github/actions/...` refs that resolve against the engine repo at the called |
| 186 | + ref (verified in the engine's own dogfood). No consumer action needed. |
0 commit comments