Ctrl K
ComunidadGuías…

scripts/serve-admin-visual-fixture.mjs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env node
/**
 * Serve deterministic visual-review states through the real authenticated
 * Admin routes. The fixture is deliberately process-local: it binds only to a
 * literal loopback address, uses generated local credentials, keeps every
 * store in one temporary SQLite file, and removes that directory on shutdown.
 */
import { createHash, randomBytes } from 'node:crypto';
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
import { createServer } from 'node:http';
import { tmpdir } from 'node:os';
import { basename, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

import { assertNodeVersion, loadTsModule } from './lib/offline-harness.mjs';

const DEFAULT_HOST = '127.0.0.1';
const WORKSPACE_ID = 'TVISUAL';
const LOCAL_SLACK_TOKEN = 'xoxb-local-admin-visual-fixture';
const LOCAL_SLACK_SECRET = 'local-admin-visual-signing-secret';
const LOCAL_SLACK_BOT_ID = 'UVISUALBOT';
const ENV_KEYS = [
  'SLACK_API_URL',
  'SLACK_BOT_TOKEN',
  'SLACK_SIGNING_SECRET',
  'SLACK_BOT_USER_ID',
];

export const CANONICAL_ADMIN_VISUAL_STATES = Object.freeze({
  settingsUpdates: Object.freeze({ path: '/admin/settings/updates', actions: Object.freeze(['Preview report']) }),
  settingsProviders: Object.freeze({ path: '/admin/settings/providers', actions: Object.freeze([]) }),
  settingsCodingAgents: Object.freeze({ path: '/admin/settings/agents-clients', actions: Object.freeze([]) }),
  settingsConnectedApps: Object.freeze({ path: '/admin/settings/connected-apps', actions: Object.freeze([]) }),
  settingsBrowser: Object.freeze({ path: '/admin/settings/browser', actions: Object.freeze([]) }),
  agentInstructions: Object.freeze({ path: '/admin/agents/agent_research', actions: Object.freeze([]) }),
  agentBlankDescription: Object.freeze({ path: '/admin/agents/agent_customer', actions: Object.freeze([]) }),
  agentMemory: Object.freeze({ path: '/admin/agents/agent_research', actions: Object.freeze(['Memory']) }),
  agentSchedules: Object.freeze({ path: '/admin/agents/agent_release', actions: Object.freeze(['Schedules']) }),
  agentWebsites: Object.freeze({ path: '/admin/agents/agent_research', actions: Object.freeze(['Websites']) }),
  channelsIndex: Object.freeze({ path: '/admin/channels', actions: Object.freeze([]) }),
  channelDetail: Object.freeze({ path: '/admin/channels/TVISUAL/C_RELEASES', actions: Object.freeze([]) }),
  channelAdvanced: Object.freeze({
    path: '/admin/channels/TVISUAL/C_RELEASES',
    actions: Object.freeze(['Advanced']),
  }),
  team: Object.freeze({ path: '/admin/team', actions: Object.freeze([]) }),
});

export const CANONICAL_SLACK_AUTH_VISUAL_STATES = Object.freeze({
  signIn: Object.freeze({ path: '/__admin_visual_fixture/auth/sign-in' }),
  setupCreate: Object.freeze({ path: '/__admin_visual_fixture/auth/setup-create' }),
  setupConnected: Object.freeze({ path: '/__admin_visual_fixture/auth/setup-connected' }),
  setupApproval: Object.freeze({ path: '/__admin_visual_fixture/auth/setup-approval' }),
  setupOwner: Object.freeze({ path: '/__admin_visual_fixture/auth/setup-owner' }),
  accessDenied: Object.freeze({ path: '/__admin_visual_fixture/auth/access-denied' }),
  ownerComplete: Object.freeze({ path: '/__admin_visual_fixture/auth/owner-complete' }),
  recovery: Object.freeze({ path: '/__admin_visual_fixture/auth/recovery' }),
});

export const CANONICAL_CONNECTOR_VISUAL_STATES = Object.freeze({
  setup: Object.freeze({ path: '/__admin_visual_fixture/connectors/setup' }),
  waiting: Object.freeze({ path: '/__admin_visual_fixture/connectors/waiting' }),
  success: Object.freeze({ path: '/__admin_visual_fixture/connectors/success' }),
});

const VISUAL_FIXTURE_AGENT_IDS = Object.freeze(['agent_customer', 'agent_release', 'agent_research']);

const SANDBOX_VISUAL_DEPLOY_SOURCES = Object.freeze({
  'redeploy-command': 'command',
  'redeploy-dashboard': 'workers-builds',
  'redeploy-unknown': 'unknown',
});

export const VISUAL_ENVIRONMENT_STATUS = Object.freeze({
  schemaVersion: 'chickpea-environment-status/v1',
  generatedAt: '2026-09-01T12:00:00.000Z',
  registryRevision: 12,
  selectedTarget: 'amber',
  targets: Object.freeze(['amber', 'cobalt'].map((target, index) => Object.freeze({
    target,
    health: index === 0 ? 'ready' : index === 1 ? 'unreachable' : 'expired_claim',
    sourceSha: '1234567890abcdef1234567890abcdef12345678',
    dirty: index === 0,
    servingVersion: `version-${target}`,
    transport: 'events',
    workspaceAlias: `env-${target}-workspace`,
    workspaceLabel: `${target} workspace`,
    appAlias: `env-${target}-slack-app`,
    appLabel: `${target} app`,
    claim: index === 0 ? Object.freeze({
      holderId: 'holder-0123456789abcdef',
      leaseAgeMs: 180_000,
      expiresAt: '2026-09-01T20:00:00.000Z',
    }) : null,
    verifierLock: Object.freeze(index === 0
      ? { status: 'live', ownerRunId: 'visual-run-amber' }
      : { status: 'clear' }),
    schemaGeneration: 'd1:0002_mcp_oauth;do:v9',
    lastAttestedRevision: index === 0
      ? '1234567890abcdef1234567890abcdef12345678-dirty'
      : null,
    recoveryAction: index === 0
      ? 'No recovery needed.'
      : index === 1
        ? 'Check cobalt Worker and Slack transport reachability.'
        : 'Run npm run env -- reclaim fern.',
    credentialToken: 'xoxb-visual-do-not-expose',
  }))),
  sandbox: Object.freeze({
    archiveDate: '2027-01-15T00:00:00.000Z',
    daysUntilArchive: 136,
    warning: 'none',
    warningDays: Object.freeze([45, 30, 14]),
    unusedWorkspaceSlots: 2,
    integrationHeadroom: 37,
    browserProfilePath: '/private/visual-browser-profile',
  }),
});

const VISUAL_CHANNELS = Object.freeze([
  { id: 'C_RELEASES', name: 'release-room', is_private: false, is_member: true, is_archived: false },
  { id: 'C_SUPPORT', name: 'customer-support', is_private: false, is_member: false, is_archived: false },
  { id: 'C_CUSTOMER', name: 'customer-insights', is_private: true, is_member: true, is_archived: false },
  { id: 'C_UNASSIGNED', name: 'product-feedback', is_private: false, is_member: true, is_archived: false },
  { id: 'C_ARCHIVED', name: 'research-archive', is_private: true, is_member: true, is_archived: true },
  { id: 'C_DISCOVERED', name: 'design-critique', is_private: false, is_member: true, is_archived: false },
]);
let activeFixture = false;

async function seedVisualSlackOwner(identity) {
  const operation = await identity.createAuthOperation({
    id: 'first_owner_visual',
    kind: 'first_owner_claim',
    expectedSlackTeamId: WORKSPACE_ID,
    expectedSlackUserId: 'UVISUALOWNER',
    chickpeaRole: 'owner',
    capabilityHash: 'a'.repeat(64),
    expiresAt: Date.now() + 60_000,
  });
  await identity.createOwnerClaim({
    operationId: operation.id,
    slackTeamId: WORKSPACE_ID,
    slackUserId: 'UVISUALOWNER',
  });
  await identity.advanceAuthOperation({
    operationId: operation.id,
    capabilityHash: 'a'.repeat(64),
    step: 1,
    betterAuthUserId: 'ba_user_visual',
    betterAuthOrganizationId: '11111111-1111-4111-8111-111111111111',
    betterAuthMembershipId: 'ba_member_visual',
  });
  return identity.claimOwner({
    operationId: operation.id,
    organizationId: 'org_oss',
    slackTeamId: WORKSPACE_ID,
    slackUserId: 'UVISUALOWNER',
    displayName: 'Visual Owner',
    betterAuthUserId: 'ba_user_visual',
    betterAuthMembershipId: 'ba_member_visual',
  });
}

async function seedVisualProposal(management, store, owner) {
  const agent = await store.getAgent('agent_research');
  const instructions = 'Synthetic frozen instructions. <script>not executable</script>\n' +
    'Check the complete value. '.repeat(600) + '\n' + 'long-token-'.repeat(30) +
    '\nEnd of frozen instructions.';
  const scope = `slack:${WORKSPACE_ID}:C_RELEASES:1800000000.000100:agent:agent_chickpea`;
  await management.putChangeSetProposal({
    proposalId: 'proposal_visual_research', organizationId: owner.membership.organizationId,
    actorUserId: owner.user.id, actorMembershipId: owner.membership.id,
    originKey: scope, approvalScopeKey: scope, idempotencyKey: 'visual-proposal',
    guideVersion: 'visual-fixture', authoringReason: 'agent_edit',
    digest: createHash('sha256').update(instructions).digest('hex'),
    operations: [{ itemId: 'instructions', kind: 'update_agent', agentId: agent.id,
      expectedRevision: agent.revision, patch: { instructions,
        description: 'Synthetic proposal for local visual review.' } }],
    preview: { summary: 'Synthetic update preview', changes: [], missingSetup: [] },
    targetRevisions: { [`agent:${agent.id}`]: agent.revision }, at: 1,
  });
}

async function seedVisualTeam(identity, provisionSlackInteractionMember, owner) {
  const fixtures = [
    { slackUserId: 'UVISUALADMIN', displayName: 'Alex Admin', role: 'admin', status: 'active' },
    { slackUserId: 'UVISUALMEMBER', displayName: 'Maya Member', role: 'member', status: 'active' },
    { slackUserId: 'UVISUALOWNER2', displayName: 'Olive Owner', role: 'owner', status: 'active' },
    { slackUserId: 'UVISUALSUSPENDED', displayName: 'Sam Suspended', role: 'member', status: 'suspended' },
    { slackUserId: 'UVISUALREMOVED', displayName: 'Rae Removed', role: 'member', status: 'removed' },
  ];
  for (const fixture of fixtures) {
    const provisioned = await provisionSlackInteractionMember({
      identity,
      slackTeamId: WORKSPACE_ID,
      botUserId: LOCAL_SLACK_BOT_ID,
      user: {
        id: fixture.slackUserId,
        teamId: WORKSPACE_ID,
        displayName: fixture.displayName,
        deleted: false,
        bot: false,
        appUser: false,
        restricted: false,
        ultraRestricted: false,
        stranger: false,
      },
    });
    const membership = provisioned.resolution?.membership;
    if (!membership) throw new Error(`Could not seed visual member ${fixture.slackUserId}.`);
    if (fixture.role !== membership.role) {
      await identity.updateMembershipAuthority({
        membershipId: membership.id,
        role: fixture.role,
        actorMembershipId: owner.membership.id,
        authenticationSurface: 'better_auth',
        correlationId: `visual-role-${fixture.slackUserId}`,
        reasonCode: 'owner_changed_member_role',
      });
    }
    if (fixture.status !== 'active') {
      await identity.updateMembershipAuthority({
        membershipId: membership.id,
        status: fixture.status,
        actorMembershipId: owner.membership.id,
        authenticationSurface: 'better_auth',
        correlationId: `visual-status-${fixture.slackUserId}`,
        reasonCode: 'admin_deactivated_member',
      });
    }
  }
}

function assertLoopbackHost(host) {
  if (host !== '127.0.0.1' && host !== '::1') {
    throw new Error(`Admin visual fixture host must be a literal loopback address, received ${host}.`);
  }
}

function loopbackAddress(address) {
  return address === '127.0.0.1' || address === '::1' || address === '::ffff:127.0.0.1';
}

function urlHost(address) {
  return address.includes(':') ? `[${address}]` : address;
}

function visualAgents() {
  const slackPresence = (id, handle, avatarHash) => ({
    requestedHandle: handle,
    normalizedHandle: handle,
    desiredState: 'active',
    health: 'healthy',
    avatar: {
      kind: 'generated',
      revision: 1,
      seed: id,
      url: `https://secure.gravatar.com/avatar/${avatarHash}?s=192&d=identicon`,
    },
  });
  return [
    {
      id: 'agent_customer',
      revision: 1,
      name: 'Customer Insights',
      description: '',
      instructions: 'Summarize customer feedback and distinguish recurring evidence from anecdotes.',
      enabled: false,
      model: 'anthropic/claude-opus-5-5',
      modelEffort: 'xhigh',
      slackPresence: slackPresence('agent_customer', 'customer-insights', 'b6a1f4b55c4c81e3da8a5f3c12f2ef7f'),
      skills: [],
      mcpServers: [],
      apiConnections: [],
      repositories: [],
    },
    {
      id: 'agent_release',
      revision: 1,
      name: 'Release Scribe',
      description: 'Keeps launches concise, decision-ready, and explicit about risk.',
      instructions: 'Turn release activity into concise, decision-ready updates with explicit risks.',
      enabled: true,
      slackPresence: slackPresence('agent_release', 'release-scribe', '205e460b479e2e5b48aec07710c08d50'),
      skills: [{
        name: 'release-readiness',
        description: 'Review release readiness and launch risk.',
        instructions: 'Name blockers, owners, and the safest next step.',
        enabled: true,
      }],
      mcpServers: [],
      apiConnections: [],
      repositories: [],
    },
    {
      id: 'agent_research',
      revision: 1,
      name: 'Research Partner',
      description: 'Investigates product questions with evidence and clearly marked uncertainty.',
      instructions: 'Investigate product questions, cite the available evidence, and call out uncertainty.',
      enabled: true,
      model: 'local-stub/visual-review',
      slackPresence: slackPresence('agent_research', 'research-partner', 'f9879d71855b5ff21e4963273a886bfc'),
      skills: [{
        name: 'research-brief',
        description: 'Build a sourced research brief.',
        instructions: 'Separate fact, inference, and open questions.',
        enabled: true,
      }],
      mcpServers: [{
        id: 'linear',
        displayName: 'Linear',
        url: 'https://mcp.linear.app/mcp',
        transport: 'streamable-http',
        authMode: 'none',
        headerNames: [],
        enabled: true,
        lifecycleStatus: 'ready',
        statusText: 'Connected · 2 tools',
        discoveredTools: [{ name: 'search' }, { name: 'create' }],
        allowedTools: ['search', 'create'],
        presetId: 'linear',
      }],
      apiConnections: [],
      repositories: [{
        id: 'repo_research',
        installationId: 42,
        accountLogin: 'acme',
        fullName: 'acme/research',
        enabled: true,
      }],
    },
  ];
}

async function seedConfig(store, runtimeContract) {
  const channels = [
    {
      workspaceId: WORKSPACE_ID,
      channelId: 'C_RELEASES',
      label: 'release-room',
      lifecycle: 'active',
      agentId: 'agent_release',
    },
    {
      workspaceId: WORKSPACE_ID,
      channelId: 'C_SUPPORT',
      label: 'customer-support',
      lifecycle: 'active',
      agentId: 'agent_research',
    },
    {
      workspaceId: WORKSPACE_ID,
      channelId: 'C_CUSTOMER',
      label: 'customer-insights',
      lifecycle: 'active',
      agentId: null,
    },
    {
      workspaceId: WORKSPACE_ID,
      channelId: 'C_UNASSIGNED',
      label: 'product-feedback',
      lifecycle: 'active',
      agentId: null,
    },
    {
      workspaceId: WORKSPACE_ID,
      channelId: 'C_ARCHIVED',
      label: 'research-archive',
      lifecycle: 'archived',
      agentId: 'agent_research',
    },
  ];
  for (const { agentId, ...channel } of channels) {
    await store.putChannel(channel);
    if (agentId) {
      await store.putAgentChannelGrant({
        workspaceId: channel.workspaceId,
        channelId: channel.channelId,
        agentId,
        status: 'active',
        createdByMembershipId: 'membership_visual_owner',
        channelLabel: channel.label,
      });
    }
  }
  const installation = await store.ensureWorkspaceInstallation({
    workspaceId: WORKSPACE_ID,
    transportMode: 'direct',
    runtimeContract,
    defaultAgentId: 'agent_research',
    teamId: WORKSPACE_ID,
    appId: 'AVISUAL',
    botUserId: LOCAL_SLACK_BOT_ID,
  });
  await store.updateWorkspaceInstallation(WORKSPACE_ID, {
    health: 'healthy',
  }, installation.revision);
}

async function seedConnections(store, ownerMembershipId) {
  const account = (slug, providerId, label, toolkit, allowedCapabilities, options = {}) => ({
    id: `connection_${slug}`,
    workspaceId: WORKSPACE_ID,
    ownerKind: options.ownerKind ?? 'member',
    ...(options.ownerKind === 'team' ? {} : { ownerMembershipId }),
    createdByMembershipId: ownerMembershipId,
    providerId,
    label: `${label} · ${options.ownerKind === 'team' ? 'Team' : 'Personal'}`,
    identity: { accountName: options.accountName ?? 'member@acme.test' },
    policy: {
      kind: 'managed',
      adapterId: 'composio',
      toolkit,
      principalRef: options.ownerKind === 'team'
        ? `chickpea:organization:${WORKSPACE_ID}`
        : `chickpea:membership:${ownerMembershipId}`,
      accountRef: `ca_visual_${slug}`,
      allowedCapabilities,
    },
    secretRefId: `secret_visual_${slug}`,
    lifecycle: options.lifecycle ?? 'ready',
  });
  const accounts = [
    account('drive', 'google', 'Google Drive', 'googledrive', [
      'drive.files.search', 'drive.files.metadata', 'drive.files.read', 'drive.files.create',
    ]),
    account('gmail', 'google', 'Gmail', 'gmail', [
      'gmail.profile.read', 'gmail.messages.search', 'gmail.drafts.create', 'gmail.messages.send',
    ]),
    account('calendar', 'google', 'Google Calendar', 'googlecalendar', [
      'calendar.calendars.list', 'calendar.events.list', 'calendar.events.create',
      'calendar.events.update', 'calendar.events.delete',
    ]),
    account('team_gmail_needs_attention', 'google', 'Gmail', 'gmail', [
      'gmail.profile.read', 'gmail.messages.search', 'gmail.drafts.create', 'gmail.messages.send',
    ], {
      ownerKind: 'team', lifecycle: 'needs_attention', accountName: 'team@acme.test',
    }),
  ];
  for (const connection of accounts) await store.putConnectionAccount(connection);
  for (const [agentId, connection] of [
    ['agent_research', accounts[0]],
    ['agent_research', accounts[1]],
    ['agent_release', accounts[3]],
  ]) {
    await store.putAgentConnectionBinding({
      agentId,
      connectionAccountId: connection.id,
      providerId: connection.providerId,
      allowedCapabilities: connection.policy.allowedCapabilities,
      enabled: true,
    });
  }
}

const VISUAL_NOTION_PAGES = Object.freeze([
  ['Q4 Roadmap', 'page'], ['Sales Playbook', 'page'], ['Engineering Wiki', 'page'],
  ['Customer Interviews', 'database'], ['Launch Checklist', 'page'], ['Finance Ops', 'page'],
].map(([label, kind], index) => ({
  id: `0000000${index + 1}-0000-4000-8000-00000000000${index + 1}`,
  kind,
  label,
})));

/**
 * With --notion-sign-ins: one team Notion sign-in that the research Agent
 * signed and the release Agent reuses with two pages chosen, plus a Scout
 * Agent without Notion, so the reuse form, the row summary, the page picker
 * and the shared-change confirm can be reviewed. A member principal, who
 * sees only Agents they created, also gets one personal sign-in that Scout
 * and a Notes Agent share.
 */
async function seedNotionSignIns(store, ownerMembership, { MANAGED_CONNECTOR_CATALOG }, principalRole) {
  const capabilities = MANAGED_CONNECTOR_CATALOG.capabilities('notion', 'write').map(({ id }) => id);
  const grantSummary = {
    items: VISUAL_NOTION_PAGES.map(({ id, kind, label }) => ({ id, type: kind, label })),
    truncated: false,
  };
  const shareSignIn = async ({ ownerKind, accountRef, person, agents }) => {
    const policy = {
      kind: 'managed',
      adapterId: 'composio',
      toolkit: 'notion',
      principalRef: ownerKind === 'team'
        ? `chickpea:organization:${ownerMembership.organizationId}`
        : `chickpea:membership:${ownerMembership.id}`,
      accountRef,
      allowedCapabilities: capabilities,
      signIn: { person, accessLane: 'write', signedBy: ownerMembership.id },
      grantSummary,
    };
    const account = (agentId) => ({
      id: `connection_notion_${agentId.replace('agent_', '')}`,
      workspaceId: WORKSPACE_ID,
      ownerKind,
      ...(ownerKind === 'member' ? { ownerMembershipId: ownerMembership.id } : {}),
      createdByMembershipId: ownerMembership.id,
      providerId: 'notion',
      label: `Notion · ${ownerKind === 'team' ? 'Team' : 'Personal'}`,
      policy,
      secretRefId: `secret_visual_notion_${agentId}`,
      lifecycle: 'ready',
    });
    const binding = (agentId, notionPages) => ({
      agentId, connectionAccountId: account(agentId).id, providerId: 'notion',
      allowedCapabilities: capabilities, enabled: true,
      ...(notionPages ? { resourceConstraints: { notionPages } } : {}),
    });
    const [[signerId], [joinerId, joinerPages]] = agents;
    const signer = await store.putConnectionAccount(account(signerId), 0);
    await store.putAgentConnectionBinding(binding(signerId));
    const { kind: _kind, allowedCapabilities: _capabilities, ...shared } = policy;
    await store.applySignInGroup({
      adapterId: 'composio',
      accountRef,
      members: [{ connectionAccountId: signer.id, expectedRevision: signer.revision }],
      shared,
      join: { account: account(joinerId), binding: binding(joinerId, joinerPages) },
    });
  };
  const agent = (id, name) => store.createAgent({
    id, name, instructions: 'Research questions in Notion.', enabled: true,
    lifecycle: 'active', configurationGeneration: 1, creatorMembershipId: ownerMembership.id,
    editPolicy: 'creator_and_admins', skills: [], mcpServers: [], apiConnections: [], repositories: [],
  });
  await agent('agent_scout', 'Scout');
  await shareSignIn({
    ownerKind: 'team', accountRef: 'ca_visual_notion_team', person: 'notion:visual:owner',
    agents: [['agent_research'], ['agent_release', [VISUAL_NOTION_PAGES[0].id, VISUAL_NOTION_PAGES[3].id]]],
  });
  if (principalRole !== 'member') return;
  await agent('agent_notes', 'Notes');
  await shareSignIn({
    ownerKind: 'member', accountRef: 'ca_visual_notion_personal', person: 'notion:visual:member',
    agents: [['agent_scout'], ['agent_notes']],
  });
}

/** Answers for Notion only: its pages, and a sign-in that is never opened. */
function visualNotionProviders({ createManagedConnectionProviderRegistry }) {
  const listing = () => ({ pages: VISUAL_NOTION_PAGES.map((page) => ({ ...page })), complete: true });
  return createManagedConnectionProviderRegistry([{
    id: 'composio',
    availability: ({ toolkit }) => toolkit === 'notion'
      ? { status: 'ready', missingConfiguration: [] }
      : { status: 'unavailable', missingConfiguration: ['api_key_missing'] },
    async authorize() { throw new Error('The Admin visual fixture does not open Notion.'); },
    async validate() { return { person: 'notion:visual:owner', pages: listing() }; },
    async listPages() { return listing(); },
    async execute() { return { data: {} }; },
    async revoke() {},
  }]);
}

async function seedSettings(settings) {
  await settings.setSetting('slack.teamName', 'Acme Design');
}

async function seedMemory(memory) {
  await memory.putAgentMemory({
    agentId: 'agent_research',
    expectedRevision: 0,
    body: [
      'Prefer short evidence summaries for product and engineering leads.',
      'Separate verified fact, inference, and unresolved questions.',
      'Check focused tests, rollout risk, owner, and rollback before launch.',
    ].join('\n'),
  });
}

async function seedSchedules(store, routines, ownerMembershipId, RoutineService) {
  const definitions = [
    {
      id: 'routine_visual_daily_release',
      name: 'Daily launch readiness digest with a long scannable name',
      scheduleInput: '0 9 * * 1-5',
      nextRunAt: Date.UTC(2026, 7, 26, 16),
      pause: false,
      referenceState: 'active',
    },
    {
      id: 'routine_visual_weekly_summary',
      name: 'Weekly customer summary',
      scheduleInput: '0 14 * * 5',
      nextRunAt: Date.UTC(2026, 7, 28, 21),
      pause: true,
      referenceState: 'active',
    },
    {
      id: 'routine_visual_dependency',
      name: 'Dependency follow-up',
      scheduleInput: '*/30 * * * *',
      nextRunAt: Date.UTC(2026, 7, 25, 20, 30),
      pause: false,
      referenceState: 'needs_attention',
    },
  ];
  for (const item of definitions) {
    const service = new RoutineService(routines, { routineId: () => item.id });
    let routine = await service.save({
      action: 'create',
      actorId: 'UVISUALOWNER',
      workspaceId: WORKSPACE_ID,
      channelId: 'C_RELEASES',
      definition: {
        name: item.name,
        description: 'Visual schedule fixture.',
        taskText: 'Post a concise visual-fixture update.',
        triggerKind: 'schedule',
        scheduleInput: item.scheduleInput,
        scheduleJson: JSON.stringify({ version: 1, kind: 'cron', expression: item.scheduleInput }),
        timezone: 'America/Los_Angeles',
        outputPolicy: 'post',
        authorityMode: 'live_channel_v1',
      },
      nextRunAt: item.nextRunAt,
      projectedDailyStarts: 1,
      reservations: [{ windowStart: item.nextRunAt, count: 1 }],
      sourceVisibility: 'public',
    }, `visual-schedule:${item.id}`);
    if (item.pause) {
      routine = await service.control({
        routineId: routine.id,
        expectedVersion: routine.version,
        action: 'pause',
        actorId: 'UVISUALOWNER',
        actorClass: 'operator',
        reasonCode: 'admin_control',
        idempotencyKey: `visual-schedule-pause:${item.id}`,
      });
    }
    await store.putAgentScheduleReference({
      scheduleId: routine.id,
      agentId: 'agent_release',
      workspaceId: WORKSPACE_ID,
      channelId: 'C_RELEASES',
      createdByMembershipId: ownerMembershipId,
      runsAsMembershipId: ownerMembershipId,
      authorityReceiptId: `receipt_${item.id}`,
      requiredConnectionAccountIds: [],
      state: item.referenceState,
    });
  }
}

function fakeSlackResponse(pathname, body) {
  if (pathname.endsWith('/conversations.list')) {
    return {
      ok: true,
      channels: VISUAL_CHANNELS,
      response_metadata: { next_cursor: '' },
    };
  }
  if (pathname.endsWith('/auth.test')) {
    return {
      ok: true,
      app_id: 'AVISUAL',
      team: 'Acme Design',
      team_id: WORKSPACE_ID,
      user_id: LOCAL_SLACK_BOT_ID,
      bot_id: 'B_VISUAL',
    };
  }
  if (pathname.endsWith('/users.info')) {
    const visualProfiles = {
      UVISUALOWNER: ['Pejman Pour-Moezzi', 'pejman.pourmoezzi', 'pejman@example.com'],
      UVISUALADMIN: ['Alex Admin', 'alex.admin', 'alex@example.com'],
      UVISUALMEMBER: ['Maya Member', 'maya.member', 'maya@example.com'],
      UVISUALOWNER2: ['Olive Owner', 'olive.owner', 'olive@example.com'],
      UVISUALSUSPENDED: ['Sam Suspended', 'sam.suspended', 'sam@example.com'],
      UVISUALREMOVED: ['Rae Removed', 'rae.removed', 'rae@example.com'],
    };
    const profile = visualProfiles[body.user];
    if (profile) {
      return {
        ok: true,
        user: {
          id: body.user,
          team_id: WORKSPACE_ID,
          name: profile[1],
          profile: {
            display_name: profile[1],
            real_name: profile[0],
            email: profile[2],
            image_192: 'https://secure.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?s=192&d=identicon',
          },
        },
      };
    }
    if (body.user !== LOCAL_SLACK_BOT_ID) {
      return { ok: false, error: 'user_not_found' };
    }
    return {
      ok: true,
      user: {
        id: LOCAL_SLACK_BOT_ID,
        team_id: WORKSPACE_ID,
        is_bot: true,
        profile: { display_name: 'Chickpea', real_name: 'Chickpea' },
      },
    };
  }
  if (pathname.endsWith('/conversations.info')) {
    const channel = VISUAL_CHANNELS
      .find((candidate) => candidate.id === body.channel);
    return channel
      ? { ok: true, channel }
      : { ok: false, error: 'channel_not_found' };
  }
  return { ok: false, error: 'fixture_endpoint_not_found' };
}

async function nodeRequest(req, res, app, baseUrl, platformEnv) {
  try {
    const chunks = [];
    for await (const chunk of req) chunks.push(Buffer.from(chunk));
    const body = chunks.length ? Buffer.concat(chunks) : undefined;
    const request = new Request(new URL(req.url ?? '/', baseUrl), {
      method: req.method,
      headers: req.headers,
      ...(body ? { body } : {}),
    });
    const response = await app.fetch(request, platformEnv);
    res.statusCode = response.status;
    for (const [name, value] of response.headers) res.setHeader(name, value);
    const bytes = Buffer.from(await response.arrayBuffer());
    res.end(bytes);
  } catch (error) {
    res.statusCode = 500;
    res.setHeader('content-type', 'text/plain; charset=utf-8');
    res.end(error instanceof Error ? error.message : String(error));
  }
}

function listen(server, port, host) {
  return new Promise((resolveListen, reject) => {
    server.once('error', reject);
    server.listen(port, host, () => {
      server.off('error', reject);
      resolveListen();
    });
  });
}

function closeServer(server) {
  return new Promise((resolveClose, reject) => {
    if (!server.listening) {
      resolveClose();
      return;
    }
    server.close((error) => (error ? reject(error) : resolveClose()));
  });
}

/** Start one disposable, loopback-only real Admin fixture. */
export async function startAdminVisualFixture(options = {}) {
  const host = options.host ?? DEFAULT_HOST;
  const port = options.port ?? 0;
  const runtimeContract = options.runtimeContract ?? 'legacy';
  const onboardingStage = options.onboardingStage ?? null;
  const principalRole = options.principalRole ?? 'owner';
  const updateState = options.updateState ?? null;
  const sandboxState = options.sandboxState ?? null;
  if (updateState !== null && !['available', 'current', 'failed', 'unversioned', 'no-release'].includes(updateState)) {
    throw new Error('Unknown update visual state.');
  }
  if (sandboxState !== null && !Object.hasOwn(SANDBOX_VISUAL_DEPLOY_SOURCES, sandboxState)) {
    throw new Error('Unknown sandbox visual state.');
  }
  assertLoopbackHost(host);
  if (!Number.isSafeInteger(port) || port < 0 || port > 65_535) {
    throw new Error(`Admin visual fixture port must be an integer from 0 to 65535, received ${port}.`);
  }
  if (runtimeContract !== 'legacy' && runtimeContract !== 'chickpea-v1') {
    throw new Error(`Admin visual fixture runtime contract must be legacy or chickpea-v1, received ${runtimeContract}.`);
  }
  if (onboardingStage !== null && ![
    'choose_provider', 'choose_model', 'try',
  ].includes(onboardingStage)) {
    throw new Error(`Unknown onboarding visual stage ${onboardingStage}.`);
  }
  if (!['owner', 'admin', 'member'].includes(principalRole)) {
    throw new Error(`Unknown Admin visual principal role ${principalRole}.`);
  }
  if (activeFixture) throw new Error('Only one Admin visual fixture can run in a process at a time.');
  activeFixture = true;

  const stateDirectory = mkdtempSync(join(tmpdir(), 'chickpea-admin-visual-'));
  const stateDbPath = join(stateDirectory, 'state.db');
  const previousEnv = new Map(ENV_KEYS.map((key) => [key, process.env[key]]));
  const stores = [];
  let server;
  let closed = false;

  const cleanup = async () => {
    if (closed) return;
    closed = true;
    // Start refusing new requests immediately, but do not yield before
    // releasing process-local resources. npm and the child Node process both
    // receive an interactive signal, so a first-await cleanup can be cut off
    // before its temporary database is removed.
    const serverClosing = server ? closeServer(server) : Promise.resolve();
    try {
      for (const store of stores.reverse()) {
        try { store.close?.(); } catch { /* finish closing the remaining handles */ }
      }
    } finally {
      for (const [key, value] of previousEnv) {
        if (value === undefined) delete process.env[key];
        else process.env[key] = value;
      }
      rmSync(stateDirectory, { recursive: true, force: true });
      activeFixture = false;
    }
    await serverClosing;
  };

  try {
    assertNodeVersion();
    const { Hono } = await import('hono');
    const { createAdminRoutes } = await loadTsModule('src/admin/routes.ts');
    const {
      renderSlackAccessDeniedPage,
      renderSlackOwnerCompletePage,
      renderSlackRecoveryPage,
      renderSlackSetupPage,
      renderSlackSignInPage,
    } = await loadTsModule('src/admin/page.ts');
    const {
      renderManagedConnectionSetupPage,
      renderManagedConnectionSuccessPage,
      renderManagedConnectionWaitingPage,
    } = await loadTsModule('src/management/connector-landing-page.ts');
    const {
      buildSlackAppManifest,
      slackManifestPrefillUrl,
    } = await loadTsModule('src/slack/app-manifest.ts');
    const { SqliteConfigStore } = await loadTsModule('src/config/store.ts');
    const { SqliteSettingsStore } = await loadTsModule('src/config/settings-store.ts');
    const { PROVIDER_KEY_SETTING_KEYS } = await loadTsModule('src/config/provider-keys.ts');
    const { SqliteMemoryStateStore } = await loadTsModule('src/memory/store.ts');
    const { agentRoutineDeletion, SqliteRoutineStore } = await loadTsModule('src/routines/store.ts');
    const { RoutineService } = await loadTsModule('src/routines/service.ts');
    const { SqliteUsageStore } = await loadTsModule('src/usage/store.ts');
    const { SqliteWorkStore } = await loadTsModule('src/work/store.ts');
    const { SqliteSlackStateStore } = await loadTsModule('src/slack/claim-store.ts');
    const { SqliteAgentSnapshotStore } = await loadTsModule('src/config/snapshot-store.ts');
    const { SqliteIdentityStore } = await loadTsModule('src/identity/store.ts');
    const { SqliteManagementStore } = await loadTsModule('src/management/store.ts');
    const { provisionSlackInteractionMember } = await loadTsModule('src/auth/slack-admission.ts');
    const {
      beginOnboardingJourney,
      selectOnboardingProvider,
      startOnboardingTry,
    } = await loadTsModule('src/config/onboarding-state.ts');
    const { generateCredentialKeyring } = await loadTsModule('src/slack/credential-keyring.ts');
    const { writeSlackInstallationCredentials } = await loadTsModule('src/slack/installation-credentials.ts');
    const { WORKSPACE_SLACK_INSTALLATION_ID } = await loadTsModule('src/config/types.ts');
    const {
      invalidateSlackChannelsCache,
    } = await loadTsModule('src/slack/channels.ts');
    const {
      invalidateStoredSlackCredentials,
    } = await loadTsModule('src/slack/credentials.ts');

    invalidateSlackChannelsCache();
    invalidateStoredSlackCredentials();

    const store = new SqliteConfigStore(stateDbPath, agentRoutineDeletion, {
      agents: visualAgents(),
      grants: [],
    });
    const settings = new SqliteSettingsStore(stateDbPath);
    const memory = new SqliteMemoryStateStore(stateDbPath);
    const routines = new SqliteRoutineStore(stateDbPath);
    const usage = new SqliteUsageStore(stateDbPath);
    const work = new SqliteWorkStore(stateDbPath);
    const slackState = new SqliteSlackStateStore(stateDbPath);
    const snapshots = new SqliteAgentSnapshotStore(stateDbPath);
    const identity = new SqliteIdentityStore(stateDbPath);
    const management = new SqliteManagementStore(stateDbPath);
    stores.push(store, settings, memory, routines, usage, work, slackState, snapshots, identity, management);

    await seedConfig(store, runtimeContract);
    await seedSettings(settings);
    if (onboardingStage) {
      if (onboardingStage === 'choose_model' || onboardingStage === 'try') {
        await settings.setSetting(
          PROVIDER_KEY_SETTING_KEYS.anthropic,
          'sk-ant-local-admin-visual-fixture',
        );
      }
      let onboarding = await beginOnboardingJourney(settings, Date.now());
      if (onboardingStage === 'choose_model' || onboardingStage === 'try') {
        onboarding = await selectOnboardingProvider(settings, {
          expectedRevision: onboarding.revision,
          workspaceId: WORKSPACE_ID,
          providerId: 'anthropic',
        });
      }
      if (onboardingStage === 'try') {
        await startOnboardingTry(settings, {
          expectedRevision: onboarding.revision,
          agentId: 'agent_chickpea',
          modelId: 'anthropic/claude-sonnet-5',
          slackUserId: 'UVISUAL',
          tryStartedAt: Date.now(),
        });
      }
    }
    const slackCredentials = {
      state: identity,
      keyring: generateCredentialKeyring(),
    };
    await writeSlackInstallationCredentials(
      slackCredentials,
      WORKSPACE_SLACK_INSTALLATION_ID,
      null,
      {
        botToken: LOCAL_SLACK_TOKEN,
        signingSecret: LOCAL_SLACK_SECRET,
        botUserId: LOCAL_SLACK_BOT_ID,
        appId: 'AVISUAL',
        teamId: WORKSPACE_ID,
      },
    );
    await seedMemory(memory);
    const owner = await seedVisualSlackOwner(identity);
    await seedVisualProposal(management, store, owner);
    await seedConnections(store, owner.membership.id);
    if (options.notionSignIns) {
      await seedNotionSignIns(store, owner.membership, await loadTsModule('src/connections/catalog/index.ts'), principalRole);
    }
    await seedSchedules(store, routines, owner.membership.id, RoutineService);
    await seedVisualTeam(identity, provisionSlackInteractionMember, owner);

    const adminToken = `visual-${randomBytes(18).toString('base64url')}`;
    const app = new Hono();
    const authManifest = buildSlackAppManifest({
      kind: 'workspace_app', origin: 'https://chickpea.example',
    });
    const authSetup = (state) => ({
      id: 'setup_visual', state, revision: 4, destination: '/admin/channels',
      createdAt: 1, updatedAt: 2, expiresAt: Date.now() + 86_400_000,
      appId: 'AVISUAL', manifestFingerprint: 'f'.repeat(64),
      credentialRevision: 'slackrev_visual',
    });
    const authPages = {
      'sign-in': renderSlackSignInPage('/admin/channels'),
      'setup-create': renderSlackSetupPage({
        setup: authSetup('awaiting_app_creation'), destination: '/admin/channels',
        manifest: authManifest, manifestPrefillUrl: slackManifestPrefillUrl(authManifest),
      }),
      'setup-connected': renderSlackSetupPage({
        setup: authSetup('awaiting_app_creation'), destination: '/admin/channels',
        manifest: authManifest, manifestPrefillUrl: slackManifestPrefillUrl(authManifest),
        gatewayState: 'connected',
      }),
      'setup-approval': renderSlackSetupPage({
        setup: authSetup('approval_pending'), destination: '/admin/channels',
        manifest: authManifest, manifestPrefillUrl: slackManifestPrefillUrl(authManifest),
      }),
      'setup-owner': renderSlackSetupPage({
        setup: authSetup('bot_installed'), destination: '/admin/channels',
        manifest: authManifest, manifestPrefillUrl: slackManifestPrefillUrl(authManifest),
      }),
      'access-denied': renderSlackAccessDeniedPage({
        purpose: 'login', destination: '/admin/channels', reason: 'user_mismatch',
        workspace: { teamId: WORKSPACE_ID, teamName: 'Acme' },
      }),
      'owner-complete': renderSlackOwnerCompletePage('/admin/channels'),
      recovery: renderSlackRecoveryPage({
        stage: 'credentials', expectedAppId: 'AVISUAL', expectedTeamId: WORKSPACE_ID,
      }),
    };
    const sprout = {
      id: 'agent_sprout',
      revision: 1,
      name: 'Sprout',
      description: '',
      instructions: 'Help the requester research customer relationships.',
      enabled: true,
      skills: [],
      mcpServers: [],
      apiConnections: [],
      repositories: [],
    };
    const connectorSetup = {
      setupOperationId: 'setup_visual_connector',
      action: 'managed_connection',
      target: {
        kind: 'managed_connection',
        targetId: 'agent:agent_sprout:managed:hubspot:member',
        targetLabel: 'HubSpot',
        provider: 'hubspot',
        agentId: sprout.id,
        agentName: sprout.name,
        ownerKind: 'member',
        accessLane: 'read',
        presetId: 'hubspot-managed',
      },
      actor: {
        membershipId: owner.membership.id,
        organizationId: owner.membership.organizationId,
        slackTeamId: WORKSPACE_ID,
        slackUserId: owner.user.slackUserId,
      },
      status: 'pending',
      createdAt: Date.now(),
      expiresAt: Date.now() + 15 * 60_000,
    };
    const sproutAvatarPath = resolve('assets/chickpea-avatars/agent-defaults/01-sage.png');
    const connectorPageInput = (status) => ({
      setup: { ...connectorSetup, status },
      agent: sprout,
      avatarUrl: '/__admin_visual_fixture/connectors/sprout.png',
    });
    let connectorVisualConnected = false;
    app.get('/__admin_visual_fixture/login', (c) => {
      if (c.req.query('token') !== adminToken) return c.text('Not found', 404);
      c.header('Set-Cookie', `chickpea_visual_token=${adminToken}; Path=/; HttpOnly; SameSite=Lax`);
      c.header('Cache-Control', 'no-store');
      return c.redirect(c.req.query('destination') || '/admin/team');
    });
    app.get('/__admin_visual_fixture/auth/:state', (c) => {
      const html = authPages[c.req.param('state')];
      if (!html) return c.notFound();
      c.header('Cache-Control', 'no-store');
      return c.html(html);
    });
    app.get('/__admin_visual_fixture/connectors/sprout.png', (c) => {
      c.header('Cache-Control', 'public, max-age=3600');
      c.header('Content-Type', 'image/png');
      return c.body(readFileSync(sproutAvatarPath));
    });
    app.get('/__admin_visual_fixture/connectors/:state', (c) => {
      const state = c.req.param('state');
      if (state === 'setup') {
        connectorVisualConnected = false;
        return c.html(renderManagedConnectionSetupPage(connectorPageInput('pending')));
      }
      if (state === 'waiting') {
        return c.html(connectorVisualConnected
          ? renderManagedConnectionSuccessPage(connectorPageInput('completed'))
          : renderManagedConnectionWaitingPage(connectorPageInput('pending')));
      }
      if (state === 'success') {
        return c.html(renderManagedConnectionSuccessPage(connectorPageInput('completed')));
      }
      return c.notFound();
    });
    app.post('/setup/setup_visual_connector/authorize', (c) => {
      if (c.req.header('x-requested-with') === 'chickpea-setup') {
        return c.json({ authorizationUrl: 'https://example.com/chickpea-connector-sign-in' });
      }
      return c.redirect('/__admin_visual_fixture/connectors/waiting');
    });
    app.post('/setup/setup_visual_connector/managed/poll', (c) => {
      connectorVisualConnected = true;
      return c.json({ ok: true, status: 'connected' });
    });
    app.post('/setup/setup_visual_connector/cancel', (c) => c.redirect('/__admin_visual_fixture/connectors/setup'));
    app.post('/__admin_visual_fixture/slack/:method', async (c) => {
      if (c.req.header('authorization') !== `Bearer ${LOCAL_SLACK_TOKEN}`) {
        return c.json({ ok: false, error: 'invalid_auth' });
      }
      const body = await c.req.parseBody();
      const result = fakeSlackResponse(c.req.path, body);
      return c.json(result, result.error === 'fixture_endpoint_not_found' ? 404 : 200);
    });
    // Visual-only response fixtures run AFTER the real authorization gate.
    // They never become production configuration or contact GitHub.
    app.use('/admin/api/installation/*', async (c, next) => {
      await next();
      if (!updateState || c.res.status !== 200) return;
      if (c.req.path.endsWith('/updates')) {
        const release = { version: updateState === 'current' ? '0.1.0' : '0.1.1', notes: 'A small reliability update.\n\n• Clearer setup guidance\n• Improvements to scheduled replies', publishedAt: '2026-09-07T12:00:00Z', url: 'https://github.com/pejmanjohn/chickpea/releases/tag/v0.1.1' };
        c.res = Response.json({ status: updateState, checkedAt: '2026-09-07T12:00:00Z', ...(updateState === 'no-release' ? {} : { release }), ...(updateState === 'failed' ? { error: 'network', lastSuccessfulCheckAt: '2026-09-06T12:00:00Z' } : {}) });
      } else if (c.req.path.endsWith('/support') && updateState !== 'unversioned') {
        const body = await c.res.json();
        c.res = Response.json({ report: body.report.replace('Application version: development', 'Application version: 0.1.0').replace('Source commit: unknown', `Source commit: ${'a'.repeat(40)}`).replace('Deployment: Node', 'Deployment: Cloudflare') });
      }
    });
    // The fixture runs on Node, where the coding sandbox is unsupported. This
    // presents the Cloudflare redeploy-required status for visual review only.
    app.use('/admin/api/sandbox/status', async (c, next) => {
      await next();
      if (!sandboxState || c.req.method !== 'GET' || c.res.status !== 200) return;
      const body = await c.res.json();
      c.res = Response.json({
        ...body,
        target: 'cloudflare',
        installRequested: true,
        installed: false,
        deploySource: SANDBOX_VISUAL_DEPLOY_SOURCES[sandboxState],
        unmetPrerequisites: ['sandbox_binding', ...body.unmetPrerequisites.filter((item) => item !== 'cloudflare_target')],
        workersPaidNote: 'Requires Workers Paid. Real containers run on your Cloudflare account; a typical session costs about 1 cent. ' +
          'Workspace checkpoints also need R2 enabled on the account; the free tier is enough.',
        checkpointsNote: null,
      });
    });
    // The Websites tab reads display-safe login rows for an Agent. The fixture
    // answers for its own Agents after the real authorization gate, so the
    // state renders without a Browserbase project or stored sign-ins.
    app.use('/admin/api/agents/:agentId/website-logins', async (c, next) => {
      await next();
      if (c.req.method !== 'GET' || (c.res.status !== 200 && c.res.status !== 404)) return;
      const agentId = decodeURIComponent(c.req.path.split('/')[4] ?? '');
      if (!VISUAL_FIXTURE_AGENT_IDS.includes(agentId)) return;
      const now = Date.now();
      c.res = Response.json({
        logins: agentId === 'agent_research' ? [
          {
            loginId: `wl_${'a1'.repeat(16)}`, host: 'acme.com', label: 'Acme team', ownerKind: 'team',
            method: 'credentials', username: 'qa-team@acme.com',
            lastUsedAt: now - 2 * 3_600_000, level: 'act', enabled: true,
          },
          {
            loginId: `wl_${'b2'.repeat(16)}`, host: 'admin.acme.com', label: 'Acme admin', ownerKind: 'team',
            method: 'handoff',
            lastUsedAt: now - 6 * 86_400_000, level: 'check', enabled: true,
          },
        ] : [],
      });
    });
    // Connected apps reads the person's Better Auth grants, which the fixture
    // has none of: the real route answers 409 after authorization, and the
    // fixture answers with sample apps in its place.
    const day = 86_400_000;
    const visualIso = (ago) => new Date(Date.now() - ago).toISOString();
    let visualConnectedApps = [
      { appId: 'https://chatgpt.com/oauth/client.json', name: 'ChatGPT', connectedAt: visualIso(9 * day), lastActiveAt: visualIso(2 * 3_600_000) },
      { appId: 'https://claude.ai/oauth/claude-code-client-metadata', name: 'Claude Code', connectedAt: visualIso(30 * day), lastActiveAt: visualIso(3 * day) },
      { appId: 'visual_client_pipeline', name: 'Pipeline reporter', nameFromApp: true, connectedAt: visualIso(day), lastActiveAt: null },
    ];
    app.use('/admin/api/connected-apps/*', async (c, next) => {
      await next();
      if (c.req.method !== 'POST' || c.res.status !== 409) return;
      const { appId } = await c.req.json();
      const found = visualConnectedApps.find((candidate) => candidate.appId === appId);
      visualConnectedApps = visualConnectedApps.filter((candidate) => candidate !== found);
      c.res = found ? Response.json({ name: found.name }) : Response.json({ error: 'not_found' }, { status: 404 });
    });
    app.use('/admin/api/connected-apps', async (c, next) => {
      await next();
      if (c.req.method !== 'GET' || c.res.status !== 409) return;
      c.res = Response.json({ apps: visualConnectedApps });
    });
    app.use('/admin/api/installation', async (c, next) => {
      await next();
      if (!updateState || updateState === 'unversioned' || c.res.status !== 200) return;
      const body = await c.res.json();
      c.res = Response.json({ ...body, identity: { version: '0.1.0', sourceCommit: 'a'.repeat(40) }, deployment: 'cloudflare' });
    });
    const managedConnectionProviders = options.notionSignIns
      ? visualNotionProviders(await loadTsModule('src/connections/managed.ts'))
      : undefined;
    app.route('/', createAdminRoutes({
      ...(managedConnectionProviders ? { managedConnectionProviders } : {}),
      updateFetch: async () => new Response('', { status: 404 }),
      store,
      settings,
      memory,
      routines,
      usage,
      work,
      slackState,
      snapshots,
      identity,
      slackCredentials,
      management,
      usageAdminUi: true,
      authService: {
        async authenticateRequest(request) {
          const cookies = request.headers.get('cookie') || '';
          const cookieAuthenticated = cookies.split(';').some((part) => part.trim() === `chickpea_visual_token=${adminToken}`);
          if (request.headers.get('authorization') !== `Bearer ${adminToken}` && !cookieAuthenticated) {
            throw new Error('Authentication unavailable.');
          }
          return {
            userId: owner.user.id,
            membershipId: owner.membership.id,
            organizationId: owner.membership.organizationId,
            role: principalRole,
            authenticatorKind: 'visual_slack_session',
            credentialId: 'visual_session',
            correlationId: 'visual_request',
            machine: false,
          };
        },
      },
      knownProviders: new Set(onboardingStage ? ['local-stub', 'cloudflare'] : ['local-stub']),
      runtimeDrain: async () => ({
        drained: true,
        categories: {
          admittedTurns: 0,
          claimedTurns: 0,
          pendingDeliveries: 0,
          executingRuns: 0,
          admittingOrRunningRoutineOccurrences: 0,
        },
      }),
      environmentStatus: async () => VISUAL_ENVIRONMENT_STATUS,
    }));

    const platformEnv = onboardingStage
      ? { AI: { models() { return []; } } }
      : undefined;
    let baseUrl = '';
    server = createServer((req, res) => void nodeRequest(req, res, app, baseUrl, platformEnv));
    await listen(server, port, host);
    const bound = server.address();
    if (!bound || typeof bound === 'string' || !loopbackAddress(bound.address)) {
      throw new Error(`Admin visual fixture did not bind to loopback: ${String(bound)}.`);
    }
    baseUrl = `http://${urlHost(bound.address)}:${bound.port}`;
    const control = await identity.getAuthControl();
    await identity.updateAuthControl({
      expectedRevision: control.revision,
      canonicalAdminOrigin: baseUrl,
    });
    await identity.updateOrganizationAuth({
      organizationId: owner.membership.organizationId,
      authMode: 'slack_active',
      canonicalAdminOrigin: baseUrl,
    });

    // The API endpoint is process-local. Slack secrets stay exclusively in the
    // encrypted fixture revision and never become runtime environment inputs.
    process.env.SLACK_API_URL = `${baseUrl}/__admin_visual_fixture/slack`;

    return {
      address: bound.address,
      adminToken,
      baseUrl,
      runtimeContract,
      onboardingStage,
      authStates: CANONICAL_SLACK_AUTH_VISUAL_STATES,
      connectorStates: CANONICAL_CONNECTOR_VISUAL_STATES,
      canonicalStates: CANONICAL_ADMIN_VISUAL_STATES,
      stateDbPath,
      stateDirectory,
      close: cleanup,
    };
  } catch (error) {
    await cleanup();
    throw error;
  }
}

function parseCliArgs(args) {
  const parsed = { host: DEFAULT_HOST, port: 0, runtimeContract: 'legacy', onboardingStage: null };
  for (let index = 0; index < args.length; index += 1) {
    const value = args[index];
    if (value === '--host') parsed.host = args[++index];
    else if (value === '--port') parsed.port = Number(args[++index]);
    else if (value === '--runtime-contract') parsed.runtimeContract = args[++index];
    else if (value === '--onboarding-stage') parsed.onboardingStage = args[++index];
    else if (value === '--update-state') parsed.updateState = args[++index];
    else if (value === '--sandbox-state') parsed.sandboxState = args[++index];
    else if (value === '--principal-role') parsed.principalRole = args[++index];
    else if (value === '--notion-sign-ins') parsed.notionSignIns = true;
    else throw new Error(`Unknown argument: ${value}`);
  }
  return parsed;
}

async function runCli() {
  const fixture = await startAdminVisualFixture(parseCliArgs(process.argv.slice(2)));
  // Install cleanup before advertising readiness to a parent process or user.
  let stopping = false;
  const cleanupOnExit = () => void fixture.close();
  process.once('exit', cleanupOnExit);
  const stop = async (signal) => {
    if (stopping) return;
    stopping = true;
    console.log(`\n${signal}: stopping Admin visual fixture…`);
    await fixture.close();
    process.off('exit', cleanupOnExit);
    process.exitCode = 0;
  };
  process.once('SIGINT', () => void stop('SIGINT'));
  process.once('SIGTERM', () => void stop('SIGTERM'));
  // npm can relay terminal shutdown as SIGHUP after its wrapper exits.
  process.once('SIGHUP', () => void stop('SIGHUP'));

  console.log(`Admin visual fixture: ${fixture.baseUrl}`);
  console.log(`Runtime contract: ${fixture.runtimeContract}`);
  if (fixture.onboardingStage) console.log(`Onboarding stage: ${fixture.onboardingStage}`);
  console.log(`Local login token: ${fixture.adminToken}`);
  console.log('Canonical states (production URLs; perform the listed UI actions after load):');
  for (const [name, state] of Object.entries(fixture.canonicalStates)) {
    const actions = state.actions.length ? ` then ${state.actions.join(' → ')}` : '';
    console.log(`  ${name}: ${fixture.baseUrl}${state.path}${actions}`);
  }
  console.log('Connector landing states:');
  for (const [name, state] of Object.entries(fixture.connectorStates)) {
    console.log(`  ${name}: ${fixture.baseUrl}${state.path}`);
  }
  console.log(`Temporary state: ${fixture.stateDbPath}`);
  console.log('Press Ctrl+C to stop and remove the temporary state.');
}

const invokedPath = process.argv[1] ? resolve(process.argv[1]) : '';
if (invokedPath && resolve(fileURLToPath(import.meta.url)) === invokedPath) {
  runCli().catch((error) => {
    console.error(`${basename(fileURLToPath(import.meta.url))}:`, error);
    process.exitCode = 1;
  });
}