Cinatra — Application Design — Artifacts

/artifacts is the library — everything the acting user's agents and uploads have produced, each row opened by its type's renderer. The library also ingests: an upload affordance types a file by its MIME base, a type picker lets the user assert its meaning among installed types, and an inline marketplace adds a type when none fits (admins install; others request). Dashboards are artifacts too — they list as rows and open the dashboard itself at its canonical surface (never rendered inline); a team, project or organization lists the dashboards in its scope on its Dashboards tab, where a scope manager can add an existing dashboard as a secondary listing (§IX). /configuration/artifacts is the Artifacts console — a tablist page whose Type definitions tab lists every type every artifact extension defines, whose Stored objects tab inventories every stored object across every artifact extension, and whose Restore objects tab browses and undoes object change sets. Artifact change review lives on the shared artifact-review surface. Part of Cinatra Application Design; match it exactly.

Version 0.10.1

I. Two surfaces — the library and the artifacts console

/artifacts library · /configuration/artifacts console

/artifacts is the library, and nothing else. It lists exactly the acting user's artifacts, for every user — there is no mode toggle, because the library is the lone view. The page opens with the standard page header (title Artifacts, a one-line description), then the library toolbar directly beneath it, which replaces the closing rule — never stack a toolbar and the etched paired rule (Application Design — Components · Page header, Toolbar). The toolbar sits over the list. The sidebar entry points here.

/configuration/artifacts is the Artifacts console. It opens with the standard page header (title Artifacts) closed by a canonical underline tablistType definitions first, Stored objects second, Restore objects third (Application Design — Components · Tabs). The Type definitions tab lists every type every artifact extension defines; the Stored objects tab is a workspace-wide inventory of every stored object across every artifact extension; the Restore objects tab browses past object change sets (time-keyed, across every extension) and undoes them (§IV). Artifact change review lives on the shared artifact-review surface (§V).

A targeted restore has its own route. Reversing a single change set the acting user is authorized for is authorized per object, so it also has a dedicated route, /configuration/artifacts/restore/[changeSetId], nested under the Artifacts console and reachable by any authorized user (§IV).

Example — /artifacts, the library (every user; no mode toggle)

Artifacts

Everything your agents and uploads have produced.

Example — /configuration/artifacts, the Artifacts console (tablist: Type definitions · Stored objects · Restore objects)

Artifacts

Every type your artifact extensions define, every stored object across them, and the change sets you can undo.

II. Library — the actor's artifacts

Artifact rows · the acting user's set

Exactly the acting user's set. The library lists precisely the artifacts the acting user may see, resolved per actor against each defining extension's access grants, so two users on the same workspace can see different libraries. It never shows non-artifact internals. Rows are artifacts: each is an object of a defined type, presented by its display name — never a raw type id.

