Skip to content

Conversation

cg-jl
Copy link

@cg-jl cg-jl commented Aug 28, 2025

Important

Introduces garbage collection improvements for the Vibe component, optimizing memory management and handling edge cases.

  • Behavior:
    • Introduces garbage collection improvements for the Vibe component, optimizing memory management.
    • Handles edge cases where memory leaks were previously occurring.
  • Performance:
    • Enhances performance by reducing memory footprint and improving cleanup efficiency.
  • Misc:
    • Updates related documentation to reflect changes in garbage collection behavior.

This description was created by Ellipsis for a6bdf0d. You can customize this summary. It will automatically update as commits are pushed.

@cg-jl cg-jl temporarily deployed to boundary-tools-dev August 28, 2025 15:58 — with GitHub Actions Inactive
@cg-jl cg-jl temporarily deployed to boundary-tools-dev August 28, 2025 15:58 — with GitHub Actions Inactive
@cg-jl cg-jl temporarily deployed to boundary-tools-dev August 28, 2025 15:58 — with GitHub Actions Inactive
Copy link

vercel bot commented Aug 28, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
promptfiddle Skipped Skipped Aug 29, 2025 0:43am

Copy link

Copy link

🔒 Entelligence AI Vulnerability Scanner

No security vulnerabilities found!

Your code passed our comprehensive security analysis.


Copy link

Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Caution

Changes requested ❌

Reviewed everything up to 65b5fe7 in 2 minutes and 52 seconds. Click for details.
  • Reviewed 1036 lines of code in 6 files
  • Skipped 0 files when reviewing.
  • Skipped posting 6 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. engine/baml-vm/src/native.rs:23
  • Draft comment:
    Avoid using 'Object::Null' as a fallback in error messages – the Object enum doesn’t define a Null variant. Use a proper error value instead.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% Without seeing the Object enum definition, I can't be 100% sure Object::Null doesn't exist. The suggestion to panic seems worse than using a null fallback for error messages. The original code change appears to be making error handling more robust, not less. I don't have visibility into the Object enum definition to verify if Null exists. The comment could be correct about the technical detail. Even if Object::Null doesn't exist, suggesting a panic is worse than using a null placeholder for error messages. The code change is making error handling more robust overall. Delete the comment. The suggestion would make the code worse by introducing panics, and we lack context to verify the claim about Object::Null.
2. engine/baml-vm/src/vm.rs:762
  • Draft comment:
    In fulfil_future, only the future at the top of the stack is updated. If the same future appears elsewhere, it won’t be replaced. Confirm that this is the intended behavior.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
3. engine/baml-vm/src/vm.rs:1388
  • Draft comment:
    Document the use of 'skip(1)' after draining the stack in DispatchFuture. It’s not immediately clear why the first element is discarded – clarifying the intended stack layout would help.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
4. engine/baml-vm/src/vm.rs:1590
  • Draft comment:
    The slicing logic for keys and values in AllocMap is quite intricate. Consider adding more inline documentation or refactoring for clarity.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
5. engine/baml-vm/src/vm.rs:796
  • Draft comment:
    Multiple calls to 'self.objects.get(...)' on the same index are repeated in error branches. Consider caching the result in a local variable to improve clarity and avoid potential duplicate lookups.
  • Reason this comment was not posted:
    Comment looked like it was already resolved.
6. engine/baml-vm/src/vm.rs:1521
  • Draft comment:
    Typographical error: "totaly" should be "totally".
  • Reason this comment was not posted:
    Comment was on unchanged code.

Workflow ID: wflow_BtO1h58sRPqHVvj9

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

