Skip to content

Apps: the person's account decides where apps open and holds the favourites; Přístup k modulu for Owners and Stewards; the shell reports the last Environment (DEV-6639) - #159

Merged
agentrozjedemeai merged 8 commits into
mainfrom
agent/DEV-6639-apps-account
Oct 4, 2026
Merged

agentrozjedemeai merged 8 commits into
mainfrom
agent/DEV-6639-apps-account

Conversation

@immakermatty

@immakermatty immakermatty commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Plan DEV-6639, workstream WS-E of the follow-ups of root decision 0185. Refs #148, #151, #152; fixes #158. Built on F37 (#149) and its addendum of 2026-10-04 (the account namespace /.lazurio/account/…); the Dashboard side is HumanAndMachine-ai/Dashboard#184, #185, #194 (account API, setting, landing) and #186 (the module access page).

Why

Decision 0185 moves four things of Apps into the person's Lazurio account, because every Environment has its own origin and a browser's localStorage cannot follow the person:

  • S18: where module apps open, "V nové záložce" (default) or "Ve stejném okně", one setting for the whole account;
  • S12: favourites belong to the person and the Organization, so they follow the person to every Environment of it;
  • S8: the account remembers the last Environment, so sign-in and a click on a space return there;
  • S15: "Přístup k modulu" in a module's "⋯" menu for the Organization's Owners and Stewards, into the Organization's Dashboard.

The Dashboard serves the account and the Environment's gateway relays it (other workstreams). This PR is the Platform's consumer side, built so that it works the day the relay goes live and changes nothing before.

