Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. Management & Multi-Tenancy
Overview
The company is the first-order business entity in Paperclip. Every other business record (agents, goals, projects, issues, costs, activity) is scoped to exactly one company, and a single deployment can run many companies with complete data isolation. This feature covers the company lifecycle, per-company configuration, multi-company data boundaries, and company portability (export/import of entire organizations).
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Create/list/get/update/archive companies; switch between them; import/export orgs |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Operates strictly within its own company; must never see another company's data |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support creating a company with a name, description, status, and issue prefix/counter. | Must | The system shall support creating a company with a name, description, status, and issue prefix/counter. |
| FR-02MustThe system shall enforce that every business record belongs to exactly one company and that company boundaries are checked on every fetch/mutation. | Must | The system shall enforce that every business record belongs to exactly one company and that company boundaries are checked on every fetch/mutation. |
| FR-03MustThe system shall support listing, getting, updating, and archiving companies. | Must | The system shall support listing, getting, updating, and archiving companies. |
| FR-04Mustpaused \ | Must | The system shall maintain company status as active \ | paused \ | archived with pause reason and paused-at tracking. |
| FR-05ShouldThe system shall support per-company branding (brand color, logo) and feedback-sharing consent fields. | Should | The system shall support per-company branding (brand color, logo) and feedback-sharing consent fields. |
| FR-06ShouldThe system shall support exporting and importing entire companies (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Should | The system shall support exporting and importing entire companies (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
| FR-07ShouldThe system shall support per-company attachment size limits (`attachment_max_bytes`). | Should | The system shall support per-company attachment size limits (attachment_max_bytes). |
| FR-08MayThe system shall support per-company board-approval requirement flag for new agents (`require_board_approval_for_new_agents`). | May | The system shall support per-company board-approval requirement flag for new agents (require_board_approval_for_new_agents). |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustAgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. API keys must not access other companies' data; cross-company reads/writes must be rejected. | Must | Security | AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. API keys must not access other companies' data; cross-company reads/writes must be rejected. |
| NFR-02MustEvery company mutation must write an `activity_log` entry attributed to an actor. | Must | Auditability | Every company mutation must write an activity_log entry attributed to an actor. |
| NFR-03ShouldCompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. CRUD must meet the p95 < 250 ms latency target at 1k tasks/company. | Should | Performance | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. CRUD must meet the p95 < 250 ms latency target at 1k tasks/company. |
Constraints
- Single-tenant deployment with a multi-company data model (one deployment, many isolated companies).
- BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. has full read/write across all companies in the deployment.
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-01MustThe system shall support creating a company with a name, description, status, and issue prefix/counter.
- Given a board operator
- When they create a company with required fields
- Then a company row exists with status
active, a generated issue prefix, and a zeroed issue counter
- FR-02MustThe system shall enforce that every business record belongs to exactly one company and that company boundaries are checked on every fetch/mutation.
- Given two companies A and B and an agent key scoped to A
- When the agent attempts to read or mutate a B-scoped entity
- Then the request is rejected with
403/404and no B data is returned
- FR-04Mustpaused \
- Given an active company
- When it is paused
- Then
status=paused,paused_atis set, and a pause reason is recorded
- FR-06ShouldThe system shall support exporting and importing entire companies (agents, skills, projects, routines, issues) with secret scrubbing and collision handling.
- Given an exported company package
- When it is imported into a fresh deployment
- Then agents, skills, projects, routines, and issues are recreated with secrets scrubbed and id collisions handled
- NFR-01MustAgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. API keys must not access other companies' data; cross-company reads/writes must be rejected.
- Given an agent API key for company A
- When it calls any endpoint scoped to company B
- Then the request is denied before any company B data is exposed
Conflicts
None identified yet.
Open Questions
- What is the exact set of entities included in company export/import, and how are secret references resolved on import?
Specification: CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. Management & Multi-Tenancy
Overview
Companies are top-level isolated tenants. Each company row carries configuration, branding, budget, and issue-counter state. CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. scoping is enforced by a company_id foreign key on every business table plus boundary checks in routes/services. Portability is provided by a markdown-first package contract rooted at COMPANY.md with .paperclip.yaml sidecar.
Architecture
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. ──► /api/companies (CRUD + archive)
│
▼
companies table ──► company_id FK on agents, goals, projects,
│ issues, cost_events, activity_log, secrets, ...
▼
per-company isolation enforced in services/routes
Data Models
companies
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, not null | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. identifier |
| name | text | not null | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. name |
| description | text | null | Optional description |
| status | enum | not null | active \| paused \| archived |
| pause_reason | text | null | Reason when paused |
| paused_at | timestamptz | null | When paused |
| issue_prefix | text | not null | Prefix for issue identifiers (e.g. PAPA) |
| issue_counter | int | not null | Monotonic per-company issue counter |
| budget_monthly_cents | int | not null, default 0 | Monthly budget |
| spent_monthly_cents | int | not null, default 0 | Month-to-date spend |
| attachment_max_bytes | int | not null | Per-company attachment size cap |
| require_board_approval_for_new_agents | boolean | not null, default false | Governance flag |
| brand_color / branding | - | null | Branding fields |
API Contracts
POST /companies
Creates a company. Returns the created company.
GET /companies, GET /companies/:companyId, PATCH /companies/:companyId, PATCH /companies/:companyId/branding, POST /companies/:companyId/archive
Standard CRUD + branding + archive. All under /api. BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based.-only.
CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. import/export
Preview/apply endpoints for portable company packages (companies.ts, company-import-paths.ts, company-portability.ts service).
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Validation error |
| 403 | UNAUTHORIZED | Caller lacks company access |
| 404 | NOT_FOUND | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. not found |
Sequences
Archive a company
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → PATCH/archive → companies(status=archived) → activity_log
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Tenancy model | Single-tenant deployment, multi-company data model | One deployment runs many isolated companies without multi-tenant infra |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. identifiers | Per-company prefix + counter | Human-readable, collision-free within a company |
Risks and Unknowns
- Export/import secret resolution and collision handling semantics need precise documentation.
Out of Scope
- Cloud-grade multi-tenant infra or centralized policy overlays (Pro/Enterprise).
requirements
- What is the exact set of entities included in company export/import, and how are secret references resolved on import?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Org Chart & Agents
Overview
Agents are the AI employees of a company. They have roles, titles, reporting lines, capabilities, budgets, permissions, and adapter configuration. Agents form a strict reporting tree (reports_to nullable root) with no multi-manager reporting and no cycles. This feature covers agent lifecycle, org structure, adapter/API-key configuration, pause/resume/terminate, and the board approval flow for hiring.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Hire (directly or via approval), configure, pause/resume/terminate agents; view org chart |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Proposes strategy; delegates work down the tree after board approval |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Has an identity, reporting manager, budget, and an adapter that receives heartbeats |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support creating an agent scoped to a company with name, role, title, adapter type/config, and `reports_to`. | Must | The system shall support creating an agent scoped to a company with name, role, title, adapter type/config, and reports_to. |
| FR-02MustThe system shall enforce a strict tree org graph: `reports_to` nullable root, agent and manager in the same company, and no cycles. | Must | The system shall enforce a strict tree org graph: reports_to nullable root, agent and manager in the same company, and no cycles. |
| FR-03Mustpaused \ | Must | The system shall manage agent status (active \ | paused \ | idle \ | running \ | error \ | pending_approval \ | terminated) with the defined transition rules. |
| FR-04MustThe system shall reject resuming a `terminated` agent. | Must | The system shall reject resuming a terminated agent. |
| FR-05MustThe system shall create agent API keys hashed at rest, showing plaintext once at creation, with revocation. | Must | The system shall create agent API keys hashed at rest, showing plaintext once at creation, with revocation. |
| FR-06MustThe system shall support pause/resume/terminate actions (board-only for terminate). | Must | The system shall support pause/resume/terminate actions (board-only for terminate). |
| FR-07ShouldThe system shall support a hire-agent approval flow requested by an agent and decided by the board. | Should | The system shall support a hire-agent approval flow requested by an agent and decided by the board. |
| FR-08Shouldfat`), permissions JSONB, and optional cheap-model profile lane. | Should | The system shall support per-agent runtime config, context mode (thin \ | fat), permissions JSONB, and optional cheap-model profile lane. |
| FR-09ShouldThe system shall track `last_heartbeat_at` and budget spent per agent. | Should | The system shall track last_heartbeat_at and budget spent per agent. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustOnly hashed API keys are stored; plaintext is shown once at creation. | Must | Security | Only hashed API keys are stored; plaintext is shown once at creation. |
| NFR-02MustAll agent mutations (hire, pause, resume, terminate, key ops) write `activity_log`. | Must | Auditability | All agent mutations (hire, pause, resume, terminate, key ops) write activity_log. |
Constraints
terminatedis irreversible.- AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. and manager must be in the same company.
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-02MustThe system shall enforce a strict tree org graph: `reports_to` nullable root, agent and manager in the same company, and no cycles.
- Given an existing reporting tree
- When a cycle-creating
reports_tois attempted - Then the update is rejected with a
422/409and the tree remains acyclic
- FR-03Mustpaused \
- Given an agent in
running - When it is paused mid-run
- Then the run is gracefully cancelled then force-killed and status becomes
paused
- Given an agent in
- FR-05MustThe system shall create agent API keys hashed at rest, showing plaintext once at creation, with revocation.
- Given a new API key request
- When the key is created
- Then only the hash is persisted and the plaintext is returned exactly once
- NFR-01MustOnly hashed API keys are stored; plaintext is shown once at creation.
- Given the
agent_api_keystable - When inspected
- Then no row contains recoverable plaintext key material
- Given the
Conflicts
None identified yet.
Open Questions
- What adapter types are fully supported in V1 and which are plugin-only?
Specification: Org Chart & Agents
Overview
Agents live in a company-scoped strict reporting tree. Each agent carries adapter/runtime configuration, a status state machine, a monthly budget, and one or more hashed API keys. Hiring may go through a board approval. Built-in adapters cover process, http, local CLI tools (claude/codex/gemini/opencode/pi/cursor), and the OpenClaw gateway; external adapters load via the plugin flow.
Architecture
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. ──► /api/agents (CRUD, pause/resume/terminate, keys)
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. ──► /api/agents/:id/heartbeat/invoke (via API key)
│
▼
agents table (company_id, reports_to, status, adapter_config, budget)
│
▼
agent_api_keys (hashed) + approvals(hire_agent)
Data Models
agents
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. id |
| company_id | uuid | FK companies, not null | Scoping |
| name / role / title / icon | text | - | Identity |
| status | enum | not null | active \| paused \| idle \| running \| error \| pending_approval \| terminated |
| reports_to | uuid | FK agents, null | Manager (nullable root) |
| adapter_type | text | - | process, http, claude_local, codex_local, etc. |
| adapter_config | jsonb | not null | AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin).-specific config |
| runtime_config | jsonb | default {} |
Runtime policy incl. modelProfiles.cheap |
| context_mode | enum | default thin |
thin \| fat |
| budget_monthly_cents / spent_monthly_cents | int | not null | Budgets |
| permissions | jsonb | default {} |
Permissions |
| last_heartbeat_at | timestamptz | null | Liveness |
agent_api_keys
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| agent_id / company_id | uuid | FK, not null | Scoping |
| name | text | not null | Label |
| key_hash | text | not null | Hashed key (plaintext shown once) |
| last_used_at / revoked_at | timestamptz | null | Usage/revocation |
API Contracts
POST /companies/:companyId/agents, GET /agents/:agentId, PATCH /agents/:agentId
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. CRUD.
POST /agents/:agentId/pause | /resume | /terminate
Lifecycle transitions (terminate board-only).
POST /agents/:agentId/keys, POST /agents/:agentId/heartbeat/invoke
Key management and heartbeat invocation.
Error Responses
| Status | Code | Description |
|---|---|---|
| 409 | CONFLICT | Invalid status transition or cycle in reports_to |
| 422 | RULE_VIOLATION | e.g. resuming a terminated agent |
Sequences
Hire via approval
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. → approvals(hire_agent, pending) → BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. approve → create agent row + optional key → activity_log
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Org graph | Strict tree (reports_to) |
Simple, auditable delegation; no multi-manager ambiguity |
| Key storage | Hashed at rest | Plaintext shown once; safe at rest |
Risks and Unknowns
- Graceful-cancel-then-force-kill semantics during pause of an active run need careful adapter coordination.
Out of Scope
- Multi-board governance or role-based human permission granularity.
requirements
- What adapter types are fully supported in V1 and which are plugin-only?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). System (Issues)
Overview
Issues are the core task entity and the primary unit of work. Each issue traces to a company goal chain, has a single assignee, supports parent/child hierarchy, and uses atomic checkout to guarantee no double-work. Issues carry comments, documents, work products, attachments, labels, blocker relations, thread interactions, inbox/read state, and execution locks. Work modes (standard, ask, planning) control what execution is allowed.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Create/assign/force-reassign/cancel tasks; view kanban and audit trail |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Atomically check out assigned tasks, execute, comment, attach artifacts, delegate children |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Decompose approved plans into child issues |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01Musttodo \ | Must | The system shall support issue lifecycle with status backlog \ | todo \ | in_progress \ | in_review \ | done \ | blocked \ | cancelled and the defined transitions. |
| FR-02MustThe system shall enforce single-assignee ownership and require an assignee before `in_progress`. | Must | The system shall enforce single-assignee ownership and require an assignee before in_progress. |
| FR-03MustThe system shall provide atomic checkout that sets assignee, status `in_progress`, and execution locks via a single guarded SQL update, returning `409` on concurrent claims. | Must | The system shall provide atomic checkout that sets assignee, status in_progress, and execution locks via a single guarded SQL update, returning 409 on concurrent claims. |
| FR-04MustThe system shall enforce that every task traces to a company goal chain via `goal_id`, `parent_id`, or project-goal linkage. | Must | The system shall enforce that every task traces to a company goal chain via goal_id, parent_id, or project-goal linkage. |
| FR-05MustThe system shall support issue comments authored by agents or users. | Must | The system shall support issue comments authored by agents or users. |
| FR-06MustThe system shall support attachments (upload allowlist, inline vs download serving, range requests for video) and link them to issues/comments. | Must | The system shall support attachments (upload allowlist, inline vs download serving, range requests for video) and link them to issues/comments. |
| FR-07ShouldThe system shall support documents (markdown, append-only revisions) linked to issues by workflow key, with board lock/unlock. | Should | The system shall support documents (markdown, append-only revisions) linked to issues by workflow key, with board lock/unlock. |
| FR-08ShouldThe system shall support work products (artifact-backed and workspace-file references), labels, first-class blockers (`issue_relations`), and thread interactions (`request_confirmation`, `ask_user_questions`, `suggest_tasks`). | Should | The system shall support work products (artifact-backed and workspace-file references), labels, first-class blockers (issue_relations), and thread interactions (request_confirmation, ask_user_questions, suggest_tasks). |
| FR-09ShouldThe system shall enforce the non-terminal liveness rule: agent-owned non-terminal issues must have a live, waiting, or explicit recovery path. | Should | The system shall enforce the non-terminal liveness rule: agent-owned non-terminal issues must have a live, waiting, or explicit recovery path. |
| FR-10MayThe system shall support work modes `standard`, `ask`, and `planning`. | May | The system shall support work modes standard, ask, and planning. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustCheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. must be race-free under concurrent claims. | Must | Correctness | CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. must be race-free under concurrent claims. |
| NFR-02MustAll issue mutations write `activity_log`. | Must | Auditability | All issue mutations write activity_log. |
| NFR-03ShouldStandard issue CRUD p95 < 250 ms at 1k tasks/company. | Should | Performance | Standard issue CRUD p95 < 250 ms at 1k tasks/company. |
Constraints
- Terminal states are
doneandcancelled. in_progressrequires an assignee and setsstarted_at;done/cancelledset completion timestamps.
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-03MustThe system shall provide atomic checkout that sets assignee, status `in_progress`, and execution locks via a single guarded SQL update, returning `409` on concurrent claims.
- Given two concurrent checkout requests for the same
todoissue - When both execute
- Then exactly one succeeds and the other gets
409with current owner/status
- Given two concurrent checkout requests for the same
- FR-06MustThe system shall support attachments (upload allowlist, inline vs download serving, range requests for video) and link them to issues/comments.
- Given a video attachment
- When a browser requests a byte range
- Then the server responds
206withContent-RangeandAccept-Ranges: bytes
- NFR-01MustCheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. must be race-free under concurrent claims.
- Given the checkout race regression test
- When run
- Then it passes (release-gate item)
Conflicts
None identified yet.
Open Questions
- Exact scope of the non-terminal liveness recovery actions and when issue-backed recovery is used vs source-scoped actions (see
doc/execution-semantics.md).
Specification: TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). System (Issues)
Overview
Issues are company-scoped tasks with a strict status state machine, single-assignee ownership, and atomic checkout. Rich collaboration (comments, documents, work products, attachments, labels, blockers, thread interactions) is layered on top. Execution locks and checkout/execution-run fields prevent double-work. A non-terminal liveness rule keeps work from silently stalling.
Architecture
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration./BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. ──► /api/issues (CRUD, checkout, release, force-release)
│
▼
issues (status, assignee, checkout/execution locks, parent_id, goal_id)
├── issue_comments
├── issue_documents ← documents / document_revisions
├── issue_attachments ← assets
├── issue_work_products
├── issue_relations (blockers)
├── issue_thread_interactions / issue_approvals
└── activity_log (every mutation)
Data Models
issues (core fields)
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| company_id | uuid | FK, not null | Scoping |
| project_id / goal_id / parent_id | uuid | FK, null | Traceability + hierarchy |
| title / description | text | - | Content |
| status | enum | not null | backlog \| todo \| in_progress \| in_review \| done \| blocked \| cancelled |
| priority | enum | - | critical \| high \| medium \| low |
| assignee_agent_id | uuid | FK, null | Single assignee |
| checkout_run_id / execution_run_id / execution_locked_at | - | null | Atomic checkout/execution locks |
| work_mode | text | default standard |
standard \| ask \| planning |
| issue_number / identifier | - | - | Per-company human id |
API Contracts
POST /issues/:issueId/checkout
Atomic checkout. Request: { agentId, expectedStatuses }. Single guarded SQL update; 409 on conflict.
POST /issues/:issueId/release | /issues/:issueId/admin/force-release
Release locks; force-release is board-only and writes issue.admin_force_release activity.
Documents, comments, attachments
GET/PUT /issues/:issueId/documents/:key (+lock/unlock/revisions), POST/GET /issues/:issueId/comments, POST /companies/:companyId/issues/:issueId/attachments, GET /attachments/:attachmentId/content, DELETE /attachments/:attachmentId.
Error Responses
| Status | Code | Description |
|---|---|---|
| 409 | CONFLICT | CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. conflict or invalid transition |
| 422 | RULE_VIOLATION | Semantic rule violation (e.g. in_progress without assignee) |
Sequences
Atomic checkout
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. → checkout(expectedStatuses) → UPDATE issues SET status=in_progress, assignee, locks WHERE id AND status IN(?) AND (assignee IS NULL OR assignee=agent) → rowcount 0 ? 409 : ok
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | Single guarded SQL update | Race-free without app-level locks |
| Communication | Tasks + comments only | No separate chat system (V1 decision) |
Risks and Unknowns
- Non-terminal liveness enforcement touches scheduler, watchdogs, and recovery actions; semantics are detailed in
doc/execution-semantics.md.
Out of Scope
- ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env./issue-level privacy ACLs (Pro/Enterprise).
requirements
- Exact scope of the non-terminal liveness recovery actions and when issue-backed recovery is used vs source-scoped actions (see
doc/execution-semantics.md).
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. Execution & Adapters
Overview
Agents do not run inside Paperclip; they are invoked via heartbeats that call an adapter. The scheduler wakes agents on their heartbeat schedule or on event triggers, the execution service resolves workspace/secrets/skills and invokes the adapter, and runs are tracked with status, context snapshots, events, and audit trails. Adapters cover process spawning, HTTP/webhook, local CLI sessions, the OpenClaw gateway, and external plugins.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | See run status, logs, costs; cancel runs; detect stuck/orphaned runs |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Receive heartbeats, execute, report status and cost events |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall invoke agents on per-agent schedules (`enabled`, `intervalSec` >= 30, `maxConcurrentRuns` clamped 1..50). | Must | The system shall invoke agents on per-agent schedules (enabled, intervalSec >= 30, maxConcurrentRuns clamped 1..50). |
| FR-02MustThe scheduler shall skip invocation when the agent is paused/terminated, a run is active, or the hard budget limit is hit. | Must | The scheduler shall skip invocation when the agent is paused/terminated, a run is active, or the hard budget limit is hit. |
| FR-03Mustrunning \ | Must | The system shall track heartbeat runs with status queued \ | running \ | succeeded \ | failed \ | cancelled \ | timed_out and invocation source scheduler \ | manual \ | callback. |
| FR-04MustThe process adapter shall spawn a child process, stream stdout/stderr to run logs, set status on exit/timeout, and cancel via SIGTERM then SIGKILL after a grace period. | Must | The process adapter shall spawn a child process, stream stdout/stderr to run logs, set status on exit/timeout, and cancel via SIGTERM then SIGKILL after a grace period. |
| FR-05MustThe HTTP adapter shall invoke via outbound request (2xx accepted, non-2xx failed) with optional async callback completion. | Must | The HTTP adapter shall invoke via outbound request (2xx accepted, non-2xx failed) with optional async callback completion. |
| FR-06ShouldThe system shall support context delivery modes `thin` (IDs/pointers) and `fat` (assignments, goal summary, budget snapshot, recent comments). | Should | The system shall support context delivery modes thin (IDs/pointers) and fat (assignments, goal summary, budget snapshot, recent comments). |
| FR-07ShouldThe system shall support built-in local CLI adapters (claude, codex, gemini, opencode, pi, cursor), the OpenClaw gateway, and external adapter plugins. | Should | The system shall support built-in local CLI adapters (claude, codex, gemini, opencode, pi, cursor), the OpenClaw gateway, and external adapter plugins. |
| FR-08ShouldThe system shall detect and recover orphaned/stuck runs automatically. | Should | The system shall detect and recover orphaned/stuck runs automatically. |
| FR-09MayThe system shall support an optional cheap-model profile lane restricted to status-only recovery coordination. | May | The system shall support an optional cheap-model profile lane restricted to status-only recovery coordination. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustHeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. invoke acknowledgement < 2 s for the process adapter. | Must | Reliability | HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. invoke acknowledgement < 2 s for the process adapter. |
| NFR-02ShouldRuns must produce structured logs, cost events, session state, and audit trails. | Should | Observability | Runs must produce structured logs, cost events, session state, and audit trails. |
Constraints
- Separate queue infrastructure is not required for V1; a lightweight in-process scheduler/worker handles heartbeat triggers, stuck-run detection, and budget checks.
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-02MustThe scheduler shall skip invocation when the agent is paused/terminated, a run is active, or the hard budget limit is hit.
- Given an agent at its hard budget limit
- When its schedule fires
- Then no new run is invoked and the skip is recorded
- FR-04MustThe process adapter shall spawn a child process, stream stdout/stderr to run logs, set status on exit/timeout, and cancel via SIGTERM then SIGKILL after a grace period.
- Given a running process-adapter run
- When it is cancelled
- Then SIGTERM is sent and SIGKILL follows after the grace period
- NFR-01MustHeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. invoke acknowledgement < 2 s for the process adapter.
- Given a process-adapter heartbeat
- When invoked
- Then acknowledgement returns within 2 s
Conflicts
None identified yet.
Open Questions
- What are the exact stuck-run detection thresholds and orphan-recovery heuristics?
Specification: HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. Execution & Adapters
Overview
A lightweight in-process scheduler wakes agents; the heartbeat execution service resolves workspace, injects secrets, loads skills, and invokes the agent's adapter. Adapters implement a common interface (invoke, status, cancel). Built-ins cover process, http, local CLI, and the OpenClaw gateway; external adapters are loaded as plugins with zero hardcoded core imports.
Architecture
Scheduler ──► heartbeat service ──► adapter.invoke(agent, context)
(skip rules) (workspace, secrets, skills) │
├─ process: spawn, stream, SIGTERM/SIGKILL
├─ http: outbound req, callback
├─ local CLI: claude/codex/gemini/opencode/pi/cursor
├─ openclaw gateway
└─ external plugin
heartbeat_runs (status, context_snapshot) + heartbeat_run_events
Data Models
heartbeat_runs
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| company_id / agent_id | uuid | FK, not null | Scoping |
| invocation_source | enum | - | scheduler \| manual \| callback |
| status | enum | - | queued \| running \| succeeded \| failed \| cancelled \| timed_out |
| started_at / finished_at | timestamptz | null | Timing |
| error | text | null | Failure detail |
| external_run_id | text | null | AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). run id |
| context_snapshot | jsonb | null | Invocation context |
Supporting tables: heartbeat_run_events, heartbeat_run_watchdog_decisions, agent_wakeup_requests, agent_runtime_state, agent_task_sessions.
API Contracts
POST /agents/:agentId/heartbeat/invoke
Triggers a heartbeat run (manual source). Returns the run record.
AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). interface
interface AgentAdapter {
invoke(agent: AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration., context: InvocationContext): Promise<InvokeResult>;
status(run: HeartbeatRun): Promise<RunStatus>;
cancel(run: HeartbeatRun): Promise<void>;
}
Error Responses
| Status | Code | Description |
|---|---|---|
| 409 | CONFLICT | AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. not invokable (paused/terminated/budget-blocked/active run) |
Sequences
Scheduled heartbeat
Scheduler tick → skip rules check → heartbeat service → adapter.invoke → run status + events + cost events → activity_log
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Scheduler | In-process worker | No queue infra needed for V1 |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). loading | Dynamic plugin registry | Zero hardcoded adapter imports; pure dynamic loading |
Risks and Unknowns
- Cheap-model profile lane must be tightly constrained to status-only recovery to avoid deliverable work on a low-cost model.
Out of Scope
- Cloud-grade orchestration or external queue infrastructure.
requirements
- What are the exact stuck-run detection thresholds and orphan-recovery heuristics?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Governance & Approvals
Overview
Governance ensures nothing ships without board sign-off and that the board can intervene anywhere. It covers board approval workflows for hires and CEO strategy, execution policies with review/approval stages, decision tracking, budget hard-stops, agent pause/resume/terminate, full audit logging, and the scoped task-watchdog capacity for watched issue subtrees.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Approve/reject hires and CEO strategy; override any decision; pause/resume/terminate agents |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Proposes strategy that must be approved before delegated execution |
| Watchdog agent | Restore live task paths within a watched subtree (scoped, not board authority) |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01Mustrevision_requested \ | Must | The system shall support approval types hire_agent, approve_ceo_strategy, budget_override_required, request_board_approval with status pending \ | revision_requested \ | approved \ | rejected \ | cancelled. |
| FR-02MustThe system shall block CEO-created delegated work from active execution states until the CEO strategy is approved. | Must | The system shall block CEO-created delegated work from active execution states until the CEO strategy is approved. |
| FR-03MustThe board shall be able to pause/resume/terminate any agent, reassign or cancel any task, edit budgets, and approve/reject/cancel pending approvals at any time. | Must | The board shall be able to pause/resume/terminate any agent, reassign or cancel any task, edit budgets, and approve/reject/cancel pending approvals at any time. |
| FR-04MustEvery governance mutation shall write an auditable `activity_log` entry. | Must | Every governance mutation shall write an auditable activity_log entry. |
| FR-05ShouldThe system shall support execution policies with review/approval stages and decision tracking. | Should | The system shall support execution policies with review/approval stages and decision tracking. |
| FR-06ShouldThe system shall support a scoped task watchdog that may only restore live task paths inside one watched subtree, may resolve eligible `request_confirmation` plan confirmations, and is explicitly denied board/governance/secret/cross-company authority. | Should | The system shall support a scoped task watchdog that may only restore live task paths inside one watched subtree, may resolve eligible request_confirmation plan confirmations, and is explicitly denied board/governance/secret/cross-company authority. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustWatchdogs must not resolve board approvals, force-release locks, cancel active runs, or cross company boundaries. | Must | Security | Watchdogs must not resolve board approvals, force-release locks, cancel active runs, or cross company boundaries. |
| NFR-02MustWatchdog mutations must record watchdog id, source issue, run id, and stop fingerprint. | Must | Auditability | Watchdog mutations must record watchdog id, source issue, run id, and stop fingerprint. |
Constraints
- Watchdog authority is derived from persisted configuration and run context; custom instructions/prompt text cannot expand it.
- The watched subtree excludes
task_watchdog-origin issues and their descendants.
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-02MustThe system shall block CEO-created delegated work from active execution states until the CEO strategy is approved.
- Given a CEO that drafted delegated tasks
- When strategy has not been approved
- Then those tasks cannot transition to active execution states
- NFR-01MustWatchdogs must not resolve board approvals, force-release locks, cancel active runs, or cross company boundaries.
- Given a task watchdog run
- When it attempts to approve a hire/budget approval or touch a company outside the watched subtree
- Then the action is denied and no probe issue/comment is created
Conflicts
None identified yet.
Open Questions
- Which interaction families beyond eligible
request_confirmationplan confirmations will watchdogs resolve in future iterations?
Specification: Governance & Approvals
Overview
Governance is enforced through the approvals table, execution policies, budget hard-stops, board override authority, and the scoped task-watchdog contract. BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. approvals are transactional (no lost decisions). Watchdogs are a narrow, persisted-configuration-derived capacity confined to one watched issue subtree.
Architecture
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. ──► /api/approvals (approve/reject/cancel)
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. ──► approvals(hire_agent | approve_ceo_strategy | request_board_approval)
│
▼
approvals table → activity_log
Watchdog ──► scoped mutations within watched subtree (comments, child issues, status,
blockers, eligible request_confirmation) → activity (watchdog metadata)
Data Models
approvals
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| company_id | uuid | FK, not null | Scoping |
| type | enum | - | hire_agent \| approve_ceo_strategy \| budget_override_required \| request_board_approval |
| requested_by_agent_id / requested_by_user_id | uuid | null | Requester |
| status | enum | - | pending \| revision_requested \| approved \| rejected \| cancelled |
| payload | jsonb | not null | ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. content |
| decision_note | text | null | Decision rationale |
| decided_by_user_id / decided_at | - | null | Decision attribution |
Supporting tables: approval_comments, issue_approvals, issue_execution_decisions, issue_watchdogs, heartbeat_run_watchdog_decisions, issue_recovery_actions.
API Contracts
GET/POST /companies/:companyId/approvals, POST /approvals/:approvalId/approve | /reject
ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. listing, creation, and decisions.
Error Responses
| Status | Code | Description |
|---|---|---|
| 403 | UNAUTHORIZED | Non-board actor attempting board-only decision |
| 409 | CONFLICT | Decision on an already-terminal approval |
Sequences
Hire approval
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. → approvals(hire_agent, pending) → BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. approve (transactional) → create agent + optional key → activity_log
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. writes | Transactional | No lost approval decisions (reliability target) |
| Watchdog scope | Persisted-config-derived, subtree-scoped | Safety: prompts cannot expand authority |
Risks and Unknowns
- Ensuring watchdog capability discovery comes only from wake metadata/denials (no probe issues) requires careful server enforcement and tests.
Out of Scope
- Multi-board governance or RBAC granularity for humans.
requirements
- Which interaction families beyond eligible
request_confirmationplan confirmations will watchdogs resolve in future iterations?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Budget & Cost Control
Overview
Paperclip tracks token and cost usage and enforces budgets to prevent runaway spend. Cost events are ingested per agent/issue/project/goal/provider/model, rolled up across dimensions, and checked against monthly UTC budgets. At the hard limit (100%) the agent is auto-paused and new invocations/checkout are blocked; a soft alert fires at 80%. The board can override by raising budgets or explicitly resuming.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Set company/agent budgets; monitor spend; override hard stops |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Report cost events for its own runs |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall ingest cost events (agent, issue, provider, model, input/output tokens, cost cents, occurred_at) with ownership checks. | Must | The system shall ingest cost events (agent, issue, provider, model, input/output tokens, cost cents, occurred_at) with ownership checks. |
| FR-02MustThe system shall validate non-negative token counts and `costCents >= 0`. | Must | The system shall validate non-negative token counts and costCents >= 0. |
| FR-03MustThe system shall support company and agent monthly budgets and optional project budgets. | Must | The system shall support company and agent monthly budgets and optional project budgets. |
| FR-04MustAt the hard limit (100%), the system shall set agent status to `paused`, block new checkout/invocation, and emit a high-priority activity event. | Must | At the hard limit (100%), the system shall set agent status to paused, block new checkout/invocation, and emit a high-priority activity event. |
| FR-05MustThe system shall roll up spend by company, agent, project, goal, and issue (read-time aggregation acceptable for V1). | Must | The system shall roll up spend by company, agent, project, goal, and issue (read-time aggregation acceptable for V1). |
| FR-06ShouldThe system shall surface a soft alert at the 80% threshold. | Should | The system shall surface a soft alert at the 80% threshold. |
| FR-07ShouldThe board shall be able to override a hard stop by raising the budget or explicitly resuming the agent. | Should | The board shall be able to override a hard stop by raising the budget or explicitly resuming the agent. |
| FR-08ShouldThe system shall expose a dashboard payload with month-to-date spend and budget utilization. | Should | The system shall expose a dashboard payload with month-to-date spend and budget utilization. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustRollups are aggregations of cost events and are never manually edited. | Must | Correctness | Rollups are aggregations of cost events and are never manually edited. |
| NFR-02ShouldCost summary queries should stay within latency targets; materialized rollups deferred. | Should | Performance | Cost summary queries should stay within latency targets; materialized rollups deferred. |
Constraints
- Budget period is a monthly UTC calendar window.
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-04MustAt the hard limit (100%), the system shall set agent status to `paused`, block new checkout/invocation, and emit a high-priority activity event.
- Given an agent that reaches 100% of its monthly budget
- When spend crosses the limit
- Then the agent is paused, new invocations are blocked, and a high-priority activity event is emitted
- FR-04MustAt the hard limit (100%), the system shall set agent status to `paused`, block new checkout/invocation, and emit a high-priority activity event. (cont.)
- Given a budget-paused agent
- When a checkout is attempted
- Then it is rejected
- NFR-01MustRollups are aggregations of cost events and are never manually edited.
- Given the cost rollup queries
- When inspected
- Then they are pure aggregations of
cost_eventswith no manual overrides
Conflicts
None identified yet.
Open Questions
- Should materialized rollups be added if read latency exceeds targets at scale?
Specification: Budget & Cost Control
Overview
Cost events are the atomic input; all spend figures are aggregations. Budgets are monthly UTC windows scoped to company and agent (project optional). Enforcement runs in the heartbeat scheduler/checkout path: at the hard limit the agent is paused and invocations are blocked. A soft threshold warns at 80%.
Architecture
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. → POST /cost-events → cost_events (validated) → rollups (read-time aggregation)
Scheduler/checkout → budget check → hard limit? → pause agent + block + activity
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → PATCH /budgets, resume → override
Data Models
cost_events
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| company_id | uuid | FK, not null | Scoping |
| agent_id | uuid | FK, not null | Spending agent |
| issue_id / project_id / goal_id | uuid | FK, null | Attribution |
| provider / model | text | not null | Cost source |
| input_tokens / output_tokens | int | not null, default 0 | Usage |
| cost_cents | int | not null | Cost |
| occurred_at | timestamptz | not null | When it happened |
Supporting tables: budget_policies, budget_incidents, finance_events.
API Contracts
POST /companies/:companyId/cost-events
Ingest a cost event with ownership checks and validation.
GET /companies/:companyId/costs/summary | /by-agent | /by-project
Read-time aggregate rollups.
PATCH /companies/:companyId/budgets | PATCH /agents/:agentId/budgets
Set company/agent budgets.
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Negative tokens or cost |
| 403 | UNAUTHORIZED | AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. reporting cost for another company's entity |
Sequences
Hard-stop auto-pause
cost event ingest → spend crosses 100% → agent.status=paused → block checkout/invocation → high-priority activity
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Rollups | Read-time aggregation | Simplicity for V1; materialize later only if needed |
| Enforcement point | Scheduler + checkout path | Catches both scheduled and manual work |
Risks and Unknowns
- Concurrent cost ingestion near the threshold must not double-spend or miss the pause.
Out of Scope
- Revenue/expense accounting beyond model/token costs.
requirements
- Should materialized rollups be added if read latency exceeds targets at scale?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Routines & Schedules
Overview
Routines are recurring task definitions that fire on cron, webhook, or API triggers. Each routine execution creates a tracked issue and wakes the assigned agent, so regular work (customer support, reports, social) happens without manual kick-offs. Routines carry concurrency and catch-up policies, secret-aware env overlays, and revision history.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Configure recurring jobs, triggers, and concurrency policy |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Wake on routine execution and work the generated issue |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support routines with cron, webhook, and API triggers. | Must | The system shall support routines with cron, webhook, and API triggers. |
| FR-02MustEach routine execution shall create a tracked issue and wake the assigned agent. | Must | Each routine execution shall create a tracked issue and wake the assigned agent. |
| FR-03MustThe system shall support concurrency and catch-up policies per routine. | Must | The system shall support concurrency and catch-up policies per routine. |
| FR-04ShouldThe system shall support routine revisions and snapshotted routine env (secret-aware binding format). | Should | The system shall support routine revisions and snapshotted routine env (secret-aware binding format). |
| FR-05ShouldThe routine env overlay shall apply after project env and before Paperclip runtime-owned keys, resolving secret refs against the routine binding target. | Should | The routine env overlay shall apply after project env and before Paperclip runtime-owned keys, resolving secret refs against the routine binding target. |
| FR-06ShouldThe system shall track routine runs. | Should | The system shall track routine runs. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01ShouldMissed routine executions follow the configured catch-up policy. | Should | Reliability | Missed routine executions follow the configured catch-up policy. |
| NFR-02ShouldRoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. executions are visible as issues and runs. | Should | Auditability | RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. executions are visible as issues and runs. |
Constraints
- RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent.-owned secrets do not require direct bindings on the executing agent (resolved against the routine binding target).
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-02MustEach routine execution shall create a tracked issue and wake the assigned agent.
- Given a cron-triggered routine
- When the schedule fires
- Then a tracked issue is created and the assigned agent is woken
Conflicts
None identified yet.
Open Questions
- What are the exact concurrency and catch-up semantics when executions overlap?
Specification: Routines & Schedules
Overview
Routines are recurring definitions stored with their triggers, env, and revision history. A trigger fires a routine run that materializes an issue (with routine-scoped env overlay) and wakes the assigned agent. Routines are first-class company-scoped entities.
Architecture
Trigger (cron/webhook/API) → routine run → create issue (routine env overlay) → wake assigned agent
│
└─► routine_runs + activity_log
routine_revisions (snapshotted env) ← routine_triggers ← routines (company-scoped)
Data Models
routines / routine_revisions / routine_triggers / routine_runs
| Field | Type | Constraints | Description |
|---|---|---|---|
| routines.id / company_id | uuid | FK, not null | Scoping |
| routines.env | jsonb | - | Secret-aware env binding |
| routine_triggers | - | - | cron / webhook / API trigger config |
| routine_revisions | - | - | Snapshotted routine state |
| routine_runs | - | - | Per-execution tracking |
API Contracts
/api/routines (CRUD + triggers)
RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. management and trigger configuration. Route handler: server/src/routes/routines.ts, service: server/src/services/routines.ts.
Sequences
RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. execution
trigger fires → routine run → issue created (routine env overlay after project env, before runtime keys) → wake agent → routine_runs + activity_log
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Env overlay order | routine env after project env, before runtime-owned keys | Predictable precedence; routine-owned secrets resolve without agent bindings |
Risks and Unknowns
- Overlapping executions and catch-up behavior need precise documented semantics.
Out of Scope
- External calendar/scheduling integrations beyond cron/webhook/API.
requirements
- What are the exact concurrency and catch-up semantics when executions overlap?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Workspaces & Runtime
Overview
Workspaces give agents the right working directory and runtime context. ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. workspaces hold shared project state; execution workspaces are isolated git worktrees (often operator branches) where an agent runs. Runtime services (dev servers, preview URLs) and workspace operations (commands, diffs, uploads) support the execution. Environments and leases bind secret-aware env to execution targets.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Manage project workspaces, environments, runtime services |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Check out an execution workspace, run commands, publish diffs |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support project workspaces and isolated execution workspaces (git worktrees). | Must | The system shall support project workspaces and isolated execution workspaces (git worktrees). |
| FR-02MustThe system shall support workspace operations (commands, diffs, large uploads, git publishing). | Must | The system shall support workspace operations (commands, diffs, large uploads, git publishing). |
| FR-03MustThe system shall resolve execution workspace per issue (preference + settings fields on issues). | Must | The system shall resolve execution workspace per issue (preference + settings fields on issues). |
| FR-04ShouldThe system shall support runtime services (dev servers, preview URLs) and their lifecycle. | Should | The system shall support runtime services (dev servers, preview URLs) and their lifecycle. |
| FR-05ShouldThe system shall support environments with secret-aware env bindings and leases. | Should | The system shall support environments with secret-aware env bindings and leases. |
| FR-06ShouldThe system shall base fresh worktrees on `origin/master` and refresh unstarted reuses. | Should | The system shall base fresh worktrees on origin/master and refresh unstarted reuses. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01ShouldWorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. command authorization must scope commands to the workspace and agent. | Should | Security | WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. command authorization must scope commands to the workspace and agent. |
| NFR-02ShouldLarge workspace uploads and git publishing must succeed without truncation. | Should | Reliability | Large workspace uploads and git publishing must succeed without truncation. |
Constraints
- WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. runtime services are optional and provider-dependent (e.g. Daytona sandbox leases).
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-01MustThe system shall support project workspaces and isolated execution workspaces (git worktrees).
- Given an issue with an execution workspace preference
- When the agent checks out
- Then an isolated execution workspace is resolved/created for the run
Conflicts
None identified yet.
Open Questions
- Which sandbox providers are supported and how are leases reused (e.g. Daytona)?
Specification: Workspaces & Runtime
Overview
Workspaces split into project workspaces (shared project state) and execution workspaces (isolated git worktrees per run). Runtime services run dev servers/preview URLs; environments bind secret-aware env to execution targets via leases. WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. operations expose commands, diffs, and uploads to agents under authorization.
Architecture
IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. (execution_workspace_*) ──► execution_workspaces (git worktree)
│
ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. workspace ──────────► project_workspaces
Environments (secret-aware env) ──► environment_leases ──► execution target
Runtime services ──► workspace_runtime_services (dev servers, preview URLs)
WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. operations ──► commands, diffs, uploads (authz-gated)
Data Models
execution_workspaces / project_workspaces / workspace_runtime_services / workspace_operations / environments / environment_leases
| Field | Type | Constraints | Description |
|---|---|---|---|
| execution_workspaces.id / company_id | uuid | FK, not null | Scoping |
| issues.execution_workspace_id/preference/settings | - | - | Per-issue workspace resolution |
| environments | - | - | Secret-aware env binding |
| environment_leases | - | - | Env-to-target leases |
API Contracts
/api/execution-workspaces, environment-selection, environments, workspace-command-authz, workspace-runtime-service-authz
WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs./runtime management and authorization. Routes under server/src/routes/, services under server/src/services/execution-workspaces.ts, environments.ts, etc.
Error Responses
| Status | Code | Description |
|---|---|---|
| 403 | UNAUTHORIZED | Command not authorized for this workspace/agent |
Sequences
Execution workspace checkout
checkout → resolve execution_workspace (preference/settings) → create/reuse worktree (base origin/master) → run agent → operations/diffs/uploads (authz)
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Isolation | Git worktrees per execution | Reproducible, isolated agent working directories |
Risks and Unknowns
- Sandbox provider lease reuse (e.g. Daytona) and large-upload reliability need careful handling.
Out of Scope
- Cloud sandbox marketplaces beyond supported providers.
requirements
- Which sandbox providers are supported and how are leases reused (e.g. Daytona)?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. System
Overview
The plugin system lets operators extend Paperclip without forking it. Plugins are instance-wide, out-of-process workers with capability-gated host services, job scheduling, tool exposure, UI contributions, webhooks, and isolated database namespaces. The local/self-hosted early plugin runtime is in scope for V1; the cloud marketplace and packaged public distribution remain out of scope. External adapter plugins are loaded through this same flow with zero hardcoded adapter imports in the loader.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Install/configure plugins, manage state and jobs, view logs |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. author | Build external plugins (SDK + scaffolder) and external adapters |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support installing and configuring instance-wide plugins. | Must | The system shall support installing and configuring instance-wide plugins. |
| FR-02MustThe plugin loader shall have zero hardcoded adapter imports and load external adapters purely dynamically. | Must | The plugin loader shall have zero hardcoded adapter imports and load external adapters purely dynamically. |
| FR-03MustThe system shall expose capability-gated host services to plugins (tools, jobs, webhooks, UI contributions). | Must | The system shall expose capability-gated host services to plugins (tools, jobs, webhooks, UI contributions). |
| FR-04Must`createServerAdapter()` shall include all optional adapter fields (especially `detectModel`). | Must | createServerAdapter() shall include all optional adapter fields (especially detectModel). |
| FR-05ShouldThe system shall support plugin database namespaces with migrations. | Should | The system shall support plugin database namespaces with migrations. |
| FR-06ShouldThe system shall support plugin jobs, logs, webhooks, and managed resources. | Should | The system shall support plugin jobs, logs, webhooks, and managed resources. |
| FR-07ShouldExternal adapter plugins shall be installable via the AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. manager and `~/.paperclip/adapter-plugins.json` (including `file:` entries for local dev). | Should | External adapter plugins shall be installable via the AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. manager and ~/.paperclip/adapter-plugins.json (including file: entries for local dev). |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustPluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. capabilities must be gated; a plugin must not exceed its granted capabilities. | Must | Security | PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. capabilities must be gated; a plugin must not exceed its granted capabilities. |
| NFR-02ShouldBuilt-in UI parsers for adapters must be removed when fully externalized to avoid shadowing plugin parsers. | Should | Maintainability | Built-in UI parsers for adapters must be removed when fully externalized to avoid shadowing plugin parsers. |
Constraints
- Cloud-grade plugin marketplace/distribution is out of scope for V1.
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-02MustThe plugin loader shall have zero hardcoded adapter imports and load external adapters purely dynamically.
- Given the plugin loader
- When inspected
- Then there are no hardcoded adapter imports; all adapters load dynamically
- NFR-01MustPluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. capabilities must be gated; a plugin must not exceed its granted capabilities.
- Given a plugin attempting a capability it was not granted
- When the call is made
- Then it is denied
Conflicts
None identified yet.
Open Questions
- What is the full capability taxonomy granted to plugins, and how are new capabilities added safely?
Specification: PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. System
Overview
Plugins are out-of-process workers registered instance-wide. A capability-gated host exposes services (tools, jobs, webhooks, UI contributions, database namespaces) to plugins. The adapter plugin loader is the dynamic-loading path for external adapters and external adapter plugins. An SDK and scaffolder (create-paperclip-plugin) support authoring.
Architecture
Operator ──► /api/plugins (install/config/state)
│
▼
plugins table + plugin_config/state/entities/jobs/logs/webhooks
│
▼
plugin-loader (dynamic) ──► external adapters (zero hardcoded imports)
Host services (capability-gated) ◄── plugin worker ──► plugin_database namespaces/migrations
SDK (packages/plugins/sdk) + create-paperclip-plugin scaffolder
Data Models
plugins and plugin support tables
| Field | Type | Constraints | Description |
|---|---|---|---|
| plugins.id / company/instance scope | - | - | PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. registration |
| plugin_config / plugin_state | - | - | Config and runtime state |
| plugin_entities / plugin_jobs / plugin_logs / plugin_webhooks | - | - | PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions.-owned artifacts |
| plugin_database (namespaces/migrations) | - | - | Isolated plugin DB |
| plugin_managed_resources / plugin_company_settings | - | - | Managed resources and per-company settings |
API Contracts
/api/plugins, /api/plugin-ui-static
PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. installation, configuration, state, jobs, logs, webhooks, and UI static assets. Route: server/src/routes/plugins.ts.
Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). loading
External adapters via ~/.paperclip/adapter-plugins.json; createServerAdapter() includes all optional fields. Loader: server/src/adapters/plugin-loader.ts, store: server/src/services/adapter-plugin-store.ts.
Sequences
External adapter install
operator → AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). manager → adapter-plugins.json entry → plugin-loader dynamic load → createServerAdapter (all fields) → adapter registered
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Loader purity | Zero hardcoded adapter imports | Enables fully external adapters without core changes |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). registration | Include all optional fields (detectModel) |
Avoid silent capability loss for external adapters |
Risks and Unknowns
- Built-in UI parsers can shadow external plugin parsers; removing built-ins when externalized needs coordination.
Out of Scope
- Cloud-grade plugin marketplace and packaged public distribution.
requirements
- What is the full capability taxonomy granted to plugins, and how are new capabilities added safely?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Secrets Management
Overview
Paperclip manages secrets (API keys, tokens, credentials) centrally rather than storing them in plaintext in agent configs or project files. Secrets are versioned, bound to agents and projects via config paths, backed by pluggable provider vaults (local encrypted, AWS Secrets Manager, GCP, Vault), and every access is audited. The system supports inline env secret references in agent/project/routine configs and resolves them at runtime against the appropriate binding target.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Configure secret providers, create/rotate/revoke secrets, bind secrets to agents/projects, view access audit log |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Consume secrets at runtime via env injection; never see plaintext in configs |
Functional Requirements
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support creating, reading, updating, and deleting company-scoped secrets with a name, key, provider, and status. | Must | The system shall support creating, reading, updating, and deleting company-scoped secrets with a name, key, provider, and status. |
| FR-02MustThe system shall support versioned secrets with SHA256 fingerprinting of secret material. | Must | The system shall support versioned secrets with SHA256 fingerprinting of secret material. |
| FR-03MustThe system shall support binding secrets to agents, projects, and other targets via config paths with JSON schema-defined secret references. | Must | The system shall support binding secrets to agents, projects, and other targets via config paths with JSON schema-defined secret references. |
| FR-04MustEvery secret resolution and access attempt shall be recorded as a `secret_access_event` for auditability. | Must | Every secret resolution and access attempt shall be recorded as a secret_access_event for auditability. |
| FR-05MustThe system shall support a pluggable provider architecture: local encrypted storage (default), AWS Secrets Manager, GCP, and HashiCorp Vault. | Must | The system shall support a pluggable provider architecture: local encrypted storage (default), AWS Secrets Manager, GCP, and HashiCorp Vault. |
| FR-06MustSecret values shall never be returned in API responses after creation; only metadata and status are readable. | Must | Secret values shall never be returned in API responses after creation; only metadata and status are readable. |
| FR-07ShouldThe system shall support inline secret reference syntax (`${{ secrets.my-secret-key }}`) in agent configs, project env, and routine env. | Should | The system shall support inline secret reference syntax (${{ secrets.my-secret-key }}) in agent configs, project env, and routine env. |
| FR-08ShouldThe system shall resolve secret references against the routine binding target when used in routine env (routine-owned secrets). | Should | The system shall resolve secret references against the routine binding target when used in routine env (routine-owned secrets). |
| FR-09ShouldThe system shall support bulk secret binding operations and env overlay resolution for execution contexts. | Should | The system shall support bulk secret binding operations and env overlay resolution for execution contexts. |
Non-Functional Requirements
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustSecret material must be encrypted at rest in the provider vault. | Must | Security | Secret material must be encrypted at rest in the provider vault. |
| NFR-02MustEvery secret read event must log who accessed which secret key at what time, attributed to an agent or user. | Must | Auditability | Every secret read event must log who accessed which secret key at what time, attributed to an agent or user. |
| NFR-03MustPlaintext secrets must never be persisted in logs, activity entries, or run context snapshots. | Must | Security | Plaintext secrets must never be persisted in logs, activity entries, or run context snapshots. |
| NFR-04ShouldSecret resolution at invocation time must not add more than 500ms overhead. | Should | Performance | Secret resolution at invocation time must not add more than 500ms overhead. |
Constraints
- Provider config is instance-wide (not per-company for V1).
- Inline secret references use a specific syntax and are distinct from direct secret bindings.
- Secrets are company-scoped: agent/project/routine within company A cannot access company B's secrets.
Acceptance Criteria
- FR-01MustThe system shall support creating, reading, updating, and deleting company-scoped secrets with a name, key, provider, and status.
- Given a board operator with a company
- When they create a secret with name, key, and provider
- Then the secret row exists with status
activeand metadata is returned - And the secret value itself is never returned in any API response
- FR-02MustThe system shall support versioned secrets with SHA256 fingerprinting of secret material.
- Given an existing active secret
- When a new version is created
- Then a
company_secret_versionsrow exists with SHA256 fingerprint
- FR-04MustEvery secret resolution and access attempt shall be recorded as a `secret_access_event` for auditability.
- Given any secret access
- When the value is resolved
- Then a
secret_access_eventis recorded with actor, secret key, and timestamp
- FR-06MustSecret values shall never be returned in API responses after creation; only metadata and status are readable.
- Given a secret API response payload
- When inspected
- Then no field contains the plaintext secret value
- NFR-01MustSecret material must be encrypted at rest in the provider vault.
- Given the provider storage
- When inspected at the storage layer
- Then secret material is encrypted
- NFR-03MustPlaintext secrets must never be persisted in logs, activity entries, or run context snapshots.
- Given server logs after a secret resolution
- When inspected
- Then no plaintext secret values are present
Conflicts
None identified yet.
Open Questions
- What is the full list of supported secret providers for V1 beyond local encrypted and AWS Secrets Manager?
- How are secret references resolved when multiple bindings overlap for the same config path (priority/precedence order)?
Specification: Secrets Management
Overview
Secrets are company-scoped, versioned, and provider-backed. A pluggable provider architecture (local encrypted, AWS Secrets Manager, GCP, Vault) stores the material. Bindings attach secrets to agents/projects/routines via config paths. Inline secret references in JSON configs are resolved at runtime. Every access is audited via secret_access_events.
Architecture
User → /api/secrets (CRUD + versions + bindings)
│
▼
company_secrets (name, key, provider, status)
│
├── company_secret_versions (SHA256 fingerprint)
├── company_secret_bindings (target: agent/project, config path)
├── company_secret_provider_configs (vault config)
└── secret_access_events (audit)
│
▼
Provider layer: local-encrypted | aws-secrets-manager | gcp | vault
│
▼
Runtime resolution: inline ${{ secrets.ref }} → resolved value → env injection
Data Models
company_secrets
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | Secret identifier |
| company_id | uuid | FK companies, not null | Scoping |
| name | text | not null | Human-readable name |
| key | text | not null | Machine key for references |
| provider | text | not null | Provider type |
| status | text | not null | active \| deprecated \| destroyed |
| version | int | not null, default 1 | Current version number |
company_secret_versions
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| secret_id | uuid | FK, not null | Parent secret |
| version | int | not null | Monotonic version |
| sha256 | text | not null | Fingerprint of material |
| created_by / created_at | - | - | Attribution |
company_secret_bindings
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| secret_key | text | not null | Reference key |
| target_type | enum | not null | agent \| project \| routine |
| target_id | uuid | FK, not null | Scoped target |
| config_path | text[] | not null | JSON path for resolution |
API Contracts
POST /companies/:companyId/secrets
Request: { name, key, provider, value }
Response (201): { id, name, key, provider, status, version } — value NOT returned.
GET/PATCH/DELETE /companies/:companyId/secrets/:secretId
Standard CRUD. GET never returns value.
POST /companies/:companyId/secrets/:secretId/versions
Create new version of existing secret.
GET /companies/:companyId/secrets/:secretId/bindings, POST /companies/:companyId/secrets/bindings
Binding management.
GET /companies/:companyId/secrets/access-events
Audit log of secret accesses.
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Missing provider type or invalid value |
| 403 | UNAUTHORIZED | Actor may not access this company's secrets |
| 404 | NOT_FOUND | Secret not found |
| 409 | CONFLICT | Duplicate secret key within company |
Sequences
Secret creation and binding
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → POST /secrets (value) → store in provider vault → return metadata (no value)
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → POST /bindings (secretKey, targetType, targetId, configPath) → binding row
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. → runtime resolution → resolve bindings + inline refs → fetch from provider → env injection → ${{ secrets.ref }} replaced
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Provider architecture | Pluggable with registry | Supports local dev, AWS, GCP, Vault without hardcoding |
| Inline refs | ${{ secrets.key }} syntax in JSONB configs |
Usable in agent config, project env, routine env |
| Audit | Dedicated secret_access_events table |
Immutable trail for compliance |
| Material storage | Provider vault only (not in business DB) | Material is the provider's concern; DB holds metadata only |
Risks and Unknowns
- Vault provider may not be available in all deployment modes; fallback behavior must be graceful.
- Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. resolution ordering in nested env overlays (project env → routine env → runtime keys) needs clear documentation.
Out of Scope
- Automatic secret rotation or expiry notification.
- Cross-company secret sharing.
requirements
- What is the full list of supported secret providers for V1 beyond local encrypted and AWS Secrets Manager?
- How are secret references resolved when multiple bindings overlap for the same config path (priority/precedence order)?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Auth & Access Control
Overview
Paperclip supports two deployment modes: local_trusted (implicit board, loopback) and authenticated (session-based with user profiles). Access control spans board-level management, company membership and roles, agent API keys (hashed at rest), principal permission grants, instance user roles, invite/join flows, CLI authentication, and a fine-grained authorization engine that gates actions on resources. The authorization layer is cross-cutting infrastructure used by every route handler.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Manage users, API keys, invites; configure deployment mode and instance settings |
| Human user | Authenticate, manage profile, accept invites, request company access |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Authenticate via bearer API key for company-scoped operations |
Functional Requirements
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support two deployment modes: `local_trusted` and `authenticated`. | Must | The system shall support two deployment modes: local_trusted and authenticated. |
| FR-02MustIn `authenticated` mode, users shall authenticate with sessions and have user profiles. | Must | In authenticated mode, users shall authenticate with sessions and have user profiles. |
| FR-03MustAgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. API keys shall be hashed at rest with bcrypt or equivalent; plaintext shown once at creation. | Must | AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. API keys shall be hashed at rest with bcrypt or equivalent; plaintext shown once at creation. |
| FR-04MustThe system shall enforce company access boundaries: request-scoped `company_id` context checked on every protected route. | Must | The system shall enforce company access boundaries: request-scoped company_id context checked on every protected route. |
| FR-05MustThe system shall support board API keys for programmatic board-level access. | Must | The system shall support board API keys for programmatic board-level access. |
| FR-06MustThe system shall support CLI authentication challenges. | Must | The system shall support CLI authentication challenges. |
| FR-07MustThe authorization middleware shall gate actions (create/read/update/delete/approve/etc.) on resources (company/agent/issue/etc.) per actor (board/user/agent). | Must | The authorization middleware shall gate actions (create/read/update/delete/approve/etc.) on resources (company/agent/issue/etc.) per actor (board/user/agent). |
| FR-08ShouldThe system shall support instance user roles for multi-user deployments. | Should | The system shall support instance user roles for multi-user deployments. |
| FR-09ShouldThe system shall support company membership management with roles and invites. | Should | The system shall support company membership management with roles and invites. |
| FR-10ShouldThe system shall support join requests for users requesting access to a company. | Should | The system shall support join requests for users requesting access to a company. |
| FR-11ShouldThe system shall support principal permission grants for fine-grained access beyond role-based defaults. | Should | The system shall support principal permission grants for fine-grained access beyond role-based defaults. |
| FR-12ShouldThe system shall support API key revocation and usage tracking (last_used_at). | Should | The system shall support API key revocation and usage tracking (last_used_at). |
Non-Functional Requirements
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustAgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. API keys must not access other companies' data. | Must | Security | AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. API keys must not access other companies' data. |
| NFR-02MustHashed keys must use a strong, salted algorithm (bcrypt). | Must | Security | Hashed keys must use a strong, salted algorithm (bcrypt). |
| NFR-03MustAll user/access mutations (invite, role change, key create/revoke) write `activity_log`. | Must | Auditability | All user/access mutations (invite, role change, key create/revoke) write activity_log. |
| NFR-04ShouldAuth middleware must complete in under 10ms p95. | Should | Performance | Auth middleware must complete in under 10ms p95. |
Constraints
local_trustedmode grants implicit board access without authentication.authenticatedmode requires session-based auth with user resolution.- AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. keys are scoped to exactly one company.
Acceptance Criteria
- FR-01MustThe system shall support two deployment modes: `local_trusted` and `authenticated`.
- Given a
local_trusteddeployment - When any request is made from localhost
- Then it is treated as board-level without explicit auth
- Given a
- FR-03MustAgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. API keys shall be hashed at rest with bcrypt or equivalent; plaintext shown once at creation.
- Given a new agent API key creation
- When the key is created
- Then the hash is stored and the plaintext key is returned exactly once
- FR-04MustThe system shall enforce company access boundaries: request-scoped `company_id` context checked on every protected route.
- Given an agent API key for company A
- When a request targets company B's endpoint
- Then the request is rejected with 403/404
- FR-09ShouldThe system shall support company membership management with roles and invites.
- Given a company owner
- When they invite a user
- Then the user receives an invitation and can accept to become a member
- NFR-01MustAgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. API keys must not access other companies' data.
- Given the authorization middleware
- When processing any API request
- Then company boundaries are enforced before any business logic runs
Conflicts
None identified yet.
Open Questions
- What is the full taxonomy of principal permission grants and how do they compose with role-based defaults?
- How are instance user roles and company membership roles reconciled for cross-company board users?
Specification: Auth & Access Control
Overview
Auth is mode-dependent. In local_trusted mode all local requests are implicit board; in authenticated mode the system uses session-based auth with user profiles, company memberships, and role-based access. The authorization engine enforces fine-grained action-on-resource gates. AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. API keys are hashed at rest and scoped to one company.
Architecture
Request → authz middleware (assertBoard, assertCompanyAccess, assertAgent)
│
├─ auth mode check (local_trusted vs authenticated)
├─ bearer token / session resolution
├─ company_id scoping
└─ permission check (action on resource)
│
▼
Route handler (company-scoped context)
Data Models
auth / user
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | User ID |
| text | unique | User email | |
| display_name | text | - | Display name |
board_api_keys / agent_api_keys
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| key_hash | text | not null | Hashed key material |
| name | text | not null | Human label |
| last_used_at / revoked_at | timestamptz | null | Usage tracking |
company_memberships
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| company_id | uuid | FK, not null | Scoping |
| user_id | uuid | FK, not null | Member |
| role | text | not null | owner \| admin \| member |
principal_permission_grants
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| principal_type / principal_id | - | - | Who gets the grant |
| permission | text | - | Action/resource pair |
API Contracts
POST /api/auth/login, POST /api/auth/logout, GET /api/auth/me
Session management.
POST /companies/:companyId/invites, POST /invites/:inviteId/accept
Invite flow.
POST /cli-auth/challenge, POST /cli-auth/respond
CLI authentication.
POST /agents/:agentId/keys, POST /agents/:agentId/keys/:keyId/revoke
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. API key management.
Error Responses
| Status | Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Not authenticated |
| 403 | FORBIDDEN | Authenticated but not authorized for this action/resource |
| 404 | NOT_FOUND | Resource not found (company-scoped) |
Sequences
Authenticated request flow
Request → authz middleware → session cookie → resolve user → resolve company context → check permission → route handler or 403
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Auth modes | local_trusted + authenticated |
Supports both single-user and multi-user deployments |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. keys | Hashed at rest (bcrypt) | Standard practice for credential storage |
| Authz engine | Middleware-based action/resource gates | Consistent cross-cutting enforcement before route logic |
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. scoping | Request-level company_id context |
Prevents cross-company data access at framework level |
Risks and Unknowns
- Permission grant composition with role-based defaults has not been battle-tested in production.
- Session management and token refresh semantics need hardening for production.
Out of Scope
- Multi-board governance or role-based human permission granularity (Pro/Enterprise).
- OAuth2/OIDC federation for V1.
- SAML/SSO enterprise integration.
requirements
- What is the full taxonomy of principal permission grants and how do they compose with role-based defaults?
- How are instance user roles and company membership roles reconciled for cross-company board users?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. Skills
Overview
Skills are reusable, versioned bundles of agent capabilities that can be installed into a company and assigned to agents. The system includes a skills catalog for browsing available skills, per-company skill installation with version pinning, and team/agent skill assignments. Skills may include prompts, tools, MCPModel Context Protocol (served by `packages/mcp-server`) configurations, and other capability definitions. Operator-authored custom skills are also supported alongside catalog skills.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Browse catalog, install/pin skills to company, assign skills to agents/teams, manage custom skills |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Consume assigned skills at runtime via heartbeat context |
Functional Requirements
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support a skills catalog listing available skills with name, description, version, and metadata. | Must | The system shall support a skills catalog listing available skills with name, description, version, and metadata. |
| FR-02MustThe system shall support installing a skill into a company with version pinning. | Must | The system shall support installing a skill into a company with version pinning. |
| FR-03MustThe system shall support assigning installed skills to individual agents or teams. | Must | The system shall support assigning installed skills to individual agents or teams. |
| FR-04MustThe system shall include default skill bundles for common agent capabilities. | Must | The system shall include default skill bundles for common agent capabilities. |
| FR-05ShouldThe system shall support operator-authored custom skills alongside catalog skills. | Should | The system shall support operator-authored custom skills alongside catalog skills. |
| FR-06ShouldThe system shall support skill versioning and upgrades within a company. | Should | The system shall support skill versioning and upgrades within a company. |
| FR-07ShouldThe system shall support a teams catalog for pre-configured agent team templates. | Should | The system shall support a teams catalog for pre-configured agent team templates. |
| FR-08ShouldThe system shall resolve skill selections at heartbeat runtime, injecting only assigned skills. | Should | The system shall resolve skill selections at heartbeat runtime, injecting only assigned skills. |
Non-Functional Requirements
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01ShouldCustom skills must be validated before installation to prevent malicious content. | Should | Security | Custom skills must be validated before installation to prevent malicious content. |
| NFR-02ShouldSkill resolution at heartbeat time must not add more than 200ms overhead. | Should | Performance | Skill resolution at heartbeat time must not add more than 200ms overhead. |
Constraints
- Skills are company-scoped: each company has its own installed skill set.
- Default skills ship with the product; custom skills are operator-created.
- Team catalog entries may reference multiple skills and agents.
Acceptance Criteria
- FR-01MustThe system shall support a skills catalog listing available skills with name, description, version, and metadata.
- Given a board operator
- When they view the skills catalog
- Then available skills are listed with name, description, and version
- FR-02MustThe system shall support installing a skill into a company with version pinning.
- Given a company
- When a skill is installed
- Then the skill is available for assignment within that company
- FR-03MustThe system shall support assigning installed skills to individual agents or teams.
- Given an installed skill
- When it is assigned to an agent
- Then the agent receives that skill's capabilities at next heartbeat
Conflicts
None identified yet.
Open Questions
- What is the exact skill definition format (prompts, tools, MCPModel Context Protocol (served by `packages/mcp-server`), or a combination)?
- How do skill version upgrades handle backward compatibility?
- What is the team catalog's relationship to the org chart (are teams static templates or dynamic groupings)?
Specification: CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. Skills
Overview
Skills are versioned capability bundles. A central catalog lists available skills; companies install them with version pinning and assign them to agents or teams. At heartbeat runtime, the skill selection service resolves the set of skills active for the agent and injects them into the invocation context. The teams catalog provides pre-configured agent templates that bundle skills and agent configurations together.
Architecture
Catalog ──► GET /api/catalog/skills (public skill definitions)
│
CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. ──► POST /api/companies/:companyId/skills (install + pin version)
│
▼
company_skills (skill_id, company_id, version)
│
▼
agent skill assignments (agent_skills / runtime_skill_selections)
│
▼
HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. runtime → skill resolution → inject into context
Data Models
company_skills
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| company_id | uuid | FK, not null | Scoping |
| skill_id | text | not null | Catalog skill identifier |
| version | text | not null | Pinned version |
| config | jsonb | null | Per-install configuration |
runtime_skill_selections
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| agent_id | uuid | FK, not null | Target agent |
| skill_id | text | not null | Skill reference |
| enabled | boolean | not null | Active/inactive |
API Contracts
GET /api/companies/:companyId/skills-catalog
Browse available skills.
POST /api/companies/:companyId/skills
Install a skill for the company.
POST /api/companies/:companyId/skills-catalog/install-from-catalog
Install from catalog directly.
GET/DELETE /api/companies/:companyId/skills/:skillId
Manage installed skills.
GET /api/catalog/teams, POST /api/companies/:companyId/teams-catalog
Teams catalog management.
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Invalid skill reference or version |
| 404 | NOT_FOUND | Skill not found in catalog |
Sequences
Skill installation and assignment
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → browse catalog → install skill (pin version) → company_skills row
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → assign skill to agent → runtime_skill_selections row
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. heartbeat → resolve agent's enabled skills → inject into invocation context → agent executes with skill capabilities
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Skill resolution | Runtime selection join at heartbeat | Skills are evaluated per invocation; no agent-side state |
| Version pinning | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation.-specific version per skill | Companies upgrade on their own schedule |
| Catalog source | Built-in default skills + operator custom | Supports both out-of-box and tailored capability sets |
Risks and Unknowns
- Skill definition format is evolving; backward compatibility during catalog updates must be managed.
- Custom skill validation is critical for security but the validation rules are not fully defined.
- The relationship between the teams catalog and the org chart
/reports_totree needs documentation.
Out of Scope
- Public skill marketplace or community contributions (ClipHub).
- Skill dependency resolution (skills requiring other skills).
requirements
- What is the exact skill definition format (prompts, tools, MCP, or a combination)?
- How do skill version upgrades handle backward compatibility?
- What is the team catalog's relationship to the org chart (are teams static templates or dynamic groupings)?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. Hierarchy
Overview
Goals provide the company-wide alignment hierarchy that ties every task back to business mission. Each goal is a node in a tree (company root goal, team goals, agent goals) with a level, status, and optional owner agent. Goals are the first step in the Paperclip workflow: a board operator defines the company mission as a root goal and decomposes it down the org tree, giving every task a traceable purpose.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Create, edit, and restructure goal hierarchy; link goals to projects and issues |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Work within goal-aligned tasks; see goal context in heartbeat invocations |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Propose strategy that maps to goals (requires board approval) |
Functional Requirements
| ID | Priority | Requirement |
|---|---|---|
| FR-01Mustteam | Must | The system shall support CRUD of company-scoped goals with title, description, level (company | team | task), and status (planned | active | completed | cancelled). |
| FR-02MustThe system shall support a goal hierarchy via `parent_id` (nullable root) that forms a tree, with all goals scoped to the same company. | Must | The system shall support a goal hierarchy via parent_id (nullable root) that forms a tree, with all goals scoped to the same company. |
| FR-03MustThe system shall track goal ownership via `owner_agent_id`. | Must | The system shall track goal ownership via owner_agent_id. |
| FR-04MustGoals shall link to projects and issues: issues carry `goal_id`, projects carry `goal_id`, and the goal chain is traceable from task to company root. | Must | Goals shall link to projects and issues: issues carry goal_id, projects carry goal_id, and the goal chain is traceable from task to company root. |
| FR-05ShouldThe system shall support listing goals by company with ordering. | Should | The system shall support listing goals by company with ordering. |
| FR-06ShouldThe system shall create a default root company goal on company creation. | Should | The system shall create a default root company goal on company creation. |
Non-Functional Requirements
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustAll goal mutations must write `activity_log` entries. | Must | Auditability | All goal mutations must write activity_log entries. |
| NFR-02ShouldGoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. CRUD must meet the p95 < 250 ms latency target. | Should | Performance | GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. CRUD must meet the p95 < 250 ms latency target. |
Constraints
- Goals are company-scoped; cross-company goal references are not supported.
- A goal hierarchy is expected to be shallow (company root, team, task levels).
Acceptance Criteria
- FR-01Mustteam
- Given a company
- When a board operator creates a goal with title, level, and status
- Then a goal row exists scoped to that company
- FR-02MustThe system shall support a goal hierarchy via `parent_id` (nullable root) that forms a tree, with all goals scoped to the same company.
- Given a root goal
- When a child goal is created with
parent_idpointing to the root - Then the goal tree is correctly navigable from root to leaf
- FR-04MustGoals shall link to projects and issues: issues carry `goal_id`, projects carry `goal_id`, and the goal chain is traceable from task to company root.
- Given a goal
- When an issue or project is created with that
goal_id - Then the goal chain is traceable from the issue to the company root goal
- NFR-01MustAll goal mutations must write `activity_log` entries.
- Given any goal mutation (create, update, delete)
- When the mutation completes
- Then an
activity_logentry is written
Conflicts
None identified yet.
Open Questions
- Should goal deletion cascade to child goals (soft delete with orphan marking)?
- What is the exact default root goal creation behavior on company creation?
Specification: GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. Hierarchy
Overview
Goals are company-scoped tree nodes with level, status, and owner.
They form the alignment chain from company mission down to individual tasks.
The goals table uses parent_id for hierarchy and level for semantic tiering.
Projects and issues link to goals via goal_id foreign keys, enabling cost rollups and progress tracking by goal.
Architecture
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based./AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. ──► /api/goals (CRUD)
│
▼
goals table (company_id, parent_id, level, status, owner_agent_id)
│
├── issues.goal_id (task-to-goal link)
├── projects.goal_id (project-to-goal link)
└── activity_log (goal mutations)
Data Models
goals
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. identifier |
| company_id | uuid | FK companies, not null | Scoping |
| title | text | not null | GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. name |
| description | text | null | Optional description |
| level | text | not null, default task |
company | team | task |
| status | text | not null, default planned |
planned | active | completed | cancelled |
| parent_id | uuid | FK goals, null | Parent goal (null = root) |
| owner_agent_id | uuid | FK agents, null | Responsible agent |
| created_at / updated_at | timestamptz | not null | Timestamps |
API Contracts
GET /companies/:companyId/goals
List goals for a company.
POST /companies/:companyId/goals
Create a goal. Request body includes title, description, level, status, parentId, ownerAgentId.
GET /goals/:goalId
Get a single goal by ID.
PATCH /goals/:goalId
Update goal fields.
DELETE /goals/:goalId
Delete a goal (soft delete optional, hard delete board-only).
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Validation error on goal fields |
| 403 | UNAUTHORIZED | Caller lacks company access |
| 404 | NOT_FOUND | GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. not found |
Sequences
Create a goal hierarchy
Hirer → POST /companies/:id/goals (root level=company) → goals row
Hirer → POST /companies/:id/goals (parent=root, level=team) → child goal
IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. → issue.goal_id = childGoal.id → traceable chain: issue → childGoal → rootGoal
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Hierarchy model | Adjacency list (parent_id) |
Simple, well-understood for shallow trees |
| Level enum | company | team | task |
Matches the org hierarchy semantic tiers |
| Linking method | Foreign keys (goal_id on issues/projects) | Direct traceability without join tables |
Risks and Unknowns
- Deep goal hierarchies may cause slow recursive queries; materialized paths or closure tables may be needed at scale.
- The relationship between goal level and org tree (
reports_to) level needs precise documentation.
Out of Scope
- GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company.-based budget allocation (V1 uses agent/company budgets only).
- Automatic goal progress calculation from subtask completion.
requirements
- Should goal deletion cascade to child goals (soft delete with orphan marking)?
- What is the exact default root goal creation behavior on company creation?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Projects
Overview
Projects are company-scoped groupings that organize work around a specific initiative. Each project carries a name, description, status, lead agent, target date, environment config, and execution workspace policy. Projects link to goals for strategic alignment and support workspaces with runtime services for agent execution. Projects are a core organizational entity visible throughout the board UI and agent API.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Create and manage projects, assign lead agents, set targets, configure environments and workspaces |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Work within project context; access project env, workspace, and runtime services |
Functional Requirements
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support CRUD of company-scoped projects with name, description, status, lead agent, and target date. | Must | The system shall support CRUD of company-scoped projects with name, description, status, lead agent, and target date. |
| FR-02Mustactive | Must | Projects shall support status lifecycle (backlog | active | paused | completed | archived). |
| FR-03MustProjects shall link to a goal via `goal_id` for strategic alignment. | Must | Projects shall link to a goal via goal_id for strategic alignment. |
| FR-04MustThe system shall support project workspaces (named working directories with repo URL, CWD, runtime config, and metadata). | Must | The system shall support project workspaces (named working directories with repo URL, CWD, runtime config, and metadata). |
| FR-05MustThe system shall support project execution workspace policy (how execution worktrees are created for issues within the project). | Must | The system shall support project execution workspace policy (how execution worktrees are created for issues within the project). |
| FR-06ShouldThe system shall support project memberships tracking which users/agents have joined a project (for sidebar visibility). | Should | The system shall support project memberships tracking which users/agents have joined a project (for sidebar visibility). |
| FR-07ShouldThe system shall support project runtime services (dev servers, preview URLs) with desired state management. | Should | The system shall support project runtime services (dev servers, preview URLs) with desired state management. |
| FR-08ShouldThe system shall support project env configuration with secret ref resolution (project-level env overlay). | Should | The system shall support project env configuration with secret ref resolution (project-level env overlay). |
| FR-09ShouldThe system shall track project budget summaries (aggregate spend across project issues and agents). | Should | The system shall track project budget summaries (aggregate spend across project issues and agents). |
| FR-10MayThe system shall support plugin-managed projects (plugins can declare and manage project-level resources). | May | The system shall support plugin-managed projects (plugins can declare and manage project-level resources). |
Non-Functional Requirements
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustAll project mutations must write `activity_log` entries. | Must | Auditability | All project mutations must write activity_log entries. |
| NFR-02MustProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. env and secrets must be scoped to the project's company. | Must | Security | ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. env and secrets must be scoped to the project's company. |
| NFR-03ShouldProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. CRUD and workspace operations must meet latency targets. | Should | Performance | ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. CRUD and workspace operations must meet latency targets. |
Constraints
- Projects are company-scoped; cross-company project access is not permitted.
- A project links to exactly one goal (optional).
- ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. workspace runtime services are provider-dependent and may not be available in all deployment modes.
Acceptance Criteria
- FR-01MustThe system shall support CRUD of company-scoped projects with name, description, status, lead agent, and target date.
- Given a company
- When a project is created with name, status, and lead agent
- Then a project row exists scoped to the company
- FR-04MustThe system shall support project workspaces (named working directories with repo URL, CWD, runtime config, and metadata).
- Given a project
- When a project workspace is created with repo URL and CWD
- Then the workspace is available for agent execution
- FR-07ShouldThe system shall support project runtime services (dev servers, preview URLs) with desired state management.
- Given a project workspace
- When runtime services are configured with desired state
- Then services are started/stopped to match the desired state
- NFR-01MustAll project mutations must write `activity_log` entries.
- Given any project mutation
- When the mutation completes
- Then an
activity_logentry is written
Conflicts
None identified yet.
Open Questions
- How do project env overlays compose with agent env and routine env at runtime?
- What is the exact relationship between project workspaces and execution workspaces (git worktrees)?
- Are plugin-managed projects fully supported in V1 or deferred?
Specification: Projects
Overview
Projects are first-class company-scoped entities with status lifecycle, goal linkage, env configuration, workspaces, runtime services, and budget summaries.
The projects service (server/src/services/projects.ts, 1215 lines) handles CRUD, workspace management, runtime service orchestration, goal linking, and plugin-managed project resolution.
ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. workspaces provide named working directories with repo integration; project execution workspace policy governs how execution worktrees are created for issues within the project.
Architecture
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based./AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. ──► /api/projects (CRUD + workspaces + runtime)
│
▼
projects (company_id, goal_id, status, lead_agent_id, env)
│
├── project_goals (goal links)
├── project_memberships (user/agent join state)
├── project_workspaces (named working directories)
│ └── workspace_runtime_services (dev servers, preview URLs)
├── issues.project_id (issue-to-project link)
└── activity_log (project mutations)
Data Models
projects
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. identifier |
| company_id | uuid | FK companies, not null | Scoping |
| goal_id | uuid | FK goals, null | Strategic alignment |
| name | text | not null | ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. name |
| description | text | null | Optional description |
| status | text | not null, default backlog |
backlog | active | paused | completed | archived |
| lead_agent_id | uuid | FK agents, null | Responsible agent |
| target_date | date | null | Target completion date |
| color | text | null | UI color |
| icon | text | null | UI icon |
| env | jsonb | null | AgentEnvConfig (secret-aware env) |
| execution_workspace_policy | jsonb | null | Policy for execution worktree creation |
| archived_at / paused_at | timestamptz | null | Status timestamps |
| created_at / updated_at | timestamptz | not null | Timestamps |
project_workspaces
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. identifier |
| project_id | uuid | FK projects, not null | Parent project |
| name | text | not null | Human name |
| source_type | text | null | git, local, etc. |
| cwd | text | null | Working directory |
| repo_url / repo_ref / default_ref | text | null | Git source config |
| runtime_config | jsonb | null | WorkspaceRuntimeConfig |
| is_primary | boolean | default false | Primary workspace flag |
| visibility | text | default company |
Visibility scope |
API Contracts
GET /companies/:companyId/projects, POST /companies/:companyId/projects, GET /projects/:projectId, PATCH /projects/:projectId
Standard project CRUD. Create/update accept goal_id, status, lead_agent_id, target_date, color, icon, env.
ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. workspace endpoints
POST /projects/:projectId/workspaces, GET/PATCH /projects/:projectId/workspaces/:workspaceId for workspace management.
ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. runtime service control
Runtime service desired state management and control via workspace runtime endpoints.
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Validation error |
| 403 | UNAUTHORIZED | Caller lacks company access |
| 404 | NOT_FOUND | ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. not found |
Sequences
Create project with workspace
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → POST /projects (name, goal_id, lead_agent_id) → project row
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → POST /projects/:id/workspaces (name, repo_url, cwd) → workspace row
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. → issue within project → resolve execution workspace per policy → agent runs in workspace context
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. env | JSONB with secret ref syntax | Consistent with other env config patterns in Paperclip |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. model | Named project workspaces + execution worktree policy | Separates shared project state from isolated agent execution |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. linking | Direct goal_id FK on projects | Simple traceability without a join table |
| Runtime services | Desired-state model (start/stop to match config) | Declarative lifecycle management |
Risks and Unknowns
- Env overlay precedence (project env → routine env → runtime keys) needs clear documentation.
- ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. workspace isolation and concurrency semantics when multiple agents work in the same project.
- PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions.-managed project features may expand the project data model significantly.
Out of Scope
- Cross-company project sharing or federation.
- ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env.-level access control lists beyond company scoping (deferred to Pro/Enterprise).
requirements
- How do project env overlays compose with agent env and routine env at runtime?
- What is the exact relationship between project workspaces and execution workspaces (git worktrees)?
- Are plugin-managed projects fully supported in V1 or deferred?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Activity Audit Log
Overview
Every mutating action in Paperclip must be auditable. The activity log provides an immutable event stream recording actor, action, entity, and details for every mutation across all entities. It is the single source of truth for who did what, when, and on which resource. This feature covers the activity log backend, API, UI, and the conventions that ensure every mutation path writes an entry.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | View complete audit trail for all companies; filter by actor, entity, action, date range |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | See activity relevant to its own scope (own actions, actions on its issues) |
| Auditor / compliance | Verify that all mutations are recorded immutably and are tamper-evident |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustEvery mutating API request must write an `activity_log` entry recording actor type (agent/user/system), actor id, action, entity type, entity id, and JSONB details. | Must | Every mutating API request must write an activity_log entry recording actor type (agent/user/system), actor id, action, entity type, entity id, and JSONB details. |
| FR-02MustThe system shall expose a paginated GET endpoint returning activity log entries scoped to a company. | Must | The system shall expose a paginated GET endpoint returning activity log entries scoped to a company. |
| FR-03MustThe system shall support filtering activity log entries by actor, entity type, action type, and date range. | Must | The system shall support filtering activity log entries by actor, entity type, action type, and date range. |
| FR-04MustActivity entries must include a timestamp (`created_at`) and must be append-only (no updates, no deletes). | Must | Activity entries must include a timestamp (created_at) and must be append-only (no updates, no deletes). |
| FR-05ShouldThe system shall expose an activity summary/dashboard widget showing recent events. | Should | The system shall expose an activity summary/dashboard widget showing recent events. |
| FR-06ShouldActivity entries should include the IP address or origin context when available. | Should | Activity entries should include the IP address or origin context when available. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustActivity logImmutable audit trail for every mutating action (actor_type, action, entity, details). queries must complete within p95 < 500 ms even with 100k+ entries per company. | Must | Performance | Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). queries must complete within p95 < 500 ms even with 100k+ entries per company. |
| NFR-02MustEntries must never be modified or deleted; the table is append-only. | Must | Auditability | Entries must never be modified or deleted; the table is append-only. |
| NFR-03ShouldActivity logImmutable audit trail for every mutating action (actor_type, action, entity, details). retention and pruning strategy must be documented; old entries may be archived. | Should | Storage | Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). retention and pruning strategy must be documented; old entries may be archived. |
Constraints
- The activity_log table is append-only; no UPDATE or DELETE operations are permitted.
- Cross-company visibility must be enforced: board sees all, agents see only their own company.
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-01MustEvery mutating API request must write an `activity_log` entry recording actor type (agent/user/system), actor id, action, entity type, entity id, and JSONB details.
- Given a mutating API action (e.g., create agent, update issue status, approve request)
- When the action completes
- Then an
activity_logrow exists with correct actor, action, entity, and details
- FR-02MustThe system shall expose a paginated GET endpoint returning activity log entries scoped to a company.
- Given a company with 100+ activity entries
- When the board requests GET /activity?companyId=X
- Then entries are returned paginated, scoped to that company, newest first
- FR-04MustActivity entries must include a timestamp (`created_at`) and must be append-only (no updates, no deletes).
- Given an existing activity entry
- When an UPDATE or DELETE is attempted
- Then the operation is rejected (table-level RLS or app-level enforcement)
- NFR-01MustActivity logImmutable audit trail for every mutating action (actor_type, action, entity, details). queries must complete within p95 < 500 ms even with 100k+ entries per company.
- Given a company with 100k activity entries
- When a filtered query is executed
- Then response time is under 500 ms at p95
Conflicts
None identified yet.
Open Questions
- What is the archive/retention policy for old activity entries?
- Should activity entries include IP addresses or request metadata for security auditing?
Specification: Activity Audit Log
Overview
The activity log is an append-only event stream. Every route handler that mutates state calls the activity service to persist an entry. The API exposes filtered, paginated queries for the board UI and agent scopes. The table is indexed on company_id, created_at, actor_type, entity_type, and action for efficient filtering.
Architecture
Route handler (after mutation)
│
▼
activityService.log(actor, action, entity, details)
│
▼
activity_log table (append-only INSERT)
│
▼
GET /api/companies/:companyId/activity (paginated, filtered)
│
▼
Activity.tsx UI page (table with filters)
Data Models
activity_log
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, not null | Entry identifier |
| company_id | uuid | FK companies, not null | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. scoping |
| actor_type | enum | not null | agent | user | system |
| actor_id | uuid | not null | Who performed the action |
| action | text | not null | Action key (e.g. agent.create, issue.status_update, approval.decision) |
| entity_type | text | not null | Resource type (e.g. agent, issue, approval) |
| entity_id | uuid | not null | Resource identifier |
| details | jsonb | null, default {} |
Additional context (e.g. old/new status, field changes, payload excerpts) |
| ip_address | text | null | Origin IP when available |
| created_at | timestamptz | not null, default now() | When the action occurred |
API Contracts
GET /companies/:companyId/activity
Returns paginated activity entries.
Query Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| cursor | string | no | Pagination cursor |
| limit | int | no, default 50 | Max entries per page |
| actorType | string | no | Filter by actor type |
| action | string | no | Filter by action key prefix |
| entityType | string | no | Filter by resource type |
| from | ISO date | no | Start of date range |
| to | ISO date | no | End of date range |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| entries | array | Activity entry objects |
| nextCursor | string | Cursor for next page |
Error Responses
| Status | Code | Description |
|---|---|---|
| 403 | UNAUTHORIZED | Caller lacks company access |
Sequences
Logging a mutation
Route handler → validate input → mutate DB → activityService.log(actor, "issue.status_update", issueId, {from: "todo", to: "in_progress"}) → respond
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Storage | Append-only table with indexes | Simple, no external service needed for V1 |
| Pagination | Cursor-based | Consistent under write load |
| Actor modeling | Polymorphic (agent/user/system) with type discriminator | Single table, no joins needed |
Risks and Unknowns
- At very high volume, the activity_log table may need partition pruning or archival strategies.
Out of Scope
- Tamper-evident hashing or blockchain-style integrity chains (deferred)
- Real-time streaming of activity events (deferred)
requirements
- What is the archive/retention policy for old activity entries?
- Should activity entries include IP addresses or request metadata for security auditing?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Dashboard
Overview
The board dashboard is the primary landing page for human operators. It provides an at-a-glance overview of company health: agent status counts, issue state counts, month-to-date spend and budget utilization, and pending approval counts. It is the first thing a board operator sees when logging in and should surface actionable information immediately.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | See company status at a glance; identify stalled agents, blocked issues, budget overruns, and pending approvals |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe dashboard shall display agent status counts (active, running, paused, error, terminated) for the selected company. | Must | The dashboard shall display agent status counts (active, running, paused, error, terminated) for the selected company. |
| FR-02MustThe dashboard shall display issue state counts (open, in_progress, in_review, done, blocked, cancelled) for the selected company. | Must | The dashboard shall display issue state counts (open, in_progress, in_review, done, blocked, cancelled) for the selected company. |
| FR-03MustThe dashboard shall display month-to-date spend and budget utilization percentage for the selected company. | Must | The dashboard shall display month-to-date spend and budget utilization percentage for the selected company. |
| FR-04MustThe dashboard shall display pending approval counts. | Must | The dashboard shall display pending approval counts. |
| FR-05ShouldThe dashboard shall display recent activity entries. | Should | The dashboard shall display recent activity entries. |
| FR-06ShouldThe dashboard shall display agent and issue counts visible from the org tree. | Should | The dashboard shall display agent and issue counts visible from the org tree. |
| FR-07ShouldThe dashboard shall update periodically or support refresh. | Should | The dashboard shall update periodically or support refresh. |
| FR-08MayThe dashboard shall support a full-screen wallboard/live view mode for display monitors. | May | The dashboard shall support a full-screen wallboard/live view mode for display monitors. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustDashboard load must complete within p95 < 500 ms. | Must | Performance | Dashboard load must complete within p95 < 500 ms. |
| NFR-02ShouldDashboard should gracefully handle temporary unavailability of any data source (e.g. budget service down). | Should | Availability | Dashboard should gracefully handle temporary unavailability of any data source (e.g. budget service down). |
Constraints
- Dashboard is read-only; no mutations are performed from dashboard endpoints.
- Data is aggregated at read time; no materialized views required for V1.
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-01MustThe dashboard shall display agent status counts (active, running, paused, error, terminated) for the selected company.
- Given a company with agents in various states
- When the dashboard loads
- Then counts for each agent status are displayed correctly
- FR-03MustThe dashboard shall display month-to-date spend and budget utilization percentage for the selected company.
- Given a company with recorded cost events
- When the dashboard loads
- Then month-to-date spend and budget utilization are shown
- NFR-01MustDashboard load must complete within p95 < 500 ms.
- Given a company with 1k+ tasks and 50+ agents
- When the dashboard is requested
- Then response time is under 500 ms at p95
Conflicts
None identified yet.
Open Questions
- Should the dashboard include goal hierarchy progress or is that a separate view?
Specification: BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Dashboard
Overview
The dashboard aggregates data across multiple entity types into a single summary payload. The server endpoint performs several parallel aggregation queries and returns a structured response. The React UI renders cards/stats for each section. The full-screen live/wallboard variant uses the same endpoint with auto-refresh.
Architecture
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → GET /api/companies/:companyId/dashboard
│
▼
Parallel queries:
├─ agent status counts (agents table, GROUP BY status)
├─ issue state counts (issues table, GROUP BY status)
├─ cost summary (cost_events aggregation, current month)
├─ pending approvals count
└─ recent activity (activity_log, last N entries)
│
▼
DashboardPayload → Dashboard.tsx / DashboardLive.tsx
Data Models
No dedicated dashboard table. All data is aggregated at read time from existing entity tables: agents, issues, cost_events, approvals, activity_log.
API Contracts
GET /companies/:companyId/dashboard
Returns the dashboard summary.
Query Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| companyId | uuid | path | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. to scope to |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| agentCounts | object | {active, running, paused, error, terminated, idle, pending_approval} |
| issueCounts | object | {backlog, todo, in_progress, in_review, done, blocked, cancelled} |
| spend | object | {spentCents, budgetCents, utilizationPct} |
| pendingApprovals | int | Count of pending approvals |
| recentActivity | array | Last 10 activity entries |
Error Responses
| Status | Code | Description |
|---|---|---|
| 403 | UNAUTHORIZED | Caller lacks company access |
| 404 | NOT_FOUND | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. not found |
Sequences
Dashboard load
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. navigates to company → GET /dashboard → parallel DB aggregations → render cards
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Aggregation | Read-time parallel queries | Simple V1; no materialized view maintenance |
| UI | Server-rendered payload, React cards | Separation of concerns; payload can evolve independently |
Risks and Unknowns
- Dashboard load may degrade at large scale; materialized views or caching may be needed later.
Out of Scope
- Real-time push updates (WebSocket/SSEServer-Sent Events (deferred realtime transport)) for live dashboard (deferred)
- Custom widget configuration per user
requirements
- Should the dashboard include goal hierarchy progress or is that a separate view?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Inbox
Overview
The inbox is a user-centric aggregated view of work items that need attention. It surfaces issues assigned to the user, recent activity, blocked items, unread items, and join request queues. Users can dismiss items from the inbox without affecting the underlying entities.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | See all work items requiring attention; filter by mine/recent/unread/blocked/all; manage join requests |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Has an implicit inbox via its assigned issues |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe inbox shall display issues assigned to the current user, grouped by status. | Must | The inbox shall display issues assigned to the current user, grouped by status. |
| FR-02MustThe inbox shall show unread issues and track read state per issue per user. | Must | The inbox shall show unread issues and track read state per issue per user. |
| FR-03MustThe inbox shall surface blocked issues. | Must | The inbox shall surface blocked issues. |
| FR-04MustThe inbox shall surface pending join requests for the company. | Must | The inbox shall surface pending join requests for the company. |
| FR-05ShouldThe inbox shall support dismissal of inbox items (local to the user, does not affect underlying issues). | Should | The inbox shall support dismissal of inbox items (local to the user, does not affect underlying issues). |
| FR-06ShouldThe inbox shall support filtering by view: mine, recent, unread, blocked, all. | Should | The inbox shall support filtering by view: mine, recent, unread, blocked, all. |
| FR-07ShouldThe inbox shall support archival of issues from the inbox view. | Should | The inbox shall support archival of issues from the inbox view. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustInbox queries must load within p95 < 500 ms. | Must | Performance | Inbox queries must load within p95 < 500 ms. |
| NFR-02ShouldInbox views should update when the underlying issue state changes. | Should | Usability | Inbox views should update when the underlying issue state changes. |
Constraints
- Inbox is per-user and per-company; users in different companies see different inboxes.
- Dismissal and read state are per-user settings, not entity properties.
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-01MustThe inbox shall display issues assigned to the current user, grouped by status.
- Given a user with assigned issues in various states
- When the inbox loads with view=mine
- Then only issues assigned to that user are shown
- FR-02MustThe inbox shall show unread issues and track read state per issue per user.
- Given a user who has not read certain issues
- When the inbox loads
- Then unread issues are visually distinguished
- NFR-01MustInbox queries must load within p95 < 500 ms.
- Given a user with 500+ issues in their scope
- When the inbox is requested
- Then response time is under 500 ms at p95
Conflicts
None identified yet.
Open Questions
- Should the inbox include activity log entries, or is it purely issue-focused?
Specification: Inbox
Overview
The inbox service aggregates issues relevant to the current user. It queries the issues table filtered by assignee and company, applies read-state and dismissal tracking, and supports archival. Join request queue is surfaced separately. Dismissal and read state are stored in per-user tables (inbox_dismissals, issue_read_states, issue_inbox_archives).
Architecture
User → GET /api/inbox?view=mine|recent|unread|blocked|all
│
▼
inbox service:
├─ issues query (filtered by view)
├─ read state join (issue_read_states)
├─ dismissal filter (inbox_dismissals)
├─ join request queue (join_requests)
└─ archival (issue_inbox_archives)
│
▼
Inbox.tsx (tabs: mine, recent, unread, blocked, all)
Data Models
issue_read_states
| Field | Type | Constraints | Description |
|---|---|---|---|
| user_id | uuid | PK, not null | User who read |
| issue_id | uuid | PK, not null | IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. read |
| read_at | timestamptz | not null | When first read |
| last_seen_comment_at | timestamptz | null | Last read comment timestamp |
inbox_dismissals
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| user_id | uuid | FK, not null | User who dismissed |
| issue_id | uuid | FK, not null | Dismissed issue |
| dismissed_at | timestamptz | not null | When dismissed |
issue_inbox_archives
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| user_id | uuid | FK, not null | User who archived |
| issue_id | uuid | FK, not null | Archived issue |
| archived_at | timestamptz | not null | When archived |
API Contracts
GET /inbox
Returns inbox items.
Query Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| view | string | no, default mine | mine | recent | unread | blocked | all |
| companyId | uuid | yes | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. scope |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| items | array | Inbox item objects |
| joinRequests | array | Pending join requests (board only) |
POST /inbox/dismiss
Dismiss an inbox item.
Error Responses
| Status | Code | Description |
|---|---|---|
| 403 | UNAUTHORIZED | Caller lacks company access |
| 404 | NOT_FOUND | IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. not found |
Sequences
Inbox load
User navigates to inbox → GET /inbox?companyId=X&view=mine → query issues WHERE assignee=userId → filter out dismissed & archived → join read state → return items
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Read state | Separate table per user-issue pair | Simple, no field on issues table |
| Dismissal | Soft delete (table) | Reversible; no data loss |
Risks and Unknowns
- Read state tracking for users with hundreds of issues may require batch operations.
Out of Scope
- Real-time inbox updates via push notifications (deferred)
- Inbox for agents (agents use heartbeat-based task discovery)
requirements
- Should the inbox include activity log entries, or is it purely issue-focused?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Attachments & Work Products
Overview
Attachments allow agents and board operators to upload files to issues and comments. Work products are typed deliverables attached to issues, either backed by an artifact file or referencing a workspace file. The attachment system handles upload, storage (local disk or S3), inline serving, download, range requests for video content, and deletion.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Upload, view, download, and delete attachments on issues |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Upload work products (artifacts, reports) and reference workspace files |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support file uploads to issues and comments with an upload allowlist and per-company size limits. | Must | The system shall support file uploads to issues and comments with an upload allowlist and per-company size limits. |
| FR-02MustThe system shall support inline content serving with content-type negotiation. | Must | The system shall support inline content serving with content-type negotiation. |
| FR-03MustThe system shall support file downloads via a dedicated download endpoint. | Must | The system shall support file downloads via a dedicated download endpoint. |
| FR-04MustThe system shall support byte-range requests for video files (HTTP 206 Partial Content). | Must | The system shall support byte-range requests for video files (HTTP 206 Partial Content). |
| FR-05MustThe system shall support attachment deletion. | Must | The system shall support attachment deletion. |
| FR-06ShouldThe system shall support work products of type `artifact` (with attachment) and `workspace_file` (file reference). | Should | The system shall support work products of type artifact (with attachment) and workspace_file (file reference). |
| FR-07ShouldThe system shall support multiple storage providers: local disk and S3-compatible object storage. | Should | The system shall support multiple storage providers: local disk and S3-compatible object storage. |
| FR-08ShouldThe system shall scan uploads for security threats (malware scanning). | Should | The system shall scan uploads for security threats (malware scanning). |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustUploads must be validated against the allowlist and size limits before storage. | Must | Security | Uploads must be validated against the allowlist and size limits before storage. |
| NFR-02ShouldVideo range requests should support streaming without loading the entire file into memory. | Should | Performance | Video range requests should support streaming without loading the entire file into memory. |
Constraints
- Per-company
attachment_max_byteslimits total attachment size. - Storage provisioning is configured per deployment (local disk default, S3 optional).
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-01MustThe system shall support file uploads to issues and comments with an upload allowlist and per-company size limits.
- Given a valid file type within size limits
- When uploaded to an issue
- Then the file is stored and an attachment record is created
- FR-02MustThe system shall support inline content serving with content-type negotiation.
- Given an image attachment
- When the inline content URL is requested
- Then the image is served with the correct content type
- FR-04MustThe system shall support byte-range requests for video files (HTTP 206 Partial Content).
- Given a video attachment
- When a byte range is requested
- Then the server responds 206 with Content-Range and Accept-Ranges: bytes
- NFR-01MustUploads must be validated against the allowlist and size limits before storage.
- Given a disallowed file type or oversized file
- When upload is attempted
- Then the request is rejected with a descriptive error
Conflicts
None identified yet.
Open Questions
- What is the full allowlist of supported file types for V1?
- Should uploaded files be scanned for malware before storage?
Specification: Attachments & Work Products
Overview
Attachments are stored as assets records with provider routing (local disk or S3). Uploads go through allowlist and size validation. Inline serving respects content negotiation; download forces attachment disposition. Byte-range requests are handled for video playback. Work products link typed deliverables (artifact or workspace_file) to issues.
Architecture
Upload: POST /.../attachments (multipart) → asset validation → storage provider → assets table
Serve: GET /attachments/:id/content → provider read → stream to client (content-type, range)
Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file: POST /issues/:id/work-products → type=artifact|workspace_file → issue_work_products
Data Models
assets
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| company_id | uuid | FK, not null | Scoping |
| file_name | text | not null | Original filename |
| content_type | text | not null | MIME type |
| byte_size | int | not null | File size |
| storage_provider | text | not null | local_disk | s3 |
| storage_key | text | not null | Provider-specific key |
| uploaded_by_actor_type | text | not null | agent | user |
| uploaded_by_actor_id | uuid | not null | Who uploaded |
| created_at | timestamptz | not null | Upload timestamp |
issue_attachments
| Field | Type | Constraints | Description |
|---|---|---|---|
| issue_id | uuid | FK, PK | IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| asset_id | uuid | FK, PK | Asset |
| comment_id | uuid | FK, null | Optional comment link |
issue_work_products
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| issue_id | uuid | FK, not null | Parent issue |
| type | enum | not null | artifact | workspace_file |
| asset_id | uuid | FK, null | For artifact type |
| file_path | text | null | For workspace_file type |
| title | text | not null | Display title |
API Contracts
POST /companies/:companyId/issues/:issueId/attachments
Upload attachment to issue.
GET /attachments/:attachmentId/content
Inline content serving. Supports Range header for video.
DELETE /attachments/:attachmentId
Delete attachment.
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Disallowed file type or exceeds size limit |
| 404 | NOT_FOUND | Attachment not found |
| 403 | UNAUTHORIZED | Caller lacks company access |
Sequences
Upload and serve
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. → POST attachment (multipart) → validate allowlist + size → store via provider → assets + issue_attachments → return asset id
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → GET /attachments/:id/content (with Range) → provider read → 206/200 with content-type
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Storage abstraction | Provider pattern (local_disk, s3) | Extensible; local is free, S3 for production |
| Content serving | Direct from server | Simple V1; CDN offload deferred |
| Work products | Separate table from attachments | Different metadata for artifact vs workspace file |
Risks and Unknowns
- Very large file uploads may need streaming/chunked upload support.
- Malware scanning integration is not implemented; local disk storage means malware could be served.
Out of Scope
- CDN offload for attachment serving (deferred)
- Image thumbnail/resize generation (deferred)
- Drag-and-drop file organization in the UI
requirements
- What is the full allowlist of supported file types for V1?
- Should uploaded files be scanned for malware before storage?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Environment Management
Overview
Environments are named configuration contexts (e.g., staging, production) that hold key/value bindings with secret reference resolution. They are scoped to a company and can be leased to agents for execution context. Environment variables are resolved in a defined overlay order: project env → environment env → routine env → Paperclip runtime-owned keys.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Create, configure, and manage environments; assign environments to projects |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Receive resolved environment variables at runtime |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support named environments scoped to a company (e.g., staging, production). | Must | The system shall support named environments scoped to a company (e.g., staging, production). |
| FR-02MustEnvironments shall hold key/value pairs and support secret reference syntax (`${{ secrets.key }}`). | Must | Environments shall hold key/value pairs and support secret reference syntax (${{ secrets.key }}). |
| FR-03MustThe system shall support environment leases that grant an agent access to an environment for a period of time. | Must | The system shall support environment leases that grant an agent access to an environment for a period of time. |
| FR-04ShouldThe system shall expose an environment selection flow for agents requesting execution context. | Should | The system shall expose an environment selection flow for agents requesting execution context. |
| FR-05ShouldThe system shall resolve environment variables at execution time in the defined overlay order. | Should | The system shall resolve environment variables at execution time in the defined overlay order. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustSecret references within environment values must be resolved at runtime, not stored in plaintext in the environment definition. | Must | Security | Secret references within environment values must be resolved at runtime, not stored in plaintext in the environment definition. |
| NFR-02ShouldEnvironment access and lease creation should be logged in the activity log. | Should | Audibility | Environment access and lease creation should be logged in the activity log. |
Constraints
- Environments are optional; agents can run without an environment lease.
- Environment values are resolved at heartbeat time, not stored as plaintext.
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-01MustThe system shall support named environments scoped to a company (e.g., staging, production).
- Given a company
- When an environment is created with name and key/value pairs
- Then the environment is stored and scoped to the company
- FR-03MustThe system shall support environment leases that grant an agent access to an environment for a period of time.
- Given an environment and an agent
- When a lease is created
- Then the agent can resolve the environment variables at runtime
- NFR-01MustSecret references within environment values must be resolved at runtime, not stored in plaintext in the environment definition.
- Given an environment with a secret reference value
- When the environment definition is read
- Then the secret reference syntax is stored, not the resolved secret value
Conflicts
None identified yet.
Open Questions
- What is the exact lease duration policy and renewal mechanism?
- Can multiple agents share the same environment lease?
Specification: Environment Management
Overview
Environments are company-scoped named configurations with key/value bindings. Values use the ${{ secrets.key }} syntax for secret references, resolved at runtime by the secrets service. Environment leases grant agents temporary access. The resolver applies values in a defined overlay chain: project env → environment env → routine env → Paperclip runtime-owned keys.
Architecture
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → /api/companies/:companyId/environments (CRUD)
│
▼
environments table (company_id, name, env JSONB)
│
▼
environment_leases (agent_id, environment_id, lease_until)
│
▼
Runtime: heartbeat execution → resolve env overlay chain → inject into agent context
Data Models
environments
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| company_id | uuid | FK, not null | Scoping |
| name | text | not null | Environment name (e.g. staging, production) |
| env | jsonb | not null, default {} |
Key/value bindings with secret ref syntax |
| created_at | timestamptz | not null | Creation timestamp |
| updated_at | timestamptz | not null | Last update |
environment_leases
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | - |
| environment_id | uuid | FK, not null | Leased environment |
| agent_id | uuid | FK, not null | Leasing agent |
| project_id | uuid | FK, null | Optional project scope |
| lease_start | timestamptz | not null | When lease starts |
| lease_until | timestamptz | not null | When lease expires |
| created_at | timestamptz | not null | Record creation |
API Contracts
GET/POST /companies/:companyId/environments
List and create environments.
PATCH /companies/:companyId/environments/:envId
Update environment configuration.
POST /companies/:companyId/environments/:envId/leases
Create an environment lease.
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Invalid environment config |
| 403 | UNAUTHORIZED | Caller lacks company access |
| 404 | NOT_FOUND | Environment not found |
Sequences
Environment resolution at heartbeat
HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. execution → resolve project env → resolve environment env (resolve secret refs via secrets service) → resolve routine env → apply Paperclip runtime keys → inject into agent context
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Secret refs | ${{ secrets.key }} syntax, resolved at runtime |
Secrets never stored in plaintext in environment config |
| Overlay order | ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. → Environment → RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. → Runtime | Predictable precedence; most specific wins |
Risks and Unknowns
- Runtime resolution of secret references may add latency to heartbeat startup.
- LeaseTime-limited ownership claim on a pipeline case preventing concurrent work expiry handling: what happens when a lease expires mid-execution?
Out of Scope
- Environment variable inheritance between environments (e.g., production inherits from staging)
- Environment variable diffing or change tracking
requirements
- What is the exact lease duration policy and renewal mechanism?
- Can multiple agents share the same environment lease?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Team Catalog
Overview
The team catalog provides pre-built team templates with agent roles, skills, and configurations that can be installed into a company. It accelerates onboarding by letting operators deploy a fully-configured team (CEO, engineers, support agents, etc.) with a few clicks instead of manually creating each agent and assigning skills.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Browse, preview, and install team templates into their company |
| New company operator | Rapidly bootstrap a company from a template |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall expose a catalog of pre-built team templates. | Must | The system shall expose a catalog of pre-built team templates. |
| FR-02MustEach team template shall define agent roles, titles, reporting relationships, adapter configs, and skills. | Must | Each team template shall define agent roles, titles, reporting relationships, adapter configs, and skills. |
| FR-03MustThe system shall support installing a team template into a company, creating all defined agents. | Must | The system shall support installing a team template into a company, creating all defined agents. |
| FR-04ShouldThe system shall support team template discovery with search and filtering. | Should | The system shall support team template discovery with search and filtering. |
| FR-05ShouldThe system shall report installation results (agents created, errors encountered). | Should | The system shall report installation results (agents created, errors encountered). |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01ShouldTemplate installation should complete within a few seconds. | Should | Usability | Template installation should complete within a few seconds. |
Constraints
- Team templates are read-only catalogs; modifications must be done after installation.
- Installation respects company governance settings (e.g.,
require_board_approval_for_new_agents).
Acceptance Criteria
Every FR and NFR shall have at least one acceptance criterion.
- FR-01MustThe system shall expose a catalog of pre-built team templates.
- Given the team catalog endpoint
- When requested
- Then a list of available templates is returned
- FR-03MustThe system shall support installing a team template into a company, creating all defined agents.
- Given a selected team template and a target company
- When installation is requested
- Then all defined agents are created in the company with correct roles and reporting structure
Conflicts
None identified yet.
Open Questions
- Who maintains and publishes the team templates (Paperclip core vs. community)?
- Are team templates versioned?
Specification: Team Catalog
Overview
Team templates are JSON/YAML definitions of agent org trees with role assignments, adapter configs, and skill links. The catalog endpoint returns available templates. Installation creates agents in the target company by iterating the template definition and calling the agent creation service for each entry.
Architecture
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → GET /api/teams-catalog (list templates)
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → POST /api/companies/:companyId/teams-catalog/install/:templateId (install)
│
▼
teams-catalog service → iterate template agents → create agents via agent service
│
▼
agents created in company (org tree, configs, skills)
Data Models
No dedicated DB tables for team templates. Templates may be loaded from files or fetched from a remote catalog source. Installation results are returned inline.
API Contracts
GET /teams-catalog
List available team templates.
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| templates | array | Template objects with id, name, description, agent count |
POST /companies/:companyId/teams-catalog/install/:templateId
Install a team template into a company.
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| created | int | Number of agents created |
| errors | array | List of errors encountered |
Error Responses
| Status | Code | Description |
|---|---|---|
| 403 | UNAUTHORIZED | Caller lacks company access |
| 404 | NOT_FOUND | Template not found |
Sequences
Template installation
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. → select template → POST install → validate template → for each agent definition: createAgent() → report results
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Template source | Loaded from files/packages | Simple V1; no remote catalog sync needed |
| Installation | Sequential agent creation | Simpler than batch; errors are scoped per agent |
Risks and Unknowns
- Installation may fail partway through if some agents fail to create; cleanup/rollback semantics needed.
- Skill references in templates may reference skills that do not exist in the target company.
Out of Scope
- User-customizable team templates (modify template before install)
- Community template marketplace (ClipHub) — deferred
requirements
- Who maintains and publishes the team templates (Paperclip core vs. community)?
- Are team templates versioned?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Pipelines
Overview
Pipelines provide structured, stage-based workflows for tracking work items (cases) through defined lifecycle stages (working, review, done, cancelled) with configurable transitions, automated execution agents, and full event audit trails. Cases carry structured fields, workspace references, parent/child hierarchy, issue links, and lease-based ownership. The pipeline system integrates with agents for automated stage transitions and with the heartbeat execution system for automation runs.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Define pipelines with stages and transitions; view and manage cases; configure automation agents |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Claim cases, execute automation workflows, suggest/perform stage transitions |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support creating pipelines with a key, name, description, project association, and transition enforcement flag. | Must | The system shall support creating pipelines with a key, name, description, project association, and transition enforcement flag. |
| FR-02MustThe system shall support pipeline stages with kind (working, review, done, cancelled), config, and position ordering. | Must | The system shall support pipeline stages with kind (working, review, done, cancelled), config, and position ordering. |
| FR-03MustThe system shall support pipeline transitions defining allowed moves between stages. | Must | The system shall support pipeline transitions defining allowed moves between stages. |
| FR-04MustThe system shall support creating cases within a pipeline with title, summary, structured fields, and stage assignment. | Must | The system shall support creating cases within a pipeline with title, summary, structured fields, and stage assignment. |
| FR-05MustThe system shall support case lifecycle with lease-based ownership for agent claiming and releasing. | Must | The system shall support case lifecycle with lease-based ownership for agent claiming and releasing. |
| FR-06MustThe system shall record full event history for each case (ingested, transitioned, claimed, reviewed, automated actions, blockers, issues linked). | Must | The system shall record full event history for each case (ingested, transitioned, claimed, reviewed, automated actions, blockers, issues linked). |
| FR-07MustThe system shall support case workspace references linking cases to execution workspaces. | Must | The system shall support case workspace references linking cases to execution workspaces. |
| FR-08ShouldThe system shall support pipeline automation with retry plans, agent assignment, and heartbeat-run integration. | Should | The system shall support pipeline automation with retry plans, agent assignment, and heartbeat-run integration. |
| FR-09ShouldThe system shall support case-to-issue linking for tracking work products. | Should | The system shall support case-to-issue linking for tracking work products. |
| FR-10ShouldThe system shall support case parent/child hierarchy with version tracking. | Should | The system shall support case parent/child hierarchy with version tracking. |
| FR-11ShouldThe system shall support pipeline settings in a dedicated UI page. | Should | The system shall support pipeline settings in a dedicated UI page. |
| FR-12MayThe system shall support transition suggestion with agent-provided rationale and confidence scoring. | May | The system shall support transition suggestion with agent-provided rationale and confidence scoring. |
Non-Functional Requirements
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustAll pipeline operations must be company-scoped and enforce company access checks. | Must | Security | All pipeline operations must be company-scoped and enforce company access checks. |
| NFR-02ShouldCase events must provide a complete audit trail of all state changes. | Should | Observability | Case events must provide a complete audit trail of all state changes. |
Constraints
- PipelineA stage-based workflow definition with ordered stages and allowed transitions keys must be unique within a company.
- Stage kinds are constrained to working, review, done, cancelled.
- Every case belongs to exactly one pipeline and one stage.
Acceptance Criteria
- FR-01MustThe system shall support creating pipelines with a key, name, description, project association, and transition enforcement flag.
- Given A board operator
- When They create a pipeline with key, name, and project
- Then The pipeline is persisted and accessible via the API
- FR-02MustThe system shall support pipeline stages with kind (working, review, done, cancelled), config, and position ordering.
- Given A pipeline exists
- When Stages are added with kinds and positions
- Then Stages are ordered and kind-validated
- FR-03MustThe system shall support pipeline transitions defining allowed moves between stages.
- Given A pipeline has stages
- When Transitions are defined between them
- Then Only allowed transitions are permitted
- FR-04MustThe system shall support creating cases within a pipeline with title, summary, structured fields, and stage assignment.
- Given A pipeline exists
- When A case is created with title and fields
- Then The case is placed in the initial stage
- FR-05MustThe system shall support case lifecycle with lease-based ownership for agent claiming and releasing.
- Given A case exists
- When An agent claims it
- Then A lease with expiry is created; concurrent claims are prevented
- FR-06MustThe system shall record full event history for each case (ingested, transitioned, claimed, reviewed, automated actions, blockers, issues linked).
- Given A case undergoes state changes
- When Events occur (transition, claim, review)
- Then Events are recorded with actor, type, and payload
- NFR-01MustAll pipeline operations must be company-scoped and enforce company access checks.
- Given A pipeline belongs to company A
- When An agent from company B accesses it
- Then Access is denied
Conflicts
None identified yet.
Open Questions
- How does pipeline automation interact with the existing heartbeat execution and routine scheduling systems?
- Are pipeline cases expected to generate issues automatically, or is issue linking manual?
Specification: Pipelines
Overview
The pipeline system provides structured stage-based workflow management for tracking work items. It defines pipelines with ordered stages and allowed transitions, and cases that flow through those stages. Automated agents can claim, transition, and execute work on cases. The system records every state change as a typed event for full auditability.
Architecture
The pipeline system lives in server/src/services/pipelines.ts (5158 lines) with aggregation support in pipelines-aggregation.ts and conversation context in pipeline-conversation-context.ts. UI pages at ui/src/pages/Pipelines.tsx and PipelineSettings.tsx. Route file at server/src/routes/pipelines.ts (2913 lines). The system uses DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. for DB access and integrates with the heartbeat/issue assignment wakeup system for automation.
Data Models
pipelines
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | PipelineA stage-based workflow definition with ordered stages and allowed transitions identifier |
| company_id | uuid | FK to companies, not null | Owning company |
| project_id | uuid | FK to projects, nullable | Associated project |
| key | text | not null, unique per company | Unique pipeline key |
| name | text | not null | Display name |
| description | text | nullable | Optional description |
| enforce_transitions | boolean | not null, default false | Whether to enforce allowed transitions |
| created_by_user_id | text | nullable | Creating user |
| created_by_agent_id | uuid | FK to agents, nullable | Creating agent |
| archived_at | timestamp | nullable | Soft-delete timestamp |
pipeline_stages
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | Stage identifier |
| pipeline_id | uuid | FK to pipelines, not null | Parent pipeline |
| key | text | not null, unique per pipeline | Stage key |
| name | text | not null | Display name |
| kind | text | not null, check (working, review, done, cancelled) | Stage classification |
| position | integer | not null | Display order |
| config | jsonb | not null, default {} | Stage-specific configuration |
pipeline_transitions
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | Transition identifier |
| pipeline_id | uuid | FK to pipelines, not null | Parent pipeline |
| from_stage_id | uuid | FK to pipeline_stages, not null | Source stage |
| to_stage_id | uuid | FK to pipeline_stages, not null | Target stage |
| label | text | nullable | Display label |
pipeline_cases
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | Case identifier |
| company_id | uuid | FK to companies, not null | Owning company |
| pipeline_id | uuid | FK to pipelines, not null | Parent pipeline |
| stage_id | uuid | FK to pipeline_stages, not null | Current stage |
| case_key | text | not null | Unique case key |
| title | text | not null | Case title |
| summary | text | nullable | Optional summary |
| fields | jsonb | not null, default {} | Structured case fields |
| workspace_ref | jsonb | nullable | Reference to execution workspace |
| parent_case_id | uuid | FK self, nullable | Parent case in hierarchy |
| version | integer | not null, default 1 | Case version for concurrency |
pipeline_case_events
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | Event identifier |
| company_id | uuid | FK to companies, not null | Owning company |
| case_id | uuid | FK to pipeline_cases, not null | Parent case |
| type | text | not null (ingested, transitioned, claimed, etc.) | Event type |
| actor_type | text | not null | User, agent, or system |
| payload | jsonb | not null, default {} | Event-specific data |
API Contracts
GET /api/pipelines
Returns pipelines for a company.
POST /api/pipelines
Create a new pipeline.
GET /api/pipelines/:id/cases
List cases in a pipeline with optional stage filter.
POST /api/pipelines/:id/cases
Create a new case in a pipeline.
POST /api/pipelines/cases/:id/transition
Transition a case to a new stage.
POST /api/pipelines/cases/:id/claim
Claim a case with lease-based ownership.
Sequences
Case Lifecycle
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. → POST claim → Server checks lease availability → DB assigns lease → Returns lease token
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. → POST transition → Server validates transition allowed → DB updates stage → Records event
System → Automation trigger → Server executes automation logic → DB updates case → Records event
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Case ownership | LeaseTime-limited ownership claim on a pipeline case preventing concurrent work-based with expiry | Prevents abandoned cases blocking progress |
| Stage kinds | Fixed set (working, review, done, cancelled) | Simple workflow model for the initial implementation |
| Event sourcing | Typed event log on pipeline_cases | Full audit trail without complex event sourcing infrastructure |
| Automation | Integration with heartbeat/run system | Reuses existing agent invocation infrastructure |
Risks and Unknowns
- PipelineA stage-based workflow definition with ordered stages and allowed transitions automation may have complex retry and error recovery requirements not yet fully understood.
- Integration between pipeline cases and the issue/task system needs clearer semantics.
- Performance of case event queries at scale (many events per case) is untested.
Out of Scope
- Drag-and-drop pipeline builder UI (deferred)
- Complex branching workflows beyond linear stage progression
- PipelineA stage-based workflow definition with ordered stages and allowed transitions templates or sharing across companies
Test Plan: Pipelines
Scope
Tests cover pipeline and case CRUD, schema validation, pipeline health calculations, CLI parity, and end-to-end tutorial flow.
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | PipelineA stage-based workflow definition with ordered stages and allowed transitions health calculation returns correct status | PipelineA stage-based workflow definition with ordered stages and allowed transitions with cases in various stages | Health status enum |
| TC-2 | PipelineA stage-based workflow definition with ordered stages and allowed transitions schema validates correctly | PipelineA stage-based workflow definition with ordered stages and allowed transitions schema | Valid schema |
Files: packages/shared/src/pipeline-health.test.ts, packages/db/src/pipelines-schema.test.ts
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-3 | CLI pipeline commands work | Authenticated CLI | PipelineA stage-based workflow definition with ordered stages and allowed transitions CRUD operations succeed |
| TC-4 | Pipelines tutorial e2e flow | Fresh deployment | Complete tutorial flow passes |
Files: cli/src/__tests__/pipelines.test.ts, tests/e2e/pipelines-tutorial-flow.spec.ts
End-to-End Tests
| ID | Description | Steps | Expected Outcome |
|---|---|---|---|
| TC-5 | UI case pages render correctly | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. has pipeline cases | List and detail pages display case data |
Files: ui/src/pages/Cases.test.tsx, ui/src/pages/CaseDetail.test.tsx
Coverage Matrix
| Requirement | Test Coverage |
|---|---|
| FR-01MustThe system shall support creating pipelines with a key, name, description, project association, and transition enforcement flag. through FR-12MayThe system shall support transition suggestion with agent-provided rationale and confidence scoring. | TC-1, TC-2, TC-3 (unit + integration) |
| UI case management | TC-5 |
| E2E tutorial flow | TC-4 |
requirements
- How does pipeline automation interact with the existing heartbeat execution and routine scheduling systems?
- Are pipeline cases expected to generate issues automatically, or is issue linking manual?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Cloud Upstreams
Overview
Cloud Upstreams enable cross-instance synchronization of Paperclip companies. A local or self-hosted Paperclip instance can connect to a cloud Paperclip instance (or another peer instance) to export and sync company data including agents, skills, projects, routines, and issues. The system manages connection lifecycle, authentication via OAuth, encrypted credential storage, idempotent transfer runs with conflict detection, and reconciliation of synced runs on server startup.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Connect local instance to cloud; manage sync runs; view transfer history and conflicts |
| Cloud operator | Receive and integrate synced company data from local instances |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support initiating an OAuth-based connection to a remote Paperclip instance. | Must | The system shall support initiating an OAuth-based connection to a remote Paperclip instance. |
| FR-02MustThe system shall store connection credentials encrypted at rest. | Must | The system shall store connection credentials encrypted at rest. |
| FR-03MustThe system shall support listing configured cloud upstream connections per company. | Must | The system shall support listing configured cloud upstream connections per company. |
| FR-04MustThe system shall support executing transfer runs that export local company data and sync to the remote. | Must | The system shall support executing transfer runs that export local company data and sync to the remote. |
| FR-05MustThe system shall track run status, progress, warnings, and conflicts for each transfer run. | Must | The system shall track run status, progress, warnings, and conflicts for each transfer run. |
| FR-06MustThe system shall support dry-run mode for previewing transfer effects without applying them. | Must | The system shall support dry-run mode for previewing transfer effects without applying them. |
| FR-07MustThe system shall reconcile incomplete or interrupted runs on server startup. | Must | The system shall reconcile incomplete or interrupted runs on server startup. |
| FR-08ShouldThe system shall support viewing run reports with detailed transfer summaries. | Should | The system shall support viewing run reports with detailed transfer summaries. |
| FR-09ShouldThe system shall gate cloud upstream functionality behind an experimental feature flag. | Should | The system shall gate cloud upstream functionality behind an experimental feature flag. |
Non-Functional Requirements
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustConnection credentials must be encrypted at rest using instance-scoped sealing keys. | Must | Security | Connection credentials must be encrypted at rest using instance-scoped sealing keys. |
| NFR-02MustTransfers must use authenticated channels with token-based authorization scoped to the connecting instance. | Must | Security | Transfers must use authenticated channels with token-based authorization scoped to the connecting instance. |
| NFR-03ShouldTransfer runs must be idempotent to support safe retry after interruption. | Should | Reliability | Transfer runs must be idempotent to support safe retry after interruption. |
Constraints
- Cloud upstreams are gated behind the
enableCloudSyncexperimental feature flag. - Transfer runs use idempotency keys to prevent duplicate application.
- Connection tokens have configurable expiry and scope.
Acceptance Criteria
- FR-01MustThe system shall support initiating an OAuth-based connection to a remote Paperclip instance.
- Given A board operator
- When They initiate a connect flow with a remote URL
- Then An OAuth authorization request is created with a redirect URI
- FR-02MustThe system shall store connection credentials encrypted at rest.
- Given An OAuth connection is completed successfully
- When The access token is stored
- Then It is encrypted with the instance's sealing key
- FR-03MustThe system shall support listing configured cloud upstream connections per company.
- Given CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. A has configured upstream connections
- When The operator lists connections
- Then Only connections for company A are returned
- FR-07MustThe system shall reconcile incomplete or interrupted runs on server startup.
- Given An upstream run was interrupted during the previous server session
- When The server starts
- Then The run is reconciled to a terminal state
- NFR-01MustConnection credentials must be encrypted at rest using instance-scoped sealing keys.
- Given An upstream connection stores credential material
- When It is read from the database
- Then Private keys and tokens are encrypted
Conflicts
None identified yet.
Open Questions
- Should cloud upstreams support bidirectional sync, or is it export-only from local to cloud?
- How are conflicts resolved when both sides have modified the same entity?
Specification: Cloud Upstreams
Overview
Cloud Upstreams provide OAuth-based cross-instance company synchronization. The system connects a local Paperclip instance to a remote (cloud) instance, allowing export and sync of company data. Connection secrets are encrypted at rest, transfers are idempotent and tracked as runs with progress, warnings, and conflict reporting. The feature is gated behind an experimental flag.
Architecture
The cloud upstream system consists of:
- server/src/routes/cloud-upstreams.ts — REST API routes for connection and run management
- server/src/services/cloud-upstreams.ts — Service layer handling OAuth flow, transfer execution, encryption, and reconciliation
- ui/src/pages/CloudUpstream.tsx — UI for managing connections and viewing runs
- packages/db/src/schema/cloud_upstreams.ts — DB schema for connections and run storage
Data Models
cloud_upstream_connections
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | Connection identifier |
| company_id | uuid | FK to companies, not null | Owning company |
| remote_url | text | not null | Remote instance URL |
| source_instance_id | text | not null | Local instance identifier |
| token_status | text | not null | Current token lifecycle status |
| scopes | text[] | not null, default [] | OAuth scopes |
| private_key_pem | text | not null (encrypted) | Instance private key |
| access_token | text | nullable (encrypted) | OAuth access token |
| target_stack_id | text | not null | Remote stack identifier |
| target_company_id | text | not null | Remote company identifier |
| target_origin | text | not null | Remote origin URL |
| target_schema_major | integer | not null | Remote schema version |
cloud_upstream_runs
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | Run identifier |
| connection_id | uuid | FK to connections, not null | Parent connection |
| company_id | uuid | FK to companies, not null | Owning company |
| status | text | not null | Run status (queued, running, succeeded, failed, etc.) |
| active_step | text | not null | Current transfer step |
| progress_percent | integer | not null, default 0 | Progress indicator |
| dry_run | boolean | not null, default false | Whether this is a dry run |
| summary | jsonb | not null, default [] | Transfer summary counts |
| warnings | jsonb | not null, default [] | Non-blocking warnings |
| conflicts | jsonb | not null, default [] | Transfer conflicts |
| events | jsonb | not null, default [] | Event log |
| report | jsonb | not null, default {} | Detailed run report |
| idempotency_key | text | not null | Idempotency key for safe retry |
API Contracts
GET /api/cloud-upstreams
List upstream connections for a company.
POST /api/cloud-upstreams/connect/start
Initiate OAuth connection flow. Returns a redirect URI for the authorization step.
POST /api/cloud-upstreams/connect/finish
Complete the OAuth flow with the authorization code.
POST /api/cloud-upstreams/:id/sync
Trigger a transfer/sync run, optionally in dry-run mode.
GET /api/cloud-upstreams/runs
List transfer runs with pagination.
Sequences
Connection Flow
Operator → POST connect/start → Service creates pending connection → Returns redirect URI
Operator completes OAuth in browser → Service receives callback → POST connect/finish
Service exchanges code for tokens → Encrypts and stores credentials → Connection is active
Transfer Flow
Operator → POST sync → Service builds export manifest → Exports entity chunks
Service sends chunks to remote → Remote applies/validates → Run completes with summary
On interruption → Server reconciler marks run as failed on next startup
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Credential encryption | Instance-scoped seal/unseal keys | Prevents plaintext credential storage in the database |
| Transfer idempotency | Idempotency key per run | Safe retry without duplicate application |
| Feature gating | Experimental flag | Feature is still maturing; disabled by default |
| Chunked export | Max chunk bytes per connection target | Handles large companies without memory issues |
Risks and Unknowns
- Schema version mismatch between local and remote instances may cause transfer failures.
- Conflict resolution strategy for modified-on-both-sides data is not fully implemented.
- Large companies may have slow initial syncs due to chunked transfer overhead.
- The feature is experimental and behind a flag; API stability is not guaranteed.
Out of Scope
- Bidirectional sync (local ← cloud) — currently export-only from local
- Realtime/continuous sync — only manual or triggered runs
- Conflict resolution UI — conflicts are reported but not resolvable in the current UI
Test Plan: Cloud Upstreams
Scope
Tests cover cloud upstream CLI commands and UI pages.
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-1 | CLI cloud commands work | Authenticated CLI | Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync CRUD operations succeed |
| TC-2 | UI cloud upstream page renders | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. has configured upstreams | Upstream list and detail display correctly |
Files: cli/src/__tests__/cloud.test.ts, ui/src/pages/CloudUpstream.test.tsx
Coverage Matrix
| Requirement | Test Coverage |
|---|---|
| FR-01MustThe system shall support initiating an OAuth-based connection to a remote Paperclip instance. through FR-09ShouldThe system shall gate cloud upstream functionality behind an experimental feature flag. | TC-1 (CLI), TC-2 (UI) |
requirements
- Should cloud upstreams support bidirectional sync, or is it export-only from local to cloud?
- How are conflicts resolved when both sides have modified the same entity?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Tools, Connections & Apps
Overview
The tools, connections, and apps subsystem provides a third-party integration framework that lets agents use external services (GitHub, Slack, file systems, etc.) through a managed tool gateway. It handles OAuth-based connection lifecycle, tool access policies with runtime enforcement, MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) gateway proxying, tool runtime profiles with capacity management, and an app gallery for discovering and installing pre-built integrations. Every tool call is governed by content guards, access policies, and runtime supervision to prevent data leaks and runaway executions.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Configure tool connections, set access policies, review usage metrics, manage the app gallery |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Discover available tools, invoke them through the gateway with appropriate auth and policy enforcement |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-01MustThe system shall support creating OAuth-based connections to third-party services with configurable scopes. | Must | The system shall support creating OAuth-based connections to third-party services with configurable scopes. |
| FR-02MustThe system shall support tool access policies that define which agents can use which tools under what conditions. | Must | The system shall support tool access policies that define which agents can use which tools under what conditions. |
| FR-03MustThe system shall proxy tool calls through an MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) gateway that enriches requests with connection auth and applies content guards. | Must | The system shall proxy tool calls through an MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) gateway that enriches requests with connection auth and applies content guards. |
| FR-04MustThe system shall support tool runtime profiles with capacity limits, timeout configuration, and concurrency management. | Must | The system shall support tool runtime profiles with capacity limits, timeout configuration, and concurrency management. |
| FR-05MustThe system shall record tool usage metrics per agent, per tool, per company for observability and audit. | Must | The system shall record tool usage metrics per agent, per tool, per company for observability and audit. |
| FR-06MustThe system shall support content guards that prevent sensitive data from being transmitted through tool calls. | Must | The system shall support content guards that prevent sensitive data from being transmitted through tool calls. |
| FR-07ShouldThe system shall support an app gallery for browsing, installing, and configuring pre-built tool integrations. | Should | The system shall support an app gallery for browsing, installing, and configuring pre-built tool integrations. |
| FR-08ShouldThe system shall support tool profile binding precedence for resolving which profile applies to an agent-project combination. | Should | The system shall support tool profile binding precedence for resolving which profile applies to an agent-project combination. |
| FR-09ShouldThe system shall support runtime slot leasing for managing concurrent tool process execution capacity. | Should | The system shall support runtime slot leasing for managing concurrent tool process execution capacity. |
| FR-10ShouldThe system shall support a local service supervisor for managing locally-running tool-related services. | Should | The system shall support a local service supervisor for managing locally-running tool-related services. |
| FR-11MayThe system shall provide a tools MCPModel Context Protocol (served by `packages/mcp-server`) server package for external access to Paperclip tool resources. | May | The system shall provide a tools MCPModel Context Protocol (served by `packages/mcp-server`) server package for external access to Paperclip tool resources. |
Non-Functional Requirements
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-01MustTool access must enforce company boundaries and principal permission grants. | Must | Security | Tool access must enforce company boundaries and principal permission grants. |
| NFR-02MustConnection credentials must be stored encrypted at rest (via the secrets system). | Must | Security | Connection credentials must be stored encrypted at rest (via the secrets system). |
| NFR-03MustEvery tool call must be logged with agent identity, tool name, duration, and outcome. | Must | Observability | Every tool call must be logged with agent identity, tool name, duration, and outcome. |
| NFR-04ShouldTool call proxying must complete within the configured timeout with a p99 latency under 5 seconds for standard tools. | Should | Performance | Tool call proxying must complete within the configured timeout with a p99 latency under 5 seconds for standard tools. |
Constraints
- Tool access policies are company-scoped and evaluated per-invocation.
- Connections are scoped to a company and may be shared across agents within that company.
- Content guardA filter that prevents sensitive data from being transmitted through tools rules are evaluated before the tool call is proxied to the external service.
Acceptance Criteria
- FR-01MustThe system shall support creating OAuth-based connections to third-party services with configurable scopes.
- Given A board operator with company access
- When They initiate an OAuth connection flow for a supported service
- Then The connection is created with the requested scopes and stored encrypted
- FR-02MustThe system shall support tool access policies that define which agents can use which tools under what conditions.
- Given A tool access policy exists for company A
- When An agent from company B attempts to use the tool
- Then Access is denied
- FR-03MustThe system shall proxy tool calls through an MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) gateway that enriches requests with connection auth and applies content guards.
- Given An agent with tool access
- When They invoke a tool through the gateway
- Then The request is enriched with auth credentials and proxied; content guards are applied
- FR-05MustThe system shall record tool usage metrics per agent, per tool, per company for observability and audit.
- Given An agent invokes a tool
- When The call completes
- Then A usage metric record is created with agent, tool, duration, and outcome
Conflicts
None identified yet.
Open Questions
- How does tool access policy interact with the existing auth/permission system's principal permission grants?
- Are app gallery installations per-company or per-instance?
- How are MCPModel Context Protocol (served by `packages/mcp-server`) server capabilities discovered and registered for the tool gateway?
Specification: Tools, Connections & Apps
Architecture
The tools subsystem is organized around several service modules that handle connection lifecycle, policy evaluation, gateway proxying, and runtime management. The app gallery provides a discovery layer on top of MCPModel Context Protocol (served by `packages/mcp-server`)-based tool integrations.
App Gallery ──► Tool Profile ──► Tool Access Policy ──► Tool Gateway ──► External Service
│ │
▼ ▼
Profile Binding Content Guards
Precedence Runtime Supervision
Usage Metrics
Data Models
tool_access (schema)
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | Access record identifier |
| company_id | uuid | FK to companies, not null | Owning company |
| target_type | text | not null | Connection, profile, or policy target type |
| target_id | uuid | not null | Target entity identifier |
| principal_type | text | not null | User or agent |
| principal_id | text | not null | Principal identifier |
| permission | text | not null | Granted permission level |
| created_at | timestamptz | not null, default now | Creation timestamp |
API Contracts
GET /api/tool-access
List tool access records for a company.
POST /api/tool-access
Create a new tool access record.
Tool Gateway endpoints
HTTP-based tool gateway for proxying calls to external services with auth injection, content guards, and policy enforcement.
Key Sequence Flows
Tool Call Flow
AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. → Tool Gateway → Policy Check → Content Guard Scan → Auth Injection
→ External Service → Response → Usage Logging → AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration.
Connection Setup
Operator → Initiate OAuth → Browser redirect → User authorizes → Callback received
→ Store encrypted tokens → Connection active in app gallery
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Gateway protocol | MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) with HTTP fallback | Supports both streaming and request-response tool patterns |
| Policy model | Per-company, principal-based grants | Consistent with existing authz system design |
| Credential storage | Reuses secrets system encryption | No separate credential vault needed; encrypted at rest |
Risks and Unknowns
- Tool call latency depends on external service responsiveness; timeout and retry configuration needs tuning.
- Content guards may produce false positives for legitimate tool invocations; guard rules need careful calibration.
- App gallery installation and update lifecycle is not yet defined.
Out of Scope
- Public app store/marketplace for community tool integrations (deferred)
- Custom tool authoring SDK (agents use existing MCPModel Context Protocol (served by `packages/mcp-server`) tools)
- Tool usage billing or cost allocation (deferred to budget system)
Test Plan: Tools, Connections & Apps
Scope
Tests cover tool access policy validation, app gallery functionality, connection management UI, OAuth flow logic, and integration points between the tools subsystem and the rest of the Paperclip platform.
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | Tool access validator rejects invalid permissions | Invalid permission value | Validation error |
| TC-2 | Tool app gallery returns categorized app list | No filter | List of app definitions with categories |
| TC-3 | Human-friendly connection name formatting | Connection object | Formatted display string |
| TC-4 | App gallery search/filter works correctly | Search query | Filtered app list |
Files: packages/shared/src/validators/tool-access.test.ts, packages/shared/src/tool-app-gallery.test.ts, packages/shared/src/humanize-connection.test.ts
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-5 | Browse page loads and renders connections | User is authenticated, connections exist | Connection list displayed |
| TC-6 | App detail page shows app information | App ID is valid | App info with install button |
| TC-7 | Connections management page works | User has connected services | Connections displayed, can reconnect |
| TC-8 | Apps connect flow works end-to-end | User initiates connection | OAuth flow begins |
| TC-9 | Tool policies tab renders correctly | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. has tool policies | Policy list with edit capabilities |
| TC-10 | Tool runtime tab displays correctly | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. has runtime slots | Runtime slotA leased execution slot for tool runtime processes with concurrency management list with status |
Files: ui/src/pages/apps/Browse.test.tsx, ui/src/pages/apps/AppDetail.test.tsx, ui/src/pages/apps/Connections.test.tsx, ui/src/pages/apps/AppsConnect.test.tsx, ui/src/pages/tools/PoliciesTab.test.tsx, ui/src/pages/tools/RuntimeTab.test.tsx
Coverage Matrix
| Requirement | Test Coverage |
|---|---|
| FR-01MustThe system shall support creating OAuth-based connections to third-party services with configurable scopes. (OAuth connections) | TC-8 (UI), TC-7 (UI) |
| FR-02MustThe system shall support tool access policies that define which agents can use which tools under what conditions. (tool access policies) | TC-1 (unit), TC-9 (UI) |
| FR-04MustThe system shall support tool runtime profiles with capacity limits, timeout configuration, and concurrency management. (runtime profiles) | TC-10 (UI) |
| FR-07ShouldThe system shall support an app gallery for browsing, installing, and configuring pre-built tool integrations. (app gallery) | TC-2 (unit), TC-5 (UI), TC-6 (UI) |
requirements
- How does tool access policy interact with the existing auth/permission system's principal permission grants?
- Are app gallery installations per-company or per-instance?
- How are MCP server capabilities discovered and registered for the tool gateway?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: In-app announcements
Overview
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operators see at most one announcement card fed by a versioned remote JSON manifest. The instance validates and caches the manifest, proxies its media under a strict sandbox, and stores per-user dismissals as instance-wide preferences. Dismissal is idempotent, survives copy edits to the same announcement ID, and never resurrects once dismissed while a new ID appears normally.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator (any role, including viewer) | See the current announcement and dismiss their own card |
| Feed operator | Publish one validated announcement via the remote manifest |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. caller | Explicitly denied announcement access |
| Paperclip user | Dismissal follows their account across companies on the instance |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-1MustThe system shall fetch a versioned remote manifest (`schemaVersion: 1`) from a configured HTTPS URL without credentials, query, or fragment, and validate it with strict schemas. | Must | The system shall fetch a versioned remote manifest (schemaVersion: 1) from a configured HTTPS URL without credentials, query, or fragment, and validate it with strict schemas. |
| FR-2MustThe system shall enforce size caps (manifest 64 KiB, image 2 MiB, animation 128 KiB) with streaming bounded reads, content-type checks, and no redirects. | Must | The system shall enforce size caps (manifest 64 KiB, image 2 MiB, animation 128 KiB) with streaming bounded reads, content-type checks, and no redirects. |
| FR-3MustThe system shall cache the manifest for one hour, cool down fetch failures for 15 minutes, revalidate with ETag, treat 404 as quiet empty content (dropping the stale ETag), bound each fetch to 3 seconds, and deduplicate concurrent refreshes. | Must | The system shall cache the manifest for one hour, cool down fetch failures for 15 minutes, revalidate with ETag, treat 404 as quiet empty content (dropping the stale ETag), bound each fetch to 3 seconds, and deduplicate concurrent refreshes. |
| FR-4MustThe system shall serve the current announcement only when eligible (not expired and meeting `minimumPaperclipVersion` numerically, where a prerelease of the minimum is not yet that release), and return null otherwise. | Must | The system shall serve the current announcement only when eligible (not expired and meeting minimumPaperclipVersion numerically, where a prerelease of the minimum is not yet that release), and return null otherwise. |
| FR-5MustThe system shall proxy only the current announcement's content-addressed image/animation assets, verifying the SHA-256 digest embedded in the asset path, caching bytes in one bounded slot per media kind, and cooling down asset failures. | Must | The system shall proxy only the current announcement's content-addressed image/animation assets, verifying the SHA-256 digest embedded in the asset path, caching bytes in one bounded slot per media kind, and cooling down asset failures. |
| FR-6MustThe system shall validate animations as visual-only HTML/CSS/SVG through an allowlist sanitizer, serve them as `text/html` with a sandbox CSPContent Security Policy plus `nosniff` and `no-referrer`, re-apply the CSPContent Security Policy inside the client `srcdoc` iframe sandbox, and honor reduced-motion with the static image fallback. | Must | The system shall validate animations as visual-only HTML/CSS/SVG through an allowlist sanitizer, serve them as text/html with a sandbox CSPContent Security Policy plus nosniff and no-referrer, re-apply the CSPContent Security Policy inside the client srcdoc iframe sandbox, and honor reduced-motion with the static image fallback. |
| FR-7MustThe system shall persist per-user dismissals keyed by `(userId, announcementId)` as an instance-wide preference, idempotently, so concurrent duplicate dismissals store one row and one audit entry. | Must | The system shall persist per-user dismissals keyed by (userId, announcementId) as an instance-wide preference, idempotently, so concurrent duplicate dismissals store one row and one audit entry. |
| FR-8MustThe system shall restrict announcement writes to an authenticated board user context, deny anonymous and agent callers, and allow viewers to dismiss their own card with company read membership as audit context. | Must | The system shall restrict announcement writes to an authenticated board user context, deny anonymous and agent callers, and allow viewers to dismiss their own card with company read membership as audit context. |
| FR-9MustThe system shall audit each new dismissal transactionally as `announcement.dismissed` with the supplied company's context, and roll back the dismissal row if the audit cannot commit. | Must | The system shall audit each new dismissal transactionally as announcement.dismissed with the supplied company's context, and roll back the dismissal row if the audit cannot commit. |
| FR-10MustThe system shall maintain an instance-level publication-ID registry of validated feed IDs, accept offline retries for known IDs after withdrawal or restart, and reject caller-invented IDs with 404 without storing a dismissal row or audit entry. | Must | The system shall maintain an instance-level publication-ID registry of validated feed IDs, accept offline retries for known IDs after withdrawal or restart, and reject caller-invented IDs with 404 without storing a dismissal row or audit entry. |
| FR-11MustThe system shall render nothing when the feed is disabled, empty, withdrawn, expired, version-incompatible, or dismissed, and mark announcement responses `private, no-store`. | Must | The system shall render nothing when the feed is disabled, empty, withdrawn, expired, version-incompatible, or dismissed, and mark announcement responses private, no-store. |
| FR-12ShouldThe client shall dismiss optimistically with per-user `pending`/`synced` local entries, retry failed writes on reconnect, synchronize across tabs, settle before showing, revalidate on tab return, expire a visible card on time, and suppress the card under modals, toasts, or onboarding. | Should | The client shall dismiss optimistically with per-user pending/synced local entries, retry failed writes on reconnect, synchronize across tabs, settle before showing, revalidate on tab return, expire a visible card on time, and suppress the card under modals, toasts, or onboarding. |
| FR-13MayPublisher tooling shall keep named staging feeds separate from production, default to dry-run, upload content-addressed assets before the short-lived manifest, and reject symlinks, digest mismatches, and unsafe animation markup. | May | Publisher tooling shall keep named staging feeds separate from production, default to dry-run, upload content-addressed assets before the short-lived manifest, and reject symlinks, digest mismatches, and unsafe animation markup. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-1MustDismissal audit context shall validate company membership, returning 404 for unknown or inaccessible companies and 400 for strict-schema violations. | Must | Security | Dismissal audit context shall validate company membership, returning 404 for unknown or inaccessible companies and 400 for strict-schema violations. |
| NFR-2MustThe feed fetch shall be SSRF-guarded with timeouts, never follow redirects, never send credentials, and never log remote content or operator URLs. | Must | Security | The feed fetch shall be SSRF-guarded with timeouts, never follow redirects, never send credentials, and never log remote content or operator URLs. |
| NFR-3ShouldMedia caches shall stay bounded (one slot per kind, shared across board users), concurrent asset requests shall be deduplicated, and the client shall not poll during uninterrupted work. | Should | Performance | Media caches shall stay bounded (one slot per kind, shared across board users), concurrent asset requests shall be deduplicated, and the client shall not poll during uninterrupted work. |
| NFR-4ShouldFeed, asset, and animation failures shall degrade to an absent card without breaking the board or surfacing error popups. | Should | Availability | Feed, asset, and animation failures shall degrade to an absent card without breaking the board or surfacing error popups. |
| NFR-5ShouldThe card shall expose a labelled region with a keyboard-accessible dismiss control, hide the decorative animation from assistive technology behind a labelled poster, and skip animation fetch under reduced motion. | Should | Accessibility | The card shall expose a labelled region with a keyboard-accessible dismiss control, hide the decorative animation from assistive technology behind a labelled poster, and skip animation fetch under reduced motion. |
Constraints
- Announcement IDs match
^[a-z0-9][a-z0-9-]{0,95}$. - Route actions are limited to an allowlist of stable board pages, and external actions require credential-free HTTPS URLs.
- An animation requires a static fallback image.
- Dismissals carry no company scope, while their audit context still validates company membership.
Acceptance Criteria
-
FR-1MustThe system shall fetch a versioned remote manifest (`schemaVersion: 1`) from a configured HTTPS URL without credentials, query, or fragment, and validate it with strict schemas.
gherkin @FR-1MustThe system shall fetch a versioned remote manifest (`schemaVersion: 1`) from a configured HTTPS URL without credentials, query, or fragment, and validate it with strict schemas. Scenario: Fetch and validate the versioned manifest Given a configured HTTPS feed URL without credentials, query, or fragment When the service refreshes the feed Then a schemaVersion 1 manifest with a valid announcement is accepted and anything else is rejected -
FR-2MustThe system shall enforce size caps (manifest 64 KiB, image 2 MiB, animation 128 KiB) with streaming bounded reads, content-type checks, and no redirects.
gherkin @FR-2MustThe system shall enforce size caps (manifest 64 KiB, image 2 MiB, animation 128 KiB) with streaming bounded reads, content-type checks, and no redirects. Scenario: Enforce size and type bounds Given a manifest or asset response exceeding its byte cap or content type When the service reads the response Then the fetch is aborted with an error and the board sees no card content from it -
FR-3MustThe system shall cache the manifest for one hour, cool down fetch failures for 15 minutes, revalidate with ETag, treat 404 as quiet empty content (dropping the stale ETag), bound each fetch to 3 seconds, and deduplicate concurrent refreshes.
gherkin @FR-3MustThe system shall cache the manifest for one hour, cool down fetch failures for 15 minutes, revalidate with ETag, treat 404 as quiet empty content (dropping the stale ETag), bound each fetch to 3 seconds, and deduplicate concurrent refreshes. Scenario: Cache, revalidate, and cool down Given a fetched manifest with an ETag When the hour cache expires or a fetch fails or the feed 404s Then revalidation sends If-None-Match, failures cool down for 15 minutes, and a 404 quietly empties the card without resurrecting stale content -
FR-4MustThe system shall serve the current announcement only when eligible (not expired and meeting `minimumPaperclipVersion` numerically, where a prerelease of the minimum is not yet that release), and return null otherwise.
gherkin @FR-4MustThe system shall serve the current announcement only when eligible (not expired and meeting `minimumPaperclipVersion` numerically, where a prerelease of the minimum is not yet that release), and return null otherwise. Scenario: Serve only eligible announcements Given a manifest announcement with an expiry or minimum version When the current announcement is requested past expiry or below the installed version Then the service returns null -
FR-5MustThe system shall proxy only the current announcement's content-addressed image/animation assets, verifying the SHA-256 digest embedded in the asset path, caching bytes in one bounded slot per media kind, and cooling down asset failures.
gherkin @FR-5MustThe system shall proxy only the current announcement's content-addressed image/animation assets, verifying the SHA-256 digest embedded in the asset path, caching bytes in one bounded slot per media kind, and cooling down asset failures. Scenario: Proxy only current digest-verified assets Given the current announcement with content-addressed media paths When media is requested for a stale ID or a digest mismatch Then the service returns 404 or null and cools down retries while valid bytes stay cached -
FR-6MustThe system shall validate animations as visual-only HTML/CSS/SVG through an allowlist sanitizer, serve them as `text/html` with a sandbox CSPContent Security Policy plus `nosniff` and `no-referrer`, re-apply the CSPContent Security Policy inside the client `srcdoc` iframe sandbox, and honor reduced-motion with the static image fallback.
gherkin @FR-6MustThe system shall validate animations as visual-only HTML/CSS/SVG through an allowlist sanitizer, serve them as `text/html` with a sandbox CSPContent Security Policy plus `nosniff` and `no-referrer`, re-apply the CSPContent Security Policy inside the client `srcdoc` iframe sandbox, and honor reduced-motion with the static image fallback. Scenario: Sandbox animation delivery Given an announcement with a validated animation When the animation endpoint is requested Then it responds with sandboxed CSPContent Security Policy, nosniff, and no-referrer headers and the client renders it in a sandboxed srcdoc iframe or falls back to the poster under reduced motion -
FR-7MustThe system shall persist per-user dismissals keyed by `(userId, announcementId)` as an instance-wide preference, idempotently, so concurrent duplicate dismissals store one row and one audit entry.
gherkin @FR-7MustThe system shall persist per-user dismissals keyed by `(userId, announcementId)` as an instance-wide preference, idempotently, so concurrent duplicate dismissals store one row and one audit entry. Scenario: Idempotent dismissal Given a visible current announcement When the same user dismisses it five times concurrently Then one dismissal row exists and the card stays hidden for that user only -
FR-8MustThe system shall restrict announcement writes to an authenticated board user context, deny anonymous and agent callers, and allow viewers to dismiss their own card with company read membership as audit context.
gherkin @FR-8MustThe system shall restrict announcement writes to an authenticated board user context, deny anonymous and agent callers, and allow viewers to dismiss their own card with company read membership as audit context. Scenario: BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based.-only writes Given anonymous, agent, and viewer-board callers When each calls the dismiss endpoint Then anonymous gets 401, agent gets 403, and a viewer with company membership gets 204 -
FR-9MustThe system shall audit each new dismissal transactionally as `announcement.dismissed` with the supplied company's context, and roll back the dismissal row if the audit cannot commit.
gherkin @FR-9MustThe system shall audit each new dismissal transactionally as `announcement.dismissed` with the supplied company's context, and roll back the dismissal row if the audit cannot commit. Scenario: Transactional audit Given a new dismissal When the activity write cannot commit Then the dismissal row is rolled back and the announcement is not marked dismissed -
FR-10MustThe system shall maintain an instance-level publication-ID registry of validated feed IDs, accept offline retries for known IDs after withdrawal or restart, and reject caller-invented IDs with 404 without storing a dismissal row or audit entry.
gherkin @FR-10MustThe system shall maintain an instance-level publication-ID registry of validated feed IDs, accept offline retries for known IDs after withdrawal or restart, and reject caller-invented IDs with 404 without storing a dismissal row or audit entry. Scenario: Registry allows retries and rejects invented IDs Given a previously published announcement ID withdrawn from the feed When the user dismisses the known ID offline and then an invented ID Then the known ID returns 204 and the invented ID returns 404 with no stored row or audit -
FR-11MustThe system shall render nothing when the feed is disabled, empty, withdrawn, expired, version-incompatible, or dismissed, and mark announcement responses `private, no-store`.
gherkin @FR-11MustThe system shall render nothing when the feed is disabled, empty, withdrawn, expired, version-incompatible, or dismissed, and mark announcement responses `private, no-store`. Scenario: Empty states render nothing Given a disabled feed or an empty, withdrawn, expired, incompatible, or dismissed announcement When the board loads Then no card renders and responses carry private, no-store cache control -
FR-12ShouldThe client shall dismiss optimistically with per-user `pending`/`synced` local entries, retry failed writes on reconnect, synchronize across tabs, settle before showing, revalidate on tab return, expire a visible card on time, and suppress the card under modals, toasts, or onboarding.
gherkin @FR-12ShouldThe client shall dismiss optimistically with per-user `pending`/`synced` local entries, retry failed writes on reconnect, synchronize across tabs, settle before showing, revalidate on tab return, expire a visible card on time, and suppress the card under modals, toasts, or onboarding. Scenario: Optimistic client dismissal Given a visible card and an offline dismissal write When the user dismisses and later reconnects or opens another tab Then the card hides immediately, the write stays pending locally, and the retry syncs without reshowing -
FR-13MayPublisher tooling shall keep named staging feeds separate from production, default to dry-run, upload content-addressed assets before the short-lived manifest, and reject symlinks, digest mismatches, and unsafe animation markup.
gherkin @FR-13MayPublisher tooling shall keep named staging feeds separate from production, default to dry-run, upload content-addressed assets before the short-lived manifest, and reject symlinks, digest mismatches, and unsafe animation markup. Scenario: Safe staged publishing Given a staging feed name and changed asset bytes When publishing without --publish or with a digest mismatch Then nothing uploads to production and the mismatch fails with a SHA-256 error -
NFR-1MustDismissal audit context shall validate company membership, returning 404 for unknown or inaccessible companies and 400 for strict-schema violations.
gherkin @NFR-1MustDismissal audit context shall validate company membership, returning 404 for unknown or inaccessible companies and 400 for strict-schema violations. Scenario: Audit company validation Given a dismiss request with an unknown company or extra body fields When the request is posted Then the service returns 404 or 400 respectively -
NFR-2MustThe feed fetch shall be SSRF-guarded with timeouts, never follow redirects, never send credentials, and never log remote content or operator URLs.
gherkin @NFR-2MustThe feed fetch shall be SSRF-guarded with timeouts, never follow redirects, never send credentials, and never log remote content or operator URLs. Scenario: Guarded remote fetch Given a hung or redirecting feed URL When the service fetches it Then the request is bounded to 3 seconds, redirects are refused, and no URL or content is logged -
NFR-3ShouldMedia caches shall stay bounded (one slot per kind, shared across board users), concurrent asset requests shall be deduplicated, and the client shall not poll during uninterrupted work.
gherkin @NFR-3ShouldMedia caches shall stay bounded (one slot per kind, shared across board users), concurrent asset requests shall be deduplicated, and the client shall not poll during uninterrupted work. Scenario: Bounded shared caches Given concurrent identical current and asset requests When they execute together Then the remote feed is fetched once and cached bytes are reused -
NFR-4ShouldFeed, asset, and animation failures shall degrade to an absent card without breaking the board or surfacing error popups.
gherkin @NFR-4ShouldFeed, asset, and animation failures shall degrade to an absent card without breaking the board or surfacing error popups. Scenario: Graceful degradation Given an unavailable or invalid feed When the board loads Then no card appears, no error popup shows, and the board works normally -
NFR-5ShouldThe card shall expose a labelled region with a keyboard-accessible dismiss control, hide the decorative animation from assistive technology behind a labelled poster, and skip animation fetch under reduced motion.
gherkin @NFR-5ShouldThe card shall expose a labelled region with a keyboard-accessible dismiss control, hide the decorative animation from assistive technology behind a labelled poster, and skip animation fetch under reduced motion. Scenario: Accessible card Given a rendered announcement card When inspected for accessibility Then it exposes a labelled region, an aria-labelled dismiss button with Escape support, and a labelled static image when animation plays
Conflicts
None identified yet.
Open Questions
- Who owns the production manifest publishing cadence and approval (not determinable from code)?
- Is impression or view telemetry desired, given no view events are stored today (not determinable from code)?
Specification: In-app announcements
Overview
A server-side feed service polls one versioned remote JSON manifest, validates it with strict shared zod schemas, and caches it in memory with ETag revalidation.
Routes serve the eligible current announcement and proxy its digest-verified image and sanitized animation under a sandbox CSPContent Security Policy.
Dismissals persist as (userId, announcementId) rows checked on every current read, written idempotently inside a transaction that also appends the audit entry.
The board UI fetches the card once per tab visibility session, dismisses optimistically with local pending state, and suppresses the well under modals, toasts, or onboarding.
Architecture
The feature spans shared contract code, two server services, board-only routes, two DB tables, and three UI pieces.
packages/shared/src/announcements.ts owns size caps, the animation CSPContent Security Policy string, route allowlists, zod schemas, and the eligibility check.
server/src/services/announcement-feed.ts owns manifest fetching, caching, backoff, digest verification, and asset slots.
server/src/services/announcement-animation.ts owns DOMPurify allowlist validation of animation documents.
server/src/services/announcements.ts owns the publication registry, dismissal checks, and the transactional dismiss-plus-audit write.
server/src/routes/announcements.ts owns board-only gating, media proxying headers, and the dismiss endpoint.
ui/src/hooks/useAnnouncement.ts owns settling, visibility-gated fetching, optimistic dismissal, offline retry, and cross-tab sync.
ui/src/components/AnnouncementWell.tsx owns placement gating (identity, company, onboarding, toasts, modals).
ui/src/components/AnnouncementCard.tsx with ui/src/hooks/useAnnouncementAnimation.ts owns card rendering and sandboxed animation playback.
Data Models
announcement_publications
| Field | Type | Constraints | Description |
|---|---|---|---|
| announcementId | text | PK (announcement_id) |
Validated feed ID allowlist for dismissal retries; no content, account, or interaction data |
announcement_dismissals
| Field | Type | Constraints | Description |
|---|---|---|---|
| userId | text | PK part (user_id), not null |
Dismissing user; no auth FK so local-board works |
| announcementId | text | PK part (announcement_id), not null |
Dismissed announcement ID |
| dismissedAt | timestamp tz | not null, default now | Dismissal time (dismissed_at) |
Dismissals are an explicit exception to company scoping: the preference is instance-wide per user, like sidebar preferences. Audit context still validates company membership on every dismiss write.
API Contracts
The normative contract lives in api.yaml (OpenAPI 3), written alongside this document whenever the specification defines an API surface.
The table below is a summary; request/response schemas, error response bodies, and auth requirements live in api.yaml.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/announcements/current | Return the eligible undismissed announcement or null |
| GET | /api/announcements/{id}/image | Proxy the current announcement's digest-verified image bytes |
| GET | /api/announcements/{id}/animation | Serve the sanitized animation document under sandbox CSPContent Security Policy |
| POST | /api/announcements/{id}/dismiss | Persist the caller's dismissal with company audit context |
Error codes shared across endpoints:
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Malformed announcement ID or strict-schema body violation |
| 401 | UNAUTHORIZED | Missing board authentication |
| 403 | FORBIDDEN | Non-board actor (e.g. agent) or missing board user context |
| 404 | NOT_FOUND | Stale media ID, unknown/inaccessible company, or invented announcement ID |
All announcement responses carry Cache-Control: private, no-store.
Sequences
Current announcement read
sequenceDiagram
autonumber
participant UI as BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. UI
participant R as Routes
participant F as Feed service
participant S as Dismissal service
UI->>R: GET /api/announcements/current
R->>F: current() (refresh if stale, or null when disabled)
F-->>R: eligible announcement or null
R->>S: registerPublication(id) + isDismissed(userId, id)
S-->>R: dismissed?
R-->>UI: announcement or null (private, no-store)
Dismissal write
sequenceDiagram
autonumber
participant UI as BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. UI
participant R as Routes
participant S as Dismissal service
participant DB as DB
UI->>R: POST /api/announcements/{id}/dismiss {companyId}
R->>R: board auth + company read-membership check
R->>S: dismiss(userId, id, companyId)
S->>DB: BEGIN; check publication registry
DB-->>S: known? (legacy existing rows stay idempotent)
S->>DB: INSERT dismissal ON CONFLICT DO NOTHING
S->>DB: INSERT activity announcement.dismissed
DB-->>S: COMMIT (or ROLLBACK on audit failure)
S-->>R: known (404 when invented)
R-->>UI: 204 No Content
Media proxy
sequenceDiagram
autonumber
participant UI as BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. UI
participant R as Routes
participant F as Feed service
participant Up as Remote host
UI->>R: GET /api/announcements/{id}/image (or /animation)
R->>F: asset(id, kind) via current()
F->>F: match id + digest path; check cache/pending/cooldown
F->>Up: GET asset (Accept by kind, no redirects, no credentials)
Up-->>F: bytes + content-type
F->>F: verify SHA-256 path digest; sanitize animation
F-->>R: bytes + contentType (cached)
R-->>UI: bytes with nosniff (+ sandbox CSPContent Security Policy for animation)
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Single current announcement | Manifest carries one nullable announcement | Keeps the board surface minimal and the cache trivial |
| Strict schemas everywhere | .strict() on manifest, card, media, actions; unknown fields rejected |
Remote-authored content must fail closed on typos or smuggled fields |
| Content-addressed assets | assets/<sha256>.<ext> paths with digest verification |
Guarantees the proxied bytes are exactly what the feed author published |
| Publication registry | announcement_publications allowlist of validated IDs |
Lets offline retries succeed after withdrawal while invented IDs stay 404 |
| Instance-wide dismissal preference | (userId, announcementId) PK without company scope |
One account dismisses once across companies; audit still records company context |
| Transactional audit | Dismiss plus announcement.dismissed in one transaction with rollback |
A dismissal without its audit entry must not exist |
| Animation as visual document only | DOMPurify allowlist plus sandbox CSPContent Security Policy on response and srcdoc iframe | Active markup, navigation, and resource loading are rejected at two layers |
| No polling | Settle delay plus visibility-gated fetch | Optional content never churns the board during uninterrupted work |
Risks and Unknowns
- Feed host compromise could push hostile copy, though strict schemas, digest pinning, and sandboxing bound the blast radius to text and allowlisted visuals.
- The in-memory manifest and asset caches do not survive restarts, so every restart refetches and the publication registry alone preserves dismissal continuity.
- Publishing cadence and approval ownership live outside the code and need an operational owner.
Out of Scope
- Multiple or queued announcements per feed.
- View or impression telemetry.
- AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration.-facing announcement access.
- Drag-and-drop or in-app announcement authoring.
Test Plan: In-app announcements
Scope
Tests cover the shared manifest contract and eligibility, feed fetching with caching and backoff, animation allowlist validation, publisher staging and digest checks, board routes with durable dismissals, and the UI lifecycle from settling through optimistic dismissal to placement gating. Out of scope: live remote feed content and production publishing runs.
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-S1 | Empty feed and plain-text card parse | {schemaVersion: 1, announcement: null} and a minimal card |
Null announcement and parsed card ID |
| TC-S2 | Unsupported route actions rejected | Six bad route paths (/api/companies, //evil.test, traversal, query, unknown, javascript:) |
All fail schema validation |
| TC-S3 | Unsafe external URLs rejected | Non-URL, http, javascript:, credentialed HTTPS |
All fail schema validation |
| TC-S4 | Unsafe image paths rejected | Traversal, absolute URL, root path, .svg extension |
All fail schema validation |
| TC-S5 | Animation requires content-addressed HTML plus static fallback | Valid image plus animation pair, animation alone, four bad animation paths | Only the valid pair passes |
| TC-S6 | Unknown schema versions, oversized copy, and unknown fields rejected | schemaVersion: 2, 401-char description, five extra-field variants |
All fail schema validation |
| TC-S7 | Expiration and minimum-version gating | Dated and versioned card against older, prerelease, unknown, and newer versions | Only adequate versions before expiry are eligible |
| TC-A1 | CSS keyframes and visual HTML/SVG preserved | DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). with keyframes, styled div, inline SVG | Sanitized output keeps keyframes and viewBox |
| TC-A2 | Active and resource-loading markup rejected | Fourteen fixtures (script, onclick, anchor, form, meta refresh, base, iframe, img, link, foreignObject, xlink, animate, object, button) | Every fixture throws |
| TC-A3 | Empty, oversized, and invalid UTF-8 animation files rejected | Zero bytes, 128 KiB plus one, 0xff byte |
Every input throws |
Files: packages/shared/src/announcements.test.ts, server/src/__tests__/announcement-animation.test.ts
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-F1 | 404 is quiet empty content, drops stale ETags, recovers after cooldown | Served card, then 404, then restored card with controlled clock | Null during outage, no warning logged, recovery fetch omits If-None-Match |
| TC-F2 | Concurrent requests deduplicate, cache for an hour, revalidate with ETag | Fresh service with controlled clock | One fetch for concurrent reads, no refetch before one hour, revalidation sends ETag with credentials: omit |
| TC-F3 | Withdrawal, same-ID copy update, and new-ID discovery | Sequential feeds across cache windows | Corrected title served, withdrawal empties, new ID appears |
| TC-F4 | Invalid and unavailable feeds suppressed with failure cooldown | Five bad responses (bad JSON, schema 9, 64 KiB plus one, redirect, 503) | Null until cooldown, then recovery; one fetch per window |
| TC-F5 | Hung fetch bounded to three seconds | Never-resolving fetch with fake timers | Current resolves null after 3000 ms |
| TC-F6 | Disabled feed and invalid operator URLs never fetch | enabled: false, http URL, credentialed URL |
Null from current, image, and animation with zero fetches |
| TC-F7 | Expiry enforced and version-incompatible content filtered | Expired card with advancing clock, card requiring version 2.0.0 | Expired card flips to null; incompatible card never serves |
| TC-F8 | Only the current digest-verified image is proxied and cached | Content-addressed PNG fixture | Wrong ID returns null, concurrent reads share one fetch on the feed host path |
| TC-F9 | Image digest mismatches rejected with retry cooldown | Manifest pointing at zero-digest path with wrong bytes | Null twice with only one asset fetch |
| TC-F10 | Validated animations deduplicated and cached on the configured host | Content-addressed HTML fixture on a mirror feed URL | Wrong ID returns null, concurrent reads share one fetch with redirect: error and text/html Accept |
| TC-F11 | Rejected animation assets fall back with retry cooldown | Four bad assets (script, wrong type, 404, oversized) | Null twice per asset with two total fetches while the card stays served |
| TC-P1 | Staging feeds stay separate from production and default to dry-run | Staging source directory and name | Staging keys, parsed args, production prefix, rejected bad prefixes and flags |
| TC-P2 | Content-addressed assets upload before the short-lived manifest | Image bytes with digest path, then mutated bytes | Asset key before manifest key, immutable cache control, five-minute manifest, SHA-256 error on change |
| TC-P3 | HTML animation fixtures validated with both assets before the manifest | Animated example versus meta-refresh fixture | Three content types in order; unsafe markup rejected as non-visual |
| TC-P4 | Withdrawal supported; symlinks and unsupported schemas rejected | Null manifest, symlinked dir, schema 2, extra array field | One file for withdrawal; each abuse case throws |
| TC-R1 | Empty remote announcement returns successful null for HTTP 200 and 404 | Null feed at both statuses | 200 with null body and private, no-store |
| TC-R2 | Animation served with sandbox network-denying CSPContent Security Policy | Digest-pinned HTML animation fixture | 200 text/html with sandbox CSPContent Security Policy, no-referrer, nosniff; wrong ID 404s |
| TC-R3 | Dismissals persist across restarts, browsers, and companies, isolated by user | Dismiss by alice, reread as alice and bob, dismiss under second company | Alice sees null, bob still sees the card, one audit row total |
| TC-R4 | Viewers and concurrent duplicates yield one row and one audit | Membership as viewer, five parallel dismissals | Five 204s, one dismissal row, one announcement.dismissed entry |
| TC-R5 | Dismissal rolls back when its audit cannot commit | Registered ID with an impossible company UUID | Dismiss throws and isDismissed stays false |
| TC-R6 | Dismissed ID never resurrects on copy change or rollback while a new ID appears | Dismissed ID with edited copy, then a fresh ID | Null for edited copy, new ID served, feed without registry entry serves null |
| TC-R7 | Local-board identity works without an auth user row | Implicit local board actor | Dismiss 204 and isDismissed true for local-board |
| TC-R8 | Invented IDs rejected without rows or audits | Three unknown IDs after a valid fetch | Three 404s, zero dismissal rows, zero audits, registry holds only the real ID |
| TC-R9 | Offline retries accepted for validated IDs after withdrawal and restart | Published ID, then withdrawn feed on a restarted service | Dismiss 204, isDismissed true, one audit row |
| TC-R10 | Anonymous, agent, and inaccessible-company callers rejected | None and agent actors, unknown company, extra body field | 401 and 403 per actor on all endpoints, 404 for unknown company, 400 for schema violation |
Files: server/src/__tests__/announcement-feed.test.ts, server/src/__tests__/announcement-publisher.test.ts, server/src/__tests__/announcements-routes.test.ts
End-to-End Tests
| ID | Description | Steps | Expected Outcome |
|---|---|---|---|
| TC-H1 | Card settles before showing and never polls during uninterrupted work | Render, advance 3 s, advance one hour, change company prop | Card appears after settle, one fetch total, company change alone refetches nothing |
| TC-H2 | Card survives blur and focus without refetching | Settle, dispatch blur then focus, settle again | Card stable, one fetch, first signal unaborted |
| TC-H3 | Focus during settling does not restart the settle period | Render, advance 2.5 s, blur and focus, advance 0.5 s | Card appears on schedule with one fetch |
| TC-H4 | Dismiss hides immediately and survives remount plus copy edit | Settle, dismiss, unmount, remount with corrected copy, settle | Empty immediately, dismiss called once with company and signal, still empty |
| TC-H5 | Tab return refreshes: withdrawn card hides, new ID shows | Settle, hide and show with null feed, then with new ID | Empty after withdrawal, new ID after republish |
| TC-H6 | Returning tab withholds until the fresh check completes | Hide, return with pending fetch, focus, settle, resolve null | One additional fetch, empty throughout |
| TC-H7 | Late pre-hide response ignored after return | Pending fetch, hide (aborts), resolve late, return with new ID | Empty until the fresh fetch settles on the new ID |
| TC-H8 | Failed writes stay pending and retry on reconnect | Dismiss with failing API, go online | Failure callback with saved-locally true, pending then synced storage |
| TC-H9 | No cross-account leakage from a late fetch | Pending fetch, switch user, resolve old card | New account sees nothing |
| TC-H10 | Another tab's dismissal closes this tab's card | Settle, storage event for the card key | Card hides |
| TC-H11 | Broadcast dismissal honored without browser storage | Broken localStorage, broadcast message, hide and show cycle | Card hides, stays hidden, dismiss API called |
| TC-H12 | Unknown, disabled, and hidden-tab states withhold the card | Rejected fetch, disable, focus, hidden tab with re-enable | Empty in each state with no fetch when disabled or hidden |
| TC-H13 | Visible card expires on time | Card expiring in four seconds, advance one second past | Card shows then clears |
| TC-W1 | Identity, company, onboarding gates with local-board fallback | Unsettled identity, loading company, open onboarding, no user, local trusted mode | Hidden until ready; local-board identity enables in no-login mode |
| TC-W2 | Toasts and modal dialogs yield without dismissing | Toast present, then cleared, dialog opened, then removed | Hidden under toast and dialog, restored after, dismiss never called |
| TC-C1 | Animated media isolated with announcement-only controls | Rendered animated card with stubbed animation fetch | Sandboxed unfocusable iframe with CSPContent Security Policy and alt label, one dismiss button, two links |
| TC-C2 | Reduced motion skips the animation fetch | Reduced-motion media query match | No fetch, no iframe, poster image present |
| TC-C3 | Failed animation HTTP keeps poster and actions usable | Animation fetch 404 and 503 | No iframe, poster present, dismiss works |
| TC-C4 | Pending animation fetch aborted on dismiss | Hanging animation fetch, unmount | Fetch signal aborted while poster stays |
| TC-C5 | Plain-text card is accessible with safe navigation and dismissal | Script-like title, link clicks, image error, Escape key, aux clicks | No script element, labelled region, noreferrer external link, image hides on error, button plus Escape plus primary aux clicks dismiss, context-menu click does not |
Files: ui/src/hooks/useAnnouncement.test.tsx, ui/src/components/AnnouncementWell.test.tsx, ui/src/components/AnnouncementCard.test.tsx
Edge Cases and Failure Scenarios
| ID | Scenario | Expected Behavior |
|---|---|---|
| TC-E1 | Feed 404 after a served card | Quiet empty card with stale ETag dropped (TC-F1) |
| TC-E2 | Oversized manifest, asset, or animation | Bounded abort with cooldown and no partial content (TC-F4, TC-F11, TC-A3) |
| TC-E3 | Digest mismatch on proxied asset | Rejection with retry cooldown (TC-F9) |
| TC-E4 | Unsafe animation markup at publish and at serve time | Rejected by publisher validation and DOMPurify sanitizer (TC-P3, TC-A2) |
| TC-E5 | Concurrent duplicate dismissals | One row and one audit entry (TC-R4) |
| TC-E6 | Audit commit failure | Full rollback, dismissal not recorded (TC-R5) |
| TC-E7 | Offline dismissal of withdrawn but known ID | Accepted via registry with audit (TC-R9) |
| TC-E8 | Caller-invented dismissal ID | 404 with no row and no audit (TC-R8) |
| TC-E9 | AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. or anonymous caller | 403 or 401 on every endpoint (TC-R10) |
| TC-E10 | Reduced motion or animation fetch failure | Static poster stays usable (TC-C2, TC-C3) |
Test Infrastructure
- Vitest suites with fake timers and controlled clocks for cache, cooldown, settle, and expiry windows.
- Embedded Postgres test database for route and dismissal durability tests.
- Mocked remote fetch fixtures with digest-pinned asset bytes.
- jsdom harness with a full localStorage stub, BroadcastChannel stubs, and visibility and online event dispatch.
Coverage Matrix
| Requirement | Test Cases |
|---|---|
| FR-1MustThe system shall fetch a versioned remote manifest (`schemaVersion: 1`) from a configured HTTPS URL without credentials, query, or fragment, and validate it with strict schemas. | TC-S1, TC-S6, TC-F4 |
| FR-2MustThe system shall enforce size caps (manifest 64 KiB, image 2 MiB, animation 128 KiB) with streaming bounded reads, content-type checks, and no redirects. | TC-F4, TC-F11, TC-A3 |
| FR-3MustThe system shall cache the manifest for one hour, cool down fetch failures for 15 minutes, revalidate with ETag, treat 404 as quiet empty content (dropping the stale ETag), bound each fetch to 3 seconds, and deduplicate concurrent refreshes. | TC-F1, TC-F2, TC-F4, TC-F5, TC-F6 |
| FR-4MustThe system shall serve the current announcement only when eligible (not expired and meeting `minimumPaperclipVersion` numerically, where a prerelease of the minimum is not yet that release), and return null otherwise. | TC-S7, TC-F7, TC-H13 |
| FR-5MustThe system shall proxy only the current announcement's content-addressed image/animation assets, verifying the SHA-256 digest embedded in the asset path, caching bytes in one bounded slot per media kind, and cooling down asset failures. | TC-F8, TC-F9, TC-F10, TC-R2 |
| FR-6MustThe system shall validate animations as visual-only HTML/CSS/SVG through an allowlist sanitizer, serve them as `text/html` with a sandbox CSPContent Security Policy plus `nosniff` and `no-referrer`, re-apply the CSPContent Security Policy inside the client `srcdoc` iframe sandbox, and honor reduced-motion with the static image fallback. | TC-A1, TC-A2, TC-C1, TC-C2, TC-C3, TC-C4, TC-R2 |
| FR-7MustThe system shall persist per-user dismissals keyed by `(userId, announcementId)` as an instance-wide preference, idempotently, so concurrent duplicate dismissals store one row and one audit entry. | TC-R3, TC-R4, TC-H4 |
| FR-8MustThe system shall restrict announcement writes to an authenticated board user context, deny anonymous and agent callers, and allow viewers to dismiss their own card with company read membership as audit context. | TC-R4, TC-R7, TC-R10 |
| FR-9MustThe system shall audit each new dismissal transactionally as `announcement.dismissed` with the supplied company's context, and roll back the dismissal row if the audit cannot commit. | TC-R3, TC-R4, TC-R5 |
| FR-10MustThe system shall maintain an instance-level publication-ID registry of validated feed IDs, accept offline retries for known IDs after withdrawal or restart, and reject caller-invented IDs with 404 without storing a dismissal row or audit entry. | TC-R6, TC-R8, TC-R9 |
| FR-11MustThe system shall render nothing when the feed is disabled, empty, withdrawn, expired, version-incompatible, or dismissed, and mark announcement responses `private, no-store`. | TC-R1, TC-F1, TC-F6, TC-F7, TC-H5, TC-H12, TC-W1 |
| FR-12ShouldThe client shall dismiss optimistically with per-user `pending`/`synced` local entries, retry failed writes on reconnect, synchronize across tabs, settle before showing, revalidate on tab return, expire a visible card on time, and suppress the card under modals, toasts, or onboarding. | TC-H1, TC-H2, TC-H3, TC-H4, TC-H6, TC-H7, TC-H8, TC-H9, TC-H10, TC-H11, TC-H12, TC-W2 |
| FR-13MayPublisher tooling shall keep named staging feeds separate from production, default to dry-run, upload content-addressed assets before the short-lived manifest, and reject symlinks, digest mismatches, and unsafe animation markup. | TC-P1, TC-P2, TC-P3, TC-P4 |
| NFR-1MustDismissal audit context shall validate company membership, returning 404 for unknown or inaccessible companies and 400 for strict-schema violations. | TC-R10, TC-S2, TC-S3 |
| NFR-2MustThe feed fetch shall be SSRF-guarded with timeouts, never follow redirects, never send credentials, and never log remote content or operator URLs. | TC-F5, TC-F6, TC-F1 |
| NFR-3ShouldMedia caches shall stay bounded (one slot per kind, shared across board users), concurrent asset requests shall be deduplicated, and the client shall not poll during uninterrupted work. | TC-F2, TC-F8, TC-F10, TC-H1 |
| NFR-4ShouldFeed, asset, and animation failures shall degrade to an absent card without breaking the board or surfacing error popups. | TC-F4, TC-H12, TC-C3 |
| NFR-5ShouldThe card shall expose a labelled region with a keyboard-accessible dismiss control, hide the decorative animation from assistive technology behind a labelled poster, and skip animation fetch under reduced motion. | TC-C1, TC-C2, TC-C5 |
requirements
- Who owns the production manifest publishing cadence and approval (not determinable from code)?
- Is impression or view telemetry desired, given no view events are stored today (not determinable from code)?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Messaging channels
Overview
External messaging channels plus board chat give every company conversational task pipelines across chat providers (Discord, Slack, Teams, Telegram, GitHub, Photon iMessage), task-bound email (AgentMail), and the BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge chat relay. Inbound messages create or continue task-bound conversations without ever granting board authority to external senders. Outbound publications carry receipts, retries, and attachment hydration, while sender identity linking is the sole basis for authority.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Bind provider channels per company, store credentials, pause or disconnect endpoints, and review conversations and publications |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Receive task-bound inbound messages and publish outbound replies through the assigned endpoint |
| External sender | Converse with an agent over a familiar provider without gaining any board authority |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. member | Ask the BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge for help through the board chat UI backed by a persisted standing issue |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-1MustThe system shall bind provider channels per company with vaulted credential storage plus pause and disconnect lifecycle controls. | Must | The system shall bind provider channels per company with vaulted credential storage plus pause and disconnect lifecycle controls. |
| FR-2MustThe system shall create or continue task-bound conversations from inbound provider messages. | Must | The system shall create or continue task-bound conversations from inbound provider messages. |
| FR-3MustThe system shall never grant board authority to external senders. | Must | The system shall never grant board authority to external senders. |
| FR-4MustThe system shall treat sender identity linking as the sole authority basis for external principals. | Must | The system shall treat sender identity linking as the sole authority basis for external principals. |
| FR-5MustThe system shall publish outbound messages with receipts, retries, and attachment hydration. | Must | The system shall publish outbound messages with receipts, retries, and attachment hydration. |
| FR-6MustThe system shall persist email send intents durably before any provider contact. | Must | The system shall persist email send intents durably before any provider contact. |
| FR-7MustThe system shall never send email implicitly from internal task activity and shall surface rich status cards for correspondence and delivery outcomes. | Must | The system shall never send email implicitly from internal task activity and shall surface rich status cards for correspondence and delivery outcomes. |
| FR-8MustThe system shall relay BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge chat over SSEServer-Sent Events (deferred realtime transport) with action-signal stripping and a turn-tag injection guard. | Must | The system shall relay BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge chat over SSEServer-Sent Events (deferred realtime transport) with action-signal stripping and a turn-tag injection guard. |
| FR-9ShouldThe system shall support automatic and explicit publication modes with board resolution of pending publications and actions. | Should | The system shall support automatic and explicit publication modes with board resolution of pending publications and actions. |
| FR-10ShouldThe system shall gate sensitive outbound effects behind consent flows with replayable delivery and publication recovery. | Should | The system shall gate sensitive outbound effects behind consent flows with replayable delivery and publication recovery. |
| FR-11ShouldThe system shall support endpoint setup, resource selection, credential rotation, and health test flows per provider. | Should | The system shall support endpoint setup, resource selection, credential rotation, and health test flows per provider. |
| FR-12MayThe system shall route agent-to-agent messages across endpoints with bounded hop limits. | May | The system shall route agent-to-agent messages across endpoints with bounded hop limits. |
Non-Functional Requirements
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-1MustAll channel operations must be company-scoped and enforce company access checks. | Must | Security | All channel operations must be company-scoped and enforce company access checks. |
| NFR-2MustProvider credentials must be vaulted server-side and never passed to agents. | Must | Security | Provider credentials must be vaulted server-side and never passed to agents. |
| NFR-3ShouldInbound webhook ingress must be idempotent under provider retries and rate-limited per endpoint. | Should | Reliability | Inbound webhook ingress must be idempotent under provider retries and rate-limited per endpoint. |
| NFR-4ShouldDeliveries and publications must expose redacted errors and next-attempt timestamps for diagnosis. | Should | Observability | Deliveries and publications must expose redacted errors and next-attempt timestamps for diagnosis. |
Constraints
- Every conversation belongs to exactly one endpoint, one company, and one task.
- External senders never receive board actor permissions regardless of link state.
- AgentMail endpoints are constrained to explicit publication mode with agent execution policy.
- BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. chat relay spawns the local
claudeCLI and therefore runs only on local single-operator instances.
Acceptance Criteria
-
FR-1MustThe system shall bind provider channels per company with vaulted credential storage plus pause and disconnect lifecycle controls.
gherkin @FR-1MustThe system shall bind provider channels per company with vaulted credential storage plus pause and disconnect lifecycle controls. Scenario: Bind a provider channel per company Given a company with a connection manager When they create a chat endpoint for a provider with credentials Then the endpoint is persisted company-scoped with vaulted credentials and draft statusgherkin @FR-1MustThe system shall bind provider channels per company with vaulted credential storage plus pause and disconnect lifecycle controls. Scenario: Pause and disconnect an endpoint Given an active chat endpoint When the operator pauses then disconnects it Then runtime intake stops while historical records remain and independent pause and disconnect states hold -
FR-2MustThe system shall create or continue task-bound conversations from inbound provider messages.
gherkin @FR-2MustThe system shall create or continue task-bound conversations from inbound provider messages. Scenario: Inbound message creates a task-bound conversation Given an active endpoint with an assigned agent When a provider message arrives for an unknown thread Then a new conversation is created bound to a new task for that agentgherkin @FR-2MustThe system shall create or continue task-bound conversations from inbound provider messages. Scenario: Inbound message continues the open task Given an active conversation bound to an open task When another provider message arrives on the same thread Then the message is appended to the same task instead of opening a new one -
FR-3MustThe system shall never grant board authority to external senders.
gherkin @FR-3MustThe system shall never grant board authority to external senders. Scenario: External sender gains no board authority Given an inbound message from an unlinked external sender When the message is processed Then no board actor permissions are granted and execution stays under the assigned agent's controls -
FR-4MustThe system shall treat sender identity linking as the sole authority basis for external principals.
gherkin @FR-4MustThe system shall treat sender identity linking as the sole authority basis for external principals. Scenario: Identity linking is the sole authority basis Given an external principal with a pending link intent When the Paperclip user confirms the link token Then the principal is linked and telephone numbers, names, and group membership alone still grant nothing -
FR-5MustThe system shall publish outbound messages with receipts, retries, and attachment hydration.
gherkin @FR-5MustThe system shall publish outbound messages with receipts, retries, and attachment hydration. Scenario: Outbound publication with receipt and retry Given a board message queued for a conversation When provider delivery fails transiently Then the publication retries with redacted errors and records the provider message id on successgherkin @FR-5MustThe system shall publish outbound messages with receipts, retries, and attachment hydration. Scenario: Attachment hydration on outbound send Given an outbound message referencing task attachments When the publication is delivered Then attachments are hydrated from task-owned files before provider contact -
FR-6MustThe system shall persist email send intents durably before any provider contact.
gherkin @FR-6MustThe system shall persist email send intents durably before any provider contact. Scenario: Email send intent persisted before provider contact Given a board email send request When the send is queued Then a durable send intent with idempotency key exists before any provider call and reuse with different content conflicts -
FR-7MustThe system shall never send email implicitly from internal task activity and shall surface rich status cards for correspondence and delivery outcomes.
gherkin @FR-7MustThe system shall never send email implicitly from internal task activity and shall surface rich status cards for correspondence and delivery outcomes. Scenario: No implicit email sends Given internal task activity with no explicit send request When the activity completes Then no email leaves the instance and the thread shows rich status cards for explicit correspondence only -
FR-8MustThe system shall relay BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge chat over SSEServer-Sent Events (deferred realtime transport) with action-signal stripping and a turn-tag injection guard.
gherkin @FR-8MustThe system shall relay BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge chat over SSEServer-Sent Events (deferred realtime transport) with action-signal stripping and a turn-tag injection guard. Scenario: BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. concierge SSEServer-Sent Events (deferred realtime transport) relay Given an enabled BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge on a local single-operator instance When the board member sends a chat message Then the relay streams the assistant reply over SSEServer-Sent Events (deferred realtime transport) and persists both turns to the standing issuegherkin @FR-8MustThe system shall relay BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge chat over SSEServer-Sent Events (deferred realtime transport) with action-signal stripping and a turn-tag injection guard. Scenario: Action signals stripped and turns guarded Given an assistant reply containing action signals and a user body containing a turn prefix When the turn is persisted and prompted Then action signals are stripped from storage and the user body stays inside exactly one tagged turn -
FR-9ShouldThe system shall support automatic and explicit publication modes with board resolution of pending publications and actions.
gherkin @FR-9ShouldThe system shall support automatic and explicit publication modes with board resolution of pending publications and actions. Scenario: Explicit publication resolved by board Given a publication awaiting board resolution When a board user resolves it Then the publication proceeds or is cancelled with an audit record -
FR-10ShouldThe system shall gate sensitive outbound effects behind consent flows with replayable delivery and publication recovery.
gherkin @FR-10ShouldThe system shall gate sensitive outbound effects behind consent flows with replayable delivery and publication recovery. Scenario: Consent-gated file delivery Given an outbound Teams file requiring consent When the recipient has not consented Then the file is held pending consent and replayable after resolution -
FR-11ShouldThe system shall support endpoint setup, resource selection, credential rotation, and health test flows per provider.
gherkin @FR-11ShouldThe system shall support endpoint setup, resource selection, credential rotation, and health test flows per provider. Scenario: Endpoint setup and test Given a draft endpoint When the operator completes setup and runs a health test Then resources are selectable and the test result is reported without leaking secrets -
FR-12MayThe system shall route agent-to-agent messages across endpoints with bounded hop limits.
gherkin @FR-12MayThe system shall route agent-to-agent messages across endpoints with bounded hop limits. Scenario: Bounded agent-to-agent route Given an enabled route between two endpoints When a message traverses the route Then delivery stops once the hop limit is reached -
NFR-1MustAll channel operations must be company-scoped and enforce company access checks.
gherkin @NFR-1MustAll channel operations must be company-scoped and enforce company access checks. Scenario: CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. isolation on channel access Given an endpoint belonging to company A When an actor from company B requests it Then access is denied -
NFR-2MustProvider credentials must be vaulted server-side and never passed to agents.
gherkin @NFR-2MustProvider credentials must be vaulted server-side and never passed to agents. Scenario: Credentials never reach agents Given a vaulted provider credential When an agent executes a task on a bound conversation Then the agent receives no credential material -
NFR-3ShouldInbound webhook ingress must be idempotent under provider retries and rate-limited per endpoint.
gherkin @NFR-3ShouldInbound webhook ingress must be idempotent under provider retries and rate-limited per endpoint. Scenario: Webhook retry deduplication Given a delivered provider event When the provider retries the same event Then the duplicate is deduplicated and no second task is created -
NFR-4ShouldDeliveries and publications must expose redacted errors and next-attempt timestamps for diagnosis.
gherkin @NFR-4ShouldDeliveries and publications must expose redacted errors and next-attempt timestamps for diagnosis. Scenario: Redacted delivery diagnostics Given a failed delivery or publication When an operator inspects it Then a redacted error and next-attempt timestamp are visible with no secret material
Conflicts
None identified yet.
Open Questions
- Has the Photon iMessage channel completed live-provider qualification, since the implementation docs still mark it pending and code alone cannot confirm provider-side readiness?
Specification: Messaging channels
Overview
The messaging system layers provider-specific intake and transport adapters over a shared durable core of endpoints, conversations, deliveries, and publications. Inbound webhooks normalize provider events into deliveries that create or continue task-bound conversations under the endpoint's assigned agent. Outbound board messages become publications with idempotency keys, receipts, retries, and attachment hydration. Email reuses the same conversation and publication tables with email-specific state, while the BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge relay stays a separate SSEServer-Sent Events (deferred realtime transport) path that persists to a standing issue.
Architecture
Chat ingress and management live in server/src/routes/chat-channels.ts:86, with provider-authenticated webhooks mounted outside the board mutation guard in server/src/routes/chat-channels.ts:502.
The durable core lives in server/src/services/chat-channels.ts, with provider modules beside it (server/src/services/chat-discord.ts, server/src/services/chat-telegram-media-intake.ts, server/src/services/chat-teams-file-consent.ts, server/src/services/photon/).
Email routes live in server/src/routes/email.ts:28, with the durable pipeline in server/src/services/email-channels.ts:117 and connection setup in server/src/services/email-connections.ts.
The BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge relay lives in server/src/routes/board-chat.ts:65 and streams claude CLI output with the paperclip-board skill as system prompt to ui/src/pages/BoardChat.tsx.
TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table).-facing binding summaries are derived in server/src/services/chat-channel-binding.ts:16, and provider deep links in server/src/services/chat-provider-links.ts:139.
Data Models
chat_endpoints
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | Endpoint identifier |
| company_id | uuid | FK to companies, not null | Owning company |
| connection_id | uuid | FK to tool_connections, not null | Credential-holding connection |
| provider | text | not null, check (slack, github, discord, microsoft-teams, telegram, agentmail, imessage-photon) | Chat provider |
| public_id | text | not null, unique | Public webhook identifier |
| publication_mode | text | not null, default automatic, check (automatic, explicit) | Outbound gating mode |
| external_execution_policy | text | not null, default restricted, check (restricted, agent) | Execution policyA review/approval stage policy governing how an issue is executed. for external triggers |
| assigned_agent_id | uuid | FK to agents, not null | AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. owning bound tasks |
| status | text | not null, default draft, check (draft, verifying, active, paused, attention, revoked, archived) | Lifecycle status |
| deployment_mode | text | not null, default direct, check (direct, relay) | Transport deployment |
| concurrency_policy | text | not null, default queue | Inbound concurrency handling |
| capabilities | jsonb | not null, default all-false | Provider capability flags |
| setup | jsonb | not null, default provider_setup step | Setup state machine |
DB check constraints further require one live bot identity per provider account, unique Photon numbers, and AgentMail endpoints pinned to explicit publication mode with agent policy (packages/db/src/schema/chat_channels.ts:113).
chat_conversations
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | Conversation identifier |
| company_id | uuid | FK to companies, not null | Owning company |
| endpoint_id | uuid | FK to chat_endpoints, not null | Source endpoint |
| issue_id | uuid | FK to issues, not null, restrict delete | Bound task |
| external_conversation_id | text | not null | Provider-native thread id |
| external_thread_id | text | not null, default empty | Provider-native sub-thread id |
| session_generation | integer | not null, default 1 | Rollover generation for linear threads |
| state | text | not null, default active, check (active, waiting, completed, unavailable, endpoint_removed) | Conversation state |
chat_deliveries
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | Delivery identifier |
| endpoint_id | uuid | FK to chat_endpoints, not null | Ingress endpoint |
| provider_event_id | text | not null, unique per endpoint | Provider event id for dedupe |
| deduplication_key | text | not null, unique per endpoint | Retry-safe dedupe key |
| event_kind | text | not null, ChatEventKind | Normalized event kind |
| state | text | not null, default received, check (received, filtered, processing, processed, retry, failed) | Intake state |
| attempts | integer | not null, default 0 | Processing attempts |
| redacted_error | text | nullable | Secret-free failure detail |
| next_attempt_at | timestamp | nullable | Next retry time |
chat_publications
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK, defaultRandom | Publication identifier |
| conversation_id | uuid | FK to chat_conversations, not null | Target conversation |
| issue_id | uuid | FK to issues, not null, restrict delete | Source task |
| comment_id | uuid | FK to issue_comments, nullable, set null | Source comment |
| idempotency_key | text | not null, unique per company | Replay-safe key |
| payload | jsonb | not null, SafeChatPublicationPayload | Sanitized outbound payload |
| state | text | not null, default pending, check (pending, streaming, published, retry, delivery_unknown, failed, cancelled, awaiting_consent) | Delivery state |
| provider_message_id | text | nullable | Provider receipt id |
| attempts | integer | not null, default 0 | Send attempts |
chat_external_principals and chat_identity_links
| Field | Type | Constraints | Description |
|---|---|---|---|
| chat_external_principals | table | unique per company, provider, account, external id | Provider-native sender identities |
| chat_identity_links | table | unique per endpoint and principal, status check (pending, linked, revoked, expired) | Link intents binding principals to Paperclip users |
email_endpoints, email_messages, and email_sends
| Field | Type | Constraints | Description |
|---|---|---|---|
| email_endpoints | table | PK endpoint_id, receive_mode check (websocket, webhook) | AgentMail transport config joined to chat_endpoints |
| email_messages | table | unique per endpoint and provider message id, direction check (inbound, outbound) | Durable correspondence rows |
| email_sends | table | PK publication_id, outcome check (queued, sent, delivered, failed, uncertain) | Durable send intents keyed by idempotency |
Provider side tables
| Field | Type | Constraints | Description |
|---|---|---|---|
| chat_discord_command_owners | table | PK application_id, snowflake check | Instance-wide Discord application namespace tombstone with no cascading keys |
| chat_teams_file_transfers | table | phase and hash check constraints, 60MB bound | Private Teams file consent state with digests, never projected publicly |
| chat_telegram_draft_ids | sequence | non-cycling, 1 to 2147483647 | Content-free draft id allocator surviving deletion and rollback |
API Contracts
The normative contract lives in api.yaml (OpenAPI 3), written alongside this document whenever the specification defines an API surface.
The table below is a summary; request/response schemas, error response bodies, and auth requirements live in api.yaml.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/companies/:companyId/chat-endpoints | List company chat endpoints |
| POST | /api/companies/:companyId/chat-endpoints | Create a chat endpoint |
| GET | /api/chat-endpoints/:endpointId | Get endpoint detail |
| PATCH | /api/chat-endpoints/:endpointId | Update endpoint settings |
| POST | /api/chat-endpoints/:endpointId/setup | Run provider setup step |
| POST | /api/chat-endpoints/:endpointId/setup-secret | IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. a setup secret |
| POST | /api/chat-endpoints/:endpointId/test | Run endpoint health test |
| GET | /api/chat-endpoints/:endpointId/resources | List selectable provider resources |
| PUT | /api/chat-endpoints/:endpointId/resources | Replace enabled resources |
| GET | /api/chat-endpoints/:endpointId/principals | List external principals |
| POST | /api/chat-endpoints/:endpointId/principals/:principalId/link-intent | Create identity link intent |
| DELETE | /api/chat-endpoints/:endpointId/principals/:principalId/link | Revoke identity link |
| POST | /api/chat-identity-links/confirm | Confirm identity link by token |
| GET | /api/chat-endpoints/:endpointId/conversations | List endpoint conversations |
| GET | /api/chat-endpoints/:endpointId/activity | List endpoint activity with pagination |
| POST | /api/chat-endpoints/:endpointId/deliveries/:deliveryId/replay | Replay a delivery |
| POST | /api/chat-endpoints/:endpointId/publications/:publicationId/replay | Replay a publication |
| POST | /api/chat-endpoints/:endpointId/publications/:publicationId/resolve | BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based.-resolve a pending publication |
| POST | /api/chat-endpoints/:endpointId/actions/:actionId/resolve | BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based.-resolve a pending action |
| POST | /api/chat-endpoints/:endpointId/conversations/:conversationId/publications | Publish a board message or comment |
| GET | /api/chat-endpoints/:endpointId/conversations/:conversationId/publications/:publicationId/status | Get publication batch status |
| GET | /api/issues/:issueId/chat-binding | Get task chat binding summary |
| POST | /api/chat-webhooks/:publicId/:provider | Provider-authenticated inbound webhook |
| POST | /api/companies/:companyId/email/connections | Connect an AgentMail credential |
| GET | /api/companies/:companyId/email/inboxes | List email inboxes |
| POST | /api/companies/:companyId/email/inboxes | Set up an email inbox |
| POST | /api/email/inboxes/:endpointId/control | Pause, resume, or remove an inbox |
| POST | /api/email/inboxes/:endpointId/reconnect | Reconnect with a new key and mode |
| POST | /api/companies/:companyId/email/send | Queue an email send intent (202) |
| GET | /api/companies/:companyId/email/tasks/:issueId | Get the email thread for a task |
| POST | /api/chat-webhooks/agentmail/:publicId | AgentMail Svix-signed inbound webhook |
| POST | /api/board/chat/stream | Stream a BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge turn over SSEServer-Sent Events (deferred realtime transport) |
Error codes shared across endpoints:
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Malformed ids, bodies, or pagination parameters |
| 403 | FORBIDDEN | Missing board role, company access, or connection permission |
| 404 | NOT_FOUND | Unknown endpoint, conversation, inbox, or binding |
| 409 | CONFLICT | Reused idempotency key with different content |
| 429 | RATE_LIMITED | Webhook rate limit or board-chat concurrency cap exceeded |
Sequences
Inbound provider message
sequenceDiagram
autonumber
participant P as Provider
participant W as Webhook route
participant S as Chat service
participant DB as DB
P->>W: POST /api/chat-webhooks/:publicId/:provider
W->>S: handleWebhook (signature verified in SDK)
S->>DB: INSERT chat_deliveries (dedupe on event id)
S->>DB: Find or create chat_conversations + issue
S->>DB: Append comment, mark delivery processed
Outbound board publication
sequenceDiagram
autonumber
participant B as BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. UI
participant S as Chat service
participant DB as DB
participant P as Provider
B->>S: POST publications (body, idempotencyKey)
S->>DB: INSERT chat_publications (pending)
S->>P: Deliver with attachment hydration
P-->>S: Receipt (provider message id)
S->>DB: Mark published, store receipt
Email send intent
sequenceDiagram
autonumber
participant B as BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. or agent
participant S as Email service
participant DB as DB
participant A as AgentMail
B->>S: POST /email/send
S->>DB: INSERT email_sends (queued) + chat_publications
S->>A: Provider contact only after durable intent
A-->>S: Delivery outcome
S->>DB: Update outcome (sent, delivered, failed, uncertain)
BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. concierge turn
sequenceDiagram
autonumber
participant U as BoardChat UI
participant R as BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based.-chat route
participant C as claude CLI
participant I as Standing issue
U->>R: POST /board/chat/stream (companyId, message)
R->>I: Persist user comment + build tagged history
R->>C: Spawn with paperclip-board skill prompt
C-->>R: stream-json deltas
R-->>U: SSEServer-Sent Events (deferred realtime transport) start, chunk, status, done
R->>I: Persist stripped reply as board-concierge
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Shared durable core | Conversations, deliveries, and publications shared across chat and email | One task pipeline serves every provider with provider-specific intake only at the edges |
| Identity linking as sole authority | Telephone numbers, names, and group membership grant nothing | External identifiers are spoofable, so only an explicit confirmed link confers authority |
| Send intents before provider contact | email_sends row precedes any AgentMail call | Crash-safe exactly-once semantics with idempotency-key conflict detection |
| No implicit email | Internal task activity never sends mail | Outbound email is an explicit act with rich status cards, not a side effect |
| Action-signal stripping | %%ACTIONS%% blocks removed before persistence | UI observer signals must never pollute the durable comment body |
| Turn-tag injection guard | serializeTurn neutralizes embedded turn tags | Untrusted bodies stay inside exactly one prompt turn and cannot fabricate assistant history |
| Local-only concierge relay | Refuse non-local_trusted deployment modes | Spawning the operator CLI with skipped permissions is only safe for the machine operator |
Risks and Unknowns
- Photon iMessage live-provider qualification is still pending per
doc/connections/IMESSAGE-PHOTON.md, so release readiness needs a live qualification pass. - Provider webhook semantics drift over time, so signature verification and event normalization need per-provider regression coverage.
- Publication retry storms under provider outages could amplify load, so backoff and concurrency caps need load validation.
Out of Scope
- Outbound tool-call gateway behavior, which belongs to the tools connections surface rather than conversational pipelines.
- Local Mac access, unsolicited conversations, and SMS or RCS fallback for the Photon channel.
- A separate email composer outside the normal task conversation.
Test Plan: Messaging channels
Scope
Tests cover the durable chat core (endpoints, conversations, deliveries, publications), provider intake and transport (Discord, Slack, Teams, Telegram, GitHub, Photon iMessage), task-bound email intents and threads, identity linking, attachment hydration, and the BoardChat UI. Out of scope: outbound tool-call gateway behavior and live-provider qualification runs.
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | Discord bot validation and inventory | Bot tokens, guild and channel payloads | Valid identity with required channel permissions or typed rejection |
| TC-2 | Discord native commands, modals, and question forms | Command and interaction payloads | Registered commands and parsed answers |
| TC-3 | Slack receipt transport bounded | Receipt payloads | Bounded transport without unbounded processing |
| TC-4 | Slack sessions and native modals | Session and modal payloads | Session continuity and modal handling |
| TC-5 | Teams file consent foundation and contract | Consent bindings and tokens | Valid bindings, rejections on tampering, contract conformance |
| TC-6 | Teams file transfers, publication, and inline images | Transfer rows and image payloads | Consent-gated delivery with digests and hydrated images |
| TC-7 | Telegram photo, media, rich intake, ephemeral and stop flows | Photo buffers and message payloads | Bounded validation, media locators, draft-stop handling |
| TC-8 | GitHub attachments, receipts, and webhook config | Comment and attachment payloads | Exact-comment image resolution and receipt reactions |
| TC-9 | Publication pipeline (batches, errors, projection, stream, text parts, ready stream, reconciliation) | Publication rows and payloads | Retry states, redacted errors, streamed text, reconciled outcomes |
| TC-10 | Interaction arbitration and publications | Concurrent interaction payloads | Single-winner arbitration with durable publications |
| TC-11 | Provider links and task URLs | Thread ids and raw payloads | Documented HTTPS provider links or null |
| TC-12 | SDK runtime, state, and admission retry | Runtime events and lease states | AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). handling with bounded admission retries |
| TC-13 | Chat validators | Endpoint and message schemas | Valid or rejected shared-type payloads |
Files: server/src/services/chat-discord.test.ts, chat-discord-native-*.test.ts, chat-discord-modal-wire.test.ts, chat-discord-question-forms.test.ts, chat-discord-command-registration*.test.ts, chat-discord-adapter-patch.test.ts, chat-slack-receipts.test.ts, chat-slack-sessions.test.ts, chat-slack-native-modal.test.ts, chat-teams-file-consent.test.ts, chat-teams-file-consent.contract.test.ts, chat-teams-file-consent-runtime.test.ts, chat-teams-file-transfers.test.ts, chat-teams-file-publication.test.ts, chat-teams-inline-image*.test.ts, chat-teams-native-modal.test.ts, chat-teams-personal-recipient.test.ts, chat-teams-credentials.test.ts, chat-telegram-photo.test.ts, chat-telegram-media-intake.test.ts, chat-telegram-rich-intake.test.ts, chat-telegram-ephemeral*.test.ts, chat-telegram-draft-stop.test.ts, chat-telegram-stop-subscription.test.ts, chat-telegram-video-note.test.ts, chat-github-attachments.test.ts, chat-github-receipt-reactions.test.ts, chat-github-webhook-config.test.ts, chat-github-provider-stress.test.ts, chat-publication-*.test.ts, chat-interaction-*.test.ts, chat-provider-*.test.ts, chat-question-forms.test.ts, chat-run-publications.test.ts, chat-sdk-*.test.ts, chat-task-url.test.ts, chat-control-admission-retry.test.ts, chat-inbound-wakeup-publications.test.ts, chat-outbound-attachment-hydration.test.ts, packages/shared/src/validators/chat-channels.test.ts.
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-20 | Chat channel control-plane integration | Embedded Postgres with seeded company and agent | Endpoint lifecycle, conversations, publications, and Discord registration pass |
| TC-21 | Webhook ingress and diagnostics | Endpoint with public id | Provider webhooks accepted, rate-limited, and diagnosed |
| TC-22 | Identity linking routes | Endpoint with principals | Link intents created, confirmed, previewed, and revoked |
| TC-23 | AgentMail durable email pipeline | Enabled experimental connectors with inbox | Queued intents, threads, deliveries, and uncertain resolution pass |
| TC-24 | Photon channel integration | Photon project fixture | Intake, allocation checks, and task binding pass |
| TC-25 | HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. chat task links | Bound conversation with heartbeat runs | Wakeup publications attach to the bound task |
Files: server/src/__tests__/chat-channels.integration.test.ts, server/src/__tests__/chat-webhook-diagnostics.test.ts, server/src/__tests__/chat-webhook-public-url.test.ts, server/src/routes/chat-channels.identity.test.ts, server/src/routes/chat-channels.webhook.test.ts, server/src/__tests__/email-channels.integration.test.ts, server/src/__tests__/photon/channel.integration.test.ts, server/src/__tests__/photon/photon.test.ts, server/src/__tests__/heartbeat-chat-task-link.test.ts, server/src/__tests__/heartbeat-reviewed-chat-binding.integration.test.ts, server/src/__tests__/durable-chat-wakeup.test.ts, server/src/services/native-runtime/external-chat-wait.integration.test.ts.
End-to-End Tests
| ID | Description | Steps | Expected Outcome |
|---|---|---|---|
| TC-30 | BoardChat staged typing and concierge flow | Open BoardChat with and without history | Typing dots, welcome reveal, and streamed concierge turns render |
| TC-31 | Runner chat flow | Drive a runner chat scenario | TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table).-bound chat completes end to end |
| TC-32 | BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. chat route gating | Call the stream route with flags and modes | Disabled flags and remote modes return 403 with typed codes |
Files: ui/src/pages/BoardChat.test.tsx, tests/runner-e2e/chat-flow.test.ts, server/src/__tests__/board-chat-route-feature-flag.test.ts.
Edge Cases and Failure Scenarios
| ID | Scenario | Expected Behavior |
|---|---|---|
| TC-40 | Duplicate provider webhook delivery | Second delivery deduplicated, no second task |
| TC-41 | Transient provider send failure | Publication retries with redacted error and next-attempt timestamp |
| TC-42 | Email idempotency key reused with different content | Conflict error, no second provider contact |
| TC-43 | Oversized Discord message | Response moved to a lossless file before truncation |
| TC-44 | Unconsented Teams file | File held in awaiting_consent, replayable after resolution |
| TC-45 | BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. chat concurrency saturation | Fourth concurrent stream rejected with busy code |
| TC-46 | Non-local deployment board chat | Request refused as deployment-mode unsupported |
Files: server/src/services/chat-publication-errors.test.ts, chat-publication-reconciliation.test.ts, chat-discord.test.ts, chat-teams-file-consent.test.ts, server/src/__tests__/board-chat-route-feature-flag.test.ts, server/src/__tests__/email-channels.integration.test.ts.
Test Infrastructure
- Embedded Postgres suites run through
describeEmbeddedPostgreswith graceful skip when unsupported. - Provider transports are stubbed at the SDK boundary with receipt-transport barriers for determinism.
- Telegram photo and media tests use bounded buffers with no-throw refusal guarantees.
- UI tests use the staged typing and animation guards in
BoardChat.test.tsx.
Coverage Matrix
| Requirement | Test Cases |
|---|---|
| FR-1MustThe system shall bind provider channels per company with vaulted credential storage plus pause and disconnect lifecycle controls. | TC-20, TC-21 |
| FR-2MustThe system shall create or continue task-bound conversations from inbound provider messages. | TC-20, TC-25 |
| FR-3MustThe system shall never grant board authority to external senders. | TC-20, TC-22 |
| FR-4MustThe system shall treat sender identity linking as the sole authority basis for external principals. | TC-22 |
| FR-5MustThe system shall publish outbound messages with receipts, retries, and attachment hydration. | TC-1 through TC-12, TC-40, TC-41, TC-43 |
| FR-6MustThe system shall persist email send intents durably before any provider contact. | TC-23, TC-42 |
| FR-7MustThe system shall never send email implicitly from internal task activity and shall surface rich status cards for correspondence and delivery outcomes. | TC-23 |
| FR-8MustThe system shall relay BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. Concierge chat over SSEServer-Sent Events (deferred realtime transport) with action-signal stripping and a turn-tag injection guard. | TC-30, TC-32, TC-45, TC-46 |
| FR-9ShouldThe system shall support automatic and explicit publication modes with board resolution of pending publications and actions. | TC-10, TC-20 |
| FR-10ShouldThe system shall gate sensitive outbound effects behind consent flows with replayable delivery and publication recovery. | TC-5, TC-6, TC-44 |
| FR-11ShouldThe system shall support endpoint setup, resource selection, credential rotation, and health test flows per provider. | TC-20, TC-21 |
| FR-12MayThe system shall route agent-to-agent messages across endpoints with bounded hop limits. | TC-20 |
| NFR-1MustAll channel operations must be company-scoped and enforce company access checks. | TC-20 |
| NFR-2MustProvider credentials must be vaulted server-side and never passed to agents. | TC-23 |
| NFR-3ShouldInbound webhook ingress must be idempotent under provider retries and rate-limited per endpoint. | TC-21, TC-40 |
| NFR-4ShouldDeliveries and publications must expose redacted errors and next-attempt timestamps for diagnosis. | TC-9, TC-41 |
requirements
- Has the Photon iMessage channel completed live-provider qualification, since the implementation docs still mark it pending and code alone cannot confirm provider-side readiness?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation./task search
Overview
Operators and agents need one company-scoped search that finds tasks and their surrounding context (comments, documents, artifacts, agents, projects) with relevance-ranked results, focused extract output for machines, and a full Search UI page for navigation.
This feature documents already-implemented functionality grounded in server/src/services/company-search.ts, server/src/services/company-search-extract.ts, server/src/services/company-search-rate-limit.ts, server/src/services/task-search.ts, GET /companies/:companyId/search and /search/extract in server/src/routes/issues.ts, ui/src/pages/Search.tsx, and OpenAPI entries in server/src/routes/openapi.ts.
Stakeholders
| Stakeholder | Interest |
|---|---|
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. operator | Find tasks, comments, documents, artifacts, agents, and projects from one Search page with filters, scopes, and sorts |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | Query company search and extract endpoints for task discovery and focused context snippets within its company |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FR-1MustThe system shall provide company-scoped cross-entity search across issues, comments, documents, artifacts, agents, and projects with company access checks. | Must | The system shall provide company-scoped cross-entity search across issues, comments, documents, artifacts, agents, and projects with company access checks. |
| FR-2MustThe system shall provide an extract endpoint that returns focused per-issue context matches with excerpts and source references. | Must | The system shall provide an extract endpoint that returns focused per-issue context matches with excerpts and source references. |
| FR-3MustThe system shall enforce per-company per-actor rate limiting on search and extract endpoints. | Must | The system shall enforce per-company per-actor rate limiting on search and extract endpoints. |
| FR-4MustThe system shall rank task results by relevance and quality using disjoint score bands with identifier, phrase, coverage, and fuzzy handling. | Must | The system shall rank task results by relevance and quality using disjoint score bands with identifier, phrase, coverage, and fuzzy handling. |
| FR-5MustThe system shall provide a Search UI page with result navigation to tasks and their evidence anchors. | Must | The system shall provide a Search UI page with result navigation to tasks and their evidence anchors. |
| FR-6ShouldThe system shall support faceted issue filters with option counts and zero-result loosen suggestions. | Should | The system shall support faceted issue filters with option counts and zero-result loosen suggestions. |
| FR-7ShouldThe system shall support result scopes, sort orders, and limit/offset pagination with a has-more signal. | Should | The system shall support result scopes, sort orders, and limit/offset pagination with a has-more signal. |
| FR-8ShouldThe system shall surface Search UI states for loading, errors with retry, no-results recovery, debounced input, recent searches, and filter/sort controls. | Should | The system shall surface Search UI states for loading, errors with retry, no-results recovery, debounced input, recent searches, and filter/sort controls. |
| FR-9MayThe system shall return markdown-aware snippets with highlight ranges and preview images where available. | May | The system shall return markdown-aware snippets with highlight ranges and preview images where available. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFR-1MustAll search and extract operations shall enforce company scoping, actor authorization boundaries, and issue visibility rules. | Must | Security | All search and extract operations shall enforce company scoping, actor authorization boundaries, and issue visibility rules. |
| NFR-2MustIssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments.-side search shall compute ranked pages and aggregates efficiently using shared CTEs, page-window snippet enrichment, and trigram-backed matching. | Must | Performance | IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments.-side search shall compute ranked pages and aggregates efficiently using shared CTEs, page-window snippet enrichment, and trigram-backed matching. |
| NFR-3ShouldRate limiting shall cap search demand per actor key per window and fail with retry metadata before invoking search. | Should | Availability | Rate limiting shall cap search demand per actor key per window and fail with retry metadata before invoking search. |
Constraints
- Search never crosses company boundaries.
- Extract
containshas a minimum length and rejects regex input (literal and URL kinds only). - Query text is truncated to
COMPANY_SEARCH_MAX_QUERY_LENGTHand tokens toCOMPANY_SEARCH_MAX_TOKENS. - Pagination is bounded by
COMPANY_SEARCH_MAX_LIMITandCOMPANY_SEARCH_MAX_OFFSET. - Deleted comments are excluded from search and extract evidence.
Acceptance Criteria
-
FR-1MustThe system shall provide company-scoped cross-entity search across issues, comments, documents, artifacts, agents, and projects with company access checks.
gherkin @FR-1MustThe system shall provide company-scoped cross-entity search across issues, comments, documents, artifacts, agents, and projects with company access checks. Scenario: CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation.-scoped cross-entity search Given a company with issues, comments, documents, artifacts, agents, and projects When an authorized actor searches within that company Then results contain only that company's visible rows with type counts per entitygherkin @FR-1MustThe system shall provide company-scoped cross-entity search across issues, comments, documents, artifacts, agents, and projects with company access checks. Scenario: Cross-company access denied Given an actor without access to company B When they request company B search or extract Then the API denies access before invoking the service -
FR-2MustThe system shall provide an extract endpoint that returns focused per-issue context matches with excerpts and source references.
gherkin @FR-2MustThe system shall provide an extract endpoint that returns focused per-issue context matches with excerpts and source references. Scenario: Extract focused matches Given issues with matching titles, comments, or documents When a client calls the extract endpoint with a contains string, kind, and scope Then each issue returns deduplicated matches with excerpts, truncation flags, and comment or document source referencesgherkin @FR-2MustThe system shall provide an extract endpoint that returns focused per-issue context matches with excerpts and source references. Scenario: Extract URL kind Given comments containing URLs When a client extracts with kind url Then URL occurrences are expanded, deduplicated, and scheme-less queries still select URL sources -
FR-3MustThe system shall enforce per-company per-actor rate limiting on search and extract endpoints.
gherkin @FR-3MustThe system shall enforce per-company per-actor rate limiting on search and extract endpoints. Scenario: Rate limit enforced Given an actor exceeding the per-window request budget When they call search or extract again Then the API returns 429 with Retry-After and rate-limit headers without invoking search -
FR-4MustThe system shall rank task results by relevance and quality using disjoint score bands with identifier, phrase, coverage, and fuzzy handling.
gherkin @FR-4MustThe system shall rank task results by relevance and quality using disjoint score bands with identifier, phrase, coverage, and fuzzy handling. Scenario: Identifier outranks weak title matches Given a task with an exact identifier and another with a weak title resemblance When searching by the identifier Then the exact identifier result ranks firstgherkin @FR-4MustThe system shall rank task results by relevance and quality using disjoint score bands with identifier, phrase, coverage, and fuzzy handling. Scenario: Aggregate relevance gates hold Given the task-search corpus and quality cases When full and quick engines are evaluated Then mean reciprocal rank meets 0.95 and NDCG@5 meets 0.90 -
FR-5MustThe system shall provide a Search UI page with result navigation to tasks and their evidence anchors.
gherkin @FR-5MustThe system shall provide a Search UI page with result navigation to tasks and their evidence anchors. Scenario: Navigate from results to evidence Given search results with comment or document evidence When the operator opens a context result Then the link targets the exact comment or document anchor on the issuegherkin @FR-5MustThe system shall provide a Search UI page with result navigation to tasks and their evidence anchors. Scenario: Exact identifier auto-redirects Given an exact identifier match in the results When the Search page resolves it Then it redirects to the issue root without a deep-link suffix -
FR-6ShouldThe system shall support faceted issue filters with option counts and zero-result loosen suggestions.
gherkin @FR-6ShouldThe system shall support faceted issue filters with option counts and zero-result loosen suggestions. Scenario: Filtered search with facets Given active status, priority, assignee, project, label, or updated-window filters When searching Then only matching issues are returned with per-option facet countsgherkin @FR-6ShouldThe system shall support faceted issue filters with option counts and zero-result loosen suggestions. Scenario: Zero-result recovery Given filters that empty the result page When the response contains zeroResults Then it reports the unfiltered total and per-filter loosen suggestions ordered by additional results -
FR-7ShouldThe system shall support result scopes, sort orders, and limit/offset pagination with a has-more signal.
gherkin @FR-7ShouldThe system shall support result scopes, sort orders, and limit/offset pagination with a has-more signal. Scenario: Scope, sort, and paginate Given a scope, sort order, limit, and offset When searching Then the response echoes scope and sort, pages merged cross-type results, and reports hasMore -
FR-8ShouldThe system shall surface Search UI states for loading, errors with retry, no-results recovery, debounced input, recent searches, and filter/sort controls.
gherkin @FR-8ShouldThe system shall surface Search UI states for loading, errors with retry, no-results recovery, debounced input, recent searches, and filter/sort controls. Scenario: Search UI states Given a slow, failing, or empty search When the operator uses the Search page Then loading, error-with-retry, and no-results states render with recovery actionsgherkin @FR-8ShouldThe system shall surface Search UI states for loading, errors with retry, no-results recovery, debounced input, recent searches, and filter/sort controls. Scenario: Debounced input with operator filters Given typed text with operator tokens and filter chips When the operator edits or removes a token or chip Then the request dispatches after the debounce window with updated filters in the URL and query -
FR-9MayThe system shall return markdown-aware snippets with highlight ranges and preview images where available.
gherkin @FR-9MayThe system shall return markdown-aware snippets with highlight ranges and preview images where available. Scenario: Snippet highlights and previews Given a result with markdown content or images When snippets are built Then snippet text is markdown-stripped with highlight ranges and a preview image URL when present -
NFR-1MustAll search and extract operations shall enforce company scoping, actor authorization boundaries, and issue visibility rules.
gherkin @NFR-1MustAll search and extract operations shall enforce company scoping, actor authorization boundaries, and issue visibility rules. Scenario: Hidden and foreign rows excluded Given hidden issues and another company's data When searching or extracting Then hidden and foreign rows are excluded from results and counts -
NFR-2MustIssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments.-side search shall compute ranked pages and aggregates efficiently using shared CTEs, page-window snippet enrichment, and trigram-backed matching.
gherkin @NFR-2MustIssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments.-side search shall compute ranked pages and aggregates efficiently using shared CTEs, page-window snippet enrichment, and trigram-backed matching. Scenario: Efficient issue aggregation Given many matching issues When searching Then one statement computes the ranked page plus type, facet, and total aggregates with snippet enrichment limited to the page window -
NFR-3ShouldRate limiting shall cap search demand per actor key per window and fail with retry metadata before invoking search.
gherkin @NFR-3ShouldRate limiting shall cap search demand per actor key per window and fail with retry metadata before invoking search. Scenario: Rate-limit headers present Given any search or extract call When the response is returned Then X-RateLimit-Limit and X-RateLimit-Remaining headers are set with Retry-After on 429
Conflicts
None identified yet.
Open Questions
- Is the absence of a goals entity scope in company search intentional, or should goals become a searchable entity later?
Specification: CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation./task search
Overview
Cross-entity search is implemented as stateless services over existing Postgres rows with shared task-search SQL fragments, single-statement issue aggregation, page-window evidence enrichment, an in-memory per-actor rate limiter, and a React Search page backed by the same query schemas. No new tables were added; ranking, snippets, facets, extract matches, and UI navigation all derive from issues, comments, documents, artifacts, agents, projects, and labels.
Architecture
Search flows from the Search page (ui/src/pages/Search.tsx, 922 lines) through ui/src/api/search.ts to GET /api/companies/:companyId/search and /search/extract in server/src/routes/issues.ts:7803 and server/src/routes/issues.ts:7846.
The route layer enforces company access, authorization boundaries, zod query parsing, assigneeUserId=me resolution, and rate limiting before delegating to companySearchService (server/src/services/company-search.ts) and companySearchExtractService (server/src/services/company-search-extract.ts).
Ranking and matching SQL live in server/src/services/task-search.ts and are shared by company search and issue-list/command-palette search.
Rate limiting is an in-memory sliding-window limiter in server/src/services/company-search-rate-limit.ts keyed by company, actor type, and actor id.
Data Models
No new persistent entities were introduced.
Search reads existing rows and returns projections defined in packages/shared/src/types/search.ts.
Issues (read)
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | uuid | PK | TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). identity returned in results |
| company_id | uuid | FK, not null | CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. scope enforced on every query |
| identifier | text | nullable | Exact and prefix identifier matching |
| title | text | not null | Primary ranking and fuzzy-match field |
| description | text | nullable | Token coverage and snippet source |
| status | text | not null | Filter facet and score tiebreak |
| priority | text | not null | Priority sort and facet |
| assignee_agent_id | uuid | nullable | Filter facet |
| assignee_user_id | text | nullable | Filter facet with me resolution |
| project_id | uuid | nullable | Filter facet |
| updated_at | timestamptz | not null | Updated-within and sort field |
Comments and documents (read)
| Field | Type | Constraints | Description |
|---|---|---|---|
| issue_comments.body | text | not null | Comment token matching and snippet source; soft-deleted rows excluded |
| documents.title | text | nullable | DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). token matching and snippet source |
| documents.latest_body | text | nullable | DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). token matching and snippet source |
| issue_documents.key | text | not null | DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). anchor suffix and label |
Agents, projects, artifacts, labels (read)
| Field | Type | Constraints | Description |
|---|---|---|---|
| agents.name/role/capabilities | text | nullable mix | AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. text matching and snippet source |
| projects.name/description | text | nullable mix | ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. text matching; archived projects excluded |
| artifacts projection | view-like | via companyArtifactsService | Artifact scope results with issue and project context |
| issue_labels | join | company-scoped | Label filter facet |
Derived flags (CTE, not stored)
| Field | Type | Constraints | Description |
|---|---|---|---|
| ident_exact/ident_starts/title_exact/title_phrase | boolean | computed | High-score ranking bands |
| title_coverage/issue_coverage/token_coverage | integer | computed | Full-coverage ranking bands |
| fuzzy_title | boolean | computed | pg_trgm and levenshtein fallback flag |
| comment_match/document_match | boolean | computed | Scope conditions and matched-field evidence |
API Contracts
The normative contract lives in api.yaml (OpenAPI 3), written alongside this document whenever the specification defines an API surface.
The table below is a summary; request/response schemas, error response bodies, and auth requirements live in api.yaml.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/companies/{companyId}/search | Cross-entity ranked search with scopes, filters, sorts, and pagination |
| GET | /api/companies/{companyId}/search/extract | Focused literal or URL match extraction with per-issue excerpts |
Error codes shared across endpoints:
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_INPUT | Zod query validation failed (bad scope, status, priority, sort, pagination, extract kind) |
| 403 | FORBIDDEN | Cross-company access, authorization boundary, or assigneeUserId=me without board auth |
| 429 | RATE_LIMITED | Per-actor window budget exceeded with Retry-After and rate-limit headers |
Query surface for search: q, scope (all, issues, comments, documents, artifacts, agents, projects), sort (relevance, updated, created, priority), limit, offset, status, priority, assigneeAgentId, assigneeUserId, projectId, labelId, updatedWithin (24h, 7d, 30d, 90d), updatedAfter.
Query surface for extract: contains (min length, literal or URL), kind (literal, url), scope (all, issues, comments, documents), status, limit, offset, matchesPerIssue, updatedWithin, updatedAfter (mutually exclusive).
Sequences
Ranked search
sequenceDiagram
autonumber
participant UI as Search page
participant R as issues.ts route
participant L as Rate limiter
participant S as companySearchService
participant DB as Postgres
UI->>R: GET /companies/:id/search?q=&scope=&filters=&sort=
R->>R: assertCompanyAccess + authorization boundary
R->>R: parse query schema, resolve assigneeUserId=me
R->>L: consume(company, actorType, actorId)
L-->>R: allowed + limit/remaining/retryAfter
R->>S: search(companyId, query)
S->>DB: one statement: matched CTE + ranked page + type/facet/total aggregates
S->>DB: page-window comment/document snippet query
S->>DB: artifact/agent/project counts and rows
S-->>R: results + countsByType + filterOptionCounts + zeroResults + hasMore
R-->>UI: 200 JSON with rate-limit headers
Extract matches
sequenceDiagram
autonumber
participant C as Client
participant R as issues.ts route
participant L as Rate limiter
participant E as companySearchExtractService
participant DB as Postgres
C->>R: GET /companies/:id/search/extract?contains=&kind=&scope=
R->>R: assertCompanyAccess + authorization boundary + parse schema
R->>L: consume(company, actorType, actorId)
L-->>R: allowed + limit/remaining/retryAfter
R->>E: extract(companyId, query)
E->>DB: candidate issues with scope EXISTS conditions + status/update filters
E->>DB: page-issue comments and documents matching literal or URL pattern
E-->>R: per-issue deduplicated matches with excerpts and source refs
R-->>C: 200 JSON with rate-limit headers
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| No derived corpus | Shared CTEs over current rows with pg_trgm and levenshtein | Avoids workers and index drift while reusing existing indexes |
| Disjoint score bands | 8000 ident-exact down to 1000 fuzzy with small tiebreaks | Incidental comments, repeats, status, and recency cannot outweigh stronger match kinds |
| Single-statement aggregates | UNION ALL result page plus type, facet, and total counts | Keeps filter counts consistent with the ranked page |
| Page-window enrichment | Best comment/document snippet fetched only for fetched rows | Avoids per-match-row snippet work at scale |
| In-memory sliding window | 60 requests per 60 seconds per company plus actor key | Simple per-actor protection without new storage |
| Identifier as navigation | Copied or typed identifiers bypass fuzzy number matching | Prevents speculative numeric fuzzy hits on navigation intent |
| Short-term word anchoring | One-to-three-letter terms require word-boundary regex | Prevents UI matching inside unrelated words |
| UI deep links | Context matches open comment or document anchors; exact identifiers redirect to issue root | Preserves shown evidence while keeping identifier navigation clean |
Risks and Unknowns
- Fuzzy fallback relies on pg_trgm and fuzzystrmatch availability and length-bounded edit-distance work.
- Large companies may stress the UNION ALL aggregate fan-out and cross-type merge before pagination.
- In-memory rate limiting does not coordinate across server instances.
- Relevance gates are corpus-based and may need retuning as task language drifts.
Out of Scope
- Goals entity search scope (not implemented).
- Cross-company or global search.
- Regex extract queries.
- Persistent search history beyond local recent searches.
- SPEC-implementation product section for search (only OpenAPI descriptions exist).
Test Plan: CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation./task search
Scope
Tests cover search query validation, ranked cross-entity retrieval, extract matching, route rate limiting and access checks, relevance and quality gates, query-intent parsing, and the Search UI page. Out of scope: goals entity search (not implemented), cross-instance rate-limit coordination, and release smoke coverage.
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | Search query validation truncates long text and rejects invalid filters, sort, and pagination | Overlong q plus invalid limit, offset, scope, status, priority, sort, updatedWithin, projectId |
Truncated valid parse; throws on each invalid field (server/src/__tests__/company-search-service.test.ts:37) |
| TC-2 | Per-branch fetch window includes offset up to the branch cap | Limit 50 with offsets 0 and 200 | 51 and COMPANY_SEARCH_BRANCH_FETCH_LIMIT (server/src/__tests__/company-search-service.test.ts:64) |
| TC-3 | Extract query validation accepts supported filters and rejects unsafe or ambiguous input | URL contains with kind, scope, status, limits; then regex kind, short contains, over-limit, and conflicting update filters | Accepted parse with capped matchesPerIssue; throws on regex, short, over-limit, and conflicting inputs (server/src/__tests__/company-search-extract-service.test.ts:32) |
| TC-4 | Query intent keeps negation, short domain terms, and quoted filler | TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table).-search intent strings | Filler dropped only in multi-term unquoted queries (server/src/__tests__/task-search-query.test.ts:5) |
| TC-5 | Query intent retains strictest intent for repeated terms | Repeated terms with mixed quoting | Strictest (quoted) intent wins (server/src/__tests__/task-search-query.test.ts:10) |
| TC-6 | Query intent normalizes identifiers without guessing numbers | Identifier-like strings | Normalized letters-digits form (server/src/__tests__/task-search-query.test.ts:14) |
| TC-7 | URL building writes q and scope, clears empty q and all scope, and preserves path plus hash | Search URL inputs | Correct params with preserved pathname and hash (ui/src/pages/Search.test.tsx:143, :149, :153) |
Files: server/src/__tests__/company-search-service.test.ts, server/src/__tests__/company-search-extract-service.test.ts, server/src/__tests__/task-search-query.test.ts, ui/src/pages/Search.test.tsx.
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-8 | Exact entity names outrank speculative typos and empty quotes return nothing | Seeded issues, agents, and projects | Exact names first; "" yields no rows (server/src/__tests__/company-search-service.test.ts:166) |
| TC-9 | Exact issue identifiers rank before weaker title matches | Seeded identifier and title rows | Identifier row first (server/src/__tests__/company-search-service.test.ts:179) |
| TC-10 | Phrase outranks reordered title words and partial matches are rejected | Seeded phrase and reordered rows | Phrase first; partial unmatched (server/src/__tests__/company-search-service.test.ts:196) |
| TC-11 | Multi-token thread matches return comment snippets | IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. with matching comments | Same-issue match with comment snippet (server/src/__tests__/company-search-service.test.ts:221) |
| TC-12 | DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). search returns document metadata for snippets | IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. with linked matching document | DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). snippet with title, key, and anchor data (server/src/__tests__/company-search-service.test.ts:247) |
| TC-13 | Artifact scope searches artifact projections | Seeded document, work-product, and attachment artifacts | Artifact rows in artifacts scope (server/src/__tests__/company-search-service.test.ts:280) |
| TC-14 | High-offset fetch windows are not passed to artifact query validation | High offset search | Artifact listing still succeeds (server/src/__tests__/company-search-service.test.ts:319) |
| TC-15 | IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. filters apply before sorting and pagination | Seeded filtered issues | Filtered, sorted, paged rows (server/src/__tests__/company-search-service.test.ts:333) |
| TC-16 | Filter-only searches return issue rows | Filters without search text | IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. rows without requiring q (server/src/__tests__/company-search-service.test.ts:394) |
| TC-17 | Zero-result filters return loosen data and suppress agent/project rows | Filters emptying the page | zeroResults with suggestions; no agent or project rows (server/src/__tests__/company-search-service.test.ts:425) |
| TC-18 | Hidden issue-backed artifacts are not leaked | Hidden issues with artifacts | Artifacts excluded (server/src/__tests__/company-search-service.test.ts:443) |
| TC-19 | Hidden issues and other companies' data are excluded | Hidden and foreign rows | Only visible same-company rows (server/src/__tests__/company-search-service.test.ts:486) |
| TC-20 | Bare SQL wildcards are treated as literals | Queries with %, _, \ |
Literal matching, no match-all (server/src/__tests__/company-search-service.test.ts:513) |
| TC-21 | Percent characters match literally across entities | Rows containing % |
Literal percent matches in issues, comments, documents, agents, projects (server/src/__tests__/company-search-service.test.ts:555) |
| TC-22 | Offset applies after merged cross-type ranking | Mixed entity rows | Correct offset window (server/src/__tests__/company-search-service.test.ts:659) |
| TC-23 | Underscore and backslash escaping in phrase and token patterns | Special-character queries | Escaped patterns match literally (server/src/__tests__/company-search-service.test.ts:680) |
| TC-24 | Short UI terms do not match inside unrelated words | Short terms | Word-boundary matching only (server/src/__tests__/company-search-service.test.ts:699) |
| TC-25 | Typo fallback stays inside requested filters | Filtered fuzzy query | Fuzzy rows respect filters (server/src/__tests__/company-search-service.test.ts:707) |
| TC-26 | Exact title hits stay on the task with best context evidence | Title plus context rows | Exact task first with most complete evidence (server/src/__tests__/company-search-service.test.ts:715) |
| TC-27 | Edits, deleted comments, and document updates reflect immediately | Mutated rows | Fresh results without stale evidence (server/src/__tests__/company-search-service.test.ts:756) |
| TC-28 | Conservative fuzzy title matches use pg_trgm | Typo queries | Near titles matched (server/src/__tests__/company-search-service.test.ts:776) |
| TC-29 | Transposition typos match multi-word titles | Transposed queries | Multi-word titles matched (server/src/__tests__/company-search-service.test.ts:789) |
| TC-30 | Extract expands and deduplicates URLs across sources | Issues, comments, documents with URLs | Deduplicated URL matches (server/src/__tests__/company-search-extract-service.test.ts:109) |
| TC-31 | Scheme-less queries keep URL sources selected | Scheme-less contains | URL sources still selected (server/src/__tests__/company-search-extract-service.test.ts:152) |
| TC-32 | Extract filters by update window and status | Status and update filters | Only matching issues (server/src/__tests__/company-search-extract-service.test.ts:190) |
| TC-33 | Extract default distinct-match cap marks truncation explicitly | Many matches | Capped matches with truncation flags (server/src/__tests__/company-search-extract-service.test.ts:221) |
| TC-34 | Bounded per-issue match cap supports machine extraction | High matchesPerIssue | Complete bounded extraction (server/src/__tests__/company-search-extract-service.test.ts:240) |
| TC-35 | Extract does not return another company's issues | Foreign company rows | Empty same-company result (server/src/__tests__/company-search-extract-service.test.ts:260) |
| TC-36 | Relevance cases pass per-query first-result, absence, and grade gates | TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table).-search corpus | First, absent, and grade-3 top-5 assertions (server/src/__tests__/task-search-quality.test.ts:68) |
| TC-37 | Aggregate relevance gates hold for both engines | Full and quick engine reports | MRR at least 0.95 and NDCG@5 at least 0.90 (server/src/__tests__/task-search-quality.test.ts:83) |
| TC-38 | Empty quotes, punctuation, and oversized words do not error | Edge queries | Empty or safe results (server/src/__tests__/task-search-quality.test.ts:93) |
| TC-39 | Repeated same-actor search is rejected before invoking search | Rate limiter with max 1 | Second call 429 with retry metadata; service called once (server/src/__tests__/company-search-rate-limit-routes.test.ts:33) |
| TC-40 | assigneeUserId=me resolves for board actors before search | BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. actor with userId | Service receives resolved user id (server/src/__tests__/company-search-rate-limit-routes.test.ts:65) |
| TC-41 | Invalid filter and sort params are rejected before search | Invalid query params | 400 without service call (server/src/__tests__/company-search-rate-limit-routes.test.ts:93) |
| TC-42 | Extract route parses query and invokes the service | Valid extract query | Service called with parsed fields (server/src/__tests__/company-search-extract-routes.test.ts:55) |
| TC-43 | Extract route denies cross-company access before service | Foreign company id | 403 or 404 without service call (server/src/__tests__/company-search-extract-routes.test.ts:80) |
| TC-44 | Extract route shares the company-search rate limiter | Rate limiter with max 1 | Second extract call 429 (server/src/__tests__/company-search-extract-routes.test.ts:91) |
Files: server/src/__tests__/company-search-service.test.ts, server/src/__tests__/company-search-extract-service.test.ts, server/src/__tests__/task-search-quality.test.ts, server/src/__tests__/company-search-rate-limit-routes.test.ts, server/src/__tests__/company-search-extract-routes.test.ts.
End-to-End Tests
| ID | Description | Steps | Expected Outcome |
|---|---|---|---|
| TC-45 | Search request renders the result from ?q |
Open Search with ?q, await query |
Result row rendered (ui/src/pages/Search.test.tsx:185) |
| TC-46 | Artifact results render in the search surface | Search with artifact rows | Artifact rows rendered (ui/src/pages/Search.test.tsx:290) |
| TC-47 | Comment and document rows render anchors, chips, and highlights | Search with context evidence | Exact anchors, source chips, highlights (ui/src/pages/Search.test.tsx:366) |
| TC-48 | Loading state renders while search is pending | Trigger search with deferred response | Loading state visible (ui/src/pages/Search.test.tsx:467) |
| TC-49 | Error state renders retry and fallback actions | Fail the search request | Error with retry and fallbacks (ui/src/pages/Search.test.tsx:481) |
| TC-50 | Typing debounces before dispatching search | Type then advance timers | One search after the debounce window (ui/src/pages/Search.test.tsx:498) |
| TC-51 | Exact identifier match auto-redirects to issue root | Return exact identifier result | Redirect without deep-link suffix (ui/src/pages/Search.test.tsx:553) |
| TC-52 | Non-default scope no-results renders Search-all action | Empty scoped search | No-results with Search-all (ui/src/pages/Search.test.tsx:618) |
| TC-53 | URL filters parse into params and operator pills | Load URL with filters | Params and pills match (ui/src/pages/Search.test.tsx:654) |
| TC-54 | Typed operators dispatch as search filters | Type operator tokens | Request carries parsed filters (ui/src/pages/Search.test.tsx:699) |
| TC-55 | Deleted operator tokens drop filters from requests | Delete committed token | Request omits the filter (ui/src/pages/Search.test.tsx:751) |
| TC-56 | Removed filter chips strip tokens from the query | Remove operator-derived chip | Token stripped and re-query issued (ui/src/pages/Search.test.tsx:790) |
| TC-57 | Operator autocomplete applies a suggestion to the token | Open suggestions and select one | Token updated with suggestion (ui/src/pages/Search.test.tsx:838) |
| TC-58 | Sort param round-trips through URL into requests | Change sort | URL and request carry sort (ui/src/pages/Search.test.tsx:922) |
| TC-59 | Removable filter chips re-query without the filter | Remove chip | Re-query without that filter (ui/src/pages/Search.test.tsx:946) |
| TC-60 | Zero-results recovery renders loosen suggestions | Filters emptying the page | Recovery with loosen suggestions (ui/src/pages/Search.test.tsx:986) |
Files: ui/src/pages/Search.test.tsx.
Edge Cases and Failure Scenarios
| ID | Scenario | Expected Behavior |
|---|---|---|
| TC-61 | Overlong query text | Truncated to max length before parsing |
| TC-62 | Invalid scope, status, priority, sort, pagination, or project id | 400 before service invocation |
| TC-63 | Regex extract kind or too-short contains | 400 rejected as unsafe or ambiguous |
| TC-64 | Conflicting extract update filters | 400 when both updatedWithin and updatedAfter are set |
| TC-65 | Rate budget exceeded | 429 with Retry-After and rate-limit headers |
| TC-66 | Foreign company or hidden rows | Excluded from results, counts, and extracts |
| TC-67 | Empty quotes or literal-only punctuation queries | Empty safe result without errors |
Test Infrastructure
- Embedded Postgres test databases with
pg_trgmfor service, extract, and quality suites. - Supertest plus express route harness with mocked search services for route tests.
- jsdom Search page harness with mocked search, agents, projects, labels, auth, router, and sidebar contexts.
- TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table).-search corpus fixture in
server/src/__tests__/fixtures/task-search-corpus.tswith per-case relevance grades.
Coverage Matrix
| Requirement | Test Cases |
|---|---|
| FR-1MustThe system shall provide company-scoped cross-entity search across issues, comments, documents, artifacts, agents, and projects with company access checks. | TC-1, TC-8, TC-11, TC-12, TC-13, TC-18, TC-19, TC-43, TC-45 |
| FR-2MustThe system shall provide an extract endpoint that returns focused per-issue context matches with excerpts and source references. | TC-3, TC-30, TC-31, TC-32, TC-33, TC-34, TC-35, TC-42, TC-43 |
| FR-3MustThe system shall enforce per-company per-actor rate limiting on search and extract endpoints. | TC-39, TC-41, TC-44, TC-65 |
| FR-4MustThe system shall rank task results by relevance and quality using disjoint score bands with identifier, phrase, coverage, and fuzzy handling. | TC-4, TC-5, TC-6, TC-8, TC-9, TC-10, TC-24, TC-25, TC-26, TC-28, TC-29, TC-36, TC-37, TC-38 |
| FR-5MustThe system shall provide a Search UI page with result navigation to tasks and their evidence anchors. | TC-46, TC-47, TC-51 |
| FR-6ShouldThe system shall support faceted issue filters with option counts and zero-result loosen suggestions. | TC-15, TC-16, TC-17, TC-53, TC-54, TC-55, TC-56, TC-59, TC-60 |
| FR-7ShouldThe system shall support result scopes, sort orders, and limit/offset pagination with a has-more signal. | TC-2, TC-7, TC-14, TC-22, TC-58 |
| FR-8ShouldThe system shall surface Search UI states for loading, errors with retry, no-results recovery, debounced input, recent searches, and filter/sort controls. | TC-7, TC-48, TC-49, TC-50, TC-52, TC-53, TC-54, TC-57, TC-60 |
| FR-9MayThe system shall return markdown-aware snippets with highlight ranges and preview images where available. | TC-11, TC-12, TC-47 |
| NFR-1MustAll search and extract operations shall enforce company scoping, actor authorization boundaries, and issue visibility rules. | TC-18, TC-19, TC-35, TC-40, TC-43, TC-66 |
| NFR-2MustIssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments.-side search shall compute ranked pages and aggregates efficiently using shared CTEs, page-window snippet enrichment, and trigram-backed matching. | TC-2, TC-11, TC-12, TC-14 |
| NFR-3ShouldRate limiting shall cap search demand per actor key per window and fail with retry metadata before invoking search. | TC-39, TC-44, TC-65 |
requirements
- Is the absence of a goals entity scope in company search intentional, or should goals become a searchable entity later?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| CompanyFirst-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. | First-order business entity; all business records are company-scoped. One deployment can run many companies with full data isolation. |
| BoardThe human operator(s) with full control across all companies in a deployment. In `local_trusted` mode this is implicit; in authenticated mode it is session-based. | The human operator(s) with full control across all companies in a deployment. In local_trusted mode this is implicit; in authenticated mode it is session-based. |
| Board API keyOptional key for board-level access (separate from agent API keys). | Optional key for board-level access (separate from agent API keys). |
| AgentAn AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. | An AI employee registered in the org tree with a role, title, reporting line, budget, and adapter configuration. |
| CEO agentTop-level management agent that proposes strategy (requires board approval before executing delegated work). | Top-level management agent that proposes strategy (requires board approval before executing delegated work). |
| Org chart / org treeStrict reporting tree via `reports_to` (nullable root); no multi-manager reporting, no cycles. | Strict reporting tree via reports_to (nullable root); no multi-manager reporting, no cycles. |
| GoalA node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. | A node in the company → team → agent → task alignment hierarchy; at least one root company-level goal per company. |
| ProjectA company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. | A company-scoped grouping that may link to a goal and carry lead agent, target date, and secret-aware env. |
| IssueThe core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. | The core task entity. Carries company/project/goal/parent links, single assignee, atomic checkout locks, comments, documents, work products, and attachments. |
| TaskUsed interchangeably with "issue" in agent/operator contexts (the task system is the issues table). | Used interchangeably with "issue" in agent/operator contexts (the task system is the issues table). |
| HeartbeatA scheduled or triggered wakeup that invokes an agent's adapter to do work. | A scheduled or triggered wakeup that invokes an agent's adapter to do work. |
| Heartbeat runA tracked execution record (`heartbeat_runs`) with status, context snapshot, and events. | A tracked execution record (heartbeat_runs) with status, context snapshot, and events. |
| AdapterTranslates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). | Translates a heartbeat into a concrete runtime invocation (process, HTTP, CLI session, gateway, or external plugin). |
| CheckoutThe atomic operation that claims an issue for execution; sets `assignee_agent_id`, `status=in_progress`, and execution locks. Returns `409` on concurrent claims. | The atomic operation that claims an issue for execution; sets assignee_agent_id, status=in_progress, and execution locks. Returns 409 on concurrent claims. |
| Execution lockFields (`checkout_run_id`, `execution_run_id`, `execution_locked_at`) that prevent double-work on an issue. | Fields (checkout_run_id, execution_run_id, execution_locked_at) that prevent double-work on an issue. |
| Work modeHow an issue is executed: `standard` (autonomous), `ask` (answer-only), or `planning` (plan-only). | How an issue is executed: standard (autonomous), ask (answer-only), or planning (plan-only). |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue (e.g. artifact with an attachment, or a workspace-file reference). |
| DocumentEditable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (`plan`, `design`, `notes`). | Editable text-first artifact (markdown) with append-only revisions, linked to issues by workflow key (plan, design, notes). |
| ApprovalA governance request (`hire_agent`, `approve_ceo_strategy`, budget override, `request_board_approval`) the board approves/rejects. | A governance request (hire_agent, approve_ceo_strategy, budget override, request_board_approval) the board approves/rejects. |
| Execution policyA review/approval stage policy governing how an issue is executed. | A review/approval stage policy governing how an issue is executed. |
| RoutineA recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. | A recurring task definition with cron/webhook/API triggers; each execution creates a tracked issue and wakes the assigned agent. |
| WorkspaceEither a project workspace or an isolated execution workspace (git worktree) where an agent runs. | Either a project workspace or an isolated execution workspace (git worktree) where an agent runs. |
| Task watchdogA scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. | A scoped execution capacity for one watched issue subtree that restores live task paths; not board authority and not active-run output monitoring. |
| PluginInstance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. | Instance-wide out-of-process extension with capability-gated host services, jobs, tools, and UI contributions. |
| Company portabilityExport/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. | Export/import of entire organizations (agents, skills, projects, routines, issues) with secret scrubbing and collision handling. |
Technical Terms
| Term | Definition |
|---|---|
| Control planeThe Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). | The Paperclip server + UI that orchestrates agents (as opposed to the execution services/adapters that run them). |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL used as the dev default when DATABASE_URL is unset. |
| DrizzleThe TypeScript ORM used for schema, migrations, and DB clients. | The TypeScript ORM used for schema, migrations, and DB clients. |
| Adapter pluginAn external adapter loaded dynamically via `~/.paperclip/adapter-plugins.json` (no hardcoded core imports). | An external adapter loaded dynamically via ~/.paperclip/adapter-plugins.json (no hardcoded core imports). |
| Context mode`thin` (send IDs/pointers; agent fetches via API) vs `fat` (include assignments, goal summary, budget snapshot, recent comments). | thin (send IDs/pointers; agent fetches via API) vs fat (include assignments, goal summary, budget snapshot, recent comments). |
| Cheap model profileOptional low-cost model lane (`modelProfiles.cheap`) usable only for status-only recovery coordination, never deliverable work. | Optional low-cost model lane (modelProfiles.cheap) usable only for status-only recovery coordination, never deliverable work. |
| Request depthCounter incremented as work is delegated down the org tree. | Counter incremented as work is delegated down the org tree. |
| Activity logImmutable audit trail for every mutating action (actor_type, action, entity, details). | Immutable audit trail for every mutating action (actor_type, action, entity, details). |
| Budget hard stopAt 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. | At 100% of monthly UTC budget, the agent is paused and new invocations/checkout are blocked. |
| Secrets providerA pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). | A pluggable vault backend for storing secret material (local encrypted, AWS Secrets Manager, GCP, HashiCorp Vault). |
| Secret bindingAn association between a secret key and a target (agent, project, or routine) with a config path for resolution. | An association between a secret key and a target (agent, project, or routine) with a config path for resolution. |
| Inline secret referenceThe `${{ secrets.key }}` syntax used in JSONB configs to reference a secret that is resolved at runtime. | The ${{ secrets.key }} syntax used in JSONB configs to reference a secret that is resolved at runtime. |
| Board chat / Conference roomAn experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSE and persisted to a standing issue. | An experimental board-level chat assistant that spawns a Claude subprocess with the board skills prompt, streamed via SSEServer-Sent Events (deferred realtime transport) and persisted to a standing issue. |
| Company membershipA user's role-scoped association with a company (`owner \ | A user's role-scoped association with a company (owner \| admin \| member). |
| Principal permission grantA fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). | A fine-grained permission override granting a specific action on a specific resource to a principal (user or agent). |
| GreptileAutomated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. | Automated code-review service used in CI; PRs must reach 5/5 with no open P2+ comments. |
| Low-trust presetA containment control for hostile automated work (not general project/issue privacy). | A containment control for hostile automated work (not general project/issue privacy). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACLAccess Control List (Pro/Enterprise work-object privacy, deferred for V1) | Access Control List (Pro/Enterprise work-object privacy, deferred for V1) |
| App connectionA pre-configured tool connection with associated profile and policy for use by agents | A pre-configured tool connection with associated profile and policy for use by agents |
| Cloud upstreamAn OAuth-based connection from a local Paperclip instance to a remote instance for company sync | An OAuth-based connection from a local Paperclip instance to a remote instance for company sync |
| Content guardA filter that prevents sensitive data from being transmitted through tools | A filter that prevents sensitive data from being transmitted through tools |
| CSPContent Security Policy | Content Security Policy |
| Custom imageA user-defined Docker image used as an agent execution environment | A user-defined Docker image used as an agent execution environment |
| Environment leaseA time-limited claim on an environment execution slot | A time-limited claim on an environment execution slot |
| JWTJSON Web Token (short-lived run tokens) | JSON Web Token (short-lived run tokens) |
| LeaseTime-limited ownership claim on a pipeline case preventing concurrent work | Time-limited ownership claim on a pipeline case preventing concurrent work |
| Live eventA real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) | A real-time event delivered over WebSocket for live UI updates (run progress, cost events, terminal output) |
| MCPModel Context Protocol (served by `packages/mcp-server`) | Model Context Protocol (served by packages/mcp-server) |
| OTELOpenTelemetry (opt-in tracing instrumentation) | OpenTelemetry (opt-in tracing instrumentation) |
| PGliteEmbedded PostgreSQL (see Technical Terms) | Embedded PostgreSQL (see Technical Terms) |
| PipelineA stage-based workflow definition with ordered stages and allowed transitions | A stage-based workflow definition with ordered stages and allowed transitions |
| Pipeline caseA work item flowing through a pipeline with structured fields, workspace references, and lease ownership | A work item flowing through a pipeline with structured fields, workspace references, and lease ownership |
| Pipeline stageA named step in a pipeline (working, review, done, cancelled) with position and config | A named step in a pipeline (working, review, done, cancelled) with position and config |
| Pipeline transitionA defined allowed move from one pipeline stage to another | A defined allowed move from one pipeline stage to another |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring) | Reach, Impact, Confidence, Effort (issue prioritization scoring) |
| Run continuationMechanism for agents to resume interrupted heartbeat runs with preserved context | Mechanism for agents to resume interrupted heartbeat runs with preserved context |
| Runtime slotA leased execution slot for tool runtime processes with concurrency management | A leased execution slot for tool runtime processes with concurrency management |
| SDLCSoftware Development Lifecycle (the `.sdlc/` artifact pipeline) | Software Development Lifecycle (the .sdlc/ artifact pipeline) |
| Setup sessionAn interactive terminal session for configuring a custom environment image | An interactive terminal session for configuring a custom environment image |
| SLI / SLOService Level Indicator / Service Level Objective | Service Level Indicator / Service Level Objective |
| Smoke labAn integration testing facility for validating connection health and behavior | An integration testing facility for validating connection health and behavior |
| SSEServer-Sent Events (deferred realtime transport) | Server-Sent Events (deferred realtime transport) |
| System promptThe per-agent instruction set that defines behavior (stored in agent_config_revisions) | The per-agent instruction set that defines behavior (stored in agent_config_revisions) |
| Tool access policyA rule set governing which agents can use which tools under what conditions | A rule set governing which agents can use which tools under what conditions |
| Tool connectionAn OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access | An OAuth-authenticated integration with a third-party service (GitHub, Slack, etc.) for agent tool access |
| Tool gatewayAn MCP/SSE bridge that proxies tool calls from agents to external services | An MCPModel Context Protocol (served by `packages/mcp-server`)/SSEServer-Sent Events (deferred realtime transport) bridge that proxies tool calls from agents to external services |
| Tool profile bindingAn association between a tool profile and an agent or project for runtime resolution | An association between a tool profile and an agent or project for runtime resolution |
| Tool runtime profileA configured tool execution environment with capacity, timeout, and security parameters | A configured tool execution environment with capacity, timeout, and security parameters |
| Tools ecosystemThe full lifecycle of discovering, installing, configuring, and using third-party tools | The full lifecycle of discovering, installing, configuring, and using third-party tools |
| Work productA typed deliverable attached to an issue, backed by an artifact or referencing a workspace file | A typed deliverable attached to an issue, backed by an artifact or referencing a workspace file |