How prebuild Normalization Prevents Validation-Order Failures
What you’ll learn
- How to separate apparent build failures into normalization and validation-order problems
- Why regenerated values should be normalized automatically before prebuild instead of fixed once
- The verification flow after encoding automatic repair and validation in the harness (the rules, procedures, and checks given to AI)
Normalize Content Before Validation to Prevent Order Failures
I expected correcting a mismatch once to keep the next build healthy, but generated values and metadata brought the same failure back. The cause was not only the value; validation was running before normalization. Normalizing mechanical differences first and checking that order turns a recurring repair into a preventable pipeline failure.
By the end of this article, you will have practical criteria for answering “How can recurring mismatches move from one-off repair into automatic normalization before validation?” in your own context.
Normalize learning_time Automatically Before Validation
This article explains how I implemented automatic normalization in prebuild (a step that runs preparation, normalization, or validation before the production build) to make build failures less likely on an Astro / Starlight learning site.
The root cause was not manual entry of learning_time. The actual issue was that the validation pipeline expected a normalized repository state, but the process that created that state was not guaranteed to run before validation.
Missing learning_time Normalization Looked Like a Build Failure
On the surface, the issue looked like a Vercel build failure or an Astro build failure. After investigation, some failures were happening before the build itself, during the prebuild stage.
In this repository, prebuild runs before npm run build. It checks the content before publication by running slug (the short identifier used in a public URL or internal article link) completion, link verification, Mermaid (a syntax for writing diagrams and flowcharts in Markdown) validation, article review, harness (the set of rules, procedures, and validation that guides AI work in a project) validation, and related checks.
One part of that flow validates the learning_time field in docs articles. learning_time is a value that can be calculated mechanically from the body length of an article. If the value no longer matches the content, the mismatch can be detected before build.
That design is reasonable. The problem was that detection came first, while the automatically fixable difference was not always normalized before validation.
The Root Cause Was Validation Order
The key distinction is that normalize-learning-time.mjs --check verifies whether the current state is correct, while --write updates files into the correct state.
If a build pipeline runs only --check, then a docs article whose learning_time is not yet normalized stops the build immediately.
That is not primarily a manual-entry problem. It is a pipeline contract problem: a difference that can be fixed mechanically was not being fixed mechanically before validation.
The fix was to guarantee the following order in both dev:check and prebuild.
"dev:check": "node scripts/normalize-learning-time.mjs --write && node scripts/normalize-learning-time.mjs --check && ..."In the actual package.json, --write runs after existing steps such as slug completion and locale synchronization, then --check runs immediately afterward. Automatically normalizable differences are updated first, and only remaining problems are treated as validation errors.
Why Auto-Repair Belongs in prebuild
Automatic normalization belongs in prebuild because the same type of fix should not be repeated manually right before every build.
This site has several validation points that come from Markdown content: article bodies, frontmatter (the metadata at the top of a Markdown article, such as title, description, and date), internal links, Mermaid diagrams, and Japanese/English pairs. When those issues surface only at build time, it becomes harder to tell whether the problem is in Astro, Vercel, article content, or a generation script.
So I gave the daily local check and the build-time precheck the same contract by wiring the same order into dev:check and prebuild.
- Fix mechanically fixable differences before validation
- Stop only on problems that cannot be repaired automatically
- Use the same order for local checks and prebuild checks
With this design, a build failure is easier to treat as a specific contract violation instead of an unclear error at the final step.
Locking the Prevention Into the Harness
Fixing package.json once is not enough. If the script order changes later, the same problem can come back. To prevent that, scripts/validate-harness.mjs now validates the order of dev:check and prebuild.
It requires both commands to satisfy these conditions.
node scripts/normalize-learning-time.mjs --writeis presentnode scripts/normalize-learning-time.mjs --checkis present--writeappears before--check
This means the change does more than make the current build pass. If a future edit breaks the ordering contract, harness:check can detect it.
Run dev:check First and Build Only When Needed
After the implementation, I do not start by running npm run build. I first use npm run dev:check to reproduce and clear the checks that are equivalent to the prebuild layer.
In this repository, npm run build is an approval-gated local artifact (an output file or deliverable generated by a build or creation process) build command. I treat Vercel deployment status as a separate hosting-side signal. For normal verification, dev:check is the first gate because it checks links, slugs, Mermaid blocks, review rules, and harness rules together.
When build verification is required, I separate the signals this way.
- Does
dev:checkpass? - After approval, does
npm run buildpass? - Does the Vercel deployment status become successful?
Separating these checks makes it easier to avoid confusing local validation failures, build failures, and remote deployment status.
Summary: Make Normalize-Then-Validate a Pipeline Contract
The important part of this fix was not the learning_time value itself. The important part was defining the repository state that must exist before build as a pipeline contract.
Mechanically normalizable differences are fixed in the early part of prebuild, then validation runs afterward. The order itself is also checked by the harness. That turns a recurring build failure pattern into an implementation that is less likely to regress.
The first action is to classify the latest failure as either a deterministic value mismatch or a content error that needs human judgment. Automating the second category can hide mistakes, so preprocessing should include only differences that the same rule can always normalize to the same result.