Skip to content

fix: guard kokoro-local provider init timing on direct navigation - #2273

Open
stz-2044 wants to merge 10 commits into
moeru-ai:mainfrom
stz-2044:fix/kokoro-local-init-guard
Open

fix: guard kokoro-local provider init timing on direct navigation#2273
stz-2044 wants to merge 10 commits into
moeru-ai:mainfrom
stz-2044:fix/kokoro-local-init-guard

Conversation

@stz-2044

Copy link
Copy Markdown

Summary

When navigating directly to the speech settings page, the provider may not be registered from the catalog yet, so getProviderConfig() returns undefined and reading config.model throws. The model combobox can also write back v-model before onMounted runs (provider registration is an async synced action).

Changes

  • packages/stage-pages/.../kokoro-local.vue: await ensureProvider() when config is missing, then re-read; guard set()/watch() against missing config; fall back to {} where a read is unavoidable.
  • pnpm-lock.yaml: remove iOS DerivedData lock entries that were accidentally committed (Xcode build cache), add missing web-worker optional marker.

Verification

  • Direct navigation to the page no longer throws.
  • Model selection persists after provider registration.
  • pnpm install --frozen-lockfile passes locally.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 582212637e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +127 to +128
if (!providerStore.getProviderConfig(providerId)) {
await providerStore.ensureProvider(providerId, providerId, {})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve Kokoro's capability-aware default on direct navigation

When a user opens this route directly on WebGPU hardware with fp16 support and no saved kokoro-local config, this new path creates an empty config instead of the provider's default config. The fallback below then persists getDefaultKokoroModel(hasWebGPU.value) without fp16Supported.value, so the saved model becomes fp32-webgpu even though the provider schema and computed getter would choose fp16-webgpu. Seed the provider with its default config or pass fp16Supported.value when filling config.model.

Useful? React with 👍 / 👎.

@stz-2044

Copy link
Copy Markdown
Author

Thanks for the review! Fixed in 20e8a83: ensureProvider is now seeded with capability-aware defaults (model: getDefaultKokoroModel(hasWebGPU, fp16Supported), voiceId: "") matching createProviderConfig, and the fallback persist also passes fp16Supported — so fp16-capable hardware keeps its fp16-webgpu default and the saved value always matches the computed getter.

@lietblue lietblue added bug Something isn't working bug/providers Some providers aren't working pr-review/well-informed Pull Request that well described, with screenshots or tests, or explained well, easier to review scope/providers Scope related to providers we support scope/ui Scope related to UI/UX, or interface improve, perf, and bugs and removed pr-review/well-informed Pull Request that well described, with screenshots or tests, or explained well, easier to review labels Aug 13, 2026
When navigating directly to the speech settings page, the provider may
not be registered from the catalog yet, so getProviderConfig() returns
undefined and reading config.model throws. Also, the model combobox can
write back v-model before onMounted runs (provider registration is an
async synced action).

- await ensureProvider() when the config is missing, then re-read it
- guard set()/watch() against missing config
- fall back to {} where a read is unavoidable

Verified locally: direct navigation no longer throws; model selection
persists after registration.
…t navigation

When opening this route directly with no saved config, ensureProvider() was
seeded with an empty object, dropping the schema defaults. The fallback then
persisted getDefaultKokoroModel(hasWebGPU) without fp16Supported, so fp16-
capable hardware saved fp32-webgpu while the computed getter showed fp16-webgpu.

Seed with { model: getDefaultKokoroModel(hasWebGPU, fp16Supported), voiceId: '' }
matching createProviderConfig defaults, and pass fp16Supported in the fallback
too so the persisted default always matches what the getter would choose.
@stz-2044
stz-2044 force-pushed the fix/kokoro-local-init-guard branch from 20e8a83 to afddffd Compare August 13, 2026 16:43

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: afddffddff

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +130 to +133
await providerStore.ensureProvider(providerId, providerId, {
model: getDefaultKokoroModel(hasWebGPU.value, fp16Supported.value),
voiceId: '',
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Seed Kokoro with the full default config

When this direct-navigation path creates Kokoro config, it omits the baseUrl: '' field that getDefaultProviderConfig() adds for providers without a base URL. shouldListProvider() compares the saved object to that full default, so this partial config stays dirty and can make a passive page open list and revalidate Kokoro as configured. Seed from the provider defaults, or include the same default fields here.

Useful? React with 👍 / 👎.

Comment on lines +61 to +64
// The combobox can write back the v-model before onMounted runs, at which
// point the provider may not be registered yet and synced actions are still
// async. Skip the write in that window; onMounted persists the default model
// right after registration, and later user selections always hit a config.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Put the timing workaround in a NOTICE block

These lines document an initialization-timing workaround, but the repo requires every workaround comment to use the // NOTICE: block with root cause and removal condition. Without that format, later readers lose the required context for when this skipped write is safe.

AGENTS.md reference: AGENTS.md:L244-L250

Useful? React with 👍 / 👎.

- Seed provider with full default config shape (baseUrl: '') so
  shouldListProvider() comparison stays clean on direct navigation
- Convert the init-timing workaround comment to the repo's // NOTICE:
  format with root cause and removal condition

Addresses PR moeru-ai#2273 codex review suggestions.
@stz-2044

Copy link
Copy Markdown
Author

Both suggestions addressed in f1d977bb:

  1. baseUrl seed: ensureProvider now seeds baseUrl: "" alongside model/voiceId, matching the getDefaultProviderConfig() shape so the saved object is not dirty for shouldListProvider().
  2. NOTICE block: the init-timing workaround comment now uses the required // NOTICE: format with root cause summary, source reference, and removal condition.

Thanks for the review!

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f1d977bbf4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// of an empty object, so fp16-capable hardware keeps its fp16-webgpu default.
if (!providerStore.getProviderConfig(providerId)) {
await providerStore.ensureProvider(providerId, providerId, {
model: getDefaultKokoroModel(hasWebGPU.value, fp16Supported.value),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Await WebGPU detection before seeding Kokoro

When a user opens this page directly on an FP16-capable WebGPU browser with no saved kokoro-local config, this branch still uses fp16Supported.value before async WebGPU detection runs. Fresh evidence: repo-wide search found detectWebGPU() only in useInferencePreload.triggerPreload(), which root App.vue calls from onMounted, after this route has mounted. As a result, the cached capability is null, FP16 stays false, and this line persists fp32-webgpu while the fp16-webgpu option is absent until a reload. Await detectWebGPU() before fetching models and seeding the config, or delay persistence until the capability cache exists.

Useful? React with 👍 / 👎.

Direct navigation can mount the kokoro-local settings page before the
app-level preload (useInferencePreload -> detectWebGPU, from App.vue
onMounted) populates the capability cache. Reading the sync cache then
yields null, fp16Supported falls back to false, and fp16-capable
hardware gets seeded with fp32-webgpu until a reload.

Await detectWebGPU() before fetching models and seeding the config.
detectWebGPU() deduplicates concurrent calls, so this shares the same
promise as the app preload when both race.
@stz-2044

Copy link
Copy Markdown
Author

Thanks for the catch — the race is real. Direct navigation mounts this page before the app-level preload (useInferencePreloaddetectWebGPU, called from App.vue onMounted) populates the capability cache, so the sync cache read returned null and fp16Supported fell back to false — seeding fp32-webgpu on fp16-capable hardware until a reload.

Fixed in e608514bc: the page now awaits detectWebGPU() before fetching models and seeding the config. detectWebGPU() deduplicates concurrent calls via pendingDetection, so when the app preload is already in flight this awaits the same promise instead of triggering a second adapter request — no duplicate detection, no extra cost.

Verified: vue-tsc --noEmit passes (0 errors).

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e608514bc3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// of an empty object, so fp16-capable hardware keeps its fp16-webgpu default.
if (!providerStore.getProviderConfig(providerId)) {
await providerStore.ensureProvider(providerId, providerId, {
model: getDefaultKokoroModel(hasWebGPU.value, fp16Supported.value),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep the seeded default comparable to provider defaults

When direct navigation happens on fp16-capable WebGPU hardware, this saves fp16-webgpu, but useProviderStore() is created at module setup before this awaited detection runs. Its providerMetadata default for Kokoro was already selected from getCachedWebGPUCapabilities() === null, so it remains fp32-webgpu; shouldListProvider() then treats this passive seed as a user-modified config and lists Kokoro as dirty/unconfigured. Use the same default source for seeding and dirty comparison, or refresh the provider default after detectWebGPU().

Useful? React with 👍 / 👎.

The provider metadata (including Kokoro's capability-dependent default
model) is selected at store setup, before WebGPU capability detection
completes. Seeding a capability-accurate default after awaiting
detectWebGPU() therefore drifted from providerMetadata.defaultConfig,
so isProviderConfigDirty() treated the passive seed as a user-modified
config and shouldListProvider() listed Kokoro as dirty/unconfigured.

- add refreshProviderDefaultConfig() to the providers store: re-selects
  a provider's default config with current runtime state
- kokoro-local page: refresh the default after await detectWebGPU(),
  then seed with getDefaultProviderConfig() - the same source the dirty
  comparison uses - so the seed is never flagged as a user edit, while
  fp16-capable hardware still keeps its fp16-webgpu default
@stz-2044

Copy link
Copy Markdown
Author

Good catch — the drift was real. providerMetadata (including Kokoro's capability-dependent defaultConfig) is selected at store setup, before capability detection completes, so seeding a capability-accurate default after await detectWebGPU() made isProviderConfigDirty() see a user-modified config and shouldListProvider() list Kokoro as dirty.

Fixed in 971bb969e:

  1. refreshProviderDefaultConfig(providerId) added to the providers store — re-selects a provider's defaultConfig with current runtime state (same createProviderConfiggetSchemaDefault path used at metadata selection).
  2. The page now refreshes the default right after await detectWebGPU(), then seeds with getDefaultProviderConfig(providerId) — the exact same source the dirty comparison reads. So the passive seed always equals the comparison baseline: isProviderConfigDirty() stays false, while fp16-capable hardware still keeps its fp16-webgpu default (round 1 requirement intact).

Verified: vue-tsc --noEmit passes for both stage-ui and stage-pages; 618/618 node tests pass (the browser-mode test files did not run here — local Playwright browser binary is missing, unrelated to this change).

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 971bb969ed

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// Source: PR #2273 review (codex bot), rounds 1 & 4.
// Removal condition: when the provider is guaranteed to be registered with
// capability-accurate defaults before this page can mount.
if (!providerStore.getProviderConfig(providerId)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refresh child-seeded Kokoro configs too

When a user opens this page directly on FP16-capable WebGPU hardware with no saved Kokoro config, the child SpeechProviderSettings mounts first and calls initializeProvider() with the stale pre-detection default. Fresh evidence in this patch: this branch then skips ensureProvider(), while refreshProviderDefaultConfig() above changes the dirty-comparison baseline to fp16-webgpu. As a result, the page keeps and loads the stale fp32-webgpu config, and shouldListProvider() treats that passive seed as a user edit. Replace the existing default config when it still matches the old provider default, or move capability-aware initialization before the child can seed the provider.

Useful? React with 👍 / 👎.

On direct navigation, the child SpeechProviderSettings mounts first and
calls initializeProvider() with the stale pre-detection default
(fp32-webgpu) before the parent page awaits detectWebGPU(). The page
then skips ensureProvider() (config exists), while
refreshProviderDefaultConfig() moves the dirty-comparison baseline to
fp16-webgpu - leaving a stale fp32 config loaded and flagged as a user
edit by shouldListProvider().

refreshProviderDefaultConfig() now snapshots the previous default and,
when the saved config still exactly matches it (a passive seed, not a
user edit), replaces it with the refreshed default via a new synchronous
replaceProviderConfig() on the config store (local mutation, no remote
round-trip, by-reference so configs/providerCredentials pick it up).
@stz-2044

Copy link
Copy Markdown
Author

Confirmed — the child SpeechProviderSettings mounts first and initializeProvider() seeds the provider with the stale pre-detection default before the parent page awaits detectWebGPU(), so my ensureProvider() guard skipped the seed and the stale fp32-webgpu config stayed loaded while the refreshed baseline moved to fp16-webgpu.

Fixed in 3e37763c2refreshProviderDefaultConfig() now:

  1. Snapshots the previous default before refreshing.
  2. Refreshes defaultConfig as before.
  3. If the saved config still exactly matches the stale default (a passive seed, not a user edit), replaces it with the refreshed default via a new synchronous replaceProviderConfig() on the config store — a local mutation with no remote round-trip that swaps the config object by reference, so configs/providerCredentials and the dirty comparison all pick it up immediately.

A real user edit never matches the stale default, so it is never clobbered. Verified: vue-tsc --noEmit passes for both packages; 618/618 node tests pass.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e37763c24

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const previousDefault = getDefaultProviderConfig(providerId)
meta.defaultConfig = getSchemaDefault(definition.createProviderConfig({ t })) as Record<string, unknown>
const saved = providerCredentials.value[providerId]
if (saved && JSON.stringify(saved) === JSON.stringify(previousDefault)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Ignore passive fields when refreshing Kokoro defaults

When a user opens the Kokoro page directly on FP16 WebGPU hardware with no saved config, SpeechProviderSettings mounts first and writes an empty apiKey into the stale default config. Fresh evidence in this version: this exact JSON check then fails because saved has passive fields that previousDefault does not. As a result, replaceProviderConfig() does not run, the page keeps fp32-webgpu, and shouldListProvider() treats the passive seed as a user edit after the default changes to fp16-webgpu. Compare only the provider default fields, or update model when it still equals the old default.

Useful? React with 👍 / 👎.

SpeechProviderSettings mounts before the parent page and writes passive
fields (e.g. apiKey: '') into the seeded config via its computed setters.
A full JSON equality check between the saved config and the stale default
then fails, so replaceProviderConfig() never runs and the page keeps
fp32-webgpu while the refreshed baseline moves to fp16-webgpu.

Compare only the fields that make up the provider default: if they are
all untouched the config is still the passive seed and safe to replace.
Any deviation means a user edit and is never clobbered.
@stz-2044

Copy link
Copy Markdown
Author

Confirmed — SpeechProviderSettings mounts first and its apiKey/baseUrl computed setters write passive fields (e.g. apiKey: '') into the seeded config, so a full JSON.stringify equality check failed and replaceProviderConfig() never ran.

Fixed in b322aff5c: the untouched-seed check now compares only the fields that make up the provider default — if every default field still equals the stale default, the config is the passive seed and is replaced with the refreshed default; any deviation means a user edit and is never clobbered (passive fields like apiKey are ignored either way).

Verified: vue-tsc --noEmit passes for both packages; the node test suite passes (618 tests; browser-mode files don't run locally due to a missing Playwright binary, unrelated to this change).

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b322aff5c9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +437 to +442
const isUntouchedPassiveSeed = saved && Object.keys(previousDefault)
.every((key) => {
const savedValue = (saved as Record<string, unknown>)[key]
const defaultValue = (previousDefault as Record<string, unknown>)[key]
return savedValue === defaultValue
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not overwrite saved Kokoro model choices

If an fp16 WebGPU user manually selects fp32-webgpu, a later reload starts with an empty WebGPU cache, so the stale previousDefault is also fp32-webgpu. This predicate then treats the saved model choice as an untouched passive seed and replaces it with fp16-webgpu, because it has no state that distinguishes a persisted user config from an auto-seeded config. Track the direct-navigation seed explicitly, or only replace configs created during this mount.

AGENTS.md reference: AGENTS.md:L198-L200

Useful? React with 👍 / 👎.

On reload with an empty WebGPU cache, the stale default snapshot equals
fp32-webgpu - the same value a user on fp16 hardware may have manually
selected. The untouched-seed predicate could not distinguish the two, so
refreshProviderDefaultConfig() would replace the user's deliberate choice
with fp16-webgpu.

Replacement now requires the caller to opt in via replaceUntouchedSeed:
the kokoro-local page captures whether a persisted config already existed
before this mount (setup runs before the child SpeechProviderSettings can
seed one) and only allows replacement when it did not - a config created
during this mount is a passive seed; a persisted one is user-owned and
never replaced.
@stz-2044

Copy link
Copy Markdown
Author

Confirmed — on reload with an empty WebGPU cache, the stale default snapshot is also fp32-webgpu, so a user who deliberately picked fp32-webgpu on fp16 hardware is value-identical to a passive seed and would get replaced. Value equality alone cannot distinguish the two.

Fixed in 62f1dcea1 — replacement now requires the caller to opt in via replaceUntouchedSeed:

  • refreshProviderDefaultConfig(providerId, { replaceUntouchedSeed }) only replaces when the flag is explicitly true and the default fields are untouched.
  • The kokoro-local page captures hadConfigBeforeMount in setup (which runs before the child SpeechProviderSettings can seed the provider) and passes replaceUntouchedSeed: !hadConfigBeforeMount — a persisted config is user-owned and never replaced; only a config created during this mount (a true passive seed) qualifies.

So a manually saved model choice survives reloads, while the direct-navigation seed still gets corrected. Verified: vue-tsc --noEmit passes for both packages; node tests pass.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 62f1dcea11

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +153 to +155
providersStore.refreshProviderDefaultConfig(providerId, {
replaceUntouchedSeed: !hadConfigBeforeMount,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Gate the Kokoro watcher during initial seeding

When direct navigation has no saved Kokoro config on WebGPU, SpeechProviderSettings mounts first and seeds the stale default, which changes model and lets the watcher below start loading fp32-webgpu while this parent onMounted still awaits capability detection. This refresh then switches the config to the capability-aware default, so the correct load queues behind an unnecessary stale model load. Gate the watcher until initial seeding finishes, or initialize the provider before the child can seed it.

Useful? React with 👍 / 👎.

On direct navigation without a saved config, the child
SpeechProviderSettings seeds the stale default, which changes `model`
and fires the watcher - loading fp32-webgpu while the parent onMounted is
still awaiting capability detection. The refresh then swaps in the
capability-aware default, queueing the correct load behind an
unnecessary stale model load.

Gate the watcher with an initialSeeding flag until onMounted finishes
initial seeding; the parent already loads the correct model explicitly
after refresh, so no model load is lost.
@stz-2044

Copy link
Copy Markdown
Author

Confirmed — the child SpeechProviderSettings seeding the stale default changes model and fires the watcher, loading fp32-webgpu while the parent onMounted is still awaiting capability detection; the refresh then queues the correct load behind an unnecessary stale one.

Fixed in bd5fc19e3: a initialSeeding flag gates the watcher until onMounted finishes initial seeding. The parent already loads the correct model explicitly after the refresh (in the same onMounted), so no model load is lost — the watcher simply no longer races ahead with the stale default. The gate is released in the finally block, so a failed seed also resumes the watcher safely.

Verified: vue-tsc --noEmit passes for both packages.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bd5fc19e39

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if (!definition || !meta)
return
const previousDefault = getDefaultProviderConfig(providerId)
meta.defaultConfig = getSchemaDefault(definition.createProviderConfig({ t })) as Record<string, unknown>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Route Kokoro default refresh through synced store

In the synced Electron runtime, this method changes store-local providerMetadata. But refreshProviderDefaultConfig is not in this store's synced.actions block. I checked requestListedProviderValidation(): background validation routes through the synced public store. Thus a direct navigation from a non-leader renderer can refresh Kokoro's default to fp16-webgpu only locally, while the leader still compares the saved config with the stale fp32-webgpu default and lists or validates the passive seed as dirty. Add this action to the synced list, or move the refresh into an already synced mutation.

Useful? React with 👍 / 👎.

In the synced Electron runtime, refreshProviderDefaultConfig() mutated
store-local providerMetadata and replaceProviderConfig() mutated the
config store without being in either store's synced.actions block. A
direct navigation from a non-leader renderer would refresh the default
to fp16-webgpu only locally, while the leader kept comparing the saved
config against the stale fp32-webgpu default - listing or validating
the passive seed as dirty.

Add both actions to their stores' synced lists so the refresh (and the
config replacement it performs) runs on the leader and the resulting
state propagates to every renderer.
@stz-2044

Copy link
Copy Markdown
Author

Confirmed — in the synced Electron runtime, refreshProviderDefaultConfig() mutated store-local providerMetadata and replaceProviderConfig() mutated the config store, neither routed through synced.actions. A non-leader renderer direct-navigating would refresh the default locally while the leader kept comparing against the stale default.

Fixed in 96943acac: both actions are now in their stores' synced.actions blocks (refreshProviderDefaultConfig on the provider store, replaceProviderConfig on the config store). With pinia-plugin-synced's leader-execution semantics, the refresh runs on the leader and the resulting config state propagates to every renderer, so the dirty comparison uses the capability-aware default everywhere.

One note: providerMetadata itself is a per-renderer setup-time constant in the existing store design (not synced state), so a non-leader window that already ran its own dirty comparison before the refresh broadcast could still show a stale listing until its computed re-evaluates against the synced config — that's a pre-existing architecture trait outside this PR's scope, and the navigation window itself is always consistent.

Verified: vue-tsc --noEmit passes for both packages; node tests pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug/providers Some providers aren't working bug Something isn't working scope/providers Scope related to providers we support scope/ui Scope related to UI/UX, or interface improve, perf, and bugs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants