const std = @import("std");const background_store = @import("../background/background_store.zig");const debug_trace = @import("../shared/debug_trace.zig");const io_mod = @import("../shared/io.zig");const session = @import("session.zig");const session_codec = @import("session_codec.zig");const session_child_store = @import("session_child_store.zig");const session_json = @import("session_json.zig");const session_log = @import("session_log.zig");const session_projection = @import("session_projection.zig");const session_display_metadata = @import("session_display_metadata.zig");const subagent_control_store = @import("../subagent/control_store.zig");const Allocator = std.mem.Allocator; const authority = @import("session_authority.zig");const paths = @import("session_store_paths.zig");const types = @import("session_store_types.zig"); const classifyAuthority = authority.classifyAuthority;const entryExistsRelative = authority.entryExistsRelative;const eventFileStat = authority.eventFileStat;const loadAuthorityMarkerOptional = authority.loadAuthorityMarkerOptional;const loadAuthorityTransitionOptional = authority.loadAuthorityTransitionOptional;const manifestSchemaVersion = authority.manifestSchemaVersion;const openSessionFile = authority.openSessionFile;const readOptionalSessionFile = authority.readOptionalSessionFile;const requireAuthorityFenceAbsent = authority.requireAuthorityFenceAbsent;const sessionDirPath = paths.sessionDirPath;const CandidateStorage = types.CandidateStorage;const DiscoveryCause = types.DiscoveryCause;const DoctorDiagnostic = types.DoctorDiagnostic;const DoctorInspectionOptions = types.DoctorInspectionOptions;const DoctorIssueKind = types.DoctorIssueKind;const ProjectionState = types.ProjectionState;const SessionSummary = types.SessionSummary;const StorageFormat = types.StorageFormat;const automatic_legacy_max_bytes = types.automatic_legacy_max_bytes;const StoreContext = types.StoreContext; pub const DiscoveryMode = enum { read_only_list, global_read_only_last, workspace_writable_last,}; const DiscoveryOutcome = enum { selected, retained, excluded, skipped,}; pub const DiscoveryCandidateMetadata = struct { id: []const u8, storage: CandidateStorage, projection_state: ProjectionState,}; pub const ReadOnlyCandidate = struct { summary: SessionSummary, storage: CandidateStorage, projection_state: ProjectionState, pub fn deinit(self: *ReadOnlyCandidate, alloc: Allocator) void { self.summary.deinit(alloc); self.* = undefined; }}; pub const WritableCandidate = struct { id: []u8, workspace_root: []u8, updated_at_ms: i64, storage: CandidateStorage, projection_state: ProjectionState, pub fn deinit(self: *WritableCandidate, alloc: Allocator) void { alloc.free(self.id); alloc.free(self.workspace_root); self.* = undefined; }}; const LegacyCandidateSummary = struct { id: []u8, workspace_root: ?[]u8 = null, created_at_ms: i64, updated_at_ms: i64, conversation_language: session.ConversationLanguage, history_len: usize, schema_version: session_json.LegacySchemaVersion = .v1, fn intoSessionSummary(self: *LegacyCandidateSummary) SessionSummary { const summary = SessionSummary{ .id = self.id, .workspace_root = self.workspace_root, .created_at_ms = self.created_at_ms, .updated_at_ms = self.updated_at_ms, .conversation_language = self.conversation_language, .history_len = self.history_len, }; self.id = undefined; self.workspace_root = null; return summary; } fn deinit(self: *LegacyCandidateSummary, alloc: Allocator) void { alloc.free(self.id); if (self.workspace_root) |root| alloc.free(root); self.* = undefined; }}; /// Frees every diagnostic in the list and the list itself. Call once on the/// slice returned by `Store.inspectForDoctor`.pub fn freeDoctorDiagnostics( alloc: Allocator, diagnostics: *std.ArrayList(DoctorDiagnostic),) void { for (diagnostics.items) |*diagnostic| diagnostic.deinit(alloc); diagnostics.deinit(alloc);} /// Appends one diagnostic for `session_id`; dupes the id so the caller keeps/// ownership of its slice. `bytes` carries an optional size for size-based kinds.pub fn appendDoctorDiagnostic( diagnostics: *std.ArrayList(DoctorDiagnostic), alloc: Allocator, session_id: []const u8, kind: DoctorIssueKind, bytes: ?u64,) !void { const owned_id = try alloc.dupe(u8, session_id); errdefer alloc.free(owned_id); try diagnostics.append(alloc, .{ .session_id = owned_id, .kind = kind, .bytes = bytes, });} fn appendDoctorGrowthDiagnostic( diagnostics: *std.ArrayList(DoctorDiagnostic), alloc: Allocator, session_id: []const u8, kind: DoctorIssueKind, bytes: u64, growth_bytes: u64, growth_frames: u64,) !void { const owned_id = try alloc.dupe(u8, session_id); errdefer alloc.free(owned_id); try diagnostics.append(alloc, .{ .session_id = owned_id, .kind = kind, .bytes = bytes, .growth_bytes = growth_bytes, .growth_frames = growth_frames, });} /// Inspects one session directory and appends a diagnostic for the first/// integrity problem it finds, dispatching to the authority-state it detects./// Owns only sequencing: the actual checks live in the three inspect* helpers/// below. Fails only on `error.OutOfMemory` from diagnostic allocation; all/// inspection errors are converted into diagnostics, never propagated.pub fn inspectDoctorSession( ctx: StoreContext, alloc: Allocator, diagnostics: *std.ArrayList(DoctorDiagnostic), session_dir: *io_mod.VerifiedDir, session_id: []const u8, options: DoctorInspectionOptions,) !void { const transition = loadAuthorityTransitionOptional(alloc, session_dir) catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (err == error.SessionPathUnsafe) .unsafe_path else .invalid_authority_transition, null, ); return; }; if (transition) |transition_value| { var owned = transition_value; defer owned.deinit(alloc); try appendDoctorDiagnostic(diagnostics, alloc, session_id, .authority_transition_pending, null); return; } const marker = loadAuthorityMarkerOptional(alloc, session_dir) catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (err == error.SessionPathUnsafe) .unsafe_path else .invalid_authority, null, ); return; }; if (marker == null) { return inspectAuthoritylessSession(ctx, alloc, diagnostics, session_dir, session_id); } var owned_marker = marker.?; defer owned_marker.deinit(alloc); if (!std.mem.eql(u8, owned_marker.session_id, session_id)) { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .invalid_authority, null); return; } return inspectSchemaV3Session(ctx, alloc, diagnostics, session_dir, session_id, options);} /// Diagnoses a session that carries no authority marker: either an in-flight/// schema-v3 creation orphan, an oversized legacy snapshot, or a missing/// authority record. Terminal: appends exactly one classification and returns.fn inspectAuthoritylessSession( ctx: StoreContext, alloc: Allocator, diagnostics: *std.ArrayList(DoctorDiagnostic), session_dir: *io_mod.VerifiedDir, session_id: []const u8,) !void { const manifest_bytes = try readOptionalSessionFile( alloc, session_dir, "session.json", session_projection.manifest_max_bytes, ); if (manifest_bytes) |bytes| { defer alloc.free(bytes); const schema_version = manifestSchemaVersion(alloc, bytes) catch { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .missing_authority, null); return; }; if (schema_version == 3) { var manifest = session_projection.decodeManifest(alloc, bytes) catch { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .missing_authority, null); return; }; defer manifest.deinit(alloc); try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (std.mem.eql(u8, manifest.id, session_id)) .authority_less_creation_orphan else .missing_authority, null, ); return; } var legacy_file = try openSessionFile(session_dir, "session.json", .read_only); defer legacy_file.close(io_mod.getIo()); const legacy_stat = try legacy_file.stat(io_mod.getIo()); if (legacy_stat.size > automatic_legacy_max_bytes) { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .oversized_legacy_snapshot, legacy_stat.size); } try inspectDoctorManagedChildren(ctx, alloc, diagnostics, session_dir, session_id); return; } const has_prepared_log = entryExistsRelative(session_dir, "events.jsonl") catch false; try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (has_prepared_log) .authority_less_creation_orphan else .missing_authority, null, );} /// Diagnoses a session that owns a valid authority marker: validates the/// projection manifest, event log size, commit watermark, compaction growth,/// stale/cleanup artifacts, managed children, and finally a canonical replay./// Terminal: stops at the first disqualifying problem.fn inspectSchemaV3Session( ctx: StoreContext, alloc: Allocator, diagnostics: *std.ArrayList(DoctorDiagnostic), session_dir: *io_mod.VerifiedDir, session_id: []const u8, options: DoctorInspectionOptions,) !void { const manifest_bytes = try readOptionalSessionFile( alloc, session_dir, "session.json", session_projection.manifest_max_bytes, ); if (manifest_bytes == null) { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .projection_missing, null); } var manifest: ?session_projection.Manifest = null; defer if (manifest) |*value| value.deinit(alloc); if (manifest_bytes) |bytes| { defer alloc.free(bytes); manifest = session_projection.decodeManifest(alloc, bytes) catch { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .projection_invalid, null); return; }; } const event_stat = eventFileStat(session_dir, "events.jsonl") catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (err == error.SessionPathUnsafe) .unsafe_path else .canonical_state_invalid, null, ); return; }; if (event_stat.size >= 128 * 1024 * 1024) { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .canonical_log_large, event_stat.size); return; } const watermark = session_log.inspectCommitWatermark(alloc, session_dir, session_id) catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (err == error.SessionPathUnsafe) .unsafe_path else .canonical_state_invalid, null, ); return; }; switch (watermark.status) { .missing, .invalid, .mismatched => { try appendDoctorDiagnostic( diagnostics, alloc, session_id, switch (watermark.status) { .missing => .commit_watermark_missing, .invalid => .commit_watermark_invalid, .mismatched => .commit_watermark_mismatched, .valid => unreachable, }, null, ); return; }, .valid => {}, } const committed_position = watermark.position.?; const has_failed_compaction = try session_log.hasValidatedCompactionTemp(alloc, session_dir, session_id); if (manifest) |value| { try appendCompactionGrowthIfNeeded( diagnostics, alloc, session_id, value, committed_position, has_failed_compaction, options, ); } if (manifest) |value| { if (session_projection.isManifestStale(value, event_stat)) { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .projection_stale, null); } try appendCleanupCandidateIfPresent(diagnostics, alloc, session_id, session_dir, value); } try inspectDoctorManagedChildren(ctx, alloc, diagnostics, session_dir, session_id); const has_commit_intent = entryExistsRelative(session_dir, "commit.pending.json") catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (err == error.SessionPathUnsafe) .unsafe_path else .invalid_commit_intent, null, ); return; }; var root = ctx.canonical_root; var state = root.loadReadOnly(alloc, session_id, .{}) catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (has_commit_intent and err == error.SessionCommitBoundaryUnavailable) .commit_intent_pending else if (has_commit_intent) .invalid_commit_intent else if (err == error.SessionPathUnsafe) .unsafe_path else .canonical_state_invalid, null, ); return; }; state.deinit(alloc);} /// Appends a compaction diagnostic when the committed log has grown past the/// configured byte/frame thresholds within the manifest's current generation./// No-op when the generation differs or growth is under threshold.fn appendCompactionGrowthIfNeeded( diagnostics: *std.ArrayList(DoctorDiagnostic), alloc: Allocator, session_id: []const u8, manifest: session_projection.Manifest, committed_position: session_log.CommitPosition, has_failed_compaction: bool, options: DoctorInspectionOptions,) !void { const same_generation = std.mem.eql(u8, &manifest.log_generation, &committed_position.log_generation); if (!same_generation) return; const growth_bytes = committed_position.through_event_log_bytes - @min(committed_position.through_event_log_bytes, manifest.generation_base_bytes); const growth_frames = committed_position.through_seq - @min(committed_position.through_seq, manifest.generation_base_seq); if (growth_bytes >= options.compaction_byte_threshold or growth_frames >= options.compaction_frame_threshold) { try appendDoctorGrowthDiagnostic( diagnostics, alloc, session_id, if (has_failed_compaction) .canonical_log_compaction_failed else .canonical_log_compaction_overdue, committed_position.through_event_log_bytes, growth_bytes, growth_frames, ); }} /// Appends a single cleanup-candidate diagnostic if the directory contains a/// compaction artifact from a generation other than the manifest's current one.fn appendCleanupCandidateIfPresent( diagnostics: *std.ArrayList(DoctorDiagnostic), alloc: Allocator, session_id: []const u8, session_dir: *io_mod.VerifiedDir, manifest: session_projection.Manifest,) !void { var entries = session_dir.dir.iterate(); while (try entries.next(io_mod.getIo())) |entry| { const generation = session_log.cleanupCandidateGeneration(entry.name) orelse continue; if (std.mem.eql(u8, &generation, &manifest.log_generation)) continue; try appendDoctorDiagnostic(diagnostics, alloc, session_id, .cleanup_candidate, null); break; }} fn inspectDoctorManagedChildren( ctx: StoreContext, alloc: Allocator, diagnostics: *std.ArrayList(DoctorDiagnostic), session_dir: *io_mod.VerifiedDir, session_id: []const u8,) !void { const display_path = try sessionDirPath(alloc, ctx.sessions_dir, session_id); defer alloc.free(display_path); var capability = session_child_store.SessionChildCapability.init( alloc, session_dir.dir, display_path, .read_only, ) catch |err| { if (err == error.OutOfMemory) return err; try appendDoctorDiagnostic(diagnostics, alloc, session_id, .unsafe_path, null); return; }; defer capability.deinit(); const child_kinds = [_]session_child_store.ManagedChildKind{ .background_records, .background_logs, .command_artifacts, .browser_artifacts, .tool_results, .subagent_control, }; for (child_kinds) |kind| { var entries = capability.iterate(alloc, kind) catch |err| { if (err == error.OutOfMemory) return err; try appendDoctorDiagnostic(diagnostics, alloc, session_id, .unsafe_path, null); return; }; entries.deinit(); } background_store.validateAllManagedRecords(alloc, &capability) catch |err| { if (err == error.OutOfMemory) return err; try appendDoctorDiagnostic(diagnostics, alloc, session_id, .canonical_state_invalid, null);Lines 1–500 of 786. Load more
const std = @import("std"); const background_store = @import("../background/background_store.zig"); const debug_trace = @import("../shared/debug_trace.zig"); const io_mod = @import("../shared/io.zig"); const session = @import("session.zig"); const session_codec = @import("session_codec.zig"); const session_child_store = @import("session_child_store.zig"); const session_json = @import("session_json.zig"); const session_log = @import("session_log.zig"); const session_projection = @import("session_projection.zig"); const session_display_metadata = @import("session_display_metadata.zig"); const subagent_control_store = @import("../subagent/control_store.zig"); const Allocator = std.mem.Allocator; const authority = @import("session_authority.zig"); const paths = @import("session_store_paths.zig"); const types = @import("session_store_types.zig"); const classifyAuthority = authority.classifyAuthority; const entryExistsRelative = authority.entryExistsRelative; const eventFileStat = authority.eventFileStat; const loadAuthorityMarkerOptional = authority.loadAuthorityMarkerOptional; const loadAuthorityTransitionOptional = authority.loadAuthorityTransitionOptional; const manifestSchemaVersion = authority.manifestSchemaVersion; const openSessionFile = authority.openSessionFile; const readOptionalSessionFile = authority.readOptionalSessionFile; const requireAuthorityFenceAbsent = authority.requireAuthorityFenceAbsent; const sessionDirPath = paths.sessionDirPath; const CandidateStorage = types.CandidateStorage; const DiscoveryCause = types.DiscoveryCause; const DoctorDiagnostic = types.DoctorDiagnostic; const DoctorInspectionOptions = types.DoctorInspectionOptions; const DoctorIssueKind = types.DoctorIssueKind; const ProjectionState = types.ProjectionState; const SessionSummary = types.SessionSummary; const StorageFormat = types.StorageFormat; const automatic_legacy_max_bytes = types.automatic_legacy_max_bytes; const StoreContext = types.StoreContext; pub const DiscoveryMode = enum { read_only_list, global_read_only_last, workspace_writable_last, }; const DiscoveryOutcome = enum { selected, retained, excluded, skipped, }; pub const DiscoveryCandidateMetadata = struct { id: []const u8, storage: CandidateStorage, projection_state: ProjectionState, }; pub const ReadOnlyCandidate = struct { summary: SessionSummary, storage: CandidateStorage, projection_state: ProjectionState, pub fn deinit(self: *ReadOnlyCandidate, alloc: Allocator) void { self.summary.deinit(alloc); self.* = undefined; } }; pub const WritableCandidate = struct { id: []u8, workspace_root: []u8, updated_at_ms: i64, storage: CandidateStorage, projection_state: ProjectionState, pub fn deinit(self: *WritableCandidate, alloc: Allocator) void { alloc.free(self.id); alloc.free(self.workspace_root); self.* = undefined; } }; const LegacyCandidateSummary = struct { id: []u8, workspace_root: ?[]u8 = null, created_at_ms: i64, updated_at_ms: i64, conversation_language: session.ConversationLanguage, history_len: usize, schema_version: session_json.LegacySchemaVersion = .v1, fn intoSessionSummary(self: *LegacyCandidateSummary) SessionSummary { const summary = SessionSummary{ .id = self.id, .workspace_root = self.workspace_root, .created_at_ms = self.created_at_ms, .updated_at_ms = self.updated_at_ms, .conversation_language = self.conversation_language, .history_len = self.history_len, }; self.id = undefined; self.workspace_root = null; return summary; } fn deinit(self: *LegacyCandidateSummary, alloc: Allocator) void { alloc.free(self.id); if (self.workspace_root) |root| alloc.free(root); self.* = undefined; } }; /// Frees every diagnostic in the list and the list itself. Call once on the /// slice returned by `Store.inspectForDoctor`. pub fn freeDoctorDiagnostics( alloc: Allocator, diagnostics: *std.ArrayList(DoctorDiagnostic), ) void { for (diagnostics.items) |*diagnostic| diagnostic.deinit(alloc); diagnostics.deinit(alloc); } /// Appends one diagnostic for `session_id`; dupes the id so the caller keeps /// ownership of its slice. `bytes` carries an optional size for size-based kinds. pub fn appendDoctorDiagnostic( diagnostics: *std.ArrayList(DoctorDiagnostic), alloc: Allocator, session_id: []const u8, kind: DoctorIssueKind, bytes: ?u64, ) !void { const owned_id = try alloc.dupe(u8, session_id); errdefer alloc.free(owned_id); try diagnostics.append(alloc, .{ .session_id = owned_id, .kind = kind, .bytes = bytes, }); } fn appendDoctorGrowthDiagnostic( diagnostics: *std.ArrayList(DoctorDiagnostic), alloc: Allocator, session_id: []const u8, kind: DoctorIssueKind, bytes: u64, growth_bytes: u64, growth_frames: u64, ) !void { const owned_id = try alloc.dupe(u8, session_id); errdefer alloc.free(owned_id); try diagnostics.append(alloc, .{ .session_id = owned_id, .kind = kind, .bytes = bytes, .growth_bytes = growth_bytes, .growth_frames = growth_frames, }); } /// Inspects one session directory and appends a diagnostic for the first /// integrity problem it finds, dispatching to the authority-state it detects. /// Owns only sequencing: the actual checks live in the three inspect* helpers /// below. Fails only on `error.OutOfMemory` from diagnostic allocation; all /// inspection errors are converted into diagnostics, never propagated. pub fn inspectDoctorSession( ctx: StoreContext, alloc: Allocator, diagnostics: *std.ArrayList(DoctorDiagnostic), session_dir: *io_mod.VerifiedDir, session_id: []const u8, options: DoctorInspectionOptions, ) !void { const transition = loadAuthorityTransitionOptional(alloc, session_dir) catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (err == error.SessionPathUnsafe) .unsafe_path else .invalid_authority_transition, null, ); return; }; if (transition) |transition_value| { var owned = transition_value; defer owned.deinit(alloc); try appendDoctorDiagnostic(diagnostics, alloc, session_id, .authority_transition_pending, null); return; } const marker = loadAuthorityMarkerOptional(alloc, session_dir) catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (err == error.SessionPathUnsafe) .unsafe_path else .invalid_authority, null, ); return; }; if (marker == null) { return inspectAuthoritylessSession(ctx, alloc, diagnostics, session_dir, session_id); } var owned_marker = marker.?; defer owned_marker.deinit(alloc); if (!std.mem.eql(u8, owned_marker.session_id, session_id)) { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .invalid_authority, null); return; } return inspectSchemaV3Session(ctx, alloc, diagnostics, session_dir, session_id, options); } /// Diagnoses a session that carries no authority marker: either an in-flight /// schema-v3 creation orphan, an oversized legacy snapshot, or a missing /// authority record. Terminal: appends exactly one classification and returns. fn inspectAuthoritylessSession( ctx: StoreContext, alloc: Allocator, diagnostics: *std.ArrayList(DoctorDiagnostic), session_dir: *io_mod.VerifiedDir, session_id: []const u8, ) !void { const manifest_bytes = try readOptionalSessionFile( alloc, session_dir, "session.json", session_projection.manifest_max_bytes, ); if (manifest_bytes) |bytes| { defer alloc.free(bytes); const schema_version = manifestSchemaVersion(alloc, bytes) catch { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .missing_authority, null); return; }; if (schema_version == 3) { var manifest = session_projection.decodeManifest(alloc, bytes) catch { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .missing_authority, null); return; }; defer manifest.deinit(alloc); try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (std.mem.eql(u8, manifest.id, session_id)) .authority_less_creation_orphan else .missing_authority, null, ); return; } var legacy_file = try openSessionFile(session_dir, "session.json", .read_only); defer legacy_file.close(io_mod.getIo()); const legacy_stat = try legacy_file.stat(io_mod.getIo()); if (legacy_stat.size > automatic_legacy_max_bytes) { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .oversized_legacy_snapshot, legacy_stat.size); } try inspectDoctorManagedChildren(ctx, alloc, diagnostics, session_dir, session_id); return; } const has_prepared_log = entryExistsRelative(session_dir, "events.jsonl") catch false; try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (has_prepared_log) .authority_less_creation_orphan else .missing_authority, null, ); } /// Diagnoses a session that owns a valid authority marker: validates the /// projection manifest, event log size, commit watermark, compaction growth, /// stale/cleanup artifacts, managed children, and finally a canonical replay. /// Terminal: stops at the first disqualifying problem. fn inspectSchemaV3Session( ctx: StoreContext, alloc: Allocator, diagnostics: *std.ArrayList(DoctorDiagnostic), session_dir: *io_mod.VerifiedDir, session_id: []const u8, options: DoctorInspectionOptions, ) !void { const manifest_bytes = try readOptionalSessionFile( alloc, session_dir, "session.json", session_projection.manifest_max_bytes, ); if (manifest_bytes == null) { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .projection_missing, null); } var manifest: ?session_projection.Manifest = null; defer if (manifest) |*value| value.deinit(alloc); if (manifest_bytes) |bytes| { defer alloc.free(bytes); manifest = session_projection.decodeManifest(alloc, bytes) catch { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .projection_invalid, null); return; }; } const event_stat = eventFileStat(session_dir, "events.jsonl") catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (err == error.SessionPathUnsafe) .unsafe_path else .canonical_state_invalid, null, ); return; }; if (event_stat.size >= 128 * 1024 * 1024) { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .canonical_log_large, event_stat.size); return; } const watermark = session_log.inspectCommitWatermark(alloc, session_dir, session_id) catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (err == error.SessionPathUnsafe) .unsafe_path else .canonical_state_invalid, null, ); return; }; switch (watermark.status) { .missing, .invalid, .mismatched => { try appendDoctorDiagnostic( diagnostics, alloc, session_id, switch (watermark.status) { .missing => .commit_watermark_missing, .invalid => .commit_watermark_invalid, .mismatched => .commit_watermark_mismatched, .valid => unreachable, }, null, ); return; }, .valid => {}, } const committed_position = watermark.position.?; const has_failed_compaction = try session_log.hasValidatedCompactionTemp(alloc, session_dir, session_id); if (manifest) |value| { try appendCompactionGrowthIfNeeded( diagnostics, alloc, session_id, value, committed_position, has_failed_compaction, options, ); } if (manifest) |value| { if (session_projection.isManifestStale(value, event_stat)) { try appendDoctorDiagnostic(diagnostics, alloc, session_id, .projection_stale, null); } try appendCleanupCandidateIfPresent(diagnostics, alloc, session_id, session_dir, value); } try inspectDoctorManagedChildren(ctx, alloc, diagnostics, session_dir, session_id); const has_commit_intent = entryExistsRelative(session_dir, "commit.pending.json") catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (err == error.SessionPathUnsafe) .unsafe_path else .invalid_commit_intent, null, ); return; }; var root = ctx.canonical_root; var state = root.loadReadOnly(alloc, session_id, .{}) catch |err| { try appendDoctorDiagnostic( diagnostics, alloc, session_id, if (has_commit_intent and err == error.SessionCommitBoundaryUnavailable) .commit_intent_pending else if (has_commit_intent) .invalid_commit_intent else if (err == error.SessionPathUnsafe) .unsafe_path else .canonical_state_invalid, null, ); return; }; state.deinit(alloc); } /// Appends a compaction diagnostic when the committed log has grown past the /// configured byte/frame thresholds within the manifest's current generation. /// No-op when the generation differs or growth is under threshold. fn appendCompactionGrowthIfNeeded( diagnostics: *std.ArrayList(DoctorDiagnostic), alloc: Allocator, session_id: []const u8, manifest: session_projection.Manifest, committed_position: session_log.CommitPosition, has_failed_compaction: bool, options: DoctorInspectionOptions, ) !void { const same_generation = std.mem.eql(u8, &manifest.log_generation, &committed_position.log_generation); if (!same_generation) return; const growth_bytes = committed_position.through_event_log_bytes - @min(committed_position.through_event_log_bytes, manifest.generation_base_bytes); const growth_frames = committed_position.through_seq - @min(committed_position.through_seq, manifest.generation_base_seq); if (growth_bytes >= options.compaction_byte_threshold or growth_frames >= options.compaction_frame_threshold) { try appendDoctorGrowthDiagnostic( diagnostics, alloc, session_id, if (has_failed_compaction) .canonical_log_compaction_failed else .canonical_log_compaction_overdue, committed_position.through_event_log_bytes, growth_bytes, growth_frames, ); } } /// Appends a single cleanup-candidate diagnostic if the directory contains a /// compaction artifact from a generation other than the manifest's current one. fn appendCleanupCandidateIfPresent( diagnostics: *std.ArrayList(DoctorDiagnostic), alloc: Allocator, session_id: []const u8, session_dir: *io_mod.VerifiedDir, manifest: session_projection.Manifest, ) !void { var entries = session_dir.dir.iterate(); while (try entries.next(io_mod.getIo())) |entry| { const generation = session_log.cleanupCandidateGeneration(entry.name) orelse continue; if (std.mem.eql(u8, &generation, &manifest.log_generation)) continue; try appendDoctorDiagnostic(diagnostics, alloc, session_id, .cleanup_candidate, null); break; } } fn inspectDoctorManagedChildren( ctx: StoreContext, alloc: Allocator, diagnostics: *std.ArrayList(DoctorDiagnostic), session_dir: *io_mod.VerifiedDir, session_id: []const u8, ) !void { const display_path = try sessionDirPath(alloc, ctx.sessions_dir, session_id); defer alloc.free(display_path); var capability = session_child_store.SessionChildCapability.init( alloc, session_dir.dir, display_path, .read_only, ) catch |err| { if (err == error.OutOfMemory) return err; try appendDoctorDiagnostic(diagnostics, alloc, session_id, .unsafe_path, null); return; }; defer capability.deinit(); const child_kinds = [_]session_child_store.ManagedChildKind{ .background_records, .background_logs, .command_artifacts, .browser_artifacts, .tool_results, .subagent_control, }; for (child_kinds) |kind| { var entries = capability.iterate(alloc, kind) catch |err| { if (err == error.OutOfMemory) return err; try appendDoctorDiagnostic(diagnostics, alloc, session_id, .unsafe_path, null); return; }; entries.deinit(); } background_store.validateAllManagedRecords(alloc, &capability) catch |err| { if (err == error.OutOfMemory) return err; try appendDoctorDiagnostic(diagnostics, alloc, session_id, .canonical_state_invalid, null);