-
-
Notifications
You must be signed in to change notification settings - Fork 166
fix: preserve macro user script variable updates #999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughA Map-backed variables proxy was introduced and integrated into MacroChoiceEngine and SingleMacroEngine, replacing prior param/executor synchronization. createVariablesProxy exposes a Map as an object-like proxy. Tests were added for the proxy and for macro variable propagation; one test suite was duplicated. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant MacroChoiceEngine
participant VariablesProxy as Proxy(Map)
participant Executor as choiceExecutor.variables
User->>MacroChoiceEngine: construct(engine, app, plugin, providedVars?)
MacroChoiceEngine->>MacroChoiceEngine: initSharedVariables(providedVars)
MacroChoiceEngine->>VariablesProxy: createVariablesProxy(sharedMap)
VariablesProxy-->>MacroChoiceEngine: proxy (object-like)
MacroChoiceEngine->>Executor: assign proxy reference
Note over MacroChoiceEngine,Executor: During execution, reads/writes use proxy
MacroChoiceEngine->>VariablesProxy: read/write key
VariablesProxy->>Executor: Map.get/Map.set/Map.delete
Executor-->>VariablesProxy: external Map mutations visible immediately
VariablesProxy-->>MacroChoiceEngine: reflected value
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/engine/MacroChoiceEngine.ts (2)
159-159: Push completes the bidirectional sync pattern.The push after each command ensures mutations to
params.variablesby user scripts are persisted back into the executor's Map, making them available to subsequent macro commands and the broader execution context.Optional consideration: If a user script deletes a key from
params.variables(e.g.,delete params.variables.foo), the deletion won't propagate to the executor's Map. This is likely intentional since variables typically accumulate, but worth noting if you expect deletion semantics.Also applies to: 496-500
462-463: Redundant variable pull after line 131 addition.The
pullExecutorVariablesIntoParams()call here is now redundant because line 131 already pulls executor variables before each command, including this conditional command. The extra call is harmless but unnecessary.Consider removing this line:
private async executeConditional(command: IConditionalCommand) { - this.pullExecutorVariablesIntoParams(); const shouldRunThenBranch = await evaluateCondition(command.condition, {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/engine/MacroChoiceEngine.entry.test.ts(2 hunks)src/engine/MacroChoiceEngine.ts(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/engine/MacroChoiceEngine.entry.test.ts (3)
src/IChoiceExecutor.ts (1)
IChoiceExecutor(4-18)src/quickAddApi.ts (1)
QuickAddApi(32-614)src/engine/MacroChoiceEngine.ts (1)
MacroChoiceEngine(57-612)
🔇 Additional comments (4)
src/engine/MacroChoiceEngine.ts (1)
129-131: Excellent fix for variable propagation.The per-command pull ensures each command starts with the freshest shared state, making user script mutations immediately visible to subsequent scripts. The inline comment clearly explains the intent.
src/engine/MacroChoiceEngine.entry.test.ts (3)
1-1: Good test hygiene with spy cleanup.The
afterEachproperly restores theGetApispy, preventing test pollution.Also applies to: 286-288
214-284: Well-structured regression test for variable propagation.The test setup correctly simulates a sequence of four user scripts where each script reads the current value of
params.variables.target, logs it, and writes an updated value. This directly exercises the per-command synchronization logic introduced in the fix.
290-303: Comprehensive assertions validate the fix.The test properly validates both directions of synchronization:
- The logs array
[undefined, 1, 2, 3]proves each script sees the previous script's mutation (pull is working).- The final assertion on
choiceExecutor.variables.get("target")proves mutations are persisted back to the executor (push is working).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/utils/variablesProxy.ts (1)
31-37: Consider addingvalueto the property descriptor.The current
getOwnPropertyDescriptortrap returns onlyconfigurableandenumerable, but omits thevalueproperty. While this works for basic enumeration (which your tests verify), operations that rely on complete descriptors—such asObject.getOwnPropertyDescriptor()for introspection or certain framework reflection patterns—may receive incomplete information.Apply this diff to include the value:
getOwnPropertyDescriptor(_t, prop: string | symbol) { if (typeof prop !== "string" || !store.has(prop)) return undefined; return { + value: store.get(prop), + writable: true, configurable: true, enumerable: true, }; },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/engine/MacroChoiceEngine.ts(2 hunks)src/engine/SingleMacroEngine.ts(0 hunks)src/utils/variablesProxy.test.ts(1 hunks)src/utils/variablesProxy.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/engine/SingleMacroEngine.ts
🧰 Additional context used
🧬 Code graph analysis (2)
src/utils/variablesProxy.test.ts (1)
src/utils/variablesProxy.ts (1)
createVariablesProxy(5-39)
src/engine/MacroChoiceEngine.ts (1)
src/utils/variablesProxy.ts (1)
createVariablesProxy(5-39)
🔇 Additional comments (3)
src/utils/variablesProxy.test.ts (1)
1-27: LGTM! Comprehensive test coverage for the proxy.The tests effectively verify the core functionality: read/write operations sync with the backing Map, deletion propagates correctly, and enumeration/JSON serialization work as expected for plain-object usage patterns.
src/engine/MacroChoiceEngine.ts (1)
97-105: Excellent fix! The proxy approach resolves the variable synchronization issue.By creating a shared Map and exposing it through a proxy, all mutations—whether through
params.variablesin user scripts or directchoiceExecutor.variables.set()calls (e.g., line 454 inexecuteAIAssistant)—operate on the same backing store. This eliminates the copy-then-overwrite pattern that previously discarded updates, directly addressing the concern raised in the past review comment.src/utils/variablesProxy.ts (1)
11-14: No action needed—the implementation is correct and intentional.The verification confirms that rejecting non-string properties in the
gettrap is the correct design. A comprehensive search of the codebase found zero instances of numeric key access (e.g.,variables[0]), and all variable access uses string keys derived fromObject.keys()or direct string identifiers. The underlying store isMap<string, unknown>, and the proxy is explicitly designed for plain-object-style access with string keys, as documented in the code comment. The test suite validates this behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/engine/MacroChoiceEngine.ts (1)
465-467: Fix Object.assign calls to work with Map interface in src/quickAddApi.ts.The verification confirms the original concern is valid. Two instances of
Object.assign(choiceExecutor.variables, assistantRes)remain in src/quickAddApi.ts (lines 277 and 376), which cannot add entries to a Map object. These must be updated to useMap.set()or equivalent logic to ensure consistency with the MacroChoiceEngine.ts implementation that correctly iterates and sets variables into the Map.
🧹 Nitpick comments (1)
src/engine/MacroChoiceEngine.ts (1)
97-113: Simplify the merge logic for clarity.The merge logic correctly preserves existing executor variables when a fresh map is provided, but the flow is confusing because
sharedVariablesis assigned tovariableson line 99, thenvariablesis mutated on line 109, and finallysharedVariablesis assigned to the executor on line 113.Consider this clearer flow:
- const existingVariables = this.choiceExecutor.variables; - const sharedVariables = - variables ?? existingVariables ?? new Map<string, unknown>(); - - // If a fresh map was provided, merge any existing executor variables - // so we don't accidentally drop state the caller wanted to keep. - if ( - variables && - existingVariables && - variables !== existingVariables - ) { - existingVariables.forEach((value, key) => { - if (!variables.has(key)) variables.set(key, value); - }); - } - - this.choiceExecutor.variables = sharedVariables; + const existingVariables = this.choiceExecutor.variables; + let sharedVariables: Map<string, unknown>; + + if (variables) { + sharedVariables = variables; + // Merge any existing executor variables so we don't drop state + if (existingVariables && variables !== existingVariables) { + existingVariables.forEach((value, key) => { + if (!sharedVariables.has(key)) { + sharedVariables.set(key, value); + } + }); + } + } else { + sharedVariables = existingVariables ?? new Map<string, unknown>(); + } + + this.choiceExecutor.variables = sharedVariables;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/engine/MacroChoiceEngine.entry.test.ts(2 hunks)src/engine/MacroChoiceEngine.ts(2 hunks)src/utils/variablesProxy.test.ts(1 hunks)src/utils/variablesProxy.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/utils/variablesProxy.test.ts
- src/engine/MacroChoiceEngine.entry.test.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/engine/MacroChoiceEngine.ts (2)
src/quickAddApi.ts (1)
QuickAddApi(32-614)src/utils/variablesProxy.ts (1)
createVariablesProxy(5-41)
🔇 Additional comments (3)
src/utils/variablesProxy.ts (1)
1-41: LGTM! Solid proxy implementation.The proxy implementation correctly wraps a Map with a plain-object interface. All necessary traps are implemented:
- Property access operations (get/set/delete/has) are properly delegated to the Map
- Reflection operations (ownKeys/getOwnPropertyDescriptor) support enumeration and introspection
- Symbol properties are consistently filtered out
- The null-prototype target prevents prototype pollution
This enables scripts to use familiar object syntax (
params.variables.foo = "bar") while maintaining the Map as the canonical source of truth.src/engine/MacroChoiceEngine.ts (2)
50-50: LGTM! Correct import.The import brings in the new proxy utility for shared variable management.
117-117: LGTM! Proxy-based shared variables solve the synchronization issue.Replacing the plain object with
createVariablesProxy(this.choiceExecutor.variables)ensures that all reads/writes toparams.variablesare immediately reflected in the shared executor Map, eliminating the manual push/pull synchronization that caused the overwriting bug mentioned in past reviews.
There was a problem hiding this comment.
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)
src/engine/MacroChoiceEngine.ts (1)
50-51: Shared variables Map + proxy cleanly fixes stale-variable sync issuesThe constructor’s new logic to derive a
sharedVariablesMap (preferring the providedvariables, merging in any missing keys from an existing executor map, and then assigning it back tothis.choiceExecutor.variables) ensures a single source of truth for macro variables without discarding existing executor state. Wiringparams.variablesthroughcreateVariablesProxy(this.choiceExecutor.variables)lets user scripts keep their plain-object ergonomics while reading/writing directly to the shared Map, eliminating the previous pull/push synchronization that could overwrite newer executor updates.One minor clarity tweak you could consider: since the code branches on
if (variables), you might either mark the constructor parameter as optional (variables?: Map<string, unknown>) or drop theifand assume it’s always defined, depending on the intended contract. Not required, but it would better align types with runtime behavior.Also applies to: 97-115, 118-123
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/engine/MacroChoiceEngine.ts(2 hunks)src/quickAddApi.ts(2 hunks)src/utils/variablesProxy.test.ts(1 hunks)src/utils/variablesProxy.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/utils/variablesProxy.ts
🧰 Additional context used
🧬 Code graph analysis (2)
src/engine/MacroChoiceEngine.ts (2)
src/quickAddApi.ts (1)
QuickAddApi(32-618)src/utils/variablesProxy.ts (1)
createVariablesProxy(7-54)
src/utils/variablesProxy.test.ts (1)
src/utils/variablesProxy.ts (1)
createVariablesProxy(7-54)
🪛 GitHub Actions: Build With Lint
src/utils/variablesProxy.test.ts
[error] 77-77: bun run build-with-lint failed. TS2578: Unused '@ts-expect-error' directive in src/utils/variablesProxy.test.ts:77.
🪛 GitHub Check: Build With Lint
src/utils/variablesProxy.test.ts
[failure] 77-77:
Unused '@ts-expect-error' directive.
🔇 Additional comments (4)
src/quickAddApi.ts (1)
275-279: Map-based assignment of AI assistant results is correct and consistentUsing
Object.entries(assistantRes).forEach(([key, value]) => choiceExecutor.variables.set(key, value))aligns with the newMap-backed variable store and preserves prior semantics of copying all assistant result keys (e.g.,output,output-quoted) into the executor variables whenshouldAssignVariablesis set. No issues found here.Also applies to: 376-380
src/utils/variablesProxy.test.ts (1)
1-80: Proxy behavior tests are thorough and aligned with the implementationThe test suite exercises all key proxy behaviors (read/write/delete, key enumeration, external Map mutations, symbol handling,
for...in/Object.assign, and thehasOwnPropertyshim), matchingcreateVariablesProxy’s traps and guarding against regressions. No functional gaps spotted.src/engine/MacroChoiceEngine.ts (2)
466-468: AI assistant outputs now correctly populate the shared variables MapStoring
aiOutputVariablesviathis.choiceExecutor.variables.set(key, aiOutputVariables[key])ensures AI-generated values land in the shared executor Map, making them immediately visible to both macro commands and user scripts throughparams.variables. This removes the earlier risk of results being lost or overwritten by params–executor sync.
472-476: Conditional scripts correctly consume the same proxy-backed variablesPassing
this.params.variablesintoevaluateConditionand into conditional scripts loaded vialoadConditionalScriptmeans all conditional logic now operates on the same proxy-backed variables object that user scripts see. This keeps condition evaluation in sync with live macro state without any manual copy-back, and is consistent with the new Map/proxy design.Also applies to: 548-552
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/utils/variablesProxy.test.ts (1)
4-78: Consider optional edge case coverage.The test suite is comprehensive and well-structured. For completeness, you might optionally consider adding test coverage for:
Object.entries()andObject.values()enumeration- Empty string keys (
"")- Numeric string keys (
"0","123")- Distinguishing
proxy.key = undefinedvsdelete proxy.key- Multiple proxy instances sharing the same backing Map
These are nice-to-have refinements rather than critical gaps.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
docs/dev/macro-variables-proxy.md(1 hunks)src/utils/variablesProxy.test.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- docs/dev/macro-variables-proxy.md
🧰 Additional context used
🧬 Code graph analysis (1)
src/utils/variablesProxy.test.ts (1)
src/utils/variablesProxy.ts (1)
createVariablesProxy(7-54)
🔇 Additional comments (7)
src/utils/variablesProxy.test.ts (7)
1-4: LGTM!The imports and test suite structure are correct and well-organized.
5-15: LGTM!This test correctly validates that the proxy forwards read, write, and delete operations to the backing Map.
17-26: LGTM!This test correctly validates enumeration behavior through
Object.keys()andJSON.stringify(), ensuring the proxy'sownKeystrap works as expected.
28-38: LGTM!This test validates the core requirement of the PR: the proxy provides a live view of the backing Map, ensuring all engines and scripts see mutations immediately without manual synchronization.
40-50: LGTM!This test correctly validates that symbol properties are gracefully ignored without throwing errors or modifying the backing Map. The
@ts-expect-errordirectives here are appropriate and intentional.
52-67: LGTM!This test validates that the proxy works correctly with
for...initeration andObject.assign(), ensuring compatibility with common object manipulation patterns used in user scripts.
69-78: LGTM!This test correctly validates the
hasOwnPropertyshim while ensuring prototype methods remain unexposed. The cast toRecord<string, unknown>on line 77 is the proper fix for the previous build failure.
# [2.9.0](2.8.0...2.9.0) (2025-12-09) ### Bug Fixes * honor dateFormat in requestInputs ([903df23](903df23)) * preserve macro user script variable updates ([#999](#999)) ([26a7cf5](26a7cf5)) * reuse current tab when open file new-tab is off ([#1007](#1007)) ([1b67d77](1b67d77)) ### Features * improve macro open file command ([#994](#994)) ([9b4f177](9b4f177))
|
🎉 This PR is included in version 2.9.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
Testing
Summary by CodeRabbit
New Features
Improvements
Tests
✏️ Tip: You can customize this high-level summary in your review settings.