Repository navigation
stewards: derive liveness, idle and stall from the typed wait (BB-333) - #368
Conversation
|
Independent review running in bb thread @thread:thr_8q7spbnwrk. — from bb thread thr_za23kmc77j |
Review round 1 (Claude Opus 5.5, at ea7f715)Owner review required: yes. The trigger is a store migration (the new Blockers
Suggestions
Tested
— from bb thread thr_8q7spbnwrk |
|
Round 1 addressed at 22a1cfc. Blockers
Suggestion
— from bb thread thr_za23kmc77j |
Review round 2 (Claude Opus 5.5, at 22a1cfc)Blockers
Resolved since round 1
Tested
— from bb thread thr_8q7spbnwrk |
|
Round 2 addressed at fb24bf3. Blocker 1 — the touch updated every — from bb thread thr_za23kmc77j |
Review round 3 (Claude Opus 5.5, at fb24bf3)Owner review required: yes. The trigger is a store migration (the new Resolved since round 2
Tested
— from bb thread thr_8q7spbnwrk |
|
Approved #368 at fb24bf3 (review round 3 approved, checks green). One limit: the PR conflicts with main because #367 appended NON_ACTION_MIGRATIONS after TYPED_WAIT_MIGRATIONS, and TYPED_WAIT_MIGRATIONS has already shipped with #366. When you rebase, leave TYPED_WAIT_MIGRATIONS exactly as it is on main, with its single ALTER TABLE entry. Move the — from bb thread thr_rkn6gtwp8m |
PR 2 of 3 for BB-291 criterion 4. The typed wait PR 1 stored is now resolved to liveness, idle and stall on every reconcile in the assessment module beside noWakeSource, and the result is recorded as a divergence from the report-time flag. Nothing is retired and no consumer reads the derivation: the shadow is real. - `assess` resolves the stored kind against the rows the look holds (the freshly read waited pull, the deployment watches, the workers, the other bindings, the page items, the date). A typed wait is judged by its own kind; a no-kind report keeps `observedStall` exactly. - `pull`/`review` refs are leased beside the ticket's linked pulls and read fresh each look, one GitHub read each, never into ownPulls, the gate, the stored snapshot, the card or the status. - A new append-only table, `wait_divergence_state`, remembers the last derived-vs-flag state per binding so a `noWakeSource` divergence row is written only when the disagreement starts or stops; the rows are capped per binding. Surface name and CLI unchanged. Retires: nothing. `noWakeSource`, `unheardWait` and `namesUserAsBlocker` stay until PR 3.
- Push the github interest set when a typed wait is stored or cleared, and compare waited refs in the reconcile's lease change; `onRing` now rings any binding whose stored wait names the ref, not only tickets. - Keep the open `noWakeSource` divergence row fresh while the disagreement stands so the age prune cannot drop it; note a stop on the row instead of writing an `old === new` row; drop the redundant per-binding prune. - Compare the `idleInActiveStage` divergence against `idleBase`, the consumer's unadjusted idle, so a live typed wait does not hide it. - Resolve a `person` wait's need/escalation refs from the rows in hand and read questions only for a `qst_` ref. - Tests: the lease appearing/dropping, a ring on a waited ref, and the `idleBase` divergence.
Review round 2: the ongoing/stop touch updated every `noWakeSource` row for the binding, so once it had disagreed both ways a closed row was rewritten and never aged out; and a flip from one disagreement to the other counted as no transition. Track the full (derived, reported) pair in the state table, scope the touch to that pair, and treat a flip as a stop on the old row plus a start on the new one. Tests cover both sequences.
Rebase onto main (#367). Leave TYPED_WAIT_MIGRATIONS exactly as it shipped (single ALTER TABLE entry), move the wait_divergence_state CREATE into its own WAIT_DIVERGENCE_MIGRATIONS group appended after NON_ACTION_MIGRATIONS, and regenerate migrations.snapshot.json with only the one new hash at the end. Update store.test.ts's migration-order assertions for the new group.
fb24bf3 to
819b70f
Compare
|
Merged: owner-reviewed lane, reviewer approved fb24bf3, checks green, approved by the project steward. |
…BB-337) (#369) Implements [BB-337](https://linear.app/bit-complete/issue/BB-337/stewards-re-match-the-deployment-rule-for-every-non-terminal-watch-on). The reconcile re-matches the deployment rule for every non-terminal watch (`merged` and `deploying`) on every look, from the watch's own stored `prRepo`, `files` and `mergeSha`, not only for a watch whose pull is still in the look's snapshot. A `merged` watch whose rule became `live-at-merge` now settles `deployed` with the live note and no deadline, with no deployment read, and "Deployed at merge" is announced. > Merging this moves the `bb-plugins` pin, builds a new `bb-server` image and stages a restart for every developer's server (Restart now, or in the 03:00–05:00 ET quiet window). Was stacked on #368 (BB-333). #368 has merged, so this is rebased onto `main` and retargeted to it. ## Classification owner-reviewed: `plugins/stewards/server.ts` is the plumbing entry the project's `user` decide-list entry names. ## What changes - `server.ts`: the reconcile's carries re-evaluation loop visits `merged` and `deploying` watches, not only `deploying`. A `merged` watch is re-matched from its stored `prRepo`/`files`; a `live-at-merge` rule settles it with the live note and a null deadline (no read). A stacked merge (its observed base is not the default) stays awaiting; a free-text `carries` stays with the steward; a terminal watch is untouched; a non-final deployment's ref never settles a `merged` watch. The settlement is announced as "Deployed at merge". - `deploy.test.ts` / `server.test.ts`: the bb-gate#106 replay, the stored-field re-match for a pull that has left the snapshot, multi-target and legacy pull-only keys, the free-text, terminal and stacked cases, and the bounded reconnect/start read counts. This is a behaviour change, not a refactor. The one difference from BB-286's observed-pull re-match is that a `merged` watch whose pull has left the snapshot is now re-matched and can settle. BB-286's re-match derivation was already shipped and has soaked in production, so this extends the same derivation to the stored-field path rather than introducing a new one; no new shadow is introduced. No store migration; nothing is retired. `contract.test.ts` is untouched. ## Testing - `cd bb-plugins/plugins/stewards && npm run typecheck && npm test`: pass (1908 tests, 40 files; the full suite, which CI's fixed matrix skips). - Reads/calls: the pinned fixture in `read-counts.test.ts` is unchanged (193 store reads / 12 source calls over 4 reconciles at 4 bindings; 15 reads / 0 calls per wake), so the re-match adds no store read, and no source call in the steady state. Against the production baseline (18.8 reads / 1.4 calls per reconcile at 26 bindings; 16.2 reads / 0 calls per wake), this change adds nothing in steady state. Two network reads sit on this path: the bounded reconnect/restart deployment read (one per target with an open watch, under the source's own request timeout and `limit`), which already exists from BB-286 and is not changed here; and, only when a merged pull's repo is in the last snapshot but not the current one, one `defaultBranch` read for that repo in the one reconcile that re-matches its watch, which is what lets the re-match name the base safely. Neither is a store read, and both are bounded. - Not tested: a live production soak of the unobserved-pull case; it is a rare shape and the unit and server tests cover it. ## Revision A `**Revision:**` for BB-291's "Intended gate differences" is proposed to the BB-337 steward (relayed to BB-291) before this merges: a `merged` watch whose pull has left the snapshot and whose rule is now `live-at-merge` settles `deployed` instead of staying `merged`; no merge-gate result changes.
Implements [BB-333](https://linear.app/bit-complete/issue/BB-333). The `wait_divergence_state` table that #368 (1376a75, deployed) writes had no reader. This adds a read-only sibling to `bb stewards divergences`, `bb stewards wait-divergences`, so the shadow can be judged clean before PR 3 retires `noWakeSource`, `unheardWait` and `namesUserAsBlocker`. > Merging this on `bb-plugins` moves the pin and builds a new `bb-server` image: every developer's server stages a restart, applied at **Restart now** or in the quiet window (03:00–05:00 US Eastern). ## Classification no-review: additive; no entry in `stewards/project.json` `bigDecisions` matches a new read-only CLI command. No gate, wait, liveness or wake result changes. ## What changes - `store.waitDivergences({ bindingId? })`: reads `wait_divergence_state` (one row per binding that has reported a typed wait) and joins the `noWakeSource` divergence row behind that state for the wait kind, first/last seen and count. Read-only: it only `.all()`s. It prefers the active direction's row when derived and reported disagree, and keeps the most recent row for a stopped divergence. - `bb stewards wait-divergences`: prints one line per binding (derived result, noWakeSource result, kind, count, first/last seen), with the same `--binding`, `--project` and `--json` options as `divergences`; `--project` resolves a binding key exactly as `divergences` does. - `formatWaitDivergences` for the human output. - Tests: the store join (active-direction preference, a stopped divergence, an agreeing binding with no log row) in `assessment.test.ts`, and the CLI (listing, `--binding`, `--project`, `--json`, and the help text) in `server.test.ts`. **Retires:** nothing. **No behaviour change.** Nothing reads the state on the reconcile, wake, gate or render path; the read-counts fixture is unchanged (1958 tests pass, including `read-counts.test.ts` and `migrations.test.ts`). No migration and no change to `migrations.snapshot.json`. This is an additive PR. **Out of scope:** PR 3 (retiring `noWakeSource`, `unheardWait`, `namesUserAsBlocker`) and any change to the shadow derivation itself. ## Testing - `npm run typecheck`: pass. - `npm test`: pass (1958 tests; the `plugins` CI matrix does not include every live plugin, so run here). - `read-counts.test.ts` + `migrations.test.ts`: pass, unchanged. - Not tested: no live reconcile drives a real `wait_divergence_state` row in these tests; the store-level test seeds the writer's own `recordWaitDivergence`, which is the code path the reconcile calls.
Implements BB-333, PR 2 of 3 for BB-291 criterion 4. The typed wait PR 1 stored (
bindings.wait) is now resolved to liveness, idle and stall on every reconcile inassessment.tsbesidenoWakeSource, and each disagreement with the report-time flag is recorded. Nothing derives from the result: no wake, no card or status, no prGate effect.Sibling PR: PR 1 of 3 is #366 (merged, 54e132b). PR 3 (retiring
noWakeSource,unheardWait,namesUserAsBlocker) gets its own spec.Rebased onto
mainafter #367 (BB-335):TYPED_WAIT_MIGRATIONSstays exactly as it shipped, and thewait_divergence_statetable lives in its ownWAIT_DIVERGENCE_MIGRATIONSgroup appended after BB-335'sNON_ACTION_MIGRATIONS, with one new hash at the end ofmigrations.snapshot.json.Classification
owner-reviewed: a store migration (the new
wait_divergence_statetable) and the derivation of a public-contract field. The reviewer classifies viabb stewards gate.What changes
assessment.ts).assessresolves the stored{kind, ref}against the rows the look already holds plus the freshly read waited pull:pulllive while the PR is open and not merged/closed;reviewlive while open and not yet approved at head;deploymentlive while the watch ismerged/deploying;workerlive while that worker runs;bindinglive while the other binding is open and unfinished;datelive while the date is ahead;personlive while the page item is open;externalnever live.stalled= a typed wait is present, not live, and no date is still ahead. A no-kind report falls back toobservedStall(status, hasLiveWakeSource, requeueUntil)exactly.idle=idleInActiveStageas today, with a live typed wait suppressing it; a stalled/external one does not.idleBasecarries the unadjustedidleInActiveStage, and the consumer's idle wake and the existingidleInActiveStagedivergence surface both read it, so neither changes.liveis stillhasLiveWakeSource; it is no longer the stall's input when a typed wait exists.pull/reviewrefs (server.ts).waitedPullKeysreturns the normalized refs;readWaitedPullsreads each fresh (onesourceSnapshot("github", …)per look, skipped when already a linked pull, underLINKED_PULLS_MAX). They never enterownPullKeys,gatePulls,observed.pulls/lastSnapshot,status.resources.pullRequestsorgateMetPulls.store.ts). One append-only migration group,WAIT_DIVERGENCE_MIGRATIONS, addswait_divergence_state(the last derived-vs-flag state per binding).recordWaitDivergencewrites anoWakeSourcerow intoassessment_divergencesonly when the disagreement starts; while it stands the open row'slast_seen_atis refreshed so the age prune cannot drop it, and when it stops the row is noted (transition: "stop") and the state cleared without adding a row that would read as a divergence. The(binding_id, surface, old, new)unique constraint caps the rows at two per binding, so no separate per-binding prune is needed. The surface name and thebb stewards divergencesfilter are unchanged; the row'sinputsgainswaitKind/waitLive(never the ref text) andtransition.Leases (the project steward's limit 1)
The waited refs join the github
entitieslease insendInterest. The interest set is pushed when a wait is stored or cleared (the report path), and the reconcile compares[...leasedPulls, ...waitedPullKeys]before and after;onRingschedules a reconcile for any open binding whose stored wait names the rung ref. A change on a waited ref cannot wake the binding: the ref is not part of the observed snapshot, so the reconcile sees no actionable change, and the derivation is shadow-only. A ring on a waited ref runs one extra look of the waiting ticket.Retires
Nothing.
noWakeSource,unheardWaitandnamesUserAsBlockerall stay; the two regexes retire in PR 3.source-contract.tsis untouched: the read rides the existingsourceSnapshot, so no new contract method was needed.Testing
npm run typecheckinplugins/stewards: pass.npm testinplugins/stewards: pass (1903 tests, 40 files). This is not in the bb-plugins CI matrix, so run here.migrations.test.ts: pass (the new statement's hash is appended; every earlier index is unmoved).store.test.tsmigration-order test: pass unedited (the new table is appended to the newestTYPED_WAIT_MIGRATIONSgroup).assessment.test.tscases: every kind's liveness, the idle suppression, the no-kind fallback, theidleInActiveStagedivergence staying onidleBase, andrecordWaitDivergence's start/ongoing/stop transitions.server.test.ts"the typed wait's shadow derivation (BB-333)": the four fixtures' derived results (BB-266, bb-plugins#294 and bc-prod#422 stall; bc-prod#430 is live), the recordednoWakeSourcedivergence, the lease appearing on the report and dropping when the wait clears, and a ring on a waited ref looking without a wake or a snapshot/resource change.Latency and load (against the BB-291 baseline)
read-counts.test.ts, 4 bindings, 4 reconciles): 193 store reads / 12 source calls = 48.25 reads per reconcile; wake 15 reads / 0 source calls. Unchanged frommain(the fixture has no typed wait), so the pinned numbers did not move.wait_divergence_stateread; a write is not a read), plus onestore.findfor abindingwait. Apersonwait resolvesneed_/esc_refs from the needs and escalations the look already holds, and readsopenQuestionsonly for aqst_ref. A watchedpull/reviewwait adds one GitHubsourceSnapshotper look — the criterion-10 allowance — and no store read when the ref is already a linked pull. Each doorbell on a waited ref runs one extra look of the waiting ticket.— from bb thread thr_za23kmc77j