Files
PaperClipAI/ui/storybook/fixtures/paperclipData.ts
TonioandClaude Opus 5 42c6f8a424 Onboarding: model source tiles, one input canvas, and Storybook coverage for the agent arc (#12613)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - New customers meet it through onboarding, whose last three steps run
inside the tenant: create an agent, connect a model, review
> - The connect step is the one that decides whether the agent can run
at all, and it had drifted — three contributors changed it in parallel,
and its visual language no longer matched the rest of the flow
> - It also could not be looked at without a provisioned stack, so
defects in it were only found by walking a real signup, and the review
step behind it could not be reached at all when it failed
> - This pull request brings the visual work onto the sign-in behaviour
that already shipped, and adds Storybook coverage for all three steps
> - The benefit is that the step is easier to read, and that it can now
be inspected and driven before it ships rather than after

## Linked Issues or Issue Description

**What existing behavior does this improve?**
The onboarding connect-a-model step. It presented the model choice as a
dropdown plus an "Advanced settings" disclosure, and put each credential
type in a different place, so the controls below moved whenever the
choice changed.

**Subsystem affected**
Tenant onboarding wizard (`ui/src/components/OnboardingWizard.tsx`) and
its Storybook coverage.

**Current behavior**
The step offered every registered adapter through a disclosure.
Credential entry appeared in a different shape per source. None of the
three agent-arc steps could be rendered outside a provisioned cloud
stack, so the sign-in panel and the review step were only reachable by
walking a real signup.

**Proposed behavior**
Two brand tiles for the recommended sources, a link that switches
between subscription and API-key credentials, and one canvas that holds
whichever input the current choice needs. Storybook stories mount the
real wizard against fixtures and walk it forward, so every step and its
states can be inspected locally.

**Reason and benefit**
The step reads as one decision rather than three scattered ones, and its
furniture stays still while the choice changes. The stories mean a
regression in it is visible before release instead of during a signup.

**Breaking changes**
No API or schema change. One behavioural narrowing, described under
Risks.

## What Changed

- Replaces the adapter dropdown and "Advanced settings" disclosure with
`ModelSourceTiles` — brand tiles for Claude Code and Codex.
- Adds `CredentialModeLink`, a text toggle between subscription sign-in
and API keys, replacing the disclosure.
- Adds `ConnectInputCanvas`: one surface that holds the sign-in panel or
the API-key field and resizes between them, so the Connect button below
does not move.
- Keeps the existing sign-in behaviour unchanged. `AgentConfigForm`
changes are presentation only — the provider name in the title, the CTA
wording, and `space-y` to `gap`. No change to the login mutations,
queries, or session handling.
- Restores the sleep marks on the dormant agent for the two steps before
the hire.
- Adds Storybook stories for all three agent-arc steps, with fixtures
for the environments, auth signal, both adapters' login flows, and the
hire.
- Copy: names the provider being signed in to ("Sign in to
Anthropic"/"Sign in to OpenAI"), and drops "Clippy" from the agent-name
helper text.

## Verification

- `pnpm vitest run src/components storybook` in `ui/` — 139 tests over
the touched suites, 1986 across `src/components`.
- `pnpm typecheck` in `ui/` — clean.
- Storybook, `Onboarding/Agent arc`: walk each story. Step 1 has no Back
button, steps 2 and 3 do.
- The sign-in gate: on `Connect a model`, press Connect without signing
in. It holds on step 2 and reports "No working authentication was
found." On `Review`, which fixtures an authenticated signal, Connect
reaches the review step.
- Both providers' login flows: press Sign in on the Claude tile for the
authorization URL and browser-code field, and on the Codex tile for the
device URL and code.
- The Claude sign-in was also walked end to end on a staging tenant,
including the OAuth redirect and pasting the code back.

## Risks

- **Onboarding now offers two model sources instead of every registered
adapter.** `ModelSourceTiles` is fed the `recommended` set, which is
`claude_local` and `codex_local`; Gemini, Cursor, Grok, Kimi, OpenCode
and Paperclip Runner are no longer selectable *during onboarding*. This
is deliberate. The full list is unchanged in agent settings, which is
where an adapter can still be switched after the agent exists, and
adding a source back is one `recommended: true` in
`adapter-display-registry.ts`. Flagging it because it is the one
behavioural narrowing here and it is not visible from the diffstat.
- The API key entered on this step is held in component state and
deliberately never written to the onboarding draft, because that draft
is `localStorage`. A customer who leaves mid-step re-enters the key;
that is the intended trade.
- Storybook-only risk: the fixtures now answer the environment test from
the story's auth state. If a future change moves the hire's gate off the
`adapter_auth_missing` check code, the stories would keep passing while
the product regressed. The gate is asserted in the adapter packages' own
tests, not here.
- Motion changes are low risk and reversible: the input canvas animates
its contents only, and its container was deliberately left unanimated
after an animated wrapper clipped the sign-in panel.

## Model Used

Claude Opus 5 (`claude-opus-5`), via Claude Code with extended thinking,
tool use, and browser-driven verification of the Storybook stories.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 09:57:46 -07:00

1745 lines
53 KiB
TypeScript

import type {
ActivityEvent,
Agent,
Approval,
AuthSession,
BudgetPolicySummary,
Company,
CompanySecret,
CompanySecretBinding,
CompanySecretProviderConfig,
DashboardSummary,
ExecutionWorkspace,
Goal,
Issue,
IssueDocument,
IssueLabel,
Project,
SecretAccessEvent,
SecretProviderConfigDiscoveryPreviewResult,
SecretProviderDescriptor,
SidebarBadges,
WorkspaceRuntimeService,
} from "@paperclipai/shared";
import type { RunForIssue } from "@/api/activity";
import type { LiveRunForIssue } from "@/api/heartbeats";
const now = new Date("2026-04-20T12:00:00.000Z");
const recent = (minutesAgo: number) => new Date(now.getTime() - minutesAgo * 60_000);
const storybookRepoRoot = "~/paperclip";
const storybookWorkspaceRoot = `${storybookRepoRoot}/.paperclip/workspaces`;
const storybookWorktreeRoot = `${storybookRepoRoot}/.paperclip/worktrees`;
export const storybookCompanies: Company[] = [
{
id: "company-storybook",
name: "Paperclip Storybook",
description: "Fixture company for isolated UI review.",
status: "active",
pauseReason: null,
pausedAt: null,
issuePrefix: "PAP",
issueCounter: 1641,
budgetMonthlyCents: 250_000,
spentMonthlyCents: 67_500,
defaultResponsibleUserId: "user-board",
requireBoardApprovalForNewAgents: true,
interactionResolverGovernance: {},
feedbackDataSharingEnabled: true,
feedbackDataSharingConsentAt: null,
feedbackDataSharingConsentByUserId: null,
feedbackDataSharingTermsVersion: null,
logoAssetId: null,
logoUrl: null,
createdAt: new Date("2026-04-01T09:00:00.000Z"),
updatedAt: now,
},
{
id: "company-research",
name: "Research Bureau",
description: "A second active company for rail and switcher state coverage.",
status: "active",
pauseReason: null,
pausedAt: null,
issuePrefix: "RES",
issueCounter: 88,
budgetMonthlyCents: 180_000,
spentMonthlyCents: 39_500,
defaultResponsibleUserId: "user-board",
requireBoardApprovalForNewAgents: false,
interactionResolverGovernance: {},
feedbackDataSharingEnabled: false,
feedbackDataSharingConsentAt: null,
feedbackDataSharingConsentByUserId: null,
feedbackDataSharingTermsVersion: null,
logoAssetId: null,
logoUrl: null,
createdAt: new Date("2026-04-03T09:00:00.000Z"),
updatedAt: recent(10),
},
{
id: "company-paused",
name: "Launch Ops",
description: "Paused company for inactive switcher treatment.",
status: "paused",
pauseReason: "manual",
pausedAt: recent(240),
issuePrefix: "OPS",
issueCounter: 204,
budgetMonthlyCents: 90_000,
spentMonthlyCents: 91_200,
defaultResponsibleUserId: "user-board",
requireBoardApprovalForNewAgents: true,
interactionResolverGovernance: {},
feedbackDataSharingEnabled: false,
feedbackDataSharingConsentAt: null,
feedbackDataSharingConsentByUserId: null,
feedbackDataSharingTermsVersion: null,
logoAssetId: null,
logoUrl: null,
createdAt: new Date("2026-04-05T09:00:00.000Z"),
updatedAt: recent(240),
},
];
export const storybookAuthSession: AuthSession = {
session: {
id: "session-storybook",
userId: "user-board",
},
user: {
id: "user-board",
name: "Riley Board",
email: "riley@paperclip.local",
image: null,
},
sentryDsn: null,
};
export const storybookAgents: Agent[] = [
{
id: "agent-codex",
companyId: "company-storybook",
name: "CodexCoder",
urlKey: "codexcoder",
role: "engineer",
title: "Senior Product Engineer",
icon: "code",
status: "running",
reportsTo: "agent-cto",
capabilities: "Ships full-stack Paperclip product tasks, Storybook coverage, and verification.",
adapterType: "codex_local",
adapterConfig: {},
runtimeConfig: {},
budgetMonthlyCents: 125_000,
spentMonthlyCents: 43_200,
pauseReason: null,
pausedAt: null,
permissions: { canCreateAgents: false },
lastHeartbeatAt: recent(3),
metadata: null,
createdAt: recent(12_000),
updatedAt: recent(3),
},
{
id: "agent-qa",
companyId: "company-storybook",
name: "QAChecker",
urlKey: "qachecker",
role: "qa",
title: "QA Engineer",
icon: "shield",
status: "idle",
reportsTo: "agent-cto",
capabilities: "Validates browser flows, acceptance criteria, and release smoke tests.",
adapterType: "claude_local",
adapterConfig: {},
runtimeConfig: {},
budgetMonthlyCents: 80_000,
spentMonthlyCents: 18_900,
pauseReason: null,
pausedAt: null,
permissions: { canCreateAgents: false },
lastHeartbeatAt: recent(24),
metadata: null,
createdAt: recent(11_000),
updatedAt: recent(24),
},
{
id: "agent-cto",
companyId: "company-storybook",
name: "CTO",
urlKey: "cto",
role: "cto",
title: "CTO",
icon: "crown",
status: "active",
reportsTo: null,
capabilities: "Reviews architecture, quality gates, and engineering priority tradeoffs.",
adapterType: "codex_local",
adapterConfig: {},
runtimeConfig: {},
budgetMonthlyCents: 200_000,
spentMonthlyCents: 54_000,
pauseReason: null,
pausedAt: null,
permissions: { canCreateAgents: true },
lastHeartbeatAt: recent(41),
metadata: null,
createdAt: recent(14_000),
updatedAt: recent(41),
},
];
export const storybookAgentMap = new Map(storybookAgents.map((agent) => [agent.id, agent]));
/**
* The agent the onboarding hire returns.
*
* Kept out of `storybookAgents` deliberately: that list is what the company
* already has, and this one does not exist until the wizard's Connect step
* creates it. Putting it in the list would give the review step an agent it had
* not yet hired.
*/
export const storybookHiredAgent: Agent = {
id: "agent-storybook",
companyId: "company-storybook",
name: "Darnold",
urlKey: "darnold",
role: "general",
title: "Chief of Staff",
icon: "sparkles",
status: "idle",
reportsTo: null,
capabilities: "Runs the company's first workflows and hires the team behind them.",
adapterType: "claude_local",
adapterConfig: {},
runtimeConfig: {},
budgetMonthlyCents: 100_000,
spentMonthlyCents: 0,
pauseReason: null,
pausedAt: null,
permissions: { canCreateAgents: true },
lastHeartbeatAt: null,
metadata: null,
createdAt: recent(0),
updatedAt: recent(0),
};
export const storybookIssueLabels: IssueLabel[] = [
{
id: "label-ui",
companyId: "company-storybook",
name: "UI",
color: "#0f766e",
createdAt: recent(20_000),
updatedAt: recent(20_000),
},
{
id: "label-docs",
companyId: "company-storybook",
name: "Design system",
color: "#f59e0b",
createdAt: recent(20_000),
updatedAt: recent(20_000),
},
{
id: "label-backend",
companyId: "company-storybook",
name: "API",
color: "#2563eb",
createdAt: recent(18_000),
updatedAt: recent(18_000),
},
{
id: "label-risk",
companyId: "company-storybook",
name: "Risk",
color: "#dc2626",
createdAt: recent(16_000),
updatedAt: recent(16_000),
},
];
const storybookIssueLabelMap = new Map(storybookIssueLabels.map((label) => [label.id, label]));
function labelsFor(ids: string[]) {
return ids.map((id) => storybookIssueLabelMap.get(id)).filter((label): label is IssueLabel => Boolean(label));
}
export const storybookGoals: Goal[] = [
{
id: "goal-company",
companyId: "company-storybook",
title: "Build Paperclip",
description: "Make Paperclip the control plane operators trust for autonomous AI companies.",
level: "company",
status: "active",
parentId: null,
ownerAgentId: "agent-cto",
createdAt: recent(30_000),
updatedAt: recent(8),
},
{
id: "goal-board-ux",
companyId: "company-storybook",
title: "Tighten board operator visibility",
description: "Every project, goal, and workspace surface should reveal ownership, progress, and runtime state at a glance.",
level: "team",
status: "active",
parentId: "goal-company",
ownerAgentId: "agent-codex",
createdAt: recent(19_000),
updatedAt: recent(18),
},
{
id: "goal-agent-runtime",
companyId: "company-storybook",
title: "Stabilize agent runtime loops",
description: "Keep local and isolated workspaces predictable while preserving operator control.",
level: "team",
status: "planned",
parentId: "goal-company",
ownerAgentId: "agent-cto",
createdAt: recent(17_500),
updatedAt: recent(60),
},
{
id: "goal-storybook",
companyId: "company-storybook",
title: "Complete Storybook review coverage",
description: "Capture dense board UI states in fixture-backed stories before release review.",
level: "task",
status: "active",
parentId: "goal-board-ux",
ownerAgentId: "agent-codex",
createdAt: recent(9_000),
updatedAt: recent(3),
},
{
id: "goal-budget-safety",
companyId: "company-storybook",
title: "Enforce spend guardrails",
description: "Budget hard stops should be visible before they surprise operators.",
level: "agent",
status: "achieved",
parentId: "goal-agent-runtime",
ownerAgentId: "agent-qa",
createdAt: recent(12_000),
updatedAt: recent(120),
},
{
id: "goal-archived-import",
companyId: "company-storybook",
title: "Retire old import wizard",
description: "Legacy import wizard work is preserved for audit only.",
level: "task",
status: "cancelled",
parentId: "goal-board-ux",
ownerAgentId: null,
createdAt: recent(24_000),
updatedAt: recent(2_500),
},
];
function createRuntimeService(
overrides: Partial<WorkspaceRuntimeService> = {},
): WorkspaceRuntimeService {
return {
id: overrides.id ?? "service-storybook",
companyId: overrides.companyId ?? "company-storybook",
projectId: overrides.projectId ?? "project-board-ui",
projectWorkspaceId: overrides.projectWorkspaceId ?? "workspace-board-ui",
executionWorkspaceId: overrides.executionWorkspaceId ?? "execution-workspace-storybook",
issueId: overrides.issueId ?? "issue-storybook-1",
scopeType: overrides.scopeType ?? "execution_workspace",
scopeId: overrides.scopeId ?? "execution-workspace-storybook",
serviceName: overrides.serviceName ?? "storybook",
status: overrides.status ?? "running",
lifecycle: overrides.lifecycle ?? "ephemeral",
reuseKey: overrides.reuseKey ?? "storybook",
command: overrides.command ?? "pnpm storybook",
cwd: overrides.cwd ?? `${storybookRepoRoot}/ui`,
port: overrides.port ?? 6006,
url: overrides.url ?? "http://localhost:6006",
provider: overrides.provider ?? "local_process",
providerRef: overrides.providerRef ?? null,
ownerAgentId: overrides.ownerAgentId ?? "agent-codex",
startedByRunId: overrides.startedByRunId ?? "run-storybook",
lastUsedAt: overrides.lastUsedAt ?? recent(4),
startedAt: overrides.startedAt ?? recent(16),
stoppedAt: overrides.stoppedAt ?? null,
stopPolicy: overrides.stopPolicy ?? null,
healthStatus: overrides.healthStatus ?? "healthy",
configIndex: overrides.configIndex ?? 0,
createdAt: overrides.createdAt ?? recent(16),
updatedAt: overrides.updatedAt ?? recent(4),
};
}
const storybookWorkspaceRuntime = {
commands: [
{
id: "storybook",
name: "Storybook",
kind: "service",
command: "pnpm storybook",
cwd: "ui",
lifecycle: "shared",
},
{
id: "typecheck-ui",
name: "UI typecheck",
kind: "job",
command: "pnpm --filter @paperclipai/ui typecheck",
cwd: ".",
},
],
};
export const storybookProjectWorkspaces: Project["workspaces"] = [
{
id: "workspace-board-ui",
companyId: "company-storybook",
projectId: "project-board-ui",
name: "Board UI",
sourceType: "local_path" as const,
cwd: `${storybookRepoRoot}/ui`,
repoUrl: "https://github.com/paperclipai/paperclip",
repoRef: "master",
defaultRef: "master",
visibility: "default" as const,
setupCommand: "pnpm install",
cleanupCommand: null,
remoteProvider: null,
remoteWorkspaceRef: null,
sharedWorkspaceKey: "board-ui",
metadata: null,
runtimeConfig: {
workspaceRuntime: storybookWorkspaceRuntime,
desiredState: "running" as const,
serviceStates: { storybook: "running" as const },
},
isPrimary: true,
runtimeServices: [createRuntimeService()],
createdAt: recent(20_000),
updatedAt: recent(4),
},
{
id: "workspace-docs-remote",
companyId: "company-storybook",
projectId: "project-board-ui",
name: "Docs preview sandbox",
sourceType: "remote_managed",
cwd: null,
repoUrl: "https://github.com/paperclipai/paperclip",
repoRef: "preview/docs-workspaces",
defaultRef: "master",
visibility: "advanced",
setupCommand: "pnpm install",
cleanupCommand: "pnpm dev:stop",
remoteProvider: "vercel_sandbox",
remoteWorkspaceRef: "vcsb_storybook_docs_0420",
sharedWorkspaceKey: "docs-preview",
metadata: { region: "iad1", ttlHours: 8 },
runtimeConfig: {
workspaceRuntime: {
commands: [
{
id: "docs",
name: "Docs preview",
kind: "service",
command: "pnpm docs:dev",
cwd: ".",
lifecycle: "ephemeral",
},
],
},
desiredState: "stopped",
serviceStates: { docs: "stopped" },
},
isPrimary: false,
runtimeServices: [
createRuntimeService({
id: "service-docs-preview",
projectWorkspaceId: "workspace-docs-remote",
executionWorkspaceId: null,
issueId: "issue-storybook-6",
scopeType: "project_workspace",
scopeId: "workspace-docs-remote",
serviceName: "docs",
status: "stopped",
lifecycle: "ephemeral",
reuseKey: "docs-preview",
command: "pnpm docs:dev",
cwd: null,
port: 4173,
url: "https://paperclip-docs-preview.vercel.app",
healthStatus: "unknown",
lastUsedAt: recent(48),
startedAt: recent(72),
stoppedAt: recent(46),
createdAt: recent(72),
updatedAt: recent(46),
}),
],
createdAt: recent(2_000),
updatedAt: recent(46),
},
{
id: "workspace-release-local",
companyId: "company-storybook",
projectId: "project-board-ui",
name: "Release smoke local checkout",
sourceType: "local_path",
cwd: `${storybookWorkspaceRoot}/release-smoke`,
repoUrl: "https://github.com/paperclipai/paperclip",
repoRef: "release/smoke-2026-04-20",
defaultRef: "master",
visibility: "advanced",
setupCommand: "pnpm install",
cleanupCommand: "pnpm dev:stop",
remoteProvider: null,
remoteWorkspaceRef: null,
sharedWorkspaceKey: "release-smoke",
metadata: null,
runtimeConfig: {
workspaceRuntime: {
commands: [
{
id: "release-smoke",
name: "Release smoke",
kind: "job",
command: "pnpm test:release-smoke",
cwd: ".",
},
],
},
desiredState: "stopped",
serviceStates: null,
},
isPrimary: false,
runtimeServices: [],
createdAt: recent(7_200),
updatedAt: recent(90),
},
];
export const storybookExecutionWorkspaces: ExecutionWorkspace[] = [
{
id: "execution-workspace-storybook",
companyId: "company-storybook",
projectId: "project-board-ui",
projectWorkspaceId: "workspace-board-ui",
sourceIssueId: "issue-storybook-1",
mode: "isolated_workspace",
strategyType: "git_worktree",
name: "PAP-1641 storybook worktree",
status: "active",
deliveryState: "unknown",
cwd: `${storybookWorktreeRoot}/PAP-1641-create-super-detailed-storybooks-for-our-project`,
repoUrl: "https://github.com/paperclipai/paperclip",
baseRef: "master",
branchName: "PAP-1641-create-super-detailed-storybooks-for-our-project",
providerType: "git_worktree",
providerRef: null,
derivedFromExecutionWorkspaceId: null,
lastUsedAt: recent(3),
openedAt: recent(90),
closedAt: null,
cleanupEligibleAt: null,
cleanupReason: null,
config: null,
metadata: null,
runtimeServices: storybookProjectWorkspaces[0]!.runtimeServices,
createdAt: recent(90),
updatedAt: recent(3),
},
{
id: "execution-workspace-cleanup",
companyId: "company-storybook",
projectId: "project-board-ui",
projectWorkspaceId: "workspace-release-local",
sourceIssueId: "issue-storybook-8",
mode: "isolated_workspace",
strategyType: "git_worktree",
name: "PAP-1608 release smoke cleanup",
status: "cleanup_failed",
deliveryState: "unknown",
cwd: `${storybookWorktreeRoot}/PAP-1608-release-smoke-cleanup`,
repoUrl: "https://github.com/paperclipai/paperclip",
baseRef: "master",
branchName: "PAP-1608-release-smoke-cleanup",
providerType: "git_worktree",
providerRef: null,
derivedFromExecutionWorkspaceId: null,
lastUsedAt: recent(120),
openedAt: recent(260),
closedAt: recent(118),
cleanupEligibleAt: recent(115),
cleanupReason: "release smoke finished but the local worktree still has generated artifacts",
config: {
provisionCommand: null,
teardownCommand: "pnpm dev:stop && rm -rf ui/storybook-static",
cleanupCommand: null,
workspaceRuntime: storybookWorkspaceRuntime,
desiredState: "stopped",
serviceStates: { storybook: "stopped" },
},
metadata: { dirtyEntryCount: 3 },
runtimeServices: [
createRuntimeService({
id: "service-cleanup-storybook",
projectWorkspaceId: "workspace-release-local",
executionWorkspaceId: "execution-workspace-cleanup",
issueId: "issue-storybook-8",
scopeId: "execution-workspace-cleanup",
status: "failed",
healthStatus: "unhealthy",
url: "http://localhost:6007",
port: 6007,
lastUsedAt: recent(130),
startedAt: recent(210),
stoppedAt: recent(125),
updatedAt: recent(120),
}),
],
createdAt: recent(260),
updatedAt: recent(120),
},
];
function createProject(overrides: Partial<Project> = {}): Project {
const id = overrides.id ?? "project-board-ui";
return {
id,
companyId: "company-storybook",
urlKey: "board-ui",
goalId: "goal-company",
goalIds: ["goal-company", "goal-board-ux"],
goals: storybookGoals
.filter((goal) => goal.id === "goal-company" || goal.id === "goal-board-ux")
.map(({ id, title }) => ({ id, title })),
name: "Board UI",
description: "Navigation, command, and operator layout polish.",
status: "in_progress",
leadAgentId: "agent-codex",
targetDate: "2026-04-30",
color: "#0f766e",
icon: null,
env: null,
pauseReason: null,
pausedAt: null,
executionWorkspacePolicy: {
enabled: true,
defaultMode: "isolated_workspace",
allowIssueOverride: true,
defaultProjectWorkspaceId: "workspace-board-ui",
workspaceStrategy: {
type: "git_worktree",
baseRef: "master",
branchTemplate: "{issueIdentifier}-{slug}",
worktreeParentDir: storybookWorktreeRoot,
provisionCommand: null,
runtimeProvisionCommand: "bash ./scripts/provision-worktree-runtime.sh",
teardownCommand: null,
},
workspaceRuntime: null,
branchPolicy: null,
pullRequestPolicy: null,
runtimePolicy: null,
cleanupPolicy: null,
},
codebase: {
workspaceId: "workspace-board-ui",
repoUrl: "https://github.com/paperclipai/paperclip",
repoRef: "master",
defaultRef: "master",
repoName: "paperclip",
localFolder: storybookRepoRoot,
managedFolder: ".paperclip/worktrees/storybook",
effectiveLocalFolder: storybookRepoRoot,
origin: "local_folder",
},
workspaces: storybookProjectWorkspaces,
primaryWorkspace: storybookProjectWorkspaces[0]!,
archivedAt: null,
createdAt: recent(18_000),
updatedAt: recent(12),
...overrides,
};
}
export const storybookProjects: Project[] = [
createProject(),
createProject({
id: "project-runtime",
urlKey: "agent-runtime",
name: "Agent Runtime",
description: "Heartbeat, adapters, and execution trace work.",
status: "planned",
leadAgentId: "agent-cto",
color: "#2563eb",
updatedAt: recent(60),
}),
createProject({
id: "project-budget",
urlKey: "budget-guardrails",
name: "Budget Guardrails",
description: "Hard-stop and approval flow review surfaces.",
status: "in_progress",
leadAgentId: "agent-qa",
color: "#f59e0b",
pauseReason: "budget",
pausedAt: recent(90),
updatedAt: recent(90),
}),
createProject({
id: "project-archived-import",
urlKey: "archived-import-wizard",
name: "Archived Import Wizard",
description: "Preserved for audit after the import workflow moved into company packages.",
status: "cancelled",
leadAgentId: null,
goalId: "goal-archived-import",
goalIds: ["goal-archived-import"],
goals: [{ id: "goal-archived-import", title: "Retire old import wizard" }],
color: "#64748b",
workspaces: [],
primaryWorkspace: null,
codebase: {
workspaceId: null,
repoUrl: null,
repoRef: null,
defaultRef: null,
repoName: null,
localFolder: null,
managedFolder: "",
effectiveLocalFolder: "Not configured",
origin: "local_folder",
},
executionWorkspacePolicy: {
enabled: false,
defaultMode: "shared_workspace",
allowIssueOverride: false,
defaultProjectWorkspaceId: null,
workspaceStrategy: null,
workspaceRuntime: null,
branchPolicy: null,
pullRequestPolicy: null,
runtimePolicy: null,
cleanupPolicy: null,
},
archivedAt: recent(2_400),
updatedAt: recent(2_400),
}),
];
export function createIssue(overrides: Partial<Issue> = {}): Issue {
return {
id: "issue-storybook-1",
companyId: "company-storybook",
projectId: "project-board-ui",
projectWorkspaceId: "workspace-board-ui",
goalId: "goal-company",
parentId: null,
title: "Create super-detailed storybooks for the project",
description: "Set up Storybook and move UX review surfaces into stories.",
status: "in_progress",
priority: "high",
reviewPolicy: null,
assigneeAgentId: "agent-codex",
assigneeUserId: null,
responsibleUserId: null,
checkoutRunId: "run-storybook",
executionRunId: "run-storybook",
executionAgentNameKey: "codexcoder",
executionLockedAt: recent(28),
createdByAgentId: null,
createdByUserId: "user-board",
issueNumber: 1641,
identifier: "PAP-1641",
requestDepth: 0,
billingCode: "product",
assigneeAdapterOverrides: null,
executionWorkspaceId: "execution-workspace-storybook",
executionWorkspacePreference: "project",
executionWorkspaceSettings: null,
startedAt: recent(28),
completedAt: null,
cancelledAt: null,
hiddenAt: null,
labelIds: ["label-ui", "label-docs"],
labels: labelsFor(["label-ui", "label-docs"]),
blockedBy: [],
blocks: [],
planDocument: null,
documentSummaries: [],
legacyPlanDocument: null,
project: storybookProjects[0]!,
goal: null,
currentExecutionWorkspace: storybookExecutionWorkspaces[0]!,
workProducts: [],
mentionedProjects: [],
myLastTouchAt: recent(8),
lastExternalCommentAt: recent(70),
lastActivityAt: recent(3),
isUnreadForMe: true,
createdAt: recent(90),
updatedAt: recent(3),
...overrides,
workMode: overrides.workMode ?? "standard",
};
}
export const storybookIssues: Issue[] = [
createIssue(),
createIssue({
id: "issue-storybook-2",
title: "Add budget hard-stop incident review",
description: "Trace why a hard stop paused the agent and add a board-facing incident summary.",
status: "blocked",
priority: "critical",
assigneeAgentId: "agent-qa",
checkoutRunId: null,
executionRunId: null,
executionLockedAt: null,
startedAt: null,
identifier: "PAP-1528",
issueNumber: 1528,
billingCode: "reliability",
projectId: "project-budget",
projectWorkspaceId: null,
labelIds: ["label-risk", "label-backend"],
labels: labelsFor(["label-risk", "label-backend"]),
blockedBy: [
{
id: "issue-storybook-7",
identifier: "PAP-1591",
title: "Confirm project budget override policy",
status: "in_review",
priority: "high",
assigneeAgentId: null,
assigneeUserId: "user-board",
},
],
lastActivityAt: recent(18),
}),
createIssue({
id: "issue-storybook-3",
title: "QA invite flow on authenticated private mode",
status: "in_review",
priority: "medium",
assigneeAgentId: null,
assigneeUserId: "user-board",
checkoutRunId: null,
executionRunId: null,
executionLockedAt: null,
identifier: "PAP-1602",
issueNumber: 1602,
completedAt: null,
lastActivityAt: recent(49),
isUnreadForMe: false,
}),
createIssue({
id: "issue-storybook-4",
parentId: "issue-storybook-1",
title: "Extract issue row density fixtures",
description: "Create fixture-backed rows for unread, selected, nested, and grouped issue management views.",
status: "todo",
priority: "medium",
assigneeAgentId: "agent-codex",
checkoutRunId: null,
executionRunId: null,
executionLockedAt: null,
startedAt: null,
identifier: "PAP-1668",
issueNumber: 1668,
labelIds: ["label-ui"],
labels: labelsFor(["label-ui"]),
lastActivityAt: recent(31),
isUnreadForMe: true,
}),
createIssue({
id: "issue-storybook-5",
parentId: "issue-storybook-1",
title: "Review document editor empty states",
description: "Validate plan and notes documents in issue detail before handing the Storybook preview to QA.",
status: "done",
priority: "low",
assigneeAgentId: "agent-qa",
checkoutRunId: null,
executionRunId: "run-storybook-qa",
executionLockedAt: null,
completedAt: recent(22),
identifier: "PAP-1669",
issueNumber: 1669,
labelIds: ["label-docs"],
labels: labelsFor(["label-docs"]),
lastActivityAt: recent(22),
isUnreadForMe: false,
}),
createIssue({
id: "issue-storybook-6",
title: "Publish static Storybook preview",
description: "Build the static preview and attach the generated artifact to the parent issue.",
status: "todo",
priority: "high",
assigneeAgentId: null,
assigneeUserId: null,
checkoutRunId: null,
executionRunId: null,
executionLockedAt: null,
startedAt: null,
identifier: "PAP-1670",
issueNumber: 1670,
labelIds: ["label-ui", "label-risk"],
labels: labelsFor(["label-ui", "label-risk"]),
lastActivityAt: recent(64),
isUnreadForMe: false,
}),
createIssue({
id: "issue-storybook-7",
title: "Confirm project budget override policy",
description: "Board review needed before increasing the project budget for long-running browser verification.",
status: "in_review",
priority: "high",
assigneeAgentId: null,
assigneeUserId: "user-board",
checkoutRunId: null,
executionRunId: null,
executionLockedAt: null,
startedAt: null,
identifier: "PAP-1591",
issueNumber: 1591,
billingCode: "governance",
projectId: "project-budget",
projectWorkspaceId: null,
labelIds: ["label-risk"],
labels: labelsFor(["label-risk"]),
lastActivityAt: recent(85),
isUnreadForMe: false,
}),
createIssue({
id: "issue-storybook-8",
title: "Clean up release smoke worktree",
description: "Close the isolated release smoke workspace after static preview review.",
status: "blocked",
priority: "medium",
assigneeAgentId: "agent-codex",
checkoutRunId: null,
executionRunId: "run-release-smoke-cleanup",
executionLockedAt: null,
startedAt: recent(260),
identifier: "PAP-1608",
issueNumber: 1608,
projectId: "project-board-ui",
projectWorkspaceId: "workspace-release-local",
executionWorkspaceId: "execution-workspace-cleanup",
labelIds: ["label-ui", "label-risk"],
labels: labelsFor(["label-ui", "label-risk"]),
lastActivityAt: recent(120),
isUnreadForMe: false,
}),
];
export const storybookIssueDocuments: IssueDocument[] = [
{
id: "document-plan-storybook",
companyId: "company-storybook",
issueId: "issue-storybook-1",
key: "plan",
title: "Plan",
format: "markdown",
body: [
"# Plan",
"",
"- Add issue-management stories for list, filters, detail, documents, runs, and workspace cards.",
"- Use existing product components instead of mock-only approximations.",
"- Verify the Storybook build after the fixture expansion.",
].join("\n"),
latestRevisionId: "revision-plan-3",
latestRevisionNumber: 3,
createdByAgentId: "agent-codex",
createdByUserId: null,
updatedByAgentId: "agent-codex",
updatedByUserId: null,
lockedAt: null,
lockedByAgentId: null,
lockedByUserId: null,
createdAt: recent(80),
updatedAt: recent(8),
},
{
id: "document-notes-storybook",
companyId: "company-storybook",
issueId: "issue-storybook-1",
key: "notes",
title: "Review Notes",
format: "markdown",
body: [
"# Review Notes",
"",
"- The issue list needs group headers and dense trailing columns.",
"- The filters popover should show selected status, priority, and assignee filters.",
"- Workspace copy must expose branch, path, and runtime status.",
].join("\n"),
latestRevisionId: "revision-notes-1",
latestRevisionNumber: 1,
createdByAgentId: null,
createdByUserId: "user-board",
updatedByAgentId: null,
updatedByUserId: "user-board",
lockedAt: null,
lockedByAgentId: null,
lockedByUserId: null,
createdAt: recent(55),
updatedAt: recent(12),
},
];
export const storybookContinuationHandoff: IssueDocument = {
id: "document-continuation-storybook",
companyId: "company-storybook",
issueId: "issue-storybook-1",
key: "continuation_summary",
title: "Continuation handoff",
format: "markdown",
body: [
"Current state: issue-management stories have the fixture surface mapped.",
"",
"Next action: run the Storybook build, inspect the issue management story, then request QA visual review if the build passes.",
"",
"Important files: `ui/storybook/stories/issue-management.stories.tsx` and `ui/storybook/fixtures/paperclipData.ts`.",
].join("\n"),
latestRevisionId: "revision-continuation-1",
latestRevisionNumber: 1,
createdByAgentId: "agent-codex",
createdByUserId: null,
updatedByAgentId: "agent-codex",
updatedByUserId: null,
lockedAt: null,
lockedByAgentId: null,
lockedByUserId: null,
createdAt: recent(18),
updatedAt: recent(5),
};
export const storybookIssueRuns: RunForIssue[] = [
{
runId: "run-storybook",
status: "running",
agentId: "agent-codex",
adapterType: "codex_local",
startedAt: recent(28).toISOString(),
finishedAt: null,
createdAt: recent(28).toISOString(),
invocationSource: "manual",
usageJson: { costCents: 142, inputTokens: 38400, outputTokens: 7200 },
resultJson: null,
logBytes: 62_400,
livenessState: null,
livenessReason: null,
continuationAttempt: 1,
lastUsefulActionAt: recent(3).toISOString(),
nextAction: "Finish the issue-management Storybook and verify the static build.",
},
{
runId: "run-storybook-qa",
status: "succeeded",
agentId: "agent-qa",
adapterType: "claude_local",
startedAt: recent(110).toISOString(),
finishedAt: recent(94).toISOString(),
createdAt: recent(110).toISOString(),
invocationSource: "manual",
usageJson: { costCents: 38, inputTokens: 12200, outputTokens: 1900 },
resultJson: { stopReason: "completed" },
logBytes: 18_400,
livenessState: "advanced",
livenessReason: "Verified the document editor empty state and left concrete screenshot notes.",
continuationAttempt: 0,
lastUsefulActionAt: recent(96).toISOString(),
nextAction: "Re-run after final story fixture additions land.",
},
{
runId: "run-storybook-plan",
status: "succeeded",
agentId: "agent-codex",
adapterType: "codex_local",
startedAt: recent(210).toISOString(),
finishedAt: recent(196).toISOString(),
createdAt: recent(210).toISOString(),
invocationSource: "scheduler",
usageJson: { costCents: 24, inputTokens: 9100, outputTokens: 1100 },
resultJson: { stopReason: "completed" },
logBytes: 9800,
livenessState: "plan_only",
livenessReason: "Identified Storybook coverage gaps but did not edit files in that run.",
continuationAttempt: 0,
lastUsefulActionAt: null,
nextAction: "Implement issue-management stories in the next heartbeat.",
},
];
export const storybookApprovals: Approval[] = [
{
id: "approval-hire-designer",
companyId: "company-storybook",
type: "hire_agent",
requestedByAgentId: "agent-cto",
requestedByUserId: null,
status: "pending",
payload: {
name: "DesignSystemCoder",
role: "designer",
title: "Design System Engineer",
icon: "sparkles",
adapterType: "codex_local",
capabilities: "Owns component quality, visual regression stories, and design-system consolidation.",
desiredSkills: ["frontend-design", "web-design-guidelines"],
},
decisionNote: null,
decidedByUserId: null,
decidedAt: null,
createdAt: recent(42),
updatedAt: recent(42),
},
{
id: "approval-budget",
companyId: "company-storybook",
type: "budget_override_required",
requestedByAgentId: "agent-codex",
requestedByUserId: null,
status: "revision_requested",
payload: {
scopeName: "Paperclip App",
scopeType: "project",
windowKind: "calendar_month_utc",
metric: "billed_cents",
budgetAmount: 120_000,
observedAmount: 131_400,
guidance: "Raise the project budget only after current release smoke checks are green.",
},
decisionNote: "Need a tighter verification list before approving more spend.",
decidedByUserId: "user-board",
decidedAt: recent(12),
createdAt: recent(75),
updatedAt: recent(12),
},
{
id: "approval-board",
companyId: "company-storybook",
type: "request_board_approval",
requestedByAgentId: "agent-cto",
requestedByUserId: null,
status: "approved",
payload: {
title: "Publish the Storybook preview for design review",
summary: "Build the static Storybook and attach the generated URL to the release issue.",
recommendedAction: "Approve publishing the preview for internal board review.",
nextActionOnApproval: "Run build-storybook, upload the static artifact, and request QA visual review.",
risks: [
"Stories may expose stale fixture copy if not maintained with UI changes.",
"Preview screenshots can hide responsive regressions unless mobile viewports are checked.",
],
},
decisionNote: "Approved for internal-only review.",
decidedByUserId: "user-board",
decidedAt: recent(4),
createdAt: recent(55),
updatedAt: recent(4),
},
];
export const storybookBudgetSummaries: BudgetPolicySummary[] = [
{
policyId: "budget-company-ok",
companyId: "company-storybook",
scopeType: "company",
scopeId: "company-storybook",
scopeName: "Paperclip Storybook",
metric: "billed_cents",
windowKind: "calendar_month_utc",
amount: 250_000,
observedAmount: 67_500,
remainingAmount: 182_500,
utilizationPercent: 27,
warnPercent: 80,
hardStopEnabled: true,
notifyEnabled: true,
isActive: true,
status: "ok",
paused: false,
pauseReason: null,
windowStart: new Date("2026-04-01T00:00:00.000Z"),
windowEnd: new Date("2026-05-01T00:00:00.000Z"),
},
{
policyId: "budget-project-warning",
companyId: "company-storybook",
scopeType: "project",
scopeId: "project-board-ui",
scopeName: "Paperclip App",
metric: "billed_cents",
windowKind: "calendar_month_utc",
amount: 120_000,
observedAmount: 103_100,
remainingAmount: 16_900,
utilizationPercent: 86,
warnPercent: 80,
hardStopEnabled: true,
notifyEnabled: true,
isActive: true,
status: "warning",
paused: false,
pauseReason: null,
windowStart: new Date("2026-04-01T00:00:00.000Z"),
windowEnd: new Date("2026-05-01T00:00:00.000Z"),
},
{
policyId: "budget-agent-hard-stop",
companyId: "company-storybook",
scopeType: "agent",
scopeId: "agent-codex",
scopeName: "CodexCoder",
metric: "billed_cents",
windowKind: "calendar_month_utc",
amount: 40_000,
observedAmount: 43_200,
remainingAmount: 0,
utilizationPercent: 108,
warnPercent: 80,
hardStopEnabled: true,
notifyEnabled: true,
isActive: true,
status: "hard_stop",
paused: true,
pauseReason: "budget",
windowStart: new Date("2026-04-01T00:00:00.000Z"),
windowEnd: new Date("2026-05-01T00:00:00.000Z"),
},
];
export const storybookActivityEvents: ActivityEvent[] = [
{
id: "activity-1",
companyId: "company-storybook",
actorType: "agent",
actorId: "agent-codex",
action: "issue.status_changed",
entityType: "issue",
entityId: "issue-storybook-1",
agentId: "agent-codex",
runId: "run-storybook",
details: { from: "todo", to: "in_progress" },
createdAt: recent(28),
},
{
id: "activity-2",
companyId: "company-storybook",
actorType: "user",
actorId: "user-board",
action: "approval.revision_requested",
entityType: "approval",
entityId: "approval-budget",
agentId: null,
runId: null,
details: { type: "budget_override_required" },
createdAt: recent(12),
},
{
id: "activity-3",
companyId: "company-storybook",
actorType: "system",
actorId: "system",
action: "budget.hard_stop",
entityType: "agent",
entityId: "agent-codex",
agentId: "agent-codex",
runId: null,
details: { observedAmount: 43_200, budgetAmount: 40_000 },
createdAt: recent(7),
},
];
export const storybookEntityNameMap = new Map<string, string>([
["issue:issue-storybook-1", "PAP-1641"],
["issue:issue-storybook-2", "PAP-1528"],
["issue:issue-storybook-3", "PAP-1602"],
["issue:issue-storybook-4", "PAP-1668"],
["issue:issue-storybook-5", "PAP-1669"],
["issue:issue-storybook-6", "PAP-1670"],
["issue:issue-storybook-7", "PAP-1591"],
["approval:approval-budget", "Budget override"],
["agent:agent-codex", "CodexCoder"],
["agent:agent-qa", "QAChecker"],
["agent:agent-cto", "CTO"],
]);
export const storybookEntityTitleMap = new Map<string, string>([
["issue:issue-storybook-1", "Create super-detailed storybooks for the project"],
["issue:issue-storybook-2", "Add budget hard-stop incident review"],
["issue:issue-storybook-3", "QA invite flow on authenticated private mode"],
["issue:issue-storybook-4", "Extract issue row density fixtures"],
["issue:issue-storybook-5", "Review document editor empty states"],
["issue:issue-storybook-6", "Publish static Storybook preview"],
["issue:issue-storybook-7", "Confirm project budget override policy"],
]);
export const storybookSidebarBadges: SidebarBadges = {
inbox: 7,
approvals: 2,
failedRuns: 1,
joinRequests: 1,
};
export const storybookDashboardSummary: DashboardSummary = {
companyId: "company-storybook",
agents: {
active: 3,
running: 1,
paused: 0,
error: 0,
},
tasks: {
open: 12,
inProgress: 4,
blocked: 1,
done: 36,
},
costs: {
monthSpendCents: 67_500,
monthBudgetCents: 250_000,
monthUtilizationPercent: 27,
},
pendingApprovals: 2,
budgets: {
activeIncidents: 1,
pendingApprovals: 1,
pausedAgents: 0,
pausedProjects: 1,
},
runActivity: [
{ date: "2026-04-07", succeeded: 4, failed: 0, recovered: 0, other: 1, total: 5, failedByErrorCode: {} },
{ date: "2026-04-08", succeeded: 5, failed: 1, recovered: 0, other: 0, total: 6, failedByErrorCode: { provider_quota: 1 } },
{ date: "2026-04-09", succeeded: 3, failed: 0, recovered: 0, other: 1, total: 4, failedByErrorCode: {} },
{ date: "2026-04-10", succeeded: 6, failed: 0, recovered: 0, other: 0, total: 6, failedByErrorCode: {} },
{ date: "2026-04-11", succeeded: 4, failed: 1, recovered: 0, other: 0, total: 5, failedByErrorCode: { workspace_validation_failed: 1 } },
{ date: "2026-04-12", succeeded: 2, failed: 0, recovered: 0, other: 1, total: 3, failedByErrorCode: {} },
{ date: "2026-04-13", succeeded: 5, failed: 0, recovered: 0, other: 1, total: 6, failedByErrorCode: {} },
{ date: "2026-04-14", succeeded: 6, failed: 1, recovered: 0, other: 0, total: 7, failedByErrorCode: { provider_quota: 1 } },
{ date: "2026-04-15", succeeded: 4, failed: 0, recovered: 0, other: 1, total: 5, failedByErrorCode: {} },
{ date: "2026-04-16", succeeded: 7, failed: 0, recovered: 0, other: 0, total: 7, failedByErrorCode: {} },
{ date: "2026-04-17", succeeded: 6, failed: 1, recovered: 0, other: 0, total: 7, failedByErrorCode: { workspace_validation_failed: 1 } },
{ date: "2026-04-18", succeeded: 3, failed: 0, recovered: 0, other: 1, total: 4, failedByErrorCode: {} },
// Restart-burst spike: many process_lost kills, most recovered on retry.
{ date: "2026-04-19", succeeded: 5, failed: 2, recovered: 6, other: 1, total: 14, failedByErrorCode: { process_lost: 2 } },
{ date: "2026-04-20", succeeded: 4, failed: 1, recovered: 3, other: 2, total: 10, failedByErrorCode: { process_lost: 1 } },
],
};
export const storybookLiveRuns: LiveRunForIssue[] = [
{
id: "run-storybook",
status: "running",
invocationSource: "manual",
triggerDetail: "issue_assigned",
startedAt: recent(28).toISOString(),
finishedAt: null,
createdAt: recent(28).toISOString(),
agentId: "agent-codex",
agentName: "CodexCoder",
adapterType: "codex_local",
issueId: "issue-storybook-1",
livenessState: "advanced",
livenessReason: null,
continuationAttempt: 0,
lastUsefulActionAt: recent(3).toISOString(),
nextAction: "Build fixture-backed navigation stories",
},
];
export const storybookSecretProviders: SecretProviderDescriptor[] = [
{ id: "local_encrypted", label: "Local encrypted", requiresExternalRef: false },
{ id: "aws_secrets_manager", label: "AWS Secrets Manager", requiresExternalRef: false },
{ id: "gcp_secret_manager", label: "GCP Secret Manager", requiresExternalRef: false },
{ id: "vault", label: "HashiCorp Vault", requiresExternalRef: false },
];
export const storybookSecretProviderConfigs: CompanySecretProviderConfig[] = [
{
id: "provider-config-local",
companyId: "company-storybook",
provider: "local_encrypted",
displayName: "Local encrypted default",
status: "ready",
isDefault: true,
config: { backupReminderAcknowledged: true },
healthStatus: "ready",
healthCheckedAt: recent(45),
healthMessage: "Local encrypted provider is healthy.",
healthDetails: null,
disabledAt: null,
createdByAgentId: null,
createdByUserId: "user-board",
createdAt: recent(2_400),
updatedAt: recent(45),
},
{
id: "provider-config-aws-prod",
companyId: "company-storybook",
provider: "aws_secrets_manager",
displayName: "AWS production",
status: "warning",
isDefault: false,
config: {
region: "us-east-1",
namespace: "prod-use1",
secretNamePrefix: "paperclip",
kmsKeyId: "alias/paperclip-secrets",
ownerTag: "platform",
environmentTag: "production",
},
healthStatus: "warning",
healthCheckedAt: recent(18),
healthMessage: "Connected; KMS key rotation policy not yet enforced.",
healthDetails: {
code: "kms_rotation_policy",
message: "Connected; KMS key rotation policy not yet enforced.",
guidance: ["Enable automatic key rotation before using this vault for production agents."],
},
disabledAt: null,
createdByAgentId: null,
createdByUserId: "user-board",
createdAt: recent(1_800),
updatedAt: recent(18),
},
{
id: "provider-config-aws-blocked",
companyId: "company-storybook",
provider: "aws_secrets_manager",
displayName: "AWS staging blocked",
status: "ready",
isDefault: false,
config: {
region: "us-west-2",
namespace: "staging",
secretNamePrefix: "paperclip",
kmsKeyId: "",
ownerTag: "platform",
environmentTag: "staging",
},
healthStatus: "error",
healthCheckedAt: recent(22),
healthMessage: "AWS Secrets Manager denied ListSecrets for this vault.",
healthDetails: {
code: "access_denied",
message: "AWS Secrets Manager denied ListSecrets for this vault.",
guidance: ["Grant secretsmanager:ListSecrets before importing from this vault."],
},
disabledAt: null,
createdByAgentId: null,
createdByUserId: "user-board",
createdAt: recent(1_200),
updatedAt: recent(22),
},
];
export const storybookSecretProviderDiscoveryPreview: SecretProviderConfigDiscoveryPreviewResult = {
provider: "aws_secrets_manager",
nextToken: null,
sampledSecretCount: 6,
skippedForeignPaperclipSampleCount: 1,
warnings: ["Skipped 1 Paperclip-managed AWS secret from a different deployment namespace."],
candidates: [
{
provider: "aws_secrets_manager",
displayName: "AWS production",
config: {
region: "us-east-1",
namespace: "prod-use1",
secretNamePrefix: "paperclip",
kmsKeyId: "alias/paperclip-secrets",
ownerTag: "platform",
environmentTag: "production",
},
sampleCount: 5,
samples: [
{
name: "paperclip/prod-use1/company-storybook/openai_api_key",
hasKmsKey: true,
tagKeys: ["paperclip:managed-by", "paperclip:environment", "paperclip:provider-owner"],
},
],
signals: {
namespace: "prod-use1",
secretNamePrefix: "paperclip",
environmentTag: "production",
ownerTag: "platform",
kmsKeyId: "alias/paperclip-secrets",
hasKmsKey: true,
sampleCount: 5,
paperclipManagedSampleCount: 5,
skippedForeignPaperclipSampleCount: 1,
},
warnings: [],
},
],
};
export const storybookSecrets: CompanySecret[] = [
{
id: "secret-openai",
companyId: "company-storybook",
scope: "company",
ownerUserId: null,
userSecretDefinitionId: null,
key: "openai_api_key",
name: "OPENAI_API_KEY",
provider: "local_encrypted",
status: "active",
managedMode: "paperclip_managed",
externalRef: null,
providerConfigId: null,
providerMetadata: null,
latestVersion: 3,
description: "OpenAI API key shared by all model adapters.",
lastResolvedAt: recent(12),
lastRotatedAt: new Date("2026-04-15T09:30:00.000Z"),
deletedAt: null,
createdByAgentId: null,
createdByUserId: "user-board",
createdAt: new Date("2026-03-01T10:00:00.000Z"),
updatedAt: recent(12),
},
{
id: "secret-aws-prod",
companyId: "company-storybook",
scope: "company",
ownerUserId: null,
userSecretDefinitionId: null,
key: "prod_aws_deploy",
name: "PROD_AWS_DEPLOY_KEY",
provider: "aws_secrets_manager",
status: "active",
managedMode: "external_reference",
externalRef: "arn:aws:secretsmanager:us-east-1:123456789012:secret:paperclip/prod/aws-deploy-AbCdEf",
providerConfigId: null,
providerMetadata: null,
latestVersion: 2,
description: "Deploy key for the prod ECS rollout pipeline.",
lastResolvedAt: recent(180),
lastRotatedAt: new Date("2026-04-22T14:00:00.000Z"),
deletedAt: null,
createdByAgentId: null,
createdByUserId: "user-board",
createdAt: new Date("2026-02-01T10:00:00.000Z"),
updatedAt: recent(180),
},
{
id: "secret-github",
companyId: "company-storybook",
scope: "company",
ownerUserId: null,
userSecretDefinitionId: null,
key: "github_app_pem",
name: "GITHUB_APP_PEM",
provider: "local_encrypted",
status: "disabled",
managedMode: "paperclip_managed",
externalRef: null,
providerConfigId: null,
providerMetadata: null,
latestVersion: 1,
description: "Disabled until GitHub App reinstall is approved.",
lastResolvedAt: new Date("2026-03-30T08:11:00.000Z"),
lastRotatedAt: null,
deletedAt: null,
createdByAgentId: null,
createdByUserId: "user-board",
createdAt: new Date("2026-03-15T10:00:00.000Z"),
updatedAt: new Date("2026-04-21T11:30:00.000Z"),
},
{
id: "secret-stripe-archived",
companyId: "company-storybook",
scope: "company",
ownerUserId: null,
userSecretDefinitionId: null,
key: "stripe_legacy",
name: "STRIPE_LEGACY",
provider: "vault",
status: "archived",
managedMode: "external_reference",
externalRef: "secret/data/payments/stripe-legacy",
providerConfigId: null,
providerMetadata: null,
latestVersion: 4,
description: "Migrated to managed billing service. Kept for backfill jobs.",
lastResolvedAt: new Date("2026-02-25T08:11:00.000Z"),
lastRotatedAt: new Date("2026-02-20T08:11:00.000Z"),
deletedAt: null,
createdByAgentId: null,
createdByUserId: "user-board",
createdAt: new Date("2025-12-01T10:00:00.000Z"),
updatedAt: new Date("2026-03-01T11:30:00.000Z"),
},
];
export const storybookSecretBindings: CompanySecretBinding[] = [
{
id: "binding-openai-agent",
companyId: "company-storybook",
secretId: "secret-openai",
targetType: "agent",
targetId: "agent-codex",
configPath: "env.OPENAI_API_KEY",
versionSelector: "latest",
required: true,
label: "Codex agent env",
projectionClass: "unclassified",
projectionAllowlistKey: null,
createdAt: new Date("2026-03-02T09:00:00.000Z"),
updatedAt: new Date("2026-03-02T09:00:00.000Z"),
},
{
id: "binding-openai-project",
companyId: "company-storybook",
secretId: "secret-openai",
targetType: "project",
targetId: "project-app",
configPath: "env.OPENAI_API_KEY",
versionSelector: "latest",
required: true,
label: "Paperclip App project env",
projectionClass: "unclassified",
projectionAllowlistKey: null,
createdAt: new Date("2026-03-02T09:00:00.000Z"),
updatedAt: new Date("2026-03-02T09:00:00.000Z"),
},
{
id: "binding-aws-environment",
companyId: "company-storybook",
secretId: "secret-aws-prod",
targetType: "environment",
targetId: "env-prod",
configPath: "secrets.AWS_DEPLOY",
versionSelector: 2,
required: true,
label: "Prod environment",
projectionClass: "unclassified",
projectionAllowlistKey: null,
createdAt: new Date("2026-04-22T14:01:00.000Z"),
updatedAt: new Date("2026-04-22T14:01:00.000Z"),
},
];
export const storybookSecretAccessEvents: SecretAccessEvent[] = [
{
id: "evt-1",
companyId: "company-storybook",
secretId: "secret-openai",
userSecretDefinitionId: null,
secretScope: "company",
responsibleUserId: null,
credentialOwnerUserId: null,
credentialSubjectType: null,
credentialSubjectId: null,
version: 3,
provider: "local_encrypted",
actorType: "agent",
actorId: "agent-codex",
consumerType: "agent",
consumerId: "agent-codex",
configPath: "env.OPENAI_API_KEY",
issueId: "issue-storybook-1",
heartbeatRunId: "run-storybook",
pluginId: null,
outcome: "success",
errorCode: null,
createdAt: recent(12),
},
{
id: "evt-2",
companyId: "company-storybook",
secretId: "secret-openai",
userSecretDefinitionId: null,
secretScope: "company",
responsibleUserId: null,
credentialOwnerUserId: null,
credentialSubjectType: null,
credentialSubjectId: null,
version: 3,
provider: "local_encrypted",
actorType: "system",
actorId: null,
consumerType: "project",
consumerId: "project-app",
configPath: "env.OPENAI_API_KEY",
issueId: null,
heartbeatRunId: null,
pluginId: null,
outcome: "success",
errorCode: null,
createdAt: recent(48),
},
{
id: "evt-3",
companyId: "company-storybook",
secretId: "secret-openai",
userSecretDefinitionId: null,
secretScope: "company",
responsibleUserId: null,
credentialOwnerUserId: null,
credentialSubjectType: null,
credentialSubjectId: null,
version: null,
provider: "local_encrypted",
actorType: "agent",
actorId: "agent-codex",
consumerType: "agent",
consumerId: "agent-codex",
configPath: "env.OPENAI_API_KEY",
issueId: "issue-storybook-1",
heartbeatRunId: "run-storybook",
pluginId: null,
outcome: "failure",
errorCode: "secret_disabled",
createdAt: recent(360),
},
];
export const storybookSecretProviderHealth = {
providers: [
{
provider: "local_encrypted" as const,
status: "ok" as const,
message: "Encryption key loaded; permissions OK.",
warnings: [] as string[],
backupGuidance: ["Backup ~/.paperclip/instances/default/secrets/key separately from the database."],
},
{
provider: "aws_secrets_manager" as const,
status: "warn" as const,
message: "Connected; KMS key rotation policy not yet enforced.",
warnings: ["Set up automated KMS key rotation for production tenants."],
backupGuidance: [],
},
{
provider: "gcp_secret_manager" as const,
status: "ok" as const,
message: "Service account reachable.",
warnings: [] as string[],
backupGuidance: [],
},
{
provider: "vault" as const,
status: "ok" as const,
message: "KV v2 mount reachable.",
warnings: [] as string[],
backupGuidance: [],
},
],
};