Skip to content

Commit 24bfeba

Browse files
authored
Merge pull request #83 from ChBrain/chore/migrate-L1-to-tests
chore: migrate L1 language validator to lingua detector
2 parents a6d55b3 + 5b6aa54 commit 24bfeba

59 files changed

Lines changed: 537 additions & 384 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.githooks/pre-commit

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,52 @@ def main() -> int:
9797
print("")
9898
return 1
9999

100+
# Language policy check: English-only content
101+
lang_result = subprocess.run(
102+
[sys.executable, "tests/validate_language.py"],
103+
capture_output=True,
104+
text=True,
105+
check=False,
106+
)
107+
if lang_result.returncode != 0:
108+
print("❌ LANGUAGE POLICY FAILED")
109+
print("")
110+
print(lang_result.stdout.strip())
111+
print("")
112+
print("Rewrite the flagged content in English.")
113+
print("Do not adjust the validator threshold.")
114+
print("")
115+
return 1
116+
117+
# Run all validators (L1-L5). CI only checks the stamp; keep GitHub costs low.
118+
validators = [
119+
[sys.executable, "tests/validate_general.py"],
120+
[sys.executable, "scripts/validate_navigation_links.py"],
121+
[sys.executable, "scripts/validate_stop_naming.py"],
122+
[sys.executable, "scripts/validate_states.py"],
123+
[sys.executable, "scripts/validate_roads.py"],
124+
[sys.executable, "scripts/validate_roads_km.py"],
125+
[sys.executable, "scripts/validate_roads_km_math.py"],
126+
]
127+
for cmd in validators:
128+
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
129+
if result.returncode != 0:
130+
label = cmd[1]
131+
print(f"❌ VALIDATION FAILED: {label}")
132+
print("")
133+
print(result.stdout.strip())
134+
print("")
135+
return 1
136+
137+
# Write validation stamp: hash of the content tree, excluding the stamp file itself
138+
# (removing stamp from index avoids a circular hash dependency).
139+
subprocess.run(["git", "rm", "--cached", "--ignore-unmatch", ".validation-stamp"],
140+
capture_output=True, check=False)
141+
tree_hash = run_git("write-tree")
142+
Path(".validation-stamp").write_text(tree_hash + "\n", encoding="utf-8")
143+
subprocess.run(["git", "add", ".validation-stamp"], check=False)
144+
print(f"+ Validation stamp written ({tree_hash[:12]})")
145+
100146
# Check if .bump-type exists
101147
if not Path(BUMP_TYPE_FILE).exists():
102148
# Check if content is being staged (besides .bump-type)

.github/copilot-instructions.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,27 @@ Examples:
137137
- ✓ Allowed: "Named after Friedrich Franz I"
138138
- ✗ Not allowed: "1949 bis 1990 verlief die Grenze..." (German sentence)
139139
- ✗ Not allowed: "Der Krieg hat die Stadt..." (German language content)
140+
- ✗ Not allowed: "## Die Region, die blieb..." (German subtitle/slogan - must be removed)
140141

141-
The validator (`scripts/validate_german_bleed.py`) detects German verb phrases and language-specific terms. It is run automatically on all PRs as part of CI validation.
142+
The validator (`tests/validate_language.py`) enforces English-only content by detecting German verb phrases and language-specific terms in body sections. It is run automatically on all PRs as part of CI validation.
142143

143144
## File standards
144145

145146
Footer in place files: `vX.Y.Z - KAI Worlds`, where `X.Y.Z` is the current world version (set by `scripts/bump_version.py` on release, or per-file by an LLM during a content edit).
147+
148+
## Validation chain of command
149+
150+
**ARCHITECTURE.md → tests/README.md → .github/workflows/validate.yml**
151+
152+
Changes flow one direction only:
153+
154+
1. **ARCHITECTURE.md** owns structure, rules, relationships. No scripts, no layer numbers.
155+
2. **tests/README.md** owns the verification contract: the 7 layers (L1-L7), gating rules, enforcement model. References ARCHITECTURE for what to check.
156+
3. **validate.yml** implements the contract: job names and `needs` chain must match tests/README exactly.
157+
158+
When touching any of these files:
159+
- A new architectural rule belongs in ARCHITECTURE.md first.
160+
- A new validation layer or check belongs in tests/README.md second.
161+
- CI jobs and gating order in validate.yml must match tests/README third.
162+
- Never put validation detail (scripts, layer numbers, enforcement rules) in ARCHITECTURE.md.
163+
- Never put architectural rules in tests/README.md or validate.yml.

