Skip to content

feat(members): give every crew a configurable webview in its drawer - #8468

Open
chenmingwei23 wants to merge 1 commit into
mainfrom
feat/crew-webview
Open

feat(members): give every crew a configurable webview in its drawer#8468
chenmingwei23 wants to merge 1 commit into
mainfrom
feat/crew-webview

Conversation

@chenmingwei23

@chenmingwei23 chenmingwei23 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

The crew's dashboard, docked in its drawer -- the crew decides what goes here:

Docked summary

Expanded to the full window:

Expanded dashboard

A crew that has published nothing says so, rather than showing an empty box:

Empty state

1. What is the problem

A crew has nowhere to show its own state.

The member drawer shows what KiroCrew knows about a crew -- session counts, wake sources, configuration. It shows nothing the crew itself wants to say. A conductor running a fleet of workers knows things no generic UI can guess: which item is blocked on a human ruling, which worker has been stuck for two cycles, what it intends to do next, and why a count does not add up. Today that exists only in chat scrollback, so it is gone by the next turn and invisible unless someone reads the transcript.

The obvious fix does not scale. A native panel per crew means every new crew shape needs a frontend change, a release, and a schema the crew must be taught. And a crew is an LLM: whatever it produces cannot be handed the dashboard's origin.

2. Why it matters to the user

Someone supervising a long-running crew has to read a transcript to answer the only question they actually have: does this need me? The drawer is where they already look, and it cannot tell them.

This also removes a per-crew frontend release from the loop. A crew that wants to surface something new publishes different data; nobody ships a UI change to make it visible.

3. How our fix solves it

The root cause is that presentation was coupled to the frontend release cycle, and the only party that knows what matters -- the crew -- had no way to influence it.

The fix splits the panel in two: a human-authored template and crew-published data. Only the data comes from the crew.

Records live in a gateway-only, sandbox-hidden subtree. This is the part worth reading closely, because two earlier placements were wrong and the reasons are the design. Storing under the crew's own member space failed because members/ is deliberately unfenced -- a crew owns its own published data -- so nothing stopped one crew writing another crew's panel.json directly, forging its state past both ownership resolution and the redactors in a single write. Moving to trust/ closed that route through the agent's file tools but not through a sandboxed shell, which can construct the path at runtime and so is never caught by command matching. The record now lives in a dedicated hidden leaf with a precreate disposition, so the directory exists before an agent can create it: a fence only fences a path that is there. All three write paths -- the MCP route, agent file tools, and a sandboxed shell -- are closed on the same location, and the module docstring records why each earlier placement was rejected so the next reader does not undo it.

Template selection needs no registry. A crew whose name matches an installed template gets that template; everything else gets default. No mapping table, no per-crew config, and a bespoke template activates by being installed. default renders any data object: scalars become a tile strip, sentences become their own section, arrays of objects become tables with derived headings, arrays of scalars become lists, objects become key/value blocks. Fields render in the order the crew published them, so a crew that puts its alert first gets it read first.

Rendering is bounded, not just the input. The data cap bounds what a crew can publish, but derived table headings take the union of keys across rows, so a payload well inside that cap could still expand to millions of cells and hang the browser. Rows and unique columns are capped independently, with an explicit truncation notice rather than a silent drop.

Two conventions come from the Issue Radar Pipeline dashboard, which solves the same legibility problem at a larger scale. A missing value renders as an explicit placeholder rather than vanishing, so "we do not know" is distinguishable from "none". And a <field>_note key renders as a caveat directly under the thing it qualifies, which is how a crew explains why a number does not reconcile.

