Skip to content

feat: add initializing and failed states to provider init lifecycle - #847

Merged
skevetter merged 6 commits into
mainfrom
ripe-panda
Aug 1, 2026
Merged

feat: add initializing and failed states to provider init lifecycle#847
skevetter merged 6 commits into
mainfrom
ripe-panda

Conversation

@skevetter

@skevetter skevetter commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Provider init previously only persisted a boolean Initialized, so any process reading provider state (CLI, desktop UI, MCP tool) saw "not initialized" the entire time Exec.Init ran, then "initialized" — with no visible in-between state, and no distinction between "never started" and "started but crashed/failed."
  • Adds a cross-process tri/four-state (not_initialized / initializing / initialized / failed) resolved via pkg/provider.ResolveInitState, using an advisory file lock (gofrs/flock, same pattern as existing workspace/task locks) held for the duration of Exec.Init as the liveness signal — self-healing to failed if the initializing process crashes, since the OS releases the lock.
  • Wires the resolved status into devsy provider list (plain + JSON), the provider_list MCP tool, and the desktop app (replacing a per-renderer-only "initializing" fake store with real backend-derived state in ProviderCard/ProviderSheet).

Test plan

  • go test ./pkg/provider/... ./cmd/provider/... ./pkg/workspace/... ./cmd/mcp/... — includes new pkg/provider/initlock_test.go covering never-attempted, live-lock (initializing), and crash-then-free-lock (failed) cases.
  • npx vitest run in desktop/ (290 tests) and npx svelte-check — both clean.
  • Not yet done: a live end-to-end pass in the built Electron app (add a provider, watch the badge transition, kill mid-init to see "failed") — no display/build pipeline available in this environment.

Summary by CodeRabbit

  • New Features

    • Added provider initialization statuses: not initialized, initializing, initialized, and failed.
    • Provider listings now display status information instead of a basic initialized flag.
    • Initialization attempts and recent error details are retained for clearer status reporting.
  • Bug Fixes

    • Improved initialization handling with serialization, reliable state persistence, and concise failure messages.
    • Provider status reporting now includes safe fallback behavior when status resolution fails.

…ycle

Provider init previously only tracked a persisted Initialized bool, so the
UI/CLI/MCP tool couldn't distinguish "in progress" or "crashed/failed" from
"never started" while a provider's init command ran. Adds a cross-process
tri-state (not_initialized/initializing/initialized/failed) derived from an
advisory file lock held for the duration of Exec.Init, so a crashed process
self-heals to "failed" instead of silently reverting.
@netlify

netlify Bot commented Aug 1, 2026

Copy link
Copy Markdown

Deploy Preview for images-devsy-sh canceled.

Name Link
🔨 Latest commit 054a573
🔍 Latest deploy log https://app.netlify.com/projects/images-devsy-sh/deploys/6a6e55921f441d000829cc48

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@skevetter, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 6 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f7f9bd68-b2ff-4280-b9bd-92b1539e58fb

📥 Commits

Reviewing files that changed from the base of the PR and between fdfe8f5 and 054a573.

📒 Files selected for processing (3)
  • cmd/provider/configure_shared.go
  • pkg/provider/initlock.go
  • pkg/provider/initlock_test.go
📝 Walkthrough

Walkthrough

Provider initialization now records attempts and errors, prevents concurrent initialization, and resolves four lifecycle states. Workspace loading and CLI, MCP, and desktop outputs expose the resolved provider status.

Changes

Provider initialization lifecycle

Layer / File(s) Summary
Initialization state and persistence
pkg/config/config.go, pkg/provider/initlock.go, cmd/provider/configure_shared.go, pkg/provider/initlock_test.go
Provider initialization records attempts and truncated errors, uses a provider-specific lock, and sets Initialized on success. State resolution reports not initialized, initializing, initialized, or failed. Tests cover persisted, locked, and interrupted states.
Workspace status resolution
pkg/workspace/provider.go
LoadAllProviders resolves and assigns provider status. It uses the persisted initialization flag as a fallback when resolution fails.
Status propagation to clients
cmd/provider/list.go, cmd/mcp/tools_provider.go, desktop/src/main/watcher.ts, desktop/src/renderer/src/lib/types/index.ts
CLI and MCP provider summaries expose status. Desktop parsing propagates status and defaults missing values to not_initialized. The renderer constrains status values with ProviderStatus.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProviderInit as initProvider
  participant ProviderState as ProviderConfig
  participant Workspace as LoadAllProviders
  participant Clients as CLI/MCP/Desktop
  ProviderInit->>ProviderState: persist initialization attempt
  ProviderInit->>ProviderState: persist success or truncated failure
  Workspace->>ProviderState: resolve provider initialization state
  Workspace->>Clients: provide provider status
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% 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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the addition of initializing and failed provider initialization states.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@netlify

netlify Bot commented Aug 1, 2026

Copy link
Copy Markdown

Deploy Preview for devsydev canceled.

Name Link
🔨 Latest commit 054a573
🔍 Latest deploy log https://app.netlify.com/projects/devsydev/deploys/6a6e5592086b8300085b84c3

