/**
 * Tests for scripts/lib/session-manager.js
 *
 * Run with: node tests/lib/session-manager.test.js
 */

const assert = require('assert');
const path = require('path');
const fs = require('fs');
const os = require('os');

const sessionManager = require('../../scripts/lib/session-manager');

// Test helper
function test(name, fn) {
  try {
    fn();
    console.log(`  \u2713 ${name}`);
    return true;
  } catch (err) {
    console.log(`  \u2717 ${name}`);
    console.log(`    Error: ${err.message}`);
    return false;
  }
}

// Create a temp directory for session tests
function createTempSessionDir() {
  const dir = path.join(os.tmpdir(), `ecc-test-sessions-${Date.now()}`);
  fs.mkdirSync(dir, { recursive: true });
  return dir;
}

function cleanup(dir) {
  try {
    fs.rmSync(dir, { recursive: true, force: true });
  } catch {
    // best-effort cleanup
  }
}

function runTests() {
  console.log('\n=== Testing session-manager.js ===\n');

  let passed = 0;
  let failed = 0;

  // parseSessionFilename tests
  console.log('parseSessionFilename:');

  if (test('parses new format with short ID', () => {
    const result = sessionManager.parseSessionFilename('2026-02-01-a1b2c3d4-session.tmp');
    assert.ok(result);
    assert.strictEqual(result.shortId, 'a1b2c3d4');
    assert.strictEqual(result.date, '2026-02-01');
    assert.strictEqual(result.filename, '2026-02-01-a1b2c3d4-session.tmp');
  })) passed++; else failed++;

  if (test('parses old format without short ID', () => {
    const result = sessionManager.parseSessionFilename('2026-01-17-session.tmp');
    assert.ok(result);
    assert.strictEqual(result.shortId, 'no-id');
    assert.strictEqual(result.date, '2026-01-17');
  })) passed++; else failed++;

  if (test('returns null for invalid filename', () => {
    assert.strictEqual(sessionManager.parseSessionFilename('not-a-session.txt'), null);
    assert.strictEqual(sessionManager.parseSessionFilename(''), null);
    assert.strictEqual(sessionManager.parseSessionFilename('random.tmp'), null);
  })) passed++; else failed++;

  if (test('returns null for malformed date', () => {
    assert.strictEqual(sessionManager.parseSessionFilename('20260-01-17-session.tmp'), null);
    assert.strictEqual(sessionManager.parseSessionFilename('26-01-17-session.tmp'), null);
  })) passed++; else failed++;

  if (test('parses long short IDs (8+ chars)', () => {
    const result = sessionManager.parseSessionFilename('2026-02-01-abcdef12345678-session.tmp');
    assert.ok(result);
    assert.strictEqual(result.shortId, 'abcdef12345678');
  })) passed++; else failed++;

  if (test('rejects short IDs less than 8 chars', () => {
    const result = sessionManager.parseSessionFilename('2026-02-01-abc-session.tmp');
    assert.strictEqual(result, null);
  })) passed++; else failed++;

  // parseSessionMetadata tests
  console.log('\nparseSessionMetadata:');

  if (test('parses full session content', () => {
    const content = `# My Session Title

**Date:** 2026-02-01
**Started:** 10:30
**Last Updated:** 14:45

### Completed
- [x] Set up project
- [x] Write tests

### In Progress
- [ ] Fix bug

### Notes for Next Session
Remember to check the logs

### Context to Load
\`\`\`
src/main.ts
\`\`\``;
    const meta = sessionManager.parseSessionMetadata(content);
    assert.strictEqual(meta.title, 'My Session Title');
    assert.strictEqual(meta.date, '2026-02-01');
    assert.strictEqual(meta.started, '10:30');
    assert.strictEqual(meta.lastUpdated, '14:45');
    assert.strictEqual(meta.completed.length, 2);
    assert.strictEqual(meta.completed[0], 'Set up project');
    assert.strictEqual(meta.inProgress.length, 1);
    assert.strictEqual(meta.inProgress[0], 'Fix bug');
    assert.strictEqual(meta.notes, 'Remember to check the logs');
    assert.strictEqual(meta.context, 'src/main.ts');
  })) passed++; else failed++;

  if (test('handles null/undefined/empty content', () => {
    const meta1 = sessionManager.parseSessionMetadata(null);
    assert.strictEqual(meta1.title, null);
    assert.deepStrictEqual(meta1.completed, []);

    const meta2 = sessionManager.parseSessionMetadata(undefined);
    assert.strictEqual(meta2.title, null);

    const meta3 = sessionManager.parseSessionMetadata('');
    assert.strictEqual(meta3.title, null);
  })) passed++; else failed++;

  if (test('handles content with no sections', () => {
    const meta = sessionManager.parseSessionMetadata('Just some text');
    assert.strictEqual(meta.title, null);
    assert.deepStrictEqual(meta.completed, []);
    assert.deepStrictEqual(meta.inProgress, []);
  })) passed++; else failed++;

  // getSessionStats tests
  console.log('\ngetSessionStats:');

  if (test('calculates stats from content string', () => {
    const content = `# Test Session

### Completed
- [x] Task 1
- [x] Task 2

### In Progress
- [ ] Task 3
`;
    const stats = sessionManager.getSessionStats(content);
    assert.strictEqual(stats.totalItems, 3);
    assert.strictEqual(stats.completedItems, 2);
    assert.strictEqual(stats.inProgressItems, 1);
    assert.ok(stats.lineCount > 0);
  })) passed++; else failed++;

  if (test('handles empty content', () => {
    const stats = sessionManager.getSessionStats('');
    assert.strictEqual(stats.totalItems, 0);
    assert.strictEqual(stats.completedItems, 0);
    assert.strictEqual(stats.lineCount, 0);
  })) passed++; else failed++;

  if (test('does not treat non-absolute path as file path', () => {
    // This tests the bug fix: content that ends with .tmp but is not a path
    const stats = sessionManager.getSessionStats('Some content ending with test.tmp');
    assert.strictEqual(stats.totalItems, 0);
    assert.strictEqual(stats.lineCount, 1);
  })) passed++; else failed++;

  // File I/O tests
  console.log('\nSession CRUD:');

  if (test('writeSessionContent and getSessionContent round-trip', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, '2026-02-01-testid01-session.tmp');
      const content = '# Test Session\n\nHello world';

      const writeResult = sessionManager.writeSessionContent(sessionPath, content);
      assert.strictEqual(writeResult, true);

      const readContent = sessionManager.getSessionContent(sessionPath);
      assert.strictEqual(readContent, content);
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  if (test('appendSessionContent appends to existing', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, '2026-02-01-testid02-session.tmp');
      sessionManager.writeSessionContent(sessionPath, 'Line 1\n');
      sessionManager.appendSessionContent(sessionPath, 'Line 2\n');

      const content = sessionManager.getSessionContent(sessionPath);
      assert.ok(content.includes('Line 1'));
      assert.ok(content.includes('Line 2'));
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  if (test('writeSessionContent returns false for invalid path', () => {
    const result = sessionManager.writeSessionContent('/nonexistent/deep/path/session.tmp', 'content');
    assert.strictEqual(result, false);
  })) passed++; else failed++;

  if (test('getSessionContent returns null for non-existent file', () => {
    const result = sessionManager.getSessionContent('/nonexistent/session.tmp');
    assert.strictEqual(result, null);
  })) passed++; else failed++;

  if (test('deleteSession removes file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'test-session.tmp');
      fs.writeFileSync(sessionPath, 'content');
      assert.strictEqual(fs.existsSync(sessionPath), true);

      const result = sessionManager.deleteSession(sessionPath);
      assert.strictEqual(result, true);
      assert.strictEqual(fs.existsSync(sessionPath), false);
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  if (test('deleteSession returns false for non-existent file', () => {
    const result = sessionManager.deleteSession('/nonexistent/session.tmp');
    assert.strictEqual(result, false);
  })) passed++; else failed++;

  if (test('sessionExists returns true for existing file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'test.tmp');
      fs.writeFileSync(sessionPath, 'content');
      assert.strictEqual(sessionManager.sessionExists(sessionPath), true);
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  if (test('sessionExists returns false for non-existent file', () => {
    assert.strictEqual(sessionManager.sessionExists('/nonexistent/path.tmp'), false);
  })) passed++; else failed++;

  if (test('sessionExists returns false for directory', () => {
    const dir = createTempSessionDir();
    try {
      assert.strictEqual(sessionManager.sessionExists(dir), false);
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  // getSessionSize tests
  console.log('\ngetSessionSize:');

  if (test('returns human-readable size for existing file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'sized.tmp');
      fs.writeFileSync(sessionPath, 'x'.repeat(2048));
      const size = sessionManager.getSessionSize(sessionPath);
      assert.ok(size.includes('KB'), `Expected KB, got: ${size}`);
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  if (test('returns "0 B" for non-existent file', () => {
    const size = sessionManager.getSessionSize('/nonexistent/file.tmp');
    assert.strictEqual(size, '0 B');
  })) passed++; else failed++;

  if (test('returns bytes for small file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'small.tmp');
      fs.writeFileSync(sessionPath, 'hi');
      const size = sessionManager.getSessionSize(sessionPath);
      assert.ok(size.includes('B'));
      assert.ok(!size.includes('KB'));
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  // getSessionTitle tests
  console.log('\ngetSessionTitle:');

  if (test('extracts title from session file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'titled.tmp');
      fs.writeFileSync(sessionPath, '# My Great Session\n\nSome content');
      const title = sessionManager.getSessionTitle(sessionPath);
      assert.strictEqual(title, 'My Great Session');
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  if (test('returns "Untitled Session" for empty content', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'empty.tmp');
      fs.writeFileSync(sessionPath, '');
      const title = sessionManager.getSessionTitle(sessionPath);
      assert.strictEqual(title, 'Untitled Session');
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  if (test('returns "Untitled Session" for non-existent file', () => {
    const title = sessionManager.getSessionTitle('/nonexistent/file.tmp');
    assert.strictEqual(title, 'Untitled Session');
  })) passed++; else failed++;

  // getAllSessions tests
  console.log('\ngetAllSessions:');

  // Override HOME to a temp dir for isolated getAllSessions/getSessionById tests
  // On Windows, os.homedir() uses USERPROFILE, not HOME — set both for cross-platform
  const tmpHome = path.join(os.tmpdir(), `ecc-session-mgr-test-${Date.now()}`);
  const tmpSessionsDir = path.join(tmpHome, '.claude', 'sessions');
  fs.mkdirSync(tmpSessionsDir, { recursive: true });
  const origHome = process.env.HOME;
  const origUserProfile = process.env.USERPROFILE;

  // Create test session files with controlled modification times
  const testSessions = [
    { name: '2026-01-15-abcd1234-session.tmp', content: '# Session 1' },
    { name: '2026-01-20-efgh5678-session.tmp', content: '# Session 2' },
    { name: '2026-02-01-ijkl9012-session.tmp', content: '# Session 3' },
    { name: '2026-02-01-mnop3456-session.tmp', content: '# Session 4' },
    { name: '2026-02-10-session.tmp', content: '# Old format session' },
  ];
  for (let i = 0; i < testSessions.length; i++) {
    const filePath = path.join(tmpSessionsDir, testSessions[i].name);
    fs.writeFileSync(filePath, testSessions[i].content);
    // Stagger modification times so sort order is deterministic
    const mtime = new Date(Date.now() - (testSessions.length - i) * 60000);
    fs.utimesSync(filePath, mtime, mtime);
  }

  process.env.HOME = tmpHome;
  process.env.USERPROFILE = tmpHome;

  if (test('getAllSessions returns all sessions', () => {
    const result = sessionManager.getAllSessions({ limit: 100 });
    assert.strictEqual(result.total, 5);
    assert.strictEqual(result.sessions.length, 5);
    assert.strictEqual(result.hasMore, false);
  })) passed++; else failed++;

  if (test('getAllSessions paginates correctly', () => {
    const page1 = sessionManager.getAllSessions({ limit: 2, offset: 0 });
    assert.strictEqual(page1.sessions.length, 2);
    assert.strictEqual(page1.hasMore, true);
    assert.strictEqual(page1.total, 5);

    const page2 = sessionManager.getAllSessions({ limit: 2, offset: 2 });
    assert.strictEqual(page2.sessions.length, 2);
    assert.strictEqual(page2.hasMore, true);

    const page3 = sessionManager.getAllSessions({ limit: 2, offset: 4 });
    assert.strictEqual(page3.sessions.length, 1);
    assert.strictEqual(page3.hasMore, false);
  })) passed++; else failed++;

  if (test('getAllSessions filters by date', () => {
    const result = sessionManager.getAllSessions({ date: '2026-02-01', limit: 100 });
    assert.strictEqual(result.total, 2);
    assert.ok(result.sessions.every(s => s.date === '2026-02-01'));
  })) passed++; else failed++;

  if (test('getAllSessions filters by search (short ID)', () => {
    const result = sessionManager.getAllSessions({ search: 'abcd', limit: 100 });
    assert.strictEqual(result.total, 1);
    assert.strictEqual(result.sessions[0].shortId, 'abcd1234');
  })) passed++; else failed++;

  if (test('getAllSessions returns sorted by newest first', () => {
    const result = sessionManager.getAllSessions({ limit: 100 });
    for (let i = 1; i < result.sessions.length; i++) {
      assert.ok(
        result.sessions[i - 1].modifiedTime >= result.sessions[i].modifiedTime,
        'Sessions should be sorted newest first'
      );
    }
  })) passed++; else failed++;

  if (test('getAllSessions handles offset beyond total', () => {
    const result = sessionManager.getAllSessions({ offset: 999, limit: 10 });
    assert.strictEqual(result.sessions.length, 0);
    assert.strictEqual(result.total, 5);
    assert.strictEqual(result.hasMore, false);
  })) passed++; else failed++;

  if (test('getAllSessions returns empty for non-existent date', () => {
    const result = sessionManager.getAllSessions({ date: '2099-12-31', limit: 100 });
    assert.strictEqual(result.total, 0);
    assert.strictEqual(result.sessions.length, 0);
  })) passed++; else failed++;

  if (test('getAllSessions ignores non-.tmp files', () => {
    fs.writeFileSync(path.join(tmpSessionsDir, 'notes.txt'), 'not a session');
    fs.writeFileSync(path.join(tmpSessionsDir, 'compaction-log.txt'), 'log');
    const result = sessionManager.getAllSessions({ limit: 100 });
    assert.strictEqual(result.total, 5, 'Should only count .tmp session files');
  })) passed++; else failed++;

  // getSessionById tests
  console.log('\ngetSessionById:');

  if (test('getSessionById finds by short ID prefix', () => {
    const result = sessionManager.getSessionById('abcd1234');
    assert.ok(result, 'Should find session by exact short ID');
    assert.strictEqual(result.shortId, 'abcd1234');
  })) passed++; else failed++;

  if (test('getSessionById finds by short ID prefix match', () => {
    const result = sessionManager.getSessionById('abcd');
    assert.ok(result, 'Should find session by short ID prefix');
    assert.strictEqual(result.shortId, 'abcd1234');
  })) passed++; else failed++;

  if (test('getSessionById finds by full filename', () => {
    const result = sessionManager.getSessionById('2026-01-15-abcd1234-session.tmp');
    assert.ok(result, 'Should find session by full filename');
    assert.strictEqual(result.shortId, 'abcd1234');
  })) passed++; else failed++;

  if (test('getSessionById finds by filename without .tmp', () => {
    const result = sessionManager.getSessionById('2026-01-15-abcd1234-session');
    assert.ok(result, 'Should find session by filename without extension');
  })) passed++; else failed++;

  if (test('getSessionById returns null for non-existent ID', () => {
    const result = sessionManager.getSessionById('zzzzzzzz');
    assert.strictEqual(result, null);
  })) passed++; else failed++;

  if (test('getSessionById includes content when requested', () => {
    const result = sessionManager.getSessionById('abcd1234', true);
    assert.ok(result, 'Should find session');
    assert.ok(result.content, 'Should include content');
    assert.ok(result.content.includes('Session 1'), 'Content should match');
  })) passed++; else failed++;

  if (test('getSessionById finds old format (no short ID)', () => {
    const result = sessionManager.getSessionById('2026-02-10-session');
    assert.ok(result, 'Should find old-format session by filename');
  })) passed++; else failed++;

  if (test('getSessionById returns null for empty string', () => {
    const result = sessionManager.getSessionById('');
    assert.strictEqual(result, null, 'Empty string should not match any session');
  })) passed++; else failed++;

  if (test('getSessionById metadata and stats populated when includeContent=true', () => {
    const result = sessionManager.getSessionById('abcd1234', true);
    assert.ok(result, 'Should find session');
    assert.ok(result.metadata, 'Should have metadata');
    assert.ok(result.stats, 'Should have stats');
    assert.strictEqual(typeof result.stats.totalItems, 'number', 'stats.totalItems should be number');
    assert.strictEqual(typeof result.stats.lineCount, 'number', 'stats.lineCount should be number');
  })) passed++; else failed++;

  // parseSessionMetadata edge cases
  console.log('\nparseSessionMetadata (edge cases):');

  if (test('handles CRLF line endings', () => {
    const content = '# CRLF Session\r\n\r\n**Date:** 2026-03-01\r\n**Started:** 09:00\r\n\r\n### Completed\r\n- [x] Task A\r\n- [x] Task B\r\n';
    const meta = sessionManager.parseSessionMetadata(content);
    assert.strictEqual(meta.title, 'CRLF Session');
    assert.strictEqual(meta.date, '2026-03-01');
    assert.strictEqual(meta.started, '09:00');
    assert.strictEqual(meta.completed.length, 2);
  })) passed++; else failed++;

  if (test('takes first h1 heading as title', () => {
    const content = '# First Title\n\nSome text\n\n# Second Title\n';
    const meta = sessionManager.parseSessionMetadata(content);
    assert.strictEqual(meta.title, 'First Title');
  })) passed++; else failed++;

  if (test('handles empty sections (Completed with no items)', () => {
    const content = '# Session\n\n### Completed\n\n### In Progress\n\n';
    const meta = sessionManager.parseSessionMetadata(content);
    assert.deepStrictEqual(meta.completed, []);
    assert.deepStrictEqual(meta.inProgress, []);
  })) passed++; else failed++;

  if (test('handles content with only title and notes', () => {
    const content = '# Just Notes\n\n### Notes for Next Session\nRemember to test\n';
    const meta = sessionManager.parseSessionMetadata(content);
    assert.strictEqual(meta.title, 'Just Notes');
    assert.strictEqual(meta.notes, 'Remember to test');
    assert.deepStrictEqual(meta.completed, []);
    assert.deepStrictEqual(meta.inProgress, []);
  })) passed++; else failed++;

  if (test('extracts context with backtick fenced block', () => {
    const content = '# Session\n\n### Context to Load\n```\nsrc/index.ts\nlib/utils.js\n```\n';
    const meta = sessionManager.parseSessionMetadata(content);
    assert.strictEqual(meta.context, 'src/index.ts\nlib/utils.js');
  })) passed++; else failed++;

  if (test('trims whitespace from title', () => {
    const content = '#   Spaces Around Title   \n';
    const meta = sessionManager.parseSessionMetadata(content);
    assert.strictEqual(meta.title, 'Spaces Around Title');
  })) passed++; else failed++;

  // getSessionStats edge cases
  console.log('\ngetSessionStats (edge cases):');

  if (test('detects notes and context presence', () => {
    const content = '# Stats Test\n\n### Notes for Next Session\nSome notes\n\n### Context to Load\n```\nfile.ts\n```\n';
    const stats = sessionManager.getSessionStats(content);
    assert.strictEqual(stats.hasNotes, true);
    assert.strictEqual(stats.hasContext, true);
  })) passed++; else failed++;

  if (test('detects absence of notes and context', () => {
    const content = '# Simple Session\n\nJust some content\n';
    const stats = sessionManager.getSessionStats(content);
    assert.strictEqual(stats.hasNotes, false);
    assert.strictEqual(stats.hasContext, false);
  })) passed++; else failed++;

  if (test('treats Unix absolute path ending with .tmp as file path', () => {
    // Content that starts with / and ends with .tmp should be treated as a path
    // This tests the looksLikePath heuristic
    const fakeContent = '/some/path/session.tmp';
    // Since the file doesn't exist, getSessionContent returns null,
    // parseSessionMetadata(null) returns defaults
    const stats = sessionManager.getSessionStats(fakeContent);
    assert.strictEqual(stats.totalItems, 0);
    assert.strictEqual(stats.lineCount, 0);
  })) passed++; else failed++;

  // getSessionSize edge case
  console.log('\ngetSessionSize (edge cases):');

  if (test('returns MB for large file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'large.tmp');
      // Create a file > 1MB
      fs.writeFileSync(sessionPath, 'x'.repeat(1024 * 1024 + 100));
      const size = sessionManager.getSessionSize(sessionPath);
      assert.ok(size.includes('MB'), `Expected MB, got: ${size}`);
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  // appendSessionContent edge case
  if (test('appendSessionContent returns false for invalid path', () => {
    const result = sessionManager.appendSessionContent('/nonexistent/deep/path/session.tmp', 'content');
    assert.strictEqual(result, false);
  })) passed++; else failed++;

  // parseSessionFilename edge cases
  console.log('\nparseSessionFilename (additional edge cases):');

  if (test('rejects uppercase letters in short ID', () => {
    const result = sessionManager.parseSessionFilename('2026-02-01-ABCD1234-session.tmp');
    assert.strictEqual(result, null, 'Uppercase letters should be rejected');
  })) passed++; else failed++;

  if (test('rejects filenames with extra segments', () => {
    const result = sessionManager.parseSessionFilename('2026-02-01-abc12345-extra-session.tmp');
    assert.strictEqual(result, null, 'Extra segments should be rejected');
  })) passed++; else failed++;

  if (test('rejects impossible month (13)', () => {
    const result = sessionManager.parseSessionFilename('2026-13-01-abcd1234-session.tmp');
    assert.strictEqual(result, null, 'Month 13 should be rejected');
  })) passed++; else failed++;

  if (test('rejects impossible day (32)', () => {
    const result = sessionManager.parseSessionFilename('2026-01-32-abcd1234-session.tmp');
    assert.strictEqual(result, null, 'Day 32 should be rejected');
  })) passed++; else failed++;

  if (test('rejects month 00', () => {
    const result = sessionManager.parseSessionFilename('2026-00-15-abcd1234-session.tmp');
    assert.strictEqual(result, null, 'Month 00 should be rejected');
  })) passed++; else failed++;

  if (test('rejects day 00', () => {
    const result = sessionManager.parseSessionFilename('2026-01-00-abcd1234-session.tmp');
    assert.strictEqual(result, null, 'Day 00 should be rejected');
  })) passed++; else failed++;

  if (test('accepts valid edge date (month 12, day 31)', () => {
    const result = sessionManager.parseSessionFilename('2026-12-31-abcd1234-session.tmp');
    assert.ok(result, 'Month 12, day 31 should be accepted');
    assert.strictEqual(result.date, '2026-12-31');
  })) passed++; else failed++;

  if (test('rejects Feb 31 (calendar-inaccurate date)', () => {
    const result = sessionManager.parseSessionFilename('2026-02-31-abcd1234-session.tmp');
    assert.strictEqual(result, null, 'Feb 31 does not exist');
  })) passed++; else failed++;

  if (test('rejects Apr 31 (calendar-inaccurate date)', () => {
    const result = sessionManager.parseSessionFilename('2026-04-31-abcd1234-session.tmp');
    assert.strictEqual(result, null, 'Apr 31 does not exist');
  })) passed++; else failed++;

  if (test('rejects Feb 29 in non-leap year', () => {
    const result = sessionManager.parseSessionFilename('2025-02-29-abcd1234-session.tmp');
    assert.strictEqual(result, null, '2025 is not a leap year');
  })) passed++; else failed++;

  if (test('accepts Feb 29 in leap year', () => {
    const result = sessionManager.parseSessionFilename('2024-02-29-abcd1234-session.tmp');
    assert.ok(result, '2024 is a leap year');
    assert.strictEqual(result.date, '2024-02-29');
  })) passed++; else failed++;

  if (test('accepts Jun 30 (valid 30-day month)', () => {
    const result = sessionManager.parseSessionFilename('2026-06-30-abcd1234-session.tmp');
    assert.ok(result, 'June has 30 days');
    assert.strictEqual(result.date, '2026-06-30');
  })) passed++; else failed++;

  if (test('rejects Jun 31 (invalid 30-day month)', () => {
    const result = sessionManager.parseSessionFilename('2026-06-31-abcd1234-session.tmp');
    assert.strictEqual(result, null, 'June has only 30 days');
  })) passed++; else failed++;

  if (test('datetime field is a Date object', () => {
    const result = sessionManager.parseSessionFilename('2026-06-15-abcdef12-session.tmp');
    assert.ok(result);
    assert.ok(result.datetime instanceof Date, 'datetime should be a Date');
    assert.ok(!isNaN(result.datetime.getTime()), 'datetime should be valid');
  })) passed++; else failed++;

  // writeSessionContent tests
  console.log('\nwriteSessionContent:');

  if (test('creates new session file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'write-test.tmp');
      const result = sessionManager.writeSessionContent(sessionPath, '# Test Session\n');
      assert.strictEqual(result, true, 'Should return true on success');
      assert.ok(fs.existsSync(sessionPath), 'File should exist');
      assert.strictEqual(fs.readFileSync(sessionPath, 'utf8'), '# Test Session\n');
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  if (test('overwrites existing session file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'overwrite-test.tmp');
      fs.writeFileSync(sessionPath, 'old content');
      const result = sessionManager.writeSessionContent(sessionPath, 'new content');
      assert.strictEqual(result, true);
      assert.strictEqual(fs.readFileSync(sessionPath, 'utf8'), 'new content');
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  if (test('writeSessionContent returns false for invalid path', () => {
    const result = sessionManager.writeSessionContent('/nonexistent/deep/path/session.tmp', 'content');
    assert.strictEqual(result, false, 'Should return false for invalid path');
  })) passed++; else failed++;

  // appendSessionContent tests
  console.log('\nappendSessionContent:');

  if (test('appends to existing session file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'append-test.tmp');
      fs.writeFileSync(sessionPath, '# Session\n');
      const result = sessionManager.appendSessionContent(sessionPath, '\n## Added Section\n');
      assert.strictEqual(result, true);
      const content = fs.readFileSync(sessionPath, 'utf8');
      assert.ok(content.includes('# Session'));
      assert.ok(content.includes('## Added Section'));
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  // deleteSession tests
  console.log('\ndeleteSession:');

  if (test('deletes existing session file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'delete-me.tmp');
      fs.writeFileSync(sessionPath, '# To Delete');
      assert.ok(fs.existsSync(sessionPath), 'File should exist before delete');
      const result = sessionManager.deleteSession(sessionPath);
      assert.strictEqual(result, true, 'Should return true');
      assert.ok(!fs.existsSync(sessionPath), 'File should not exist after delete');
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  if (test('deleteSession returns false for non-existent file', () => {
    const result = sessionManager.deleteSession('/nonexistent/session.tmp');
    assert.strictEqual(result, false, 'Should return false for missing file');
  })) passed++; else failed++;

  // sessionExists tests
  console.log('\nsessionExists:');

  if (test('returns true for existing session file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, 'exists.tmp');
      fs.writeFileSync(sessionPath, '# Exists');
      assert.strictEqual(sessionManager.sessionExists(sessionPath), true);
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  if (test('returns false for non-existent file', () => {
    assert.strictEqual(sessionManager.sessionExists('/nonexistent/file.tmp'), false);
  })) passed++; else failed++;

  if (test('returns false for directory (not a file)', () => {
    const dir = createTempSessionDir();
    try {
      assert.strictEqual(sessionManager.sessionExists(dir), false, 'Directory should not count as session');
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  // getAllSessions pagination edge cases (offset/limit clamping)
  console.log('\ngetAllSessions (pagination edge cases):');

  if (test('getAllSessions clamps negative offset to 0', () => {
    const result = sessionManager.getAllSessions({ offset: -5, limit: 2 });
    // Negative offset should be clamped to 0, returning the first 2 sessions
    assert.strictEqual(result.sessions.length, 2);
    assert.strictEqual(result.offset, 0);
    assert.strictEqual(result.total, 5);
  })) passed++; else failed++;

  if (test('getAllSessions clamps NaN offset to 0', () => {
    const result = sessionManager.getAllSessions({ offset: NaN, limit: 3 });
    assert.strictEqual(result.sessions.length, 3);
    assert.strictEqual(result.offset, 0);
  })) passed++; else failed++;

  if (test('getAllSessions clamps NaN limit to default', () => {
    const result = sessionManager.getAllSessions({ offset: 0, limit: NaN });
    // NaN limit should be clamped to default (50), returning all 5 sessions
    assert.ok(result.sessions.length > 0);
    assert.strictEqual(result.total, 5);
  })) passed++; else failed++;

  if (test('getAllSessions clamps negative limit to 1', () => {
    const result = sessionManager.getAllSessions({ offset: 0, limit: -10 });
    // Negative limit should be clamped to 1
    assert.strictEqual(result.sessions.length, 1);
    assert.strictEqual(result.limit, 1);
  })) passed++; else failed++;

  if (test('getAllSessions clamps zero limit to 1', () => {
    const result = sessionManager.getAllSessions({ offset: 0, limit: 0 });
    assert.strictEqual(result.sessions.length, 1);
    assert.strictEqual(result.limit, 1);
  })) passed++; else failed++;

  if (test('getAllSessions handles string offset/limit gracefully', () => {
    const result = sessionManager.getAllSessions({ offset: 'abc', limit: 'xyz' });
    // String non-numeric should be treated as 0/default
    assert.strictEqual(result.offset, 0);
    assert.ok(result.sessions.length > 0);
  })) passed++; else failed++;

  if (test('getAllSessions handles fractional offset (floors to integer)', () => {
    const result = sessionManager.getAllSessions({ offset: 1.7, limit: 2 });
    // 1.7 should floor to 1, skip first session, return next 2
    assert.strictEqual(result.offset, 1);
    assert.strictEqual(result.sessions.length, 2);
  })) passed++; else failed++;

  if (test('getAllSessions handles Infinity offset', () => {
    // Infinity should clamp to 0 since Number(Infinity) is Infinity but
    // Math.floor(Infinity) is Infinity — however slice(Infinity) returns []
    // Actually: Number(Infinity) || 0 = Infinity, Math.floor(Infinity) = Infinity
    // Math.max(0, Infinity) = Infinity, so slice(Infinity) = []
    const result = sessionManager.getAllSessions({ offset: Infinity, limit: 2 });
    assert.strictEqual(result.sessions.length, 0);
    assert.strictEqual(result.total, 5);
  })) passed++; else failed++;

  // getSessionStats with code blocks and special characters
  console.log('\ngetSessionStats (code blocks & special chars):');

  if (test('counts tasks with inline backticks correctly', () => {
    const content = '# Test\n\n### Completed\n- [x] Fixed `app.js` bug with `fs.readFile()`\n- [x] Ran `npm install` successfully\n\n### In Progress\n- [ ] Review `config.ts` changes\n';
    const stats = sessionManager.getSessionStats(content);
    assert.strictEqual(stats.completedItems, 2, 'Should count 2 completed items');
    assert.strictEqual(stats.inProgressItems, 1, 'Should count 1 in-progress item');
    assert.strictEqual(stats.totalItems, 3);
  })) passed++; else failed++;

  if (test('handles special chars in notes section', () => {
    const content = '# Test\n\n### Notes for Next Session\nDon\'t forget: <important> & "quotes" & \'apostrophes\'\n';
    const stats = sessionManager.getSessionStats(content);
    assert.strictEqual(stats.hasNotes, true, 'Should detect notes section');
    const meta = sessionManager.parseSessionMetadata(content);
    assert.ok(meta.notes.includes('<important>'), 'Notes should preserve HTML-like content');
  })) passed++; else failed++;

  if (test('counts items in multiline code-heavy session', () => {
    const content = '# Code Session\n\n### Completed\n- [x] Refactored `lib/utils.js`\n- [x] Updated `package.json` version\n- [x] Fixed `\\`` escaping bug\n\n### In Progress\n- [ ] Test `getSessionStats()` function\n- [ ] Review PR #42\n';
    const stats = sessionManager.getSessionStats(content);
    assert.strictEqual(stats.completedItems, 3);
    assert.strictEqual(stats.inProgressItems, 2);
  })) passed++; else failed++;

  // getSessionStats with empty content
  if (test('getSessionStats handles empty string content', () => {
    const stats = sessionManager.getSessionStats('');
    assert.strictEqual(stats.totalItems, 0);
    // Empty string is falsy in JS, so content ? ... : 0 returns 0
    assert.strictEqual(stats.lineCount, 0, 'Empty string is falsy, lineCount = 0');
    assert.strictEqual(stats.hasNotes, false);
    assert.strictEqual(stats.hasContext, false);
  })) passed++; else failed++;

  // ── Round 26 tests ──

  console.log('\nparseSessionFilename (30-day month validation):');

  if (test('rejects Sep 31 (September has 30 days)', () => {
    const result = sessionManager.parseSessionFilename('2026-09-31-abcd1234-session.tmp');
    assert.strictEqual(result, null, 'Sep 31 does not exist');
  })) passed++; else failed++;

  if (test('rejects Nov 31 (November has 30 days)', () => {
    const result = sessionManager.parseSessionFilename('2026-11-31-abcd1234-session.tmp');
    assert.strictEqual(result, null, 'Nov 31 does not exist');
  })) passed++; else failed++;

  if (test('accepts Sep 30 (valid 30-day month boundary)', () => {
    const result = sessionManager.parseSessionFilename('2026-09-30-abcd1234-session.tmp');
    assert.ok(result, 'Sep 30 is valid');
    assert.strictEqual(result.date, '2026-09-30');
  })) passed++; else failed++;

  console.log('\ngetSessionStats (path heuristic edge cases):');

  if (test('multiline content ending with .tmp is treated as content', () => {
    const content = 'Line 1\nLine 2\nDownload file.tmp';
    const stats = sessionManager.getSessionStats(content);
    // Has newlines so looksLikePath is false → treated as content
    assert.strictEqual(stats.lineCount, 3, 'Should count 3 lines');
  })) passed++; else failed++;

  if (test('single-line content not starting with / treated as content', () => {
    const content = 'some random text.tmp';
    const stats = sessionManager.getSessionStats(content);
    assert.strictEqual(stats.lineCount, 1, 'Should treat as content, not a path');
  })) passed++; else failed++;

  console.log('\ngetAllSessions (combined filters):');

  if (test('combines date filter + search filter + pagination', () => {
    // We have 2026-02-01-ijkl9012 and 2026-02-01-mnop3456 with date 2026-02-01
    const result = sessionManager.getAllSessions({
      date: '2026-02-01',
      search: 'ijkl',
      limit: 10
    });
    assert.strictEqual(result.total, 1, 'Only one session matches both date and search');
    assert.strictEqual(result.sessions[0].shortId, 'ijkl9012');
  })) passed++; else failed++;

  if (test('date filter + offset beyond matches returns empty', () => {
    const result = sessionManager.getAllSessions({
      date: '2026-02-01',
      offset: 100,
      limit: 10
    });
    assert.strictEqual(result.sessions.length, 0);
    assert.strictEqual(result.total, 2, 'Two sessions match the date');
    assert.strictEqual(result.hasMore, false);
  })) passed++; else failed++;

  console.log('\ngetSessionById (ambiguous prefix):');

  if (test('returns first match when multiple sessions share a prefix', () => {
    // Sessions with IDs abcd1234 and efgh5678 exist
    // 'e' should match efgh5678 (only match)
    const result = sessionManager.getSessionById('efgh');
    assert.ok(result, 'Should find session by prefix');
    assert.strictEqual(result.shortId, 'efgh5678');
  })) passed++; else failed++;

  console.log('\nparseSessionMetadata (edge cases):');

  if (test('handles unclosed code fence in Context section', () => {
    const content = '# Session\n\n### Context to Load\n```\nsrc/index.ts\n';
    const meta = sessionManager.parseSessionMetadata(content);
    // Regex requires closing ```, so no context should be extracted
    assert.strictEqual(meta.context, '', 'Unclosed code fence should not extract context');
  })) passed++; else failed++;

  if (test('handles empty task text in checklist items', () => {
    const content = '# Session\n\n### Completed\n- [x] \n- [x] Real task\n';
    const meta = sessionManager.parseSessionMetadata(content);
    // \s* in the regex bridges across newlines, collapsing the empty
    // task + next task into a single match. This is an edge case —
    // real sessions don't have empty checklist items.
    assert.strictEqual(meta.completed.length, 1);
  })) passed++; else failed++;

  // ── Round 43: getSessionById default excludes content ──
  console.log('\nRound 43: getSessionById (default excludes content):');

  if (test('getSessionById without includeContent omits content, metadata, and stats', () => {
    // Default call (includeContent=false) should NOT load file content
    const result = sessionManager.getSessionById('abcd1234');
    assert.ok(result, 'Should find the session');
    assert.strictEqual(result.shortId, 'abcd1234');
    // These fields should be absent when includeContent is false
    assert.strictEqual(result.content, undefined, 'content should be undefined');
    assert.strictEqual(result.metadata, undefined, 'metadata should be undefined');
    assert.strictEqual(result.stats, undefined, 'stats should be undefined');
    // Basic fields should still be present
    assert.ok(result.sessionPath, 'sessionPath should be present');
    assert.ok(result.size !== undefined, 'size should be present');
    assert.ok(result.modifiedTime, 'modifiedTime should be present');
  })) passed++; else failed++;

  // ── Round 54: search filter scope and getSessionPath utility ──
  console.log('\nRound 54: search filter scope and path utility:');

  if (test('getAllSessions search filter matches only short ID, not title or content', () => {
    // "Session" appears in file CONTENT (e.g. "# Session 1") but not in any shortId
    const result = sessionManager.getAllSessions({ search: 'Session', limit: 100 });
    assert.strictEqual(result.total, 0, 'Search should not match title/content, only shortId');
    // Verify that searching by actual shortId substring still works
    const result2 = sessionManager.getAllSessions({ search: 'abcd', limit: 100 });
    assert.strictEqual(result2.total, 1, 'Search by shortId should still work');
  })) passed++; else failed++;

  if (test('getSessionPath returns absolute path for session filename', () => {
    const filename = '2026-02-01-testpath-session.tmp';
    const result = sessionManager.getSessionPath(filename);
    assert.ok(path.isAbsolute(result), 'Should return an absolute path');
    assert.ok(result.endsWith(filename), `Path should end with filename, got: ${result}`);
    // Since HOME is overridden, sessions dir should be under tmpHome
    assert.ok(result.includes('.claude'), 'Path should include .claude directory');
    assert.ok(result.includes('sessions'), 'Path should include sessions directory');
  })) passed++; else failed++;

  // ── Round 66: getSessionById noIdMatch path (date-only string for old format) ──
  console.log('\nRound 66: getSessionById (noIdMatch — date-only match for old format):');

  if (test('getSessionById finds old-format session by date-only string (noIdMatch)', () => {
    // File is 2026-02-10-session.tmp (old format, shortId = 'no-id')
    // Calling with '2026-02-10' → filenameMatch fails (filename !== '2026-02-10' and !== '2026-02-10.tmp')
    // shortIdMatch fails (shortId === 'no-id', not !== 'no-id')
    // noIdMatch succeeds: shortId === 'no-id' && filename === '2026-02-10-session.tmp'
    const result = sessionManager.getSessionById('2026-02-10');
    assert.ok(result, 'Should find old-format session by date-only string');
    assert.strictEqual(result.shortId, 'no-id', 'Should have no-id shortId');
    assert.ok(result.filename.includes('2026-02-10-session.tmp'), 'Should match old-format file');
    assert.ok(result.sessionPath, 'Should have sessionPath');
    assert.ok(result.date === '2026-02-10', 'Should have correct date');
  })) passed++; else failed++;

  // Cleanup — restore both HOME and USERPROFILE (Windows)
  process.env.HOME = origHome;
  if (origUserProfile !== undefined) {
    process.env.USERPROFILE = origUserProfile;
  } else {
    delete process.env.USERPROFILE;
  }
  try {
    fs.rmSync(tmpHome, { recursive: true, force: true });
  } catch {
    // best-effort
  }

  // ── Round 30: datetime local-time fix and parseSessionFilename edge cases ──
  console.log('\nRound 30: datetime local-time fix:');

  if (test('datetime day matches the filename date (local-time constructor)', () => {
    const result = sessionManager.parseSessionFilename('2026-06-15-abcdef12-session.tmp');
    assert.ok(result);
    // With the fix, getDate()/getMonth() should return local-time values
    // matching the filename, regardless of timezone
    assert.strictEqual(result.datetime.getDate(), 15, 'Day should be 15 (local time)');
    assert.strictEqual(result.datetime.getMonth(), 5, 'Month should be 5 (June, 0-indexed)');
    assert.strictEqual(result.datetime.getFullYear(), 2026, 'Year should be 2026');
  })) passed++; else failed++;

  if (test('datetime matches for January 1 (timezone-sensitive date)', () => {
    // Jan 1 at UTC midnight is Dec 31 in negative offsets — this tests the fix
    const result = sessionManager.parseSessionFilename('2026-01-01-abc12345-session.tmp');
    assert.ok(result);
    assert.strictEqual(result.datetime.getDate(), 1, 'Day should be 1 in local time');
    assert.strictEqual(result.datetime.getMonth(), 0, 'Month should be 0 (January)');
  })) passed++; else failed++;

  if (test('datetime matches for December 31 (year boundary)', () => {
    const result = sessionManager.parseSessionFilename('2025-12-31-abc12345-session.tmp');
    assert.ok(result);
    assert.strictEqual(result.datetime.getDate(), 31);
    assert.strictEqual(result.datetime.getMonth(), 11); // December
    assert.strictEqual(result.datetime.getFullYear(), 2025);
  })) passed++; else failed++;

  console.log('\nRound 30: parseSessionFilename edge cases:');

  if (test('parses session ID with many dashes (UUID-like)', () => {
    const result = sessionManager.parseSessionFilename('2026-02-13-a1b2c3d4-session.tmp');
    assert.ok(result);
    assert.strictEqual(result.shortId, 'a1b2c3d4');
    assert.strictEqual(result.date, '2026-02-13');
  })) passed++; else failed++;

  if (test('rejects filename with missing session.tmp suffix', () => {
    const result = sessionManager.parseSessionFilename('2026-02-13-abc12345.tmp');
    assert.strictEqual(result, null, 'Should reject filename without -session.tmp');
  })) passed++; else failed++;

  if (test('rejects filename with extra text after suffix', () => {
    const result = sessionManager.parseSessionFilename('2026-02-13-abc12345-session.tmp.bak');
    assert.strictEqual(result, null, 'Should reject filenames with extra extension');
  })) passed++; else failed++;

  if (test('handles old-format filename without session ID', () => {
    // The regex match[2] is undefined for old format → shortId defaults to 'no-id'
    const result = sessionManager.parseSessionFilename('2026-02-13-session.tmp');
    if (result) {
      assert.strictEqual(result.shortId, 'no-id', 'Should default to no-id');
    }
    // Either null (regex doesn't match) or has no-id — both are acceptable
    assert.ok(true, 'Old format handled without crash');
  })) passed++; else failed++;

  // ── Round 33: birthtime / createdTime fallback ──
  console.log('\ncreatedTime fallback (Round 33):');

  // Use HOME override approach (consistent with existing getAllSessions tests)
  const r33Home = path.join(os.tmpdir(), `ecc-r33-birthtime-${Date.now()}`);
  const r33SessionsDir = path.join(r33Home, '.claude', 'sessions');
  fs.mkdirSync(r33SessionsDir, { recursive: true });
  const r33OrigHome = process.env.HOME;
  const r33OrigProfile = process.env.USERPROFILE;
  process.env.HOME = r33Home;
  process.env.USERPROFILE = r33Home;

  const r33Filename = '2026-02-13-r33birth-session.tmp';
  const r33FilePath = path.join(r33SessionsDir, r33Filename);
  fs.writeFileSync(r33FilePath, '{"type":"test"}');

  if (test('getAllSessions returns createdTime from birthtime when available', () => {
    const result = sessionManager.getAllSessions({ limit: 100 });
    assert.ok(result.sessions.length > 0, 'Should find the test session');
    const session = result.sessions[0];
    assert.ok(session.createdTime instanceof Date, 'createdTime should be a Date');
    // birthtime should be populated on macOS/Windows — createdTime should match it
    const stats = fs.statSync(r33FilePath);
    if (stats.birthtime && stats.birthtime.getTime() > 0) {
      assert.strictEqual(
        session.createdTime.getTime(),
        stats.birthtime.getTime(),
        'createdTime should match birthtime when available'
      );
    }
  })) passed++; else failed++;

  if (test('getSessionById returns createdTime field', () => {
    const session = sessionManager.getSessionById('r33birth');
    assert.ok(session, 'Should find the session');
    assert.ok(session.createdTime instanceof Date, 'createdTime should be a Date');
    assert.ok(session.createdTime.getTime() > 0, 'createdTime should be non-zero');
  })) passed++; else failed++;

  if (test('createdTime falls back to ctime when birthtime is epoch-zero', () => {
    // This tests the || fallback logic: stats.birthtime || stats.ctime
    // On some FS, birthtime may be epoch 0 (falsy as a Date number comparison
    // but truthy as a Date object). The fallback is defensive.
    const stats = fs.statSync(r33FilePath);
    // Both birthtime and ctime should be valid Dates on any modern OS
    assert.ok(stats.ctime instanceof Date, 'ctime should exist');
    // The fallback expression `birthtime || ctime` should always produce a valid Date
    const fallbackResult = stats.birthtime || stats.ctime;
    assert.ok(fallbackResult instanceof Date, 'Fallback should produce a Date');
    assert.ok(fallbackResult.getTime() > 0, 'Fallback date should be non-zero');
  })) passed++; else failed++;

  // Cleanup Round 33 HOME override
  process.env.HOME = r33OrigHome;
  if (r33OrigProfile !== undefined) {
    process.env.USERPROFILE = r33OrigProfile;
  } else {
    delete process.env.USERPROFILE;
  }
  try { fs.rmSync(r33Home, { recursive: true, force: true }); } catch (_e) { /* ignore cleanup errors */ }

  // ── Round 46: path heuristic and checklist edge cases ──
  console.log('\ngetSessionStats Windows path heuristic (Round 46):');

  if (test('recognises Windows drive-letter path as a file path', () => {
    // The looksLikePath regex includes /^[A-Za-z]:[/\\]/ for Windows
    // A non-existent Windows path should still be treated as a path
    // (getSessionContent returns null → parseSessionMetadata(null) → defaults)
    const stats1 = sessionManager.getSessionStats('C:/Users/test/session.tmp');
    assert.strictEqual(stats1.lineCount, 0, 'C:/ path treated as path, not content');
    const stats2 = sessionManager.getSessionStats('D:\\Sessions\\2026-01-01.tmp');
    assert.strictEqual(stats2.lineCount, 0, 'D:\\ path treated as path, not content');
  })) passed++; else failed++;

  if (test('does not treat bare drive letter without slash as path', () => {
    // "C:session.tmp" has no slash after colon → regex fails → treated as content
    const stats = sessionManager.getSessionStats('C:session.tmp');
    assert.strictEqual(stats.lineCount, 1, 'Bare C: without slash treated as content');
  })) passed++; else failed++;

  console.log('\nparseSessionMetadata checkbox case sensitivity (Round 46):');

  if (test('uppercase [X] does not match completed items regex', () => {
    const content = '# Test\n\n### Completed\n- [X] Uppercase task\n- [x] Lowercase task\n';
    const meta = sessionManager.parseSessionMetadata(content);
    // Regex is /- \[x\]\s*(.+)/g — only matches lowercase [x]
    assert.strictEqual(meta.completed.length, 1, 'Only lowercase [x] should match');
    assert.strictEqual(meta.completed[0], 'Lowercase task');
  })) passed++; else failed++;

  // getAllSessions returns empty result when sessions directory does not exist
  if (test('getAllSessions returns empty when sessions dir missing', () => {
    const tmpDir = createTempSessionDir();
    const origHome = process.env.HOME;
    const origUserProfile = process.env.USERPROFILE;
    try {
      // Point HOME to a dir with no .claude/sessions/
      process.env.HOME = tmpDir;
      process.env.USERPROFILE = tmpDir;
      // Re-require to pick up new HOME
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      const freshSM = require('../../scripts/lib/session-manager');
      const result = freshSM.getAllSessions();
      assert.deepStrictEqual(result.sessions, [], 'Should return empty sessions array');
      assert.strictEqual(result.total, 0, 'Total should be 0');
      assert.strictEqual(result.hasMore, false, 'hasMore should be false');
    } finally {
      process.env.HOME = origHome;
      process.env.USERPROFILE = origUserProfile;
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      cleanup(tmpDir);
    }
  })) passed++; else failed++;

  // ── Round 69: getSessionById returns null when sessions dir missing ──
  console.log('\nRound 69: getSessionById (missing sessions directory):');

  if (test('getSessionById returns null when sessions directory does not exist', () => {
    const tmpDir = createTempSessionDir();
    const origHome = process.env.HOME;
    const origUserProfile = process.env.USERPROFILE;
    try {
      // Point HOME to a dir with no .claude/sessions/
      process.env.HOME = tmpDir;
      process.env.USERPROFILE = tmpDir;
      // Re-require to pick up new HOME
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      const freshSM = require('../../scripts/lib/session-manager');
      const result = freshSM.getSessionById('anything');
      assert.strictEqual(result, null, 'Should return null when sessions dir does not exist');
    } finally {
      process.env.HOME = origHome;
      process.env.USERPROFILE = origUserProfile;
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      cleanup(tmpDir);
    }
  })) passed++; else failed++;

  // ── Round 78: getSessionStats reads real file when given existing .tmp path ──
  console.log('\nRound 78: getSessionStats (actual file path → reads from disk):');

  if (test('getSessionStats reads from disk when given path to existing .tmp file', () => {
    const dir = createTempSessionDir();
    try {
      const sessionPath = path.join(dir, '2026-03-01-test1234-session.tmp');
      const content = '# Real File Stats Test\n\n**Date:** 2026-03-01\n**Started:** 09:00\n\n### Completed\n- [x] First task\n- [x] Second task\n\n### In Progress\n- [ ] Third task\n\n### Notes for Next Session\nDon\'t forget the edge cases\n';
      fs.writeFileSync(sessionPath, content);

      // Pass the FILE PATH (not content) — this exercises looksLikePath branch
      const stats = sessionManager.getSessionStats(sessionPath);
      assert.strictEqual(stats.completedItems, 2, 'Should find 2 completed items from file');
      assert.strictEqual(stats.inProgressItems, 1, 'Should find 1 in-progress item from file');
      assert.strictEqual(stats.totalItems, 3, 'Should find 3 total items from file');
      assert.strictEqual(stats.hasNotes, true, 'Should detect notes section from file');
      assert.ok(stats.lineCount > 5, `Should have multiple lines from file, got ${stats.lineCount}`);
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  // ── Round 78: getAllSessions hasContent field ──
  console.log('\nRound 78: getAllSessions (hasContent field):');

  if (test('getAllSessions hasContent is true for non-empty and false for empty files', () => {
    const isoHome = path.join(os.tmpdir(), `ecc-hascontent-${Date.now()}`);
    const isoSessions = path.join(isoHome, '.claude', 'sessions');
    fs.mkdirSync(isoSessions, { recursive: true });
    const savedHome = process.env.HOME;
    const savedProfile = process.env.USERPROFILE;
    try {
      // Create one non-empty session and one empty session
      fs.writeFileSync(path.join(isoSessions, '2026-04-01-nonempty-session.tmp'), '# Has content');
      fs.writeFileSync(path.join(isoSessions, '2026-04-02-emptyfile-session.tmp'), '');

      process.env.HOME = isoHome;
      process.env.USERPROFILE = isoHome;
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      const freshSM = require('../../scripts/lib/session-manager');

      const result = freshSM.getAllSessions({ limit: 100 });
      assert.strictEqual(result.total, 2, 'Should find both sessions');

      const nonEmpty = result.sessions.find(s => s.shortId === 'nonempty');
      const empty = result.sessions.find(s => s.shortId === 'emptyfile');

      assert.ok(nonEmpty, 'Should find the non-empty session');
      assert.ok(empty, 'Should find the empty session');
      assert.strictEqual(nonEmpty.hasContent, true, 'Non-empty file should have hasContent: true');
      assert.strictEqual(empty.hasContent, false, 'Empty file should have hasContent: false');
    } finally {
      process.env.HOME = savedHome;
      process.env.USERPROFILE = savedProfile;
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      fs.rmSync(isoHome, { recursive: true, force: true });
    }
  })) passed++; else failed++;

  // ── Round 75: deleteSession catch — unlinkSync throws on read-only dir ──
  console.log('\nRound 75: deleteSession (unlink failure in read-only dir):');

  if (test('deleteSession returns false when file exists but directory is read-only', () => {
    if (process.platform === 'win32' || process.getuid?.() === 0) {
      console.log('    (skipped — chmod ineffective on Windows/root)');
      return;
    }
    const tmpDir = path.join(os.tmpdir(), `sm-del-ro-${Date.now()}`);
    fs.mkdirSync(tmpDir, { recursive: true });
    const sessionFile = path.join(tmpDir, 'test-session.tmp');
    fs.writeFileSync(sessionFile, 'session content');
    try {
      // Make directory read-only so unlinkSync throws EACCES
      fs.chmodSync(tmpDir, 0o555);
      const result = sessionManager.deleteSession(sessionFile);
      assert.strictEqual(result, false, 'Should return false when unlinkSync fails');
    } finally {
      try { fs.chmodSync(tmpDir, 0o755); } catch { /* best-effort */ }
      fs.rmSync(tmpDir, { recursive: true, force: true });
    }
  })) passed++; else failed++;

  // ── Round 81: getSessionStats(null) ──
  console.log('\nRound 81: getSessionStats(null) (null input):');

  if (test('getSessionStats(null) returns zero lineCount and empty metadata', () => {
    // session-manager.js line 158-177: getSessionStats accepts path or content.
    // typeof null === 'string' is false → looksLikePath = false → content = null.
    // Line 177: content ? content.split('\n').length : 0 → lineCount: 0.
    // parseSessionMetadata(null) returns defaults → totalItems/completedItems/inProgressItems = 0.
    const stats = sessionManager.getSessionStats(null);
    assert.strictEqual(stats.lineCount, 0, 'null input should yield lineCount 0');
    assert.strictEqual(stats.totalItems, 0, 'null input should yield totalItems 0');
    assert.strictEqual(stats.completedItems, 0, 'null input should yield completedItems 0');
    assert.strictEqual(stats.inProgressItems, 0, 'null input should yield inProgressItems 0');
    assert.strictEqual(stats.hasNotes, false, 'null input should yield hasNotes false');
    assert.strictEqual(stats.hasContext, false, 'null input should yield hasContext false');
  })) passed++; else failed++;

  // ── Round 83: getAllSessions TOCTOU statSync catch (broken symlink) ──
  console.log('\nRound 83: getAllSessions (broken symlink — statSync catch):');

  if (test('getAllSessions skips broken symlink .tmp files gracefully', () => {
    // getAllSessions at line 241-246: statSync throws for broken symlinks,
    // the catch causes `continue`, skipping that entry entirely.
    const isoHome = path.join(os.tmpdir(), `ecc-r83-toctou-${Date.now()}`);
    const sessionsDir = path.join(isoHome, '.claude', 'sessions');
    fs.mkdirSync(sessionsDir, { recursive: true });

    // Create one real session file
    const realFile = '2026-02-10-abcd1234-session.tmp';
    fs.writeFileSync(path.join(sessionsDir, realFile), '# Real session\n');

    // Create a broken symlink that matches the session filename pattern
    const brokenSymlink = '2026-02-10-deadbeef-session.tmp';
    fs.symlinkSync('/nonexistent/path/that/does/not/exist', path.join(sessionsDir, brokenSymlink));

    const origHome = process.env.HOME;
    const origUserProfile = process.env.USERPROFILE;
    process.env.HOME = isoHome;
    process.env.USERPROFILE = isoHome;
    try {
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      const freshManager = require('../../scripts/lib/session-manager');
      const result = freshManager.getAllSessions({ limit: 100 });

      // Should have only the real session, not the broken symlink
      assert.strictEqual(result.total, 1, 'Should find only the real session, not the broken symlink');
      assert.ok(result.sessions[0].filename === realFile,
        `Should return the real file, got: ${result.sessions[0].filename}`);
    } finally {
      process.env.HOME = origHome;
      process.env.USERPROFILE = origUserProfile;
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      fs.rmSync(isoHome, { recursive: true, force: true });
    }
  })) passed++; else failed++;

  // ── Round 84: getSessionById TOCTOU — statSync catch returns null for broken symlink ──
  console.log('\nRound 84: getSessionById (broken symlink — statSync catch):');

  if (test('getSessionById returns null when matching session is a broken symlink', () => {
    // getSessionById at line 307-310: statSync throws for broken symlinks,
    // the catch returns null (file deleted between readdir and stat).
    const isoHome = path.join(os.tmpdir(), `ecc-r84-getbyid-toctou-${Date.now()}`);
    const sessionsDir = path.join(isoHome, '.claude', 'sessions');
    fs.mkdirSync(sessionsDir, { recursive: true });

    // Create a broken symlink that matches a session ID pattern
    const brokenFile = '2026-02-11-deadbeef-session.tmp';
    fs.symlinkSync('/nonexistent/target/that/does/not/exist', path.join(sessionsDir, brokenFile));

    const origHome = process.env.HOME;
    const origUserProfile = process.env.USERPROFILE;
    try {
      process.env.HOME = isoHome;
      process.env.USERPROFILE = isoHome;
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      const freshSM = require('../../scripts/lib/session-manager');

      // Search by the short ID "deadbeef" — should match the broken symlink
      const result = freshSM.getSessionById('deadbeef');
      assert.strictEqual(result, null,
        'Should return null when matching session file is a broken symlink');
    } finally {
      process.env.HOME = origHome;
      process.env.USERPROFILE = origUserProfile;
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      fs.rmSync(isoHome, { recursive: true, force: true });
    }
  })) passed++; else failed++;

  // ── Round 88: parseSessionMetadata null date/started/lastUpdated fields ──
  console.log('\nRound 88: parseSessionMetadata content lacking Date/Started/Updated fields:');
  if (test('parseSessionMetadata returns null for date, started, lastUpdated when fields absent', () => {
    const content = '# Title Only\n\n### Notes for Next Session\nSome notes\n';
    const meta = sessionManager.parseSessionMetadata(content);
    assert.strictEqual(meta.date, null,
      'date should be null when **Date:** field is absent');
    assert.strictEqual(meta.started, null,
      'started should be null when **Started:** field is absent');
    assert.strictEqual(meta.lastUpdated, null,
      'lastUpdated should be null when **Last Updated:** field is absent');
    // Confirm other fields still parse correctly
    assert.strictEqual(meta.title, 'Title Only');
    assert.strictEqual(meta.notes, 'Some notes');
  })) passed++; else failed++;

  // ── Round 89: getAllSessions skips subdirectories (!entry.isFile()) ──
  console.log('\nRound 89: getAllSessions (subdirectory skip):');

  if (test('getAllSessions skips subdirectories inside sessions dir', () => {
    // session-manager.js line 220: if (!entry.isFile() || ...) continue;
    // Existing tests create non-.tmp FILES to test filtering (e.g., notes.txt).
    // This test creates a DIRECTORY — entry.isFile() returns false, so it should be skipped.
    const isoHome = path.join(os.tmpdir(), `ecc-r89-subdir-skip-${Date.now()}`);
    const sessionsDir = path.join(isoHome, '.claude', 'sessions');
    fs.mkdirSync(sessionsDir, { recursive: true });

    // Create a real session file
    const realFile = '2026-02-11-abcd1234-session.tmp';
    fs.writeFileSync(path.join(sessionsDir, realFile), '# Test session');

    // Create a subdirectory inside sessions dir — should be skipped by !entry.isFile()
    const subdir = path.join(sessionsDir, 'some-nested-dir');
    fs.mkdirSync(subdir);

    // Also create a subdirectory whose name ends in .tmp — still not a file
    const tmpSubdir = path.join(sessionsDir, '2026-02-11-fakeid00-session.tmp');
    fs.mkdirSync(tmpSubdir);

    const origHome = process.env.HOME;
    const origUserProfile = process.env.USERPROFILE;
    process.env.HOME = isoHome;
    process.env.USERPROFILE = isoHome;
    try {
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      const freshManager = require('../../scripts/lib/session-manager');
      const result = freshManager.getAllSessions({ limit: 100 });

      // Should find only the real file, not either subdirectory
      assert.strictEqual(result.total, 1,
        `Should find 1 session (the file), not subdirectories. Got ${result.total}`);
      assert.strictEqual(result.sessions[0].filename, realFile,
        `Should return the real file. Got: ${result.sessions[0].filename}`);
    } finally {
      process.env.HOME = origHome;
      process.env.USERPROFILE = origUserProfile;
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      fs.rmSync(isoHome, { recursive: true, force: true });
    }
  })) passed++; else failed++;

  // ── Round 91: getSessionStats with mixed Windows path separators ──
  console.log('\nRound 91: getSessionStats (mixed Windows path separators):');

  if (test('getSessionStats treats mixed Windows separators as a file path', () => {
    // session-manager.js line 166: regex /^[A-Za-z]:[/\\]/ checks only the
    // character right after the colon. Mixed separators like C:\Users/Mixed\session.tmp
    // should still match because the first separator (\) satisfies the regex.
    const stats = sessionManager.getSessionStats('C:\\Users/Mixed\\session.tmp');
    assert.strictEqual(stats.lineCount, 0,
      'Mixed separators should be treated as path (file does not exist → lineCount 0)');
    assert.strictEqual(stats.totalItems, 0, 'Non-existent path should have 0 items');
  })) passed++; else failed++;

  // ── Round 92: getSessionStats with UNC path treated as content ──
  console.log('\nRound 92: getSessionStats (Windows UNC path):');

  if (test('getSessionStats treats UNC path as content (not recognized as file path)', () => {
    // session-manager.js line 163-166: The path heuristic checks for Unix paths
    // (starts with /) and Windows drive-letter paths (/^[A-Za-z]:[/\\]/). UNC paths
    // (\\server\share\file.tmp) don't match either pattern, so the function treats
    // the string as pre-read content rather than a file path to read.
    const stats = sessionManager.getSessionStats('\\\\server\\share\\session.tmp');
    assert.strictEqual(stats.lineCount, 1,
      'UNC path should be treated as single-line content (not a recognized path)');
  })) passed++; else failed++;

  // ── Round 93: getSessionStats with drive letter but no slash (regex boundary) ──
  console.log('\nRound 93: getSessionStats (drive letter without slash — regex boundary):');

  if (test('getSessionStats treats drive letter without slash as content (not a path)', () => {
    // session-manager.js line 166: /^[A-Za-z]:[/\\]/ requires a '/' or '\'
    // immediately after the colon.  'Z:nosession.tmp' has 'Z:n' which does NOT
    // match, so looksLikePath is false even though .endsWith('.tmp') is true.
    const stats = sessionManager.getSessionStats('Z:nosession.tmp');
    assert.strictEqual(stats.lineCount, 1,
      'Z:nosession.tmp (no slash) should be treated as single-line content');
    assert.strictEqual(stats.totalItems, 0,
      'Content without session items should have 0 totalItems');
  })) passed++; else failed++;

  // Re-establish test environment for Rounds 95-98 (these tests need sessions to exist)
  const tmpHome2 = path.join(os.tmpdir(), `ecc-session-mgr-test-2-${Date.now()}`);
  const tmpSessionsDir2 = path.join(tmpHome2, '.claude', 'sessions');
  fs.mkdirSync(tmpSessionsDir2, { recursive: true });
  const origHome2 = process.env.HOME;
  const origUserProfile2 = process.env.USERPROFILE;

  // Create test session files for these tests
  const testSessions2 = [
    { name: '2026-01-15-aaaa1111-session.tmp', content: '# Test Session 1' },
    { name: '2026-02-01-bbbb2222-session.tmp', content: '# Test Session 2' },
    { name: '2026-02-10-cccc3333-session.tmp', content: '# Test Session 3' },
  ];
  for (const session of testSessions2) {
    const filePath = path.join(tmpSessionsDir2, session.name);
    fs.writeFileSync(filePath, session.content);
  }

  process.env.HOME = tmpHome2;
  process.env.USERPROFILE = tmpHome2;

  // ── Round 95: getAllSessions with both negative offset AND negative limit ──
  console.log('\nRound 95: getAllSessions (both negative offset and negative limit):');

  if (test('getAllSessions clamps both negative offset (to 0) and negative limit (to 1) simultaneously', () => {
    const result = sessionManager.getAllSessions({ offset: -5, limit: -10 });
    // offset clamped: Math.max(0, Math.floor(-5)) → 0
    // limit clamped: Math.max(1, Math.floor(-10)) → 1
    // slice(0, 0+1) → first session only
    assert.strictEqual(result.offset, 0,
      'Negative offset should be clamped to 0');
    assert.strictEqual(result.limit, 1,
      'Negative limit should be clamped to 1');
    assert.ok(result.sessions.length <= 1,
      'Should return at most 1 session (slice(0, 1))');
  })) passed++; else failed++;

  // ── Round 96: parseSessionFilename with Feb 30 (impossible date) ──
  console.log('\nRound 96: parseSessionFilename (Feb 30 — impossible date):');

  if (test('parseSessionFilename rejects Feb 30 (passes day<=31 but fails Date rollover)', () => {
    // Feb 30 passes the bounds check (month 1-12, day 1-31) at line 37
    // but new Date(2026, 1, 30) → March 2 (rollover), so getMonth() !== 1 → returns null
    const result = sessionManager.parseSessionFilename('2026-02-30-abcd1234-session.tmp');
    assert.strictEqual(result, null,
      'Feb 30 should be rejected by Date constructor rollover check (line 41)');
  })) passed++; else failed++;

  // ── Round 96: getAllSessions with limit: Infinity ──
  console.log('\nRound 96: getAllSessions (limit: Infinity — pagination bypass):');

  if (test('getAllSessions with limit: Infinity returns all sessions (no pagination)', () => {
    // Number(Infinity) = Infinity, Number.isNaN(Infinity) = false
    // Math.max(1, Math.floor(Infinity)) = Math.max(1, Infinity) = Infinity
    // slice(0, 0 + Infinity) returns all elements
    const result = sessionManager.getAllSessions({ limit: Infinity });
    assert.strictEqual(result.limit, Infinity,
      'Infinity limit should pass through (not clamped or defaulted)');
    assert.strictEqual(result.sessions.length, result.total,
      'All sessions should be returned (no pagination truncation)');
    assert.strictEqual(result.hasMore, false,
      'hasMore should be false since all sessions are returned');
  })) passed++; else failed++;

  // ── Round 96: getAllSessions with limit: null ──
  console.log('\nRound 96: getAllSessions (limit: null — destructuring default bypass):');

  if (test('getAllSessions with limit: null clamps to 1 (null bypasses destructuring default)', () => {
    // Destructuring default only fires for undefined, NOT null
    // rawLimit = null (not 50), Number(null) = 0, Math.max(1, 0) = 1
    const result = sessionManager.getAllSessions({ limit: null });
    assert.strictEqual(result.limit, 1,
      'null limit should become 1 (Number(null)=0, clamped via Math.max(1,0))');
    assert.ok(result.sessions.length <= 1,
      'Should return at most 1 session (clamped limit)');
  })) passed++; else failed++;

  // ── Round 97: getAllSessions with whitespace search filters out everything ──
  console.log('\nRound 97: getAllSessions (whitespace search — truthy but unmatched):');

  if (test('getAllSessions with search: " " returns empty because space is truthy but never matches shortId', () => {
    // session-manager.js line 233: if (search && !metadata.shortId.includes(search))
    // ' ' (space) is truthy so the filter is applied, but shortIds are hex strings
    // that never contain spaces, so ALL sessions are filtered out.
    // The search filter is inside the loop, so total is also 0.
    const result = sessionManager.getAllSessions({ search: ' ', limit: 100 });
    assert.strictEqual(result.sessions.length, 0,
      'Whitespace search should filter out all sessions (space never appears in hex shortIds)');
    assert.strictEqual(result.total, 0,
      'Total should be 0 because search filter is applied inside the loop (line 233)');
    assert.strictEqual(result.hasMore, false,
      'hasMore should be false since no sessions matched');
    // Contrast with null/empty search which returns all sessions:
    const allResult = sessionManager.getAllSessions({ search: null, limit: 100 });
    assert.ok(allResult.total > 0,
      'Null search should return sessions (confirming they exist but space filtered them)');
  })) passed++; else failed++;

  // ── Round 98: getSessionById with null sessionId throws TypeError ──
  console.log('\nRound 98: getSessionById (null sessionId — crashes at line 297):');

  if (test('getSessionById(null) throws TypeError when session files exist', () => {
    // session-manager.js line 297: `sessionId.length > 0` — calling .length on null
    // throws TypeError because there's no early guard for null/undefined input.
    // This only surfaces when valid .tmp files exist in the sessions directory.
    assert.throws(
      () => sessionManager.getSessionById(null),
      { name: 'TypeError' },
      'null.length should throw TypeError (no input guard at function entry)'
    );
  })) passed++; else failed++;

  // Cleanup test environment for Rounds 95-98 that needed sessions
  // (Round 98: parseSessionFilename below doesn't need sessions)
  process.env.HOME = origHome2;
  if (origUserProfile2 !== undefined) {
    process.env.USERPROFILE = origUserProfile2;
  } else {
    delete process.env.USERPROFILE;
  }
  try {
    fs.rmSync(tmpHome2, { recursive: true, force: true });
  } catch {
    // best-effort
  }

  // ── Round 98: parseSessionFilename with null input throws TypeError ──
  console.log('\nRound 98: parseSessionFilename (null input — crashes at line 30):');

  if (test('parseSessionFilename(null) throws TypeError because null has no .match()', () => {
    // session-manager.js line 30: `filename.match(SESSION_FILENAME_REGEX)`
    // When filename is null, null.match() throws TypeError.
    // Function lacks a type guard like `if (!filename || typeof filename !== 'string')`.
    assert.throws(
      () => sessionManager.parseSessionFilename(null),
      { name: 'TypeError' },
      'null.match() should throw TypeError (no type guard on filename parameter)'
    );
  })) passed++; else failed++;

  // ── Round 99: writeSessionContent with null path returns false (error caught) ──
  console.log('\nRound 99: writeSessionContent (null path — error handling):');

  if (test('writeSessionContent(null, content) returns false (TypeError caught by try/catch)', () => {
    // session-manager.js lines 372-378: writeSessionContent wraps fs.writeFileSync
    // in a try/catch. When sessionPath is null, fs.writeFileSync throws TypeError:
    // 'The "path" argument must be of type string or Buffer or URL. Received null'
    // The catch block catches this and returns false (does not propagate).
    const result = sessionManager.writeSessionContent(null, 'some content');
    assert.strictEqual(result, false,
      'null path should be caught by try/catch and return false');
  })) passed++; else failed++;

  // ── Round 100: parseSessionMetadata with ### inside item text (premature section termination) ──
  console.log('\nRound 100: parseSessionMetadata (### in item text — lazy regex truncation):');
  if (test('parseSessionMetadata truncates item text at embedded ### due to lazy regex lookahead', () => {
    const content = `# Session

### Completed
- [x] Fix issue ### with parser
- [x] Normal task

### In Progress
- [ ] Debug output
`;
    const meta = sessionManager.parseSessionMetadata(content);
    // The lazy regex ([\s\S]*?)(?=###|\n\n|$) terminates at the first ###
    // So the Completed section captures only "- [x] Fix issue " (before the inner ###)
    // The second item "- [x] Normal task" is lost because it's after the inner ###
    assert.strictEqual(meta.completed.length, 1,
      'Only 1 item extracted — second item is after the inner ### terminator');
    assert.strictEqual(meta.completed[0], 'Fix issue',
      'Item text truncated at embedded ### (lazy regex stops at first ### match)');
  })) passed++; else failed++;

  // ── Round 101: getSessionStats with non-string input (number) throws TypeError ──
  console.log('\nRound 101: getSessionStats (non-string input — type confusion crash):');
  if (test('getSessionStats(123) throws TypeError (number reaches parseSessionMetadata → .match() fails)', () => {
    // typeof 123 === 'number' → looksLikePath = false → content = 123
    // parseSessionMetadata(123) → !123 is false → 123.match(...) → TypeError
    assert.throws(
      () => sessionManager.getSessionStats(123),
      { name: 'TypeError' },
      'Non-string input (number) should crash in parseSessionMetadata (.match not a function)'
    );
  })) passed++; else failed++;

  // ── Round 101: appendSessionContent(null, 'content') returns false (error caught) ──
  console.log('\nRound 101: appendSessionContent (null path — error handling):');
  if (test('appendSessionContent(null, content) returns false (TypeError caught by try/catch)', () => {
    const result = sessionManager.appendSessionContent(null, 'some content');
    assert.strictEqual(result, false,
      'null path should cause fs.appendFileSync to throw TypeError, caught by try/catch');
  })) passed++; else failed++;

  // ── Round 102: getSessionStats with Unix nonexistent .tmp path (looksLikePath heuristic) ──
  console.log('\nRound 102: getSessionStats (Unix nonexistent .tmp path — looksLikePath → null content):');
  if (test('getSessionStats returns zeroed stats when Unix path looks like file but does not exist', () => {
    // session-manager.js lines 163-166: looksLikePath heuristic checks typeof string,
    // no newlines, endsWith('.tmp'), startsWith('/').  A nonexistent Unix path triggers
    // the file-read branch → readFile returns null → parseSessionMetadata(null) returns
    // default empty metadata → lineCount: null ? ... : 0 === 0.
    const stats = sessionManager.getSessionStats('/nonexistent/deep/path/session.tmp');
    assert.strictEqual(stats.totalItems, 0,
      'No items from nonexistent file (parseSessionMetadata(null) returns empty arrays)');
    assert.strictEqual(stats.lineCount, 0,
      'lineCount: 0 because content is null (ternary guard at line 177)');
    assert.strictEqual(stats.hasNotes, false,
      'No notes section in null content');
    assert.strictEqual(stats.hasContext, false,
      'No context section in null content');
  })) passed++; else failed++;

  // ── Round 102: parseSessionMetadata with [x] checked items in In Progress section ──
  console.log('\nRound 102: parseSessionMetadata ([x] items in In Progress — regex skips checked):');
  if (test('parseSessionMetadata skips [x] checked items in In Progress section (regex only matches [ ])', () => {
    // session-manager.js line 130: progressSection regex uses `- \[ \]\s*(.+)` which
    // only matches unchecked checkboxes.  Checked items `- [x]` in the In Progress
    // section are silently ignored — they don't match the regex pattern.
    const content = `# Session

### In Progress
- [x] Already finished but placed here by mistake
- [ ] Actually in progress
- [x] Another misplaced completed item
- [ ] Second active task
`;
    const meta = sessionManager.parseSessionMetadata(content);
    assert.strictEqual(meta.inProgress.length, 2,
      'Only unchecked [ ] items should be captured (2 of 4)');
    assert.strictEqual(meta.inProgress[0], 'Actually in progress',
      'First unchecked item');
    assert.strictEqual(meta.inProgress[1], 'Second active task',
      'Second unchecked item');
  })) passed++; else failed++;

  // ── Round 104: parseSessionMetadata with whitespace-only notes section ──
  console.log('\nRound 104: parseSessionMetadata (whitespace-only notes — trim reduces to empty):');
  if (test('parseSessionMetadata treats whitespace-only notes as absent (trim → empty string → falsy)', () => {
    // session-manager.js line 139: `metadata.notes = notesSection[1].trim()` — when the
    // Notes section heading exists but only contains whitespace/newlines, trim() returns "".
    // Then getSessionStats line 178: `hasNotes: !!metadata.notes` — `!!""` is `false`.
    // So a notes section with only whitespace is treated as "no notes."
    const content = `# Session

### Notes for Next Session
   \t

### Context to Load
\`\`\`
file.ts
\`\`\`
`;
    const meta = sessionManager.parseSessionMetadata(content);
    assert.strictEqual(meta.notes, '',
      'Whitespace-only notes should trim to empty string');
    // Verify getSessionStats reports hasNotes as false
    const stats = sessionManager.getSessionStats(content);
    assert.strictEqual(stats.hasNotes, false,
      'hasNotes should be false because !!"" is false (whitespace-only notes treated as absent)');
    assert.strictEqual(stats.hasContext, true,
      'hasContext should be true (context section has actual content)');
  })) passed++; else failed++;

  // ── Round 105: parseSessionMetadata blank-line boundary truncates section items ──
  console.log('\nRound 105: parseSessionMetadata (blank line inside section — regex stops at \\n\\n):');

  if (test('parseSessionMetadata drops completed items after a blank line within the section', () => {
    // session-manager.js line 119: regex `(?=###|\n\n|$)` uses lazy [\s\S]*? with
    // a lookahead that stops at the first \n\n. If completed items are separated
    // by a blank line, items below the blank line are silently lost.
    const content = '# Session\n\n### Completed\n- [x] Task A\n\n- [x] Task B\n\n### In Progress\n- [ ] Task C\n';
    const meta = sessionManager.parseSessionMetadata(content);
    // The regex captures "- [x] Task A\n" then hits \n\n and stops.
    // "- [x] Task B" is between the two sections but outside both regex captures.
    assert.strictEqual(meta.completed.length, 1,
      'Only Task A captured — blank line terminates the section regex before Task B');
    assert.strictEqual(meta.completed[0], 'Task A',
      'First completed item should be Task A');
    // Task B is lost — it appears after the blank line, outside the captured range
    assert.strictEqual(meta.inProgress.length, 1,
      'In Progress should still capture Task C');
    assert.strictEqual(meta.inProgress[0], 'Task C',
      'In-progress item should be Task C');
  })) passed++; else failed++;

  // ── Round 106: getAllSessions with array/object limit — Number() coercion edge cases ──
  console.log('\nRound 106: getAllSessions (array/object limit coercion — Number([5])→5, Number({})→NaN→50):');
  if (test('getAllSessions coerces array/object limit via Number() with NaN fallback to 50', () => {
    const isoHome = path.join(os.tmpdir(), `ecc-r106-limit-coerce-${Date.now()}`);
    const isoSessionsDir = path.join(isoHome, '.claude', 'sessions');
    fs.mkdirSync(isoSessionsDir, { recursive: true });
    // Create 3 test sessions
    for (let i = 0; i < 3; i++) {
      const name = `2026-03-0${i + 1}-aaaa${i}${i}${i}${i}-session.tmp`;
      const filePath = path.join(isoSessionsDir, name);
      fs.writeFileSync(filePath, `# Session ${i}`);
      const mtime = new Date(Date.now() - (3 - i) * 60000);
      fs.utimesSync(filePath, mtime, mtime);
    }
    const origHome = process.env.HOME;
    const origUserProfile = process.env.USERPROFILE;
    process.env.HOME = isoHome;
    process.env.USERPROFILE = isoHome;
    try {
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      const freshManager = require('../../scripts/lib/session-manager');
      // Object limit: Number({}) → NaN → fallback to 50
      const objResult = freshManager.getAllSessions({ limit: {} });
      assert.strictEqual(objResult.limit, 50,
        'Object limit should coerce to NaN → fallback to default 50');
      assert.strictEqual(objResult.total, 3, 'Should still find all 3 sessions');
      // Single-element array: Number([2]) → 2
      const arrResult = freshManager.getAllSessions({ limit: [2] });
      assert.strictEqual(arrResult.limit, 2,
        'Single-element array [2] coerces to Number 2 via Number([2])');
      assert.strictEqual(arrResult.sessions.length, 2, 'Should return only 2 sessions');
      assert.strictEqual(arrResult.hasMore, true, 'hasMore should be true with limit 2 of 3');
      // Multi-element array: Number([1,2]) → NaN → fallback to 50
      const multiArrResult = freshManager.getAllSessions({ limit: [1, 2] });
      assert.strictEqual(multiArrResult.limit, 50,
        'Multi-element array [1,2] coerces to NaN → fallback to 50');
    } finally {
      process.env.HOME = origHome;
      process.env.USERPROFILE = origUserProfile;
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      fs.rmSync(isoHome, { recursive: true, force: true });
    }
  })) passed++; else failed++;

  // ── Round 109: getAllSessions skips .tmp files that don't match session filename format ──
  console.log('\nRound 109: getAllSessions (non-session .tmp files — parseSessionFilename returns null → skip):');
  if (test('getAllSessions ignores .tmp files with non-matching filenames', () => {
    const isoHome = path.join(os.tmpdir(), `ecc-r109-nonsession-${Date.now()}`);
    const isoSessionsDir = path.join(isoHome, '.claude', 'sessions');
    fs.mkdirSync(isoSessionsDir, { recursive: true });
    // Create one valid session file
    const validName = '2026-03-01-abcd1234-session.tmp';
    fs.writeFileSync(path.join(isoSessionsDir, validName), '# Valid Session');
    // Create non-session .tmp files that don't match the expected pattern
    fs.writeFileSync(path.join(isoSessionsDir, 'notes.tmp'), 'personal notes');
    fs.writeFileSync(path.join(isoSessionsDir, 'scratch.tmp'), 'scratch data');
    fs.writeFileSync(path.join(isoSessionsDir, 'backup-2026.tmp'), 'backup');
    const origHome = process.env.HOME;
    const origUserProfile = process.env.USERPROFILE;
    process.env.HOME = isoHome;
    process.env.USERPROFILE = isoHome;
    try {
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      const freshManager = require('../../scripts/lib/session-manager');
      const result = freshManager.getAllSessions({ limit: 100 });
      assert.strictEqual(result.total, 1,
        'Should find only 1 valid session (non-matching .tmp files skipped via !metadata continue)');
      assert.strictEqual(result.sessions[0].shortId, 'abcd1234',
        'The one valid session should have correct shortId');
    } finally {
      process.env.HOME = origHome;
      process.env.USERPROFILE = origUserProfile;
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      fs.rmSync(isoHome, { recursive: true, force: true });
    }
  })) passed++; else failed++;

  // ── Round 108: getSessionSize exact boundary at 1024 bytes — B→KB transition ──
  console.log('\nRound 108: getSessionSize (exact 1024-byte boundary — < means 1024 is KB, 1023 is B):');
  if (test('getSessionSize returns KB at exactly 1024 bytes and B at 1023', () => {
    const dir = createTempSessionDir();
    try {
      // Exactly 1024 bytes → size < 1024 is FALSE → goes to KB branch
      const atBoundary = path.join(dir, 'exact-1024.tmp');
      fs.writeFileSync(atBoundary, 'x'.repeat(1024));
      const sizeAt = sessionManager.getSessionSize(atBoundary);
      assert.strictEqual(sizeAt, '1.0 KB',
        'Exactly 1024 bytes should return "1.0 KB" (not "1024 B")');

      // 1023 bytes → size < 1024 is TRUE → stays in B branch
      const belowBoundary = path.join(dir, 'below-1024.tmp');
      fs.writeFileSync(belowBoundary, 'x'.repeat(1023));
      const sizeBelow = sessionManager.getSessionSize(belowBoundary);
      assert.strictEqual(sizeBelow, '1023 B',
        '1023 bytes should return "1023 B" (still in bytes range)');

      // Exactly 1MB boundary → 1048576 bytes
      const atMB = path.join(dir, 'exact-1mb.tmp');
      fs.writeFileSync(atMB, 'x'.repeat(1024 * 1024));
      const sizeMB = sessionManager.getSessionSize(atMB);
      assert.strictEqual(sizeMB, '1.0 MB',
        'Exactly 1MB should return "1.0 MB" (not "1024.0 KB")');
    } finally {
      cleanup(dir);
    }
  })) passed++; else failed++;

  // ── Round 110: parseSessionFilename year 0000 — JS Date maps year 0 to 1900 ──
  console.log('\nRound 110: parseSessionFilename (year 0000 — Date constructor maps 0→1900):');
  if (test('parseSessionFilename with year 0000 produces datetime in 1900 due to JS Date legacy mapping', () => {
    // JavaScript's multi-arg Date constructor treats years 0-99 as 1900-1999
    // So new Date(0, 0, 1) → January 1, 1900 (not year 0000)
    const result = sessionManager.parseSessionFilename('0000-01-01-abcd1234-session.tmp');
    assert.notStrictEqual(result, null, 'Should parse successfully (regex \\d{4} matches 0000)');
    assert.strictEqual(result.date, '0000-01-01', 'Date string should be "0000-01-01"');
    assert.strictEqual(result.shortId, 'abcd1234');
    // The key quirk: datetime is year 1900, not 0000
    assert.strictEqual(result.datetime.getFullYear(), 1900,
      'JS Date maps year 0 to 1900 in multi-arg constructor');
    // Year 99 maps to 1999
    const result99 = sessionManager.parseSessionFilename('0099-06-15-testid01-session.tmp');
    assert.notStrictEqual(result99, null, 'Year 0099 should also parse');
    assert.strictEqual(result99.datetime.getFullYear(), 1999,
      'JS Date maps year 99 to 1999');
    // Year 100 does NOT get the 1900 mapping — it stays as year 100
    const result100 = sessionManager.parseSessionFilename('0100-03-10-validid1-session.tmp');
    assert.notStrictEqual(result100, null, 'Year 0100 should also parse');
    assert.strictEqual(result100.datetime.getFullYear(), 100,
      'Year 100+ is not affected by the 0-99 → 1900-1999 mapping');
  })) passed++; else failed++;

  // ── Round 110: parseSessionFilename rejects uppercase IDs (regex is [a-z0-9]) ──
  console.log('\nRound 110: parseSessionFilename (uppercase ID — regex [a-z0-9]{8,} rejects [A-Z]):');
  if (test('parseSessionFilename rejects filenames with uppercase characters in short ID', () => {
    // SESSION_FILENAME_REGEX uses [a-z0-9]{8,} — strictly lowercase
    const upperResult = sessionManager.parseSessionFilename('2026-01-15-ABCD1234-session.tmp');
    assert.strictEqual(upperResult, null,
      'All-uppercase ID should be rejected by [a-z0-9]{8,}');
    const mixedResult = sessionManager.parseSessionFilename('2026-01-15-AbCd1234-session.tmp');
    assert.strictEqual(mixedResult, null,
      'Mixed-case ID should be rejected by [a-z0-9]{8,}');
    // Confirm lowercase is accepted
    const lowerResult = sessionManager.parseSessionFilename('2026-01-15-abcd1234-session.tmp');
    assert.notStrictEqual(lowerResult, null,
      'All-lowercase ID should be accepted');
    assert.strictEqual(lowerResult.shortId, 'abcd1234');
  })) passed++; else failed++;

  // ── Round 111: parseSessionMetadata context with nested triple backticks — lazy regex truncation ──
  console.log('\nRound 111: parseSessionMetadata (nested ``` in context — lazy \\S*? stops at first ```):");');
  if (test('parseSessionMetadata context capture truncated by nested triple backticks', () => {
    // The regex: /### Context to Load\s*\n```\n([\s\S]*?)```/
    // The lazy [\s\S]*? matches as few chars as possible, so it stops at the
    // FIRST ``` it encounters — even if that's inside the code block content.
    const content = [
      '# Session',
      '',
      '### Context to Load',
      '```',
      'const x = 1;',
      '```nested code block```',  // Inner ``` causes premature match end
      'const y = 2;',
      '```'
    ].join('\n');
    const meta = sessionManager.parseSessionMetadata(content);
    // Lazy regex stops at the inner ```, so context only captures "const x = 1;\n"
    assert.ok(meta.context.includes('const x = 1'),
      'Context should contain text before the inner backticks');
    assert.ok(!meta.context.includes('const y = 2'),
      'Context should NOT contain text after inner ``` (lazy regex stops early)');
    // Without nested backticks, full content is captured
    const cleanContent = [
      '# Session',
      '',
      '### Context to Load',
      '```',
      'const x = 1;',
      'const y = 2;',
      '```'
    ].join('\n');
    const cleanMeta = sessionManager.parseSessionMetadata(cleanContent);
    assert.ok(cleanMeta.context.includes('const x = 1'),
      'Clean context should have first line');
    assert.ok(cleanMeta.context.includes('const y = 2'),
      'Clean context should have second line');
  })) passed++; else failed++;

  // ── Round 112: getSessionStats with newline-containing absolute path — treated as content ──
  console.log('\nRound 112: getSessionStats (newline-in-path heuristic):');
  if (test('getSessionStats treats absolute .tmp path containing newline as content, not a file path', () => {
    // The looksLikePath heuristic at line 163-166 checks:
    //   !sessionPathOrContent.includes('\n')
    // A string with embedded newline fails this check and is treated as content
    const pathWithNewline = '/tmp/sessions/2026-01-15\n-abcd1234-session.tmp';

    // This should NOT throw (it's treated as content, not a path that doesn't exist)
    const stats = sessionManager.getSessionStats(pathWithNewline);
    assert.ok(stats, 'Should return stats object (treating input as content)');
    // The "content" has 2 lines (split by the embedded \n)
    assert.strictEqual(stats.lineCount, 2,
      'Should count 2 lines in the "content" (split at \\n)');
    // No markdown headings = no completed/in-progress items
    assert.strictEqual(stats.totalItems, 0,
      'Should find 0 items in non-markdown content');

    // Contrast: a real absolute path without newlines IS treated as a path
    const realPath = '/tmp/nonexistent-session.tmp';
    const realStats = sessionManager.getSessionStats(realPath);
    // getSessionContent returns '' for non-existent files, so lineCount = 1 (empty string split)
    assert.ok(realStats, 'Should return stats even for nonexistent path');
    assert.strictEqual(realStats.lineCount, 0,
      'Non-existent file returns empty content with 0 lines');
  })) passed++; else failed++;

  // ── Round 112: appendSessionContent with read-only file — returns false ──
  console.log('\nRound 112: appendSessionContent (read-only file):');
  if (test('appendSessionContent returns false when file is read-only (EACCES)', () => {
    if (process.platform === 'win32') {
      // chmod doesn't work reliably on Windows — skip
      assert.ok(true, 'Skipped on Windows');
      return;
    }
    const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'r112-readonly-'));
    const readOnlyFile = path.join(tmpDir, '2026-01-15-session.tmp');
    try {
      fs.writeFileSync(readOnlyFile, '# Session\n\nInitial content\n');
      // Make file read-only
      fs.chmodSync(readOnlyFile, 0o444);
      // Verify it exists and is readable
      const content = fs.readFileSync(readOnlyFile, 'utf8');
      assert.ok(content.includes('Initial content'), 'File should be readable');

      // appendSessionContent should catch EACCES and return false
      const result = sessionManager.appendSessionContent(readOnlyFile, '\nAppended data');
      assert.strictEqual(result, false,
        'Should return false when file is read-only (fs.appendFileSync throws EACCES)');

      // Verify original content unchanged
      const afterContent = fs.readFileSync(readOnlyFile, 'utf8');
      assert.ok(!afterContent.includes('Appended data'),
        'Original content should be unchanged');
    } finally {
      try { fs.chmodSync(readOnlyFile, 0o644); } catch (_e) { /* ignore permission errors */ }
      fs.rmSync(tmpDir, { recursive: true, force: true });
    }
  })) passed++; else failed++;

  // ── Round 113: parseSessionFilename century leap year validation (1900, 2100 not leap; 2000 is) ──
  console.log('\nRound 113: parseSessionFilename (century leap year — 100/400 rules):');
  if (test('parseSessionFilename rejects Feb 29 in century non-leap years (1900, 2100) but accepts 2000', () => {
    // Gregorian rule: divisible by 100 → NOT leap, UNLESS also divisible by 400
    // 1900: divisible by 100 but NOT by 400 → NOT leap → Feb 29 invalid
    const result1900 = sessionManager.parseSessionFilename('1900-02-29-abcd1234-session.tmp');
    assert.strictEqual(result1900, null,
      '1900 is NOT a leap year (div by 100 but not 400) — Feb 29 should be rejected');

    // 2100: same rule — NOT leap
    const result2100 = sessionManager.parseSessionFilename('2100-02-29-test1234-session.tmp');
    assert.strictEqual(result2100, null,
      '2100 is NOT a leap year — Feb 29 should be rejected');

    // 2000: divisible by 400 → IS leap → Feb 29 valid
    const result2000 = sessionManager.parseSessionFilename('2000-02-29-leap2000-session.tmp');
    assert.notStrictEqual(result2000, null,
      '2000 IS a leap year (div by 400) — Feb 29 should be accepted');
    assert.strictEqual(result2000.date, '2000-02-29');

    // 2400: also divisible by 400 → IS leap
    const result2400 = sessionManager.parseSessionFilename('2400-02-29-test2400-session.tmp');
    assert.notStrictEqual(result2400, null,
      '2400 IS a leap year (div by 400) — Feb 29 should be accepted');

    // Verify Feb 28 always works in non-leap century years
    const result1900Feb28 = sessionManager.parseSessionFilename('1900-02-28-abcd1234-session.tmp');
    assert.notStrictEqual(result1900Feb28, null,
      'Feb 28 should always be valid even in non-leap years');
  })) passed++; else failed++;

  // ── Round 113: parseSessionMetadata title with markdown formatting — raw markdown preserved ──
  console.log('\nRound 113: parseSessionMetadata (title with markdown formatting — raw markdown preserved):');
  if (test('parseSessionMetadata captures raw markdown formatting in title without stripping', () => {
    // The regex /^#\s+(.+)$/m captures everything after "# ", including markdown
    const boldContent = '# **Important Session**\n\nSome content';
    const boldMeta = sessionManager.parseSessionMetadata(boldContent);
    assert.strictEqual(boldMeta.title, '**Important Session**',
      'Bold markdown ** should be preserved in title (not stripped)');

    // Inline code in title
    const codeContent = '# `fix-bug` Session\n\nContent here';
    const codeMeta = sessionManager.parseSessionMetadata(codeContent);
    assert.strictEqual(codeMeta.title, '`fix-bug` Session',
      'Inline code backticks should be preserved in title');

    // Italic in title
    const italicContent = '# _Urgent_ Review\n\n**Date:** 2026-01-01';
    const italicMeta = sessionManager.parseSessionMetadata(italicContent);
    assert.strictEqual(italicMeta.title, '_Urgent_ Review',
      'Italic underscores should be preserved in title');

    // Mixed markdown in title
    const mixedContent = '# **Bold** and `code` and _italic_\n\nBody text';
    const mixedMeta = sessionManager.parseSessionMetadata(mixedContent);
    assert.strictEqual(mixedMeta.title, '**Bold** and `code` and _italic_',
      'Mixed markdown should all be preserved as raw text');

    // Title with trailing whitespace (trim should remove it)
    const trailingContent = '# Title with spaces   \n\nBody';
    const trailingMeta = sessionManager.parseSessionMetadata(trailingContent);
    assert.strictEqual(trailingMeta.title, 'Title with spaces',
      'Trailing whitespace should be trimmed');
  })) passed++; else failed++;

  // ── Round 115: parseSessionMetadata with CRLF line endings — section boundaries differ ──
  console.log('\nRound 115: parseSessionMetadata (CRLF line endings — \\r\\n vs \\n in section regexes):');
  if (test('parseSessionMetadata handles CRLF content — title trimmed, sections may over-capture', () => {
    // Title regex /^#\s+(.+)$/m: . matches \r, trim() removes it
    const crlfTitle = '# My Session\r\n\r\n**Date:** 2026-01-15';
    const titleMeta = sessionManager.parseSessionMetadata(crlfTitle);
    assert.strictEqual(titleMeta.title, 'My Session',
      'Title should be trimmed (\\r removed by .trim())');
    assert.strictEqual(titleMeta.date, '2026-01-15',
      'Date extraction unaffected by CRLF');

    // Completed section with CRLF: regex ### Completed\s*\n works because \s* matches \r
    // But the boundary (?=###|\n\n|$) — \n\n won't match \r\n\r\n
    const crlfSections = [
      '# Session\r\n',
      '\r\n',
      '### Completed\r\n',
      '- [x] Task A\r\n',
      '- [x] Task B\r\n',
      '\r\n',
      '### In Progress\r\n',
      '- [ ] Task C\r\n'
    ].join('');

    const sectionMeta = sessionManager.parseSessionMetadata(crlfSections);

    // \s* in "### Completed\s*\n" matches the \r before \n, so section header matches
    assert.ok(sectionMeta.completed.length >= 2,
      'Should find at least 2 completed items (\\s* consumes \\r before \\n)');
    assert.ok(sectionMeta.completed.includes('Task A'), 'Should find Task A');
    assert.ok(sectionMeta.completed.includes('Task B'), 'Should find Task B');

    // In Progress section: \n\n boundary fails on \r\n\r\n, so the lazy [\s\S]*?
    // stops at ### instead — this still works because ### is present
    assert.ok(sectionMeta.inProgress.length >= 1,
      'Should find at least 1 in-progress item');
    assert.ok(sectionMeta.inProgress.includes('Task C'), 'Should find Task C');

    // Edge case: CRLF content with NO section headers after Completed —
    // \n\n boundary fails, so [\s\S]*? falls through to $ (end of string)
    const crlfNoNextSection = [
      '# Session\r\n',
      '\r\n',
      '### Completed\r\n',
      '- [x] Only task\r\n',
      '\r\n',
      'Some trailing text\r\n'
    ].join('');

    const noNextMeta = sessionManager.parseSessionMetadata(crlfNoNextSection);
    // Without a ### boundary, the \n\n lookahead fails on \r\n\r\n,
    // so [\s\S]*? extends to $ and captures everything including trailing text
    assert.ok(noNextMeta.completed.length >= 1,
      'Should find at least 1 completed item in CRLF-only content');
  })) passed++; else failed++;

  // ── Round 117: getSessionSize boundary values — B/KB/MB formatting thresholds ──
  console.log('\nRound 117: getSessionSize (B/KB/MB formatting at exact boundary thresholds):');
  if (test('getSessionSize formats correctly at B→KB boundary (1023→"1023 B", 1024→"1.0 KB") and KB→MB', () => {
    const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'r117-size-boundary-'));
    try {
      // Zero-byte file
      const zeroFile = path.join(tmpDir, '2026-01-01-session.tmp');
      fs.writeFileSync(zeroFile, '');
      assert.strictEqual(sessionManager.getSessionSize(zeroFile), '0 B',
        'Empty file should be "0 B"');

      // 1 byte file
      const oneByteFile = path.join(tmpDir, '2026-01-02-session.tmp');
      fs.writeFileSync(oneByteFile, 'x');
      assert.strictEqual(sessionManager.getSessionSize(oneByteFile), '1 B',
        'Single byte file should be "1 B"');

      // 1023 bytes — last value in B range (size < 1024)
      const file1023 = path.join(tmpDir, '2026-01-03-session.tmp');
      fs.writeFileSync(file1023, 'x'.repeat(1023));
      assert.strictEqual(sessionManager.getSessionSize(file1023), '1023 B',
        '1023 bytes is still in B range (< 1024)');

      // 1024 bytes — first value in KB range (size >= 1024, < 1024*1024)
      const file1024 = path.join(tmpDir, '2026-01-04-session.tmp');
      fs.writeFileSync(file1024, 'x'.repeat(1024));
      assert.strictEqual(sessionManager.getSessionSize(file1024), '1.0 KB',
        '1024 bytes = exactly 1.0 KB');

      // 1025 bytes — KB with decimal
      const file1025 = path.join(tmpDir, '2026-01-05-session.tmp');
      fs.writeFileSync(file1025, 'x'.repeat(1025));
      assert.strictEqual(sessionManager.getSessionSize(file1025), '1.0 KB',
        '1025 bytes rounds to 1.0 KB (1025/1024 = 1.000...)');

      // Non-existent file returns '0 B'
      assert.strictEqual(sessionManager.getSessionSize('/nonexistent/file.tmp'), '0 B',
        'Non-existent file should return "0 B"');
    } finally {
      fs.rmSync(tmpDir, { recursive: true, force: true });
    }
  })) passed++; else failed++;

  // ── Round 117: parseSessionFilename with uppercase short ID — regex rejects [A-Z] ──
  console.log('\nRound 117: parseSessionFilename (uppercase short ID — regex [a-z0-9] rejects uppercase):');
  if (test('parseSessionFilename rejects uppercase short IDs because regex uses [a-z0-9] not [a-zA-Z0-9]', () => {
    // The regex: /^(\d{4}-\d{2}-\d{2})(?:-([a-z0-9]{8,}))?-session\.tmp$/
    // Note: [a-z0-9] — lowercase only

    // All uppercase — rejected
    const upper = sessionManager.parseSessionFilename('2026-01-15-ABCDEFGH-session.tmp');
    assert.strictEqual(upper, null,
      'All-uppercase ID should be rejected (regex uses [a-z0-9])');

    // Mixed case — rejected
    const mixed = sessionManager.parseSessionFilename('2026-01-15-AbCdEfGh-session.tmp');
    assert.strictEqual(mixed, null,
      'Mixed-case ID should be rejected (uppercase chars not in [a-z0-9])');

    // All lowercase — accepted
    const lower = sessionManager.parseSessionFilename('2026-01-15-abcdefgh-session.tmp');
    assert.notStrictEqual(lower, null, 'All-lowercase ID should be accepted');
    assert.strictEqual(lower.shortId, 'abcdefgh');

    // Uppercase hex-like (common in UUIDs) — rejected
    const hexUpper = sessionManager.parseSessionFilename('2026-01-15-A1B2C3D4-session.tmp');
    assert.strictEqual(hexUpper, null,
      'Uppercase hex ID should be rejected');

    // Lowercase hex — accepted
    const hexLower = sessionManager.parseSessionFilename('2026-01-15-a1b2c3d4-session.tmp');
    assert.notStrictEqual(hexLower, null, 'Lowercase hex ID should be accepted');
    assert.strictEqual(hexLower.shortId, 'a1b2c3d4');
  })) passed++; else failed++;

  // ── Round 119: parseSessionMetadata "Context to Load" code block extraction ──
  console.log('\nRound 119: parseSessionMetadata ("Context to Load" — code block extraction edge cases):');
  if (test('parseSessionMetadata extracts Context to Load from code block, handles missing/nested blocks', () => {
    // Valid context extraction
    const validContent = [
      '# Session\n\n',
      '### Context to Load\n',
      '```\n',
      'file1.js\n',
      'file2.ts\n',
      '```\n'
    ].join('');
    const validMeta = sessionManager.parseSessionMetadata(validContent);
    assert.strictEqual(validMeta.context, 'file1.js\nfile2.ts',
      'Should extract content between ``` markers and trim');

    // Missing closing backticks — regex doesn't match, context stays empty
    const noClose = [
      '# Session\n\n',
      '### Context to Load\n',
      '```\n',
      'file1.js\n',
      'file2.ts\n'
    ].join('');
    const noCloseMeta = sessionManager.parseSessionMetadata(noClose);
    assert.strictEqual(noCloseMeta.context, '',
      'Missing closing ``` should result in empty context (regex no match)');

    // No code block after header — just plain text
    const noBlock = [
      '# Session\n\n',
      '### Context to Load\n',
      'file1.js\n',
      'file2.ts\n'
    ].join('');
    const noBlockMeta = sessionManager.parseSessionMetadata(noBlock);
    assert.strictEqual(noBlockMeta.context, '',
      'Plain text without ``` should not be captured as context');

    // Nested code block — lazy [\s\S]*? stops at first ```
    const nested = [
      '# Session\n\n',
      '### Context to Load\n',
      '```\n',
      'first block\n',
      '```\n',
      'second block\n',
      '```\n'
    ].join('');
    const nestedMeta = sessionManager.parseSessionMetadata(nested);
    assert.strictEqual(nestedMeta.context, 'first block',
      'Lazy quantifier should stop at first closing ``` (not greedy)');

    // Empty code block
    const emptyBlock = '# Session\n\n### Context to Load\n```\n```\n';
    const emptyMeta = sessionManager.parseSessionMetadata(emptyBlock);
    assert.strictEqual(emptyMeta.context, '',
      'Empty code block should result in empty context (trim of empty)');
  })) passed++; else failed++;

  // ── Round 120: parseSessionMetadata "Notes for Next Session" extraction edge cases ──
  console.log('\nRound 120: parseSessionMetadata ("Notes for Next Session" — extraction edge cases):');
  if (test('parseSessionMetadata extracts notes section — last section, empty, followed by ###', () => {
    // Notes as the last section (no ### or \n\n after)
    const lastSection = '# Session\n\n### Notes for Next Session\nRemember to review PR #42\nAlso check CI status';
    const lastMeta = sessionManager.parseSessionMetadata(lastSection);
    assert.strictEqual(lastMeta.notes, 'Remember to review PR #42\nAlso check CI status',
      'Notes as last section should capture everything to end of string via $ anchor');
    assert.strictEqual(lastMeta.hasNotes, undefined,
      'hasNotes is not a direct property of parseSessionMetadata result');

    // Notes followed by another ### section
    const withNext = '# Session\n\n### Notes for Next Session\nImportant note\n### Context to Load\n```\nfiles\n```';
    const nextMeta = sessionManager.parseSessionMetadata(withNext);
    assert.strictEqual(nextMeta.notes, 'Important note',
      'Notes should stop at next ### header');

    // Notes followed by \n\n (double newline)
    const withDoubleNewline = '# Session\n\n### Notes for Next Session\nNote here\n\nSome other text';
    const dblMeta = sessionManager.parseSessionMetadata(withDoubleNewline);
    assert.strictEqual(dblMeta.notes, 'Note here',
      'Notes should stop at \\n\\n boundary');

    // Empty notes section (header only, followed by \n\n)
    const emptyNotes = '# Session\n\n### Notes for Next Session\n\n### Other Section';
    const emptyMeta = sessionManager.parseSessionMetadata(emptyNotes);
    assert.strictEqual(emptyMeta.notes, '',
      'Empty notes section should result in empty string after trim');

    // Notes with markdown formatting
    const markdownNotes = '# Session\n\n### Notes for Next Session\n- [ ] Review **important** PR\n- [x] Check `config.js`\n\n### Done';
    const mdMeta = sessionManager.parseSessionMetadata(markdownNotes);
    assert.ok(mdMeta.notes.includes('**important**'),
      'Markdown bold should be preserved in notes');
    assert.ok(mdMeta.notes.includes('`config.js`'),
      'Markdown code should be preserved in notes');
  })) passed++; else failed++;

  // ── Round 121: parseSessionMetadata Started/Last Updated time extraction ──
  console.log('\nRound 121: parseSessionMetadata (Started/Last Updated time extraction):');
  if (test('parseSessionMetadata extracts Started and Last Updated times from markdown', () => {
    // Standard format
    const standard = '# Session\n\n**Date:** 2026-01-15\n**Started:** 14:30\n**Last Updated:** 16:45';
    const stdMeta = sessionManager.parseSessionMetadata(standard);
    assert.strictEqual(stdMeta.started, '14:30', 'Should extract started time');
    assert.strictEqual(stdMeta.lastUpdated, '16:45', 'Should extract last updated time');

    // With seconds in time
    const withSec = '# Session\n\n**Started:** 14:30:00\n**Last Updated:** 16:45:59';
    const secMeta = sessionManager.parseSessionMetadata(withSec);
    assert.strictEqual(secMeta.started, '14:30:00', 'Should capture seconds too ([\\d:]+)');
    assert.strictEqual(secMeta.lastUpdated, '16:45:59');

    // Missing Started but has Last Updated
    const noStarted = '# Session\n\n**Last Updated:** 09:00';
    const noStartMeta = sessionManager.parseSessionMetadata(noStarted);
    assert.strictEqual(noStartMeta.started, null, 'Missing Started should be null');
    assert.strictEqual(noStartMeta.lastUpdated, '09:00', 'Last Updated should still be extracted');

    // Missing Last Updated but has Started
    const noUpdated = '# Session\n\n**Started:** 08:15';
    const noUpdMeta = sessionManager.parseSessionMetadata(noUpdated);
    assert.strictEqual(noUpdMeta.started, '08:15', 'Started should be extracted');
    assert.strictEqual(noUpdMeta.lastUpdated, null, 'Missing Last Updated should be null');

    // Neither present
    const neither = '# Session\n\nJust some text';
    const neitherMeta = sessionManager.parseSessionMetadata(neither);
    assert.strictEqual(neitherMeta.started, null, 'No Started in content → null');
    assert.strictEqual(neitherMeta.lastUpdated, null, 'No Last Updated in content → null');

    // Loose regex: edge case with extra colons ([\d:]+ matches any digit-colon combo)
    const loose = '# Session\n\n**Started:** 1:2:3:4';
    const looseMeta = sessionManager.parseSessionMetadata(loose);
    assert.strictEqual(looseMeta.started, '1:2:3:4',
      'Loose [\\d:]+ regex captures any digits-and-colons combination');
  })) passed++; else failed++;

  // ── Round 122: getSessionById old format (no-id) — noIdMatch path ──
  console.log('\nRound 122: getSessionById (old format no-id — date-only filename match):');
  if (test('getSessionById matches old format YYYY-MM-DD-session.tmp via noIdMatch path', () => {
    const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'r122-old-format-'));
    const origHome = process.env.HOME;
    const origUserProfile = process.env.USERPROFILE;
    const origDir = process.env.CLAUDE_DIR;
    try {
      // Set up isolated environment
      const claudeDir = path.join(tmpDir, '.claude');
      const sessionsDir = path.join(claudeDir, 'sessions');
      fs.mkdirSync(sessionsDir, { recursive: true });
      process.env.HOME = tmpDir;
      process.env.USERPROFILE = tmpDir; // Windows: os.homedir() uses USERPROFILE
      delete process.env.CLAUDE_DIR;

      // Clear require cache for fresh module with new HOME
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      const freshSM = require('../../scripts/lib/session-manager');

      // Create old-format session file (no short ID)
      const oldFile = path.join(sessionsDir, '2026-01-15-session.tmp');
      fs.writeFileSync(oldFile, '# Old Format Session\n\n**Date:** 2026-01-15\n');

      // Search by date — triggers noIdMatch path
      const result = freshSM.getSessionById('2026-01-15');
      assert.ok(result, 'Should find old-format session by date string');
      assert.strictEqual(result.shortId, 'no-id',
        'Old format should have shortId "no-id"');
      assert.strictEqual(result.date, '2026-01-15');
      assert.strictEqual(result.filename, '2026-01-15-session.tmp');

      // Search by non-matching date — should not find
      const noResult = freshSM.getSessionById('2026-01-16');
      assert.strictEqual(noResult, null,
        'Non-matching date should return null');
    } finally {
      process.env.HOME = origHome;
      if (origUserProfile !== undefined) process.env.USERPROFILE = origUserProfile;
      else delete process.env.USERPROFILE;
      if (origDir) process.env.CLAUDE_DIR = origDir;
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      fs.rmSync(tmpDir, { recursive: true, force: true });
    }
  })) passed++; else failed++;

  // ── Round 123: parseSessionMetadata with CRLF line endings — section boundaries break ──
  console.log('\nRound 123: parseSessionMetadata (CRLF section boundaries — \\n\\n fails to match \\r\\n\\r\\n):');
  if (test('parseSessionMetadata CRLF content: \\n\\n boundary fails, lazy match bleeds across sections', () => {
    // session-manager.js lines 119-134: regex uses (?=###|\n\n|$) to delimit sections.
    // On CRLF content, a blank line is \r\n\r\n, NOT \n\n. The \n\n alternation
    // won't match, so the lazy [\s\S]*? extends past the blank line until it hits
    // ### or $. This means completed items may bleed into following sections.
    //
    // However, \s* in /### Completed\s*\n/ DOES match \r\n (since \r is whitespace),
    // so section headers still match — only blank-line boundaries fail.

    // Test 1: CRLF with ### delimiter — works because ### is an alternation
    const crlfWithHash = [
      '# Session Title\r\n',
      '\r\n',
      '### Completed\r\n',
      '- [x] Task A\r\n',
      '### In Progress\r\n',
      '- [ ] Task B\r\n'
    ].join('');
    const meta1 = sessionManager.parseSessionMetadata(crlfWithHash);
    // ### delimiter still works — lazy match stops at ### In Progress
    assert.ok(meta1.completed.length >= 1,
      'Completed section should find at least 1 item with ### boundary on CRLF');
    // Check that Task A is found (may include \r in the trimmed text)
    const taskA = meta1.completed[0];
    assert.ok(taskA.includes('Task A'),
      'Should extract Task A from completed section');

    // Test 2: CRLF with \n\n (blank line) delimiter — this is where it breaks
    const crlfBlankLine = [
      '# Session\r\n',
      '\r\n',
      '### Completed\r\n',
      '- [x] First task\r\n',
      '\r\n',         // Blank line = \r\n\r\n — won't match \n\n
      'Some other text\r\n'
    ].join('');
    const meta2 = sessionManager.parseSessionMetadata(crlfBlankLine);
    // On LF, blank line stops the lazy match. On CRLF, it bleeds through.
    // The lazy [\s\S]*? stops at $ if no ### or \n\n matches,
    // so "Some other text" may end up captured in the raw section text.
    // But the items regex /- \[x\]\s*(.+)/g only captures checkbox lines,
    // so the count stays correct despite the bleed.
    assert.strictEqual(meta2.completed.length, 1,
      'Even with CRLF bleed, checkbox regex only matches "- [x]" lines');

    // Test 3: LF version of same content — proves \n\n works normally
    const lfBlankLine = '# Session\n\n### Completed\n- [x] First task\n\nSome other text\n';
    const meta3 = sessionManager.parseSessionMetadata(lfBlankLine);
    assert.strictEqual(meta3.completed.length, 1,
      'LF version: blank line correctly delimits section');

    // Test 4: CRLF notes section — lazy match goes to $ when \n\n fails
    const crlfNotes = [
      '# Session\r\n',
      '\r\n',
      '### Notes for Next Session\r\n',
      'Remember to review\r\n',
      '\r\n',
      'This should be separate\r\n'
    ].join('');
    const meta4 = sessionManager.parseSessionMetadata(crlfNotes);
    // On CRLF, \n\n fails → lazy match extends to $ → includes "This should be separate"
    // On LF, \n\n works → notes = "Remember to review" only
    const lfNotes = '# Session\n\n### Notes for Next Session\nRemember to review\n\nThis should be separate\n';
    const meta5 = sessionManager.parseSessionMetadata(lfNotes);
    assert.strictEqual(meta5.notes, 'Remember to review',
      'LF: notes stop at blank line');
    // CRLF notes will be longer (bleed through blank line)
    assert.ok(meta4.notes.length >= meta5.notes.length,
      'CRLF notes >= LF notes length (CRLF may bleed past blank line)');
  })) passed++; else failed++;

  // ── Round 124: getAllSessions with invalid date format (strict equality, no normalization) ──
  console.log('\nRound 124: getAllSessions (invalid date format — strict !== comparison):');
  if (test('getAllSessions date filter uses strict equality so wrong format returns empty', () => {
    // session-manager.js line 228: `if (date && metadata.date !== date)` — strict inequality.
    // metadata.date is always "YYYY-MM-DD" format. Passing a different format like
    // "2026/01/15" or "Jan 15 2026" will never match, silently returning empty.
    // No validation or normalization occurs on the date parameter.
    const origHome = process.env.HOME;
    const origUserProfile = process.env.USERPROFILE;
    const origDir = process.env.CLAUDE_DIR;
    const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'r124-date-format-'));
    const homeDir = path.join(tmpDir, 'home');
    fs.mkdirSync(path.join(homeDir, '.claude', 'sessions'), { recursive: true });

    try {
      process.env.HOME = homeDir;
      process.env.USERPROFILE = homeDir; // Windows: os.homedir() uses USERPROFILE
      delete process.env.CLAUDE_DIR;
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      const freshSM = require('../../scripts/lib/session-manager');

      // Create a session file with valid date
      const sessionsDir = path.join(homeDir, '.claude', 'sessions');
      fs.writeFileSync(
        path.join(sessionsDir, '2026-01-15-abcd1234-session.tmp'),
        '# Test Session'
      );

      // Correct format — should find 1 session
      const correct = freshSM.getAllSessions({ date: '2026-01-15' });
      assert.strictEqual(correct.sessions.length, 1,
        'Correct YYYY-MM-DD format should match');

      // Wrong separator — strict !== means no match
      const wrongSep = freshSM.getAllSessions({ date: '2026/01/15' });
      assert.strictEqual(wrongSep.sessions.length, 0,
        'Slash-separated date does not match (strict string equality)');

      // US format — no match
      const usFormat = freshSM.getAllSessions({ date: '01-15-2026' });
      assert.strictEqual(usFormat.sessions.length, 0,
        'MM-DD-YYYY format does not match YYYY-MM-DD');

      // Partial date — no match
      const partial = freshSM.getAllSessions({ date: '2026-01' });
      assert.strictEqual(partial.sessions.length, 0,
        'Partial YYYY-MM does not match full YYYY-MM-DD');

      // null date — skips filter, returns all
      const nullDate = freshSM.getAllSessions({ date: null });
      assert.strictEqual(nullDate.sessions.length, 1,
        'null date skips filter and returns all sessions');
    } finally {
      process.env.HOME = origHome;
      if (origUserProfile !== undefined) process.env.USERPROFILE = origUserProfile;
      else delete process.env.USERPROFILE;
      if (origDir) process.env.CLAUDE_DIR = origDir;
      delete require.cache[require.resolve('../../scripts/lib/utils')];
      delete require.cache[require.resolve('../../scripts/lib/session-manager')];
      fs.rmSync(tmpDir, { recursive: true, force: true });
    }
  })) passed++; else failed++;

  // ── Round 124: parseSessionMetadata title edge cases (no space, wrong level, multiple, empty) ──
  console.log('\nRound 124: parseSessionMetadata (title regex edge cases — /^#\\s+(.+)$/m):');
  if (test('parseSessionMetadata title: no space after # fails, ## fails, multiple picks first, empty trims', () => {
    // session-manager.js line 95: /^#\s+(.+)$/m
    // \s+ requires at least one whitespace after #, (.+) captures rest of line

    // No space after # — \s+ fails to match
    const noSpace = '#NoSpaceTitle\n\nSome content';
    const meta1 = sessionManager.parseSessionMetadata(noSpace);
    assert.strictEqual(meta1.title, null,
      '#NoSpaceTitle has no whitespace after # → title is null');

    // ## (H2) heading — ^ anchors to line start, but # matches first char only
    // /^#\s+/ matches the first # then \s+ would need whitespace, but ## has another #
    // Actually: /^#\s+(.+)$/ → "##" → # then \s+ → # is not whitespace → no match
    const h2 = '## Subtitle\n\nContent';
    const meta2 = sessionManager.parseSessionMetadata(h2);
    assert.strictEqual(meta2.title, null,
      '## heading does not match /^#\\s+/ because second # is not whitespace');

    // Multiple # headings — first match wins (regex .match returns first)
    const multiple = '# First Title\n\n# Second Title\n\nContent';
    const meta3 = sessionManager.parseSessionMetadata(multiple);
    assert.strictEqual(meta3.title, 'First Title',
      'Multiple H1 headings: .match() returns first occurrence');

    // # followed by spaces then text — leading spaces in capture are trimmed
    const padded = '#   Padded Title   \n\nContent';
    const meta4 = sessionManager.parseSessionMetadata(padded);
    assert.strictEqual(meta4.title, 'Padded Title',
      'Extra spaces: \\s+ matches multiple spaces, (.+) captures, .trim() cleans');

    // # followed by just spaces (no actual title text)
    // Surprising: \s+ is greedy and includes \n, so it matches "    \n\n" (spaces + newlines)
    // Then (.+) captures "Content" from the next non-empty line!
    const spacesOnly = '#    \n\nContent';
    const meta5 = sessionManager.parseSessionMetadata(spacesOnly);
    assert.strictEqual(meta5.title, 'Content',
      'Spaces-only after # → \\s+ greedily matches spaces+newlines, (.+) captures next line text');

    // Tab after # — \s includes tab
    const tabTitle = '#\tTab Title\n\nContent';
    const meta6 = sessionManager.parseSessionMetadata(tabTitle);
    assert.strictEqual(meta6.title, 'Tab Title',
      'Tab after # matches \\s+ (\\s includes \\t)');
  })) passed++; else failed++;

  // Summary
  console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
  process.exit(failed > 0 ? 1 : 0);
}

runTests();
