From 93e3143573a03ec6b3cffa891132c62469ac9218 Mon Sep 17 00:00:00 2001 From: brother7 <7brother7@gmail.com> Date: Tue, 1 Sep 2026 23:59:36 +0800 Subject: [PATCH] fix(plugins): preserve unavailable release failures --- ...01-web-search-runtime-stale-r2-6f4a2d91.md | 135 ++++++++++++++++++ electron/coding-plugins/hosted-admission.ts | 3 + tests/unit/hosted-admission.test.ts | 27 ++++ 3 files changed, 165 insertions(+) create mode 100644 .project-docs/30-worklog/tasks/20260901-web-search-runtime-stale-r2-6f4a2d91.md diff --git a/.project-docs/30-worklog/tasks/20260901-web-search-runtime-stale-r2-6f4a2d91.md b/.project-docs/30-worklog/tasks/20260901-web-search-runtime-stale-r2-6f4a2d91.md new file mode 100644 index 0000000..de363c5 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260901-web-search-runtime-stale-r2-6f4a2d91.md @@ -0,0 +1,135 @@ +# Task: Diagnose Web Search runtime stale after reinstall + +## Identity + +- Task ID: 20260901-web-search-runtime-stale-r2-6f4a2d91 +- Mode: Feature +- Branch: codex/20260901-web-search-runtime-stale-r2-6f4a2d91-web-search-runtime-stale-r2 +- Worktree: D:\Datas\OthersProjects\makelore-web-search-runtime-stale-r2-6f4a2d91 +- Base commit: 2c5b60ea78e014a712a47bdbb9efd156f52f3a1d +- Owner: codex-root +- Status: Ready for Integration + +## Scope + +- Diagnose the installed-client `plugin_runtime_stale` failure after reinstall and a + newly created Conversation, using the latest Pi session, installed artifact metadata, + Main log, frozen worker manifest, and server Admission response as evidence. +- Establish a deterministic read-only reproduction/inspection loop before reading or + changing product code. If the fault is client-owned, make the smallest correction in + the Marketplace/effective-resolver/hosted-admission/Web Search invocation seam with + focused regressions. If evidence locates a deployed server-state problem, stop client + mutation and report the exact server prerequisite instead. +- Preserve the user root worktree and its two unrelated untracked package records. All + product, test, and task-record writes remain in this isolated worktree. + +## Intent And Constraints + +- Concurrent Task Gate: Passed. Task `20260901-web-search-runtime-stale-r2-6f4a2d91` + owns this worktree/branch at exact base + `2c5b60ea78e014a712a47bdbb9efd156f52f3a1d`; the dirty root was not adopted, + stashed, reset, cleaned, or modified. +- Planning Gate: Passed after reading the project-memory startup set, current state, + decisions, architecture/module/data-flow, business rules, success criteria, + glossary, evidence/reflection/commitment/stale indexes, the prior assignment and + frozen-worker corrections, official bundled-plugin integration, Web Search main + merge, and the only active Web Search peer record. +- The old coordinator task `20260831-web-search-client-5b9d2e63` remains at base + `0a86ec825...` with no live ownership of this post-merge runtime failure. No semantic + or write-scope conflict was found. +- Confirmed product invariants: a new parent worker must freeze an acquired, enabled, + assigned, exact official bundled Release; child workers receive no hosted tool; + invocation still requires current account/project/policy and exact server-owned + Release Admission; Renderer/Pi must not receive credentials or Provider authority. +- The previous source correction permits only a verified frozen worker to survive a + transient stale Library projection. The user's fresh reinstall/new Conversation + disproves that correction as a complete explanation, so it will not be broadened + without new evidence. +- Do not restart or terminate the running client, invoke paid Web Search/OpenAI, alter + Account Library/project/Agent state, package, push, deploy, publish, or touch server + state without a separately evidenced need and authority. + +## Project Context Loaded + +- Project goal: keep Electron Main as the authenticated Marketplace/hosted-Plugin + authority while exposing only frozen, admitted parent tools to Pi. +- Current integrated focus: Web Search is an official bundled optional hosted Plugin; + assignment discovery and frozen-worker stale-Library fixes are already on `main`. +- Active task scope: isolate the remaining installed-client `plugin_runtime_stale` + boundary and correct only the proven owner. +- Decisions affecting this task: ADR-006 single Pi runtime and frozen logical-thread + ownership; distinct Library, project, Agent, runtime Admission, confirmation, and + billing states; no hot-swap and no child inheritance. +- Evidence/commitments: installed artifacts must be verified rather than inferred from + source; live paid Provider and production activation remain out of scope. +- Likely modules if client-owned: `electron/coding-plugins/registry.ts`, + `effective-resolver.ts`, `hosted-admission.ts`, `marketplace-client.ts`, Web Search + adapter, and their focused tests. +- Unknowns to resolve first: exact installed source/verification head; exact newest Pi + tool event and frozen manifest; whether failure occurs before or inside hosted + Admission; whether `/resolve` reports stale, mismatched request identity, or a + different exact Release. +- Gate result: Passed. + +## Diagnosis Evidence + +- The newest Pi session deterministically reproduces two non-billing + `plugin_runtime_stale` results for `makelore_web_search`; both stop before Provider + dispatch or Token Point reservation. +- The new worker manifest is current and correct: parent-only Web Search Skill/tool, + Release `00000000-0000-4000-8000-000000000204`, version `1.0.0`, contract v1, + exact project/account snapshot, and platform-metered policy are frozen. +- The installed `app.asar` is version `1.2.4` and contains the prior + `resolveForInvocation` fix, disproving missing installation, stale conversation, + missing Agent assignment, and missing prior product code. +- Live anonymous Marketplace detail returns `stable_release=null` and + `stable_version=null`. The server task history confirms `0077` lists metadata only; + `0078` creates the exact bundled stable Release and has not been deployed. +- The integrated `0078` migration additionally declares `min_makelore_version=2.0.0`, + while the complete compatible runtime is demonstrably present in installed + MakeLore `1.2.4`. Once `0078` is deployed this would become the next Admission + blocker. +- The remaining client-owned defect is diagnostic fidelity: an explicit Marketplace + `action=unavailable` result is currently folded into `plugin_runtime_stale` instead + of the existing typed `plugin_release_unavailable` failure. + +## Outcome + +- Corrected the hosted Admission boundary so an explicit Marketplace + `action='unavailable'` becomes the existing typed + `plugin_release_unavailable` failure instead of being folded into the unrelated + `plugin_runtime_stale` worker-snapshot error. +- Added regressions for both currently reachable unavailable reasons: + `plugin_release_not_ready` and `plugin_client_incompatible`. +- The fix is deliberately diagnostic, not a client-side bypass: exact frozen Release, + Account, project, policy, Admission, Provider, and billing validation remain intact. + The actual production prerequisite is the server's missing `0078` Release migration + followed by the `0079` compatibility correction. + +## Verification + +- Deterministic installed-client evidence: newest Pi session reproduced two + `plugin_runtime_stale` Web Search calls; the frozen parent manifest and installed + `1.2.4` app contained the expected Skill, tool, Release, account/project snapshot, + and prior resolver implementation. +- TDD RED: both explicit unavailable-reason cases returned + `plugin_runtime_stale`; GREEN after the boundary correction. +- `pnpm exec vitest run tests/unit/hosted-admission.test.ts + tests/unit/web-search-plugin-adapter.test.ts + tests/unit/game-resource-plugin-adapter.test.ts`: 3 files, 25 tests passed. +- `pnpm exec tsc --noEmit`: passed. +- Scoped ESLint for the changed implementation and test: passed. +- `git diff --check`: passed. No installed client, user root worktree, Account/Library, + project/Agent assignment, Provider, billing, or production state was mutated. + +## Follow-ups + +- Integrate this small diagnostic correction into the next MakeLore build so future + Release availability failures remain truthful. It is not required to make the + current installed runtime compatible once the server Release/migration gate is fixed. +- Deploy/apply server `0078` and `0079`, reopen the worker, then verify Provider + readiness before performing an explicitly authorized paid Web Search smoke. + +## Promotion Candidates + +- None recorded. diff --git a/electron/coding-plugins/hosted-admission.ts b/electron/coding-plugins/hosted-admission.ts index f3ae3aa..e8f8e14 100644 --- a/electron/coding-plugins/hosted-admission.ts +++ b/electron/coding-plugins/hosted-admission.ts @@ -192,6 +192,9 @@ export class MarketplaceHostedAdmissionResolver { throw stale('Hosted Plugin worker admission is stale'); } const item = resolved.items.find(({ pluginId: candidate }) => candidate === plugin); + if (item?.action === 'unavailable') { + throw unavailable('Hosted Plugin Release is unavailable'); + } const matches = item && (bundled ? matchesBundledFrozenRelease(item, bundled, plugin) : matchesFrozenRelease(item, installed, plugin)); diff --git a/tests/unit/hosted-admission.test.ts b/tests/unit/hosted-admission.test.ts index e50967e..81321d9 100644 --- a/tests/unit/hosted-admission.test.ts +++ b/tests/unit/hosted-admission.test.ts @@ -122,6 +122,33 @@ describe('MarketplaceHostedAdmissionResolver', () => { expect(error).toMatchObject({ code: 'plugin_runtime_stale', status: 409, retryable: false }); }); + it.each([ + 'plugin_release_not_ready', + 'plugin_client_incompatible', + ])('preserves an explicit unavailable Release as plugin_release_unavailable (%s)', async (reason) => { + const marketplace = { + resolve: vi.fn(async () => resolved({ + action: 'unavailable', + releaseAdmissionId: null, + reason, + })), + } as unknown as Pick; + const unavailableResolver = new MarketplaceHostedAdmissionResolver({ + packageStore: { getInstalledRelease: vi.fn(async () => installed()) } as unknown as Pick, + marketplace, + makeloreVersion: '1.2.4', + }); + + await expect(unavailableResolver.resolve({ + pluginId: PLUGIN_ID, + workerSnapshot: { requestId: REQUEST_ID, pluginReleaseId: RELEASE_ID }, + })).rejects.toMatchObject({ + code: 'plugin_release_unavailable', + status: 409, + retryable: false, + }); + }); + it('projects an account change as a typed non-retryable worker failure', async () => { const { resolver, resolve } = resolverFixture(); resolve.mockRejectedValueOnce({ code: 'marketplace_account_changed' });