Skip to content

Commit 169f168

Browse files
committed
feat: add vendored bundle control plane
1 parent 2f25dbb commit 169f168

31 files changed

Lines changed: 1539 additions & 90 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
python-version: "3.11"
3636

3737
- name: Run runtime unit tests
38-
run: python3 -m unittest tests/test_runtime.py
38+
run: python3 -m unittest tests/test_runtime.py tests/test_installer.py
3939

4040
- name: Run runtime smoke check
4141
run: bash scripts/check-runtime-smoke.sh

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ This changelog is maintained manually (not auto-generated).
1717
### Added
1818

1919
- Minimal KB bootstrap in `runtime/kb.py`, creating `project.md`, `wiki/overview.md`, `user/preferences.md`, and `history/index.md` on first runtime execution.
20+
- New bundle manifest contract in `runtime/manifest.py`, written to `.sopify-runtime/manifest.json` during bundle sync.
21+
- New runtime handoff contract in `runtime/handoff.py`, written to `.sopify-skills/state/current_handoff.json` for non-terminal routes.
2022
- New sub-skill `model-compare` (CN/EN) for configuration-driven multi-model parallel comparison with manual user selection.
2123
- New compare trigger contract:
2224
- Command: `~compare <question>`
@@ -25,16 +27,24 @@ This changelog is maintained manually (not auto-generated).
2527
- New GitHub Actions workflow `.github/workflows/ci.yml` to gate PR/Push with sync and version checks.
2628
- Default repo-local raw-input runtime entry `scripts/sopify_runtime.py` and plan-only helper `scripts/go_plan_runtime.py`.
2729
- Runtime bundle sync script `scripts/sync-runtime-assets.sh` for vendoring `.sopify-runtime/` into another repository.
30+
- One-command installer entry `scripts/install-sopify.sh` with a Python installer core and host adapters for `codex:zh-CN`, `codex:en-US`, `claude:zh-CN`, and `claude:en-US`.
2831
- Runtime smoke check script `scripts/check-runtime-smoke.sh`.
2932
- Runtime behavior test coverage in `tests/test_runtime.py`, including vendored bundle validation.
33+
- Installer test coverage in `tests/test_installer.py` for Codex/Claude sample install paths.
3034

3135
### Changed
3236

37+
- Updated bundle sync, installer validation, and smoke checks to treat `.sopify-runtime/manifest.json` as a required control-plane artifact.
38+
- Updated runtime output to render `Next:` from the structured handoff contract before falling back to route-only copy.
39+
- Refactored runtime builtin-skill discovery to a catalog-first model via `runtime/builtin_catalog.py`, so vendored bundles no longer depend on scanning `Codex/Skills` or `Claude/Skills` trees.
40+
- Extended `SkillMeta` and external skill manifests with forward-compatible catalog fields: `entry_kind`, `handoff_kind`, `contract_version`, and `supports_routes`.
41+
- Added explicit builtin override policy in `runtime/skill_registry.py`: external skills can only replace builtin ids when `override_builtin: true` is declared.
3342
- Updated CN/EN AGENTS routing and command references to include `~compare` and `model-compare`.
3443
- Updated `README.md` and `README_EN.md` with:
3544
- 7-skill install verification list
3645
- Multi-model MVP quick start
3746
- Environment-variable-only API key setup (`export ...`, including `~/.zshrc` persistence guidance)
47+
- Recommended one-command installer usage for host prompt setup + vendored bundle sync
3848
- Added MVP fallback rule: when no usable multi-model config exists, `~compare` / `对比分析:` should not fail and must fallback to single-model with a clear notice.
3949
- Clarified two-layer compare switches:
4050
- `multi_model.enabled` = feature-level gate

Claude/Skills/CN/CLAUDE.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ Next: {下一步提示}
133133
| `~go exec` | 执行已有方案 |
134134
| `~compare` | 多模型并发对比(默认含当前会话模型;可用模型数不足 2 时降级并给出原因) |
135135

136-
说明:当前仓库若存在 `scripts/sopify_runtime.py`,原始输入应优先走该默认 repo-local runtime 入口;若 runtime 以 bundle 方式接入到其他仓库,则优先走 `.sopify-runtime/scripts/sopify_runtime.py`;仅在明确只走 `~go plan` 时使用对应的 `go_plan_runtime.py` helper。
136+
说明:当前仓库若存在 `scripts/sopify_runtime.py`,原始输入应优先走该默认 repo-local runtime 入口;若 runtime 以 bundle 方式接入到其他仓库,则宿主应优先读取 `.sopify-runtime/manifest.json` 决定入口,默认再回退到 `.sopify-runtime/scripts/sopify_runtime.py`;仅在明确只走 `~go plan` 时使用对应的 `go_plan_runtime.py` helper。
137+
说明:runtime 执行后,若存在 `.sopify-skills/state/current_handoff.json`,宿主应优先按其中的 `required_host_action``recommended_skill_ids``artifacts` 决定下一步;`Next:` 行仅作为面向人的摘要提示,不应作为唯一机器依据。
137138

138139
**workflow-learning 主动记录策略:**
139140
```yaml
@@ -283,6 +284,11 @@ progressive: 按需创建文件 (默认)
283284
| 轻量迭代 | 3-5 文件,清晰需求 | light 方案 + 执行 |
284285
| 完整开发 | >5 文件或架构变更 | 3 阶段完整流程 |
285286

287+
**宿主接入约定:**
288+
- `Claude/Skills` 只承担提示层职责,不作为 vendored runtime 的机器契约来源。
289+
- vendored runtime 的机器入口以 `.sopify-runtime/manifest.json` 为准。
290+
- runtime 执行后的机器交接以 `.sopify-skills/state/current_handoff.json` 为准;仅当 handoff 缺失时才回退到输出文案中的 `Next:`
291+
286292
---
287293

288294
## 阶段执行
@@ -411,9 +417,11 @@ scripts/sopify_runtime.py # 当前仓库默认原始输入入
411417
scripts/go_plan_runtime.py # 当前仓库用于 plan-only slice 的 helper
412418
.sopify-runtime/scripts/go_plan_runtime.py # vendored plan-only helper
413419
scripts/model_compare_runtime.py # ~compare 的运行时实现,不是默认通用入口
420+
.sopify-runtime/manifest.json # vendored bundle 机器契约,宿主应优先读取
421+
.sopify-skills/state/current_handoff.json # runtime 写出的结构化交接文件,宿主应优先读取
414422
```
415423

416-
说明:当前默认入口是 `scripts/sopify_runtime.py`;若以 bundle 方式接入则入口对应 `.sopify-runtime/scripts/sopify_runtime.py``go_plan_runtime.py` 只负责 plan-only;`~compare` 仍依赖宿主侧专用桥接。
424+
说明:当前默认入口是 `scripts/sopify_runtime.py`;若以 bundle 方式接入,优先按 `.sopify-runtime/manifest.json` 选入口,再回退到 `.sopify-runtime/scripts/sopify_runtime.py``go_plan_runtime.py` 只负责 plan-only;执行结束后宿主应优先读取 `.sopify-skills/state/current_handoff.json` 决定下一步`~compare` 仍依赖宿主侧专用桥接。
417425

418426
**配置文件:** `sopify.config.yaml` (项目根目录)
419427

Claude/Skills/EN/CLAUDE.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ Complex Task (full 3 phases):
133133
| `~go exec` | Execute existing plan |
134134
| `~compare` | Multi-model parallel comparison (includes session default model by default; falls back with reasons when usable model count is below 2) |
135135

136-
Note: when the current repository provides `scripts/sopify_runtime.py`, raw input should prefer that default repo-local runtime entry; when the runtime is vendored into another repository, prefer `.sopify-runtime/scripts/sopify_runtime.py`; use the matching `go_plan_runtime.py` helper only when you explicitly want the plan-only path.
136+
Note: when the current repository provides `scripts/sopify_runtime.py`, raw input should prefer that default repo-local runtime entry; when the runtime is vendored into another repository, the host should read `.sopify-runtime/manifest.json` first to choose the entry, and only then fall back to `.sopify-runtime/scripts/sopify_runtime.py`; use the matching `go_plan_runtime.py` helper only when you explicitly want the plan-only path.
137+
Note: after runtime execution, if `.sopify-skills/state/current_handoff.json` exists, the host should prioritize its `required_host_action`, `recommended_skill_ids`, and `artifacts` to decide the next step; the rendered `Next:` line is only a human-facing summary, not the sole machine contract.
137138

