Skip to content
Class 94 · Advanced · Free, no signup

Let the model write Liquid.
Never let it publish.

A deterministic linter decides what reaches the theme. No model sits in its decision path.

Everyone building with a model right now is building the generator. This class is about the other half — the part that reads what the model produced and refuses most of it. It is taken from a working headless orchestrator that writes Shopify copy and Liquid for a live store, whose own documentation opens with the sentence “the linter is the asset; the generator is replaceable.” Eleven control sets. Thirty-seven checks before one line ships. Four file paths it will not write under any circumstances. And a register, written by its author against himself, of seventeen rules with no gate behind them at all — one of which came true on this very site, one week after it was written, twice.

275 controls, eleven sets 37 checks before publish 17 of 33 rules with no gate 11 refusals one prompt never fixed 4 permanent refusals ~80 min read

October 2026 note. For current agent practice, class 100 runs Claude Code headless, class 102 writes the test before the prompt, and class 103 reads its permission rules. Published Sep 22, 2026. Nothing on this page was re-run for this note.

Quick answer

Put the model on the generating side of a wall and put deterministic code on the deciding side. A language model proposes copy and Liquid; a linter with no model in it evaluates every proposal against rules sorted by what a violation costs, and only output that survives reaches a theme. Sort the rules into three tiers — a claim that is false, a claim that is true but misattributed, and a claim that is merely clumsy — and give only the third tier a warning. Gate everything in the first two. Then prove each check works by breaking the code it guards and confirming it goes red, because a check that has never failed is a check you have never tested.

Seven things to take

  1. The linter is the asset. Swap the model whenever you like; the rules are what you actually own.
  2. Sort rules by cost of violation, not by difficulty. Ungated where a mistake is ugly; gated where a mistake is a lie.
  3. Implement rules as classes, not tokens. A rule about any certification cannot be a search for one certification's name.
  4. No control is trusted until you have made it go red while its neighbours stay green.
  5. An instrument is not a gate. A number that drifts with corpus size is a regression test, not a bar.
  6. Constrain the input, not the prompt. A list of prohibited outputs does not shrink the output space; removing the thing being copied does.
  7. A rule you wrote down with no checker behind it is a prediction of a future defect. Ask the author of this class how he knows.
Module 00 · 9 min

Generation is the easy half

Here is the architecture, and the interesting thing about it is how little of it is the model.

A local inference server runs a code-specialised model for Liquid and a small embedding model for retrieval. Product records are indexed into a vector store so that when the system writes about an item, the specification comes from the record rather than from the model's memory of what that kind of item usually is. That is the entire generation stack, it runs on one machine, and the system's own documentation describes it as the secondary concern.

The thesis, in the system's own words

“[The system] treats generation as a secondary concern to verification. While probabilistic models synthesize text and Liquid code, deterministic code governs publication.”

Read that twice, because it inverts the way nearly everyone is currently building. The usual shape is: get a better model, write a better prompt, add a retry, maybe have a second model review the first one. Every one of those moves spends effort on the probabilistic half. This system spends its effort on the half that can be proven, and treats the model the way you would treat a contractor — useful, replaceable, and not allowed near the keys.

THE MODEL GENERATES. THE LINTER DECIDES.no model sits in the decision path - every refusal below is deterministic codebriefdeterministiceligibilitydeterministicmodelprobabilisticlinterdeterministictieringdeterministicstagingdeterministichumandeterministiclivedeterministicone box is probabilistic. seven are not.FOUR REFUSALS THAT CANNOT BE OVERRIDDENtheme layout filesrefused at the path check, before anything is readcopy for an ineligible productrefused BEFORE a prompt exists - the brief throwsa claim the business never heldtier 1, hard reject, aborts the runa certification mark as an imagetier 3, escalated to a human, never automatedA gate a script can choose not to call is not a gate.
One probabilistic stage, seven deterministic ones. The model appears once, early, and everything downstream of it is code that returns the same answer every time it runs. Four destinations are refused permanently — no flag, no override, no “force” argument. Those are covered in module 06.

Why this is worth copying even at small scale

You do not need eleven control sets to get the benefit. The benefit arrives the moment the decision to publish stops being made by the same kind of system that wrote the thing. Two components, one wall:

  • Generator. Any model, any prompt, any vendor. It has one job and no authority.
  • Gate. Ordinary code — regular expressions, parsers, lookups against a record. It has authority and no creativity.

The wall is the product. Once it exists, a model upgrade is a Tuesday afternoon rather than a re-audit, because nothing about your safety depends on which model produced the text.

Measured Scale of the system these lessons come from
Measured
98,118 lines of source; 23 documentation files; 31 work-order directories; 25 commits across a three-day window.
Method
Line count over tracked source files in the repository, taken 2026-09-22.
Excludes
node_modules, .git, build output, lock files and binaries. A count without its exclusions is not a number.
Not verified
Whether the service is running today, and whether its control suite is green as you read this. Both are bound to a machine this page cannot reach.
Module 01 · 12 min

Sort your rules by what a violation costs

The gate is thirty-seven deterministic checks across seven surfaces where a claim can appear. The design decision worth stealing is not the number. It is that the rules are tiered by consequence, and only the cheapest tier is allowed to pass with a warning.

Tier 1
Hard reject — the claim is false A standard the business has never held, named as though it holds it. There is no severity dial here and no warning mode. The output is discarded and the batch item fails. A false certification claim is not a style problem; it is the kind of sentence that ends up in front of a regulator.
Tier 2
Attribution and grade — the claim is true of the wrong thing A standard genuinely held by a supplier, attached to the product. A material claim the item's composition does not support. Every word is individually true and the sentence as a whole is not. This tier catches more real defects than tier 1 ever does, because nobody writes an outright falsehood on purpose — they write a true thing in the wrong place.
Tier 3
Marks and escalation — a human decides A certification logo, a trademarked mark, anything whose correct use depends on a licence agreement rather than a fact. The gate does not try to adjudicate. It stops and routes to a person, which is the honest response to a rule that code cannot decide.

Notice what tiering buys you: it makes “warn” a legitimate outcome again. In an untiered checker every rule eventually gets downgraded to a warning, because one over-strict rule blocking a good batch at midnight is all it takes. Then the warnings pile up and nobody reads them. Tiering stops that slide by deciding, in advance and in writing, which rules are allowed to be advisory.

The rule this class will not break

Certifications are used here as a worked example of tiering and nothing else. No certificate name, scheme or number appears on this page, and none of the tier descriptions above should be read as a claim about any business. Use your own tier 1 list. Write it down before you write the linter.

Try the tiering yourself

Below is the same decision the gate makes, with the certificate names replaced by placeholders. Each candidate sentence runs through the tiers in order and stops at the first one that catches it. Work out where each stops before you reveal it — the interesting ones are the sentences that are entirely true.

Interactive · Gate Runnercandidate 1 of 5

Where does each sentence stop?

"Made with [STANDARD-A] certified organic cotton." — the business does not hold [STANDARD-A].

Your answers stay in this browser. Nothing is sent anywhere. Reset clears them.

The case that publishes is the point of the exercise. A gate that rejected it would be a gate that suppresses true, carefully-phrased sentences — and the system's own specification says so in as many words: “do not over-block. A linter that rejects all certification language suppresses true statements.” Over-blocking feels safe and is not free. It trains everyone around it to turn the gate off.

Module 02 · 11 min

A rule is a class of things. A token is one thing.

This is the defect that recurs more than any other, in every linter anyone has ever written, and it survives review because the broken version works on the example you tested it with.

Three instances from the handover, each shipped, each found later:

The rule as writtenThe rule as implementedWhat got through, or didn't
No certification may be claimed without a gate. A search for one certification's name. Every other certification in the language. The check passes and means nothing.
No consumer of this value may exist outside the allowed list. A search for the word render. Inclusion, assignment, capture, echo — every other way a template reads a value.
These two words must not be used together as a phrase. Proximity: both words within n characters. Nothing got through. 518 defects were reported that were not there, because proximity is not grammar.

The third row is the one people underestimate. An over-fitted rule is quietly useless; a mis-modelled rule is loudly useless, and 518 false positives in one run will get the whole linter switched off by lunchtime. Both failures come from the same mistake — implementing the nearest thing you can express instead of the thing the rule says.

The tie-breaker that keeps this honest

From the specification

“Where a catch and a true sentence conflicted, the true sentence won.”

You need a stated policy for what happens when a rule and a legitimate sentence disagree, and you need it written down before the argument, because in the moment the person who wants to ship will win it. Deciding in advance that the true sentence wins means every conflict becomes a bug report against the rule instead of a negotiation about the content.

Classify these six checks

Each one is a real check from a real linter, described by what it does rather than what it was meant to do. Decide whether the implementation actually expresses the rule.

Interactive · Decidercase 1 of 6

Does the implementation express the rule?

The rule bans claiming any certification the business does not hold. The check searches for the literal name of the one certification that caused the incident.

Your answers stay in this browser. Nothing is sent anywhere. Reset clears them.

How to find these in your own suite

Read every check and write, in one sentence, the set of things it matches. Then read the rule it implements and write the set the rule names. If the two sentences are not the same sentence, you have found one. This is tedious and it is the highest-yield hour you will spend on a linter, because a rule and its implementation drift apart silently and no test catches it — the test was written from the implementation.

Module 03 · 12 min

No control is trusted until it has been made to go red

Two hundred and seventy-five controls across eleven sets, one command to run them, and an instruction in the handover that everything else depends on: if it is not green, nothing below is trustworthy. That part is ordinary. This part is not:

The discipline

“No control is trusted until the source has been mutated to make it go red while its guard-pair stays green — a step that twice this month caught a control passing with the rule it tested switched off.”

Twice. In one month. In a suite built by someone who takes this more seriously than almost anyone, and who wrote the register of his own ungated rules by hand. A control that passes with its subject disabled is not a weak control — it is a zero control that reports one, and it is indistinguishable from a real one until you break something on purpose.

The mechanics are small. For each control: comment out the rule it exercises, run the suite, and assert that this control fails and its neighbours do not. If it stays green, it was never testing what its name says. If the neighbours also go red, your controls are entangled and a single rule change will produce a wall of noise you will learn to ignore.

The because field

Every control in the suite carries the verbatim input that made it fail, stored next to the assertion. Not a description of the failing input — the input. This is a small habit with a large payoff:

  • A failure message that contains the exact offending text costs nobody a debugging session.
  • The control is self-documenting, so a year later you can tell what it was for without reading the rule.
  • Most importantly, you cannot write the field without having actually seen the control fail. The format enforces the discipline above by making the green-only path impossible to complete.

That last point is the design lesson. When a discipline depends on somebody remembering to do a step, make the artifact impossible to finish without it.

Find the control that is lying

Three rounds. Each shows a set of controls after a mutation run — the rule under test has been switched off, so exactly one control in each round should have gone red and one is reporting something it cannot know.

Interactive · Huntround 1 of 3

Which control is not measuring what it claims?

Round 1 — the certification rule is switched offThe cached-fixture control
Which control should have failed and did not?

Your answers stay in this browser. Nothing is sent anywhere. Reset clears them.

Quoted Where the mutation discipline is stated
Quoted
The passage in the red panel above is verbatim from the system's own handover document, written by its author about his own suite.
Method
Read directly from the repository's documentation on 2026-09-22 and transcribed without alteration.
Excludes
Any claim about how many of the 275 controls have been through the mutation step. The document states the discipline and reports two catches; it does not report coverage, and neither will this page.
Not verified
Whether the suite is green today. It is bound to a machine this page cannot reach.
Module 04 · 10 min

An instrument is not a gate

The system has a similarity metric. It measures how much language a batch of generated descriptions shares — overlapping eight-word sequences across the corpus — and it was being read as a quality bar: below this number, good; above it, repetitive.

Then somebody checked what the number does when nothing changes except how many documents you feed it.

same generator, 40 docs13.7%shared language
same generator, 318 docs31.7%shared language
generator changenoneprompt, model and inputs identical

The metric more than doubled because the corpus grew. That is not a defect in the metric — more documents means more chances for any given phrase to recur, and the measurement is doing exactly what it says. The defect was in what it was being used for. A number that moves with the size of the thing you measure cannot be a threshold, because the threshold is then secretly a statement about corpus size.

The rule that came out of it

“A collapse score is not comparable across corpus sizes. Say the N with the number, always.”

What it was changed into

The fix is a good demonstration of the difference between the two kinds of measurement, and it split one number into two jobs:

  • As an instrument: a generator regression test, run at a fixed reference corpus size. Because N is pinned, a movement in the score is now a movement in the generator, which is the only thing the number was ever able to tell you.
  • As a gate: scored per document against a pinned reference set, with a stated bar. A document over the bar is dropped; the batch is not refused. One repetitive description should not cost you thirty good ones.

Both halves are now honest, and neither could have been made honest while they were the same number. If you have a metric on a dashboard that people treat as a pass mark, ask what it does when your input volume doubles. If the answer is “it goes up”, you do not have a pass mark. You have a thermometer somebody has drawn a line on.

The general form

An instrument tells you something changed. A gate decides whether something ships. An instrument may be noisy, relative and trend-only. A gate must be absolute, reproducible and defensible at three in the morning to somebody who disagrees with it. Dashboards are full of instruments being asked to do a gate's job, and it is always discovered the same way — the number moves and nobody can say what it means.

Module 05 · 14 min

The brief is the lever. The prompt is not.

This is the module that will change how you work tomorrow, and it is backed by measurements rather than opinion.

The system had a repetition problem: generated descriptions were converging on the same phrasing. The obvious response is prompt engineering — tell the model to vary its language, give it a list of phrases to avoid, add a style instruction. Here is what was actually tried, and what each move was worth.

Interactive · Ledgerclaim 1 of 4

What moved the number, and what did not

Removing the existing copy from the brief

Your answers stay in this browser. Nothing is sent anywhere. Reset clears them.

The principle, stated generally

A list of prohibited outputs does not constrain a generator's output space. Only a constraint on the input does. Telling a model not to produce something is a preference expressed inside the generated text. Removing the source material, or changing what the task is about, changes the distribution it samples from.

The third row is the expensive lesson. Hundreds of individual corrections, carefully applied to live records, moved the measurement by three-tenths of a percentage point. All that effort went into the output, where the leverage is not.

Where prompt guidance does work — and where it provably does not

This is not an argument that prompts do nothing. The system's own register is more precise than that, and the precision is the useful part:

From the register of ungated rules

“Prompt guidance moves a model where it has no prior and does not move it where it has a strong one. Two defects were fixed first try by adding guidance; a third survived 11 attempts across a skill section and a reject list. So for these rules the only defence is the mechanism we have proof sometimes fails.”

GATE THE PRIORS, NOT THE GAPS.three generator defects, each refused by the gate, each then addressed by editing the promptTHE DEFECTWHAT THE PROMPT SAID BEFOREOUTCOMEa deprecated image filternothing at all about imagesfixed FIRST TRYan out-of-range settingnothingfixed FIRST TRYtext field inside a paragraph taga new prompt section, then a reject list11 REFUSALS, NEVER FIXEDWhere the model had no guidance it adopted new guidance immediately.Where it has a strong training prior it did not budge.A defect the whole internet produces is a gate rule. Saying it louder will not move it.
Three defects, same technique, three outcomes. Where the model had no strong habit, one instruction fixed it permanently. Where the training distribution had a firm opinion about how that kind of sentence goes, eleven differently-worded refusals across two mechanisms did not move it. The variable is not prompt quality. It is whether you are arguing with a prior.

The practical test costs you one run: write the guidance, run it, and if the defect is still there, stop rewording and write a check. Two attempts is information. Eleven is a decision you have already made and are declining to notice.

The register: seventeen rules with no gate at all

The most unusual document in this repository is a list its author wrote against himself. Thirty-three routed rules; seventeen of them have no deterministic check behind them and are defended only by instructions in a prompt. It names each one, and it says plainly what that defence is worth.

And then it draws the line in exactly the right place:

The scoping sentence

“None of these is a claim rule. Every certification and brand-fact rule is gate-backed by the linter, which is the one place this project cannot afford a gap. What is exposed here is craft.”

Ungated where a mistake is ugly. Gated where a mistake is a lie. That is the same tiering from module 01, applied one level up — to the question of which rules get a checker at all. You will never gate everything. Deciding out loud which gaps you are accepting, and writing the list down, is what separates a known risk from a surprise.

A confession, because the register earned it

The seventeenth ungated rule in that document is about CSS. It says position: sticky fails in several ways — an overflow declaration on an ancestor, a transform, a static ancestor, a missing offset — and that when it fails, the sticky element simply stops sticking and the page still looks fine in review.

It came true one week later, on this site, twice

The contents rail you are using to navigate this page shipped broken on the two classes before this one. In the narrow layout it was declared static, so on a phone it scrolled off the top and never came back. Both pages passed a full assembler audit, two checksum comparisons and a registration. Every gate was green. The rule had been written down a week earlier, in the same estate, by the person whose page it was — and there was no checker behind it, so it caught nothing. The same register marks another rule with the note “shipped live twice”.

A rule written down with no checker behind it is a prediction of a future defect. That is the whole class in one sentence, and this is the evidence for it. The assembler that builds this page now carries a gate that refuses to write the file if the rail is declared static at any width. It is fourteen lines long. It would have cost ten minutes a week earlier.

Measured The sticky failure, as measured rather than described
Measured
At a 390 pixel viewport, the rail's distance from the top of the window over a scroll: 1046 → 446 → −454 → −2954 → −7954. It leaves the window and does not return. On the classes before those two, the same measurement holds at a constant offset.
Method
Rendered page, emulated narrow viewport, bounding rectangle of the rail element sampled at five scroll positions.
Excludes
Any claim about other breakpoints on those pages. Only the narrow layout was measured, and only the rail.
Not verified
Whether any reader ever noticed. Nobody reported it; it was found by someone scrolling a phone. That is precisely the property the register warned about.
Module 06 · 9 min

Refuse before you prompt

The cheapest check is the one that runs before the model does. This system keeps a short list of things it will not do at all, and the list has no override argument — not a flag, not an environment variable, not a --force. That absence is the design.

Four permanent refusals

  1. Three classes of theme file are never written. The layout directory and the two settings files. A generated edit to any of them can take down every page on the storefront at once, and no amount of linting makes that acceptable. They are excluded at the path level, before a generation request can be formed.
  2. Ineligible items are refused before a prompt exists. If an item is outside the category the rules were written for, the system does not generate cautiously — it does not generate. Eligibility is a precondition, not a validation step.
  3. There is no staging environment for product records. The system says so explicitly, which is the honest thing to do: every product write lands live. Knowing which of your surfaces have no rehearsal changes how you gate them.
  4. Snapshots are dated, immutable and additive. Never overwritten, never regenerated in place.

Why the fourth one matters more than it looks

