Skip to content

feat(logging): add durable lifecycle logging foundation - #1174

Merged
ndizazzo merged 1 commit into
mainfrom
logging-foundation-v3
Aug 12, 2026
Merged

ndizazzo merged 1 commit into
mainfrom
logging-foundation-v3

Conversation

@ndizazzo

@ndizazzo ndizazzo commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

First PR in the logging stack. This branch is based directly on main and establishes the durable, privacy-safe logging foundation consumed by the API and console layers.

Typed logging contracts

  • Adds canonical request, event, artifact, proxy-attempt, replay, audit, and summary types in mesh-llm-events.
  • Adds bounded identifiers, ordered lifecycle transitions, terminal-state guarantees, and versioned replay envelopes.
  • Keeps the new observability surface local: no protobuf, gossip, ALPN, or native-runtime ABI changes.

Durable storage and retention

  • Adds the mesh-llm-log-store crate with SQLite migrations, transactional schema upgrades, future-schema rejection, repositories, cursors, and typed store errors.
  • Persists request summaries, lifecycle events, proxy attempts, artifacts, audit records, and webhook records.
  • Adds independently configurable request, audit, and webhook retention with bounded cleanup and orphan prevention.
  • Adds privacy-aware artifact capture with runtime-owned size/count limits and a true metadata-only mode.

Runtime logging foundation

  • Adds the non-blocking logging bus, writer, registry, sequence tracking, lifecycle guards, persistence worker, and runtime state.
  • Logging remains fail-open for request processing while exposing bounded health and drop accounting.
  • Adds embedded SDK access and runtime/config wiring for the logging service.

Privacy and audit hardening

  • Sanitizes lifecycle, audit, JSON, token, path, header, and free-form text at ingress and persistence boundaries.
  • Adds private audit directory/file creation, serialized rotation, symlink/reparse-point rejection, and surfaced I/O failures.
  • Keeps join tokens visible only in the trusted local TUI; ordinary pretty/JSON log projections remain redacted.
  • Adds schema validation for logging and audit settings; the final canonical [logging.audit] nesting is completed by feat(console): add typed logs ledger and maintenance UI #1176 without a deprecated compatibility layer.

Repository integration

  • Registers the new crate with affected-crate, Clippy, publishing, and PR-build routing.
  • Updates the CI topology/inventory for the additional crate and test coverage.

Validation

  • PR Quality Checks — passed.
  • PR Builds — passed across crate tests, platform products, SDK smokes, and scripted/inference smokes.
  • PR Website Checks — passed.
  • Focused mesh-llm-events, mesh-llm-log-store, config, TUI, and host logging tests passed locally, along with formatting and warnings-denied Clippy.

Stack

Guardrails

This PR does not add operator HTTP endpoints or the logs console. It establishes the contracts, privacy policy, storage, configuration, and runtime primitives those layers use. No mesh wire schema, ALPN label, native ABI, OTLP log API, or generated website artifact is changed.

Summary by CodeRabbit

  • New Features

    • Added configurable audit logging with levels, formats, rotation, and secure storage.
    • Added durable request logging with replay, retention, lifecycle tracking, artifact capture, and privacy safeguards.
    • Added SQLite-backed history with pagination, cleanup, integrity checks, and secure artifact storage.
    • Selected logging limits can now be updated without restarting the application.
    • Added validation for logging, audit, retention, storage, and webhook settings.
  • Bug Fixes

    • Improved fail-open behavior when logging or artifact storage is unavailable.
    • Private-mesh guidance no longer displays or copies invitation tokens and directs users to trusted operators.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds structured audit and lifecycle logging, SQLite-backed persistence, privacy-safe artifact capture, runtime configuration apply support, Windows ACL coverage, workspace integration, and invitation presentation changes.

Changes

Logging and persistence stack

Layer / File(s) Summary
Configuration and event contracts
crates/mesh-llm-config/..., crates/mesh-llm-events/..., crates/mesh-llm-cli/...
Adds logging and audit configuration, validation, CLI options, audit sinks, sanitization, lifecycle events, canonical envelopes, identifiers, replay sequences, summaries, and artifact metadata.
SQLite storage and artifact capture
crates/mesh-llm-log-store/...
Adds SQLite migrations, repositories, pagination, retention cleanup, private artifact files, checksums, recovery, and fail-open capture.
Host runtime logging and configuration apply
crates/mesh-llm-host-runtime/src/logging/..., crates/mesh-llm-host-runtime/src/runtime/..., crates/mesh-llm-host-runtime/src/sdk/...
Adds logging initialization, privacy policy enforcement, replay delivery, lifecycle tracking, persistence workers, runtime health, shutdown handling, embedded startup support, and live logging-limit application.
Configuration response handling
crates/mesh-llm-host-runtime/src/mesh/..., crates/mesh-llm-host-runtime/src/protocol/...
Maps live and restart-required configuration results and initializes logging and audit fields in protocol fixtures and conversions.

Invitation presentation and CI

Layer / File(s) Summary
Invitation presentation
crates/mesh-llm-tui/..., crates/mesh-llm-ui/..., scripts/console-format.js
Removes private invitation-token copy controls from the TUI and web header. Displays invitation readiness and retains operator-facing token output where applicable.
Workspace and Windows CI integration
Cargo.toml, scripts/..., .github/workflows/pr_builds.yml, ci/ci.md, .agents/skills/manage-ci/...
Registers mesh-llm-log-store in workspace, affected-crate, clippy, publication, and Windows privacy-ACL test paths.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: experimental

Suggested reviewers: i386, michaelneale

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the pull request's main change: a durable logging foundation with lifecycle logging.
Docstring Coverage ✅ Passed Docstring coverage is 91.18% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch logging-foundation-v3

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

This pull request is currently a draft. Reviews will not take place until the PR is marked as ready for review.

@ndizazzo ndizazzo added this to the 0.76.0 milestone Aug 5, 2026
@ndizazzo ndizazzo self-assigned this Aug 5, 2026
@ndizazzo
ndizazzo force-pushed the logging-foundation-v3 branch 4 times, most recently from e84d1d9 to 65eb641 Compare August 11, 2026 02:26
@ndizazzo
ndizazzo force-pushed the logging-foundation-v3 branch from 65eb641 to d458ec6 Compare August 11, 2026 02:35
@ndizazzo
ndizazzo force-pushed the logging-foundation-v3 branch from d458ec6 to e605e92 Compare August 11, 2026 05:12
@ndizazzo
ndizazzo marked this pull request as ready for review August 11, 2026 05:13
@github-actions
github-actions Bot requested a review from michaelneale August 11, 2026 05:13
@ndizazzo
ndizazzo requested a review from i386 August 11, 2026 05:13

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
crates/mesh-llm-tui/src/output/rendering/join_token.rs (1)

146-168: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Allocate enough height for the invite message.

Lines 146-168 return a one-row area in normal mode. join_token_message renders the mesh label and token on separate lines. Paragraph clips the token line.

This also causes tui_invite_event_exposes_token_in_dashboard to fail because it expects the rendered token. Use the panel inner area, or reserve at least the wrapped message height, for normal-mode rendering.

Proposed fix
     Rect {
         x: inner_x,
-        y: panel_area.y.saturating_add(panel_area.height / 2),
+        y: panel_area.y.saturating_add(1),
         width: inner_right.saturating_sub(inner_x),
-        height: 1,
+        height: panel_area.height.saturating_sub(2),
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-tui/src/output/rendering/join_token.rs` around lines 146 -
168, Update join_token_text_area to reserve enough vertical space for both the
mesh label and token rendered by join_token_message, rather than returning a
one-row Rect in normal mode. Use the panel’s inner area or the required wrapped
message height while preserving the existing empty-area behavior for
insufficient panel dimensions.
crates/mesh-llm-host-runtime/src/runtime/run_auto.rs (1)

256-262: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Audit logging never starts for --local-model-only and --plugin.

run_runtime_cli returns at Line 257 for local_model_only and at Line 261 for plugin. Both returns happen before the config load at Line 278 and before init_audit_logging at Line 290. A user who passes --audit-log-path together with --local-model-only gets no audit sink and no error. The local-model-only path still serves the OpenAI API, so the requested audit records are lost.

Initialize audit logging before these early returns, or reject the flag combination explicitly.

Also applies to: 289-297

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/runtime/run_auto.rs` around lines 256 - 262,
Update run_runtime_cli so audit logging is initialized before the
local_model_only and plugin early returns, ensuring --audit-log-path is honored
for both paths. Reuse the existing configuration loading and init_audit_logging
flow where possible, or explicitly reject incompatible flag combinations if
initialization cannot occur before dispatch.
crates/mesh-llm-host-runtime/src/mesh/owner_control/mod.rs (1)

761-768: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Keep synchronous persistence outside the configuration-state lock. apply_with_live_logging performs only short in-memory logging updates, but apply writes the configuration and revision sidecar while config_state remains locked. These filesystem operations can stall configuration readers; narrow the lock scope.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/mesh/owner_control/mod.rs` around lines 761
- 768, Update the closure around config_state in the apply flow so
apply_with_live_logging runs while holding the lock only for its in-memory state
update, then release the lock before performing synchronous persistence of the
configuration and revision sidecar. Preserve the returned result, revision, and
config hash by reacquiring the state as needed, and use the existing
apply-related symbols without changing unrelated behavior.
crates/mesh-llm-host-runtime/src/lib.rs (1)

137-144: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Do not make plugin startup depend on host config loading.

When options.plugin is set, run_runtime_cli starts plugin::run_plugin_process before loading host config. This ? can now prevent plugin startup when the host config is malformed or unreadable. Bypass config loading for plugin mode, or handle the error fail-open and skip logging initialization.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/lib.rs` around lines 137 - 144, Update
initialize_host_runtime_for_options so plugin mode does not depend on
plugin::load_config succeeding: detect options.plugin before loading host
configuration, and continue plugin startup without logging initialization when
that config is malformed or unreadable. Preserve the existing config loading and
logging initialization behavior for non-plugin runtimes.
🟡 Minor comments (21)
crates/mesh-llm-tui/src/output/EVENTS.md-8-8 (1)

8-8: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Correct the invitation-token privacy statement.

Line 8 says that the dashboard does not retain or display the raw token. Lines 17 and 32 document the opposite behavior. DashboardState::apply_output_event() also retains the token for the join-token panel.

State that the TUI removes token copy controls instead.

Proposed fix
-- Pretty mode consumes the same events to update the dashboard, event history, endpoint cards, model progress, and process rows. Invitation readiness is represented without retaining or displaying a raw token.
+- Pretty mode consumes the same events to update the dashboard, event history, endpoint cards, model progress, and process rows. Invitation readiness is represented without a token copy control.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-tui/src/output/EVENTS.md` at line 8, Update the
invitation-readiness statement in EVENTS.md to remove the inaccurate claim that
the TUI does not retain or display the raw token; state instead that the TUI
removes token copy controls, consistent with
DashboardState::apply_output_event() and the behavior documented elsewhere.
crates/mesh-llm-ui/src/features/app-shell/components/AppHeader.tsx-164-166 (1)

164-166: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Hide the local-endpoint instruction for private meshes.

When isPublicMesh is false and apiDirectUrl is empty, the next message still tells the user that http://127.0.0.1:9337/v1 is available locally. The private-mesh branch does not establish that endpoint.

Render the local-endpoint instruction only for the public-mesh branch, or replace it with private connection guidance.

Proposed fix
-                  <div className="text-xs text-muted-foreground">
-                    This gives you <code className="text-[0.7rem]">http://127.0.0.1:9337/v1</code> locally — point any
-                    OpenAI-compatible app at it.
-                  </div>
+                  {isPublicMesh ? (
+                    <div className="text-xs text-muted-foreground">
+                      This gives you <code className="text-[0.7rem]">http://127.0.0.1:9337/v1</code> locally — point any
+                      OpenAI-compatible app at it.
+                    </div>
+                  ) : null}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-ui/src/features/app-shell/components/AppHeader.tsx` around
lines 164 - 166, Update the conditional message in the AppHeader render to avoid
presenting a local endpoint for private meshes. When isPublicMesh is false and
apiDirectUrl is empty, render only trusted operator-channel connection guidance
or no local-endpoint instruction; retain the existing public-mesh guidance for
isPublicMesh true.
crates/mesh-llm-config/src/validate.rs-965-982 (1)

965-982: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use valid configuration keys in the regression fixture.

Neither runtime.bind_port nor owner_control.bind_addr belongs to the corresponding configuration struct. Unknown nested keys are discarded during deserialization. Use valid fields, such as runtime.listen_all and owner_control.bind = "0.0.0.0:9337", so the assertion covers a realistic v1 configuration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-config/src/validate.rs` around lines 965 - 982, Update the
regression fixture used with validate_config_diagnostics to use valid MeshConfig
fields: replace runtime.bind_port with runtime.listen_all and
owner_control.bind_addr with owner_control.bind set to "0.0.0.0:9337". Keep the
deserialization and zero-diagnostics assertions unchanged.
crates/mesh-llm-log-store/src/tests.rs-955-991 (1)

955-991: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

The comments misdescribe these assertions, and the stated constraint is not exercised.

Lines 956-989 insert aud-3, aud-5, and aud-4, all with Some("req-1"). The comment at Line 967 says "different request + different entry should work", but the call passes the same request_id. The three inserts differ only by entry_id, so they test one behavior three times. No insert uses a second request_id, so UNIQUE(request_id, entry_id) is never exercised across distinct requests.

Insert a second summary and use its request_id for one of the entries, then correct the comments.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-log-store/src/tests.rs` around lines 955 - 991, Update the
audit-entry test around insert_audit_entry to create a second summary with a
distinct request_id, use that request_id for one entry such as aud-5, and
correct the nearby comments to accurately describe same-request/different-entry
and different-request/different-entry cases. Preserve the expected count and
existing unique-entry behavior.
crates/mesh-llm-log-store/src/repositories/cleanup.rs-73-91 (1)

73-91: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The queued count under-reports deleted artifact pointers.

queue_artifact_deletions returns the row count from INSERT OR IGNORE, and the caller adds it to total. INSERT OR IGNORE skips rows whose (artifact_id, request_id) pair is already present in pending_artifact_deletions, which happens whenever a previous cleanup run queued a pointer that the artifact worker has not yet acknowledged. The following DELETE FROM artifact_pointers still removes those rows. The returned total therefore drifts below the real number of deleted rows, and insert_cleanup_run persists that inaccurate figure.

Return the DELETE row count instead.

🐛 Proposed fix
     ) -> Result<i64, LogStoreError> {
-        let queued = transaction
+        transaction
             .execute(
                 "INSERT OR IGNORE INTO pending_artifact_deletions (artifact_id, request_id) \
                  SELECT artifact_id, request_id FROM artifact_pointers WHERE occurred_at < ?",
                 rusqlite::params![cutoff],
             )
-            .map_err(LogStoreError::Sqlite)? as i64;
-        transaction
+            .map_err(LogStoreError::Sqlite)?;
+        let deleted = transaction
             .execute(
                 "DELETE FROM artifact_pointers WHERE occurred_at < ?",
                 rusqlite::params![cutoff],
             )
-            .map_err(LogStoreError::Sqlite)?;
-        Ok(queued)
+            .map_err(LogStoreError::Sqlite)? as i64;
+        Ok(deleted)
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-log-store/src/repositories/cleanup.rs` around lines 73 - 91,
Update queue_artifact_deletions to return the row count from the DELETE FROM
artifact_pointers execution rather than the INSERT OR IGNORE count. Preserve
both SQL operations and their error handling, while ensuring the returned value
reflects every deleted pointer so the caller’s total and insert_cleanup_run
remain accurate.
crates/mesh-llm-events/src/audit/sanitization.rs-213-236 (1)

213-236: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

This test fails on Windows.

Line 215 uses std::env::var("HOME").expect(...). Windows normally sets USERPROFILE and not HOME, so the test panics there. The stack adds Windows CI coverage for this workspace, so the failure is reachable.

Read whichever variable is present, or gate the test to Unix.

💚 Proposed fix
-        let home = std::env::var("HOME").expect("HOME should be available to the test");
+        let home = ["HOME", "USERPROFILE"]
+            .into_iter()
+            .find_map(|variable| std::env::var(variable).ok())
+            .filter(|home| !home.is_empty())
+            .expect("HOME or USERPROFILE should be available to the test");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-events/src/audit/sanitization.rs` around lines 213 - 236,
Update the test metadata_strings_are_recursively_secret_path_and_length_safe to
obtain the home directory from an environment variable available across
platforms, preferring HOME and falling back to USERPROFILE before asserting.
Preserve the existing redaction and truncation assertions.
crates/mesh-llm-events/src/audit.rs-336-363 (1)

336-363: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

A failed reopen after rotation leaves a stale file handle.

If open_private_append_file at Line 359 returns an error, the rename chain has already run. state.file still refers to the old inode, which is now the .1 rotated file, and state.size keeps its pre-rotation value. The error propagates once, but the next emit_audit call writes through the stale descriptor into the rotated file and retries rotation. Those events are then removed by a later rotation, so audit records are lost without a signal.

Record the failure in the state and refuse further writes until a reopen succeeds.

🛡️ Proposed fix sketch
-        state.file = open_private_append_file(&self.config.path)?;
-        state.size = 0;
+        match open_private_append_file(&self.config.path) {
+            Ok(file) => {
+                state.file = file;
+                state.size = 0;
+            }
+            Err(error) => {
+                state.reopen_failed = true;
+                return Err(error);
+            }
+        }

Add reopen_failed: bool to AuditFileState, and return an error early from emit_audit while the flag is set. Retry the reopen on the next emit and clear the flag when it succeeds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-events/src/audit.rs` around lines 336 - 363, Update
AuditFileState, rotate_if_needed, and emit_audit to track reopen_failed after
rotation. When open_private_append_file fails, set the flag and prevent
subsequent writes through the stale state.file; on a later emit, retry reopening
the active audit path, clear the flag and reset state.size only after success,
while preserving the error propagation on failed attempts.
crates/mesh-llm-log-store/src/tests.rs-665-682 (1)

665-682: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assert the specific error variant.

foreign_keys_enforced checks only result.is_err(). The test passes even if the insert fails for an unrelated reason, such as a malformed payload or a missing column, so it would not detect PRAGMA foreign_keys being turned off if some other error appeared first. Other tests in this file match a variant, for example LogStoreError::AlreadyExists.

💚 Proposed fix
-    assert!(
-        result.is_err(),
-        "orphan insert should fail — foreign_keys=ON"
-    );
+    let error = result.expect_err("orphan insert should fail — foreign_keys=ON");
+    assert!(
+        matches!(error, LogStoreError::Sqlite(_)),
+        "expected a SQLite constraint error, got: {error:?}"
+    );

Match the variant that the store actually maps a foreign-key violation to.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-log-store/src/tests.rs` around lines 665 - 682, Update the
foreign_keys_enforced test to match the specific LogStoreError variant returned
for SQLite foreign-key violations instead of only asserting result.is_err().
Reuse the existing error-matching style from nearby tests, while preserving the
orphan insert setup and ensuring unrelated errors fail the test.
crates/mesh-llm-events/src/audit/sanitization.rs-57-59 (1)

57-59: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

The invite-token exception also bypasses length bounding.

Line 57 returns before redact_audit_text runs. An invite-token string therefore skips the MAX_AUDIT_TEXT_LEN truncation and the home-path normalization. The module header states that this code bounds untrusted audit metadata before emission, so an oversized value written under an *_invite_token key defeats that bound and inflates the rotating audit file.

Keep the exception from the secret-marker redaction, but still bound the length.

🛡️ Proposed fix
-    if matches!(value, Value::String(_)) && key.is_some_and(is_invite_token_key) {
-        return;
-    }
+    if let Value::String(token) = value
+        && key.is_some_and(is_invite_token_key)
+    {
+        if token.chars().count() > MAX_AUDIT_TEXT_LEN {
+            let prefix: String = token.chars().take(MAX_AUDIT_TEXT_LEN).collect();
+            *token = format!("{prefix}... [TRUNCATED]");
+        }
+        return;
+    }

Add a regression test for an oversized invite-token value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-events/src/audit/sanitization.rs` around lines 57 - 59,
Update the sanitization flow around the invite-token check so invite-token
strings bypass only secret-marker redaction while still passing through
redact_audit_text for length bounding and home-path normalization. Add a
regression test covering an oversized invite-token value and verify the emitted
result is truncated to MAX_AUDIT_TEXT_LEN.
crates/mesh-llm-events/src/logging/artifacts.rs-30-97 (1)

30-97: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove unapproved dead_code suppressions.

The change suppresses warnings for staged public API instead of documenting an approved exception or keeping the API warning-clean.

  • crates/mesh-llm-events/src/logging/artifacts.rs#L30-L97: remove the #[allow(dead_code)] attributes, or document an approved and narrow exception.
  • crates/mesh-llm-events/src/logging/identifiers.rs#L28-L28: remove the #[allow(dead_code)] attribute, or document an approved and narrow exception.
  • crates/mesh-llm-events/src/logging/replay.rs#L26-L26: remove the #[allow(dead_code)] attribute, or document an approved and narrow exception.

As per coding guidelines, do not use #[allow(...)] without a clear reason and developer approval.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-events/src/logging/artifacts.rs` around lines 30 - 97, Remove
the unapproved #[allow(dead_code)] attributes from the artifact metadata fields
and methods in crates/mesh-llm-events/src/logging/artifacts.rs:30-97, the
affected declaration in crates/mesh-llm-events/src/logging/identifiers.rs:28-28,
and the affected declaration in
crates/mesh-llm-events/src/logging/replay.rs:26-26. Only retain an attribute if
you add a clear, narrow, developer-approved exception.

Source: Coding guidelines

crates/mesh-llm-events/src/logging/artifacts.rs-136-145 (1)

136-145: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Deserialize the fixture in this test.

Line 138 creates a JSON fixture with omitted boolean fields. The test then creates a new value instead of deserializing that fixture. This does not verify the #[serde(default)] compatibility contract.

Proposed fix
-        let _json = r#"{"artifact_id":"00000000-0000-4000-a000-00000000001","kind":"chunk","bytes":128,"checksum":"","version":1}"#;
-        // This would fail if the artifact_id format is invalid UUID. Let's test with a valid one.
-        let meta = ArtifactMetadata::new(ArtifactKind::Chunk, 128);
+        let json = r#"{"artifact_id":"00000000-0000-4000-a000-000000000001","kind":"chunk","bytes":128,"checksum":"","version":1}"#;
+        let meta: ArtifactMetadata = serde_json::from_str(json).unwrap();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-events/src/logging/artifacts.rs` around lines 136 - 145,
Update test_defaults_for_boolean_flags to deserialize the minimal JSON fixture
into ArtifactMetadata instead of constructing it with ArtifactMetadata::new.
Keep the fixture’s omitted boolean fields and assert that deserialization
populates redacted, truncated, missing, and corrupt as false.
crates/mesh-llm-log-store/src/store.rs-19-24 (1)

19-24: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Use sub-second precision in mesh_llm_log_store::SystemClock.

LogStoreSink::persist_audit_entry and runtime health-audit paths use this clock. Second-level timestamps make audit ordering depend on ID tie-breakers. Use one fixed-width fractional format before persisting rows. Lifecycle timestamps and RequestRegistry already use the host runtime clock with millisecond precision.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-log-store/src/store.rs` around lines 19 - 24, Update
SystemClock::now to format UTC timestamps with fixed-width millisecond precision
before persisting them, matching the host runtime clock’s precision and
preserving the existing timestamp structure.
crates/mesh-llm-host-runtime/src/logging/writer.rs-9-14 (1)

9-14: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

recursion_blocks conflates real recursion with cross-thread contention.

in_error_path is one process-wide AtomicBool, not a thread-local. Lines 10-13 document that choice, and concurrent_error_path_is_suppressed_and_restored (line 228) encodes it. The suppression behaviour is sound for a fail-open path.

The accounting is not. When thread A holds the error path and thread B calls try_record_error for an unrelated request, line 84 increments recursion_blocks. An operator reading that counter cannot distinguish two very different conditions:

  • A self-logging loop was prevented. That indicates a defect and needs investigation.
  • Two independent audits raced and one was discarded. That indicates load, and the discarded audit is lost data.

service.rs compounds this: record_fallback_suppressed (line 107) increments the same counter for a third condition, a fallback whose own persistence failed. Three causes share one number.

Split the counter so each cause is separately observable.

♻️ Proposed split
 pub struct FailOpenWriter {
     recursion_guard: Arc<RecursionGuard>,
     pub write_drops: Arc<AtomicU64>,
-    /// Number of times the error-fallback path was blocked by recursion detection.
+    /// Number of times the error-fallback path was blocked because another
+    /// fallback was already in flight. This covers both true recursion on one
+    /// thread and contention between threads.
     pub recursion_blocks: Arc<AtomicU64>,
+
+    /// Number of times a fallback audit's own persistence failure was
+    /// deliberately not re-reported.
+    pub fallback_suppressions: Arc<AtomicU64>,
 }
     pub fn record_fallback_suppressed(&self) {
-        self.recursion_blocks.fetch_add(1, Ordering::Relaxed);
+        self.fallback_suppressions.fetch_add(1, Ordering::Relaxed);
     }

delivery_shutdown.rs line 30 and service_tests.rs line 1706 assert on recursion_blocks and need updating to the correct counter.

Also applies to: 83-86

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/logging/writer.rs` around lines 9 - 14,
Split the shared recursion_blocks accounting into distinct counters for
self-recursion prevention, cross-thread error-path contention, and fallback
persistence failure. Update try_record_error and record_fallback_suppressed to
increment their respective counters, expose the new counters through the
existing reporting interface, and update delivery_shutdown.rs and
service_tests.rs assertions to use the appropriate counter while preserving the
current suppression behavior.
crates/mesh-llm-log-store/src/artifacts.rs-449-455 (1)

449-455: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Align the "never written" predicate between read_artifact and status.

read_artifact requires all three of media_kind.is_none(), checksum.is_none(), and bytes == 0 to report ArtifactMissing. status requires only checksum.is_none() and bytes == 0. A pointer row that has a media_kind but no checksum and zero bytes therefore reports ArtifactStatus::Missing from status, while read_artifact continues to the filesystem and can return Ok. Operators comparing a status check against a read see contradictory results for the same artifact.

Extract one predicate and call it from both paths.

♻️ Proposed shared predicate
// near the other helpers
fn pointer_has_no_stored_content(row: &crate::repositories::ArtifactPointerRow) -> bool {
    row.checksum.is_none() && row.bytes == 0
}
-        if row.media_kind.is_none() && row.checksum.is_none() && row.bytes == 0 {
+        if pointer_has_no_stored_content(&row) {

Also applies to: 570-573

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-log-store/src/artifacts.rs` around lines 449 - 455, Extract a
shared `pointer_has_no_stored_content` predicate near the existing helpers that
checks for a missing checksum and zero bytes, then use it in both
`read_artifact` and `status`. Remove the additional `media_kind.is_none()`
requirement from the `read_artifact` missing-artifact check so both paths
consistently classify such pointers as `ArtifactMissing`/`Missing`.
crates/mesh-llm-host-runtime/src/logging/writer.rs-78-96 (1)

78-96: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

The documented meaning of the return value does not match the behaviour.

Line 78 states that try_record_error returns "false if blocked by recursion guard (caller should proceed silently)". Line 96 returns result.is_ok(), so it also returns false when the recorder panics. try_record_error_catches_panic (line 167) confirms this.

The two cases need different operator responses. A recursion block is expected under load. A recorder panic is a defect. LoggingService::write_error_audit (service.rs line 907) forwards this boolean to its caller as "was written", so both cases report identically.

Correct the doc comment, or return an enum that names the three outcomes.

♻️ Minimal doc correction
-    /// Attempt to record an error/audit entry. Returns `true` if the fallback path was entered successfully, `false` if blocked by recursion guard (caller should proceed silently). This method is designed to never panic — it absorbs all internal failures.
+    /// Attempt to record an error/audit entry. Returns `true` when the
+    /// fallback path was entered and the recorder completed. Returns `false`
+    /// in two cases: the recursion guard blocked entry, or the recorder
+    /// panicked and the panic was absorbed. The caller proceeds silently in
+    /// both cases. This method never panics.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/logging/writer.rs` around lines 78 - 96,
Update the documentation for try_record_error to accurately state that it
returns false both when recursion blocks entry and when the recorder panics,
while true indicates successful recorder completion. Keep the existing boolean
behavior and implementation unchanged, including the silent handling of both
failure cases.
crates/mesh-llm-log-store/src/repositories.rs-166-182 (1)

166-182: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

row.get(N).ok() hides column type errors as None.

rusqlite maps a SQL NULL into Option<T> directly, so row.get(3)? already yields None for a null terminal_at. Using .ok() additionally swallows InvalidColumnType and FromSqlConversionFailure. If a migration changes status_code from INTEGER to TEXT, get_summary returns status_code: None for every row instead of failing, and callers report a summary with no status rather than an error.

Replace .ok() with ? on the Option<T> columns. The same pattern appears in list_summaries (lines 204-213) and in the three ArtifactPointerRow mappers (lines 631-632, 667-668, 740-741).

🐛 Proposed fix
             Ok(SummaryRow {
                 request_id: row.get(0)?,
                 state: row.get(1)?,
                 created_at: row.get(2)?,
-                terminal_at: row.get(3).ok(),
-                route: row.get(4).ok(),
-                model: row.get(5).ok(),
-                provider: row.get(6).ok(),
-                engine: row.get(7).ok(),
-                status_code: row.get(8).ok(),
-                error_msg: row.get(9).ok(),
-                tenant_id: row.get(10).ok(),
-                account_id: row.get(11).ok(),
-                user_id: row.get(12).ok(),
+                terminal_at: row.get(3)?,
+                route: row.get(4)?,
+                model: row.get(5)?,
+                provider: row.get(6)?,
+                engine: row.get(7)?,
+                status_code: row.get(8)?,
+                error_msg: row.get(9)?,
+                tenant_id: row.get(10)?,
+                account_id: row.get(11)?,
+                user_id: row.get(12)?,
             })

Also applies to: 625-638

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-log-store/src/repositories.rs` around lines 166 - 182,
Replace .ok() with ? for nullable column mappings in the SummaryRow mapper used
by get_summary, preserving Option<T> handling for SQL NULL while propagating
conversion errors. Apply the same change in list_summaries and all three
ArtifactPointerRow mappers, including the mappings near their corresponding
query results.
crates/mesh-llm-host-runtime/src/logging/service_tests.rs-1011-1030 (1)

1011-1030: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The channel-independence assertion cannot fail.

Line 1020 reads other_current <= 101 || other_ch == ch. The enclosing if other_ch != ch on line 1017 already guarantees the second operand is false, so it contributes nothing. The first operand compares against the magic bound 101, which no channel can exceed in this test: each channel receives at most 100 explicit events plus the one admitted event.

The assertion therefore holds for any implementation, including one where all three channels share a single counter. The stated intent — "other channels weren't affected by events on this channel" — is not checked.

Capture each channel's sequence before the loop and assert it is unchanged after.

💚 Proposed fix
-        // Verify other channels weren't affected by events on this channel.
-        for other_ch in [
-            ReplayChannel::Requests,
-            ReplayChannel::Operations,
-            ReplayChannel::System,
-        ] {
-            if other_ch != ch {
-                let other_current = svc.sequences_ref().current(other_ch);
-                assert!(
-                    other_current <= 101 || other_ch == ch,
-                    "channel {:?} should not have advanced beyond its own events (got {})",
-                    other_ch,
-                    other_current
-                );
-            }
-        }
+        // Verify other channels were not advanced by events on this channel.
+        for (other_ch, before) in &untouched {
+            assert_eq!(
+                svc.sequences_ref().current(*other_ch),
+                *before,
+                "channel {other_ch:?} must not advance from {ch:?} events"
+            );
+        }

Build untouched immediately before the 100-event loop, holding the current value of each channel other than ch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/logging/service_tests.rs` around lines 1011
- 1030, Update the test around the per-channel event loop to capture each
non-target channel’s sequence value immediately before emitting the 100 events,
storing these values in an `untouched` collection keyed by channel. Replace the
ineffective `other_current <= 101 || other_ch == ch` assertion in the
verification loop with a comparison against the corresponding pre-loop value,
ensuring every channel other than `ch` is unchanged.
crates/mesh-llm-host-runtime/src/logging/service_tests.rs-332-337 (1)

332-337: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

TestClock repeats timestamps after 60 calls.

now computes (n % 60) and formats it as the seconds field, so call 60 returns the same string as call 0. Several tests advance the clock far past that bound: test_monotonic_channel_sequences performs 300 enqueues and test_queue_never_exceeds_capacity performs 10,000, and every enqueue calls clock.now().

Duplicate occurred_at values matter because the log store orders and paginates on (occurred_at, id). durable_sink_reopens_one_summary_with_ordered_retry_events reads back rows with ORDER BY occurred_at ASC, event_id ASC (line 760) and asserts an exact event sequence. That test stays under 60 ticks today, so it passes. Any added event or added setup call pushes it over and makes the ordering depend on random UUID comparison.

Format the full counter instead of wrapping it.

🐛 Proposed fix
 impl Clock for TestClock {
     fn now(&self) -> String {
         let n = self.counter.fetch_add(1, AtomicOrdering::Relaxed);
-        format!("2025-01-01T00:00:{:02}Z", (n % 60) as u32)
+        // Never wrap: the log store orders and paginates on occurred_at, so a
+        // repeated timestamp makes read-back ordering nondeterministic.
+        format!("2025-01-01T00:00:00.{:09}Z", n)
     }
 }

test_manual_pump_persists_exact_entry_without_consuming_replay asserts the literal "2025-01-01T00:00:00Z" on line 1564 and needs updating with this change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/logging/service_tests.rs` around lines 332 -
337, Update TestClock::now to format the full atomic counter value instead of
applying modulo 60, preserving the existing timestamp prefix and zero-padded
seconds format. Also update
test_manual_pump_persists_exact_entry_without_consuming_replay to expect the
resulting timestamp representation.
crates/mesh-llm-host-runtime/src/logging/service.rs-428-437 (1)

428-437: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

A manual queue with capacity 0 counts a drop before anything is queued.

spawn clamps the worker channel with self.config.queue_capacity.max(1) on line 655, because Tokio rejects a zero-capacity channel. LoggingService::new (line 517) and new_disabled (line 567) do not apply the same clamp to DeliveryMode::Manual { capacity }.

With queue_capacity: 0, the first offer_persistence_to call evaluates pending.len() >= *capacity as 0 >= 0, which is true. pending.pop_front() then returns None because the queue is empty, yet line 432 still increments persistence_queue_drops. One drop is recorded although no entry was discarded. The queue then holds exactly one entry rather than zero.

test_zero_capacity_service_can_spawn_without_panicking (service_tests.rs line 1444) covers the spawn path only, so this manual path is untested.

Apply the same clamp for consistency.

🐛 Proposed fix
         let delivery = Arc::new(Mutex::new(DeliveryMode::Manual {
             pending: VecDeque::new(),
-            capacity: config.queue_capacity,
+            // Match the worker channel clamp in `spawn`, so manual and worker
+            // ownership account for drops identically.
+            capacity: config.queue_capacity.max(1),
         }));

Apply the same change in new_disabled (line 567) and in the pump_sync restore at line 980.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/logging/service.rs` around lines 428 - 437,
Clamp the manual delivery capacity to at least 1 wherever it is initialized or
restored, including LoggingService::new, new_disabled, and pump_sync. Use the
same queue_capacity.max(1) behavior as spawn so zero-capacity services neither
count a drop for an empty queue nor retain more entries than the effective
capacity.
crates/mesh-llm-host-runtime/src/logging/service_tests/delivery_shutdown.rs-63-109 (1)

63-109: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

A regression in this test hangs the suite instead of failing it.

The hook at lines 75-82 performs a blocking recv() on a std::sync::mpsc receiver. The test asserts on line 103 that the write task has not finished, which proves the SQLite call ran on the blocking pool.

If a future change moves that call back onto the async task, the hook's blocking recv() runs on the current_thread executor. That executor is then parked and can never poll the spawn_blocking join future on line 98, so started_rx.recv() never returns to the test. The test hangs until the CI job timeout rather than reporting the regression.

The condition the test guards against is exactly the condition that produces the hang. Bound the wait.

💚 Proposed bounded wait
-    tokio::task::spawn_blocking(move || started_rx.recv())
-        .await
-        .expect("start observer joins")
-        .expect("blocking worker started");
+    tokio::task::spawn_blocking(move || {
+        started_rx.recv_timeout(Duration::from_secs(5))
+    })
+    .await
+    .expect("start observer joins")
+    .expect("the SQLite operation must reach the blocking pool");

release_tx.send(()) on line 107 then still unblocks the hook on the success path, and a regression reports a failed expectation within five seconds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/logging/service_tests/delivery_shutdown.rs`
around lines 63 - 109, Bound the `started_rx.recv()` wait in
`log_store_sqlite_busy_seam_never_blocks_the_shared_tokio_executor` to five
seconds so a regression cannot hang the current-thread executor indefinitely.
Preserve the existing `spawn_blocking` observation and success-path
`release_tx.send(())` behavior, while making timeout failure report through the
test expectation.
crates/mesh-llm-log-store/src/artifacts_tests.rs-641-696 (1)

641-696: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Gate the whole test with #[cfg(unix)].

On Windows, store, target_dir, and link_path are created but never used, because every use sits inside #[cfg(unix)] blocks. This produces unused-variable warnings in the Windows test build. The neighbouring symlink tests already gate at the test level.

♻️ Proposed change
+#[cfg(unix)]
 #[test]
 fn artifact_symlink_rejected() {
+    use std::os::unix::fs::symlink;
+
     let tmp = tempfile::tempdir().expect("create temp dir");

Then remove the inner #[cfg(unix)] attributes and the std::os::unix::fs:: path prefix.

As per coding guidelines: "Do not leave warnings, unused code, or dead code introduced by a change."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-log-store/src/artifacts_tests.rs` around lines 641 - 696,
Gate the entire artifact_symlink_rejected test with #[cfg(unix)] so its
Unix-only setup and assertions are excluded on Windows. Remove the inner
#[cfg(unix)] blocks and use the platform-appropriate symlink call without the
Unix-specific path prefix.

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 61518d01-0884-4460-a539-681bb8cde0e4

📥 Commits

Reviewing files that changed from the base of the PR and between bdd0beb and e605e92.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (97)
  • .agents/skills/manage-ci/references/current-inventory.md
  • .github/workflows/pr_builds.yml
  • Cargo.toml
  • ci/ci.md
  • crates/mesh-llm-cli/src/parser/commands.rs
  • crates/mesh-llm-config/src/lib.rs
  • crates/mesh-llm-config/src/model.rs
  • crates/mesh-llm-config/src/model/built_in_schema.rs
  • crates/mesh-llm-config/src/validate.rs
  • crates/mesh-llm-events/Cargo.toml
  • crates/mesh-llm-events/src/audit.rs
  • crates/mesh-llm-events/src/audit/sanitization.rs
  • crates/mesh-llm-events/src/lib.rs
  • crates/mesh-llm-events/src/logging/artifacts.rs
  • crates/mesh-llm-events/src/logging/envelope.rs
  • crates/mesh-llm-events/src/logging/events.rs
  • crates/mesh-llm-events/src/logging/identifiers.rs
  • crates/mesh-llm-events/src/logging/lifecycle.rs
  • crates/mesh-llm-events/src/logging/mod.rs
  • crates/mesh-llm-events/src/logging/proxy.rs
  • crates/mesh-llm-events/src/logging/replay.rs
  • crates/mesh-llm-events/src/logging/summaries.rs
  • crates/mesh-llm-events/src/logging/tests.rs
  • crates/mesh-llm-host-runtime/Cargo.toml
  • crates/mesh-llm-host-runtime/src/config_schema.rs
  • crates/mesh-llm-host-runtime/src/lib.rs
  • crates/mesh-llm-host-runtime/src/logging/bus.rs
  • crates/mesh-llm-host-runtime/src/logging/foundation.rs
  • crates/mesh-llm-host-runtime/src/logging/lifecycle.rs
  • crates/mesh-llm-host-runtime/src/logging/limits.rs
  • crates/mesh-llm-host-runtime/src/logging/mod.rs
  • crates/mesh-llm-host-runtime/src/logging/persistence.rs
  • crates/mesh-llm-host-runtime/src/logging/policy.rs
  • crates/mesh-llm-host-runtime/src/logging/registry.rs
  • crates/mesh-llm-host-runtime/src/logging/runtime_state.rs
  • crates/mesh-llm-host-runtime/src/logging/sequences.rs
  • crates/mesh-llm-host-runtime/src/logging/service.rs
  • crates/mesh-llm-host-runtime/src/logging/service_tests.rs
  • crates/mesh-llm-host-runtime/src/logging/service_tests/delivery_shutdown.rs
  • crates/mesh-llm-host-runtime/src/logging/writer.rs
  • crates/mesh-llm-host-runtime/src/mesh/owner_control/mod.rs
  • crates/mesh-llm-host-runtime/src/mesh/owner_control_response.rs
  • crates/mesh-llm-host-runtime/src/mesh/plugin_config.rs
  • crates/mesh-llm-host-runtime/src/protocol/config_tests.rs
  • crates/mesh-llm-host-runtime/src/protocol/convert.rs
  • crates/mesh-llm-host-runtime/src/protocol/tests/config.rs
  • crates/mesh-llm-host-runtime/src/runtime/config_state.rs
  • crates/mesh-llm-host-runtime/src/runtime/options.rs
  • crates/mesh-llm-host-runtime/src/runtime/run_auto.rs
  • crates/mesh-llm-host-runtime/src/runtime/tests/startup_models.rs
  • crates/mesh-llm-host-runtime/src/runtime/tracing_writer.rs
  • crates/mesh-llm-host-runtime/src/sdk.rs
  • crates/mesh-llm-host-runtime/src/sdk/embedded_logging.rs
  • crates/mesh-llm-host-runtime/tests/audit_test.rs
  • crates/mesh-llm-log-store/Cargo.toml
  • crates/mesh-llm-log-store/README.md
  • crates/mesh-llm-log-store/src/artifact_privacy.rs
  • crates/mesh-llm-log-store/src/artifacts.rs
  • crates/mesh-llm-log-store/src/artifacts_tests.rs
  • crates/mesh-llm-log-store/src/capture.rs
  • crates/mesh-llm-log-store/src/capture_tests.rs
  • crates/mesh-llm-log-store/src/cursor.rs
  • crates/mesh-llm-log-store/src/error.rs
  • crates/mesh-llm-log-store/src/lib.rs
  • crates/mesh-llm-log-store/src/migrations.rs
  • crates/mesh-llm-log-store/src/repositories.rs
  • crates/mesh-llm-log-store/src/repositories/cleanup.rs
  • crates/mesh-llm-log-store/src/store.rs
  • crates/mesh-llm-log-store/src/tests.rs
  • crates/mesh-llm-tui/src/output/EVENTS.md
  • crates/mesh-llm-tui/src/output/dashboard.rs
  • crates/mesh-llm-tui/src/output/formatting.rs
  • crates/mesh-llm-tui/src/output/mod.rs
  • crates/mesh-llm-tui/src/output/rendering/join_token.rs
  • crates/mesh-llm-tui/src/output/rendering/layout.rs
  • crates/mesh-llm-tui/src/output/rendering/mod.rs
  • crates/mesh-llm-tui/src/output/rendering/tui.rs
  • crates/mesh-llm-tui/src/output/state.rs
  • crates/mesh-llm-tui/src/output/tests/formatting.rs
  • crates/mesh-llm-tui/src/output/tests/mod.rs
  • crates/mesh-llm-tui/src/output/tests/rendering.rs
  • crates/mesh-llm-ui/e2e/smoke/live-parity.spec.ts
  • crates/mesh-llm-ui/src/App.test.tsx
  • crates/mesh-llm-ui/src/app/layout/RootLayout.test.tsx
  • crates/mesh-llm-ui/src/features/app-shell/components/AppHeader.test.tsx
  • crates/mesh-llm-ui/src/features/app-shell/components/AppHeader.tsx
  • crates/mesh-llm-ui/src/features/chat/components/ChatPage.tsx
  • crates/mesh-llm/src/commands/runtime.rs
  • crates/mesh-llm/src/lib.rs
  • scripts/affected-crates.sh
  • scripts/console-format.js
  • scripts/console-format.test.js
  • scripts/plan-clippy-batches.sh
  • scripts/plan-pr-build-jobs.py
  • scripts/publish-crates.sh
  • scripts/tests/test_plan_pr_build_jobs.py
  • scripts/tests/test_pr_workflow_artifacts.py
💤 Files with no reviewable changes (3)
  • crates/mesh-llm-tui/src/output/formatting.rs
  • crates/mesh-llm-tui/src/output/mod.rs
  • crates/mesh-llm-tui/src/output/state.rs

Comment thread crates/mesh-llm-host-runtime/src/logging/service.rs Outdated
Comment thread crates/mesh-llm-host-runtime/src/mesh/owner_control/mod.rs
ndizazzo added a commit that referenced this pull request Aug 11, 2026
ndizazzo added a commit that referenced this pull request Aug 11, 2026
@ndizazzo
ndizazzo force-pushed the logging-foundation-v3 branch from 1a71f5d to 4983851 Compare August 11, 2026 07:17

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
crates/mesh-llm-tui/src/output/tests/rendering.rs (2)

596-605: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert that the copy-control removal remains enforced.

The test checks token retention and rendering. It does not check that the copy control stays absent. tui_layout still exposes join_token_copy_button, and the layout sets it to a zero area. Add a zero-area assertion so a later copy-control regression cannot pass this test unnoticed.

Suggested assertion
     let areas = tui_layout(Rect::new(0, 0, 120, 24), &state);
+    assert_eq!(areas.join_token_copy_button.width, 0);
+    assert_eq!(areas.join_token_copy_button.height, 0);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-tui/src/output/tests/rendering.rs` around lines 596 - 605,
Extend the rendering test around tui_layout to assert that
areas.join_token_copy_button has zero width and height, preserving the enforced
removal of the copy control while keeping the existing token rendering
assertions unchanged.

607-618: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Retain constrained-terminal coverage for the token.

The new snapshot uses 120x24, so the token fits without wrapping. The change removes the full-screen wrapping coverage. Because invite_token remains displayed, keep a narrow or short-frame test that verifies wrapping and redraw behavior. Small-terminal regressions can otherwise pass.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-tui/src/output/tests/rendering.rs` around lines 607 - 618,
Extend the rendering tests around tui_join_token_text_area to retain
constrained-terminal coverage for invite_token: add or restore a narrow or
short-frame case that forces token wrapping and verifies the resulting redraw
behavior. Keep the existing two-row height assertions and ensure the test
exercises the wrapped layout rather than only the 120x24 snapshot dimensions.
crates/mesh-llm-host-runtime/src/runtime/config_state.rs (1)

463-502: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The live-logging apply sequence is duplicated in owner_control.

apply_with_live_logging and apply_owner_control_config_with_persistence in crates/mesh-llm-host-runtime/src/mesh/owner_control/mod.rs (lines 152-189) implement the same five steps: prepare, apply_live_logging, persist, restore_live_logging on PersistError, and the AppliedLive remap. Two copies of the rollback rule can diverge.

Extract the shared sequence into one function that accepts the persist step as a parameter, then call it from both sites.

As per coding guidelines: "Do not add Rust methods or functions exceeding configured Clippy line-count or cognitive-complexity limits; use small, semantically named helpers and clear control-flow phases."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/runtime/config_state.rs` around lines 463 -
502, The live-logging apply sequence is duplicated between
ConfigState::apply_with_live_logging and
apply_owner_control_config_with_persistence. Extract the shared prepare, live
apply, persistence callback, rollback-on-PersistError, finish, and
Applied-to-Live remapping flow into a small semantically named helper that
accepts the persistence operation as a parameter, then have both methods
delegate to it while preserving existing behavior and keeping complexity within
Clippy limits.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/mesh-llm-host-runtime/src/runtime/config_state.rs`:
- Around line 431-438: Guard the prepare/commit gap in config_state.rs: in
finish_apply, replace the debug_assert_eq! with a runtime revision check that
returns ApplyResult::RevisionConflict { current_revision: self.revision } when
self.revision + 1 differs from pending.revision. In
crates/mesh-llm-host-runtime/src/mesh/owner_control/mod.rs lines 152-194, verify
every shared config_state writer acquires apply_serialization_lock and route any
bypassing writer through that lock.

In `@crates/mesh-llm-log-store/src/tests.rs`:
- Around line 721-727: Update the constraint-error mapping around
is_unique_constraint_error to detect SQLITE_CONSTRAINT_FOREIGNKEY separately and
return the appropriate distinct LogStoreError variant for foreign-key failures.
Keep primary-key and unique constraint violations mapped to AlreadyExists, and
update the orphan-insert assertion in the test to expect the foreign-key
variant.

---

Nitpick comments:
In `@crates/mesh-llm-host-runtime/src/runtime/config_state.rs`:
- Around line 463-502: The live-logging apply sequence is duplicated between
ConfigState::apply_with_live_logging and
apply_owner_control_config_with_persistence. Extract the shared prepare, live
apply, persistence callback, rollback-on-PersistError, finish, and
Applied-to-Live remapping flow into a small semantically named helper that
accepts the persistence operation as a parameter, then have both methods
delegate to it while preserving existing behavior and keeping complexity within
Clippy limits.

In `@crates/mesh-llm-tui/src/output/tests/rendering.rs`:
- Around line 596-605: Extend the rendering test around tui_layout to assert
that areas.join_token_copy_button has zero width and height, preserving the
enforced removal of the copy control while keeping the existing token rendering
assertions unchanged.
- Around line 607-618: Extend the rendering tests around
tui_join_token_text_area to retain constrained-terminal coverage for
invite_token: add or restore a narrow or short-frame case that forces token
wrapping and verifies the resulting redraw behavior. Keep the existing two-row
height assertions and ensure the test exercises the wrapped layout rather than
only the 120x24 snapshot dimensions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15e116f5-54b5-46d6-8df1-4e04cc4eadfe

📥 Commits

Reviewing files that changed from the base of the PR and between bd09eda and 1a71f5d.

📒 Files selected for processing (28)
  • crates/mesh-llm-config/src/validate.rs
  • crates/mesh-llm-events/src/audit.rs
  • crates/mesh-llm-events/src/audit/sanitization.rs
  • crates/mesh-llm-events/src/logging/artifacts.rs
  • crates/mesh-llm-events/src/logging/identifiers.rs
  • crates/mesh-llm-events/src/logging/replay.rs
  • crates/mesh-llm-host-runtime/src/exact_test_wrappers.rs
  • crates/mesh-llm-host-runtime/src/lib.rs
  • crates/mesh-llm-host-runtime/src/logging/service.rs
  • crates/mesh-llm-host-runtime/src/logging/service_tests.rs
  • crates/mesh-llm-host-runtime/src/logging/service_tests/delivery_shutdown.rs
  • crates/mesh-llm-host-runtime/src/logging/writer.rs
  • crates/mesh-llm-host-runtime/src/mesh/owner_control/mod.rs
  • crates/mesh-llm-host-runtime/src/mesh/owner_control_response.rs
  • crates/mesh-llm-host-runtime/src/runtime/config_state.rs
  • crates/mesh-llm-host-runtime/src/runtime/run_auto.rs
  • crates/mesh-llm-host-runtime/src/runtime/tests/startup_models.rs
  • crates/mesh-llm-log-store/src/artifacts.rs
  • crates/mesh-llm-log-store/src/artifacts_tests.rs
  • crates/mesh-llm-log-store/src/repositories.rs
  • crates/mesh-llm-log-store/src/repositories/cleanup.rs
  • crates/mesh-llm-log-store/src/store.rs
  • crates/mesh-llm-log-store/src/tests.rs
  • crates/mesh-llm-tui/src/output/EVENTS.md
  • crates/mesh-llm-tui/src/output/rendering/join_token.rs
  • crates/mesh-llm-tui/src/output/tests/rendering.rs
  • crates/mesh-llm-ui/src/features/app-shell/components/AppHeader.test.tsx
  • crates/mesh-llm-ui/src/features/app-shell/components/AppHeader.tsx
💤 Files with no reviewable changes (2)
  • crates/mesh-llm-events/src/logging/identifiers.rs
  • crates/mesh-llm-events/src/logging/replay.rs
🚧 Files skipped from review as they are similar to previous changes (15)
  • crates/mesh-llm-host-runtime/src/mesh/owner_control_response.rs
  • crates/mesh-llm-host-runtime/src/logging/service_tests/delivery_shutdown.rs
  • crates/mesh-llm-ui/src/features/app-shell/components/AppHeader.test.tsx
  • crates/mesh-llm-log-store/src/store.rs
  • crates/mesh-llm-events/src/audit/sanitization.rs
  • crates/mesh-llm-tui/src/output/rendering/join_token.rs
  • crates/mesh-llm-config/src/validate.rs
  • crates/mesh-llm-ui/src/features/app-shell/components/AppHeader.tsx
  • crates/mesh-llm-events/src/audit.rs
  • crates/mesh-llm-log-store/src/artifacts.rs
  • crates/mesh-llm-log-store/src/repositories/cleanup.rs
  • crates/mesh-llm-log-store/src/artifacts_tests.rs
  • crates/mesh-llm-events/src/logging/artifacts.rs
  • crates/mesh-llm-log-store/src/repositories.rs
  • crates/mesh-llm-host-runtime/src/logging/service.rs

Comment thread crates/mesh-llm-host-runtime/src/runtime/config_state.rs
Comment thread crates/mesh-llm-log-store/src/tests.rs Outdated
ndizazzo added a commit that referenced this pull request Aug 11, 2026
@ndizazzo
ndizazzo force-pushed the logging-foundation-v3 branch 6 times, most recently from c8803a1 to a849461 Compare August 11, 2026 22:46
@ndizazzo
ndizazzo force-pushed the logging-foundation-v3 branch from c86ec11 to 76d7ca8 Compare August 12, 2026 06:58
@ndizazzo
ndizazzo merged commit 1141b86 into main Aug 12, 2026
52 checks passed
@ndizazzo
ndizazzo deleted the logging-foundation-v3 branch August 12, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant