import test from "node:test";
import { initLogger } from "../src/logger.ts";
import assert from "node:assert/strict";
import os from "node:os";
import path from "node:path";
import Database from "better-sqlite3";
import { mkdtemp, mkdir, readFile, rm, stat, symlink, writeFile } from "node:fs/promises";
import { StorageManager } from "../src/storage.ts";
import {
  backupExistingProjection,
  rebuildMemoryProjection,
  repairMemoryProjection,
  verifyMemoryProjection,
} from "../src/maintenance/rebuild-memory-projection.ts";
import { runMemoryGovernance } from "../src/maintenance/memory-governance.ts";
import {
  getMemoryProjectionPath,
  initializeMemoryProjectionDb, markProjectedMemoryPathInvalid,
  probeProjectionHealth,
  readProjectedEntityMentions,
  readProjectedLatestReviewQueue,
  readProjectedMemoryBrowse, updateProjectedMemoryPath,
  readProjectedMemoryState,
  readProjectedMemoryTimeline,
  readProjectedNativeKnowledgeChunks,
} from "../src/memory-projection-store.ts";

async function writeText(baseDir: string, relPath: string, content: string): Promise<void> {
  const full = path.join(baseDir, relPath);
  await mkdir(path.dirname(full), { recursive: true });
  await writeFile(full, content, "utf-8");
}

function memoryDoc(options: {
  id: string;
  content: string;
  category?: string;
  created?: string;
  updated?: string;
  confidence?: number;
  confidenceTier?: string;
  entityRef?: string;
  tags?: string[];
  structuredAttributes?: Record<string, string>;
}): string {
  return [
    "---",
    `id: ${options.id}`,
    `category: ${options.category ?? "fact"}`,
    `created: ${options.created ?? "2026-03-01T00:00:00.000Z"}`,
    `updated: ${options.updated ?? options.created ?? "2026-03-01T00:00:00.000Z"}`,
    "source: test",
    `confidence: ${options.confidence ?? 0.8}`,
    `confidenceTier: ${options.confidenceTier ?? "implied"}`,
    `tags: [${(options.tags ?? ["projection"]).map((tag) => `"${tag}"`).join(", ")}]`,
    ...(options.structuredAttributes
      ? [`structuredAttributes: ${JSON.stringify(options.structuredAttributes)}`]
      : []),
    ...(options.entityRef ? [`entityRef: ${options.entityRef}`] : []),
    "---",
    "",
    options.content,
    "",
  ].join("\n");
}