@github-actions github-actions Bot added the size/l label Aug 1, 2026
@skevetter skevetter changed the title Add initializing/failed states to provider init lifecycle feat: add initializing/failed states to provider init lifecycle Aug 1, 2026
# Conflicts:
#	desktop/src/renderer/src/lib/components/provider/ProviderCard.svelte
#	desktop/src/renderer/src/lib/components/provider/ProviderCard.test.ts
#	desktop/src/renderer/src/lib/components/provider/ProviderSheet.svelte
#	desktop/src/renderer/src/lib/components/provider/ProviderSheet.test.ts
#	desktop/src/renderer/src/lib/components/provider/ProviderWizard.svelte
#	desktop/src/renderer/src/lib/stores/providers.ts
No mutex needed around the config mutation in initProvider: the flock
already serializes concurrent callers for the same provider, in-process
or across processes, since each call opens an independent file
description. Also trims verbose comments per review.
@skevetter
skevetter marked this pull request as ready for review August 1, 2026 20:07

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

Caution

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

⚠️ Outside diff range comments (1)
cmd/provider/configure_shared.go (1)

185-231: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reset Initialized before re-running init, or a failed retry is reported as initialized.

initProvider sets entry.InitAttempted = true and clears entry.InitError before running the command, but it never resets entry.Initialized. ResolveInitState in pkg/provider/initlock.go returns InitStateInitialized as soon as state.Initialized is true, before it ever looks at InitAttempted or InitError.

For a provider that previously initialized successfully, a later re-init attempt that fails leaves Initialized=true and InitError=<message> both persisted. ResolveInitState will still report initialized, so the failure is invisible to devsy provider list, the provider_list MCP tool, and the desktop UI, even though InitError is populated on disk.

Reset Initialized to false alongside InitAttempted and InitError before running the command, so a re-init in progress correctly resolves to initializing, and a failed re-init correctly resolves to failed.

🐛 Proposed fix to reset Initialized on each attempt
 	entry.InitAttempted = true
 	entry.InitError = ""
+	entry.Initialized = false
 	if err := config.SaveConfig(devsyConfig); err != nil {
 		return fmt.Errorf("save init state: %w", err)
 	}

Consider adding a regression test in pkg/provider/initlock_test.go that calls ResolveInitState with &config.ProviderConfig{Initialized: true, InitError: "boom"} to lock in the intended precedence once fixed.

🤖 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 `@cmd/provider/configure_shared.go` around lines 185 - 231, Update initProvider
to reset entry.Initialized to false alongside InitAttempted and InitError before
saving state and running the initialization command, so retries resolve as
initializing or failed rather than initialized. Preserve the existing success
path that sets Initialized to true, and add a regression test in
ResolveInitState coverage for an initialized provider with InitError to verify
the failure state is reported.
🤖 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 `@cmd/provider/configure_shared.go`:
- Around line 233-240: Update truncateInitError to truncate by Unicode rune
count rather than byte indices, while preserving the 500-rune maximum and
existing ellipsis behavior. Ensure the returned string remains valid UTF-8 when
multibyte characters occur at the truncation boundary.

In `@pkg/provider/initlock.go`:
- Around line 40-66: Update ResolveInitState to use the lock’s TryRLock() for
status probing instead of TryLock(), allowing concurrent read-only probes while
still failing when an exclusive initialization holds the lock. Preserve
TryLock() in initProvider for the active mutating initialization path and keep
the existing state and error handling unchanged.

---

Outside diff comments:
In `@cmd/provider/configure_shared.go`:
- Around line 185-231: Update initProvider to reset entry.Initialized to false
alongside InitAttempted and InitError before saving state and running the
initialization command, so retries resolve as initializing or failed rather than
initialized. Preserve the existing success path that sets Initialized to true,
and add a regression test in ResolveInitState coverage for an initialized
provider with InitError to verify the failure state is reported.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e9a69a25-46b6-4770-af11-54e15b02656a

📥 Commits

Reviewing files that changed from the base of the PR and between 4a2db36 and fdfe8f5.

📒 Files selected for processing (9)
  • cmd/mcp/tools_provider.go
  • cmd/provider/configure_shared.go
  • cmd/provider/list.go
  • desktop/src/main/watcher.ts
  • desktop/src/renderer/src/lib/types/index.ts
  • pkg/config/config.go
  • pkg/provider/initlock.go
  • pkg/provider/initlock_test.go
  • pkg/workspace/provider.go

Comment thread cmd/provider/configure_shared.go
Comment thread pkg/provider/initlock.go
@skevetter
skevetter marked this pull request as draft August 1, 2026 20:15
@skevetter skevetter changed the title feat: add initializing/failed states to provider init lifecycle feat: add initializing and failed states to provider init lifecycle Aug 1, 2026
…ead lock for probes

- initProvider left Initialized=true across a failed re-init, so a provider
  that previously succeeded reported "initialized" through a subsequent
  failed retry instead of "failed". Now reset alongside InitAttempted.
- truncateInitError sliced by byte index, which can split a multi-byte rune
  and produce invalid UTF-8. Truncate by rune count instead.
- ResolveInitState used an exclusive TryLock to merely probe the init lock,
  so two concurrent status reads could transiently report "initializing"
  for each other. Use TryRLock, which is compatible with other readers and
  still blocked by the exclusive lock initProvider holds while running.
@skevetter
skevetter marked this pull request as ready for review August 1, 2026 21:00
@skevetter
skevetter merged commit 9bdf783 into main Aug 1, 2026
68 checks passed
@skevetter
skevetter deleted the ripe-panda branch August 1, 2026 21:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant