Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion skills/core/open-knowledge/references/linking.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
- **Every link must resolve to a doc that exists by the time you're done.** Within a single multi-doc authoring pass, linking a page you'll create later in the same pass is fine — `brokenLinks` reports it as `no-such-doc` until the target lands, an expected transient forward-reference rather than a wrong-path error. What's not acceptable is *leaving* a dead link behind: create the target in the same pass, or record it as a tracked task (`TaskCreate`, or your host's task tool — if the host has none, tell the user) and leave the mention as plain prose.
- **Never wrap a link in backticks.** `` `[text](./foo.md)` `` is a bug — the backticks make it render as literal code rather than a link.
- **Never use HTML anchors** (`<a href="...">`). Markdown link syntax only.
- **`brokenLinks` on the write/edit response is your primary check — read it before walking away.** Every `write`/`edit` returns `brokenLinks` in the SAME payload: `[]` means every outbound link resolves only when `brokenLinkSuppression` is absent and `warnings` has no `link-check-deferred` entry (that entry means the server was still starting and checked nothing — that doc's links are re-checked only when you write or edit it again after startup, or by an `audit`, which can time out until then); a populated list names each broken `href` with its `resolvedTo` + `reason` (`no-such-doc` = resolved to a doc that doesn't exist; `no-such-file` = resolved to a linked asset / source file that isn't on disk at that path; `unresolvable` = the path escapes the content root, usually one `../` too many). This validates **every** local link, not just doc links: a wrong-depth `[src](../../foo.py)` to a source file is caught too. It is report-only — the write landed regardless; fix or remove every one in a follow-up `edit`.
- **`brokenLinks` on the write/edit response is your primary check — read it before walking away.** Every `write`/`edit` returns `brokenLinks` in the SAME payload: `[]` means every outbound link resolves only when `brokenLinkSuppression` is absent and `warnings` has no `link-check-deferred` entry (that entry means the server was still starting and checked nothing — that doc's links are re-checked only when you write or edit it again after startup, or by an `audit`, which can time out until then); a populated list names each broken `href` with its `resolvedTo` + `reason` (`no-such-doc` = resolved to a doc that doesn't exist; `no-such-file` = resolved to a linked asset / source file that isn't on disk at that path; `excluded` = the file is on disk but a `.gitignore` or `.okignore` rule keeps it out of the project, so re-include it or its folder with a `!` rule in `.okignore` (for example `!ignored/`) or link something else; `unresolvable` = the path escapes the content root, usually one `../` too many). This validates **every** local link, not just doc links: a wrong-depth `[src](../../foo.py)` to a source file is caught too. It is report-only — the write landed regardless; fix or remove every one in a follow-up `edit`.
- **An empty `brokenLinks` alongside a `brokenLinkSuppression` does not mean every link resolves.** That field appears only when the project's reserved-log policy withheld findings, and it carries `{ reason: "reserved-log-policy", count: N }` and nothing else. No href comes back because none of them is repair work: the write landed in a lowercase-stemmed `log.md` or `log.mdx`, an append-only history whose entries deliberately reference pages that moved or were never written. **Never rewrite a reserved log's existing entries to clear a link.** Append new entries instead, and treat `links({ kind: "dead" })` on that file as a deliberate audit of raw state, never as a repair queue. The project can turn the policy off (**Settings ▸ This project ▸ Preferences ▸ Content rules**), in which case those findings come back through the normal channels.
- **`audit` is the authoritative end-state link check** — run it once at the end of a multi-doc pass. Read `brokenLinkSuppression` on its result too: when present, the reserved-log policy withheld `count` findings, so a clean result is filtered rather than proof that every raw link resolves. Do not repair those log findings; use `links({ kind: "dead" })` only when deliberately inspecting raw state. Caveat: `audit` reports links only while the project's `validation.links` setting is not `off` (at `off` it returns clean with no warning), so if the project has silenced link validation, fall back to `links({ kind: "dead" })` — the unconditional read of the dead-link graph, which also lists skill-bundle docs excluded from the Problems plane (optionally scoped: `links({ kind: "dead", sourceDocuments: ["your/doc"] })`). Otherwise `links` is the graph-navigation reader, supplemental to `audit`, not a substitute. Companion `links` kinds: `backlinks` (incoming), `forward` (outgoing), `orphans` (no incoming), `hubs` (high-incoming), `suggest` (untextualized mentions worth linking).
- **The tool and the editor agree.** Both resolve a bare name the same way — exact, then slug, then folder index, then basename — so a link that opens when you click it is never reported dead, and a link reported dead is genuinely unresolvable. A target that names an asset, or names nothing at all, stays silent rather than being reported as a broken doc link.
Expand Down