From e1470622707f9603f7aed2aa499722033f4a939c Mon Sep 17 00:00:00 2001
From: "sanze.li" <2522048902@qq.com>
Date: Tue, 12 May 2026 15:24:04 +0800
Subject: [PATCH 1/3] refactor(blueprint): reformat tasks.md post-P4c
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Compress 10 completed milestones (P0→P4c) into summary table
- Restructure into: mainline milestones (P4d/P5/P6), evidence candidates, independent product lines, process tools, closed/absorbed items, deferred items
- Add post-P4c execution rules (serial convergence, dependency chain, evidence-driven upgrade)
- Anchor P4d to payload_capable + 接续增强 (not convention_only)
- Add plan intake checklist covering all 5 active tiers
- Add upgrade criteria to P4d and evidence candidates
---
.sopify-skills/blueprint/tasks.md | 167 ++++++++++++++++++------------
1 file changed, 98 insertions(+), 69 deletions(-)
diff --git a/.sopify-skills/blueprint/tasks.md b/.sopify-skills/blueprint/tasks.md
index 3d022e0..a2cfe03 100644
--- a/.sopify-skills/blueprint/tasks.md
+++ b/.sopify-skills/blueprint/tasks.md
@@ -1,113 +1,142 @@
# 蓝图路线图与待办
-本文定位: 路线图全景 + 未完成长期项与明确延后项。已完成里程碑仅保留一行摘要与归档指引。不替代当前 plan 的执行任务清单。
+本文定位: 路线图全景 + 开放项与延后项。已完成里程碑仅保留一行摘要与归档指引。不替代当前 plan 的执行任务清单。
-## 执行优先级(已确认)
-
-以下顺序是硬约束。前一项未稳定前,不进入后一项实现。
+## 产品方向
> **对齐原则**:Sopify 总方向是 Protocol-first / Validator-centered / Runtime-optional。主航道的每一步都是"先 formalize protocol/validator 层契约,再让 runtime 作为参考实现消费"。不以 runtime 内部治理为驱动。蓝图变更优先做能强化证据与授权层的事,优先做能让外部宿主看懂、接入、被验证的事;AI + 单人维护应串行收敛,不同时开多条线。
-> **先行切片例外**:以下两类改动不受上述顺序约束:① 已在后续里程碑描述中显式标注"可先行"的 presentation-only 切片;② 已证明不影响 protocol / validator / runtime machine contract 的纯展示层改动。除此之外,任何涉及契约面的工作必须等前置里程碑稳定。
+## 后 P4c 执行规则
-> **结构重构锚点**:跨 contract 的模块拆分与 legacy control surface 收口应与里程碑同步——P1 后优先 subject resolution / plan lookup 统一入口(✅),P1.5 后优先 authorization policy / gate-receipt 收敛(✅),P2 后优先 action contract adapter 统一(✅),P3a sunset 表最终清理(✅)。engine.py / decision_tables.py 系统拆分属于 P4b(runtime_surface_consolidation),需 P4a 外部消费面冻结后执行。不阻止与上述 contract 无关且不改变 machine truth 的低风险整理。
+P0→P4c 主航道已全部完成。后续执行遵循以下原则:
-| 优先级 | 任务 | 前置条件 | 说明 |
-|--------|------|---------|------|
-| P0 | Blueprint rebaseline | 无 | 已完成。重写 blueprint,实体化 ADR,定义削减目标 |
-| P1 | subject_identity_binding | P0 | 已完成。protocol / validator / runtime 三联动定义"操作的是谁" |
-| P1.5 | execution_authorization_spine | P1 | 已完成。操作化 ADR-017 ExecutionAuthorizationReceipt,规划授权链路 |
-| P2 | local_action_contracts | P1.5 | 已完成。在主体已绑定前提下收敛局部动作 contract |
-| P3a | contract_aligned_cleanup | P2 | 已完成。以 protocol/validator 已稳定为前提,清理 runtime 旧 contract 面 |
-| P3b | perimeter_cleanup | P3a | 已完成。外围面清理:release gate 修复、CHANGELOG 去文件列表化、tests 分类、旧概念清理 |
-| P4a | external_surface_freeze | P3b | 已完成。薄切片:冻结不可删外部消费面 keep-list |
-| P4b | runtime_surface_consolidation | P4a | 已完成。prove-kept-or-delete 证明 <20K 不可达,实删 15 LOC |
-| P4b.5 | runtime_optionality_audit | P4b | 已完成。设计/审计型:宿主接入层级矩阵 + 消费矩阵 + blast radius + 综合裁定 |
-| P4c | host_consumption_governance | P4b.5 | 已完成。宿主只消费 contract,不定义 truth |
+1. **串行收敛**:未选定下一主线前,不并行开启多个会扩张 machine truth 的方向。
+2. **主线依赖链**:P4d → P5 → P6 仍按依赖链推进;在前项尚未形成足够结论前,不把后项提升为正式主线。
+3. **证据驱动升级**:证据型候选服务于主线但不占 P 编号;其产出是主线里程碑的升级证据,不是独立的主航道步骤。(关于硬约束 §8"新增项必须挂回现有 P 主航道"的释义:此约束要求新增开放项服务既有 P 主线,不能另起一条主航道;不等于每个开放项都必须拿一个 P 编号。)
-### P0: Blueprint Rebaseline(已完成)
+## 已完成主航道(P0→P4c)
-✅ 已完成。重写 blueprint 三件套、实体化 ADR、定义削减预算表、落地 protocol.md v0。细节见 git history。
+| 编号 | 名称 | 一行摘要 | 归档 |
+|------|------|---------|------|
+| P0 | Blueprint Rebaseline | 蓝图三件套 + ADR 实体化 + 削减预算表 + protocol.md v0 | git history |
+| P1 | Subject Identity Binding | protocol §7 normative + Validator fail-closed + execute_existing_plan subject binding | `history/2026-05/20260504_subject_identity_binding/` |
+| P1.5 | Execution Authorization Spine | 4 方案包 + 3 先行切片 + 1 桥接切片,操作化 ADR-017 ExecutionAuthorizationReceipt | `history/2026-05/20260505_p15_*` ~ `20260506_p15_*` |
+| P2 | Local Action Contracts | admission contract 闭合(subject binding + delta schema + action-effect pairing) | `history/2026-05/20260506_p2_local_action_contracts/` |
+| P3a | Contract-Aligned Cleanup | runtime 旧 contract 面清理 + execution routing 收敛 + dead path cleanup | `history/2026-05/20260507_p3a_contract_aligned_surface_cleanup/` |
+| P3b | Perimeter Cleanup | release gate 修复 + CHANGELOG 压缩 + tests 分类 + replay 下线 + 旧概念清理 | `history/2026-05/20260508_p3b_perimeter_cleanup/` |
+| P4a | External Surface Freeze | 15 条 keep-list + 20 条字段分类(纯文档) | `history/2026-05/20260509_p4a_external_surface_freeze/` |
+| P4b | Runtime Surface Consolidation | prove-kept-or-delete 全量扫描,24,334 LOC,15 LOC 实删 | `history/2026-05/20260509_p4b_runtime_surface_consolidation/` |
+| P4b.5 | Runtime Optionality Audit | 3 级梯度 + 消费矩阵 + blast radius + 综合裁定(纯审计) | `history/2026-05/20260510_p4b5_runtime_optionality_audit/` |
+| P4c | Host Consumption Governance | 契约投影 + 增强声明 + 渲染收敛 + prompt -140 行 + protocol.md §8 唯一入口 | `history/2026-05/20260510_p4c_host_consumption_governance/` |
-### P1: Subject Identity & Existing Plan Binding(已完成)
+## 主线里程碑
-✅ 已完成(归档:`history/2026-05/20260504_subject_identity_binding/`)。protocol §7 subject identity 升格 normative、Validator admission fail-closed、execute_existing_plan subject binding。P1 语义债(DECISION_REJECT consult 伪装)在 P1.5-A 收口。
+### P4d: New Host Pilot
-### P1.5: Execution Authorization Spine(已完成)
+选 1 个非 deep 宿主做试点(payload_capable + 接续增强),不接完整 runtime。验证官方最低新宿主画像是否成立、P4b.5/P4c 的分层是否真正降低接入成本。
-✅ 已完成(4 方案包 + 3 先行切片 + 1 桥接切片)。归档:`history/2026-05/20260505_p15_*` ~ `20260506_p15_*`
+**Companion: Continuation Entry Convergence**
-### P2: Local Action Contracts on Bound Subjects(已完成)
+统一宿主级官方入口语义(Inspect Active Work / Continue Active Work / Start New Work),覆盖同宿主跨 session 与跨宿主接续。只消费现有 frozen contract,不新增 machine truth,不绑定 runtime 正则/路由实现。不规定入口语法或关键词,宿主自行选择暴露形式。有活动工作或 pending checkpoint 时 Start New Work 必须显式仲裁。当前 `~go exec` 是 Continue Active Work 的命令级实现,应被 host-level 入口语义取代。结合 P4d 非 deep 宿主试点 formalize。
-✅ 已完成。admission contract 闭合(subject binding + delta schema + action-effect pairing)。归档:`history/2026-05/20260506_p2_local_action_contracts/`
+- 前置:P4c ✅
+- 状态:待启动
+- 升级判断:试点宿主走通 payload_capable + 接续增强的最小接续链路(至少 handoff 消费,常见配套为 plan binding + run state),且接续入口语义验证可行,即可声明 P4d 结论足够,评估 P5 启动
-### P3a: Contract-Aligned Surface Cleanup(已完成)
+### P5: Contract Surface Shrinkage
-✅ 已完成。runtime 旧 contract 面清理 + execution routing 收敛 + knowledge_sync audit trail + dead path cleanup。Runtime 结构性减重(26K→<20K)剥离为 P4b。归档:`history/2026-05/20260507_p3a_contract_aligned_surface_cleanup/`
+在 P4d 验证后,按 evidence 逐项删除或降级 deep runtime 专属的 contract surface(bridge capability / manifest entry / installer bundle 项)。此时已知哪些 contract 是新宿主需要 vs 历史包袱。
-### P3b: Perimeter Cleanup(已完成)
+- 前置:P4d
+- 状态:待 P4d 结论
+- 升级判断:P4d 试点产出 keep / delete / downgrade 裁定表,覆盖所有 deep-only contract 面
-✅ 已完成。Release gate 修复、CHANGELOG 压缩、tests 分类标注、replay/workflow-learning 下线、README 首屏降噪、旧概念清理。归档:`history/2026-05/20260508_p3b_perimeter_cleanup/`
+### P6: Runtime Sunset / Reference Runtime
-### P4a: External Surface Freeze(已完成)
+将 runtime 明确降级为 reference implementation 或 deep host hardening layer。新宿主默认走 Protocol/Convention 模式,runtime 不再承载新增产品能力。可能与 P5 合并。
-✅ 已完成。Frozen External Surface keep-list(15 条)+ Output Rendering Audit(20 条字段分类 + 5 个已知热点)。纯文档变更,不写运行代码。归档:`history/2026-05/20260509_p4a_external_surface_freeze/`
+- 前置:P5
+- 状态:待 P5 结论
+- 升级判断:P5 裁定表执行完毕,deep-only surface 削减到可维护水位
-### P4b: Runtime Surface Consolidation(已完成)
+## 证据型候选(为下一主线提供升级证据,不占 P 编号)
-✅ 已完成。prove-kept-or-delete 全量扫描证明 runtime 在当前 contract 约束下已接近最小可行体积(24,334 LOC)。<20K 目标在不改 distribution/installer contract 的约束下不可达。交付物:Phase 0 test re-audit(653 hard / 31 soft gate)、Phase 1 CI/preflight 真实降载、Phase 2 全量死代码扫描(15 LOC 删除)。归档:`history/2026-05/20260509_p4b_runtime_surface_consolidation/`
+> 以下项目服务于主线里程碑的推进判断,不是独立的主航道步骤。其产出(验证报告、试点数据、compliance 结论)是主线决策的输入。
-### P4b.5: Runtime Optionality & Host Onboarding Audit(已完成)
+### First-Use Adoption Proof
-✅ 已完成。设计/审计型,不改代码。交付物:S1 Forbidden Surface(F1-F8)、S2 消费矩阵 + opt-in 增强组合 + 官方接入画像、S3 Blast Radius 审计(15 功能区 + 语义来源→落盘→contract 映射)、S4 综合裁定 + P4c 前提声明。归档:`history/2026-05/20260510_p4b5_runtime_optionality_audit/`
+验证非作者用户/宿主能安装、触发、理解、走通 Sopify 首次使用链路。覆盖 install/bootstrap 文案压缩、status/doctor 用户语言化验收、scripts 用户面与维护面分离、至少一个非作者首次采用 walkthrough。前提:P4c 已确保默认路径不暴露内部 taxonomy。边界:只做首次采用证明与用户面收口,不重开 protocol/runtime 产品定位,不回头扩 machine contract。
-### P4c: Host Consumption Governance(已完成)
+- 前置:P4c ✅
+- 服务主线:P4d(提供首次采用基线数据)
+- 最小交付证据:至少 1 个非作者用户完成首次安装→触发→理解→走通链路的记录(含卡点与解决方式)
-✅ 已完成(P4c-5 Prompt Asset 结构收口显式跳过)。P4b.5 审计结论转化为可执行治理:宿主只消费稳定 contract,不再定义 machine truth。交付物:P4c-1 契约投影矩阵(deep_verified † 全部消除)、P4c-2 增强声明/检测协议、P4c-3a output/doctor/handoff 渲染收敛、P4c-3b 首接触/prompt 收敛(-140 行 route taxonomy 删除,protocol.md §8 引用替代)、P4c-4 protocol.md §8 唯一入口 + 文档披露梯度 + builtin skill 披露 + design.md 结构整理。跨切片 invariant 5/5 PASS,红线 6/6 无违反。归档:`history/2026-05/20260510_p4c_host_consumption_governance/`
+### Protocol Compliance Phase 2
-## 未完成长期项
+在 Phase 1 文件断言之上,参考 Superpowers headless behavioral test 做端到端行为验证;扩展 Convention smoke 到完整最小生命周期(含 knowledge_sync / blueprint writeback)。
-### P4b 后续路线(P4c 后视评估)
+- 前置:P4c ✅
+- 服务主线:P4d / P5(提供合规性证据)
+- 最小交付证据:Convention smoke 覆盖 Convention 模式最小生命周期,全部 PASS(具体步骤在 Phase 2 开包时定义)
-- [ ] P4d New Host Pilot:选 1 个非 deep 宿主做试点(convention_only 或 payload_capable),不接完整 runtime。验证 P4b.5/P4c 的分层是否真正降低接入成本。可与 P4c 后期并行启动。
-- [ ] Continuation Entry Convergence:统一宿主级官方入口语义(Inspect Active Work / Continue Active Work / Start New Work),覆盖同宿主跨 session 与跨宿主接续。只消费现有 frozen contract,不新增 machine truth,不绑定 runtime 正则/路由实现。不规定入口语法或关键词,宿主自行选择暴露形式(命令、按钮、菜单等)。有活动工作或 pending checkpoint 时 Start New Work 必须显式仲裁。当前 `~go exec` 是 Continue Active Work 的命令级实现,应被 host-level 入口语义取代。_触发条件:P4c 验收后,结合 P4d 非 deep 宿主试点 formalize_
-- [ ] P5 Contract Surface Shrinkage:在 P4d 验证后,按 evidence 逐项删除或降级 deep runtime 专属的 contract surface(bridge capability / manifest entry / installer bundle 项)。此时已知哪些 contract 是新宿主需要 vs 历史包袱。
-- [ ] P6 Runtime Sunset / Reference Runtime:将 runtime 明确降级为 reference implementation 或 deep host hardening layer。新宿主默认走 Protocol/Convention 模式,runtime 不再承载新增产品能力。可能与 P5 合并。
+### 第三方宿主自助接入 Convention 证明
-### 其他长期项
+不指定下一个官方深适配目标,先把 Convention quickstart + compliance check 做出来,再由外部宿主自行验证接入。
-- [ ] 补宿主级 first-hop ingress proof / diagnostics
-- [ ] `~compare` shortlist facade 收敛进默认主链路
-- [-] `workflow-learning` 独立 helper 与更稳定 replay retrieval → P3b replay 能力下线后,未来如需重设计另行评估
-- [ ] blueprint 索引摘要更细粒度自动刷新
-- [ ] history feature_key 聚合视图
+- 前置:P4c ✅
+- 服务主线:P4d(提供外部接入可行性证据)
+- 最小交付证据:Convention quickstart 文档 + compliance check 脚本可独立运行,至少 1 个非 deep 宿主跑通
+
+## 独立产品线
+
+### CrossReview
+
+独立审查内核,宿主中立。Phase 0 决策全部锁定(命名/边界/仓库形态/资产层/MVP artifact/集成顺序),Phase 1-4 待推进。与主航道无硬依赖,可独立推进。
-- [ ] CrossReview Phase 4a:advisory skill 接入 develop 后审查
-- [ ] Plan intake checklist(在 intake 模板/脚本落地前,后续新 plan 开包时手工回答以下问题):
- 1. 主命中哪个蓝图里程碑(P4b.5 / P4c / P4d / P5 / P6)?若不命中主线,须显式标记为"长期项"或"延后项",不强行归类
+- Registry 状态:active + governance deferred
+- 详见:`plan/20260418_cross_review_engine/`
+- 相关蓝图项:CrossReview Phase 4a(advisory skill 接入 develop 后审查)
+
+## 流程与工具项
+
+- [ ] Plan intake checklist(后续新 plan 开包时手工回答以下问题):
+ 1. 主命中哪个蓝图活跃分层?主线里程碑(P4d / P5 / P6)、证据型候选(Adoption Proof / Compliance Phase 2 / Convention 证明)、独立产品线(CrossReview)?若不命中以上任一,须显式标记为"流程工具项"或"延后项",不强行归类
2. 这次改动定义的是 contract acceptance boundary,还是 execution strategy / implementation wave?(前者进 blueprint,后者留方案包)
3. 是否新增、删除、替代 action / route / state / checkpoint / receipt 中的任一 machine truth?若是,对照 `design.md` 削减预算表
4. 若涉及 legacy surface,替代 contract 是否已在 `design.md` sunset 表中对应里程碑稳定?
5. 若影响 Core promotion rule / hard max / ownership / validator authority,须补充 ADR impact
-- [ ] Multi-host review contract 正式化(protocol.md §7 从 informative/draft 升级为 normative)— 部分由 P1 subject identity 升格推进
+- [ ] 补宿主级 first-hop ingress proof / diagnostics
+- [ ] `~compare` shortlist facade 收敛进默认主链路
+- [ ] blueprint 索引摘要更细粒度自动刷新
+- [ ] history feature_key 聚合视图
+- [ ] Multi-host review contract 正式化(protocol.md §7 从 informative/draft 升级为 normative)
- [ ] 方案级收敛语义操作化(risk ladder + 验证深度规则 + 多审查者冲突解决)
- [ ] 轻量化产品指标与 acceptance gate(首次上手步骤数、必需文件数、默认 workflow 必需 contract 数)
-- [-] Convention 入口兑现差距 → 已转入 P1.5 可先行切片,非落地完成
- [ ] 产品层 ↔ 实现层 contract matrix 正式化(ownership / admission / lifecycle responsibilities)
-- [ ] Protocol Compliance Phase 2:在 Phase 1 文件断言之上,参考 Superpowers headless behavioral test 做端到端行为验证;扩展 Convention smoke 到完整最小生命周期(含 knowledge_sync / blueprint writeback)。外部启发:Superpowers headless Claude 测试,准入 T1 Adoption。_触发条件:P4c 验收通过后评估是否提升为里程碑_
-- [ ] 第三方宿主自助接入 Convention 证明:不指定下一个官方深适配目标,先把 Convention quickstart + compliance check 做出来,再由外部宿主自行验证接入。_触发条件:P4c 验收通过后评估是否提升为里程碑_
-- [-] 第三方 CLI 宿主能力边界治理(P4a→P4c bridge):已设计,待 P4c 消费。Host Capability Ladder(3 级 canonical 梯度)、接入判定 Checklist、Convention Quickstart 最小交付面、Prompt 镜像治理原则已落地 → `blueprint/design.md` Host Capability Governance 节。_P4c 消费该结论。_
-- [ ] First-Use Adoption Proof:验证非作者用户/宿主能安装、触发、理解、走通 Sopify 首次使用链路。覆盖 install/bootstrap 文案压缩、status/doctor 用户语言化验收、scripts 用户面与维护面分离、至少一个非作者首次采用 walkthrough。前提:P4c 已确保默认路径不暴露内部 taxonomy。边界:只做首次采用证明与用户面收口,不重开 protocol/runtime 产品定位,不回头扩 machine contract,不把维护者脚本整理泛化成大规模重构。语义方向待收窄(External Adoption Proof 或 User-Facing Onboarding Convergence),编号待定。_触发条件:P4c 验收通过后评估是否提升为里程碑_
+
+## 已关闭 / 已吸收项
+
+| 原项 | 处置状态 | 归因 |
+|------|---------|------|
+| workflow-learning 独立 helper | 终止 | P3b replay 能力下线,未来如需重设计另行评估 |
+| Convention 入口兑现差距 | 已并入 | 已转入 P1.5 先行切片 |
+| ~replay 更多入口 | 终止 | P3b replay 能力已下线 |
+| 第三方 CLI 宿主能力边界治理(P4a→P4c bridge) | 已消费 | P4c 已消费,Host Capability Governance 已落地 design.md |
## 明确延后项
-- [-] runtime 全接管 develop orchestrator
-- [-] 非 CLI 宿主图形化表单
-- [-] history 正文纳入默认长期上下文
-- [-] daily index
-- [-] ~replay 更多入口 → P3b replay 能力已下线
-- [-] runtime 独立 preferences_artifact
-- [-] 偏好自动归纳/提炼
-- [-] `sopify init --minimal` 等新增 CLI 面(Convention 入口优先通过文档/模板兑现)
-- [-] 知识自动提炼(Hermes Agent persistent memory + curator 方向,T0 Reference;与 runtime 全接管、偏好自动归纳有直接张力)
-- [-] 声明式工作流引擎(Spec-Kit YAML workflow engine 方向,T0 Reference;与 Runtime-optional 有张力)
+| 项目 | 复审触发 |
+|------|---------|
+| runtime 全接管 develop orchestrator | Runtime-optional 方向明确转向时 |
+| 非 CLI 宿主图形化表单 | 有非 CLI 宿主试点需求时 |
+| history 正文纳入默认长期上下文 | 上下文窗口或 KB 机制显著变化时 |
+| daily index | 用户数增长到需要每日索引时 |
+| runtime 独立 preferences_artifact | 偏好管理成为产品瓶颈时 |
+| 偏好自动归纳/提炼 | 与 runtime 全接管/知识自动提炼方向收敛时 |
+| `sopify init --minimal` 等新增 CLI 面 | Convention 入口优先通过文档/模板兑现;P4d 试点后评估 |
+| 知识自动提炼(Hermes Agent 方向) | 与 Runtime-optional 张力解决后 |
+| 声明式工作流引擎(Spec-Kit 方向) | 与 Runtime-optional 张力解决后 |
+
+## 遴选决策记录
+
+(待填写)
From 35c43737ba7dfb539e6470aba81a5a11e75e22f8 Mon Sep 17 00:00:00 2001
From: "sanze.li" <2522048902@qq.com>
Date: Wed, 13 May 2026 11:17:57 +0800
Subject: [PATCH 2/3] fix: YAML block scalar support + installer bundle smoke
isolation
- Add block scalar parsing (|, |-, >, >-) to built-in YAML parser
- Isolate bundle smoke HOME to prevent user-level skills from
interfering with validation
- External skill front-matter parse errors degrade gracefully
(fail-closed for builtin, tolerant for user skills)
Release-Sync: yes
Release-Version: 2026-05-13.111757
Release-Date: 2026-05-13
---
CHANGELOG.md | 12 +++++
Claude/Skills/CN/CLAUDE.md | 2 +-
Claude/Skills/EN/CLAUDE.md | 2 +-
Codex/Skills/CN/AGENTS.md | 2 +-
Codex/Skills/EN/AGENTS.md | 2 +-
README.md | 10 +++-
README.zh-CN.md | 10 +++-
installer/validate.py | 5 ++
runtime/_yaml.py | 69 +++++++++++++++++++++++++++-
runtime/skill_registry.py | 13 ++++--
tests/test_installer_validate.py | 2 +
tests/test_runtime_config.py | 18 ++++++++
tests/test_runtime_skill_registry.py | 55 ++++++++++++++++++++++
13 files changed, 190 insertions(+), 12 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index af256c6..d458383 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,18 @@ Format: Summary → Changed → Plan Packages. File-level details live in `git l
## [Unreleased]
+## [2026-05-13.111757] - 2026-05-13
+
+### Summary
+
+- Changes across: Runtime, Tests, Changed.
+
+### Changed
+
+- **Runtime**: Updated runtime internals (2 files)
+- **Tests**: Updated automated coverage (3 files)
+- **Changed**: Updated project files (1 files)
+
## [2026-05-11.202509] - 2026-05-11
### Summary
diff --git a/Claude/Skills/CN/CLAUDE.md b/Claude/Skills/CN/CLAUDE.md
index 9df1b46..6540302 100644
--- a/Claude/Skills/CN/CLAUDE.md
+++ b/Claude/Skills/CN/CLAUDE.md
@@ -1,5 +1,5 @@
-
+
# Sopify - 自适应 AI 编程助手
diff --git a/Claude/Skills/EN/CLAUDE.md b/Claude/Skills/EN/CLAUDE.md
index 3793878..2ef80a7 100644
--- a/Claude/Skills/EN/CLAUDE.md
+++ b/Claude/Skills/EN/CLAUDE.md
@@ -1,5 +1,5 @@
-
+
# Sopify - Adaptive AI Programming Assistant
diff --git a/Codex/Skills/CN/AGENTS.md b/Codex/Skills/CN/AGENTS.md
index c11b054..3a2afa0 100644
--- a/Codex/Skills/CN/AGENTS.md
+++ b/Codex/Skills/CN/AGENTS.md
@@ -1,5 +1,5 @@
-
+
# Sopify - 自适应 AI 编程助手
diff --git a/Codex/Skills/EN/AGENTS.md b/Codex/Skills/EN/AGENTS.md
index bd68ff8..dcba08c 100644
--- a/Codex/Skills/EN/AGENTS.md
+++ b/Codex/Skills/EN/AGENTS.md
@@ -1,5 +1,5 @@
-
+
# Sopify - Adaptive AI Programming Assistant
diff --git a/README.md b/README.md
index fc0c990..7d5f71c 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
[](./LICENSE)
[](./LICENSE-docs)
-[](#version-history)
+[](#version-history)
[](./CONTRIBUTING.md)
English · [简体中文](./README.zh-CN.md) · [Quick Start](#quick-start) · [Contributors](./CONTRIBUTORS.md)
@@ -50,6 +50,14 @@ Sopify uses project-level conventions to make critical steps visible. The basic
| Decisions are invisible and non-auditable | Plan changes force re-confirmation — the AI cannot silently proceed |
| Each session's learning is disposable | Plans, decisions, and reviews persist as reusable project assets |
+## Architecture
+
+
+

+
+
+User input flows through a host adapter (Codex, Claude, etc.) into the Core Protocol, where every action is proposed, validated, gated, and receipted. The Validator is the sole authorizer — the host LLM is only a proposal source. Knowledge layers (blueprint, plan, history) persist across sessions and hosts.
+
## Installation
Two-step install (recommended for first-time review):
diff --git a/README.zh-CN.md b/README.zh-CN.md
index d5aa3d2..41e04b0 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -8,7 +8,7 @@
[](./LICENSE)
[](./LICENSE-docs)
-[](#版本历史)
+[](#版本历史)
[](./CONTRIBUTING_CN.md)
[English](./README.md) · 简体中文 · [快速开始](#快速开始) · [贡献者](./CONTRIBUTORS.md)
@@ -50,6 +50,14 @@ Sopify 用项目级约定把关键节点变成可见流程。基础过程记录
| 决策不可见、不可审计 | 方案变更后必须重新确认 — AI 不能静默继续 |
| 每个 session 的学习都是一次性的 | 方案、决策、审查结论沉淀为可复用的项目资产 |
+## 架构
+
+
+

+
+
+用户输入经过宿主适配器(Codex、Claude 等)进入核心协议层,每个操作都经历提议、校验、闸门、收据四步。Validator 是唯一授权者 — 宿主 LLM 只是提议来源。知识层(蓝图、方案、历史)跨 session 和宿主持久保留。
+
## 安装说明
两步安装(推荐首次使用时先审查再执行):
diff --git a/installer/validate.py b/installer/validate.py
index 4e4a63c..4272164 100644
--- a/installer/validate.py
+++ b/installer/validate.py
@@ -127,6 +127,11 @@ def _build_bundle_smoke_env(*, payload_manifest_path: Path | None) -> dict[str,
env = dict(os.environ)
if payload_manifest_path is not None:
env["SOPIFY_PAYLOAD_MANIFEST"] = str(payload_manifest_path)
+ # Keep bundle smoke focused on bundle/runtime assets instead of inheriting
+ # arbitrary user-level skills from the current machine.
+ isolated_home = Path(env.get("TMPDIR") or "/tmp") / "sopify-bundle-smoke-home"
+ isolated_home.mkdir(parents=True, exist_ok=True)
+ env["HOME"] = str(isolated_home)
return env
diff --git a/runtime/_yaml.py b/runtime/_yaml.py
index 9694fc6..d28789d 100644
--- a/runtime/_yaml.py
+++ b/runtime/_yaml.py
@@ -97,7 +97,14 @@ def _parse_mapping(lines: Sequence[_Line], index: int, indent: int) -> Tuple[dic
if line.content.startswith("- "):
break
key, remainder = _split_key_value(line)
- if remainder == "":
+ if _is_block_scalar_marker(remainder):
+ value, index = _parse_block_scalar(
+ lines,
+ index + 1,
+ parent_indent=indent,
+ style=remainder,
+ )
+ elif remainder == "":
index += 1
if index < len(lines) and lines[index].indent > indent:
value, index = _parse_block(lines, index, lines[index].indent)
@@ -136,7 +143,15 @@ def _parse_list(lines: Sequence[_Line], index: int, indent: int) -> Tuple[list[A
if _looks_like_mapping_entry(item_text):
key, remainder = _split_key_value(_Line(indent=indent + 2, content=item_text, line_number=line.line_number))
item: dict[str, Any] = {}
- if remainder == "":
+ if _is_block_scalar_marker(remainder):
+ value, index = _parse_block_scalar(
+ lines,
+ index,
+ parent_indent=indent,
+ style=remainder,
+ )
+ item[key] = value
+ elif remainder == "":
if has_child:
value, index = _parse_block(lines, index, lines[index].indent)
else:
@@ -197,3 +212,53 @@ def _parse_scalar(value: str) -> Any:
inner = value[1:-1]
return inner.replace(r"\'", "'").replace(r'\"', '"')
return value
+
+
+def _is_block_scalar_marker(value: str) -> bool:
+ return value in {"|", "|-", ">", ">-"}
+
+
+def _parse_block_scalar(
+ lines: Sequence[_Line],
+ index: int,
+ *,
+ parent_indent: int,
+ style: str,
+) -> Tuple[str, int]:
+ if index >= len(lines) or lines[index].indent <= parent_indent:
+ return "", index
+
+ block_indent = lines[index].indent
+ chunks: list[str] = []
+ while index < len(lines):
+ line = lines[index]
+ if line.indent < block_indent:
+ break
+ if line.indent == parent_indent:
+ break
+ if line.indent < block_indent:
+ raise YamlParseError(f"Unexpected indentation at line {line.line_number}")
+ relative_indent = line.indent - block_indent
+ chunks.append((" " * relative_indent) + line.content)
+ index += 1
+
+ if style.startswith("|"):
+ text = "\n".join(chunks)
+ else:
+ paragraphs: list[str] = []
+ current: list[str] = []
+ for chunk in chunks:
+ if chunk == "":
+ if current:
+ paragraphs.append(" ".join(current))
+ current = []
+ paragraphs.append("")
+ else:
+ current.append(chunk)
+ if current:
+ paragraphs.append(" ".join(current))
+ text = "\n".join(paragraphs)
+
+ if not style.endswith("-"):
+ text += "\n"
+ return text, index
diff --git a/runtime/skill_registry.py b/runtime/skill_registry.py
index 85aea8b..abdd187 100644
--- a/runtime/skill_registry.py
+++ b/runtime/skill_registry.py
@@ -7,7 +7,7 @@
from typing import Dict, Iterable, Mapping, Optional, Sequence
import re
-from ._yaml import load_yaml
+from ._yaml import YamlParseError, load_yaml
from .builtin_catalog import load_builtin_skills
from .models import RuntimeConfig, SkillMeta
from .skill_schema import SkillManifestError, normalize_skill_manifest
@@ -77,7 +77,7 @@ def _discover_under_root(self, root: Path, source: str) -> Iterable[SkillMeta]:
def _read_skill(self, skill_file: Path, source: str) -> Optional[SkillMeta]:
text = skill_file.read_text(encoding="utf-8")
- front_matter = _parse_front_matter(text)
+ front_matter = _parse_front_matter(text, fail_closed=(source == "builtin"))
skill_dir = skill_file.parent
raw_manifest = _load_manifest(skill_dir / "skill.yaml")
try:
@@ -131,11 +131,16 @@ def _read_skill(self, skill_file: Path, source: str) -> Optional[SkillMeta]:
)
-def _parse_front_matter(text: str) -> dict[str, object]:
+def _parse_front_matter(text: str, *, fail_closed: bool = False) -> dict[str, object]:
match = _FRONT_MATTER_RE.match(text)
if not match:
return {}
- data = load_yaml(match.group(1))
+ try:
+ data = load_yaml(match.group(1))
+ except YamlParseError:
+ if fail_closed:
+ raise
+ return {}
return data if isinstance(data, dict) else {}
diff --git a/tests/test_installer_validate.py b/tests/test_installer_validate.py
index 52dc0e1..70c933c 100644
--- a/tests/test_installer_validate.py
+++ b/tests/test_installer_validate.py
@@ -39,6 +39,8 @@ def test_smoke_uses_explicit_payload_manifest_env(self) -> None:
self.assertEqual(output, "ok")
env = mock_run.call_args.kwargs.get("env") or {}
self.assertEqual(env.get("SOPIFY_PAYLOAD_MANIFEST"), str(payload_manifest))
+ self.assertIn("HOME", env)
+ self.assertTrue(env["HOME"].endswith("sopify-bundle-smoke-home"))
def test_failure_details_always_include_exit_status_and_command(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
diff --git a/tests/test_runtime_config.py b/tests/test_runtime_config.py
index 9dcca6f..840ab3b 100644
--- a/tests/test_runtime_config.py
+++ b/tests/test_runtime_config.py
@@ -51,3 +51,21 @@ class YamlLoaderTests(unittest.TestCase):
def test_quoted_list_item_with_colon_is_parsed_as_string(self) -> None:
payload = load_yaml('triggers:\n - "~go"\n - "status:"\n')
self.assertEqual(payload["triggers"], ["~go", "status:"])
+
+ def test_folded_block_scalar_is_supported(self) -> None:
+ payload = load_yaml(
+ "name: sample\n"
+ "description: >-\n"
+ " first line\n"
+ " second line\n"
+ )
+ self.assertEqual(payload["description"], "first line second line")
+
+ def test_literal_block_scalar_is_supported(self) -> None:
+ payload = load_yaml(
+ "name: sample\n"
+ "notes: |\n"
+ " first line\n"
+ " second line\n"
+ )
+ self.assertEqual(payload["notes"], "first line\nsecond line\n")
diff --git a/tests/test_runtime_skill_registry.py b/tests/test_runtime_skill_registry.py
index 6a07142..92cd023 100644
--- a/tests/test_runtime_skill_registry.py
+++ b/tests/test_runtime_skill_registry.py
@@ -323,3 +323,58 @@ def test_skill_registry_user_alias_precedence_prefers_public_aliases(self) -> No
self.assertEqual(user_skill.source, "user")
self.assertEqual(claude_skill.description, "claude-only")
self.assertEqual(claude_skill.source, "user")
+
+ def test_skill_registry_tolerates_external_front_matter_block_scalars(self) -> None:
+ with tempfile.TemporaryDirectory() as temp_dir:
+ workspace = Path(temp_dir)
+ user_home = workspace / "home"
+ skill_dir = user_home / ".claude" / "skills" / "block-scalar-skill"
+ skill_dir.mkdir(parents=True)
+ (skill_dir / "SKILL.md").write_text(
+ "---\n"
+ "name: block-scalar-skill\n"
+ "description: >-\n"
+ " first line\n"
+ " second line\n"
+ "---\n\n"
+ "# block-scalar-skill\n",
+ encoding="utf-8",
+ )
+ (skill_dir / "skill.yaml").write_text(
+ "id: block-scalar-skill\nmode: advisory\n",
+ encoding="utf-8",
+ )
+
+ config = load_runtime_config(workspace)
+ skills = SkillRegistry(config, user_home=user_home).discover()
+ skill = next(item for item in skills if item.skill_id == "block-scalar-skill")
+
+ self.assertEqual(skill.description, "first line second line")
+ self.assertEqual(skill.source, "user")
+
+ def test_skill_registry_ignores_invalid_external_front_matter_when_manifest_is_valid(self) -> None:
+ with tempfile.TemporaryDirectory() as temp_dir:
+ workspace = Path(temp_dir)
+ user_home = workspace / "home"
+ skill_dir = user_home / ".claude" / "skills" / "invalid-front-matter"
+ skill_dir.mkdir(parents=True)
+ (skill_dir / "SKILL.md").write_text(
+ "---\n"
+ "name: invalid-front-matter\n"
+ "description:\n"
+ " broken\n"
+ "---\n\n"
+ "# invalid-front-matter\n",
+ encoding="utf-8",
+ )
+ (skill_dir / "skill.yaml").write_text(
+ "id: invalid-front-matter\nmode: advisory\n",
+ encoding="utf-8",
+ )
+
+ config = load_runtime_config(workspace)
+ skills = SkillRegistry(config, user_home=user_home).discover()
+ skill = next(item for item in skills if item.skill_id == "invalid-front-matter")
+
+ self.assertEqual(skill.name, "invalid-front-matter")
+ self.assertEqual(skill.description, "")
From d9959e6c189445c604a7a3e094b4ceb4332dc1bd Mon Sep 17 00:00:00 2001
From: "sanze.li" <2522048902@qq.com>
Date: Wed, 13 May 2026 11:21:32 +0800
Subject: [PATCH 3/3] docs: add architecture diagram to README + expand
Adoption Proof criteria
- Add simplified architecture SVG to README (EN + CN)
- Image in assets/ (SVG primary, PNG fallback)
- Expand First-Use Adoption Proof deliverables in blueprint tasks.md:
examples/ demo, workflow visual asset, release prerequisite
- Move test pass rate to independent quality gate item
- Add GitHub Release pipeline to process/tooling items
---
.sopify-skills/blueprint/tasks.md | 8 +++-
assets/sopify-architecture.png | Bin 0 -> 236851 bytes
assets/sopify-architecture.svg | 77 ++++++++++++++++++++++++++++++
3 files changed, 84 insertions(+), 1 deletion(-)
create mode 100644 assets/sopify-architecture.png
create mode 100644 assets/sopify-architecture.svg
diff --git a/.sopify-skills/blueprint/tasks.md b/.sopify-skills/blueprint/tasks.md
index a2cfe03..2090b63 100644
--- a/.sopify-skills/blueprint/tasks.md
+++ b/.sopify-skills/blueprint/tasks.md
@@ -69,7 +69,11 @@ P0→P4c 主航道已全部完成。后续执行遵循以下原则:
- 前置:P4c ✅
- 服务主线:P4d(提供首次采用基线数据)
-- 最小交付证据:至少 1 个非作者用户完成首次安装→触发→理解→走通链路的记录(含卡点与解决方式)
+- 前置条件:GitHub Release 可用——install 脚本 `curl` / `iwr` 命令实际可执行(安装链路的物理前提)
+- 最小交付证据:
+ 1. 至少 1 个非作者用户完成首次安装→触发→理解→走通链路的记录(含卡点与解决方式)
+ 2. `examples/` 包含至少 1 个可独立跟随的端到端 demo(含配置、操作步骤、预期输出)
+ 3. README 包含至少 1 个视觉资产(截图或 GIF),展示核心工作流的用户可感知效果(架构图不计入此项)
### Protocol Compliance Phase 2
@@ -113,6 +117,8 @@ P0→P4c 主航道已全部完成。后续执行遵循以下原则:
- [ ] 方案级收敛语义操作化(risk ladder + 验证深度规则 + 多审查者冲突解决)
- [ ] 轻量化产品指标与 acceptance gate(首次上手步骤数、必需文件数、默认 workflow 必需 contract 数)
- [ ] 产品层 ↔ 实现层 contract matrix 正式化(ownership / admission / lifecycle responsibilities)
+- [ ] GitHub Release pipeline 建立(首次 release 创建 + tag 规范 + install 脚本端到端验证)
+- [ ] 测试套件健康基线(pass rate ≥ 99%;当前基线 717 tests / 695 passed / 22 failed = 97%)
## 已关闭 / 已吸收项
diff --git a/assets/sopify-architecture.png b/assets/sopify-architecture.png
new file mode 100644
index 0000000000000000000000000000000000000000..86092a65f9b264585ddcefac9b72af924d08cc5f
GIT binary patch
literal 236851
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