Make gameplay testing instructions more explicit — Claude Code cannot launch CK3 or interact with the UI, so the test script must clearly describe what the human player needs to do. Add guidance on game speed and what to look for. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
20 KiB
Testing Protocol — Lords of Ash
How to test each milestone, what tools to use, and how Claude Code can help.
Overview
Each milestone has testing checkpoints defined in its milestone doc
(docs/milestones/M<N>-*.md). This document provides the shared testing
infrastructure: debug decisions, log analysis, helper scripts, and a
workflow that lets Claude Code do most of the heavy lifting.
The core idea: temporary debug decisions gated behind a debug_mode game
rule let us force specific game states for testing without relying on console
commands. Combined with log analysis scripts and structured test runs, Claude
Code can validate milestone completion semi-autonomously.
File Locations
CK3 Paths (this system)
| What | Path |
|---|---|
| Game install | ~/.local/share/Steam/steamapps/common/Crusader Kings III/ |
| Vanilla game data | ~/.local/share/Steam/steamapps/common/Crusader Kings III/game/ |
| User data | ~/.local/share/Paradox Interactive/Crusader Kings III/ |
| Logs directory | ~/.local/share/Paradox Interactive/Crusader Kings III/logs/ |
| Mod descriptor (game) | ~/.local/share/Paradox Interactive/Crusader Kings III/mod/lords-of-ash.mod |
| DLC/mod load config | ~/.local/share/Paradox Interactive/Crusader Kings III/dlc_load.json |
| Mod project root | /var/home/jeroenschweitzer/Projects/lords-of-ash/ |
| Mod game files | /var/home/jeroenschweitzer/Projects/lords-of-ash/lords_of_ash/ |
Key Log Files
| File | Purpose | Typical size |
|---|---|---|
error.log |
All errors and warnings. Primary debug file. | 5-25 MB |
game.log |
General game flow and loading stages | 5-10 MB |
setup.log |
Initialization, mod loading order | ~30 KB |
debug.log |
Verbose debug output | 5-10 MB |
text.log |
Missing localization keys | 0-1 MB |
database_conflicts.log |
Key conflicts between mod and vanilla | 0-100 KB |
Shortcuts
# Alias for the logs directory
export CK3_LOGS="$HOME/.local/share/Paradox Interactive/Crusader Kings III/logs"
export CK3_GAME="$HOME/.local/share/Steam/steamapps/common/Crusader Kings III/game"
export CK3_MOD="/var/home/jeroenschweitzer/Projects/lords-of-ash/lords_of_ash"
Log Analysis Commands
Quick health check after a test run
# Clear old logs before a test run (do this BEFORE launching CK3)
: > "$CK3_LOGS/error.log"
: > "$CK3_LOGS/text.log"
# After the test run, check for mod-specific errors
grep -c 'mistborn\|lords.of.ash\|scadrial\|00_mistborn\|00_scadrial' "$CK3_LOGS/error.log"
# Show the first 50 mod-related errors (first errors are most important)
grep -n 'mistborn\|lords.of.ash\|scadrial\|00_mistborn\|00_scadrial' "$CK3_LOGS/error.log" | head -50
# Check for crash-causing errors
grep -i 'crash\|fatal\|assert\|CTD' "$CK3_LOGS/error.log"
# Check for encoding issues (causes cascading parse failures)
grep -i 'utf8-bom\|encoding' "$CK3_LOGS/error.log"
# Check for missing localization keys
grep -c 'Missing localization' "$CK3_LOGS/text.log"
grep 'mistborn\|scadrial' "$CK3_LOGS/text.log" | head -20
# Check mod loading order in setup.log
grep -i 'lords.of.ash\|mistborn' "$CK3_LOGS/setup.log"
Tail logs in real-time during a test run
# Watch error.log live while playing (run in a separate terminal)
tail -f "$CK3_LOGS/error.log" | grep --line-buffered 'mistborn\|scadrial\|00_mistborn'
# Watch for any new errors (not mod-filtered)
tail -f "$CK3_LOGS/error.log" | grep --line-buffered -i 'error\|warning'
# Watch game.log for event firing
tail -f "$CK3_LOGS/game.log" | grep --line-buffered 'mistborn'
Categorize errors
# Count errors by type
grep 'mistborn\|scadrial' "$CK3_LOGS/error.log" | \
sed 's/.*\(Missing\|Invalid\|Unexpected\|Duplicate\|Unknown\).*/\1/' | \
sort | uniq -c | sort -rn
# Find which files have the most errors
grep 'mistborn\|scadrial' "$CK3_LOGS/error.log" | \
grep -oP '[^\s]*\.txt' | sort | uniq -c | sort -rn | head -20
# Check for specific error patterns
grep 'Missing closing bracket' "$CK3_LOGS/error.log" | head -10
grep 'Invalid scope' "$CK3_LOGS/error.log" | head -10
grep 'not found' "$CK3_LOGS/error.log" | grep 'mistborn\|scadrial' | head -10
Debug Game Rule
All debug decisions are gated behind a game rule that is off by default. This rule should be created as part of M1 (Foundation) alongside the Narrative/Sandbox toggle.
Game Rule Definition
File: lords_of_ash/common/game_rules/00_mistborn_game_rules.txt
debug_mode = {
default = debug_off
debug_off = {
# Normal gameplay — no debug decisions visible
}
debug_on = {
# Debug decisions appear in the decision list
# Console-like power for testing without opening console
}
}
Debug Decision Template
All debug decisions follow this pattern:
debug_<action>_decision = {
is_shown = {
has_game_rule = debug_on
is_ai = no
}
# No cost, no cooldown, immediate effect
effect = {
# ... the debug action
}
}
File: lords_of_ash/common/decisions/99_debug_decisions.txt
The 99_ prefix ensures debug decisions load last and sort to the bottom.
This file is removed or emptied before Steam release (M8 task).
Debug Decisions Per Milestone
M1 — Foundation
No debug decisions needed. M1 validates that the mod loads.
Test method: Boot the game, check error.log, verify no crashes.
M2 — The Metallic Arts
| Decision | Effect | Tests |
|---|---|---|
debug_grant_allomantic_potential |
Adds trait_allomantic_potential to player |
Snapping prerequisites |
debug_trigger_snapping |
Forces snapping event chain on player | Snapping event flow |
debug_grant_mistborn |
Adds trait_mistborn + all Misting traits |
Trait interactions, compounding |
debug_grant_feruchemist |
Adds trait_feruchemist |
Compounding checks |
debug_grant_hemalurgist |
Adds trait_hemalurgist + spike traits |
Hemalurgy events |
debug_set_metal_supply |
Cycles metal supply: abundant → low → depleted | Metal scarcity events |
debug_set_allomantic_strength |
Cycles: weak → normal → strong → savant | Strength tier effects |
debug_spawn_misting_courtier |
Spawns a courtier with random Misting trait | Court composition testing |
M3 — The Final Empire
| Decision | Effect | Tests |
|---|---|---|
debug_set_attention_0 |
Sets Lord Ruler attention to 0 | Attention floor behavior |
debug_set_attention_50 |
Sets Lord Ruler attention to 50 | Mid-tier attention events |
debug_set_attention_100 |
Sets Lord Ruler attention to 100 | Max attention consequences |
debug_spawn_inquisitor |
Spawns a Steel Inquisitor in player's court | Inquisitor interactions |
debug_join_keepers |
Instantly grants keeper_identity flag |
Keeper network events |
debug_grant_kandra_contract |
Spawns a kandra in player's court | Kandra mechanics |
debug_trigger_education |
Starts education for a child immediately | Education track testing |
debug_unlock_legacy_tier |
Grants 1000 renown + unlocks next legacy | Legacy perk validation |
debug_reveal_all_secrets |
Exposes all secrets in player's court | Secret/hook system |
debug_spawn_artifact |
Spawns a menu of artifact types to choose from | Artifact system |
M4 — Scadrial on the Map
| Decision | Effect | Tests |
|---|---|---|
debug_declare_house_war |
Instantly starts House War CB against random neighbor | CB mechanics |
debug_start_scheme |
Opens a scheme against random courtier | Scheme flow |
debug_set_caste_law |
Cycles: strict → relaxed → abolished | Caste law effects |
debug_create_half_skaa |
Creates a half-skaa child with hidden heritage | Caste events |
debug_set_government |
Cycles: imperial → theocratic → elective | Government transitions |
debug_create_faction |
Spawns a rebellion faction at 80% power | Faction ultimatum |
debug_change_contract |
Sets all vassal contracts to max obligation | Contract effects |
debug_add_secret |
Menu: choose a secret type to add to player | Secret integration |
debug_set_court_type |
Cycles court types: keep → canton → hideout | Court type behavior |
debug_unlock_all_innovations |
Unlocks all innovations (both eras) | Innovation effects |
debug_set_era_liberation |
Sets lord_ruler_fallen flag |
Post-rebellion systems |
debug_assign_lifestyle |
Menu: choose a lifestyle + focus | Lifestyle/perk testing |
debug_host_activity |
Menu: choose an activity to host immediately | Activity flow |
M5 — The Crew
| Decision | Effect | Tests |
|---|---|---|
debug_toggle_game_mode |
Switches Narrative ↔ Sandbox mode mid-game | Game rule gating |
debug_fill_crew |
Spawns all 8 crew members if missing | Crew completeness |
debug_kill_kelsier |
Kills Kelsier, triggers succession | Vin succession mechanic |
debug_assign_crew_roles |
Resets all crew roles to defaults | Band council |
debug_vacate_crew_role |
Removes a crew member from their role | Vacancy penalties |
debug_simulate_no_dlc |
Disables RtP features, tests fallback | Non-DLC path |
M6 — The Plan
| Decision | Effect | Tests |
|---|---|---|
debug_complete_crew_assembly |
Sets all crew assembly flags as done | Skip to later arcs |
debug_advance_skaa_army |
Sets skaa army to max strength | Army arc testing |
debug_trigger_infiltration |
Starts noble infiltration arc immediately | Infiltration events |
debug_start_rebellion |
Forces rebellion arc to fire | Rebellion mechanics |
debug_trigger_martyrdom |
Kills Kelsier in rebellion context | Martyrdom event chain |
debug_skip_to_arc |
Menu: jump to any story arc | Non-linear testing |
M7 — The Deepness
| Decision | Effect | Tests |
|---|---|---|
debug_trigger_raid |
Starts a raid on nearest noble house | Raid event chain |
debug_discover_eleventh_metal |
Grants eleventh metal insight immediately | Eleventh metal prereq |
debug_trigger_duel |
Forces a Mistborn duel with random opponent | Duel mechanics |
debug_trigger_ball_assassination |
Forces assassination variant at next ball | Ball assassination |
debug_set_atium_supply |
Cycles atium economy states | Atium system |
debug_start_endgame |
Sets all endgame prerequisites, fires assault | Fall of Kredik Shaw |
debug_spawn_artifact_rare |
Menu: choose rare/lore artifact to spawn | Rare artifact testing |
debug_host_mistborn_activity |
Menu: choose Mistborn activity to host | Activity testing |
debug_trigger_council_event |
Menu: choose a council interaction event | Council events |
M8 — The Art of Metals
No unique debug decisions needed. M8 is graphics, map, and polish. Testing uses the existing debug decisions from M2-M7.
Focus: Error.log is completely clean. Visual review of icons, portraits, map rendering. Performance profiling.
Testing Workflow
Per-Milestone Test Protocol
When a milestone is feature-complete, run this workflow:
1. Pre-test setup
# Clear logs
: > "$CK3_LOGS/error.log"
: > "$CK3_LOGS/text.log"
: > "$CK3_LOGS/game.log"
# Verify mod is in active playset
cat "$HOME/.local/share/Paradox Interactive/Crusader Kings III/dlc_load.json"
# Verify mod descriptor path is correct
cat "$HOME/.local/share/Paradox Interactive/Crusader Kings III/mod/lords-of-ash.mod"
2. Boot test
Launch CK3 with the mod enabled. Do NOT start a game yet.
# After CK3 loads to main menu, check for load errors:
grep -c 'mistborn\|scadrial' "$CK3_LOGS/error.log"
# Target: 0 mod-related errors
# Check setup.log for mod loading confirmation
grep 'lords.of.ash' "$CK3_LOGS/setup.log"
3. Start a test game
- Select "End of an Empire" bookmark
- Enable
Debug Modegame rule (set todebug_on) - Start as the character specified in the milestone's testing checkpoint
4. Execute testing checkpoints
Follow the Player Test Script in the milestone doc (section below). Each milestone has a numbered list of specific in-game actions the player must perform. Claude Code cannot launch CK3 or interact with the UI — these steps require a human.
For each checkpoint:
- Perform the specific action described in the test script
- Verify the expected visual/gameplay outcome
- Note any issues (wrong text, missing icons, broken behavior)
- After all actions, quit to desktop so Claude Code can analyze logs
5. Extended playthrough
After all checkpoints pass:
# Clear logs again for the extended run
: > "$CK3_LOGS/error.log"
- Play the number of years specified in the milestone doc (default: 5 years)
- Set game speed to 4 or 5
- Observe for: vanilla bleed-through, event spam, broken mechanics, performance degradation
- Note anything that looks wrong, even if it doesn't crash
# After the playthrough, full log analysis:
echo "=== Error count ==="
wc -l "$CK3_LOGS/error.log"
echo "=== Mod-specific errors ==="
grep -c 'mistborn\|scadrial' "$CK3_LOGS/error.log"
echo "=== Error categories ==="
grep 'mistborn\|scadrial' "$CK3_LOGS/error.log" | \
grep -oP '(Missing|Invalid|Unexpected|Duplicate|Unknown)\w*' | \
sort | uniq -c | sort -rn
echo "=== Missing localization ==="
grep -c 'mistborn\|scadrial' "$CK3_LOGS/text.log"
echo "=== Files with errors ==="
grep 'mistborn\|scadrial' "$CK3_LOGS/error.log" | \
grep -oP '[^\s/]*\.txt' | sort | uniq -c | sort -rn | head -20
6. Generate test report
Claude Code can generate a structured test report by:
- Reading
error.logand filtering for mod errors - Categorizing errors by type and file
- Cross-referencing against the milestone's acceptance criteria
- Producing a pass/fail checklist
Claude Code Testing Capabilities
What Claude Code can do autonomously
- Static analysis: Read mod files and check for syntax issues, missing references, unmatched brackets, missing localization keys
- Log analysis: Read and categorize error.log after a test run
- Cross-reference validation: Check that every trait referenced in events exists in trait files, every localization key used has a definition, every modifier referenced is defined
- Encoding checks: Verify all
.txtand.ymlfiles have UTF-8 BOM - Vanilla comparison: Compare mod file structure against vanilla to find missing required fields
What requires human interaction
- Launching CK3: Claude Code cannot start the game
- Gameplay testing: Playing the game, clicking through events, visual inspection
- Screenshot review: Checking that UI elements display correctly
- Subjective quality: "Does this feel like Scadrial?"
Pre-launch validation script
Claude Code should run this before every test launch to catch issues early:
#!/bin/bash
# pre-test-validate.sh — Run before launching CK3
# Usage: bash docs/scripts/pre-test-validate.sh
MOD_DIR="/var/home/jeroenschweitzer/Projects/lords-of-ash/lords_of_ash"
ERRORS=0
echo "=== Lords of Ash Pre-Test Validation ==="
echo ""
# 1. Check encoding on all .txt and .yml files
echo "--- Encoding check ---"
find "$MOD_DIR" -name '*.txt' -o -name '*.yml' | while read f; do
if ! file "$f" | grep -q 'BOM'; then
echo "MISSING BOM: $f"
ERRORS=$((ERRORS + 1))
fi
done
# 2. Check for unmatched braces in .txt files
echo "--- Brace matching ---"
find "$MOD_DIR" -name '*.txt' | while read f; do
OPEN=$(grep -o '{' "$f" | wc -l)
CLOSE=$(grep -o '}' "$f" | wc -l)
if [ "$OPEN" -ne "$CLOSE" ]; then
echo "BRACE MISMATCH: $f (open=$OPEN, close=$CLOSE)"
fi
done
# 3. Check for @# comment prefix (CK3 uses # not @#)
echo "--- Invalid comment prefix ---"
grep -rn '@#' "$MOD_DIR" --include='*.txt' | head -10
# 4. Check mod descriptor
echo "--- Mod descriptor ---"
if [ -f "$HOME/.local/share/Paradox Interactive/Crusader Kings III/mod/lords-of-ash.mod" ]; then
echo "OK: Mod descriptor exists"
grep 'path=' "$HOME/.local/share/Paradox Interactive/Crusader Kings III/mod/lords-of-ash.mod"
else
echo "MISSING: Mod descriptor not found in game mod directory"
fi
# 5. Check that debug decisions file exists (if past M1)
if [ -f "$MOD_DIR/common/decisions/99_debug_decisions.txt" ]; then
echo "OK: Debug decisions file exists"
else
echo "NOTE: No debug decisions file yet (expected before M2 testing)"
fi
echo ""
echo "=== Validation complete ==="
Post-run analysis script
#!/bin/bash
# post-test-analyze.sh — Run after a CK3 test session
# Usage: bash docs/scripts/post-test-analyze.sh
CK3_LOGS="$HOME/.local/share/Paradox Interactive/Crusader Kings III/logs"
echo "=== Lords of Ash Post-Test Analysis ==="
echo "Log directory: $CK3_LOGS"
echo "Analysis time: $(date)"
echo ""
# 1. Error.log summary
echo "--- error.log ---"
TOTAL=$(wc -l < "$CK3_LOGS/error.log" 2>/dev/null || echo 0)
MOD=$(grep -c 'mistborn\|scadrial\|lords.of.ash' "$CK3_LOGS/error.log" 2>/dev/null || echo 0)
echo "Total errors: $TOTAL"
echo "Mod-related: $MOD"
if [ "$MOD" -gt 0 ]; then
echo ""
echo "Error categories:"
grep 'mistborn\|scadrial' "$CK3_LOGS/error.log" | \
grep -oP '(Missing|Invalid|Unexpected|Duplicate|Unknown)\S*' | \
sort | uniq -c | sort -rn
echo ""
echo "Top 10 files with errors:"
grep 'mistborn\|scadrial' "$CK3_LOGS/error.log" | \
grep -oP '\S*\.txt' | sort | uniq -c | sort -rn | head -10
echo ""
echo "First 10 mod errors:"
grep -n 'mistborn\|scadrial' "$CK3_LOGS/error.log" | head -10
fi
# 2. Missing localization
echo ""
echo "--- text.log (missing localization) ---"
LOC=$(grep -c 'mistborn\|scadrial' "$CK3_LOGS/text.log" 2>/dev/null || echo 0)
echo "Missing mod localization keys: $LOC"
if [ "$LOC" -gt 0 ]; then
echo "Keys:"
grep 'mistborn\|scadrial' "$CK3_LOGS/text.log" | head -20
fi
# 3. Database conflicts
echo ""
echo "--- database_conflicts.log ---"
CONF=$(wc -l < "$CK3_LOGS/database_conflicts.log" 2>/dev/null || echo 0)
echo "Database conflicts: $CONF"
# 4. Crash indicators
echo ""
echo "--- Crash indicators ---"
CRASHES=$(grep -ci 'crash\|fatal\|assert\|CTD' "$CK3_LOGS/error.log" 2>/dev/null || echo 0)
echo "Crash-related entries: $CRASHES"
if [ "$CRASHES" -gt 0 ]; then
grep -i 'crash\|fatal\|assert\|CTD' "$CK3_LOGS/error.log" | head -5
fi
echo ""
echo "=== Analysis complete ==="
if [ "$MOD" -eq 0 ] && [ "$LOC" -eq 0 ] && [ "$CRASHES" -eq 0 ]; then
echo "RESULT: CLEAN — no mod errors, no missing loc, no crashes"
else
echo "RESULT: ISSUES FOUND — see details above"
fi
Static Validation Checks (Claude Code)
These checks can be run without launching CK3. Claude Code should run them as part of milestone review:
1. Localization completeness
For every trait, event, decision, modifier, and title defined in mod files,
verify a matching localization key exists in localization/english/.
Pattern: trait_<name> → needs trait_<name>:0 and trait_<name>_desc:0
Pattern: <event_namespace>.<id>.t → event title
Pattern: <event_namespace>.<id>.desc → event description
Pattern: <decision_name> → needs <decision_name>:0 and <decision_name>_desc:0
2. Reference integrity
- Every
has_trait = trait_Xin triggers/effects →trait_Xexists incommon/traits/ - Every
has_modifier = mod_X→mod_Xexists incommon/modifiers/ - Every
trigger_event = { id = X }→ eventXexists inevents/ - Every
faith = faith:X→ faithXexists incommon/religion/ - Every
culture = culture:X→ cultureXexists incommon/culture/
3. Encoding audit
Every .txt and .yml file in lords_of_ash/ must be UTF-8 with BOM.
4. Comment syntax
CK3 uses # for comments. The @ symbol is for scripted variables only.
No @# patterns should exist in any .txt file.
Release Checklist (M8 — Before Steam)
Before publishing to Steam Workshop:
- Remove or gate
99_debug_decisions.txt(hide behind a game rule that is not visible to players, or delete the file entirely) - Remove
debug_modegame rule from game setup screen (or rename to something hidden) - Run full error.log clean pass (zero mod errors)
- Run
text.logclean pass (zero missing localization) - Verify
lords-of-ash.modhas correctsupported_versionfor current CK3 patch - Test without any other mods enabled
- Test with Roads to Power and Royal Court DLC both enabled and disabled
- Verify non-DLC fallbacks work (M5)
- Full Narrative Mode playthrough: bookmark → endgame
- Full Sandbox Mode playthrough: 30 years as a noble house
- Performance: game speed acceptable at 5-speed for 50 years