Make Workstreams readable and advance PRs from All PRs - #12
Merged
Merged
Conversation
5105a75 removed the Advance engine, but every writer still read its saved jobs as a live lock. Nothing can start, settle, or cancel a job any more, so a job a reload left queued, running, or uncertain blocked Address, merges, inventory writes, thread messages, and effort archive and merge on its PR and checkout permanently. thread_message told you to cancel the job, but no cancel exists. - Delete advance.reserved(), its status set, and the routing-facts schema only the lock read. - Drop the Advance check from withPrWriter, directActionRun, agentOn, inventoryActions.writer, thread_message (owner and queue mode), effort archive, effort merge, and the merge preview's blockers. - Keep list(): thread links, reconcileThreadIntent, and All PRs' merged or closed settlement still read the history. - Replace the test that pinned the lock with ones that merge, request review, list for Address, and message a PR whose only owner is an unsettled legacy job. No schema or migration change, and nothing writes advance_batches. The rollback build reads the store as before and restores the lock. The live store has 37 legacy jobs, none unsettled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Whether a PR was on Your turn, and whether Address could take it, was decided in five places with different rules. All PRs read the row's lead button, so a PR with an approval's note and a thread at work on it stayed on Your turn while the same PR with a comment left. An old batch's link switched off the at-work check for good, so All PRs offered a checkbox the server then refused. The deck never read Dismiss, so its Address sent a PR you had dismissed, and an old batch's Idle link led the row over the thread now working the PR. - Add turnOf to your-turn.ts: a pure, browser-safe rule over a PR's facts (feedback owed, hold, effort pile, Dismiss, a thread at work, and where its last Address sent it). It returns where the PR lists (turn, dismissed, held, in-flight, or other) and whether Address takes it, or why not. It never reads the row's lead button. - yourTurn reads the PR's facts only. It no longer takes attention's reasons or a hold flag, and reads the approval's note through feedbackToAddress. - All PRs, its badge, and its checkboxes read the row's turn. The deck files a PR a thread works on In flight, leads with the old batch's link only while the PR is on Your turn, and addresses only Your turn rows. Deck rows carry turn in place of yourTurn. - The Address listing and the dispatch re-checks, before and after the GitHub read, go through turnOf. Dispatch now refuses a PR with no feedback in the listing's words: "No feedback waits on you." A done or archived effort's PR stays on Your turn, but Address no longer offers to take it. No store or schema change; a rollback to a993fcf reads the same store. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An Address batch thread's link to each PR lived in the board's run log, and Sent rebuilt it by comparing a claim's start with the batch's confirm time. The run log's rules for one action per thread broke batches: a batch of more than one PR never read as Needs you, a failed one read as Idle with no reason, and 200 newer runs pruned the link, so the PR lost its thread on its row and the card. - Add pr_threads (pr_url, thread_id, batch_id, linked_at), an appended migration. Dispatch writes a row per PR when the thread's start returns, and spawn metadata names the batch. Recovery after a reload writes the link when it binds a claim to the thread BB made. Each load copies links an earlier build left only in the run log. - Sent joins the PR's newest Address item with its newest link and BB's status for that thread. A thread asking you something reads Needs you, whatever the batch's size, and a failed thread reads "Failed: <why>". A refusal keeps the older batch's thread linked. No time comparison decides which batch a thread belongs to. - A batch thread holds its PRs while BB lists it starting, queued, at work, or asking you, or, not listed yet, within two minutes of its link and after the last thread list began. A claim whose start hasn't returned still holds its PR. Agent and thread starts, messages, and Address read that one rule; the run log's open claims no longer count once bound. - Work-context links read pr_threads too, so the card and the row's executor keep the batch thread after pruning. Dispatch still writes, binds, and signals ADDRESS_RUN rows for one release. A rollback to a993fcf reads them as before and ignores pr_threads and the extra metadata key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dcc7c6e read a batch thread's hold from BB's word alone, and took two silences for a finish that a993fcf never trusted. After a reload, until the first thread list answered, a thread linked over two minutes ago read as gone. A new thread BB listed idle before its first turn read as done. Either way Address listed its PRs again and started a second batch thread on them. - Before any thread list answers after a load, a batch thread is starting: it holds its PRs as its open claim did. A list that never answers holds them until one does. - Under two minutes from its link, a thread BB lists idle or failed is starting unless its own idle or failed event said so: the grace reconcileRuns gives a claim. The event map that kept why a thread failed now keeps that its turn ended. No store change. A rollback to a993fcf reads the store as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8112413 stopped a checkout scan from rewriting an authored PR, but the scan still stamped the PR read: it set checked_at to now and cleared a failed read's time and reason, over facts it didn't write. The row then read "checked just now" instead of "read failed", and a Refresh that waited out the scan took that stamp for its own read and skipped GitHub. - observe() marks read only the open PRs the inventory doesn't keep. An authored PR's read stays the inventory's poll and Refresh's. Merged and closed PRs still leave the inventory as before. No schema change. A rollback to a993fcf reads the store as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dcc7c6e made BB's word decide whether a batch thread holds a PR, but kept the run log's copy of its claims deciding it in four places. Effort archive, the merge preview, and the merge itself counted only running claims, so a batch thread resumed after its claims ended didn't stop them: the effort archived, and the preview showed no blocker. A message to a PR's own thread counted claims BB's word had ended, and refused what Address let through. - Archive, the merge preview, and the merge wait for each PR addressHeld holds, or its checkout, as they wait for an open run. The preview names the PR. - None of them, nor thread_message's owners, read an Address claim from the run log. A claim whose start hasn't returned still holds through addressHeld. - Name the run log's remaining Address readers: the badge, Map rows, thread links, and the read of its PRs once claims end. No store change. A rollback to a993fcf reads the store as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
333fb77 made the deck's Address skip a PR you dismissed from Your turn, but the deck still filed it by the button its row leads with: under Work, counted in Needs you. All PRs lists it apart, under Dismissed, so the deck kept asking for a move you had declined. - place() files a row turnOf lists as dismissed In flight, as it does a row a thread is at work on. It leaves Needs you, the card's next steps, and Fix and Ask, until the head moves or a person says more. No store change. A rollback to a993fcf reads the store as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The card now leads with up to three moves, ranked by one rule (someone waits on you, one step from merged, your blockers, a reviewer holding a PR 4+ days), each over All PRs' own rows. Chores fold into one Advance line. A finish line shows tickets done, the project target or cycle end, and an ETA at the last 14 days' merge pace; p opens five answers. Notes, Threads, Linear, Held, and All PRs become footer toggles. Strip chips count Your turn only. Removed: the bento tiles, the nine sections and their buttons, need-you counts, four of Advance's five entry points, the deck's batch bar, and ghost, lands-here, and moved-to rows. Accept moves to ⇧A so p can open progress; Revoke confirmation moves to ⌘K. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
detailQuery now asks for priority, priorityLabel, estimate, createdAt, startedAt, completedAt, and canceledAt. The fields are optional on LinearDetail, so a row cached before them still reads; it refills when its 12-hour cache runs out rather than all at once. linear_detail.detail is JSON, so there is no migration. The scan's sync also covers live efforts' own tickets and the tickets of PRs merged in the last 14 days, so a ticket's state no longer freezes when its last PR merges. Each ticket still waits out its 12-hour cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A card's moves end with Reconcile when Linear and GitHub disagree: one line per mismatch, with its tickets and one button. "2 Done in Linear · 2 PRs open" shows those PRs under it; "1 open with every PR merged" opens the ticket in Linear. It writes nothing and has no key, so the hint bar skips it. A held effort shows none. Each row on a card carries its ticket on a chip, with Linear's priority as a glyph, red only for Urgent. The p expand adds a Left line, such as "1 Urgent · 1 High · 6 of 9 pts left", when Linear gives priorities or points. deck_get reads each merged PR's tickets, so a ticket still open after its PRs merged in the last 14 days counts. The preview fixture gains priorities, points, and both mismatches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
classify_move puts PRs All PRs lists into an effort, One-offs, or a new effort by name, out of whichever effort has each now, or none. It is one action through the assignment store's move, with an audit row per PR naming where it was, so classify_undo puts each back and removes a new effort again. A PR that isn't open now, a done or archived effort, or a taken name refuses it, and a destination it would have made is removed. classify_get also lists the efforts on the active pile, One-offs aside, and the suggestions you dismissed, which classify_dismiss keeps in KV as one key per PR naming the effort. No migration: the rows use existing sources and the from_effort_id column, so the last build still undoes them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion Every open row in All PRs selects now, not only Your turn's. Move to effort…, or e, opens a small type-to-filter picker over the selection bar: the active efforts, One-offs, and a new effort by the typed name. Enter moves the selection through classify_move at once, with Undo, and the rows regroup under their new effort. e with nothing selected moves the focused row. A row under No effort shows the classifier's pick for it as a quiet "→ Effort?" chip, with its signals on hover. A click or ⇧A accepts it through classify_assign, as a service card's Accept does, with Undo; × hides that effort's suggestion for the PR, and Undo shows it again. A No effort group with several offers Accept all suggestions (N). Only an effort pick at medium or better shows: a low or tied pick, a new effort, and One-offs show nothing. Address stays Your turn's: with any other row selected, the bar's Address is off and says "Address takes Your turn rows only.", and b says why. e and ⇧A are the registry's existing Move and Accept keys, so the hint bar, ⌘K, and ? list them in All PRs too; y is Mark ready. The deck's card rows keep their checkboxes on Your turn alone. The inventory preview gains routing frames at 1100 and 420 px, with the picker open and a new effort typed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The card freed a row as soon as its write was sent, but deck_batch_plan still skips a sent write until you mark the row seen after it. So a card could read Advance 2 or Fix 1… and open a listing that skipped a row as "A write on it is waiting or just ran." for up to a day. The card now reads the plan's own rule (counted): a write holds its row from its Undo window until you mark the row seen after it lands. A thread's fix leaves GitHub as it was, so only your look says the read is past it. Mark seen offers to settle a write that landed. A test pins the card's chores and Fix move to what the plan takes, for each state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reconcile's "N open with every PR merged" read each ticket's cached Linear state, which the sync refetched only once its 12-hour cache ran out. A ticket read while its PR was open kept that state after the merge, so nearly every merge showed a false mismatch for up to 12 hours. Linear moves a merged PR's tickets itself, soon after the merge. So the sync now reads a ticket again after its newest merge, on each scan until a read lands 10 minutes past it, and Reconcile counts a ticket only from such a read. linear.readAt gives the deck each ticket's read time. The newest merge naming a ticket in any effort counts. No migration: the cache already kept fetched_at. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"N of M done" counted canceled tickets in M but never in N, so an effort whose tickets were all done or canceled could never read all done: Done, Done, Canceled read "2 of 3 done" beside "0 open tickets". M is now the tickets Linear didn't cancel. With every ticket canceled, the line counts merged PRs, as with no Linear data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The composer's effort chip and its popover still showed the old amber "N need you", chores included, so an effort read 0 on its strip chip and, say, 6 amber above the composer. The deck also ordered a session's first strip by that count. deck_get's cards now carry yourTurn: the rows All PRs lists on Your turn, as the strip chip counts them. The chip, its popover's choices, and the active pile's order read it, so amber means a person waits on you everywhere. The thread effort contract's needsYou becomes yourTurn, and the chip says "5 your turn" as the strip chip does. needsYou stays on deck_get for agents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…vancement # Conflicts: # plugins/workstreams/ghactions.test.ts # plugins/workstreams/howto.tsx # plugins/workstreams/inventory-server.test.ts # plugins/workstreams/outcomes.test.ts # plugins/workstreams/workstream-attention.test.ts # plugins/workstreams/workstreams-inbox.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Workstreams now uses effort cards to read exact PR and thread status, and All PRs to advance work. Cards link directly to the chosen PR; All PRs exposes the existing confirmation, merge-preview, feedback, hold, and review actions. Other open PRs can start a fresh worker conversation, including PRs whose previous worker was archived. Both lists support group selection: Advance selected previews the exact selected PRs, binds their shown heads, then starts one fresh conversation for the eligible selection. The Other heading has a select-all checkbox; mixed selections across both lists work. Existing workers do not block that explicit restart; holds, stopped efforts, and changed heads still do.
Plan Advance All starts a planning thread for every open PR except held PRs. It saves cached PR, checkout, effort, Linear, dependency, and worker facts once, supplies a compact attention index, and uses bounded Jev clustering with a rules fallback. The thread prioritizes and proposes a plan; it does not execute it. Held PR records are excluded from the saved context and Jev input.
This brings the existing Workstreams deck branch into main, including retirement of the old Pipeline/Work/Roster execution surfaces. It also fixes monorepo PR project lookup and creation of a missing effort parent, and includes the completed thread-effort read/refresh optimization. Other plugins are unchanged.
Validation: typecheck, plugin build, 1,590 passing tests, browser bundle/isolation and migration-upgrade checks, and production-component renders at desktop and phone widths, including group selection and its exact-scope confirmation. New planner and fresh-worker tests use a fake host; no real worker or GitHub write was triggered during verification.