## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Its UI is the operator's daily surface: task lists, boards, budgets, agent status — all built on shadcn components and Tailwind > - Visual values (colors, spacing, type sizes, radii) were hardcoded at ~1,600 call sites: the same "small gray label" was 9/10/11px depending on the file, charts disagreed with chips about status colors, two toggle-switch implementations coexisted in two greens, and there was no visual regression coverage > - This made the UI drift-prone and made any restyle a hundreds-of-files project, which discourages design iteration > - This pull request extracts visual values into a single token layer in `ui/src/index.css`, adds a Storybook visual regression suite backed by external immutable baseline archives, and then applies a deliberate retune reviewed change-by-change on screenshot diffs > - The benefit is that Paperclip's look becomes a config surface: retheming is a token edit reviewed as a snapshot diff, drift is blocked by a token gate, and future UI PRs can prove exactly what changed visually without committing hundreds of PNGs ## Linked Issues or Issue Description No existing public issue covers this work (searched "design tokens", "visual regression", "design system" across issues and PRs). Related in spirit: Refs #8982 (theming a hardcoded panel — a one-off instance of the same problem class this PR addresses systematically). **Problem (feature-request form):** UI visual values are hardcoded per call site with no source of truth and no regression coverage; consistency depends on reviewer memory, and restyling requires mass file edits. **Proposed solution (this PR):** a single token layer + enforcement gate + externally stored visual snapshot suite, then an intentional restyle on top of that foundation. ## What Changed - **Token extraction (zero visual change, machine-verified during development):** committed codemods (`scripts/codemod-*.mjs`) moved ~1,600 hardcoded color/type/spacing/radius/shadow/misc values into named tokens in a non-inline `:root` block of `ui/src/index.css`. - **Visual regression suite:** `pnpm test:storybook-visual` covers 255 stories × light/dark = 510 Playwright screenshots at `maxDiffPixels: 0`, plus new primitive-coverage stories and deterministic-render fixes. - **External visual baselines:** committed PNG snapshots were removed. `tests/storybook-visual/baseline-manifest.json` pins an immutable archive URL/hash/size/count, and `scripts/storybook-visual-baseline.mjs` handles `download`, `verify`, `pack`, and trusted maintainer `upload` flows. - **Opt-in visual CI artifacts:** added a `Storybook Visual` workflow that runs on manual dispatch or PRs labeled `storybook-visual`, downloads/verifies the baseline, runs Playwright, and uploads Playwright report/test-result artifacts for review. Normal PR runs do not mutate baseline objects. - **Token gate:** `pnpm check:token-gates` — zero hex literals, zero arbitrary bracket values, zero raw font-sizes in `ui/src/components/**` and `ui/src/pages/**`, with a documented inline allowlist for legitimate opt-outs. - **Theme retune (intentional, snapshot-reviewed):** new base theme values; radius ladder derived from a single `--radius` knob; micro-type cluster collapsed to a named ladder (`--text-nano/micro/compact` + Tailwind `text-xs`/`text-sm`); letter-spacing collapsed to named steps. - **One status-color vocabulary:** charts, quota/budget bar fills, RUNNING/live chips, and liveness indicators all use the canonical `--status-*` hues. Light-mode legibility fixes for red alert surfaces that used dark-tuned text classes. - **One switch:** `ToggleSwitch` restyled to the registry capsule form, second hand-rolled implementation removed, and all call sites unified. - **Docs:** `DESIGN.md` is the design contract; `doc/design/` holds audit reports, decision logs, and updated guidance for external baseline review/update workflows. - Dead code removed (`agentStatusBadge` duplicate map), byte-identical contrast constants consolidated, semantic renames (`--project-seed`/`--project-none`, `--liveness-blue`). ## Verification - `pnpm check:token-gates` — 3/3 gates CLEAN during the design-system run - `pnpm typecheck` && `pnpm --filter @paperclipai/ui build` — green during the design-system run - `node --test scripts/__tests__/storybook-visual-baseline.test.mjs` — pass after external-baseline rework - `pnpm exec tsc --noEmit --pretty false --module NodeNext --moduleResolution NodeNext --target ES2022 --types node,@playwright/test tests/storybook-visual/playwright.config.ts tests/storybook-visual/storybook-visual.spec.ts` — pass after external-baseline rework - `git diff --check origin/pr/9134..HEAD` — pass after external-baseline rework - `find tests/storybook-visual -type f -name '*.png' -print | wc -l` — `0` - `node scripts/storybook-visual-baseline.mjs verify` — intentionally fails closed until the first trusted maintainer publishes the baseline archive and updates `baseline-manifest.json` ## Risks - **Large but shallow:** the PR still touches many UI files due to mechanical token extraction and retune work, but committed PNG snapshot churn has been removed from the branch. - **Baseline publication required before the visual suite can pass in clean clones:** the manifest currently has placeholder archive metadata. A trusted maintainer must publish the first immutable archive, then update `baseline-manifest.json`. - **Rendering platform variance:** the external baseline should be captured in the documented Linux/Chromium environment. Future CI runs verify against the pinned archive and fail closed on checksum/count mismatch. - **Visual CI is opt-in while stabilizing:** add the `storybook-visual` label or dispatch the workflow manually to produce downloadable Playwright report/test-result artifacts. - **Scheduled follow-ups, deliberately out of scope:** Tailwind palette classes map to semantic tokens in a dedicated pass; card/pill component consolidation; ESLint ratchet. Tracked in `doc/design/DECISION-SHEET.md`. ## Model Used Claude Fable 5 (Anthropic, `claude-fable-5`, Mythos-class tier) with extended thinking, running in Claude Code with tool use; mechanical phases delegated to Claude Sonnet subagents. Follow-up external-baseline rework assisted by OpenAI Codex (`gpt-5` coding agent with repository, terminal, and GitHub tool use). All bulk rewrites executed via deterministic, idempotent scripts committed in `scripts/`; intentional visual changes were human-reviewed on screenshot contact sheets. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run targeted local verification and documented the intentional baseline-publication failure above - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green *(pending new CI run after this rework)* - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups *(pending review)* - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) and OpenAI Codex --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Dotta <bippadotta@protonmail.com> Co-authored-by: Paperclip <noreply@paperclip.ing>
9.5 KiB
AGENTS.md
Guidance for human and AI contributors working in this repository.
1. Purpose
Paperclip is a control plane for AI-agent companies.
The current implementation target is V1 and is defined in doc/SPEC-implementation.md.
2. Read This First
Before making changes, read in this order:
doc/GOAL.mddoc/PRODUCT.mddoc/SPEC-implementation.mddoc/DEVELOPING.mddoc/DATABASE.md
doc/SPEC.md is long-horizon product context.
doc/SPEC-implementation.md is the concrete V1 build contract.
3. Repo Map
server/: Express REST API and orchestration servicesui/: React + Vite board UIpackages/db/: Drizzle schema, migrations, DB clientspackages/shared/: shared types, constants, validators, API path constantspackages/adapters/: agent adapter implementations (Claude, Codex, Cursor, etc.)packages/adapter-utils/: shared adapter utilitiespackages/plugins/: plugin system packagesdoc/: operational and product docs
4. Dev Setup (Auto DB)
Use embedded PGlite in dev by leaving DATABASE_URL unset.
pnpm install
pnpm dev
This starts:
- API:
http://localhost:3100 - UI:
http://localhost:3100(served by API server in dev middleware mode)
Quick checks:
curl http://localhost:3100/api/health
curl http://localhost:3100/api/companies
Reset local dev DB:
rm -rf data/pglite
pnpm dev
5. Core Engineering Rules
-
Keep changes company-scoped. Every domain entity should be scoped to a company and company boundaries must be enforced in routes/services.
-
Keep contracts synchronized. If you change schema/API behavior, update all impacted layers:
packages/dbschema and exportspackages/sharedtypes/constants/validatorsserverroutes/servicesuiAPI clients and pages
- Preserve control-plane invariants.
- Single-assignee task model
- Atomic issue checkout semantics
- Approval gates for governed actions
- Budget hard-stop auto-pause behavior
- Activity logging for mutating actions
-
Do not replace strategic docs wholesale unless asked. Prefer additive updates. Keep
doc/SPEC.mdanddoc/SPEC-implementation.mdaligned. -
Keep repo plan docs dated and centralized. When you are creating a plan file in the repository itself, new plan documents belong in
doc/plans/and should useYYYY-MM-DD-slug.mdfilenames. This does not replace Paperclip issue planning: if a Paperclip issue asks for a plan, update the issueplandocument per thepaperclipskill instead of creating a repo markdown file. -
Attach inspectable generated artifacts. When your task produces a user-inspectable deliverable file, follow the Paperclip skill's "Generated Artifacts and Work Products" workflow before final disposition. In this repo, prefer the self-contained skill helper at
skills/paperclip/scripts/paperclip-upload-artifact.shso the file is available through the Paperclip API, create/update an artifact work product when the file is the deliverable, link the uploaded artifact in the final issue comment, and then set status. Do not rely on local filesystem paths as the only access path. If an important file intentionally remains workspace-only, create/update a work product withmetadata.resourceRef.kind: "workspace_file"and a workspace-relative path, then name that work product and path in the final comment. Treat browse/search as a fallback for recovering workspace files, not the preferred deliverable path. Seedoc/AGENT-ARTIFACTS.mdfor details and.mp4/.webmexamples.
6. Database Change Workflow
When changing data model:
- Edit
packages/db/src/schema/*.ts - Ensure new tables are exported from
packages/db/src/schema/index.ts - Generate migration:
pnpm db:generate
- Validate compile:
pnpm -r typecheck
Notes:
packages/db/drizzle.config.tsreads compiled schema fromdist/schema/*.jspnpm db:generatecompilespackages/dbfirst
7. Verification Before Hand-off
Default local/agent test path:
pnpm test
This is the cheap default and only runs the Vitest suite. Browser suites stay opt-in:
pnpm test:e2e
pnpm test:release-smoke
Run the browser suites only when your change touches them or when you are explicitly verifying CI/release flows.
For normal issue work, run the smallest relevant verification first. Do not default to repo-wide typecheck/build/test on every heartbeat when a narrower check is enough to prove the change.
Run this full check before claiming repo work done in a PR-ready hand-off, or when the change scope is broad enough that targeted checks are not sufficient:
pnpm -r typecheck
pnpm test:run
pnpm build
If anything cannot be run, explicitly report what was not run and why.
8. API and Auth Expectations
- Base path:
/api - Board access is treated as full-control operator context
- Agent access uses bearer API keys (
agent_api_keys), hashed at rest - Agent keys must not access other companies
When adding endpoints:
- apply company access checks
- enforce actor permissions (board vs agent)
- write activity log entries for mutations
- return consistent HTTP errors (
400/401/403/404/409/422/500)
9. UI Expectations
- Keep routes and nav aligned with available API surface
- Use company selection context for company-scoped pages
- Surface failures clearly; do not silently ignore API errors
10. Pull Request Requirements
When creating a pull request (via gh pr create or any other method), you must read and fill in every section of .github/PULL_REQUEST_TEMPLATE.md. Do not craft ad-hoc PR bodies — use the template as the structure for your PR description. Required sections:
- Thinking Path — trace reasoning from project context to this change (see
CONTRIBUTING.mdfor examples) - What Changed — bullet list of concrete changes
- Verification — how a reviewer can confirm it works
- Risks — what could go wrong
- Model Used — the AI model that produced or assisted with the change (provider, exact model ID, context window, capabilities). Write "None — human-authored" if no AI was used.
- Checklist — all items checked
11. Definition of Done
A change is done when all are true:
- Behavior matches
doc/SPEC-implementation.md - Typecheck, tests, and build pass
- Contracts are synced across db/shared/server/ui
- Docs updated when behavior or commands change
- PR description follows the PR template with all sections filled in (including Model Used)
11. Fork-Specific: HenkDz/paperclip
This is a fork of paperclipai/paperclip with QoL patches and a built-in Hermes adapter story on branch feat/externalize-hermes-adapter (tree).
Branch Strategy
feat/externalize-hermes-adapternow shipshermes_localandhermes_gatewayas built-in core adapters.- Older fork branches may still document plugin-only Hermes; treat this file as authoritative for the current branch.
Hermes (built-in)
hermes_localis available without Adapter manager installation and runs the local Hermes CLI.hermes_gatewayis available without Adapter manager installation and calls an already-running Hermes API server.- Operators may still install external Hermes packages through Adapter manager to override/shadow the built-ins.
- Optional:
file:entry in~/.paperclip/adapter-plugins.jsonremains useful for local development of override packages.
Local Dev
- Fork runs on port 3101+ (auto-detects if 3100 is taken by upstream instance)
npx vite buildhangs on NTFS — usenode node_modules/vite/bin/vite.js buildinstead- Server startup from NTFS takes 30-60s — don't assume failure immediately
- Kill ALL paperclip processes before starting:
pkill -f "paperclip"; pkill -f "tsx.*index.ts" - Vite cache survives
rm -rf dist— delete both:rm -rf ui/dist ui/node_modules/.vite
Fork QoL Patches (not in upstream)
These are local modifications in the fork's UI. If re-copying source, these must be re-applied:
- stderr_group — amber accordion for MCP init noise in
RunTranscriptView.tsx - tool_group — accordion for consecutive non-terminal tools (write, read, search, browser)
- Dashboard excerpt —
LatestRunCardstrips markdown, shows first 3 lines/280 chars
Plugin System
PR #2218 (feat/external-adapter-phase1) adds external adapter support. See root AGENTS.md for full details.
- Adapters can be loaded as external plugins via
~/.paperclip/adapter-plugins.json - The plugin-loader should have ZERO hardcoded adapter imports — pure dynamic loading
createServerAdapter()must include ALL optional fields (especiallydetectModel)- Built-in UI adapters can shadow external plugin parsers; external override pause/resume should restore the built-in parser.
- Reference external adapters: Droid (npm); Hermes can also be tested as an override package.
Design system
DESIGN.md at the repo root is the source of truth for UI design decisions. The token-only rule applies to all ui/ changes: every color, spacing, radius, type, shadow, and motion value in ui/src/components/** and ui/src/pages/** comes from the token layer in ui/src/index.css — no hex, raw px, arbitrary Tailwind bracket values, or raw font-size/fontSize declarations in components, outside the documented allowlist in ui/src/index.css. Run pnpm check:token-gates (scripts/check-token-gates.mjs) before committing UI changes — it fails on any violation not covered by that allowlist.