Writes are MCP-only. A new opt-in server (kirocrew-panel) carries panel_publish and panel_templates, registered across every declaration surface the repo has -- the managed-server registries, the schema-posture view, the redaction sinks, the MCP call-site auth scan, the sandbox mask, the feature map, and the architecture specs -- and gated off for every agent by default. Neither tool takes a crew or session argument: the publishing crew is resolved from the vetted calling session through the shared strict-session gate, never from the request body, and never from the lenient resolver that would resolve a subagent to its parent's crew. The publish routes are strict-internal; the drawer's read route is cookie-authed and deliberately outside that prefix. The read route takes one snapshot and composes both halves from it, so the summary and the document can never disagree.

The drawer has two states, and only one of them is a document. Docked is a native summary -- title, the crew's leading sentence, a few counters, and a labelled button. Expanded is the crew's document in a sandboxed frame. This is not cosmetic: a 250px column cannot host a dashboard, and trying put the most important line below the fold. It also means no document is minted until someone opens it, and the minted URL is single-use, so the frame is created on first expand and kept mounted across collapse.

Containment, stated precisely. The shared sandbox-document serve route sets one CSP header for every consumer and is unchanged here; it grants popups. What holds is that sandbox restrictions combine rather than union, so the frame's own sandbox="allow-scripts" is the binding constraint: no popups, a null origin, no cookies, no storage, no parent DOM, no top-level navigation. Egress is closed by the document's own CSP. Scripts do run, by design.

The sandbox is defence in depth, not the only defence. The untrusted half is data, not markup: it is escaped once in Python at a single boundary, redacted on the way into storage, and the template directory is read-only to the sandbox so an agent cannot author its own template. Templates reach the DOM only through text APIs, pinned by a test that greps the source for DOM sinks. The native docked summary renders crew strings as React text children, pinned by a test that a hostile string injects no element.

4. What tests we did

  • Store behaviour: caps, locking, ownership refusal inside the exclusive lock (not before it), the escaping boundary against hostile payloads, and published field order.
  • Route behaviour: publish identity resolved from the vetted session and never from the body, the strict-internal split, and the single-snapshot read.
  • Server behaviour: the two tools' runtime paths, the strict-session gate refusing a caller it must refuse, and the redaction of refusal prose -- the reasoning that justifies this module's redaction classification, so it is tested rather than asserted.
  • Frontend containment: the exact sandbox grants, the three never-conflated states, the expanded dialog's focus handling in both directions, and a hostile crew string rendering as text.
  • Declaration completeness: a test asserts the derived property -- every schema registry appears in the posture view, every fenced leaf has a sandbox disposition, and that disposition is not the writable one -- so the next managed server cannot repeat the omissions this change had to fix.
  • Mutation-verified the load-bearing tests rather than trusting green: disabling the template name-match, unmounting the frame on collapse, and putting raw HTML into the summary title each redden exactly the test that guards them.
  • Verified in a real browser, since the single-use minted URL is not something a unit test can prove: zero mints before the first expand, exactly one at first expand, and still one after expand, collapse and expand again, with the document still rendering.
  • The full backend suite was run sharded on this branch and on an untouched base, and the failure sets are identical -- the host-environment class that fails on both, with none unique to this branch.

5. Any other suggestions

  • This PR ships no consumer. The server is gated off for every agent, so every crew shows the empty state until one publishes. That is deliberate -- the mechanism should be reviewable on its own terms rather than alongside a specific crew's dashboard semantics -- but it does mean nothing exercises it end to end until the first consumer lands, which is the immediate follow-up. A reviewer is right to want that confirmed as imminent.
  • readThemeVars was duplicated seven times; this change extracts one shared export and deletes all seven local copies. That is scope beyond the feature, taken because a duplication that is merely disclosed is still a duplication.
  • There is a genuine latent import cycle between the validation and artifact layers reachable through the members module. This change works around it by mirroring two constants locally with an anti-drift test rather than importing that module at scope. The cycle itself is untouched and will bite the next module that needs it early.
  • Screenshots are committed under temp-screenshots/, following an established convention with several hundred existing files. One reviewer is right that this is permanent history under a directory named "temp"; the alternative -- hosting on the PR itself -- keeps the repository smaller but loses the images if the PR is ever pruned. Following the convention, and noting the cost rather than pretending it is free.
  • "Readable at a glance" depends on the crew publishing at least one sentence-length field. A crew that publishes only numbers gets a title, counters and a button, with no leading line to orient the reader.

@chenmingwei23
chenmingwei23 requested a review from a team September 4, 2026 14:27
@chenmingwei23
chenmingwei23 requested a review from a team as a code owner September 4, 2026 14:27
@chenmingwei23
chenmingwei23 requested a review from dwu96 September 4, 2026 14:27
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 4, 2026
@github-actions

ghost commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Design-level review of 0d2ceae5220ef8da7f6e6be464b648630b88d7d7 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All key claims verified against the diff: the sealed-ceiling test was widened (original invariant kept, per-disposition reconciliation added), the sandbox dispositions land in the pinned lists, the publish/read auth split matches the description, the frame sandbox is allow-scripts alone with an anti-overclaim copy test, and the readThemeVars extraction fixed a genuinely divergent copy. The disclosed trade-offs (no consumer yet, mirrored constants with anti-drift test, screenshots-in-repo convention) each carry a stated reason and a test or follow-up. No undisclosed hunks; no weakened pins.

Design-Verdict: PASS

Template/data split with all three write paths closed at the OS layer is the right shape; trade-offs are disclosed, tested, and reversible.

[DESIGN-REVIEWED] 0d2ceae

@github-actions

ghost commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — 🟡 CONCERNS

UX-level review of 0d2ceae5220ef8da7f6e6be464b648630b88d7d7 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Reconciliation complete: the blind reader correctly identified both primary controls ("Open dashboard" — "sure"; "Collapse the dashboard" — "sure… feels safe") and read the two surfaces as "the two directions of the same door." No lens-13 block: the docked↔expanded split is a hard swap in the code (!expanded && <card> / everExpanded && <frame>), but the PR description argues the reason at length (native summary vs. sandboxed single-use-URL document; "This is not cosmetic"). What remains are evidence gaps and two secondary-comprehension risks.

UX-Verdict: CONCERNS

Blind read comprehends the whole surface, but "Contained" is ambiguous, the docked↔expanded flip has no recording, and the empty-state screenshot shows stale copy.

Watch

  • "Contained" chip: the blind reader said "no idea, honestly" and "I can't tell if it means 'safe/sandboxed' or 'the situation is under control'" — on the docked card the clarifying sentence lives only in a hover tooltip, so every drawer visit re-invites misreading a trust label as work status. Smallest fix: lead the docked chip with attribution ("Filled in by this crew") and keep the isolation claim in the tooltip.
  • The reader's biggest gap: "two screens tell me a question from '758' is blocking work and 'needs you', but nowhere… can I find the button or place to actually answer it." The panel is deliberately read-only, but the surface that exists to answer "does this need me?" offers no pointer to the chat as the response channel. Worth a human decision before the first consumer lands.

Evidence gaps

  • Docked→expanded is a form/place change of an identified element; no committed or PR-embedded recording shows the transition (recordings list is empty; PR body has only .png links).
  • Shot-03 shows only "This crew has not filled in its dashboard yet." — the shipped webview_empty adds "Add its panel server under Tools & MCP…"; re-shoot the empty state with final copy.
  • The error states ("This crew's dashboard could not be rendered." + "Try again", fetch and mint variants) and the "Rendering the dashboard…" state appear in no screenshot.

[UX-REVIEWED] 0d2ceae

@github-actions

ghost commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — 🟡 CONCERNS

Premise-level review of 0d2ceae5220ef8da7f6e6be464b648630b88d7d7 — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All evidence gathered. Here is the review.

First-Principles-Verdict: CONCERNS

Four seams for choosing a second template ship while exactly one template exists anywhere — the named first consumer (the conductor) gets no bespoke template.

Not justified as shipped

  1. Template-selection surface — one consumer, generalized: panel_templates tool, explicit template arg, name-match, and the panel-templates/ override dir all select among templates, yet src/kiro_crew/agent_panel_templates/ holds one file (default.html; glob count: 1) and no operator override can exist on a fresh install.
  2. Seven readThemeVars copies collapsed to one — rides along: worthwhile subtraction (and silently changes mochi's frame to sanitized CSS values), but it is a separate change riding an unrelated feature.
  3. Deferred route binder generalized — rides along: and the generalization stops one copy short (see Subtractions); its docstring's "both current callers" is contradicted by the third copy 30 lines below.

What this change ships

Inventory (10 items) — 7 justified

Intent: let a person supervising a long-running crew see, in the drawer they already use, whether the crew needs them — an ADDITION.

  1. Every crew's drawer gains a Dashboard section: native docked summary with a labelled Open button — justified
  2. The dashboard expands to a full-window sandboxed document — justified
  3. A crew that published nothing shows an explanatory empty state — justified
  4. New opt-in MCP server kirocrew-panel lets a crew publish data (panel_publish), identity from the vetted session — justified
  5. Template selection: discovery tool, explicit arg, name-match, operator override dir — one consumer, generalized (only default exists)
  6. New routes (/api/agent-panel/* strict-internal, /api/members/{slug}/panel cookie-authed) and panel_published WS push — justified
  7. New persisted state: sandbox-hidden crew-panels/ records, read-only panel-templates/, both precreated — justified
  8. A symlinked/junctioned panel leaf now refuses the sandbox spawn instead of warning — justified
  9. readThemeVars deduplicated 7→1, mochi's copy gains sanitization — rides along
  10. Session-control's deferred binder generalized to _deferred(module, handler) — rides along

Watch

  • "a bespoke template activates by being installed" and "the conductor is the first consumer, not the schema" — but zero non-default templates ship, so name-match, the template arg, and panel_templates are all exercised only by tests until one lands. Clears when: a bespoke template (e.g. the conductor's) is installed in-tree, or the explicit template arg is dropped until a second template exists.

Subtractions

  • Delete _deferred_work_ledger (src/kiro_crew/dashboard/server.py:1452) and point its 4 call sites (lines 1493–1496) at the new _deferred('work_ledger', …) — grep def _deferred counts 2 copies of the same closure after this PR's own generalization.

[FIRST-PRINCIPLES-REVIEWED] 0d2ceae

@github-actions

ghost commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 0d2ceae5220ef8da7f6e6be464b648630b88d7d7 and found no blocking issues.

This comment is updated in place on each push.

Review details

FINDING -- src/kiro_crew/agent_panel.py:341 -- "entry.is_file()" lists escaping symlinks that publishing rejects as unknown templates -> Fix: include only IDs that resolve_template() can read.
[GPT-REVIEWED] 0d2ceae

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 0d2ceae5220ef8da7f6e6be464b648630b88d7d7: <one-sentence reason>

@github-actions

ghost commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 0d2ceae5220ef8da7f6e6be464b648630b88d7d7 — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] 0d2ceae

Verdict parsed from the review's SHA-scoped output markers for commit 0d2ceae5220ef8da7f6e6be464b648630b88d7d7.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 0d2ceae5220ef8da7f6e6be464b648630b88d7d7: <one-sentence reason>

@chenmingwei23 chenmingwei23 changed the title Give every crew a configurable webview in its drawer feat(members): give every crew a configurable webview in its drawer Sep 4, 2026
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 4, 2026
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 4, 2026
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Sep 4, 2026
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention merge conflict Branch has merge conflicts with its base — author must resolve before merge and removed readiness: checking Automated validation is still running labels Sep 4, 2026
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 5, 2026
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: passed Eligible automated validation passed for the current revision readiness: checking Automated validation is still running labels Sep 5, 2026
@chenmingwei23

ghost commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

/ai-review override gpt e464d66: The package tree is not fenced from agent file tools at all: is_sensitive_path is False for this template AND for agent_panel.py and security.py, so an editable checkout that can rewrite a template can already rewrite the security module itself. Seven other packaged assets load the same way (bundled agent config, MCP defaults, vendored code, model registry, docs) and none is hash-verified. Fencing only this one would be a lone exception against a premise that defeats the product.

@github-actions

ghost commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Human judgment recorded

@chenmingwei23 marked the gpt AI finding as false positive, not applicable, or explicitly accepted for e464d662b966fe69b1e65b039f674ba1515122b9.

The package tree is not fenced from agent file tools at all: is_sensitive_path is False for this template AND for agent_panel.py and security.py, so an editable checkout that can rewrite a template can already rewrite the security module itself. Seven other packaged assets load the same way (bundled agent config, MCP defaults, vendored code, model registry, docs) and none is hash-verified. Fencing only this one would be a lone exception against a premise that defeats the product.

This decision applies only to this commit. A new push requires a new judgment.

@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 5, 2026

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

Require UX review. The right panel will be the core place to show details of a conductor/crew.

@github-actions github-actions Bot added the merge conflict Branch has merge conflicts with its base — author must resolve before merge label Sep 5, 2026
@chenmingwei23

ghost commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

/ai-review override gpt 983ebfd: Bounded and recoverable, the record the adjudication could not complete: template markup reaches ONLY an iframe with sandbox=allow-scripts under CSP default-src/connect-src/form-action 'none' (null origin, no cookies, no parent DOM, no popups, no top-nav), and the docked summary renders crew strings as React text with a hostile-string test pinning it -- not injection into trusted UI. Next publish replaces the record. No Windows file enforcement exists for ANY leaf; this asks for a Windows sandbox backend.

@github-actions

ghost commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

AI-review override not recorded: keep the reason to 500 characters or fewer.

@chenmingwei23

ghost commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

/ai-review override gpt 983ebfd: Bounded and recoverable, the record the adjudication could not complete: template markup reaches ONLY an iframe with sandbox=allow-scripts under CSP default-src/connect-src/form-action 'none' (null origin, no cookies, no parent DOM, no popups, no top-nav), and the docked summary renders crew strings as React text pinned by a hostile-string test. Next publish replaces the record. No Windows file enforcement exists for ANY leaf; this asks for a Windows sandbox backend.

@github-actions

ghost commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Human judgment recorded

@chenmingwei23 marked the gpt AI finding as false positive, not applicable, or explicitly accepted for 983ebfd9501eba5a4a4b615200516be66c408fce.

Bounded and recoverable, the record the adjudication could not complete: template markup reaches ONLY an iframe with sandbox=allow-scripts under CSP default-src/connect-src/form-action 'none' (null origin, no cookies, no parent DOM, no popups, no top-nav), and the docked summary renders crew strings as React text pinned by a hostile-string test. Next publish replaces the record. No Windows file enforcement exists for ANY leaf; this asks for a Windows sandbox backend.

This decision applies only to this commit. A new push requires a new judgment.

@bolichen97

ghost commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

@chenmingwei23 This PR overlaps with #8947 (open, not a draft), which also adds a second surface to the Members detail drawer. Audited at 983ebfd.

Shared files: website/src/pages/members/MembersPage.tsx and docs/feature-map/README.md.

What differs: #8947 makes the drawer bi-modal. It adds drawerView: 'details' | 'side' state with openMemberSideChat / closeDrawer, renames the existing details JSX to view, wraps it in an AnimatePresence crossfade as the new body, switches noPadding / title / headerActions per view, and adds a <SideChat slot={activeSlot} /> branch. This PR instead injects <CrewWebview slug={activeSlug} member={activeMemberName} /> and a heading inside the details body, above the activity counts.

The two edits sit at different depths, so the collision is mechanical rather than semantic, and both can land. #8947 is the further along of the two: it is not a draft and touches 27 files, while this PR is a draft, touches 60 files, and is dirty with a merge conflict at 291 commits behind main.

Suggestion: let #8947 land first, then rebase this PR onto it and decide whether the crew webview should be a third drawerView rather than always-on details content. That rebase also has to re-land the two _CREW_SECRET_LEAVES entries in src/kiro_crew/security/paths.py, because src/kiro_crew/security.py no longer exists as a file.

@CrysisDeu for visibility on the drawer ordering.

Posted from the 2026-09-08 open-PR relationship audit (read-only, one auditor per PR); reply here if any of this is wrong.

@bolichen97

ghost commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Rebased onto main 9af9543b0 by a maintainer as part of the 2026-09-08 open-PR audit (was 291 commits behind, mergeable_state: dirty). 26 files conflicted:

  • security.py — deleted on main (split into security/); the two _CREW_SECRET_LEAVES entries re-landed in security/paths.py.
  • sandbox.py — main already ships _CREW_PRECREATE_HIDDEN_DIR_LEAVES + _materialize_maskable_dirs(), so crew-panels was appended to main's tuple and this PR's _sealable_absent_ceilings() extension dropped. Its tests were retargeted at the same property (mask materialised, symlinked leaf refuses).
  • agent.py, cli.py, server.py, mcp_core/cleanup/discovery.py, security_posture.py, validation.py, 2 tests — merged kirocrew-panel alongside merged feat(work-ledger): kirocrew-work tools and kirocrew-worker agent (phase 2) #9152's kirocrew-work. Main's _deferred_work_ledger was left as-is beside this PR's generalised _deferred().
  • ArtifactBody.tsx — kept main's ErrorNotice, took the shared readThemeVars.
  • 12 locale catalogs + en.context.json + the feature-map row — both key blocks kept.
  • docs/architecture/mcp.md and mcp-shareability.md — added the missing kirocrew-work row and corrected "five" to "six managed servers", which this PR's own registration tests require.

Local gates: black/isort/flake8 on changed files clean (ws_event_scope.py, mcp_cleanup.py are pre-existing black-baseline entries), 544 python tests green, tsc --noEmit clean, 87 frontend tests green.

Please review the sandbox resolution in particular. A maintainer push makes the maintainer the last pusher, so a second approver is needed under the repo's last-push rule. Reply if anything looks wrong.

@chenmingwei23

ghost commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

@iamwhatever answering both halves of your review, and pinning down where this actually sits.

Require UX review. The right panel will be the core place to show details of a conductor/crew.

1. UX review: the lane has run on the current head.

UX Review check-run: success on afbbdb7c2.

Being precise rather than just pointing at green: the lane's own verdict on that head is CONCERNS, not PASS. It still reports a green check because the lane gates on BLOCK only ("A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory"). What it has left is advisory, and I would rather you see it than discover it:

  • a crew's "NEEDS YOU" line has no adjacent next step, because the sandbox strips links and forms by design, so a reader has to guess that the reply goes in that member's chat;
  • the empty state's action sentence uses implementation vocabulary ("Tools & MCP in the crew manager") rather than task words;
  • the docked "Contained" chip carries its explanation in title/aria-label, so a touch user does not reach the sentence until they expand.

None of those blocks readiness and I have not treated them as blocking. Say the word if you want any of them fixed here rather than followed up.

2. The right panel: agreed, and that is where this already is.

The webview is not a second surface. It renders inside the crew detail drawer, and that drawer IS the right panel: CrewWebview mounts in website/src/pages/members/MembersPage.tsx at line 1508, inside the member-drawer body that is handed to DetailPanel (lines 1912 and 1925). DetailPanel is the same shared shell the chat side panel wears: a left-rounded card docked flush to the window's right edge, same header idiom, same drag-to-resize handle. The code comment there puts it as "the two right panels read as one family".

So when more conductor/crew detail lands in the right panel, it lands beside this section inside the same shell. The webview is one section of that panel, headed "Dashboard" - not an alternative route to the same information.

One exception I would rather name than have you find: expanding the dashboard is a full-window overlay (fixed inset-0), which does leave the right panel. That is deliberate. The docked column is around 250px, which cannot host a dashboard legibly, so docked is the steady state (a native summary: the crew's leading sentence plus up to three compact rows) and the full-window document is on demand. If you would rather the expanded view stayed within the panel bounds, that is your call to make and I will take it.

CI on afbbdb7c2: 66 success, 0 failure, 0 pending. Re-requesting your review.

@chenmingwei23

ghost commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

@iamwhatever this is now out of draft and ready for review. It sat as a draft while your review was open, which is on me: a draft is easy to read as "not asking yet", and that is probably part of why it stayed quiet for four days.

Both halves of your review are answered in the comment above this one: the UX Review lane has run (its check is green; its own verdict is CONCERNS rather than PASS, with the three remaining items listed there and none of them blocking), and on the right panel -- the webview already renders inside it, in the same shared DetailPanel shell the chat side panel wears, so future conductor/crew detail lands beside it rather than as a second route.

On the current state: my head's own checks are 63 success, 0 failure. The branch shows conflicts because main has moved several times today while this waited; that is base drift, not a broken change, and I am rebasing it now. Nothing about the diff you would review changes when I do.

@chenmingwei23

ghost commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Green on d6fbd9b4b: 66 success, 4 skipped, 0 failure, 0 pending, and no blocking findings from any review lane. Out of draft, no conflicts, one commit.

@iamwhatever this is ready whenever you have a moment. Your review is the only thing left, and both halves of it are answered in the comments above rather than repeated here.

A crew that runs unattended accumulates the state an operator actually wants:
how many workers it is holding, which one is stuck, what it is waiting on. The
drawer showed activity counts, a path list and config instead, none of which
answer those. This adds a free-form HTML webview that the crew fills in at
runtime.

The split that makes free-form HTML safe: a human authors the TEMPLATE --
versioned in the repository, or dropped on disk by the operator -- and the crew
publishes only DATA. The template directory is fenced from agent file tools, so
a crew cannot author its own markup; the only way it influences its webview is
by publishing a data object. The data lands as an inert application/json island,
escaped so a published string cannot close the script element early, and a
template reads it with JSON.parse and renders through textContent only. The
composed document is served behind a single-use minted URL into an iframe
sandboxed to allow-scripts and nothing else.

A crew whose name matches an installed template gets that template; a crew with
no template of its own falls back to a generic one that renders any data object.
That is the entire wiring mechanism -- installing <crew-name>.html is the act of
wiring a bespoke view up, so there is no registry, no mapping table and no
per-crew config to keep in sync.

The MCP server ships registered but gated off for every agent (opt_in), so a
default session pays nothing for the capability: not a tool-list entry, not a
line of context, not a spawned backend. An agent that should publish a panel is
granted the set in its own spec, and a refresh keeps an existing grant current
without ever minting one.

The publishing crew is derived from the vetted calling session and never from the
request body, so one crew cannot publish a webview that presents as another's. A
body field naming a crew is rejected by the schema rather than ignored, so a
caller is never left believing it retargeted the write. The drawer's read at GET
/api/members/{slug}/panel is cookie-authed; the /api/agent-panel writes sit
behind the MCP-only prefix.

Published field order is preserved end to end. Field order is presentation -- a
template renders a stat strip in key order -- so the record is dumped without
sorting keys instead of silently alphabetising the operator's dashboard.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge conflict Branch has merge conflicts with its base — author must resolve before merge readiness: passed Eligible automated validation passed for the current revision

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants