Skip to main content

url-linter.js — CEO-facing URL Verification Gate

url-linter.js — Usage Runbook

Owner: CodeCraft Created: 2026-04-22 Purpose: Prevent broken URLs from reaching CEO. Enforces ZAKON URL.


When to Use

Run pre-ceo-publish.sh BEFORE sending ANY of these to Alem:

  • Partner shortlists
  • Market analysis documents
  • Research briefs with external sources
  • Decision documents (*-VERIFIED.md, *ceo-decision*, *ceo-brief*)
  • Any file in ~/system/specs/

Trigger rule: "Every URL in a CEO-facing document MUST pass url-linter.js before publish."


Quick Start

# Gate check before CEO delivery (REQUIRED)
bash ~/system/hooks/pre-ceo-publish.sh ~/system/specs/my-doc.md

# Direct linter run
node ~/system/tools/url-linter.js ~/system/specs/my-doc.md

# Scan entire specs directory
node ~/system/tools/url-linter.js ~/system/specs/

# JSON output (for scripts)
node ~/system/tools/url-linter.js ~/system/specs/my-doc.md --json

All Options

node url-linter.js <file-or-dir> [options]

Options:
  --fix              Replace redirected URLs with final URLs; remove NXDOMAIN URLs and append a FIXME flag
  --json             Output JSON report (machine-readable)
  --fail-on=<N>      Exit 1 if N or more broken URLs (default: 1)
  --concurrency=<N>  Max parallel checks (default: 5)
  --timeout=<ms>     Per-URL curl timeout in ms (default: 15000)
  --cache=<path>     SQLite cache path (default: ~/system/state/url-linter-cache.db)
  --no-cache         Skip cache, always re-check every URL
  --cache-ttl=<h>    Cache TTL in hours (default: 6)
  --quiet            Only show failures and summary

Exit Codes

Code Meaning
0 All URLs pass (or below fail-on threshold)
1 One or more broken URLs found (at or above fail-on threshold)
2 Usage error or fatal (file not found, etc.)

What Gets Checked

URL patterns extracted from markdown:

  • [text](https://url) — link syntax
  • <https://url> — angle bracket URLs
  • https://url — bare URLs

Test method: curl -sSIL --max-time <N> <url>

Result Classification
HTTP 2xx PASS
HTTP 3xx followed by 2xx PASS (shown as REDIRECT)
HTTP 4xx, 5xx FAIL
NXDOMAIN (cannot resolve) FAIL
Timeout FAIL
Connection refused FAIL

Cache

Results are cached in ~/system/state/url-linter-cache.db (SQLite).

  • TTL: 6 hours by default
  • Re-running on same file with same URLs uses cache — completes in < 1s
  • Override: --no-cache to force fresh check
  • Cache is safe to delete: rm ~/system/state/url-linter-cache.db

PostToolUse Hook (automatic)

The hook ~/system/hooks/url-linter-gate.sh runs automatically after Write/Edit on CEO-facing files. It runs async (does not block the write) but prints a warning to stderr if broken URLs are found.

CEO-facing patterns that trigger automatic check:

  • */system/specs/*
  • */evidence/ceo-*
  • *-VERIFIED.md
  • *-shortlist*
  • *ceo-decision*
  • *ceo-brief*
  • *ceo-report*

Hook activation is present in ~/.claude/settings.json under PostToolUse for Write|Edit|MultiEdit. The canonical hook block is:

{
  "matcher": "Write|Edit|MultiEdit",
  "hooks": [
    {
      "type": "command",
      "command": "bash ~/system/hooks/url-linter-gate.sh",
      "timeout": 60000,
      "async": true
    }
  ]
}

Pre-CEO Publish Script

bash ~/system/hooks/pre-ceo-publish.sh <file>

This wraps url-linter.js with:

  • Formatted output header
  • Slack-shareable block on failure
  • Exit 0 (safe) / Exit 1 (blocked)

Make this the last step before any CEO delivery.


Retroactive Audit

To scan all existing specs for broken URLs:

node ~/system/tools/url-linter.js ~/system/specs/ --json > /tmp/url-audit.json

Or run the full retroactive audit:

bash ~/system/hooks/pre-ceo-publish.sh ~/system/specs/

Incident Context

Origin: 2026-04-24 HR-FISK incident. John published www.porezna-uprava.gov.hr/bi/Stranice/Popis-informacijskih-posrednika.aspx (NXDOMAIN) to CEO in a "VERIFIED" document. URL was copied from web-search output without curl verification.

Actual working URL: porezna-uprava.gov.hr/hr/popis-informacijskih-posrednici/8019 (HTTP 200).

Root cause: RC5 (Petter Graff audit) — citation propagation without re-verification. This tool closes that gap permanently.


ZAKON URL (exact text)

Every external URL and email domain published in a CEO-facing document must have a fresh HTTP verification within 10 minutes of document delivery. Verification method: curl -sI --max-time 10 returning 2xx or 3xx. NXDOMAIN, 4xx, or timeout = document blocked. No exceptions for "found in registry" or "from web search". Web search results are unverified input, not verified facts.