Row anatomy. Each row is a title line over a muted meta line, hairline-divided, the last row dropping its divider. The title line carries a leading renderer glyph, the artifact name (the artifact's display name), and a muted extension label naming the type's defining extension — the extension that defines that type, listed on the console's Type definitions tab (§IV). The row opens through that type's renderer, or, when the extension ships no renderer for the representation, through the generic fallback (§III). A file-form artifact (an upload) shows its MIME type in a mono label and opens through the MIME viewer. A dashboard row is the one exception: it renders nothing inline and instead navigates to the dashboard's canonical surface (§VIII). The meta line carries the owner / visibility and the relative updated time; the row's right edge holds the Open affordance.

Faceting & search. The Type facet filters the list by defining extension or type; the search field filters by name. An unfiltered library is grouped only by recency.

Example — Library, populated
  • Q3 re-engagement email Outreach

    Team: Growth · Draft · updated 8 minutes ago

    Open
  • Enterprise ICP · 240 prospects Prospect Lists

    Organization: Acme Corp · updated 2 hours ago

    Open
  • 2026 pricing sheet.pdf application/pdf

    Private · uploaded yesterday

    Open
Example — empty

No artifacts yet

Artifacts appear here as your agents produce work and as you upload files.

Example — error

Couldn't load your artifacts

The library source failed. Retry

III. Renderer dispatch — how a row opens

Type renderer · MIME · fallback

Dispatch by type. Opening a row resolves its renderer from the artifact's type — the type its defining extension registered a renderer for. Three dispatch cases cover every row:

Read authorization. A row the viewer may list but not read opens to a not-authorized panel, never to its bytes — the surface never serves a representation the viewer isn't cleared to read.

Example — typed renderer (email draft)
Outreachtype renderer
To: prospects@acme.example
Re-connecting on Q3 priorities

Hi there — following up on the pilot we scoped last quarter. Are you open to a short call next week?

Example — MIME handler (PDF)
application/pdfMIME viewer
Example — generic fallback (no renderer)
@acme/support:casestructured data
{
  "subject": "Login loop on SSO",
  "priority": "high",
  "open": true
}

IV. The Artifacts console — type definitions, stored objects, restore

/configuration/artifacts · Type definitions · Stored objects · Restore objects · /configuration/artifacts/restore/[changeSetId]

One page, three tabs. /configuration/artifacts is the Artifacts console. Its page header carries a canonical underline tablistType definitions first, Stored objects second, Restore objects third (see Application Design — Components · Tabs; underline only, no pill tabs). The tablist selects between a global type registry, a global stored-object inventory, and the change-set restore console; the etched paired-line rule begins to the right of the last tab and runs to the page edge, and the tabs never stack a second rule.

Type definitions — the global type registry. The Type definitions tab lists every type every artifact extension defines, alphabetically across all extensions — one workspace-wide registry, never scoped to a single extension. A type exists only because an installed artifact extension defines it, so exactly one extension defines each type; any extension that needs it declares the defining extension as a dependency, which guarantees the type is present wherever it is used. Each row therefore names the type (its display name over its type id), the one Defined by extension (its manifest display name), and the Used by extensions that declared that dependency. It is a read-only inventory: record inspection only, taking no actions and making no decisions. The empty state reads that no artifact extension defines a type yet.

Example — Type definitions tab, the global type registry (every artifact extension, alphabetical)
Type Defined by Used by
Case
@acme/support:case
Support Desk Escalations
Email draft
@cinatra-ai/email:draft
Email Sequences, Inbox Zero Agent
Email thread
@cinatra-ai/email:thread
Email
Prospect list
@cinatra-ai/prospects:list
Prospect Lists
Every stored object has a defining extension

There is no untyped floor and no default catch-all extension: every object stored is an object of a type some installed artifact extension defines, and dependents guarantee that type is present at install time (an extension that needs a type declares its definer as a dependency). Record inspection therefore always resolves an object to the extension that defines its type — the Defined by extension listed here, and the type named against every row of the global Stored objects inventory below.

Stored objects — the global inventory. The Stored objects tab lists every stored object of every artifact extension — one workspace-wide inventory for record inspection, never scoped to a single extension. Each row names the object by its display name over a mono meta line (its type id, object id, version and updated time), and the row's right edge carries the object's scope as an entity-named labelTeam: Growth, Organization: Acme Corp. It is a read-only inventory: record inspection only, taking no actions and making no decisions. The empty state reads that no objects are stored yet.

Example — Stored objects tab, the global inventory (every artifact extension)
  • Q3 re-engagement email

    @cinatra-ai/email:draft · obj_2a4e… · v1 · updated 8 minutes ago

    Team: Growth
  • Renewal nudge — cohort B

    @cinatra-ai/email:draft · obj_9c71… · v3 · updated 2 hours ago

    Organization: Acme Corp
  • Enterprise ICP · 240 prospects

    @cinatra-ai/prospects:list · obj_4f10… · v2 · updated yesterday

    Organization: Acme Corp

Restore objects — undoing object change sets. The Restore objects tab browses object change sets and undoes them. Every save, edit or delete an agent run — or a user action — makes over objects is captured as a reversible change set. The tab lists them time-keyed, across every extension, and each entry says exactly what it would restore: the operation (what changed), the affected type, how many objects, and when. The row's Undo action reverses that change set.

Authorized per object. A change set is restorable when the acting user holds restore authorization for every object it touched — the same per-object check the restore action enforces on confirm. There is no administrator bypass: an actor authorized for every affected object may restore it whatever their role, and an actor not authorized for some affected object is refused. The tab lists the change sets the actor may act on; each row's right edge holds an Undo action that opens the restore confirmation in place (an inline modal), and the empty state reads that there is nothing to undo.

Entry points outside the console. Two affordances deep-link to a single change set's restore surface, /configuration/artifacts/restore/[changeSetId]: the in-chat Undo last action chip beneath an agent-run card, and the Saved … · Undo toast on a successful mutation. Each renders only when the acting user is eligible to restore that change set — authorized for every affected object, and the change set still restorable. For a non-eligible user no affordance appears: no chip, no toast action — suppression over a disabled control, because a transient safety affordance that cannot act is noise, and a rendered control must never dead-end on the not-authorized panel. Opened directly, the /configuration/artifacts/restore/[changeSetId] route holds to the same eligibility: it serves the addressed change set's restore surface for an authorized actor of any role, and shows the standard not-authorized state to anyone else.

Example — Restore objects tab, the change-set list
  • Deleted 12 objects · @cinatra-ai/email:draft

    by an agent run · 6 minutes ago

  • Enriched 8 objects · @acme/support:case

    by an agent run · 41 minutes ago

  • Bulk visibility change · 40 objects

    by you · 1 hour ago

Example — targeted-restore entry points, by eligibility
Eligible actor — affordance shown
Saved draft
agent run · deleted 3 objects
Not eligible — no affordance
Saved draft no Undo
agent run · deleted 3 objects

No chip and no toast action — suppressed, not disabled, and never a link that dead-ends on the not-authorized panel.

Example — /configuration/artifacts/restore/[changeSetId], authorized actor (any role)
Restore this change?

Deleted 3 objects · @cinatra-ai/email:draft

change-set cs_5e21… · by an agent run · 2 minutes ago. You are authorized to restore every affected object — no administrator role required.

V. Change review — on the artifact-review surface

Shared review surface

One shared review surface. Agent-proposed changes to artifacts — enrichment field merges and the like — are reviewed on the shared, generic artifact-review surface. That surface — immutable review targets, gate-provenance authorization, and the host decision chrome — is not defined here; this spec only references it. A notification that a change awaits review deep-links to that surface — decide-once review lands in /notifications (see Application Design — Notifications), which federates pending approvals; a field-level artifact review opens on the artifact-review surface itself.

One place for every decision

A change proposal is a diff to review over an immutable target, exactly the shape the generic artifact-review surface already handles — so every human decision over an artifact, whatever produced the proposal, is reviewed in one place. This spec owns the library, the renderer, and the Artifacts console (type definitions, stored objects, restore); the review surface itself is outside this spec's scope. An applied change is an ordinary reversible change set, undoable through the Artifacts console's Restore objects tab (§IV).

VI. Upload & typing — the library ingest path

/artifacts library · MIME-base type · user-meaning picker

The library ingests, not just lists. The library toolbar (§I) carries a primary Upload action at its right edge, and the whole list region is a drop target. An upload takes two conceptual steps: a file is typed by its MIME base deterministically, then the user may assert its meaning in a type picker. The base step is exactly-one-or-refuse and never asks the user; the meaning step is optional and always the user's. This section is design only — the ingest pipeline, the required-base extensions and the classifier machinery are specified outside this page.

MIME-base typing — one base or a visible refusal. A file's detected MIME resolves to exactly one installed required base type — the roster is image, pdf, audio, video, chart, zip, text, document (office formats) and json. The bases are MIME-disjoint: a detected MIME either lands on one base or is refused (§VI.3). The base is the artifact's format axis and is set at write; the picker never changes it (§VI.1). A base-typed artifact with no asserted meaning is a complete, valid file artifact — meaning is optional, not a precondition.

Placement & states. The Upload control sits in the toolbar; the drop target is the list. The affordance reads across these states — idle (the button, plus a faint hint that files can be dropped), drag-over (the list region highlights and reads Drop to upload), uploading (a transient progress row at the top of the list), typed (the new row appears, or the type picker opens when the user chooses to assert meaning), and refused (the recourse panel — §VI.3). These interaction states are shown as example frames below; the machine-checkable state axis (§X) stays the closed loading · error · empty · kind:artifact set.

Example — the Upload affordance in the library toolbar (idle)
or drop a file
Example — drag-over

Drop to upload

Typed by its file kind on drop.

Example — uploading
2026 pricing sheet.pdf
62%
Example — typed (base resolved, no meaning yet)
2026 pricing sheet.pdfapplication/pdf

Base pdf · Set meaning

VI.1 · The installed-type picker — a user meaning assertion, never a re-type

Meaning, not format. After the base resolves, the user may open the type picker to assert what the file means. The picker admits only installed, file-accepting types whose accepts include the detected MIME — it is a user-sourced meaning assertion over those types, never a re-type: the format base (§VI) is fixed, and a PDF stays a PDF. Confirming the picker writes a user-sourced eligible meaning assertion, which outranks any matcher or agent assertion (see Renderer dispatch §III for the ranked model). If no installed type accepts the detected MIME, the picker offers the inline marketplace (§VII).

Derived meaning & suggestion chips. A classifier may derive a meaning for the file. When its confidence is at or above the pack's threshold, that meaning auto-surfaces as the row's presented identity — presentation only, with an org-level toggle as escape hatch — and the picker opens pre-selected on it. When confidence is below threshold, the candidate never auto-applies; it appears as a suggestion chip the user may confirm. Confirming a suggestion records the user's choice as a user-sourced meaning — which outranks the machine's — and the machine's suggestion is never silently promoted into it; dismissing it leaves the base-only row. The assertion lifecycle and the matcher service are specified outside this page; this spec fixes only the visible provenance and ranking.

Example — type picker (installed types accepting application/pdf; a below-threshold suggestion offered)
What is this?

Assert what 2026 pricing sheet.pdf means. This sets its meaning, not its file kind (application/pdf).

Suggested
Installed types that accept PDF
  • Contract @acme/legal:contractLegal
  • Pricing sheet @acme/sales:pricing-sheetSales
None of these — find a type…
Meaning ranks above the machine; the base never moves

The picker only ever asserts a user-sourced meaning — the highest-ranked identity in Renderer dispatch (§III). It cannot change the file's base (its format axis, set at upload): the choices are the installed types whose accepts admit the detected MIME, never a different file kind. Confirming a below-threshold suggestion chip yields a fresh user-sourced meaning; the machine's draft is never promoted into it.

VI.3 · Refusal with recourse

A refused upload is visible, and offers a way forward. Refusal is the base case only: when a file's detected MIME lands on no installed base the file cannot be stored, so the upload is refused — but never silently (this supersedes the chat silent-drop). The refusal names the file kind and gives recourse: for a user who can install, a deep link to the marketplace filtered to a base that accepts the detected MIME; for everyone else, a one-click Request install (§VII). The panel never dead-ends. This is distinct from missing meaning: when the base did resolve but no installed meaning type fits, nothing is refused — the artifact is kept as a base-only file row (§VI), and the picker's None of these path (§VI.1) offers a meaning type from the same marketplace.

Example — refused upload (no installed base accepts the detected MIME) with recourse

Can't type schematic.dwg

No installed base type accepts image/vnd.dwg. Install a base that handles it, then re-upload.

VII. Installing a type — the inline marketplace tab

Inline · kind:artifact catalog · admin install · non-admin request

Install without leaving the upload — and installing is admin-only. When no installed type fits, the picker (§VI.1) and the refusal panel (§VI.3) switch to an inline Marketplace tab, beside an Installed tab, inside the same picker panel — so the upload context and the return path are never lost. The tab is the public catalog filtered to artifact-kind packs (the storefront itself is Application Design — Marketplace), narrowed to a base when reached from a refusal (§VI.3) or a meaning type when reached from the picker. Whether the tab installs or only requests depends on the actor: a platform administrator installs; everyone else requests.

Administrator — install inline. A platform administrator browses the artifact-kind packs, picks one, confirms an install-scope dialog, and the pack installs without a redirect. The card shows an installing state; once the install settles the new type becomes selectable and the picker (§VI.1) refreshes to include it. The install mechanics — scope, dependency resolution and registry readiness — are Application Design — Extensions; this spec fixes only the visible flow and its states.

Non-administrator — one-click Request install. Marketplace installs from the picker stay platform-admin-only. A non-admin gets a functional one-click Request install on each pack: it notifies the platform admins — carrying the pack, the requester and a marketplace deep link — and coalesces repeat clicks onto one request, then flips the card to a muted Request sent state. The admin completes the install from that notification (Application Design — Notifications). No dead-end, no disabled button with no path.

Example — inline marketplace tab, administrator (artifact-kind packs; one installing)
kind: artifact · accepts PDF
Back to picker typing 2026 pricing sheet.pdf
  • Contract

    Accepts PDF, DOCX · defines @acme/legal:contract

  • Pricing sheet

    Installing · selectable once it settles…

    Installing
Example — non-administrator: one-click Request install (before / after)
Contract

Needs a platform admin to install.

Contract

Requested · admins notified.

Request sent
Install stays admin-only; the request is real

A non-admin never installs from the picker. Request install is a functional action, not a placeholder: it notifies the platform admins — the pack, the requester and a marketplace deep link — reflects a Request sent state, and coalesces repeated clicks onto one request; the admin installs from that notification.

VIII. Dashboards as artifacts

Library rows · pointer detail · scope chips

A dashboard is an artifact. Every dashboard has a paired artifact row of the type @cinatra-ai/dashboard-artifact:dashboard, so it lists in the library and opens like any other artifact. It reads as a normal library row — a dashboard glyph, its name, the muted defining-extension label (Dashboards, or a meaning pack), scope and updated time — with one difference from a file row: no Download. A dashboard is a live view, not a file, so the row offers only Open, which navigates straight to the dashboard itself at its canonical surface — a dashboard is never previewed or rendered inline in the library.

Example — a dashboard row in the library (Open only; no Download)
  • Pipeline health — Q3Dashboards

    Team: Growth · updated 20 minutes ago

    Open

The artifact detail page — a pointer, not a viewer. A dashboard-typed artifact's detail route /artifacts/[id] is a pointer surface: it never previews or renders the dashboard inline. It shows the artifact's metadata — the dashboard glyph, its name, the defining-extension label, the scope chips and the updated time — over a single primary Open dashboard affordance that navigates to the dashboard's canonical surface. A dashboard is a live view with its own home, so it is always opened there; the library row and this detail page only point to it. There is no read-only grid, no embedded portlet render, and no in-page rendering here — the canonical surface owns both viewing and editing.

Example — /artifacts/[id], the dashboard pointer detail (metadata + Open dashboard + scope chips; no inline render)
Pipeline health — Q3 Dashboards
In scope

A dashboard is a live view — this page points to it, it does not render here.

@cinatra-ai/dashboard-artifact:dashboard · updated 20 minutes ago

Open dashboard

Scope chips & routing. A dashboard has exactly one canonical home — its single entityRef, one canonical entity identity that never changes. Beyond that home, a dashboard can be listed in other scopes as secondary reference listings (a separate listing relation, not a second home), and the pointer detail page renders one scope chip per listing. A chip opens the dashboard in that scope's context; the library row and the Open dashboard affordance both open the dashboard at its canonical surface; none of these re-anchor the dashboard — the singular entityRef is never moved.

Dashboards are always visible in their scope

A dashboard is always visible to everyone in its scope — project, team or organization — with no admin-specific access anywhere. The scope chips name exactly the scopes in which every member can already see it; there is no admin-gated dashboard state and no admin-only access. A reference in the whole-workspace collection is the one separate case: it is a link whose listing is viewer-filtered, so it names no scope every member can already see, and a platform administrator may mark it visible to everyone (§IX.4) — a read-only grant on that link, never on a scope's access. This differs from the workspace-wide object console (§IV), which is record inspection, not a per-scope visibility grant.

IX. The Dashboards tab — every entity page's first tablist entry

Entity-page tablist (Dashboards · Assistants · Agents · Artifacts · Skills, Settings last) · secondary listings · add / remove

Every entity page opens on a Dashboards tab. A personal, project, team, organization or workspace page carries a canonical underline tablist (Application Design — Components · Tabs) whose entries are, in this order, Dashboards, Assistants, Agents, Artifacts and Skills. On a project, team or organization a Settings entry is appended last — that entity's management pane, where rename, visibility and the members / access section live folded together. A personal scope and the workspace scope have no Settings pane, so their tablist is those five entries alone. Dashboards is a plain tablist entry like any other — no pill, no bespoke chrome — and the etched paired-line rule begins to the right of the last tab and runs to the page edge. This section fixes the Dashboards tab and the tablist it sits in; the other four tabs and the Settings pane are their own surfaces.

What the Dashboards tab lists. On a team, project or organization the tab lists the dashboards in that scope — a dashboard whose canonical home is this scope (the singular entity identity of §VIII), and any secondary reference listing added here (a separate listing relation, never a second home; §VIII). Both open the dashboard at its canonical surface, never rendered inline (§VIII), and a scope manager can add an existing dashboard as a listing (§IX.1). On a personal scope the tab shows the acting user's own dashboards. The whole-workspace scope is a reference target: its Dashboards tab behaves exactly like the other scopes' Dashboards tab (§IX.3) — the same per-user dashboard shell with its non-removable Overview default, and the same single Add dashboard popup, whose Reference a dashboard from the scopes below section is how a lower-scope dashboard is brought up to the workspace. A personal scope keeps its landed shape — Create new and the installed catalog only, with no reference section — so the reference half of Add dashboard is the three shared scopes and the workspace.

Row anatomy — no relation badge, no per-row type label. Each row carries a leading dashboard glyph, the dashboard name, the updated time, and an Open affordance. Every row on this tab is a dashboard, so no row repeats a Dashboards type label, and a row does not advertise where the dashboard lives — whether it is homed here or merely listed here is not surfaced. A dashboard's canonical home never moves: this tab neither re-homes nor deletes. The single place the home / listing difference shows is the Remove control, which appears only on a removable row — a secondary listing, which can be unlisted — and never on a dashboard homed in this scope. Removability is read from the presence of Remove alone; there is no Home or Listed badge. Open navigates to the dashboard's canonical surface exactly as in §VIII — the tab points, it never renders a dashboard inline.

Example — a team page, Dashboards tab selected (one homed row, no Remove; two listings, with Remove); the strip reads Dashboards · Assistants · Agents · Artifacts · Skills with Settings appended last

Growth

Team

The dashboards in Team: Growth.

  • Pipeline health — Q3

    updated 20 minutes ago

    Open
  • Revenue attribution

    updated 2 hours ago

    Open
  • Onboarding funnel

    updated yesterday

    Open
Example — a personal scope: the five entries with no Settings tab (no Settings pane — the reading the workspace scope carries too), listing the user's own dashboards, and there is no add-to-scope

Personal

Your scope

The dashboards you own.

  • Weekly focus

    updated 3 hours ago

    Open
  • Reading queue

    updated yesterday

    Open

IX.1 · Add to scope — the picker over dashboards the scope may list

One Add dashboard popup, three sections. Add dashboard opens a single popup carrying three sections, in this order — Create new, then Reference a dashboard from the scopes below, then Add from the installed catalog. Create new and the installed catalog each produce a dashboard homed in this scope; the middle section is the add-to-scope picker this subsection fixes — the reference relation of §VIII, never a second home. The picker drawn below is that middle section on a team tab; the whole popup is drawn once, on the workspace landing (§IX.3), and reads the same on a team, project or organization tab. A personal scope keeps its landed shape — Create new and the installed catalog only, with no reference section.

The picker's pool is the acting user's own dashboards; only scope-visible ones are directly addable. Add dashboard opens a picker over the dashboards the acting user can see, excluding any already homed or listed here — the same exclusion holds for the workspace tab's reference section (§IX.3). This is the collection-add contract made visible: a candidate is addable iff the actor may see it AND a generic member of the scope could already see it (the scope-vantage guard) — the two reads the contract composes with the scope-write gate (§IX.2). A candidate the actor can see but the scope cannot yet is not directly addable and is never widened silently: it still appears in the picker, but only with its recourse (below) — never an in-place add.

Recourse when the scope can't see it — the promotion request. When a candidate is invisible to the scope, its row names the visibility it must reach for the scope to see it and offers the promotion request in that vocabulary — team on a team tab, organization on an organization tab, and a team target is offered only to a member of that team (a non-member sees no offer). The request is the recourse, not an immediate add: the dashboard lists only once its visibility has actually widened and been granted. A project tab has no promotion recourse — a project is a resource refinement, not a visibility tier — so a candidate the project cannot see is simply not addable there, stated plainly with no offer (fail-closed, exactly as the contract yields a null recourse for that scope kind).

The workspace is a reference target, and its collection is filtered at read time. The workspace tab admits a reference to a dashboard homed in any scope below it — the bring a lower-scope dashboard up to the workspace move — and a reference never widens access by itself. In place of the scope-vantage guard, the listing is viewer-filtered: an entry appears only when the viewer holds the everyone-grant on it (§IX.4) or passes the target's home access. So a workspace tab carries no promotion recourse — nothing is widened to make a reference addable — and the only widening in this collection is the explicit, audited grant of §IX.4.

Example — the add-to-scope picker (addable rows + a scope-invisible candidate offering the promotion request)
Add a dashboard to Team: Growth

Lists an existing dashboard here as a reference — its canonical home does not move. Only dashboards this team can already see are listable.

  • Expansion pipelinethe team can already see this
  • Win/loss reviewthe team can already see this
  • Personal experimentsprivate — the team can't see it yet
The contract's structured denial, rendered

The picker never adds a dashboard the scope can't see, and never widens one silently. Such a candidate — actor-visible but scope-invisible — is surfaced only as a promotion request naming the exact target visibility (team / organization), and a team target only to a member of that team; on a project tab, where no promotion widen applies, it reads as not addable with no offer. This is the collection-add evaluation order made visible — actor-see, then scope-write (§IX.2), then scope-see — with the recourse offered only on the scope-see failure (gates 1–2 having passed), and only when the actor could actually submit it.

IX.2 · Who sees Add and Remove — the scope-write gate

The management controls are gated to the scope's managers. Add dashboard and every row's Remove appear only to a principal who may write (manage) this scope's Dashboards collection. Grounded in the shipped scope-authority vocabulary, that is a team admin on a team tab, an organization admin or owner on an organization tab, and a project admin or owner on a project tab. This is the design-side shape of the collection-add contract's injected actor-may-write-scope decision (gate 2): the precise predicate is the platform's to implement, and the population it names is pinned here. On the workspace tab the curators are named differently, the collection there being references rather than a scope's own dashboards: organization admins add and remove only links whose target's home organization is theirs, and platform administrators curate all of them (§IX.3).

Everyone else sees a read-only tab. On the three shared scopes — team, project and organization — a member without write authority still sees the Dashboards tab and every row — homed and listed alike — and opens any of them; they simply get no Add affordance and no Remove control. Suppression, not a disabled control: a management action the member cannot take is not rendered. The full add is authorized only when all three hold — the actor may see the dashboard, the actor may write the scope, and the whole scope may see the dashboard; when the first two hold but only the scope-see gate fails, the §IX.1 promotion recourse is offered (a failure of either earlier gate is a plain denial, no recourse). The whole-workspace tab reads differently, because its collection is references: the listing a member sees is viewer-filtered (§IX.3), that filter stands in for the scope-vantage gate, so no promotion recourse is offered there and the only widening is the audited grant of §IX.4.

Example — the same tab for a scope manager vs. a member without write access (the permission axis)
Scope manager — Add + Remove shown
Add dashboard
Revenue attribution Remove
Member — read-only, still sees every row
no Add
Revenue attribution Open
Read stays universal; only management is gated

A dashboard is always visible to everyone in its scope — this tab keeps that on the three shared scopes: every member sees every homed and listed dashboard and can open it, with no admin-gated visibility on any of them. The scope-write gate covers curation of the collection — adding and removing listings — a different axis from access to the dashboard. Gating who may add or remove a listing is not admin-gated access; the dashboard itself stays open to all in scope. The single place an administrator touches visibility is the workspace reference collection's visible to everyone grant (§IX.4) — a read-only grant on a link, never on the dashboard's home access.

Example — the tab across its load states (permission-denied is the read-only member view, §IX.2)
empty

No dashboards in this scope yet

A manager can Add an existing dashboard, or create one that homes here.

loading
error

Couldn't load this scope's dashboards

IX.3 · The workspace Dashboards landing — the Overview default and the reference collection

What changed, and why. The workspace was excluded from add-to-scope: a dashboard could be listed in a team, a project or an organization, and the scope that holds them all could list nothing. It is now admitted as a reference target, so a dashboard homed anywhere below can be brought up to the workspace and read there beside the workspace's own — one page a member opens to see the whole workspace's dashboards instead of walking every scope. Admitting it widens no access: a reference is a link, the target keeps its single canonical home and its own home access (§VIII), the listing is viewer-filtered so a member reads only entries they could already reach, and the one deliberate widening — the read-only visible to everyone grant of §IX.4 — is a platform administrator's explicit, audited act on a single link. Sharing a reference with named people or invitationsinvitation-granular sharing — is a named follow-up outside this drawing.

The landing. The workspace page opens on Dashboards, carrying the five-entry strip with no Settings entry (§IX). The tab is a per-user dashboard shell — each member reads their own list — whose first row is Overview, the default the shell always carries: it is non-removable, so it shows no Remove, exactly as a dashboard homed in a scope shows none (§IX). The two other rows in this example are references, and therefore removable; a dashboard this tab's popup creates, or adds from the installed catalog, homes in the workspace and shows no Remove, exactly as Overview does (§IX). Row anatomy is unchanged: no row advertises where its dashboard lives, and the visible to everyone mark (§IX.4) is an access mark on the link, never a relation badge.

Example — the workspace page, Dashboards tab selected (the non-removable Overview default, then two referenced rows, one marked visible to everyone)

Workspace

All scopes

Your dashboards in the whole workspace — and the ones referenced up from the scopes below.

  • Overview

    updated 5 minutes ago · the default — always here, and never removable

    Open
  • Pipeline health — Q3

    updated 20 minutes ago

    Open
  • Revenue attribution

    updated 2 hours ago

    Visible to everyone Open
Example — the single Add dashboard popup and its three sections: Create new, Reference a dashboard from the scopes below, Add from the installed catalog
Add dashboard

One popup, three sections. A reference lists an existing dashboard here as a link — its canonical home does not move, and nobody gains access by the listing alone.

Create new
Dashboard name

Homes in the workspace.

Reference a dashboard from the scopes below
  • Support load — weeklyhomed in Team: Support
  • Spend by projecthomed in Organization: Acme

The link never widens access. A member reads the entry only when they already pass the target's home access, or a platform administrator marks it visible to everyone (§IX.4).

Add from the installed catalog
  • Adoption overview@cinatra-ai/dashboard-artifact:dashboard

A catalog dashboard homes in the workspace, exactly as a created one does.

Access rules of the reference collection

Four rules hold over every reference in this collection. A reference never widens access by itself — it is a link beside the dashboard's one canonical home (§VIII). The listing is viewer-filtered: an entry appears only when the viewer holds the everyone-grant on it (§IX.4) or passes the target's home access. Organization admins add and remove only links whose target's home organization is theirs. And platform administrators curate all of them.

IX.4 · Visible to everyone — the read-only grant on a reference

One concrete grant, on the link row. A platform administrator may mark a reference visible to everyone. The mark is a concrete grant on the link row — not a property of the dashboard, and not a change to its home access — and it carries read only: every member may then see and open the referenced dashboard, and write access never widens. It is revoked by unsetting it or by removing the link, and both the grant and its revocation are audited.

Who may set it, and what everyone else sees. The control is a platform administrator's alone. For every other principal it is drawn muted and disabled with the reason named — the drawing's disabled-control reading, not a suppression, because the mark itself is information a member may read: they see that the entry is visible to everyone, and why they cannot change it. Suppression stays the rule for the collection's own management controls (§IX.2); this one control is a stated exception, because its state is part of what the row says.

Example — the mark on a referenced entry, and the control that sets and unsets it: a platform administrator vs. everyone else
Platform administrator — the grant is settable
Revenue attribution Visible to everyone
Visible to everyone

On: every member may open it, read-only. Unset it, or remove the link, to revoke — both are audited.

Visible to everyone

Off: the same link, read by the target's home access alone.

Everyone else — the control is muted, with the reason
Revenue attribution Visible to everyone
Visible to everyone

Only a platform administrator can set or unset who a reference is visible to.

Example — the same referenced entry as a non-admin viewer who passes no home access on it: with the grant, and without it
With the grant — listed, and read-only
Revenue attribution Visible to everyone Open

No Remove and no write affordance: the grant carries read alone, and curation belongs to the collection's admins (§IX.2).

Without the grant — not listed for this viewer

The entry is not in this viewer's list

The listing is viewer-filtered: with no everyone-grant and no home access on the target, there is no row — not a locked one.

Read-only, revocable, audited

The grant carries read alone — a member who reaches a dashboard through it may open it and may change nothing; write access stays the target's own and the grant never widens it. Unsetting the mark, or removing the link, revokes it, and both the setting and the revocation are audited, so a reference that was visible to everyone and is no longer leaves a record of who changed it and when.

X. Axes the spec carries — responsive · theme · state

Breakpoints · single cream theme · the data-state set

Every new surface honors the three axes the Application Design family carries. The affordances above (§VI–§IX) are specified once and hold across breakpoint, theme and state — a conformance run checks each axis, so the spec names them.

Responsive. The library sits at the wide data width (max-w-7xl). On a narrow viewport the toolbar wraps — the search field takes the full row, the Type / Scope / Upload controls wrap beneath it, and Upload stays reachable (never behind an overflow). The type picker and the inline marketplace tab are bounded panels that shrink to the viewport (max-width:100%); the marketplace pack list is single-column already. The dashboard pointer detail card wraps to the narrow width — its scope chips flow to a new line and the Open dashboard affordance drops beneath the metadata — and any wide table scrolls inside its own container rather than widening the page. The Dashboards tab (§IX) wraps too — the entity-page tablist (Dashboards, Assistants, Agents, Artifacts, Skills, and Settings last on the shared scopes) wraps onto a further underline row rather than scrolling or hiding an entry behind an overflow, its Add dashboard affordance drops beneath the tab strip, each row's controls flow to a new line rather than overflowing, and the add-to-scope picker is a bounded panel that shrinks to the viewport (max-width:100%).

Theme. There is one theme — the shared cream palette. Every new surface draws only from the shared tokens (the :root block); no affordance introduces a colour, and the base style block is identical to every sibling spec (Conventions · self-contained specs). Set on cream.

State. The machine-checkable state axis is the closed data-state set — loading, error, empty, plus kind:artifact — carried on the conformance surfaces above. The richer interaction states each affordance moves through are shown as example frames and bound to their outcomes via data-action. The table names them so a build covers each:

Example — interaction states per new affordance
Affordance Interaction states data-state
Upload (§VI)idle · drag-over · uploading · typed · refusedloading · error · kind:artifact
Type picker (§VI.1)choosing · suggestion · confirmed · auto-surfacedloading · error
Marketplace tab (§VII)browse · installing · request-sent · empty · errorempty · error · loading · kind:artifact
Dashboard pointer (§VIII)loading · ready · open-dashboard · errorloading · error
Dashboards tab (§IX)read-only · manager · empty · loading · error · open-dashboard · remove-listingempty · error · loading · kind:artifact
Add-to-scope picker (§IX.1)listable · adding · added · promotion-offer · not-addable · errorempty · error · loading