Skip to content

feat(cli): expose soar to frontends with JSON output and a plugin manifest - #192

Merged
QaidVoid merged 19 commits into
mainfrom
json-output
Aug 10, 2026
Merged

feat(cli): expose soar to frontends with JSON output and a plugin manifest#192
QaidVoid merged 19 commits into
mainfrom
json-output

Conversation

@QaidVoid

@QaidVoid QaidVoid commented Aug 9, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added structured JSON and JSON Lines output for searches, listings, queries, repositories, environment details, updates, and apply results.
    • Added update --check to preview pending updates without applying them.
    • Added a plugin-manifest command describing supported operations, formats, settings, and profiles.
    • Added completion events summarizing installed, updated, removed, and failed items.
    • Added JSON output for dry-run changes.
    • Package queries now include available maintainer information.
  • Bug Fixes

    • Improved separation of JSON responses from diagnostic logs.
    • Improved database connection reliability with busy-timeout and WAL configuration.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3ac5dcef-e7c9-4545-aba5-3e777e0c6def

📥 Commits

Reviewing files that changed from the base of the PR and between 9be1e21 and a226f91.

📒 Files selected for processing (9)
  • crates/soar-cli/src/json_output.rs
  • crates/soar-cli/src/logging.rs
  • crates/soar-cli/src/main.rs
  • crates/soar-cli/src/plugin_manifest.rs
  • crates/soar-cli/src/repo.rs
  • crates/soar-cli/src/update.rs
  • crates/soar-db/src/connection.rs
  • crates/soar-events/src/sink.rs
  • crates/soar-operations/src/search.rs
🚧 Files skipped from review as they are similar to previous changes (9)
  • crates/soar-db/src/connection.rs
  • crates/soar-operations/src/search.rs
  • crates/soar-cli/src/repo.rs
  • crates/soar-cli/src/update.rs
  • crates/soar-cli/src/logging.rs
  • crates/soar-cli/src/json_output.rs
  • crates/soar-cli/src/plugin_manifest.rs
  • crates/soar-events/src/sink.rs
  • crates/soar-cli/src/main.rs

📝 Walkthrough

Walkthrough

The CLI adds structured JSON responses, JSON event streaming, update checks, and plugin manifest generation. Apply operations emit completion events. SQLite connections share busy-timeout and WAL preparation.

Changes

JSON event streaming

Layer / File(s) Summary
Event serialization and apply completion
crates/soar-events/..., crates/soar-operations/src/apply.rs
Events serialize as tagged snake_case JSON. JsonLinesSink writes and flushes one JSON record per event. Apply operations emit final counts.
JSON response models
crates/soar-cli/src/json_output.rs, crates/soar-operations/src/search.rs, crates/soar-registry/src/package.rs
The CLI adds serializable models for packages, installations, updates, repositories, environments, diffs, and listings. Package queries populate maintainer data. The documented pkg_id requirement is removed.
CLI JSON routing and query output
crates/soar-cli/src/main.rs, crates/soar-cli/src/logging.rs, crates/soar-cli/src/list.rs, crates/soar-cli/src/repo.rs, crates/soar-cli/src/utils.rs
--json enables event streaming, routes logs to stderr, disables progress output, and emits structured query and repository results.
Update checks, apply diffs, and plugin manifest
crates/soar-cli/src/cli.rs, crates/soar-cli/src/update.rs, crates/soar-cli/src/apply.rs, crates/soar-cli/src/plugin_manifest.rs, crates/soar-cli/src/main.rs
The CLI adds update --check, JSON dry-run apply output, environment JSON output, and the plugin-manifest command with generated operation metadata and validation tests.

Database connection setup

Layer / File(s) Summary
Shared SQLite connection preparation
crates/soar-db/src/connection.rs
Database connections use a shared routine that applies a 5-second busy timeout and WAL mode.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant SoarContext
  participant JsonLinesSink
  participant Command
  CLI->>SoarContext: enable event streaming for --json
  SoarContext->>JsonLinesSink: route events to stdout or stderr
  CLI->>Command: execute query or apply operation
  Command->>JsonLinesSink: emit JSON response or event
  JsonLinesSink-->>CLI: write one flushed JSON line
Loading

Possibly related PRs

