Index: src/services/ap-core.js
===================================================================
--- src/services/ap-core.js	(revision ea3e72258d753919c01c4a4397857ebbe5890b4e)
+++ src/services/ap-core.js	(revision 5ae4d61c76ecec8dcb8eaeeb0ff5379e69be6f02)
@@ -151,8 +151,31 @@
  * dat geen benadering meer maar de tag zelf, en dan hoort die te staan zoals
  * hij er staat. Eerder won het veld, en verdween de geschreven vorm.
+ *
+ * `opts.ruw` voor inhoud die nog niet door de renderer is geweest: dan staan de
+ * hashtags er als kale tekst en niet als <a class="hashtag">. buildNote krijgt
+ * het bewerkte lijf en heeft dit niet nodig; wie rechtstreeks uit posts.content
+ * leest wel -- anders vindt hij er geen enkele en valt hij stil terug op het
+ * tagveld, precies de vorm die hier juist niet moest winnen.
  */
-export function buildHashtagList(base, tagsField, content) {
+export function hashtagTagsRuw(base, content) {
+  const tags = [], seen = new Set();
+  // Moet met een LETTER beginnen: "#12" in "issue #12" is een nummer en geen
+  // tag, en die zou hier anders als hashtag de deur uit gaan.
+  const re = /(^|[\s>(\[])#(\p{L}[\p{L}\p{M}\p{N}_]*)/gu;
+  let m;
+  while ((m = re.exec(content || ''))) {
+    const k = m[2].toLowerCase();
+    if (seen.has(k)) continue; seen.add(k);
+    tags.push({ type: 'Hashtag', href: `${base}/tag/${encodeURIComponent(k)}`, name: '#' + m[2] });
+  }
+  return tags;
+}
+
+export function buildHashtagList(base, tagsField, content, opts = {}) {
   const out = [], seen = new Set();
-  for (const h of hashtagTags(base, content)) {
+  const uitLijf = opts.ruw
+    ? [...hashtagTags(base, content), ...hashtagTagsRuw(base, content)]
+    : hashtagTags(base, content);
+  for (const h of uitLijf) {
     const k = h.name.slice(1).toLowerCase(); if (seen.has(k)) continue; seen.add(k);
     out.push(h);
Index: src/services/music/index.js
===================================================================
--- src/services/music/index.js	(revision ea3e72258d753919c01c4a4397857ebbe5890b4e)
+++ src/services/music/index.js	(revision 5ae4d61c76ecec8dcb8eaeeb0ff5379e69be6f02)
@@ -380,5 +380,5 @@
   // de geschreven vorm voorgaat. Een eigen lijst hier zou de tags uit de tekst
   // missen en de rest anders spellen dan dezelfde post elders doet.
-  const tags = buildHashtagList(base, post.tags, post.content);
+  const tags = buildHashtagList(base, post.tags, post.content, { ruw: true });
   if (tags.length) obj.tag = tags;
 
Index: test/music-uitgave.test.js
===================================================================
--- test/music-uitgave.test.js	(revision ea3e72258d753919c01c4a4397857ebbe5890b4e)
+++ test/music-uitgave.test.js	(revision 5ae4d61c76ecec8dcb8eaeeb0ff5379e69be6f02)
@@ -203,5 +203,5 @@
   const p = maakPost({
     id: 'p-schrijf', slug: 'zoals-geschreven', titel: 'Zoals geschreven',
-    content: '<p>Zie <a class="hashtag" href="/tag/doenweniet">#DoenweNiet</a></p>[[track:t-een]]',
+    content: '<p>Zie #DoenweNiet</p>[[track:t-een]]',
     tags: '["Doen we Niet","lofi"]',
   });
@@ -213,2 +213,10 @@
   assert.equal(col.tag[0].href, `${BASE}/tag/doenweniet`, 'de slug blijft wel klein: dat is een adres');
 });
+
+test('"issue #12" levert geen tag op -- een nummer is geen hashtag', () => {
+  const p = maakPost({
+    id: 'p-nr', slug: 'nummer', titel: 'Nummer',
+    content: '<p>Zie issue #12 en #lofi</p>[[track:t-twee]]',
+  });
+  assert.deepEqual(M.buildPostTrackCollection(BASE, site, p).tag.map((t) => t.name), ['#lofi']);
+});
