Repository navigation
Shell: the Organization rail — Environment picker with the gear, clean Apps tiles with the ⋯ menu, favourites, Marketplace and + Nový modul for Owners (F36 addendum, DEV-6639) - #154
Conversation
…n place (F36 addendum, DEV-6639) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tes and Marketplace, clean tiles with the ⋯ menu, toasts, + Nový modul behind GitHub's Owner answer Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…, actions and favourites; GitHub's Owner answer, fail closed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… route Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… built; vendor pin 5bbc1f78 with unchanged bytes Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
|
agentrozjedemeai
left a comment
There was a problem hiding this comment.
Review on exact head b718089 — CHANGES_REQUESTED. Two findings:
-
[P1] Preserve canonical Organization slugs in the rail (
src/launchpad/shell-document.ts:67-73). This filtersorganization.organizationwith the GitHub-login regex, although the canonical Organization manifest accepts a distinct slug (andsrc/shell/contract.ts:131accepts_and.). With a real synthetic Folder fixture, an executable Organizationalpha_internalbound to GitHubalpha-forgeappeared in the catalog with a module but was absent fromshellOrganizations; neither the rail nor the Environment picker can reach it. Validate the slug according to its own supported contract, and validate onlyforgeLoginas a GitHub login. Add a regression using a canonical slug different from its bound login. -
[P2] Expire the browser owner answer (
src/launchpad/catalog-panel.ts:493-515). The server cache expires after five minutes, butowners: Map<string, boolean | "reading">retains eachtrueorfalsefor the entire page lifetime and never asks the server again, even afterrefresh()or a changed catalog. After the Environment loses Owner membership (or the bound login changes), "+ Nový modul" remains visible indefinitely until reload, contrary to the live/five-minute rule. Conversely a newly signed-in Owner never sees it. Revalidate on expiry/refresh and invalidate the answer when its login changes; this is a stale UI offer, not a server-side access grant.
Evidence: 32 focused tests passed; lint, typecheck, public check, proof build and smoke passed. Local owner HTTP probe: unadmitted 403, admitted active admin 200, POST 405. The server-side TTL returned true before expiry and false afterward; the browser has no corresponding expiry. CI is green on both platforms. No merge.
…the page asks the Owner answer again after five minutes, a login change or a catalog read The producer held Organization slugs to the GitHub login rule, so a canonical slug with `_` or `.` (bound to a different login) dropped out of the rail and the picker. The contract now admits the manifest's slugs (nonblank, at most 128, no control characters, no surrounding space); only `forgeLogin` is a login. A real-Folder regression reads `alpha_internal.v2` bound to `alpha-forge`. The page kept each Owner answer for its whole life. `owner-answer.ts` now holds the page's copy: it stands as long as the Launchpad's (one shared constant), is asked again after it, per bound login, and after every catalog read, keeping the last answer while it is asked again. Unit tests cover the expiry, the login change and the reset. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…, section rhythm, 1152 px page, the workstation named by its computer; the picker's list in the top layer, the host's tone and a narrow host Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Visual polish round on head |
agentrozjedemeai
left a comment
There was a problem hiding this comment.
Review of exact head 6a0d654: CHANGES_REQUESTED. The canonical-slug fix is correct and the Folder regression passes. The owner-answer cache now keys by login and expires when read, but the page still does not uphold the stated five-minute live answer.
-
[P2] Revalidate the visible Owner tile when its answer expires (
src/launchpad/catalog-panel.ts:497-519, 892-965;src/launchpad/owner-answer.ts:22-30). Expiry is checked only fromownerOf()during a render; neither the owner answer nor the Apps page schedules a render at the deadline. Leave the Apps home open after an active admin answer and revoke that membership: at five minutes no request fires and "+ Nový modul" remains indefinitely until an unrelated route/render/refresh. The unit test callsread()manually after advancing its fake clock, so it misses this real-page behavior. Arrange a bounded expiry wakeup/revalidation (and cleanup if needed), then prove the rendered tile updates without navigation or reload. This remains only a UI offer, not a server authorization bypass. -
[P2] Ignore obsolete Owner responses after a catalog refresh (
src/launchpad/owner-answer.ts:32-43,src/launchpad/catalog-panel.ts:503-517, 969-983).clear()erases the map but does not invalidate outstanding GETs;refresh()immediately renders the old catalog, starting a new GET before the new catalog arrives. If an older GET A returns after the refreshed GET B, itssettle()overwrites B for the same Organization/login and can restore a staletruefor another five minutes. Reproduced on this exact source: start A, clear, start B, settle B=false, settle A=true =>read()returns{owner:true,ask:false}. Guard settlements with a request generation/identity and cover this ordering in a regression. -
[P3, documentation]
docs/decisions.md:3609-3615still says a workstation is "Tento počítač" and is never named by a machine, whereasshell-document.ts:159-165, 196-200now labels it with the computer hostname. If that polish is intentional, update the F36 addendum's naming contract so the decision and implementation agree.
Evidence: isolated checkout at the SHA, git diff --check clean, 31 focused tests pass (slug, shell, Owner, Apps, routes); Ubuntu/macOS CI green, 0 review threads and no other pending reviewer. An attempted broader local gate exceeded the command wait and produced no usable result; not claimed as passed. @immakermatty please address these findings together on a new head and request re-review. No merge.
… answer to the question it is asking; the workstation's name in F36 Pablo's round 2: the page checked an Owner answer's expiry only while it rendered, so an open Apps home kept "+ Nový modul" past the five minutes; and a late answer to a question asked before a catalog read could overwrite the newer one. `owner-answer.ts` now numbers each question and settles only the one being asked (a catalog read or a newer question drops the older), and names the earliest expiry; the page sets one timer for it and renders then, which asks again. Unit tests cover the stale and overtaken answers and the expiry. The F36 addendum names a workstation by its computer, as built. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
agentrozjedemeai
left a comment
There was a problem hiding this comment.
Review of exact head 1fd6c2f: CHANGES_REQUESTED.
The numbered-question settlement fixes the obsolete-response race; the F36 workstation naming now matches the implementation. The expiry timer fixes the single-Organization case, but a previously visited Organization can consume its one scheduled wakeup.
[P2] Re-arm the Owner-expiry timer after an expiry wake when the expired answer is not the visible Organization (src/launchpad/catalog-panel.ts:525-533, 627-647). Reproduction: visit Organization A; its Owner answer settles at t=1 s (expires t=301 s). Switch to B; its answer settles at t=2 s (expires t=302 s). wakeForOwners() schedules the only timer at t=301 s. At that time render() reads only B; B is still valid and starts no GET, so no .finally() re-arms the timer. B stays open after t=302 s with its old "+ Nový modul" tile and no recheck until unrelated interaction. Source-level probe of the actual createOwnerAnswers() at t=301 s returned B {owner:true,ask:null} and nextExpiry()=301000, although B expires at 302000. The timer callback only invokes render(). Prune inactive answers or advance/re-arm the timer across expired off-screen answers without a one-second spin, and cover A→B navigation plus B expiry in a regression.
Evidence: isolated checkout pinned to 1fd6c2f; git diff --check clean; 27 focused tests, lint and typecheck pass. Both GitHub CI jobs pass. A broader local run timed out waiting/executing and is not claimed as passed. Zero unresolved threads and no competing reviewer request at the pre-write gate. @immakermatty please address this remaining P2 on a new head; no merge.
…an expired answer off screen sets no wake Pablo's round 3: visiting Organization A then B left one timer at A's expiry; at that wake only B rendered, B still stood, nothing re-armed, and B's expiry was missed. `nextExpiry()` now names the earliest answer that still stands, and the wake re-arms after rendering, so the page wakes for B next; an expired answer of an Organization not shown is asked again only when shown and keeps no timer spinning. A regression walks A → B → both expiries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
agentrozjedemeai
left a comment
There was a problem hiding this comment.
Review of exact head 843ae05: APPROVED.
The remaining P2 is fixed: nextExpiry() skips expired off-screen answers; the expiry callback renders the visible Organization and re-arms for the next standing answer. In the A (t=1 s) → B (t=2 s) regression, A's wake at t=301 s leaves B valid and schedules t=302 s; B's wake starts its new question without an off-screen timer loop. The prior obsolete-response guard and F36 workstation naming remain unchanged in this repair delta.
Evidence: isolated clean checkout at this SHA; 38 focused tests across Owner, Apps, shell and routes passed; git diff --check clean. Exact-head GitHub CI passed on Ubuntu 24.04 and macOS 14 (full lint, typecheck, test, public check, proof build and smoke). Zero unresolved review threads and no competing requested reviewer at the pre-write gate. A separate local full-check attempt exceeded the tool wait, so I do not claim it passed locally; CI supplied the full-gate result. @immakermatty exact-head QA complete. No merge performed.
…ctual identity Pablo's review: the target said Team and Automated Environments act under the Team's identity. A Team Environment acts through the brokered Lazurio for GitHub app within its Team's grants (root 0147, 0165); an Automated Environment through its persona's own GitHub account (root 0169). The tile follows GitHub's live answer for the actual identity and fails closed, which is the Owner check #154 built (F36 Organization-rail addendum, 8). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nt picker with the gear, quiet selection, clean Apps column and tiles (DEV-6639) (#150) * Docs: the Lazurio shell's second iteration — rail of spaces, Environment picker with the gear, quiet selection, clean Apps column and tiles (DEV-6639) Records the target decided on 2026-10-04 (shell wireframe review, confirmed by Matěj) in the target shell of launchpad-development.md and as an addendum to decision F36: the rail switches spaces (personal space and one GitHub avatar per Organization, account photo at the bottom, ink ring), the column head carries the Environment picker with the Settings gear, Environment names say what the Environment is for, selection is the design system's quiet surface, and Apps lands in the last Environment with a column of Všechny moduly, Soubory, Oblíbené and Marketplace, clean tiles with a "⋯" menu, favourites and "+ Nový modul". Passages rewritten by #145 (the two sections) are left to it. Workspace composition 7.5, the parity's visual-design note and the marketplace status follow. Nothing is implemented; issues #146, #147 and #148 track it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Docs: cite root decision 0185 for the shell's second iteration The target shell and the F36 addendum named root decision 0179 as what the second iteration supersedes, but not the root record of the iteration itself: decision 0185 (HumanAndMachines/Lazurio#487). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Docs: F36 point 1 records the first build; the Organization-rail addendum supersedes it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Docs: + Nový modul follows the live capability of the Environment's actual identity Pablo's review: the target said Team and Automated Environments act under the Team's identity. A Team Environment acts through the brokered Lazurio for GitHub app within its Team's grants (root 0147, 0165); an Automated Environment through its persona's own GitHub account (root 0169). The tile follows GitHub's live answer for the actual identity and fails closed, which is the Owner check #154 built (F36 Organization-rail addendum, 8). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Why
Matěj and Anička tuned the shell wireframe on the night of 2026-10-03/04 (
HumanAndMachine-ai/prototypes-lazurio1cbad15, #4). Matěj asked for the Launchpad to follow its default navigation, the Organization rail, "cleanly, with no leftovers of the original wireframe concepts".The reasoning behind the new model:
The release goes to the hosted Environments tonight, and the people's PDF guide is re-shot from it.
This builds on #145 (Workspace / Productionspace, merged) and replaces the parts of #142 (F36) that the new model supersedes. Root decision 0179 (the rail of Environments) is being amended by the Operator to match.
What changes for people
Rail = Organizations. From the top:
Labels still show at once. A click on a space goes to the last Environment you were in there, else its first, in the same app. There are no Environment icons, folders, "+N", colour fills or gear in the rail any more.
Column head. The Environment picker shows a glyph, the Environment's name and who it is for. It never shows the machine's name: a Team Environment is "Team ", an Automated one its persona, a work one "Pracovní", a workstation "Tento počítač".
dashboard.lazurio.ai/orgs/<slug>). Then come its Environments, the current one checked "tady jsi", and "Všechny Organizace" widens the list.Apps column:
Apps home:
Tile click:
Favourites sit in the column and come first in their section, in the column's order.
localStorage, one list per Organization."+ Nový modul" is the last Workspace tile.
gh api user/memberships/orgs/<forge login>→ active,admin. The Launchpad reads it behind the same admission as every read (GET /api/organizations/<org>/owner), with a timeout and a 5-minute cache, failing closed.newModulePromptgoes to the clipboard, this Environment's Chat opens in a new tab (paired), and a short message says to paste it into a new chat. A workstation only copies.Cleanly removed
The contract
lazurio.shell.v1changes in place, with no compatibility layer. Its producer and elements ship in one binary and no fork reads it yet:labelis its own name or null, and it gainsassignee;accentand gainsdashboard;avatar;addOrganization.Deliberately not changed / not in this PR
forgeLogin, the update pill, admission rules and the length bounds.infrastays hidden, per F32's final addendum and root Launchpad: the Environment pairs its own browser into Chat (GET /.lazurio/chat/pairing, operator scopes) #486. The wireframe at 1cbad15 still draws an Infra tile; prototypes-lazurio#5 tracks removing it./orgs/<slug>/application-accesssets a person's access level inside an application (OIDC client: none / user / admin), not which Teams reach a module's repository. Linking there would be a wrong target. A Steward check also needs a per-repomaintainread.Part of #147 and #148.
Docs
docs/launchpad-development.md.Verification
CI=true bun run check: see the latest comment. One full run had two unrelated load-flaky tests,update-compiledandmodule-default-preparation, which pass alone; the rerun is green.tests/shell.test.ts: contract and parser, the producer's names (Team, persona, kind, assignee) and Dashboards, the rail's spaces and their targets per app, and the picker's list (one space, all, filtered).tests/apps-view.test.ts: sections without subtitles, clean tiles, actions with the wireframe's messages, favourites first and the stored list read defensively, and the new-module prompt.tests/organization-owner.test.ts: only an active admin counts; cached; a member, a refusal or a timeout is no; no call on a Team Environment.tests/launchpad-shell-routes.test.ts: admission of the Owner route.bun scripts/preview-launchpad.ts hosted|local cs|en <port>; the preview'sghanswers the Owner question for the example Organization). Clicked through:https://<module>.<vm>…/in a new tab;Visual polish to the wireframe (round 2)
Measured against clean wireframe shots at 1cbad15, 1440×900:
lz-tile--row).app/v1/src/Icon.tsx, iconoir-react 7.12.1), drawn at stroke 1.5. The licence is vendored atsrc/shell/vendor/LICENSE-iconoir.txtwith its hash in the README and the vendor test. The Lucide paths are gone.MacBook-Pro), second line "tento počítač", as the wireframe's workstation entry. A hosted Environment is still never named by its machine. Chat and Automate stay disabled there: a workstation has no recorded Chat or MausBot origin.github.com/<login>.pngwhen the login is bound; without a bound login there is nothing to ask without a network call.For the forks (Lazurio/OpenMausBot#20), in the library:
<lazurio-column-head>detects it from the nearest ancestor with an opaque background, by luminance, on connect and on aprefers-color-schemechange, and sets:host([data-host-tone=dark]): no frame, light text, argb(255 255 255 / .1)hover. A host may set--lazurio-host-tone: dark | light, which wins. The slot stays<lazurio-column-head active="automate">.popover="auto", light dismiss), so a host's overflow or sticky bar cannot clip or cover it.Verified in Chrome: the dark host is detected and the custom property wins; the 80 px host shows glyphs only; the popover opens above a clipping, stacking column and closes on Escape.
Risks
ghbeing signed in as the person. Without it the tile is simply absent./o/<slug>). The other Environments in the rail come with the Dashboard.🤖 Generated with Claude Code
Note
Rebuild shell rail as Organization spaces with Environment picker and Apps favourites (F36 addendum)
Switchercomponent, next to a Settings gear and the app tabs. See elements.ts.⋯overflow menu for favourites and info links, per-Organization browser favourites, toasts, and an owner-gated+ Nový modultile that hands a prompt to Chat. See catalog-panel.ts./api/organizations/<name>/ownerandcreateOwnerCheckin server.ts and organization-owner.ts. Only an active admin GitHub membership grants the New module tile; all failures fail closed./marketplace, new English/Czech copy, shell icons, and design decision documentation in decisions.md.assignee,dashboardURLs,addOrganization, and operatoravatarfields; malformed shell documents are rejected in contract.ts. Organization accents are removed. Catalog search, refresh control, section subtitles, and the module/repository tree no longer render.Macroscope summarized b718089.