---
title: Monday — workspaces, sharing and the internals (part 2)
---

# Monday — workspaces, sharing and the internals (part 2)

## What it is

This is part 2 of the [Monday](monday.md) page — a historical name for the board app now called Mission Control. Everything here was carried over unchanged in the rename.

## Where to find it

Under the current name: see [mission-control.md](mission-control.md), where this material now lives.

## How it behaves

### Workspaces functionality (post-Phase-4 fast-follow)

A pixel-near rebuild of monday's **Workspaces** across three surfaces. **(1) Left-nav header** — the plain `<select>` became a monday workspace block: a colored icon tile (initial + home glyph; tint from a new `workspaces.color` or a deterministic id-hash fallback), a name + switcher popover, and a **⋯ actions menu** (`WorkspaceMenu`) — Manage workspace, Edit workspace (icon-color + name + description modal), Sort workspace (a per-workspace localStorage display order), Add new workspace, Browse all workspaces, View archive/trash, and a confirm-gated Delete (disabled for the Main/last workspace). **(2) Manage-workspace page** (`/workspace/:id`, rebuilt `WorkspacePage`) — a banner (large icon, name, inline-editable **description**, Feedback (inert) / **Members** → `/users` / ⋯) over **Recents · Content · Permissions** tabs: Content is a boards+dashboards asset table (creator avatar, creation date, **Last modified**, folder, search, selection-delete, a real **Cleanup mode** that flags assets unmodified 30+ days; the AI-summary column is intentionally dropped); Recents reuses the Home recent-visit locators + favorite stars; Permissions sets the workspace access level (Open/Closed via `workspaces.kind`), manages **workspace owners** (people only — add/remove, last owner is guarded), and — for a **closed** workspace — the specific **members & teams with access** (an open workspace just reads "everyone in your account"), all via a searchable `PrincipalPicker` over the account directory + teams. **(3) User-management page** (`/users`, `UserManagementPage`) — the account directory as a full table with an editable **role** dropdown (`PATCH /members/:id`), a **Products** multi-select (real, seeded with `work_management`), **status**, **Teams** chips, joined/invited dates, an **Invite** dialog (reuses the member invite), and a **Manage teams** modal over a real **Teams** subsystem.