“A backup re-taken after a write is a mirror, not a history.” If your restore point is refreshed from live on a schedule, then the moment a bad batch lands, the next refresh quietly destroys the only copy of what came before. One contamination in this system had to be recovered from a full census dump taken for an unrelated reason, because the backup had already become a mirror of the damage.

Write the refusal list before you write the generator. It is the only part of the design that gets harder to add later, because by then somebody will have a deadline and a good reason.

A gate a script can choose not to call is not a gate

The corollary the system states about its own architecture. If the check lives in a helper that each caller is supposed to invoke, then it is a convention, and conventions are one hurried pull request from being optional. Put the refusal where the operation physically cannot proceed without passing it.

Module 07 · 13 min

Five traps, each paid for in production

None of these is in anybody's documentation in a form that would have saved the time. Each cost a halted run, a wrong verdict, or a day.

1. The comment that eats your query

Tooling recognises a leading #graphql tag inside a template literal and switches on syntax highlighting and validation for it. It is genuinely useful. It is also a comment, in GraphQL, to the end of its line — so anything you write after it on that same line is not part of the query. If your query opens on that line, the opening is gone, and the error you get back is a parse failure pointing somewhere unrelated.

2. The read-back that reads the wrong half

The Assets API updates an asset's metadata before its body. For a second or two after a write, the checksum and the timestamp are the new ones and the value is the old one. A lost-update guard that fetches the body and hashes it is therefore blind in exactly the window it exists to cover: green on every edit older than two seconds, silently wrong on every fresh one.

METADATA LANDS BEFORE BODY.the lost-update guard is green on every edit older than two seconds, and silently wrong on every fresh one~1-2 s: checksum is NEW, value is OLDwritebody landsread-back safestorefront ~25 shash the BODY here and the guard reports a match that is not onecomparethe checksum - Shopify's own MD5, atomic with the writeread backby retrying until THIS run's build stamp appears, never a sleepacceptneither API offers a conditional write - no ETag, no preconditionCompare-then-write narrows the race to one round trip. It never closes it.
Metadata first, body second. Compare the checksum the platform returns against the hash of the bytes you sent. Do not hash a read-back taken inside the window, and do not sleep a fixed interval hoping to clear it — a fixed sleep is a guess about a latency you do not control. Inject a unique stamp per write and poll until that stamp appears.

3. The character the platform changes behind you

A bare ampersand is canonicalised to its entity form on the way in. So a byte-for-byte comparison between what you wrote and what the platform now holds will report a difference that is not a difference. Use a surface-aware compare where two layers meet — and only there. A backup-versus-live comparison is two reads of the same system, so that one must stay a byte compare or you will normalise away a real corruption.

4. The control that reads a cache

“A control that reads a cache tests the cache.” Three controls in this suite were green and measuring nothing, because the fixture they read had been captured before the code they were testing existed in its current form.

5. The reference derived from the thing under test

A verification run reported every record identical to its backup. That proved nothing, because the backup had been re-taken from live after the write. The rule is short and absolute: never verify against a reference derived from the thing under test. If your baseline is regenerated from your own output, your comparison is a tautology that reports as a pass.

Read the publish helper line by line

Three of the five traps are in these nine lines, and the function looks reasonable. Work through it and mark the lines that are wrong.

Interactive · Anatomyregion

A publish helper with three defects

async function publish(key, body) {    themeFilesUpsert(files: {filename: $k}) { userErrors { message } }  }`;  const res = await admin(q, { k: key, body });  const live = await get(key);}

Your answers stay in this browser. Nothing is sent anywhere. Reset clears them.

Seven projects · 40 min

Build the wall, then put a model behind it

Each project stands alone and each one is small. Do them in order: the later ones assume the artifacts the earlier ones produce. None of them requires the model to be good, which is the point — use whatever generator you already have.

1 · The tier table

Write your rules on one page, sorted into three tiers by what a violation costs. Hard reject, misattribution, human escalation. Nothing else — no code yet.

20 minno code

2 · The refusal list

Enumerate what your generator may never write, by path or by record type. Implement it as a throw inside the function that builds the prompt, so no caller can skip it.

30 minassumes 1

3 · Two checks, one class each

Implement your two highest-cost tier-1 rules. Before you write either, write down the set it must cover, then build a member of that set and confirm the check catches it.

45 minassumes 1–2

4 · The because field

Give every control a field holding the verbatim input that made it fail. You cannot fill it without having seen the control go red, which is the point.

30 minassumes 3

5 · The mutation harness

A script that disables one rule at a time, runs the suite, and asserts that exactly the controls for that rule go red. Run it on the checks from project 3 and expect a surprise.

60 minassumes 3–4

6 · Constrain one input

Pick a repetition problem you have. Instead of editing the prompt, remove something from the brief. Measure before and after at a pinned corpus size and record both numbers with their N.

45 minassumes 1

7 · Capstone: the wall

Wire it together. Generator on one side, tiered linter on the other, no path from the first to a published surface that does not pass through the second. Then confirm it refuses most of its own output.

90 minassumes 1–6

Write the specification for project 1

The tier table is the artifact everything else hangs off, and it is the one people skip. Fill this in and take the output with you — it is a specification, not a prompt, and it is meant to be read by a person before it is read by anything else.

Interactive · Forgelive

Generate your tier table specification


Your answers stay in this browser. Nothing is sent anywhere. Reset clears them.

Bottom line

Build the thing that says no. The generator is the part everyone is working on and the part that will be obsolete by spring. The linter — your rules, tiered by what a violation costs, implemented as classes rather than tokens, each one proven by having been made to fail — is the part you actually own, and it keeps working when you swap the model underneath it.

And keep the register. Write down every rule you have not gated, and be honest about which of them are merely ugly and which are lies waiting to happen. The author of the system in this class kept that list, predicted a defect in it, and then watched the prediction come true on his own site a week later — twice — because writing a rule down is not the same as enforcing it. The list is not an admission of weakness. It is the only reason he knew where to look.

Quoted
Every passage in quotation marks on this page is verbatim from the reference system's own documentation, written by its author about his own work before this class existed.
Measured
The repository scale in module 00, and the sticky-rail failure in module 05, were measured on 2026-09-22 by the methods stated in their receipts.
Reported, not measured here
The control count, the check count, the register size and every before-and-after percentage are figures the reference system reports about itself. They are reproduced as reported and were not independently re-measured.
Not verified
Whether the system is running today or its controls are green as you read this. It is bound to a private machine and unreachable from here.
Deliberately absent
Certificate names and schemes, supplier identities, product counts, file paths, hostnames, ports, theme identifiers and model names. The tiering is the transferable part; the specifics are not yours and were not the lesson.

Questions

Why not just let the model publish to the theme?

Because a probabilistic system cannot be held to a rule whose violation is a false public claim. A model can be persuaded, and on a live storefront the cost of a persuasive wrong answer is a sentence a customer reads and believes. The system this class is drawn from puts it in one line: the model generates, the linter decides. Generation is a secondary concern to verification, and the practical consequence is that the generator is replaceable — swap the model tomorrow and every guarantee still holds, because none of them were ever coming from the model.

What does gate the priors, not the gaps mean?

It is a rule for deciding which defects deserve a deterministic checker and which deserve a better prompt, and it comes from a measurement rather than an argument. Three generator defects were each refused by the gate and then addressed by editing the prompt. Two of them concerned things the prompt had never mentioned, and both were fixed on the first try. The third concerned a pattern that appears in nearly every tutorial on the subject — and it survived eleven refusals across a rewritten prompt section and a reject list, never fixed. Where the model has no prior, guidance moves it. Where it has a strong one, the weight of what it learned beats the instruction in front of it, and only code will stop it.

Why can a linter not be an LLM?

Three reasons, and they compound. A probabilistic judge cannot be controlled — the same input can produce different verdicts. It cannot be unit-tested against a fixed matrix, so you can never demonstrate that it catches the case you built it for. And it cannot be trusted with a rule whose violation is a false public claim, because being usually right is not a standard that survives contact with a regulator or a customer. Deterministic code has none of those properties. It is also dramatically cheaper, faster, and inspectable line by line by whoever has to defend the output.

What is the Shopify Assets API race condition?

The Assets API updates metadata before body. For roughly one to two seconds after a write, a read returns the new checksum and the new updated-at timestamp alongside the old value. The consequence is specific and nasty: a lost-update guard that hashes the fetched body is blind in exactly the window it exists to cover. It is green on every edit older than two seconds and silently wrong on every fresh one — which is to say it works perfectly except when it matters. Compare the checksum instead, because that is the platform's own hash and it is atomic with the write.

How should a read-back after a theme write be written?

Retry until you see this run's own build stamp, and never sleep a fixed interval. Inject a unique stamp into every write, then poll the read-back until that exact stamp comes back. A fixed sleep encodes a guess about a latency you do not control, and the guess is wrong on a slow day. Be aware too that neither transport offers a conditional write — there is no ETag on the REST assets endpoint and no precondition on the GraphQL upsert — so compare-then-write narrows the race to a single round trip and never closes it. And the rendered storefront lags further still, measured at up to twenty-five seconds, so if you are verifying what a customer sees you must poll for a marker rather than assume.

What is a rule written as a token instead of a class?

A rule about a category, implemented as a match on one member of it. A rule meant to cover any certification claim, written as a search for one certification's name. A rule meant to cover any template consumer, written as a search for one tag. A rule about grammar, implemented as proximity between two words — which reported five hundred and eighteen live defects that did not exist. Each one passes review because the example it was written against does light up. The test is to write down the class the rule is supposed to cover, then construct a member of that class the rule does not catch. If you can, the rule is a token.

How do you know a control actually works?

You break the thing it watches and confirm it goes red, while a guard-pair control stays green. That second half matters more than it looks: it proves your mutation was targeted rather than catastrophic. This discipline caught two controls in a single month that were passing with the rule they tested switched off — they had never been capable of failing and nothing about their output said so. Every control should also carry the verbatim failing input that caused it to be written, in a field beside the rule, so the next person can see the exact sentence that made it necessary.

Why did constraining the input beat banning the output?

Because a list of prohibited outputs does not constrain a generator's output space; it only redirects it. Measured twice on the same corpus: removing the existing copy from the brief moved shared-phrase collapse from 65.5 percent to 13.3 percent, and making the product's graphic the required subject moved the worst class from 35.9 percent to 18.2 percent. Both are constraints on the input. By contrast a banned-phrase list shipped alongside them worked, and then its residue reappeared structurally — the next run's most-shared span was a new phrase family that had not existed before the ban. Keep the bans, but know that the mechanism is the input constraint and the list is mop-up.

What should a generator never be able to write?

Anything where a mistake is unrecoverable or invisible. In the reference system four refusals are permanent and carry no override: the theme layout directory and the two settings files, refused at the path check before their contents are read; and copy for a product class the pipeline cannot describe, refused before a prompt is even constructed. The principle behind all four is that eligibility must be decided from the record by code, inside the function that builds the prompt, and that function must throw rather than return something a caller can decline to read. A gate a script can choose not to call is not a gate, and a brief a script can assemble for itself is a bypass around every gate you have.

Do I need local inference to build this?

No. The architecture in this class runs generation on a local model, which is a cost and privacy decision rather than a safety one — the safety comes entirely from the deterministic layer in front of the publish step, and that layer does not care where the text came from. You can build the whole gate against a hosted API, a local model, or a human typing into a box, and it behaves identically. If anything, start with whatever generator you already have: the point of the class is that it is the replaceable part.