← The Machine Room · Case File 002 — KorCRM Dogfooding · --:--:-- GST · Sheet 1 of 7
CASE FILE · KORCRM · AGENCY OPS / AI PIPELINE DWG NO. AH-CS-002

KorCRM writes my agency's Upwork proposals — three drafts, one survivor, and a gate that ships nothing until the slop score reads zero. The judge is a rival model.

The operating system I built for my own agency, in production at crm.korlane.com: a CRM and money ledger where eleven automations post atomically, and a five-stage AI pipeline where Claude writes, GPT grades, and monthly recalibration re-weights the grader from real outcomes.

GMAIL + CHROME EXTENSION → INGEST
→ ANALYZE → STRATEGIZE → GENERATE ×3
→ RIVAL JUDGE → GATE → HUMAN REVIEW

$0.15 – $1.20 PER PROPOSAL, BY TIER — RATE CARD, ESTIMATED
OUTCOMES RE-GRADE THE GRADER — MONTHLY
0
THE SLOP SCORE A PROPOSAL MUST CARRY TO SHIP —
DETECTED DETERMINISTICALLY, INSTANT, AT ZERO COST  VERIFIED
Drawn byAbu Huraira Mukhtar
RoleSole Architect & Engineer
Build window2026-02 → 2026-06 · ~4 MO
Scale1 Agency · Daily Use
ClientKorlane — my own shop
StatusIn production · crm.korlane.com
Source165,337 LOC · 295 Commits
Sheet1 of 7
SHEET 2 OF 7 · ABSTRACTGENERAL NOTES APPLY

01 · One agency runs on it — its builder's

Six views — Today, Hunt, Craft, Deliver, Money, Learn — replace the spreadsheet that used to run an Upwork agency. Mine.

Underneath the views sits a real operating system: a Postgres data spine where eleven automation rules fire on every entity change — connects debited when a proposal goes out, refunds credited on outbid, income mirrored to transactions — each rule atomic inside a single prisma.$transaction, every mutation writing an append-only audit row. Above it sits the machine this dossier is really about: a five-stage AI pipeline that turns a scraped job posting into a scored, gated, human-reviewed proposal.

One honesty note before any drawing: KorCRM was built for one agency — my own. The multi-tenant access layer exists so my team can share it safely, not as evidence of paying customers. What the daily use buys instead is something rarer on a portfolio page: every claim here survived contact with production, because I am the production.

GENERAL NOTES
1 · 22 @KORCRM/* PACKAGES — 12 PLATFORM · 9 DOMAIN · 1 ORCHESTRATION VERIFIED
2 · 194 ADRS · 274 LOGGED GOTCHAS
3 · ~1000 UNIT + 244 REAL-POSTGRES TESTS · 164 FILES
4 · NEXT.JS 16 · POSTGRES · BULLMQ · WXT EXTENSION
5 · MARK — THE FOREST-K MONOGRAM, A SHIPPED ASSET; PRINTED IN INK HERE, ONE ACCENT PER SHEET
SHEET 3 OF 7 · FIELD NOTESTHE BEFORE-STATE

02 · Eleven p.m., proposal forty

Somewhere tonight a freelancer is pasting proposal number forty into a text box, and the connects are spent whether or not anyone reads it.

The feed refreshed an hour ago. The job looks winnable, so the draft starts — from a blank page, again, because nothing recorded what the winning proposals had in common. The connects ledger lives in one spreadsheet tab, income in another, and the client who hired us in March is a row nobody updated. Most replies never come, and the ones that do arrive carry no lesson: the loop has no memory. Every evening ends the same way — money spent on attention, words spent on instinct, and nothing learned that survives until morning.

FIG. 1 shows the loop as lived. It runs; it never learns; it has no exit.

OBSERVED
CONNECTS — SPENT, REPLY OR NOT
MEMORY OF WINS — NONE
EVERY DRAFT — FROM SCRATCH
BASELINE — OWNER-OBSERVED, OWN AGENCY CLAIMED
UPWORK FEED · ALL NIGHT CONNECTS — SPENT EITHER WAY PROPOSAL №40 · 11 PM FROM SCRATCH — NO MEMORY OF WHAT WON REPLY — NONE · NO RECORD WHAT WON LAST TIME? — NO ANSWER. THE RETURN LEG CARRIES NOTHING BACK.
FIG. 1 — THE MANUAL TREADMILL, AS LIVEDSCALE — ONE EVENING : ONE PROPOSAL
SHEET 4 OF 7 · SECTION A-ACUT THROUGH THE PIPELINE, INBOX → GATE

03 · Claude writes. A rival grades.

GMAIL · POLL EXTENSION · 3 EXTRACTORS BULLMQ ANALYZE GPT-5.4 NANO STRATEGIZE CLAUDE OPUS GENERATE ×3 CLAUDE SONNET WRITER ≠ JUDGE RUBRIC ×8 CHECKS ×5 THE JUDGE — GPT-5.4 RIVAL PROVIDER, BY DESIGN SLOP 0 8 × ≥4 GATE COMPOSITE ≥3.5 REFINE — ≤2 REWRITES, RE-JUDGED, THEN HUMAN REVIEW QUEUE VIEWED REPLIED INTERVIEWED HIRED OUTCOME EVENTS RUBRIC WEIGHTS · Vn MONTHLY RECALIBRATION — WHAT GOT HIRED RE-WEIGHTS THE RUBRIC. THE GRADER GETS RE-GRADED. SNAPSHOT PER RUN — OLD SCORES STAY REPRODUCIBLE JOB POSTING → GATED PROPOSAL · $0.15–$1.20 PER RUN, METERED · EVERY SCORE LIVES ON EVALUATION_RUNS
FIG. 2 — SECTION A-A · PROPOSAL PIPELINE, 5 STAGESBULLMQ WORKERS — EVERY BEAT IS A REAL JOB
STAGE 0 · TWO COLLECTORS, ONE QUEUE

1Intake

Gmail polling catches Upwork's notification emails; a WXT Chrome extension reads the live job page through three dedicated DOM extractors. BullMQ workers dedupe and normalize both streams into one job record. The pipeline starts from data, never from a paste.

STAGES 1–2 · THINK BEFORE WRITING

2Analyze, then strategize

GPT-5.4 Nano reads the posting for the client's real problem, red flags, and a job-quality score — cheap intelligence before any expensive prose. Claude Opus then decides how to win: hook type, framing, proof point, problem-agitate-solve structure. Strategy is its own stage so a bad plan never gets beautifully written.

STAGE 3 · BEST-OF-3, TEMP 0.8

3Three drafts, one survivor

Claude Sonnet writes three candidates in parallel — same strategy, different execution. Single-shot generation is a coin flip; best-of-three means you pick a winner instead of hoping for one.

STAGE 4 · THE ANTI-SELF-ENHANCEMENT FORK

4The judge is a rival

GPT-5.4 scores every candidate on eight weighted rubric dimensions plus five deterministic checks — deliberately a different provider from the writer, because a model grading its own prose inflates it. The gate is arithmetic, not opinion: all eight dimensions ≥ 4, slop score 0, composite ≥ 3.5. Miss any one and nothing ships.

STAGE 5 · THE GATE CANNOT BE ARGUED WITH

5Refine, or confess

A failing winner gets at most two rewrites aimed at the flagged dimensions, each re-judged from scratch. Still failing, it lands in the human review queue flagged below-threshold — the machine never quietly ships its own rejects, and a human sends everything that leaves.

THE CLOSING BEAT · OUTCOMES RE-GRADE THE GRADER

6The loop finally learns

Every shipped proposal is tracked — viewed, replied, interviewed, hired. Monthly recalibration re-weights the rubric from those outcomes, and every historical evaluation keeps its own rubricWeightsSnapshot, so the past stays reproducible while the judge improves. This dashed arc is the exit FIG. 1 never had.

DETAIL B · THE SHAPE AROUND FIG. 2

A monolith with customs borders

165K lines and one database, but 22 packages with hard frontiers: nine domain packages that never import each other, cross-domain reads through ports, writes only through named orchestration flows like submitProposal.

Chose a modular monolith with lint-enforced boundaries over microservices or a free-for-all — because one person operates this in production, and borders you can lint are borders that hold. Cost: Wave 9 — roughly thirty sub-phases of rework, boundaries lint ratcheted warn → error, every mocked-Prisma test replaced with real Postgres, a legacy OrchestrationService facade deleted. Revisit when any domain needs an independent deploy cadence. VERIFIED
DETAIL C · ADR-192, THE DEFERRAL

Finish, or don't start

The automation service still carries its legacy package name. Promoting it into the @korcrm/* convention mid-wave meant touching the deploy pipeline for a naming win.

Chose to defer the promotion in writing — ADR-192, with four explicit tipping conditions, each mechanically observable, and an out-of-band recipe for whoever picks it up. Because a half-migration is the band-aid: either promote fully or keep the legacy fully. Cost: the boundaries lint stays blind to one service until a tripwire fires. VERIFIED
SHEET 5 OF 7 · REVISION HISTORYALL ROWS TRACEABLE

04 · Failures, filed as revisions

Four months of production use leaves marks. Here they are, dated and traceable — including the one a portfolio would normally hide.

REV
DATE
WHAT BROKE
THE FIX
TRACE
A
2026-03
PrototypeThe first build was Google Apps Script on Google Sheets. Five weeks in, the spreadsheet model couldn't hold the automation and analytics the agency actually needed.
Rebuild, earlyReplaced wholesale with a Postgres + Next.js web app before the sunk cost grew teeth. The Sheets phase survives only as the repo's name.
FIRST COMMITS 2026-02-27
B
2026-05
LintThe canonical plan capped files at max-lines: 20 — which flagged half of all API routes, because Next.js route files are a framework convention, not a smell. A gate that cries wolf gets ignored.
Measure the right thingSwitched to max-lines-per-function: 20. The false positives vanished; the rule caught the seventeen real violators.
WAVE 9 PLAN · ESLINT CONFIG
C
2026-05→06
ArchitectureThe growing monolith started taxing every change: tangled imports, mocked-Prisma tests that passed while production diverged, one god-facade fronting all writes.
Full-stop reworkWave 9: 22 bounded-context packages, boundaries lint warn → error, mocked-Prisma tests replaced by 244+ real-Postgres suites (zero mocked remaining), the facade deleted in favor of named flow functions.
ADR-185 → 193 · ~30 SUB-PHASES
D
2026-06-02
SecurityThe LiteLLM proxy — holding every LLM provider's API key — was published on 0.0.0.0:4000 and internet-reachable. The firewall said the box was closed; Docker's publish rules had opened it anyway, ahead of ufw.
Same day, at rootBound the proxy to 127.0.0.1, moved internal traffic onto the Docker network, and wrote the incident down. No evidence of compromise was found; the exposure was real regardless.
COMMIT DD33A2C
E
2026-06
IngestGmail history sync was anchored to a single label — mail that Upwork's changing formats or the user's own filters filed elsewhere silently never entered the pipeline.
Stop trusting labelsLabel-agnostic history sync: read the history stream itself, filter by sender and shape, not by where the mail happened to land.
FIX/GMAIL-SYNC-* · 2026-06-19

Read REV D twice; I did. A proxy whose one job is holding secrets sat reachable from the internet because two correct systems — Docker publishing a port, ufw filtering INPUT — composed into a wrong one. The fix took a day. The lesson cost more: on a solo product, you are also the security team, and the only honest response to that is the habit this table demonstrates — find it, root-cause it, bind it, write it down where clients can read it.

REV D · FILED
EXPOSURE — REAL, INTERNET-REACHABLE
COMPROMISE — NO EVIDENCE FOUND
FIX — SAME DAY · BOUND TO 127.0.0.1
CAUSE — DOCKER PUBLISH BYPASSES UFW
SHEET 6 OF 7 · APPROVAL PLATEPROVENANCE ON EVERY NUMBER

05 · The gate is public. The win-rate is not.

3 1Candidates written per run; one survives the composite ranking. Single-shot generation is a coin flip — this isn't.VERIFIED
8 × ≥4Weighted rubric dimensions, every one at 4-of-5 or nothing ships. Weights are versioned; every change is a new rubric version.VERIFIED · MECHANISM
11Automation rules on the ledger side — connects debits, refunds, income mirroring — each atomic inside a single prisma.$transaction.VERIFIED
$0.15–1.20Per proposal, budget tier to premium, from the pipeline's own cost table — estimated per run, metered in production.CLAIMED · RATE CARD
MonthlyRubric weights recalibrated from real outcome events; immutable snapshots keep every historical score reproducible.VERIFIED · MECHANISM
30–40%Self-enhancement bias reduction from cross-provider judging — an industry finding this design adopts, not a number measured here.CLAIMED · DESIGN RATIONALE
GENERAL NOTE — TWO NUMBERS THIS SHEET REFUSES TO PRINT

Customer count. One agency runs on KorCRM — mine. The multi-tenant access layer exists so my own team works safely inside it; it is not evidence of paying customers, and this page will not dress it up as traction.

Win-rate. The feedback loop exists and recalibrates monthly; outcome events are in the schema doing their job. The win-rate itself is my agency's business data, not a portfolio claim — a number I act on is not automatically a number I advertise.

SPECIMEN — ONE EVALUATION RUN, DRAWN TO THE EVALUATION_RUNS CONTRACT
EVALUATION RUN · RUBRIC V1JUDGE — GPT-5.4 · WRITER — CLAUDE SONNET
Pattern interruptW 10%4
DiagnosisW 20%5
SpecificityW 15%4
ProofW 15%4
Client focusW 10%4
Human voiceW 10%4
Hook strengthW 15%5
CTA clarityW 5%4
COMPOSITE 4.35 / 5 · SLOP 0 · 5 CHECKS PASSGATE — PASS → REVIEW QUEUE
FIELDS, WEIGHTS AND GATE LOGIC AS SHIPPED; SCORES ILLUSTRATIVE — REAL EVALUATIONS GRADE REAL CLIENT JOBS, WHICH ARE BUSINESS DATA, NOT PORTFOLIO MATERIAL.

What the plate means for anyone running a pipeline of their own: the expensive part of AI writing isn't the writing — it's knowing, mechanically, when the output is allowed to exist, and feeding what happened next back into the grader. That whole apparatus is what you saw in FIG. 2, and it runs for the price of a coffee per proposal.

Discuss a system like this →
SHEET 7 OF 7 · CLOSE-OUTPLAIN VOICE

06 · What the ledger doesn't record

The plain version, without the drafting frame: I built this because my own evenings looked like FIG. 1, and I was tired of paying for attention with no memory of what earned it.

The pipeline gets the headlines, but the part I'd defend in any interview is duller: 194 recorded decisions, 274 logged gotchas, a revision table that keeps its security incident on the page, and a rework wave where I stopped shipping features for a month to make the architecture tell the truth. A proposal machine without outcome memory is just a faster typewriter — the loop was always the product.

And the honest scope line, one more time: this is a one-agency system, run daily by the person who built it. That's a smaller claim than "SaaS with customers" and a stronger one than most demos — every screw in this drawing has been turned under load, by me, this quarter.

UNRESOLVED
@KORCRM/AUTOMATION PROMOTION — DEFERRED · 4 TRIPWIRES · ADR-192
GMAIL SYNC FIX — ON BRANCH AT DOSSIER DATE
WIN-RATE — PRIVATE, BY POLICY
DRAWN & BUILT BY
ABU HURAIRA MUKHTAR · SOLO · 2026-02 → 2026-06
A. H. M.
SHEET 7 OF 7
END OF CASE FILE 002
THE DRAWING OFFICE · AFTER HOURS

This one isn't a client story. My own shop runs on it.

KorCRM filed proposals this week and will file more tomorrow — the machine on these sheets is how my agency eats. If your business still runs on a spreadsheet and somebody's memory, bring it over. The first conversation costs you nothing.

Start a conversation → ← Back to the machine room
NEXT CASE FILE · 003 EHR Behavioral → SHEET 1 OF 7