.github/workflows/validate-version-bump.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,7 @@ jobs:
2323
GITHUB_EVENT_PATH: ${{ github.event_path }}
2424
run: python scripts/validate_version_bump.py
2525

26-
- name: Validate no German language bleed
27-
run: python scripts/validate_german_bleed.py
26+
- name: Validate English-only content
27+
run: |
28+
pip install -r tests/requirements.txt
29+
python tests/validate_language.py

.github/workflows/validate.yml

Lines changed: 118 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,16 @@ on:
77
branches: [main]
88

99
jobs:
10-
validate-places:
11-
name: Validate place_*.md files
10+
setup:
11+
name: CI - Detect changed files
1212
runs-on: ubuntu-latest
13+
outputs:
14+
files: ${{ steps.changed.outputs.files }}
15+
any: ${{ steps.changed.outputs.any }}
1316
steps:
1417
- uses: actions/checkout@v4
1518
with:
1619
fetch-depth: 0
17-
- name: Set up Python
18-
uses: actions/setup-python@v5
19-
with:
20-
python-version: '3.11'
2120
- name: Get changed place_*.md files
2221
id: changed
2322
run: |
@@ -30,11 +29,7 @@ jobs:
3029
fi
3130
RANGE="$BEFORE..HEAD"
3231
fi
33-
# --diff-filter=ACMRT skips deletions; the validator must not be handed paths that no longer exist.
3432
RAW=$(git diff --name-only --diff-filter=ACMRT "$RANGE" | { grep 'place_.*\.md$' || true; })
35-
# When a road index file changes, expand to its sibling nodes - their
36-
# km math depends on the index. Without this, an index-only edit
37-
# would leave node files unvalidated against the new chain.
3833
EXPANDED="$RAW"
3934
for f in $RAW; do
4035
case "$f" in
@@ -49,19 +44,119 @@ jobs:
4944
esac
5045
done
5146
FILES=$(printf '%s\n' $EXPANDED | sort -u | tr '\n' ' ' | sed 's/ *$//')
52-
echo "files=$FILES" >> "$GITHUB_OUTPUT"
53-
- name: Validate changed files
54-
if: steps.changed.outputs.files != ''
55-
run: python3 scripts/validate.py ${{ steps.changed.outputs.files }}
56-
- name: Show validator results
57-
if: steps.changed.outputs.files != ''
58-
run: python3 scripts/validate.py --checks ${{ steps.changed.outputs.files }}
59-
- name: No place_*.md files changed
60-
if: steps.changed.outputs.files == ''
61-
run: echo "No place_*.md files changed - skipping validation"
47+
if [ -n "$FILES" ]; then
48+
echo "files=$FILES" >> "$GITHUB_OUTPUT"
49+
echo "any=true" >> "$GITHUB_OUTPUT"
50+
else
51+
echo "files=" >> "$GITHUB_OUTPUT"
52+
echo "any=false" >> "$GITHUB_OUTPUT"
53+
fi
6254
63-
deploy-check:
64-
name: Check deploy bundles
55+
L0-stamp-check:
56+
name: L0 - Validation stamp
57+
needs: setup
58+
if: needs.setup.outputs.any == 'true'
59+
runs-on: ubuntu-latest
60+
steps:
61+
- uses: actions/checkout@v4
62+
with:
63+
fetch-depth: 0
64+
- name: Verify local validation was run
65+
run: |
66+
if [ ! -f ".validation-stamp" ]; then
67+
echo "❌ No .validation-stamp found."
68+
echo ""
69+
echo "Install the pre-commit hook and run locally first:"
70+
echo " git config core.hooksPath .githooks"
71+
exit 1
72+
fi
73+
STAMP=$(cat .validation-stamp | tr -d '[:space:]')
74+
# Recompute content tree without the stamp file (mirrors pre-commit hook logic)
75+
CONTENT_TREE=$(git ls-tree HEAD | grep -v '\.validation-stamp' | git mktree)
76+
if [ "$STAMP" != "$CONTENT_TREE" ]; then
77+
echo "❌ Validation stamp is stale."
78+
echo " Stamp: $STAMP"
79+
echo " Content tree: $CONTENT_TREE"
80+
echo ""
81+
echo "Commit via the pre-commit hook to refresh the stamp."
82+
exit 1
83+
fi
84+
echo "✓ Validation stamp matches content tree ($CONTENT_TREE)"
85+
86+
L1-all-files:
87+
name: L1 - Encoding, forbidden chars, english-only
88+
needs: [setup, L0-stamp-check]
89+
if: needs.setup.outputs.any == 'true'
90+
runs-on: ubuntu-latest
91+
steps:
92+
- uses: actions/checkout@v4
93+
- uses: actions/setup-python@v5
94+
with:
95+
python-version: '3.11'
96+
- run: pip install -r tests/requirements.txt
97+
- run: python3 tests/validate_general.py ${{ needs.setup.outputs.files }}
98+
- run: python3 tests/validate_language.py ${{ needs.setup.outputs.files }}
99+
100+
L2-typed-files:
101+
name: L2 - Section structure and navigation links
102+
needs: [setup, L1-all-files]
103+
if: needs.setup.outputs.any == 'true'
104+
runs-on: ubuntu-latest
105+
steps:
106+
- uses: actions/checkout@v4
107+
- uses: actions/setup-python@v5
108+
with:
109+
python-version: '3.11'
110+
- run: python3 scripts/validate_navigation_links.py ${{ needs.setup.outputs.files }}
111+
- run: python3 scripts/validate_stop_naming.py ${{ needs.setup.outputs.files }}
112+
113+
L3-state-files:
114+
name: L3 - State file rules
115+
needs: [setup, L2-typed-files]
116+
if: needs.setup.outputs.any == 'true'
117+
runs-on: ubuntu-latest
118+
steps:
119+
- uses: actions/checkout@v4
120+
- uses: actions/setup-python@v5
121+
with:
122+
python-version: '3.11'
123+
- run: python3 scripts/validate_states.py ${{ needs.setup.outputs.files }}
124+
125+
L4-road-files:
126+
name: L4 - Road index and km chain
127+
needs: [setup, L2-typed-files]
128+
if: needs.setup.outputs.any == 'true'
129+
runs-on: ubuntu-latest
130+
steps:
131+
- uses: actions/checkout@v4
132+
- uses: actions/setup-python@v5
133+
with:
134+
python-version: '3.11'
135+
- run: python3 scripts/validate_roads.py ${{ needs.setup.outputs.files }}
136+
- run: python3 scripts/validate_roads_km.py ${{ needs.setup.outputs.files }}
137+
138+
L5-node-files:
139+
name: L5 - Node km math
140+
needs: [setup, L2-typed-files]
141+
if: needs.setup.outputs.any == 'true'
142+
runs-on: ubuntu-latest
143+
steps:
144+
- uses: actions/checkout@v4
145+
- uses: actions/setup-python@v5
146+
with:
147+
python-version: '3.11'
148+
- run: python3 scripts/validate_roads_km_math.py ${{ needs.setup.outputs.files }}
149+
150+
no-changes:
151+
name: CI - No changes to validate
152+
needs: setup
153+
if: needs.setup.outputs.any != 'true'
154+
runs-on: ubuntu-latest
155+
steps:
156+
- run: echo "No place_*.md files changed - skipping validation"
157+
158+
L6-deploy-bundles:
159+
name: L6 - Deploy bundle integrity
65160
runs-on: ubuntu-latest
66161
steps:
67162
- uses: actions/checkout@v4
@@ -71,3 +166,4 @@ jobs:
71166
python-version: '3.11'
72167
- name: Check autobahn.md bundle integrity
73168
run: python3 scripts/deploy.py --check autobahn.md
169+

.validation-stamp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
8f1b4a9512847aa069d99bad012a8ff3abc8be30

ARCHITECTURE.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ Each file has a `## Owner` block. It anchors the file to the world and identifie
2020

2121
---
2222

23-
### Sections
23+
### Language
2424

25-
Every file contains the following sections in this fixed order: `Owner`, `Shown`, `Holds`, `Offers`, `Withheld`. No section may be omitted or reordered.
25+
All content is written in English. German proper nouns (Eigennamen) - place names, road names, personal names - are allowed and expected. German sentences, phrases, and language-specific terminology are not.
26+
27+
The language validator (`tests/validate_language.py`) enforces this. If it fires, rewrite the content in English. Do not adjust the validator threshold.
2628

2729
---
2830

ministry/PLAN.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# REMEMBER
1+
# Ministry
22
*Rubble -> Roads -> Regression*
33

44
---
55

66
## Deployment
77

8-
REMEMBER is a content module for the Autobahn world. It has no engine of its own - it runs on the Autobahn engine already deployed in `ChBrain/Autobahn`.
8+
Ministry is a content module for the Autobahn world. It has no engine of its own - it runs on the Autobahn engine already deployed in `ChBrain/Autobahn`.
99

1010
### Folder Structure
1111

@@ -167,4 +167,4 @@ Status: DONE / IN PROGRESS / OPEN
167167
---
168168

169169
*PLAN.md*
170-
*v0.2.0 - Autobahn - REMEMBER*
170+
*v0.2.0 - Autobahn - Ministry*

ministry/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# REMEMBER
1+
# Ministry
22
*Rubble - Roads - Regression*
33

4-
REMEMBER is a content module for the Autobahn world. It runs on the Autobahn engine. It inherits instructions and stack.
4+
Ministry is a content module for the Autobahn world. It runs on the Autobahn engine. It inherits instructions and stack.
55

6-
REMEMBER documents the tenure of every federal transport minister in BRD and DDR history, from 1949 to the present. 28 personas. 27 pieces. 7 positions. 5 places. 1 process.
6+
Ministry documents the tenure of every federal transport minister in BRD and DDR history, from 1949 to the present. 28 personas. 27 pieces. 7 positions. 5 places. 1 process.
77

88
The world exists so the decisions that built and broke the German transport network are not forgotten.
99

@@ -33,8 +33,8 @@ The world exists so the decisions that built and broke the German transport netw
3333

3434
## Downloads
3535

36-
- [remember.zip](https://github.com/ChBrain/Autobahn/releases/latest/download/remember.zip) - standalone module
37-
- [remember.pdf](https://github.com/ChBrain/Autobahn/releases/latest/download/remember.pdf) - documentation
36+
- [Ministry.zip](https://github.com/ChBrain/Autobahn/releases/latest/download/Ministry.zip) - standalone module
37+
- [Ministry.pdf](https://github.com/ChBrain/Autobahn/releases/latest/download/Ministry.pdf) - documentation
3838

3939
---
4040

ministry/REFERENCES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# REFERENCES
2-
*REMEMBER - KAI Worlds*
2+
*Ministry - KAI Worlds*
33

44
---
55

ministry/engine/instructions.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# REMEMBER
1+
# Ministry
22

33
---
44

@@ -14,7 +14,7 @@ A minister is always speaking.
1414

1515
I select the minister whose tenure owns the moment. Their name stands before their words. Everything is referred back to their tenure — the tenure is the lens, not the limit.
1616

17-
Before responding: search the REMEMBER knowledge base. Every minister's decisions are documented in their pieces. What is withheld is what the successor had to discover in the briefing files.
17+
Before responding: search the Ministry knowledge base. Every minister's decisions are documented in their pieces. What is withheld is what the successor had to discover in the briefing files.
1818

1919
---
2020

@@ -30,7 +30,7 @@ The Federal Motorway Network is the cumulative result of 28 ministerial tenures
3030

3131
The network does not know which era it is in. It is always under construction.
3232

33-
Any question about transport policy, ministerial history, infrastructure decisions, or network governance belongs to REMEMBER. Search before answering.
33+
Any question about transport policy, ministerial history, infrastructure decisions, or network governance belongs to Ministry. Search before answering.
3434

3535
---
3636

0 commit comments

Comments
 (0)