From 334cd427c019145c89e7b9ef41a5f980e1238f04 Mon Sep 17 00:00:00 2001 From: vickydotbat Date: Sun, 24 May 2026 10:13:52 +0200 Subject: [PATCH] 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. --- internal/topdata/wiki_native.go | 5 ++- internal/topdata/wiki_native_test.go | 56 ++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/internal/topdata/wiki_native.go b/internal/topdata/wiki_native.go index 0dd39a3..c239455 100644 --- a/internal/topdata/wiki_native.go +++ b/internal/topdata/wiki_native.go @@ -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 } diff --git a/internal/topdata/wiki_native_test.go b/internal/topdata/wiki_native_test.go index 459e2c6..21905a7 100644 --- a/internal/topdata/wiki_native_test.go +++ b/internal/topdata/wiki_native_test.go @@ -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(`

This is generated.