Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -210,21 +210,28 @@ function booleanish(value) {

function sensitivityFor({ filePath, body, frontmatter }) {
const tags = parseList(frontmatter.tags || frontmatter.tag || '');
const declaredPrivacyTier = String(frontmatter.privacy_tier || '').trim().toLowerCase();
const privacyTier = ['public', 'local-private', 'private', 'sensitive', 'secret'].includes(declaredPrivacyTier)
? declaredPrivacyTier
: null;
const haystack = [filePath, frontmatter.status, frontmatter.type, frontmatter.project, tags.join(' '), String(body || '')]
.join('\n')
.toLowerCase();
const reasons = [];
if (booleanish(frontmatter.private) || booleanish(frontmatter.sensitive)) {
reasons.push('frontmatter marks note private/sensitive');
}
if (['private', 'sensitive', 'secret'].includes(privacyTier)) {
reasons.push(`frontmatter privacy_tier is ${privacyTier}`);
}
if (tags.some((tag) => /^(private|sensitive|secret|credentials?|medical|financial|tax)$/i.test(tag))) {
reasons.push('frontmatter tags exclude automatic import');
}
for (const term of SECRET_TERMS) {
if (haystack.includes(term)) reasons.push(`contains excluded term: ${term}`);
}
return {
privacyTier: reasons.length ? 'sensitive' : 'local-private',
privacyTier: privacyTier || (reasons.length ? 'sensitive' : 'local-private'),
excluded: reasons.length > 0,
reasons: [...new Set(reasons)],
};
Expand Down
22 changes: 22 additions & 0 deletions modules/jarvos-secondbrain/tests/knowledge-units.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,25 @@ test('sensitive notes keep local knowledge units but block downstream promotion'
assert.equal(artifact.knowledgeUnits[0].downstreamEligibility.gbrain, false);
assert.equal(artifact.knowledgeUnits[0].downstreamEligibility.memoryPromotion, false);
});

test('declared private privacy tiers block downstream promotion', () => {
for (const privacyTier of ['private', 'sensitive', 'secret']) {
const { notesDir, filePath } = noteFixture();
const artifact = buildArtifact({
filePath,
notesDir,
title: 'Covert Plan',
body: 'This innocuous text does not match a sensitivity heuristic.',
frontmatter: { privacy_tier: privacyTier },
created: true,
});

assert.equal(artifact.privacyTier, privacyTier);
assert.equal(artifact.sensitivity.excluded, true);
assert.equal(artifact.gbrain.status, 'skipped');
assert.equal(artifact.memoryWiki.status, 'skipped');
assert.equal(artifact.knowledgeUnits[0].privacyDecision.tier, privacyTier);
assert.equal(artifact.knowledgeUnits[0].downstreamEligibility.gbrain, false);
assert.equal(artifact.knowledgeUnits[0].downstreamEligibility.memoryWiki, false);
}
});
Loading