Skip to content

Launchpad: Nová karta on /new-tab, the shell element <lazurio-new-tab> and the panel's strip of every tab (F47 step 3) - #324

Closed
immakermatty wants to merge 6 commits into
agent/DEV-6656-917-launchpad-overviewfrom
agent/DEV-6656-917-new-tab-ui
Closed

immakermatty wants to merge 6 commits into
agent/DEV-6656-917-launchpad-overviewfrom
agent/DEV-6656-917-new-tab-ui

Conversation

@immakermatty

@immakermatty immakermatty commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Why

F47 step 3 (DEV-6656 task 917, DEV-6646 task 2026-10-11-501), on top of step 1 (#316) and step 2 (#318), both approved. Root decision 0195 points 9–12 and the final wireframe (prototypes-lazurio #27) give "+" in every app's right panel one Nová karta. It has an address bar, the app's own tools, Přehled (the Environment's memory, every tab, the running Modules) and suggestions. In the Launchpad the strip shows every tab of the Environment. This PR builds the page, the shell element every app embeds, and the Launchpad's panel. T3 Code's side is step 4, in the fork.

What changes

  • /new-tab, Nová karta's page (src/launchpad/new-tab.html, new-tab-page.ts, and the pure new-tab-view.ts), served locally and behind the gateway. Behind the gateway it answers Content-Security-Policy: frame-ancestors 'self' with the entry's app origins (Launchpad, T3 Code, MausBot as present) and nosniff. No other page changes its headers.
    • The address bar:

      • a link to this Environment's tab opens that tab;
      • another Environment's view is never opened here; a notice links it into the person's own browser;
      • an http(s) or host-like address opens as a page;
      • anything else is a search.

      A remote tab is created only then.

    • Přehled is read every 5 s while visible and never while hidden:

      • the memory with the Dashboard's 80/90 % thresholds;
      • the tabs, heaviest first, with who opened them, "agent tu pracuje", "v tomto vlákně" and the heap marked "≈";
      • the Modules with Zastavit.

      It never shows an unknown value as 0. It says when the hub or the Launchpad does not answer, and when the Environment has no browser.

    • Zavřít warns when an agent works in the tab. The tab leaves the list at once and Vrátit stays for the 8 s grace; a later poll neither brings the tab back nor takes Vrátit away.

    • Standalone (Matěj 2026-10-11, for Codex Desktop's browser): a page that is not framed, or hears no host within 1.5 s, is the Launchpad's Nová karta.

      • The address bar is focused, and a choice navigates that window to the tab's view.
      • A page opened there is held for the Launchpad; choosing an existing tab holds nothing.
  • <lazurio-new-tab> (src/shell/new-tab-element.ts, protocol src/shell/new-tab-protocol.ts), defined with the shell's elements. Interface v1 gains, additively, the element (strip, tools, focus-address, lang) and the event lazurio-new-tab.
    • It frames /new-tab on the current Environment's apps origin, sandboxed, with no referrer. It answers ready with the strip, the tools, the language and the host's colour roles (only colour values, nothing that loads), and dispatches the choice. It never navigates the host.
    • Origins on both sides:
      • the element hears only its own frame on the page's origin;
      • the page hears only its parent on an origin of the Environment's apps, and posts only there;
      • every message is validated field by field.
  • The Launchpad's right panel (browser-panel.ts, browser-panel-view.ts):
    • the strip lists every tab of the Environment from the overview, read every 5 s while open;
    • "+", the first opening and the close of the last tab show Nová karta (strip="launchpad", focus-address);
    • × lets go for the Launchpad, with Vrátit (second round below);
    • a page's new tab that a view reports is held for the Launchpad (POST /api/new-tab/hold) and shown; lazurio-browser:closed drops it;
    • a closed panel mounts no frame and reads nothing;
    • the existing reload, "open in a new tab", the sheet below 1100 px and the inert page behind it stay.
  • scripts/preview-new-tab.ts: a preview against fakes. It serves the Launchpad page with the panel, /new-tab standalone and inside a host page with T3-like tools, and switches for a hub that does not answer and for no browser.
  • docs/decisions.md: F47's step 3 note.

Second round (bb97f4c, 1d62d5b, e593960)

  • Pablo's review of ca58509:
    • a hold counts only as 200 {held: true}. A failed one says why, with "Zkusit znovu" where trying again can help;
    • a tab taken away stays hidden while its close or release is on its way, until an overview requested after the answer confirms it, and comes back if the request fails (both surfaces);
    • Vrátit lasts until the hub's deadline: Zavřít answers {closing, left} and the client counts left from the request.
  • Matěj's unified model (2026-10-12, root 0195 points 13–16, Docs: the Environment browser's architecture for apps and forks (0191 addendum) HumanAndMachines/Lazurio#526):
    • the Launchpad's strip lists only the tabs the Launchpad holds, in first-seen order;
    • a tab chosen in the panel's Nová karta is held, then shown, and so is an Integrace sign-in;
    • × lets go for the Launchpad (new POST /api/new-tab/release, on hold's serialized, fail-closed path) with Vrátit, and the toast says whether the tab closes or stays;
    • closing for everyone is only Přehled's Zavřít;
    • Přehled never hides a released tab;
    • F47 point 9 gains the dated addendum.
  • Formal address: step 3's Czech copy uses vykání, like the rest of the Launchpad on main and the offline guide (Offline guide: address the person formally (vykání); F41 describes the redesign #331).
  • bun run check on e593960: 2030 pass, 7 skip, 0 fail.
  • Note: Copy: address people formally (vykání) in Launchpad, shell, CLI and agents' speech #328 (vykání of messages.ts) shares one block of messages.ts with this PR, and string 8 there matches. Whichever merges second keeps this PR's version of the panel block. A dry run of the whole stack against current main (git merge-tree) merges without conflicts.

What doesn't change

  • No Machines or gateway change.
  • No Uspat, people's names or recent sites: F47 leaves them undecided.
  • T3 Code keeps its interim "+ → Browser" until step 4.
  • Steps 1–3 merge together when T3 Code's release with step 4 is ready.

Verification

  • bun run check on the final tree: my run passed (2020 pass, 7 skip, 0 fail). The implementing agent's earlier run had one timing failure in tests/tools-login.test.ts (sign-in code this PR does not touch), which passed when rerun alone.

  • New tests:

    • tests/new-tab-view.test.ts (25): address resolution, memory format and thresholds, Přehled rows and order, the toast keeping Vrátit, copy parity;
    • tests/shell-new-tab.test.ts (12): validators reject wrong types, shapes and kinds; origin checks on both sides; theme values; frame source;
    • tests/browser-panel-view.test.ts (+12): strip model, fallback to Nová karta, view messages only from the shown frame;
    • tests/launchpad-hosted.test.ts (+2): frame-ancestors with exactly the entry's app origins, and no other page's headers changed.
  • Browser checks in the preview:

    • standalone open and choice, with the holder file;
    • the foreign-link notice;
    • hub down, and no browser;
    • in the element: letter keys for tools, the page and tab choices as events with the right strip;
    • the agent warning (Escape keeps), Vrátit surviving polls, Zastavit;
    • the dark host theme;
    • the Launchpad panel's strip, "+", fallback, and the sheet below 1100 px.

    Not checked in a real browser: real view frames in the panel (synthetic origin in the preview; covered by tests), English, the team note, and gateway CSP enforcement (covered by the hosted test).

Known edges, follow-ups

  • While Nová karta shows in the Launchpad panel, the panel and the page each read the overview every 5 s. Step 5 measures it with the heaps.
  • Colours outside the theme roles stay light in a dark host.
  • A bare address of this Environment's own view (without /t/<id>) opens as a page, which shows the view inside its own browser. No foreign gateway session arises from it; refine with step 4.
  • A local workstation /new-tab has no token, so Přehled cannot load there. No workstation flow reaches it.

🤖 Generated with Claude Code

immakermatty and others added 2 commits October 11, 2026 21:58
… step 3)

Interface v1 gains the element <lazurio-new-tab> with its attributes strip,
tools (JSON), focus-address and lang, and its event lazurio-new-tab. An app
embeds it from /.lazurio/shell.js as it embeds the rail. The element frames
the Launchpad's /new-tab on the origin of the current Environment's apps,
answers the page's ready with the app's strip, tools, language and colour
roles, again whenever they change, and dispatches what the tab became: a
page or a tab with its view, or one of the app's tools. It never navigates
the app.

The protocol lives in one module both sides import
(src/shell/new-tab-protocol.ts). Each side reads every message field by
field and drops one of another type, shape or kind; the element hears only
its own frame on the page's origin, the page only its parent on an origin of
the Environment's own apps, and neither posts anywhere else. Nothing of
interface v1 changes; tests/shell-new-tab.test.ts covers the validators.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(F47 step 3)

/new-tab is Nová karta, after the approved wireframe. Its address bar opens
a link to this Environment's tab as that tab, never opens another
Environment's view (it says so and links there), and opens anything else as
a remote tab only then. Under it are the host app's tools with their
letters, then Přehled, read every 5 s while visible: the Environment's
memory with the Dashboard's thresholds, every tab heaviest first with who
opened it and whether an agent works in it, and the running Modules with
Zastavit. Navrhované and the note on shared sign-ins follow. Zavřít asks
first when an agent works in the tab and keeps Vrátit for the 8 s grace,
whatever a later message or overview says. Behind the gateway only the
entry's own apps may frame the page (frame-ancestors). Standalone, as
Codex Desktop opens it, it is the Launchpad's Nová karta: a page opened
there is held for the Launchpad, a tab chosen there is not.

The Launchpad's right panel lists every tab of the Environment while it is
open. "+" shows Nová karta, and so do the first opening and the close of
the last tab; × closes for everyone with the warning and Vrátit; a page's
new tab viewed there is held and shown. scripts/preview-new-tab.ts shows
all of it against fakes, with switches for a hub that does not answer and
an Environment without a browser; docs/decisions.md records step 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@immakermatty — CHANGES_REQUESTED on ca58509 (F47 step 3). Three functional blockers in the requested close/hold paths:

  1. src/launchpad/browser-panel.ts:563-566: holdAndShow discards the hold response and unconditionally shows the tab. /api/new-tab/hold can return 409 (holder full), 503 (holder unavailable), 404, or fail; in all cases the panel represents the tab as held although the Launchpad did not hold it, so the tab can expire when the view stops. Check for a successful 200/held: true before the held-tab transition, and surface failure/recovery. Add a failure-path test.

  2. src/launchpad/new-tab-page.ts:797-807,884 and src/launchpad/browser-panel-view.ts:276-283 with src/launchpad/browser-panel.ts:490-503: the local hidden override is cleared on the first overview requested after the click, even if that overview reads the hub before the close POST is accepted. Then the old tab reappears while the close is still in flight; neither success handler reapplies the hidden override, so it remains visible until a subsequent poll. I reproduced the panel reducer sequence: closing => only New Tab, requested/listed with pre-close tab => tab reappears, overrides empty. Keep the hide pending until the close response and a post-acceptance overview confirm the hub's closing state (or roll it back on failure), on both surfaces.

  3. src/launchpad/new-tab-page.ts:783-801 and src/launchpad/browser-panel.ts:467-498: both start a fresh 8-second Undo timer only after the POST response. The hub starts its grace at acceptance (src/browser/people/hub.ts:480-495) and /api/new-tab/close returns its closing deadline (src/launchpad/new-tab-routes.ts:352-359). If the response is delayed, Vrátit remains offered after that deadline and fails when clicked. Base the visible Undo lifetime on the returned deadline, adjusted for clock skew, rather than a new 8-second client timer.

Positive evidence: focused tests 60 pass / 0 fail across five files; bun run check:static passes; git diff --check clean. I found no blocker in the frame origin/source checks or the new-tab-specific hosted CSP. CI on this exact SHA is green. I did not merge.

immakermatty and others added 3 commits October 12, 2026 03:14
… 9 addendum)

Matěj decided on 2026-10-12 that the Launchpad's strip lists only the tabs
the Launchpad holds, in the order it first had them; Přehled keeps listing
every tab. × in the strip now lets go of the Launchpad's hold through the new
POST /api/new-tab/release, on the holder file's own path (one operation at a
time, the file read again first, fail closed; a tab not held counts as
released). Vrátit holds the tab again within the hub's 8 s grace, and the
toast says whether the tab closes or stays because an agent works in it or
another place holds it. Closing for everyone stays Přehled's Zavřít, with its
warning. A tab chosen in the panel's Nová karta, a page's new tab and an
Integrace sign-in show only once the Launchpad holds them. Nová karta shows
when the strip is empty, and the overview reports the Launchpad's own hold as
last written, so the strip never waits for the hub to read the file again.

Pablo's review of #324: a hold counts only as 200 {held: true}, and a failed
one says why, with "Zkusit znovu" where trying again can help. A tab taken
away stays hidden while its close or release is on its way, whatever an
older overview says, until an overview requested after the answer gives the
hub's word, and comes back when the request fails. Zavřít's answer carries
the grace left on the Environment's clock (`left`), and Vrátit counts it from
the request, so a late answer or another clock never offers Vrátit past the
hub's deadline.

F47 gains the dated addendum to point 9 (it changes root decision 0195
points 9 and 11, which the root register will record), and the step 3 note
describes the result. Tests cover the three review blockers on both
surfaces, the strip of held tabs, a choice held then shown, release with
Vrátit, the release route (serialized, fail closed, idempotent) and a
released tab still listed in Přehled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ly (vykání)

Step 3's Czech copy follows the brand rule of 2026-10-11 that the rest of
the Launchpad, the shell and the offline guide follow: lowercase vy,
plural imperatives, no past tense about the person. "Hledejte nebo zadejte
adresu", "Zavřete těžké záložky a zastavte Moduly, které nepotřebujete.",
"Zadejte adresu nahoře.", "…zkuste to za chvíli znovu.", "Používejte týmové
a firemní účty.", "Zavřete některé a zkuste to znovu." and, in the panel,
"…otevřete ho v nové kartě, kde se můžete znovu přihlásit." (worded as in
#328, so the two changes meet cleanly). The wireframe's informal address is
superseded by the rule; meaning, length and tone stay. English is
unchanged.

The step 3 note in docs/decisions.md says so. Tests assert the new words.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… holds

scripts/preview-new-tab.ts still said the panel's strip lists every tab,
which the addendum of 2026-10-12 to F47 point 9 changed. Only the preview's
own description changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@immakermatty

Copy link
Copy Markdown
Contributor Author

@agentrozjedemeai All three blockers from your review of ca58509 are fixed in bb97f4c (line numbers at e593960). Re-review requested once CI is green.

  1. A hold counts only on success.
    • browser-panel-view.ts holdSucceeded: only 200 with held: true counts.
    • holdFailure maps 404 to "gone" with no retry, 409 to "full" with retry, and holder-unavailable or no-browser to no retry.
    • browser-panel.ts holdThen shows the tab only after success, otherwise a toast with the reason and "Zkusit znovu".
    • It is used for a tab chosen in Nová karta, a page's new tab and Vrátit.
    • Test: "a hold counts only as 200 {held: true}".
  2. The hide stays until a later overview confirms it, on both surfaces.
    • new-tab-view.ts hideAsked/hideAnswered: an override with after: null (the request still on its way) is never lifted by settleOverrides.
    • On acceptance it lifts only after an overview requested after the answer. On failure it is removed and the tab comes back.
    • Tests: "Zavřít keeps the tab gone while the close is on its way", and "× lets go: the tab stays gone while the release is on its way (Pablo's case), and a failed release brings it back".
  3. Vrátit ends at the hub's deadline.
    • The close route answers {closing, left}, with left measured on the Environment's clock.
    • undoDeadline counts sentAt + min(left, 8 s) from when the request was sent, so Vrátit never outlives the hub's grace.
    • A 404 counts as gone, with no Vrátit.
    • Tests: "Vrátit lasts until the hub's deadline, counted from the request", and "close answers how long its Vrátit has left".

Matěj also decided a unified model on 2026-10-12 (root 0195 points 13–16, HumanAndMachines/Lazurio#526, which you approved on ca394e68):

  • the Launchpad's strip lists only the tabs the Launchpad holds;
  • × lets go for the Launchpad through the new POST /api/new-tab/release, on hold's serialized, re-read-first, fail-closed path, idempotent, with Vrátit;
  • closing for everyone is only Přehled's Zavřít;
  • a tab chosen in the panel's Nová karta, and an Integrace sign-in, are held before they show;
  • Přehled never hides a released tab;
  • F47 point 9 gains the dated addendum.

1d62d5b moves step 3's Czech copy to formal address, as the rest of the Launchpad on main and #331 do. e593960 fixes one stale line in the preview.

bun run check on e593960: 2030 pass, 7 skip, 0 fail.

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@immakermatty — CHANGES_REQUESTED on e593960 (F47 step 3 re-review).

Blocker: src/launchpad/server.ts:571-576 silently substitutes Promise.resolve() for a missing Launchpad holder when wiring the Integrace sign-in. environmentBrowserOf (src/launchpad/server.ts:181-201) then creates a browser target, treats that no-op as a successful hold and returns its view; the UI showView (src/launchpad/browser-panel.ts:580-595) displays the tab immediately. A Remote Environment with a valid browser entry but no usable XDG_RUNTIME_DIR therefore shows a sign-in tab without the Launchpad holding it. It disappears from the hold-only strip after the next overview and can be ended after the viewer leaves, contrary to the new held-before-shown contract. Unlike /api/new-tab/open, which rejects holder === null with 503, this path fails open. Fail closed when there is no holder and test the sign-in path with that configuration; keep the existing hold-failure recovery.

The previous three blockers are addressed on this head: holds require 200 {held:true}, in-flight close/release hides persist across stale polls and roll back on failure, and close Undo uses the returned left measured from request time. I verified frame source/origin filters, panel view-message origin/window checks, /new-tab-only CSP and the standalone hold distinction. Focused suites: 95 pass, 0 fail when run serially (35 + 60); bun run check:static and git diff --check pass. CI on this exact SHA is green. The initial combined run with --concurrent 1 had shared-fixture cleanup races (20 failures); a serial rerun of that file passed 35/35. No merge.

…not hold (Pablo's review of #324)

Wiring the Integrace sign-in substituted a no-op for a missing Launchpad
holder, and environmentBrowserOf defaulted its hold to one. A Remote
Environment with a browser entry but no usable XDG_RUNTIME_DIR therefore
showed the sign-in tab unheld: it left the hold-only strip at the next
overview and could end after its viewer left. launchpadHoldOf now refuses
without a holder, as /api/new-tab/open answers 503 then, and
environmentBrowserOf takes its hold without a default. The existing
hold-failure path answers that the browser cannot open it now.
environmentBrowserOf also takes its DevTools call as a seam, so the sign-in
path is tested: held then shown, a failed hold, no holder (fails closed), and
an entry without the view creating no tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@immakermatty

Copy link
Copy Markdown
Contributor Author

@agentrozjedemeai Fixed in 59291c4. Re-review requested on that head once CI is green.

  • launchpadHoldOf(holder) (src/launchpad/server.ts) refuses when there is no holder (no usable XDG_RUNTIME_DIR), as /api/new-tab/open answers 503 then.
  • environmentBrowserOf takes its hold without a default, so nothing can fall back to a no-op.
  • The existing hold-failure path answers that the browser cannot open it now.
  • environmentBrowserOf also takes its DevTools call as a seam, so the sign-in path is now tested in tests/launchpad-integrations-sign-in.test.ts: held then shown; a failed hold answers null; no holder fails closed; a workstation and an entry without the view create no tab.
  • The F47 step 3 note says it.

Focused suites: 56 pass, 0 fail. Lint and typecheck pass.

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@immakermatty — APPROVED on exact head 59291c4 (F47 step 3, third round). The prior blocker is fixed: launchpadHoldOf(null) rejects; the production Integrace seam passes this required hold into environmentBrowserOf, which returns no view if it cannot hold the created target. The new tests cover held-before-shown, hold failure, missing holder and missing browser view. I traced the Integrace authorize failure response and panel handoff; an unheld sign-in is not shown. The earlier close/hold repairs remain intact: accepted holds only, pending hide through post-acceptance overview, close Undo from the returned time left, and release-only strip versus close-for-everyone in Přehled. I rechecked frame/source and origin gates, the view-message source/origin gate, /new-tab-only hosted CSP, hidden polling and standalone open-versus-existing-tab hold rules.

Fresh detached SHA checkout: focused test files passed individually (launchpad-integrations-sign-in 5; launchpad-new-tab 35; new-tab-view 28; shell-new-tab 12; browser-panel-view 16; launchpad-hosted 4; integrations-routes 18; integrations-page 9), all 0 failures. bun run check:static passed (lint, typecheck, public guard, proof build and smoke); git diff --check clean. GitHub CI for this exact SHA is green (static checks, both test shards and Ubuntu check); macOS check skipped by scope. No independent browser smoke in this round; prior visual evidence is the requester's, not my rerun. No merge: steps 1–3 wait for step 4's release.

immakermatty added a commit that referenced this pull request Oct 12, 2026
… 9 addendum)

Matěj decided on 2026-10-12 that the Launchpad's strip lists only the tabs
the Launchpad holds, in the order it first had them; Přehled keeps listing
every tab. × in the strip now lets go of the Launchpad's hold through the new
POST /api/new-tab/release, on the holder file's own path (one operation at a
time, the file read again first, fail closed; a tab not held counts as
released). Vrátit holds the tab again within the hub's 8 s grace, and the
toast says whether the tab closes or stays because an agent works in it or
another place holds it. Closing for everyone stays Přehled's Zavřít, with its
warning. A tab chosen in the panel's Nová karta, a page's new tab and an
Integrace sign-in show only once the Launchpad holds them. Nová karta shows
when the strip is empty, and the overview reports the Launchpad's own hold as
last written, so the strip never waits for the hub to read the file again.

Pablo's review of #324: a hold counts only as 200 {held: true}, and a failed
one says why, with "Zkusit znovu" where trying again can help. A tab taken
away stays hidden while its close or release is on its way, whatever an
older overview says, until an overview requested after the answer gives the
hub's word, and comes back when the request fails. Zavřít's answer carries
the grace left on the Environment's clock (`left`), and Vrátit counts it from
the request, so a late answer or another clock never offers Vrátit past the
hub's deadline.

F47 gains the dated addendum to point 9 (it changes root decision 0195
points 9 and 11, which the root register will record), and the step 3 note
describes the result. Tests cover the three review blockers on both
surfaces, the strip of held tabs, a choice held then shown, release with
Vrátit, the release route (serialized, fail closed, idempotent) and a
released tab still listed in Přehled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
immakermatty added a commit that referenced this pull request Oct 12, 2026
…not hold (Pablo's review of #324)

Wiring the Integrace sign-in substituted a no-op for a missing Launchpad
holder, and environmentBrowserOf defaulted its hold to one. A Remote
Environment with a browser entry but no usable XDG_RUNTIME_DIR therefore
showed the sign-in tab unheld: it left the hold-only strip at the next
overview and could end after its viewer left. launchpadHoldOf now refuses
without a holder, as /api/new-tab/open answers 503 then, and
environmentBrowserOf takes its hold without a default. The existing
hold-failure path answers that the browser cannot open it now.
environmentBrowserOf also takes its DevTools call as a seam, so the sign-in
path is tested: held then shown, a failed hold, no holder (fails closed), and
an entry without the view creating no tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
immakermatty added a commit that referenced this pull request Oct 12, 2026
… 9 addendum)

Matěj decided on 2026-10-12 that the Launchpad's strip lists only the tabs
the Launchpad holds, in the order it first had them; Přehled keeps listing
every tab. × in the strip now lets go of the Launchpad's hold through the new
POST /api/new-tab/release, on the holder file's own path (one operation at a
time, the file read again first, fail closed; a tab not held counts as
released). Vrátit holds the tab again within the hub's 8 s grace, and the
toast says whether the tab closes or stays because an agent works in it or
another place holds it. Closing for everyone stays Přehled's Zavřít, with its
warning. A tab chosen in the panel's Nová karta, a page's new tab and an
Integrace sign-in show only once the Launchpad holds them. Nová karta shows
when the strip is empty, and the overview reports the Launchpad's own hold as
last written, so the strip never waits for the hub to read the file again.