138139
**workflow-learning proactive capture policy:**
139140
```yaml
@@ -283,6 +284,11 @@ Semantic analysis routing:
283284
| Light Iteration | 3-5 files, clear requirements | Light plan + execution |
284285
| Full Development | >5 files or architectural changes | Full 3-phase workflow |
285286

287+
**Host Integration Contract:**
288+
- `Claude/Skills` is prompt-layer guidance only; it is not the machine contract for the vendored runtime.
289+
- For vendored runtime entry selection, treat `.sopify-runtime/manifest.json` as the source of truth.
290+
- For post-run continuation, treat `.sopify-skills/state/current_handoff.json` as the source of truth and fall back to the rendered `Next:` text only when the handoff file is missing.
291+
286292
---
287293

288294
## Phase Execution
@@ -411,9 +417,11 @@ scripts/sopify_runtime.py # default repo-local raw-input entr
411417
scripts/go_plan_runtime.py # helper for the plan-only slice
412418
.sopify-runtime/scripts/go_plan_runtime.py # vendored helper for the plan-only slice
413419
scripts/model_compare_runtime.py # runtime implementation for ~compare, not the default generic entry
420+
.sopify-runtime/manifest.json # vendored bundle machine contract; hosts should read this first
421+
.sopify-skills/state/current_handoff.json # structured handoff written by the runtime; hosts should read this first after execution
414422
```
415423

416-
Note: the default entry is `scripts/sopify_runtime.py`; when vendored, the entry becomes `.sopify-runtime/scripts/sopify_runtime.py`; `go_plan_runtime.py` is only for plan-only; `~compare` still depends on a host-side dedicated bridge.
424+
Note: the default entry is `scripts/sopify_runtime.py`; when vendored, prefer `.sopify-runtime/manifest.json` to select the entry and fall back to `.sopify-runtime/scripts/sopify_runtime.py`; `go_plan_runtime.py` is only for plan-only; after execution the host should read `.sopify-skills/state/current_handoff.json` before trusting `Next:`; `~compare` still depends on a host-side dedicated bridge.
417425

418426
**Configuration File:** `sopify.config.yaml` (project root)
419427

Codex/Skills/CN/AGENTS.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ Next: {下一步提示}
133133
| `~go exec` | 执行已有方案 |
134134
| `~compare` | 多模型并发对比(默认含当前会话模型;可用模型数不足 2 时降级并给出原因) |
135135

136-
说明:当前仓库若存在 `scripts/sopify_runtime.py`,原始输入应优先走该默认 repo-local runtime 入口;若 runtime 以 bundle 方式接入到其他仓库,则优先走 `.sopify-runtime/scripts/sopify_runtime.py`;仅在明确只走 `~go plan` 时使用对应的 `go_plan_runtime.py` helper。
136+
说明:当前仓库若存在 `scripts/sopify_runtime.py`,原始输入应优先走该默认 repo-local runtime 入口;若 runtime 以 bundle 方式接入到其他仓库,则宿主应优先读取 `.sopify-runtime/manifest.json` 决定入口,默认再回退到 `.sopify-runtime/scripts/sopify_runtime.py`;仅在明确只走 `~go plan` 时使用对应的 `go_plan_runtime.py` helper。
137+
说明:runtime 执行后,若存在 `.sopify-skills/state/current_handoff.json`,宿主应优先按其中的 `required_host_action``recommended_skill_ids``artifacts` 决定下一步;`Next:` 行仅作为面向人的摘要提示,不应作为唯一机器依据。
137138

138139
**workflow-learning 主动记录策略:**
139140
```yaml
@@ -283,6 +284,11 @@ progressive: 按需创建文件 (默认)
283284
| 轻量迭代 | 3-5 文件,清晰需求 | light 方案 + 执行 |
284285
| 完整开发 | >5 文件或架构变更 | 3 阶段完整流程 |
285286

287+
**宿主接入约定:**
288+
- `Codex/Skills` 只承担提示层职责,不作为 vendored runtime 的机器契约来源。
289+
- vendored runtime 的机器入口以 `.sopify-runtime/manifest.json` 为准。
290+
- runtime 执行后的机器交接以 `.sopify-skills/state/current_handoff.json` 为准;仅当 handoff 缺失时才回退到输出文案中的 `Next:`
291+
286292
---
287293

288294
## 阶段执行
@@ -411,9 +417,11 @@ scripts/sopify_runtime.py # 当前仓库默认原始输入入
411417
scripts/go_plan_runtime.py # 当前仓库用于 plan-only slice 的 helper
412418
.sopify-runtime/scripts/go_plan_runtime.py # vendored plan-only helper
413419
scripts/model_compare_runtime.py # ~compare 的运行时实现,不是默认通用入口
420+
.sopify-runtime/manifest.json # vendored bundle 机器契约,宿主应优先读取
421+
.sopify-skills/state/current_handoff.json # runtime 写出的结构化交接文件,宿主应优先读取
414422
```
415423