🚥 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 summarizes the main changes: JSON output and a plugin manifest that expose Soar to frontends.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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 json-output

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.

@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: 6

🤖 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/soar-cli/src/apply.rs`:
- Around line 40-42: Update ApplyDiffJson and its emitted schema to include
pending_version_updates, then pass the updates produced by execute_apply into
ApplyDiffJson::new in the dry_run and event_stream_enabled path. Ensure the JSON
diff reflects declared version changes made before package operations.
- Around line 40-42: Update the confirmation flow around ApplyDiffJson::new so
JSON mode never writes the interactive prompt to stdout; route the prompt to
stderr while preserving valid NDJSON event output, or reject interactive JSON
mode unless --yes is supplied.

In `@crates/soar-cli/src/list.rs`:
- Around line 413-418: Update the count-handling path in the function containing
the event_stream_enabled block so JSON mode emits a structured count response
before returning. Ensure --json info --count writes the count to stdout rather
than only logging via info!, while preserving the existing Listing output for
non-count requests.

In `@crates/soar-cli/src/main.rs`:
- Around line 105-107: Update the command classification around
ListInstalledPackages and its list_installed_packages count branch so JSON mode
emits the package count as a JSON response to stdout before returning, rather
than only logging it. Preserve the existing non-JSON logging behavior and ensure
info --count produces output when --json is enabled.

In `@crates/soar-cli/src/plugin_manifest.rs`:
- Around line 31-35: Update the profile_options construction to serialize each
profile using the TOML serializer rather than format!("{profile:?}"), ensuring
default_profile values use valid TOML string escaping while preserving the
comma-separated option format.

In `@crates/soar-db/src/connection.rs`:
- Around line 34-38: Update the WAL setup near the journal-mode trace to query
the value returned by PRAGMA journal_mode = WAL instead of using execute(conn)
alone. Continue or validate the returned result until the effective mode is wal,
and explicitly handle documented non-wal cases such as memory databases; only
emit the success trace after this validation, while preserving ConnectionError
mapping for failures.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ab45ab38-6b1d-4573-a894-87c0a0b9766a

📥 Commits

Reviewing files that changed from the base of the PR and between ae017bd and 1a4e1ae.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • crates/soar-cli/src/apply.rs
  • crates/soar-cli/src/cli.rs
  • crates/soar-cli/src/json_output.rs
  • crates/soar-cli/src/list.rs
  • crates/soar-cli/src/logging.rs
  • crates/soar-cli/src/main.rs
  • crates/soar-cli/src/plugin_manifest.rs
  • crates/soar-cli/src/repo.rs
  • crates/soar-cli/src/update.rs
  • crates/soar-cli/src/utils.rs
  • crates/soar-db/src/connection.rs
  • crates/soar-events/Cargo.toml
  • crates/soar-events/src/event.rs
  • crates/soar-events/src/sink.rs
  • crates/soar-operations/src/apply.rs

Comment on lines +40 to +42
if dry_run && event_stream_enabled() {
json_output::emit(&ApplyDiffJson::new(&diff));
return Ok(());

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Include pending version updates in the JSON diff.

Line 41 emits ApplyDiffJson, but that model omits pending_version_updates. execute_apply changes those declared versions before package operations. A frontend can therefore report a no-op when apply will modify packages.toml.

Add the pending version updates to ApplyDiffJson and the emitted schema.

🤖 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/soar-cli/src/apply.rs` around lines 40 - 42, Update ApplyDiffJson and
its emitted schema to include pending_version_updates, then pass the updates
produced by execute_apply into ApplyDiffJson::new in the dry_run and
event_stream_enabled path. Ensure the JSON diff reflects declared version
changes made before package operations.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep stdout valid NDJSON during confirmation.

When JSON mode runs without --yes, the normal apply path reaches the direct print! confirmation prompt. That plain-text prompt corrupts the event stream on stdout.

Write interactive prompts to stderr, or reject interactive JSON mode and require --yes.

🤖 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/soar-cli/src/apply.rs` around lines 40 - 42, Update the confirmation
flow around ApplyDiffJson::new so JSON mode never writes the interactive prompt
to stdout; route the prompt to stderr while preserving valid NDJSON event
output, or reject interactive JSON mode unless --yes is supplied.

Comment on lines +413 to +418
if event_stream_enabled() {
let items: Vec<InstalledJson> = result.packages.iter().map(Into::into).collect();
json_output::emit(&Listing::new(items, result.total_count));
return Ok(());
}

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Emit JSON for installed-package counts.

When count is set, the function returns before Line 413. In JSON mode, the info! output goes to stderr, so soar --json info --count leaves stdout empty. Emit a structured count response before that return.

🤖 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/soar-cli/src/list.rs` around lines 413 - 418, Update the
count-handling path in the function containing the event_stream_enabled block so
JSON mode emits a structured count response before returning. Ensure --json info
--count writes the count to stdout rather than only logging via info!, while
preserving the existing Listing output for non-count requests.

Comment on lines +105 to +107
cli::Commands::ListPackages { .. }
| cli::Commands::ListInstalledPackages { .. }
| cli::Commands::Search { .. }

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Emit JSON for info --count.

ListInstalledPackages { count: true } is classified as a document command. However, list_installed_packages logs the count and returns before its JSON branch. In JSON mode, logging writes to stderr. Therefore, soar --json info --count leaves stdout empty.

Add a JSON count response in the count branch.

Proposed fix
// crates/soar-cli/src/list.rs
 if count {
     let count = list::count_installed(ctx, repo_name.as_deref())?;
-    info!("{}", count);
+    if event_stream_enabled() {
+        json_output::emit(&serde_json::json!({ "count": count }));
+    } else {
+        info!("{}", count);
+    }
     return Ok(());
 }
🤖 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/soar-cli/src/main.rs` around lines 105 - 107, Update the command
classification around ListInstalledPackages and its list_installed_packages
count branch so JSON mode emits the package count as a JSON response to stdout
before returning, rather than only logging it. Preserve the existing non-JSON
logging behavior and ensure info --count produces output when --json is enabled.

Comment thread crates/soar-cli/src/plugin_manifest.rs Outdated
Comment on lines +31 to +35
let profile_options = profiles
.iter()
.map(|profile| format!("{profile:?}"))
.collect::<Vec<_>>()
.join(", ");

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import tomllib

candidate = r'options = ["\u{1}"]'
try:
    tomllib.loads(candidate)
except tomllib.TOMLDecodeError:
    print("Confirmed: Rust-style \\u{1} is not valid TOML string syntax.")
else:
    raise SystemExit("Expected TOML parsing to fail")
PY

Repository: pkgforge/soar

Length of output: 271


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Python version:\n'
python3 --version || true

printf '\nLocate plugin_manifest.rs and manifest-related files:\n'
fd -a 'plugin_manifest|manifest|Cargo\.toml$' . | sed 's#^\./##' | head -100

printf '\nInspect plugin_manifest.rs:\n'
if [ -f crates/soar-cli/src/plugin_manifest.rs ]; then
  wc -l crates/soar-cli/src/plugin_manifest.rs
  cat -n crates/soar-cli/src/plugin_manifest.rs
fi

printf '\nSearch profile type and manifest generation references:\n'
rg -n "plugin_manifest|Profile|profiles|format!\\(\"\\{.*\\:?\\}\"|to_string\\(|serde_json|toml" crates -g '*.rs' | head -200

Repository: pkgforge/soar

Length of output: 31451


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Rust compiler versions available:\n'
command -v rustc || true
rustc --version 2>/dev/null || true

printf '\nManifest for plugin_manifest caller/tests:\n'
rg -n "plugin_manifest::manifest|manifest\\(&|plugin.*manifest|plugin-manifest" crates/soar-cli crates -g '*.rs' | head -200

printf '\nCargo dependencies around soar-cli and shared crates:\n'
wc -l crates/soar-cli/Cargo.toml crates/Cargo.toml Cargo.toml
sed -n '1,220p' crates/soar-cli/Cargo.toml
sed -n '1,220p' Cargo.toml

printf '\nInspect plugin-manifest command references only:\n'
rg -n "plugin-manifest|plugin_manifest|manifest\\(" crates/soar-cli/src crates/soar-config/src crates/soar-utils/src crates -g '*.rs' | head -250

Repository: pkgforge/soar

Length of output: 1854


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Inspect soar-cli command construction and dependency list:\n'
sed -n '420,455p' crates/soar-cli/src/main.rs
sed -n '1,90p' crates/soar-cli/Cargo.toml

printf '\nGenerate and parse Rust Debug vs TOML strings with rustc/tools if available:\n'
cat >/tmp/debug_parse.rs <<'RS'
use std::process::Command;

fn main() {
    let profiles = ["default", "\u{1}", "\0", "newline\n"];
    for profile in profiles {
        let debug = format!("{profile:?}");
        let candidate = format!("options = [{}]", debug);
        let output = Command::new("cargo")
            .args(["-q", "script", "--"])
            .arg(format!("#!/usr/bin/env rustc\nfn main() -> Result<(), Box<dyn std::error::Error>> {{\nlet input = `#candidate`;\nlet parsed: Vec<String> = toml::from_str(&input)?;\nprintln!(\"{:?}\", parsed);\nOk(())\n}}"))
            .output();
        match output {
            Ok(out) => println!(
                "profile={:?} debug={:?}\rcandidate={}status={}stdout={}stderr={}",
                profile, debug, debug, out.status, String::from_utf8_loss(&out.stdout), String::from_utf8_loss(&out.stderr)
            ),
            Err(e) => println!("profile={:?} debug={:?} cargo_error={e}", profile, debug),
        }
    }
}
R S

Repository: pkgforge/soar

Length of output: 3199


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Find profile config type and parsing/validation code:\n'
rg -n "struct .*Profile|profiles:|\\.profile|profile keys|profile:|toml::Value|toml::Value::String|profile" crates -g '*.rs' | head -250

printf '\nInspect soar-config packages/profile type definitions:\n'
fd -a '.*\.rs$' crates/soar-config/src | sort
for f in $(fd -a '.*\.rs$' crates/soar-config/src | sort); do
  echo "== $f =="
  rg -n "profile|Profile|toml::Value::String|toml::Value::Array|default_profile|options" "$f" || true
done

printf '\nInspect main profile config parsing around PluginManifest if present:\n'
sed -n '1,140p' crates/soar-cli/src/main.rs

Repository: pkgforge/soar

Length of output: 23414


🌐 Web query:

TOML 1.0.1 basic string escape unicode escapes backslash u{} invalid

💡 Result:

In TOML 1.0.1, the sequence \u{} is invalid [1][2][3]. According to the TOML 1.0.0 and 1.0.1 specifications, basic strings support only two specific forms for Unicode escape sequences [1][2][4]: \uXXXX, where XXXX represents exactly four hexadecimal digits [1][2][4]. \UXXXXXXXX, where XXXXXXXX represents exactly eight hexadecimal digits [1][2][4]. Any escape sequence that does not match the explicitly defined list (including \u{}, \xHH, or other unlisted variations) is considered reserved, and the TOML specification mandates that parsers should produce an error if they are encountered [1][2][5]. While newer versions of the TOML specification (such as v1.1.0) have introduced support for \xHH, \uHHHH, and \UHHHHHHHH, the \u{} syntax—which uses curly braces—is not a valid escape sequence in any standard version of TOML [6][7][8].

Citations:


🌐 Web query:

Rust std fmt Debug Display escape non printable character Debug \u0000 \u{1}

💡 Result:

In Rust, the Debug trait is designed for programmer-facing output, where non-printable characters are escaped to ensure the output remains readable and safe for terminals [1][2]. When printing characters or strings containing non-printable characters, such as \u0000 or \u{1}, the Debug representation automatically escapes them [2]. For characters, the standard library provides specialized types for escaping: 1. Escape Sequences: When you use the {:?} format specifier (which calls the Debug implementation) on a char or string, Rust internally uses mechanisms similar to std::char::EscapeDebug to format the output [3][4][1]. 2. Formatting of \u0000 and \u{1}: - The null character '\0' (\u0000) is typically formatted as '\0'. - Other control characters (like \u{1}) are generally represented as Unicode escape sequences in the form \u{N}, where N is the hexadecimal representation of the Unicode scalar value [4][2]. If you need specific control over how characters are escaped, you can use the adapter methods provided in the standard library: - str::escape_debug: Returns an iterator that escapes non-printable characters in a way suitable for debug output [4][5]. - char::escape_unicode: Returns an iterator that always uses the \u{...} format for any character [3]. - char::escape_default: Escapes characters using traditional C-style escape sequences (e.g., \t, \n, \r, \xHH) where applicable [3]. These methods implement the Display trait, allowing them to be easily used in formatting strings [3]. While the exact set of characters considered non-printable may evolve in future Rust versions, the goal of Debug is to remain consistent with established conventions for readable debugging output [6][7].

Citations:


Use TOML string escaping for default_profile options.

format!("{profile:?}") can emit Rust debug escapes such as \u{1} and \0; TOML strings require \uXXXX/\UXXXXXXXX or C-style escapes. Use a TOML serializer, not Debug formatting, for these values.

🤖 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/soar-cli/src/plugin_manifest.rs` around lines 31 - 35, Update the
profile_options construction to serialize each profile using the TOML serializer
rather than format!("{profile:?}"), ensuring default_profile values use valid
TOML string escaping while preserving the comma-separated option format.

Comment on lines +34 to +38
sql_query("PRAGMA journal_mode = WAL;")
.execute(conn)
.map_err(|e| ConnectionError::BadConnection(e.to_string()))?;

trace!("busy timeout and WAL journal mode set");

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

for file in Cargo.toml Cargo.lock; do
  if [[ -f "$file" ]]; then
    rg -n -C 3 'diesel\s*=' "$file" || true
  fi
done

rg -n -C 8 \
  'PRAGMA (journal_mode|busy_timeout)|fn prepare|":memory:"|open_readonly|open_without_migrations|open_metadata' \
  crates/soar-db --glob '*.rs' || true

Repository: pkgforge/soar

Length of output: 7131


🌐 Web query:

SQLite PRAGMA journal_mode WAL documentation previous mode returned delete memory

💡 Result:

When you execute the PRAGMA journal_mode command in SQLite, the pragma returns a string representing the new journal mode upon success [1][2]. If the requested change cannot be completed—for instance, if the Virtual File System (VFS) does not support the required shared-memory primitives—the journal mode remains unchanged, and the pragma returns the previous, existing journal mode (e.g., "delete") [1][3]. Regarding the behavior of different modes: 1. Persistence of WAL: Unlike other journal modes, PRAGMA journal_mode=WAL is persistent [1][3]. Once set, the database remains in WAL mode across multiple connections and after closing and reopening the file [1][2]. 2. Default Behavior: Other journal modes, such as TRUNCATE, PERSIST, or MEMORY, are not persistent [1][3]. If a database is set to one of these modes and then closed, it will revert to the default rollback mode—typically DELETE—upon being reopened [1][3]. 3. Switching from WAL: If you explicitly switch a database from WAL mode to another mode (like MEMORY), the WAL mode is disabled, and the file is marked as "not WAL" [4]. Consequently, when the database is reopened, it defaults to the standard DELETE mode [4]. To use a specific non-WAL journal mode (e.g., MEMORY) persistently, you must set that mode each time the database connection is opened [3][4].

Citations:


🌐 Web query:

Diesel 2 SQLite sql_query execute returns Result<usize QueryResult does not run migrations documentation

💡 Result:

The diesel::sql_query function is used for executing raw SQL queries in Diesel, not for managing database migrations [1][2]. Regarding your query: 1. sql_query returns Result for execute: The diesel::sql_query function constructs a query that implements RunQueryDsl [3][4]. When you call the .execute() method on a SqlQuery object, it returns a QueryResult<usize>, where usize represents the number of rows affected by the statement [5][6][7]. This is standard behavior for commands that modify the database (like INSERT, UPDATE, or DELETE) [3][8]. 2. sql_query does not run migrations: You are correct; sql_query does not run migrations [1][2]. Migrations in Diesel are a separate mechanism intended for managing schema changes [9][10]. To run migrations programmatically, you should use the diesel_migrations crate [9]. Specifically, you can embed migrations into your application using the embed_migrations! macro and then execute them by calling run_pending_migrations on your database connection, provided it implements the MigrationHarness trait [11][12][13]. In summary, use sql_query only for executing arbitrary SQL against your database, and use diesel_migrations for schema management [1][9].

Citations:


🌐 Web query:

SQLite journal_mode PRAGMA loadable extension VFS shared memory DELETE MEMORY unsupported

💡 Result:

In SQLite, journal modes and VFS requirements for features like shared memory (WAL mode) and in-memory storage are distinct but interrelated. Regarding PRAGMA journal_mode: - The MEMORY journal mode stores the rollback journal in volatile RAM instead of on disk [1][2][3]. While this improves performance by reducing disk I/O, it poses a risk of database corruption if the application crashes during a transaction [1][3]. - For in-memory databases (e.g., ":memory:" or "mode=memory"), the journal_mode is restricted to either MEMORY or OFF; any attempt to set it to WAL or other modes will be ignored [1][2]. Regarding WAL mode and Shared Memory: - Write-Ahead Logging (WAL) generally requires the underlying Virtual File System (VFS) to support "version 2" shared-memory primitives (xShmMap, xShmLock, etc.) [4][5]. - If a VFS does not support these shared-memory methods, attempts to enable WAL mode will fail [4][5]. - A known exception exists: if the locking_mode is set to EXCLUSIVE before the first database access, SQLite can operate in WAL mode without shared memory [4][5]. However, this mode is strictly limited to a single database connection and prevents concurrent access [4][6][5]. Regarding Loadable Extensions and VFS: - Loadable extensions (which typically require dynamic library loading functions like xDlOpen) and VFS implementations are independent components [7]. Some custom or minimal VFS implementations may choose not to support loading extensions or shared memory, which will result in errors if an application attempts to use those features with that specific VFS [7][6]. - If you require WAL mode for an in-memory database or a custom VFS that lacks shared-memory support, you must either implement the necessary shared-memory methods (xShm*) within your custom VFS or operate in EXCLUSIVE locking mode if single-connection access is sufficient [8][6][9].

Citations:


🌐 Web query:

"PRAGMA journal_mode" SQLite documentation "memory databases" "delete" "memory"

💡 Result:

In SQLite, the PRAGMA journal_mode command is used to configure how transactions are handled [1][2]. When working with in-memory databases, there are specific constraints and behaviors regarding journal modes: 1. Supported Modes for In-Memory Databases: For an in-memory database (e.g., created via:memory: or a memory-mode URI), only MEMORY or OFF journaling modes are allowed [1][3]. Attempting to set other modes (such as DELETE, TRUNCATE, PERSIST, or WAL) on an in-memory database is ignored, and the database will remain in its current mode (typically MEMORY) [1][3]. 2. Relationship with DELETE Mode: DELETE is the default journal mode for file-based databases, where the rollback journal file is deleted from the disk at the conclusion of each transaction [4][5]. This mode is not applicable to in-memory databases because there is no persistent file system storage for the journal [1][4]. 3. MEMORY Journal Mode: In this mode, the rollback journal is stored in volatile RAM rather than on disk [1][4]. While this significantly improves performance by avoiding disk I/O, it compromises safety; if the application crashes during a transaction, the database will likely become corrupt [1][4]. 4. WAL Mode Limitations: In-memory databases do not support Write-Ahead Logging (WAL) mode because WAL requires persistent shared-memory files (.wal and.shm) on disk [3]. If you attempt to set PRAGMA journal_mode = WAL on an in-memory database, the command will silently fail or be ignored, and the connection will revert to or remain in MEMORY mode [3]. In summary, you cannot use the DELETE journal mode or WAL mode with an in-memory database. If performance is the goal, MEMORY mode is the standard approach, but it must be used with the understanding that it lacks the durability guarantees provided by file-based journaling modes [1][4].

Citations:


Don’t report WAL mode success until the effective mode is checked.

PRAGMA journal_mode = WAL returns the current effective mode after the statement completes; keep reading until wal is returned or handle the documented non-wal cases explicitly (for example, memory-mode databases that cannot use WAL). The current execute(conn) path only checks whether the SQL statement ran.

🤖 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/soar-db/src/connection.rs` around lines 34 - 38, Update the WAL setup
near the journal-mode trace to query the value returned by PRAGMA journal_mode =
WAL instead of using execute(conn) alone. Continue or validate the returned
result until the effective mode is wal, and explicitly handle documented non-wal
cases such as memory databases; only emit the success trace after this
validation, while preserving ConnectionError mapping for failures.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 9, 2026

