krites — interface contracts (CLI, studio, MCP)¶
Status: DRAFT / intent (companion to 0001-krites.md)
Owner: Matt Cockayne
Last updated: 2026-06-21
0. How to read this¶
This defines the interface contracts for krites' three surfaces — the CLI
(low-level), the studio web UI (primary, §4), and MCP (§5): the commands,
their inputs / outputs / side-effects, the studio's functionality + UX, and the
MCP tool surface. It exists to anchor tests — the godog scenarios and unit
tests of 0001-krites.md §8.
- The CLI command contracts (§2–§3) are the single source of truth; the
studio (§4) and MCP (§5) are alternative drivers of the same operations on
the same shoot workspace (
0001§3). - These are guidance, not law. Shapes change as we build; when they do, update this doc and the tests together. The contract assertions under each command are the bits worth pinning.
- Requirement IDs (
R-AREA-n) give traceability: each should map to ≥1 unit test or.featurescenario. Stable handles, not a priority order. - Keywords: MUST = a contract a test enforces; SHOULD = expected but negotiable; MAY = optional.
- Consistent with
0001-krites.md. Where they disagree, 0001 wins and this is stale.
1. CLI conventions (cross-cutting)¶
Apply to every command; per-command sections note only departures.
1.1 Shoot scope¶
R-SCOPE-1(MUST) A command resolves the current shoot from--shoot <path>, else the nearest enclosing directory containing.krites/, else the configured default; with none resolvable it exits with a usage error and guidance, never guessing.R-SCOPE-2(MUST) The CLI scopes to one shoot; switching between many shoots is a studio capability (R-UI-2), not a CLI one.R-SCOPE-3(MUST) A command resolves all relative frame references against the shoot'soriginals/.
1.2 Global flags¶
| Flag | Meaning |
|---|---|
--shoot, -s <path> |
operate on this shoot workspace |
--profile <name> |
cull profile for this run; else the configured default (§0001 §6) |
--look <name> |
look for develop/export; else the default |
--json |
machine-readable output on stdout (GTB pkg/output) |
--yes, -y |
assume yes; never prompt (non-interactive / scripted) |
--dry-run |
validate + report intended actions; no writes, no spend, no pixels |
--force |
bypass the analysis/preview cache; recompute |
--config, --log-level, -v |
GTB-standard config + logging |
R-GLOBAL-1(MUST) Output is human-readable by default;--jsonemits a structured object, and on--jsonstdout is valid JSON only (logs → stderr).R-GLOBAL-2(MUST)--yesmakes a command fully non-interactive; one that would need input and cannot proceed exits with a usage error, never hangs.R-GLOBAL-3(MUST)--dry-runperforms no writes, no pixel rendering, and no network calls that spend credit, and reports what it would do.R-GLOBAL-4(MUST) Validation failures exit non-zero before any side effect; errors are reported via the GTB error handler (cockroachdb/errors) with a hint where one helps.R-GLOBAL-5(SHOULD) Long operations show progress on stderr (N of M frames); under--json, progress is suppressed or structured, never interleaved into the stdout JSON.R-GLOBAL-6(MUST) Re-running with unchanged inputs reuses cached analysis (same frame bytes + same profile ⇒ same verdict, no recompute);--forceoverrides.R-GLOBAL-7(MUST) Deterministic logic (sharpness, exposure, hashing, clustering, straighten angle, crop geometry, verdict resolution) is pure and reproducible — identical input ⇒ identical output, unit-tested directly.R-GLOBAL-8(MUST) Every provider call (decode, ML, inpaint) has a timeout; a hung backend fails the frame with a reason, never blocks the batch.R-GLOBAL-9(SHOULD) Batch operations are resumable — interrupting a cull of 4,000 frames and re-running continues from the cache, not from zero.
1.3 The non-destructive guarantee (the load-bearing contract)¶
R-ND-1(MUST) No command ever modifies a file underoriginals/after ingest. Tests assert original bytes/mtime are unchanged across cull / develop / retouch / remove.R-ND-2(MUST) Verdicts and edits are records (verdicts.yaml,edits/); the only command that writes new pixels isexport(intoexport/), and a command that would (e.g.remove's preview) writes into the cache/export, never over an original.R-ND-3(MUST) Every verdict/edit is reversible:resetreturns a frame to its post-ingest state; undo/redo is total in the studio (R-UI-9).R-ND-4(MUST) Deleting.krites/analysis/or.krites/previews/and re-running reproduces them with no loss of decisions (only caches are disposable).
1.4 Privacy¶
R-PRIV-1(MUST) Every cloud-capable provider is off by default; using one requires explicit config opt-in.R-PRIV-2(MUST) When a command would send frame data off-machine, it says so (and the studio shows a persistent indicator,R-UI-19);--dry-runreports it without sending.R-PRIV-3(MUST) Secrets (cloud API keys) live in the GTB keychain / env, never in the shoot or the project config file.
2. Ingest, cull & verdicts¶
2.1 krites ingest <dir>¶
Registers a shoot workspace from a source folder.
R-ING-1(MUST) Creates<shoot>/.krites/andshoot.yaml(id, name, date from EXIF, source, frame count); copies or links sources intooriginals/per--copy(default) /--link.R-ING-2(MUST) Reads EXIF (camera, lens, capture time, orientation) and generates a preview per frame; RAW decodes via theDecoderprovider.R-ING-3(MUST) Idempotent — re-ingesting the same source adds only new frames, never duplicates or disturbs existing verdicts/edits.R-ING-4(SHOULD) Reports a summary (N frames, formats, date range);--jsonreturns the manifest.
2.2 krites cull [--profile <name>] [--reanalyze]¶
The judge. Runs analysis → verdicts.
R-CULL-1(MUST) For each frame, computes the profile's enabled signals (sharpness, exposure, eye/blink, dedup cluster; +expression/aesthetic when enabled) and caches the raw analysis (.krites/analysis/<frame>.json).R-CULL-2(MUST) Resolves a verdict (keep/maybe/reject) from the signals via the named cull profile (0001§6) — hard gates →reject, the best-of-cluster →keep, others →maybe— and records human-readable reasons per frame inverdicts.yaml.R-CULL-3(MUST) Never deletes or moves a frame; arejectverdict is a label, not a file operation (R-ND-1).R-CULL-4(MUST) Verdict resolution is pure given (signals, profile) — unit-tested with golden fixtures (sharp / blurry / blink / burst / over- / under-exposed) independent of the ML backends.R-CULL-5(MUST)--reanalyzerecomputes signals; without it, cached analysis is reused (R-GLOBAL-6), only the verdict is re-resolved if the profile changed.R-CULL-6(SHOULD) Reports counts per verdict and per dominant reason;--jsonreturns the full verdict set.
2.3 krites verdict <frame> keep|maybe|reject / krites reset <frame>¶
R-VRD-1(MUST) Overrides krites' verdict for a frame and records that it is a human override (so the Phase 4 learning loop,0001§11, can weight it).R-VRD-2(MUST)reset <frame>clears overrides + edits, returning the frame to post-ingest state (R-ND-3).
2.4 krites dedup [--profile <name>]¶
R-DUP-1(MUST) Clusters near-duplicate / burst frames (perceptual hash + time proximity) and ranks within each cluster; records the cluster id and the proposed best on each member.R-DUP-2(MUST) Clustering + ranking are deterministic and unit-tested on a fixture burst.R-DUP-3(SHOULD) Re-running after a verdict override keeps the human's pick as the cluster's chosen frame.
3. Develop, retouch, remove & export¶
3.1 krites straighten / krites crop¶
R-DEV-1(MUST) Each proposes an edit (a level angle; a crop rectangle) recorded on the frame's edit record — never auto-applied destructively; the studio previews it (R-UI-12).R-DEV-2(MUST) Straighten angle is clamped to a sane maximum and the geometry is pure/unit-tested; crop respects a target aspect and stays within the frame.R-DEV-3(SHOULD)--allbatch-proposes across keepers; each remains individually overridable.
3.2 krites develop [--look <name>]¶
R-LOOK-1(MUST) Applies a look (white balance + tone + grade/LUT) from the catalog as develop settings on the edit record (not baked pixels until export);--lookselects, else the default.R-LOOK-2(MUST) Looks are config-driven (0001§6) — no hardcoded grade/curve constants in Go.
3.3 krites retouch (Phase ≥2) / krites remove <frame> --region (Phase ≥3)¶
R-RT-1(MUST) Retouch applies only the bounded effect set (0001§4.4), each toggle-able and recorded as reversible settings.R-RM-1(MUST)removerecords a mask + inpaint result reference on the edit record (non-destructive,R-ND-2); the inpaint runs through theInpainterprovider with a timeout (R-GLOBAL-8).R-RM-2(MUST) If theInpainteris cloud-backed,R-PRIV-2disclosure applies.
3.4 krites export¶
R-EXP-1(MUST) Renders the selected verdict set (defaultkeep) applying each frame's edits (straighten / crop / look / retouch / removals) intoexport/, to the chosen format/size/quality, carrying EXIF/copyright.R-EXP-2(MUST) It is the only pixel-producing command (R-ND-2); it never writes outsideexport/and never alters originals.R-EXP-3(SHOULD) Re-export is idempotent for unchanged frames (skips or overwrites deterministically);--dry-runlists what it would render.
3.5 krites xmp write|read¶
R-XMP-1(MUST, Phase 1)writeemits XMP sidecars beside originals carrying the cull verdict → star rating + pick/reject flag + colour label — readable by Lightroom (0001§4.7, §13-Q5). This is the core of the primary (~90%) finish-in-Lightroom workflow.R-XMP-1b(MUST, Phase 2) Once develop exists,writealso carries crop, straighten angle, and white balance into Camera-Raw XMP, so keepers open in Lightroom already leveled/cropped/WB-corrected. Looks / retouch / object- removal are never written to XMP (krites-export-only) — no half-applied Adobe develop mapping.R-XMP-2(MUST)readingests existing XMP ratings/labels so krites and Lightroom round-trip without clobbering each other; round-trip is unit-tested.R-XMP-3(MUST) Writing XMP is opt-in and never touches the originals' pixels (it writes the.xmpcompanion only).
3.6 krites profile|look list|show|add|edit|rm¶
R-CAT-1(MUST) Manage the cull-profile and look catalogs through the GTB config layer;initseedswedding-default+ a neutral look (0001§6).R-CAT-2(MUST) Edits hot-reload into a running studio; a profile change re-resolves verdicts without re-analysis (R-CULL-5).
4. The studio (web UI) — krites studio [--port N]¶
The studio is krites' primary surface, and its full interface contract —
functional requirements (R-UI-*), UX + mockup, and the HTTP + SSE API
contract (R-API-*) — lives in its own spec:
0003-studio.md. That is the authoritative source; this
section is a pointer.
In brief: a local, single-user, localhost-bound, desktop-first web app
(GTB service lifecycle, go:embed-ed frontend) over the same shoot workspace
the CLI uses — UI and CLI are interchangeable, the studio adds no domain logic of
its own. The frontend is a Svelte SPA (a non-binding default — the API contract
is framework-agnostic) talking to a thin Go JSON+SSE API, browser-served now and
Wails-ready for a native macOS app later (0001 §13-Q2). See 0003 for the
endpoint table and every requirement.
5. MCP surface¶
krites exposes its commands as MCP tools (GTB mcp feature) so an assistant can
drive the read/analysis loop — a third surface mirroring the CLI.
R-MCP-1(MUST) Read/analysis commands (ingest,cull,dedup,verdict,straighten,crop,develop,profile/look list|show) are exposed as MCP tools mirroring their CLI contracts.R-MCP-2(MUST) Pixel-producing / destructive-feeling commands are gated off the MCP surface viasetup.ExcludeFromMCP— at minimumexport,remove, andxmp write(anything that writes pixels/sidecars beside the user's files). The exclusion is build-time only (no runtime lever); toggle withgtb enable|disable mcp <command>+ rebuild, never by hand-editing the generatedcmd.go.R-MCP-3(MUST) MCP tools honour the same non-destructive (R-ND-*) and privacy (R-PRIV-*) contracts as the CLI. ```