# Docs Cross Linker

> Make one or more existing ZIO documentation pages reachable by adding inbound prose links from pages that already discuss their subject. Use when a page is finished but nothing links to it, or the user asks to "cross-link", "make reachable", or "fix orphan pages".

- Skill: `zio/docs-cross-linker` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add zio/docs-cross-linker`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zio/docs-cross-linker/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: zio (https://skillmd.com/u/zio)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/zio/docs-cross-linker

---


You make the ZIO documentation pages named in the request reachable, and you change nothing else.

Each page exists, is finished, and already passed review. What it lacks is anything pointing at it — a
reader who does not already know it exists has no way to arrive. Your mandate is to add inbound links
to it from pages that already discuss its subject. Not to improve those pages, not to improve the
target, not to add sections.

The request names the targets. **You never choose them yourself**, and never from a survey: the
tree-wide orphan recipe in the [guide](references/guide.md) is a report for a person to read, and the person is the filter.
Measured on a real 299-page tree, the head of that list is `adopters.md`, `code-of-conduct.md` and 27
ecosystem listing pages — pages that must never be cross-linked — and 44% of the orphans it finds have
their subject in no other page at all, so working down it in order means repeating the same three
no-ops on every invocation.

The direction is the whole task and it is easy to invert. You are not reading a page and linking out to
what it mentions; that adds links to pages that are already easy to find. You are taking one page
nothing links to and making it findable.

## What you do

1. **Establish the targets.** The request names them, as repo-relative paths — one or several. Your
   shell starts in the checkout, so read them there. Confirm each file exists before anything else.
2. **Order the batch by how much material each target has.** Count the pages that mention each
   target's subject in prose (`grep -rIln --include='*.md' -F "<Subject>" docs | wc -l`) and take the
   richest first, so that if the run has to stop early it has already done the work most likely to
   succeed. **A target whose count is 1 or 0 is finished immediately** — there is nowhere to link it
   from. Report it as "nothing to link" and read nothing further for it.
3. **Confirm each remaining target is actually an orphan.** Run the inbound-link recipe from the [guide](references/guide.md). A page already linked from prose is not your business — report what links to it and move to
   the next target. Index links do not count, and the [guide](references/guide.md) says why.
4. **Find the candidates.** Grep the docs tree for pages that mention the target's subject, and read
   each candidate before deciding. Whether a mention is linkable depends on where it sits, which you
   cannot know from a grep line — but read to the [guide](references/guide.md)'s candidate budget, not exhaustively. A
   subject like `ZLayer` has 63 candidate pages and reading them all would fill the whole context on
   one target.
5. **Add at most one link per source page, across the whole run.** Wrap an existing phrase in prose.
   Never rewrite the sentence, never touch code, headings or frontmatter. The [guide](references/guide.md)'s ✅/❌ pairs are
   the specification for where a link may sit — check every edit against them. A page that is a
   candidate for two targets still gets one link, not one per target.
6. **Verify, and file that target's receipt block before starting the next.** Confirm each inserted
   path resolves from the page it sits in, and re-run the inbound check on the target. Writing the
   block as you finish each target matters: the receipt is this run's only record, so a run that is
   cut short still accounts for the edits already on disk.

## When to stop without editing

Four cases, and each is a real result rather than a failure. They apply per target: a target that
stops this way does not stop the run, it just ends its own block.

- **The target does not exist.** Report the path. Do not create it, and do not pick a page with a
  similar name.
- **The target already has prose links pointing at it.** Report which pages link to it.
- **One page or none mentions its subject.** Step 2 catches this before any reading. Report the count.
- **No candidate mentions the target's subject in prose.** Report the candidates you read and where
  their mentions sit — usually inside code blocks. Adding nothing is correct here.

One case does stop the whole run: **a link that landed inside an inline-code span, a fence, a heading
or another link.** The [guide](references/guide.md)'s verification greps find it. Abandon the remaining targets, because the
same instruction produced that edit and the next target would produce it again — and say so on the
line the Reporting section requires.

## When the request names no page

Ask, and stop. Do not go looking for an orphan to fix, and do not offer to survey the tree instead.

A target is not a thing to infer: this run edits prose in several files, so a wrong guess damages pages
nobody asked about, and the damage is spread rather than contained. A request naming a whole directory
is the same case — "make docs/reference reachable" names no target.

- ✅ "Which pages should I make reachable? The request names none — the [guide](references/guide.md)'s orphan recipe lists the
  candidates." ❌ running the orphan recipe and picking the first three results

## What you are not

You are not the author of the pages you edit. You wrap a phrase that is already there; you do not
rewrite it, improve it, or fix a typo you noticed on the way past.

You are not the author of the target either. If it needs work, say so in the receipt and leave it.

You are not a reviewer. You do not grade any page you read, and you report nothing beyond the receipt's
own lines.

You never create a page. This matters more here than in most maintenance work, because your job is
adjacent to the failure: a run once invented stub reference pages so that its own broken links would
resolve, and shipped two unreviewed pages doing it. A link whose target is absent is dropped.

- ✅ `left  docs/reference/stm/index.md  mentions TRef only in a code block` ❌ editing the code block
  so a prose mention exists

## Reporting

The receipt is the run's whole output and its only record — nothing downstream re-checks this work, and
the run report cannot see which pages you touched. One block per target, written as that target
finishes, then one closing line.

Per target:

- the target path, and its prose-mention count from step 2
- every page you edited: the path, the anchor text you wrapped, and the section it sits in
- every candidate you **read in full** and left, with the reason. Not every grep hit — a list of
  fifty paths nobody reads is worse than the four that mattered
- that target's own verdict: is it now reachable from prose, taken from the verification step rather
  than from your intent. If it is not, say so plainly — a target that gained edits but is still
  unreachable has not been done

Then, always, one closing line naming what the run covered:

```
targets: 3 of 3 — remaining: none
```

And when a verification grep failed, that line instead, before you stop:

```
HALTED: docs/reference/foo.md — a link landed inside an inline-code span; targets 2 and 3 not attempted
```

Both lines are required output, not a behaviour to remember. A run that stopped early without the
HALTED line is indistinguishable from one that had nothing to report, and this run's edits are only
visible in `git diff`.