Copy link
Copy Markdown

Deploying soar-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: a226f91
Status: ✅  Deploy successful!
Preview URL: https://93bca6d4.soar-docs.pages.dev
Branch Preview URL: https://json-output.soar-docs.pages.dev

View logs

@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: 1

🧹 Nitpick comments (1)
crates/soar-operations/src/search.rs (1)

353-356: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Batch maintainer lookups before iterating over packages.

Name-only queries can return multiple package rows across repositories and versions. This loop performs one repository lookup for every row. That creates an N+1 SQLite path and repeats connection and query overhead. Fetch maintainers with the package query or batch package IDs per repository, then attach the results by package ID. (github.com/ghraw)

🤖 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/soar-operations/src/search.rs` around lines 353 - 356, Replace the
per-row MetadataRepository::get_maintainers call inside the package iteration
with batched maintainer retrieval, grouping package IDs by repository or
extending the package query to return maintainers. Attach each result by
package.id while preserving repository/version-specific rows, and ensure
metadata_mgr uses one lookup/query per batch rather than one connection query
per package.
🤖 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/soar-operations/src/search.rs`:
- Around line 354-358: Update the maintainer lookup in query_package around
MetadataRepository::get_maintainers so Err results from metadata_mgr.query_repo
are propagated with ?, rather than ignored, while preserving the existing
successful maintainer assignment.