test("projection-store queries fail open when projection database is absent", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-missing-"));
  try {
    const current = readProjectedMemoryState(memoryDir, "missing-memory");
    const timeline = readProjectedMemoryTimeline(memoryDir, "missing-memory", 50);

    assert.equal(current, null);
    assert.equal(timeline, null);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("projection-store skips current rows whose paths escape the memory directory", async () => {
  const baseDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-escape-base-"));
  const memoryDir = path.join(baseDir, "memory");
  const outsidePath = path.join(baseDir, "outside.md");
  try {
    await mkdir(memoryDir, { recursive: true });
    await writeFile(outsidePath, "escaped projection needle", "utf-8");
    const projectionPath = getMemoryProjectionPath(memoryDir);
    await mkdir(path.dirname(projectionPath), { recursive: true });
    const db = new Database(projectionPath);
    try {
      initializeMemoryProjectionDb(db);
      db.prepare(`
        INSERT INTO memory_current (
          memory_id,
          category,
          status,
          lifecycle_state,
          path_rel,
          created_at,
          updated_at,
          archived_at,
          superseded_at,
          entity_ref,
          source,
          confidence,
          confidence_tier,
          memory_kind,
          access_count,
          last_accessed,
          tags_json,
          preview_text
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `).run(
        "escaped-row",
        "fact",
        "active",
        null,
        "../outside.md",
        "2026-03-08T00:00:00.000Z",
        "2026-03-08T01:00:00.000Z",
        null,
        null,
        null,
        "test",
        0.8,
        "implied",
        null,
        null,
        null,
        "[]",
        "",
      );
    } finally {
      db.close();
    }

    assert.equal(readProjectedMemoryState(memoryDir, "escaped-row"), null);
    const browse = readProjectedMemoryBrowse(memoryDir, {
      query: "escaped projection needle",
      limit: 10,
      offset: 0,
    });
    assert.equal(browse?.total, 0);
    assert.deepEqual(browse?.memories, []);
  } finally {
    await rm(baseDir, { recursive: true, force: true });
  }
});

test("rebuildMemoryProjection dry-run computes current rows and timeline rows without writing output", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-dry-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-1.md",
      `---
id: fact-1
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T01:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["alpha"]
---

alpha
`,
    );

    const result = await rebuildMemoryProjection({ memoryDir });
    assert.equal(result.dryRun, true);
    assert.equal(result.currentRows, 1);
    assert.equal(result.timelineRows, 2);
    assert.equal(result.usedLifecycleLedger, false);
    await assert.rejects(() => stat(result.outputPath));
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("rebuildMemoryProjection includes hot cold and archived memories", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-cold-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-hot.md",
      memoryDoc({
        id: "fact-hot",
        content: "hot memory",
        created: "2026-03-08T00:00:00.000Z",
        updated: "2026-03-08T01:00:00.000Z",
        tags: ["hot"],
      }),
    );
    await writeText(
      memoryDir,
      "cold/facts/2026-03-08/fact-cold.md",
      memoryDoc({
        id: "fact-cold",
        content: "cold memory",
        created: "2026-03-08T02:00:00.000Z",
        updated: "2026-03-08T03:00:00.000Z",
        tags: ["cold"],
      }),
    );
    await writeText(
      memoryDir,
      "archive/2026-03-08/fact-archived.md",
      [
        "---",
        "id: fact-archived",
        "category: fact",
        "created: 2026-03-08T04:00:00.000Z",
        "updated: 2026-03-08T05:00:00.000Z",
        "source: test",
        "confidence: 0.8",
        "confidenceTier: implied",
        'tags: ["archived"]',
        "status: archived",
        "archivedAt: 2026-03-08T06:00:00.000Z",
        "---",
        "",
        "archived memory",
        "",
      ].join("\n"),
    );

    const result = await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T12:00:00.000Z"),
    });

    assert.equal(result.scannedMemories, 3);
    assert.equal(result.currentRows, 3);
    assert.equal(result.timelineRows, 7);
    assert.ok(readProjectedMemoryState(memoryDir, "fact-hot"));
    assert.ok(readProjectedMemoryState(memoryDir, "fact-cold"));
    assert.ok(readProjectedMemoryState(memoryDir, "fact-archived"));
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("projected browse excludes support passport records before pagination", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-memory-projection-private-"));
  try {
    for (const [id, tag] of [
      ["card", "support-passport-card"],
      ["audit", "support-passport-audit"],
      ["near-card", "support-passport-card-extra"],
      ["public", "public"],
    ] as const) {
      await writeText(
        memoryDir,
        `facts/2026-03-08/${id}.md`,
        memoryDoc({ id, content: `${id} content`, tags: [tag] }),
      );
    }
    await writeText(
      memoryDir,
      "facts/2026-03-08/attribute-only.md",
      memoryDoc({
        id: "attribute-only",
        content: "attribute-only content",
        tags: ["public"],
        structuredAttributes: { "support-passport-owner": "private-owner" },
      }),
    );
    await rebuildMemoryProjection({ memoryDir, dryRun: false });

    const browse = readProjectedMemoryBrowse(memoryDir, {
      excludePrivateRecords: true,
      limit: 1,
      offset: 0,
    });

    assert.equal(browse?.total, 2);
    assert.deepEqual(browse?.memories.map((memory) => memory.id), ["near-card"]);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("rebuildMemoryProjection writes current-state and timeline rows and backs up existing projection", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-live-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-1.md",
      `---
id: fact-1
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T01:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["alpha"]
entityRef: person-josh
---

alpha
`,
    );
    await writeText(
      memoryDir,
      "state/memory-lifecycle-ledger.jsonl",
      [
        JSON.stringify({
          eventId: "evt-1",
          memoryId: "fact-1",
          eventType: "created",
          timestamp: "2026-03-08T00:00:00.000Z",
          actor: "storage.writeMemory",
          ruleVersion: "memory-lifecycle-ledger.v1",
        }),
        JSON.stringify({
          eventId: "evt-2",
          memoryId: "fact-1",
          eventType: "updated",
          timestamp: "2026-03-08T01:00:00.000Z",
          actor: "storage.updateMemory",
          ruleVersion: "memory-lifecycle-ledger.v1",
        }),
      ].join("\n") + "\n",
    );
    await writeText(memoryDir, "state/memory-projection.sqlite", "legacy-db");

    const result = await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T12:00:00.000Z"),
    });
    assert.equal(result.currentRows, 1);
    assert.equal(result.timelineRows, 2);
    assert.equal(result.usedLifecycleLedger, true);
    assert.equal(result.backupPath != null, true);
    await stat(result.outputPath);

    const backupRaw = await readFile(result.backupPath as string, "utf-8");
    assert.equal(backupRaw, "legacy-db");

    const current = readProjectedMemoryState(memoryDir, "fact-1");
    assert.ok(current);
    assert.equal(current?.entityRef, "person-josh");
    assert.equal(current?.status, "active");

    const timeline = readProjectedMemoryTimeline(memoryDir, "fact-1", 20);
    assert.ok(timeline);
    assert.deepEqual(
      timeline?.map((entry) => [entry.eventType, entry.timestamp]),
      [
        ["created", "2026-03-08T00:00:00.000Z"],
        ["updated", "2026-03-08T01:00:00.000Z"],
      ],
    );
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("projection reads lazily migrate legacy schema columns for existing projection stores", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-legacy-browse-"));
  try {
    const projectionPath = getMemoryProjectionPath(memoryDir);
    await mkdir(path.dirname(projectionPath), { recursive: true });
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-legacy.md",
      memoryDoc({
        id: "fact-legacy",
        content: `${"alpha ".repeat(60)}needle beyond preview depth`,
        created: "2026-03-08T00:00:00.000Z",
        updated: "2026-03-08T01:00:00.000Z",
      }),
    );

    const db = new Database(projectionPath);
    try {
      db.exec(`
        CREATE TABLE meta (
          key TEXT PRIMARY KEY,
          value TEXT NOT NULL
        );

        CREATE TABLE memory_current (
          memory_id TEXT PRIMARY KEY,
          category TEXT NOT NULL,
          status TEXT NOT NULL,
          lifecycle_state TEXT,
          path_rel TEXT NOT NULL,
          created_at TEXT NOT NULL,
          updated_at TEXT NOT NULL,
          archived_at TEXT,
          superseded_at TEXT,
          entity_ref TEXT,
          source TEXT NOT NULL,
          confidence REAL NOT NULL,
          confidence_tier TEXT NOT NULL,
          memory_kind TEXT,
          access_count INTEGER,
          last_accessed TEXT
        );
      `);
      db.prepare(`
        INSERT INTO memory_current (
          memory_id,
          category,
          status,
          lifecycle_state,
          path_rel,
          created_at,
          updated_at,
          archived_at,
          superseded_at,
          entity_ref,
          source,
          confidence,
          confidence_tier,
          memory_kind,
          access_count,
          last_accessed
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `).run(
        "fact-legacy",
        "fact",
        "active",
        null,
        "facts/2026-03-08/fact-legacy.md",
        "2026-03-08T00:00:00.000Z",
        "2026-03-08T01:00:00.000Z",
        null,
        null,
        null,
        "test",
        0.8,
        "implied",
        null,
        null,
        null,
      );

    } finally {
      db.close();
    }

    const projected = readProjectedMemoryState(memoryDir, "fact-legacy");
    assert.ok(projected);
    assert.equal(projected?.memoryId, "fact-legacy");

    const browse = readProjectedMemoryBrowse(memoryDir, {
      limit: 20,
      offset: 0,
    });
    assert.ok(browse);
    assert.equal(browse?.total, 1);
    assert.equal(browse?.memories[0]?.id, "fact-legacy");

    const migrated = new Database(projectionPath, { readonly: true });
    try {
      const columns = migrated
        .prepare(`PRAGMA table_info(memory_current)`)
        .all() as Array<{ name: string }>;
      assert.equal(columns.some((column) => column.name === "tags_json"), true);
      assert.equal(columns.some((column) => column.name === "preview_text"), true);
    } finally {
      migrated.close();
    }
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("privacy-filtered browse lazily migrates schema-v3 projections to private_record (#2387)", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-v3-"));
  try {
    const projectionPath = getMemoryProjectionPath(memoryDir);
    await mkdir(path.dirname(projectionPath), { recursive: true });
    const db = new Database(projectionPath);
    try {
      db.exec(`
        CREATE TABLE meta (
          key TEXT PRIMARY KEY,
          value TEXT NOT NULL
        );

        CREATE TABLE memory_current (
          memory_id TEXT PRIMARY KEY,
          category TEXT NOT NULL,
          status TEXT NOT NULL,
          path_rel TEXT NOT NULL,
          created_at TEXT NOT NULL,
          updated_at TEXT NOT NULL,
          entity_ref TEXT,
          source TEXT NOT NULL,
          confidence REAL NOT NULL,
          confidence_tier TEXT NOT NULL,
          tags_json TEXT NOT NULL DEFAULT '[]',
          preview_text TEXT NOT NULL DEFAULT ''
        );
      `);
      const insert = db.prepare(`
        INSERT INTO memory_current (
          memory_id, category, status, path_rel, created_at, updated_at,
          entity_ref, source, confidence, confidence_tier, tags_json, preview_text
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `);
      insert.run(
        "fact-public", "fact", "active", "facts/2026-03-08/public.md",
        "2026-03-08T00:00:00.000Z", "2026-03-08T01:00:00.000Z",
        null, "test", 0.8, "implied", "[]", "public preview",
      );
      insert.run(
        "pref-passport", "preference", "active", "preferences/2026-03-08/passport.md",
        "2026-03-08T00:00:00.000Z", "2026-03-08T01:00:00.000Z",
        null, "test", 0.8, "implied",
        JSON.stringify(["support-passport-card"]), "private preview",
      );
    } finally {
      db.close();
    }

    // First privacy-filtered browse cannot serve from the v3 store...
    assert.equal(
      readProjectedMemoryBrowse(memoryDir, { limit: 20, offset: 0, excludePrivateRecords: true }),
      null,
    );
    // ...but it migrates the store on the way out.
    const migrated = new Database(projectionPath);
    try {
      const columns = migrated
        .prepare(`PRAGMA table_info(memory_current)`)
        .all() as Array<{ name: string }>;
      assert.equal(columns.some((column) => column.name === "private_record"), true);
      const privateFlag = (memoryId: string): unknown =>
        migrated
          .prepare(`SELECT private_record FROM memory_current WHERE memory_id = ?`)
          .get(memoryId);
      for (const [memoryId, expected] of [["pref-passport", 1], ["fact-public", 0]] as const) {
        const row = privateFlag(memoryId);
        assert.ok(row && typeof row === "object" && "private_record" in row);
        assert.equal(Number(row.private_record), expected);
      }
    } finally {
      migrated.close();
    }

    const browse = readProjectedMemoryBrowse(memoryDir, {
      limit: 20,
      offset: 0,
      excludePrivateRecords: true,
    });
    assert.ok(browse);
    assert.equal(browse?.total, 1);
    assert.equal(browse?.memories[0]?.id, "fact-public");

    assert.equal(probeProjectionHealth(memoryDir).state, "openable");
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("verifyMemoryProjection treats legacy projection schemas as existing rows", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-legacy-verify-"));
  try {
    const projectionPath = getMemoryProjectionPath(memoryDir);
    await mkdir(path.dirname(projectionPath), { recursive: true });
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-legacy.md",
      memoryDoc({
        id: "fact-legacy",
        content: "legacy verify row",
        created: "2026-03-08T00:00:00.000Z",
        updated: "2026-03-08T01:00:00.000Z",
      }),
    );

    const db = new Database(projectionPath);
    try {
      db.exec(`
        CREATE TABLE memory_current (
          memory_id TEXT PRIMARY KEY,
          category TEXT NOT NULL,
          status TEXT NOT NULL,
          lifecycle_state TEXT,
          path_rel TEXT NOT NULL,
          created_at TEXT NOT NULL,
          updated_at TEXT NOT NULL,
          archived_at TEXT,
          superseded_at TEXT,
          entity_ref TEXT,
          source TEXT NOT NULL,
          confidence REAL NOT NULL,
          confidence_tier TEXT NOT NULL,
          memory_kind TEXT,
          access_count INTEGER,
          last_accessed TEXT
        );
      `);
      db.prepare(`
        INSERT INTO memory_current (
          memory_id,
          category,
          status,
          lifecycle_state,
          path_rel,
          created_at,
          updated_at,
          archived_at,
          superseded_at,
          entity_ref,
          source,
          confidence,
          confidence_tier,
          memory_kind,
          access_count,
          last_accessed
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `).run(
        "fact-legacy",
        "fact",
        "active",
        null,
        "facts/2026-03-08/fact-legacy.md",
        "2026-03-08T00:00:00.000Z",
        "2026-03-08T01:00:00.000Z",
        null,
        null,
        null,
        "test",
        0.8,
        "implied",
        null,
        null,
        null,
      );
    } finally {
      db.close();
    }

    const verify = await verifyMemoryProjection({ memoryDir });
    assert.equal(verify.projectionExists, true);
    assert.equal(verify.actualCurrentRows, 1);
    assert.deepEqual(verify.missingCurrentMemoryIds, []);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("scoped rebuild preserves out-of-scope legacy projection rows", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-legacy-scope-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-older.md",
      `---
id: fact-older
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T01:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
entityRef: project-original
tags: ["older"]
---

older
`,
    );
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-recent.md",
      `---
id: fact-recent
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T05:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
entityRef: project-after
tags: ["recent"]
---

recent updated
`,
    );

    const projectionPath = getMemoryProjectionPath(memoryDir);
    await mkdir(path.dirname(projectionPath), { recursive: true });
    const db = new Database(projectionPath);
    try {
      db.exec(`
        CREATE TABLE memory_current (
          memory_id TEXT PRIMARY KEY,
          category TEXT NOT NULL,
          status TEXT NOT NULL,
          lifecycle_state TEXT,
          path_rel TEXT NOT NULL,
          created_at TEXT NOT NULL,
          updated_at TEXT NOT NULL,
          archived_at TEXT,
          superseded_at TEXT,
          entity_ref TEXT,
          source TEXT NOT NULL,
          confidence REAL NOT NULL,
          confidence_tier TEXT NOT NULL,
          memory_kind TEXT,
          access_count INTEGER,
          last_accessed TEXT
        );

        CREATE TABLE memory_timeline (
          event_id TEXT PRIMARY KEY,
          memory_id TEXT NOT NULL,
          event_type TEXT NOT NULL,
          timestamp TEXT NOT NULL,
          event_order INTEGER NOT NULL,
          actor TEXT NOT NULL,
          reason_code TEXT,
          rule_version TEXT NOT NULL,
          related_memory_ids_json TEXT,
          before_json TEXT,
          after_json TEXT,
          correlation_id TEXT
        );
      `);
      const insert = db.prepare(`
        INSERT INTO memory_current (
          memory_id,
          category,
          status,
          lifecycle_state,
          path_rel,
          created_at,
          updated_at,
          archived_at,
          superseded_at,
          entity_ref,
          source,
          confidence,
          confidence_tier,
          memory_kind,
          access_count,
          last_accessed
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `);
      insert.run(
        "fact-older",
        "fact",
        "active",
        null,
        "facts/2026-03-08/fact-older.md",
        "2026-03-08T00:00:00.000Z",
        "2026-03-08T01:00:00.000Z",
        null,
        null,
        "project-corrupt",
        "test",
        0.8,
        "implied",
        null,
        null,
        null,
      );
      insert.run(
        "fact-recent",
        "fact",
        "active",
        null,
        "facts/2026-03-08/fact-recent.md",
        "2026-03-08T00:00:00.000Z",
        "2026-03-08T05:00:00.000Z",
        null,
        null,
        "project-before",
        "test",
        0.8,
        "implied",
        null,
        null,
        null,
      );
      const insertTimeline = db.prepare(`
        INSERT INTO memory_timeline (
          event_id,
          memory_id,
          event_type,
          timestamp,
          event_order,
          actor,
          reason_code,
          rule_version,
          related_memory_ids_json,
          before_json,
          after_json,
          correlation_id
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `);
      insertTimeline.run(
        "fact-older-created",
        "fact-older",
        "created",
        "2026-03-08T00:00:00.000Z",
        0,
        "system",
        null,
        "legacy",
        null,
        null,
        null,
        null,
      );
      insertTimeline.run(
        "fact-recent-created",
        "fact-recent",
        "created",
        "2026-03-08T00:00:00.000Z",
        0,
        "system",
        null,
        "legacy",
        null,
        null,
        null,
        null,
      );
    } finally {
      db.close();
    }

    const scoped = await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      updatedAfter: "2026-03-08T02:00:00.000Z",
      now: new Date("2026-03-08T06:00:00.000Z"),
    });

    assert.equal(scoped.currentRows, 1);
    const older = readProjectedMemoryState(memoryDir, "fact-older");
    const recent = readProjectedMemoryState(memoryDir, "fact-recent");
    assert.equal(older?.entityRef, "project-corrupt");
    assert.equal(recent?.entityRef, "project-after");
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("scoped rebuild treats updatedBefore as an exclusive upper bound", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-before-boundary-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-inside.md",
      memoryDoc({
        id: "fact-inside",
        content: "Inside the projection window.",
        updated: "2026-03-08T04:59:59.999Z",
      }),
    );
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-boundary.md",
      memoryDoc({
        id: "fact-boundary",
        content: "Exactly on the exclusive projection boundary.",
        updated: "2026-03-08T05:00:00.000Z",
      }),
    );

    const result = await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      updatedBefore: "2026-03-08T05:00:00.000Z",
      now: new Date("2026-03-08T06:00:00.000Z"),
    });

    assert.equal(result.currentRows, 1);
    const db = new Database(getMemoryProjectionPath(memoryDir));
    try {
      const rows = db.prepare("SELECT memory_id FROM memory_current ORDER BY memory_id").all() as Array<{ memory_id: string }>;
      assert.deepEqual(rows.map((row) => row.memory_id), ["fact-inside"]);
    } finally {
      db.close();
    }
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("backupExistingProjection rethrows non-missing stat failures", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-stat-failure-"));
  try {
    await writeFile(path.join(memoryDir, "state"), "not-a-directory", "utf-8");

    await assert.rejects(
      () => backupExistingProjection(
        memoryDir,
        path.join(memoryDir, "state", "memory-projection.sqlite"),
        new Date("2026-03-08T06:00:00.000Z"),
      ),
      /ENOTDIR|not a directory/i,
    );
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("projection browse matches full content beyond preview text for text queries", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-query-fallback-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-query.md",
      memoryDoc({
        id: "fact-query",
        content: `${"alpha ".repeat(60)}needle beyond preview depth`,
        created: "2026-03-08T00:00:00.000Z",
        updated: "2026-03-08T01:00:00.000Z",
      }),
    );

    await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T12:00:00.000Z"),
    });

    const browse = readProjectedMemoryBrowse(memoryDir, {
      query: "needle beyond preview depth",
      status: "active",
      category: "fact",
      limit: 20,
      offset: 0,
    });
    assert.ok(browse !== null);
    assert.equal(browse!.total, 1);
    assert.equal(browse!.memories[0]?.id, "fact-query");
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("rebuildMemoryProjection preserves archived status parity for archived files without explicit status", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-archived-"));
  try {
    await writeText(
      memoryDir,
      "archive/2026-03-08/fact-archived.md",
      `---
id: fact-archived
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T01:00:00.000Z
archivedAt: 2026-03-08T02:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["archived"]
---

archived without explicit status
`,
    );

    const result = await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T12:00:00.000Z"),
    });
    assert.equal(result.currentRows, 1);

    const current = readProjectedMemoryState(memoryDir, "fact-archived");
    assert.ok(current);
    assert.equal(current?.status, "archived");
    assert.equal(current?.archivedAt, "2026-03-08T02:00:00.000Z");
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("rebuildMemoryProjection treats active-plus-archivedAt memories as archived", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-archived-override-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-archived-override.md",
      `---
id: fact-archived-override
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T01:00:00.000Z
status: active
archivedAt: 2026-03-08T02:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["archived"]
---

archivedAt should override active
`,
    );

    await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T12:00:00.000Z"),
    });

    const current = readProjectedMemoryState(memoryDir, "fact-archived-override");
    assert.ok(current);
    assert.equal(current?.status, "archived");
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("rebuildMemoryProjection can refresh only memories inside an updated-at window", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-window-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-older.md",
      `---
id: fact-older
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T01:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
entityRef: project-older
tags: ["older"]
---

older
`,
    );
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-recent.md",
      `---
id: fact-recent
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T03:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
entityRef: project-before
tags: ["recent"]
---

recent
`,
    );

    await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T04:00:00.000Z"),
    });
    {
      const db = new Database(getMemoryProjectionPath(memoryDir));
      try {
        db.prepare("UPDATE memory_current SET entity_ref = ? WHERE memory_id = ?")
          .run("project-corrupt", "fact-older");
      } finally {
        db.close();
      }
    }

    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-recent.md",
      `---
id: fact-recent
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T05:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
entityRef: project-after
tags: ["recent"]
---

recent updated
`,
    );

    const scoped = await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      updatedAfter: "2026-03-08T02:00:00.000Z",
      now: new Date("2026-03-08T06:00:00.000Z"),
    });

    assert.equal(scoped.currentRows, 1);
    const scopedVerify = await verifyMemoryProjection({
      memoryDir,
      updatedAfter: "2026-03-08T02:00:00.000Z",
    });
    assert.equal(scopedVerify.ok, true);
    const older = readProjectedMemoryState(memoryDir, "fact-older");
    const recent = readProjectedMemoryState(memoryDir, "fact-recent");
    assert.equal(older?.entityRef, "project-corrupt");
    assert.equal(recent?.entityRef, "project-after");

    const fullVerify = await verifyMemoryProjection({ memoryDir });
    assert.equal(fullVerify.ok, false);
    assert.deepEqual(fullVerify.mismatchedCurrentMemoryIds, ["fact-older"]);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("scoped rebuild removes deleted memories that still exist in the projection", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-delete-window-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-recent.md",
      `---
id: fact-recent
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T05:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["recent"]
---

recent
`,
    );

    await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T06:00:00.000Z"),
    });

    await rm(path.join(memoryDir, "facts/2026-03-08/fact-recent.md"));

    const scoped = await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      updatedAfter: "2026-03-08T02:00:00.000Z",
      now: new Date("2026-03-08T07:00:00.000Z"),
    });

    assert.equal(scoped.currentRows, 0);
    assert.equal(readProjectedMemoryState(memoryDir, "fact-recent"), null);
    assert.equal(readProjectedMemoryTimeline(memoryDir, "fact-recent", 20), null);

    const verify = await verifyMemoryProjection({ memoryDir });
    assert.equal(verify.ok, true);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("scoped rebuild refreshes native knowledge rows from the latest sync snapshot", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-native-window-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-recent.md",
      `---
id: fact-recent
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T05:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["recent"]
---

recent
`,
    );
    await writeText(
      memoryDir,
      "state/native-knowledge/curated-include-sync.json",
      JSON.stringify(
        {
          version: 1,
          updatedAt: "2026-03-08T06:00:00.000Z",
          files: {
            "docs/identity.md": {
              sourcePath: "docs/identity.md",
              sourceKind: "identity",
              title: "Identity",
              privacyClass: "internal",
              derivedDate: "2026-03-08",
              sourceHash: "hash-initial",
              syncConfigHash: "sync-identity",
              mtimeMs: 1,
              deleted: false,
              chunks: [
                {
                  chunkId: "nk-identity-1",
                  sourcePath: "docs/identity.md",
                  title: "Identity",
                  sourceKind: "identity",
                  startLine: 1,
                  endLine: 2,
                  content: "Initial identity note.",
                  privacyClass: "internal",
                  sourceHash: "hash-initial",
                },
              ],
            },
          },
        },
        null,
        2,
      ),
    );
    const initialGovernance = await runMemoryGovernance({
      memoryDir,
      mode: "shadow",
      now: new Date("2026-03-08T06:10:00.000Z"),
    });

    await rebuildMemoryProjection({
      memoryDir,
      defaultNamespace: "global",
      dryRun: false,
      now: new Date("2026-03-08T06:30:00.000Z"),
    });

    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-recent.md",
      `---
id: fact-recent
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T07:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["recent"]
---

recent updated
`,
    );
    await writeText(
      memoryDir,
      "state/native-knowledge/curated-include-sync.json",
      JSON.stringify(
        {
          version: 1,
          updatedAt: "2026-03-08T07:00:00.000Z",
          files: {
            "docs/identity.md": {
              sourcePath: "docs/identity.md",
              sourceKind: "identity",
              title: "Identity",
              privacyClass: "internal",
              derivedDate: "2026-03-08",
              sourceHash: "hash-updated",
              syncConfigHash: "sync-identity",
              mtimeMs: 2,
              deleted: false,
              chunks: [
                {
                  chunkId: "nk-identity-2",
                  sourcePath: "docs/identity.md",
                  title: "Identity",
                  sourceKind: "identity",
                  startLine: 1,
                  endLine: 2,
                  content: "Updated identity note.",
                  privacyClass: "internal",
                  sourceHash: "hash-updated",
                },
              ],
            },
          },
        },
        null,
        2,
      ),
    );
    const updatedGovernance = await runMemoryGovernance({
      memoryDir,
      mode: "shadow",
      now: new Date("2026-03-08T07:10:00.000Z"),
    });

    const scoped = await rebuildMemoryProjection({
      memoryDir,
      defaultNamespace: "global",
      dryRun: false,
      updatedAfter: "2026-03-08T06:45:00.000Z",
      now: new Date("2026-03-08T07:30:00.000Z"),
    });

    assert.equal(scoped.currentRows, 1);
    assert.equal(scoped.nativeKnowledgeRows, 1);
    assert.equal(scoped.reviewQueueRows, updatedGovernance.reviewQueue.length);
    assert.deepEqual(readProjectedNativeKnowledgeChunks(memoryDir), [
      {
        chunkId: "nk-identity-2",
        sourcePath: "docs/identity.md",
        title: "Identity",
        sourceKind: "identity",
        startLine: 1,
        endLine: 2,
        derivedDate: "2026-03-08",
        sessionKey: undefined,
        workflowKey: undefined,
        author: undefined,
        agent: undefined,
        namespace: "global",
        privacyClass: "internal",
        sourceHash: "hash-updated",
        preview: "Updated identity note.",
      },
    ]);
    const projectedReviewQueue = readProjectedLatestReviewQueue(memoryDir);
    assert.ok(projectedReviewQueue?.found);
    assert.equal(projectedReviewQueue?.runId, updatedGovernance.runId);
    assert.notEqual(projectedReviewQueue?.runId, initialGovernance.runId);

    const verify = await verifyMemoryProjection({ memoryDir, defaultNamespace: "global" });
    assert.equal(verify.ok, true);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("verifyMemoryProjection reports drift when projection is missing current rows", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-verify-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-1.md",
      `---
id: fact-1
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T01:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["alpha"]
---

alpha
`,
    );

    await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T02:00:00.000Z"),
    });

    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-2.md",
      `---
id: fact-2
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T03:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["beta"]
---

beta
`,
    );

    const verify = await verifyMemoryProjection({ memoryDir });
    assert.equal(verify.ok, false);
    assert.deepEqual(verify.missingCurrentMemoryIds, ["fact-2"]);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("repairMemoryProjection dry-run reports drift and write mode repairs it", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-repair-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-1.md",
      `---
id: fact-1
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T01:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["alpha"]
---

alpha
`,
    );

    await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T02:00:00.000Z"),
    });

    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-2.md",
      `---
id: fact-2
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T03:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["beta"]
---

beta
`,
    );

    const dryRun = await repairMemoryProjection({ memoryDir });
    assert.equal(dryRun.dryRun, true);
    assert.equal(dryRun.repaired, false);
    assert.equal(dryRun.verify.ok, false);

    const writeResult = await repairMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T04:00:00.000Z"),
    });
    assert.equal(writeResult.dryRun, false);
    assert.equal(writeResult.repaired, true);
    assert.equal(writeResult.verify.ok, true);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("StorageManager reads archive-path files as archived even without explicit status", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-storage-archived-read-"));
  try {
    await writeText(
      memoryDir,
      "archive/2026-03-08/fact-archived.md",
      `---
id: fact-archived
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T01:00:00.000Z
archivedAt: 2026-03-08T02:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["archived"]
---

archived without explicit status
`,
    );

    const storage = new StorageManager(memoryDir);
    const current = await storage.getProjectedMemoryState("fact-archived");
    assert.ok(current);
    assert.equal(current?.status, "archived");
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("StorageManager treats memories with archivedAt as archived in projected-state fallback", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-storage-archivedat-read-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-archivedat.md",
      `---
id: fact-archivedat
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T01:00:00.000Z
archivedAt: 2026-03-08T02:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["archived"]
---

archivedAt without explicit status
`,
    );

    const storage = new StorageManager(memoryDir);
    const current = await storage.getProjectedMemoryState("fact-archivedat");
    assert.ok(current);
    assert.equal(current?.status, "archived");
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("StorageManager does not infer archived status from archive in ancestor directories", async () => {
  const archiveParent = path.join(os.tmpdir(), "archive");
  await mkdir(archiveParent, { recursive: true });
  const memoryDir = await mkdtemp(path.join(archiveParent, "engram-storage-live-under-archive-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-active.md",
      `---
id: fact-active
category: fact
created: 2026-03-08T00:00:00.000Z
updated: 2026-03-08T01:00:00.000Z
source: test
confidence: 0.8
confidenceTier: implied
tags: ["active"]
---

active memory under archive-named parent
`,
    );

    const storage = new StorageManager(memoryDir);
    const current = await storage.getProjectedMemoryState("fact-active");
    assert.ok(current);
    assert.equal(current?.status, "active");
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("StorageManager projection helpers fail open to markdown and lifecycle ledger", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-storage-projection-fallback-"));
  try {
    const storage = new StorageManager(memoryDir);
    const { id: memoryId } = await storage.writeMemory("fact", "fallback memory", {
      source: "test",
      tags: ["fallback"],
    });
    await storage.updateMemory(memoryId, "fallback memory updated");

    const current = await storage.getProjectedMemoryState(memoryId);
    assert.ok(current);
    assert.equal(current?.status, "active");

    const timeline = await storage.getMemoryTimeline(memoryId);
    assert.deepEqual(
      timeline.map((entry) => entry.eventType),
      ["created", "updated"],
    );

    await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T12:00:00.000Z"),
    });

    const projectedTimeline = await storage.getMemoryTimeline(memoryId);
    assert.deepEqual(
      projectedTimeline.map((entry) => entry.eventType),
      ["created", "updated"],
    );

    const { id: secondId } = await storage.writeMemory("fact", "written after projection rebuild", {
      source: "test",
    });
    await storage.updateMemory(secondId, "written after projection rebuild updated");

    const fallbackAfterProjection = await storage.getMemoryTimeline(secondId);
    assert.deepEqual(
      fallbackAfterProjection.map((entry) => entry.eventType),
      ["created", "updated"],
    );
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("StorageManager falls back when projection database exists but has no timeline row for a memory", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-storage-projection-empty-row-"));
  try {
    const storage = new StorageManager(memoryDir);
    const { id: memoryId } = await storage.writeMemory("fact", "fallback memory", {
      source: "test",
      tags: ["fallback"],
    });
    await storage.updateMemory(memoryId, "fallback memory updated");

    const projectionPath = getMemoryProjectionPath(memoryDir);
    await mkdir(path.dirname(projectionPath), { recursive: true });
    const db = new Database(projectionPath);
    try {
      initializeMemoryProjectionDb(db);
    } finally {
      db.close();
    }

    const timeline = await storage.getMemoryTimeline(memoryId);
    assert.deepEqual(
      timeline.map((entry) => entry.eventType),
      ["created", "updated"],
    );
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("rebuildMemoryProjection projects entity mentions, native knowledge chunks, and governance review queue", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-derived-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-01/fact-duplicate-a.md",
      memoryDoc({
        id: "fact-duplicate-a",
        content: "Projection parity requires complete lifecycle coverage.",
        created: "2026-03-01T00:00:00.000Z",
        updated: "2026-03-01T01:00:00.000Z",
        confidence: 0.95,
        confidenceTier: "explicit",
        entityRef: "person-alex",
        tags: [" projection ", "team:ops", "team:ops"],
      }),
    );
    await writeText(
      memoryDir,
      "facts/2026-03-02/fact-duplicate-b.md",
      memoryDoc({
        id: "fact-duplicate-b",
        content: "Projection parity requires complete lifecycle coverage.",
        created: "2026-03-02T00:00:00.000Z",
        updated: "2026-03-02T01:00:00.000Z",
        confidence: 0.45,
        confidenceTier: "inferred",
        tags: ["projection"],
      }),
    );
    await writeText(
      memoryDir,
      "state/native-knowledge/curated-include-sync.json",
      JSON.stringify(
        {
          version: 1,
          updatedAt: "2026-03-09T12:00:00.000Z",
          files: {
            "docs/identity.md": {
              sourcePath: "docs/identity.md",
              sourceKind: "identity",
              title: "Identity",
              privacyClass: "internal",
              derivedDate: "2026-03-09",
              sourceHash: "hash-identity",
              syncConfigHash: "sync-identity",
              mtimeMs: 1,
              deleted: false,
              chunks: [
                {
                  chunkId: "nk-identity-1",
                  sourcePath: "docs/identity.md",
                  title: "Identity",
                  sourceKind: "identity",
                  startLine: 1,
                  endLine: 3,
                  content: "Alex maintains the Engram memory system.",
                  privacyClass: "internal",
                  sourceHash: "hash-identity",
                },
              ],
            },
          },
        },
        null,
        2,
      ),
    );

    const governance = await runMemoryGovernance({
      memoryDir,
      mode: "shadow",
      now: new Date("2026-03-09T12:00:00.000Z"),
    });
    const result = await rebuildMemoryProjection({
      memoryDir,
      defaultNamespace: "global",
      dryRun: false,
      now: new Date("2026-03-09T12:05:00.000Z"),
    });

    assert.equal(result.entityMentionRows, 2);
    assert.equal(result.nativeKnowledgeRows, 1);
    assert.equal(result.reviewQueueRows >= 1, true);

    const projectedCurrent = readProjectedMemoryState(memoryDir, "fact-duplicate-a");
    assert.ok(projectedCurrent);
    assert.deepEqual(projectedCurrent?.tags, ["projection", "team:ops"]);

    const entityMentions = readProjectedEntityMentions(memoryDir);
    assert.ok(entityMentions);
    assert.deepEqual(
      entityMentions?.map((row) => `${row.memoryId}::${row.entityRef}::${row.mentionSource}`),
      [
        "fact-duplicate-a::person-alex::frontmatter.entityRef",
        "fact-duplicate-a::team:ops::tag",
      ],
    );

    const nativeKnowledge = readProjectedNativeKnowledgeChunks(memoryDir);
    assert.ok(nativeKnowledge);
    assert.equal(nativeKnowledge?.length, 1);
    assert.deepEqual(nativeKnowledge?.[0], {
      chunkId: "nk-identity-1",
      sourcePath: "docs/identity.md",
      title: "Identity",
      sourceKind: "identity",
      startLine: 1,
      endLine: 3,
      derivedDate: "2026-03-09",
      sessionKey: undefined,
      workflowKey: undefined,
      author: undefined,
      agent: undefined,
      namespace: "global",
      privacyClass: "internal",
      sourceHash: "hash-identity",
      preview: "Alex maintains the Engram memory system.",
    });

    const projectedReviewQueue = readProjectedLatestReviewQueue(memoryDir);
    assert.ok(projectedReviewQueue);
    assert.ok(projectedReviewQueue.found);
    assert.equal(projectedReviewQueue.runId, governance.runId);
    assert.equal(projectedReviewQueue.reviewQueue?.some((entry) => entry.reasonCode === "exact_duplicate"), true);

    const verify = await verifyMemoryProjection({ memoryDir, defaultNamespace: "global" });
    assert.equal(verify.ok, true);
    assert.equal(verify.expectedEntityMentionRows, 2);
    assert.equal(verify.actualEntityMentionRows, 2);
    assert.equal(verify.expectedNativeKnowledgeRows, 1);
    assert.equal(verify.actualNativeKnowledgeRows, 1);
    assert.equal(verify.expectedReviewQueueRows >= 1, true);
    assert.equal(verify.expectedReviewQueueRows, verify.actualReviewQueueRows);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("verifyMemoryProjection reports drift in projected entity mentions, native knowledge, and review queue rows", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-derived-drift-"));
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-01/fact-duplicate-a.md",
      memoryDoc({
        id: "fact-duplicate-a",
        content: "Projection parity requires complete lifecycle coverage.",
        created: "2026-03-01T00:00:00.000Z",
        updated: "2026-03-01T01:00:00.000Z",
        confidence: 0.95,
        confidenceTier: "explicit",
        entityRef: "person-alex",
        tags: ["projection", "team:ops"],
      }),
    );
    await writeText(
      memoryDir,
      "facts/2026-03-02/fact-duplicate-b.md",
      memoryDoc({
        id: "fact-duplicate-b",
        content: "Projection parity requires complete lifecycle coverage.",
        created: "2026-03-02T00:00:00.000Z",
        updated: "2026-03-02T01:00:00.000Z",
        confidence: 0.45,
        confidenceTier: "inferred",
        tags: ["projection"],
      }),
    );
    await writeText(
      memoryDir,
      "state/native-knowledge/curated-include-sync.json",
      JSON.stringify(
        {
          version: 1,
          updatedAt: "2026-03-09T12:00:00.000Z",
          files: {
            "docs/identity.md": {
              sourcePath: "docs/identity.md",
              sourceKind: "identity",
              title: "Identity",
              privacyClass: "internal",
              derivedDate: "2026-03-09",
              sourceHash: "hash-identity",
              syncConfigHash: "sync-identity",
              mtimeMs: 1,
              deleted: false,
              chunks: [
                {
                  chunkId: "nk-identity-1",
                  sourcePath: "docs/identity.md",
                  title: "Identity",
                  sourceKind: "identity",
                  startLine: 1,
                  endLine: 3,
                  content: "Alex maintains the Engram memory system.",
                  privacyClass: "internal",
                  sourceHash: "hash-identity",
                },
              ],
            },
          },
        },
        null,
        2,
      ),
    );

    const governance = await runMemoryGovernance({
      memoryDir,
      mode: "shadow",
      now: new Date("2026-03-09T12:00:00.000Z"),
    });
    await rebuildMemoryProjection({
      memoryDir,
      defaultNamespace: "global",
      dryRun: false,
      now: new Date("2026-03-09T12:05:00.000Z"),
    });

    const db = new Database(getMemoryProjectionPath(memoryDir));
    try {
      db.prepare(`
        DELETE FROM memory_entity_mentions
        WHERE memory_id = ? AND entity_ref = ? AND mention_source = ?
      `).run("fact-duplicate-a", "person-alex", "frontmatter.entityRef");
      db.prepare("DELETE FROM native_knowledge_chunks WHERE chunk_id = ?").run("nk-identity-1");
      db.prepare("DELETE FROM memory_review_queue WHERE run_id = ? AND entry_id = ?").run(
        governance.runId,
        "review:fact-duplicate-b:exact_duplicate",
      );
    } finally {
      db.close();
    }

    const verify = await verifyMemoryProjection({ memoryDir, defaultNamespace: "global" });
    assert.equal(verify.ok, false);
    assert.deepEqual(
      verify.missingEntityMentionKeys,
      ["fact-duplicate-a::person-alex::frontmatter.entityRef"],
    );
    assert.deepEqual(verify.missingNativeKnowledgeChunkIds, ["nk-identity-1"]);
    assert.deepEqual(
      verify.missingReviewQueueEntryIds,
      [`${governance.runId}::review:fact-duplicate-b:exact_duplicate`],
    );
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("projection browse filters realpath-invalid rows before counting and paginating", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-invalid-page-"));
  const outsidePath = path.join(os.tmpdir(), `engram-memory-projection-outside-${process.pid}-${Date.now()}.md`);
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-valid.md",
      memoryDoc({
        id: "fact-valid",
        content: "valid projected memory",
        created: "2026-03-08T00:00:00.000Z",
        updated: "2026-03-08T01:00:00.000Z",
      }),
    );
    await writeText(
      memoryDir,
      "facts/2026-03-08/fact-invalid.md",
      memoryDoc({
        id: "fact-invalid",
        content: "invalid projected memory",
        created: "2026-03-08T00:00:00.000Z",
        updated: "2026-03-08T02:00:00.000Z",
      }),
    );

    await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T12:00:00.000Z"),
    });
    await writeFile(outsidePath, "symlink escape", "utf-8");
    await symlink(outsidePath, path.join(memoryDir, "facts/2026-03-08/fact-invalid-link.md"));
    const db = new Database(getMemoryProjectionPath(memoryDir));
    try {
      db.prepare("UPDATE memory_current SET path_rel = ?, path_valid = 0 WHERE memory_id = ?")
        .run("facts/2026-03-08/fact-invalid-link.md", "fact-invalid");
    } finally {
      db.close();
    }

    const browse = readProjectedMemoryBrowse(memoryDir, {
      limit: 1,
      offset: 0,
    });

    assert.ok(browse);
    assert.equal(browse.total, 1);
    assert.deepEqual(browse.memories.map((memory) => memory.id), ["fact-valid"]);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
    await rm(outsidePath, { force: true });
  }
});
test("projection browse validates only the returned page for current schemas", async (t) => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-page-cost-"));
  try {
    const projectionPath = getMemoryProjectionPath(memoryDir);
    await mkdir(path.dirname(projectionPath), { recursive: true });
    await writeText(memoryDir, "facts/page.md", "page row");
    const db = new Database(projectionPath);
    try {
      initializeMemoryProjectionDb(db);
      const insert = db.prepare(`
        INSERT INTO memory_current (
          memory_id, category, status, lifecycle_state, path_rel, path_valid,
          created_at, updated_at, archived_at, superseded_at, entity_ref,
          source, confidence, confidence_tier, memory_kind, access_count,
          last_accessed, tags_json, preview_text
        ) VALUES (?, 'fact', 'active', NULL, 'facts/page.md', 1,
          ?, ?, NULL, NULL, NULL, 'test', 0.8, 'implied', NULL, NULL, NULL, '[]', '')
      `);
      const insertMany = db.transaction(() => {
        for (let index = 0; index < 3_000; index += 1) {
          const timestamp = new Date(Date.UTC(2026, 2, 8, 0, 0, 0, index)).toISOString();
          insert.run(`fact-${String(index).padStart(4, "0")}`, timestamp, timestamp);
        }
      });
      insertMany();
    } finally {
      db.close();
    }

    const fs = process.getBuiltinModule("fs");
    const originalRealpathSync = fs.realpathSync;
    let realpathCalls = 0;
    t.mock.method(fs, "realpathSync", (...args: Parameters<typeof fs.realpathSync>) => {
      realpathCalls += 1;
      return originalRealpathSync(...args);
    });

    const browse = readProjectedMemoryBrowse(memoryDir, {
      limit: 1,
      offset: 0,
    });

    assert.ok(browse);
    assert.equal(browse.total, 3_000);
    assert.equal(browse.memories.length, 1);
    assert.ok(realpathCalls <= 2);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});
test("projection browse fills pages after a managed archive updates path validity", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-moved-page-"));
  try {
    for (let index = 1; index <= 3; index += 1) {
      await writeText(
        memoryDir,
        `facts/2026-03-08/fact-${index}.md`,
        memoryDoc({
          id: `fact-${index}`,
          content: `projected memory ${index}`,
          created: `2026-03-08T0${index}:00:00.000Z`,
          updated: `2026-03-08T0${index}:00:00.000Z`,
        }),
      );
    }
    await rebuildMemoryProjection({
      memoryDir,
      dryRun: false,
      now: new Date("2026-03-08T12:00:00.000Z"),
    });
    const storage = new StorageManager(memoryDir);
    const archivedMemory = (await storage.readAllMemories())
      .find((memory) => memory.frontmatter.id === "fact-3");
    assert.ok(archivedMemory);
    assert.equal(typeof await storage.archiveMemory(archivedMemory), "string");

    const firstBrowse = readProjectedMemoryBrowse(memoryDir, {
      limit: 2,
      offset: 0,
    });
    assert.ok(firstBrowse);
    assert.deepEqual(firstBrowse.memories.map((memory) => memory.id), ["fact-2", "fact-1"]);
    assert.equal(firstBrowse.total, 2);

    const secondBrowse = readProjectedMemoryBrowse(memoryDir, {
      limit: 2,
      offset: 0,
    });
    assert.ok(secondBrowse);
    assert.deepEqual(secondBrowse.memories.map((memory) => memory.id), ["fact-2", "fact-1"]);
    assert.equal(secondBrowse.total, 2);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});
test("markProjectedMemoryPathInvalid removes a row from projected totals", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-mark-invalid-"));
  try {
    await writeText(memoryDir, "facts/fact-invalid.md", memoryDoc({
      id: "fact-invalid",
      content: "projected memory",
    }));
    await rebuildMemoryProjection({ memoryDir, dryRun: false });

    markProjectedMemoryPathInvalid(memoryDir, "fact-invalid");

    assert.equal(readProjectedMemoryBrowse(memoryDir, { limit: 1, offset: 0 })?.total, 0);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("updateProjectedMemoryPath follows a moved memory file", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-update-path-"));
  try {
    await writeText(memoryDir, "facts/fact-moved.md", memoryDoc({
      id: "fact-moved",
      content: "projected memory",
    }));
    await rebuildMemoryProjection({ memoryDir, dryRun: false });

    updateProjectedMemoryPath(memoryDir, "fact-moved", "cold/facts/fact-moved.md");

    const db = new Database(getMemoryProjectionPath(memoryDir), { readonly: true });
    try {
      const row = db.prepare("SELECT path_rel, path_valid FROM memory_current WHERE memory_id = ?")
        .get("fact-moved") as { path_rel: string; path_valid: number };
      assert.deepEqual(row, { path_rel: "cold/facts/fact-moved.md", path_valid: 1 });
    } finally {
      db.close();
    }
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("tier migration updates the projected memory path", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-tier-move-"));
  try {
    const storage = new StorageManager(memoryDir);
    await storage.writeMemory("fact", "projected memory", { source: "test" });
    const memory = (await storage.readAllMemories())[0];
    assert.ok(memory);
    await rebuildMemoryProjection({ memoryDir, dryRun: false });

    const migrated = await storage.migrateMemoryToTier(memory, "cold");
    const projected = readProjectedMemoryBrowse(memoryDir, { limit: 1, offset: 0 });

    assert.ok(projected);
    assert.equal(projected.memories[0]?.path, migrated.targetPath);
    assert.equal(projected.total, 1);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("projection browse drops unmanaged missing files but leaves totals stale", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-unmanaged-rm-"));
  try {
    for (let index = 1; index <= 3; index += 1) {
      await writeText(
        memoryDir,
        `facts/fact-${index}.md`,
        memoryDoc({
          id: `fact-${index}`,
          content: `projected memory ${index}`,
          updated: `2026-03-08T0${index}:00:00.000Z`,
        }),
      );
    }
    await rebuildMemoryProjection({ memoryDir, dryRun: false });
    await rm(path.join(memoryDir, "facts", "fact-3.md"));

    const browse = readProjectedMemoryBrowse(memoryDir, { limit: 2, offset: 0 });

    assert.ok(browse);
    assert.deepEqual(browse.memories.map((memory) => memory.id), ["fact-2", "fact-1"]);
    assert.equal(browse.total, 3);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});
test("projection text browse reuses the resolved path for returned rows", async (t) => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-query-resolve-"));
  try {
    await writeText(memoryDir, "facts/fact-query.md", memoryDoc({
      id: "fact-query",
      content: "query resolution needle",
    }));
    await rebuildMemoryProjection({ memoryDir, dryRun: false });
    const fs = process.getBuiltinModule("fs");
    const originalRealpathSync = fs.realpathSync;
    let realpathCalls = 0;
    t.mock.method(fs, "realpathSync", (...args: Parameters<typeof fs.realpathSync>) => {
      realpathCalls += 1;
      return originalRealpathSync(...args);
    });

    const browse = readProjectedMemoryBrowse(memoryDir, {
      query: "query resolution needle",
      limit: 1,
      offset: 0,
    });

    assert.equal(browse?.memories[0]?.id, "fact-query");
    assert.ok(realpathCalls <= 2);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("projection text browse drops deleted files that match on stored metadata", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-query-deleted-"));
  try {
    for (const suffix of ["kept", "gone"]) {
      await writeText(memoryDir, `facts/fact-${suffix}.md`, memoryDoc({
        id: `fact-${suffix}`,
        content: `shared metadata needle ${suffix}`,
      }));
    }
    await rebuildMemoryProjection({ memoryDir, dryRun: false });
    await rm(path.join(memoryDir, "facts", "fact-gone.md"));

    const browse = readProjectedMemoryBrowse(memoryDir, {
      query: "shared metadata needle",
      limit: 10,
      offset: 0,
    });

    assert.ok(browse);
    assert.deepEqual(browse.memories.map((memory) => memory.id), ["fact-kept"]);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("rebuildMemoryProjection keeps hot duplicate IDs and reports skipped cold and blank records", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-anomalies-"));
  const warnings: string[] = [];
  const logger = {
    info() {},
    warn(message: string) { warnings.push(message); },
    error() {},
  };
  initLogger(logger);
  try {
    await writeText(
      memoryDir,
      "facts/2026-03-08/hot.md",
      memoryDoc({ id: "duplicate-id", content: "hot winner" }),
    );
    await writeText(
      memoryDir,
      "cold/facts/2026-03-08/cold.md",
      memoryDoc({ id: "duplicate-id", content: "cold loser" }),
    );
    await writeText(
      memoryDir,
      "facts/2026-03-08/blank-a.md",
      memoryDoc({ id: "", content: "blank one" }),
    );
    await writeText(
      memoryDir,
      "archive/2026-03-08/blank-b.md",
      memoryDoc({ id: "   ", content: "blank two" }),
    );
    const duplicateEvent = {
      eventId: "duplicate-event",
      memoryId: "duplicate-id",
      eventType: "created",
      timestamp: "2026-03-08T00:00:00.000Z",
      actor: "test",
      ruleVersion: "memory-lifecycle-ledger.v1",
    };
    await writeText(
      memoryDir,
      "state/memory-lifecycle-ledger.jsonl",
      `${JSON.stringify(duplicateEvent)}\n${JSON.stringify(duplicateEvent)}\n`,
    );

    const result = await rebuildMemoryProjection({ memoryDir, dryRun: false });

    assert.equal(result.currentRows, 1);
    assert.equal(readProjectedMemoryState(memoryDir, "duplicate-id")?.preview, "hot winner");
    assert.equal(result.skippedDuplicateMemories.length, 1);
    assert.equal(result.skippedDuplicateTimelineEvents.length, 1);
    assert.equal(result.skippedBlankIdMemories.length, 2);
    assert.match(result.skippedDuplicateMemories[0]!.keptPath, /facts\/2026-03-08\/hot\.md$/);
    assert.match(result.skippedDuplicateMemories[0]!.skippedPath, /cold\/facts\/2026-03-08\/cold\.md$/);
    assert.equal(warnings.some((message) => message.includes("duplicate-id") && message.includes("hot.md") && message.includes("cold.md")), true);
    assert.equal(warnings.filter((message) => message.includes("blank memory id")).length, 2);
  } finally {
    initLogger();
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("StorageManager rejects blank frontmatter IDs before writing", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-blank-id-write-"));
  try {
    const storage = new StorageManager(memoryDir);
    const { id } = await storage.writeMemory("fact", "valid memory", { source: "test" });
    const memory = await storage.getMemoryById(id);
    assert.ok(memory);

    await assert.rejects(
      () => storage.writeMemoryFrontmatter(memory!, { id: " \t " }),
      /memory frontmatter id must not be blank/,
    );
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("StorageManager rate-limits warnings for projection fallbacks by consumer", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-warning-"));
  const warnings: string[] = [];
  const originalDateNow = Date.now;
  initLogger({
    info() {},
    warn(message) { warnings.push(message); },
    error() {},
  });
  try {
    const storage = new StorageManager(memoryDir);
    const { id } = await storage.writeMemory("fact", "fallback warning", { source: "test" });
    let now = originalDateNow();
    Date.now = () => now;

    await storage.getMemoryTimeline(id);
    await storage.getMemoryTimeline(id);
    await storage.getProjectedMemoryState(id);
    await storage.getProjectedMemoryState(id);
    await storage.browseProjectedMemories({ limit: 10, offset: 0 });
    await storage.browseProjectedMemories({ limit: 10, offset: 0 });
    now += 5 * 60_000;
    await storage.getMemoryTimeline(id);

    assert.equal(
      warnings.filter((message) => message.includes("getMemoryTimeline") && message.includes("projection")).length,
      2,
    );
    assert.equal(
      warnings.filter((message) => message.includes("getProjectedMemoryState") && message.includes("projection")).length,
      1,
    );
    assert.equal(
      warnings.filter((message) => message.includes("browseProjectedMemories") && message.includes("projection")).length,
      1,
    );
  } finally {
    Date.now = originalDateNow;
    initLogger();
    await rm(memoryDir, { recursive: true, force: true });
  }
});

test("updateProjectedMemoryPath derives validity for symlink-escaping targets", async () => {
  const memoryDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-move-escape-"));
  const outsideDir = await mkdtemp(path.join(os.tmpdir(), "engram-memory-projection-move-outside-"));
  try {
    await writeText(memoryDir, "facts/fact-escape.md", memoryDoc({
      id: "fact-escape",
      content: "projected memory",
    }));
    await rebuildMemoryProjection({ memoryDir, dryRun: false });

    const outsidePath = path.join(outsideDir, "escaped.md");
    await writeFile(outsidePath, "escaped body", "utf-8");
    await mkdir(path.join(memoryDir, "cold", "facts"), { recursive: true });
    await symlink(outsidePath, path.join(memoryDir, "cold", "facts", "fact-escape.md"));

    updateProjectedMemoryPath(memoryDir, "fact-escape", "cold/facts/fact-escape.md");

    const browse = readProjectedMemoryBrowse(memoryDir, { limit: 5, offset: 0 });
    assert.ok(browse);
    assert.equal(browse.total, 0);
    assert.equal(browse.memories.length, 0);
  } finally {
    await rm(memoryDir, { recursive: true, force: true });
    await rm(outsideDir, { recursive: true, force: true });
  }
});
