culture(germany): migrate hofstede_bag -> khai_lexicon (v1 -> v2 schema)#567
Conversation
Mechanical rename, framework unchanged (Hofstede 6D, ±5 band). The
new schema names Hofstede as one possible marker_set: so other
measurement frameworks can plug into the same lexicon shape later.
Files:
- hofstede_bag.yaml -> khai_lexicon.yaml
- hofstede_decisions.md -> khai_lexicon_decisions.md
- culture_german_position.md frontmatter: hofstede: aggregate
-> markers: aggregate
Schema deltas in the YAML:
- country: germany + parent: null -> subject: germany + region: europe
+ subregion: germany
- hofstede_scores: -> marker_scores:
- new keys: khai_lexicon_version: 1, marker_set: hofstede
Scores unchanged: PDI 35, IDV 67, UAI 65, MAS 66, LTO 83, IND 40.
Brownfield score-validity check (per the "if you touch, you verify"
contract): re-read culture_german_position.md against marker_scores.
test_marker_derived.py::test_derived_within_tolerance[germany] passes
-- the position file's voice still hits target within ±5 across all
six dimensions. Outcome recorded in khai_lexicon_decisions.md under
"v1 -> v2 schema migration".
Unblocks PR #564 (DACH baking on culture/europe): once Austria and
Switzerland are migrated the same way, the brownfield R3 gate stops
firing on the three position files there.
khai_tests.review on this PR
|
khai-tests==0.1.19 requires every khai file to open with a ## Title chapter (the "TO pair": Title + Owner). engine/wow position files already carry it; existing culture position files don't, which fails khai - Position rules on every PR that modifies one. Adds: ## Title German after the L1 "# Position: German" and before "## Owner". Verification: - khai_tests.components.test_khai_position: 6/6 pass - test_marker_derived.py[germany]: 2/2 pass (text addition does not fire any bag word; scores remain in the ±5 band) - validate_pr_brownfield.py: still clean The other 13 unmigrated cultures will need the same chapter when they migrate. Austria and Switzerland get it as part of their migrations (this PR's pattern).
CI gap closed — Position rules
Fix ( ## Title
Germanbetween the L1 Verification
Learning carried to Austria + SwitzerlandWhen their migration PRs land, they get the same chapter ( Notes for the broader fleetThis pattern ( Generated by Claude Code |
khai_tests.review on this PR
|
First country migration from the v1
hofstede_bag.yamlschema to the v2khai_lexicon.yamlschema. Framework unchanged (Hofstede 6D, ±5 band). Sets the pattern for Austria, Switzerland, and the other 11 unmigrated cultures.What changed
regions/europe/germany/hofstede_bag.yamlkhai_lexicon.yamlregions/europe/germany/hofstede_decisions.mdkhai_lexicon_decisions.mdculture_german_position.mdfrontmatter:hofstede: aggregatemarkers: aggregateSchema deltas in the YAML
country: germany+parent: nullsubject: germany+region: europe+subregion: germanyhofstede_scores:marker_scores:khai_lexicon_version: 1,marker_set: hofstedeThe denylist and the six bags (PDI / IDV / UAI / MAS / LTO / IND, each with high/low) carry over verbatim. The framework calls Hofstede one possible
marker_set:now so other measurement frameworks can plug into the same lexicon shape later, but the dimension semantics and the scoring scale are unchanged.Scores
Unchanged. Identical to
data/hofstede_scores.json(the fixed target):Score-validity re-check
Per the brownfield contract: touching
culture_german_position.md(frontmatter swap) triggers a re-read of the position file against the declaredmarker_scores. The check ran viatest_marker_derived.py::test_derived_within_tolerance[germany]and passes — the position file's current voice still lands within ±5 on every dimension. Outcome recorded inkhai_lexicon_decisions.mdunder "v1 -> v2 schema migration".No score adjustments were needed. Had any dimension fallen out of the ±5 band, the content would have been adjusted to hit the fixed target — never the other way around.
Verification
python tests/branch_scope.py advise→ all three files route toculture/germany(baseculture/release) ✅python tests/validate_pr_brownfield.py origin/culture/release→ "Touched files are migration-current" ✅pytest tests/test_marker_derived.py -k germany→ 2/2 pass ✅--no-verify✅Unblocks
culture/europe): once Austria and Switzerland are migrated the same way, the brownfield R3 gate stops firing on the three DACH position files there.Follow-up not in this PR
Several test/script files still carry the
hofstede_*filename even though the abstraction has moved tomarker_*. They should be renamed in a governance PR:scripts/update_hofstede_readme.pytests/test_hofstede_bag_loader.py,test_hofstede_bag_fork.py,test_hofstede_bag_locked.py,test_hofstede_bag_quality.py,test_hofstede_bag_shape.py,test_hofstede_score_home.py,test_hofstede_reference.py,test_hofstede_structure.py,test_update_hofstede_readme.pytests/test_marker_derived.pyis the precedent: same logic, abstract name, drives the ±5 band check this PR depends on.Ref: #564, #545
Generated by Claude Code