return Err(InternalError::TypeError {
expected: ObjectType::Instance.into(),
got: ObjectType::of(&self.objects[reference]).into(),
got: ObjectType::of(self.objects.get(reference).unwrap_or(&Object::Null)).into(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Avoid using 'Object::Null' as a fallback in error reporting – the Object enum doesn’t have a Null variant, which makes these unwraps potentially misleading.

Suggested change
got: ObjectType::of(self.objects.get(reference).unwrap_or(&Object::Null)).into(),
got: ObjectType::of(self.objects.get(reference).unwrap_or(&Object::Stale)).into(),

Copy link

Review Summary

🏷️ Draft Comments (4)

Skipped posting 4 draft comments that were valid but scored below your review threshold (>=10/15). Feel free to update them here.

engine/baml-vm/src/debug.rs (2)

145-155: display_object returns <invalid object> for any error from objects.get, including stale references, which may mask real bugs or GC errors.

📊 Impact Scores:

  • Production Impact: 2/5
  • Fix Specificity: 5/5
  • Urgency Impact: 2/5
  • Total Score: 9/15

🤖 AI Agent Prompt (Copy & Paste Ready):

In engine/baml-vm/src/debug.rs, lines 145-155, the function `display_object` currently returns `<invalid object>` for any error from `objects.get`, which can mask real bugs such as stale references or GC errors. Update the function to distinguish between Ok and Err results from `objects.get`, and include the error message in the output (e.g., `<object error: ...>`), so that debugging and error reporting are more accurate.

89-101: display_instruction and display_object use multiple nested objects.get() calls per instruction, causing repeated lookups and potential performance degradation when displaying large bytecode listings.

📊 Impact Scores:

  • Production Impact: 2/5
  • Fix Specificity: 3/5
  • Urgency Impact: 2/5
  • Total Score: 7/15

🤖 AI Agent Prompt (Copy & Paste Ready):

In engine/baml-vm/src/debug.rs, lines 89-101, the code repeatedly calls `objects.get()` for nested object lookups in `display_instruction`, which can cause performance issues when displaying large bytecode listings. Refactor this block to minimize repeated lookups by storing intermediate results (e.g., cache `instance.class` in a variable and reuse it). Ensure the logic and error handling remain unchanged.

engine/baml-vm/src/vm.rs (1)

832-853, 1504-1523, 1517-1523, 1135-1135, 1279-1279, 1319-1319, 1350-1350, 1627-1627, 1562-1562: The main VM execution loop (exec) repeatedly copies and reassigns the Frame and function on every instruction, causing unnecessary heap and stack churn and reducing interpreter throughput.

📊 Impact Scores:

  • Production Impact: 3/5
  • Fix Specificity: 3/5
  • Urgency Impact: 2/5
  • Total Score: 8/15

🤖 AI Agent Prompt (Copy & Paste Ready):

Refactor the main VM execution loop in engine/baml-vm/src/vm.rs (lines 832-853, 1504-1523, 1517-1523, 1135, 1279, 1319, 1350, 1627, 1562) to avoid copying and reassigning the `Frame` and `function` on every instruction. Instead, operate directly on the mutable reference from `self.frames.last_mut()` and only update when changing frames. This will reduce stack churn and improve interpreter throughput.

engine/baml-vm/src/vm/indexable.rs (1)

249-257: ObjectPool::from_vec allocates a new Vec<ObjectSlot> and copies all objects, causing O(n) allocation/copy overhead for large pools; this can be avoided by reusing the original vector with minimal wrapping.

📊 Impact Scores:

  • Production Impact: 2/5
  • Fix Specificity: 1/5
  • Urgency Impact: 1/5
  • Total Score: 4/15

🤖 AI Agent Prompt (Copy & Paste Ready):

Optimize `ObjectPool::from_vec` in engine/baml-vm/src/vm/indexable.rs (lines 249-257) to avoid O(n) allocation and copy when wrapping a Vec<Object> into ObjectPool. If ObjectSlot is layout-compatible with Object, use unsafe Vec transmute to wrap without copying; otherwise, fall back to the current map/collect. Ensure safety and platform compatibility.

Comment on lines 112 to 114
/// Stale variant for freed/uninitialized slots in the object pool.
Stale,

Choose a reason for hiding this comment

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

correctness: The new Object::Stale variant is not handled in ObjectPool::get()/get_mut() or in VM logic, so accessing a stale/freed object may return Object::Stale and cause silent logic errors instead of raising StaleObjectReference.

🤖 AI Agent Prompt for Cursor/Windsurf

📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue

In engine/baml-vm/src/vm.rs, ensure that all accesses to objects via `ObjectPool::get()`/`get_mut()` and all VM logic that matches on `Object` properly detect and error on `Object::Stale`, returning `InternalError::StaleObjectReference` instead of allowing silent logic errors. Update all relevant match arms and accessors to enforce this contract.

Comment on lines 796 to 809
pub(crate) fn as_object(
&self,
value: &Value,
object_type: ObjectType,
) -> Result<ObjectIndex, InternalError> {
let Value::Object(index) = value else {
return Err(InternalError::TypeError {
expected: object_type.into(),
got: self.type_of(value),
});
};

Ok(*index)
}

Choose a reason for hiding this comment

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

correctness: The as_object helper in Vm does not check for stale object slots, so it may return a valid index for a stale/freed object, leading to undefined behavior.

🤖 AI Agent Prompt for Cursor/Windsurf

📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue

In engine/baml-vm/src/vm.rs, lines 796-809, update the `as_object` method to check if the object at the given index is `Object::Stale`. If so, return `InternalError::StaleObjectReference`. This prevents returning indices for freed objects and ensures stale references are never treated as valid.
📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
pub(crate) fn as_object(
&self,
value: &Value,
object_type: ObjectType,
) -> Result<ObjectIndex, InternalError> {
let Value::Object(index) = value else {
return Err(InternalError::TypeError {
expected: object_type.into(),
got: self.type_of(value),
});
};
Ok(*index)
}
pub(crate) fn as_object(
&self,
value: &Value,
object_type: ObjectType,
) -> Result<ObjectIndex, InternalError> {
let Value::Object(index) = value else {
return Err(InternalError::TypeError {
expected: object_type.into(),
got: self.type_of(value),
});
};
match self.objects.get(*index) {
Ok(Object::Stale) => Err(InternalError::StaleObjectReference),
Ok(obj) => Ok(*index),
Err(e) => Err(e),
}
}

Comment on lines 775 to 781
pub fn collect_garbage(&mut self) {
self.objects.drain(self.runtime_allocs_offset..);
// For now, just a placeholder until we implement the full GC
// This will be replaced with the mark-sweep implementation
self.objects.truncate(self.runtime_allocs_offset);
self.mark_bits.truncate(self.runtime_allocs_offset);
self.free_list.clear();
}

Choose a reason for hiding this comment

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

performance: collect_garbage currently truncates all runtime objects instead of performing a mark-and-sweep, causing O(n) object leaks and unbounded memory growth in long-running VMs.

🤖 AI Agent Prompt for Cursor/Windsurf

📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue

Implement a real mark-and-sweep generational garbage collector in engine/baml-vm/src/vm.rs, lines 775-781. The current `collect_garbage` just truncates all runtime objects, which causes O(n) memory leaks and prevents long-running VMs from reclaiming unused memory. Replace this with a proper mark phase (traverse all roots, mark reachable objects using `mark_bits`), then a sweep phase (free unreachable objects, update `free_list`, and reset mark bits). Ensure all object references (stack, globals, escaped_futures, etc.) are considered roots.

Comment on lines 785 to 792
let index = self.objects.len();
self.objects.push(ObjectSlot {
object: Object::Array(values),
generation: 0,
});
self.mark_bits.push(false);
Value::Object(ObjectIndex::from_raw(index))
}

Choose a reason for hiding this comment

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

performance: Object allocation (e.g., in alloc_array, AllocArray, AllocInstance, DispatchFuture, AllocMap) always appends to the pool, never reusing freed slots, causing unbounded memory growth and poor cache locality.

🤖 AI Agent Prompt for Cursor/Windsurf

📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue

Refactor all object allocation sites in engine/baml-vm/src/vm.rs (lines 785-792, 1122-1131, 1335-1347, 1397-1406, 1616-1624) to reuse freed slots from `free_list` before appending to the object pool. When allocating, check `free_list` for available indices, update the slot and its generation, and only append if no free slots exist. This prevents unbounded memory growth and improves memory locality.

Copy link

Copy link

codecov bot commented Aug 29, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link

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