|
| 1 | +# Engineer Bot — learning (retrospective) extraction — DAILY CRON. |
| 2 | +# |
| 3 | +# Over an adaptive look-back window the engine gathers merged PRs (diff + review |
| 4 | +# comments) AND recent engineer-bot author-run console logs ITSELF via the GitHub |
| 5 | +# API — no in-workflow context gathering, no per-PR trigger — and if the model |
| 6 | +# finds durable, reusable learnings, opens ONE ROLLING PR on a stable branch |
| 7 | +# (`ai/learning-pr`), appending a dated section per day until a human merges it. |
| 8 | +# Human-gated by design: it NEVER commits the canonical log directly. |
| 9 | +# |
| 10 | +# Own job (NOT `uses: databricks/databricks-bot-engine/...`): an external repo |
| 11 | +# can't resolve the internal engine's reusable workflows ("not found"). It shares |
| 12 | +# the SAME prelude the other bots use — ./.github/actions/bot-prelude (tokens + |
| 13 | +# Node + pinned engine install) — so the engine pin stays single-sourced in |
| 14 | +# bot-prelude's `engine-ref` default (no second SHA to drift). |
| 15 | +# |
| 16 | +# Opt-in is purely via the `retrospective:` block in .bot/config.yaml + this |
| 17 | +# workflow; absent that block the engine phase is a clean no-op. |
| 18 | +name: Engineer Bot — Learning |
| 19 | + |
| 20 | +on: |
| 21 | + schedule: |
| 22 | + # 17:23 UTC daily — off-peak, off-:00 minute (GitHub delays/drops on-the-hour crons). |
| 23 | + - cron: "23 17 * * *" |
| 24 | + workflow_dispatch: |
| 25 | + inputs: |
| 26 | + since: |
| 27 | + description: 'ISO lower bound to shorten the window and recover a wedged flow. Empty = adaptive cursor.' |
| 28 | + type: string |
| 29 | + default: '' |
| 30 | + window-hours: |
| 31 | + # STRING, not number: a `type: number` workflow_dispatch input fails the |
| 32 | + # whole run at startup ("workflow file issue") when combined with the |
| 33 | + # `schedule` trigger. argparse coerces it to int downstream. |
| 34 | + description: 'Fallback look-back window (hours) used only when there is no prior successful run.' |
| 35 | + type: string |
| 36 | + default: '24' |
| 37 | + |
| 38 | +permissions: |
| 39 | + contents: write # push the learning branch / open the learning PR |
| 40 | + pull-requests: write |
| 41 | + actions: read # Track B lists engineer-bot author runs + logs via the App token; |
| 42 | + # the engineer-bot App installation must ALSO carry actions:read |
| 43 | + # (a missing scope surfaces as a 403 that fails the whole run — |
| 44 | + # list_author_runs raises, no escape hatch). |
| 45 | + id-token: write # JFrog OIDC exchange for the engine/SDK/CLI install |
| 46 | + |
| 47 | +concurrency: |
| 48 | + # One learning run at a time; a queued run waits rather than racing the rolling |
| 49 | + # PR's branch. Not keyed on a PR number (this is a cron, no PR event). |
| 50 | + group: engineer-bot-learning-cron |
| 51 | + cancel-in-progress: false |
| 52 | + |
| 53 | +jobs: |
| 54 | + learning: |
| 55 | + environment: azure-prod # DATABRICKS_HOST / DATABRICKS_TOKEN live here |
| 56 | + runs-on: |
| 57 | + group: databricks-protected-runner-group |
| 58 | + labels: [linux-ubuntu-latest] |
| 59 | + timeout-minutes: 20 |
| 60 | + steps: |
| 61 | + # Checkout the default branch (the learning PR is cut from it) FIRST, so the |
| 62 | + # local `./` composites below resolve. persist-credentials:false — the |
| 63 | + # retrospective sets its own authenticated push remote (see the run step), |
| 64 | + # so no token is left in .git/config. |
| 65 | + - name: Checkout default branch (learning PR is cut from it) |
| 66 | + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 |
| 67 | + with: |
| 68 | + fetch-depth: 0 |
| 69 | + persist-credentials: false |
| 70 | + |
| 71 | + # No JFrog setup step here: install-bot-engine (via bot-prelude) does its own |
| 72 | + # keyless OIDC→JFrog mint and passes the credential through job-local files + |
| 73 | + # per-command --index-url flags. It deliberately never reads PIP_INDEX_URL / |
| 74 | + # JFROG_ACCESS_TOKEN from the environment, so a setup-jfrog step would be both |
| 75 | + # redundant AND a credential leak (setup-jfrog exports a token-bearing |
| 76 | + # PIP_INDEX_URL to $GITHUB_ENV, exposing it to every later step — including the |
| 77 | + # one that runs the model). Mirrors the read-only sibling reviewer-bot.yml. |
| 78 | + - name: Setup Python |
| 79 | + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 |
| 80 | + with: |
| 81 | + python-version: '3.11' |
| 82 | + |
| 83 | + # Shared prelude: mint the engineer-bot token (opens the learning PR) + the |
| 84 | + # engine-scoped token, set up Node, install the pinned engine (PAT-free). The |
| 85 | + # engine pin comes from bot-prelude's `engine-ref` default — the SINGLE source |
| 86 | + # of truth for every bot; there is no second SHA in this file to drift. |
| 87 | + - name: Bot prelude (tokens + Node + engine install) |
| 88 | + id: prelude |
| 89 | + uses: ./.github/actions/bot-prelude |
| 90 | + with: |
| 91 | + app-id: ${{ secrets.ENGINEER_BOT_APP_ID }} |
| 92 | + private-key: ${{ secrets.ENGINEER_BOT_APP_PRIVATE_KEY }} |
| 93 | + |
| 94 | + # NOTE: no git-identity step — the engine's retrospective configures the git |
| 95 | + # user AND DCO sign-off itself from .bot/config.yaml `bot_login_prefix`. |
| 96 | + # NOTE: no context-gather step — the daily-cron engine enumerates merged PRs |
| 97 | + # + author runs itself over the adaptive window. |
| 98 | + - name: Extract learnings + open rolling PR |
| 99 | + env: |
| 100 | + GH_TOKEN: ${{ steps.prelude.outputs.token }} |
| 101 | + GITHUB_REPOSITORY: ${{ github.repository }} |
| 102 | + # Only the `<workspace>/serving-endpoints/` prefix matters: |
| 103 | + # sdk_agent.translate_endpoint rewrites this to `.../serving-endpoints/anthropic` |
| 104 | + # and discards the model path segment. The effective model comes from |
| 105 | + # .bot/config.yaml `retrospective.model` (or the engine default). |
| 106 | + MODEL_ENDPOINT: https://${{ secrets.DATABRICKS_HOST }}/serving-endpoints/anthropic/invocations |
| 107 | + DATABRICKS_TOKEN: ${{ secrets.DATABRICKS_TOKEN }} |
| 108 | + RUNNER_TEMP: ${{ runner.temp }} |
| 109 | + SINCE: ${{ inputs.since }} |
| 110 | + WINDOW_HOURS: ${{ inputs.window-hours }} |
| 111 | + # The retrospective pushes the learning branch with a plain `git push |
| 112 | + # origin`, and the checkout ran persist-credentials:false — so set an |
| 113 | + # authenticated push remote from the minted App token first, mirroring |
| 114 | + # engineer-bot.yml's publish step. --since / --window-hours are passed only |
| 115 | + # when provided via workflow_dispatch (the schedule trigger leaves them |
| 116 | + # empty → the adaptive cursor drives the window). |
| 117 | + run: | |
| 118 | + git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" |
| 119 | + args=(--repo-dir "$GITHUB_WORKSPACE") |
| 120 | + [ -n "$SINCE" ] && args+=(--since "$SINCE") |
| 121 | + [ -n "$WINDOW_HOURS" ] && args+=(--window-hours "$WINDOW_HOURS") |
| 122 | + python -m databricks_bot_engine.engineer_bot.retrospective "${args[@]}" |
0 commit comments