Frame01 / 32

AI systems that are measured, governed and economical.

A working case study: curatorial.app, a multi-model, multi-agent AI platform for curators, designed, evaluated and operated end to end.

PORTFOLIO FOR

AI Strategy Consulting → AI Solutions Architecture

curatorial.app · AI systems portfolio · October 202601
Strategy02 / 32

The market is small and specific: about 55–80k curators, of whom 9–21k are realistically reachable.

Institutional curators40–50k worldwidereachable: 4–10k · $0.4–1.2M ARR¹Independent curators15–25k worldwidereachable: 4.5–10k · $0.36–0.96M ARR¹Students & emerging3–5k graduates a yearreachable: 0.6–1.5k · $36–108k ARR¹
55–80kcore curators worldwide
9–21krealistically reachable
$0.8–2.3Mannual revenue potential¹

At 10–30% penetration, before a modelled 30–50% uplift from power users who bring their own key. Built bottom-up from public data, e.g. about 12,400 full-time US museum curators (BLS, 2025).

¹ From the November 2025 market analysis, the project's starting point: first database migration 22 Oct 2025. Pricing and model assumptions have since evolved; planning estimates, not traction.
curatorial.app · AI systems portfolio · October 202602
Strategy03 / 32

Sequence the segments: start where acquisition is cheap, move upmarket on proof.

PHASE 1

Students & independent curators

Largest reachable pool, lowest acquisition cost, fastest feedback loop. Reached through content and word of mouth.

PHASE 2

Institutional curators

Higher willingness to pay, but 6–12 month procurement. Enter bottom-up: individuals adopt first, the team follows.

PHASE 3

Gallery directors & adjacent roles

Expands the addressable market once the product and the proof exist.

THREE SALES MOTIONS, MATCHED TO THE SEGMENT
Solo curator
pure product-led: self-serve sign-up, upgrade by usage
Institutional team
product-led sales: the product spots team signals, a human closes the first deals
Enterprise network
sales-led: pilots, custom terms, account management
the order is a risk decision, not a size decision
a human sales motion is added only where deal size justifies it
curatorial.app · AI systems portfolio · October 202603
Strategy04 / 32

Approach: product-led growth, with a free tier that earns the first win.

Sign up freeno cardActivateupload + first insightInvitecolleague views freeThey joinaccount to participateHit a limittokens, projectsUpgradewhen value is proven

Why product-led

With roughly fifteen thousand reachable users, paid advertising reaches the wrong people. In a niche, the product reaches curators through curators.

Why a permanent free tier

The first win must cost nothing: upload two or three sources, run one analysis. The free tier runs on a cheap open-weights model with caps, plus a few taster queries on a better one.

How it is measured

One activation milestone (two uploads and one feature used), a target of under 48 hours to reach it, and a free-to-paid target set before launch.

targets, not results: the product is pre-launch
upgrade by usage limits, not by sales pressure
curatorial.app · AI systems portfolio · October 202604
Strategy05 / 32

Structure: five tiers, each unlocking a clear job: try, work, write, collaborate, govern.

FreeProScholarTeamEnterprise
Price / month$0$19$39$79custom
Editors included1115unlimited
Read-only viewersunlimitedunlimitedunlimitedunlimitedunlimited
Commentersnoyesyesyesyes
AI model classopen-weightsstandardstandard + advancedadvancedprivate
Signature unlocktaster queries on a better modeltaster of writing modeswriting modes, style-guide upload, BYOKshared research pools, shared style guides, rolesSSO, audit logs, dedicated hosting

Public list prices. Model class follows the tier: cost per user rises only where the value does.

Free → Pro → Scholar → Team → Enterprise
storage, uploads and tokens are capped per tier to bound cost
curatorial.app · AI systems portfolio · October 202605
Strategy06 / 32

Team and multiplayer: viewers are free, editors are the product.

THE DECISION

Give viewers away. Charge for editors.

Read-only
every tier, unlimited
Comment
Pro and above
Edit
1 on individual tiers, 5 on Team

Unlimited viewers on every plan, including Free, turns each shared project into an invitation.

Why it is safe

