docs(repo): propose one Linear release pipeline for all bots - #220
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
Devin Review found 1 potential issue.
2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
075ef5f to
8e71bbc
Compare
Size
Changed files at head: 0 code, 276 comments, 73 blanks. |
Drive-by cleanup found while writing #220. ## Why The repo is `morpho-org/morpho-bots`, `package.json#name` is `@morpho-org/morpho-bots`, and the Linear team is `Bots`. It also holds a market maker now alongside the liquidation and reallocation bots, so "curator bots" is stale as a name *and* as a description of what is in here. `services/quoter-signer` was deleted in #214; only untracked build residue remains on disk. ## What Seven one-line edits across `README.md`, `AGENTS.md`, `docs/CONVENTIONS.md`, `.claude/agents/product-manager.md`, `.claude/commands/create-issue.md`, and `packages/ci-scripts/README.md`. ## Deliberately left alone - **`packages/swaps/src/constants.ts`** — `LIFI_INTEGRATOR = 'morpho-curator-bots'` is a wire value sent to LiFi on every quote, not prose. Changing it changes outbound attribution and needs coordination on their side; a venue that 4xxs on a bad param reads as `no_route` and sidelines itself silently. - **`docs/decisions/**`** — TIBs are immutable once accepted, and `TIB-2026-04-16-bootstrap-curator-bots.md` is a link target from `README.md`, `docs/INDEX.md`, and the kill-switch TIB. - **`docs/retros/README.md`** — its example filename names that historical artifact, not the repo. No ticket: this ships as it was found rather than being deferred. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Production trails main per bot and only git records the gap. Wire deploys into Linear Releases so an issue shows which release shipped it. One shared continuous pipeline rather than one per bot: --include-paths and --base-ref are per-invocation CLI flags, so per-bot attribution is a CI concern. Filters come from the workspace dependency graph, which narrows the ambiguity to bot-kit and utils. Records why per-bot pipelines, a staging pipeline, the org's catch-all pipeline, and a linked-issue status check were not adopted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Surfaced while implementing: .github, ci-scripts, and docs changes fall outside every bot's path filter, so those issues never attach to a release. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Path filters gate commits; issues come from the PR number on the commit, so a PR spanning two bots attributes all its issues to whichever releases first. No filter fixes that. Also records root build inputs as every bot's code, and the root-commit base for a bot with no predecessor tag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8e71bbc to
a6992af
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 075ef5f344
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Ambiguity is not limited to the all-four packages: swaps and contracts have multiple consumers too. The scan base is the newest ancestor tag, not the newest tag, because Release is not serialized and reruns exist. Issue linkage is a convention until the review instruction is actually written. Credential recovery is bounded only while no status automation is wired. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Reviewed the current documentation head as the base of the #220 -> #221 stack, including BOTS-140 and the existing review discussions.
The revised decision records the load-bearing limitations: path filtering admits whole commits/PRs rather than individual linked issues; multi-consumer packages remain ambiguous; issue-link enforcement is not implemented here; and recovery must account for downstream state changes if release-completion automation is added later. The ancestor-based scan policy is now consistent with the implementation reviewed in #221. The broader deployment ordering problem remains BOTS-128, not additional scope for this stack.
Checks run locally on the combined stack: changed-file formatting and git diff --check passed. The documentation bytes in the child match this head. GitHub's current Test job was still running at the last check, so this is a code/design approval, not a full-CI or deployment-completion claim.
Nonblocking wording nit: the PR description still says ambiguity narrows to only bot-kit/utils; the corrected TIB is broader. Update that summary when convenient.
|
Approved this reviewed head: #220 (review). Stack implementation and execution evidence are in the #221 approval; wording note in the review is nonblocking. Full CI remains incomplete at this review snapshot: Test jobs are still running. No merge performed. |
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Fixed the wording nit — the description said ambiguity narrows to The scan-base bullet also moved from "newest ancestor tag" to "nearest reachable tag", matching the |
Applies the Microsoft Writing Style Guide: shorter sentences carrying one idea, active voice, contractions, and second person where the text gives guidance. Cuts em-dash asides from twenty to four and spells out numbers under ten. No decision content changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Implements BOTS-140. Decision record: #220 (TIB-2026-09-14). ## Why "Merged" and "live" are different facts here, and only the second is recorded — in Railway and in `<bot>-<PR#>` tags. During an incident the question is per bot: is this fix running? This records each production deploy as a Linear release, so the issue itself carries the answer. ## What A `Linear` job in `deploy-bot.yml`, after `Release`, syncing one release per production deploy into the `Bots` pipeline. Per-bot attribution inside a single shared pipeline comes from two per-invocation flags: - `base_ref` — that bot's previous `<bot>-*` tag. `prev` was already computed for the GitHub release notes; it moves above the idempotency branch and excludes the current tag, so a rerun emits it too. - `include_paths` — a new `paths` field on `packages/ci-scripts/manifest.json`, holding the bot's own directory plus every workspace package it depends on, transitively. It reaches the workflow as the `linear_paths` matrix leg, alongside `publish_image`. A test asserts each bot's `paths` against the real dependency graph, walking `package.json` files. That is the guard that matters: a bot that gains a workspace dependency without gaining the path keeps producing releases, just missing the work it shipped — a failure that looks like nothing at all. Verified it fails by removing `packages/swaps/**` from `blue-liq` and watching it go red. ## Safety Not funds-at-risk — it touches no calldata, signing, or sizing, and runs strictly after a deploy and tag that already succeeded. - The job runs at `permissions: contents: read` and `continue-on-error: true`. The action fetches and executes a prebuilt CLI at runtime, which is why it is not in `Release` alongside `contents: write` and `GH_TOKEN`, and why a Linear outage cannot break the chain `publish-quoter-bot-npm.yml` keys off the `<bot> / Release` job name for. - `LINEAR_ACCESS_KEY` lives in the `linear-release` GitHub Environment with a `main`-only deployment branch policy, not as a repo secret — `checks.yml` runs on `pull_request`, so repo-level secrets are reachable from a branch PR that edits a workflow. - The action is pinned by commit SHA and `cli_version` pinned explicitly. - A guard step fails the job on an empty `linear_paths`, because an unfiltered sync would attribute every bot's commits to this bot's release. - The pipeline key is scoped to one pipeline; it cannot reach code, Railway, signing keys, or funds. ## Verified - `pnpm --filter @repo/ci-scripts run typecheck`, `pnpm lint`, `pnpm format:check`, `pnpm knip` — all clean. - `pnpm test` — 2952 passed. The 4 failing files are the fork/e2e suites that need `RPC_URL_8453`, which is absent locally and unrelated to this diff. - `bots/quoter-bot/test/scripts/railway.utils.test.ts` passes unchanged. It slices `deploy-bot.yml` by job markers, which is why the new job sits between `Notify:` and `Image:` — both slices stay byte-identical. ## Rollout Nothing to stage: the first real release is the test. A wrong or empty release row is deletable in Linear, no status automation fires from it, and the job fails open, so there is nothing to protect with a dry run — while a temporary `dry_run: true` that outlives its revert would leave the sync silently writing nothing, which looks exactly like working. Backfill is expected and wanted on the first sync per bot: `prev` is still a CalVer tag, so quoter-bot's first release sweeps everything since `quoter-bot-2026.08.31-1` — commits that genuinely have not reached production. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why
Production trails
mainper bot, and nothing outside git records the gap. Since the release-intent gate landed (#210), a merge redeploys every staging bot but deploys to production only the bot named in an approvedReleases <bot>line — so "merged" and "live" are different facts, and only the second is recorded. quoter-bot cut nine releases in the three weeks to 2026-08-31 and none since; blue-liq has cut one in two months.What
A TIB proposing that production deploys sync into a single continuous Linear release pipeline named
Bots, so an issue shows which release shipped it and for which bot.The load-bearing choice is one shared pipeline rather than one per bot.
linear-release's--include-pathsand--base-refare per-invocation CLI flags, so per-bot attribution is a CI concern, not a pipeline concern — which matters because the workspace is capped at 15 pipelines with 5 already used, and per-bot would cost 4 now and 7 once the vault bots and kill-switch onboard. Path filters come from the workspace dependency graph rather than a blanketpackages/wildcard, which narrows cross-bot ambiguity without removing it — every package with more than one consumer still matches several filters. The deeper limit, which review sharpened and the TIB now states, is that attribution is per pull request rather than per path: a filter admits a commit, and the issues then come from the PR on that commit, so any PR spanning two bots attributes all of its issues to whichever releases first.Records why a staging pipeline, the org's catch-all pipeline, a linked-issue status check, and deriving filters in code were all rejected.
Docs only — no behavior change. The implementation lands separately under BOTS-140.
Notes for the reviewer
Releases <bot>is already reserved in this repo as the deploy keyword; the TIB opens by separating that from Linear Releases, the tracker feature. Worth checking that the distinction reads clearly.Botsexists, and thelinear-releaseGitHub Environment holdsLINEAR_ACCESS_KEYbehind amain-only branch policy.Merged→Doneon release) is deferred, not rejected — it is Linear dashboard config and needs no repo change. Recorded as an Open Question.Closes BOTS-140 only once the implementation lands; this PR is the decision record.
🤖 Generated with Claude Code