diff --git a/.project/objectives/DASHBOARD_CATEGORIES.md b/.project/objectives/DASHBOARD_CATEGORIES.md
index cd4f522c..7defc8fb 100644
--- a/.project/objectives/DASHBOARD_CATEGORIES.md
+++ b/.project/objectives/DASHBOARD_CATEGORIES.md
@@ -204,7 +204,7 @@
| [p2-36](p2-36-data-resources-building-duplicates.md) | 🟡 partial | P2 | Reconcile the 14 building IDs defined in both `resources/buildings/` and `data/buildings/` | — | 🟢 |
| [p2-37](p2-37-react-calculator-metadata-surface.md) | ✅ done | P2 | React calculator UI — surface flavor, lore, clan_affinity, archetype filter | [tourguide](../team-leads/tourguide.md) | 🟢 |
| [p2-38](p2-38-unit-audio-cues-stubs.md) | ✅ done | P2 | Unit audio_cues stub strings — selection/move/attack lines for the dwarven roster | [asset-audio](../team-leads/asset-audio.md) | 🟢 |
-| [p2-39](p2-39-chronicle-hall-phantom-unlock.md) | 🔴 stub | P2 | Resolve `chronicle_hall` phantom unlock in `chronicle_keeping` culture tech | — | 🟢 |
+| [p2-39](p2-39-chronicle-hall-phantom-unlock.md) | ✅ done | P2 | Resolve `chronicle_hall` phantom unlock in `chronicle_keeping` culture tech | — | 🟢 |
| [p3-01](p3-01-courier-diplomacy.md) | 🟡 partial | P3 | Courier-gated diplomacy — open borders + shared maps via tech-tiered courier units | [envoy](../team-leads/envoy.md) | 🟢 |
| [p3-02](p3-02-hybrid-merged-structures.md) | ❌ missing | P3 | Hybrid merged structures — war_academy, assault_citadel, cavalry_corps, gunnery_corps | — | 🟢 |
| [p3-03](p3-03-courier-route-resolver.md) | 🔴 stub | P3 | Courier route resolver — real hex pathfinding, per-tier movement, severable infrastructure | [envoy](../team-leads/envoy.md) | 🟢 |
diff --git a/.project/objectives/DASHBOARD_COMPLETED.md b/.project/objectives/DASHBOARD_COMPLETED.md
index 71051da9..efe6a733 100644
--- a/.project/objectives/DASHBOARD_COMPLETED.md
+++ b/.project/objectives/DASHBOARD_COMPLETED.md
@@ -120,4 +120,5 @@
| [p2-32](p2-32-guide-data-driven-enums.md) | Replace hardcoded page enums with JSON data reads | — | [tourguide](../team-leads/tourguide.md) | 2026-04-18 |
| [p2-37](p2-37-react-calculator-metadata-surface.md) | React calculator UI — surface flavor, lore, clan_affinity, archetype filter | — | [tourguide](../team-leads/tourguide.md) | 2026-04-27 |
| [p2-38](p2-38-unit-audio-cues-stubs.md) | Unit audio_cues stub strings — selection/move/attack lines for the dwarven roster | — | [asset-audio](../team-leads/asset-audio.md) | 2026-04-27 |
+| [p2-39](p2-39-chronicle-hall-phantom-unlock.md) | Resolve `chronicle_hall` phantom unlock in `chronicle_keeping` culture tech | — | — | 2026-04-27 |
diff --git a/.project/objectives/README.md b/.project/objectives/README.md
index 2a3ad0e9..c4e5a4a9 100644
--- a/.project/objectives/README.md
+++ b/.project/objectives/README.md
@@ -16,9 +16,9 @@
|---|---|---|---|---|---|---|---|
| **P0** | 0 | 0 | 0 | 0 | 0 | 43 | 43 |
| **P1** | 1 | 8 | 0 | 10 | 1 | 31 | 51 |
-| **P2** | 0 | 3 | 2 | 1 | 0 | 30 | 36 |
+| **P2** | 0 | 3 | 1 | 1 | 0 | 31 | 36 |
| **P3 (oos)** | 0 | 1 | 1 | 1 | 19 | 0 | 22 |
-| **total** | **1** | **12** | **3** | **12** | **20** | **104** | **152** |
+| **total** | **1** | **12** | **2** | **12** | **20** | **105** | **152** |
@@ -76,7 +76,6 @@
| [p2-18](p2-18-guide-public-deployment.md) | 🟡 partial | Guide web app — public hosting + deploy pipeline | — | — | 2026-04-17 | 🟢 unblocked |
| [p2-36](p2-36-data-resources-building-duplicates.md) | 🟡 partial | Reconcile the 14 building IDs defined in both `resources/buildings/` and `data/buildings/` | — | — | 2026-04-27 | 🟢 unblocked |
| [p2-11a](p2-11a.md) | 🔴 stub | SaveManager: add Unit.serialize/deserialize and City.production_queue serialize path | — | — | 2026-04-26 | 🟢 unblocked |
-| [p2-39](p2-39-chronicle-hall-phantom-unlock.md) | 🔴 stub | Resolve `chronicle_hall` phantom unlock in `chronicle_keeping` culture tech | — | — | 2026-04-27 | 🟢 unblocked |
| [p2-35](p2-35-palace-evolution-system.md) | ❌ missing | Palace evolution system — longhouse → great_hall → citadel → grand_citadel + function-shedding | — | — | 2026-04-27 | 🟢 unblocked |
## Out of Scope
diff --git a/.project/objectives/objectives.json b/.project/objectives/objectives.json
index 05d8f826..7b738d84 100644
--- a/.project/objectives/objectives.json
+++ b/.project/objectives/objectives.json
@@ -1,10 +1,10 @@
{
- "generated_at": "2026-04-28T05:44:38Z",
+ "generated_at": "2026-04-28T22:21:08Z",
"totals": {
- "done": 104,
+ "done": 105,
"in_progress": 1,
"partial": 12,
- "stub": 3,
+ "stub": 2,
"missing": 12,
"oos": 20,
"total": 152
@@ -1416,7 +1416,7 @@
"id": "p2-39",
"title": "Resolve `chronicle_hall` phantom unlock in `chronicle_keeping` culture tech",
"priority": "p2",
- "status": "stub",
+ "status": "done",
"scope": "game1",
"updated_at": "2026-04-27",
"blocked_by": [],
diff --git a/.project/objectives/p2-39-chronicle-hall-phantom-unlock.md b/.project/objectives/p2-39-chronicle-hall-phantom-unlock.md
index c57b8763..398c8ca6 100644
--- a/.project/objectives/p2-39-chronicle-hall-phantom-unlock.md
+++ b/.project/objectives/p2-39-chronicle-hall-phantom-unlock.md
@@ -2,7 +2,7 @@
id: p2-39
title: Resolve `chronicle_hall` phantom unlock in `chronicle_keeping` culture tech
priority: p2
-status: stub
+status: done
scope: game1
updated_at: 2026-04-27
evidence:
@@ -43,11 +43,10 @@ is currently out of scope.
## Acceptance criteria
-- [ ] Decide A vs B (default A).
-- [ ] If A: edit `public/resources/culture/oral_tradition.json:96` `"chronicle_hall"` → `"bardic_circle"`. Verify no other reference to `chronicle_hall` remains in the data tree (`grep -r chronicle_hall public/`).
-- [ ] If B: author `public/resources/buildings/chronicle_hall.json` conforming to `building.schema.json`. Add to `public/games/age-of-dwarves/data/buildings/manifest.json`.
-- [ ] `python3 tools/validate-game-data.py` → 0 failed.
-- [ ] Run dashboard regen.
+- [✓] Decide A vs B (default A). Option A chosen.
+- [✓] If A: edit `public/resources/culture/oral_tradition.json:96` `"chronicle_hall"` → `"bardic_circle"`. Verify no other reference to `chronicle_hall` remains in the data tree (`grep -r chronicle_hall public/`). — Only hit in dist/guide bundle (compiled artifact, not source data).
+- [✓] `python3 tools/validate-game-data.py` → 0 failed. (317 passed, 0 failed)
+- [✓] Run dashboard regen.
## Non-goals
diff --git a/public/resources/culture/oral_tradition.json b/public/resources/culture/oral_tradition.json
index 7f36304a..77eec4d2 100644
--- a/public/resources/culture/oral_tradition.json
+++ b/public/resources/culture/oral_tradition.json
@@ -93,7 +93,7 @@
],
"unlocks": {
"buildings": [
- "chronicle_hall"
+ "bardic_circle"
],
"units": [],
"improvements": [],
|