Repository navigation
ci: let release-please PRs run checks without approval - #69
Merged
Merged
Conversation
GitHub holds pull_request runs on PRs that GITHUB_TOKEN opens or pushes for approval. Mint a short-lived GitHub App token for release-please when the release app is configured, falling back to GITHUB_TOKEN otherwise.
Owner
Author
|
Merging under the "local ci is good" rule, with CI not green on this head.
No test was changed or loosened. The flake is still open. |
ANT1OFF
added a commit
that referenced
this pull request
Nov 2, 2026
#80) Simplifies how orchestrator work shows in the sidebar and on the Board, and makes the two agree. Presentation only: features and groups stay in the server and the ledger, and nothing in `packages/` changes. ### Sidebar - **Orchestrator → workers.** The feature headings are gone, in the open sidebar and the compact rail's flyout. A worker's feature stays on its hover card. - **Order** was spawn order; it is now needs you, working, waiting on CI or a review, then the rest, stable within each, with held briefs last. - **Plain status lines.** The sidebar showed the raw activity (`Running grep -n …`, `Reading …/scratch/…`, `Running: a background shell • ssh …`). It now goes through `activityGist`, the Board's own words, so a command or path can't reach a row. `Waiting on CI · #69` and other headlines are unchanged. ### Board - **Ungrouped lines** get an Other heading under the bands, so they no longer read as the last group's. No heading when no band is above them. - **Line name** is the worker's title, with `+N` for more workers. The slug moves to the hover. A line's only worker no longer repeats that title on its chip. - **Finished work leaves for Done.** A line goes when its workers are merged, delivered or archived, or when the ledger no longer has them and nothing runs, the rule the sidebar already used. The second part is what clears the stale "reported done" lines: a closed worker's card stays as it last was. A held brief, a worker still running or asking, and a merged worker that still has work going all keep a line on the Board. - **Show archived became Show finished**, and brings all of it back. It keeps the existing `boardArchivedShown` setting. ### Decision: one-feature groups stay as bands Stages and the plan editor live on the band. Now that finished lines leave, a group shrinks to one line mid-effort, so flattening it would make its stage disappear and reappear as work moved. Bands also stay stable when a second feature joins. ### Known limits - A worker that only reported done, and is still in the ledger, stays on the Board because the orchestrator may act on its report. The sidebar already hides those, so the two differ there. - Reading is worded "Reading code" rather than "Reading a file", because this reuses the Board's words so both surfaces agree. - Sidebar order follows tone, so a row can move when its state changes. - Not run against a live provider or real workspace data: screenshots and e2e use the synthetic mock. ### Verification On this head, rebased onto `main`: - `bun run typecheck`, `bun run fmt:check`, `bun run check:scaffold`: pass - `vitest run src/orchestration src/views src/thread src/sidebar src/shell`: 1082 passed - `bun run --filter @gram/web e2e overview-sidebar board-parity board-rail board-virtual feature-sheet orchestration workers-list held-briefs worker-questions getting-around --workers=1`: 105 passed, 1 failed - The failure is `orchestration.spec.ts:1870`, the digest's report evidence, which this change doesn't touch. It passed in the earlier run and 3/3 with `--repeat-each=3` alone, so I read it as a flake under load on a shared machine. - Not run: the full `bun run check`, left to the merge gate. The e2e caught one real bug on the way: an Other heading as its own list entry had no tab stop, so Tab died there. The heading now belongs to the first ungrouped line's entry, and `board-virtual`'s Tab test covers it. Specs that pinned the old headings and `Show archived` are updated, not deleted. ### Screenshots **The sidebar's workers under their orchestrator, plain status lines, and the Board beside** (dark) | Before | After | | --- | --- | |  |  | **Board: finished work gone to Done, Show finished in the bar brings it back** (dark) | Before | After | | --- | --- | |  |  |
ANT1OFF
added a commit
that referenced
this pull request
Nov 3, 2026
## What When a worker or an orchestrator is at rest waiting on something, the sidebar now says what, at a glance: an hourglass and a short line under its name, and the whole list on hover. The Board's chips and ⌘J's Workers list read the same selector, so the three surfaces say the same words. Presentation only. Nothing in `packages/` changes: everything comes from the ledger the web already gets (PRs, wakes, watched runs, limit waits) and the threads store. ### One "waiting on" model `apps/web/src/orchestration/waiting-on.ts` turns a calm worker's or orchestrator's waits into: - `label`: the row's line, e.g. `Waiting · checks on #69` - `reasons`: every wait, each with a kind, a short form and a full line - `hint`: the hover, i.e. the label with `+N` when more apply, then every wait's line | Kind | Label | Comes from | | --- | --- | --- | | checks | `Waiting · checks on #69`, `Waiting · local check #69` | required checks pending; Gram's local check running | | review | `Waiting · review on #69` | the review bot reviewing the head | | merge | `Waiting · merge of #69`, `Waiting · #69 held` | an armed `merge_when_ready`; a hold on an agent's work | | dependency | `Waiting · #102 to merge`, `Waiting · acme/api#7 to merge`, `Waiting · #645 to move` | `depends_on`; a wake on another PR | | run | `Waiting · Release run` | `run_monitor` | | condition | `Waiting · main to move`, `Waiting · thread to finish`; any other kind as its chip reads it (`wakeConditionLabel`) | #108's condition wakes; the hover names the thread and says `wakeConditionWhen` | | timer | `Waiting · until 08:10`, `Waiting · until Wed 09:00` | a timed wake | | limit | `Paused · limit until 08:10`, `Paused · retry at 07:05` | a usage-limit or rate-limit wait | | held | `Held · by you` | the user holds the worker | | frozen | (reserved) | nothing produces it yet; the merge freeze will | **Which wait the label names.** What holds everything else comes first (a limit, a frozen base, your hold). Then what clears soonest by its nature (CI, the review bot, Gram's merge), then the longer waits (another PR, a run, a condition). A timed wake comes last, since it's mostly the fallback beside the others. Within a kind, the soonest wins. **Waiting is never "needs you".** A wait is only said of a calm tone (waiting, idle, or finished with a run still watched), never beside work, an ask or a failure. Nothing here touches the Needs you list or counts. ### Surfaces - **Sidebar worker rows**: the hourglass line replaces the card's now line ("checks running", "you hold it") while the worker waits. The hover card lists every wait. - **Orchestrator line**: a second line under the workspace name for its own pending wakes, or a limit it waits out. The line's hover adds the list. It's hidden while the workspace is paused or parked. - **Board**: a chip's line is the same label, and its hover the same list. - **⌘J**: a row's now line is the same label, and its hover the same list. - The waiting tone's label is now "Waiting" rather than "Waiting on checks", since it covers more than checks. ### Behaviour change A worker waiting out a usage limit (`limitWait` in the ledger) now reads as waiting on automation, with the hollow dot. Before, its activity ignored the limit, so it showed idle (or attention). ### Labels kept to four words The Board keeps a chip's line to four words. Every label fits, with the ` · ` separator counted as punctuation; `board-rail.spec.ts` now counts it that way. ### Condition kinds still to come `conditionShort` special-cases `branchMoved` and `threadFinished` only. Any other kind falls back to contracts' `wakeConditionLabel`, and its hover line to `wakeConditionWhen`. So #110 (`freezeLifted`), #111 (`ciStuck`, machine load and disk) and #112 (`releasePublished`, `versionChanged`) show up without a change here. Whichever lands last adds any icon, words or priority of its own. The `frozen` kind is reserved for #110's frozen-main state. ## Screenshots From `pr-shots` on synthetic mock data. The "kinds" scene (`simulateWaitingOn`) adds a timed wake, a watched release run, a usage limit, an armed merge, a wake on another PR and one on `main` moving, and the orchestrator resting on two wakes of its own (a thread to finish, ranked first, and a timed one). **The sidebar over `casefiles`: workers waiting on CI, the review bot and a dependency say what they wait on** (dark) _After only: the base timed out staging it; the light pair below has the before._  **The sidebar over `casefiles`: workers waiting on CI, the review bot and a dependency say what they wait on** (light) | Before | After | | --- | --- | |  |  | **Every kind of wait in the sidebar, and the orchestrator resting on a wake of its own** (dark) _After only: the scene's mock scenario is new in this PR._  **Every kind of wait in the sidebar, and the orchestrator resting on a wake of its own** (light) _After only: the scene's mock scenario is new in this PR._  **A waiting worker's hover card lists every wait, the one its row names first** (dark) _After only: the scene's mock scenario is new in this PR._  **The same waits on the Board's chips, in the sidebar's words** (dark) _After only: the scene's mock scenario is new in this PR._  **The same waits on the Board's chips, in the sidebar's words** (light) _After only: the scene's mock scenario is new in this PR._  **⌘J's Workers list says the same** (dark) _After only: the scene's mock scenario is new in this PR._  **The same `casefiles` workers on the Board: the chips agree with Quiet's dots** (dark) | Before | After | | --- | --- | |  |  | **The same `casefiles` workers on the Board: the chips agree with Quiet's dots** (light) | Before | After | | --- | --- | |  |  | ## Verification On `fc8e877`, rebased onto `main` after #108: - `bun run typecheck`, `bun run lint`, `bun run fmt:check`, `bun run check:scaffold`: pass - `vitest run src/orchestration src/views src/sidebar src/thread src/shell src/api`: 1228 passed (on `767c1af`); `waiting-on.test.ts`: 30 passed on the head - Affected e2e, `--workers=2` (`overview-sidebar board-parity board-rail board-virtual feature-sheet orchestration workers-list quiet-view overview-extras workspace-density reaching-workers worker-questions one-thread-budgets ask-stack`): 135 passed, 3 failed. Two were tests to update for this change (the orchestrator's waits read in rank order; ⌘J's four-word cap counting the ` · `), fixed in `fc8e877`. The third, `overview-sidebar.spec.ts:851` (a Needs you count of 9 for 8), is untouched by this and passed alone. All three passed alone on the head. - Specs updated, not deleted: `board-rail` and `workers-list` count words without the separator; `overview-sidebar` reads a row's name with its wait, and tells the agent's glyph from the hourglass. - Not run: the full `bun run check`, left to the merge gate. ## Known limits - `frozen` has a label and a rank but no source until the merge freeze lands. - A thread-finished condition shows "thread to finish" on the row; the thread's title is in the hover only, to keep the line short. - Synthetic mock data only; not run against a live provider or real workspace.
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.
Why
Every push to the release-please PR (#66) leaves "Application checks" and "Scaffold boundaries" at
action_required, so they only run after someone clicks Approve workflows to run.Cause. Since June 2026, GitHub holds
pull_requestruns (opened,synchronize,reopened) for approval whenGITHUB_TOKENcreated or updated the PR (changelog, GITHUB_TOKEN docs). The repository's fork-PR approval policy (all_external_contributors) plays no part:698fd0c):event=pull_request,actor=github-actions[bot](Bot),triggering_actor=github-actions[bot], head repositoryANT1OFF/gram, so the PR comes from a branch in this repository, not a fork;action_requiredruns on release-please's branch look the same.The docs name only one way around it: create or update the PR with a GitHub App installation token or a PAT. No repository setting exempts the bot, so the approval policy stays as it is.
Repository setting changed
total_count: 0). After:release-please, with deployment branch policycustom_branch_policies: trueand one branch policy,main(typebranch), andcan_admins_bypassset tofalse(it was created astrue). There are no reviewers or wait timers and no secrets yet.fork-pr-contributor-approvalis stillall_external_contributors. Allowed actions, SHA pinning and default workflow permissions are untouched.What
release-please.yml: theproposejob now runs in therelease-pleaseenvironment, so the app key can be read only frommain. If the environment variableRELEASE_PLEASE_APP_CLIENT_IDis set, theproposejob first mints a GitHub App installation token withactions/create-github-app-token, pinned to the v3.3.0 SHA. The token is scoped to this repository, carries onlycontents: writeandpull-requests: write, and is revoked when the job ends. release-please then uses that token. Without the variable, it usesgithub.tokenexactly as it does today.mainorworkflow_dispatchand is still API-only, with no checkout or script while it holds a write token. Default and top-level permissions stay read-only. No PR-triggered workflow can read the secret.actions/*is already in the allowed-actions list. Fork approval still applies to new external contributors.tools/test-release-please.mjs: the workflow assertions now cover the new step, its pin, its exact inputs and the token fallback.docs/RELEASING.md: a short "Release app" section with the one-time setup steps.Setup (the owner has to do this; until then nothing changes)
The steps are in
docs/RELEASING.md#release-app: create a GitHub App with Contents and Pull requests read/write and no webhook, install it onANT1OFF/gramonly, then set the secretRELEASE_PLEASE_APP_PRIVATE_KEYand the variableRELEASE_PLEASE_APP_CLIENT_IDin therelease-pleaseenvironment (--env release-please).Verification
node tools/test-release-please.mjs <pinned action npm-ci dir>: 10/10 passnode tools/check-release-please.mjs: OKbun run check:scaffold: passbun run fmt:check: passNot verified live. Proving that the app token removes the approval needs the app and its secret, which only the owner can create. After setup, run
gh workflow run release-please.yml --repo ANT1OFF/gram, then confirm theapp-tokenstep ran and that the release PR's runs on its next push are notaction_required.Independent review of 60a252e, then a re-review of 6fade81: nothing blocking either time. The re-review also confirmed that the step
ifresolves environment-levelvarsand that runs on main pass the branch policy. Following its advice, admin bypass is off. It also cautions that any future main-branch workflow on an untrusted trigger (pull_request_target,workflow_run) must not name this environment. First review: Its two non-blocking points are fixed in 6fade81: the key now sits in a main-only environment, and docs step 5 says what happens to runs already held.Known limits:
release-pleaseenvironment.feat:/fix:merge.