mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-20 06:02:14 +03:00
docs: sync all documentation to v3.8.24 + count-guard & wiki/prose CI (#3804)
Integrated into release/v3.8.26 — reconciled: kept #3900's superset wiki-sync, dropped the superseded sync-wiki-home; counts taken from the newer canonical release docs; net-new value = vale/markdownlint docs-lint infra + 7 new docs (Notion/Obsidian/Plugin-Marketplace/Cost-Tracking/Free-Provider-Rankings/Feature-Flags/Egress-Policy) + check-docs-counts-sync guard.
This commit is contained in:
committed by
GitHub
parent
bed3984ac1
commit
e697538beb
@@ -1,14 +1,26 @@
|
||||
#!/usr/bin/env node
|
||||
// Validates that count-based assertions in docs match the actual code state.
|
||||
// Examples checked:
|
||||
// - executors count in open-sse/executors/
|
||||
// - routing strategies in src/shared/constants/routingStrategies.ts
|
||||
// - OAuth providers in src/lib/oauth/providers/
|
||||
// - A2A skills in src/lib/a2a/skills/
|
||||
// - Cloud agents in src/lib/cloudAgent/agents/
|
||||
//
|
||||
// Exits 0 on success, 1 on detected drift.
|
||||
// Two tiers of checks:
|
||||
// • STRICT (always blocking — exit 1 on drift): high-confidence, slow-moving counts
|
||||
// that historically caused the worst drift across README / AGENTS / docs.
|
||||
// - provider count (source of truth: docs/reference/PROVIDER_REFERENCE.md total,
|
||||
// which is auto-generated from src/shared/constants/providers.ts)
|
||||
// - i18n locale count (source of truth: config/i18n.json `locales`)
|
||||
// • SOFT (heuristic — only fails with --strict): file-count based assertions that can
|
||||
// false-positive.
|
||||
// - executors count in open-sse/executors/
|
||||
// - routing strategies in src/shared/constants/routingStrategies.ts
|
||||
// - OAuth providers in src/lib/oauth/providers/
|
||||
// - A2A skills in src/lib/a2a/skills/
|
||||
// - Cloud agents in src/lib/cloudAgent/agents/
|
||||
//
|
||||
// Exits 0 on success, 1 on STRICT drift (or any drift with --strict).
|
||||
// Run: node scripts/check/check-docs-counts-sync.mjs
|
||||
//
|
||||
// NOTE: the provider check trusts PROVIDER_REFERENCE.md as the canonical total. If a
|
||||
// provider is added to the code but the reference is not regenerated, this guard will
|
||||
// not catch it — regenerate with `npm run gen:provider-reference` before relying on it.
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
@@ -48,68 +60,144 @@ function countRoutingStrategies() {
|
||||
return (m[1].match(/"[^"]+"/g) || []).length;
|
||||
}
|
||||
|
||||
function docContains(docPath, needle) {
|
||||
const abs = path.join(ROOT, "docs", docPath);
|
||||
if (!fs.existsSync(abs)) return false;
|
||||
return fs.readFileSync(abs, "utf8").includes(needle);
|
||||
// PURE: parse the canonical provider total out of the auto-generated catalog text.
|
||||
export function parseProviderTotal(referenceText) {
|
||||
if (!referenceText) return 0;
|
||||
const m = referenceText.match(/Total providers:\s*\*\*(\d+)\*\*/);
|
||||
return m ? Number(m[1]) : 0;
|
||||
}
|
||||
|
||||
const checks = [
|
||||
{
|
||||
label: "Executors count",
|
||||
actual: countFiles("open-sse/executors"),
|
||||
docKey: "executors",
|
||||
docs: ["architecture/ARCHITECTURE.md", "architecture/CODEBASE_DOCUMENTATION.md"],
|
||||
},
|
||||
{
|
||||
label: "Routing strategies count",
|
||||
actual: countRoutingStrategies(),
|
||||
docKey: "strategies",
|
||||
docs: ["routing/AUTO-COMBO.md", "architecture/RESILIENCE_GUIDE.md"],
|
||||
},
|
||||
{
|
||||
label: "OAuth providers count",
|
||||
actual: countFiles("src/lib/oauth/providers"),
|
||||
docKey: "OAuth providers",
|
||||
docs: ["architecture/ARCHITECTURE.md"],
|
||||
},
|
||||
{
|
||||
label: "A2A skills count",
|
||||
actual: countFiles("src/lib/a2a/skills"),
|
||||
docKey: "A2A skills",
|
||||
docs: ["frameworks/A2A-SERVER.md"],
|
||||
},
|
||||
{
|
||||
label: "Cloud agents count",
|
||||
actual: countFiles("src/lib/cloudAgent/agents"),
|
||||
docKey: "cloud agents",
|
||||
docs: ["frameworks/CLOUD_AGENT.md", "frameworks/AGENT_PROTOCOLS_GUIDE.md"],
|
||||
},
|
||||
];
|
||||
// STRICT: canonical provider total, read from the auto-generated catalog.
|
||||
export function readProviderTotal() {
|
||||
const abs = path.join(ROOT, "docs", "reference", "PROVIDER_REFERENCE.md");
|
||||
if (!fs.existsSync(abs)) return 0;
|
||||
return parseProviderTotal(fs.readFileSync(abs, "utf8"));
|
||||
}
|
||||
|
||||
let drift = 0;
|
||||
console.log("Docs counts sync report");
|
||||
console.log("=======================");
|
||||
|
||||
for (const c of checks) {
|
||||
console.log(`\n• ${c.label}: ${c.actual} (real)`);
|
||||
for (const doc of c.docs) {
|
||||
const found = docContains(doc, String(c.actual));
|
||||
if (found) {
|
||||
console.log(` ✓ docs/${doc} mentions "${c.actual}"`);
|
||||
} else {
|
||||
console.log(` ⚠ docs/${doc} does NOT mention "${c.actual}" for ${c.docKey}`);
|
||||
drift++;
|
||||
}
|
||||
// STRICT: canonical i18n locale count, read from the shared config.
|
||||
export function countLocales() {
|
||||
const abs = path.join(ROOT, "config", "i18n.json");
|
||||
if (!fs.existsSync(abs)) return 0;
|
||||
try {
|
||||
const cfg = JSON.parse(fs.readFileSync(abs, "utf8"));
|
||||
return Array.isArray(cfg.locales) ? cfg.locales.length : 0;
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
console.log();
|
||||
if (drift > 0) {
|
||||
console.warn(`⚠ ${drift} potential drift(s) detected. Review the docs above.`);
|
||||
// Soft-fail by default (count-based heuristic can false-positive).
|
||||
// To enforce, pass --strict.
|
||||
if (process.argv.includes("--strict")) process.exit(1);
|
||||
} else {
|
||||
console.log("✓ All checks pass.");
|
||||
// PURE: tally STRICT vs SOFT drift for a list of checks, given a content lookup.
|
||||
// `getContent(file) -> string | null`. A check whose `actual` is 0 is skipped (the
|
||||
// source count could not be determined). Returns { strict, soft, lines }.
|
||||
export function tallyDrift(checks, getContent) {
|
||||
let strict = 0;
|
||||
let soft = 0;
|
||||
const lines = [];
|
||||
for (const c of checks) {
|
||||
const tier = c.strict ? "STRICT" : "soft";
|
||||
lines.push(`\n• ${c.label}: ${c.actual} (real) [${tier}]`);
|
||||
if (!c.actual) {
|
||||
lines.push(` ⚠ could not determine ${c.docKey} count from source — skipping`);
|
||||
continue;
|
||||
}
|
||||
for (const f of c.files) {
|
||||
const content = getContent(f);
|
||||
const found = content != null && content.includes(String(c.actual));
|
||||
if (found) {
|
||||
lines.push(` ✓ ${f} mentions "${c.actual}"`);
|
||||
} else {
|
||||
lines.push(` ${c.strict ? "✗" : "⚠"} ${f} does NOT mention "${c.actual}" for ${c.docKey}`);
|
||||
if (c.strict) strict++;
|
||||
else soft++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { strict, soft, lines };
|
||||
}
|
||||
|
||||
export function buildChecks() {
|
||||
return [
|
||||
{
|
||||
label: "Provider count",
|
||||
actual: readProviderTotal(),
|
||||
docKey: "providers",
|
||||
strict: true,
|
||||
files: ["README.md", "AGENTS.md", "CLAUDE.md"],
|
||||
},
|
||||
{
|
||||
label: "i18n locales count",
|
||||
actual: countLocales(),
|
||||
docKey: "i18n locales",
|
||||
strict: true,
|
||||
files: ["docs/README.md", "docs/guides/I18N.md", "AGENTS.md"],
|
||||
},
|
||||
{
|
||||
label: "Executors count",
|
||||
actual: countFiles("open-sse/executors"),
|
||||
docKey: "executors",
|
||||
strict: false,
|
||||
files: ["docs/architecture/ARCHITECTURE.md", "docs/architecture/CODEBASE_DOCUMENTATION.md"],
|
||||
},
|
||||
{
|
||||
label: "Routing strategies count",
|
||||
actual: countRoutingStrategies(),
|
||||
docKey: "strategies",
|
||||
strict: false,
|
||||
files: ["docs/routing/AUTO-COMBO.md", "docs/architecture/RESILIENCE_GUIDE.md"],
|
||||
},
|
||||
{
|
||||
label: "OAuth providers count",
|
||||
actual: countFiles("src/lib/oauth/providers"),
|
||||
docKey: "OAuth providers",
|
||||
strict: false,
|
||||
files: ["docs/architecture/ARCHITECTURE.md"],
|
||||
},
|
||||
{
|
||||
label: "A2A skills count",
|
||||
actual: countFiles("src/lib/a2a/skills"),
|
||||
docKey: "A2A skills",
|
||||
strict: false,
|
||||
files: ["docs/frameworks/A2A-SERVER.md"],
|
||||
},
|
||||
{
|
||||
label: "Cloud agents count",
|
||||
actual: countFiles("src/lib/cloudAgent/agents"),
|
||||
docKey: "cloud agents",
|
||||
strict: false,
|
||||
files: ["docs/frameworks/CLOUD_AGENT.md", "docs/frameworks/AGENT_PROTOCOLS_GUIDE.md"],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function main() {
|
||||
const checks = buildChecks();
|
||||
const getContent = (relPath) => {
|
||||
const abs = path.join(ROOT, relPath);
|
||||
return fs.existsSync(abs) ? fs.readFileSync(abs, "utf8") : null;
|
||||
};
|
||||
|
||||
console.log("Docs counts sync report");
|
||||
console.log("=======================");
|
||||
const { strict, soft, lines } = tallyDrift(checks, getContent);
|
||||
for (const l of lines) console.log(l);
|
||||
|
||||
console.log();
|
||||
if (strict > 0) {
|
||||
console.error(
|
||||
`✗ ${strict} STRICT drift(s) detected. ` +
|
||||
`Update the docs above to the real counts, or regenerate auto-generated sources ` +
|
||||
`(npm run gen:provider-reference).`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
if (soft > 0) {
|
||||
console.warn(`⚠ ${soft} potential (soft) drift(s) detected. Review the docs above.`);
|
||||
if (process.argv.includes("--strict")) process.exit(1);
|
||||
} else {
|
||||
console.log("✓ All checks pass.");
|
||||
}
|
||||
}
|
||||
|
||||
const invokedDirectly =
|
||||
process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
||||
if (invokedDirectly) main();
|
||||
|
||||
Reference in New Issue
Block a user