Imported from Patrick9263/pickpic (
AGENTS.md). Install upstream withnpx skills add Patrick9263/pickpic. Copyright stays with the author.
Codex guidance for PickPic
Before making changes, read CLAUDE.md: What this is, Commands, Working sessions, Traps that cost real time, Conventions, and the Architecture sections relevant to your task. This file is an entrypoint, not a replacement. Read the scheduled-review details only when working on that tooling.
Noncanonical orientation — see What this is and Architecture: PickPic is a private photo-proofing system. A heart is an edit request, not a social reaction. Originals stay local, and the iPad's durable on-device queue is authoritative for upload state. The React frontend and one TypeScript Worker codebase serve three deployments sharing D1 and R2; the native Swift iPad app converts RAW files and uploads progressively.
Instruction ownership
CLAUDE.md is the canonical source for assistant-neutral repository knowledge:
architecture, commands, testing, operational safeguards, implementation traps, and
conventions. AGENTS.md is Codex's discovery entrypoint and addendum; its shared-rule
summaries are noncanonical and link back to CLAUDE.md.
- Update shared guidance in
CLAUDE.md, regardless of which assistant is working. If that makes anAGENTS.mdsummary or pointer inaccurate, update both files atomically in the same PR. - Put Codex-only guidance in
AGENTS.md. Keep Claude-only tooling, automation, permissions, and scheduled-job guidance in the Claude-specific portions ofCLAUDE.md. - Before committing either instruction file, check the other's pointers and summaries for needed changes. Synchronization is part of the current change; do not open a routine follow-up issue for it.
- Only create a synchronization issue when the companion update is genuinely blocked or intentionally deferred. Identify the exact rule, the canonical change, and the file or section still needing an update.
Codex workflow
- Use a
codex/branch in an isolated worktree based on freshly fetchedorigin/main. Inspectgit worktree list --porcelain; reuse this task's managed worktree if present. Use Codex's managed-worktree tool when available and pass the returned directory explicitly to shell commands. Never nest a worktree or alter the shared checkout or another session's worktree, including locked Claude sessions. See the noncanonical safety summary below and the canonical Working sessions rules. - Stop at a reviewable PR unless Patrick explicitly authorizes further action; never infer permission to merge, deploy, or apply migrations from a development request. Check the staged paths and diff before committing, and report checks, caveats, branch, and commit with the PR.
- GitHub authentication is local setup: use
gh auth status; if invalid, ask Patrick to rungh auth login -h github.com. Never store or print credentials to fix authentication. Do not copy.claude/settings.local.jsonpermissions into repository configuration. - Use
.nvmrcthroughnvm use(install withnvm installif needed), thennpm ci. Verifynode --versionandnpm --versionrather than relying on a machine-specific PATH. No project.codex/config.tomlis needed for this setup.
Commands and testing at a glance
Noncanonical summary of Commands and Working sessions; read those sections for full commands and caveats.
- Correctness gate:
npm run checkruns lint,format:check, all three Vitest suites, and the TypeScript/Vite build. Run it before pushing TypeScript or Prettier-scanned root-file changes, including these instructions, using Node 22. npm run devserves current web/Worker edits;wrangler devcan serve stale build output.npm run format:checkchecks formatting;npm run formatwrites it. Run tools from your own worktree root: Prettier uses the repository.gitignore, including the nested.claude/worktrees/exclusion.npm run testseparates pure Worker helpers (plain Node,vitest.config.ts), Worker/D1 integration tests (workerd,vitest.config.workers.ts), and frontend helpers/components (jsdom,vitest.config.src.ts). The Worker suite shares D1: keep file parallelism off and clear each file's tables inbeforeEach. Preserve the frontend script'sNODE_OPTIONS=--no-experimental-webstorage.- Add tests for new pure logic in the same PR.
npm run test:e2eis the local Playwright gallery smoke suite, outsidecheckand CI; run it for gallery-flow changes. It seeds local D1 via migrations, so respect any task prohibition on migrations and report that validation limit. See theAUTH_MODE=sessioncaveat in Commands before running it. - Keep TypeScript (
worker/+src/) and Swift (ipad/) sessions separate. For iPad changes, use thexcodebuild ... clean buildcommand in Commands againstipad/PickPic.xcodeproj, schemePickPic, with scratch derived data. A build does not run tests: runxcodebuild testand extendPickPicTestswhen touching pure logic or decoding. Filter large build logs while preserving the command's exit status. Never hand-edit the generated Swift PlaygroundsPackage.swift(see iPad architecture).
Safety reminders before implementation
These are noncanonical summaries, not substitutes for the linked sections.
- Working sessions and
Conventions: one focused PR, no unrelated refactors.
Preserve existing user changes and explanatory code comments. For source files
around 1,200 lines or larger, use
rgto find symbols and read relevant ranges; remeasure file sizes rather than reading whole large files. - Commands and CI/CD: D1 migrations are
manual and stay out of CI. A migration must be applied before
its PR merges, from a checkout containing the new file. All three Workers share
the database, and a push to
maindeploys all three. A green check is not authorization to run those operations. - Conventions: never commit secrets, credentials, local environment files, or Claude local settings. Runtime secrets live in Cloudflare. Preserve custom domains and admin Access protection. Security findings must stay out of the public tracker; see the private-reporting rule in Scheduled review job, without adopting the unattended job's implementation workflow.
- Traps 1–2: new persisted
UploadJobfields must decode withdecodeIfPresent(...) ?? default; olderupload-queue.jsonmust still load or in-flight state is lost. AnUploadStagechange affects exhaustive switches and ad-hocstage ==/||checks across files; search both shapes and preferisActiveOperationfor busy state. - Traps 3–4: have Patrick fully quit Xcode
before editing
project.pbxproj. New Swift sources underPickPic.swiftpm/need all four project entries (build file, file reference, group child, Sources phase); confirm the build'sSwiftFileList, since a green build can omit them. - Traps 5–6: duplicate detection is
server-authoritative via source hashes and the unique partial index on
(event_id, source_sha256). Preflight is only an optimization and must fall back to conversion on failure.original_filenameis the RAW filename;byte_sizeis the proof JPEG size. Neither filename alone nor filename plus that byte size proves a RAW duplicate. - Trap 7: missing GPS is normal. Use filename/capture time as reliable signals and retain public coordinate rounding.