Skip to content

perf(lifecycle): cut install time by ~21% via script reuse, builder import caching, and phase-end migration optimization - #125

Merged
buke merged 14 commits into
mainfrom
chore/log-noise-merge-install-flow
Jul 1, 2026
Merged

perf(lifecycle): cut install time by ~21% via script reuse, builder import caching, and phase-end migration optimization#125
buke merged 14 commits into
mainfrom
chore/log-noise-merge-install-flow

Conversation

@buke

@buke buke commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

This branch reduces install document total wall time by ~21.5% (115s → 90s in local benchmarks), primarily by eliminating redundant hook/migration script runs and caching builder imports across modules.

Commits

  • chore(lifecycle): reduce module operation log noise — drop verbose heartbeat logs in TTY mode to reduce output noise.
  • feat(lifecycle): unify pipeline-driven spinner progress — consolidate spinner updates into the pipeline callback layer for consistent progress reporting.
  • feat(esmresolver): annotate metrics with build context — add module/application labels to ESM resolver metrics for better observability.
  • perf(lifecycle): reduce phase-end migration overhead — trim unnecessary migration work during the finalizing phase.
  • perf(lifecycle): reuse migration scripts across phase-end modules — share compiled migration script runners across modules rather than re-initializing per module.
  • perf(lifecycle): reuse hook scripts and cache install builder importsprimary performance gain: cache JsScripts across install steps and share hook runners; responsible for ~21% install-time reduction across all modules.

Benchmark

Metric Baseline (origin/main) This branch Delta
install document total 115,364 ms 90,515 ms -24,849 ms (-21.5%)
Per-module script_ms ~5–7 s/module ~2–3 s/module ~-50%

Benchmark logs: .choysum/tmp/regression/install-document-breakdown-{baseline,optimized}-*.summary.log

Changed Files

File Change
internal/module/lifecycle/modulemanager.go Spinner consolidation, log-noise reduction, phase-end migration reuse
internal/module/evolution/hooks/runner.go Hook script caching and reuse
internal/module/evolution/scripts/runner_decorators.go Migration script runner reuse
internal/module/artifact/build/backend/builder.go Builder import caching
internal/module/artifact/pipeline/pipeline.go Unified progress events
internal/esmresolver/resolver.go Metrics annotation

Testing

  • go test ./internal/module/lifecycle ./internal/module/artifact/build/backend ./internal/module/evolution/... ./internal/esmresolver/... — all pass.

By submitting this pull request, you agree to the Contributor License Agreement (CLA) of this project. If you have not yet signed it, please follow the instructions provided by @cla-bot below.

Summary by CodeRabbit

  • New Features

    • Added unified pipeline progress reporting across install, uninstall, and upgrade, including stage-level started/completed/failed events.
    • Improved observability by attaching module and application metadata to runtime metrics/logs when available.
    • Added support for reusing previously loaded hook/executor scripts to reduce redundant work.
  • Bug Fixes

    • Prevented entry-point import specifier caching from leaking across runtime-scope changes.
    • Refined lifecycle progress/spinner transitions with more consistent duration reporting.
  • Tests

    • Added/expanded coverage for unified progress events, script equivalence/reuse, and reload rollback behavior.

buke added 6 commits July 1, 2026 15:53
- Merge install planning duration into a single module operation plan info log while keeping plan details at debug level.

- Downgrade install finalizing step completion logs to debug and fold finalizing duration fields into the final module operation completed info log.

- Align uninstall and upgrade plan summary logs with install by appending planning duration to the plan info attributes.
- Add unified pipeline progress events for module, app, bundles, and web stages via OnProgress callbacks.

- Wire install, uninstall, and upgrade flows to the shared progress events so spinner updates stay consistent across operations.

- Use global-stage spinner copy for bundles and web builds to avoid misleading root-module prefixes while keeping existing log fields unchanged.
- Add resolver options to attach module and application labels to cumulative esm resolver metrics logs.

- Wire backend and web builders to pass module and application names when constructing resolver plugins.

- Extend resolver logger tests to assert module and application fields are emitted in metrics output.
- Prefer loading already-built runtime scripts before falling back to rebuilding module entry scripts in migration runner resolution.

- Add per-module phase-end completion timing logs during install and upgrade finalization for hotspot diagnosis.
- Add scripts.RunOptions.ReuseExecutorScripts and skip redundant executor reloads when script set is unchanged.

- Make migration wrapper script module-agnostic and pass app/module selectors via request args to keep script content stable.

- Reuse loaded executor scripts throughout install finalizing phase_end loop and restore once after the loop in module manager.

- Update migration runner tests and add coverage proving script reuse avoids repeated reloads across module runs.
- Add hooks.RunOptions.ReuseExecutorScripts and generic hook wrapper args to reduce repeated script reloads during install hook phases.

- Restore js executor scripts once for the full install operation and enable hook script reuse in installer pre_init/post_init runs.

- Cache backend builder entryPointImports per builder run so prebuild/build passes avoid duplicate DB and filesystem scans.

- Update hook runner tests with reuse-path coverage and generic wrapper assertions.
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds resolver metadata labels, unified pipeline progress events, executor script reuse for hook and migration runners, and lifecycle wiring for progress, spinner, and phase-end execution.

Changes

Module observability and execution reuse

Layer / File(s) Summary
Resolver labels
internal/esmresolver/resolver.go, internal/esmresolver/resolver_test.go
Adds moduleName and application resolver fields, option helpers, and conditional metrics-log attributes, with tests asserting the emitted labels.
Builder resolver wiring and cache
internal/module/artifact/build/backend/builder.go, internal/module/artifact/build/web/webBuilder.go
Backend and web builders conditionally pass module/application resolver options, and the backend builder caches computed entry-point imports across runtime-state bindings.
Unified pipeline progress
internal/module/artifact/pipeline/pipeline.go, internal/module/artifact/pipeline/pipeline_test.go
Adds unified progress events and emits stage-level progress across app build/generate, bundles/web build, install, uninstall, and upgrade flows, with test coverage for emitted stages and name summarization.
Hook runner reuse
internal/module/evolution/hooks/runner.go, internal/module/evolution/hooks/runner_test.go
Adds executor-script reuse, passes app/module/phase into a generic hook wrapper, and skips redundant reloads when scripts are equivalent, with tests covering wrapper shape and reload counts.
Migration runner reuse
internal/module/evolution/scripts/runner_decorators.go, internal/module/evolution/scripts/runner_decorators_test.go
Adds executor-script reuse, passes app and module identifiers through registry and migration execution, prefers runtime scripts, and avoids redundant reloads, with tests updated for new arguments and reload behavior.
Lifecycle progress wiring
internal/module/lifecycle/installer.go, internal/module/lifecycle/modulemanager.go, internal/module/lifecycle/modulemanager_logging_test.go
Installer and module manager pass reuse into phase-end hooks, add spinner stage mappings and progress callbacks, and rewrite install, uninstall, and upgrade logging and completion handling around progress events and duration breakdowns.
Estimated code review effort: 4 (Complex) ~75 minutes

Possibly related PRs

  • choysum-dev/choysum#89: Both PRs change internal/esmresolver/resolver.go and the resolver logger/metrics expectations in resolver_test.go.
  • choysum-dev/choysum#112: Both PRs touch the same resolver metrics logging path and attribute construction in internal/esmresolver/resolver.go.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main performance change: install-time reduction through script reuse, import caching, and phase-end migration optimization.
Description check ✅ Passed The description includes the required CLA agreement text and is otherwise substantive, with summary, benchmarks, changed files, and testing notes.
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 chore/log-noise-merge-install-flow

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

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces several enhancements, including adding module and application name metadata to the ESM resolver metrics, caching entry-point imports in the backend module builder, implementing a unified pipeline progress reporting mechanism with spinner updates, and optimizing JS script execution by allowing script reuse across module loops. The review feedback highlights several critical issues where b or b.module is dereferenced before a nil check in builder.go and webBuilder.go, which could lead to panics. Additionally, it is recommended to extend the script reuse optimization to the module upgrade flow in modulemanager.go to ensure consistency with the install flow.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread internal/module/artifact/build/backend/builder.go Outdated
Comment thread internal/module/artifact/build/backend/builder.go
Comment thread internal/module/artifact/build/backend/builder.go
Comment thread internal/module/artifact/build/web/webBuilder.go
Comment thread internal/module/artifact/build/web/webBuilder.go
Comment thread internal/module/lifecycle/modulemanager.go
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
internal/module/artifact/pipeline/pipeline.go (1)

958-976: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Delay the unified install-completed event until post-install generation succeeds.

Line 958 emits module.install.completed before generateModulesForApp(app) can fail. If that generation fails, legacy OnInstallProgress reports failed, but unified OnProgress has already reported success and never emits module.install.failed.

Proposed fix
-			emitProgress(ProgressEvent{Stage: ProgressStageModuleInstallCompleted, Current: index + 1, Total: totalModules, Module: moduleName, Duration: installDuration})
 			logStep(slog.LevelInfo, "module installed",
 				"installed_module", mod.Name,
 				"duration_ms", installDuration.Milliseconds(),
 			)
@@
 				if err := generateModulesForApp(app); err != nil {
+					emitProgress(ProgressEvent{Stage: ProgressStageModuleInstallFailed, Current: index + 1, Total: totalModules, Module: moduleName, Duration: installDuration, Err: err})
 					if cb.OnInstallProgress != nil {
 						cb.OnInstallProgress(ModuleInstallProgress{
@@
 					return err
 				}
 				generated[app] = true
 			}
+			emitProgress(ProgressEvent{Stage: ProgressStageModuleInstallCompleted, Current: index + 1, Total: totalModules, Module: moduleName, Duration: installDuration})
🤖 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 `@internal/module/artifact/pipeline/pipeline.go` around lines 958 - 976, The
unified module install completion event is emitted too early in the install
flow, before generateModulesForApp can still fail. Update the install path
around the emitProgress call in pipeline.go so module.install.completed is only
sent after generateModulesForApp(app) succeeds, and if that generation fails,
emit the matching unified failure event for the same module/install step instead
of success. Keep the existing legacy OnInstallProgress behavior in sync with the
same success/failure outcome.
🧹 Nitpick comments (3)
internal/module/artifact/build/backend/builder.go (1)

195-199: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant/misleading nil check on b.module.

Line 195 (and 215) dereferences b.module.Path unconditionally right before the new if b.module != nil guard on the next line. If b.module can genuinely be nil, WithModulePath(b.module.Path) already panics before the guard is reached; if it can't be nil (as implied by other unguarded uses like b.module.ApplicationStr at Line 117/164), the guard is dead code. Either fix the ordering so the nil-guard actually protects all b.module field accesses, or drop the guard since ModuleBuilder.module is apparently never nil in this path.

Example fix if module can be nil
-		resolverOpts := []esmresolver.Option{
-			esmresolver.WithCacheDir(runtimeOptions.defaultChoysumPath),
-			esmresolver.WithTarget("es2020"),
-			esmresolver.WithModulePath(b.module.Path),
-		}
-		if b.module != nil {
-			resolverOpts = append(resolverOpts, esmresolver.WithModuleName(b.module.Name), esmresolver.WithApplicationName(b.module.ApplicationStr))
-		}
+		resolverOpts := []esmresolver.Option{
+			esmresolver.WithCacheDir(runtimeOptions.defaultChoysumPath),
+			esmresolver.WithTarget("es2020"),
+		}
+		if b.module != nil {
+			resolverOpts = append(resolverOpts,
+				esmresolver.WithModulePath(b.module.Path),
+				esmresolver.WithModuleName(b.module.Name),
+				esmresolver.WithApplicationName(b.module.ApplicationStr))
+		}

Also applies to: 215-219

🤖 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 `@internal/module/artifact/build/backend/builder.go` around lines 195 - 199,
The nil check around b.module in builder.go is redundant and misleading because
b.module.Path is already accessed before the guard, so either reorder the
b.module checks in the affected builder setup to protect every b.module field
access (including WithModulePath, WithModuleName, and WithApplicationName), or
remove the guard entirely if ModuleBuilder.module is guaranteed non-nil in this
flow. Use the existing resolverOpts construction in the builder method as the
place to make the change so the handling of b.module is consistent.
internal/module/lifecycle/modulemanager.go (1)

1521-1522: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Apply phase-end script reuse to upgrade as well.

Install phase-end passes ReuseExecutorScripts: m.jsExecutor != nil, but upgrade still reloads per module. Mirror the install path here, including an executor snapshot/restore around the upgrade operation if reuse is enabled, so multi-module upgrades benefit from the same migration-script reuse.

🤖 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 `@internal/module/lifecycle/modulemanager.go` around lines 1521 - 1522,
Phase-end script reuse is only applied for install, not upgrade. Update the
upgrade path in ModuleManager’s phase-end handling to mirror the install flow by
passing ReuseExecutorScripts when invoking the runner and, when reuse is
enabled, taking an executor snapshot before the upgrade and restoring it
afterward so multi-module upgrades reuse migration scripts across modules.
internal/module/artifact/pipeline/pipeline_test.go (1)

278-288: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the app build/generate completion stages too.

The new API includes ProgressStageAppBuildCompleted and ProgressStageAppGenerateCompleted, but this test only requires their started counterparts. Add both completion stages so regressions in duration/error completion events are caught.

Proposed fix
 		ProgressStageAppStageStarted,
 		ProgressStageAppBuildStarted,
+		ProgressStageAppBuildCompleted,
 		ProgressStageAppGenerateStarted,
+		ProgressStageAppGenerateCompleted,
 		ProgressStageBundlesBuildStarted,
🤖 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 `@internal/module/artifact/pipeline/pipeline_test.go` around lines 278 - 288,
The stage coverage in the pipeline progress test is incomplete: it asserts only
the started events for app build and generate, so regressions in the completion
events can slip through. Update the stage list in the pipeline test that
iterates over ProgressStage values to also include
ProgressStageAppBuildCompleted and ProgressStageAppGenerateCompleted, keeping
the existing ProgressStageAppBuildStarted and ProgressStageAppGenerateStarted
checks intact.
🤖 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 `@internal/module/evolution/hooks/runner.go`:
- Around line 363-370: In executeWithScripts on Runner, restore the previous
jsExecutor script set if r.jsExecutor.Reload(scripts...) fails after
SetJsScripts has already been called. Update the changedScripts branch to save
prevScripts, revert via r.jsExecutor.SetJsScripts(prevScripts) before returning
the reload error, and keep the behavior localized to this reload path so
equivalentScripts continues to reflect the actual runtime state.

In `@internal/module/evolution/scripts/runner_decorators.go`:
- Around line 321-327: The script update path in r.jsExecutor should not leave
metadata mutated if Reload fails. In runner_decorators.go, around the
changedScripts block, capture the previous script list from GetJsScripts before
calling SetJsScripts and Reload, and if Reload returns an error restore the old
scripts on the executor before returning the error. Use the existing jsExecutor
methods (GetJsScripts, SetJsScripts, Reload) in the migration runner flow so
later calls don’t incorrectly skip reloading based on stale equivalentScripts
state.

In `@internal/module/lifecycle/modulemanager.go`:
- Around line 902-905: The deferred restore in modulemanager.go’s lifecycle
cleanup ignores failures from m.jsExecutor.Reload, so restore problems are
hidden and the executor may remain in a bad state. Update the defer in the
modulemanager restore path to handle and log any Reload error, using the
existing m.jsExecutor and previousExecutorScripts context so failures are
visible during later lifecycle runs.

---

Outside diff comments:
In `@internal/module/artifact/pipeline/pipeline.go`:
- Around line 958-976: The unified module install completion event is emitted
too early in the install flow, before generateModulesForApp can still fail.
Update the install path around the emitProgress call in pipeline.go so
module.install.completed is only sent after generateModulesForApp(app) succeeds,
and if that generation fails, emit the matching unified failure event for the
same module/install step instead of success. Keep the existing legacy
OnInstallProgress behavior in sync with the same success/failure outcome.

---

Nitpick comments:
In `@internal/module/artifact/build/backend/builder.go`:
- Around line 195-199: The nil check around b.module in builder.go is redundant
and misleading because b.module.Path is already accessed before the guard, so
either reorder the b.module checks in the affected builder setup to protect
every b.module field access (including WithModulePath, WithModuleName, and
WithApplicationName), or remove the guard entirely if ModuleBuilder.module is
guaranteed non-nil in this flow. Use the existing resolverOpts construction in
the builder method as the place to make the change so the handling of b.module
is consistent.

In `@internal/module/artifact/pipeline/pipeline_test.go`:
- Around line 278-288: The stage coverage in the pipeline progress test is
incomplete: it asserts only the started events for app build and generate, so
regressions in the completion events can slip through. Update the stage list in
the pipeline test that iterates over ProgressStage values to also include
ProgressStageAppBuildCompleted and ProgressStageAppGenerateCompleted, keeping
the existing ProgressStageAppBuildStarted and ProgressStageAppGenerateStarted
checks intact.

In `@internal/module/lifecycle/modulemanager.go`:
- Around line 1521-1522: Phase-end script reuse is only applied for install, not
upgrade. Update the upgrade path in ModuleManager’s phase-end handling to mirror
the install flow by passing ReuseExecutorScripts when invoking the runner and,
when reuse is enabled, taking an executor snapshot before the upgrade and
restoring it afterward so multi-module upgrades reuse migration scripts across
modules.
🪄 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: 00748f30-d641-4dc0-9bbc-648bfd20993d

📥 Commits

Reviewing files that changed from the base of the PR and between 76a815a and e7b3f51.

📒 Files selected for processing (12)
  • internal/esmresolver/resolver.go
  • internal/esmresolver/resolver_test.go
  • internal/module/artifact/build/backend/builder.go
  • internal/module/artifact/build/web/webBuilder.go
  • internal/module/artifact/pipeline/pipeline.go
  • internal/module/artifact/pipeline/pipeline_test.go
  • internal/module/evolution/hooks/runner.go
  • internal/module/evolution/hooks/runner_test.go
  • internal/module/evolution/scripts/runner_decorators.go
  • internal/module/evolution/scripts/runner_decorators_test.go
  • internal/module/lifecycle/installer.go
  • internal/module/lifecycle/modulemanager.go

Comment thread internal/module/evolution/hooks/runner.go
Comment thread internal/module/evolution/scripts/runner_decorators.go
Comment thread internal/module/lifecycle/modulemanager.go
- Restore previous executor scripts when Reload fails in hooks and migration runners.

- Delay module.install.completed emission until per-app generation succeeds and emit module.install.failed on generation errors.

- Align upgrade phase-end script execution with ReuseExecutorScripts and log js executor restore reload failures.

- Guard resolver module options and nil receiver access ordering in backend/web builders.
@buke

buke commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@buke

buke commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces several performance and observability enhancements, including caching entry-point imports in the module builder, adding module and application labels to the ESM resolver metrics, and implementing a unified progress event system to report detailed pipeline execution stages. Additionally, it optimizes JS hook and migration runners by allowing scripts to be reused on the shared executor across runs. The review feedback correctly highlights two important issues: first, in resolveScripts, compilation errors from buildModuleEntryScript are swallowed when LoadRuntimeScripts fails; second, the Upgrade flow in ModuleManager lacks the script restoration deferred block that was added to the Install flow, which could lead to script leakage on the shared executor.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread internal/module/evolution/scripts/runner_decorators.go Outdated
Comment thread internal/module/lifecycle/modulemanager.go
- Preserve buildModuleEntryScript errors in resolveScripts when runtime scripts are missing.

- Add upgrade-flow js executor script restoration with reload-failure warning, matching install flow cleanup.

- Add regression test for resolveScripts fallback error precedence.
@buke

buke commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces observability labels (module and application names) to the ESM resolver, implements caching for entry-point imports in the backend builder, and adds a unified progress event system to the artifact pipeline to update user-facing progress views. Additionally, it optimizes JS script execution by allowing loaded scripts to be reused across module loops to avoid redundant reloads. The feedback points out that while script restoration blocks were added to the Install and Uninstall operations in the module manager, the Upgrade operation is missing this restoration logic, which could leave the shared executor in an inconsistent state.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread internal/module/lifecycle/modulemanager.go
- Add missing js executor script restoration defer in Upgrade flow.

- Log reload restore failures consistently with Install/Uninstall.
@buke

buke commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces unified progress reporting across pipeline execution stages, adds caching for entry-point imports in the backend builder, and implements a mechanism to reuse loaded JS scripts on the shared executor to avoid redundant reloads during module lifecycle operations. Feedback on these changes highlights potential nil pointer dereference panics in builder.go and webBuilder.go due to inconsistent nil-checking of b.module. Additionally, in modulemanager.go, the spinner state should be cleared if runner.RunPhase fails during the finalizing phase of the upgrade operation to prevent a stuck terminal UI.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread internal/module/artifact/build/backend/builder.go Outdated
Comment thread internal/module/artifact/build/web/webBuilder.go Outdated
Comment thread internal/module/lifecycle/modulemanager.go
- Clear spinner state before returning when Upgrade phase-end RunPhase fails.
@buke

buke commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces unified progress reporting across pipeline execution stages (install, uninstall, upgrade, and builds) and optimizes JS execution by allowing loaded scripts to be reused across module loops. It also adds module and application labels to the ESM resolver for better observability. However, a critical bug was identified in the migration runner where prioritizing cached runtime scripts over building the latest source code during upgrades can cause new migration scripts to be skipped.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread internal/module/evolution/scripts/runner_decorators.go Outdated
- Resolve migration scripts source-first for Pre/Post/Validate and keep runtime-first only for PhaseEnd.

- Add regression coverage for phase-aware script resolution preference and update runner tests accordingly.
@buke

buke commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a unified progress tracking system across pipeline execution stages, adds observability labels to the ESM resolver, implements caching for entry-point imports, and optimizes JS hook and migration execution by allowing script reuse on the shared executor. Feedback on these changes suggests extracting duplicated progress-handling logic in ModuleManager into a helper method, removing the now-obsolete buildHookEnvScript and its tests, and resolving inconsistent nil checks on b.module in both builder.go and webBuilder.go to prevent potential panics.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread internal/module/lifecycle/modulemanager.go
Comment thread internal/module/evolution/hooks/runner_test.go
Comment thread internal/module/artifact/build/backend/builder.go Outdated
Comment thread internal/module/artifact/build/backend/builder.go Outdated
Comment thread internal/module/artifact/build/web/webBuilder.go Outdated
Comment thread internal/module/artifact/build/web/webBuilder.go Outdated
buke added 2 commits July 1, 2026 21:14
- Remove obsolete hook env script helper and related unit-test assertions.

- Restore consistent module non-nil invariant usage in backend/web resolver option assembly by removing partial nil guards.
- Add handlePipelineSharedProgress to centralize shared app/bundles/web spinner stage updates.

- Reuse the helper in install, uninstall, and upgrade pipeline OnProgress callbacks while keeping operation-specific module stage handling in each callback.

- Preserve existing user-facing progress messages and fallback behavior for unknown app names and total app counts.
@buke

buke commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces performance optimizations and observability improvements to the module lifecycle and build pipeline. Key changes include caching entry-point imports in the module builder, implementing a unified progress reporting mechanism (OnProgress) integrated with CLI spinner updates, and optimizing JS execution by making hook/migration wrappers generic and allowing script reuse on the shared JS executor to avoid redundant reloads. Additionally, module and application labels have been added to the ESM resolver for enhanced observability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
internal/module/evolution/scripts/runner_decorators_test.go (1)

154-172: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the new prepareRunnerModuleSource helper instead of duplicating setup.

This test manually re-implements the same modules-path/tsconfig/entry-point setup that prepareRunnerModuleSource (lines 38-60) was just extracted to encapsulate. Using the helper here keeps the fixture logic in one place and avoids future drift between the two copies.

♻️ Proposed refactor
-	if err := os.MkdirAll(testRuntimeScope.cfg.ModulesPath, 0o755); err != nil {
-		t.Fatalf("mkdir modules path: %v", err)
-	}
-	if err := os.WriteFile(filepath.Join(testRuntimeScope.cfg.ModulesPath, "tsconfig.json"), []byte(`{"compilerOptions":{"baseUrl":".","paths":{"`@/`*":["./*"]}}}`), 0o644); err != nil {
-		t.Fatalf("write tsconfig: %v", err)
-	}
-
-	entryPoint := filepath.Join(testRuntimeScope.cfg.ModulesPath, "base", "service", "index.ts")
-	if err := os.MkdirAll(filepath.Dir(entryPoint), 0o755); err != nil {
-		t.Fatalf("mkdir entry dir: %v", err)
-	}
-	if err := os.WriteFile(entryPoint, []byte("export const migration = {}\n"), 0o644); err != nil {
-		t.Fatalf("write entry point: %v", err)
-	}
+	prepareRunnerModuleSource(t, testRuntimeScope, "base", "service/index.ts", "export const migration = {}\n")
🤖 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 `@internal/module/evolution/scripts/runner_decorators_test.go` around lines 154
- 172, The test setup in TestResolveScripts_SourceFirstUnlessPhaseEnd is
duplicating the modules-path, tsconfig, and entry-point fixture logic that
prepareRunnerModuleSource already encapsulates. Replace the manual
os.MkdirAll/os.WriteFile setup with a call to prepareRunnerModuleSource in
runner_decorators_test.go, then use its returned entry-point/module source
values when building the test case so the fixture logic stays centralized and
consistent.
🤖 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.

Nitpick comments:
In `@internal/module/evolution/scripts/runner_decorators_test.go`:
- Around line 154-172: The test setup in
TestResolveScripts_SourceFirstUnlessPhaseEnd is duplicating the modules-path,
tsconfig, and entry-point fixture logic that prepareRunnerModuleSource already
encapsulates. Replace the manual os.MkdirAll/os.WriteFile setup with a call to
prepareRunnerModuleSource in runner_decorators_test.go, then use its returned
entry-point/module source values when building the test case so the fixture
logic stays centralized and consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4aa91f66-2522-4cba-8856-1257a47aa70b

📥 Commits

Reviewing files that changed from the base of the PR and between dd1c6be and c9beb75.

📒 Files selected for processing (7)
  • internal/module/artifact/build/backend/builder.go
  • internal/module/artifact/build/web/webBuilder.go
  • internal/module/evolution/hooks/runner.go
  • internal/module/evolution/hooks/runner_test.go
  • internal/module/evolution/scripts/runner_decorators.go
  • internal/module/evolution/scripts/runner_decorators_test.go
  • internal/module/lifecycle/modulemanager.go
💤 Files with no reviewable changes (2)
  • internal/module/evolution/hooks/runner.go
  • internal/module/evolution/hooks/runner_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/module/artifact/build/web/webBuilder.go
  • internal/module/artifact/build/backend/builder.go
  • internal/module/lifecycle/modulemanager.go
  • internal/module/evolution/scripts/runner_decorators.go

- Reuse prepareRunnerModuleSource in TestResolveScripts_SourceFirstUnlessPhaseEnd instead of duplicating setup.

- Add TestHandlePipelineSharedProgress covering all shared stage branches and edge cases (app stage, build, generate, bundles, web, unknown stage).

- Add TestEquivalentScripts unit test for nil, different lengths, different content, and nil-element comparisons.

- Add TestExecuteWithScriptsReloadFailureRollback for both scripts and hooks packages, verifying script restoration and double-reload on failure.

- Add TestRunPhaseNonRequiredHookUnavailable covering non-required phase resolution and execution failure warning paths.

- Add TestSummarizeInfoNames for empty, dedup, empty-string skip, all-empty, and over-limit cases.
@buke
buke merged commit d67083c into main Jul 1, 2026
41 of 42 checks passed
@buke
buke deleted the chore/log-noise-merge-install-flow branch July 1, 2026 13:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
internal/module/evolution/scripts/runner_decorators_test.go (1)

434-465: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Same rollback-assertion concern as internal/module/evolution/hooks/runner_test.go.

failingReloadExecutor.Reload returns the configured error without ever calling e.inner.Reload(...), so baseExecutor's stored scripts (set once at Line 521) are never actually mutated by either reload attempt. The "verify executor scripts were restored to previous" check at Lines 541-544 therefore doesn't prove that executeWithScripts performs a real rollback against a stateful executor — only the reload call-order/argument assertions (Lines 547-555) meaningfully validate the rollback path.

Also applies to: 514-556

🤖 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 `@internal/module/evolution/scripts/runner_decorators_test.go` around lines 434
- 465, The rollback test in failingReloadExecutor.Reload is only stubbing the
error path and never mutating the wrapped executor state, so the restore
assertion in executeWithScripts is not proving a real rollback. Update
failingReloadExecutor so it can simulate a stateful Reload by delegating to the
inner executor before/while returning the configured error, and keep the
reloaded capture so the assertions in runner_decorators_test still verify both
the rollback call order and that the baseExecutor scripts were actually
restored.
internal/module/artifact/pipeline/pipeline_test.go (1)

2094-2143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Solid coverage; consider adding an exact-boundary case.

Tests cover nil, single, dedup, empty-skip, all-empty, and over-limit (infoSummaryNameListLimit+1), but not the exact boundary (infoSummaryNameListLimit items) where the > comparison in summarizeInfoNames should still return the compact list rather than nil. Adding that case would guard against an off-by-one regression in the > vs >= check.

🤖 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 `@internal/module/artifact/pipeline/pipeline_test.go` around lines 2094 - 2143,
Add a test case in TestSummarizeInfoNames for the exact boundary of
infoSummaryNameListLimit, since summarizeInfoNames should still return the
compact name list when the input size equals the limit. Use the existing
summarizeInfoNames helper and compare against the over-limit case to verify the
> check does not accidentally behave like >=, and keep the assertion aligned
with the other subtests.
internal/module/evolution/hooks/runner_test.go (1)

636-666: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Tighten the rollback test coverage The restored-scripts check only exercises the explicit SetJsScripts(prevScripts) restore; because hooksFailingReloadExecutor.Reload short-circuits on reloadErr, it doesn’t prove the rollback Reload(prevScripts...) call has any effect. If the intent is to cover rollback behavior, make the failure wrapper delegate the second reload or drop this assertion.

🤖 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 `@internal/module/evolution/hooks/runner_test.go` around lines 636 - 666, The
rollback test is only verifying the direct SetJsScripts restore path, while
hooksFailingReloadExecutor.Reload currently returns early on reloadErr and never
exercises the rollback Reload(prevScripts...) behavior. Update the test around
hooksFailingReloadExecutor and the rollback flow so the failure wrapper still
delegates the rollback reload call, or remove the restored-scripts assertion if
that path is not meant to be covered. Keep the coverage focused on the actual
rollback behavior in the failing reload scenario.
🤖 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.

Nitpick comments:
In `@internal/module/artifact/pipeline/pipeline_test.go`:
- Around line 2094-2143: Add a test case in TestSummarizeInfoNames for the exact
boundary of infoSummaryNameListLimit, since summarizeInfoNames should still
return the compact name list when the input size equals the limit. Use the
existing summarizeInfoNames helper and compare against the over-limit case to
verify the > check does not accidentally behave like >=, and keep the assertion
aligned with the other subtests.

In `@internal/module/evolution/hooks/runner_test.go`:
- Around line 636-666: The rollback test is only verifying the direct
SetJsScripts restore path, while hooksFailingReloadExecutor.Reload currently
returns early on reloadErr and never exercises the rollback
Reload(prevScripts...) behavior. Update the test around
hooksFailingReloadExecutor and the rollback flow so the failure wrapper still
delegates the rollback reload call, or remove the restored-scripts assertion if
that path is not meant to be covered. Keep the coverage focused on the actual
rollback behavior in the failing reload scenario.

In `@internal/module/evolution/scripts/runner_decorators_test.go`:
- Around line 434-465: The rollback test in failingReloadExecutor.Reload is only
stubbing the error path and never mutating the wrapped executor state, so the
restore assertion in executeWithScripts is not proving a real rollback. Update
failingReloadExecutor so it can simulate a stateful Reload by delegating to the
inner executor before/while returning the configured error, and keep the
reloaded capture so the assertions in runner_decorators_test still verify both
the rollback call order and that the baseExecutor scripts were actually
restored.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5ff8766f-11a9-4045-880a-848bb41f2705

📥 Commits

Reviewing files that changed from the base of the PR and between c9beb75 and d3de00d.

📒 Files selected for processing (4)
  • internal/module/artifact/pipeline/pipeline_test.go
  • internal/module/evolution/hooks/runner_test.go
  • internal/module/evolution/scripts/runner_decorators_test.go
  • internal/module/lifecycle/modulemanager_logging_test.go

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.

1 participant