From 5bfea5c8192611afbe4459013ffc67155d847dc2 Mon Sep 17 00:00:00 2001 From: levineam Date: Fri, 14 Aug 2026 14:33:25 -0400 Subject: [PATCH] fix(notes): preserve YAML privacy tags --- .../src/lib/note-schema.js | 9 +++-- .../tests/manual-notes-maintenance.test.js | 35 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/modules/jarvos-secondbrain/packages/jarvos-secondbrain-notes/src/lib/note-schema.js b/modules/jarvos-secondbrain/packages/jarvos-secondbrain-notes/src/lib/note-schema.js index a93ac30d..dcbc1aa7 100644 --- a/modules/jarvos-secondbrain/packages/jarvos-secondbrain-notes/src/lib/note-schema.js +++ b/modules/jarvos-secondbrain/packages/jarvos-secondbrain-notes/src/lib/note-schema.js @@ -263,7 +263,7 @@ function parseScalarValue(raw) { const items = []; for (const line of String(raw).split('\n')) { const m = line.match(/^\s*-\s+(.*)$/); - if (m) items.push(m[1].trim()); + if (m) items.push(stripQuotes(m[1])); } if (items.length) return items; } @@ -306,7 +306,12 @@ function parseScalarValue(raw) { try { return JSON.parse(trimmed); } catch (_) { - // Fall through. + // YAML flow sequences do not require JSON-style quoted strings. + if (trimmed.startsWith('[') && trimmed.endsWith(']')) { + const contents = trimmed.slice(1, -1).trim(); + if (!contents) return []; + return contents.split(',').map((item) => stripQuotes(item)); + } } } return stripQuotes(trimmed); diff --git a/modules/jarvos-secondbrain/tests/manual-notes-maintenance.test.js b/modules/jarvos-secondbrain/tests/manual-notes-maintenance.test.js index c093647a..d90ebc97 100644 --- a/modules/jarvos-secondbrain/tests/manual-notes-maintenance.test.js +++ b/modules/jarvos-secondbrain/tests/manual-notes-maintenance.test.js @@ -266,6 +266,41 @@ test('manual notes apply keeps sensitive artifacts local and clears automatic qu assert.equal(summary.gates.sensitiveNotesExcludedFromAutomaticQueues, true); }); +test('manual notes apply recognizes private tags in standard YAML lists', () => { + const { notesDir, knowledgeDir, statePath } = fixture(); + const variants = { + 'Block Tags.md': 'tags:\n - private', + 'Quoted Block Tags.md': 'tags:\n - "sensitive"', + 'Flow Tags.md': 'tags: [private, reference]', + }; + + for (const [name, tags] of Object.entries(variants)) { + writeNote(notesDir, name, [ + '---', + 'status: active', + 'type: reference', + 'project: ""', + 'created: 2026-06-22', + 'updated: 2026-06-22', + 'author: andrew', + tags, + '---', + '', + `# ${name.replace(/\.md$/, '')}`, + '', + 'This note must remain out of automatic queues.', + ].join('\n')); + } + + const report = processOnce(flagsFor({ notesDir, knowledgeDir, statePath, apply: true }), applyOptions()); + + assert.equal(report.optimization.sensitiveSkipped, 3); + assert.equal(report.optimization.gbrainQueued, 0); + assert.equal(report.optimization.memoryWikiQueued, 0); + assert.deepEqual(readJson(path.join(knowledgeDir, 'gbrain-import-queue.json')).entries, {}); + assert.deepEqual(readJson(path.join(knowledgeDir, 'memory-wiki-queue.json')).entries, {}); +}); + test('manual notes apply parks unfixable frontmatter without optimizer side effects', () => { const { notesDir, knowledgeDir, statePath } = fixture(); const original = [