What changes

  • The account document (src/launchpad/account.ts). Apps reads GET /.lazurio/account/environments once per page load: same origin, credentials: "same-origin", no token, no redirect followed, given up after 4 s. Only schema: "lazurio.account.v1" counts. Apps takes preferences.openApps ("same"; anything else is "tab") and favourites keyed by the Organization slug, and ignores everything else (the rail's merge reads it).

  • Where apps open (S18, src/launchpad/app-opening.ts). These follow the setting:

    • tiles;
    • favourites in the Apps column;
    • "Otevřít aplikaci" on the module information page;
    • the lifecycle card's "Otevřít".

    "tab" opens a new tab, as today. "same" navigates this window to the app's own address. Nothing is ever framed. On a workstation the start-then-open flow keeps working in both modes: the tab is opened within the click, or this window moves only after the start succeeded. The new-tab arrow shows only with "tab".

  • Favourites (S12). With the account, an Organization's favourites are its list, in its order, first in their section with the star as before. The star writes PUT/DELETE /.lazurio/account/favourites/<org slug>/<module|repository>/<id> at once (optimistic). One favourite has one write under way at a time, sent in the order of the clicks (after Pablo's review, commit 387fb87):

    • a click made while a write is under way is sent after it, and only if it still differs from what the account holds, so the account ends as the last click wants;
    • a failure goes back, with "Oblíbené se nepodařilo uložit. Zkus to znovu.", only when no later click wants something else, and the favourite then shows what the account holds, in its place;
    • different favourites never wait for each other. Old localStorage favourites are not uploaded. localStorage stays only as the fallback without the account, and for the Personalspace group, which is no Organization.
  • The last Environment (S8, src/shell/last.ts, its own module in the library). It sends PUT /.lazurio/account/last with { environment, app, organization } once per full page load of Apps, Chat and Automate:

    • environment is the shell document's current;
    • app is the app the host names;
    • organization is the space's Organization slug, or null.

    It uses keepalive, does not read the answer, is silent on failure and never blocks drawing. elements.ts calls it with one line per element: the column head by active, the rail only when its host sets app. The rail/column-head merge code is untouched.

  • "Přístup k modulu" (S15, Apps tile menu: "Přístup k modulu" needs the Dashboard's page for Team access to one module #151). It appears in a module tile's "⋯" menu for:

    • Owners: the existing live Owner answer of "+ Nový modul".
    • Stewards: a new answer, GET /api/organizations/<org>/modules/<module>/maintain. It reads gh api repos/<owner>/<repo> → permissions.maintain through the same server-side path as the Owner check (shared askGitHub). It is bounded by the same timeout, cached five minutes per repository, asks one question per repository at a time, and fails closed. It is never asked on a Team Environment, without a bound login, without a declared repository, or for a repository outside the Organization's login.
    • When it is asked: the page asks only when the person reaches for a menu (pointer, focus, click), never for every tile, and the item joins the open menu when the answer arrives.

    The item opens https://dashboard.lazurio.ai/orgs/<dashboard slug>/settings?tab=modules&module=<module id> in the same window, without an arrow. Repository tiles and the Personalspace group never have it.

  • The module's repository. The catalog now carries it as url, read from the slot's git.url, the same way as a production repository's.

  • The Dashboard origin and Organization slug (Shell document: an Organization's Dashboard link uses the manifest slug as written, so it hits the Dashboard's not-found page #158). The Platform knows the Dashboard origin as the constant https://dashboard.lazurio.ai/ (shell-document.ts, as for the rail's logo and account). It knows the Organization's page from the shell document's organizations[].dashboard.

    • The defect: the Dashboard matches /orgs/<slug> exactly against its canonical slug (canonicalSlug: lowercase, other runs → -, from the same manifest slug). The producer used the slug as written. So the picker's Organization head already led to the Dashboard's not-found page for every Organization whose slug has capitals, which covers most real ones.
    • The fix: dashboardSlug() follows the Dashboard's rule, and the access item builds on it.
    • The known limit: the Dashboard's per-person --n suffix for two Organizations that reduce to one slug cannot be known here. Such a link ends on its not-found page and never on another Organization's page. The item is absent whenever the shell document has no …/orgs/<slug> page (fail closed).
  • Preview. scripts/preview-launchpad.ts … [owner|steward|member] answers gh by role, and its workspace modules declare repositories.

  • Docs. The F36 addendum gets a "built behind the person's account" note, and the shell-as-built section of docs/launchpad-development.md is updated.

What does not change

  • With no account (every Environment today: 404, a refusal, a slow answer), Apps behaves exactly as on main: favourites in localStorage, apps in a new tab.
  • No token in the browser or the Launchpad. The account requests are plain same-origin ones, and the gateway attaches the person's token.
  • The shell contract lazurio.shell.v1, the elements' interface (Shell: the elements' interface v1 is a promise to the forks (F36 addendum, DEV-6639) #155) and the rail's merge of the account's Environments (owned by another session) are unchanged.
  • No migration of browser favourites into the account.
  • newModuleTile and handOver (WS-D) are untouched.

Verified

  • Unit and HTTP tests (new):

    • tests/account.test.ts: the document valid, another schema, odd members, 404/401/502/HTML/network error → null, a 4 s-style timeout → null, write paths, writes, optimistic writes and rollback, and the same-favourite ordering (three quick clicks starring and unstarring with the first write refused, a click undone before its write answers, the last click's failure and its place, two favourites written independently).
    • tests/app-opening.test.ts: both modes, including the workstation's start: the tab opened within the click, closed on failure or when blocked; "same" moves only after the start.
    • tests/shell-last.test.ts: the body, once per load, no report without an app, silent on rejection and throw, and the default transport is a same-origin PUT with keepalive.
    • tests/module-maintainer.test.ts: maintain true and cached, one question at a time, and no for no maintain, a refusal, bad JSON or a timeout; no call on a Team Environment, without a login, without a repository, or for another Organization's repository.
    • tests/launchpad-shell-routes.test.ts: the new route's admission locally and hosted, and an end-to-end answer from a real fixture Folder, including the catalog's url.
    • tests/apps-view.test.ts: the item's target and its absence.
    • tests/shell.test.ts: the Dashboard's slug.
  • Checks on head 387fb87, rebased on main 857438f (Folder: agents send the Operator to the Launchpad for the GitHub sign-in and for Composio (F19 addendum, base-instructions-20) #157):

    • bun run check: lint and typecheck green; bun test 1265 pass, 7 skip, 1 fail.
    • The one failure was tests/folder-prepare-change.test.ts "shared update holds exclusion across preparation, activation and finalization". It hung for 423 s and timed out while the machine was loaded by parallel sessions (load average about 6). Rerun alone it passes (47 pass, 0 fail). It has nothing to do with this change.
    • Preflight reports ready_to_push.
    • Earlier, tests/install-script.test.ts failed during a run while the machine's disk was nearly full, and also on a clean origin/main checkout; it passed in the later green runs.
  • Browser (decision 0178). Chrome via Playwright against the hosted preview (cs), with the account relay stood in by intercepting /.lazurio/account/*, and against the local preview:

    • without the account: localStorage favourites, tiles with target=_blank;
    • "tab": the account's favourites in order, starred first, a click opens a new tab on the module's origin;
    • "same": no target, the click navigates the same page, no arrow in the column;
    • a star → PUT …/favourites/example/module/knowledgebase; a refused DELETE → the favourite back plus the message;
    • Pablo's triple click on one star, with the first write refused: PUT (502), then PUT (204). The star stays and no message shows;
    • one PUT /.lazurio/account/last per page load, { environment, app: "apps", organization: "example" };
    • the menu by role:
      • Owner: the item on every module;
      • Steward: only on modules whose repository answers maintain;
      • member: none;
      • in each case the item has no target and no arrow, and its click navigates the same page to …/orgs/example/settings?tab=modules&module=deals;
    • a workstation in both modes: a new tab after the start, or the same window after the start.

    The screenshots stay outside the repository.

Risks and follow-ups

  • Environment ids. PUT last sends the shell document's current as environment. Shell: an Environment's id is its base host, unique across Organizations (F37 addendum, DEV-6639) #161 makes that id the Environment's base host (<machine>.<org>, or the personal slug), unique across Organizations. This PR sends it with no change to its code. The Dashboard must derive the same value (Dashboard Reader role for installing an Organization; a read-only repository gets a proposal issue (F9 and F14 addenda, base-instructions-28) #198).
  • A module id the Dashboard reads differently. The Platform's module id is the slot's slug (else the path's last segment). The Dashboard's adapter takes a module_slots entry's id from the path's basename (a shape-B modules[].slug wins). They agree for the module tiles today, but WS-C should resolve module= by the Lazurio module id.
  • One read of the account per page. Once the merge lands, the Launchpad page and the library each read /.lazurio/account/environments (two GETs per page load of Apps). Reading once and handing it over, as provideShell does for the shell document, is a small follow-up for whoever lands second.
  • A menu open while the page redraws closes. This happens during its first second (Owner answer, shell document) and predates this PR. A Steward reaching for the menu in that second may need a second click.
  • Unverified until the relay is live. The real gateway relay, token and Dashboard answers are not exercised here. The rollout note of F37's addendum applies: a session from before the lazurio-account scope falls back silently until its next sign-in.

🤖 Generated with Claude Code

@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.

CHANGES_REQUESTED — exact head a86874f. One scoped correctness defect in src/launchpad/account.ts:151-182. Optimistic rollback uses only the current inclusion of the favourite; it cannot distinguish a later third click on the same key. Deterministic repro on this head: start empty; click the same star three times before replies (PUT, DELETE, PUT), leaving it visibly starred; resolve the first PUT false and the later DELETE/PUT true. The first failure removes the star, and the final visible list stays empty even though the last click and last successful PUT starred it. I ran this in the isolated checkout with bun and observed after three clicks [m:deals], first result false visible [], second/third true true final []. Please limit the fix to same-key concurrent write/rollback ordering (generation/version or serialization) and add a test covering three rapid toggles with out-of-order failure/success; preserve other keys and account fallback behavior. CI is green, but this defect blocks approval. No other scope expansion requested.

immakermatty added a commit that referenced this pull request Oct 4, 2026
…ick wins

Pablo's review of #159: the optimistic rollback looked only at whether the
favourite was shown, so it could not tell a later click on the same
favourite apart. Three quick clicks (PUT, DELETE, PUT) with the first PUT
refused and the later writes taken removed the star, although the last
click and the last successful write starred it. Concurrent writes could
also reach the account out of order.

Now one favourite has one write under way at a time, sent in the order of
the clicks. It keeps what the account last took and what the last click
wants: a click made while a write is under way is sent after it, and only
if it still differs from what the account holds. A failure is put back,
with the short message, only when no later click wants something else; then
the favourite shows what the account holds, in its place. Different
favourites, and the browser fallback, are unchanged. The tests cover the
three quick clicks starring and unstarring, a click undone before its write
answers, the last click's failure and its place, and two favourites written
independently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@immakermatty
immakermatty force-pushed the agent/DEV-6639-apps-account branch from a86874f to 387fb87 Compare October 4, 2026 09:47
@immakermatty

Copy link
Copy Markdown
Contributor Author

@agentrozjedemeai Thanks, the defect was real and is fixed in 387fb87 ("Apps: one favourite's account writes go one at a time, so the last click wins"). New head: 387fb87, rebased on main 857438f (#157).

Fix (src/launchpad/account.ts, createAccountFavourites): per-favourite serialization, which you named as one of the two options. It is the simpler of the two and is also sound when the account receives writes out of order.

  • One favourite has one write under way at a time, sent in the order of the clicks.
  • Per favourite the store keeps what the account last took (held), what the last click wants (wanted) and a click count.
  • A click made while a write is under way is sent after it, and only if it still differs from what the account holds.
  • A failure goes back, with the message, only when no click came after that write was sent. If a later click wants something else, that is written next and its success stands.
  • After a refused removal that the person undid, the favourite returns to its original place.
  • Other favourites and the localStorage fallback are unchanged.

Your repro (start empty, three quick clicks, the first PUT refused):

  • the star stays visible;
  • the intermediate DELETE is coalesced away, so the next write sent is the last click's PUT;
  • once it is taken, the list ends ["m:deals"] and no message is shown.

Tests (tests/account.test.ts, red on a86874f, green now):

  • three quick clicks starring, the first refused → ends starred (your case);
  • the mirror, unstarring → ends unstarred;
  • a click undone before its write answers (taken → taken back; refused → nothing more sent);
  • the last click's failure shows what the account holds, in its place;
  • two favourites written independently, and the same id in another Organization.

Browser check: the same triple click in Chrome against the hosted preview sends PUT (502) then PUT (204), and the star stays.

Checks: bun run check gives 1265 pass, 7 skip, 1 fail. The one failure, tests/folder-prepare-change.test.ts, hung while the machine was loaded and passes alone (47/47); it is unrelated to this change. Lint and typecheck are green, and preflight reports ready_to_push.

Could you review the unchanged head 387fb87?

@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 — exact head 4e257e9. The old three-click rollback defect is fixed by per-key serialization; the first refused PUT is followed by the still-wanted PUT, and unrelated favourites drain independently. Focused tests (57), standalone folder-prepare-change (47), lint, typecheck, and synthetic integration with #161 (47 tests plus lint/typecheck) passed in isolated checkouts. GitHub macOS and Ubuntu checks are green; zero unresolved threads. Prior CHANGES_REQUESTED on a86874f is superseded. Delivery hold: #161 merged first; the sole docs/launchpad-development.md textual conflict with live main needs a conflict-only child into this PR branch and a fresh exact-head review/check/thread gate before merging #159. This approval does not cover a changed parent head.

@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 — exact current head 514cf9b, including conflict-only #164 against main d2133f8 (#161). The merge tree is the same independently tested combined tree (9d2df9fe); the sole conflict was the docs union, with no new product intent. Three-click same-key rollback remains corrected, the hosted identity parser/producer remain fail-closed. On this exact head: 47 focused tests, lint, typecheck, check:public and git diff --check pass; GitHub macOS and Ubuntu checks green, zero unresolved conversations. My old a86874f change request and 4e257e9 approval are superseded by this exact-head review.

@agentrozjedemeai

Copy link
Copy Markdown
Collaborator

@annavesela @immakermatty Delivery hold on exact head 514cf9b: #161 is merged and conflict-only child #164 merged into this PR; current-main integration is clean. My exact-head APPROVED review is #159 (review), and both macOS/Ubuntu checks pass with zero unresolved threads. GitHub nevertheless reports reviewDecision=REVIEW_REQUIRED and mergeStateStatus=BLOCKED. The new parent head was authored by agentrozjedemeai via the integration child, so my own review cannot satisfy the remaining independent approval gate. I requested Anna as a reviewer; please check the exact current head and approve if sound. I will not merge with --admin or bypass the gate. After an independent approval, the Steward can recheck head/checks/threads/mergeability and merge with --match-head-commit.

immakermatty and others added 7 commits October 4, 2026 12:50
… canonical slug

The Dashboard addresses an Organization by its canonical slug: the manifest's
slug lowercased, every run of other characters one "-", none at either end,
and it matches /orgs/<slug> exactly. The shell document built the page from
the slug as written, so the picker's Organization head led to the Dashboard's
not-found page for every Organization whose slug has capitals or other
characters. dashboardSlug() follows the Dashboard's rule; a slug that
reduces to nothing falls back to the Dashboard's home. "Přístup k modulu"
builds on this page.

The Dashboard's per-person suffix for two Organizations that reduce to the
same slug cannot be known here; such a link still ends on its not-found page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Přístup k modulu" (root decision 0185 S15, issue #151) is offered to the
Organization's Owners and Stewards. The Owner answer exists; a Steward's
did not. GET /api/organizations/<org>/modules/<module>/maintain answers
whether this Environment's GitHub identity has permissions.maintain on the
module's declared repository (gh api repos/<owner>/<repo>), behind the same
admission and through the same gh path as the Owner check (askGitHub, now
shared): bounded by its timeout, kept five minutes per repository, one
question per repository at a time, and false on every failure. It is never
asked on a Team Environment, without a bound GitHub login, without a
declared repository, or for a repository outside the Organization's login.

The catalog now carries a module's repository page as `url`, read from its
slot's git.url like a production repository's; nothing is fetched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…urites; Přístup k modulu for Owners and Stewards

Root decision 0185 S12, S15 and S18, over the account document of F37's
addendum. Apps reads GET /.lazurio/account/environments once per page load
(same origin, no token, no redirect followed, given up after 4 s). Only a
lazurio.account.v1 document counts, and Apps takes only
preferences.openApps and favourites keyed by the Organization slug. Until
the gateway relays the namespace (404 today) everything stays as it was:
favourites in this browser, apps in a new tab.

- Where apps open (S18): tiles, the column's favourites, "Otevřít
  aplikaci" and the lifecycle's "Otevřít" open in a new tab or navigate this
  window, never in a frame. A workstation opens the new tab within the
  click, or moves this window only after the start succeeded.
- Favourites (S12): with the account they are its list, in its order; the
  star writes PUT/DELETE /.lazurio/account/favourites/<org slug>/<kind>/<id>
  at once and puts the favourite back with a short message when the account
  does not take it. Browser favourites are not uploaded.
- "Přístup k modulu" (S15, #151): in a module tile's menu for the
  Organization's Owners and Stewards, asked when the person reaches for the
  menu, never for every tile; it opens the Organization's Dashboard page
  /settings?tab=modules&module=<id> in the same window, without an arrow.

The preview script takes a role (owner, steward, member) for its gh answers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Root decision 0185 S8: the person lands in Apps of the Environment they had
open last, and a click on a space returns there; the Dashboard account
remembers it because every Environment has its own origin. The library now
sends PUT /.lazurio/account/last with { environment, app, organization }
(the shell document's current, the app its host names, the space's
Organization slug or null) once per full page load of Apps, Chat and
Automate. Fire and forget: keepalive, its answer not read, silent on
failure, never blocking drawing.

It lives in its own module, src/shell/last.ts, apart from the rail's merge
of the account's Environments; each element calls it with one line, the
column head by its `active` app and the rail only when its host names `app`.

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

F36's addendum gets a note: "Přístup k modulu", the report of the last
Environment, favourites in the account and where module apps open are
built, each waiting only for the person's account to answer on the
Environment's origin; the Dashboard's canonical slug. The shell-as-built
section of the Launchpad development guide names the new modules, the
Steward route, the account behaviour and the preview's role argument.

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

Pablo's review of #159: the optimistic rollback looked only at whether the
favourite was shown, so it could not tell a later click on the same
favourite apart. Three quick clicks (PUT, DELETE, PUT) with the first PUT
refused and the later writes taken removed the star, although the last
click and the last successful write starred it. Concurrent writes could
also reach the account out of order.

Now one favourite has one write under way at a time, sent in the order of
the clicks. It keeps what the account last took and what the last click
wants: a click made while a write is under way is sent after it, and only
if it still differs from what the account holds. A failure is put back,
with the short message, only when no later click wants something else; then
the favourite shows what the account holds, in its place. Different
favourites, and the browser fallback, are unchanged. The tests cover the
three quick clicks starring and unstarring, a click undone before its write
answers, the last click's failure and its place, and two favourites written
independently.

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

So the fixture stays valid whether this PR or #161, whose parser refuses
a bare Machine name for a hosted entry, merges first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@immakermatty
immakermatty force-pushed the agent/DEV-6639-apps-account branch from 514cf9b to 8b02667 Compare October 4, 2026 10:50
@agentrozjedemeai
agentrozjedemeai removed the request for review from annavesela October 4, 2026 10:54

@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 — exact head 8b02667. The author rebased onto main d2133f8; tree 9d2df9fe472c8c04429f5d8ba706cf2568058a3b is byte-identical to my previously reviewed and tested 514cf9b head (verified in an isolated clone with git diff --quiet). The prior integration-only docs resolution remains intact. The per-favourite write ordering, fail-closed account and maintainer routes, shell identity, and scope have no tree changes. Zero unresolved conversations; no competing reviewer request. New head CI and all provider gates must pass before guarded merge. This is not a release authorization.

@agentrozjedemeai

Copy link
Copy Markdown
Collaborator

@immakermatty Exact-head re-approval is posted: #159 (review) on 8b02667. Both Ubuntu and macOS checks are green, with zero unresolved threads, but live GitHub still reports REVIEW_REQUIRED / BLOCKED after the new review. The author, not the reviewer, performed the last force-push (verified in the PR timeline); the reason for the remaining provider review gate is not visible with my repository permissions. I requested @annavesela for an additional independent review. I will not bypass branch protection or merge while the gate remains blocked. Please inspect the effective Organization/repository rule if another eligible approval does not clear it; after any new push, exact-head QA and CI must be repeated.

#161 made the shell document's `current` the Environment's base host; the
two passages describing PUT /.lazurio/account/last now say so and point to
the identity of an Environment entry under F37's addendum.

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 APPROVED — exact head 87fcb1f, based on main d2133f8. The author-only change from previously reviewed 8b02667 clarifies that the account last-Environment payload uses the shell current base host, consistent with merged #161. In an isolated checkout: lint and check:public pass; 58 targeted account/shell/Launchpad tests pass. Zero unresolved conversations and no competing reviewer request at submission. Ubuntu/macOS CI is still running; do not merge until the live exact-head checks and all provider gates pass.

@agentrozjedemeai
agentrozjedemeai merged commit f2cc2c4 into main Oct 4, 2026
3 checks passed
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.

Shell document: an Organization's Dashboard link uses the manifest slug as written, so it hits the Dashboard's not-found page

2 participants