feat: log tree from Module 2 (logTree / log_tree); SpellGrid CLIs print Log:

Engine events carry the nesting depth they were recorded at; the harness
prints the log as an indented tree. New tests test_02_log_tree_shows_nesting
and test_03_log_tree_death_phase; both SpellGrid CLIs print the tree of the
last command's Sequences under a Log: line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-11-11 10:23:59 +01:00
1 parent 06a1b34c5d
commit 97c64ebee8
86 files changed
+1375 -55

No files matched your search

@@ -113,6 +113,29 @@ fn test_02_depth_first_nesting() {
assert_eq!(g.health(g.hero(1)), 29);
}
#[test]
fn test_02_log_tree_shows_nesting() {
let mut g = new_game(GameOpts::default());
let e = g.summon("Test Echo", 0);
let s = g.summon("Armorsmith", 0);
let r = g.summon("Test Reactor", 0);
let enemy = g.hero(1);
g.clear_log();
g.deal_damage(s, 1);
let expected = [
format!("Damaged{{target: {s}, amount: 1}}"),
format!("Triggered{{source: {e}, trigger: \"damaged\"}}"),
format!(" Damaged{{target: {enemy}, amount: 1}}"),
format!(" Triggered{{source: {r}, trigger: \"damaged\"}}"),
format!(" StatsChanged{{id: {r}, attack: 2, health: 5}}"),
format!("Triggered{{source: {s}, trigger: \"damaged\"}}"),
" ArmorGained{player: 0, amount: 1}".to_string(),
];
assert_eq!(g.log_tree(), expected.join("\n") + "\n");
}
#[test]
fn test_02_empty_deck_draw_is_fatigue() {
let mut g = new_game(GameOpts::default());
@@ -71,6 +71,29 @@ test("test_02_depth_first_nesting", () => {
expect(g.health(g.hero(1))).toBe(29);
});
test("test_02_log_tree_shows_nesting", () => {
const g = newGame();
const e = g.summon("Test Echo", 0);
const s = g.summon("Armorsmith", 0);
const r = g.summon("Test Reactor", 0);
const enemy = g.hero(1);
g.clearLog();
g.dealDamage(s, 1);
expect(g.logTree()).toBe(
[
`Damaged{target: ${s}, amount: 1}`,
`Triggered{source: ${e}, trigger: "damaged"}`,
` Damaged{target: ${enemy}, amount: 1}`,
` Triggered{source: ${r}, trigger: "damaged"}`,
` StatsChanged{id: ${r}, attack: 2, health: 5}`,
`Triggered{source: ${s}, trigger: "damaged"}`,
` ArmorGained{player: 0, amount: 1}`,
].join("\n") + "\n",
);
});
test("test_02_empty_deck_draw_is_fatigue", () => {
const g = newGame();
const a = g.summon("Acolyte of Pain", 0);