# Paxel Integration (P5)

Co-Vibe runs the **official** YC Paxel tool — the public `ycombinator/paxel`
Docker image from [paxel.ycombinator.com](https://paxel.ycombinator.com) — never
a clone. Paxel analyzes a developer's local Claude Code, Codex, and Cursor
sessions and returns a builder profile (scores, archetypes, decision patterns).

Because the transcripts live on the developer's machine, the **local companion**
executes Paxel locally; the Co-Vibe server only records the request and the
resulting report reference. No transcript or source content passes through the
Co-Vibe server or the browser.

## Flow

1. A developer opens their own profile (Performance → their row) and clicks
   **Run Paxel** (optionally choosing *All repos* and a project name). This POSTs
   `{ action: "request" }` to `/api/paxel`, creating a `paxel_runs` row with
   status `requested`.
2. On the developer's machine, `covibe-local paxel` — Paxel is strictly
   user-triggered; the background watch loop never claims or runs it —
   authenticates with the MCP token and `POST`s `{ operation: "claim" }` to
   `/api/paxel/runner`. The server atomically moves the oldest pending run to
   `running` and returns its options, so a UI-requested run is picked up the
   next time the developer runs `covibe-local paxel`. Claiming with
   `create: true` (ad-hoc companion run) while the developer already has a
   requested/running run is rejected with a 409 so a developer never has more
   than one active run.
3. The companion verifies Docker is running, then executes the official image:

   ```bash
   docker run --rm \
     -v ~/.claude:/claude:ro -v ~/.cursor:/cursor:ro -v ~/.codex:/codex:ro \
     -v "$(pwd)":/workspace \
     ycombinator/paxel [--project NAME] [--since DATE] [--no-repo] [--no-sentry]
   ```

   For an *all-repo* run the working-tree mount is omitted and `--no-repo` is
   passed. The worktree mount is only added for a single-repo run.
4. The companion parses the official output for the report URL/token and `POST`s
   `{ operation: "complete", status, report_url, report_token }` back to
   `/api/paxel/runner`. The run becomes `completed` (or `failed` — Docker exit,
   Docker unavailable, or timeout all report a useful `error`).
5. The profile polls `/api/paxel` while a run is active — at the
   `pollIntervalMs` cadence the overview response supplies — and shows the
   report link + token when it lands.

## Strictly user-triggered (no watch integration)

The machine-level `covibe-local watch` loop never claims or executes Paxel
runs — by design, no background process touches local agent transcripts
unattended. Clicking **Run Paxel** in the UI queues a `requested` run; it
executes only when the developer runs `covibe-local paxel` on their machine,
which claims the pending run before creating an ad-hoc one. The watch loop
logs `Paxel runs are user-triggered (covibe-local paxel)` at startup.

## Timeouts and stale runs

- **Execution timeout** (companion): the Docker process is killed after 30
  minutes by default; override with `COVIBE_PAXEL_TIMEOUT_MS` or
  `--timeout-ms`. A timed-out run is completed `failed` with a timeout error.
- **Stale run cleanup** (server): a run stuck in `running` whose `started_at`
  is older than 2 hours (override with `COVIBE_PAXEL_STALE_MS`) is marked
  `failed` with error `abandoned by companion` and a `paxel_run_abandoned`
  work event. The cleanup runs lazily during the `/api/paxel` overview GET and
  the runner claim — no cron needed.

## Running it

```bash
# one-off, current repo (creates + runs immediately):
npm exec -- covibe-local paxel --base-url http://localhost:3000

# all repos, named project, skip Sentry:
npm exec -- covibe-local paxel --all-repos --project acme --no-sentry

# preview the exact docker command without executing (no token, no server contact):
npm exec -- covibe-local paxel --dry-run

# a UI-requested run is claimed by the same command — run it after clicking
# Run Paxel in the console (the background watch loop never runs Paxel):
npm exec -- covibe-local paxel
```

Keep `COVIBE_MCP_TOKEN` exported (same token used for MCP) for real runs; a
`--dry-run` works without it. The companion never prints the raw token, and
`error` strings are clipped to the last 1000 characters of stderr with `cv_`
tokens masked.

## Visibility & enablement

- **Capability flag** `paxel_enabled` (see `.env.example` `COVIBE_PAXEL_ENABLED`):
  default on for local/self-hosted, off for the central hosted origin where the
  server cannot reach a developer's Docker. A tenant admin can toggle it from
  Settings (`{ action: "set_enabled" }`).
- **Per-run sharing** `tenant_visible`: a completed report is private to the
  developer by default; the owner can mark it team-visible from the profile
  (`{ action: "set_visibility" }`). Team-visible reports appear read-only on
  that developer's Performance profile for teammates in the same workspace.
- **Image/binary overrides** `COVIBE_PAXEL_IMAGE` / `COVIBE_PAXEL_DOCKER` support
  podman, OrbStack, and Colima, and pin a specific image for CI.

## Data model

`paxel_runs` is workspace-scoped with Postgres RLS plus repository workspace
filters and stores: `run_type` (`repo|all-repo`), `project`, `since`, `no_repo`,
`no_sentry`, `status` (`requested|running|completed|failed|cancelled`),
`tenant_visible`, `requested_by` (`ui|companion`), `report_url`, `report_token`,
`output_reference`, `error`, and request/start/complete timestamps. No transcript
or diff content is ever stored.

## Code map

- Schema: `src/server/db/schema-paxel-statements.ts`
- Repository: `src/server/repositories/paxel-runs.ts`
- Feature flag: `src/server/paxel/feature-flag.ts`
- UI actions / overview: `src/server/paxel/actions.ts` → `src/app/api/paxel/route.ts`
- Companion runner (token auth): `src/server/paxel/runner.ts` → `src/app/api/paxel/runner/route.ts`
- Stale cleanup: `src/server/paxel/stale.ts`
- Companion command: `scripts/local-runtime/paxel-run.ts` (+ `paxel-deps.ts`, `paxel-command.ts`), wired in `scripts/local-companion.ts`
- Unwired watch claimer (kept for reference, no callers): `scripts/local-runtime/paxel-watch.ts`
- UI panel: `src/features/console/views/redesign/profile-paxel.tsx` (admin
  toggle in `redesign/admin-controls.tsx`)
