Files
paperclip-adapter-claude-k8s/CLAUDE.md
Chris Farhood 995c6761a0 docs(CLAUDE.md): require verification of every action's outcome
Add a "Verify everything" section enumerating the proof Claude must
produce after pushes, CI runs, publishes, file edits, and state-changing
CLI/API calls. Exit code 0 is not evidence of success.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 07:03:36 -04:00

75 lines
5.5 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
This is a Paperclip adapter plugin that runs Claude Code agents as isolated Kubernetes Jobs instead of inside the main Paperclip process. It uses the `@kubernetes/client-node` library to interact with the K8s API.
## CI/CD
Build and publish are handled by GitHub Actions on tag push — do **not** build locally. To release a new version, bump `package.json` with `npm version` and push the tag — CI handles the rest.
## Verify everything
Never assume an action succeeded because the command exited 0 or because it "should" have worked. Verify the *outcome* of every action you take, especially anything async, anything that crosses a process/network/CI boundary, and anything that publishes or deploys.
Required checks:
- **After `git push`**: confirm the remote ref moved (`git ls-remote origin <branch>` or `gh` API) and confirm the expected workflow run was triggered. If a tag was pushed, confirm the tag exists on the remote.
- **After triggering a CI workflow**: poll `gh run view <id>` until it completes. Do not declare success until every job in the run is `completed/success`. If it fails, pull `gh run view <id> --log-failed` and diagnose before reporting back.
- **After a release/publish**: verify the artifact actually exists at its destination (`npm view <pkg>@<version>` for npm, registry/API check for others). A workflow that exited 0 is not proof — read the logs and verify the artifact.
- **After file edits**: re-read the file (or run `git diff`) to confirm the change landed exactly as intended.
- **After running tests/typecheck/build**: read the output. "It exited 0" is not enough — check that the expected number of tests ran, no suites were skipped silently, no warnings indicate the change didn't take effect.
- **After any state-changing API/CLI call** (kubectl, gh, npm, git config, etc.): query the resulting state and confirm it matches expectations.
If verification fails or is impossible, say so explicitly. Never paper over uncertainty with optimistic phrasing like "should be fine" or "looks good" — either you verified it or you didn't, and the user needs to know which.
## Common Commands
```bash
npm run typecheck # Type-check without emitting (local dev only)
npm test # Run tests (vitest run)
npm run test:watch # Run tests in watch mode
npm run coverage # Run tests with coverage
```
Do not run `npm run build` locally — it's run by the CI pipeline. To release: bump version (`npm version`), push, and CI publishes automatically.
Single test file: `npx vitest run src/server/execute.test.ts`
## Architecture
### Entry Point
`src/index.ts` exports `createServerAdapter()` which returns a `ServerAdapterModule` with all adapter capabilities. It re-exports types, models, and the execute function.
### Server Module (`src/server/`)
- **`execute.ts`** — Core execution flow: checks for concurrent runs, creates a K8s Job, waits for pod scheduling, streams logs, waits for job completion, parses Claude's stream-json output, and returns the result. Also handles cleanup and retention.
- **`job-manifest.ts`** — Builds the K8s Job manifest. Key design: an init container (busybox) writes the prompt to an emptyDir volume, then the main `claude` container reads it via stdin. The shared PVC is mounted at `/paperclip` with `HOME=/paperclip` to enable session resume.
- **`k8s-client.ts`** — Wrapper around `@kubernetes/client-node`. Caches the KubeConfig and self-pod introspection (`getSelfPodInfo`) which discovers the container image, imagePullSecrets, DNS config, PVC claim name, and env vars to forward to Job pods.
- **`config-schema.ts`** — Returns the UI config schema (typed as `ConfigFieldSchema[]`) that Paperclip's web UI renders as a form.
- **`parse.ts`** — Parses Claude's `stream-json` output format to extract session IDs, token usage, cost, and summaries.
- **`session.ts`** — Session codec for session resume.
- **`skills.ts`** / **`models.ts`** — Implement `listSkills`/`syncSkills` and `listModels` for the `ServerAdapterModule` interface.
### CLI Module (`src/cli/`)
- **`format-event.ts`** — Formats Claude stream events for terminal output.
### UI Parser (`src/ui-parser.ts`)
- Parses adapter-specific UI configuration fields.
### Config Schema Note
The types in `config-schema.ts` (`ConfigFieldSchema`) must match what Paperclip's `SchemaConfigFields` component expects, since Paperclip's server calls `adapter.getConfigSchema()` and the UI reads the JSON at runtime.
## Key Design Decisions
1. **Pod introspection** — On first `execute()` call, `getSelfPodInfo()` reads the running pod's spec via K8s API and caches it. Every subsequent Job inherits the Deployment's image, secrets, DNS, and PVC without additional config.
2. **Concurrency guard** — Before creating a Job, `execute.ts` lists existing Jobs labeled with the agent ID and blocks if any are still running (prevents session conflicts on the shared PVC).
3. **Prompt delivery** — The prompt is written by a busybox init container to an `emptyDir` volume, then read by the main `claude` container via `stdin`. This avoids escaping issues with env vars containing complex characters.
4. **Log streaming** — Uses `k8s.Log` follow mode with automatic reconnection. If the follow stream ends before the job completes (API disconnect), a one-shot log read is used as fallback.
5. **Session resume** — Works via the shared `/paperclip` PVC mounted as `HOME`. The `runtimeSessionId` is passed via `--resume` to the Claude CLI.