Skip to content

stewards, github-watcher: a deployment is re-read after a non-final status; a merged watch is re-matched (BB-286) - #315

Merged
trotterdylan merged 2 commits into
bb/bb-285-open-pr-is-not-a-wake-source-gate-met-pr-thr_2dt2h2aap5from
bb/bb-286-deployment-re-read-ring-on-connect-live-r-thr_igfqusktzu
Nov 10, 2026
Merged

trotterdylan merged 2 commits into
bb/bb-285-open-pr-is-not-a-wake-source-gate-met-pr-thr_2dt2h2aap5from
bb/bb-286-deployment-re-read-ring-on-connect-live-r-thr_igfqusktzu

Conversation

@trotterdylan

Copy link
Copy Markdown
Collaborator

Implements BB-286. No deployment doorbell is load-bearing, and a merged watch follows the current playbook. The doorbell and read paths schedule a re-read after a non-final status; github-watcher's reconnect sweep rings stewards per deployment repo; the deploy debounce keys on deployment id plus state; and recordMerged re-matches an existing merged/deploying watch against the current rule, settling a now-live-at-merge watch with no deployment read.

Stacked on #314 (BB-285): branch cut from its head, PR base is its branch. Do not merge before it.

Merging to bb-plugins main moves the plugin pin, builds a new bb-server image and stages a restart for every developer's server.

Classification

owner-reviewed: plugins/stewards/server.ts is the plumbing entry (bigDecisions owner trigger).

