diff --git a/.project-docs/30-worklog/tasks/20260828-marketplace-mlm05-merger-c73a91e4.md b/.project-docs/30-worklog/tasks/20260828-marketplace-mlm05-merger-c73a91e4.md new file mode 100644 index 0000000..d1ddbdc --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260828-marketplace-mlm05-merger-c73a91e4.md @@ -0,0 +1,193 @@ +# Task: Marketplace Release A client MLM-05 merger and package proof + +## Identity + +- Task ID: 20260828-marketplace-mlm05-merger-c73a91e4 +- Mode: Feature +- Branch: codex/20260828-marketplace-mlm05-merger-c73a91e4-marketplace-mlm05-merger +- Worktree: D:\Datas\OthersProjects\makelore-plugin-marketplace-client-mlm05-merger +- Base commit: 2c4f766b3b61d4540919495043322d438cc17ec3 +- Owner: marketplace-mlm05-merger +- Status: Ready for Integration + +## Scope + +- Verify the already integrated Marketplace Release A client product at exact + coordinator head `2c4f766b3b61d4540919495043322d438cc17ec3` for ticket MLM-05. +- Own only package-artifact proof script/test updates, cross-module integration + tests, Marketplace E2E, this task record, and task-prefixed verification evidence. +- Do not re-cherry-pick MLM-01 through MLM-04 and do not redesign or edit their + parser, Marketplace client/Package Store, resolver, Main, Pi, or Renderer product + sources. A real integration defect in those files is returned to its owner as a + concrete blocker. + +## Intent And Constraints + +- Prove exact schema-1 compatibility and schema-2 declarative `skill_only` package + behavior, descriptor/signature trust, account isolation, atomic installation, + Library/install/project/assignment separation, one effective snapshot, Pi and + Renderer projections, and existing Data Service/P0 behavior. +- Prove the packed product contains the trusted Marketplace route/assets and a + schema-2 skill-only package can materialize without server policy, while the + default production trust store fails closed because no official public key has + been supplied. Ephemeral public/private test material may be injected only by + tests and must not enter product files or packed shared index data. +- Preserve Release A exclusions: no arbitrary executable/script/native/MCP/hook/LSP + path, hidden auto-acquire/enable/assignment, hosted adapter, Plugin Charges, + Token Point writes, or account/token/admission fields in the shared package index. +- Run no XMA-01, publish, deployment, push, or PR action. Report packaging/network + deviations exactly and retain the official signing-key activation hold. + +## Project Context Loaded + +Task context: +- Task ID: `20260828-marketplace-mlm05-merger-c73a91e4` +- Mode: Feature +- Branch: `codex/20260828-marketplace-mlm05-merger-c73a91e4-marketplace-mlm05-merger` +- Worktree: `D:\Datas\OthersProjects\makelore-plugin-marketplace-client-mlm05-merger` +- Base commit: `2c4f766b3b61d4540919495043322d438cc17ec3` +- Other active local tasks: the clean client coordinator plus completed MLM-01, + MLM-02, MLM-03, and MLM-04 source worktrees; unrelated historical tasks remain in + separate registered worktrees. +- Overlap or semantic-conflict assessment: no unresolved conflict. The parent + coordinator explicitly delegated MLM-05 exclusively to this task. Product owners' + scopes are read-only inputs; this task owns only proof/integration-test/evidence + seams and will return any product defect to the appropriate owner. + +Read: +- `AGENTS.md`; bundled `maintain-project-docs` and `implement-spec` skills. +- Complete Marketplace implementation specification and ticket graph, including + MLM-05 and the XMA-01 boundary. +- Accepted curated Plugin Center design, relevant project-memory startup, + architecture/domain/evidence/reflection/commitment/stale records, coordinator + record, and all four source task records. + +Relevant understanding: +- Project goal: a curated Operations-issued Plugin Marketplace whose local + distributed packages are declarative and whose user states remain separate. +- Current integrated focus: all MLM-01 through MLM-04 product commits are already + present; MLM-05 is verification/package proof, not another merge. +- Active task scope: proof scripts/tests, cross-module integration, Marketplace E2E, + and task evidence only. +- Active constraints: preserve P0/Data Service/Pi/preview regressions and Release A + exclusions; no product-source redesign, XMA-01, publication, or PR. +- Decisions affecting this task: Main owns auth/network/filesystem/trust; production + trust is code-owned and fail-closed; project plugin file remains schema 1 and + retains unknown IDs; current workers are frozen; child workers remain empty. +- Evidence, reflections, or commitments affecting this task: Windows Pi artifact + proof is an existing release gate; official Marketplace public key is absent and + therefore production-trust activation is HOLD, not PASS. +- Files or modules likely involved: existing focused tests, Marketplace E2E, + package/Pi artifact proof scripts, and task-prefixed evidence only. +- Unknowns, stale docs, or conflicts: canonical shared project memory predates this + feature branch; the frozen spec/design/coordinator records are authoritative. No + package/network deviation is known before the required runs. + +Gate result: +- Concurrent Task Gate: Passed. Project-doc validation and exact task-context owner, + mode, branch, worktree, and base checks succeeded. +- Planning Gate: Passed. Source parents/scopes/clean states and coordinator ancestry + are exact; no semantic conflict blocks verification. + +## Implementation Plan + +1. Run the focused parser/signature/store/client/resolver/Main/Pi/Renderer contract + suite and the complete Plugin P0/Data Service/Pi/preview regression set. A focused + failure determines the owning ticket and blocks broad validation until classified. +2. Add only missing MLM-05-owned cross-module/package-artifact proof coverage needed + to prove schema-2 skill-only packed assets, trusted-key activation hold, Library/ + install/effective route availability, and absence of secrets/shared-index authority. +3. Run typecheck, exact lint, full unit/pressure suite, Vite build, Windows Electron, + Pi artifact verification, Marketplace/full E2E, and package build/proof when the + locked dependencies are reachable. Record exact deviations rather than converting + them to passes. +4. Run Release A exclusion/source scans, `git diff --check`, documentation drift, + commit one MLM-05 source/evidence change with sole parent the exact base, complete + task context, and return a clean `READY_FOR_INTEGRATION` handoff. + +## Outcome + +- Verified the already integrated MLM-01 through MLM-04 product tree without + re-cherry-picking or modifying parser, client/store, resolver, Main, Pi, or + Renderer product sources. +- Added the missing MLM-05-owned final artifact proof. The Pi product verifier now + requires the real `app.asar` to contain the schema-2 Skill-only descriptor and + fail-closed signature path, Main Library/install/update routes, effective snapshot + fields, and Renderer Marketplace assets. It also verifies the exact build root's + Marketplace trust source remains an empty code-owned store with no environment + override or private-key content. +- The real Windows package contains those Marketplace assets. Production Marketplace + trust remains an activation HOLD because the official Ed25519 public key is absent; + tests continue to prove injected ephemeral-key success without committing key + material to the product. +- Release A exclusion scans found no arbitrary process/MCP/hook/LSP/native execution, + hidden acquire/install/enable/assignment chain, hosted adapter, Plugin Charges, + Plugin Credits, Token Point transaction path, or account/token/admission authority + in the shared package index. The schema parser's rejected `mcp` component and + declarative `makelore-hosted.v1` protocol are required validation vocabulary, not + execution implementations. + +## Verification + +- Git/source ledger: + - MLM-01 source `352a3b7280bb48854beb5281d2b4923b76793367`, sole parent + `4d8b1fcec0a751d2935effc7816c7e59f568ec65`, product `898e2b7...`. + - MLM-02 source `1b6f5aaccaf55fc98657fc93824015471818f6e6`, sole parent + `c73fcf1d2d2e5dccea6f3b403a3b7c00bdc0b25a`, product `4052fa8...`. + - MLM-03 source `7ad6b8c66d9ca64b5778690667c91c424aae456a`, sole parent + `1d64b89499f68de721e0f1c845dad2c57f1a78ed`, product `05917a7...`. + - MLM-04 source `d61221d34da49f97dd4a9aeb1081fb3544cc6c86`, sole parent + `8b6824a8ba08d8df98fc75af17e170bf3d8ed630`, product `97c9ad1...`. + - All source worktrees and coordinator were clean at their exact heads; each source + and product tree was identical; `78fb7d7 -> 1ca8deb -> 2c4f766` and product merge + order through MLM-04 passed ancestry checks. +- Dependency restoration: repository-pinned pnpm `10.33.4`; frozen install passed + with 997 packages reused from the local store, zero downloads, and no lock change. +- Focused Marketplace integration: 15 files / 121 tests passed. +- Complete P0/Data Service/Pi/preview regression: 9 files / 78 passed / 2 expected + staged-runtime skips. +- Artifact proof unit: 1 file / 9 tests passed; scoped ESLint passed. +- `corepack pnpm run typecheck`: passed after the final proof change. +- `corepack pnpm run lint:check`: passed with zero errors and the exact unchanged five + warnings: one Home exhaustive-deps warning and four Makelore Fast Refresh warnings. +- `corepack pnpm test`: 208 normal files / 1,761 passed / 2 staged-runtime skips; + isolated pressure suite 1/1 passed. +- `corepack pnpm run build:vite`: passed. Existing dynamic-import and large-chunk + warnings remained; no new build failure. +- `corepack pnpm run test:electron:windows`: 2 files / 6 tests passed. +- Target Marketplace/Project Plugins Electron E2E: 2/2 passed. +- Full `corepack pnpm run test:e2e`: 27 passed / 1 failed. The one failure exactly + matches the frozen baseline: `tests/e2e/pi-coding-first-chat.spec.ts:575` timed out + after 30 seconds because the `当前对话模型` combobox remained disabled at + `selectOption`. Marketplace and Project Plugins passed in the same full run. This + is recorded as a baseline deviation, not converted to a pass and not repaired by + MLM-05. +- `corepack pnpm run package:win`: passed, including Python/uv acquisition, Vite, + win32-x64 Pi staging, unpacked Electron product, NSIS installer, and blockmap. + Installer: 211,958,675 bytes, SHA-256 + `AF4C33E9A7141059A4DAD47F2340E4A526CBBEE31C6555A0F79C55B44AAFD167`. + `app.asar`: 175,574,694 bytes, SHA-256 + `C40E55652D45CBF1ACEBFB0B5CA3377095963F601FD38252209589E94372399C`. +- `corepack pnpm run verify:artifact:pi`: final PASS. It proved the Marketplace + artifact markers and empty production trust, one exact bundled Data Service + package with ten tools, four core Skills, Pi 0.84.2 closure, no product-owned + OpenCode resource, and no development-path residue. The nested real-provider/ + cross-platform runtime report remains accurately `partial-pass` under its existing + waivers; this does not reduce the outer artifact result or become Marketplace + production-trust evidence. +- First proof failure and correction: the initial private-key scan matched a generic + PEM parser string in a dependency. It was an over-broad proof false positive, not + product key material. The check was narrowed to the actual Marketplace trust-source + authority, then its unit and real artifact verifier passed. + +## Follow-ups + +- Supply the official Ed25519 Marketplace public key through the code-owned trust + store before claiming production activation; keep the corresponding private key + only in the Works Square deployment secret boundary. +- The existing full-E2E Pi model-combobox timeout remains a baseline deviation owned + outside MLM-05. No Marketplace failure depends on it. + +## Promotion Candidates + +- None recorded. diff --git a/scripts/lib/pi-product-artifact.mjs b/scripts/lib/pi-product-artifact.mjs index fcaeac2..7302983 100644 --- a/scripts/lib/pi-product-artifact.mjs +++ b/scripts/lib/pi-product-artifact.mjs @@ -33,6 +33,29 @@ const EXTENSION_CONTRACT_MARKERS = Object.freeze([ 'makelore.write-lease', 'MAKELORE_PI_BRIDGE_URL', ]); +const MARKETPLACE_ARTIFACT_MARKERS = Object.freeze({ + packageTrust: Object.freeze([ + 'makelore-plugin-release.v1', + 'skill_only', + 'plugin_signature_invalid', + 'signing key is not trusted', + ]), + mainRoutes: Object.freeze([ + '/api/coding/plugin-marketplace', + 'plugin-marketplace\\/install\\/', + 'plugin-marketplace\\/update\\/', + ]), + effectiveSnapshot: Object.freeze([ + 'effectiveSkillIds', + 'pluginReleaseIds', + ]), + renderer: Object.freeze([ + '/api/coding/plugin-marketplace/catalog', + '/api/coding/plugin-marketplace/library', + '免费获取', + '我的插件', + ]), +}); const PI_AI_PROVIDER_PREFIX = 'pi-runtime/node_modules/@earendil-works/pi-ai/dist/providers/'; const PI_AI_PROVIDER_ASAR_PREFIX = 'app.asar/node_modules/@earendil-works/pi-ai/dist/providers/'; export const BUNDLED_CODING_PLUGIN_RESOURCE_ROOT = 'resources/coding-plugins'; @@ -264,6 +287,33 @@ export function collectForbiddenAsarPaths(appAsar, pattern = /opencode/i) { }; } +export function verifyMarketplaceClientArtifact(appAsarContents, productionTrustSource) { + const missing = Object.entries(MARKETPLACE_ARTIFACT_MARKERS).flatMap(([group, markers]) => ( + markers + .filter((marker) => !appAsarContents.includes(Buffer.from(marker))) + .map((marker) => `${group}:${marker}`) + )); + if (missing.length > 0) { + throw new Error(`Packaged app.asar does not contain Marketplace contract markers: ${missing.join(', ')}`); + } + if (!/CODE_OWNED_PLUGIN_SIGNING_KEYS\s*=\s*Object\.freeze\(\s*\{\}\s+as/u.test(productionTrustSource) + || productionTrustSource.includes('process.env') + || productionTrustSource.includes('-----BEGIN PRIVATE KEY-----') + || productionTrustSource.includes('-----BEGIN ED25519 PRIVATE KEY-----')) { + throw new Error('Marketplace production trust source is not an empty code-owned fail-closed store'); + } + return { + schema2SkillOnly: true, + productionTrust: 'official-key-absent-fail-closed', + libraryInstallAndEffectiveRoutes: true, + rendererAssets: true, + productionKeyIds: [], + privateKeyMaterialInTrustSource: false, + markers: MARKETPLACE_ARTIFACT_MARKERS, + result: 'pass', + }; +} + async function filesContainingNeedles(root, needles) { const matches = []; const visit = async (path) => { @@ -514,6 +564,10 @@ export async function verifyPiProductArtifact({ projectRoot, executable }) { throw new Error(`Pi runtime manifest contains absolute paths: ${JSON.stringify(absoluteManifestValues)}`); } const appAsarContents = await readFile(appAsar); + const marketplaceTrustSource = await readFile( + join(root, 'electron', 'coding-plugins', 'trusted-keys.ts'), + 'utf8', + ); const physicalOpenCodePaths = await collectForbiddenResourcePaths(resourcesDirectory); const asarOpenCodePaths = collectForbiddenAsarPaths(appAsar); const openCodeResourcePaths = classifyOpenCodeResourcePaths([ @@ -531,6 +585,7 @@ export async function verifyPiProductArtifact({ projectRoot, executable }) { resourcesDirectory, appAsarContents, }); + const marketplace = verifyMarketplaceClientArtifact(appAsarContents, marketplaceTrustSource); const actualSkills = bundledPluginResources.coreResources.skills; const missingExtensionMarkers = EXTENSION_CONTRACT_MARKERS.filter( (marker) => !appAsarContents.includes(Buffer.from(marker)), @@ -582,6 +637,7 @@ export async function verifyPiProductArtifact({ projectRoot, executable }) { }, packagedClosure, bundledPlugins: bundledPluginResources, + marketplace, extension: { contractMarkers: EXTENSION_CONTRACT_MARKERS, packaged: true, diff --git a/tests/unit/pi-product-artifact.test.ts b/tests/unit/pi-product-artifact.test.ts index 2350dbf..0daf7c0 100644 --- a/tests/unit/pi-product-artifact.test.ts +++ b/tests/unit/pi-product-artifact.test.ts @@ -15,6 +15,7 @@ import { verifyBundledCodingPluginResources, defaultProductExecutable, validatePiArtifactMetadata, + verifyMarketplaceClientArtifact, } from '../../scripts/lib/pi-product-artifact.mjs'; import { parsePiArtifactVerifierArgs } from '../../scripts/verify-pi-product-artifact.mjs'; @@ -185,6 +186,49 @@ describe('final Pi product artifact verification', () => { }); }); + it('proves the packaged Marketplace trust, routes, effective snapshot, and Renderer assets', () => { + const artifact = Buffer.from([ + 'makelore-plugin-release.v1', 'skill_only', 'plugin_signature_invalid', + 'signing key is not trusted', '/api/coding/plugin-marketplace', + 'plugin-marketplace\\/install\\/', 'plugin-marketplace\\/update\\/', + 'effectiveSkillIds', 'pluginReleaseIds', + '/api/coding/plugin-marketplace/catalog', '/api/coding/plugin-marketplace/library', + '免费获取', '我的插件', + ].join('\n')); + + const trustSource = `export const CODE_OWNED_PLUGIN_SIGNING_KEYS = Object.freeze( + {} as Readonly>, + );`; + expect(verifyMarketplaceClientArtifact(artifact, trustSource)).toMatchObject({ + schema2SkillOnly: true, + productionTrust: 'official-key-absent-fail-closed', + libraryInstallAndEffectiveRoutes: true, + rendererAssets: true, + productionKeyIds: [], + privateKeyMaterialInTrustSource: false, + result: 'pass', + }); + }); + + it('rejects a packaged Marketplace missing a required asset or containing a private key', () => { + const emptyTrust = 'CODE_OWNED_PLUGIN_SIGNING_KEYS = Object.freeze({} as Readonly>);'; + expect(() => verifyMarketplaceClientArtifact(Buffer.from('makelore-plugin-release.v1'), emptyTrust)) + .toThrow('Marketplace contract markers'); + + const complete = Buffer.from([ + 'makelore-plugin-release.v1', 'skill_only', 'plugin_signature_invalid', + 'signing key is not trusted', '/api/coding/plugin-marketplace', + 'plugin-marketplace\\/install\\/', 'plugin-marketplace\\/update\\/', + 'effectiveSkillIds', 'pluginReleaseIds', + '/api/coding/plugin-marketplace/catalog', '/api/coding/plugin-marketplace/library', + '免费获取', '我的插件', + ].join('\n')); + expect(() => verifyMarketplaceClientArtifact( + complete, + `${emptyTrust}\nprocess.env.PLUGIN_KEY`, + )).toThrow('empty code-owned fail-closed store'); + }); + it('rejects a packaged plugin tree that drops an SDK asset or catalog marker', async () => { const fixture = await bundledResourceFixture(); await rm(path.join(