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
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# #91 — fails when a link into the engine repo names a branch instead of the
# ref the build resolved. Its own step so the failure is legible in Checks
# rather than buried in astro's output, same reasoning as the fetch step.
- name: Engine links are pinned, not on a branch
run: npm run check:engine-refs

- name: Type check
run: npm run check

Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
"dev": "npm run fetch && astro dev",
"build": "npm run fetch && astro build",
"preview": "astro preview",
"check": "astro check",
"check": "node scripts/check-engine-refs.mjs && astro check",
"check:ai-crawlers": "node scripts/check-ai-crawlers.mjs",
"og": "node scripts/generate-og.mjs"
"og": "node scripts/generate-og.mjs",
"check:engine-refs": "node scripts/check-engine-refs.mjs"
},
"dependencies": {
"@astrojs/rss": "^4.0.19",
Expand Down
82 changes: 82 additions & 0 deletions scripts/check-engine-refs.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* Fails when a link into the keel repository names a branch instead of the ref
* this build resolved (#91).
*
* #85 pinned the docs pipeline to the published release tag; #91 did the same
* for hand-written links, via `src/lib/engine-url.ts`. Neither is self-keeping:
* the next person to paste a GitHub URL into a component or a copy file
* reintroduces the skew, and nothing would notice.
*
* This is that guard, and it exists because a review pointed out the PR
* claiming it already did. `grep blob/main` was the sentence; it was never
* wired to anything, and it would not have caught two forms this repo already
* contained — `tree/main/packages` and `raw.githubusercontent.../keel/main/...`.
* So the pattern here matches any engine URL carrying a branch name, in any of
* the shapes GitHub serves.
*
* ## The two allowlisted exceptions, and why each is correct
*
* `scripts/install.sh` — deliberately NOT pinnable. The file does not exist at
* v0.11.2 (`git cat-file -e v0.11.2:scripts/install.sh` fails); it lives on the
* default branch only. Pinning it would turn the install page's primary command
* into a 404. The install page says so in its own copy.
*
* `src/content/` — fetched release-note prose, gitignored, written by
* scripts/fetch-release.mjs. Those links are quotes from releases as published;
* rewriting them would edit what a release said.
*/
import { readdirSync, readFileSync, statSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join, relative } from "node:path";

const root = dirname(dirname(fileURLToPath(import.meta.url)));
const SRC = join(root, "src");

/** Any keel URL whose ref position holds a branch-shaped name rather than a tag. */
const BRANCHY = /CodeGateSoftware\/keel\/(?:blob|tree|raw)\/(main|master|HEAD)\//g;
/** raw.githubusercontent.com puts the ref straight after the repo, with no verb. */
const RAW = /raw\.githubusercontent\.com\/CodeGateSoftware\/keel\/(main|master|HEAD)\//g;

const ALLOW = [
{ path: "src/components/pages/InstallPage.astro", why: "scripts/install.sh exists only on the default branch (#91)" },
{ path: "src/content/", why: "fetched release-note quotes — the site must not rewrite what a release said" },
];

const allowed = (rel) => ALLOW.some((a) => rel === a.path || rel.startsWith(a.path));

function* walk(dir) {
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (statSync(full).isDirectory()) yield* walk(full);
else yield full;
}
}

const findings = [];
for (const file of walk(SRC)) {
const rel = relative(root, file);
if (allowed(rel)) continue;
const text = readFileSync(file, "utf8");
for (const re of [BRANCHY, RAW]) {
re.lastIndex = 0;
let m;
while ((m = re.exec(text)) !== null) {
const line = text.slice(0, m.index).split("\n").length;
findings.push(`${rel}:${line} — engine link pinned to '${m[1]}': ${m[0]}`);
}
}
}

if (findings.length > 0) {
console.error(`\nFAIL: ${findings.length} engine link(s) name a branch instead of the resolved ref:\n`);
for (const f of findings) console.error(` - ${f}`);
console.error(
"\nBuild the URL with `engineSourceUrl` from src/lib/engine-url.ts, which pins to the ref\n" +
"in data/docs-meta.json. A branch link invites a reader to verify a release against code\n" +
"that is not in it (#91). If the target genuinely only exists on the default branch, add it\n" +
"to ALLOW in this file with the reason.\n",
);
process.exit(1);
}

console.log(`ok: no engine link names a branch (${ALLOW.length} documented exception(s))`);
6 changes: 4 additions & 2 deletions src/components/Footer.astro
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ENGINE_DISCUSSIONS_URL,
} from "../i18n/config";
import { home } from "../i18n/pages/home";
import { engineBlobUrl } from "../lib/engine-url";

interface Props {
locale: Locale;
Expand All @@ -16,8 +17,9 @@ const { locale } = Astro.props;
const chrome = t(locale);
const c = home[locale];

const honestExperiment =
"https://github.com/CodeGateSoftware/keel/blob/main/docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md";
const honestExperiment = engineBlobUrl(
"docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
);

const editionLinks = [
{ label: "English", locale: "en" as Locale },
Expand Down
5 changes: 4 additions & 1 deletion src/components/pages/ComparePage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import HonestBox from "../HonestBox.astro";
import { compare, compareColumns, compareRows } from "../../i18n/pages/compare";
import { home } from "../../i18n/pages/home";
import { alternatesFor, localePath, type Locale } from "../../i18n/config";
import { engineBlobUrl } from "../../lib/engine-url";

interface Props {
locale: Locale;
Expand All @@ -23,7 +24,9 @@ const honestLinks = [
: locale === "fr"
? "Le compte rendu de l'expérience"
: "The experiment record",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
href: engineBlobUrl(
"docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
),
},
];
---
Expand Down
7 changes: 5 additions & 2 deletions src/components/pages/CompliancePage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Base from "../../layouts/Base.astro";
import HonestBox from "../HonestBox.astro";
import { compliance } from "../../i18n/pages/compliance";
import { alternatesFor, localePath, type Locale } from "../../i18n/config";
import { engineBlobUrl, engineSourceUrl } from "../../lib/engine-url";
import { home } from "../../i18n/pages/home";

interface Props {
Expand All @@ -21,7 +22,9 @@ const honestLinks = [
: locale === "fr"
? "Le compte rendu de l'expérience"
: "The experiment record",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
href: engineBlobUrl(
"docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
),
},
{
label:
Expand Down Expand Up @@ -86,7 +89,7 @@ const honestLinks = [
c.links.items.map((link) => (
<li>
<h3>
<a href={link.href} lang={link.href.includes("#scholarly") ? undefined : "en"}>
<a href={engineSourceUrl(link)} lang={link.hash === "#scholarly-review-status" ? undefined : "en"}>
{link.label}
</a>
</h3>
Expand Down
3 changes: 2 additions & 1 deletion src/components/pages/FeaturesPage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Base from "../../layouts/Base.astro";
import ForOperators from "../ForOperators.astro";
import { features } from "../../i18n/pages/features";
import { alternatesFor, localePath, type Locale } from "../../i18n/config";
import { engineSourceUrl } from "../../lib/engine-url";

interface Props {
locale: Locale;
Expand Down Expand Up @@ -40,7 +41,7 @@ const c = features[locale];
</ul>
)}
<p class="verify">
{c.verifyNote}{locale === "fr" ? "\u00A0:" : ":"} <a href={feature.verify.href} class="mono">{feature.verify.label}</a>
{c.verifyNote}{locale === "fr" ? "\u00A0:" : ":"} <a href={engineSourceUrl(feature.verify)} class="mono">{feature.verify.label}</a>
</p>
</section>
))
Expand Down
5 changes: 4 additions & 1 deletion src/components/pages/HomePage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ENGINE_URL,
} from "../../i18n/config";
import { t } from "../../i18n/ui";
import { engineBlobUrl } from "../../lib/engine-url";

interface Props {
locale: Locale;
Expand Down Expand Up @@ -67,7 +68,9 @@ const nextItems = c.next.items.map((item, index) => ({ ...item, href: nextTarget
const honestLinks = [
{
label: c.honest.experimentLabel,
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
href: engineBlobUrl(
"docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
),
},
{ label: c.honest.announcementLabel, href: "https://github.com/CodeGateSoftware/keel/discussions/304" },
];
Expand Down
3 changes: 2 additions & 1 deletion src/components/pages/InstallPage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Base from "../../layouts/Base.astro";
import ForOperators from "../ForOperators.astro";
import CodeBlock from "../CodeBlock.astro";
import { readDataFile } from "../docs/nav";
import { engineBlobUrl } from "../../lib/engine-url";
import { install } from "../../i18n/pages/install";
import { alternatesFor, formatDate, localePath, type Locale } from "../../i18n/config";
import { t } from "../../i18n/ui";
Expand Down Expand Up @@ -252,7 +253,7 @@ const fromSourceCommands = [
<ul>{c.unsigned.notMeaning.map((item) => <li>{item}</li>)}</ul>

<p>
<a href="https://github.com/CodeGateSoftware/keel/blob/main/docs/desktop-install.md">
<a href={engineBlobUrl("docs/desktop-install.md")}>
{c.unsigned.more} →
</a>
</p>
Expand Down
32 changes: 19 additions & 13 deletions src/i18n/pages/compliance.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { LocalizedPage } from "../config";
import type { EngineSource } from "../../lib/engine-url";

/**
* Compliance (FR-2): the Shariah methodology in plain terms — what keel
Expand All @@ -15,7 +16,9 @@ export interface ComplianceContent {
doesNot: { title: string; items: string[] };
framing: { title: string; body: string[] };
attest: { title: string; body: string[]; command: string; commandNote: string };
links: { title: string; items: { label: string; note: string; href: string }[] };
/** Primary sources in the engine repo. Copy carries the repo-relative path;
* CompliancePage builds the URL at the ref this build resolved (#91). */
links: { title: string; items: (EngineSource & { label: string; note: string })[] };
translatedFromRev?: string;
}

Expand Down Expand Up @@ -96,22 +99,23 @@ export const compliance: LocalizedPage<ComplianceContent> = {
{
label: "The fiqh basis (docs/fiqh-basis.md)",
note: "The Shariah reasoning keel encodes, ruling by ruling, each with its in-repo source.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/fiqh-basis.md",
path: "docs/fiqh-basis.md",
},
{
label: "Scholarly review status",
note: "What a scholarly review would cover, what it would and would not signify — and the standing status: not reviewed.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/fiqh-basis.md#scholarly-review-status",
path: "docs/fiqh-basis.md",
hash: "#scholarly-review-status",
},
{
label: "The honest result — experiment record",
note: "No shipped rule family is net-positive at the taker fee actually paid; every number stated.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
path: "docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
},
{
label: "The glossary (docs/glossary.md)",
note: "The single source for the vocabulary — fiqh terms are anchored, never authored.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/glossary.md",
path: "docs/glossary.md",
},
],
},
Expand Down Expand Up @@ -194,22 +198,23 @@ export const compliance: LocalizedPage<ComplianceContent> = {
{
label: "الأساس الفقهي (docs/fiqh-basis.md)",
note: "الاستدلال الشرعي الذي يُشفّره كيل، حكمًا حكمًا، مع مصدر كلِّ حكمٍ داخل المستودع.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/fiqh-basis.md",
path: "docs/fiqh-basis.md",
},
{
label: "حالة المراجعة العلمية",
note: "ما ستغطّيه المراجعة العلمية، وما تعنيه وما لا تعنيه — والحالة القائمة: غير مُراجَع.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/fiqh-basis.md#scholarly-review-status",
path: "docs/fiqh-basis.md",
hash: "#scholarly-review-status",
},
{
label: "النتيجة الصادقة — سجلّ التجربة",
note: "لا تحقّق أيُّ عائلةٍ من القواعد المُصدَّرة ربحًا صافيًا عند رسوم الآخذ الفعلية؛ وكلُّ الأرقام مذكورة.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
path: "docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
},
{
label: "المسرد (docs/glossary.md)",
note: "المصدر الوحيد للمصطلحات — والمصطلحات الفقهية مُسنَدةٌ إلى مصادرها، لا مؤلَّفة.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/glossary.md",
path: "docs/glossary.md",
},
],
},
Expand Down Expand Up @@ -292,22 +297,23 @@ export const compliance: LocalizedPage<ComplianceContent> = {
{
label: "La base fiqh (docs/fiqh-basis.md)",
note: "Le raisonnement Shariah que keel encode, règle par règle, avec sa source dans le dépôt.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/fiqh-basis.md",
path: "docs/fiqh-basis.md",
},
{
label: "Le statut de l'examen par des savants",
note: "Ce que couvrirait un examen par des savants, ce qu'il signifierait ou non — et le statut actuel : non examinée.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/fiqh-basis.md#scholarly-review-status",
path: "docs/fiqh-basis.md",
hash: "#scholarly-review-status",
},
{
label: "Le résultat honnête — le compte rendu de l'expérience",
note: "Aucune famille de règles livrée ne dégage un résultat net positif aux frais de preneur réellement payés ; tous les chiffres sont donnés.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
path: "docs/experiments/2026-08-13-restated-under-a-production-faithful-engine.md",
},
{
label: "Le glossaire (docs/glossary.md)",
note: "L'unique source du vocabulaire — les termes du fiqh y sont ancrés, jamais inventés.",
href: "https://github.com/CodeGateSoftware/keel/blob/main/docs/glossary.md",
path: "docs/glossary.md",
},
],
},
Expand Down
Loading
Loading