Targeted stale wiki cache reuse fix

**Scope Audited**
`module/` release/wiki wrapper flow and `toolkit/` wiki generation cache logic.

**Changes Made**
Root cause was stale wiki cache reuse. The generator cache considered digest + page count, but not generator version, so a restored VPS cache with old generated output could skip regeneration.

Changed [wiki_native.go](/home/vicky/Projects/nwnee-shadowsoverwestgate/toolkit/internal/topdata/wiki_native.go:29) to bump the wiki generator to `nodebb-tiptap-html-v3`, and changed [wiki_native.go](/home/vicky/Projects/nwnee-shadowsoverwestgate/toolkit/internal/topdata/wiki_native.go:268) so cached pages are current only when `state.version` matches.

**Configuration/Schema Impact**
No YAML/config/schema changes. Existing `.cache/wiki/state.json` files with `nodebb-tiptap-html-v2` will be invalidated and regenerated.

**Compatibility Impact**
This is compatible with existing caches; it only forces one regeneration when the new toolkit runs. After that, normal digest-based skipping resumes.

**Tests Added**
Added [TestBuildWikiRegeneratesOldGeneratorCache](/home/vicky/Projects/nwnee-shadowsoverwestgate/toolkit/internal/topdata/wiki_native_test.go:183), which simulates a `v2` cache and verifies `build-wiki` regenerates it.

**Validation Performed**
- Confirmed new test failed before the fix with `wiki status "skipped"`.
- `go test ./internal/topdata -count=1` passes.
- `./build-tool.sh` succeeds.
- `SOW_TOOLS_DEV_BINARY=../toolkit/tools/sow-toolkit ./build-wiki.sh` now reports:
  - `wiki pages: 1163`
  - `wiki status: generated`
- Local generated race pages: `17`.

**Remaining Risk / VPS Next Step**
The VPS release job must use this rebuilt/published toolkit. On the next run, you should see the wiki build regenerate once before deploy, and deploy should load `1163` local pages instead of `1092`, including `races:*` pages.
This commit is contained in:
2026-05-24 10:13:52 +02:00
parent 153f46fbc8
commit 334cd427c0
2 changed files with 60 additions and 1 deletions
+4 -1
View File
@@ -26,7 +26,7 @@ const (
legacyWikiRootDirName = ".wiki"
wikiPagesDirName = "pages"
wikiStateFileName = "state.json"
wikiGeneratorVersion = "nodebb-tiptap-html-v2"
wikiGeneratorVersion = "nodebb-tiptap-html-v3"
wikiGeneratedStatus = "generated"
wikiSkippedStatus = "skipped"
)
@@ -266,6 +266,9 @@ func wikiStatusPagesEnabled(cfg project.TopDataWikiConfig) bool {
}
func wikiCachedPagesAreCurrent(outputDir string, state wikiStateDocument) bool {
if state.Version != wikiGeneratorVersion {
return false
}
if state.Pages <= 0 {
return false
}
+56
View File
@@ -180,6 +180,62 @@ func TestBuildWikiRegeneratesMissingCachedPages(t *testing.T) {
}
}
func TestBuildWikiRegeneratesOldGeneratorCache(t *testing.T) {
root := testProjectRoot(t)
mkdirAll(t, filepath.Join(root, "topdata", "data", "skills"))
writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
writeFile(t, filepath.Join(root, "topdata", "data", "skills", "base.json"), `{
"output": "skills.2da",
"columns": ["Label", "Name", "Description", "HideFromLevelUp", "Untrained", "KeyAbility", "ArmorCheckPenalty", "Constant"],
"rows": [
{
"id": 0,
"key": "skills:athletics",
"Label": "Athletics",
"Name": {"tlk": {"key": "skills:athletics.name", "text": "Athletics"}},
"Description": {"tlk": {"key": "skills:athletics.description", "text": "Ability: Strength."}},
"HideFromLevelUp": "0",
"Untrained": "1",
"KeyAbility": "STR",
"ArmorCheckPenalty": "1",
"Constant": "SKILL_ATHLETICS"
}
]
}`+"\n")
writeFile(t, filepath.Join(root, "topdata", "data", "skills", "lock.json"), `{"skills:athletics":0}`+"\n")
proj := testProject(root)
proj.Config.TopData.ReferenceBuilder = ""
if _, err := BuildNative(proj, nil); err != nil {
t.Fatalf("BuildNative failed: %v", err)
}
statePath := filepath.Join(root, ".cache", "wiki", "state.json")
state, err := loadWikiState(statePath)
if err != nil {
t.Fatalf("read state: %v", err)
}
state.Version = "nodebb-tiptap-html-v2"
if err := saveWikiState(statePath, state); err != nil {
t.Fatalf("save old-version state: %v", err)
}
result, err := BuildWikiWithOptions(proj, BuildWikiOptions{}, nil)
if err != nil {
t.Fatalf("BuildWikiWithOptions failed: %v", err)
}
if result.Status != wikiGeneratedStatus {
t.Fatalf("expected old generator cache to regenerate, got wiki status %q", result.Status)
}
refreshed, err := loadWikiState(statePath)
if err != nil {
t.Fatalf("read refreshed state: %v", err)
}
if refreshed.Version != wikiGeneratorVersion {
t.Fatalf("expected refreshed generator version %q, got %q", wikiGeneratorVersion, refreshed.Version)
}
}
func TestRenderNodeBBManagedHTMLPreservesHeadinglessTemplateHTML(t *testing.T) {
source := strings.TrimSpace(`
<p class="wiki-callout wiki-callout--status">This is generated.</p>