diff --git a/.project-docs/30-worklog/tasks/20260921-game-audio-client-b74d9f31.md b/.project-docs/30-worklog/tasks/20260921-game-audio-client-b74d9f31.md index 49fe178..91013f2 100644 --- a/.project-docs/30-worklog/tasks/20260921-game-audio-client-b74d9f31.md +++ b/.project-docs/30-worklog/tasks/20260921-game-audio-client-b74d9f31.md @@ -29,23 +29,30 @@ - New adapter + one official optional bundled game-audio plugin expose music full, explicit music preview, sound single/pack/variants, cancel. Native price/release/project/request authority stays in Main. Four static tool-operation mappings, project-wide parent availability and empty child scope; no new picker/store/partner gate or provider secrets. - Result card displays saved relative paths/MIME/bytes and loads local audio only after a click, with native controls/no autoplay. Reuses bounded project media reader through a receipt/account-owned Host route; no Provider URL or audio payload enters model messages. Composition shares leases/tracker and resumes receipts. Registry manifest duplicate checks now use plugin/capability/operation identity (global tool names remain unique), permitting two distinct audio capabilities to both name operation generate. - Additional owned paths beyond the initial outline: read-only saved-audio Host route and composition service port, export of existing media reader, Chat progress/preview component and test, relevant effective-resolver/manifest/Registry/artifact assertions, one synthetic-audio Electron case reusing the existing first-chat fixture, README and package version 2.0.1. No plugin navigation/store authority changes. +- Packaged continuation corrected two actual pre-existing artifact-check assumptions in the already-owned verifier: schema-2 skill_only project-scaffold legitimately has no adapter/tools, while hosted plugins still require tools; current Renderer uses plugin-tab-available/plugin-tab-installed rather than deleted introductory copy. Tests cover the real shipped scaffold and negative hosted-without-tools case. No application/Plugin UI code changed in this continuation. ## Verification +- 2026-09-21 continuation: same task start/status PASS, original base/branch/ownership retained. Reused unchanged project context and audio design, re-read AGENTS/own record and active peer scopes (read-only/undefined reviews and WeChat diagnosis). Planning Gate Passed for local packaged artifact verification only. Build output remains ignored and unpublished; no root workspace, user installation, production API or paid-generation changes. + - TDD RED→GREEN at transport, delivery, adapter/Registry seams; tests cover literal confirmation, fixed mode/pricing/identity, streamed bounds, account change, lost response, automatic recovery without repeat POST, partial restart, owner-only local preview, no fake not_started after submission and nullable receipt normalization. - Final focused set before the normalization refinement: 7 files / 45 passed. Final refined transport/adapter/delivery slice: 3 files / 13 passed. Typecheck PASS. Full lint PASS (0 errors, 8 existing CloudAgents/CloudCosts/Home/Makelore warnings); final scoped ESLint includes the added Electron test, PASS. - Full initial unit run: 2,133 passed / 2 skipped / 1 failed (unchanged pi-managed-worker-opener test expects runtime-v6, unchanged runtime is v7). Subsequent broad run excluding only that stale test file and isolated pressure: 244 files / 2,136 passed / 2 skipped; pressure 1/1 PASS. Later additions tested in focused runs; no false full-clean claim. -- Vite Renderer/Main/Preload/utility build PASS. Artifact verifier now requires game-audio ID and hosted route; artifact contract tests PASS. No Windows installer or signed app.asar was produced, so packaged GA-04 acceptance remains open. +- Initial Vite Renderer/Main/Preload/utility build PASS. Artifact verifier requires game-audio ID and hosted route. The continuation below builds and checks a Windows installer; real-provider GA-04 acceptance remains open. - Actual built Electron synthetic-audio test PASS 1/1 (2.5s): saved result→expand process/tool→explicit preview→native audio duration 0.25s and paused/no autoplay; screenshot visually inspected. It mocks the existing Host seam with a synthetic PCM WAV, not Meowa generation or live billing. First run only needed opening the existing collapsed process group in the test. - Existing plugin-marketplace/project-plugins Electron diagnostics 4/4 FAIL on stale assertions (removed introductory copy, old local/bundled buttons, old scope=project URL). Those test files and Plugins/ProjectConfiguration product modules have no task diff; current base uses 可选/已安装 tabs and scope=all. These failures are recorded, not hidden or repaired as part of audio. +- Windows continuation: `corepack pnpm run package:win` PASS with --publish never; installer release/Makelore-2.0.1-win-x64.exe (212375107 bytes) and win-unpacked/Makelore.exe produced locally. Product source is committed 09e0ce5cf3d113b3bb7b128b2b04bc355d16173b; at build time only this task record was dirty. Later continuation changes are verifier/test/documentation only. Packaged metadata has no embedded gitCommit; the runtime verifier reports source HEAD as fallback, so this is not represented as embedded commit attestation. +- `node scripts/verify-packaged-electron-runtime.mjs --allow-dirty --manifest release/game-audio-artifact-verification.json` PASS: Electron 43.4.0 / Node 24.18.1, native/Unicode-copy checks, self-contained Python/uv/npm and installer existence. --allow-dirty covered the owned task record only at that time. Existing verifier SHA256 output for the installer: 27741ADBD9CFD45C10701D1146A88866CAF7ED2F6B541EA41AED54FC849C9F32. No new checksum mechanism was added. +- `node scripts/verify-pi-product-artifact.mjs --report release/game-audio-pi-verification.json` PASS on that actual unpacked product after the two verifier corrections: shipped audio manifest/Skill/four tools, main-reachable hosted route, all bundled resources and pinned Pi 0.84.2 closure/worker/session tests. Its global cross-platform runtime aggregation remains partial (no Linux run here; macOS/prior Provider waivers are not new acceptance). `node scripts/verify-publish-runtime.mjs` PASS for packaged npm 11.6.2; this command verifies only and does not publish. +- Final verifier unit tests 18/18 PASS; scoped ESLint and final typecheck PASS; task-aware doc drift and diff-check PASS. Raw artifact reports stay under ignored release/. Paired server task separately verified real PostgreSQL 16.13 migrations and concurrent billing with synthetic Meowa responses. No real Meowa generation, user-machine install, signing/distribution claim, merge, push or production activation occurred. ## Follow-ups -- GA-00/GA-04 HOLD: real Meowa final result shape, MIME/size/count/expiry/charge and PostgreSQL concurrency not verified. No new fee/rate invented and no live generation, publication, install, push, deploy or merge. +- GA-00/GA-04 HOLD: real Meowa final result shape, MIME/size/count/expiry/charge and paired live accepted-generation journey remain unverified. Scoped PostgreSQL concurrency and Windows packaging are now verified separately, not substitutes for that journey. No new fee/rate invented and no live generation, publication, install, push, deploy or merge. - A real accepted generation using the paired server and packaged client still requires separately bounded paid-canary authorization, plus actual price activation by Operations. The playable synthetic Electron fixture is not a substitute. - Unrelated base runtime-v6 and old plugin-page E2E assertions need their own maintenance task; no source reset/stash/clean or root worktree writes performed. ## Promotion Candidates - Target: current-state/module-map/domain rules at integration. Add project-wide Game Audio >=2.0.1, single confirmation→internal polling→all-file auto-save, separate job/delivery/billing phases and receipt-owned local preview. Evidence: public seams, actual built Electron test and paired server record. No conflict with image delivery, Data Service or selected-model Web Search; shared IDs and protocol remain unchanged. -- Target: evidence/commitments. Preserve real-provider/PG/packaged activation holds and exact baseline diagnostic failures. Human confirmation required for paid canary/activation; no canonical promotion in this feature worktree. +- Target: evidence/commitments. Promote scoped Windows package and paired PostgreSQL evidence; preserve real-provider/end-to-end activation holds and exact baseline diagnostic failures. Human confirmation required for paid canary/activation; no canonical promotion in this feature worktree. diff --git a/scripts/lib/pi-product-artifact.mjs b/scripts/lib/pi-product-artifact.mjs index 94c2ffe..e93123f 100644 --- a/scripts/lib/pi-product-artifact.mjs +++ b/scripts/lib/pi-product-artifact.mjs @@ -69,7 +69,8 @@ const MARKETPLACE_ARTIFACT_MARKERS = Object.freeze({ renderer: Object.freeze([ '/api/coding/plugin-marketplace/catalog', '/api/coding/plugin-marketplace/library', - '为你的智能体添加插件,拓展更多能力。', + 'plugin-tab-available', + 'plugin-tab-installed', 'acquire', 'install_stable', 'enable_project', @@ -645,17 +646,18 @@ export async function verifyBundledCodingPluginResources({ if (packagedCapability.schemaVersion === 1 && sdkAssetPaths.length === 0) { throw new Error(`Packaged schema-1 coding plugin has no SDK assets: ${packageName}`); } - const adapterId = packagedCapability.schemaVersion === 2 + const skillOnly = packagedCapability.schemaVersion === 2 && packagedCapability.runtime?.kind === 'skill_only'; + const adapterId = skillOnly ? null : packagedCapability.schemaVersion === 2 ? packagedCapability.pluginId : packagedCapability.adapterId; const tools = Array.isArray(packagedCapability.tools) ? packagedCapability.tools : []; const toolNames = tools.map((tool) => tool?.name); - if (typeof adapterId !== 'string' || adapterId.length === 0 || tools.length === 0 + if ((skillOnly ? tools.length !== 0 : typeof adapterId !== 'string' || adapterId.length === 0 || tools.length === 0) || toolNames.some((name) => typeof name !== 'string' || name.length === 0) || new Set(toolNames).size !== toolNames.length) { throw new Error(`Packaged coding plugin adapter/tool catalog is invalid: ${packageName}`); } - catalogMarkers.push(packagedManifest.name, adapterId, ...toolNames); + catalogMarkers.push(packagedManifest.name, ...(adapterId ? [adapterId] : []), ...toolNames); packages.push({ id: packagedManifest.name, version: packagedManifest.version, @@ -690,7 +692,7 @@ export async function verifyBundledCodingPluginResources({ root: BUNDLED_CODING_PLUGIN_RESOURCE_ROOT, packages, catalog: { - adapters: [...new Set(packages.map(({ adapterId }) => adapterId))], + adapters: [...new Set(packages.map(({ adapterId }) => adapterId).filter(Boolean))], tools: [...new Set(packages.flatMap(({ tools }) => tools))], packagedInAsar: true, }, diff --git a/tests/unit/pi-product-artifact.test.ts b/tests/unit/pi-product-artifact.test.ts index ee65371..0997b94 100644 --- a/tests/unit/pi-product-artifact.test.ts +++ b/tests/unit/pi-product-artifact.test.ts @@ -37,7 +37,7 @@ const MARKETPLACE_ARTIFACT_TEXT = [ 'plugin-marketplace\\/install\\/', 'plugin-marketplace\\/update\\/', 'effectiveSkillIds', 'pluginReleaseIds', '/api/coding/plugin-marketplace/catalog', '/api/coding/plugin-marketplace/library', - '为你的智能体添加插件,拓展更多能力。', 'acquire', 'install_stable', 'enable_project', '/project-config/plugins', + 'plugin-tab-available', 'plugin-tab-installed', 'acquire', 'install_stable', 'enable_project', '/project-config/plugins', ].join('\n'); async function createAsarFixture(source: string, archive: string) { @@ -76,7 +76,7 @@ function matchingMetadata() { }; } -async function bundledResourceFixture({ schemaVersion = 1, includeSdkAssets = true } = {}) { +async function bundledResourceFixture({ schemaVersion = 1, includeSdkAssets = true, includeTools = true } = {}) { const root = await mkdtemp(path.join(tmpdir(), 'makelore-bundled-plugin-')); roots.push(root); const projectRoot = path.join(root, 'project'); @@ -90,7 +90,7 @@ async function bundledResourceFixture({ schemaVersion = 1, includeSdkAssets = tr ? { runtime: { kind: 'platform_hosted', protocol: 'makelore-hosted.v1' } } : {}), skills: [{ id: 'example', entry: '../skills/example/SKILL.md', grants: [] }], - tools: [{ name: 'example_read' }], + tools: includeTools ? [{ name: 'example_read' }] : [], }; await mkdir(path.join(pluginRoot, 'com.makelore'), { recursive: true }); await mkdir(path.join(pluginRoot, 'skills', 'example'), { recursive: true }); @@ -243,6 +243,26 @@ describe('final Pi product artifact verification', () => { }); }); + it('accepts the shipped skill-only scaffold without an adapter', async () => { + const fixture = await bundledResourceFixture({ schemaVersion: 2, includeSdkAssets: false }); + const scaffold = path.resolve('resources/coding-plugins/project-scaffold'); + await cp(scaffold, path.join(fixture.projectRoot, 'resources/coding-plugins/project-scaffold'), { recursive: true }); + await cp(scaffold, path.join(fixture.resourcesDirectory, 'resources/coding-plugins/project-scaffold'), { recursive: true }); + const result = await verifyBundledCodingPluginResources({ + ...fixture, + appAsarContents: Buffer.concat([fixture.appAsarContents, Buffer.from(' makelore.project-scaffold')]), + }); + expect(result.packages).toEqual(expect.arrayContaining([ + expect.objectContaining({ id: 'makelore.project-scaffold', tools: [] }), + ])); + expect(result.catalog.adapters).toEqual(['example.plugin']); + }); + + it('still rejects a hosted plugin missing its tool catalog', async () => { + const fixture = await bundledResourceFixture({ schemaVersion: 2, includeSdkAssets: false, includeTools: false }); + await expect(verifyBundledCodingPluginResources(fixture)).rejects.toThrow('adapter/tool catalog is invalid'); + }); + it('proves the packaged Marketplace trust, routes, effective snapshot, and Renderer assets', () => { const artifact = Buffer.from(MARKETPLACE_ARTIFACT_TEXT); @@ -405,7 +425,7 @@ describe('final Pi product artifact verification', () => { 'makelore-device-package.v1 /api/coding/device-packages device-parent-workers', '/api/coding/plugin-marketplace plugin-marketplace\\/install\\/ plugin-marketplace\\/update\\/', 'effectiveSkillIds pluginReleaseIds', - '/api/coding/plugin-marketplace/catalog /api/coding/plugin-marketplace/library 为你的智能体添加插件,拓展更多能力。 acquire install_stable enable_project /project-config/plugins', + '/api/coding/plugin-marketplace/catalog /api/coding/plugin-marketplace/library plugin-tab-available plugin-tab-installed acquire install_stable enable_project /project-config/plugins', ].join('\n')); const appAsar = path.join(root, 'app.asar'); await createAsarFixture(source, appAsar); @@ -424,7 +444,7 @@ describe('final Pi product artifact verification', () => { 'makelore.game-audio /api/plugins/v1/hosted/game-audio/generations', '/api/coding/plugin-marketplace plugin-marketplace\\/install\\/ plugin-marketplace\\/update\\/', 'effectiveSkillIds pluginReleaseIds', - '/api/coding/plugin-marketplace/catalog /api/coding/plugin-marketplace/library 为你的智能体添加插件,拓展更多能力。 acquire enable_project /project-config/plugins', + '/api/coding/plugin-marketplace/catalog /api/coding/plugin-marketplace/library plugin-tab-available plugin-tab-installed acquire enable_project /project-config/plugins', ].join('\n')); const staleAsar = path.join(root, 'stale.asar'); await createAsarFixture(staleSource, staleAsar);