Round 122: Tests for findFiles glob dot escaping (*.txt must not match filetxt),
listAliases limit=0/negative/NaN returning all due to JS falsy check, and
getSessionById matching old YYYY-MM-DD-session.tmp filenames via noIdMatch path.
Total: 926 tests, all passing.
- parseSessionFilename rejects Feb 29 in century non-leap years (1900, 2100) but accepts 2000/2400
- replaceInFile with /(?:)/g zero-width regex inserts at every position boundary
- parseSessionMetadata preserves raw markdown formatting (**bold**, `code`, _italic_) in titles
Total: 899 tests
Round 110: Tests for findFiles with unreadable root directory returning
empty array (vs Round 71 which tested subdirectory), parseSessionFilename
year 0000 exposing JS Date 0-99→1900-1999 mapping quirk, and uppercase
session ID rejection by [a-z0-9]{8,} regex. 890 tests total.
Round 109:
- appendFile creating new file in non-existent directory (ensureDir + appendFileSync)
- getExecCommand with ../ path traversal in binary (SAFE_NAME_REGEX allows ../)
- getAllSessions skips .tmp files that don't match session filename format
- grepFile with ^$ pattern verifies empty line matching including trailing newline phantom
- replaceInFile with self-reintroducing replacement confirms single-pass behavior
- setAlias with whitespace-only title exposes missing trim validation vs sessionPath
- countInFile with named capture groups verifies match(g) ignores group details
- grepFile with multiline (m) flag confirms flag is preserved unlike stripped g
- getAllSessions with array/object limit tests Number() coercion edge cases
- parseSessionMetadata: blank line within Completed section truncates items
due to regex lookahead (?=###|\n\n|$) stopping at \n\n boundary
- grepFile: sticky (y) flag not stripped like g flag, causing stateful
.test() behavior that misses matching lines
- getExecCommand: object args bypass SAFE_ARGS_REGEX (typeof !== 'string')
but coerce to "[object Object]" in command string
- findFiles with both maxAge AND recursive combined (option interaction test)
- parseSessionMetadata truncates item text at embedded ### due to lazy regex
- cleanupAliases callback returning 0 (falsy non-boolean) removes alias via !0 coercion
Total tests: 860
- replaceInFile returns true even when pattern doesn't match (silent rewrite)
- grepFile treats CR-only (\r) file as single line (splits on \n only)
- writeSessionContent(null) returns false (TypeError caught by try/catch)
- getSessionById(null) throws TypeError at line 297 (null.length)
- parseSessionFilename(null) throws TypeError at line 30 (null.match())
- findFiles with maxAge: -1 deterministically excludes all files
- detectFromPackageJson with scoped package name (@scope/pkg@version)
returns null because split('@')[0] yields empty string
- getPackageManager skips empty string CLAUDE_PACKAGE_MANAGER via
falsy short-circuit (distinct from unknown PM name test)
- session-end buildSummarySection includes Tools Used but omits
Files Modified when transcript has only Read/Grep tools
Total tests: 842
- getSessionStats: drive letter without slash (Z:nosession.tmp) treated as content
- countInFile: case-insensitive regex with g flag auto-appended (/foo/i → /foo/ig)
- countInFile: case-insensitive regex with g flag preserved (/foo/gi stays /foo/gi)
- Test countInFile returns 0 for object pattern type (non-string non-RegExp)
- Test getSessionStats treats Windows UNC path as content (not file path)
- Test detectFromPackageJson returns null for empty string packageManager field
Total tests: 836
- Test getCommandPattern('') produces valid regex for empty action string
- Test detectFromPackageJson returns null for whitespace-only packageManager
- Test getSessionStats treats mixed Windows path separators as file path
Total tests: 833
- Test readStdinJson timeout path when stdin never closes (resolves with {})
- Test readStdinJson timeout path with partial invalid JSON (catch resolves with {})
- Test saveAliases backup restore double failure (inner restoreErr catch at line 135)
Total tests: 830
- replaceInFile with empty replacement string verifies text deletion works
- parseSessionMetadata asserts date/started/lastUpdated are null when fields absent
- countInFile with valid file but non-matching pattern returns 0
Total: 824 tests
- loadAliases resets to defaults when aliases field is a truthy non-object (string)
- detectFromPackageJson returns null for empty (0-byte) package.json
- evaluate-session uses learned_skills_path config override with ~ expansion
Round 80: Three previously untested conditional branches:
- session-end.js: entry.message?.role === 'user' third OR condition
(fires when type is not 'user' but message.role is)
- package-manager.js: getExecCommand with truthy non-string args
(typeof check short-circuits, value still appended via ternary)
- validate-hooks.js: legacy array format parsing path (lines 115-135)
with 'Hook N' error labels instead of 'EventType[N]'
- evaluate-session: main().catch when HOME is non-directory (ENOTDIR)
- suggest-compact: main().catch double-failure when TMPDIR is non-directory
- validate-hooks: invalid JSON in hooks.json triggers error exit
Total tests: 831 → 834
- session-manager: deleteSession returns false when dir is read-only (EACCES)
- pre-compact: main().catch handler when HOME is non-directory (ENOTDIR)
- session-end: main().catch handler when HOME is non-directory (ENOTDIR)
Total tests: 828 → 831
- setup-package-manager: setGlobal catch when HOME is non-directory (ENOTDIR)
- setup-package-manager: setProject catch when CWD is read-only (EACCES)
- session-start: main().catch handler when ensureDir throws (exit 0, don't block)
Total tests: 825 → 828
Round 73: Add 3 tests for genuine untested code paths:
- session-aliases cleanupAliases returns failure when save blocked after removing aliases
- session-aliases setAlias returns failure when save blocked on new alias creation
- validate-commands silently skips broken symlinks in skill directory scanning