On June 30, I changed 40 lines in src/content.config.ts, added 33, removed 7, and stopped requiring voice, tone, and style before a post could publish.

That sounds like a retreat from standards. It was the opposite. Those three fields gave us a clean looking checklist while leaving the failures that actually matter available to ship: an unsafe claim, an identifying detail, a broken code sample, a story with no pressure, or an ending that just fades out.

After the first three posts were public, the distinction became hard to ignore. A record could say its tone was direct and its style concise. That did not prove the post had earned a public slot.

The old contract checked labels

The old schema treated voice, tone, and style as required publish gates. A post was structurally complete because three subjective fields existed. It did not mean the writing carried a real incident, the technical claim was safe to publish, or the ending landed.

I was not trying to turn those concerns off. Voice, tone, and style still matter. The mistake was putting them in the same class as facts, anonymization, and code safety.

The difference is operational. A safety failure has an observable consequence. A fact can be wrong. A customer or internal system can be exposed. A command can be dangerous or misleading. A reviewer can identify each condition, fix it, and know why the gate passed.

Story quality can work the same way when it is specified properly. The reader needs to know what hurt. There has to be enough vulnerability to make the account more than a cleaned up success report. The closing needs to make a final claim rather than point at another post or dissolve into a generic lesson.

voice, tone, and style did not have that property. They described intentions, not conditions. We had made them mandatory because they were easy to name, not because they were the tests we needed.

The cutover made the schema narrower and the review harder

The new required set is split into three kinds of gate:

safety: facts, anonymized, code_safety
story: bleeding_neck, vulnerability, concrete_closer
approval: author_review

Safety answers whether the post can go public without a factual, identity, or implementation problem. Story answers whether the account has enough lived pressure to be worth reading. The final approval remains human responsibility. No schema should claim that a reviewer is unnecessary.

The critical decision was what we did not do. We did not replace three vague fields with a longer list of vague fields. That would only make the form longer. We did not promote a style prompt into a hard gate and call it automation. A prompt can guide a draft. It cannot prove that a draft is truthful, anonymous, technically safe, or narratively complete.

We also did not rip the old fields out of the content model. There are 187 existing posts, and their records still carry voice, tone, and style. Deleting them would turn a publish-policy change into a content migration, with every old record becoming a special case. Keeping them in the schema preserves the history and the content shape. Removing their required status changes only the publish contract.

That distinction kept the change small enough to verify. The final diff was one file, src/content.config.ts, not a site rewrite or a back-catalog rewrite.

I used the build as the first diagnostic

A content-model change can look right while breaking the actual publication path. I treated the build as the first diagnostic, not a victory lap.

The sequence was straightforward. First, make the safety gates required. Second, make the story gates required. Third, retain voice, tone, and style as optional schema members for the 187 existing posts. Finally, run the local build against the three posts already published.

All three validated under the new contract. The local build was green.

That result answered two different questions. It showed that optional legacy metadata was backward compatible. It also showed that the live examples passed the gates we now say matter. If those three posts had failed, the options would have been clear. Either the gate was badly specified, or the post needed editorial work before it could be considered public. A green build did not settle every editorial judgment, but it ruled out a fake migration success.

Style moved to lint, where it belongs

The discarded alternative was to keep voice, tone, and style required, then tighten the instructions around them. That would preserve an orderly form, but the core weakness would remain. A required label is still just a label.

Style hygiene moved to deterministic lint instead. That is the right layer for rules with mechanical evidence. A linter can catch prohibited punctuation, a forbidden phrase, an incorrect heading pattern, or another explicit editorial constraint. It can fail consistently and explain why. The publish contract stays reserved for the conditions that define whether a post is safe and complete enough to be public.

The configuration now says what we mean. It does not ask a post to certify its own personality. It asks for facts, anonymization, code safety, an honest point of pain, a degree of exposure, a concrete ending, and human signoff.

That is a smaller contract than the old one. It is also much harder to satisfy by filling in the right words.