Backend (`monday-back`): migration **0012** adds `workspaces.color` + `description` (flow through `/nav/tree`); **0013** adds `boards.updatedAt` + `dashboards.updatedAt` ("Last modified" — auto-bumped via Drizzle `$onUpdateFn` on row edits and via `touchBoard()` at the `publish()` realtime chokepoint on content edits); **0014** adds the `teams` + `team_members` tables + a `teamsRoutes` module (account-scoped CRUD + membership); **0015** adds `users.products` (jsonb) + extends `PATCH /members/:id` (role/products) and the member payload (products, joinedAt, invitedAt). `PATCH /workspaces/:id` gained color/description/kind. Migration **0016** adds the `workspace_principals` table (polymorphic user|team rows, `owner`|`member` role) backing the Permissions tab — seeded so every workspace keeps a real owner (the creator on `POST /workspaces` + register; the account's earliest user in the migration backfill) — with `GET/POST/DELETE /workspaces/:id/principals` (owners-are-users, never-zero-owners 409, and account-isolation guards). Boards gained `GET /boards/archived` + `/boards/trash` listings and a `POST /boards/:id/restore` (clears archived + soft-delete; ownership via the board→workspace→account join). Pure Content-tab + Permissions helpers (asset shaping + staleness; owner-removal + picker-candidate filtering) are unit-tested (`tests/unit/plugins/mission-control/manage-workspace.test.ts`, `workspace-permissions.test.ts`); every route has bun coverage (the backend package’s workspace-permissions bun test, not tracked in this repo). Design specs `docs/superpowers/specs/2026-06-08-monday-workspaces-design.md`, `2026-06-08-monday-workspace-permissions-design.md`.

### Kanban card actions + "⋯" menu (post-Phase-4 fast-follow)

Hovering a **Kanban** card reveals monday's two actions in the top-right: a **pencil** (opens the item card) and a **⋯** overflow menu (`features/board/KanbanCardMenu.tsx`, on the shared `Popover`). The menu mirrors monday's real order: **Open task · Move to ▸ · Duplicate ▸ · Copy name · Copy task link · Add subitem · Customize cards ▸ · Archive · Delete** (Delete confirms first). "Move to" and "Duplicate" open **right-side flyout submenus** (`features/board/CardMenuFlyout.tsx` — `SubmenuRow`/`FlyoutItem`, portaled out of the menu so they aren't clipped, flipping left near the screen edge): **Move to → Move to group** (lists the board's other groups → `patchItem({groupId})`) or **Move to board** (lists other boards from `/nav/tree` → a new `POST /items/:id/move-to-board`, which reassigns the board + a target group and drops the board-scoped column values); **Duplicate → without / with updates** (the existing `POST /items/:id/duplicate`, with `?includeUpdates=true` also cloning the conversation thread). **Copy name** / **Copy task link** write to the clipboard (the link is `#/board/<id>?item=<id>`; `BoardView` opens an item from a `?item=` param on load).

**Subitems** are real item rows tagged with `parent_item_id` (no migration — the column already existed). `GET /boards/:id/full` now returns top-level rows in `items` and child rows separately in `subitems`, so every other view is unchanged; the Kanban card renders its subitems as **nested name-only mini-cards** with a footer **count badge** and an inline **"+ Add sub-task"** composer (`POST /items/:id/subitems`), which the menu's **Add subitem** row opens and focuses. Backend behavior is covered by `monday-back`'s item-card-actions bun test (a package not tracked in this repo); the subitem grouping helper is unit-tested (`groupSubitemsByParent` in `tests/unit/plugins/mission-control/kanban-data.test.ts`).

### Account & passwords (parity fast-follow)

The login screen gained a **"Forgot password?"** link: enter your email and you get a **neutral** confirmation ("if that email exists, we've sent a reset link") that never reveals whether the address is registered. Because the plugin runs on an **in-memory router** — email links can't deep-link into it, the same constraint that puts the invitation-accept pages on the backend — the reset itself happens on a **backend-served HTML page** the emailed link points to. The reset token is **single-use, expires in one hour**, and a fresh request **supersedes** any earlier link; completing a reset **signs out all existing sessions**.

Signed in, the top-right **account menu** (the avatar) adds **"Change password"** — a modal that requires your **current password** and a new one of **at least 8 characters**. Changing it **signs out all your OTHER devices** while the current one stays signed in.

**Logout is now server-side** — signing out revokes the session on the backend too (not just a local token wipe), so it takes effect even after a long idle. (There is still **no email verification** on signup — accounts are usable immediately.)

### Board sharing & private-board enforcement (parity fast-follow)

The board header gained a **Share** button → a modal with a **copyable join link** and the board's **member list**. The join link points at a **backend-served join page** (same pattern as reset/invite — the in-memory router can't be deep-linked); another user on the **same account** signs in there and is added to the board as an **editor**. Private boards are now **enforced end-to-end**: only a board's **members** — owner / editor / viewer principals, held directly or via a **team** — can see a board in listings or open it. Only the **board owner** can **mint, rotate, or revoke** the share link or **add/remove members**.

### Workspace invite link (parity fast-follow)

The workspace **⋯ menu** gained **"Copy invite link."** It **reuses the workspace's existing invite link** and only **mints a new one when none exists yet**, so the link stays stable across copies. Only **workspace owners** can manage the link.

### Local SQLite mirror (Phase 5A)

Phase 5A adds a **local SQLite mirror** of Monday board data for offline access and as the foundation for automation (Phase 5B), CLI (5C), inbox (5D), and UI (5F). Five tables — `pm_boards`, `pm_items`, `pm_columns`, `pm_column_values`, `pm_sync_state` — cache the data returned by `GET /boards/:id/full`. These tables are a **read cache**, not the authority; the Monday backend (Postgres) remains the source of truth.

A background **sync service** (`pm-sync-service`) runs on a 5-minute heartbeat, re-fetching any board whose `last_sync_at` is older than 5 minutes. Each sync writes all tables in a **single SQLite transaction** (no partial state) and soft-deletes items no longer present in the response. A WebSocket connection to the Monday backend's `/realtime` endpoint listens for `board-changed` events and triggers an immediate re-sync of the affected board, so changes made in the Monday UI appear locally within seconds.

The sync service is **gated** on `mondayEnabled` + `mondayRefreshToken` — if Monday is disabled or the user hasn't logged in, the service does nothing. Auth is handled by a main-process token manager (`pm-auth`) that reads the encrypted refresh token from settings, deduplicates concurrent refresh calls, and retries on 401.

### Board automation engine (Phase 5C)

Phase 5C adds an **event-driven automation engine** that fires action chains when board data changes. The sync service snapshots board state before each sync, diffs after, and emits typed `BoardEvent` objects (7 event types: `item_created`, `item_archived`, `item_status_changed`, `item_assigned`, `item_due_date_passed`, `item_moved_to_group`, `item_column_changed`). The automation engine matches events against user-defined rules in `pm_board_automations` and dispatches action chains through the existing Omniscio automation dispatcher.

**Safety controls:** master toggle (`pmAutomationsEnabled`, default off), event dedup (SQLite `pm_automation_processed_events` with 7-day retention), 5-second per-rule cooldown, stop-on-match, and always-persist run records (even on failure).

A **due-date checker** (`pm-due-date-checker`) runs every 15 minutes, scans date columns for overdue items, and feeds `item_due_date_passed` events into the engine. Two **v2 actions** — `pm.move-item` and `pm.set-column-value` — call the Monday backend REST API via `pmAuthedFetch`.

**CLI routes:** `GET/POST/PATCH/DELETE /pm/automations` — all auth-gated, rate-limited, Zod-validated. **IPC:** 6 channels for CRUD + presets + run history, all Zod-validated with push on mutation. Five **preset templates** (status-done-notify, new-item-assign, due-date-overdue-move, assigned-to-me-inbox, blocked-create-session).

**Per-rule approval:** rules created via CLI default to `approval_status = 'pending_approval'` and are excluded from `listActiveBoardAutomations()` until approved. A `pm-automation-approval` inbox source surfaces pending rules in the unified inbox (gated on `mondayEnabled` + `pmAutomationsEnabled`). IPC channels: `pm:board-automation:pending-list` (cross-board pending query) and `pm:board-automation:set-approval` (approve/reject).

**Feature gating:** registered as `'pm-automations'` in `UNRELEASED_FEATURES` with status `'in-development'`. No renderer UI yet (that is Phase 5F).

- **Contract:** `pm-automation-contract.md`
- **Key files:** `src/main/services/pm/automation/pm-automation-service.ts` (engine), `pm-board-diff.ts` (diff), `pm-due-date-checker.ts` (cron), `pm-automation-queries.ts` (CRUD), `pm-automation-presets.ts` (presets), `src/main/ipc/pm-automation-handlers.ts` (IPC), `src/main/services/cli/cli-server-pm-automation-routes.ts` (CLI)

### PM event channel bridge

Bridges PM board events into the **channel automation engine** (`automations` table), adding a fourth trigger type `on_pm_event` alongside `on_message`, `manual`, and `on_session_state`. This lets users create channel automations like "when a task goes overdue, nudge me in inbox" using the same rules UI as any other automation.

**How it works:** the PM sync service calls `bridgePmEventsToChannelAutomations(events)` fire-and-forget after each sync. The bridge checks both `automationsEnabled` and `pmAutomationsEnabled`, queries `listEnabledPmEventAutomations()` (channel automations with `triggerType: 'on_pm_event'`), matches events against each rule's `PmEventConfig` (event type + board scope + optional board/column filter), enforces a 5-second per-rule cooldown, deduplicates within a batch using `bridge:`-prefixed keys, and dispatches through `dispatchActionChain()` passing the `BoardEvent` as v2 trigger data so actions can read `itemId`/`boardId` from `ctx.trigger`. Errors are caught per-automation and never propagate to the sync pipeline.

