Refresh verification recipes and restore CI - #284
tella-butler wants to merge 71 commits into
Conversation
🤖 OS review · request changes · confidence 2/5Safe once the archive recipe ordering is fixed. The selector updates match the current UI, but the revised flow restores its only archived fixture before the required phone verification, making that portion impossible to complete.
🔁 Not merge-ready and no live session owns this branch — add the |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| - **Clear and filter.** Refill the searchbox with an empty value, choose the `Filters` button using the exact accessible name from the current snapshot, and select one visible repository or person. Capture the filter state and narrowed result list. | ||
| - **Open a result.** Choose the button whose name starts with `Instrument request tracing in api-gateway`. Its transcript opens and keeps the archived state visible. | ||
| - **Restore.** From `/archived`, choose `Restore session` on desktop or `Restore` on phone. Confirm it disappears from `/api/sessions?archived=only&slim=1` and reappears in its active workspace. | ||
| - **Check phone layout.** Repeat search and result opening at 390x844. Select `Everyone` again because reopening the route resets the owner filter. Search and filters must remain reachable without desktop hover. |
There was a problem hiding this comment.
🔴 P1 — Run the phone check before restoring the sole archived session
The recipe creates exactly one archived result, then the preceding Restore step removes it from the archived slice. Executing the bullets in order therefore reaches this phone step with no Instrument request tracing in api-gateway row to search for or open, so the required phone verification cannot run. This also contradicts the new gotcha that says to restore last. Move the restore step after the phone layout check.
There was a problem hiding this comment.
Fixed in b393351: the recipe now runs the phone archive check before restoring its only archived result.
The browser regression now carries the instrument that found the last two bugs. An in-page probe, installed before the fixture loads, records every programmatic scroll write with its stack and real effect, every scroll and gesture event, every animation or transition inside the scroller, and one sample per frame of the entry at the viewport top. Between the settle points the regression asserts that this entry moves only by what the reader scrolled, that no two writers cancel each other inside one frame, that rows never transition their geometry, and that hydrated history never fades in. Those were exactly the wobble and the interrupted-scroll classes a trace had to be read to see. Two shapes from production join the fixture: a reply taller than a phone viewport measured for the first time, and growth above the reader that is not a keyed prepend, inside their own row and in the row above, through a new grow control. On the phone a touch fling toward history gets growth above the reader while momentum runs: the correction must wait for the momentum, land exactly, and never cancel the fling. The adapter's source-string tests are gone; the regression is the behavioral contract and the unit tests keep the pure helpers.
Reuse the headless Chrome and fixture server of the motion budgets step.
* Re-enable sandbox automations on disposable Executors * Add thinking message visibility preference * Support custom OpenAI-compatible providers with catalogs and discovery (#273) * Support custom OpenAI-compatible providers with catalogs and discovery A provider id unknown to Pi and Open Session runs when its config declares api: openai-completions with a base URL, the same shape Wafer is injected with. Per-model catalog rows (inline, catalogFile, or discovered) override the conservative 131072/32768 stub, and an opt-in GET /v1/models poll fills picker ids without removing operator-pinned ones. Settings writes preserve the fields the form does not edit. Refs #241 * Fix custom provider review findings * Preserve case-sensitive provider model ids --------- Co-authored-by: Michiel Westerbeek <[email protected]> * Surface GitHub App installations in the repo picker (#271) Installing the workspace App on a second account (for example an organization, after onboarding pinned installationOwner to the personal account that created the App) left the Add repository picker silently empty: the pinned installation saw no repositories, nothing revealed that the App was installed elsewhere, and the only way out was hand-editing config.json. - github-app.ts: listGithubAppInstallations() lists every account the App is installed on with the App JWT alone, so it answers even when the pinned owner matches no installation. Briefly cached. - GET /api/setup/github/repos carries installationOwner and the installation list (with the selected one marked) alongside the repos, and includes the configured owner in the repo-list cache key so switching owners does not serve the previous installation's list for the TTL. - The Add repository picker offers an installation switcher when the App is installed on more than one account (PUT /api/setup/github applies live), explains an empty installation instead of a bare 'No repositories match.', and offers one-click pinning when the configured owner matches none of them. Refs #242 * Keep pi Claude sessions on the account holding their SDK conversation Every pi Claude request re-picked an account, and the least-used tiebreak round-robins equal accounts, so a session hopped subscriptions on nearly every step. The stored SDK session is tagged with the account whose config dir owns it, so each hop replayed the full context onto a fresh SDK session (a prompt cache write, not a read) and burned limits on all subscriptions evenly. Pass the stored session's account as stickyId through pickBridgeAccount into resolveAccount's existing sticky step. Pins, exhaustion, sidelining, and the in-turn walk still move the session off it. * Own the transcript reader anchor at commit time Capture the entry at the viewport top in getSnapshotBeforeUpdate, the instant before the DOM changes, and settle it after the commit as a delta on the current scroll position. The previous restore replayed an absolute viewport captured on an earlier scroll event, which undid reader movement and fought TanStack's own compensation inside one commit (+358/-358 and +5145/-5145 pairs in a live trace). A virtualizer scroll write now commits its moved rows in the same frame, and corrections defer while a touch or its momentum is in flight. Drop the permanent transform transition on trailing rows: positions are compensated by instant scroll writes, so a 200ms glide against them read as the transcript wobbling on its own. Tail growth below a reader who left the live edge now appears beneath them with no scroll write. * Give the reader anchor sole ownership of its commit While an anchor is held, TanStack's size-based measurement compensation stands down: the settle after the commit restores the entry at the viewport top from the DOM itself. A live trace showed the guess (a spanning row's first measurement) being undone by that settle in the same frame, two writes where one is enough. * Fix disposable automation resumes --------- Co-authored-by: Michael <[email protected]> Co-authored-by: Jaap Frolich <[email protected]>
Meridian's early-stop tracker only counts mcp__oc__* calls as client-forwarded. When the model emits a mangled name such as mcp__ocuser__bash (seen live on Fable 5.1), the SDK answers "No such tool available" without running PreToolUse, the visible turn never settles, and with maxTurns=1 the query ends with nothing captured: the run fails with "Reached maximum number of turns (1)". Capture those tool_use blocks into the tracker and the forwarded list after noteAssistantMessage. The SDK's error result then settles the checkpoint and pi answers the unknown name with its own "Tool X not found" result, so the model gets to retry like it would in Claude Code. ToolSearch stays SDK-internal.
* Surface GitHub App installations in the repo picker Installing the workspace App on a second account (for example an organization, after onboarding pinned installationOwner to the personal account that created the App) left the Add repository picker silently empty: the pinned installation saw no repositories, nothing revealed that the App was installed elsewhere, and the only way out was hand-editing config.json. - github-app.ts: listGithubAppInstallations() lists every account the App is installed on with the App JWT alone, so it answers even when the pinned owner matches no installation. Briefly cached. - GET /api/setup/github/repos carries installationOwner and the installation list (with the selected one marked) alongside the repos, and includes the configured owner in the repo-list cache key so switching owners does not serve the previous installation's list for the TTL. - The Add repository picker offers an installation switcher when the App is installed on more than one account (PUT /api/setup/github applies live), explains an empty installation instead of a bare 'No repositories match.', and offers one-click pinning when the configured owner matches none of them. Refs #242 * Use every GitHub App installation * Import organization members through that organization's installation
* Refine archived session hierarchy * Align archive controls in the top bar
* Require Tella-issued editor fixture leases * Verify Tella editor leases server-side * Use verified creator login for Tella grants * Close editor lease enforcement gaps * Keep Tella fixture identity consistent * Canonicalize editor preview routes * Preserve delegated creator identity * Source creator identity from trusted callers * Preserve identity for authenticated task sessions
Thinking rows were filtered per transcript and rendered in place, so the newest thought could sit above later tool calls. Arrange thinking per work rail instead: with "latest" the newest thought of a live turn follows its last step as a status and disappears once the turn settles; "all" keeps every thought where it happened; "none" shows nothing. The collapsed live rail header shows the current thought or last tool as its activity.
A bash call that starts with `cd <dir> && …` only sets up its working directory; the row's one-liner now shows the command that follows. The expanded view still carries the full text. Applied in the shared protocol derivation (web, phone web) and the native Swift summary.
Keep child sessions nested for inventory purposes, but render a row only when the child owns a workspace or worktree. Distinct linked pull requests retain their status icon; the root workspace's PR is not repeated on child rows.
A create whose workspace, credential or branch effects ran long was invisible for minutes: the session file was written only inside the opening effect, after those effects settled, so the web shell was the only copy of the session and its prompt. Persist and announce the session as soon as its create dispatch and setup plan are durable, before the git work, in the one path every adapter and boot recovery share. Hold the session busy until the opening turn takes run admission, so a prompt sent meanwhile queues instead of starting a turn in a worktree that does not exist yet, and list it as running. A setup failure after the announce lands on the visible session and retires its dispatch instead of leaving a busy, empty row. Both adapters now share one actor-backed materializer with a ten-minute backstop: the projected session no longer relies on the client replaying its create to drive preparation.
The tellahq installation hit its hourly GraphQL limit about once a day. Nearly all of the spend was pr-info's `gh pr view <branch>`: gh resolves a branch by wrapping the whole field set in `pullRequests(headRefName:, first: 30)`, and with `commits` (100 x 100 authors) GitHub prices that at 32 points. The same fields by number cost 1. CI webhook bursts made it worse: every check_run, status and workflow_run delivery dropped the detail cache and broadcast pr_updated, so open tabs re-read the PR once per delivery for as long as a pipeline ran. - pr-info: resolve the branch to a number first (bulk cache, then a memo seeded from the durable snapshot, then a one-point lookup) and view by number. A memoized number is verified against headRefName once. - pr-webhook: fold CI deliveries per branch into one refresh per 30s window, invalidating at broadcast time. PR, review, comment and push deliveries still refresh promptly. - github-budget: report the bucket as consumers' own GraphQL responses see it (rateLimit is free to request), next to a probe that resolves its token through the same service credential path. The old probe logged used=0 through every exhaustion.
A chip's text is folded into the prompt verbatim, so pasting a few megabytes put the whole block in context and the turn failed before the model saw a word of it: "Prompt is too long" on a single exchange, which compaction cannot shorten. Past 200k characters (about 50k tokens) the web composer now stages the paste as pasted-text.txt through the same upload path a dropped file takes. The agent gets the usual attached-file note and reads it with its tools; the transcript shows the file chip and a download. Below the line a paste still collapses into a chip, and a team note keeps the chip at any size because it has no file channel. Started by Michiel Westerbeek in this OS session: https://os.tella.dev/session/os-01a0671c-d75e-79c1-bd39-3f19f3713358
A distill timer used to wait in-handler on a process-wide serial queue, holding one of the shared kernel timer slots for the whole wait plus the model call. Many sessions going idle together could occupy every slot and delay scheduled prompts, agent waits, and ask escalations. The handler now tries to take the single distill slot; when it is taken it replaces its own timer 15-30s out and returns immediately.
Local main fell 20 commits behind origin/main because git pull --ff-only refuses the shared checkout as soon as one dirty file overlaps an upstream commit, and nobody may discard another session's edit. Sessions then rebased only their own commit or committed from a throwaway worktree, and the shared branch drifted for everyone; 44 of the 61 blocking files were byte-identical to origin/main, residue of edits that had already landed. scripts/shared-checkout-sync.ts fast-forwards main in place: clean paths follow upstream, edits that already landed become clean, genuine edits are three-way merged onto the new base (index and worktree separately, with a pre-merge copy under .git/shared-checkout-sync/), conflicts are left untouched and listed, and the ref moves last by compare-and-swap. AGENTS.md now makes it the only sanctioned way to move main, requires it at task start and after every push, and forbids the worktree and rebase-only workarounds.
The skill is now written against the pinned [email protected] rather than the early v4 beta the upstream (joelhooks/effectts-skills, MIT) targeted, and it carries an Open Session-specific reference for the frontend lifecycle runtimes. Allowlist it next to the other tracked skills and ignore .agent-sources/, the source mirror it asks agents to clone for API lookups.
Add the hosted Braintrust MCP (https://api.braintrust.dev/mcp, bearer API key header) to mcp-config.example.json and the MCP-only integrations section of the setup docs. Started by Michiel Westerbeek in this OS session: https://os.tella.dev/session/os-01a06762-a6ea-70a4-a59c-c70fc533c2b1
Install (ubuntu-latest) has failed on every main run since 2026-08-28 with only "the server did not start at http://127.0.0.1:3850" and a hint to run `opensession logs`, which nobody can do on a finished runner. On failure, dump the state and journal of every user unit the installer created, the listeners, the health probe, and the log files, so the log says why the service did not come up.
The supervisor bound its control socket at the fixed /run/opensession-gateway/control.sock. That directory is what RuntimeDirectory= creates for the system service; a rootless user install gets $XDG_RUNTIME_DIR/opensession-gateway instead, so the user unit died at boot with ENOENT on every start, the socket unit kept 3850 open with nothing behind it, and the installer reported a server that never came up. This is what has failed Install (ubuntu-latest) on every main run since 2026-08-28. Derive the path from RUNTIME_DIRECTORY, which systemd sets for both scopes, keeping the fixed path as the fallback outside systemd and the explicit OPENSESSION_GATEWAY_CONTROL_SOCKET override on top.
reviewerRemovalClearsSessionRequest resolves the request's first name to a GitHub login through the roster, which user-mappings bakes from this host's config at module load. The test assumed a host where "Kent" maps to kentdebruin, so it passed on the instance and failed on every CI run since it landed. Seed a fixture roster the way the analytics tests do. (cherry picked from commit 82a3b70)
Two more stale spots in CI that main's earlier failures had been hiding: The transcript motion step polled only the fixture server, then handed CDP_PORT to a fuzz tool that dials once and does not retry, so a Chrome that was still starting on a cold runner failed the step with a ConnectionRefused and no Chrome log. Poll the debugging port as well, and print Chrome's and the fixture's logs when the step fails. The installer job's service-definition check still looked for the pre-supervisor ExecStart, so once the user service actually booted the job fell over on that assertion instead. Accept the supervisor entry.
Mid-fling on the phone viewport the probe lands on a range placeholder rather than a mounted entry, and its anchor id is `range:` plus the id of the entry that opens the range. turnOf() only knew bare hydration ids, so the mobile pass asserted "no fixture entry far enough above the reader" on every CI run since the check was added. Accept the prefix: the range starts at that turn, which is what a reader parked there sees.
Summary
Verification
verify-20260902-060134-1065969bun run checkbun audit --audit-level=highbun run test:transcript-scrollProduct gaps found
bks-demo-prshowsNo transcript available for this sessioneven though the demo fixture supplies transcript linesRestorebutton did not clear the archived state; desktopRestore sessiondidStarted by Jaap Frolich in this OS session