# Shared Content Publisher V3.1 项目级、声明式、失败关闭的内容发布器。它不内置行业、批次、任务或 attempt 名称;生产与测试均通过同一个严格 JSON 配置和同一个 CLI 入口运行。 ```powershell python -m shared_content_publisher --config --validate-only python -m shared_content_publisher --config python -m shared_content_publisher --config --read-direct-current python -m shared_content_publisher.postcommit_archive --config python -m shared_content_publisher.postcommit_archive --rollback-config ``` 核心边界: - 首次正式写入是 exclusive lock;持锁后、任何 ledger append 前,再次完整校验 config、根/卷、ledger/current、prior manifest+self、candidate exact-set、link/evidence 与历史树,并重新推导唯一 sequence。 - `canonical_audit_id` 与 `review_handoff_id` 是不同字段和不同 ID 域;release preimage 只使用 canonical audit。 - ledger 的 next event/attempt sequence 从精确当前 ledger 推导并与配置声明交叉验证;同一个公共验证器逐行绑定全部 17 列、精确状态回执、严格递增 UTC 和每阶段 `PID+UUID` 进程实例身份。 - 每个终态持久化 `SHARED_CONTENT_PUBLISHER_ATTEMPT_EVENT_CHAIN_V1` 规范化事件链的 SHA-256、行数、起止 UTC 和逐阶段进程身份;正常提交、幂等重放、回滚与恢复均复用同一验证入口,任一列漂移均不得返回 `IDEMPOTENT_COMMITTED`。 - 候选先发布到同卷新 release 目录,最后只原子替换既有 cases Markdown `当前成果索引.md`;result Markdown 保持为指向 cases 的薄入口,不引入 JSON pointer 或消费者迁移。 - current manifest 必须精确覆盖发布成员,manifest-self 路径、target exact-set、非空 link/evidence closure 都是硬门禁。 - 完全相同的已提交调用只有在 config bytes/hash、根/卷、terminal、完整事件链、Markdown 入口、target exact-set、snapshot 与 lock absence 全部一致时才幂等返回;已切换后的有效配置漂移先回滚 Markdown current,再失败关闭。 - Windows 路径通过长路径前缀读取;每个身份由一次完整读取和一次独立流式 SHA-256 复核。 ## DIRECT_STABLE_PATH_SET_V1 当既有消费者直接读取稳定 Markdown/CSV 路径、不能迁移到 release 目录时,V3 配置可选择 `DIRECT_STABLE_PATH_SET_V1`: - `candidate.rows[].formal_relative_path` 就是唯一稳定目的地;除唯一 cases `当前成果索引.md` 激活点外,只允许落在该行业的 `核心文档/`、`manifest/` 或 `ana-data/result//`。 - 配置以 `expected.destination_prior` 同时冻结每个目的地的已有文件身份或 ABSENT 状态;已有和缺失成员可以在同一事务中发布。 - 行业根第一次建立 canonical current 时可使用严格的 `expected.prior.mode=ABSENT_GENESIS_V1`:`prior.rows` 必须为空,cases/result current、current state 与全部声明目的地必须实测 ABSENT;发布后仍复用同一锁、恢复锚、事件链、回滚与幂等验证,不以 genesis 名义绕开正式闭环。 - 完整候选先复制到同卷 fresh staging;提交顺序固定为 payload/core、manifest/result metadata、最后 cases current index。`commit_plan.json` 明确绑定逐文件顺序,cases current 永远是最后且唯一激活点。 - 每个已声明目的地在 exclusive lock 后、首个 ledger append 前重读;正式写使用同卷 per-file atomic replace/CreateNew。任何提交边界失败最多执行一次精确逆序回滚:恢复 prior snapshot、删除本 attempt 新建文件,并复核完整 prior stable set。 - postcommit、replay、recovery 共用目的地 exact identity、candidate/current manifest、scope extra-file、snapshot、terminal 与 17 列 event-chain 验证;稳定路径漂移或 undeclared extra 永远不能返回假幂等成功。 - 正式 current 消费必须调用公共 `read_direct_current_snapshot()`,或使用 `--read-direct-current` 做只读闭环证明。读前要求 publisher lock absent 并冻结 cases current index/current manifest 双身份;读完整目标集后再次要求 lock absent 且双身份不变。锁存在返回 `RETRY_PUBLISH_IN_PROGRESS`,identity/exact-set 漂移返回 `STOP_NOT_CURRENT`,均不返回 payload 或 current 标记。 - 快照、`PREPARED`、`VERIFIED`、首个稳定目的地前和每个稳定目的地提交后的 durable prefix 均由一个恢复验证器管理。同 config 新进程先绑定 snapshot、receipt、staging、全部 17 列 event chain 与每个目的地 prior/candidate 真值,再精确恢复 prior 并返回可严格重放的 `ROLLED_BACK`;合法未完成前缀不再返回 `TERMINAL_REPEAT_BLOCKED`。 - 正式 attempt receipt 不再逐文件暴露 pre-anchor 半成品。完整 prior snapshot、commit plan、attempt contract 与 canonical anchor manifest 先在由 task/case/batch/run/config 身份唯一推导的同卷私有目录内逐成员 `fsync`、关闭、回读,再以 no-overwrite 原子目录改名一次发布。原子改名前的可归属残片在 formal prior/ledger 不变时由同 config 安全退役并重建;finalized member、extra 或 official anchor 的篡改返回明确 `RECOVERY_REQUIRED`,不得盲删或继续发布。 - 旧 `MARKDOWN_CURRENT_INDEX_REPLACE_V1` / V2 schema 保持兼容,不改变其 release-directory 行为。 机器可读 schema: - `batch_config.schema.json`:V2 release-directory 策略; - `batch_config.direct_stable_path_set.schema.json`:V3 direct stable-path 策略。 - `postcommit_archive.schema.json`:发布成功后的旧目录原子归档及恢复回执。 - `postcommit_archive.rollback.schema.json`:仅在独立 `AUTH-` 与 `AUDIT-` 绑定后恢复 legacy 原路径。 ## 发布后旧目录归档 正式 current 已由 `DIRECT_STABLE_PATH_SET_V1` 提交并完成精确回读后,可用独立的 `postcommit_archive` 协调器把一个完整旧目录原子改名到同卷审计包: - 移动前必须完整回放发布器 `COMMITTED` 状态,并逐行验证旧路径映射、旧树和新正式树; - 归档合同、映射表和 manifest 先以 CreateNew 方式形成持久回执锚,目录原子改名是唯一旧目录变更; - 重命名前中断保留旧目录,重命名后中断由同一配置验证归档树并补齐终态; - source/target 同时存在或同时缺失、回执不完整、旧树/归档树/正式树漂移都进入 `RECOVERY_REQUIRED`,不得把不确定状态声明为已归档; - 成功终态为 `ARCHIVED`,同配置完整回放为 `IDEMPOTENT_ARCHIVED`。 归档回放会从冻结的 publisher config 复核完整正式集合(包括所有 core、manifest-self、 cases/result 两入口),并复算 17 列事件链及 lock absence,不只验证 legacy map 覆盖的 目标。若整体迁移随后获得独立回退授权,必须另建严格 rollback config;协调器先绑定原 archive config/terminal、完整正式集合、归档树和授权身份,建立独立回退回执锚后,才可 以一次 no-overwrite 原子改名恢复 legacy 原路径。回退成功与同配置重放分别返回 `LEGACY_RESTORED` 和 `IDEMPOTENT_LEGACY_RESTORED`,原 archive config 不得再次归档。 即使进程在 rollback 目录原子改名后、terminal 发布前中断,重启也必须先重新验证 archive anchor、648 项正式集合、17 列事件链、lock、canonical 645 项及恢复后的 legacy 645 项;任一漂移只返回 `RECOVERY_REQUIRED`,不得补写或重放成功终态。 正式批次配置由项目管理员在实现审核通过后另行生成,本实现不包含任何真实批次配置。