By design, viewers read and comment; generating, uploading and indexing are editor actions. Viewers therefore cost almost nothing to host, and the expensive actions sit behind a paid seat.

Why it works

A colleague invited to review a project has to create an account to take part. They see the product on real material, which is the cheapest acquisition there is. Needing a second editor is the natural step to Team.

The trade-off

Most viewers will never pay. That is accepted: they are the growth channel, and the cost ceiling is designed into the permission model, not left to hope.

the costly actions are designed to sit with editors
the funnel to Team is built into the permission model
curatorial.app · AI systems portfolio · October 202606
Strategy07 / 32

Strategy and architecture are one design: every commercial choice has a technical enforcement.

THE MOAT IS ACCUMULATED STATE
Embedded library
indexed sources, mapped connections, extracted insights
Collaboration graph
colleagues invited, shared projects, team workspaces
Learned voice
style profile that improves with use
Portfolio
research history that lives on the platform

No fine-tuned model needed: a growing corpus of curatorial material that any model can use through retrieval.

HOW THE STRATEGY BECAME ARCHITECTURE
Free tier at near-zero cost
open-weights model tier, per-tier caps, atomic token accounting
Unlimited viewers
read-only role enforced by row-level security
Tiers with different models
one wrapper, per-tier and per-feature routing
Heavy users pay their own way
bring-your-own-key vault
Teams share research
shared pools under team-scoped security
the rest of this deck shows how each one is built, tested and operated
curatorial.app · AI systems portfolio · October 202607
Frame08 / 32

A niche, source-heavy domain, served by three cooperating systems.

Curators do research-heavy, deadline-driven writing and planning. Collections systems have no AI; general chat tools have no grounding in the curator's own sources.

  • Independent curators, museum staff, researchers
  • Research → writing → exhibition, in one workspace
  • Every answer grounded in the user's own library, with citations
curatorial-appNext.js on VercelWeb app, API, agent endpointsPostgres + pgvector (Supabase)curatorial-inferenceFastAPI · Celery · RedisExtraction, OCR, transcriptionAsync workers, retriescuratorial-opsBot fleet · Curatorial OSSupport, content, research, opsScheduled + conversational agentsagents call the app's API as their own principals
3 repos, one GitHub org
1 Postgres backbone
1 small server for inference + bots
curatorial.app · AI systems portfolio · October 202608
Frame09 / 32

What this portfolio demonstrates: position, build, evaluate, operate, govern.

01
Position

Market, segments, free tier, tiers, team model, moat.

slides 2 – 7
02
Build

RAG, multimodal, model tiers, BYOK.

slides 10 – 16
03
Evaluate

Real-route tests, baselines, honest coverage.

slides 17 – 19
04
Operate

Cost, monitoring, errors, bots, lifecycle, data.

slides 20 – 27
05
Govern

Security, domain purity, decision records.

slides 23 · 28 – 30

Slide 31 names what I would do differently. Slide 32 turns the case study into an engagement approach.

curatorial.app · AI systems portfolio · October 202609
Act 1 · Product intelligence10 / 32

20+ LLM-backed endpoints in five families, with one deliberate stance: it questions before it writes.

Research & synthesis
summaries, comparison, gap analysis, synthesis matrices, citation extraction, translation
Writing & drafting
essay drafting, critical feedback, edit suggestions, repurposing, voice and style learning
Data entry & parsing
natural-language entry that creates structured records
Media
transcription in, spoken audio overviews out
Summaries & exports
project and notebook summaries, bibliographic export
DESIGN STANCE
Augment the thinking. Don't replace it.

The writing flow is Socratic by design. It reads what the curator wrote, asks one question tied to a specific phrase, and only then helps structure and draft. The aim is to tease out and sharpen the writer's own ideas, not to generate text on their behalf.

ThinkingQuestionStructureOutlineDraft

Each question records its move type and what it was grounded in.

curatorial.app · AI systems portfolio · October 202610
Act 1 · Product intelligence11 / 32

Multimodal in and out: lectures and meetings become notes, podcasts and lectures.

Recordlecture, meetingTranscribemultilingualIndexinto a notebookAsk & synthesisegrounded in sourcesScriptmulti-voice panelSpeakaudio overview

In

Documents, images, and audio recordings. Speech-to-text was tested across English, Japanese and Arabic.

Think

Everything lands in the same retrieval layer, so questions and summaries cite recordings the same way they cite papers.

Out

Scripted panel conversations rendered to speech. Voice choice came from a blind listening test of four candidates.

All of this is user-facing today. Models as of October 2026: MAI-Transcribe-2 (speech-to-text), MAI-Voice-2.1-Flash (text-to-speech).

curatorial.app · AI systems portfolio · October 202611
Act 1 · Product intelligence12 / 32

Extraction is a service, not a step inside the app.

UploadPDF · DOCX · imageNative textparse firstOCR fallbackscanned pagesClean-uplayout-awareReturnwebhook to app
  • Documents become clean text before any language model sees them.
  • Parse the file natively first; OCR only when a page is a scan.
  • Every result reports which method produced it and an OCR confidence score.
  • Runs as a separate service: async queue, retries with exponential backoff (3 attempts).
  • Self-hosted open-source OCR, so document content never leaves our infrastructure for this step.
  • The app treats it as a replaceable dependency behind one endpoint.
FastAPI + Celery + Redis
native parse → OCR (Tesseract) fallback
method + confidence returned with every result
curatorial.app · AI systems portfolio · October 202612
Act 1 · Product intelligence13 / 32

Retrieval-augmented generation, kept inside one security boundary.

Chunksemantic splitEmbed4000-d vectorsStorepgvector · HNSWRetrievehybrid searchGroundinject sourcesCitelink to source

One store

Vectors live in the same Postgres as the data they describe. Search runs under the same row-level security, so one user's query cannot see another's library. (Moved from a separate vector database in March 2026.)

Three services

Indexing coordinator, retrieval service and embedding service: three small single-purpose modules instead of one pipeline class.

Measured

Retrieval is tested directly: 24 curatorial queries against a seeded corpus, scored on whether the right source lands in the top three.

recall@3 = 24/24 on the seeded set
Postgres + pgvector, halfvec(4000)
citations carried through to answers
curatorial.app · AI systems portfolio · October 202613
Act 1 · Product intelligence14 / 32

Embeddings chosen for fit: Qwen3-Embedding-8B, truncated to what the database can index.

Multilingual
100+ languages, including code
Long context
32K-token inputs
Instruction-aware
task prefixes steer the embedding
Open weights
Apache-2.0 licence per the published card
Truncatable
Matryoshka dimension control

Trade-off, accepted deliberately: changing the embedding model forces a full re-index and re-calibration of every similarity threshold, so it is treated as an architectural decision, not a config tweak.

native output4096stored (database index ceiling)4000Matryoshka-style training lets the vector be shortenedwith little loss, so it fits the widest vector Postgrescan index with HNSW.
Model as of October 2026: Qwen3-Embedding-8B via an API aggregator
facts from the published model card; verify at time of use
curatorial.app · AI systems portfolio · October 202614
Act 1 · Product intelligence15 / 32

Many models, one door: tiers, per-feature choices, and a fallback route.

Product features20+ endpointsunifiedLLMthe only doorOpen-weightsLlama 4 ScoutMiniGemini 2.5 FlashFullGPT-6 LunaPer-feature overridese.g. Qwen3.8 FlashOpenRouterprimary routeVercel AI Gatewayruntime fallbackBYOKuser's own key, direct

Every call passes through one wrapper: usage, cost, user and feature are attributed automatically.

Model tier follows subscription tier; individual features can override with a better-suited model.

Swapping a model is a configuration change, tested before release.

Models as of October 2026; they change quickly, the architecture doesn't
provider abstraction predates today's model set
curatorial.app · AI systems portfolio · October 202615
Act 1 · Product intelligence16 / 32

Bring your own key: cost moves to the customer, prompts stay with their provider.

SYSTEM KEY (default)RequestPlatform keytiers + routingPlatform paysBYOKRequestKey vaultdecrypt per requestUser's providerdirect
  • Cost transfer: heavy users bring their own key and pay their provider directly.
  • Enterprise privacy: prompts go straight to the customer's provider under their own agreement.
  • Security: keys sit in a database vault, are never written to process environment, and are injected per request.
  • Scope today: text generation. Embeddings always use the platform's model so the index stays consistent.
