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>
5.5 KiB
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>orghAPI) 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 iscompleted/success. If it fails, pullgh run view <id> --log-failedand 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
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 mainclaudecontainer reads it via stdin. The shared PVC is mounted at/paperclipwithHOME=/paperclipto 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 asConfigFieldSchema[]) that Paperclip's web UI renders as a form.parse.ts— Parses Claude'sstream-jsonoutput format to extract session IDs, token usage, cost, and summaries.session.ts— Session codec for session resume.skills.ts/models.ts— ImplementlistSkills/syncSkillsandlistModelsfor theServerAdapterModuleinterface.
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
-
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. -
Concurrency guard — Before creating a Job,
execute.tslists existing Jobs labeled with the agent ID and blocks if any are still running (prevents session conflicts on the shared PVC). -
Prompt delivery — The prompt is written by a busybox init container to an
emptyDirvolume, then read by the mainclaudecontainer viastdin. This avoids escaping issues with env vars containing complex characters. -
Log streaming — Uses
k8s.Logfollow mode with automatic reconnection. If the follow stream ends before the job completes (API disconnect), a one-shot log read is used as fallback. -
Session resume — Works via the shared
/paperclipPVC mounted asHOME. TheruntimeSessionIdis passed via--resumeto the Claude CLI.