-
Notifications
You must be signed in to change notification settings - Fork 7
feat(fleet): give every sister agent the fleet surfaces (ADR 0048 §8) #2294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,26 @@ | ||
| // Gate for the header dropdown's "Fleet settings" item (#1708). The fleet is managed | ||
| // from its HOST instance only (ADR 0042) — a member window gets the item DISABLED with | ||
| // a tooltip pointing at the host instead of a broken/irrelevant panel. | ||
| // Gate for the fleet affordances a console window offers: the header dropdown's "New agent" | ||
| // + "Fleet settings" items, and the ⌘K Fleet Room. Originally hub-only (#1708/#1999) on the | ||
| // theory that any member window manages a fleet-of-one and so could only spawn NESTED agents | ||
| // by accident. That holds for exactly one of the four windows below — a spawned member | ||
| // reached DIRECTLY on its own port — so the gate asks that question alone, and every sister | ||
| // agent gets the same fleet surfaces its hub has. | ||
| // | ||
| // Host / member / standalone matrix (window = how the console reaches an instance): | ||
| // | ||
| // host window (hub, or any instance with no slug in the URL and no member flag) | ||
| // → ENABLED. Covers both a fleet host and a STANDALONE instance — standalone is | ||
| // where you create your first fleet member, so it must never be locked out. | ||
| // member via the hub's slug window (/app/agent/<slug>/) | ||
| // → DISABLED. The Box ▸ Fleet settings group only exists on the host window | ||
| // (ADR 0047 §7.7): opening it here lands on an unrelated fallback section. | ||
| // Signal: the URL slug (client-side, no fetch needed). | ||
| // → ENABLED. `/api/fleet` and `/api/archetypes` are HUB paths in the console router | ||
| // (`isHubPath`, lib/api.ts) — never slug-scoped — so every fleet read and write from | ||
| // a sister agent's window already lands on the hub's supervisor: the hub's real | ||
| // roster, the hub's fleet to create into, the hub's members to start/stop. Nothing | ||
| // nests; the sister manages the same fleet its hub does. | ||
| // spawned workspace member reached DIRECTLY (its own port) | ||
| // → DISABLED. Its own /api/fleet is a fleet-of-one (its workspaces root is empty | ||
| // by construction) — "managing" it there creates a nested fleet by accident. | ||
| // → DISABLED. The one true nesting path: its own /api/fleet is a fleet-of-one (its | ||
| // workspaces root is empty by construction), so creating there spawns a GRANDCHILD | ||
| // the hub's roster never shows, and breaks the invariant that a member's empty | ||
| // workspaces root keeps `shutdown_all` hub-only (graph/workspaces/manager.py). | ||
| // Signal: the member self-reports `member: true` on its /api/fleet host entry. | ||
| // remote member (ADR 0042 §I) reached directly at its own URL | ||
| // → ENABLED, deliberately. Registration is one-sided on the hub — the remote has | ||
|
|
@@ -26,13 +33,12 @@ import type { FleetAgent } from "../lib/types"; | |
| export const FLEET_SETTINGS_MEMBER_TOOLTIP = "Fleet settings are managed from the host instance"; | ||
|
|
||
| /** | ||
| * Why "Fleet settings" is unavailable in this window — or `null` when it's allowed. | ||
| * `agents` is the polled /api/fleet list; `slug` is `currentSlug()` (the window's URL slug, | ||
| * `"host"` when the console talks to its instance directly). | ||
| * Why the fleet surfaces are unavailable in this window — or `null` when they're allowed. | ||
| * `agents` is the polled /api/fleet list. The window's URL slug deliberately does NOT enter | ||
| * into it: a sister agent's slug window drives the hub's fleet, exactly as the host console does. | ||
| */ | ||
| export function fleetSettingsDisabledReason(agents: FleetAgent[], slug: string): string | null { | ||
| if (slug !== "host") return FLEET_SETTINGS_MEMBER_TOOLTIP; // hub slug window on a member | ||
| export function fleetSettingsDisabledReason(agents: FleetAgent[]): string | null { | ||
| const self = agents.find((a) => a.host); | ||
| if (self?.member) return FLEET_SETTINGS_MEMBER_TOOLTIP; // spawned member, reached directly | ||
| return null; // fleet host, or a standalone instance (no fleet configured yet) | ||
| return null; // host, sister-agent slug window, standalone, or a remote's own console | ||
|
Comment on lines
+36
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Treat an empty roster as unknown, not authorized. The callers use
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: protoLabsAI/protoAgent
Length of output: 313
🏁 Script executed:
Repository: protoLabsAI/protoAgent
Length of output: 9925
🌐 Web query:
Playwright toHaveCount matcher does disabled elements count visible to match visible assertions💡 Result:
In Playwright, the toHaveCount assertion counts all elements that match the provided locator, regardless of whether they are disabled or hidden [1][2]. It does not automatically filter out elements based on their visibility or enabled state [2]. If you need to count only visible elements or only enabled elements, you must refine your locator to explicitly include those criteria: For visible elements: Use the:visible CSS pseudo-class or the.filter({ visible: true }) method [3][2][4]. Example: await expect(page.locator('button:visible')).toHaveCount(3); Example: await expect(page.locator('button').filter({ visible: true })).toHaveCount(3); For enabled elements: Use the:disabled CSS pseudo-class or filter by state [5]. Example: await expect(page.locator('button:not(:disabled)')).toHaveCount(2); By default, standard locators match all elements present in the DOM that satisfy the selector, including those that are disabled (e.g., have a [disabled] attribute) or hidden (e.g., have display: none) [1][2]. The toHaveCount assertion simply asserts that the total count of matches meets your expectation [6][7].
Citations:
🌐 Web query:
Playwright toBeVisible assertion button disabled default aria-disabled ignored💡 Result:
In Playwright, the toBeVisible assertion checks only for element visibility (a non-empty bounding box and no visibility:hidden style) [1][2][3]. It does not check whether an element is enabled or disabled [4][5]. Regarding your query about disabled states: 1. toBeVisible and disabled state: The toBeVisible assertion is completely independent of whether a button is disabled, enabled, or has aria-disabled attributes [4][1][5]. An element can be both visible and disabled at the same time [1][2]. 2. Playwright's definition of "disabled": Playwright considers an element disabled if it meets specific criteria [1][3]. Crucially, for descendants of an element with the aria-disabled=true attribute, Playwright recognizes the element as disabled [1][6][2]. 3. Default behavior: Playwright does not ignore aria-disabled. If a button is a descendant of an element with aria-disabled="true", Playwright correctly identifies it as disabled [1][3]. If you are experiencing issues where Playwright is not respecting aria-disabled, ensure that the locator is targeting the correct element and that the attribute is correctly applied to a parent in the DOM hierarchy [6]. If you need to verify that a button is disabled, you should use the dedicated toBeDisabled assertion rather than relying on visibility checks [4][7]. Summary of Key Assertions: - toBeVisible: Checks if the element is visible in the DOM (non-empty bounding box, not visibility:hidden) [1][3]. - toBeDisabled: Checks if the element is disabled via the disabled attribute, fieldset, or parent aria-disabled="true" attribute [4][1][3].
Citations:
Assert sibling controls are enabled, not merely rendered.
toBeVisible()only checks DOM visibility, so a disabled siblingStartbutton can still pass. AppendtoBeEnabled()after the visibility asserts at Line 435 and Line 448 to catch a guard that disables all fleet controls instead of only the current agent’s own controls.🤖 Prompt for AI Agents