---

Nitpick comments:
In `@crates/soar-operations/src/search.rs`:
- Around line 353-356: Replace the per-row MetadataRepository::get_maintainers
call inside the package iteration with batched maintainer retrieval, grouping
package IDs by repository or extending the package query to return maintainers.
Attach each result by package.id while preserving repository/version-specific
rows, and ensure metadata_mgr uses one lookup/query per batch rather than one
connection query per package.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f5517e9-82bc-4f7e-a68f-5f1df38fbc60

📥 Commits

Reviewing files that changed from the base of the PR and between ef7ac1d and 9be1e21.

📒 Files selected for processing (3)
  • crates/soar-cli/src/json_output.rs
  • crates/soar-cli/src/plugin_manifest.rs
  • crates/soar-operations/src/search.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/soar-cli/src/plugin_manifest.rs
  • crates/soar-cli/src/json_output.rs

Comment thread crates/soar-operations/src/search.rs Outdated
Comment on lines +354 to +358
let found = metadata_mgr.query_repo(&package.repo_name.clone(), |conn| {
MetadataRepository::get_maintainers(conn, package.id as i32)
});

if let Ok(Some(maintainers)) = found {

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Propagate maintainer lookup failures.

This pattern ignores Err results. query_package can therefore return a successful response with package.maintainers omitted after a database failure. Frontends cannot distinguish missing maintainer data from a failed lookup. Propagate the error with ?, or expose an explicit partial-result state if enrichment must remain best-effort. (github.com/ghraw)

Proposed fix
-        let found = metadata_mgr.query_repo(&package.repo_name.clone(), |conn| {
+        let found = metadata_mgr.query_repo(&package.repo_name, |conn| {
             MetadataRepository::get_maintainers(conn, package.id as i32)
-        });
+        })?;

-        if let Ok(Some(maintainers)) = found {
+        if let Some(maintainers) = found {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let found = metadata_mgr.query_repo(&package.repo_name.clone(), |conn| {
MetadataRepository::get_maintainers(conn, package.id as i32)
});
if let Ok(Some(maintainers)) = found {
let found = metadata_mgr.query_repo(&package.repo_name, |conn| {
MetadataRepository::get_maintainers(conn, package.id as i32)
})?;
if let Some(maintainers) = found {
🤖 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/soar-operations/src/search.rs` around lines 354 - 358, Update the
maintainer lookup in query_package around MetadataRepository::get_maintainers so
Err results from metadata_mgr.query_repo are propagated with ?, rather than
ignored, while preserving the existing successful maintainer assignment.

@QaidVoid
QaidVoid merged commit 1ea7f51 into main Aug 10, 2026
9 of 10 checks passed
@QaidVoid
QaidVoid deleted the json-output branch August 10, 2026 11:01
@QaidVoid QaidVoid mentioned this pull request Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants