# MEXP14 QA-EDC-02 外部分发候选验证 日期:2026-07-06 项目:Mindraw 角色:QA ## 用户目标 PMO 补派 `QA-EDC-02`,要求在两个 Review Gate 已回写代码复核层面 `PASS` 后,按 `QA-EDC-01` 计划尽可能执行 MEXP14 外部分发候选验证。 限制: - 不改源码、需求、设计、App/plugin 产物或发布资料。 - 不替代码审核员、PMO Gate、运营或用户验收下结论。 - 真实 fresh machine 优先;没有真实 fresh machine 时,只能把 clean profile 结果标成 fallback,不能冒充跨机器验证。 ## 已完成事项 - 读取并遵守项目入口和 QA 规则:`AGENTS.md`、`INDEX.md`、`02-P/plan-progress.markdown` MEXP14 计划项、`QA-EDC-01` 计划/阻塞 JSON、App 候选证据、plugin 候选证据、两个 Review Gate PASS 行。 - 复核 App candidate `1.2.3 / Build202607061556`: - SHA256 与记录一致。 - `release-manifest.json` 显示 `mexp14-external-distribution-candidate-not-formal-release-not-notarized-installer`。 - `hdiutil verify` 通过。 - DMG 只读挂载后包含 `Mindraw.app` 和 `Applications` link。 - Info.plist 中 `bundleId=com.mindraw.desktop`、version `1.2.3`、build `Build202607061556`、`mindraw://` scheme、`.mindraw` / `.excalidraw` document types 均可见。 - `codesign --verify --deep --strict` 通过,但签名为 ad-hoc、无 TeamIdentifier。 - `spctl` 对 DMG 和 mounted App 都返回 rejected,符合未签名 / 未 notarized 边界,但会阻塞普通外部安装。 - 复核 plugin candidate: - `SHA256SUMS.txt` 全部 OK。 - tarball SHA256 为 `ee06e91a866049b6a8062367f77fc659aa3c3c9f5638f7b0c2b61ad8e14aa2f1`。 - marketplace 名称为 `mindraw-mexp14-edc-20260706154843`。 - 解包后 validator、`npm run check`、lifecycle / HTTP / MCP smoke 均通过。 - SkillSpector skill directory 为 `SAFE`;plugin root 仍为 `CAUTION`,命中 `scripts/mindraw-canvas-service.mjs:4`,按 `crg-ce3b411d6cca=PASS` 只视为代码复核层面已接受风险。 - 执行 clean Codex CLI profile fallback: - 临时 profile 初始无 marketplace/plugin。 - 从 candidate root 和 tarball extract 两条路径都能 `codex plugin marketplace add` + `codex plugin add`。 - `codex plugin list` 可见 `mindraw@mindraw-mexp14-edc-20260706154843`。 - installed cache 的 validator 与 `npm run check` 通过。 - clean CLI profile 中 remove plugin / remove marketplace 成功,回到无 marketplace/plugin。 - 当前 warmed-up profile 未留下 MEXP14 candidate 安装,只保留 `mindraw@personal`。 ## 关键决策 - `QA-EDC-02` 结果写为 `PARTIAL`,不写 QA PASS。 - Clean Codex CLI profile 只能证明候选安装源可被隔离 CLI profile 识别、安装、校验和移除;不能证明 Codex Desktop plugin 可见、sidebar GUI、App foreground、真实保存刷新或跨机器可用。 - 未执行 `/Applications` 安装、App first launch 和 App rollback,以避免覆盖当前机器状态;这些必须在 fresh machine 或 clean macOS user 上执行。 - `spctl` rejected 不是本轮 QA 误差,而是候选本身 `ad-hoc / unsigned / not-notarized` 的外部分发风险,需要 PMO/OPS 明确是否允许进入外部用户复验。 ## 文件变更 - 新增:`02-P/MEXP14-ExternalDistributionCandidate/qa/qa-edc-02-external-distribution-check-20260706.md` - 新增:`02-P/MEXP14-ExternalDistributionCandidate/qa/qa-edc-02-external-distribution-check-20260706.json` - 更新:`02-P/plan-progress.markdown` - MEXP14 状态更新为 `Active / QA-EDC-02 reported PARTIAL / not formal release`。 - `QA-EDC-02` 任务卡标记为已执行,结果 `PARTIAL`。 - Progress Log 追加 QA-EDC-02 证据和剩余缺口。 - 更新:`INDEX.md` - 登记 QA-EDC-02 报告和 JSON 证据路径。 ## 核心事实 / 变更快照 - 变更对象:MEXP14 `P-20260706-external-distribution-candidate` 的 QA-EDC-02 状态。 - 事实来源: - `02-P/plan-progress.markdown` - `02-P/MEXP14-ExternalDistributionCandidate/qa/qa-edc-02-external-distribution-check-20260706.md` - `02-P/MEXP14-ExternalDistributionCandidate/qa/qa-edc-02-external-distribution-check-20260706.json` - 变更前: - `QA-EDC-02` 已派发,状态为 in progress。 - `QA-EDC-01` 只产出计划和阻塞记录。 - 变更后: - `QA-EDC-02` 已执行可运行范围验证。 - 结果为 `PARTIAL`。 - `PM_NOTIFY_READY=yes` 表示 partial evidence 可供 PMO 收敛,不代表 QA PASS、PMO Gate、正式发布或用户验收。 - 已覆盖: - App SHA、manifest、DMG verify、只读挂载、metadata、URL scheme、document types、codesign、doctor metadata check、Gatekeeper 表达。 - Plugin SHA、tarball、marketplace、validator、`npm run check`、lifecycle / HTTP / MCP smoke、SkillSpector、clean Codex CLI profile install/list/remove。 - 未覆盖: - 真实 fresh machine。 - clean macOS user。 - clean Codex Desktop GUI profile。 - 新机器 `/Applications` 安装和首次启动。 - 真实 App 编辑保存后 sidebar 刷新。 - App rollback。 - 外部机器 plugin rollback baseline。 ## 可复用命令 / Runbook 用途:复跑 MEXP14 QA-EDC-02 的 App artifact、plugin package 和 clean Codex CLI profile fallback 验证。 前置条件: - 在 `/Users/ar/Projects/Mindraw` 项目根运行。 - App candidate 已存在于 `03-O/C2.builds/releases/Build202607061556/`。 - Plugin candidate 已存在于 `P2.mindrawforcodex/03-O/C2.builds/MEXP14-ExternalDistributionCandidate/mindraw-plugin-0.1.0+codex.20260704090518-EDC20260706154843/`。 App candidate artifact / Gatekeeper 表达: ```bash DMG=/Users/ar/Projects/Mindraw/03-O/C2.builds/releases/Build202607061556/Mindraw_1.2.3_aarch64-Build202607061556.dmg MANIFEST=/Users/ar/Projects/Mindraw/03-O/C2.builds/releases/Build202607061556/release-manifest.json BUILD_INFO=/Users/ar/Projects/Mindraw/03-O/C2.builds/releases/Build202607061556/build-info.txt shasum -a 256 "$DMG" "$MANIFEST" "$BUILD_INFO" jq '{version,buildLabel,releaseClass,packageType,source,artifacts,signing,notarization,validation}' "$MANIFEST" hdiutil verify "$DMG" MP=/tmp/mindraw-qa-edc-02-mount-$$ mkdir -p "$MP" hdiutil attach -nobrowse -readonly -mountpoint "$MP" "$DMG" /usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$MP/Mindraw.app/Contents/Info.plist" /usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$MP/Mindraw.app/Contents/Info.plist" /usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$MP/Mindraw.app/Contents/Info.plist" /usr/libexec/PlistBuddy -c 'Print :CFBundleURLTypes' "$MP/Mindraw.app/Contents/Info.plist" /usr/libexec/PlistBuddy -c 'Print :CFBundleDocumentTypes' "$MP/Mindraw.app/Contents/Info.plist" codesign --verify --deep --strict --verbose=2 "$MP/Mindraw.app" codesign -dv --verbose=4 "$MP/Mindraw.app" spctl --assess --type open --verbose=4 "$DMG" spctl --assess --type execute --verbose=4 "$MP/Mindraw.app" npm run doctor:mindraw -- --app "$MP/Mindraw.app" --pretty hdiutil detach "$MP" ``` Plugin candidate package validation: ```bash CANDIDATE_ROOT=/Users/ar/Projects/Mindraw/P2.mindrawforcodex/03-O/C2.builds/MEXP14-ExternalDistributionCandidate/mindraw-plugin-0.1.0+codex.20260704090518-EDC20260706154843 cd "$CANDIDATE_ROOT" && shasum -a 256 -c SHA256SUMS.txt TARBALL="$CANDIDATE_ROOT/plugin/mindraw-plugin-0.1.0+codex.20260704090518-EDC20260706154843.tar.gz" shasum -a 256 "$TARBALL" TMPDIR=$(mktemp -d /tmp/mindraw-qa-edc-02-plugin.XXXXXX) tar -xzf "$TARBALL" -C "$TMPDIR" jq -r '.name' "$TMPDIR/install-source/.agents/plugins/marketplace.json" PYTHONPATH=/tmp/mindraw-pyyaml python3 /Users/ar/.codex/skills/.system/plugin-creator/scripts/validate_plugin.py "$TMPDIR/install-source/plugins/mindraw" npm run --prefix "$TMPDIR/install-source/plugins/mindraw" check node "$TMPDIR/install-source/plugins/mindraw/scripts/mindraw-canvas-service.mjs" smoke-lifecycle node "$TMPDIR/install-source/plugins/mindraw/scripts/mindraw-canvas-service.mjs" smoke-http node "$TMPDIR/install-source/plugins/mindraw/scripts/mindraw-canvas-service.mjs" smoke-mcp skillspector scan "$TMPDIR/install-source/plugins/mindraw/skills/mindraw-open-canvas" --no-llm skillspector scan "$TMPDIR/install-source/plugins/mindraw" --no-llm ``` Clean Codex CLI profile install / remove fallback: ```bash CANDIDATE_ROOT=/Users/ar/Projects/Mindraw/P2.mindrawforcodex/03-O/C2.builds/MEXP14-ExternalDistributionCandidate/mindraw-plugin-0.1.0+codex.20260704090518-EDC20260706154843 CLEAN_ROOT=$(mktemp -d /tmp/mindraw-qa-edc-02-clean-profile.XXXXXX) export HOME="$CLEAN_ROOT/home" export CODEX_HOME="$CLEAN_ROOT/codex-home" export AGENTS_HOME="$CLEAN_ROOT/agents-home" export XDG_CACHE_HOME="$CLEAN_ROOT/xdg-cache" export XDG_CONFIG_HOME="$CLEAN_ROOT/xdg-config" mkdir -p "$HOME" "$CODEX_HOME" "$AGENTS_HOME" "$XDG_CACHE_HOME" "$XDG_CONFIG_HOME" codex plugin marketplace list codex plugin list codex plugin marketplace add "$CANDIDATE_ROOT/plugin/install-source" codex plugin add mindraw@mindraw-mexp14-edc-20260706154843 codex plugin list codex plugin remove mindraw@mindraw-mexp14-edc-20260706154843 codex plugin marketplace remove mindraw-mexp14-edc-20260706154843 codex plugin marketplace list codex plugin list ``` 验证结果判定: - App SHA / manifest / `hdiutil verify` / mounted metadata / `codesign --verify` 应通过。 - `spctl` rejected 是当前候选的已知 Gatekeeper 风险,不应写成通过。 - Plugin validator / `npm run check` / smoke 应通过。 - Skill directory SkillSpector 应为 `SAFE`;plugin root 仍会是 `CAUTION`,需保留 review risk note。 - Clean CLI profile install/remove 通过仍不能替代 Codex Desktop clean profile。 搜索关键词:`QA-EDC-02`、`Build202607061556`、`mindraw-mexp14-edc-20260706154843`、`spctl rejected`、`clean Codex CLI profile`、`PARTIAL` ## 未决问题 - PMO 需要判断下一状态是 `BLOCKED_PENDING_FRESH_MACHINE`,还是带明确 Gatekeeper / rollback 风险的外部用户复验。 - OPS 需要把实际 App/plugin 候选值补进 distribution kit,并补外部机器可用的 plugin rollback baseline 或 remove-only 策略。 - QA / PMO 仍需真实 fresh Mac 或 clean macOS user 验证:App 安装启动、Codex Desktop plugin 可见、sidebar preview、编辑/双击进入 App、真实保存后 sidebar 刷新或 fail-closed、重启恢复、rollback。 ## 后续建议 - 不要把 clean CLI profile fallback 写成 fresh machine pass。 - 如果允许外部用户继续试未 notarized 包,必须在测试说明里明确 Gatekeeper 风险、预期阻断和停止/回报路径。 - 如果目标是正式外部分发而非受邀候选,应补 Developer ID signing / notarization,再重新跑 QA。