How extensions surface in the Cinatra app — the ListingCard and its install-state machine, the extension detail modal, the installed-extensions manager, the All-Agents agent card, the extension settings page, the post-install configuration-needs Callout, the per-agent assignment page where skills and context artifacts are assigned per scope, and the Upload Extension screen where a package is installed from a repository link. Part of Cinatra Application Design; match it exactly.
In the app each extension surfaces as a ListingCard whose action reflects per-instance state. The install CTA is the four-state machine taken straight from /configuration/marketplace: Install now when it isn't installed, a disabled Installed pill at the current version, Update now when a newer version sits in the catalog, and Restore for one that was removed; a submit in flight reads Installing…. When an extension can't run on this Cinatra instance its install greys out and an Incompatible line replaces the compatible check. The six cards show one of each. Install now does not open a popup: it swaps the card's body in place to the install panel (§I.1) — there is no install dialog on this surface.
The card's action row is one line: the price keeps its own centred line above, then the install control and More details sit side by side beneath it, details on the right. That single line is the resting layout for the label set the app's own breakpoints actually carry — it wraps to a second line only when a control's label runs long enough to force it (a longer pending state, or a narrower card at a smaller breakpoint); this drawing shows three columns, the app's own grid runs four at xl, and the contract holds at either. The vendor-name checkmark carried no verification claim — it rendered whenever the catalog held a vendor display name, nothing more — and is removed from every card here; nothing in the app is lost, since the mark never meant anything the admin could act on. The connector kind glyph — the byline icon above and the §II dependency-list icon alike — is now the lower half of the joined-plug mark drawn once in Application Design — Connectors, recentred and rescaled across the full 24-unit viewBox so it reads clean at the 13px byline size; both the app's Plug and this spec's prior Link rendering are superseded by it. Truncated card text always carries its full string as a native title — the clamped name and the ellipsised vendor line both — so the untruncated text is one hover away and stays in the accessibility tree regardless of what's visibly cut.
Install now swaps the card's body in place — there is no popup. Clicking Install now keeps the card's header band (icon, name, byline) exactly as it was and replaces only the body — price, rating, description — with the install panel: the access-scope picker, preselected to Workspace: All, and Cancel / Install now actions. A close × in the header's corner does the same thing as Cancel — it returns the card to its idle state, discarding the in-progress selection. The picker component itself — its states, the scope vocabulary, and the installMode row set — is specified normatively in Application Design — Permissions; this page fixes only its extension-surface mounting.
A fixed block-size, so the grid never jumps. The panel renders at the same block-size as the idle card it replaces — recorded here as a spec constant so a card's row in the grid-auto-rows: 1fr listing grid never grows or shrinks when the panel opens or closes: 299px at md / lg / xl alike — the card face is a single fixed height, with a one-line action row and no vendor-name checkmark on its byline, so one constant covers all three breakpoints. Errors are a toast, never inline: a failed install neither redraws the panel with an error state nor grows its height — it reports through the app's toast surface, and the panel stays exactly as the admin left it so the selection is never lost.
The header band — icon, name, byline — carries over unchanged; only the lower region swaps. The picker opens preselected to Workspace: All (the broadest audience, per the shared picker's installMode row set — Application Design — Permissions §III) and its closed value renders exactly as the row reads, per the single-mode trigger ≡ row rule.
Clicking More details on a ListingCard opens this modal. Its body embeds the extension's listing detail from marketplace.cinatra.ai — the same layout as the marketplace listing detail (§IV of the Marketplace spec), stripped of the site header/footer, the storefront's own Install now, the More Extensions tab, the pagination and the Related extensions rail. The modal is details-only — it carries no footer and no install/update/restore action of its own (owner ruling, 2026-08-04); install/update/restore run from the §I ListingCard and the §III installed card, never from this dialog.
A dialog on --paper — the same surface as pages — over a dimmed overlay (which stops below the navbar). Its body scrolls top-to-bottom only — everything is sized to fit the width, so nothing ever scrolls sideways — while the header stays fixed. The header carries just a close ✕. The body is the marketplace listing detail lifted whole — the app-logo tile, title and {Type} by {Vendor} line (the vendor links out to its marketplace.cinatra.ai store), the price, the Details / Reviews / Changelog tabs, then either the README beside its specs column (version, last-updated, Compatible up to: Cinatra {version}, installations) or the WooCommerce reviews list beside its rating summary (the average, the star-by-star breakdown and per-level counts — the submission form is dropped in-app), and the share row — but nothing that navigates the storefront (no More Extensions tab, no prev/next pagination, no Related extensions) and no install/update/restore action of any kind (owner ruling, 2026-08-04 — the modal footer is removed; see II above). The specs column ends with the extension’s Dependencies — the other Cinatra extensions it declares in cinatra.dependencies (a read-only list here in the app; on the marketplace page each links to its listing), not its npm packages — and a Changelog tab that renders the extension’s root CHANGELOG file as per-version release notes, or a No changelog available message when the extension ships no CHANGELOG.
Manage installed agents, skills, connectors, and artifacts. Each installed extension is one horizontal card, split three ways: a logo + name panel on the left (the ListingCard mark of §I) — on the extension’s category colour, or fully greyed out when it is archived — with the {Type} by {Vendor} line beneath the name; a white middle carrying the description, then the version with its status beside it — a green check for Active, a green lock for a Locked system extension, a grey cross for Archived, plus an Update available chip when a newer compatible version is published; and a white actions panel on the right (Settings, More details). A status filter — the four status views All · Active · Locked · Archived (§III.1–§III.6 below) — plus Marketplace and Upload sit above the list.
One card per installed extension, divided into three panels. The left panel reuses the ListingCard logo tile + italic white title on the extension's categorical ground — with the type/vendor line beneath the title — so an installed extension reads as the same object it did in the marketplace. The white middle stacks the description and the pinned --font-mono version with its lifecycle status — a green check for Active, a green lock for a Locked system extension (always active — it cannot be archived or uninstalled, so it shares the live-green ink but reads distinctly from a normal active row), a grey cross for Archived, plus an Update available chip when a newer compatible version is published (states below). Archived extensions render entirely in grey (the coloured ground, kind emblem, status and actions all desaturate) so they read as inactive at a glance. The white right panel — set off by a navy hairline — carries the per-instance actions: Settings opens the extension's configuration, More details opens the §II detail modal.
The status filter partitions the installed list into four views. Above the list, a single-select control chooses which installed extensions show — All, Active (the default), Locked or Archived — and drives a shareable ?tab= URL. Active, Locked and Archived are a clean partition of the installed set by the lifecycle status above — a Locked extension lives under Locked, not the default Active view — and All is their union.
?tab= query (its canonical URL), and ?tab=all, ?tab=locked, ?tab=archived each address their view. ?tab=active resolves to Active too, but is non-canonical — the selector never writes it (choosing Active clears the query). The no-query landing view stays Active — adding options must not change what users see today. Any absent, empty, or otherwise invalid / unknown ?tab= value falls back to Active — a defined, tested fallback, never a 404 or a dead / misrendered tab, so a bookmarked or hand-edited URL always resolves.status: active), Locked (status: locked) or Archived (status: archived). A locked extension appears under Locked (and, as their union, All) and is excluded from the default Active view; no row is dropped or double-counted across views, and every view's count is exact.Beside the version and its Active/Archived status, the spec-line carries one more optional badge — an Update available chip. It reuses the status-indicator badge treatment (--font-mono, the smallest 2xs step, uppercase, 0.1em tracking, weight 700, a 12px mark) but in the --blue action accent with a circular-arrow glyph, so it reads as timely news, not an action. It appears only when a newer compatible version is published for a registry-comparable source. Four states: Update available — chip present; Up to date; Incompatible — a newer version exists but needs a newer Cinatra, so the row greys out under the §I ABI-compat treatment and shows no chip; and Non-comparable — github refs, 0.0.0-dev.* and local builds have no registry version to compare, so they never show a chip and never fall back to a string-equality guess. The chip is the only update information the card carries: no explanatory update text ever appears in the card. The wordings behind the last two states — Newer version needs a newer Cinatra, No registry version to compare — surface on the §V extension-settings page's Maintenance · Update row instead. The chip is status only: the card's actions stay exactly Settings and More details. Where the update itself runs is out of scope for this page (owner ruling, 2026-08-04 — the §II detail-modal footer that used to carry it is removed; a replacement surface is a separate, not-yet-specified decision).
The redesigned card for the /agents "All Agents" tab. Derived from the §III Installed-extensions card — the same coloured logo + name panel (with the {Kind} by {Vendor} byline beneath the name) and description — but without the version and the Active / Archived indicator: an agent you run doesn't need either surfaced here. The right panel's primary action becomes Run, marked with a play icon in place of the settings gear; Settings — now a text link, not the gear button — and More details stay. The description is capped at three lines.
Same three-panel anatomy as §III — coloured logo tile, white middle, white right panel set off by a navy hairline — minus the version/status row, so the middle panel carries just the (three-line-clamped) description, with the byline beneath the name in the coloured panel. The right panel drops to a single primary action, Run, plus Settings (the §VII assignment page at the card’s scope) and More details (the §II detail modal). The two text links sit side by side in the same treatment, Settings to the left; the Settings link carries the address §VII gives it, <scope-base>/agents/<vendor>/<slug>/settings?tab=skills|artifacts, at the card’s scope. The assistant row of the Assistants tab carries the same right panel with Chat as its primary action and the same two text links, the Settings link opening the assistant’s assignment page of §VII — <scope-base>/assistants/<vendor>/<slug>/settings?tab=skills — with the Skills pane alone.
One page for everything you can do to an installed extension. Settings (§III) opens this page — a Narrow column that gathers the extension's actions into grouped sections (Permissions, Marketplace, Maintenance), ending in a red Danger zone. The page header matches the §II detail modal, with nothing in the top-right. Destructive and one-way actions are red and always confirm.
The page holds to the Narrow column: the §II detail header, then the groups — Permissions and Marketplace, then Maintenance, then the red Danger zone. Skills and context artifacts are assigned per scope on the agent’s assignment page (§VII), never on this admin page. Permissions reuses the install-time access picker (project · team · organization scopes, plus the two always-offered workspace scopes — Workspace: All and Workspace: Admins only — which only a platform admin may select; for every other role those two rows render disabled with the reason Requires platform admin., matching the disabled treatment the org/team/project rows already use) for connectors and artifacts — the picker component itself is specified normatively in Application Design — Permissions. The workspace scopes set the post-install audience — a separate dimension from who may install at that scope: a Workspace: All install is visible to every workspace member, and a Workspace: Admins only install is visible to admins only. Agents and skills are managed from their own pages, so for those the section links there instead of showing the picker — an agent to the Agents page, a skill to its configuration page. Update carries the extension's update status as the row's description — the §III card states spelled out in words: the available version when a compatible update is ready, Newer version needs a newer Cinatra when the newer version is incompatible, No registry version to compare for github/dev/local sources, and an up to date line otherwise — with the button greyed out whenever there is nothing to run. This row is where that explanation lives: the §III card itself never carries explanatory update text (it shows at most the Update available chip). Archive and Activate are complementary — exactly one is live: Activate greys out until the extension is archived, Archive greys out once it is. Destructive and one-way actions are the destructive red button and always confirm; Force-delete additionally demands a typed reason and warns that it bypasses restore, and publishing to the marketplace is one-way — and gated: on the instance being a registered marketplace vendor (until then the action is muted beside a Register for marketplace link), and on the kind being publishable — a registered vendor viewing a kind that can't be published yet sees the action muted, Marketplace publishing isn't available for this extension yet.
When a freshly-installed extension still needs its connectors set up, it can't run yet — so it reads as inactive until you finish. On the Extensions page (§III) the extension renders in the archived treatment — the greyed ground, desaturated kind emblem, muted name and actions — with a thin status strip attached to the bottom of its card. The strip carries the design system's Needs review status colours (the .pill.hold mustard tint) and lists, centred, each required connector's human name as a link to its setup page. The moment every required connection is configured the card flips back to its normal active colours and the strip disappears. Installing never waits on it — this is a follow-up reminder, not a gate.
The card is the §III installed-extension card, rendered in the archived treatment — greyed ground, desaturated kind emblem, muted name, byline and actions — because at least one required connector is not configured yet, so the extension can't run. A thin status strip is attached seamlessly to the bottom of the card in the design system's Needs review colours (the .pill.hold mustard tint over --mustard-ink, a mustard hairline on top). Its content is centred: a warning glyph, then Set up connections first:, then each required connector's displayName (its manifest.displayName — the same human-readable name the §I ListingCard and the Connectors grid use) as a link to that connector's setup page (§II of the Connectors design). Every connector among the installed extension and its auto-installed dependencies is checked against its own readiness, and only the unconfigured ones are listed; the moment all are configured the card flips to its normal active colours and the strip is absent. Install stays non-blocking — the strip is a follow-up reminder, not a gate.
Where skills and context artifacts are assigned to a package — one scope at a time. The Settings button on an agent or assistant entry — on the scope tabs’ cards, the Settings text link of §IV — opens that package’s assignment page. The page has two panes: Skills and Artifacts — where Artifacts means context artifacts, the stored artifacts that supply background context to the agent’s runs through named slots in its manifest. Assistants take skills only, so an assistant’s page renders the Skills pane alone. The admin page of §V never assigns either.
<scope-base>/agents/<vendor>/<slug>/settings?tab=skills|artifacts; an assistant’s is <scope-base>/assistants/<vendor>/<slug>/settings?tab=skills. The five scope bases are /personal, /organizations/<id>, /teams/<id>, /projects/<id> and /workspace. Assignments are per scope. On a team page you assign for that team; on a project page for that project; on your personal page for yourself. The scope comes from the address, not from a picker.Choosing is a typeahead, the same one the co-owner picker uses: a labelled single-line field that opens a list beneath itself and narrows as you type. The label sits above the field and belongs to it. Each option is two lines — the skill's own name in ink, and beneath it the extension that provides it followed by by {Vendor} in muted text, with the vendor clause dropped when the extension declares none — and carries the provider's lifecycle badge on the right, the same Active / Locked vocabulary the installed-extension card uses. The active option is marked by ground alone. The list offers only what can actually be chosen: skills already chosen for this agent are not offered again. Long result sets extend the list rather than paginating it visibly. Choosing an option clears the field and closes the list, so the next search starts from nothing rather than from the last thing typed; the cursor stays in the field.
The list has three states that are not results, and they are never conflated. While a search is in flight the list shows a single italic searching row. When the search returned nothing it says so plainly. When the search failed it says so — in the destructive colour, distinct from the empty state — because “nothing matched” and “we could not look” lead an admin to opposite next actions. A failure never renders as an empty list.
Chosen skills are a plain list of rows under the field, in the order they were chosen, using the same row chrome as the extension-owner list: two lines of text on the left, a state badge, then a quiet trailing remove control. A hairline separates rows and the last row carries none, so the list ends on the section's own rule rather than a double line. Above the list, a muted count hint always states how many of the five allowed skills are chosen. At five the field greys out and stops accepting input, and the hint says what to do about it — the limit is explained in place, never as a refusal after the fact. There is no floor: every row is removable, including the last one, and a section with nothing chosen is a normal, correct state — the field, the hint and the empty list, with no warning treatment. This is where the section departs from the owner list it borrows its chrome from, which must always keep one owner.
Adding and removing are saved immediately, not gathered behind a save button. A chosen skill appears as a row at once, dimmed and carrying a Saving badge in place of its state badge while the write is in flight; its remove control swaps the bin glyph for a spinner in the same slot and stops responding, so the row never changes shape as it settles. A removal dims its own row the same way. If the write fails, the row is taken back out — the list returns to exactly what it was — and a message in the destructive colour appears beneath the list naming the skill, saying plainly what went wrong, and confirming that nothing was changed. Each message belongs to its own skill: two changes failing at once show two lines rather than one quietly replacing the other, and a line clears when that same skill is tried again. The messages are scoped to the section and never replace it.
A skill can stop being usable after it was chosen — its extension gets archived, or the extension re-declares it as something the platform consumes internally rather than something an agent can be given. Such a row stays in the list. It is never silently dropped and never quietly re-added to the chooser: it renders with its name muted, a warning-toned badge naming the state — Archived, Role changed, Not installed — and, critically, a fully live remove control, because clearing it is exactly what the admin came to do. A degraded row still counts toward the five. The one row that does not appear is one whose extension was uninstalled outright: that choice is gone, not degraded, and the list simply no longer has it.
The page holds to the Narrow column and opens on the §II detail header, which names the scope being configured above the package — vendor and slug, the way every package address names it. Beneath it the two-pane Skills | Artifacts strip, drawn as the §II tab strip; an assistant’s page renders the Skills pane alone and carries no strip at all. The settings UI states both limits on every assignment page: 5 per scope here, at most 5 effective per run. The Skills pane is the drawing above — the chosen rows, the chooser and each of its states, unchanged from the section this page took them from. The Artifacts pane is the pane for context artifacts, drawn one group per declared manifest slot.
The Artifacts pane is assigned differently from Skills, and the difference is the manifest. Context artifacts are not one flat list per scope: each is assigned to a named slot the agent’s manifest declares, so the pane draws one group per slot — the slot’s name, the artifact kind it takes and its bounds, and beneath them the same labelled typeahead the Skills pane uses, narrowed to the stored artifacts of this scope that match that kind, with the same searching / no matches / search failed states and the same chosen-row chrome. Every choice is made by hand: an artifact is never auto-suggested and never filled in on the admin’s behalf. The pane carries no cap line of its own — storage is uncapped per scope; the numbers that bind are the slot’s own minItems and maxItems, enforced when the agent runs. That is why a slot at its upper bound greys its field with the hint that says what to do about it, and why a slot with nothing chosen is a normal state rather than a warning: the run then uses what it finds in its own context.
Upload opens a screen, not a dialog. The Upload control that sits above the installed list (§III) opens the Upload Extension screen: an eyebrow reading Extensions over the title Upload Extension, and, at the right, a single outline action reading Back to Marketplace that points at the marketplace page. The header carries no divider beneath it. Under the header sits a strip of exactly two tabs — File and GitHub — and the File tab opens first. This section draws the GitHub tab: the repository-link road.
The repository road asks for a link and nothing else. There is no account to connect, no connector to install, and nothing to state before a person may type: anyone can download the archive of a public repository’s default branch or of a release without being signed in to GitHub, so the person provides the link and Cinatra fetches the archive. A connection precondition drawn on this road — a sign-in step, a connect-your-account state, any gate in front of the field — is a defect, never a drawn state.
The repository field. One labelled field, Repository URL, carrying a link glyph inside it and the placeholder https://github.com/owner/repo, with the submit action Continue beside it. Continue is dead while the field is empty, live the moment a link is typed, and reads Looking up... while the lookup is in flight. The field’s description reads: A link to a public github.com repository holding an agent, skill, connector or artifact package - the repository page, a branch, a release page, or the archive ZIP link. The archive is downloaded without signing in to GitHub. The package declares its own kind; this instance reads it from the archive. The shapes it accepts are owner/repo or a github.com URL.
The ref field. A second labelled field, Branch, tag or commit (optional), whose placeholder reads the repository's default branch — an empty field means the repository’s default branch, not an unresolved state. Its description reads: Overrides whatever the link names. Whatever you name is downloaded once, pinned to the single commit its archive was generated from, shown below, and installed at exactly that commit.
Continue resolves in place. Continue resolves on the screen itself — no popup opens, and nothing is drawn inline in the fields. What it mounts is the same install panel §I.1 already fixes, without a card. Above the picker the panel carries four readings on three rows, in this order: the outline kind badge, and beside it on the same row the package name with its version; then, on the second row and set in the monospace face, the line that reads owner/repo pinned at the resolved commit sha; then, on the third row, monospace again and breaking anywhere, the provenance line, which names the repository, the ref and the archive address. Beneath the readings sits the §I.1 panel itself: the monospace Install for eyebrow, the access-scope picker preselected to Workspace: All, and the right-aligned Cancel / Install now row, whose submit reads Installing... while it is in flight.
Cancel is the whole of the close. This mounting has no card header band — there is no icon, name and byline band for a corner control to sit in — so it draws no corner cross. Cancel is the single close affordance, and it carries §I.1’s close-panel -> card-restored outcome: it returns the screen to its choose-a-package state and discards the selection. The typed link stays in the field.
A refusal is a toast, never inline. On this screen a refusal reports on the app's toast surface only — never inline in the panel, never as a field error — and the panel and its selection are left exactly as they were, which is §I.1’s own rule carried onto this mounting. Two refusals are fixed. A reference outside the accepted shapes is refused with no request made off this instance, reading: Invalid GitHub repository reference: "...". Use "owner/repo" or a github.com URL. And when a link names no ref and no default-branch name can be established, the install falls through, reading: this install could not resolve the branch, tag or release name to record - the link named none and the archive that was downloaded does not name the branch it was generated from. Type the branch, tag or release to install from and try again. Nothing was written.
The resolved panel is the §I.1 install panel on a mounting with no card — no header band, and so no corner cross: Cancel is the whole of the close. The three rows above the Install for eyebrow are readings, not fields to edit; the provenance row breaks anywhere, because an archive address is long. Where the install itself then lands — the installed card of §III — is that section’s drawing, not this one’s.