Pablo's review of #324: a hold counts only as 200 {held: true}, and a failed
one says why, with "Zkusit znovu" where trying again can help. A tab taken
away stays hidden while its close or release is on its way, whatever an
older overview says, until an overview requested after the answer gives the
hub's word, and comes back when the request fails. Zavřít's answer carries
the grace left on the Environment's clock (`left`), and Vrátit counts it from
the request, so a late answer or another clock never offers Vrátit past the
hub's deadline.

F47 gains the dated addendum to point 9 (it changes root decision 0195
points 9 and 11, which the root register will record), and the step 3 note
describes the result. Tests cover the three review blockers on both
surfaces, the strip of held tabs, a choice held then shown, release with
Vrátit, the release route (serialized, fail closed, idempotent) and a
released tab still listed in Přehled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
immakermatty added a commit that referenced this pull request Oct 12, 2026
…not hold (Pablo's review of #324)

Wiring the Integrace sign-in substituted a no-op for a missing Launchpad
holder, and environmentBrowserOf defaulted its hold to one. A Remote
Environment with a browser entry but no usable XDG_RUNTIME_DIR therefore
showed the sign-in tab unheld: it left the hold-only strip at the next
overview and could end after its viewer left. launchpadHoldOf now refuses
without a holder, as /api/new-tab/open answers 503 then, and
environmentBrowserOf takes its hold without a default. The existing
hold-failure path answers that the browser cannot open it now.
environmentBrowserOf also takes its DevTools call as a seam, so the sign-in
path is tested: held then shown, a failed hold, no holder (fails closed), and
an entry without the view creating no tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@immakermatty

Copy link
Copy Markdown
Contributor Author

Merged through #333 by rebase, on Matěj's instruction "Publikuj a releasuj". This PR's six commits are 9b0d1b3..165facf on main, patch-identical to 5aeb7f9..59291c4 per git range-diff.

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.

2 participants