# 海报投稿盒交接简报 — poster-desk-lab/1.0.2

[下载完整教学 ZIP](https://yenpo.app/examples/poster-desk-handoff-1.0.2.zip) · SHA-256：`c39d19b210c05f4667cd74db4934459a079408d14c5e7678c24cd087e1df5185`

这是 ZIP 内简报的阅读副本。下方源码和脚本路径均相对于解压后的 `poster-desk-lab` 目录；执行命令和复制验收表需要完整 ZIP。

任务：收集海报标题，供稍后在本机查看。代码、投稿和待核验承诺均为独立构造的教学材料，不是真实用户、事故或某段 AI 输出。需要 Node.js 22+，不安装依赖、不联网、不用凭据。解压到全新目录，进入 `poster-desk-lab`；逐条运行下方命令，先验证包，再按本版本行号读源码。

## 功能事实

| ID  | 承诺与实际观察                                                                                           | 分类及范围                                                                                 | 本版本源码依据                                                                     |
| --- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
| P1  | “投稿后可以再查看。”另一个 Node 进程从同一保留目录读回相同 ID 和标题。                                   | **implemented / 已实现**：本地文件、顺序进程。不等于崩溃后持久性、远程数据库或部署后保留。 | `src/store.mjs` 46–76 行；`src/cli.mjs` 12–17 行                                   |
| P2  | “回执已经发送。”返回成功时附有样例预览，字段为 `adapter: fixture`、`delivery: not-attempted`。           | **mock / 模拟**：只有预览，没有尝试发送；在本包范围，“已发送”的承诺不成立。                | `src/cli.mjs` 1–2、15–17 行选择 `src/receipt.mjs` 3–7 行和 `fixtures/receipt.json` |
| P3  | “同一投稿重试不重复。”相同 ID、标题按顺序再提交，返回 `created: false`，列表仍一条；同 ID 改标题会拒绝。 | **implemented / 已实现**：同目录、相同内容、顺序重试。不承诺并发、写入中断或外部通知重试。 | `src/store.mjs` 46–60 行                                                           |
| P4  | “部署替换后投稿仍保留。”包里没有部署环境或数据目录生命周期资料。保留目录能读回，换新目录则为空。         | **unknown / 未知**：两个本地结果不能说明部署会保留哪个目录，不能把 P1 推成 P4。            | `src/store.mjs` 7–9、22–38 行；本快照未提供部署配置                                |

“已实现”只指已示范范围；“模拟”是已知的替代路径，不是未经测试的真实服务；“未知”表示缺少判断所需的环境证据，不等于已知没有实现。

## 接收与复现

```sh
node --version
node scripts/verify-package.mjs
node scripts/copy-receiver.mjs
node src/cli.mjs init receiver-state
node src/cli.mjs submit receiver-state cloud-poster "Cloud shapes"
node src/cli.mjs list receiver-state
node src/cli.mjs submit receiver-state cloud-poster "Cloud shapes"
node src/cli.mjs list receiver-state
node src/cli.mjs submit receiver-state cloud-poster "Different title"
node src/cli.mjs list receiver-state
node src/cli.mjs init replacement-state
node src/cli.mjs list replacement-state
node src/cli.mjs list receiver-state
node src/cli.mjs init receiver-state
node src/cli.mjs list receiver-state
node --test tests/contract.test.mjs tests/receiver-copy.test.mjs
```

第一次提交的 `accepted`、`created` 为 true；回执仍为 fixture，delivery 为 not-attempted。列表为 `{"cards":[{"id":"cloud-poster","title":"Cloud shapes"}]}`。相同内容重试的 `created` 为 false，列表不变。

改标题那条命令**退出 2**，stderr 为 `{"error":"card-id-conflict"}`，stdout 为空，随后列表仍保留原标题。新目录 `replacement-state` 的列表是 `{"cards":[]}`；原目录仍能读回卡片。重复初始化原目录也**退出 2**，报 `{"error":"EEXIST"}`，不会替换它。这两项是预期拒绝，不要把退出码改记为通过，也不要用 `&&` 串命令。重复初始化被拒绝后的 list 应再次读回原卡片。五项测试通过，其中一项实际复制验收表、填写包外副本，再确认原包仍能通过校验。

每条 CLI 命令都是独立进程，所以检查的是保留文件时的进程重启。新目录为空只是本地对照，**没有执行真实部署替换**。命令只会创建两个新的子目录；再次复现请重新解压，不用现有项目或生产数据目录。

## 接收验收与待查项

上方复制命令把 `RECEIVER.md` 模板复制到解压包外的 `../poster-desk-receiver.md`；已有副本会被拒绝，不会覆盖。按自己的输出和源码阅读填写**包外副本**，保留包内原模板不变。验收记录不属于原始包哈希清单。填写后，再运行：

```sh
node scripts/verify-package.mjs
```

此时原包仍应通过。验收条件：P1/P3 在限定范围成立，P2 保持模拟分类，P4 保持待查。未知项不推翻已经核验的本地事实，也不能被绿灯测试变成部署承诺。

若要确认真实环境的 P4，须取得准确部署/存储配置，并以授权的非敏感数据，独立验证已知记录在该环境替换前后的结果。本包不包含这种环境或验证。并发写入、断电、备份恢复和真实通知也未评估。源码先保存卡片再准备回执；回执出错时，卡片可能已经保存，不能仅凭报错推断没有写入。
