From 3973be2a902ba958903daee13643e5b75a528271 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Sun, 10 May 2026 17:01:14 +0200 Subject: [PATCH] feat(voyage): sync browser-side anchor-parser regex with Node-side allowlist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 16 of v4.3 playground plan. Mirror lib/parsers/anchor-parser.mjs ANCHOR_LINE_RE + ATTR_RE + ID_RE constants verbatim into voyage-playground.html inline-script (file COLON COLON scheme compat — no ES-module). parseAnchor(line) validates id matches ANN-NNNN, target non-empty, line positive integer, snippet ≤80c, intent in {fix,change,question,block}. Trace: SC6, research/02 Sec T4, plan-critic blocker B4 + scope-guardian DEP-3. Cross-file regex sync verified by static-grep test. --- .../voyage/playground/voyage-playground.html | 34 +++++++++++++++++++ .../playground/voyage-playground.test.mjs | 20 +++++++++++ 2 files changed, 54 insertions(+) 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'); +});