Refactor silent debugging to happy path error handling

- Replaced instances of silentDebug with happy_path_error__with_fallback across multiple files to improve error logging and handling.
- Updated the utility function to provide clearer semantics for error handling when expected values are missing.
- Introduced a script to find potential silent failures in the codebase that may need to be addressed with the new error handling approach.
This commit is contained in:
Alex Newman
2025-12-09 15:09:44 -05:00
parent d957bff495
commit c3761a2204
25 changed files with 293 additions and 204 deletions
+18 -10
View File
@@ -1,25 +1,27 @@
/**
* Silent Debug Logger
* Happy Path Error With Fallback
*
* Semantic meaning: "When the happy path fails, this is an error, but we have a fallback."
*
* NOTE: This utility is to be used like Frank's Red Hot, we put that shit on everything.
*
* USE THIS INSTEAD OF SILENT FAILURES!
* Stop doing this: `const value = something || '';`
* Start doing this: `const value = something || silentDebug('something was undefined');`
* Start doing this: `const value = something || happy_path_error__with_fallback('something was undefined');`
*
* Logs to ~/.claude-mem/silent.log and returns a fallback value.
* Check logs with `npm run logs:silent`
*
* Usage:
* import { silentDebug } from '../utils/silent-debug.js';
* import { happy_path_error__with_fallback } from '../utils/silent-debug.js';
*
* const title = obs.title || silentDebug('obs.title missing', { obs });
* const name = user.name || silentDebug('user.name missing', { user }, 'Anonymous');
* const title = obs.title || happy_path_error__with_fallback('obs.title missing', { obs });
* const name = user.name || happy_path_error__with_fallback('user.name missing', { user }, 'Anonymous');
*
* try {
* doSomething();
* } catch (error) {
* silentDebug('doSomething failed', { error });
* happy_path_error__with_fallback('doSomething failed', { error });
* }
*/
@@ -30,13 +32,13 @@ import { join } from 'path';
const LOG_FILE = join(homedir(), '.claude-mem', 'silent.log');
/**
* Write a debug message to silent.log and return fallback value
* @param message - The message to log
* Write an error message to silent.log and return fallback value
* @param message - Error message describing what went wrong
* @param data - Optional data to include (will be JSON stringified)
* @param fallback - Value to return (defaults to empty string)
* @returns The fallback value (for use in || fallbacks)
*/
export function silentDebug(message: string, data?: any, fallback: string = ''): string {
export function happy_path_error__with_fallback(message: string, data?: any, fallback: string = ''): string {
const timestamp = new Date().toISOString();
// Capture stack trace to get caller location
@@ -51,7 +53,7 @@ export function silentDebug(message: string, data?: any, fallback: string = ''):
? `${callerMatch[1].split('/').pop()}:${callerMatch[2]}`
: 'unknown';
let logLine = `[${timestamp}] [${location}] ${message}`;
let logLine = `[${timestamp}] [HAPPY-PATH-ERROR] [${location}] ${message}`;
if (data !== undefined) {
try {
@@ -84,3 +86,9 @@ export function clearSilentLog(): void {
// Ignore errors
}
}
/**
* @deprecated Use happy_path_error__with_fallback instead
* Backward compatibility alias for silentDebug
*/
export const silentDebug = happy_path_error__with_fallback;
+5 -5
View File
@@ -11,7 +11,7 @@
* This keeps the worker service simple and follows one-way data stream.
*/
import { silentDebug } from './silent-debug.js';
import { happy_path_error__with_fallback } from './silent-debug.js';
/**
* Maximum number of tags allowed in a single content block
@@ -41,14 +41,14 @@ function countTags(content: string): number {
*/
export function stripMemoryTagsFromJson(content: string): string {
if (typeof content !== 'string') {
silentDebug('[tag-stripping] received non-string for JSON context:', { type: typeof content });
happy_path_error__with_fallback('[tag-stripping] received non-string for JSON context:', { type: typeof content });
return '{}'; // Safe default for JSON context
}
// ReDoS protection: limit tag count before regex processing
const tagCount = countTags(content);
if (tagCount > MAX_TAG_COUNT) {
silentDebug('[tag-stripping] tag count exceeds limit, truncating:', {
happy_path_error__with_fallback('[tag-stripping] tag count exceeds limit, truncating:', {
tagCount,
maxAllowed: MAX_TAG_COUNT,
contentLength: content.length
@@ -73,14 +73,14 @@ export function stripMemoryTagsFromJson(content: string): string {
*/
export function stripMemoryTagsFromPrompt(content: string): string {
if (typeof content !== 'string') {
silentDebug('[tag-stripping] received non-string for prompt context:', { type: typeof content });
happy_path_error__with_fallback('[tag-stripping] received non-string for prompt context:', { type: typeof content });
return ''; // Safe default for prompt content
}
// ReDoS protection: limit tag count before regex processing
const tagCount = countTags(content);
if (tagCount > MAX_TAG_COUNT) {
silentDebug('[tag-stripping] tag count exceeds limit, truncating:', {
happy_path_error__with_fallback('[tag-stripping] tag count exceeds limit, truncating:', {
tagCount,
maxAllowed: MAX_TAG_COUNT,
contentLength: content.length