vaulted keys, per-request injection
BYOK bypasses both platform routes
embeddings: platform model only
curatorial.app · AI systems portfolio · October 202616
Act 2 · Evidence it works17 / 32

Evaluation against the real application, with a judge that can't grade its own homework.

Fixture dataseeded usersReal routeauth → tools → DBAssertionsright tool? right count?LLM judgepinned, separate modelBaselinemedian − 0.1StoredPostgres

Test the real thing

Evals call the application's actual routes with real authentication and real tools, not a bare model, so tool selection and permissions are part of the test.

Judge independence

The grading model is pinned and never the model under comparison. The request-visible model override is gated by a shared secret.

Regression, not perfection

A baseline is the median of repeated live runs; a change is flagged when the score drops by more than a tenth.

7 evaluation suites
39 test definitions
results stored in Postgres for comparison
curatorial.app · AI systems portfolio · October 202617
Act 2 · Evidence it works18 / 32

What evaluation caught: four stories where the test paid for itself.

Diagnose before blaming

Every count test failed with 'permission denied'. Instead of patching permissions, I isolated the permission layer, proved it correct, and found a paid-tier feature gate the test user had never been given.

A real bug, found by a score

A notebook generator scored 0–10 out of 100 on groundedness. Retrieval was reading an unpopulated column. After the fix: 0.96. Likely affected every earlier output, not just the test.

Wrong exception, silent masking

One route returned a generic error on recoverable model output because it caught the SDK's exception type, not the wrapper's. Found by the harness, fixed, same class checked elsewhere.

Refusing to baseline a failure

A style-extraction route failed its schema on 6 of 6 live calls. It is excluded from baselines and labelled, rather than quietly recording failure as 'normal'.

count questions: 3/3 correct answers, 3/3 correct tool
retrieval: recall@3 24/24
run live, October 2026
curatorial.app · AI systems portfolio · October 202618
Act 2 · Evidence it works19 / 32

Coverage, stated honestly: wide definitions, narrower baselines, small samples.

Test definitions39Routes with a baseline8Stored baseline runs3

Breadth is ahead of depth, and the slide says so.

  • Tested: most LLM endpoints now have at least one eval definition, including chat, drafting, research, extraction and notebooks.
  • Baselined: a smaller set has repeated live runs and a regression threshold.
  • Recalibrated openly: latency budgets set for an older model were re-measured and revised after the model changed. Quality rubrics were not loosened.
  • Not yet: human-rated review, larger samples, harder near-duplicate retrieval cases.
tested ≠ baselined ≠ recently run
samples are small by design (real API cost)
curatorial.app · AI systems portfolio · October 202619
Act 3 · Operate it20 / 32

Cost is designed in: priced once, deducted atomically, attributed to a feature.

Requestuser + featureGate & reservetier, quota, tokensModel callvia one wrapperPrice onceone price tableDeduct atomicallyrow-lockedLedgerwho, what, which model

One price, two consumers

A single function prices every call and feeds both billing and telemetry, so reconciliation is a "did we miss a call site?" check, never "do two calculations agree?".

Guard the edges

Zero-token responses are flagged instead of silently billed as free. Unknown models fall back to a default rate and raise a visible warning.

Attribution by design

Each call carries a canonical feature name, so cost per capability, not just per provider, is answerable. Model choice is a cost/quality decision with a test behind it.

billing by token count; cost figures are for monitoring
overdraw race: decided, deferred, then fixed with row locking (slide 30)
curatorial.app · AI systems portfolio · October 202620
Act 3 · Operate it21 / 32

Monitoring that survives serverless and a small server.

App (serverless)Inference serviceBot fleetOTel collectortraces · metrics · logsGrafana Clouddashboards + alert rulesBugsinkself-hosted errorsFleet watchdogrestarts degraded bots

One collector receives telemetry from every component and fans it out; errors go to a self-hosted, Sentry-compatible tracker.

Serverless handlers flush telemetry before returning, or traces are lost when the function freezes.

Dashboards and alert rules are kept as code; alert routing is the part still being finished (slide 31).

