4.5 KiB
AGENTS.md: guide for AI agents and contributors
Photocraft is an open-source, native, Photoshop-comparable image editor written in Rust only (no JavaScript or TypeScript). Read this file first, then docs/.
1. Orientation (5 minutes)
| Read | Why |
|---|---|
docs/architecture.md |
Crate map, dependency layers, the engine/UI seam, document model |
docs/development.md |
Build, test, run, drive the app programmatically, debug tricks |
docs/contributing.md |
Rules: clean-room, tests, layering, style, commits |
docs/control-protocol.md |
JSON control channel: how agents drive and screenshot the running app |
docs/ui-design.md |
Design tokens, themes, widgets, and how to match Photoshop's look |
crates/<name>/README.md (where present) |
Public API of that crate |
2. Workspace map
crates/
geom color raster L0 foundation (runtime pixel formats, COW tiles, blend math)
psd codecs L0 standalone format crates (no workspace deps; publishable)
doc L1 document model (layers, masks, adjustments, effects: pure data)
ops paint algo text L2 history, brush engine, imaging algorithms, text engine
compose gpu format L3 CPU compositor (the oracle), wgpu compositor, .pcraft native format
io L4 document <-> PSD / flat formats
engine L5 Session + command registry (every action is a command)
ui-egui automation L6 egui shell (thin: all actions go through the engine); MCP server
testkit test helpers
apps/
photocraft desktop app (eframe/wgpu), TCP control server
photocraft-cli headless CLI (convert/info/run/batch/commands/mcp)
xtask/ cargo xtask layers | wasm | ci | stats | corpus
Layering is enforced by cargo xtask layers. A crate may depend only on lower layers. psd and codecs depend on nothing in the workspace. Nothing below ui-egui may use egui, eframe, winit or rfd.
3. Golden rules
- Everything is a command. New user-visible behaviour = a command in
crates/engine/src/commands.rs(id, label, menu path, shortcut, params doc,enabled,run) plus tests incrates/engine/src/tests.rs. The UI, CLI, control channel and future MCP all dispatch commands by id. - No format or colour assumptions. Bit depth (8/16/32f) and colour model (RGB/Gray/CMYK/Lab…) are runtime data. Never introduce a
u8-only pixel path in public APIs. Test at several depths. - Clean-room. We studied Photon Studio (proprietary) and Photoshop for behaviour and look only. Never copy their code, shaders or assets. Implement from public specs (Adobe PSD spec, ISO 32000 blend modes, papers) and observation. Third-party assets must be permissively licensed and recorded (see
assets/). - Tests are the gate. Every change comes with tests. Format crates use round-trip, synthetic-generator, oracle and fuzz tests. Keep the real-file PSD corpus results from regressing.
- The UI is thin and data-driven. UI state lives in
ui-egui/src/state.rs(serde), so the control channel can read and drive it. Colours and radii come fromtheme::Tokens, never hard-coded. - Verify UI changes visually. Launch the app with
--control, drive it, takeui.screenshot, and look at the PNG. Seedocs/development.md. - Never break wasm. L0–L6 must
cargo check --target wasm32-unknown-unknown(runcargo xtask wasm).
4. Before you finish a task
cargo test -p <crates you touched>
cargo clippy -p <crates> --all-targets -- -D warnings
cargo xtask layers
cargo xtask wasm # if you touched L0–L6
Parallel agents: use your own target dir (CARGO_TARGET_DIR=target/agent-<name>) to avoid the Cargo build lock, and edit only the crates you own. Keep every Cargo.toml valid at all times: the crates/* glob means one broken manifest breaks everyone's build. Create or rewrite manifests atomically: write to a temp file outside crates/, then mv it into place.
5. Where things are tracked
plan/(local, gitignored): research, parity plan, execution plan, estimates.docs/roadmap.md: milestones M0–M12 and their status.log/(local, gitignored): the dev log.
6. Keeping the native format complete
photocraft-format deliberately fails to compile when a photocraft-doc struct gains a field, so
nothing is silently dropped from .pcraft saves. When you add a doc field, add it to
crates/format/src/manifest.rs and convert.rs with #[serde(default)] so older files still load.