/** * Tier 3 — END-TO-END: Phase 6 derived-artifact storage layer. * * Covers (§10 E2E obligations): * - Self-partition: local inference produces summary → writer persists per owner tier * - Delegated enrichment: denied without delegatedEnrichmentAllowed, allowed with it * - Convenience vs privacy_max branch (encryptor stub) * - Note delete removes summary + vector + stale-flags insight * - runDiscoverPass produces an insight event end-to-end through the writer */ import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { createDerivedArtifactWriter, WRITER_REASONS, } from '../lib/companion-artifact-writer.mjs'; import { createClientEncryptor, } from '../lib/companion-client-encryptor.mjs'; import { buildConvenienceProvenance, PROVENANCE_SCHEMA_VERSION, } from '../lib/companion-provenance-validator.mjs'; import { TERMINAL_STATES } from '../lib/companion-tier-resolver.mjs'; import { runDiscoverPass } from '../lib/memory-consolidate.mjs'; // ── E2E test double infrastructure ─────────────────────────────────────────── function buildE2EStores() { const noteStore = new Map(); // notePath → frontmatter fields const vectorStore = []; const insightStore = []; const maintenanceLog = []; const writeNoteFn = (_vaultPath, notePath, opts) => { const prev = noteStore.get(notePath) ?? {}; // Merge, treating null values as explicit nulls (deletion) const merged = { ...prev }; for (const [k, v] of Object.entries(opts.frontmatter ?? {})) { merged[k] = v; } noteStore.set(notePath, merged); }; const vs = { upsert: async (points) => { vectorStore.push(...points); }, deleteByPath: async (notePath) => { const idx = vectorStore.findIndex((v) => v.path === notePath); if (idx >= 0) vectorStore.splice(idx, 1); }, }; const mm = { store: (type, data) => { if (type === 'insight') insightStore.push(data); if (type === 'maintenance') maintenanceLog.push(data); return { id: `mem_${Date.now()}`, ts: new Date().toISOString() }; }, }; return { noteStore, vectorStore, insightStore, maintenanceLog, writeNoteFn, vs, mm }; } // ── E2E: self-partition convenience write ───────────────────────────────────── describe('E2E — self-partition summary write at convenience tier', () => { it('full flow: local inference → writer → host-readable frontmatter', async () => { const { noteStore, writeNoteFn } = buildE2EStores(); const writer = createDerivedArtifactWriter({ writeNoteFn, vaultPath: '/vault', vaultRegistryAvailable: false, }); const prov = buildConvenienceProvenance({ generatedBy: 'user-self', source: 'companion', model: 'llama-3', modelVersion: '3.1', runtimeVersion: '0.9.0', lane: 'local', artifactType: 'ai_summary', sourceNotePath: 'research/quantum.md', sourceEventId: 'mem_q001', }); const result = await writer.write( { summary: 'Quantum entanglement is a physical phenomenon.' }, prov, { lane: 'local', containsPrivateData: false, isDelegate: false, delegatedManagedAllowed: false, enrichesDelegatedPartition: false, delegatedEnrichmentAllowed: false, }, ); assert.equal(result.ok, true); assert.equal(result.terminalState, TERMINAL_STATES.HOST_READABLE); const fm = noteStore.get('research/quantum.md'); assert.ok(fm); assert.equal(fm.ai_summary, 'Quantum entanglement is a physical phenomenon.'); assert.ok(fm.ai_summary_provenance); assert.equal(fm.ai_summary_provenance.generated_by, 'user-self'); assert.equal(fm.ai_summary_provenance.schema_version, PROVENANCE_SCHEMA_VERSION); }); }); // ── E2E: delegated enrichment default-OFF ──────────────────────────────────── describe('E2E — delegated enrichment: denied by default (D6.3.3)', () => { it('rejects delegated local-lane write without owner opt-in', async () => { const { writeNoteFn, noteStore } = buildE2EStores(); const writer = createDerivedArtifactWriter({ writeNoteFn, vaultPath: '/vault' }); const prov = buildConvenienceProvenance({ generatedBy: 'delegate-user', source: 'companion', model: 'llama-3', modelVersion: '3.1', lane: 'local', artifactType: 'ai_summary', sourceNotePath: 'owner/note.md', sourceEventId: 'mem_d001', }); // D6.3.6: cross-partition write → self-partition only until tenancy gate const result = await writer.write({ summary: 'delegate summary' }, prov, { lane: 'local', containsPrivateData: false, isDelegate: true, delegatedManagedAllowed: false, enrichesDelegatedPartition: true, // actor ≠ owner delegatedEnrichmentAllowed: false, // owner has NOT opted in }); assert.equal(result.ok, false); assert.equal(result.reason, WRITER_REASONS.SELF_PARTITION_ONLY); assert.equal(noteStore.size, 0, 'No write should occur'); }); }); // ── E2E: privacy_max branch (encryptor stub) ────────────────────────────────── describe('E2E — privacy_max branch with stub encryptor', () => { it('stores only ciphertext when encryptor is available', async () => { const { noteStore, writeNoteFn } = buildE2EStores(); const stubEncryptor = createClientEncryptor({ isAvailable: () => true, encrypt: (bytes, _opts) => ({ ciphertext: new Uint8Array(bytes.length).fill(0xab), wrappedDekRef: 'wrapped-dek-for-vault', alg: 'STUB-AES-256', }), }); const writer = createDerivedArtifactWriter({ writeNoteFn, vaultPath: '/vault', vaultRegistryAvailable: true, encryptor: stubEncryptor, }); const prov = buildConvenienceProvenance({ generatedBy: 'priv-user', source: 'companion', model: 'llama-3', modelVersion: '3.1', lane: 'local', artifactType: 'ai_summary', sourceNotePath: 'private/note.md', sourceEventId: 'mem_p001', }); // Override privacy_tier after building (buildConvenienceProvenance always sets convenience) const privProv = { ...prov, privacy_tier: 'privacy_max' }; const result = await writer.write( { summary: 'My private content' }, privProv, { lane: 'local', containsPrivateData: true, isDelegate: false, delegatedManagedAllowed: false }, ); assert.equal(result.ok, true); assert.equal(result.terminalState, TERMINAL_STATES.CLIENT_ENCRYPTED); const fm = noteStore.get('private/note.md'); assert.ok(fm.ai_summary_ciphertext, 'Ciphertext must be stored'); assert.equal(fm.ai_summary, undefined, 'Plaintext ai_summary must NOT be stored'); assert.equal(fm.ai_summary_provenance.wrapped_dek_ref, 'wrapped-dek-for-vault'); }); it('fails closed when encryptor unavailable — nothing written', async () => { const { noteStore, writeNoteFn } = buildE2EStores(); const writer = createDerivedArtifactWriter({ writeNoteFn, vaultPath: '/vault', vaultRegistryAvailable: true, // Default encryptor (unavailable) }); const prov = buildConvenienceProvenance({ generatedBy: 'priv-user', source: 'companion', model: 'llama-3', modelVersion: '3.1', lane: 'local', artifactType: 'ai_summary', sourceNotePath: 'private/note.md', sourceEventId: 'mem_p002', }); const privProv = { ...prov, privacy_tier: 'privacy_max' }; const result = await writer.write({ summary: 'secret' }, privProv, { lane: 'local', containsPrivateData: true, isDelegate: false, delegatedManagedAllowed: false, }); assert.equal(result.ok, false); assert.equal(noteStore.size, 0); }); }); // ── E2E: note delete removes all derived artifacts ──────────────────────────── describe('E2E — note delete removes summary + vector, stale-flags insight (D6.5)', () => { it('full delete flow across all stores', async () => { const { noteStore, vectorStore, maintenanceLog, writeNoteFn, vs, mm } = buildE2EStores(); // Pre-populate via writer const writer = createDerivedArtifactWriter({ writeNoteFn, vaultPath: '/vault', vectorStore: vs, mm, }); // Write summary const sumprov = buildConvenienceProvenance({ generatedBy: 'u', source: 'companion', model: 'm', modelVersion: '1', lane: 'local', artifactType: 'ai_summary', sourceNotePath: 'notes/about-to-delete.md', sourceEventId: 'mem_s001', }); await writer.write({ summary: 'Will be deleted.' }, sumprov, { lane: 'local', containsPrivateData: false, isDelegate: false, delegatedManagedAllowed: false, }); // Write embedding const embprov = buildConvenienceProvenance({ generatedBy: 'u', source: 'companion', model: 'm', modelVersion: '1', lane: 'local', artifactType: 'embedding', sourceNotePath: 'notes/about-to-delete.md', sourceEventId: 'mem_e001', }); await writer.write({ vector: [0.1, 0.2], payload: {} }, embprov, { lane: 'local', containsPrivateData: false, isDelegate: false, delegatedManagedAllowed: false, }); assert.ok(noteStore.has('notes/about-to-delete.md')); assert.equal(vectorStore.length, 1); // Delete const delResult = await writer.deleteArtifacts({ notePath: 'notes/about-to-delete.md' }); assert.equal(delResult.ok, true); // Frontmatter nulled const fm = noteStore.get('notes/about-to-delete.md'); assert.equal(fm.ai_summary, null); assert.equal(fm.ai_summary_provenance, null); // Vector purged assert.equal(vectorStore.filter((v) => v.path === 'notes/about-to-delete.md').length, 0); // Maintenance stale-flag recorded for aggregate insight re-enrichment (D6.5.2) assert.ok(maintenanceLog.some((m) => m.deleted_note_path === 'notes/about-to-delete.md')); }); }); // ── E2E: runDiscoverPass end-to-end through writer ──────────────────────────── describe('E2E — runDiscoverPass end-to-end through writer', () => { it('produces insight event in insightStore via writer', async () => { const { insightStore, mm } = buildE2EStores(); const mockWriter = { write: async (artifact, provenance, _ctx) => { // Route to insightStore as the writer would if (provenance.artifact_type === 'insight') { insightStore.push({ ...artifact, provenance }); } return { ok: true, terminalState: 'host_readable' }; }, deleteArtifacts: async () => ({ ok: true, stores: [] }), checkReEnrichmentEligibility: () => ({ eligible: false, reason: 'current' }), }; const config = { vault_path: '/vault', vault_id: 'e2e-vault', llm: { model: 'test-m', model_version: '1.0' }, memory: {}, daemon: { llm: { max_tokens: 256 } }, }; const consolidations = [ { id: 'mem_c1', data: { topic: 'quantum', facts: ['QE is real', 'QE is weird'] } }, ]; const fakeLlm = async () => JSON.stringify({ connections: ['quantum connects to cryptography'], contradictions: [], open_questions: ['Is QE deterministic?'], }); const result = await runDiscoverPass(config, consolidations, { llmFn: fakeLlm, mm, writer: mockWriter, }); assert.equal(result.dry_run, false); assert.ok(Array.isArray(result.connections)); assert.equal(result.connections[0], 'quantum connects to cryptography'); // Insight must be in the store assert.equal(insightStore.length, 1); assert.ok(insightStore[0].provenance); assert.equal(insightStore[0].provenance.artifact_type, 'insight'); }); });