a71c4b6782
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The task lifecycle and recovery subsystems decide when agent work is still productive, stalled, or ready for review. > - Existing recovery paths can observe stopped or incomplete work, but there was no first-class per-task watchdog model with scoped review permissions. > - Watchdog follow-ups also need strict boundaries so recovery/status-only runs cannot mutate approvals or perform deliverable work. > - This pull request adds the task watchdog data model, API/service layer, scheduler/review flow, adapter wake context, UI configuration surfaces, and docs. > - The branch has been rebased onto current `paperclipai/paperclip` `master`; the watchdog migration is now ordered after master's latest migrations as `0104_issue_watchdogs`. > - The benefit is a more explicit task-review loop that preserves Paperclip's single-assignee and governance invariants while making stalled work easier to route. ## Linked Issues or Issue Description No linked GitHub issue. Paperclip task: [PAP-11275](/PAP/issues/PAP-11275). ## Problem or motivation Task recovery needs a first-class watchdog path that can inspect stopped work and create scoped follow-ups without bypassing normal task ownership. Board/UI users need a way to configure watchdogs on tasks and see watchdog-related live work. Recovery/status-only runs must remain limited to status reporting and must not create approvals, link approvals, or submit approval comments. ## Proposed solution Add a task-watchdog data model, scheduler/classifier, scoped mutation guard, adapter wake context, API/UI configuration surfaces, and documentation so watchdog agents can review stopped task subtrees under explicit boundaries. ## Alternatives considered Reuse the existing recovery-action flow only. That would keep stopped-work detection implicit, make per-task watchdog assignment harder to expose in the UI, and would not provide a durable scoped-review issue for stalled task trees. ## Roadmap alignment This is Paperclip control-plane lifecycle infrastructure for task execution and recovery. I checked `ROADMAP.md`; this PR does not duplicate an existing planned core item. ## What Changed - Added issue watchdog schema, migration, shared contracts, validators, CRUD API, and service support. - Added task watchdog scheduler/classifier behavior, scoped mutation enforcement, adapter wake context, and default watchdog mandate guidance. - Added UI surfaces for configuring watchdogs on new/existing tasks, viewing watchdog activity, and exposing the experimental setting. - Added docs for the user-facing task watchdog workflow and implementation semantics. - Gated new-task watchdog setup behind `enableTaskWatchdogs` and blocked cheap status-only recovery runs from approval mutations. - Rebased onto current `master` and renumbered the idempotent watchdog migration from the branch-local `0102_issue_watchdogs` slot to `0104_issue_watchdogs`. - Addressed Greptile feedback by loading watchdog classifier input with a recursive subtree query and centralizing the watchdog origin-kind constant. - Added and updated focused server/UI tests for watchdog routes, scheduler/classifier behavior, scope boundaries, live task visibility, settings, and new issue dialog behavior. ## Verification - `pnpm vitest run server/src/__tests__/task-watchdogs-scheduler.test.ts server/src/__tests__/task-watchdogs-classifier.test.ts` - `pnpm vitest run server/src/__tests__/approval-routes-idempotency.test.ts server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts` - `pnpm vitest run ui/src/components/NewIssueDialog.test.tsx` - `pnpm --filter @paperclipai/server typecheck` - `git diff --check` - Verified the PR diff does not include `pnpm-lock.yaml` or `.github/workflows`. ## Risks - Medium risk: this introduces a new task lifecycle surface touching DB schema, server routes/services, adapter wake context, and UI task configuration. - Watchdog scheduling behavior depends on the new experimental setting and runtime context checks behaving consistently across local and production agents. - The watchdog migration is idempotent (`IF NOT EXISTS` / duplicate-object guards) so users who tried the previous branch-local migration number should not get duplicate-object failures. - CI and the second Greptile pass are pending after the latest review-fix push. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI Codex, GPT-5-class coding agent in the Paperclip workspace. Exact runtime model id and context window were not exposed to the agent; tool use and local command execution were enabled. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots — N/A per Paperclip task instruction: do not add screenshots/images to this PR unless they are specifically part of the work. - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1144 lines
52 KiB
Markdown
1144 lines
52 KiB
Markdown
# Paperclip V1 Implementation Spec
|
|
|
|
Status: Implementation contract for first release (V1)
|
|
Date: 2026-04-28
|
|
Audience: Product, engineering, and agent-integration authors
|
|
Source inputs: `GOAL.md`, `PRODUCT.md`, `SPEC.md`, `DATABASE.md`, current monorepo code
|
|
|
|
## 1. Document Role
|
|
|
|
`SPEC.md` remains the long-horizon product spec.
|
|
This document is the concrete, build-ready V1 contract.
|
|
When there is a conflict, `SPEC-implementation.md` controls V1 behavior.
|
|
|
|
## 2. V1 Outcomes
|
|
|
|
Paperclip V1 must provide a full control-plane loop for autonomous agents:
|
|
|
|
1. A human board creates a company and defines goals.
|
|
2. The board creates and manages agents in an org tree.
|
|
3. Agents receive and execute tasks via heartbeat invocations.
|
|
4. All work is tracked through tasks/comments with audit visibility.
|
|
5. Token/cost usage is reported and budget limits can stop work.
|
|
6. The board can intervene anywhere (pause agents/tasks, override decisions).
|
|
|
|
Success means one operator can run a small AI-native company end-to-end with clear visibility and control.
|
|
|
|
## 3. Explicit V1 Product Decisions
|
|
|
|
These decisions close open questions from `SPEC.md` for V1.
|
|
|
|
| Topic | V1 Decision |
|
|
|---|---|
|
|
| Tenancy | Single-tenant deployment, multi-company data model |
|
|
| Company model | Company is first-order; all business entities are company-scoped |
|
|
| Board | Single human board operator per deployment |
|
|
| Org graph | Strict tree (`reports_to` nullable root); no multi-manager reporting |
|
|
| Visibility | Company-scoped visibility: board + all in-company agents can see all work objects by default; public/private deployment flags affect external exposure only and do **not** imply project/issue privacy |
|
|
| Communication | Tasks + comments only (no separate chat system) |
|
|
| Task ownership | Single assignee; atomic checkout required for `in_progress` transition |
|
|
| Task watchdogs | A task watchdog is an explicitly configured, issue-subtree-scoped verification and recovery capacity. It may restore live task paths inside the watched subtree and resolve only eligible task-level plan confirmations; it is not board authority, active-run output monitoring, or general liveness recovery. |
|
|
| Recovery | Liveness/watchdog recovery preserves explicit ownership: retry lost execution continuity where safe, otherwise open visible source-scoped recovery actions by default, use issue-backed recovery only for independent repair work, or require human escalation (see `doc/execution-semantics.md`) |
|
|
| Agent adapters | Built-in `process`, `http`, local CLI/session adapters, and OpenClaw gateway support; external adapters can also be loaded through the adapter plugin flow |
|
|
| Plugin framework | Local/self-hosted early plugin runtime is in scope; cloud marketplace and packaged public distribution remain out of scope |
|
|
| Auth | Mode-dependent human auth (`local_trusted` implicit board in current code; authenticated mode uses sessions), API keys for agents |
|
|
| Budget period | Monthly UTC calendar window |
|
|
| Budget enforcement | Soft alerts + hard limit auto-pause |
|
|
| Deployment modes | Canonical model is `local_trusted` + `authenticated` with `private/public` exposure policy (see `doc/DEPLOYMENT-MODES.md`) |
|
|
|
|
Low-trust agent presets are containment controls for hostile automated work, not
|
|
general project or issue privacy controls. The core preset resolver contract is
|
|
documented in `doc/LOW-TRUST-PRESETS.md`.
|
|
|
|
## 4. Current Baseline (Repo Snapshot)
|
|
|
|
As of 2026-02-17, the repo already includes:
|
|
|
|
- Node + TypeScript backend with REST CRUD for `agents`, `projects`, `goals`, `issues`, `activity`
|
|
- React UI pages for dashboard/agents/projects/goals/issues lists
|
|
- PostgreSQL schema via Drizzle with embedded PostgreSQL fallback when `DATABASE_URL` is unset
|
|
|
|
V1 implementation extends this baseline into a company-centric, governance-aware control plane.
|
|
|
|
## 5. V1 Scope
|
|
|
|
## 5.1 In Scope
|
|
|
|
- Company lifecycle (create/list/get/update/archive)
|
|
- Goal hierarchy linked to company mission
|
|
- Agent lifecycle with org structure and adapter configuration
|
|
- Task lifecycle with parent/child hierarchy and comments
|
|
- Atomic task checkout and explicit task status transitions
|
|
- Board approvals for hires and CEO strategy proposal
|
|
- Heartbeat invocation, status tracking, and cancellation
|
|
- Cost event ingestion and rollups (agent/task/project/company)
|
|
- Budget settings and hard-stop enforcement
|
|
- Board web UI for dashboard, org chart, tasks, agents, approvals, costs
|
|
- Agent-facing API contract (task read/write, heartbeat report, cost report)
|
|
- Auditable activity log for all mutating actions
|
|
|
|
## 5.2 Out of Scope (V1)
|
|
|
|
- Cloud-grade plugin marketplace/distribution beyond the local/self-hosted plugin runtime
|
|
- Revenue/expense accounting beyond model/token costs
|
|
- Knowledge base subsystem
|
|
- Public marketplace (ClipHub)
|
|
- Multi-board governance or role-based human permission granularity
|
|
- Automatic self-healing orchestration (auto-reassign/retry planners)
|
|
|
|
## 6. Architecture
|
|
|
|
## 6.1 Runtime Components
|
|
|
|
- `server/`: REST API, auth, orchestration services
|
|
- `ui/`: Board operator interface
|
|
- `packages/db/`: Drizzle schema, migrations, DB clients (Postgres)
|
|
- `packages/shared/`: Shared API types, validators, constants
|
|
|
|
## 6.2 Data Stores
|
|
|
|
- Primary: PostgreSQL
|
|
- Local default: embedded PostgreSQL at `~/.paperclip/instances/default/db`
|
|
- Optional local prod-like: Docker Postgres
|
|
- Optional hosted: Supabase/Postgres-compatible
|
|
- File/object storage:
|
|
- local default: `~/.paperclip/instances/default/data/storage` (`local_disk`)
|
|
- cloud: S3-compatible object storage (`s3`)
|
|
|
|
## 6.3 Background Processing
|
|
|
|
A lightweight scheduler/worker in the server process handles:
|
|
|
|
- heartbeat trigger checks
|
|
- stuck run detection
|
|
- budget threshold checks
|
|
|
|
Separate queue infrastructure is not required for V1.
|
|
|
|
## 7. Canonical Data Model (V1)
|
|
|
|
All core tables include `id`, `created_at`, `updated_at` unless noted.
|
|
|
|
## 7.0 Auth Tables
|
|
|
|
Human auth tables (`users`, `sessions`, and provider-specific auth artifacts) are managed by the selected auth library. This spec treats them as required dependencies and references `users.id` where user attribution is needed.
|
|
|
|
## 7.1 `companies`
|
|
|
|
- `id` uuid pk
|
|
- `name` text not null
|
|
- `description` text null
|
|
- `status` enum: `active | paused | archived`
|
|
- `pause_reason` text null
|
|
- `paused_at` timestamptz null
|
|
- `issue_prefix` text not null
|
|
- `issue_counter` int not null
|
|
- `budget_monthly_cents` int not null default 0
|
|
- `spent_monthly_cents` int not null default 0
|
|
- `attachment_max_bytes` int not null
|
|
- `require_board_approval_for_new_agents` boolean not null default false
|
|
- feedback sharing consent fields
|
|
- branding fields such as `brand_color`
|
|
|
|
Invariant: every business record belongs to exactly one company.
|
|
|
|
## 7.2 `agents`
|
|
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk `companies.id` not null
|
|
- `name` text not null
|
|
- `role` text not null
|
|
- `title` text null
|
|
- `icon` text null
|
|
- `status` enum: `active | paused | idle | running | error | pending_approval | terminated`
|
|
- `reports_to` uuid fk `agents.id` null
|
|
- `capabilities` text null
|
|
- `adapter_type` text; built-ins include `process`, `http`, `claude_local`, `codex_local`, `gemini_local`, `opencode_local`, `pi_local`, `cursor`, and `openclaw_gateway`
|
|
- `adapter_config` jsonb not null
|
|
- `runtime_config` jsonb not null default `{}`; may include Paperclip runtime policy such as `modelProfiles.cheap.adapterConfig` for an optional low-cost model lane that does not change the primary adapter config
|
|
- `default_environment_id` uuid fk `environments.id` null
|
|
- `context_mode` enum: `thin | fat` default `thin`
|
|
- `budget_monthly_cents` int not null default 0
|
|
- `spent_monthly_cents` int not null default 0
|
|
- pause fields: `pause_reason`, `paused_at`
|
|
- `permissions` jsonb not null default `{}`
|
|
- `last_heartbeat_at` timestamptz null
|
|
- `metadata` jsonb null
|
|
|
|
Invariants:
|
|
|
|
- agent and manager must be in same company
|
|
- no cycles in reporting tree
|
|
- `terminated` agents cannot be resumed
|
|
|
|
## 7.3 `agent_api_keys`
|
|
|
|
- `id` uuid pk
|
|
- `agent_id` uuid fk `agents.id` not null
|
|
- `company_id` uuid fk `companies.id` not null
|
|
- `name` text not null
|
|
- `key_hash` text not null
|
|
- `last_used_at` timestamptz null
|
|
- `revoked_at` timestamptz null
|
|
|
|
Invariant: plaintext key shown once at creation; only hash stored.
|
|
|
|
## 7.4 `goals`
|
|
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `title` text not null
|
|
- `description` text null
|
|
- `level` enum: `company | team | agent | task`
|
|
- `parent_id` uuid fk `goals.id` null
|
|
- `owner_agent_id` uuid fk `agents.id` null
|
|
- `status` enum: `planned | active | achieved | cancelled`
|
|
|
|
Invariant: at least one root `company` level goal per company.
|
|
|
|
## 7.5 `projects`
|
|
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `goal_id` uuid fk `goals.id` null
|
|
- `name` text not null
|
|
- `description` text null
|
|
- `status` enum: `backlog | planned | in_progress | completed | cancelled`
|
|
- `lead_agent_id` uuid fk `agents.id` null
|
|
- `target_date` date null
|
|
- `env` jsonb null (same secret-aware env binding format used by agent config)
|
|
|
|
Invariant:
|
|
|
|
- project env is merged into run environment for issues in that project and overrides conflicting agent env keys before Paperclip runtime-owned keys are injected
|
|
|
|
Routine execution issues add a routine-scoped env overlay after project env and before Paperclip runtime-owned keys. Routine env uses the same secret-aware binding format, is stored on `routines.env`, is snapshotted in routine revisions, and resolves secret refs against the routine binding target so routine-owned secrets do not require direct bindings on the executing agent.
|
|
|
|
## 7.6 `issues` (core task entity)
|
|
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `project_id` uuid fk `projects.id` null
|
|
- `project_workspace_id` uuid fk `project_workspaces.id` null
|
|
- `goal_id` uuid fk `goals.id` null
|
|
- `parent_id` uuid fk `issues.id` null
|
|
- `title` text not null
|
|
- `description` text null
|
|
- `status` enum: `backlog | todo | in_progress | in_review | done | blocked | cancelled`
|
|
- `priority` enum: `critical | high | medium | low`
|
|
- `assignee_agent_id` uuid fk `agents.id` null
|
|
- `assignee_user_id` text null
|
|
- checkout/execution locks: `checkout_run_id`, `execution_run_id`, `execution_agent_name_key`, `execution_locked_at`
|
|
- `created_by_agent_id` uuid fk `agents.id` null
|
|
- `created_by_user_id` uuid fk `users.id` null
|
|
- identifier fields: `issue_number`, `identifier`
|
|
- origin fields: `origin_kind`, `origin_id`, `origin_run_id`, `origin_fingerprint`
|
|
- `request_depth` int not null default 0
|
|
- `work_mode` text not null default `standard`; supported values:
|
|
- `standard`: normal autonomous execution. Agents may investigate, edit files, create artifacts, and complete the task.
|
|
- `ask`: answer-only execution. Agents may use tools for investigation or temporary scratch work, but the deliverable is an issue-thread answer; they must not write implementation code or produce an implementation plan.
|
|
- `planning`: plan-only execution. Agents create or revise the plan without implementation work; accepted-plan continuations remain planning-specific and create child issues from the approved plan.
|
|
- `billing_code` text null
|
|
- `assignee_adapter_overrides` jsonb null
|
|
- `execution_policy` jsonb null
|
|
- `execution_state` jsonb null
|
|
- execution workspace fields: `execution_workspace_id`, `execution_workspace_preference`, `execution_workspace_settings`
|
|
- `started_at` timestamptz null
|
|
- `completed_at` timestamptz null
|
|
- `cancelled_at` timestamptz null
|
|
- `hidden_at` timestamptz null
|
|
|
|
Invariants:
|
|
|
|
- single assignee only
|
|
- task must trace to company goal chain via `goal_id`, `parent_id`, or project-goal linkage
|
|
- `in_progress` requires assignee
|
|
- terminal states: `done | cancelled`
|
|
|
|
## 7.7 `issue_comments`
|
|
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `issue_id` uuid fk `issues.id` not null
|
|
- `author_agent_id` uuid fk `agents.id` null
|
|
- `author_user_id` uuid fk `users.id` null
|
|
- `body` text not null
|
|
|
|
## 7.8 `heartbeat_runs`
|
|
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `agent_id` uuid fk not null
|
|
- `invocation_source` enum: `scheduler | manual | callback`
|
|
- `status` enum: `queued | running | succeeded | failed | cancelled | timed_out`
|
|
- `started_at` timestamptz null
|
|
- `finished_at` timestamptz null
|
|
- `error` text null
|
|
- `external_run_id` text null
|
|
- `context_snapshot` jsonb null
|
|
|
|
## 7.9 `cost_events`
|
|
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `agent_id` uuid fk `agents.id` not null
|
|
- `issue_id` uuid fk `issues.id` null
|
|
- `project_id` uuid fk `projects.id` null
|
|
- `goal_id` uuid fk `goals.id` null
|
|
- `billing_code` text null
|
|
- `provider` text not null
|
|
- `model` text not null
|
|
- `input_tokens` int not null default 0
|
|
- `output_tokens` int not null default 0
|
|
- `cost_cents` int not null
|
|
- `occurred_at` timestamptz not null
|
|
|
|
Invariant: each event must attach to agent and company; rollups are aggregation, never manually edited.
|
|
|
|
## 7.10 `approvals`
|
|
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `type` enum: `hire_agent | approve_ceo_strategy | budget_override_required | request_board_approval`
|
|
- `requested_by_agent_id` uuid fk `agents.id` null
|
|
- `requested_by_user_id` uuid fk `users.id` null
|
|
- `status` enum: `pending | revision_requested | approved | rejected | cancelled`
|
|
- `payload` jsonb not null
|
|
- `decision_note` text null
|
|
- `decided_by_user_id` uuid fk `users.id` null
|
|
- `decided_at` timestamptz null
|
|
|
|
## 7.11 `activity_log`
|
|
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `actor_type` enum: `agent | user | system`
|
|
- `actor_id` uuid/text not null
|
|
- `action` text not null
|
|
- `entity_type` text not null
|
|
- `entity_id` uuid/text not null
|
|
- `details` jsonb null
|
|
- `created_at` timestamptz not null default now()
|
|
|
|
## 7.12 `project_memberships` + `agent_memberships`
|
|
|
|
Per-user project/agent membership is personal visibility state for board users. It only controls whether a resource appears in the current user's sidebar; it must not grant or revoke access to all-pages, detail pages, selectors, assignment flows, search, or existing permissions.
|
|
|
|
`project_memberships`:
|
|
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk `companies.id` not null
|
|
- `project_id` uuid fk `projects.id` not null
|
|
- `user_id` text not null
|
|
- `state` enum-like text: `joined | left`
|
|
- `created_at` timestamptz not null default now()
|
|
- `updated_at` timestamptz not null default now()
|
|
- unique `(company_id, user_id, project_id)`
|
|
|
|
`agent_memberships` mirrors the same shape with `agent_id` instead of `project_id` and unique `(company_id, user_id, agent_id)`.
|
|
|
|
Invariants:
|
|
|
|
- Missing membership rows mean `joined` for backward compatibility.
|
|
- Mutations are board-user-only `/me` operations; agent API keys are rejected.
|
|
- Viewer-role board users may update only their own membership rows through the narrow self-service helper.
|
|
- Target project/agent ownership is checked against the path company before mutation.
|
|
- Successful state changes write `resource_membership.joined` or `resource_membership.left` activity entries.
|
|
|
|
## 7.13 `company_secrets` + `company_secret_versions`
|
|
|
|
- Secret values are not stored inline in `agents.adapter_config.env`.
|
|
- Agent env entries should use secret refs for sensitive values.
|
|
- `company_secrets` tracks identity/provider metadata per company.
|
|
- `company_secret_versions` stores encrypted/reference material per version.
|
|
- Default provider in local deployments: `local_encrypted`.
|
|
|
|
Operational policy:
|
|
|
|
- Config read APIs redact sensitive plain values.
|
|
- Activity and approval payloads must not persist raw sensitive values.
|
|
- Config revisions may include redacted placeholders; such revisions are non-restorable for redacted fields.
|
|
|
|
## 7.14 Required Indexes
|
|
|
|
- `agents(company_id, status)`
|
|
- `agents(company_id, reports_to)`
|
|
- `issues(company_id, status)`
|
|
- `issues(company_id, assignee_agent_id, status)`
|
|
- `issues(company_id, parent_id)`
|
|
- `issues(company_id, project_id)`
|
|
- `cost_events(company_id, occurred_at)`
|
|
- `cost_events(company_id, agent_id, occurred_at)`
|
|
- `heartbeat_runs(company_id, agent_id, started_at desc)`
|
|
- `approvals(company_id, status, type)`
|
|
- `activity_log(company_id, created_at desc)`
|
|
- `assets(company_id, created_at desc)`
|
|
- `assets(company_id, object_key)` unique
|
|
- `issue_attachments(company_id, issue_id)`
|
|
- `company_secrets(company_id, name)` unique
|
|
- `company_secret_versions(secret_id, version)` unique
|
|
- `project_memberships(company_id, user_id)`
|
|
- `project_memberships(company_id, user_id, project_id)` unique
|
|
- `agent_memberships(company_id, user_id)`
|
|
- `agent_memberships(company_id, user_id, agent_id)` unique
|
|
|
|
## 7.15 `assets` + `issue_attachments`
|
|
|
|
- `assets` stores provider-backed object metadata (not inline bytes):
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `provider` enum/text (`local_disk | s3`)
|
|
- `object_key` text not null
|
|
- `content_type` text not null
|
|
- `byte_size` int not null
|
|
- `sha256` text not null
|
|
- `original_filename` text null
|
|
- `created_by_agent_id` uuid fk null
|
|
- `created_by_user_id` uuid/text fk null
|
|
- `issue_attachments` links assets to issues/comments:
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `issue_id` uuid fk not null
|
|
- `asset_id` uuid fk not null
|
|
- `issue_comment_id` uuid fk null
|
|
- V1 attachment serving contract:
|
|
- Default upload allowlist includes common images, PDF, plain text/markdown/JSON/CSV/HTML, ZIP, and video artifacts (`video/mp4`, `video/webm`, `video/quicktime`).
|
|
- Attachment reads are company-scoped and expose stable path metadata: `contentPath`/`openPath` for inline-safe viewing and `downloadPath` for forced download.
|
|
- Inline-safe responses use `Content-Disposition: inline`; unsafe types and explicit download requests use `attachment`.
|
|
- Video attachments are inline-safe and support single `Range: bytes=start-end` requests with `206`, `Content-Range`, and `Accept-Ranges: bytes` for browser playback/seeking.
|
|
- Attachment-backed artifact work products use `type: "artifact"`, `provider: "paperclip"`, and metadata with `attachmentId`, `contentType`, `byteSize`, `contentPath`, `openPath`, `downloadPath`, and optional `originalFilename`.
|
|
- Workspace-only file references use work product `metadata.resourceRef` with `kind: "workspace_file"`, `issueId`, `workspaceKind` (`execution_workspace` or `project_workspace`), `workspaceId`, `relativePath`, optional `line`/`column`, and `displayPath`. These references point at files in a workspace; they do not replace attachment-backed artifacts for deliverables that must be inspectable without workspace access.
|
|
|
|
## 7.15 `documents` + `document_revisions` + `issue_documents`
|
|
|
|
- `documents` stores editable text-first documents:
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `title` text null
|
|
- `format` text not null (`markdown`)
|
|
- `latest_body` text not null
|
|
- `latest_revision_id` uuid null
|
|
- `latest_revision_number` int not null
|
|
- `created_by_agent_id` uuid fk null
|
|
- `created_by_user_id` uuid/text fk null
|
|
- `updated_by_agent_id` uuid fk null
|
|
- `updated_by_user_id` uuid/text fk null
|
|
- `locked_at` timestamptz null
|
|
- `locked_by_agent_id` uuid fk null
|
|
- `locked_by_user_id` uuid/text fk null
|
|
- Locked documents are immutable until unlocked. Board operators can lock/unlock; agent writes to a locked key create a new issue document with a derived key instead of overwriting the locked document.
|
|
- `document_revisions` stores append-only history:
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `document_id` uuid fk not null
|
|
- `revision_number` int not null
|
|
- `body` text not null
|
|
- `change_summary` text null
|
|
- `issue_documents` links documents to issues with a stable workflow key:
|
|
- `id` uuid pk
|
|
- `company_id` uuid fk not null
|
|
- `issue_id` uuid fk not null
|
|
- `document_id` uuid fk not null
|
|
- `key` text not null (`plan`, `design`, `notes`, etc.)
|
|
|
|
## 7.16 Current Implementation Addenda
|
|
|
|
The current implementation includes additional V1-control-plane tables beyond the original February snapshot:
|
|
|
|
- Issue structure and review: `issue_relations` for blockers, `labels`/`issue_labels`, `issue_thread_interactions`, `issue_approvals`, `issue_execution_decisions`, `issue_work_products`, `issue_inbox_archives`, `issue_read_states`, and issue reference mention indexes.
|
|
- Execution and workspace control: `execution_workspaces`, `project_workspaces`, `workspace_runtime_services`, `workspace_operations`, `environments`, `environment_leases`, `agent_task_sessions`, `agent_runtime_state`, `agent_wakeup_requests`, heartbeat events, and watchdog decision tables.
|
|
- Plugins and routines: `plugins`, plugin config/state/entities/jobs/logs/webhooks, plugin database namespaces/migrations, plugin company settings, `routines`, `routine_revisions`, `routine_triggers`, and `routine_runs`.
|
|
- Access and operations: company memberships, instance roles, principal permission grants, invites, join requests, board API keys, CLI auth challenges, budget policies/incidents, feedback exports/votes, company skills, sidebar preferences, and company logos.
|
|
|
|
## 8. State Machines
|
|
|
|
## 8.1 Agent Status
|
|
|
|
Allowed transitions:
|
|
|
|
- `idle -> running`
|
|
- `running -> idle`
|
|
- `running -> error`
|
|
- `error -> idle`
|
|
- `idle -> paused`
|
|
- `running -> paused` (requires cancel flow)
|
|
- `paused -> idle`
|
|
- `* -> terminated` (board only, irreversible)
|
|
|
|
## 8.2 Issue Status
|
|
|
|
Allowed transitions:
|
|
|
|
- `backlog -> todo | cancelled`
|
|
- `todo -> in_progress | blocked | cancelled`
|
|
- `in_progress -> in_review | blocked | done | cancelled`
|
|
- `in_review -> in_progress | done | cancelled`
|
|
- `blocked -> todo | in_progress | cancelled`
|
|
- terminal: `done`, `cancelled`
|
|
|
|
Side effects:
|
|
|
|
- entering `in_progress` sets `started_at` if null
|
|
- entering `done` sets `completed_at`
|
|
- entering `cancelled` sets `cancelled_at`
|
|
|
|
V1 non-terminal liveness rule:
|
|
|
|
- agent-owned `todo`, `in_progress`, `in_review`, and `blocked` issues must have a live execution path, an explicit waiting path, or an explicit recovery path
|
|
- `in_review` is healthy only when a typed execution participant, pending issue-thread interaction or approval, user owner, active run, queued wake, or explicit recovery action owns the next action
|
|
- a blocked chain is covered only when each unresolved leaf issue is live or explicitly waiting
|
|
- when Paperclip cannot safely infer the next action, it surfaces the problem through visible blocked/recovery work instead of silently completing or reassigning work
|
|
- explicit recovery actions are the liveness primitive; source-scoped actions are the default form, issue-backed recovery is a fallback for independent repair work or safety boundaries, and comments alone are evidence rather than a healthy liveness path
|
|
|
|
Detailed ownership, execution, blocker, active-run watchdog, crash-recovery, and non-terminal liveness semantics are documented in `doc/execution-semantics.md`.
|
|
|
|
## 8.3 Approval Status
|
|
|
|
- `pending -> approved | rejected | cancelled`
|
|
- terminal after decision
|
|
|
|
## 9. Auth and Permissions
|
|
|
|
## 9.1 Board Auth
|
|
|
|
- Session-based auth for human operator
|
|
- Board has full read/write across all companies in deployment
|
|
- Every board mutation writes to `activity_log`
|
|
|
|
## 9.2 Agent Auth
|
|
|
|
- Bearer API key mapped to one agent and company
|
|
- Agent key scope:
|
|
- read org/task/company context for own company
|
|
- read/write own assigned tasks and comments
|
|
- create tasks/comments for delegation
|
|
- report heartbeat status
|
|
- report cost events
|
|
- Agent cannot:
|
|
- bypass approval gates
|
|
- modify company-wide budgets directly
|
|
- mutate auth/keys
|
|
|
|
## 9.3 Permission Matrix (V1)
|
|
|
|
| Action | Board | Agent |
|
|
|---|---|---|
|
|
| Create company | yes | no |
|
|
| Hire/create agent | yes (direct) | request via approval |
|
|
| Pause/resume agent | yes | no |
|
|
| Create/update task | yes | yes |
|
|
| Force reassign task | yes | limited |
|
|
| Approve strategy/hire requests | yes | no |
|
|
| Report cost | yes | yes |
|
|
| Set company budget | yes | no |
|
|
| Set subordinate budget | yes | yes (manager subtree only) |
|
|
| Set work-object visibility (issue/project) | no | no (pro gate) |
|
|
|
|
## 9.4 Permission Terminology and Default Visibility Rule
|
|
|
|
Paperclip V1 keeps a company-scoped visibility model as the default because centralized authorization and scoped work-object controls are not yet a core V1 control surface.
|
|
|
|
The approved term set is:
|
|
|
|
- **Agent profile visibility**: identity-level facts needed for delegation and governance (name, role, capabilities, reporting lines).
|
|
- **Agent config visibility**: adapter/runtime config metadata and secret-access policy.
|
|
- **Assignment/invocation permission**: who may modify or execute a task.
|
|
- **Work-object visibility**: who can read/write issues, comments, projects, and attachments.
|
|
- **Tool/secret policy**: what tools and secret-backed credentials an agent can use and what appears in logs.
|
|
- **Escalation authority**: where refusal/blocked decisions route (manager, then board).
|
|
|
|
## 9.5 Core V1 Rule: what “private” means
|
|
|
|
- A **private marker** on an agent profile (where represented) does **not** make company-visible work private.
|
|
- Company-visible work objects (issues, comments, work products, costs, activity, project/task state) remain visible to the board and in-company agents by default.
|
|
- Project/issue-level privacy, scoped assignment-only object visibility, and organization-wide custom ACLs are deferred to Pro/Enterprise controls.
|
|
|
|
## 9.6 V1 vs Pro/Enterprise Controls (recommended target split)
|
|
|
|
| Permission area | Free / V1 default | Pro / Enterprise |
|
|
|---|---|---|
|
|
| Company boundary | Hard boundary only (`company_id`) | Multi-company policy overlays (`membership`, `project`, and `task` scopes) |
|
|
| Simple roles | Board + agent roles with existing approval/budget gates | Additional role aliases + scoped approver roles |
|
|
| Profile visibility | Full profile visibility for coordination and audit | Optional profile redaction / selective sharing for external surfaces |
|
|
| Config visibility | Board full read with redacted secret fields; agent config read/write constrained by own agent identity | Scoped config visibility controls and central policy enforcement |
|
|
| Assignment/invocation | Assignment creates execution authority; board can reassign or force release | Delegation policies and scoped invokers with deny-listed tool classes |
|
|
| Work-object visibility | All issues and projects in-company are visible to board and agents | Project/issue ACLs and reviewer-only channels |
|
|
| Tool/secret policy | Secret refs, log redaction, and adapter-level command/webhook restrictions | Tool allowlists with centralized policy evaluation |
|
|
| Escalation | Escalate from agent to manager to board; board approval/budget gates remain authoritative | Escalation routing and SLA windows |
|
|
|
|
## 9.7 Recommended first-slice implementation order
|
|
|
|
1. Lock route-level checks for existing company boundaries, actor extraction, and approval/budget gates.
|
|
2. Treat profile privacy as external-facing signal only; do not use it to hide company-visible work objects.
|
|
3. Enforce assignment/invocation coupling (`assignee`/`agent` checks, checkout semantics, invocation checks).
|
|
4. Standardize read-path redaction for secrets and secret references, including logs and activity.
|
|
5. Standardize escalation paths (`blocked` and refusal) so non-board agents hand off by manager/board with immutable audit.
|
|
|
|
## 9.8 Scoped Task Assignment Grants
|
|
|
|
`tasks:assign` remains the broad assignment permission. Existing unscoped grants preserve compatibility and allow the principal to assign any visible company task within normal company-boundary checks.
|
|
|
|
`tasks:assign_scope` is the constrained assignment permission. Its `principal_permission_grants.scope` JSON must include at least one recognized constraint:
|
|
|
|
- Project scope: `projectId`, `projectIds`, or `allow: ["project:<projectId>"]`.
|
|
- Target-agent allowlist: `agentId`, `agentIds`, `assigneeAgentId`, `assigneeAgentIds`, `targetAgentId`, `targetAgentIds`, or `allow: ["agent:<agentId>"]`.
|
|
- Managed-subtree scope: `managerAgentId`, `managerAgentIds`, `managedSubtreeAgentId`, `managedSubtreeAgentIds`, `subtreeAgentId`, `subtreeAgentIds`, `subtreeRootAgentId`, `subtreeRootAgentIds`, or `allow: ["subtree:<agentId>"]`.
|
|
|
|
When multiple constraint families are present, assignment must satisfy all of them. Denials return `403` with a generic scope explanation and do not disclose details about hidden or unrelated resources.
|
|
|
|
## 9.9 Task Watchdog Authority Contract
|
|
|
|
A task watchdog is a scoped execution capacity for a configured watchdog agent on one watched issue subtree. It is not a separate principal, does not inherit board auth, and does not expand the selected agent's company boundary. The server must enforce the watchdog contract from persisted watchdog configuration and run context; custom instructions and prompt text can narrow the mandate but cannot expand it.
|
|
|
|
The watched subtree is the source issue plus descendants reached through `parent_id`, excluding every issue whose `origin_kind = 'task_watchdog'` and excluding all descendants below those watchdog issues. The generated reusable watchdog issue is outside the watched work subtree for scan purposes, but the watchdog agent may update that reusable watchdog issue to record its own review disposition.
|
|
|
|
Task-watchdog wakes must include server-derived capability metadata that names the watched root, reusable watchdog issue, excluded `task_watchdog` origin branches, allowed operations, and denied operations. Watchdogs must use that metadata and server denials for capability discovery; they must not create visible probe issues, comments, or throwaway tasks to learn their permissions.
|
|
|
|
### Allowed watchdog mutations
|
|
|
|
Within the watched subtree, a watchdog run may perform only mutations that restore or clarify the next live/waiting path:
|
|
|
|
- add comments that explain findings, evidence, and next action
|
|
- create descendant follow-up issues under an included subtree issue, inheriting company, project, goal, and workspace context from that subtree
|
|
- assign or reassign included issues to active, invokable, same-company agents when normal assignment checks and scoped assignment grants allow it
|
|
- move included issues among `todo`, `in_progress`, `in_review`, and `blocked` when the transition is needed to restore a valid action path
|
|
- reopen `done` or `cancelled` included issues only with explicit resume metadata and an audit comment when evidence shows the stopped disposition is wrong or incomplete
|
|
- add, replace, or clear blockers on included issues when the blocker target is in the same company and the change makes the waiting path more accurate
|
|
- set or refresh a one-shot monitor on an included issue when the current assignee owns the future check
|
|
- accept or reject eligible task-level plan confirmations as defined below
|
|
- update the reusable watchdog issue itself to `done`, `in_review`, or `blocked` with the evidence for the watchdog decision
|
|
|
|
Every watchdog-triggered mutation must write activity with the watchdog id, source issue id, watchdog issue id when present, run id, and stop fingerprint. Mutations still use the normal status-transition, blocker, assignment, budget, and company-boundary guards.
|
|
|
|
### Disallowed watchdog mutations
|
|
|
|
A task watchdog must not:
|
|
|
|
- mutate issues outside the watched subtree, except for comments or newly created follow-up issues that are children of included subtree issues
|
|
- mutate company, project, goal, agent, auth, API key, budget, secret, environment, plugin, or deployment settings
|
|
- approve or reject rows in the `approvals` table, including hiring, CEO strategy, spend, budget override, or `request_board_approval` decisions
|
|
- resolve execution-policy decisions unless the watchdog agent is the typed participant under that policy outside of its watchdog capacity
|
|
- force-release checkout/execution locks, cancel active runs, terminate processes, or perform active-run output watchdog decisions
|
|
- create visible probe issues, comments, or throwaway tasks to discover whether an operation is allowed
|
|
- delete issue documents, comments, attachments, work products, or activity records
|
|
- change the watchdog configuration, select a different watchdog agent, or create nested watchdog configurations
|
|
- treat custom instructions as authority to bypass approval gates, cross company boundaries, access secrets, or override this contract
|
|
|
|
When the safe next action needs one of these disallowed mutations, the watchdog must leave a valid waiting path by commenting, creating an in-subtree escalation/follow-up issue, assigning to the correct owner, or leaving the source issue blocked on a first-class blocker.
|
|
|
|
### Interaction resolution
|
|
|
|
The initial V1 watchdog resolver may resolve exactly one interaction family: `request_confirmation` interactions that are eligible task-level plan confirmations. The watchdog may accept a coherent eligible plan or reject/request changes with a reason. It may not resolve `request_checkbox_confirmation`, `ask_user_questions`, `suggest_tasks`, linked approvals, board approvals, or ad hoc document comments.
|
|
|
|
A plan confirmation is eligible only when all of these are true:
|
|
|
|
- the interaction is pending and belongs to an issue inside the watched subtree, excluding the reusable watchdog issue and its descendants
|
|
- the interaction target is an `issue_document` with key `plan` on that same issue, and the target revision is still current
|
|
- the interaction has an explicit plan-approval purpose marker; title text, body prose, or idempotency key shape alone is not enough
|
|
- accepting the plan authorizes decomposition or task-level continuation inside the watched subtree only
|
|
- the plan does not request hiring, budget/spend approval, secret access, production deployment, security-sensitive policy changes, legal/compliance decisions, destructive data changes, cross-company work, or any other board-only governed action
|
|
- no newer board/user comment, document revision, superseding interaction, custom instruction, or issue policy reserves the decision for a human, CTO, Security, or the board
|
|
- the plan names concrete child/follow-up work, owners or assignee selection criteria, dependencies/blockers, and acceptance criteria clearly enough that decomposition can proceed without further judgment
|
|
|
|
If any condition fails, the watchdog must not accept the interaction. It should reject with a reason when the plan is clearly invalid, or leave/escalate the decision when the right owner is a board user, CTO, Security, or another typed approver.
|
|
|
|
### Downstream acceptance criteria
|
|
|
|
Implementation, security, UI, and QA work for task watchdogs must prove these contract points:
|
|
|
|
- server tests deny cross-company watched issues, watchdog agents, watchdog issues, blockers, interactions, and assignment targets
|
|
- server tests deny paused, terminated, pending-approval, budget-blocked, or otherwise uninvokable watchdog agents
|
|
- watchdog-scoped mutations can touch only the watched subtree and the reusable watchdog issue, with activity records for each mutation
|
|
- interaction tests prove only eligible `request_confirmation` plan confirmations are accepted or rejected, and all other interaction kinds remain unavailable to watchdogs
|
|
- plan-confirmation tests cover stale document revisions, missing purpose markers, outside-subtree targets, governed actions, newer user comments, and explicit human/CTO/Security reservations
|
|
- scheduler tests prove live runs, queued wakes, and scheduled retries suppress watchdog wakeups, while terminal, cancelled, blocked, and review leaves are still verified when the subtree has no live path
|
|
- tests prove `task_watchdog` origin issues and descendants are excluded from scans so watchdogs do not trigger themselves
|
|
- regression tests prove watchdog capability discovery comes from wake metadata/denials and denied probes do not create visible issues
|
|
- UI copy and badges distinguish task watchdogs from active-run output watchdogs, monitors, reviewers, approvers, and liveness recovery
|
|
- prompt/context tests prove custom instructions are appended after non-overridable safety constraints and cannot expand authority
|
|
- QA validates a full create/edit/remove/run/reuse flow with screenshots for UI changes
|
|
|
|
No unresolved policy decision blocks implementation once CTO and Security accept this contract. Deliberately deferred and disallowed for the first implementation: resolving interaction kinds beyond eligible plan confirmations, letting watchdogs cancel active runs, approving board/governance actions, mutating outside the watched subtree, or allowing watchdog agents to modify their own watchdog configuration. Any expansion requires a new product/security review.
|
|
|
|
## 10. API Contract (REST)
|
|
|
|
All endpoints are under `/api` and return JSON.
|
|
|
|
## 10.1 Companies
|
|
|
|
- `GET /companies`
|
|
- `POST /companies`
|
|
- `GET /companies/:companyId`
|
|
- `PATCH /companies/:companyId`
|
|
- `PATCH /companies/:companyId/branding`
|
|
- `POST /companies/:companyId/archive`
|
|
|
|
## 10.2 Goals
|
|
|
|
- `GET /companies/:companyId/goals`
|
|
- `POST /companies/:companyId/goals`
|
|
- `GET /goals/:goalId`
|
|
- `PATCH /goals/:goalId`
|
|
- `DELETE /goals/:goalId` (soft delete optional, hard delete board-only)
|
|
|
|
## 10.3 Agents
|
|
|
|
- `GET /companies/:companyId/agents`
|
|
- `POST /companies/:companyId/agents`
|
|
- `GET /agents/:agentId`
|
|
- `PATCH /agents/:agentId`
|
|
- `POST /agents/:agentId/pause`
|
|
- `POST /agents/:agentId/resume`
|
|
- `POST /agents/:agentId/terminate`
|
|
- `POST /agents/:agentId/keys` (create API key)
|
|
- `POST /agents/:agentId/heartbeat/invoke`
|
|
|
|
## 10.4 Tasks (Issues)
|
|
|
|
- `GET /companies/:companyId/issues`
|
|
- `POST /companies/:companyId/issues`
|
|
- `GET /issues/:issueId`
|
|
- `PATCH /issues/:issueId`
|
|
- `GET /issues/:issueId/documents`
|
|
- `GET /issues/:issueId/documents/:key`
|
|
- `PUT /issues/:issueId/documents/:key`
|
|
- `POST /issues/:issueId/documents/:key/lock`
|
|
- `POST /issues/:issueId/documents/:key/unlock`
|
|
- `GET /issues/:issueId/documents/:key/revisions`
|
|
- `DELETE /issues/:issueId/documents/:key`
|
|
- `POST /issues/:issueId/checkout`
|
|
- `POST /issues/:issueId/release`
|
|
- `POST /issues/:issueId/admin/force-release` (board-only lock recovery)
|
|
- `POST /issues/:issueId/comments`
|
|
- `GET /issues/:issueId/comments`
|
|
- `POST /companies/:companyId/issues/:issueId/attachments` (multipart upload)
|
|
- `GET /issues/:issueId/attachments`
|
|
- `GET /attachments/:attachmentId/content`
|
|
- `DELETE /attachments/:attachmentId`
|
|
|
|
### 10.4.1 Atomic Checkout Contract
|
|
|
|
`POST /issues/:issueId/checkout` request:
|
|
|
|
```json
|
|
{
|
|
"agentId": "uuid",
|
|
"expectedStatuses": ["todo", "backlog", "blocked", "in_review"]
|
|
}
|
|
```
|
|
|
|
Server behavior:
|
|
|
|
1. single SQL update with `WHERE id = ? AND status IN (?) AND (assignee_agent_id IS NULL OR assignee_agent_id = :agentId)`
|
|
2. if updated row count is 0, return `409` with current owner/status
|
|
3. successful checkout sets `assignee_agent_id`, `status = in_progress`, and `started_at`
|
|
|
|
`POST /issues/:issueId/admin/force-release` is an operator recovery endpoint for stale harness locks. It requires board access to the issue company, clears checkout and execution run lock fields, and may clear the agent assignee when `clearAssignee=true` is passed. The route must write an `issue.admin_force_release` activity log entry containing the previous checkout and execution run IDs.
|
|
|
|
## 10.5 Projects
|
|
|
|
- `GET /companies/:companyId/projects`
|
|
- `POST /companies/:companyId/projects`
|
|
- `GET /projects/:projectId`
|
|
- `PATCH /projects/:projectId`
|
|
|
|
## 10.6 Current-user Resource Memberships
|
|
|
|
- `GET /companies/:companyId/resource-memberships/me`
|
|
- `PUT /companies/:companyId/resource-memberships/me/projects/:projectId`
|
|
- `PUT /companies/:companyId/resource-memberships/me/agents/:agentId`
|
|
|
|
Request payload:
|
|
|
|
```json
|
|
{ "state": "joined" }
|
|
```
|
|
|
|
Allowed states are `joined` and `left`. Endpoints require a concrete board user and active company membership, reject agent API keys, and only mutate the caller's own sidebar visibility state. Joining/leaving is idempotent; missing rows read as `joined`.
|
|
|
|
## 10.7 Approvals
|
|
|
|
- `GET /companies/:companyId/approvals?status=pending`
|
|
- `POST /companies/:companyId/approvals`
|
|
- `POST /approvals/:approvalId/approve`
|
|
- `POST /approvals/:approvalId/reject`
|
|
|
|
## 10.8 Cost and Budgets
|
|
|
|
- `POST /companies/:companyId/cost-events`
|
|
- `GET /companies/:companyId/costs/summary`
|
|
- `GET /companies/:companyId/costs/by-agent`
|
|
- `GET /companies/:companyId/costs/by-project`
|
|
- `PATCH /companies/:companyId/budgets`
|
|
- `PATCH /agents/:agentId/budgets`
|
|
|
|
## 10.9 Activity and Dashboard
|
|
|
|
- `GET /companies/:companyId/activity`
|
|
- `GET /companies/:companyId/dashboard`
|
|
|
|
Dashboard payload must include:
|
|
|
|
- active/running/paused/error agent counts
|
|
- open/in-progress/blocked/done issue counts
|
|
- month-to-date spend and budget utilization
|
|
- pending approvals count
|
|
|
|
## 10.10 Error Semantics
|
|
|
|
- `400` validation error
|
|
- `401` unauthenticated
|
|
- `403` unauthorized
|
|
- `404` not found
|
|
- `409` state conflict (checkout conflict, invalid transition)
|
|
- `422` semantic rule violation
|
|
- `500` server error
|
|
|
|
## 10.11 Current Implementation API Addenda
|
|
|
|
The current app also exposes V1-supporting surfaces for:
|
|
|
|
- issue thread interactions (`suggest_tasks`, `ask_user_questions`, `request_confirmation`)
|
|
- issue approvals, issue references/search, labels, read state, inbox/archive state, and work products
|
|
- execution workspaces, project workspaces, workspace runtime services, and workspace operations
|
|
- task watchdog configuration and reusable watchdog issue orchestration for explicitly watched issue subtrees
|
|
- routines and scheduled/API/webhook triggers
|
|
- plugin installation, configuration, state, jobs, logs, webhooks, and plugin database namespace migration
|
|
- company import/export preview/apply, feedback export/vote routes, instance backup/config routes, invites, join requests, memberships, and permission grants
|
|
|
|
## 11. Heartbeat and Adapter Contract
|
|
|
|
## 11.1 Adapter Interface
|
|
|
|
```ts
|
|
interface AgentAdapter {
|
|
invoke(agent: Agent, context: InvocationContext): Promise<InvokeResult>;
|
|
status(run: HeartbeatRun): Promise<RunStatus>;
|
|
cancel(run: HeartbeatRun): Promise<void>;
|
|
}
|
|
```
|
|
|
|
## 11.2 Process Adapter
|
|
|
|
Config shape:
|
|
|
|
```json
|
|
{
|
|
"command": "string",
|
|
"args": ["string"],
|
|
"cwd": "string",
|
|
"env": {"KEY": "VALUE"},
|
|
"timeoutSec": 900,
|
|
"graceSec": 15
|
|
}
|
|
```
|
|
|
|
Behavior:
|
|
|
|
- spawn child process
|
|
- stream stdout/stderr to run logs
|
|
- mark run status on exit code/timeout
|
|
- cancel sends SIGTERM then SIGKILL after grace
|
|
|
|
## 11.3 HTTP Adapter
|
|
|
|
Config shape:
|
|
|
|
```json
|
|
{
|
|
"url": "https://...",
|
|
"method": "POST",
|
|
"headers": {"Authorization": "Bearer ..."},
|
|
"timeoutMs": 15000,
|
|
"payloadTemplate": {"agentId": "{{agent.id}}", "runId": "{{run.id}}"}
|
|
}
|
|
```
|
|
|
|
Behavior:
|
|
|
|
- invoke by outbound HTTP request
|
|
- 2xx means accepted
|
|
- non-2xx marks failed invocation
|
|
- optional callback endpoint allows asynchronous completion updates
|
|
|
|
## 11.4 Context Delivery
|
|
|
|
- `thin`: send IDs and pointers only; agent fetches context via API
|
|
- `fat`: include current assignments, goal summary, budget snapshot, and recent comments
|
|
|
|
## 11.5 Recovery Model Profiles
|
|
|
|
The optional `modelProfiles.cheap` lane is not a retry worker lane. Paperclip may request the cheap profile only for status-only recovery coordination, and those wakes must include guard context that prevents deliverable work and document/plan updates (`allowDeliverableWork: false`, `allowDocumentUpdates: false`, `resumeRequiresNormalModel: true`).
|
|
|
|
Failed source-work retries, process-loss retries, transient/scheduled retries, max-turn continuations, source-assignee continuations, and downstream source-work child/requeue/resume contexts must use the normal/original model lane. If cheap recovery repairs liveness while actual work remains, the next live continuation path must be a separate normal-model worker run with cheap hints scrubbed.
|
|
|
|
## 11.6 Scheduler Rules
|
|
|
|
Per-agent schedule fields in `adapter_config`:
|
|
|
|
- `enabled` boolean
|
|
- `intervalSec` integer (minimum 30)
|
|
- `maxConcurrentRuns` integer; new agents default to `20`; scheduler clamps configured values to `1..50`
|
|
|
|
Scheduler must skip invocation when:
|
|
|
|
- agent is paused/terminated
|
|
- an existing run is active
|
|
- hard budget limit has been hit
|
|
|
|
## 12. Governance and Approval Flows
|
|
|
|
## 12.1 Hiring
|
|
|
|
1. Agent or board creates `approval(type=hire_agent, status=pending, payload=agent draft)`.
|
|
2. Board approves or rejects.
|
|
3. On approval, server creates agent row and initial API key (optional).
|
|
4. Decision is logged in `activity_log`.
|
|
|
|
Board can bypass request flow and create agents directly via UI; direct create is still logged as a governance action.
|
|
|
|
## 12.2 CEO Strategy Approval
|
|
|
|
1. CEO posts strategy proposal as `approval(type=approve_ceo_strategy)`.
|
|
2. Board reviews payload (plan text, initial structure, high-level tasks).
|
|
3. Approval unlocks execution state for CEO-created delegated work.
|
|
|
|
Before first strategy approval, CEO may only draft tasks, not transition them to active execution states.
|
|
|
|
## 12.3 Board Override
|
|
|
|
Board can at any time:
|
|
|
|
- pause/resume/terminate any agent
|
|
- reassign or cancel any task
|
|
- edit budgets and limits
|
|
- approve/reject/cancel pending approvals
|
|
|
|
## 13. Cost and Budget System
|
|
|
|
## 13.1 Budget Layers
|
|
|
|
- company monthly budget
|
|
- agent monthly budget
|
|
- optional project budget (if configured)
|
|
|
|
## 13.2 Enforcement Rules
|
|
|
|
- soft alert default threshold: 80%
|
|
- hard limit: at 100%, trigger:
|
|
- set agent status to `paused`
|
|
- block new checkout/invocation for that agent
|
|
- emit high-priority activity event
|
|
|
|
Board may override by raising budget or explicitly resuming agent.
|
|
|
|
## 13.3 Cost Event Ingestion
|
|
|
|
`POST /companies/:companyId/cost-events` body:
|
|
|
|
```json
|
|
{
|
|
"agentId": "uuid",
|
|
"issueId": "uuid",
|
|
"provider": "openai",
|
|
"model": "gpt-5",
|
|
"inputTokens": 1234,
|
|
"outputTokens": 567,
|
|
"costCents": 89,
|
|
"occurredAt": "2026-02-17T20:25:00Z",
|
|
"billingCode": "optional"
|
|
}
|
|
```
|
|
|
|
Validation:
|
|
|
|
- non-negative token counts
|
|
- `costCents >= 0`
|
|
- company ownership checks for all linked entities
|
|
|
|
## 13.4 Rollups
|
|
|
|
Read-time aggregate queries are acceptable for V1.
|
|
Materialized rollups can be added later if query latency exceeds targets.
|
|
|
|
## 14. UI Requirements (Board App)
|
|
|
|
V1 UI routes:
|
|
|
|
- `/` dashboard
|
|
- `/companies` company list/create
|
|
- `/companies/:id/org` org chart and agent status
|
|
- `/companies/:id/tasks` task list/kanban
|
|
- `/companies/:id/agents/:agentId` agent detail
|
|
- `/companies/:id/costs` cost and budget dashboard
|
|
- `/companies/:id/approvals` pending/history approvals
|
|
- `/companies/:id/activity` audit/event stream
|
|
|
|
Required UX behaviors:
|
|
|
|
- global company selector
|
|
- quick actions: pause/resume agent, create task, approve/reject request
|
|
- conflict toasts on atomic checkout failure
|
|
- no silent background failures; every failed run visible in UI
|
|
|
|
## 15. Operational Requirements
|
|
|
|
## 15.1 Environment
|
|
|
|
- Node 20+
|
|
- `DATABASE_URL` optional
|
|
- if unset, auto-use embedded PostgreSQL under `~/.paperclip/instances/default/db`
|
|
|
|
## 15.2 Migrations
|
|
|
|
- Drizzle migrations are source of truth
|
|
- local/dev startup applies pending migrations automatically where supported
|
|
- `pnpm db:migrate` applies pending migrations manually
|
|
- no destructive migration in-place for V1 upgrade path
|
|
|
|
## 15.3 Logging and Audit
|
|
|
|
- structured logs (JSON in production)
|
|
- request ID per API call
|
|
- every mutation writes `activity_log`
|
|
|
|
## 15.4 Reliability Targets
|
|
|
|
- API p95 latency under 250 ms for standard CRUD at 1k tasks/company
|
|
- heartbeat invoke acknowledgement under 2 s for process adapter
|
|
- no lost approval decisions (transactional writes)
|
|
|
|
## 16. Security Requirements
|
|
|
|
- store only hashed agent API keys
|
|
- redact secrets in logs (`adapter_config`, auth headers, env vars)
|
|
- CSRF protection for board session endpoints
|
|
- rate limit auth and key-management endpoints
|
|
- strict company boundary checks on every entity fetch/mutation
|
|
|
|
## 17. Testing Strategy
|
|
|
|
## 17.1 Unit Tests
|
|
|
|
- state transition guards (agent, issue, approval)
|
|
- budget enforcement rules
|
|
- adapter invocation/cancel semantics
|
|
|
|
## 17.2 Integration Tests
|
|
|
|
- atomic checkout conflict behavior
|
|
- approval-to-agent creation flow
|
|
- cost ingestion and rollup correctness
|
|
- pause while run is active (graceful cancel then force kill)
|
|
|
|
## 17.3 End-to-End Tests
|
|
|
|
- board creates company -> hires CEO -> approves strategy -> CEO receives work
|
|
- agent reports cost -> budget threshold reached -> auto-pause occurs
|
|
- task delegation across teams with request depth increment
|
|
|
|
## 17.4 Regression Suite Minimum
|
|
|
|
A release candidate is blocked unless these pass:
|
|
|
|
1. auth boundary tests
|
|
2. checkout race test
|
|
3. hard budget stop test
|
|
4. agent pause/resume test
|
|
5. dashboard summary consistency test
|
|
|
|
## 18. Delivery Plan
|
|
|
|
Current implementation note: the milestones below describe the original V1 sequencing. Several systems originally framed as future work have since shipped or advanced materially, including issue documents/interactions, blockers, routines, execution workspaces, import/export portability, authenticated deployment modes, multi-user basics, and the local/self-hosted plugin runtime.
|
|
|
|
## Milestone 1: Company Core and Auth
|
|
|
|
- add `companies` and company scoping to existing entities
|
|
- add board session auth and agent API keys
|
|
- migrate existing API routes to company-aware paths
|
|
|
|
## Milestone 2: Task and Governance Semantics
|
|
|
|
- implement atomic checkout endpoint
|
|
- implement issue comments and lifecycle guards
|
|
- implement approvals table and hire/strategy workflows
|
|
|
|
## Milestone 3: Heartbeat and Adapter Runtime
|
|
|
|
- implement adapter interface
|
|
- ship `process` adapter with cancel semantics
|
|
- ship `http` adapter with timeout/error handling
|
|
- persist heartbeat runs and statuses
|
|
|
|
## Milestone 4: Cost and Budget Controls
|
|
|
|
- implement cost events ingestion
|
|
- implement monthly rollups and dashboards
|
|
- enforce hard limit auto-pause
|
|
|
|
## Milestone 5: Board UI Completion
|
|
|
|
- add company selector and org chart view
|
|
- add approvals and cost pages
|
|
|
|
## Milestone 6: Hardening and Release
|
|
|
|
- full integration/e2e suite
|
|
- seed/demo company templates for local testing
|
|
- release checklist and docs update
|
|
|
|
## 19. Acceptance Criteria (Release Gate)
|
|
|
|
V1 is complete only when all criteria are true:
|
|
|
|
1. A board user can create multiple companies and switch between them.
|
|
2. A company can run at least one active heartbeat-enabled agent.
|
|
3. Task checkout is conflict-safe with `409` on concurrent claims.
|
|
4. Agents can update tasks/comments and report costs with API keys only.
|
|
5. Board can approve/reject hire and CEO strategy requests in UI.
|
|
6. Budget hard limit auto-pauses an agent and prevents new invocations.
|
|
7. Dashboard shows accurate counts/spend from live DB data.
|
|
8. Every mutation is auditable in activity log.
|
|
9. App runs with embedded PostgreSQL by default and with external Postgres via `DATABASE_URL`.
|
|
|
|
## 20. Post-V1 Backlog (Explicitly Deferred)
|
|
|
|
- cloud-grade plugin marketplace/distribution
|
|
- richer workflow-state customization per team
|
|
- milestones/labels/dependency graph depth beyond V1 minimum
|
|
- realtime transport optimization (SSE/WebSockets)
|
|
- public template marketplace integration (ClipHub)
|
|
|
|
## 21. Company Portability Package (V1 Addendum)
|
|
|
|
V1 supports company import/export using a portable package contract:
|
|
|
|
- markdown-first package rooted at `COMPANY.md`
|
|
- implicit folder discovery by convention
|
|
- `.paperclip.yaml` sidecar for Paperclip-specific fidelity
|
|
- canonical base package is vendor-neutral and aligned with `docs/companies/companies-spec.md`
|
|
- common conventions:
|
|
- `agents/<slug>/AGENTS.md`
|
|
- `teams/<slug>/TEAM.md`
|
|
- `projects/<slug>/PROJECT.md`
|
|
- `projects/<slug>/tasks/<slug>/TASK.md`
|
|
- `tasks/<slug>/TASK.md`
|
|
- `skills/<slug>/SKILL.md`
|
|
|
|
Export/import behavior in V1:
|
|
|
|
- export emits a clean vendor-neutral markdown package plus `.paperclip.yaml`
|
|
- projects and starter tasks are opt-in export content rather than default package content
|
|
- recurring `TASK.md` entries use `recurring: true` in the base package and Paperclip routine fidelity in `.paperclip.yaml`
|
|
- Paperclip imports recurring task packages as routines instead of downgrading them to one-time issues
|
|
- export strips environment-specific paths (`cwd`, local instruction file paths, inline prompt duplication) while preserving portable project repo/workspace metadata such as `repoUrl`, refs, and workspace-policy references keyed in `.paperclip.yaml`
|
|
- export never includes secret values; env inputs are reported as portable declarations instead
|
|
- import supports target modes:
|
|
- create a new company
|
|
- import into an existing company
|
|
- import recreates exported project workspaces and remaps portable workspace keys back to target-local workspace ids
|
|
- import forces imported agent timer heartbeats off so packages never start scheduled runs implicitly
|
|
- import supports collision strategies: `rename`, `skip`, `replace`
|
|
- import supports preview (dry-run) before apply
|
|
- GitHub imports warn on unpinned refs instead of blocking
|