**Three v2 actions** for PM-triggered automations:

- `pm.update-linked-status` — updates a column value on the triggering item (reads `itemId` from `ctx.trigger`; skips gracefully if no trigger context)
- `pm.add-comment` — adds a comment/update to the triggering item (same trigger skip behavior)
- `pm.create-item` — creates a new item on a specified board (does NOT require trigger context)

**Four preset templates** in the channel automation presets: Overdue Task Nudge (`pm-overdue-nudge`), Status Change Update (`pm-status-changed-update`), Comment on New Items (`pm-new-item-comment`, v2), Assignment Notification (`pm-assignment-alert`).

**Architecture notes:** the bridge is SEPARATE from the board automation engine (Phase 5C) — board automations use `pm_board_automations` table, channel bridge uses the `automations` table with `pm_event_config` column. The `dispatchActionChain` function accepts an optional `v2TriggerData` parameter that populates `trigger` on the v2 action context.

- **Contract:** `pm-automation-contract.md` (§ PM event channel bridge)
- **Key files:** `src/main/services/pm/pm-event-channel-bridge.ts` (bridge), `src/main/services/automation/v2/dispatcher.ts` (v2TriggerData), `src/main/services/automation/v2/actions/pm-update-linked-status.ts`, `pm-add-comment.ts`, `pm-create-item.ts` (actions), `src/main/services/automation/legacy/automation-presets.ts` (presets), `src/main/db/migrations/20260720203018-add-pm-event-config-to-automations.ts` (migration)

### Workflow Engine Nodes (Phase 5H)

Phase 5H integrates PM boards into the **Workflow Engine** with a poll-based trigger and five action nodes. The trigger watcher polls all synced boards every 60 seconds, computing deltas to fire workflows. The actions proxy writes through to the Monday backend via `pmAuthedFetch`.

**Trigger node** (`trigger.pm_item`): configurable event filter (`item_status_changed`, `item_assigned`, `item_created`, `item_overdue`), optional `boardId`/`columnId` scoping. First tick establishes a baseline (no false-fires on start). Dedup by `pm-trigger:boardId:itemId:event:value`. Kill switch: `AMC_DISABLE_PM_WORKFLOW_TRIGGER`.

**Action nodes:**

- `action.pm_create_item` — create an item on a board (title, group, column values via JSON)
- `action.pm_transition` — set a column value (status transition)
- `action.pm_comment` — add an update/comment to an item
- `action.pm_assign` — assign a person to an item (personsAndTeams column)
- `action.pm_search` — search items by name/text (reads from local SQLite mirror)

**Architecture:** nodes are auto-registered via Vite's `import.meta.glob('./nodes/*-node.ts')`. Side effects route through `WorkflowEnginePorts` (never raw DB/fetch from a node). `'pm'` is added to `WORKFLOW_TRIGGER_SOURCES` and renders as "PM Board" in the run-history viewer.

- **Contract:** `pm-workflow-nodes-contract.md`
- **Key files:** `src/main/services/workflow-engine/pm-item-trigger.ts` (watcher), `src/main/services/workflow-engine/nodes/pm-*-node.ts` (6 node files), `src/main/services/workflow-engine/ports.ts` (port implementations), `src/main/services/workflow-engine/types.ts` (port interface)

### Inbox source (Phase 5D)

Phase 5D adds a **"board" inbox source** that surfaces Monday board events as
unified inbox rows. Six event types create rows: **assigned to you**,
**mentioned in an update**, **status changed on your item**,
**due date approaching** (1 day), **due date passed**, and
**automation fired**. Events are detected locally from the Phase 5A SQLite
mirror tables on each sync -- no Monday API polling beyond what the sync
service already does.

**How to use it:** enable **Settings → Features -> "Monday board events
in inbox"** (on by default when Monday is enabled). Events appear as inbox rows
with a status-coloured dot routed through the `status-*` tokens (so custom themes
recolour it): the **needs-you** attention colour for most events, and the
**error** colour for overdue items. Click a row to see the item
name, board name, and a **"Go to Board"** button that opens the Monday panel.
Dismiss like any inbox item (far-future snooze with undo).

**How it works (internals):** the main-process `pm-inbox-event-detector`
subscribes to the `pm:sync-updated` push. On each sync it queries the pm_*
mirror tables, resolves the current user (cached from `GET /auth/me`), detects
which events are active, upserts them into `pm_inbox_events` (dedup on
`event_type + item_id`), soft-deletes cleared events, and emits
`inbox-pm/updated` if changed. The renderer's `pm-inbox-store`
(`createDerivedInboxStore`) listens for the push and projects events as
`integration: 'board'` items in the unified inbox. Dismiss uses the universal
far-future snooze pattern (kind `'board'`). The detail pane routes through
`InboxSummaryRedirectPane`.

### Data import/export (Phase 5G)

Two CLI routes let agent sessions move data between files and Monday boards:

- **Export** (`GET /pm/data/export/:boardId?format=csv|xlsx|json|txt`) — fetches the board from the Monday backend, transforms to the requested format, writes a temp file, returns the path. Includes CSV formula injection defense.
- **Import** (`POST /pm/data/import { filePath, newBoardName?, boardId?, format? }`) — reads a local file (CSV/XLSX/JSON/TXT), auto-detects format from extension, infers column types (14 types: status, priority, date, numbers, email, phone, link, checkbox, dropdown, rating, long-text, text, plus header-name heuristics), detects groups from a "Group"/"Section" column, and creates boards/groups/columns/items on the Monday backend. Caps: 5000 rows, 50MB file size, 20K chars per cell.

Works independently of the Phase 5A local mirror — calls the Monday backend REST API directly via `pm-auth`.

- **Contract:** `pm-data-transfer-contract.md`
- **Key files:** `src/main/services/pm/data-transfer/` (export, import, column inference, xlsx bridge, types), `src/main/services/cli/cli-server-pm-data-transfer-routes.ts`

### Data Import (Phase 5F)

A one-time Python tool (`monday-exports/import.py`) that reads Monday.com xlsx board exports and imports them into the Monday clone backend via REST API. Two-pass workflow:

1. **Discover mode** (`python import.py --discover`): reads xlsx files from `monday-exports/boards/`, infers column types from cell values, writes `column-map.json`.
2. **Import mode** (`python import.py --import --email X --password Y`): reads `column-map.json`, creates boards/groups/columns/items/values on the backend via REST.

Successfully imported 29 boards with ~1,900 items. The tool handles all 18 column types (status, date, timeline, people, numbers, dropdown, files, rating, text, long-text, checkbox, link, email, phone, country, world-clock, week, priority). Design spec: `docs/superpowers/specs/2026-07-14-monday-data-import-design.md`.

### Board Templates (Phase 5C)

Save a board's full structure as a reusable template and deploy it into new
boards. Right-click a board in the left nav → "Save as template" opens a
dialog (name, description, shared/private visibility, include-items toggle).
Templates are stored on the backend and browsable in a **Template Gallery**
(opened from a "T" button in the left nav header) with three sections:
built-in, team (shared), and private.

**Deploy preview** shows the template's groups, columns, views, and
automations. Each automation has a toggle and a **cost badge** (`$`) on
actions that invoke AI or spawn sessions (detected by `isCostBearingAction`
scanning the action chain for `ai_classify`, `ai_generate`, `spawn_session`).
Users can disable cost-bearing automations before deployment.

**Template editor** lets owners update name, description, icon, visibility,
and the template's structure (rename/remove groups, columns, views,
automations). Changes autosave with an 800ms debounce.

**Portability**: column and group references inside automations are replaced
with stable `templateKey`s (`col_1`, `grp_1`) on save and remapped back to
real IDs on deploy (matching by title, falling back to position).

- **Contract:** `pm-board-templates-contract.md`
- **Key files:** `src/main/services/pm/templates/pm-template-portability.ts` (portability engine), `src/main/services/cli/cli-server-pm-template-routes.ts` (7 CLI routes), `src/plugins/monday/web/features/templates/` (gallery, deploy preview, save dialog, editor)

### Calendar Overlay (Phase 5G)

PM due dates and sprint timelines appear as a visual overlay on the Calendar
alongside Google Calendar events. Items with `date` or `timeline` column
values render as CalendarEvents, color-coded by board with overdue items
highlighted in `status-error` red.

**How to use it:** enable **Settings -> Monday -> "PM due dates in Calendar"**
(on by default when Monday is enabled). Due dates appear as single-day or
timed events; sprint timelines appear as spanning bars across the calendar.
Click any PM event to see the board name, date info, overdue warning, and an
**"Open in PM"** button that navigates to the Monday panel.

**How it works (internals):** the `CALENDAR_LIST_PM_EVENTS` IPC handler queries
the pm_* SQLite mirror tables (joining pm_column_values with pm_items,
pm_columns, pm_boards, pm_groups), filters for `date` and `timeline` column
types, then transforms matching rows into CalendarEvent objects with PM
metadata fields (`source: 'pm'`, `pmBoardId`, `pmBoardColor`, `pmOverdue`,
`pmType`). The calendar store fetches PM events in parallel with Google Calendar
via `Promise.all` and listens to `pm:sync-updated` for live updates. The
`resolveEventColor()` dispatcher routes PM events to `getPmEventColor` (board
color or overdue red) while Google events use the existing cascade.

- **Contract:** `pm-calendar-overlay-contract.md`
- **Key files:** `src/main/services/pm/calendar/pm-calendar-queries.ts` (SQLite query), `src/main/services/pm/calendar/pm-calendar-service.ts` (transform), `src/main/ipc/calendar-handlers.ts` (handler), `src/renderer/src/features/calendar/calendar-colors.ts` (resolveEventColor)

### Session Linking

Sessions can be linked to PM items bidirectionally. A linked session shows a
**PM item chip** in the session header (accent-colored, displays the item name).
Clicking the chip navigates to the Monday board; right-clicking unlinks.

**Linking paths:**

- At session creation: pass `pmItemId` to `createSessionWithPrompt` (the `sessions.pm_item_id` FK is set in the INSERT)
- After creation: the `PM_SESSION_SET_LINK` IPC channel sets or clears the link
- From a PM item: `POST /pm/items/:id/start-session` CLI route spawns a new session pre-linked to the item, with PM context injected via `pmSessionContextProvider`

**Status sync:** `registerPmAgentLink()` listens to `sessionEvents.on('statusChanged')`.
When a linked session changes status, it maps to a closed vocabulary
(`idle` / `running` / `needs_you` / `finished` / `error`) and emits a
`PM_SESSION_LINK_CHANGED` push event. The chip subscribes via `usePushListener`
(300ms debounce) for live updates.

**Data model:** the FK is `sessions.pm_item_id` (nullable). A companion
`pm_item_links` row (entity_type = 'session') keeps the link browsable from
the item side. Both are written atomically in a SQLite transaction.

- **Contract:** `pm-session-link-contract.md`
- **Key files:** `src/main/services/pm/pm-agent-link.ts` (core service), `src/main/ipc/pm-session-link-handlers.ts` (IPC), `src/renderer/src/features/sessions/PmItemChip.tsx` (UI chip), `src/main/services/cli/cli-server-pm-routes.ts` (CLI route)

## For agents

### CLI surface (Phase 5B)

Phase 5B adds **10 CLI routes** under `/pm/` on the control server (`127.0.0.1:19519`) so any agent session can read and write board data programmatically. Read endpoints serve from the local SQLite mirror tables; write endpoints proxy through to the Monday backend REST API via `pm-auth`.

**Read routes** (bearer + read-budgeted 60/min): `GET /pm/boards` (list), `GET /pm/boards/:id` (detail with groups + columns), `GET /pm/boards/:id/items` (items with enriched column values and filters for status/assignee/overdue/group/archived/limit), `GET /pm/search?q=` (cross-board item search by name or column text).

**Write routes** (bearer + mutation-limited 10/min, apply-immediately): `POST /pm/boards/:id/items` (create item), `PATCH /pm/items/:id` (update name/group), `PUT /pm/items/:id/columns/:colId` (set cell value), `DELETE /pm/items/:id`, `POST /pm/boards/:id/columns` (add column). All writes are proxied to the Monday backend and require a valid Monday session.

**Workspace sync route** (bearer + mutation-limited): `POST /pm/workspaces/:id/sync` fetches all boards in a workspace from the Monday backend and syncs each one into the local mirror. Continues past individual board failures and returns `{ synced, failed, total }`. This is the entry point for initial workspace-level sync — callers no longer need to know individual board IDs.

All routes are feature-gated on `mondayEnabled` (404 when off). Route handlers, Zod schemas, and the backend proxy helper live in `cli-server-pm-routes.ts`; read queries are in `pm-queries.ts`. The spoke doc for agents is `pm.md`.

### How it works

Auth is **unified with Omniscio's Global Auth** (Firebase) — users sign in once via Omniscio and get access to all boards; there is no separate Monday login. The plugin sends Firebase ID tokens (obtained from the main process via IPC) as Bearer auth to a **separately-hosted backend** (`amc-back/`, a Bun + Elysia + Postgres + Drizzle service; Omniscio does not bundle it). The backend URL is **hardcoded** in the plugin (`web/lib/runtime-config.ts`) — a fixed deploy target, not a user setting. The backend verifies tokens via `admin.auth().verifyIdToken()` (dual-auth: also accepts legacy JWT during migration). Firebase tokens auto-refresh (1hr expiry, handled by the main process).

### Code references

- **Isolated app:** `src/plugins/mission-control/web/` — `amc/AmcMondayApp.tsx` (composition root), `lib/api.ts` (authedFetch), `lib/runtime-config.ts`, `lib/session-bridge.ts` (Firebase auth bridge). Framework-free shared code in `src/plugins/monday/shared/` (`@monday/shared`). Isolated from Omniscio tooling — own `tsconfig.json` (`npm run typecheck:monday`), scoped Tailwind (`tailwind.monday.config.js` → `npm run build:monday-css`), excluded from Omniscio's tsconfig/eslint/prettier.
- **Omniscio glue (normal tooling scope):** `src/renderer/src/features/mission-control/` — `MondayPanel.tsx` (panel host) + `vendored-monday.d.ts` (typecheck boundary). Vite aliases `@monday/web` + `@monday/shared` in `electron.vite.renderer-shared.ts`.
- **Sentinel + registry:** `MONDAY_PROJECT_ID = '__monday__'` in `src/shared/virtual-project-ids.ts`; the `id: 'monday'` entries in `src/shared/integration-registry.ts` + `src/renderer/src/integrations/ui-registry.ts` (`panelOwnsLayout: true`, lazy `MondayPanel`).
- **Settings:** `mondayEnabled` in `src/shared/types/settings/mission-control-settings.ts` (+ the Zod slice). Auth is via Omniscio's global Firebase auth — no Monday-specific credential.
- **IPC:** `monday:get-firebase-token` channel (`src/main/ipc/mission-control-auth-handlers.ts`) provides Firebase ID tokens to the plugin.
- **Backend:** the `amc-back/` folder in this repo (moved in 2026-09-27). Deployed to Cloud Run at `amcback.jls.dev/monday` (service `amc-backend`, GCP project `amc-499110`) with `bash amc-back/deploy.sh`.

## Related

- [Monday](monday.md) — part 1, and the note explaining the rename.
- [mission-control.md](mission-control.md) — the same feature under its current name.