416-
说明:当前默认入口是 `scripts/sopify_runtime.py`;若以 bundle 方式接入则入口对应 `.sopify-runtime/scripts/sopify_runtime.py``go_plan_runtime.py` 只负责 plan-only;`~compare` 仍依赖宿主侧专用桥接。
424+
说明:当前默认入口是 `scripts/sopify_runtime.py`;若以 bundle 方式接入,优先按 `.sopify-runtime/manifest.json` 选入口,再回退到 `.sopify-runtime/scripts/sopify_runtime.py``go_plan_runtime.py` 只负责 plan-only;执行结束后宿主应优先读取 `.sopify-skills/state/current_handoff.json` 决定下一步`~compare` 仍依赖宿主侧专用桥接。
417425

418426
**配置文件:** `sopify.config.yaml` (项目根目录)
419427

Codex/Skills/EN/AGENTS.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ Complex Task (full 3 phases):
133133
| `~go exec` | Execute existing plan |
134134
| `~compare` | Multi-model parallel comparison (includes session default model by default; falls back with reasons when usable model count is below 2) |
135135

136-
Note: when the current repository provides `scripts/sopify_runtime.py`, raw input should prefer that default repo-local runtime entry; when the runtime is vendored into another repository, prefer `.sopify-runtime/scripts/sopify_runtime.py`; use the matching `go_plan_runtime.py` helper only when you explicitly want the plan-only path.
136+
Note: when the current repository provides `scripts/sopify_runtime.py`, raw input should prefer that default repo-local runtime entry; when the runtime is vendored into another repository, the host should read `.sopify-runtime/manifest.json` first to choose the entry, and only then fall back to `.sopify-runtime/scripts/sopify_runtime.py`; use the matching `go_plan_runtime.py` helper only when you explicitly want the plan-only path.
137+
Note: after runtime execution, if `.sopify-skills/state/current_handoff.json` exists, the host should prioritize its `required_host_action`, `recommended_skill_ids`, and `artifacts` to decide the next step; the rendered `Next:` line is only a human-facing summary, not the sole machine contract.
137138

138139
**workflow-learning proactive capture policy:**
139140
```yaml
@@ -283,6 +284,11 @@ Semantic analysis routing:
283284
| Light Iteration | 3-5 files, clear requirements | Light plan + execution |
284285
| Full Development | >5 files or architectural changes | Full 3-phase workflow |
285286

287+
**Host Integration Contract:**
288+
- `Codex/Skills` is prompt-layer guidance only; it is not the machine contract for the vendored runtime.
289+
- For vendored runtime entry selection, treat `.sopify-runtime/manifest.json` as the source of truth.
290+
- For post-run continuation, treat `.sopify-skills/state/current_handoff.json` as the source of truth and fall back to the rendered `Next:` text only when the handoff file is missing.
291+
286292
---
287293

288294
## Phase Execution
@@ -411,9 +417,11 @@ scripts/sopify_runtime.py # default repo-local raw-input entr
411417
scripts/go_plan_runtime.py # helper for the plan-only slice
412418
.sopify-runtime/scripts/go_plan_runtime.py # vendored helper for the plan-only slice
413419
scripts/model_compare_runtime.py # runtime implementation for ~compare, not the default generic entry
420+
.sopify-runtime/manifest.json # vendored bundle machine contract; hosts should read this first
421+
.sopify-skills/state/current_handoff.json # structured handoff written by the runtime; hosts should read this first after execution
414422
```
415423

416-
Note: the default entry is `scripts/sopify_runtime.py`; when vendored, the entry becomes `.sopify-runtime/scripts/sopify_runtime.py`; `go_plan_runtime.py` is only for plan-only; `~compare` still depends on a host-side dedicated bridge.
424+
Note: the default entry is `scripts/sopify_runtime.py`; when vendored, prefer `.sopify-runtime/manifest.json` to select the entry and fall back to `.sopify-runtime/scripts/sopify_runtime.py`; `go_plan_runtime.py` is only for plan-only; after execution the host should read `.sopify-skills/state/current_handoff.json` before trusting `Next:`; `~compare` still depends on a host-side dedicated bridge.
417425

418426
**Configuration File:** `sopify.config.yaml` (project root)
419427

README.md

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,35 +48,28 @@
4848

4949
### 安装
5050

51-
**Claude Code 用户**
51+
**一键接入(推荐)**
5252

