From 6e89f264744a1ebbdfc6ab6ff49d77762eb19b3f Mon Sep 17 00:00:00 2001 From: ajmallesh Date: Mon, 27 Oct 2025 10:14:19 -0700 Subject: [PATCH 1/3] feat: improve audit log naming with timestamp and app context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhances audit log directory naming from `{hostname}_{uuid}` to `{timestamp}_{appName}_{hostname}_{shortId}` for better discoverability and benchmarking analysis. Changes: - Add extractAppName() helper to extract app name from config files - Add smart fallback: use port number for localhost without config - Update generateSessionIdentifier() to include timestamp prefix - Shorten session ID to first 8 characters for readability Examples: - With config: 20251025T193847Z_myapp_localhost_efc60ee0/ - Without config: 20251025T193913Z_8080_localhost_d47e3bfd/ - Remote: 20251024T004401Z_noconfig_example-com_d47e3bfd/ Benefits: - Chronologically sortable audit logs - Instant app identification in directory listings - Efficient filtering for benchmarking queries - Non-breaking: existing logs keep their names 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/audit/utils.js | 68 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/src/audit/utils.js b/src/audit/utils.js index 58a2f05..45666e8 100644 --- a/src/audit/utils.js +++ b/src/audit/utils.js @@ -17,16 +17,78 @@ export const SHANNON_ROOT = path.resolve(__dirname, '..', '..'); export const AUDIT_LOGS_DIR = path.join(SHANNON_ROOT, 'audit-logs'); /** - * Generate standardized session identifier: {hostname}_{sessionId} + * Extract application name from config file or URL + * @param {string} configFile - Config filename (e.g., "app-config.yaml") + * @param {string} webUrl - Target web URL + * @returns {string} App name (e.g., "app", "8080", "noconfig") + */ +function extractAppName(configFile, webUrl) { + // If config file provided, extract app name from it + if (configFile) { + // Remove .yaml/.yml extension + let baseName = configFile.replace(/\.(yaml|yml)$/i, ''); + + // Remove path if present (e.g., "configs/app-config.yaml") + baseName = baseName.split('/').pop(); + + // Extract parts before "config" + // app-config → app + // my-app-config → myapp + const parts = baseName.split('-'); + const configIndex = parts.indexOf('config'); + + if (configIndex > 0) { + // Take everything before "config" and join without hyphens + return parts.slice(0, configIndex).join('').toLowerCase(); + } + + // Fallback: just use the whole thing without hyphens + return baseName.replace(/-/g, '').toLowerCase(); + } + + // No config file - use port number for localhost, "noconfig" for remote + const url = new URL(webUrl); + if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') { + // Use port number (default to 80 for http, 443 for https) + const port = url.port || (url.protocol === 'https:' ? '443' : '80'); + return port; + } + + // Remote URL without config + return 'noconfig'; +} + +/** + * Generate standardized session identifier with timestamp and app context + * Format: {timestamp}_{appName}_{hostname}_{sessionIdShort} + * Example: 20251025T193847Z_myapp_localhost_efc60ee0 + * * @param {Object} sessionMetadata - Session metadata from Shannon store * @param {string} sessionMetadata.id - UUID session ID * @param {string} sessionMetadata.webUrl - Target web URL + * @param {string} [sessionMetadata.configFile] - Config filename (optional) + * @param {string} [sessionMetadata.createdAt] - ISO 8601 timestamp (optional, defaults to now) * @returns {string} Formatted session identifier */ export function generateSessionIdentifier(sessionMetadata) { - const { id, webUrl } = sessionMetadata; + const { id, webUrl, configFile, createdAt } = sessionMetadata; + + // Extract hostname const hostname = new URL(webUrl).hostname.replace(/[^a-zA-Z0-9-]/g, '-'); - return `${hostname}_${id}`; + + // Extract app name from config file or URL + const appName = extractAppName(configFile, webUrl); + + // Format timestamp (use createdAt if available, otherwise use current time) + let timestamp = createdAt || new Date().toISOString(); + // Convert to compact ISO 8601: 2025-10-25T01:37:36.174Z → 20251025T013736Z + timestamp = timestamp.replace(/[-:]/g, '').replace(/\.\d{3}Z/, 'Z'); + + // Use first 8 characters of session ID for uniqueness + const sessionIdShort = id.substring(0, 8); + + // Combine: timestamp_appName_hostname_sessionIdShort + return `${timestamp}_${appName}_${hostname}_${sessionIdShort}`; } /** From 95b8e876eb2c59828d909ee29fadd4ccf15faa9d Mon Sep 17 00:00:00 2001 From: ajmallesh Date: Mon, 27 Oct 2025 10:55:53 -0700 Subject: [PATCH 2/3] fix: use session's original createdAt instead of current time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed bug where audit system would create duplicate folders for the same session because it was using current time instead of the session's original createdAt timestamp. Bug behavior: - Session created at T1 → folder: {T1}_app_host_id/ - Audit re-initialized at T2 → NEW folder: {T2}_app_host_id/ - Result: 2 folders per session with same ID but different timestamps Root cause: - metrics-tracker.js:65 was calling formatTimestamp() (current time) - Should use sessionMetadata.createdAt (original creation time) Impact: Each running benchmark was creating 2 audit log folders instead of 1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/audit/metrics-tracker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audit/metrics-tracker.js b/src/audit/metrics-tracker.js index 4496cb0..eb8d97d 100644 --- a/src/audit/metrics-tracker.js +++ b/src/audit/metrics-tracker.js @@ -62,7 +62,7 @@ export class MetricsTracker { webUrl: this.sessionMetadata.webUrl, repoPath: this.sessionMetadata.repoPath, status: 'in-progress', - createdAt: formatTimestamp() + createdAt: this.sessionMetadata.createdAt || formatTimestamp() }, metrics: { total_duration_ms: 0, From 7f3bff9b362c4bfcade53dd6cfe5750605c5b3aa Mon Sep 17 00:00:00 2001 From: ajmallesh Date: Mon, 27 Oct 2025 13:30:25 -0700 Subject: [PATCH 3/3] Revert "feat: improve audit log naming with timestamp and app context" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts the timestamp-based naming scheme that was causing audit log fragmentation. Each agent execution was creating a new folder because the timestamp kept changing. Reverting back to simple, stable naming: {hostname}_{sessionId} This ensures ONE folder per session, preventing the bug where multiple folders were created for the same session. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/audit/utils.js | 68 ++-------------------------------------------- 1 file changed, 3 insertions(+), 65 deletions(-) diff --git a/src/audit/utils.js b/src/audit/utils.js index 45666e8..58a2f05 100644 --- a/src/audit/utils.js +++ b/src/audit/utils.js @@ -17,78 +17,16 @@ export const SHANNON_ROOT = path.resolve(__dirname, '..', '..'); export const AUDIT_LOGS_DIR = path.join(SHANNON_ROOT, 'audit-logs'); /** - * Extract application name from config file or URL - * @param {string} configFile - Config filename (e.g., "app-config.yaml") - * @param {string} webUrl - Target web URL - * @returns {string} App name (e.g., "app", "8080", "noconfig") - */ -function extractAppName(configFile, webUrl) { - // If config file provided, extract app name from it - if (configFile) { - // Remove .yaml/.yml extension - let baseName = configFile.replace(/\.(yaml|yml)$/i, ''); - - // Remove path if present (e.g., "configs/app-config.yaml") - baseName = baseName.split('/').pop(); - - // Extract parts before "config" - // app-config → app - // my-app-config → myapp - const parts = baseName.split('-'); - const configIndex = parts.indexOf('config'); - - if (configIndex > 0) { - // Take everything before "config" and join without hyphens - return parts.slice(0, configIndex).join('').toLowerCase(); - } - - // Fallback: just use the whole thing without hyphens - return baseName.replace(/-/g, '').toLowerCase(); - } - - // No config file - use port number for localhost, "noconfig" for remote - const url = new URL(webUrl); - if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') { - // Use port number (default to 80 for http, 443 for https) - const port = url.port || (url.protocol === 'https:' ? '443' : '80'); - return port; - } - - // Remote URL without config - return 'noconfig'; -} - -/** - * Generate standardized session identifier with timestamp and app context - * Format: {timestamp}_{appName}_{hostname}_{sessionIdShort} - * Example: 20251025T193847Z_myapp_localhost_efc60ee0 - * + * Generate standardized session identifier: {hostname}_{sessionId} * @param {Object} sessionMetadata - Session metadata from Shannon store * @param {string} sessionMetadata.id - UUID session ID * @param {string} sessionMetadata.webUrl - Target web URL - * @param {string} [sessionMetadata.configFile] - Config filename (optional) - * @param {string} [sessionMetadata.createdAt] - ISO 8601 timestamp (optional, defaults to now) * @returns {string} Formatted session identifier */ export function generateSessionIdentifier(sessionMetadata) { - const { id, webUrl, configFile, createdAt } = sessionMetadata; - - // Extract hostname + const { id, webUrl } = sessionMetadata; const hostname = new URL(webUrl).hostname.replace(/[^a-zA-Z0-9-]/g, '-'); - - // Extract app name from config file or URL - const appName = extractAppName(configFile, webUrl); - - // Format timestamp (use createdAt if available, otherwise use current time) - let timestamp = createdAt || new Date().toISOString(); - // Convert to compact ISO 8601: 2025-10-25T01:37:36.174Z → 20251025T013736Z - timestamp = timestamp.replace(/[-:]/g, '').replace(/\.\d{3}Z/, 'Z'); - - // Use first 8 characters of session ID for uniqueness - const sessionIdShort = id.substring(0, 8); - - // Combine: timestamp_appName_hostname_sessionIdShort - return `${timestamp}_${appName}_${hostname}_${sessionIdShort}`; + return `${hostname}_${id}`; } /**