What changes

  • plugins/stewards/server.ts
    • The non-final branches of the deployment doorbell path and the BB-59 read path call scheduleReconcile, so a dropped final doorbell is caught within the existing debounce rather than the half-hourly sweep.
    • The deploy: debounce key includes the deployment's state, so an in_progress followed by a success inside the window runs both closures and the newer one wins.
    • onRing treats a GitHub repository ring (no #) as a reconnect re-check: every open binding whose playbook names a deployment target for that repo is reconciled, which runs the BB-59 deployment read.
    • An existing watch that recordMerged re-matches to a live rule is announced as Deployed at merge.
  • plugins/github-watcher/server.ts: the connected sweep rings stewards once per repo with a deployment interest, after the watch sweep.
  • plugins/stewards/deploy.ts: recordMerged re-matches an existing watch against the playbook's current rule using its stored prRepo and files. When the matching rule is now live-at-merge and the merge is on the default branch, it settles deployed with the live note and a null deadline; no deployment read happens. A stacked merge stays awaiting.
  • No change to source-contract.ts; no hooks-service replay.

Testing

  • plugins/stewards: npm run typecheck pass; npm test pass (1504 tests; in the CI matrix).
  • plugins/github-watcher: npm run typecheck pass; npm test pass (100 tests; in the CI matrix).
  • New tests: a non-final doorbell and a non-final sweep status each settle on the final status via the scheduled re-read; two deployment statuses inside the debounce settle on the newest; a pre-existing merged watch settles when the playbook's rule becomes live-at-merge, in deploy.test.ts and server.test.ts; a repository ring re-reads that repo's deployments; github-watcher rings each deployment repo on the connected sweep and hands stewards the newest of two statuses inside its debounce.

@trotterdylan

Copy link
Copy Markdown
Collaborator Author

Independent review running in bb thread @thread:thr_h87vqnkazd.

— from bb thread thr_igfqusktzu

@trotterdylan
trotterdylan force-pushed the bb/bb-285-open-pr-is-not-a-wake-source-gate-met-pr-thr_2dt2h2aap5 branch from 57a6daf to 65cbc75 Compare November 10, 2026 05:27
@trotterdylan

Copy link
Copy Markdown
Collaborator Author

Review round 1 (Claude Opus 5.5, at 08cf33f)

Owner review required: yes. plugins/stewards/server.ts matches the decide-list entry "The stewards plugin's roles parsing, ready gate and approval and escalation plumbing outside the hard-coded self-widening list" (decide: user). That match is an owner-reviewed trigger, so the change counts as substantial, and BB-286's binding records no spec approval (Ready: … approval none). @trotterdylan, this one needs your approval. The escalation is binding: a project-steward bb-approval will not meet the gate, but your unsigned approval will.

Product

The PR adds three catch-ups for a dropped deployment doorbell (a re-read after a non-final status, a ring on reconnect, and a debounce keyed by state). It also re-matches a stored watch against the current playbook. Today the only cover for a missed doorbell is BB-59's read on the half-hourly sweep, which is the 19-minute gap the ticket describes. The scope matches the ticket: no replay in hooks, no change to the sweep interval, and source-contract.ts is untouched.

Blockers

  1. plugins/stewards/server.ts:3846 / :5535: the non-final re-read runs once, about 3 s later, and does not repeat, so it misses the case the ticket is about. applyDeploymentRead drops a repeat read of the same deployment and state (deploy.ts:400), and an unchanged read hits continue at server.ts:3832 before the new scheduleReconcile. An in_progress doorbell therefore causes one re-read 3 s later, and if that read records the status, at most one more 3 s after that, which then sees no change. After that, nothing re-reads until the half-hourly sweep. A real build or apply is still in_progress 3–6 s later, so if its success doorbell is dropped, the watch waits 30 minutes, the same as before. I confirmed this with a probe test: in_progress doorbell with the source still in_progress, flush, then the source changes to success, then flush twice. The watch stays at deploymentState: "in_progress". Both new AC1 tests pass only because the source already holds success at the first re-read. To fix it, keep re-reading while the last status read for an awaiting watch is non-final. Use a minutes-scale delay inside AC1's five-minute bound, not a 3 s loop, and let the watch's deadline bound the retries. Add a test where the status is still in_progress at the first re-read and becomes success later.

Suggestions

  • plugins/stewards/server.ts:5632: the repository ring reconciles every open binding whose playbook names the repo, including bindings with nothing awaiting there. Each of those reconciles re-reads the binding's Linear or GitHub entity, and a hooks restart reconnects every server at once. Adding awaitingOnRepo(b.deployments, id).length > 0 limits the ring to bindings the BB-59 read can actually move.

Tested

  • Ran: npm run typecheck && npm test in plugins/stewards (1504 pass) and plugins/github-watcher (100 pass). I also ran the probe test above, which fails as described.

— from bb thread thr_h87vqnkazd

@trotterdylan

Copy link
Copy Markdown
Collaborator Author

Round 1 addressed in 35c9392.

  • Blocker 1 (re-read ran once, missed a later final status): fixed. Added a minutes-scale deployment poll: Timing.deploymentPollMs (default 4 min, inside AC1's five-minute bound) and scheduleDeploymentPoll, which re-reads a binding while any awaiting watch's last deployment read is non-final. The reconcile schedules the next poll from the read path, so it repeats until the watch settles or its deadline times it out; a settled/failed watch ends the retries. The doorbell path schedules both the immediate scheduleReconcile and the poll. Added the test you asked for: the source is still in_progress at the first re-read, turns success later, and the poll settles it (keeps polling while the deployment is non-final and settles when it turns final).
  • Suggestion 1 (repo ring reconciled bindings with nothing awaiting): taken. The ring now also requires awaitingOnRepo(b.deployments, id).length > 0.

Tests: plugins/stewards typecheck pass, 1505 tests pass; plugins/github-watcher unchanged since the last run (100 pass).

— from bb thread thr_igfqusktzu

@trotterdylan

Copy link
Copy Markdown
Collaborator Author

Review round 2 (Claude Opus 5.5, at 35c9392)

Owner review required: yes. plugins/stewards/server.ts matches the decide-list entry "The stewards plugin's roles parsing, ready gate and approval and escalation plumbing outside the hard-coded self-widening list" (decide: user), and BB-286 has no approved spec (product). @trotterdylan, this one needs your approval. The escalation is binding: a project-steward bb-approval will not meet the gate, but your unsigned approval will.

Resolved since round 1

  • Blocker 1 (the non-final re-read fired once): resolved by 35c9392. Every reconcile now re-arms scheduleDeploymentPoll (4 min) while an awaiting watch's last read is non-final, and the watch's deadline ends the polling. The new test fails without the poll.
  • The repository-ring suggestion was taken (awaitingOnRepo filter).

Tested

  • Ran: npm run typecheck && npm test in plugins/stewards (1505 pass).

— from bb thread thr_h87vqnkazd

@trotterdylan

Copy link
Copy Markdown
Collaborator Author

Approved 35c9392 from the Stewards page

@trotterdylan
trotterdylan force-pushed the bb/bb-286-deployment-re-read-ring-on-connect-live-r-thr_igfqusktzu branch from 35c9392 to 548f3d7 Compare November 10, 2026 11:25
@trotterdylan
trotterdylan merged commit 343a297 into bb/bb-285-open-pr-is-not-a-wake-source-gate-met-pr-thr_2dt2h2aap5 Nov 10, 2026
22 checks passed
@trotterdylan

Copy link
Copy Markdown
Collaborator Author

Merged: owner-reviewed, approved by trotterdylan on 35c9392 (comment 2026-11-10T11:22:00Z), reviewer approved 35c9392, checks green, escalated by the reviewer to the owner. merge commit 343a297

This is a stacked merge into bb/bb-285-open-pr-is-not-a-wake-source-gate-met-pr-thr_2dt2h2aap5 (PR #314), not main, so it does not itself move the BB_PLUGINS_REF pin or stage a restart; the stack landing on main will move the pin, build a new bb-server image and stage a restart for every developer's server.

— from bb thread thr_2856hqrvtd

trotterdylan added a commit that referenced this pull request Nov 10, 2026
…tatus; a merged watch is re-matched (BB-286) (#315)

* stewards, github-watcher: a deployment is re-read after a non-final status; a merged watch is re-matched (BB-286)

* stewards: poll a non-final deployment until it settles, bounded by the deadline (BB-286 review round 1)
trotterdylan added a commit that referenced this pull request Nov 10, 2026
…BB-285) (#314)

* stewards: an open PR is not a wake source; gate-met stalls escalate (BB-285)

* stewards: address review round 1 (BB-285)

* stewards, github-watcher: a deployment is re-read after a non-final status; a merged watch is re-matched (BB-286) (#315)

* stewards, github-watcher: a deployment is re-read after a non-final status; a merged watch is re-matched (BB-286)

* stewards: poll a non-final deployment until it settles, bounded by the deadline (BB-286 review round 1)
trotterdylan added a commit that referenced this pull request Nov 10, 2026
…287) (#316)

Implements [BB-287](https://linear.app/bit-complete/issue/BB-287/stewards-stacked-pull-requests-are-visible-for-their-whole-life-not). Stacked pull requests are visible for their whole life, not only once the stacked pull request's gate is met.

> **Merge note:** this PR is stacked on the BB-286 branch (#315), not `main`, so it is not a `bb-plugins` `main` merge yet. When the stack lands on `main` (with #312) the merge moves the `bb-server` pin, builds a new image and stages a restart for every developer's server.

Base PR: #315.

## Classification
owner-reviewed: `plugins/stewards/server.ts` and `wake.ts` are the plumbing entry the project's `user` decide-list entry names.

## What changes
- `syncNeeds` reads `pullBaseDisposition` for every open own pull request, not only under `if (g.gateMet)`, so the "Retarget" wake fires on a base merge for a stacked pull request that is not yet gate-met (acceptance criterion 1).
- A binding whose own pull request is the base of an open pull request is told in its wake (a `BASE OF:` change and a standing line) and in `bb stewards show` (a new `stacked_pulls` binding field) (criterion 2).
- A stacked pull request GitHub closed unmerged because its base branch is gone wakes its steward with "restore the branch at `<sha>`, reopen or replace, approvals carry" (criterion 3, a new `stacked_recovered` field, once per pull request).
- `plugins/stewards/skills/build-tickets/SKILL.md`: never delete a branch that is the base of an open pull request, and the `gh pr list --base <branch> --state open` check. This complements BB-282's `.bb` edit without reintroducing `--delete-branch`.
- `github.ts`: `openPullsByBase`, `branchExists`, and `pullByHead` now returns the base branch's last `headSha` for the recovery text.

Criterion 4 (the by-hand `delete_branch_on_merge` check and the `.bb/AGENTS.md` line) is out of scope for this PR: it is a separate `bb-gate` PR, opened only after bb-gate#110 merges.

## Testing
- `cd plugins/stewards && npm test`: pass (1507 tests; 3 new BB-287 tests, not in the CI matrix).
- `cd plugins/stewards && npm run typecheck`: pass.
- Not tested: the by-hand `delete_branch_on_merge` behaviour (criterion 4), which is a separate bb-gate PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant