diff --git a/plugins/voyage/playground/voyage-playground.html b/plugins/voyage/playground/voyage-playground.html
index 6be85c3..2baf445 100644
--- a/plugins/voyage/playground/voyage-playground.html
+++ b/plugins/voyage/playground/voyage-playground.html
@@ -28,6 +28,40 @@
})();
+
+
diff --git a/plugins/voyage/tests/playground/voyage-playground.test.mjs b/plugins/voyage/tests/playground/voyage-playground.test.mjs
index e724ab6..0d48e23 100644
--- a/plugins/voyage/tests/playground/voyage-playground.test.mjs
+++ b/plugins/voyage/tests/playground/voyage-playground.test.mjs
@@ -200,3 +200,23 @@ test('voyage-playground.html declares popstate handler (v4.3 Step 15 back/forwar
const text = readFileSync(HTML, 'utf-8');
assert.match(text, /'popstate'/, 'popstate listener required for browser back/forward');
});
+
+test('voyage-playground.html declares VOYAGE_ANCHOR_RE constant (v4.3 Step 16 anchor allowlist)', () => {
+ const text = readFileSync(HTML, 'utf-8');
+ assert.match(text, /VOYAGE_ANCHOR_RE\s*=\s*\/\^/, 'VOYAGE_ANCHOR_RE regex constant required');
+ assert.match(text, /VOYAGE_ANCHOR_ATTR_RE\s*=\s*\//, 'VOYAGE_ANCHOR_ATTR_RE constant required');
+ assert.match(text, /VOYAGE_ANCHOR_ID_RE\s*=\s*\/\^ANN-/, 'VOYAGE_ANCHOR_ID_RE constant required');
+});
+
+test('voyage-playground.html anchor regex matches Node-side allowlist (v4.3 Step 16 cross-file sync)', () => {
+ const html = readFileSync(HTML, 'utf-8');
+ const node = readFileSync(join(ROOT, 'lib', 'parsers', 'anchor-parser.mjs'), 'utf-8');
+ const htmlMatch = html.match(/voyage:anchor[^/]+/)?.[0];
+ const nodeMatch = node.match(/voyage:anchor[^/]+/)?.[0];
+ assert.equal(htmlMatch, nodeMatch, 'first voyage:anchor token in HTML must mirror Node-side parser exactly');
+});
+
+test('voyage-playground.html declares parseAnchor validator (v4.3 Step 16)', () => {
+ const text = readFileSync(HTML, 'utf-8');
+ assert.match(text, /function\s+parseAnchor\s*\(\s*line\s*\)/, 'parseAnchor(line) function required');
+});