CogniPrep is a practice library for employer assessments. Last week it grew by 132 public pages in one round: 48 employer guides, 9 new test-format pages, 3 hubs, a STAR method guide, 6 score explainers, 15 Dutch pages and 50 pages about individual named tests. The live sitemap went from 398 URLs to 530. The diff against the commit before the round is 230 files and 29,921 insertions.
That work ran as fourteen branches, each in its own git worktree, merged into one integration branch and then into main. Fifteen merge commits in all.
Parallel content work is easy in the parts nobody shares. The hard part is that internal linking is the entire point of the round, and a worker who wants to link to a page another worker is building has only bad options. Link to it and you ship a 404 if that branch slips. Leave the link out and nobody ever goes back to add it, which is how a 132-page expansion turns into 132 orphans.
Fix the URLs in the plan, not in the code
The shared plan document that every worker read first has a section headed "Fixed URLs, so cross-links work after the merge". It lists exact paths: the nine new /tests/<format> slugs, the three hub paths, /interview/star-method, and /nl. Then one rule:
Do not link to any other page that does not exist yet.
The interesting omission is the per-test pages. Those live at /games/<provider>/<test>, and nobody could guess the slugs in advance, so the plan says to link them "only once they exist; do not guess test slugs". A URL can be forward-declared only if somebody has already decided what it is. Everything else waits.
The guard tests had to be told
Every content registry in this codebase has a test that asserts its internal links resolve, by checking the route folder exists on disk:
const folder = path.join(process.cwd(), 'app', ...item.href.split('/').filter(Boolean)); expect(existsSync(path.join(folder, 'page.tsx'))).toBe(true); On a worker branch, a forward reference fails exactly that assertion. So two branches carried a temporary allowance. The per-test pages guard got an 18-entry set:
// Pages from the November 2026 SEO round that other workers are building on // their own branches (docs/seo-expansion-2026-11.md, "Fixed URLs"). /tests is // still one dynamic route until the formats worker splits it into folders. // Remove this allowance once that round is merged. const BUILT_IN_PARALLEL = new Set([...]); and its assertion became exists || BUILT_IN_PARALLEL.has(item.href).
The Dutch cluster's test had the same problem in a smaller shape, plus one extra wrinkle. One worker was converting /tests from a single dynamic [test-type] route into fourteen folders while the Dutch worker was linking into it, so "does this page exist" had two correct answers at once:
if (path.startsWith('/tests/')) { // /tests/<slug> is one dynamic route over TEST_TYPES until the formats // worker converts it to folders; either shape counts. return ( existsSync(join(APP, path, 'page.tsx')) || readFileSync(join(ROOT, 'lib/tests/test-types.ts'), 'utf8').includes(`slug: '${path.slice(7)}'`) ); } A test that tolerates both shapes of a route during a migration window is not a weaker test. It is the same assertion with the migration's own uncertainty written into it, for as long as that uncertainty exists.
Deleting the allowance is part of the merge
The last commit of the round took the 27-line allowance block out and put the assertion back to expect(exists, ...). That is the step people skip, and it matters more than the allowance itself: an exception left behind after it stops being needed is indistinguishable from an exception you still depend on. Six months later nobody can tell which it is.
One of the two allowances did survive, and it is worth naming rather than hiding: the Dutch test still carries a two-entry FIXED_URLS set for /tests/abstract-reasoning and /tests/cognitive-ability. Both pages exist now, so the set no longer changes the result of any assertion. It is a fossil, and it goes next.
Did the forward references pay off
The per-test pages carry 101 "Read next" links across 28 distinct targets. 46 of those 101 point at pages that did not exist when the link was typed. /game-based-assessments, a hub built by a different worker on a different branch, has 14 inbound links from the test pages alone. Without the fixed-URL list, almost none of those would have been written, because the person writing the page had no page to link to.
Arithmetic is the cheapest check that the round did what it said: 398 URLs before, plus 50 test pages, 15 Dutch pages, 9 formats, 3 hubs, 1 STAR page, 6 posts and 48 employer pages, is 530. The live sitemap says 530.
See it
Open cogniprep.app/games/arctic-shores/code-breaker and scroll to the "Read next" block. Both of its links, to /game-based-assessments and /tests/cognitive-ability, were written on a branch where neither page existed. Follow them, then check cogniprep.app/sitemap.xml: grep -c '<loc>' on that file returns 530.
If you want the other side of the same contract, cogniprep.app/tests is the hub that only became fourteen separate pages after the merge.


