DANYLO PRAVDA
0%
ALL WORK

AUTOMATION — PRODUCTION —

Imprimatur

Creative review and approval that doesn't die in email

by

5 min read

Read this with AI

AT A GLANCE

AT A GLANCEIMPRIMATUR
STATUS
PRODUCTION
TIMELINE
2026-07 — 2026-07 · 25 DAYS
LANGUAGES
TypeScript / Python / SQL
CATEGORY
AUTOMATION
0

LIVE PRE-CHECK RUNS

OUTCOME

Deployed and running on real files since July 2026.

METRICS

0

M.01LIVE PRE-CHECK RUNS

Real verdicts computed on real file bytes by the worker, not replays. Test and demo dominated, because there are no clients yet.

0

M.02RUNS THAT CAUGHT SOMETHING

Verdicts where at least one check did not pass, meaning issues that would otherwise have reached a reviewer. The rate is a fact about our fixtures, several of which carry planted violations.

0

M.03EVENTS ON THE APPEND-ONLY SPINE

Across 446 deliverables. Nothing in this table can be updated or deleted. A database trigger raises, and the application's role was never granted the permission.

0-5s

M.04PRE-CHECK SPEED

Per deliverable, end to end, all deterministic tiers. About four minutes for a 48-asset campaign on one worker.

0

M.05TESTS

628 TypeScript (Vitest) plus 149 Python (pytest), and 24 Playwright specs that drive a real browser against the real deployment.

CH.01

The problem is the inbox, not the design

A studio sends a banner to a client. The client replies with a screenshot and a paragraph. Someone exports a new version, attaches it, and the thread forks. Three replies later nobody can say which file was approved, or when, or by whom. If the client later says "I never approved that," there is nothing to point at.

The measurable cost is in the round-trips. Eight days to approval on average, more than three versions to sign-off, five or more hours a month spent chasing feedback (sources in the repo's analysis document). The unmeasured cost is worse, and it is the embarrassing misses. A banner goes out at the wrong size. A typo sits on the artwork. A colour is nearly, but not quite, the client's brand red. Those reach the client, and each one spends a little of the studio's credibility.

CH.02

What I built

Imprimatur is three things that share one record.

A pre-check that reads the actual bytes. Before the client sees anything, the work is checked two ways. Deterministic tiers run first: dimensions and format via Pillow, spelling on the artwork via OCR (it names the misspelled word and marks where it sits), brand colour as a perceptual CIE-Lab distance, and the client's logo via feature matching. Then a judgment tier, Claude Opus with vision, reading the real image against the studio's brief. Each check posts its own honest verdict, and each one can skip and say so rather than guess.

A review link with no account. The client opens one URL. They point, box, highlight, arrow, or draw freehand directly on the work. They thread comments on any mark, and they can attach a picture of what they mean. Then they approve, or ask for changes. Across drafts the link always shows the newest one, older drafts stay browsable and read-only with their decisions intact, and an onion-skin overlay blends the previous draft under the current so the client can see exactly what moved.

A record nobody can rewrite. Every version, mark, comment and decision is an event in one append-only stream. That is enforced in the database. A trigger raises on UPDATE and DELETE, and the application connects as a role that was never granted those permissions. The audit trail, the cycle-time metric and the replayable demo are all projections over that single stream.

CH.03

How it works, and the decisions worth defending

The event spine is append-only in the database, not by convention. An audit trail whose integrity depends on application code being careful is not an audit trail. A trigger raises on any UPDATE, DELETE or TRUNCATE, and the app's Postgres role holds only SELECT and INSERT on that table. Two independent mechanisms, either of which alone would do the job. A correction is a new event, always.

Heavy compute pulls, and never receives. The pre-check worker runs on a self-hosted machine and polls the queue over HTTPS with a bearer token. It has no inbound port, no database credential and no storage credential. It leases a job, downloads the file over a signed URL, computes, and posts the verdict back. The blast radius of that machine being compromised is one bearer token.

The client gets a capability, not an account. Asking a reviewing client to create a password is where review tools lose the person whose approval you actually need. The link is the credential: minted, hashed at rest, expiring, revocable. This one decision shapes the whole review surface, including the fact that it carries no sign-in button anywhere, because a reviewer who was told they need no account should not be shown one.

Every check can honestly say it does not know. A skipped check is a first-class outcome, and the reason a check skipped is masked on the client's screen unless the worker explicitly marked it readable. Most skip reasons carry internal detail that has no business on a studio's client-facing page. Telling a studio their logo is missing because our download failed would be a fabricated verdict about their artwork.

CH.04

What it cost to be honest

The rule that shaped this codebase more than any other: a number on a screen must trace to a logged event. That sounds cheap and is not. It means the landing page's counters return a dash rather than a zero when the database cannot be read, because an unreachable record is an absence rather than a measurement of nothing. It means a check that samples or caps its work has to say so on every outcome, including the passing one, or a clean bill is fabricated. It means a demonstration surface that offers our own sample files has to be able to show a real catch on real bytes, or it is showing the machine running rather than the machine working.

Nine such bugs were caught in one feature alone, most of them the same shape: the screen saying a thing happened when it had not. A refused upload still posted its comment. A failure notice outlived the retry that succeeded. A file picker went on naming a file it no longer held. None of them crashed anything. All of them told a small lie to somebody's client.

CH.05

Results

Every commit passes a gate chain that includes an AI critic reading the diff, and it caught six of those nine. 628 TypeScript tests and 149 Python tests cover the units, and 24 Playwright specs drive a real browser against the real deployment: an actual reviewer drawing with every tool, attaching an actual PNG, and a hostile fixture (a script wearing a .png name) being refused by a magic-byte sniff.

The whole product was then run cold, from zero. A fresh identity that had never seen the app created an account, hit the invite wall, asked for access through the product's own door, was granted it from the admin side, redeemed the code, and ran a complete project: drop, goal, confirm, live pre-check, client marks, request-changes, a second draft, approval, the record. Twenty-six screenshots and a written verdict per step. That run found three problems, which are recorded in it rather than smoothed away. Two are fixed, and the third turned out to be a fact about browser automation rather than about the product.

CH.06

Where it is honest about its limits

There are no clients. Every number above is our own test, demo and end-to-end traffic against a shared production database, and the documentation says so wherever those numbers appear. The two-week genuine-usage measurement has not been run, because it cannot be simulated.

The logo check has two measured ceilings that are reported rather than tuned away. A white mark on a busy dark texture is not found, and the reliable scale band is about half-size to triple-size, not the wider band originally assumed. The public copy states the measured band, because a specification's assumption is not a measurement.

STACK

LANGTypeScriptPythonSQL
FXNext.js 16 (App Router)React 19Drizzle ORMPlaywrightVitestpytest
INFRAVercelNeon PostgresVercel BlobClerkA pull worker on a self-hosted Windows machine
AIClaude Opus (vision) for the AI review tierClaude Opus for intake proposalsRapidOCROpenCV (ORB + RANSAC) for logo detectionPillow and CIE-Lab delta-E for deterministic checks

SKILLS DEMONSTRATED

Append-only event sourcing with database-enforced immutability · Least-privilege database roles as defence in depth · Capability-link access without accounts · A pull-based worker over HTTPS, so heavy compute holds no credentials · Multi-tier verification, deterministic checks first and a vision model last · Honest degradation, where every tier can skip and says so · Trust-boundary design for anonymous uploads · Real-browser end-to-end testing against production