Bugfix/git - #3
Conversation
When cmd_fetch downloads new projects, it now creates the git repos in config.git_base/git-versions/ instead of directly in config.git_base. This prevents conflicts with existing files and git repos that may already be in config.git_base (which is set to the project root directory when running from /mnt/data1/time-2026/05-may/07/arist). Each project still gets its own git repo, now properly isolated in the git-versions subdirectory. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
When cmd_fetch downloads new projects, it now creates the git repos in config.git_base/git-versions/ instead of directly in config.git_base. Additionally: - Added .gitignore to exclude .tar.gz files from git repos - Skip nested _aristotle directories during file copy to avoid conflicts - Each project still gets its own git repo, now properly isolated in the git-versions subdirectory with proper ignores for tarballs. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
📝 WalkthroughWalkthroughGit versioning now uses a dedicated ChangesGit versioning updates
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant cmd_fetch
participant cmd_version
participant commit_project
participant Git
cmd_fetch->>cmd_version: invoke with git-versions root
cmd_version->>commit_project: synchronize and commit project
commit_project->>Git: stage and commit with metadata date
Git-->>commit_project: return short HEAD hash
commit_project-->>cmd_version: return short hash or skip
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@src/version.rs`:
- Around line 77-79: Update commit_project so git init, add, commit, and hash
lookup failures are propagated as errors instead of returning Ok(None) or being
treated as skips; use the existing command-handling logic around commit_project
and cmd_version. Ensure cmd_version aggregates failed repositories and returns
an error when any repository operation fails, rather than reporting them as
skipped or committed.
- Around line 98-104: Before staging in the version-generation flow, remove
existing matching tarballs and nested `_aristotle` project files/directories
from `repo`, not just during copying. Update the `.gitignore` handling in the
relevant `src/version.rs` logic to merge and persist the required ignore rules
even when the file already exists, then ensure cleanup occurs before `git add
-A`.
- Around line 29-39: The fallback parsing in the git date logic can panic on
short extracted_at values. In the parse_from_rfc3339 error branch within the
git_date initialization, use extracted_at.get(..19) and only call
NaiveDateTime::parse_from_str when that slice exists; otherwise return
extracted_at.to_string() as the fallback.
🪄 Autofix (Beta)
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
Run ID: 378164ae-64c8-4798-bfe4-4129ed26a76b
📒 Files selected for processing (2)
src/fetch.rssrc/version.rs
| let git_date = if extracted_at != "unknown" { | ||
| match chrono::DateTime::parse_from_rfc3339(extracted_at) { | ||
| Ok(dt) => format!("@{}", dt.timestamp()), | ||
| Err(_) => { | ||
| // Try parsing as bare ISO 8601 (no timezone) | ||
| match chrono::NaiveDateTime::parse_from_str( | ||
| &extracted_at[..19], | ||
| "%Y-%m-%dT%H:%M:%S", | ||
| ) { | ||
| Ok(ndt) => format!("@{}", ndt.and_utc().timestamp()), | ||
| Err(_) => extracted_at.to_string(), |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
ast-grep outline src/version.rs --match commit_project --view expanded
rg -n -C3 'extracted_at|parse_from_rfc3339|NaiveDateTime' src -g '*.rs'
rg -n -C3 'extracted_at|malformed.*date|short.*date' . -g '*test*' -g '*.rs'Repository: meta-introspector/aristotle-cli-rs
Length of output: 12338
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the versioning flow and the metadata writers/consumers around extracted_at.
sed -n '1,220p' src/version.rs
printf '\n---\n'
sed -n '1,220p' src/fetch.rs
printf '\n---\n'
sed -n '1450,1505p' src/main.rs
printf '\n---\n'
sed -n '1,120p' src/replay.rs
# Look for any other writers or parsers of extracted_at that might emit non-RFC3339 values.
rg -n 'extracted_at|to_rfc3339|parse_from_rfc3339|NaiveDateTime|parse_from_str\(' src -g '*.rs'Repository: meta-introspector/aristotle-cli-rs
Length of output: 24471
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the surrounding versioning control flow to see whether a panic aborts the whole run.
sed -n '220,360p' src/version.rs
rg -n 'commit_project\(|cmd_version\(' src/version.rs src/main.rs -g '*.rs'Repository: meta-introspector/aristotle-cli-rs
Length of output: 4228
Handle short or malformed extracted_at values
&extracted_at[..19] will panic if the RFC3339 parse fails and the string is shorter than 19 bytes, aborting the whole versioning run on a bad metadata record. Use a safe slice like get(..19) before parsing the fallback format.
🤖 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 `@src/version.rs` around lines 29 - 39, The fallback parsing in the git date
logic can panic on short extracted_at values. In the parse_from_rfc3339 error
branch within the git_date initialization, use extracted_at.get(..19) and only
call NaiveDateTime::parse_from_str when that slice exists; otherwise return
extracted_at.to_string() as the fallback.
| if !out.status.success() { | ||
| warn!(project = %project_name, "git init failed: {}", String::from_utf8_lossy(&out.stderr)); | ||
| return Ok(false); | ||
| return Ok(None); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
ast-grep outline src/version.rs --match 'commit_project|cmd_version' --view expanded
rg -n -C4 'Ok\(None\)|Ok\(Some|rev-parse|Committed|Skipped|Failed' src/version.rs src/fetch.rsRepository: meta-introspector/aristotle-cli-rs
Length of output: 5284
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- src/fetch.rs (around versioning call) ---'
sed -n '180,250p' src/fetch.rs | cat -n
echo
echo '--- src/version.rs (commit_project and cmd_version) ---'
sed -n '1,330p' src/version.rs | cat -nRepository: meta-introspector/aristotle-cli-rs
Length of output: 15737
Keep git command failures as errors, not skips. commit_project turns init/add/commit failures into Ok(None) and hash lookup failures into ?, so cmd_version can finish with failed repos counted as skipped or committed. Propagate these command failures and surface an aggregate failure instead.
🤖 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 `@src/version.rs` around lines 77 - 79, Update commit_project so git init, add,
commit, and hash lookup failures are propagated as errors instead of returning
Ok(None) or being treated as skips; use the existing command-handling logic
around commit_project and cmd_version. Ensure cmd_version aggregates failed
repositories and returns an error when any repository operation fails, rather
than reporting them as skipped or committed.
| // Write .gitignore to exclude tarballs and nested projects | ||
| let gitignore_path = repo.join(".gitignore"); | ||
| if !gitignore_path.exists() { | ||
| let gitignore_content = "*_aristotle.tar.gz\n*.tar.gz\n"; | ||
| fs::write(&gitignore_path, gitignore_content)?; | ||
| changed = true; | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
ast-grep outline src/version.rs --match commit_project --view expanded
rg -n -C4 'gitignore|tar\.gz|_aristotle|remove_dir|remove_file|git.*rm' src/version.rsRepository: meta-introspector/aristotle-cli-rs
Length of output: 2971
🏁 Script executed:
#!/bin/bash
sed -n '1,220p' src/version.rsRepository: meta-introspector/aristotle-cli-rs
Length of output: 7904
Remove stale generated content before staging. src/version.rs:98-130 only skips tarballs and nested _aristotle paths on copy; anything already in repo/ stays tracked, and an existing .gitignore is never updated. Delete matching destination files/dirs and merge these ignore rules before git add -A.
🤖 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 `@src/version.rs` around lines 98 - 104, Before staging in the
version-generation flow, remove existing matching tarballs and nested
`_aristotle` project files/directories from `repo`, not just during copying.
Update the `.gitignore` handling in the relevant `src/version.rs` logic to merge
and persist the required ignore rules even when the file already exists, then
ensure cleanup occurs before `git add -A`.
|
SummaryThis run covers core command-line workflows for fetching results, versioning them into git repositories, and re-running syncs with no changes, alongside edge cases around path routing and timestamp handling. Healthy baseline behavior is present on standard flows, but robustness issues appear when metadata or configuration inputs are malformed or unusual. Merge with caution — this PR introduces attributable reliability issues, including a medium-severity crash path in timestamp handling plus minor routing/date-handling defects that can misroute output or turn commit problems into silent skips. There is also an unrelated high-severity sync integrity issue in the broader codebase, but it is not attributable to this PR. Tests run by ItoAdditional Findings DetailsThese findings are unrelated to the current changes but were observed during testing. 🟠 git add failure leaves mutated but uncommitted repo state
Evidence Package⚪ Fetch and pipeline split repo roots
Evidence PackageTip Reply with @itoqa to send us feedback on this test run. |
| if downloaded > 0 { | ||
| println!("\n Versioning new results..."); | ||
| crate::version::cmd_version(Some(config.results_dir.clone()), Some(config.git_base.clone()))?; | ||
| crate::version::cmd_version(Some(config.results_dir.clone()), Some(config.git_base.join("git-versions")))?; |
There was a problem hiding this comment.
Fetch double-nests git versions path
What failed: Expected no extra path segment when git_base already targets git-versions, but fetch routed version output to a double-nested git-versions/git-versions directory.
Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
- Severity: Minor
- Impact: Some users may hit a routing session that stops responding after completion and need to restart the app/session to continue.
- Steps to Reproduce:
- Configure
git_baseto a path whose final segment is alreadygit-versions. - Run
cargo run -- fetchso fetch triggers versioning after downloads. - Inspect the printed git base and created repo path; they appear under
.../git-versions/git-versions/<project_id>instead of.../git-versions/<project_id>.
- Configure
- Stub / mock content: The test run used a local mock Aristotle API endpoint and fixture directories to drive fetch/versioning in a non-production environment; no bypass changed the git path-routing logic under test.
- Code Analysis: I inspected
src/fetch.rsandsrc/version.rs. Fetch now callscmd_version(..., Some(config.git_base.join("git-versions")))at line 216, whilecmd_versionuses the provided output directory directly asgit_base(src/version.rslines 229 and 275). There is no guard in fetch to avoid appending whenconfig.git_basealready ends withgit-versions, so this path construction deterministically produces double nesting for the ROUTING-2 configuration. The smallest practical fix is to normalize in fetch before callingcmd_version(for example, reuseconfig.git_baseas-is when its last segment isgit-versions). - Why this is likely a bug: This is a production code path mismatch, not a harness artifact: the constructed path is wrong by string/path composition alone and reproduces with a normal valid config value. The logic can strand versioned repos under an unintended root even when fetch/download succeeds.
Relevant code
src/fetch.rs:216
crate::version::cmd_version(Some(config.results_dir.clone()), Some(config.git_base.join("git-versions")))?;src/version.rs:229
let git_base = output_dir.unwrap_or_else(|| results_dir.join("git-versions"));src/version.rs:275
let repo_dir = git_base.join(repo_name);Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.
**Minor severity — Fetch double-nests git versions path**
**What failed:** Expected no extra path segment when `git_base` already targets `git-versions`, but fetch routed version output to a double-nested `git-versions/git-versions` directory.
- **Impact:** Some users may hit a routing session that stops responding after completion and need to restart the app/session to continue.
- **Steps to reproduce:**
1. Configure `git_base` to a path whose final segment is already `git-versions`.
2. Run `cargo run -- fetch` so fetch triggers versioning after downloads.
3. Inspect the printed git base and created repo path; they appear under `.../git-versions/git-versions/<project_id>` instead of `.../git-versions/<project_id>`.
- **Stub / mock content:** The test run used a local mock Aristotle API endpoint and fixture directories to drive fetch/versioning in a non-production environment; no bypass changed the git path-routing logic under test.
- **Code analysis:** I inspected `src/fetch.rs` and `src/version.rs`. Fetch now calls `cmd_version(..., Some(config.git_base.join("git-versions")))` at line 216, while `cmd_version` uses the provided output directory directly as `git_base` (`src/version.rs` lines 229 and 275). There is no guard in fetch to avoid appending when `config.git_base` already ends with `git-versions`, so this path construction deterministically produces double nesting for the ROUTING-2 configuration. The smallest practical fix is to normalize in fetch before calling `cmd_version` (for example, reuse `config.git_base` as-is when its last segment is `git-versions`).
- **Why this is likely a bug:** This is a production code path mismatch, not a harness artifact: the constructed path is wrong by string/path composition alone and reproduces with a normal valid config value. The logic can strand versioned repos under an unintended root even when fetch/download succeeds.
**Relevant code:**
`src/fetch.rs:216`
~~~rust
crate::version::cmd_version(Some(config.results_dir.clone()), Some(config.git_base.join("git-versions")))?;
~~~
`src/version.rs:229`
~~~rust
let git_base = output_dir.unwrap_or_else(|| results_dir.join("git-versions"));
~~~
`src/version.rs:275`
~~~rust
let repo_dir = git_base.join(repo_name);
~~~| Ok(dt) => format!("@{}", dt.timestamp()), | ||
| Err(_) => { | ||
| // Try parsing as bare ISO 8601 (no timezone) | ||
| match chrono::NaiveDateTime::parse_from_str( |
There was a problem hiding this comment.
Short extracted_at string crashes version command
What failed: Expected behavior: invalid or short timestamps should be handled without crashing, allowing other projects to continue. Actual behavior: the process panics while slicing extracted_at and aborts the entire run.
Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
- Severity: Medium
- Impact: The version command can crash and stop processing remaining projects when commit timestamp metadata is shorter than expected, blocking version output for affected runs.
- Steps to Reproduce:
- Create an Aristotle project metadata file with
extracted_atset to2026-07-10T12:34:5. - Run
aristotle-manager version --results-dir <results> --output-dir <git-output>. - Observe panic at
src/version.rs:35:34withbyte index 19 is out of bounds, and process exit 101.
- Create an Aristotle project metadata file with
- Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
- Code Analysis: In
src/version.rs, the PR-addedgit_datefallback slicesextracted_atusing&extracted_at[..19]before parsing%Y-%m-%dT%H:%M:%S. Rust string slicing panics on out-of-bounds byte indices, so an 18-byte value like2026-07-10T12:34:5crashes at runtime. The panic occurs insidecommit_projectbefore error-return paths and is not recoverable through the surroundingResulthandling incmd_version. Smallest practical fix: guard the slice length (or avoid slicing entirely) and treat unparsable values as a non-fatal per-project skip/error path. - Why this is likely a bug: The failure is directly explained by production code semantics: out-of-bounds Rust string slicing panics deterministically for short inputs. No test harness stubs or environment manipulation are needed to trigger it, so this is a real application defect.
Relevant code
src/version.rs:34-37
match chrono::NaiveDateTime::parse_from_str(
&extracted_at[..19],
"%Y-%m-%dT%H:%M:%S",
) {src/version.rs:278-291
match commit_project(&repo_dir, project) {
Ok(Some(rev)) => { ... }
Ok(None) => { ... }
Err(e) => {
skipped += 1;
warn!(project = %name, error = %e, "Failed");
}
}Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.
**Medium severity — Short extracted_at string crashes version command**
**What failed:** Expected behavior: invalid or short timestamps should be handled without crashing, allowing other projects to continue. Actual behavior: the process panics while slicing `extracted_at` and aborts the entire run.
- **Impact:** The version command can crash and stop processing remaining projects when commit timestamp metadata is shorter than expected, blocking version output for affected runs.
- **Steps to reproduce:**
1. Create an Aristotle project metadata file with `extracted_at` set to `2026-07-10T12:34:5`.
2. Run `aristotle-manager version --results-dir <results> --output-dir <git-output>`.
3. Observe panic at `src/version.rs:35:34` with `byte index 19 is out of bounds`, and process exit 101.
- **Stub / mock content:** No stubs, mocks, or bypasses were applied for this test in the recorded run.
- **Code analysis:** In `src/version.rs`, the PR-added `git_date` fallback slices `extracted_at` using `&extracted_at[..19]` before parsing `%Y-%m-%dT%H:%M:%S`. Rust string slicing panics on out-of-bounds byte indices, so an 18-byte value like `2026-07-10T12:34:5` crashes at runtime. The panic occurs inside `commit_project` before error-return paths and is not recoverable through the surrounding `Result` handling in `cmd_version`. Smallest practical fix: guard the slice length (or avoid slicing entirely) and treat unparsable values as a non-fatal per-project skip/error path.
- **Why this is likely a bug:** The failure is directly explained by production code semantics: out-of-bounds Rust string slicing panics deterministically for short inputs. No test harness stubs or environment manipulation are needed to trigger it, so this is a real application defect.
**Relevant code:**
`src/version.rs:34-37`
~~~rust
match chrono::NaiveDateTime::parse_from_str(
&extracted_at[..19],
"%Y-%m-%dT%H:%M:%S",
) {
~~~
`src/version.rs:278-291`
~~~rust
match commit_project(&repo_dir, project) {
Ok(Some(rev)) => { ... }
Ok(None) => { ... }
Err(e) => {
skipped += 1;
warn!(project = %name, error = %e, "Failed");
}
}
~~~| let extracted_at = metadata["extracted_at"] | ||
| .as_str() | ||
| .unwrap_or("unknown"); | ||
| let git_date = if extracted_at != "unknown" { |
There was a problem hiding this comment.
Raw extracted_at fallback feeds invalid git --date
What failed: Invalid extracted_at input is forwarded to git commit --date, commit creation fails for the project, and cmd_version aggregates that failure into the Skipped counter without a distinct failed count.
Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
- Severity: Minor
- Impact: Version generation can skip creating the expected commit when an extracted timestamp is malformed, while the run still appears successful.
- Steps to Reproduce:
- Create a *_aristotle fixture with aristotle_metadata.json containing extracted_at='this-date-string-should-fail-git-date'.
- Run
aristotle-manager version --results-dir <results_dir> --output-dir <git_output>. - Observe git reporting
fatal: invalid date formatand the final summary counting the project under Skipped with exit code 0.
- Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
- Code Analysis: In src/version.rs, lines 29-40 parse extracted_at and fall back to extracted_at.to_string() on parse failure, and lines 186-193 pass that value to
git commit --date. For non-git-compatible strings this predictably fails and returns Ok(None) via lines 215-218, which cmd_version handles as skipped at lines 283-286; the summary at lines 295-296 only prints Committed and Skipped, so commit failures are not surfaced as their own outcome class. - Why this is likely a bug: The new fallback path treats an invalid external date as normal input, but git rejects it and the project commit is lost. A small practical fix is to validate fallback dates before calling git and track commit errors in a dedicated failed counter in the summary.
Relevant code
src/version.rs:29-40
let git_date = if extracted_at != "unknown" {
match chrono::DateTime::parse_from_rfc3339(extracted_at) {
Ok(dt) => format!("@{}", dt.timestamp()),
Err(_) => {
match chrono::NaiveDateTime::parse_from_str(&extracted_at[..19], "%Y-%m-%dT%H:%M:%S") {
Ok(ndt) => format!("@{}", ndt.and_utc().timestamp()),
Err(_) => extracted_at.to_string(),
}
}
}
}src/version.rs:186-193
let out = Command::new("git")
.args([
"-c", "user.name=aristotle-manager",
"-c", "user.email=aristotle@harmonic.fun",
"commit",
"-F", &msg_path.to_string_lossy(),
"--date", &git_date,
])src/version.rs:283-296
Ok(None) => {
skipped += 1;
debug!(project = %name, "No changes or no metadata");
}
...
println!(" Committed: {}", committed);
println!(" Skipped: {}", skipped);Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.
**Minor severity — Raw extracted_at fallback feeds invalid git --date**
**What failed:** Invalid extracted_at input is forwarded to git commit --date, commit creation fails for the project, and cmd_version aggregates that failure into the Skipped counter without a distinct failed count.
- **Impact:** Version generation can skip creating the expected commit when an extracted timestamp is malformed, while the run still appears successful.
- **Steps to reproduce:**
1. Create a *_aristotle fixture with aristotle_metadata.json containing extracted_at='this-date-string-should-fail-git-date'.
2. Run `aristotle-manager version --results-dir <results_dir> --output-dir <git_output>`.
3. Observe git reporting `fatal: invalid date format` and the final summary counting the project under Skipped with exit code 0.
- **Stub / mock content:** No stubs, mocks, or bypasses were applied for this test in the recorded run.
- **Code analysis:** In src/version.rs, lines 29-40 parse extracted_at and fall back to extracted_at.to_string() on parse failure, and lines 186-193 pass that value to `git commit --date`. For non-git-compatible strings this predictably fails and returns Ok(None) via lines 215-218, which cmd_version handles as skipped at lines 283-286; the summary at lines 295-296 only prints Committed and Skipped, so commit failures are not surfaced as their own outcome class.
- **Why this is likely a bug:** The new fallback path treats an invalid external date as normal input, but git rejects it and the project commit is lost. A small practical fix is to validate fallback dates before calling git and track commit errors in a dedicated failed counter in the summary.
**Relevant code:**
`src/version.rs:29-40`
~~~rust
let git_date = if extracted_at != "unknown" {
match chrono::DateTime::parse_from_rfc3339(extracted_at) {
Ok(dt) => format!("@{}", dt.timestamp()),
Err(_) => {
match chrono::NaiveDateTime::parse_from_str(&extracted_at[..19], "%Y-%m-%dT%H:%M:%S") {
Ok(ndt) => format!("@{}", ndt.and_utc().timestamp()),
Err(_) => extracted_at.to_string(),
}
}
}
}
~~~
`src/version.rs:186-193`
~~~rust
let out = Command::new("git")
.args([
"-c", "user.name=aristotle-manager",
"-c", "user.email=aristotle@harmonic.fun",
"commit",
"-F", &msg_path.to_string_lossy(),
"--date", &git_date,
])
~~~
`src/version.rs:283-296`
~~~rust
Ok(None) => {
skipped += 1;
debug!(project = %name, "No changes or no metadata");
}
...
println!(" Committed: {}", committed);
println!(" Skipped: {}", skipped);
~~~
Summary by CodeRabbit
New Features
Bug Fixes