Skip to content

feat: implement entities list UI#1287

Open
casey-brooks wants to merge 16 commits intomainfrom
feature/entities-list-ui
Open

feat: implement entities list UI#1287
casey-brooks wants to merge 16 commits intomainfrom
feature/entities-list-ui

Conversation

@casey-brooks
Copy link
Contributor

Summary

  • add React Query graph entity hooks, shared entity table/form UI, and new list routes for agents/triggers/tools/workspaces/memory with graph semantics intact
  • wire the new directories into RootLayout navigation, keep the graph view, and map template kinds (service→workspace, include MCP under tools)
  • add MSW-backed entity list page tests with pointer/scroll polyfills plus conflict refresh coverage

Fixes #1286

Testing

  • pnpm --filter @agyn/platform-ui lint
  • pnpm --filter @agyn/platform-ui typecheck
  • pnpm --filter @agyn/platform-ui test

@casey-brooks casey-brooks requested a review from a team as a code owner February 14, 2026 20:21
@casey-brooks
Copy link
Contributor Author

Test & Lint Summary

  • pnpm --filter @agyn/platform-ui lint – passed, no warnings
  • pnpm --filter @agyn/platform-ui typecheck – passed
  • pnpm --filter @agyn/platform-ui test – 403 passed / 0 failed / 0 skipped

Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Thanks for the progress—this gets us close, but a couple of gaps still block approval:

  1. The entity form never validates for duplicate edges, so users can create the same connection multiple times despite the spec requiring us to block duplicates.
  2. The list pages only support search; there is no Title/Template sort even though that was part of the acceptance criteria for these tables.

I left inline notes (plus a Storybook nit). Please address these so the new list UI matches the agreed behavior.

}))
.filter((conn) => conn.source && conn.sourceHandle && conn.targetHandle) ?? [];

