Skip to content

Commit efe7234

Browse files
committed
ci: guard language-registry consistency across all surfaces
Add hooks/validate-language-registry.sh + a dogfood-gate job that fails the build when the language family disagrees across surfaces. Source of truth is languages/*.md; the guard asserts LANGUAGES.a2ml (excluding forward-looking proposed/exploratory entries), the boundary-hook LANGS list, and ECOSYSTEM.a2ml [language-repos] match it exactly, and that language-status-tracker.jl is a superset. Prevents the drift the reconciliation just fixed from recurring. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t5KLJDtqdeDi1EMNqNx6K
1 parent 524e0e3 commit efe7234

2 files changed

Lines changed: 130 additions & 1 deletion

File tree

.github/workflows/dogfood-gate.yml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,14 +257,36 @@ jobs:
257257
echo "" >> "$GITHUB_STEP_SUMMARY"
258258
echo "Verifies that no language-implementation content (proofs, grammars, compilers, specs, or a re-vendored language directory) has leaked into the coordinator. Language code lives in each \`hyperpolymath/<lang>\` repo, referenced here — not embedded." >> "$GITHUB_STEP_SUMMARY"
259259
260+
# ---------------------------------------------------------------------------
261+
# Job 5b: Language-registry consistency — every surface lists the same family
262+
# ---------------------------------------------------------------------------
263+
language-registry:
264+
name: Language registry consistency
265+
runs-on: ubuntu-latest
266+
timeout-minutes: 15
267+
268+
steps:
269+
- name: Checkout repository
270+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
271+
272+
- name: Enforce language-registry consistency
273+
run: bash hooks/validate-language-registry.sh
274+
275+
- name: Write summary
276+
if: always()
277+
run: |
278+
echo "## Language Registry Consistency" >> "$GITHUB_STEP_SUMMARY"
279+
echo "" >> "$GITHUB_STEP_SUMMARY"
280+
echo "Verifies every language appears on all registry surfaces (\`languages/\`, \`LANGUAGES.a2ml\`, the boundary-hook \`LANGS\` list, \`ECOSYSTEM.a2ml\`, and the status tracker), so the family cannot silently drift out of sync. Canonical set: \`languages/*.md\`." >> "$GITHUB_STEP_SUMMARY"
281+
260282
# ---------------------------------------------------------------------------
261283
# Job 6: Dogfooding summary
262284
# ---------------------------------------------------------------------------
263285
dogfood-summary:
264286
name: Dogfooding compliance summary
265287
runs-on: ubuntu-latest
266288
timeout-minutes: 15
267-
needs: [a2ml-validate, k9-validate, empty-lint, groove-check, coordinator-boundary]
289+
needs: [a2ml-validate, k9-validate, empty-lint, groove-check, coordinator-boundary, language-registry]
268290
if: always()
269291

270292
steps:
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: MPL-2.0
3+
# Language-registry consistency guard.
4+
#
5+
# nextgen-languages registers its language family across several surfaces. They
6+
# drifted badly before (a language present on one surface, missing on another),
7+
# so this check fails the build when they disagree.
8+
#
9+
# Source of truth: the human-facing pointers in `languages/<id>.md`. Every other
10+
# surface MUST list exactly that same set of ids:
11+
# - .machine_readable/LANGUAGES.a2ml ([[language]] stanzas; forward-looking entries with
12+
# status "proposed"/"exploratory" are exempt — they
13+
# are not yet committed and carry no languages/ pointer)
14+
# - hooks/validate-coordinator-boundary.sh (the LANGS re-vendoring guard list)
15+
# - .machine_readable/6a2/ECOSYSTEM.a2ml ([language-repos] keys)
16+
# - language-status-tracker.jl (const LANGUAGES — must be a SUPERSET;
17+
# it also tracks playgrounds/ecosystem/umbrella)
18+
#
19+
# See 0-AI-MANIFEST.a2ml and EXTRACTION-MANIFEST.md.
20+
set -euo pipefail
21+
22+
cd "$(git rev-parse --show-toplevel)"
23+
24+
ERRORS=0
25+
26+
# ── Source of truth: languages/<id>.md pointers (excluding README) ────────────
27+
family() {
28+
for f in languages/*.md; do
29+
b="$(basename "$f" .md)"
30+
[[ "$b" == "README" ]] || printf '%s\n' "$b"
31+
done | sort -u
32+
}
33+
34+
# ── LANGUAGES.a2ml: committed [[language]] ids (status not proposed/exploratory) ──
35+
a2ml_registered() {
36+
awk '
37+
/^\[\[language\]\]/ { if (id != "") print id, status; id=""; status="" ; next }
38+
/^id[ \t]*=/ { line=$0; sub(/^id[ \t]*=[ \t]*"/,"",line); sub(/".*/,"",line); id=line }
39+
/^status[ \t]*=/ { line=$0; sub(/^status[ \t]*=[ \t]*"/,"",line); sub(/".*/,"",line); status=line }
40+
END { if (id != "") print id, status }
41+
' .machine_readable/LANGUAGES.a2ml \
42+
| awk '$2 != "proposed" && $2 != "exploratory" { print $1 }' | sort -u
43+
}
44+
45+
# ── boundary-hook LANGS list ──────────────────────────────────────────────────
46+
hook_langs() {
47+
grep -E '^LANGS=' hooks/validate-coordinator-boundary.sh \
48+
| sed -E 's/^LANGS="//; s/"[[:space:]]*$//' \
49+
| tr ' ' '\n' | sed '/^$/d' | sort -u
50+
}
51+
52+
# ── ECOSYSTEM.a2ml [language-repos] keys (excluding the `note` key) ───────────
53+
ecosystem_keys() {
54+
awk '
55+
/^\[language-repos\]/ { inblk=1; next }
56+
/^\[/ { inblk=0 }
57+
inblk && /^[^#].*=/ {
58+
key=$0; sub(/[ \t]*=.*/,"",key); gsub(/["[:space:]]/,"",key)
59+
if (key != "note" && key != "") print key
60+
}
61+
' .machine_readable/6a2/ECOSYSTEM.a2ml | sort -u
62+
}
63+
64+
# ── tracker const LANGUAGES vector (quoted ids) ───────────────────────────────
65+
tracker_langs() {
66+
awk '/const LANGUAGES[ \t]*=[ \t]*\[/ { inblk=1; next } inblk && /^\]/ { inblk=0 } inblk' \
67+
language-status-tracker.jl \
68+
| grep -oE '"[^"]+"' | tr -d '"' | sort -u
69+
}
70+
71+
FAMILY="$(family)"
72+
COUNT="$(printf '%s\n' "$FAMILY" | wc -l | tr -d ' ')"
73+
printf 'language-registry: source of truth = languages/*.md (%s entries)\n' "$COUNT"
74+
75+
# report_diff <surface-name> <surface-set> <mode: exact|superset>
76+
report_diff() {
77+
local name="$1" have="$2" mode="$3" missing extra
78+
missing="$(comm -23 <(printf '%s\n' "$FAMILY") <(printf '%s\n' "$have") || true)"
79+
if [[ -n "$missing" ]]; then
80+
printf 'ERROR: %s is MISSING family languages:\n' "$name"
81+
printf '%s\n' "$missing" | sed 's/^/ - /'
82+
ERRORS=$((ERRORS + 1))
83+
fi
84+
if [[ "$mode" == "exact" ]]; then
85+
extra="$(comm -13 <(printf '%s\n' "$FAMILY") <(printf '%s\n' "$have") || true)"
86+
if [[ -n "$extra" ]]; then
87+
printf 'ERROR: %s lists languages NOT in languages/ (add a pointer, or remove them):\n' "$name"
88+
printf '%s\n' "$extra" | sed 's/^/ - /'
89+
ERRORS=$((ERRORS + 1))
90+
fi
91+
fi
92+
}
93+
94+
report_diff ".machine_readable/LANGUAGES.a2ml" "$(a2ml_registered)" exact
95+
report_diff "hooks/validate-coordinator-boundary.sh LANGS" "$(hook_langs)" exact
96+
report_diff ".machine_readable/6a2/ECOSYSTEM.a2ml" "$(ecosystem_keys)" exact
97+
report_diff "language-status-tracker.jl const LANGUAGES" "$(tracker_langs)" superset
98+
99+
if [[ "$ERRORS" -gt 0 ]]; then
100+
printf '\nlanguage-registry check FAILED (%s surface(s) out of sync).\n' "$ERRORS"
101+
printf 'Every language must appear on all registry surfaces. Update them together —\n'
102+
printf 'the canonical set is languages/*.md; see .machine_readable/LANGUAGES.a2ml.\n'
103+
exit 1
104+
fi
105+
106+
printf 'language-registry: OK (all surfaces agree on the %s-language family)\n' "$COUNT"
107+
exit 0

0 commit comments

Comments
 (0)