Skip to content
Merged
Show file tree
Hide file tree
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
16 changes: 16 additions & 0 deletions .github/workflows/coherence.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Coherence

on:
pull_request:
push:
branches: [main]

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: node scripts/check-coherence.mjs
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,7 @@ Non-negotiables:
(cloud/self-hosted, fresh/upgrade, existing agents, subscriptions).
- Findings rejected by Seb (`references/decisions.md`) are never
re-raised unless the platform changed again on that point.
- Every page exists twice: `<page>.mdx` at the repo root, published by
Mintlify, and `llm-gateway/docs/<page>.mdx`, its dead twin. Edit both, or
the change never reaches the site. The coherence check enforces it.
- Invariants are machine-checked: `node scripts/check-coherence.mjs` must be GREEN before any issue is filed or any docs PR is pushed. Reversing a recorded decision requires the written derivation of the reversal protocol (see the docs-sync skill).
30 changes: 30 additions & 0 deletions scripts/check-coherence.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
* must have its row in reference/environment-variables.mdx.
* 2. ERROR-CODE invariant: any M### code cited in a docs page must
* have its page at errors/M###.mdx.
* 3. TWIN invariant: every page exists twice, at the repo root and under
* llm-gateway/docs/. The root copy is the published one. The two must
* stay identical, so that a correction made in the wrong file is caught
* here instead of silently changing nothing on the site.
*
* Usage: node scripts/check-coherence.mjs
* Exit 0 = GREEN, 1 = violations (each printed with file:line).
Expand Down Expand Up @@ -100,6 +104,32 @@ for (const f of files) {
});
}

// --- Twin invariant --------------------------------------------------------
// Mintlify publishes the root copy. The copy under llm-gateway/docs/ is dead
// weight kept for now, and an edit made there alone changes nothing on the
// site, so the two must match byte for byte.
const TWIN_DIR = join(ROOT, "llm-gateway/docs");
if (existsSync(TWIN_DIR)) {
const twins = walk(TWIN_DIR).map((f) => relative(TWIN_DIR, f));
for (const page of twins) {
const root = join(ROOT, page);
if (!existsSync(root)) {
violations.push(`llm-gateway/docs/${page} has no twin at the repo root, so it is never published`);
continue;
}
if (readFileSync(root, "utf8") !== readFileSync(join(TWIN_DIR, page), "utf8")) {
violations.push(`${page} differs from llm-gateway/docs/${page} — edit both, the root copy is the published one`);
}
}
for (const f of walk(ROOT)) {
const rel = relative(ROOT, f);
if (rel.startsWith("llm-gateway/") || !rel.endsWith(".mdx")) continue;
if (!existsSync(join(TWIN_DIR, rel))) {
violations.push(`${rel} has no twin under llm-gateway/docs/`);
}
}
}

const unique = [...new Set(violations)];
if (unique.length === 0) {
console.log(`Coherence: GREEN (${files.length} pages, ${defined.size} vars defined)`);
Expand Down
Loading