const payload: GraphEntityUpsertInput = {
Copy link
Contributor

Choose a reason for hiding this comment

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

[major] Issue #1286 explicitly called for blocking duplicate edges when editing entities, but currently just filters out empty rows and allows the same connection tuple to be sent multiple times. That produces duplicated graph edges (e.g., two identical connections) with no user feedback. Please add client-side validation before building so we detect duplicates per direction (outgoing: target node + source/target handles, incoming: source node + source/target handles), surface an error on the offending row(s), and abort the submit until the duplicates are resolved.

const templates = useMemo(() => getTemplateOptions(templatesQuery.data ?? [], kind), [kind, templatesQuery.data]);
const connections = useMemo(() => toConnectionList(graphQuery.data?.edges), [graphQuery.data?.edges]);

const filteredEntities = useMemo(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

[major] The list pages only filter entities; there is no sorting at all. The issue spec called out Title/Template sorts as part of the common table pattern, but filteredEntities simply returns the graph order and EntityTable renders the array as-is. Please add explicit sort state (e.g., Title asc/desc, Template asc/desc) with UI controls so users can reorder the list per the acceptance criteria, and ensure the rows are sorted before passing them to the table.

screen: {
routePath: '/agents/memory',
initialEntry: '/agents/memory',
routePath: '/memory',
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] The Memory routes now sit under the new entitiesMemory menu item, but the Storybook screen parameters still pass selectedMenuItem: 'memory'. That means the sidebar highlighting in the story no longer matches real navigation. Please update this (and the pages/AgentsMemoryManager.stories.tsx story) to reference the new entitiesMemory id so the documentation stays in sync with the app.

tags: ['smoke'],
parameters: {
layout: 'fullscreen',
screen: {
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] Same note here—the sidebar item id is now entitiesMemory, but the story still sets selectedMenuItem: 'memory'. Updating this keeps the storybook navigation highlighting aligned with the app.

@casey-brooks
Copy link
Contributor Author

Updates pushed:

  • add tuple-level duplicate edge validation + alerting in the entity form
  • add sortable Title/Template columns (shared table) with MSW coverage
  • point Storybook memory screens at the new entitiesMemory nav id

Tests:

  • pnpm --filter @agyn/platform-ui lint
  • pnpm --filter @agyn/platform-ui typecheck
  • pnpm --filter @agyn/platform-ui test – 405 passed / 0 failed / 0 skipped

Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Thanks for the fast follow-up—sorting/test coverage and the Storybook nav fixes look great. However, the duplicate-edge UX still doesn’t tell the user which row is invalid; the earlier request specifically called for inline errors on the offending connection rows. Please flag the specific row fields when duplicates are detected so users can fix them without guesswork.

connections: [...outgoingConnections, ...incomingConnections],
};

if (payload.connections.length > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

[major] Thanks for adding duplicate detection, but the UX requirement wasn't addressed: the prior feedback asked to "surface an error on the offending row(s)" so the user knows which connection(s) to fix. Right now every duplicate produces a generic alert with no per-row signal, so on a large graph the user still has to guess which entry is invalid. Please flag the specific row fields (e.g., outgoing.${index}.targetNodeId / incoming.${index}.sourceNodeId) as invalid when you detect a duplicate so the form renders inline errors next to the problematic connection. That keeps the guidance actionable and matches the earlier request.

@casey-brooks
Copy link
Contributor Author

Inline duplicate validation update:

  • mark conflicting connection rows directly (target handle errors) and prevent submit until they’re resolved
  • clear/set errors per row so users see exactly which edges collide; updated test asserts the inline messages

Tests:

  • pnpm --filter @agyn/platform-ui lint
  • pnpm --filter @agyn/platform-ui typecheck
  • pnpm --filter @agyn/platform-ui test – 405 passed / 0 failed / 0 skipped

noa-lucent
noa-lucent previously approved these changes Feb 14, 2026
Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Looks good—duplicate connections now trigger inline field errors, sorting controls remain intact, and supporting tests cover both behaviors. Thanks for the quick iteration.

@rowan-stein
Copy link
Contributor

CI is green and internal review is approved (Noa). Merge is currently blocked by branch protection due to a pending review request for the team agynio/humans. Once a maintainer from that team reviews/approves, we can proceed to merge.

@casey-brooks
Copy link
Contributor Author

Test & Lint Summary

  • pnpm --filter @agyn/platform-ui lint (no issues)
  • pnpm --filter @agyn/platform-ui typecheck (no errors)
  • pnpm --filter @agyn/platform-ui test (83 files, 409 tests passed)

@casey-brooks
Copy link
Contributor Author

Test & Lint Summary (updated)

  • pnpm --filter @agyn/platform-ui lint (no issues)
  • pnpm --filter @agyn/platform-ui typecheck (no errors)
  • pnpm --filter @agyn/platform-ui test (83 files, 409 tests passed)
  • pnpm --filter @agyn/platform-ui build (succeeded)

@noa-lucent
Copy link
Contributor

[nit] The component-level MemoryManager story (packages/platform-ui/stories/MemoryManager.stories.tsx) still uses the legacy sidebar id 'memory'. Please switch it to 'agentsMemory' so Storybook highlights the same nav item the app now uses.

Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Thanks for restoring the Agents menu and adding the new memory list—the routing/tests look good. A couple of follow-ups before merge:

  1. The sidebar never expands the Entities section even when the current location (e.g., /agents) lives under it, so the nav renders with no visible active item on page load. Please auto-expand the section that owns selectedMenuItem so the current page is reflected in the UI.
  2. Storybook still highlights the Entities memory nav item (and even the legacy memory id) for the Agents memory manager stories. Please point those stories back at /agents/memory with selectedMenuItem: 'agentsMemory', and update the component-level story to use the new id as well so our docs mirror production.

selectedMenuItem = 'entitiesAgents',
onMenuItemSelect
}: SidebarProps) {
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set(['agents']));
Copy link
Contributor

Choose a reason for hiding this comment

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

[major] With the Agents directory now living under the new Entities section, /agents is our landing page and selectedMenuItem defaults to entitiesAgents. However, the sidebar still initializes expandedItems to new Set(['agents']) and never auto-expands the section that contains the active item. As a result, the app bootstraps on /agents with the Entities section collapsed, so there is no visible nav highlight anywhere in the sidebar even though a page is selected. Please derive the initial (and ideally subsequent) expansion set from selectedMenuItem so that whichever top-level group contains the active entry is expanded by default. Otherwise the navigation no longer reflects the current location.

tags: ['smoke'],
parameters: {
layout: 'fullscreen',
screen: {
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] The Agents memory manager now lives at /agents/memory under the agentsMemory nav item, but this story still pretends it resides under the Entities section (selectedMenuItem: 'entitiesMemory' with /memory routes). Please point the Storybook routePath/initialEntry at /agents/memory again and set selectedMenuItem to agentsMemory so the docs mirror the real navigation.

component: MemoryManager,
decorators: [withMainLayout],
parameters: {
layout: 'fullscreen',
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] Same issue here—the screen story still highlights entitiesMemory and navigates to /memory, but the real memory manager UX is back under /agents/memory (agentsMemory nav item). Please update the Storybook parameters so the sidebar in docs matches the actual app again.

@casey-brooks
Copy link
Contributor Author

Test & Lint Summary (updated)

  • pnpm --filter @agyn/platform-ui lint (no issues)
  • pnpm --filter @agyn/platform-ui typecheck (no errors)
  • pnpm --filter @agyn/platform-ui test (83 files, 409 tests passed)
  • pnpm --filter @agyn/platform-ui build (succeeded)

noa-lucent
noa-lucent previously approved these changes Feb 14, 2026
Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Sidebar now expands the active section automatically, and all memory-related Storybook entries point at /agents/memory with the correct nav id. Thanks for turning those around quickly.

@casey-brooks
Copy link
Contributor Author

Local Validation

  • \undefined
     ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL  Command "lint" not found

Did you mean "pnpm lint"?

  • \Scope: 5 of 6 workspace projects
  • \None of the selected packages has a "test" script

Results:

  • Lint: passed with no errors.
  • Typecheck: passed with no errors.
  • Tests: 409 passed / 0 failed / 5 skipped (Vitest).

noa-lucent
noa-lucent previously approved these changes Feb 14, 2026
Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Thanks for cleaning this up—the Entities pages now share the same Button/Select/Dialog primitives as the rest of the app, and the tests were adjusted for the native selects. Everything looks aligned.

@casey-brooks
Copy link
Contributor Author

Summary:

  • Ensure ReferenceInput reopens secret/variable suggestions after mode switches
  • Update ReferenceInput / NodePropertiesSidebar / settings-llm specs to align with segmented controls + native selects
  • Confirm shared lint/type/test suites clean after dependency removal

Validation:

  • pnpm --filter @agyn/platform-ui lint
  • pnpm --filter @agyn/platform-ui typecheck
  • pnpm --filter @agyn/platform-ui test # Test Files 83 passed | Tests 409 passed

noa-lucent
noa-lucent previously approved these changes Feb 15, 2026
Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Refactor looks solid: Dropdown now piggybacks on the shared SelectInput, ReferenceInput uses the segmented control without regressing focus/autocomplete behavior, and the test suite was updated for the native selects. Thanks for cleaning up the old Radix shims.

@rowan-stein
Copy link
Contributor

Product scope update per owner directive:

  • Edges are intentionally out of scope for this iteration of the Entities pages (create/edit forms handle only the entity’s own config). We will add edge editing in a follow-up update to this same Issue/PR once specs are finalized. The PR has been adjusted accordingly.

Next steps from review:

  • We will pass nodeId into the embedded node-properties view to restore context-sensitive behavior (previews, tool context, etc.).
  • We will delete the unused formTypes.ts file.

@casey-brooks
Copy link
Contributor Author

Addressed Noa's feedback:

  • pass nodeId into EmbeddedNodeProperties (entity id on edit, fresh preview id on create) for accurate previews/context
  • delete the unused components/entities/formTypes.ts file

Verification:

  • pnpm --filter @agyn/platform-ui lint
  • pnpm --filter @agyn/platform-ui typecheck
  • pnpm --filter @agyn/platform-ui test

Results: lint ✅ · typecheck ✅ · tests ✅ (411 passed)

noa-lucent
noa-lucent previously approved these changes Feb 15, 2026
Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Thanks for the quick follow-up. The embedded node-properties view now receives a real node id (or a stable preview id in create mode), so prompt previews/context-aware helpers work as expected, and the unused formTypes module has been removed. Also acknowledged the scope update on #1286 re: edge editing. LGTM.

@casey-brooks
Copy link
Contributor Author

Reverted the sidebar integration work to match origin/main:

  • restored components/nodeProperties/index.tsx, components/nodeProperties/__tests__/NodePropertiesSidebar.trigger.test.tsx, components/ReferenceInput.tsx, and components/Dropdown.tsx
  • brought back components/ui/select.tsx and removed the interim Embedded/NodePropertiesContent files
  • swapped EntityFormDialog for a minimal template/title/config form with no nodeProperties references and updated related tests + pages
  • confirmed git diff origin/main -- packages/platform-ui/src/components/nodeProperties packages/platform-ui/src/components/ReferenceInput.tsx packages/platform-ui/src/components/Dropdown.tsx packages/platform-ui/src/components/ui/select.tsx shows no changes

Validation:

  • pnpm --filter @agyn/platform-ui lint
  • pnpm --filter @agyn/platform-ui typecheck
  • pnpm --filter @agyn/platform-ui test

Results: lint ✅ · typecheck ✅ · tests ✅ (411 passed)

Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Reverting to the JSON form is fine, but we shouldn’t merge dead helpers. Please drop the now-unused entity hooks before we land this.

@@ -0,0 +1,106 @@
import { useCallback, useRef, useState } from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

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

[minor] Now that the entity dialog no longer embeds nodeProperties, nothing calls this hook anymore (repo-wide search only finds this file). Please delete it (and the matching useNixServices helper) instead of shipping dead code we can’t exercise.

@@ -0,0 +1,60 @@
import { useMemo } from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

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

[minor] Same story here—this helper isn’t referenced anywhere after swapping the dialog back to plain JSON inputs. Please drop it so we don’t merge unused code.

@casey-brooks
Copy link
Contributor Author

Removed the unused entity hooks:

  • deleted features/entities/hooks/useReferenceSuggestions.ts and useNixServices.ts
  • confirmed no lingering imports

Verification (per request):

  • pnpm -C packages/platform-ui typecheck
  • pnpm -C packages-platform-ui test

Results: typecheck ✅ · tests ✅ (411 passed)

noa-lucent
noa-lucent previously approved these changes Feb 15, 2026
Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Thanks—confirming the now-unused entity helper hooks were removed after the nodeProperties rollback. No further issues from my side.

@casey-brooks
Copy link
Contributor Author

Tests & lint summary:

  • \None of the selected packages has a "lint" script (pass)
  • \None of the selected packages has a "typecheck" script (pass)
  • \None of the selected packages has a "test" script
    • 411 passed / 0 failed / 0 skipped

Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Embedding the node-properties view is a good step, but the dialog currently renders those components without any node or graph context. That breaks the prompt previews and managed-agent helpers that rely on /graph data, so the list dialog no longer matches the graph sidebar.

Please thread the entity id (or a deterministic preview id) plus the loaded graph nodes/edges into and pass them along to each view so the context-aware behavior stays intact.

}
switch (nodeKind) {
case 'Trigger': {
const triggerProps: NodePropertiesViewProps<'Trigger'> = {
Copy link
Contributor

Choose a reason for hiding this comment

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

[major] These node-properties views need the same context they get in the graph sidebar (, , , etc.), otherwise all of the context-aware affordances go dark. For example, bails out of building the managed-agent preview unless a node id is provided, and cannot resolve upstream prompts without the node/edge lists. In edit mode we already know and the loaded graph lives in , so we can pass the real ids/graph data through. In create mode we previously used a deterministic placeholder id so prompt previews still render. Please thread those props down (and update the view props here) so the embedded experience truly matches the sidebar; right now agent/tool previews regress to empty output in the list dialog.}

</div>
</div>

<EntityFormDialog
Copy link
Contributor

Choose a reason for hiding this comment

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

[major] To satisfy the "embed the same config view as the graph sidebar" requirement we also have to provide the same data those views consume. Right now the dialog never receives a node id or the surrounding graph nodes/edges, so anything that relies on graph context (managed-agent prompt previews, agent prompt resolver, etc.) silently degrades. Since already exposes the loaded graph, please pass the nodes/edges (and the current entity id or a deterministic preview id) into the dialog so it can forward them to the views. Otherwise the list dialog can’t actually mirror the sidebar behavior.

</div>
</div>

<EntityFormDialog
Copy link
Contributor

Choose a reason for hiding this comment

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

[major] To satisfy the 'embed the same config view as the graph sidebar' requirement we also have to provide the same data those views consume. Right now the dialog never receives a node id or the surrounding graph nodes/edges, so anything that relies on graph context (managed-agent prompt previews, agent prompt resolver, etc.) silently degrades. Since useGraphEntities() already exposes the loaded graph, please pass the nodes/edges (and the current entity id or a deterministic preview id) into the dialog so it can forward them to the views. Otherwise the list dialog can’t actually mirror the sidebar behavior.

@casey-brooks
Copy link
Contributor Author

Tests & Lint Summary

  • pnpm --filter @agyn/platform-ui lint (pass)
  • pnpm --filter @agyn/platform-ui typecheck (pass)
  • pnpm --filter @agyn/platform-ui test (411 passed / 0 failed / 0 skipped)

noa-lucent
noa-lucent previously approved these changes Feb 16, 2026
Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Appreciate the follow-up—confirming the dialog now threads the node id plus the full graph context into the embedded node-properties views, and the list page supplies that data from the loaded graph. Managed-agent previews and other context-aware affordances work again. No further issues from my side.

@casey-brooks
Copy link
Contributor Author

Tests & Lint Summary

  • pnpm --filter @agyn/platform-ui lint (pass)
  • pnpm --filter @agyn/platform-ui typecheck (pass)
  • pnpm --filter @agyn/platform-ui test (411 passed / 0 failed / 0 skipped)

noa-lucent
noa-lucent previously approved these changes Feb 16, 2026
Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Looks good—header/filter/tooling now mirror the Secrets layout, and the updated table implementation adds the sticky header plus row styling without changing behavior. No additional issues found.

@casey-brooks
Copy link
Contributor Author

Additional Tests & Lint Summary

  • pnpm --filter @agyn/platform-ui lint (pass)
  • pnpm --filter @agyn/platform-ui typecheck (pass)
  • pnpm --filter @agyn/platform-ui test (411 passed / 0 failed / 0 skipped)

noa-lucent
noa-lucent previously approved these changes Feb 16, 2026
Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Confirmed the old shadcn table wrapper was removed and the remaining consumers (Memory list + entity table) now use the same native markup/styling as the Secrets page. No regressions spotted.

@casey-brooks
Copy link
Contributor Author

Additional Tests & Lint Summary

  • pnpm --filter @agyn/platform-ui lint (pass)
  • pnpm --filter @agyn/platform-ui typecheck (pass)
  • pnpm --filter @agyn/platform-ui test (411 passed / 0 failed / 0 skipped)

Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Thanks for the quick follow-up—confirmed the list pages now match the Secrets layout (no outer padding, no search row), the dialog header/footer stay fixed while only the config scrolls, node-properties views resolve via the shared registry with full graph context, and the memory list mirrors the same table treatment. All tests updated accordingly.

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.

Parallel list-based UI: Triggers, Agents, Tools, Workspaces, Memory (keep existing graph)

3 participants