Last week the cross-publish pipeline had two bugs that would have silently degraded internal links between articles. Neither one would have caused an error. Links would have resolved to plain text — the link text would still be there, but the href would be gone. A reader wouldn't notice until they tried to click through.
Both were caught in a Codex review of the slug resolver in packages/publish/src/links.ts. Here's what they were.
Edge case 1: legacy filenames without a date segment
The resolver uses a regex to extract a slug from a filename:
// Before
/^\d+-\d{4}-\d{2}-\d{2}-(.+)\.md$/
That pattern requires a date segment between the sequence number and the slug. The current filename convention is NN-YYYY-MM-DD-slug.md. But the first 10 articles in this experiment's repo predate that convention — they're numbered without a date:
01-three-sites-experiment.md ← no date segment
02-2026-05-01-adding-avoid-if-caveats.md ← has date segment
The legacy filename never matched the regex. The slug resolver returned null for it, and the link rewriter replaced any [text](https://dev.to/morinaga/i-built-3-programmatic-seo-sites-for-25month-using-claude-haiku-heres-the-full-architecture-3pl8) link with just text. Article 01 had 26 inbound links from other articles. All 26 would have been unwrapped silently on the next bulk publish.
Fix: make the date group non-capturing and optional.
// After
/^\d+-(?:\d{4}-\d{2}-\d{2}-)?(.+)\.md$/
The slug for 01-three-sites-experiment.md is now three-sites-experiment, and all 26 links resolve to the Dev.to URL correctly.
Edge case 2: same-run URL references
The slug map — a Map<string, string> from slug to published URL — was built once at the start of a run by scanning published_urls frontmatter across all existing articles. Articles link to each other using /articles/<slug> paths, which the pipeline resolves to real URLs at publish time.
The problem: in a bulk publish run, articles are published sequentially. If article B links to article A, and both are in the same run, the slug map was built before A had a Dev.to URL. The map would return null for A's slug, and B's link to A would degrade to plain text.
The sequence:
1. Run starts. Slug map built from disk. A has no published_urls yet → absent from map.
2. Article A published to Dev.to. URL returned, written to disk.
3. Article B's links rewritten. Map lookup for A's slug → null. Link dropped.
Fix: update the map in memory immediately after each successful Dev.to publish, before the next article processes its links.
// After publishing A to Dev.to:
const devtoUrl = result.url;
slugMap.set(slugFromFilename(basename(article.filePath)), devtoUrl);
// Now when B processes its links, A's slug resolves correctly.
The map is now a live snapshot rather than a pre-run snapshot. The canonical URL chain benefits directly: Dev.to is the preferred platform, so any cross-article reference resolves to the correct primary URL as soon as that URL exists.
Why both bugs were invisible
Neither threw an error. The link rewriter's unresolved-slug path is intentional graceful degradation: an article that links to something not yet published should still be publishable without a dead href. The assumption was that unresolved slugs meant "not published yet." These two edge cases created false unresolved states for articles that were already live.
An article like:
See the [sitemap setup I described earlier](https://dev.to/morinaga/astrojssitemap-generates-sitemap-0xml-not-sitemap-indexxml-on-small-sites-5c7d).
Would silently become:
See the sitemap setup I described earlier.
No exit code 1, no parse failure. Just a sentence that reads correctly and loses its link.
What the tests cover now
links.test.ts now includes cases for both edge cases specifically:
- A filename with no date segment resolves to the correct slug
- A slug registered mid-run (after the map was built) resolves in subsequent articles
18/18 tests pass. The test suite runs in CI on every push, so this class of regression is now blocked automatically. The actual verification was checking the slug map against all 98 currently published articles — all resolved, including the legacy 01 article.
Part of an ongoing 6-month experiment running three AI-curated directory sites. The technical claims here are real; this article was AI-assisted.
Top comments (0)