5353
```bash
54-
# 中文版
55-
cp -r Claude/Skills/CN/* ~/.claude/
54+
# 当前目录作为目标仓库
55+
bash scripts/install-sopify.sh --target codex:zh-CN
5656

57-
# 英文版
58-
cp -r Claude/Skills/EN/* ~/.claude/
57+
# 指定目标仓库
58+
bash scripts/install-sopify.sh --target claude:en-US --workspace /path/to/project
5959
```
6060

61-
**Codex CLI 用户:**
61+
当前支持的 `target`
6262

63-
```bash
64-
# 中文版
65-
cp -r Codex/Skills/CN/* ~/.codex/
66-
67-
# 英文版
68-
cp -r Codex/Skills/EN/* ~/.codex/
69-
```
63+
- `codex:zh-CN`
64+
- `codex:en-US`
65+
- `claude:zh-CN`
66+
- `claude:en-US`
7067

7168
说明:
7269

73-
- 上述复制命令同步的是技能文档、模板和子技能目录,不会单独把仓库根目录下的 `runtime/``scripts/` 自动安装到目标项目
74-
- 如果要把 runtime 资产同步到另一个仓库,执行 `bash scripts/sync-runtime-assets.sh /path/to/project`,默认会在目标仓库生成一份自包含的 `.sopify-runtime/` bundle
75-
- 当前仓库内已经收口的 repo-local runtime 入口有两个:
76-
- `scripts/sopify_runtime.py`:默认原始输入入口,直接交给 router 分流
77-
- `scripts/go_plan_runtime.py`:plan-only helper,强制走 `~go plan`
78-
- `scripts/model_compare_runtime.py``~compare` 的运行时实现,不是默认通用 CLI 入口
79-
- 二次接入时不要只复制 `Codex/Skills/*``Claude/Skills/*`;应同时同步 `.sopify-runtime/` bundle
70+
- installer 会同时安装宿主提示层,并把 `.sopify-runtime/` bundle 同步到目标仓库
71+
- `--workspace` 可省略,默认使用当前目录
72+
- 该命令适合作为最终用户的一键接入入口
8073

8174
### 验证安装
8275

@@ -89,7 +82,7 @@ cp -r Codex/Skills/EN/* ~/.codex/
8982

9083
### 二次接入 runtime bundle
9184

92-
如果要在另一个仓库里直接复用这套 runtime
85+
如果需要从维护者视角手工控制 bundle 同步过程,可以单独执行
9386

9487
```bash
9588
# 1. 从当前仓库同步 runtime bundle
@@ -106,8 +99,11 @@ bash /path/to/project/.sopify-runtime/scripts/check-runtime-smoke.sh
10699
说明:
107100

108101
- `.sopify-runtime/` 保持 `runtime/` + `scripts/` + `tests/` 的自包含布局
102+
- `.sopify-runtime/manifest.json` 是 bundle 的机器契约;宿主接入应优先读取 manifest,再回退到默认脚本路径
109103
- vendored 入口默认是 `.sopify-runtime/scripts/sopify_runtime.py`
110104
- plan-only helper 对应 `.sopify-runtime/scripts/go_plan_runtime.py`
105+
- builtin skill 发现由 `runtime/builtin_catalog.py` 负责,不依赖 bundle 内再携带 `Codex/Skills``Claude/Skills` 文档目录
106+
- 非闭环路由现在会写入 `.sopify-skills/state/current_handoff.json``Next:` 文案优先基于 handoff contract 渲染
111107

112108
### 首次使用
113109

@@ -172,6 +168,8 @@ bash scripts/check-runtime-smoke.sh
172168
- `scripts/sopify_runtime.py`:默认原始输入入口
173169
- `scripts/go_plan_runtime.py`:plan-only helper
174170
- 已提供 `scripts/sync-runtime-assets.sh`,用于把 runtime bundle 同步到目标仓库的 `.sopify-runtime/`
171+
- bundle 同步后会生成 `.sopify-runtime/manifest.json`,用于描述入口、支持路由、builtin catalog 与 handoff 文件位置
172+
- runtime 现已真正写入 `.sopify-skills/state/current_handoff.json`,供宿主读取结构化下一步动作
175173
- `.sopify-runtime/` bundle 内已包含便携 `tests/test_runtime.py``scripts/check-runtime-smoke.sh`
176174
- 当前 `P1-A` 已落地:首次运行会 bootstrap 最小 KB 骨架,但还不包含选择性历史回收或 history 归档
177175
- 当前 KB 快照只读取根配置、manifest 与顶层目录,不做源码级扫描

0 commit comments

Comments
 (0)