OpenTelemetry end to end
7 alert rules defined as code
bots watched by a health-checking watchdog
curatorial.app · AI systems portfolio · October 202621
Act 3 · Operate it22 / 32

Errors are typed inside and masked outside.

// what the client ever sees
{ "code": "QUOTA_EXCEEDED",
  "message": "Monthly limit reached" }

// what stays server-side, in logs only
stack trace · SQL · provider response · user data

Masking is a design rule, not a habit: it is checked by a lint rule and a pre-push gate.

Every route runs the same wrapper
Authenticate
Authorise
Check quota
Run handler
Serialise error
  • A small typed error hierarchy (invariant, permission, quota, balance, auth, not found) maps to stable codes.
  • Model failures are their own typed case: unparsable structured output is handled explicitly, not leaked as a 500.
  • Wrong-exception bugs of this kind were found by the evals (slide 18).
clients get {code, message} only
6 domain error types
enforced by lint + gate
curatorial.app · AI systems portfolio · October 202622
Act 3 · Operate it23 / 32

Security as architecture: four principles, each enforced by something.

Row-level security first

Authorisation is enforced by the database itself. Application code is the second line of defence, not the first.

219 / 219 tables with RLS
602 policies

Service role is a scalpel

The bypass-everything database role is reserved for trusted internal paths with an explicit user identity, and is lint-guarded from client code.

one accessor, one rule

Keys in a vault

Customer API keys are encrypted in a database vault and decrypted per request. They are never environment variables and never logged.

vault + per-request injection

Say little on failure

Clients receive an error code, never an internal message. Privileged database functions have explicit grants reviewed before use.

masked errors by default
principles, not an audit report
an ongoing authorisation audit finds and closes gaps
curatorial.app · AI systems portfolio · October 202623
Act 4 · Ecosystem24 / 32

Agents and bots connect through one governed door.

Web UIBots (own identity)AI clients (MCP)Route wrapperauth · permission · quota · maskingApp API406 route filesAgent endpoints30 routesMCP server6 tools

Bots authenticate as their own principals with signed tokens, not by impersonating a user.

Secrets are validated at boot: a bad configuration fails fast, not on the first live request.

The customer-service bot answers from a knowledge base, and an eval checks it does not invent features.

bot identity records survive soft-delete rebinding
MCP lets external AI clients use the library
curatorial.app · AI systems portfolio · October 202624
Act 4 · Ecosystem25 / 32

The Curatorial OS: a fleet of narrow agents, each with one job.

Curabot
customer service, knowledge base
Contentbot
content drafting + analytics
Userbot
research assistant for users
Codebot
PR review + digests
Ceobot
health synthesis, admin reports
Pabot
personal assistant
Radarbot
scheduled radar triggers
Researchbot
daily digest to Telegram
Lifecyclebot
lifecycle email runner
Watchdog
restarts degraded bots
Infra monitor
host + plan usage
One small server
containers and scheduled timers, built elsewhere and pulled on deploy
15 packages in the monorepo
13 containers + scheduled timers, live
each bot has one job and its own identity
curatorial.app · AI systems portfolio · October 202625
Act 4 · Ecosystem26 / 32

Customer lifecycle as a closed loop, with the model kept out of the send path.

Sign-upOnboardingIn-product eventsSegmentsNurtureUpgrade

Lifecycle messaging is deliberately deterministic: a queue and a fire-and-exit runner, no model in the send path.

The same business data supports product-led growth: onboarding, behavioural segments, upgrade prompts.

Pipeline built and exercised with synthetic customers; no customer data appears in this deck.

email through a transactional provider
synthetic data used to build + test the loop
curatorial.app · AI systems portfolio · October 202626
Act 4 · Ecosystem27 / 32

Postgres as the single backbone: users, company data, vectors and keys.

Postgresone backboneCustomer datarow-level securityCompany databusiness schema · syntheticVectorspgvector · halfvec(4000)Key vaultencrypted API keysMigrations800+ versioned files

One database means one security model, one backup story and one place to reason about data. It also means no staging copy today: a pre-launch trade-off named on slide 31.

219 tables, all with RLS
events → projection pattern for the company schema
4,799 synthetic customers · 4,751 synthetic events
curatorial.app · AI systems portfolio · October 202627
Act 5 · Principles28 / 32

Domain-driven design: the rules live in one pure layer.

Route wrapperauth · permission · quota · maskingRepositoriestranslate domain ↔ databasePure domainrules only: no framework, no I/O
// one place for role rules
RoleChangeValidator.canPromote(actor, target, team) {
  actor.role === 'owner'  &&  actor.status === 'active'
  && target.role !== 'owner' && sameTeam(target, team)
}
  • Business rules live in one tested place; five routes can't drift apart.
  • The domain layer imports no web, database or UI framework. A hook blocks it if anyone tries.
  • A review once found a missing "owner must be active" rule here; the fix touched one file.
19 bounded contexts
134 domain source files · 123 test files
0 framework imports in the domain
curatorial.app · AI systems portfolio · October 202628
Act 5 · Principles29 / 32

AI-assisted delivery with guardrails: LLMs write the code, rules decide what ships.

DISCLOSURE

Every line of code was written by LLMs, under my oversight, planning and guidance. I review, specify and decide.

That makes the guardrails the real engineering product: they are what turn fast generation into dependable software.

Plan
a spec and a test regime before code
Hooks
41 automatic checks while editing: purity, security, migrations
Skills
120 reusable procedures that load by context
Tests
2,277 test files, tiered by risk
Pre-push gate
full checks must pass before code leaves the machine
enforcement beats documentation
hosted CI workflows exist, paused for billing; local gates are the live control
curatorial.app · AI systems portfolio · October 202629
Act 5 · Principles30 / 32

Decision records: what, what else, and what it costs.

DECISION 1

Consolidate the inference host

What: Inference, queue and bots moved from several vendors to one small server.

Alternative: Stay split across a managed app platform, a hosted queue and the bot host.

Trade-off: One pipeline and lower fixed cost, in exchange for a single point of failure.

DECISION 2

Compare-and-set for terminal writes

What: A stalled worker can wake after recovery and overwrite it. Writes now check the claim timestamp.

Alternative: An id-only guard cannot tell the stale write from the legitimate one.

Trade-off: Slightly more code per state machine; recovery can no longer be silently undone.

DECISION 3

Token overdraw: defer, then fix

What: Documented as a known limitation in May with options laid out; replaced by row-locked atomic deduction in August.

Alternative: Serialisable transactions, ordered queues.

Trade-off: Accepted a small risk first to avoid lock contention; paid it down once reviewed.

records live in the repo next to the code
the third shows a decision revisited on schedule
curatorial.app · AI systems portfolio · October 202630
Act 6 · Close31 / 32

Honest limits: what I'd change and what is still open.

WHAT I WOULD DO DIFFERENTLY
  • Secrets manager from day one, not adopted later
  • Decide the test regime before any code
  • Domain-driven design from the first commit
  • Mermaid diagrams from the start: low-context coding agents (this began a year ago, with far less capable models) needed a standing map of services, repos, VPN and deployment
WHAT'S NEXT, STATED PLAINLY
  • Eval depth: bigger samples, human-rated review, harder retrieval cases
  • One environment today: a staging database is the next safeguard before launch
  • Single small server: fine for cost, a known single point of failure
  • Alert routing to finish; CI paused for billing, local gates carry the load
  • Entity resolution across sources in the knowledge graph
  • Machine-checked, not yet human-read, translations
  • Authorisation audits continue: gaps found, closed, central policy being tightened
curatorial.app · AI systems portfolio · October 202631
Act 6 · Close32 / 32

From case study to engagement: the same method, applied to your organisation.

01

Diagnose

Inventory AI use cases, call sites, data flows and cost drivers.

map + risk register
02

Decide

Model choice as a decision: criteria, test, decision, review date. Build, buy or configure.

decision records
03

Prove

Small eval set per use case and baselines before rollout; human-in-the-loop designed in.

eval harness + baseline
04

Operate

Cost attribution, error and abuse guardrails, review cadence.

run-book + dashboards
indicative shape; scope and timing agreed per client
each step mirrors work done on this project
curatorial.app · AI systems portfolio · October 202632