Skip to content

Agents: full detail page at /agents/<slug> - #172

Merged
TheGreatAxios merged 5 commits into
mainfrom
cl-6414-agent-detail-page
Aug 21, 2026
Merged

Agents: full detail page at /agents/<slug>#172
TheGreatAxios merged 5 commits into
mainfrom
cl-6414-agent-detail-page

Conversation

@TheGreatAxios

@TheGreatAxios TheGreatAxios commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Closes CL-6414 — https://linear.app/abklabs/issue/CL-6414

Replaces the /agents/<slug> placeholder with the real agent detail page.

What's on the page

  • Identity card — editable display name (deriveDisplayName/withDisplayNames, PR Agents: display names with slug as stable identifier #158) in react-ui's Input, the immutable slug beside it in muted mono, and the lifecycle state in words next to its pill.
  • Default model — a select over the bench's catalog models, with Bench default as a real, writable state.
  • System prompt — full editor, saved with the display name in one write.
  • Skills — the existing AgentSkillsPicker over the bench's skill registry; attach/detach is a real mutation.
  • Recent runs — this definition's runs, newest first, states in words (Running now, Failed), each linking to the existing Insights run surface.
  • Duplicate / Archive / Save in the StageTopBar action slot; crumbs are Agents → display name.

Write-path decisions

Edit Route
display name + system prompt PUT /agent-definitions/:id
default model POST /agent-definitions/:id/capabilities (kind: "model")
clearing the model DELETE /agent-definitions/:id/capabilities/model (new)
pinned skills PUT /agent-definitions/:id/skills
archive / restore PUT /agent-definitions/:id/status (new)
duplicate POST /agent-definitions (same call the create panel makes)
slug → definition GET /agent-definitions/by-name/:name (new)

One Save writes only the dirty parts. Those parts are separate requests, so a Save can land partway — the page reports exactly which parts committed and which failed, and reloads either way, rather than claiming a clean failure over a half-applied change. Folding the three into one transactional route is the real fix and is deliberately deferred rather than faked here.

Review round applied

  1. Model dirtiness compares against the loaded value alone, so picking Bench default on a pinned agent is a real edit; clearing writes through the new DELETE verb (withoutAgentModel drops the inference source, leaving prompt and tool-package pins untouched).
  2. Partial-save honesty as above — SaveReport names saved and failed parts; the notice is held by the route so it survives the reload.
  3. Duplicate 409 says what collided ("triage-bot-copy" already exists — open that copy, or rename it, before duplicating this agent again) instead of a "try again" a retry could never satisfy.
  4. Duplicate while dirty is blocked with a caption, rather than silently copying the saved version.
  5. Post-duplicate awaits the directory refresh before navigating, so the new agent's page no longer flashes "No such agent".
  6. Slug resolution is server-side (by-name), so agent CL-6324: rebase the stack onto main, and what the first real boot found #101 answers on its own URL instead of 404ing off a truncated listing page.
  7. One name everywhere — the page derives its display name exactly as the roster row does.
  8. Status invariant documented beside the archive route and below.

The workflow_definition.status invariant

Writers of that column in this build:

  1. Row creation. @intx/hub-sessions' ensureWorkflowDefinitionForAsset inserts with the column default (deployed) under onConflictDoNothing on (assetId, wireHash) — re-deploying the same definition body over an archived row is a no-op and cannot silently un-archive it. Editing an agent through this package's routes only repopulates the asset; it never re-projects a definition row.
  2. apps/hub's undeployAgentDefinition, which writes stopped — the same direction as archiving, so the two cannot fight.
  3. This PR's route, both directions.

Interchange's workflowDefinitionStore.rollback touches currentVersion, not status.

The one hole, stated loudly: deploying a changed body over the same asset mints a second definition row (new wireHash misses the unique constraint), deployed by default, beside the archived one. Nothing in this build takes that path for a hand-authored agent — only createAgentDefinitionCore and Interchange's own selector deploys call the ensure — but archiving by row status is what makes it reachable, and it is real rather than hypothetical.

Scoped out / deferred

  • Delete. No route tears down a definition's asset and history; archiving is the reversible lifecycle the platform backs.
  • A separate description field. A definition's row description IS its display name (where deriveDisplayName reads it), and the purpose blurb inside its workflow.json has neither a read nor a write route today. A "Description" input would have edited the display name twice.
  • Deferred: one transactional save route covering all three parts; and an optimistic-concurrency precondition on definition writes, being ticketed wave-wide.

Only the agent entry is removed from detail-placeholders.tsx; Skills, Plugins, and Routines placeholders stay for their own tickets.

Checks

bun run check passes (typecheck, lint, tests) except two pre-existing failures in workflows/assistant — tool-package pin versions and the connector-request scenario — unrelated to this branch and failing for the same reasons on main.

https://claude.ai/code/session_01Shhie5zM8L54bLHq5gFQti

Covers the page at /agents/<slug>: the identity card's editable display
name beside its immutable slug, the default-model select, the full
system-prompt editor, the skills section, recent runs linking into the
Insights run surface, and the Duplicate/Archive/Save trio in the top
bar's action slot. Every edit is asserted on the request body that leaves
the page, so a second write path would fail the suite rather than pass it
quietly.

Also covers the archive/restore mutation this needs on the owning
package: PUT /agent-definitions/:id/status writes only the row's
lifecycle status, leaves the definition's asset and git history intact,
refuses anything outside the schema's two states, and 404s for an
unknown definition or a workbench host.
Replaces the agent placeholder with the real screen: an identity card
(editable display name, immutable slug in muted mono, lifecycle state),
a default-model select over the bench's catalog, a full system-prompt
editor, the pinned-skills picker, and recent runs linking into the
Insights run surface. Duplicate, Archive/Restore, and Save live in the
StageTopBar action slot, with crumbs Agents -> display name.

Every write reuses a mutation that already existed: display name and
system prompt through PUT /agent-definitions/:id, the model through the
guided capability route, skills through PUT .../skills, and a duplicate
through the same POST /agent-definitions the create panel uses. One Save
writes only the dirty parts.

Archive is the one lifecycle verb that had no route, so it lands in
@corbits/agent-directory as PUT /agent-definitions/:id/status: it moves
the row between `deployed` and `stopped` and touches nothing else, which
is what makes restoring it a single write back rather than a re-create.
Delete stays out — tearing down a definition's asset and history has no
route, and archiving is the reversible lifecycle the platform backs. A
description field stays out too: a definition's row description IS its
display name, and the purpose blurb inside its workflow.json has neither
a read nor a write route today.
…ring paths

Review follow-ups, as tests first: selecting "Bench default" on an agent
with a pinned model is a real edit that clears it; a Save that lands
partway names the parts that committed alongside the one that failed; a
duplicate whose handle collides says what collided instead of "try
again"; Duplicate waits while the editor is dirty rather than copying the
saved version behind the person's back; and the page titles from the same
derived display name the roster uses.

On the package side: a definition resolves by its immutable slug through
its own route (so an agent past the listing's pagination ceiling still
answers on its own URL), and un-pinning a model is its own verb that
leaves the prompt and tool-package pins untouched.
… detail page

Review fixes for the agent detail page:

- "Bench default" is a writable state again. Model dirtiness now compares
  against the loaded value alone, and clearing goes through a new
  DELETE /agent-definitions/:id/capabilities/model — its own verb, because
  "no model" is not a name the capability route's inventory check could
  accept. `withoutAgentModel` drops the inference source and leaves
  everything else alone.
- A Save is three requests, so it can land partway. It now reports which
  parts committed and which did not, and reloads either way, instead of
  claiming a clean failure over a half-applied change.
- A duplicate whose handle already exists says what collided; retrying
  could never have cleared it.
- Duplicate waits while the editor is dirty, with a caption saying why,
  rather than silently copying the saved version.
- Duplicating awaits the directory refresh before navigating, so the new
  agent's page no longer flashes "No such agent".
- The slug resolves server-side through GET /agent-definitions/by-name/:name
  instead of scanning the definitions listing's single page, so an agent
  past that cap still answers on its own URL.
- The page derives its display name exactly like the roster does, so one
  agent never reads under two names.
- The archive route now documents the status invariant it relies on and
  every other writer of `workflow_definition.status` in this build.

Polish: react-ui's Input for the display name, run states in words rather
than wire enums, the archived caption says chats already running keep
going, and the stale header comment about a removed roster panel is gone.
create-agent-panel no longer re-exports slugify after the search-morph
rebase. Duplicate-handle minting goes through the same @corbits/slug the
create panel already uses.
@TheGreatAxios
TheGreatAxios force-pushed the cl-6414-agent-detail-page branch from d51e635 to 17b8a07 Compare August 21, 2026 00:36
@TheGreatAxios
TheGreatAxios merged commit 483dfa5 into main Aug 21, 2026
0 of 2 checks passed
@TheGreatAxios
TheGreatAxios deleted the cl-6414-agent-detail-page branch August 21, 2026 00:36
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.

1 participant