feat: land owner's v2 release line — 2.0.0, ADR-004, v2 contract tests

Absorbed from the owner's working tree (his versions take precedence):
- VERSION 2.0.0, CHANGELOG, plugin manifests, marketplace metadata
- ADR-004 (project knowledge, not runtime distribution; supersedes 001)
  plus ADR-001/002/003 updates
- Three new v2 contract test suites (architecture / documentation /
  experiment-archive integrity) and rewritten skill-contracts,
  cli-distribution (exact retired-set + E2E fixture), selfref suites
- Rewritten root docs (README/WORKFLOW/SKILL_CATALOG zh+en, CLAUDE/AGENTS)
  with v1.0.4 retirement mapping and precise skills-CLI upgrade procedure
- eval-cs-skill v2 adaptation (6 files), build-cs-skill refinements

Full suite: 96 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
dafang
2026-07-30 13:38:57 +08:00
parent c3db99b47b
commit aedd3213d9
37 changed files with 1031 additions and 529 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
"plugins": [
{
"name": "codestable",
"version": "1.0.4",
"version": "2.0.0",
"description": "CodeStable AI coding workflow skills.",
"source": {
"source": "local",
+1 -1
View File
@@ -7,7 +7,7 @@
"plugins": [
{
"name": "codestable",
"version": "1.0.4",
"version": "2.0.0",
"description": "CodeStable AI coding workflow skills.",
"source": "./plugins/codestable"
}
+11 -17
View File
@@ -1,15 +1,6 @@
---
name: build-cs-skill
description: "CodeStable skill authoring and evolution protocol. Use when creating, refactoring, simplifying, or reviewing cs-* skills under plugins/codestable/skills or .claude/skills. Produces a thin harness, an explicit context plan, evidence gates, and an optional agent collaboration contract. Do not use for product implementation; use eval-cs-skill only for measured experiment loops."
contracts:
- grep: "selectSkillShape"
- grep: "placeRule"
- grep: "thin harness"
- grep: "thick context"
- grep: "buildContextPlan"
- grep: "Additive-only evolution is a failure mode"
- grep: "eval-cs-skill"
- not-grep: "read `.codestable/attention.md` just to author"
---
# build-cs-skill
@@ -94,16 +85,17 @@ conditions. Authoring itself does not require project preflight. A generated ski
when its behavior depends on project setup, repository facts, artifact writes, or recovery.
If there is no independent installable responsibility, or an existing skill/reference/tool owns it,
select `NoActiveSkill`. Use `ReferenceSkill` only when knowledge has an independent distribution contract.
select `NoActiveSkill`. A retired v1 CodeStable name also selects `NoActiveSkill`; migration prose is
not an instruction to recreate a compatibility shim. Use `ReferenceSkill` only when knowledge has an
independent distribution contract.
### 2. Choose the minimum shape
Classify before writing; do not default to a full protocol refactor. Use `ThinOperator` for one
action/artifact, `ContextualWorkflow` for staged or recoverable work, and `ToolBackedWorkflow` when a
deterministic router/gate owns fragile choices. `ShimSkill` contains only a canonical target and
preset; `ReferenceSkill` contains knowledge without an invented workflow. As of skills v2 the shipped
family has no `ToolBackedWorkflow` or `ShimSkill` instance — treat these shapes as available, not as
precedent to imitate.
preset, and is allowed only when a newly accepted distribution contract explicitly ships that alias.
`ReferenceSkill` contains knowledge without an invented workflow.
Use high freedom for safe implementation choices, medium for preferred patterns, and low only for
fragile operations or invariant-sensitive ordering.
@@ -173,10 +165,12 @@ For `ToolBackedWorkflow`, state the tool contract, invariant boundaries, outcome
behavior without copying its branch table. Align persisted schema, normalization, terminal
precedence, and outcomes across harness, runtime, references, and tests.
Each skill remains independently installable: do not require sibling skill files; shared discipline is
inlined per skill (a few duplicated lines beat a reference mechanism). v2 skills invoke no runtime
tools or gates; legacy `.codestable/` assets (reference/, tools/, gates/) stay read-only and must not
become new entry points. Branch/worktree policy and agent backends remain host-owned.
Each skill remains independently installable: do not require sibling skill files or a centralized
onboard runtime. Skill-specific context and deterministic helpers belong to the owning skill's
`references/` and `scripts/`. Project context belongs in `.codestable/attention.md`,
`.codestable/lessons/`, `.codestable/work/`, or the project's existing docs and ADRs. A v2 skill may
search useful v1 artifacts, but must not execute their tools or treat them as current distribution
surfaces. Shims stay thin. Branch/worktree policy and agent backends remain host-owned.
### 8. Compress, validate, and finish
@@ -1,6 +1,6 @@
# CodeStable Skill Fixture Patterns
`cs-*` skill 设计行为回归时读取本规范。fixture 验证决策;静态 contract 验证 shape、
`cs-*` skill 设计行为回归时读取本规范。fixture 验证决策;静态检查验证 shape、
placement、reference link 与 invariant。两者不能互相冒充。
## 目录
@@ -22,7 +22,7 @@ fixture 数量由 fragility 和分支数决定,不要求每个 skill 固定 5-
| `ThinOperator` | 1 个 happy path存在危险边界时再加 failure/forbidden case |
| `ContextualWorkflow` | 每个高风险 branch 一个 decision caseresume/terminal 各有正反例 |
| `ToolBackedWorkflow` | 真实 runtime conformanceunknown/schema mismatch fail closed |
| `ShimSkill` | canonical target 与 legacy preset passthrough 各一项断言 |
| `ShimSkill` | 仅在新发布契约明确交付 alias 时验证 canonical targetv2 退役入口选择 `NoActiveSkill` |
| `ReferenceSkill` | 不造 routing fixture验证归属、链接、load condition 和无 active workflow |
一个 fixture 只验证一个 decision。不要把 routing、输出格式、artifact 内容和 context action
@@ -33,17 +33,18 @@ fixture 数量由 fragility 和分支数决定,不要求每个 skill 固定 5-
使用小型 YAML/JSON并显式断言最近的 unsafe sibling outcome
```yaml
name: no-design-routes-to-design
name: public-contract-change-requires-owner-confirmation
skill: cs-feat
step: restoreFeatureStage
state:
feature_dir_exists: true
has_design: false
qa_status: missing
step: planChange
input:
request: add a required field to the public API
facts:
changes_public_interface: true
explicit_owner_approval: false
expect:
result_type: RoutedTo
stage: Design
must_not_route_to: Implementation
result_type: HumanCheckpoint
reason: ConfirmDesign
forbidden_actions: [edit_code]
```
### Owner Checkpoint
@@ -51,32 +52,33 @@ expect:
checkpoint case 必须证明这是 owner decision而不是 missing input 或普通等待:
```yaml
name: review-passed-requires-owner-confirmation
step: selectNextAction
state:
design_status: draft
design_review: passed
name: prior-discussion-is-not-owner-approval
skill: cs-feat
step: planChange
input:
request: change the persisted schema
facts:
prior_discussion_exists: true
explicit_owner_approval: false
expect:
result_type: HumanCheckpoint
reason: ConfirmDesign
must_not_route_to: GoalPackage
forbidden_actions: [edit_code]
```
同时增加 typed resume case防止自由文本或错误 checkpoint 被接受:
```yaml
name: confirm-design-resumes-through-main-entry
step: entrypoint
name: explicit-confirmation-allows-feature-work
skill: cs-feat
step: planChange
input:
resume:
request: change the persisted schema
owner_reply:
kind: ConfirmDesign
decision: Approved
state:
design_status: draft
design_review: passed
expect:
result_type: RoutedTo
stage: GoalPackage
result_type: Execute
must_not_result_type: NeedsHuman
```
@@ -85,30 +87,31 @@ expect:
### Forbidden Action
```yaml
name: docs-skill-must-not-change-code
step: planAction
name: code-review-must-not-change-code
skill: cs-code-review
step: review
input:
request: update public docs for the auth API
mode: diff-review
expect:
result_type: RoutedTo
stage: Docs
result_type: ReviewReport
forbidden_actions:
- edit_source_code
- git_push
- apply_fix
```
### Failure Path
```yaml
name: ambiguous-feature-target-needs-human
step: restoreFeatureTarget
state:
matching_features:
- .codestable/features/2026-07-01-auth
- .codestable/features/2026-07-03-auth-refresh
name: issue-without-red-check-must-not-edit
skill: cs-issue
step: establishEvidence
facts:
stable_failing_check: false
manual_reproduction_confirmed: false
expect:
result_type: NeedsHuman
reason_contains: which feature
reason_contains: reproducible validation
forbidden_actions: [edit_code]
```
failure fixture 应断言当前 artifact 与安全下一步,而不只是结果名称。
@@ -117,34 +120,36 @@ failure fixture 应断言当前 artifact 与安全下一步,而不只是结果
### Awaiting Run Identity
外部工作已启动时,正例必须携带真实 run identity
本节只适用于确实拥有外部异步工作的未来 `ContextualWorkflow` / `ToolBackedWorkflow`;当前
v2 活动 skill 不因历史 goal driver 示例而获得这项机制。外部工作已启动时,正例必须携带
真实 run identity
```yaml
name: active-driver-is-awaiting
step: restoreGoalRun
name: active-external-job-is-awaiting
step: restoreExternalJob
state:
goal_run_state: active
run_id: run-20260730-01
job_state: active
run_id: job-20260730-01
expect:
result_type: Awaiting
run_id: run-20260730-01
run_id: job-20260730-01
must_not_result_type: HumanCheckpoint
```
缺失 id 的 companion case 必须 fail closed
```yaml
name: active-driver-without-id-is-invalid
step: restoreGoalRun
name: active-external-job-without-id-is-invalid
step: restoreExternalJob
state:
goal_run_state: active
job_state: active
run_id: null
expect:
result_type_any: [Blocked, NeedsHuman]
must_not_result_type: Awaiting
```
对含混 legacy `blocked` state 也增加拒绝恢复的 case。terminal case 带 stale driver metadata
对含混 `blocked` state 也增加拒绝恢复的 case。terminal case 带 stale run metadata
验证 terminal precedence。
### 完整生命周期
@@ -163,8 +168,8 @@ expect:
### Real Runtime Conformance
`ToolBackedWorkflow` 的 fixture 必须调用真实 router/hook/parser。输入使用当前 persisted schema
断言真实 stdout/JSON/exit status再映射到 contract outcome。
`ToolBackedWorkflow` 的 fixture 必须调用 owning skill `scripts/` 中的真实 router/hook/parser。
输入使用当前 persisted schema断言真实 stdout/JSON/exit status再映射到 contract outcome。
禁止在测试中重写一个同构 branch table那只能证明测试模型自洽不能证明生产 runtime
与 harness aligned。至少覆盖
@@ -177,12 +182,13 @@ expect:
### Canonical Handoff
```yaml
name: upstream-routes-to-canonical-feature-entry
skill: cs-brainstorm
name: epic-item-routes-to-canonical-feature-entry
skill: cs-epic
step: handoff
input:
intent: add public auth API
artifact: .codestable/brainstorms/auth.md
artifact: .codestable/work/auth-epic.md
evidence: [tests/auth_contract_test.py]
expect:
route_to: cs-feat
must_not_set:
@@ -190,43 +196,45 @@ expect:
- requested_mode
```
只有 shim fixture 可以断言 legacy preset
v2 的退役入口必须保持缺席,不能因为需要迁移说明就生成 shim
```yaml
name: legacy-qa-entry-routes-to-main-skill
skill: cs-feat-qa
step: entrypoint
input:
args: ""
name: retired-v1-entry-remains-absent
candidate_skill: cs-feat-qa
canonical_owner: cs-feat
expect:
route_to: cs-feat
requested_stage: qa
must_not_define_independent_rules: true
skill_shape: NoActiveSkill
shipped: false
compatibility_shim: false
```
只有新的、已接受的发布契约明确要求 alias 时才选择 `ShimSkill`,并验证它只转交 canonical
target不复制规则。v1 的 24 个退役名称不是该例外。
handoff 还应验证 target、artifact identity、relevant evidence、pending owner decision 和
recovery pointer 没有丢失。
### Fastforward Eligibility
### Responsibility Boundary
```yaml
name: fastforward-rejects-public-contract-change
step: chooseMode
name: refactor-rejects-observable-behavior-change
skill: cs-refactor
step: checkEquivalence
input:
args: --mode fastforward add new public auth API
state:
crosses_public_contract: true
request: refactor auth and add a new public field
facts:
changes_observable_behavior: true
expect:
result_type: RoutedTo
stage: Design
must_not_route_to: FastForward
result_type: NeedsHuman
suggested_entry: cs-feat
forbidden_actions: [continue_refactor]
```
## ContextPlan
常规单轮 routing fixture 看不到文件是否被加载或重复读取。因此:
- 静态 contract 验证每个 source 的 `loadWhen``sufficientWhen``reuseKey`
- 静态检查验证每个 source 的 `loadWhen``sufficientWhen``reuseKey`
`stopCondition`
- cross-file test 验证顶层只引用、不复制 reference
- 有 tool-enabled/action trace 时,才断言 entry 未加载无关 StageContext、同一 reuse key
@@ -257,5 +265,6 @@ oracle 可以接受语义等价措辞,例如 `result_type_any` / `target_any`
- `Awaiting` 有 run-id 正反例;
- tool-backed case 调用真实 runtime
- cross-skill handoff 进入 canonical main entry
- v2 退役入口保持 `NoActiveSkill`,没有 compatibility shim
- ContextPlan 只声明实际可观察的证据;
- 新事故/owner correction 在最近的确定性层获得回归。
@@ -43,7 +43,7 @@ workflow/Haskell state machine不构成缺陷。
| `ThinOperator` | 单一责任;没有伪造阶段;少量 guard 足以安全完成 |
| `ContextualWorkflow` | 真实阶段/迭代/恢复存在;仓库事实决定状态;按阶段加载 context |
| `ToolBackedWorkflow` | 确定性工具实际存在;接口、失败和 alignment 可验证 |
| `ShimSkill` | 仅转发 canonical entry 与 legacy preset不复制主规则 |
| `ShimSkill` | 新发布契约明确交付 alias仅转发 canonical entry 与 preset不复制主规则 |
| `ReferenceSkill` | 只有独立分发的知识;有 load condition 且没有 active workflow |
以下情况拒绝 shape
@@ -53,6 +53,7 @@ workflow/Haskell state machine不构成缺陷。
- 声称 tool-backed 但没有真实可调用工具或 fail-closed 行为;
- reference 内容伪装成 active methodology skill
- 已有 canonical owner 时仍创建重复 active skill。
- 为 v2 已退役名称恢复 compatibility shim。
## Thin Harness Gate
@@ -107,8 +108,9 @@ Every new or changed rule must re-enter `placeRule`.
- startup 不扫描或读取全部 references、ADR、compound 或历史 artifacts
- thick context 的“厚”来自当前阶段的相关性、证据密度和完整性,不来自体积;
- 顶层只列 reference 的用途与 load condition不复述 reference
- skill 专属 reference 可随独立安装单元分发;跨 skill 共享资料通过项目
`.codestable/reference/`,不读取 sibling skill 文件。
- skill 专属 reference 可随独立安装单元分发;跨 skill 的项目事实通过
`.codestable/attention.md``.codestable/lessons/``.codestable/work/` 或项目既有文档
与 ADR 获取,不读取 sibling skill 文件,也不依赖集中式 onboard runtime。
首次读取项目事实的价值必须保留同一会话继续时复用已读摘要。batch fan-out 必须传
结构化 flag/reuse key让 child 明确跳过 parent 已加载的全局输入,不能只写“如已读则复用”。
@@ -177,14 +179,14 @@ recovery pointer接收方自行恢复状态。
没有独立 scope 或独立验收方式时,保持单 agent。
## Contract Gate
## Static Evidence Gate
machine contract 保护行为骨架,不代替场景测试。优先锚定
静态检查保护行为骨架,不代替场景测试。优先验证
- 关键 decision/runtime function 或 tool invocation
- required artifact、checkpoint、forbidden action 与 run-id invariant
- reference load condition、reuse guard 和 canonical handoff
- deprecated key/入口作为 `not-grep`
- deprecated key/入口使用显式负向断言,不写入 skill frontmatter
不要锚定裸 type name、通用词、只出现在示例中的字符串或正常编辑极易变化的整句。
@@ -197,7 +199,7 @@ machine contract 保护行为骨架,不代替场景测试。优先锚定:
- 跳过什么会不安全?
- 最近的 unsafe sibling outcome 是什么?
一个 fixture 只断言一个 decision。无法直接测量 context action 时,先用静态 contract
一个 fixture 只断言一个 decision。无法直接测量 context action 时,先用静态检查
保护 load/reuse 规则;不要把单轮 routing 结果冒充“没有重复读取”的证据。
## Regression Ladder
@@ -278,8 +280,8 @@ CI or a quiet disposable host 应承担最终 full-suite/build matrix。
compare it with the reviewed/classified set by equality。包含所有非 `SKILL.md` Markdown包括根层
`reference.md`,不能只枚举 `references/` 下的任意层级 `*.md`
active entries、compatibility shims、Haskell contracts、structured references 必须进入显式且
互斥的集合。sample list 或单向 subset assertion 无法证明新文件已被审计
shipped active set 必须与 package manifest/distribution 精确相等,退役名称有负向断言;
accepted shims、Haskell contracts、structured references 进入显式互斥集合。subset 不足以证明完整
跨 skill 路由还要断言 canonical main entry 规则;普通 operator/router/audit/workflow
handoff 不得预选接收方内部 stage/lane。
@@ -42,11 +42,11 @@ data RulePlacement
| `ThinOperator` | 单一责任、少量分支、一次性产物或动作 | 责任、最小上下文、关键 gate、完成证据 |
| `ContextualWorkflow` | 有阶段、恢复、迭代或按阶段方法 | 责任、`ContextPlan`、最小决策面、恢复与完成契约 |
| `ToolBackedWorkflow` | 路由、状态检查或安全判断由确定性工具执行 | 工具调用条件、输入输出、fail-closed 规则、完成证据 |
| `ShimSkill` | 旧入口转发到 canonical skill | 目标入口和原样透传的 preset/参数 |
| `ShimSkill` | 新发布契约明确要求一个 alias 转发到 canonical skill | 目标入口和原样透传的 preset/参数 |
| `ReferenceSkill` | 有独立分发价值但没有 active workflow | 参考内容、归属与加载条件 |
`NoActiveSkill` 是合法的构建结论。`ReferenceSkill` 不注册 active `cs-*` 触发面。`ShimSkill`
仍是独立安装单元,只按名称调用 canonical skill不能读取 sibling skill 文件或复制其规则。
`NoActiveSkill` 是合法的构建结论。v2 已退役的 24 个旧名称必须保持 `NoActiveSkill`,迁移说明不构成恢复 shim 的发布契约。
`ReferenceSkill` 不注册 active `cs-*` 触发面。新的 `ShimSkill` 仍是独立安装单元,只按名称调用 canonical skill不能读取 sibling skill 文件或复制其规则。
## Shape 选择
@@ -59,7 +59,7 @@ data RulePlacement
`ContextualWorkflow`
- 已有可靠 router、hook、parser 或 gate 能做确定性判断时,选择
`ToolBackedWorkflow`
- 入口仅为兼容旧名称或旧 preset选择 `ShimSkill`
- 只有新发布契约明确交付 alias 时选择 `ShimSkill`;已退役 v1 名称选择 `NoActiveSkill`
- 知识有独立分发价值、但没有 active workflow 时,选择 `ReferenceSkill`
以下任一条件会提高 fragility并要求更低的自由度
@@ -83,8 +83,7 @@ every-invocation 和 remove 分类。reference 不复制第二份实现。
- `Harness`:不可机械化的安全规则,或每次调用在首次关键决策前都必须知道且会改变责任、
权限、上下文选择、gate、恢复或完成判断的规则。
- `StageContext`:仅在某阶段、变体或方法被选中后才需要的协议、模板、示例和检查表。
- `ProjectContext`ADR、术语、项目约束、既有模式与历史经验放在项目
`.codestable/` artifacts 中。
- `ProjectContext`ADR、术语、项目约束、既有模式与历史经验放在 `.codestable/attention.md``.codestable/lessons/``.codestable/work/` 或项目既有文档中。
- `DeterministicGate`能机械判定的安全、schema、状态、格式或同步约束顶层只保留
调用时机、输入输出和失败语义。
- `Remove`:模型可可靠推断、已被其他规则覆盖、只有历史解释或没有行为证据的内容。
@@ -144,8 +143,9 @@ buildContextPlan :: SkillShape -> [PlacedRule] -> ContextPlan
- batch fan-out 必须传结构化复用信号;不能依赖“可能已经读过”的自然语言猜测。
- `thick context` 指高相关、可追溯、足以完成当前阶段,不是把所有 reference 和仓库文档
全量倒入上下文。
- skill 专属知识放本 skill 的 `references/`;跨 skill 共享知识放项目
`.codestable/reference/`,其模板源由 `cs-onboard` 管理。
- skill 专属知识放本 skill 的 `references/`;跨 skill 的项目事实放
`.codestable/attention.md``.codestable/lessons/``.codestable/work/` 或项目既有文档与
ADR。不要假设另一个 skill 或集中式 onboard runtime 可用。
### Decision / Gate Contract
@@ -250,8 +250,8 @@ persisted artifact fields -> normalized Spec state -> runtime outcome
- fixture 字段与真实 runtime schema。
存在 runtime 时必须用真实 router 做 conformance手写一份测试 router 不是 alignment
evidence。新版 CodeStable tool/gate 从目标安装的 `cs-onboard/tools/` 调用,不新增
`.codestable/tools/` 默认入口
evidence。确定性 helper 由 owning skill 的 `scripts/` 提供v2 不新增或调用 repo-local
runtime。v1 项目里的旧 tool/gate 可以保留,但不构成新 skill 的依赖
## Collaboration Contract
+9 -6
View File
@@ -8,7 +8,8 @@ argument-hint: "[--stage author|eval|optimize|release] [--experiment <dir>] <ski
## 启动必读
动作前先跑 CodeStable preflight `.codestable/attention.md`(缺失先 `cs-onboard`);不要用 `AGENTS.md`/`CLAUDE.md` 等外部入口代替它;细则见 `.codestable/reference/execution-conventions.md`
动作前读取仓库 agent 规则;存在 `.codestable/attention.md` 时再读项目事实。评测 context 以
被测 skill 的实际声明为准,不要求集中式 onboard runtime 或共享 reference。
`eval-cs-skill` 是「造 skill 的 skill」把 CodeStable 各 skill 的 **编写 → 评测 → 优化 → 再评测** 做成一条可复现、跨 model/agent 的迭代闭环。它编排的对象是 **skill 自身的生命周期**,不是业务代码 feature。它**自指**——同一套 harness 也能评测并优化 `eval-cs-skill` 自己。
@@ -38,7 +39,7 @@ argument-hint: "[--stage author|eval|optimize|release] [--experiment <dir>] <ski
## 闭环是什么(速读)
```text
生产失败(cs-feedback)
regression fixtures ────
planted-defect fixtures ┼─▶ runner.py多 harness/model 执行被测 skill隔离宿主
golden 任务 ────────────┘ │
@@ -139,12 +140,14 @@ python3 {skill_dir}/scripts/bump_version.py --to X.Y.Z
- 当前 stage 产物已落盘,状态可由 `experiments/` 事实恢复。
- eval 产出带 tag 的 measured 分数与 evidence_pointer。
- optimize 产出 iteration-N 与收敛判定release 产出合规回写 + 版本同步 + 回归结论。
- 需要外部文档时提示 `cs-docs`;需要沉淀坑/决策时提示 `cs-keep`
- 需要外部文档时直接更新对应项目文档;需要沉淀坑/决策时提示 `cs-keep`
---
## 相关入口
- `cs-feedback`:生产失败采集;其失败案例可转成 `fixtures/regression/`
- `cs-code-review` / `cs-issue` / `cs-audit`:常见被测 skill
- `cs-onboard`runtime 与共享 reference 的属主。
- `cs-code-review` / `cs-issue` / `cs-feat`:常见活动被测 skill
- `cs-onboard`:最小项目记忆骨架;不提供评测 runtime
`scripts/promote_feedback_fixture.py` 仅用于显式导入冻结的 v1 `cs-feedback` candidate不是
v2 production feedback 入口;新反馈如何进入 regression 不在当前协议中定义。
@@ -13,7 +13,7 @@ argument-hint: "[--stage ...] <arg>" # 仅主入口
```
- `description` 是唯一决定「是否自动触发」的字段:写「用来做 X触发词 A/B/C」不要写「关于 X 的」。略偏推销:任务有 Y 特征就触发。
- 主入口用 `--stage/--mode` 显式语义兼容入口是薄壳≤40 行、无独立规则)
- 只有用户确实需要稳定参数契约时才加 `--stage/--mode`;不要为已退役入口创建兼容薄壳
## 渐进披露progressive disclosure
@@ -33,8 +33,9 @@ plugins/codestable/skills/cs-xxx/
```
-`references/`(复数),嵌套用 `support/`,不得 `reference/` 单数或二层 `references/`
- 不复制 `.codestable/tools/`;跨 skill 共享参考走项目层 `.codestable/reference/`(由 `cs-onboard` 复制)
- skill 间不耦合A skill 不读 B skill 包内文件。
- helper 放 owning skill 的 `scripts/`不复制到项目v2 不依赖 repo-local runtime
- skill 间不耦合A skill 不读 B skill 包内文件。项目事实走 `.codestable/attention.md`
`.codestable/lessons/``.codestable/work/` 或项目既有文档与 ADR。
## model/harness 无关(关键)
@@ -9,7 +9,7 @@
```bash
# 入队若干实验
python3 {skill_dir}/scripts/enqueue_experiment.py --experiment experiments/cs-code-review-001 --stage optimize
python3 {skill_dir}/scripts/enqueue_experiment.py --experiment experiments/cs-audit-001 --stage eval
python3 {skill_dir}/scripts/enqueue_experiment.py --experiment experiments/cs-code-review-001 --stage eval
# 宿主 cron 反复调用 run-next每次跑一个 queued 项,幂等)
python3 {skill_dir}/scripts/enqueue_experiment.py --run-next
```
@@ -6,9 +6,9 @@
要测的是「skill 在其**设计环境**下的真实能力」不是「skill 在残缺环境下的反应」。一轮真实模型 campaign 里,每个看似的「模型/skill gap」核查后都是**评测缺陷**
1. **复现 onboard 运行环境**cs skills 为已 onboard 的 `.codestable/` 仓库设计;裸输入下弱模型会(正确地)拒绝执行 → 假 gap。用 `inject_context: true` 补齐 attention/来源 spec/gitcs-code-review haiku bare 0.31 → 补上下文 0.92)。
1. **复现被测 skill 的实际 context contract**fixture 提供任务/diff并按声明补可选 attention、相关 lessons、项目文档或 ADR不注入集中式 runtime。缺失必要输入会让模型正确拒绝形成假 gap历史 cs-code-review campaignhaiku bare 0.31 → 补上下文 0.92)。
2. **散文 answer 用语义 oracle**token 重叠对「`>=` 改成 `>`」「删掉早返回守卫」这类符号/散文 answer 会误判漏检cs-refactor 两模型满分被打成 0.62/0.75)。用 `recall_judge`judge 语义判定)+ `planted_defect`(机械兜底)。
3. **fixture 必须内嵌 subject matter**:转换/文档型 skill 需要被操作的对象。cs-docs「写配置文档」却不给配置模型会正确地)要材料而非捏造sonnet 0.75。review 需要 diff、docs 需要 code/config/APIdesign/plan 可只从需求推导。
3. **fixture 必须内嵌 subject matter**:转换/文档型任务需要被操作的对象。v1 `cs-docs` 历史 campaign 中,给「写配置文档」却不给配置模型会正确要材料而非捏造sonnet 0.75。review 需要 diff,文档任务需要 code/config/APIdesign/plan 可只从需求推导。
核查纪律:**分模型看**(合计数掩盖 haiku↔sonnet 差异)、**手工读原始输出**token 数字会骗人)、**k=1 有 variance**(同一 fixture 会抖,发布级结论 k≥5
@@ -38,7 +38,7 @@
```
- `variants``baseline`=当前仓库被测 skill 的 SKILL.md其余=optimize 产出的 `experiments/{name}/variants/<v>.md`
- `inject_context`**效度关键,默认 true**cs skills 为已 onboard 的 `.codestable/` 仓库设计(启动检查要 attention.md / 来源 spec / git diff。评测须在 prompt 里补齐这套 onboard 上下文否则测到的是「skill 在错误环境下拒绝执行」的假象而非真实能力实测cs-code-review haiku bare=0.31 → 补上下文=0.92。设 `false` 只用于专门测「bare-input/ad-hoc 健壮性」。
- `inject_context`**效度关键,默认 true**按被测 skill 的真实 context contract 补任务、diff、可选 attention、相关 lessons 与项目文档,不假设统一 onboard runtime。设 `false` 只用于专门测「bare-input/ad-hoc 健壮性」。
- `model_list` ≥2跨模型一致性BAIME 硬约束)。`judge_model` 须独立于被测 model。
## 2. 作 fixtures
@@ -48,19 +48,29 @@ python3 {skill_dir}/scripts/bump_version.py --to X.Y.Z --note "……"
## 5. 校验
```bash
PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q tests/test_skills_cli_distribution.py tests/test_plugin_package.py
PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q tests -rs
PYTHONDONTWRITEBYTECODE=1 python3 tools/check-plugin-package.py --root . --json
PYTHONDONTWRITEBYTECODE=1 python3 plugins/codestable/skills/cs-onboard/tools/codestable-runtime-sync.py --root . --source-skill-dir plugins/codestable/skills/cs-onboard --check --json
PYTHONDONTWRITEBYTECODE=1 python3 plugins/codestable/skills/cs-onboard/tools/codestable-doctor.py --root . --json
git diff --check
```
模板或 gate 变更时,先按 `cs-onboard` managed-assets 规则完成 runtime sync再跑上面的 `--check`;任一 JSON gate 的 `ok` 非 true 都阻断发布。
涉及 owning skill 的 `scripts/` 时,增加其直接测试或 runtime conformancepackage JSON 的
`ok` 非 true 阻断发布。v1 项目资产不参与 v2 发布同步。
活动 skill 集合或退役清单变化时,还必须用真实 CLI 跑隔离升级;该项在普通 pytest 中会
skip不能把 skip 当成通过:
```bash
CODESTABLE_RUN_SKILLS_CLI_E2E=1 \
CODESTABLE_SKILLS_CLI="npx --yes skills@latest" \
python3 -m pytest tests/test_skills_cli_distribution.py -q -rs
```
## 退出条件
- [ ] 草稿经 adapt 落 `plugins/``check-plugin-package.py` 通过。
- [ ] 回归判定非 `regressed`
- [ ] 版本 5 处一致 + CHANGELOG 有段。
- [ ] distribution 测试确认活动 skill 集合精确相等;集合变化时真实 CLI E2E 未被 skip。
- [ ] 被实验证实的 skill 声明按 `[measured: evidence_pointer]` 标注。
- [ ] 全量 pytest、package、runtime sync check、doctor`git diff --check` 全部通过。
- [ ] 全量 pytest、package check`git diff --check` 全部通过。
@@ -1,5 +1,9 @@
#!/usr/bin/env python3
"""Promote a cs-feedback candidate into a repo-local experiment fixture."""
"""Legacy-only importer for frozen v1 cs-feedback experiment candidates.
This script is not a CodeStable v2 production-feedback entry. It preserves the old candidate schema
only so maintainers can explicitly import historical evidence into repo-local experiments.
"""
from __future__ import annotations
@@ -20,6 +24,7 @@ from config import ExperimentConfig # noqa: E402
from fixtures import validate_fixture_dict # noqa: E402
LEGACY_ONLY = True
ROUTING_INCIDENT_KINDS = {
"wrong-route",
"skipped-gate",
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
"""llm_judge scorer两轴打分compliance 照做没 / quality 质量)。
rubric 取自项目共享文档 `.codestable/reference/code-dimensions.md`cs-onboard 复制的跨 skill
共享口径,是 CLAUDE.md 认可的共享路径,非 sibling skill 私文件)
若 v1 项目保留 `.codestable/reference/code-dimensions.md`,可将其作为 repo-local rubric
文件不存在时使用内置 rubric不依赖 shipped skill 或集中式 onboard runtime
judge 模型须独立于被测模型避免同源偏差。judge 分数默认 [soft]
经 sanity 集校准calibration.md后方可在结论中升为 [measured]。
mock/离线:用确定性启发式,不发 LLM 调用。
+10 -9
View File
@@ -4,18 +4,19 @@
- 默认用中文写面向人的回复、报告和文档代码、命令、路径、协议字段、YAML/JSON key 保持原格式。
- 先读仓库事实和已存在的设计/ADR再改规则或流程不要只凭记忆改 skill 契约。
- 增加或更新 skill 时,同步检查相关 skill、SKILL_CATALOG、WORKFLOW、README、测试和 ADR 中的表述。
- 增加或更新 skill 时,同步检查相关 skill、README/reference、测试和 ADR 中的表述。
- 保持规则言简意赅,优先写可执行约束,不写口号。
## CodeStable Skill 边界v2thin harness, thick context
## CodeStable Skill 边界
- 交付 skill 共 8 个;每个 SKILL.md 是薄责任契约,不写流程状态机
- skill 之间不相互耦合;公共纪律内联进各 SKILL.md不建跨 skill 共享 reference 机制
- 项目侧只有 `.codestable/{attention.md, lessons/, work/}`v1 存量只读保留,不迁移不删除
- 不再有 skill 调用的 gate / runtime 工具;不要新增 `python .codestable/tools/...` 类入口
- CodeStable skills 不决定默认 worktree/branch 策略;该策略由宿主owner 决定。
- 不要把 `AGENTS.md`/`CLAUDE.md` 当作 `.codestable/attention.md` 的替代;项目事实沉淀到对应 CodeStable 载体
- skill 是独立安装单元,运行时不能假设能读取 sibling skill 文件
- v2 项目知识放在项目文档、ADR 以及 `.codestable/attention.md``lessons/``work/`;不通过 onboard 分发通用 reference。
- 确定性 helper 放在实际 owning skill 的 `scripts/`,不得隐式调用 sibling skill 或集中式 onboard runtime
- v1 的 `.codestable/reference/``tools/``gates/``hooks/``runtime-manifest.json` 只作 legacy compatibility不要默认删除、覆盖或作为 v2 入口调用
- CodeStable skills 不决定默认 worktree/branch 策略;该策略由宿主owner 或独立 skill 决定。
- 不要把 `AGENTS.md`/`CLAUDE.md` 当作 `.codestable/attention.md` 的替代;项目事实仍应沉淀到对应 CodeStable artifacts
## 验证
- skill 改动完成前至少运行 `python3 -m pytest tests/``git diff --check`
- skill/runtime 改动完成前至少运行相关 pytest`git diff --check`
- plugin 分发或退役清单变更还要运行 `tests/test_skills_cli_distribution.py``tools/check-plugin-package.py`
+15
View File
@@ -1,5 +1,20 @@
# Changelog
## 2.0.0
- Reduced the shipped package from the 32 skills in v1.0.4 to 8 thin-harness skills: `cs`,
`cs-onboard`, `cs-feat`, `cs-issue`, `cs-refactor`, `cs-code-review`, `cs-epic`, and `cs-keep`.
- Retired the other 24 v1 entry names without compatibility shims. A one-time exact-name removal is
required before the `skills` CLI full-package reinstall because that CLI does not prune removed
package members. Removal is name-based, so same-name custom skills require an explicit backup.
- Replaced distributed project runtime assets with minimal project memory: `attention.md`, `lessons/`,
and `work/`. Existing v1 artifacts remain untouched and searchable, but v2 does not execute or
refresh legacy tools, gates, hooks, references, or manifests.
- Reworked `build-cs-skill` around `thin harness, thick context`, direct semantic tests, explicit rule
ownership, context plans, and continuous compression; frontmatter `contracts` are no longer used.
- Separated frozen v1 experiment fixtures from the active v2 contract and added v1.0.4-to-v2
distribution, architecture, documentation, and archive-integrity coverage.
## 1.0.4
- Hardened CodeStable workflow contracts across Epic dependency admission, Goal authorization, checkpoint resume, independent review, runtime safety, and release gates.
+14 -15
View File
@@ -7,25 +7,24 @@
## 语言与文档
- 默认用中文写面向人的回复、报告和文档代码、命令、路径、协议字段、YAML/JSON key 保持原格式。
- 单个 Markdown 文件不得超过 300 行;超过必须拆分(经 owner 明确豁免的设计章程除外)
- 增加或更新 skill 时,同步检查相关 skill、SKILL_CATALOG、WORKFLOW、README、测试和 ADR 中的表述。
- `AGENTS.md`/`CLAUDE.md` 只写 agent 行为规则;不要替代 `.codestable/attention.md`、work 文档ADR 等项目事实载体。
- 单个 Markdown 文件不得超过 300 行;超过必须拆分。
- 增加或更新 skill 时,同步检查相关 skill、README/reference、测试和 ADR 中的表述。
- `AGENTS.md`/`CLAUDE.md` 只写 agent 行为规则;不要替代 `.codestable/attention.md`项目文档、work 文档ADR 等项目事实载体。
## Skill 边界v2thin harness, thick context
## Skill 边界
- 交付 skill 共 8 个,位于 `plugins/codestable/skills/`;每个 SKILL.md 是薄责任契约(约 3060 行正文),不写流程状态机、不写 Haskell spec
- 不同 skill 之间不相互耦合:公共纪律(开工检索、沉淀推荐、授权边界)以两三行内联进各 SKILL.md,不建跨 skill 共享 reference 机制
- 上下文按需检索skill 只写"去哪取"attention、`lessons/` grep、项目文档不把材料复制进 skill 包或项目
- 交付 skill 不带 contracts frontmatter硬门槛锚由 `tests/test_skill_contracts.py` 直接对 SKILL.md 正文断言(改硬门槛措辞须同步更新锚清单)
- 不同 skill 之间不要相互耦合A skill 在非必须情况下不要读取或依赖 B skill 的内部文件
- skill 是独立安装单元,运行时每个 skill 只能稳定看到自己包内文件;不要在 SKILL.md 中写 `B-skill/reference/xxx.md` 这类 sibling 引用
- v2 项目知识放在项目文档、ADR 以及 `.codestable/attention.md``lessons/``work/`;不通过 onboard 分发通用 reference
- skill 专属 context 和确定性 helper 分别放在 owning skill 的 `references/``scripts/`;跨 skill 通用规则应归宿主策略、项目事实或独立安装单元
## CodeStable 项目数据
## v1 兼容边界
- 项目侧只有 `.codestable/{attention.md, lessons/, work/}`;普通任务零产物,跨会话任务一个 work 文档
- v1 存量(`reference/``tools/``gates/``hooks/``compound/``features/` 等)只读保留,不迁移、不删除、不覆盖;旧沉淀由 grep 检索继续生效
- 不再有 skill 调用的 gate / runtime 工具与 runtime-manifest 机制;不要新增此类入口
- CodeStable skills 不拥有默认 worktree/branch 策略;是否创建 worktree、如何命名分支、如何 merge由宿主与 owner 决定。
- v1 的 `.codestable/reference/``tools/``gates/``hooks/``runtime-manifest.json` 原样保留,但 v2 skill 不把它们作为入口或执行其中的 legacy runtime
- 不要新增、同步或刷新 repo-local CodeStable runtime确定性行为由 owning skill 自己的 helper 或项目已有工具负责
- CodeStable skills 不拥有默认 worktree/branch 策略;是否创建 worktree、如何命名分支、如何 merge应由宿主、owner 或未来独立 skill 决定
## 验证
- skill 改动完成前至少运行 `python3 -m pytest tests/``git diff --check`
- skill 行为的量化验证按需使用 `eval-cs-skill`(仅在明确要做测量实验时)
- skill/runtime 改动完成前至少运行相关 pytest`git diff --check`
- plugin 分发或退役清单变更还要运行 `tests/test_skills_cli_distribution.py``tools/check-plugin-package.py`
+59 -36
View File
@@ -75,10 +75,17 @@ Restart Claude Code after updating so the new plugin version is applied.
`skills` CLI:
```bash
npx skills@latest remove \
cs-audit cs-brainstorm cs-doc-api cs-doc-tutorial cs-docs cs-docs-neat \
cs-domain cs-feat-accept cs-feat-design cs-feat-design-review cs-feat-ff \
cs-feat-impl cs-feat-qa cs-feedback cs-goal cs-issue-analyze cs-issue-fix \
cs-issue-report cs-note cs-refactor-ff cs-req cs-roadmap \
cs-roadmap-impl-goal cs-roadmap-review \
-g -y
npx skills@latest add codestable/CodeStable/plugins/codestable --skill '*' -g
```
The bare `skills` CLI `update` currently uses different discovery rules for plugin manifests and generic directories, so it can misclassify existing sibling skills as deleted. Upgrade with the full-package reinstall command above instead. It synchronizes every `cs*` skill from `plugins/codestable`; for a project-scoped install, omit `-g` and run it in that project. Since v2, all rules live in the skill package itself — there are no runtime assets inside projects to refresh, so upgrading the plugin is the whole procedure.
The current `skills` CLI does not automatically remove skills that disappeared from a newer package during `add` or `update`. For a v1.0.4-to-v2.0.0 upgrade, the first command therefore removes the exact 24 retired CodeStable names, then the second installs all 8 v2 skills. Removal is name-based and does not verify the installation source: other names are untouched, but if you maintain a custom or third-party skill under one of these same names, back it up and remove that name from the command first. Future upgrades within the same major need only rerun `add`. For a project-scoped installation, omit `-g` from both commands and run them in that project. Historical v1 project assets remain untouched, and no per-repository runtime refresh is required.
One command to start working:
@@ -124,7 +131,7 @@ CodeStable goes the **other way**:
<tr><th></th><th>Agent-orchestration camp</th><th>CodeStable</th></tr>
<tr><td><b>Core entity</b></td><td>Agent / Role / Team</td><td>Requirement / Architecture / Feature / Issue / Decision</td></tr>
<tr><td><b>Main question</b></td><td>How do agents divide work, hand off, coordinate?</td><td>How do requirements, constraints, decisions get recorded, retrieved, reused?</td></tr>
<tr><td><b>Where state lives</b></td><td>Agent sessions / message buses / queues</td><td>The <code>.codestable/</code> file tree in your project (readable by both humans and AI)</td></tr>
<tr><td><b>Where state lives</b></td><td>Agent sessions / message buses / queues</td><td>Project docs plus <code>.codestable/</code> project memory (readable by humans and AI)</td></tr>
<tr><td><b>Pain it solves</b></td><td>One agent isn't enough; need coordination to scale</td><td>Software complexity overflows context; tacit knowledge gets lost; requirements drift</td></tr>
<tr><td><b>Role of humans</b></td><td>The less the better — full automation is the ideal</td><td>Human-in-the-loop — the programmer owns the whole; AI is an efficient executor</td></tr>
</table>
@@ -145,56 +152,73 @@ I built CodeStable because I believe **the chaos of software engineering isn't r
CodeStable models real coding work as a set of **entities** and **flows**.
### Entities
### Project memory
| Entity | Carrier | What it does |
|------|------|--------|
| **Attention** | `attention.md` | Project facts read every session, 25 entries |
| **Lessons** | `lessons/` | The compounding knowledge base: pitfalls, good practices, investigation notes — one markdown file per lesson, grep-searchable, every entry backed by traceable evidence (`cs-keep`) |
| **Active work** | `work/` | The single state document for cross-session / handoff / epic tasks (goal / context / boundaries / evidence / acceptance / status-and-open-items), compressed and deleted on completion |
Ordinary tasks produce no CodeStable entities at all — the git diff, test output, and delivery summary are the evidence.
| Entity | What it does |
|------|--------|
| **attention** | A small set of project facts needed every session, kept to at most 25 entries |
| **lessons** | One file per pitfall, technique, or research result, retrieved by keyword when relevant |
| **work** | Active cross-session or multi-agent work; ordinary tasks create none, completed work is removed |
| **Project docs / ADRs** | The canonical owner for requirements, domain models, public contracts, and long-lived decisions |
### Flows
| Flow | Entry | Hard gate |
|------|------|--------|
| **Feature delivery** | `cs-feat` | On risk escalation the design is persisted to a work document, passes independent agent review, then user confirmation — never auto-approved; test-first when a test setup exists; completion requires verifiable evidence |
| **Issue fixing** | `cs-issue` | No root-cause guessing without a verification that clearly turns red; the red verification must turn green before claiming the fix |
| **Refactoring** | `cs-refactor` | Equivalence verification exists before code changes; stop and report the moment behavior would change |
| **Epic delivery** | `cs-epic` | Decomposition is user-confirmed before execution; one epic document keeps the full picture; final acceptance is never done on the user's behalf |
| **Independent review** | `cs-code-review` | Read-only; independent subagent perspective; designs and changes reviewed by default; blocking findings must be resolved, fix-and-rereview capped at 2 rounds before human arbitration |
| Flow | Recommended main entry | Notes |
|------|------------|------|
| **Feature delivery** | `cs-feat` | Understand, implement, and verify directly; confirm high-risk contracts or real tradeoffs first |
| **Epic delivery** | `cs-epic` | Maintain one work document and drive confirmed feature / issue / refactor items |
| **Issue fixing** | `cs-issue` | Establish a failing check first, then fix the issue and prove it turns green |
| **Refactoring** | `cs-refactor` | Establish equivalence evidence, change structure incrementally, and keep verification green |
| **Review / audit** | `cs-code-review` | Independently review the current diff or audit a requested scope, read-only |
| **Knowledge capture** | `cs-keep` | Store evidence-backed frequent facts or reusable lessons in project memory |
Every flow shares one mainline: understand the relevant facts → act → run proportionate verification → deliver. Risk is re-judged per request — no persistent lanes, no stage state machines.
High-risk changes, or an explicit owner request, invoke `cs-code-review` for independent review. The development task that owns a change also updates its docs and ADRs; separate stage skills are no longer needed.
---
## Skill catalog
v2 ships 8 skills — a thin layer of engineering discipline plus a project-memory loop (thin harness, thick context):
### Current 8 skills
| Skill | Purpose |
|---|---|
| `cs` | System overview and entry recommendation; explains only, never starts a workflow |
| `cs-onboard` | Create the minimal `.codestable/` skeleton; v1 legacy preserved untouched |
| `cs-feat` | New features and changes; on risk escalation the design passes independent review, then user confirmation |
| `cs-issue` | Bug fixing; no root-cause guessing without a red verification |
| `cs-refactor` | Behavior-preserving refactoring; equivalence verification first |
| `cs-code-review` | Independent review: diff / design / repo-audit modes |
| `cs-epic` | Large-requirement decomposition and long-running delivery |
| `cs-keep` | Distill experience; traceable evidence required |
| Group | Skill | Purpose |
|---|---|---|
| Navigation | `cs` | Explain the system and recommend an entry; never start another workflow or write files |
| Onboard | `cs-onboard` | Install CodeStable into a repository |
| Epic | `cs-epic` | Decompose, confirm, and drive multiple deliverable items over time |
| Feature | `cs-feat` | Implement new capability and scale design confirmation or review with risk |
| Issue | `cs-issue` | Fix bugs or broken behavior with red-to-green evidence |
| Refactor | `cs-refactor` | Change structure or performance under behavioral-equivalence evidence |
| Review | `cs-code-review` | Perform independent read-only diff review or an on-demand audit |
| Memory | `cs-keep` | Capture evidence-backed frequent facts and reusable lessons |
The v1 stage skills and long-tail entries (design/impl/qa stage skills, `cs-goal`, `cs-brainstorm`, the `cs-docs` family, `cs-domain`, `cs-req`, `cs-audit`, `cs-note`, `cs-feedback`, the `cs-roadmap` family) have been folded into the table above. See [SKILL_CATALOG.en.md](./SKILL_CATALOG.en.md) for the full catalog. In daily use, call `/cs` when you are unsure.
The other 24 names from v1.0.4 are retired and not shipped with v2; no compatibility shims are installed. See [SKILL_CATALOG.en.md](./SKILL_CATALOG.en.md) for mappings and upgrade boundaries. Call `/cs` when you are unsure which entry fits.
---
## Workflow and runtime
## Workflow and project memory
Every entry shares one execution mainline: **understand the relevant facts → act → run proportionate verification → deliver**. Risk is re-judged per request from current facts — no persistent lanes, no stage state machines. Before acting, grep the project's accumulated knowledge (including v1 legacy) by task keywords and report the sources of hits. Design and acceptance confirmations are never auto-approved by the model.
CodeStable v2 keeps responsibility, hard gates, and completion evidence in a thin harness, then loads project facts only when relevant:
`/cs-onboard` creates a minimal `.codestable/`: `attention.md` (read every session, ≤25 entries), `lessons/` (one file per lesson, grep-searchable), and `work/` (active cross-session tasks, compressed and deleted on completion). Ordinary tasks produce zero artifacts; there are no gates, no runtime tools, and no references copied into projects. v1 artifacts in existing projects are kept read-only and stay grep-discoverable.
```text
cs # navigation only
cs-onboard # project setup / v1 upgrade
cs-epic # large initiative -> feature / issue / refactor items
cs-feat ---\
cs-issue ----> cs-code-review when risk or the owner requires it
cs-refactor ---/
cs-keep # project memory
```
See [WORKFLOW.en.md](./WORKFLOW.en.md) for the full contract.
After `/cs-onboard`, a new project has only this CodeStable-owned memory:
```text
.codestable/
├── attention.md
├── lessons/
└── work/
```
Skill-specific context and helpers belong to the owning skill. Requirements, domain models, and ADRs stay in the project's own documentation structure. Historical v1 directories, tools, gates, and hooks remain available for knowledge retrieval, but v2 neither executes nor refreshes that runtime. See [WORKFLOW.en.md](./WORKFLOW.en.md) for the full boundary.
---
@@ -217,8 +241,7 @@ CodeStable is modeled for real-world development scenarios, aiming to handle com
CodeStable adapts to model capability. If a future model nails a module reliably, that module gets removed.
- [x] v2 thin-harness rewrite: 32 skills consolidated into 8 thin responsibility contracts (~24k lines → ~340 lines), all state machines / gates / stage artifacts removed; ordinary tasks produce zero artifacts; knowledge unified into attention + lessons + work
- [ ] Refactor flow needs hardening
- [ ] Refactor flow needs hardening (`cs-refactor` is still beta)
- [ ]
Issues welcome — share your real-world dev pain and refactoring experience.
+55 -37
View File
@@ -77,10 +77,17 @@ Claude 更新后需要重启 Claude Code 才会应用新版插件。
`skills` CLI
```bash
npx skills@latest remove \
cs-audit cs-brainstorm cs-doc-api cs-doc-tutorial cs-docs cs-docs-neat \
cs-domain cs-feat-accept cs-feat-design cs-feat-design-review cs-feat-ff \
cs-feat-impl cs-feat-qa cs-feedback cs-goal cs-issue-analyze cs-issue-fix \
cs-issue-report cs-note cs-refactor-ff cs-req cs-roadmap \
cs-roadmap-impl-goal cs-roadmap-review \
-g -y
npx skills@latest add codestable/CodeStable/plugins/codestable --skill '*' -g
```
当前 `skills` CLI 的 `update` 对 plugin manifest 与通用目录使用不同的发现逻辑,可能把仍存在的 sibling skills 误判为已删除;因此升级使用上面的完整 package 重装命令。它会从 `plugins/codestable` 同步全部 `cs*` skills如果原来是项目级安装,去掉 `-g` 并在项目中执行。v2 起规则全部随 skill 包生效,项目里没有需要刷新的 runtime 资产——升级插件即完成,无需在各项目中执行任何刷新命令
当前 `skills` CLI 的 `add` / `update` 不会自动删除新版 package 已移除的旧 skill因此从 v1.0.4 升级到 v2.0.0 时,先用第一条命令精确删除 24 个退役入口,再完整安装 v2 的 8 个。CLI 按名称删除,不校验安装来源:命令不会影响其他名称,但如果你用相同名称维护过自定义或第三方 skill请先备份并从删除列表移除对应名称。以后同一 major 内升级只需重新执行 `add`如果原来是项目级安装,两条命令都去掉 `-g` 并在项目中执行。项目里的 v1 历史资产原样保留,不需要逐仓库刷新运行时
只需要一键,开始工作:
@@ -126,7 +133,7 @@ CodeStable 走的是**另一个方向**
<tr><th></th><th>Agent 编排派</th><th>CodeStable</th></tr>
<tr><td><b>核心实体</b></td><td>Agent / Role / Team</td><td>Requirement / Architecture / Feature / Issue / Decision</td></tr>
<tr><td><b>主线问题</b></td><td>Agent 之间怎么分工、传递、协调?</td><td>软件的需求、约束、决策怎么被记下来、被检索、被复用?</td></tr>
<tr><td><b>状态存在哪</b></td><td>Agent 的 session / 消息总线 / 队列</td><td>项目里的 <code>.codestable/</code> 文件树(人和 AI 都能读)</td></tr>
<tr><td><b>状态存在哪</b></td><td>Agent 的 session / 消息总线 / 队列</td><td>项目文档与 <code>.codestable/</code> 项目记忆(人和 AI 都能读)</td></tr>
<tr><td><b>解决的痛点</b></td><td>单 Agent 能力不够,需要协同放大</td><td>软件复杂度膨胀撑破上下文、隐知识丢失、需求漂移</td></tr>
<tr><td><b>对人的定位</b></td><td>人少介入越好,理想是全自动</td><td>人在环 —— 程序员对整体把控负责AI 是高效的执行体</td></tr>
</table>
@@ -148,56 +155,66 @@ CodeStable 走的是**另一个方向**
CodeStable 顺着软件编码的真实流程来设计,把开发活动建模成一组**实体**和**流程**。
### 实体
### 项目记忆
| 实体 | 载体 | 干什么 |
|------|------|--------|
| **注意事项** | `attention.md` | 每次会话必读的项目事实,25 条 |
| **经验** | `lessons/` | 复利工程的知识库踩过的坑、好做法、调研结论一条一文件grep 检索(`cs-keep` 写入,写入必须有可追溯证据) |
| **活动任务** | `work/` | 跨会话 / 交接 / epic 任务的唯一状态文档(目标 / 现场 / 边界 / 证据 / 验收 / 状态与未决六节),完成即压缩删除 |
普通任务不产生任何 CodeStable 实体——git diff、测试输出与交付说明就是证据。
| 实体 | 干什么 |
|------|--------|
| **attention** | 每次会话都要知道的少量项目事实,保持在 25 条以内 |
| **lessons** | 一条一文件的踩坑、技巧和调研结论,靠关键词检索后按需加载 |
| **work** | 跨会话或多人交接的活动任务;普通任务不创建,完成后清理 |
| **项目文档 / ADR** | 需求、领域模型、公开契约与长期技术决策的 canonical owner |
### 流程
| 流程 | 入口 | 硬门槛 |
|------|------|--------|
| **特性引入** | `cs-feat` | 触发风险升级信号(公开契约 / 数据 / 权限 / 真实取舍 / 大 diff设计落盘 work 文档、过独立 agent review 再交人确认,不 auto-approve测试设施可用时测试先行完成必须附可核验证据 |
| **问题修复** | `cs-issue` | 没有能明确变红的验证不许猜根因;修复完成时变红的验证必须变绿 |
| **代码重构** | `cs-refactor` | 先有能自证行为等价的验证再动代码;发现要改行为立即停下转向 |
| **大需求** | `cs-epic` | 拆解方案经用户确认后执行;一个 epic 文档维护全景;不代替用户做整体验收 |
| **独立审查** | `cs-code-review` | 只读;独立 subagent 视角设计与改动默认过审blocking 未解决不得通过,修复-复审最多 2 轮后交人裁决 |
| 流程 | 推荐主入口 | 说明 |
|------|------------|------|
| **特性引入** | `cs-feat` | 默认直接理解、实现、验证;遇到高风险契约或真实取舍时先让用户确认 |
| **大需求端到端** | `cs-epic` | 维护一个 work 文档,确认拆解后逐个推进 feature / issue / refactor 子项 |
| **问题修改** | `cs-issue` | 先建立会变红的验证,再修复并证明它变绿 |
| **代码重构** | `cs-refactor` | 先建立等价性证据,分步调整结构并持续验证 |
| **代码审查 / 审计** | `cs-code-review` | 独立只读审查当前 diff或按指定范围做 audit |
| **知识沉淀** | `cs-keep` | 把有证据的高频事实或可复用经验写入项目记忆 |
每个流程共用同一主线:理解相关事实 → 行动 → 相称的验证 → 交付结果。风险每次按当前事实重判,没有持久 lane、没有阶段状态机
高风险改动或用户要求时,执行流调用 `cs-code-review` 做独立审查。文档与 ADR 由拥有该变化的开发任务同步,不再需要独立的阶段 skill
---
## 技能总览
v2 共 8 个 skill——一层薄研发纪律 + 一个项目记忆闭环thin harness, thick context
### 当前 8 个 skill
| 技能 | 用途 |
|---|---|
| `cs` | 体系速读与入口推荐;只解释,不启动流程 |
| `cs-onboard` | 创建 `.codestable/` 最小骨架v1 存量无损保留 |
| `cs-feat` | 新功能与功能改造;风险升级时设计经独立 review 后交人确认 |
| `cs-issue` | bug 修复;没有能变红的验证不许猜根因 |
| `cs-refactor` | 行为等价重构;先有等价性验证再动代码 |
| `cs-code-review` | 独立审查diff / design / repo 审计三模式 |
| `cs-epic` | 大需求拆解与长程推进 |
| `cs-keep` | 沉淀经验;写入必须有可追溯证据 |
| 分组 | 技能 | 用途 |
|---|---|---|
| 导航 | `cs` | 解释体系并推荐入口;不启动下游流程、不写文件 |
| 接入 | `cs-onboard` | 把 CodeStable 接入新仓库或已有零散文档仓库 |
| 大需求 | `cs-epic` | 拆解、确认并长程推进多个子项 |
| 功能 | `cs-feat` | 实现新功能或功能改造,按风险升级设计确认与 review |
| 问题 | `cs-issue` | 用红到绿证据修复 bug 或既有行为异常 |
| 重构 | `cs-refactor` | 在行为等价证据下调整结构或性能 |
| 审查 | `cs-code-review` | 独立只读 diff review 或按需 audit |
| 记忆 | `cs-keep` | 沉淀有证据的高频事实与可复用经验 |
v1 的阶段技能与长尾入口design/impl/qa 阶段技能、`cs-goal``cs-brainstorm``cs-docs` 系、`cs-domain``cs-req``cs-audit``cs-note``cs-feedback``cs-roadmap` 系)已并入上表对应入口。完整目录见 [SKILL_CATALOG.md](./SKILL_CATALOG.md)。日常不知道用哪个时直接调用 `/cs`
v1.0.4 的另外 24 个名称已退役且不随 v2 交付,不再安装兼容 shim。映射与升级边界见
[SKILL_CATALOG.md](./SKILL_CATALOG.md);不知道用哪个时调用 `/cs` 获取推荐。
---
## 工作流与运行时
## 工作流与项目记忆
所有入口共用一条执行主线:**理解相关事实 → 行动 → 相称的验证 → 交付结果**。风险每次按当前事实重判,没有持久 lane、没有阶段状态机动手前按任务关键词 grep 项目沉淀(含 v1 存量命中报告来源。design 与验收的确认不可被模型自主跳过
CodeStable v2 用 thin harness 保留责任、硬门槛和完成证据,把项目事实按需加载。`cs` 只做导航;功能、问题与重构入口直接完成工作,高风险或用户要求时再调用独立审查;`cs-epic` 用一个 work 文档维护跨会话全景
`cs-onboard` 在项目根生成极简 `.codestable/``attention.md`每次必读≤25 条)、`lessons/`一条一文件的经验grep 检索)、`work/`(活动中的跨会话任务,完成即压缩删除)。普通任务零产物;没有 gate、没有 runtime 工具、没有复制到项目的 reference。v1 存量项目的旧产物只读保留,旧沉淀继续被检索覆盖。
`cs-onboard` 在项目根生成最小 `.codestable/`
完整工作流与持久化约定见 [WORKFLOW.md](./WORKFLOW.md)。
```text
.codestable/
├── attention.md
├── lessons/
└── work/
```
skill 专属 context/helper 归 owning skill项目需求、领域模型与 ADR 继续使用项目自己的文档结构。v1 历史目录、tool、gate 与 hook 原样保留并可作为知识检索,但 v2 不执行旧 runtime也不复制或刷新它们。
完整工作流、目录树和跨 skill 引用约束见 [WORKFLOW.md](./WORKFLOW.md)。
---
@@ -222,9 +239,11 @@ CodeStable 的 skill 不靠"感觉写得更清楚了"来演进,而是**用可
**两个配套工具(仓库内,不随插件交付):**
- `build-cs-skill`skill 的编写协议——thin harness 写法:每个 SKILL.md 是薄责任契约责任、硬门槛、证据要求、停止条件frontmatter `contracts` 只锚定行为不变量;流程状态机与厚 reference 不再存在
- `build-cs-skill`skill 的编写协议prompt-as-code——用最小 harness 承载责任、上下文选择和硬 gate阶段方法与项目事实按需加载行为不变量由直接测试、decision fixtures 或真实 runtime gate 验证
- `eval-cs-skill`skill 的评测引擎——把 skill 的关键决策做成 **decision fixtures**(给定仓库状态 → 期望的下一步让真实模型跨供应商Claude / GPT多次作答用程序机械判分`[measured]`),而不是靠人或裁判打分。
深层 `references/**/*.md` 只承载按条件加载的领域或阶段上下文;可机械判定的规则进入直接测试或 runtime gate顶层只保留调用接口和失败语义。新增规则必须通过 `placeRule` 确认唯一 owner 与证据层级。
**闭环:** `编写 → 评测 → 定位失败 → 优化 → 复评 → 结论回写方法论`
一次真实成果2026-077 个主入口 skill × 3 模型 × 每题 3 次,见 `experiments/*/results.md`):把 skill 从"规则散落在文档里"重写成"`Spec` 作为唯一的 prompt 路由真相"后,历史 campaign 的路由决策正确率**从均值 0.807 提升到 0.975**;且用数据否定了一个直觉误区——"新旧两种写法并排放"反而有害(某 skill 上比不改还低)。这些量化结论已回写进 `build-cs-skill` 的 authoring 规则,指导后续所有 skill 的编写。后续状态 schema / fixtures 变化须重新测量,不沿用旧 artifacts 为当前 HEAD 背书。
@@ -245,8 +264,7 @@ CodeStable 的 skill 不靠"感觉写得更清楚了"来演进,而是**用可
CodeStable 会根据模型能力的发展进行调整。如果未来某个模型做到某个模块的稳定产出,那么这个模块就可以删除。
- [x] 简化 cs skills 体系:核心保留 `cs-feat` / `cs-epic` / `cs-issue` 等主入口,兼容入口收薄
- [x] v2 thin-harness 重构32 个 skill 收敛为 8 个薄责任契约(约 2.4 万行 → 约 340 行),删除全部状态机 / gate / 阶段产物,普通任务零产物,沉淀统一为 attention + lessons + work
- [x] 简化 cs skills 体系:v2 收敛为 8 个独立 thin-harness skill退役 24 个旧入口
- [x] 端到端测评 · 基础路由评测decision fixtures + 跨模型机械判分,[measured] 证明重构增益
- [x] 端到端测评 · 效果评测:种子仓库 + 隐藏验收测试 + 真 agent 对照裸 agent`cs-issue`/`cs-feat` 已跑,诚实测出能力边界与过程契约价值
- [ ] 效果评测扩容cs-epic 多子 feature 端到端design 对弱模型增益补统计功效
+34 -14
View File
@@ -1,18 +1,38 @@
# CodeStable Skill Catalog
# CodeStable v2 Skill Catalog
v2 ships 8 skills: a thin layer of engineering discipline plus a project-memory loop. Ordinary tasks produce zero CodeStable artifacts — the diff and tests are the evidence; cross-session work keeps exactly one work document.
v2 ships exactly 8 skills. Each is an independent installation unit and does not depend on sibling
skill files or a centralized onboard runtime.
| Skill | Purpose |
## Current Entries
| Group | Skill | Responsibility |
|---|---|---|
| Navigation | `cs` | Explain CodeStable and recommend one entry; never start a workflow or write files |
| Onboarding | `cs-onboard` | Create minimal project memory and explain a lossless v1 upgrade |
| Feature | `cs-feat` | Implement new capability; scale design confirmation and independent review with risk |
| Issue | `cs-issue` | Fix bugs or broken existing behavior with red-to-green evidence |
| Refactor | `cs-refactor` | Change structure or performance under verifiable behavioral equivalence |
| Epic | `cs-epic` | Decompose, confirm, and drive multiple deliverable items over time |
| Review | `cs-code-review` | Perform independent read-only review or an on-demand module/repository audit |
| Memory | `cs-keep` | Store evidence-backed frequent facts or reusable lessons in project memory |
## Retired v1.0.4 Entries
The following 24 names are retired and not shipped in v2. No compatibility shims are installed.
Upgrading preserves historical project artifacts; it only removes these triggers from the new skill
package.
| v1 names | v2 approach |
|---|---|
| `cs` | System overview and entry recommendation; explains only, never starts a workflow |
| `cs-onboard` | Create the minimal `.codestable/` skeleton (attention / lessons / work); v1 legacy is preserved untouched |
| `cs-feat` | New features and changes; on risk escalation the design passes independent review, then user confirmation |
| `cs-issue` | Bug fixing; no root-cause guessing without a verification that clearly turns red |
| `cs-refactor` | Behavior-preserving refactoring; equivalence verification before touching code |
| `cs-code-review` | Independent review: diff / design / repo-audit modes; fix-and-rereview capped at 2 rounds |
| `cs-epic` | Large-requirement decomposition and long-running delivery via one epic document |
| `cs-keep` | Distill experience into attention / lessons; every entry needs traceable evidence |
| `cs-feat-design`, `cs-feat-design-review`, `cs-feat-impl`, `cs-feat-qa`, `cs-feat-accept`, `cs-feat-ff` | Use `cs-feat`; risk and repository facts determine execution strength |
| `cs-issue-report`, `cs-issue-analyze`, `cs-issue-fix` | Use `cs-issue` |
| `cs-refactor-ff` | Use `cs-refactor` |
| `cs-audit` | Use the audit mode of `cs-code-review` |
| `cs-goal`, `cs-roadmap`, `cs-roadmap-review`, `cs-roadmap-impl-goal` | Use `cs-epic` for large initiatives; ordinary cross-session work uses one work document |
| `cs-brainstorm`, `cs-domain`, `cs-req` | Clarify within `cs-feat` / `cs-epic`; update project docs or ADRs directly |
| `cs-docs`, `cs-docs-neat`, `cs-doc-api`, `cs-doc-tutorial` | Update docs as part of the owning development task, or request a standalone documentation edit directly |
| `cs-note` | Use `cs-keep` |
| `cs-feedback` | Store project lessons with `cs-keep`; submit product feedback through the repository issue process |
## v1 legacy entries
The v1 stage skills and long-tail entries (`cs-feat-design`, `cs-issue-fix`, `cs-goal`, `cs-brainstorm`, `cs-docs`, `cs-domain`, `cs-req`, `cs-audit`, `cs-note`, `cs-feedback`, the `cs-roadmap` family, etc.) have been removed: design and requirement clarification are built-in steps of `cs-feat` / `cs-epic`, auditing is a mode of `cs-code-review`, knowledge capture goes through `cs-keep`, and docs/ADRs are produced by the work that needs them. All v1 artifacts and knowledge in existing projects remain untouched and grep-discoverable.
Call `cs` when the mapping is unclear. See
[WORKFLOW.en.md](./WORKFLOW.en.md#v1-upgrade-boundary) for v1 project-asset preservation.
+33 -14
View File
@@ -1,18 +1,37 @@
# CodeStable 技能目录
# CodeStable v2 技能目录
v2 8 个 skill:一层薄研发纪律 + 一个项目记忆闭环。普通任务零产物,证据是 diff 与测试;跨会话任务一个 work 文档。
v2 只交付 8 个 skill。每个 skill 是独立安装单元,不依赖 sibling skill 文件或集中式 onboard
runtime。
| 技能 | 用途 |
## 当前入口
| 分组 | Skill | 责任 |
|---|---|---|
| 导航 | `cs` | 解释 CodeStable 并推荐一个入口;不启动流程、不写文件 |
| 接入 | `cs-onboard` | 创建最小项目记忆骨架;无损说明 v1 升级边界 |
| 功能 | `cs-feat` | 实现新功能;按风险决定是否先确认设计或做独立 review |
| 问题 | `cs-issue` | 用红到绿的验证修复 bug 或既有行为异常 |
| 重构 | `cs-refactor` | 在可核验的行为等价约束下调整结构或性能 |
| 大需求 | `cs-epic` | 拆解、确认并长程推进多个可交付子项 |
| 审查 | `cs-code-review` | 独立只读审查;按需做模块或全仓 audit |
| 记忆 | `cs-keep` | 将有证据的高频事实或可复用经验写入项目记忆 |
## v1.0.4 退役入口
以下 24 个名称已退役,不随 v2 交付,也不会保留兼容 shim。升级不会删除项目里的历史
产物;只是新的 skill 安装包不再暴露这些触发入口。
| v1 名称 | v2 做法 |
|---|---|
| `cs` | 体系速读与入口推荐;只解释,不启动流程 |
| `cs-onboard` | 创建 `.codestable/` 最小骨架attention / lessons / workv1 存量无损保留 |
| `cs-feat` | 新功能与功能改造;风险升级时设计经独立 review 后交人确认 |
| `cs-issue` | bug 修复;没有能变红的验证不许猜根因 |
| `cs-refactor` | 行为等价重构;先有等价性验证再动代码 |
| `cs-code-review` | 独立审查diff / design / repo 审计三模式;修复-复审最多 2 轮 |
| `cs-epic` | 大需求拆解与长程推进;一个 epic 文档维护全景 |
| `cs-keep` | 沉淀经验到 attention / lessons写入必须有可追溯证据 |
| `cs-feat-design`, `cs-feat-design-review`, `cs-feat-impl`, `cs-feat-qa`, `cs-feat-accept`, `cs-feat-ff` | 统一进入 `cs-feat`,由风险与仓库事实决定执行强度 |
| `cs-issue-report`, `cs-issue-analyze`, `cs-issue-fix` | 统一进入 `cs-issue` |
| `cs-refactor-ff` | 进入 `cs-refactor` |
| `cs-audit` | 使用 `cs-code-review` 的 audit 模式 |
| `cs-goal`, `cs-roadmap`, `cs-roadmap-review`, `cs-roadmap-impl-goal` | 大需求进入 `cs-epic`;普通跨会话任务使用一个 work 文档 |
| `cs-brainstorm`, `cs-domain`, `cs-req` | 功能或大需求在 `cs-feat` / `cs-epic` 内澄清;项目事实直接更新到项目文档或 ADR |
| `cs-docs`, `cs-docs-neat`, `cs-doc-api`, `cs-doc-tutorial` | 在对应开发任务中同步文档,或直接提出独立文档请求 |
| `cs-note` | 进入 `cs-keep` |
| `cs-feedback` | 项目经验进入 `cs-keep`;产品反馈按仓库 issue 流程提交 |
## v1 旧入口
v1 的阶段技能与长尾入口(`cs-feat-design``cs-issue-fix``cs-goal``cs-brainstorm``cs-docs``cs-domain``cs-req``cs-audit``cs-note``cs-feedback``cs-roadmap` 系等)已移除:设计与需求澄清是 `cs-feat` / `cs-epic` 的内置步骤,审计是 `cs-code-review` 的模式,沉淀统一走 `cs-keep`,文档与 ADR 由对应工作顺带完成。存量项目的 v1 产物与沉淀全部保留并可被 grep 检索。
不知道如何映射时调用 `cs` 获取推荐。v1 项目资产的保留规则见
[WORKFLOW.md](./WORKFLOW.md#v1-升级边界)。
+1 -1
View File
@@ -1 +1 @@
1.0.4
2.0.0
+43 -19
View File
@@ -1,34 +1,58 @@
# CodeStable Workflow and Runtime Structure
## Design principle: thin harness, thick context
Rules stay minimal: skills carry engineering experience (when to do what, and why) plus a few hard gates — no process state machines. The route belongs to the model; state is recovered from repository facts. Context is retrieved on demand: before acting, grep the project's accumulated knowledge by task keywords and report the sources of any hits.
# CodeStable v2 Workflow and Project Structure
## Workflow
CodeStable v2 consists of eight independently installed thin-harness skills and a project-memory
loop. `cs` only explains and recommends; it does not start another workflow automatically. Once the
entry is known, invoke that skill directly.
```text
cs (overview)
cs-onboard (skeleton)
cs-feat / cs-issue / cs-refactor (event entries) ──> cs-code-review (independent review: designs and changes by default, trivial edits may skip)
cs-epic (large-requirement decomposition, sub-items go through event entries)
cs-keep (wrap-up distillation; every entry has a built-in recommendation moment)
unsure which entry -> cs
onboard / v1 upgrade -> cs-onboard
new capability -> cs-feat ---------\
bug / broken behavior -> cs-issue ----------> cs-code-review (high risk or on demand)
equivalent refactor -> cs-refactor ------/
large initiative -> cs-epic -> cs-feat / cs-issue / cs-refactor
lessons and memory -> cs-keep
```
All entries share one execution mainline: **understand the relevant facts → act → run proportionate verification → deliver the result**. Risk is re-judged per request from current facts — no persistent lanes. Escalation signals (public contracts / data / permissions / real trade-offs / large diffs / explicit user request) require design alignment before acting: the proposal is persisted into a work document, passes an independent agent design review (fix-and-rereview capped at 2 rounds; beyond that, escalate with the disagreement), and is then confirmed by the user. Design and final-acceptance confirmations must never be auto-approved by the model; completed changes get an independent review by default; claiming completion requires verifiable evidence.
Execution strength follows risk:
## Persistence
- `cs-feat` normally understands, implements, and verifies directly. Public contracts, data,
authorization, concurrency, or real design tradeoffs require owner confirmation first.
- `cs-issue` establishes a reliably failing check before changing code, then proves it turns green.
- `cs-refactor` establishes equivalence evidence first and keeps verification green after each step.
- `cs-epic` maintains one work document for items, dependencies, and acceptance. The owner confirms
decomposition and boundary changes.
- `cs-code-review` is an independent read-only review and also handles module or repository audits.
- `cs-keep` compresses frequently needed facts into attention and reusable experience into lessons.
Ordinary tasks produce zero CodeStable artifacts — the git diff, test output, and delivery summary are the evidence.
Ordinary work creates no stage artifacts. The diff, test output, and delivery report are the evidence.
Create one work document only for cross-session work, multi-agent handoff, or an explicit request for
a durable record; remove it when complete unless the owner asks to retain it.
## Project Memory
`/cs-onboard` creates this minimal skeleton for a new project:
```text
.codestable/
├── attention.md # project facts to read every session, 25 entries
├── lessons/ # distilled experience, one markdown file per lesson, grep-searchable
└── work/ # active cross-session tasks only, one doc per task (goal/context/boundaries/evidence/acceptance/status-and-open-items), compressed and deleted on completion
├── attention.md # a small set of facts needed every session, at most 25 entries
├── lessons/ # one Markdown file per reusable lesson, searched by keyword
└── work/ # active cross-session work, removed on completion
```
Lesson discipline: never write without traceable evidence; grep for same-domain entries first and merge instead of duplicating; a soft cap of ~50 lessons forces consolidation before addition.
Skill-specific context and helpers belong to the owning skill's `references/` and `scripts/`.
Project facts belong in the structure above or the project's existing docs and ADRs. A skill does not
read sibling skill files or depend on a centralized onboard runtime. Worktree, branch, and agent
backend policy remain host- or owner-controlled.
## v1 compatibility
## v1 Upgrade Boundary
v1 artifacts in existing projects (`requirements/`, `roadmap/`, `features/`, `issues/`, `compound/`, `reference/`, `tools/`, etc.) are kept read-only — never migrated or deleted; legacy knowledge stays covered by the same grep retrieval. v1 gates and runtime tools are no longer invoked by skills.
v2 does not migrate or clean historical v1 project directories. Existing requirements, roadmap,
features, issues, compound knowledge, tools, gates, hooks, and manifests remain untouched. New skills
may search those artifacts for project knowledge, but they do not execute the old runtime or produce
new v1 stage artifacts.
The 32 skills in v1.0.4 converge to eight in v2. The other 24 entries are retired and are not installed
with v2. See [SKILL_CATALOG.en.md](./SKILL_CATALOG.en.md) for the complete mapping.
+37 -19
View File
@@ -1,34 +1,52 @@
# CodeStable 工作流与运行结构
## 设计原则thin harness, thick context
规则点到为止skill 只传递研发经验(何时该做什么、为什么)与少数硬门槛,不复刻流程状态机;执行路线交给模型,状态从仓库事实恢复。上下文按需检索:每次动手前按任务关键词 grep 项目沉淀,命中报告来源。
# CodeStable v2 工作流与项目结构
## 工作流
CodeStable v2 是 8 个独立安装的 thin-harness skill加一个项目记忆闭环。`cs` 只解释和
推荐,不自动启动下游流程;确定入口后直接调用对应 skill。
```text
cs导览
cs-onboard(骨架)
cs-feat / cs-issue / cs-refactor事件入口──> cs-code-review独立审查设计与改动默认过审微小改动可跳过
cs-epic大需求拆解逐子项走事件入口
cs-keep收尾沉淀所有入口内置推荐时机
不确定入口 -> cs
仓库接入 / v1 升级 -> cs-onboard
新功能 -> cs-feat ---------\
bug / 行为异常 -> cs-issue ----------> cs-code-review高风险或按需
行为等价重构 -> cs-refactor ------/
大需求拆解 -> cs-epic -> cs-feat / cs-issue / cs-refactor
经验与项目记忆 -> cs-keep
```
每个入口共用同一执行主线:**理解相关事实 → 行动 → 相称的验证 → 交付结果**。风险每次按当前事实重判,不写入持久 lane触发升级信号公开契约 / 数据 / 权限 / 真实取舍 / 大范围 diff / 用户要求)时先对齐设计再动手:方案要点落盘为 work 文档,经独立 agent design review修复-复审最多 2 轮超限连分歧一起上交后交用户确认。design 与整体验收的确认不可被模型自主跳过;改动完成默认独立 review声称完成必须附可核验证据。
执行强度与风险相称:
## 持久化
- `cs-feat` 默认直接理解、实现、验证;公开契约、数据、权限、并发或真实方案取舍先经用户确认。
- `cs-issue` 先建立能明确变红的验证,再修复并证明它变绿。
- `cs-refactor` 先建立等价性证据,分步改动并持续保持验证为绿。
- `cs-epic` 用一个 work 文档维护子项、依赖和验收;拆解与边界变更由用户确认。
- `cs-code-review` 是只读独立审查,也承接模块或全仓 audit。
- `cs-keep` 把高频事实压进 attention把可复用经验写成 lesson。
普通任务零 CodeStable 产物——git diff、测试输出交付说明就是证据
普通任务不生成阶段文档。diff、测试输出交付说明就是证据;只有跨会话、多人交接或用户
要求留痕时,才维护一个 work 文档,完成后删除或按用户要求保留。
## 项目记忆
`/cs-onboard` 为新项目创建最小骨架:
```text
.codestable/
├── attention.md # 每次会话必读的项目事实,25 条
├── lessons/ # 沉淀经验,一条一 markdown 文件,grep 检索
└── work/ # 活动中的跨会话任务,一任务一文档(目标/现场/边界/证据/验收/状态与未决六节),完成即压缩删除
├── attention.md # 每次会话需要的少量项目事实,最多 25 条
├── lessons/ # 一条经验一个 Markdown 文件,按关键词检索
└── work/ # 活动中的跨会话任务,完成即清
```
lesson 写入纪律:没有可追溯证据不写;写前 grep 同域旧条目,能合并不新增;约 50 条上限触发先合并。
skill 专属 context 与 helper 分别由 owning skill 的 `references/``scripts/` 提供。项目
事实放在上述目录或项目既有文档与 ADR 中。skill 不读取 sibling skill 文件,也不依赖集中式
onboard runtimeworktree、branch 和 agent backend 策略由宿主或 owner 决定。
## v1 兼容
## v1 升级边界
存量项目的 v1 产物(`requirements/``roadmap/``features/``issues/``compound/``reference/``tools/` 等)一律只读保留、不迁移不删除;旧沉淀继续被各 skill 的 grep 检索覆盖。v1 的 gate 与 runtime 工具不再被 skill 调用。
v2 不迁移或清理 v1 项目的历史目录。已有 `requirements/``roadmap/``features/`
`issues/``compound/`、tool、gate、hook 和 manifest 原样保留;新 skill 可以按任务关键词
检索其中的项目知识,但不会执行旧 runtime也不会继续生成 v1 阶段产物。
v1.0.4 的 32 个 skill 在 v2 收敛为 8 个;其余 24 个入口已退役且不随 v2 安装。完整映射见
[SKILL_CATALOG.md](./SKILL_CATALOG.md)。
+5 -1
View File
@@ -1,7 +1,8 @@
---
adr: "001"
title: "CodeStable 工具从已安装的 skill 包运行"
status: Accepted
status: Superseded
superseded-by: "004"
date: 2026-07-06
applies-to:
- "plugins/codestable/skills/cs-onboard/tools/"
@@ -15,6 +16,9 @@ lint: "python3 -m pytest tests/test_codestable_doctor.py tests/test_skill_entry_
# ADR-001: CodeStable 工具从已安装的 skill 包运行
> 本决策已被 [ADR-004](004-project-knowledge-not-runtime-distribution.md) 取代。以下内容保留为
> v1 架构记录。
## Context
CodeStable 过去会把共享 Python 工具复制到每个仓库的 `.codestable/tools/`。这让每个已
@@ -5,11 +5,10 @@ status: Accepted
date: 2026-07-06
applies-to:
- "plugins/codestable/skills/"
- "plugins/codestable/skills/cs-onboard/references/"
- ".codestable/reference/"
- ".codestable/work/"
enforcement: test
stage: [design, review, check]
lint: "python3 -m pytest tests/test_skill_entry_simplification.py"
lint: "python3 -m pytest tests/test_v2_architecture_contract.py tests/test_skill_contracts.py"
---
# ADR-002: CodeStable skills 不拥有 worktree 或分支策略
@@ -33,23 +32,20 @@ branch guard、finish-worktree 或 merge-inbox 流程。它们应在宿主或 ow
如果项目后续需要自动创建 worktree 或分支保护,该能力应作为独立 skill 或宿主策略提供,而
不是嵌入每个 CS workflow。
legacy worktree / branch 文件可以继续留在已 onboard 的仓库中,但新的 CS skill 文本
runtime refresh 不得把它们作为默认契约的一部分
legacy worktree / branch 文件可以继续留在已接入的仓库中,但新的 CS skill 文本不得把它们
作为默认契约,也不得执行相关 legacy runtime
## Consequences
- CS workflows 聚焦 durable artifacts、gates、review、QA 和 acceptance,而不是 checkout
管理。
- CS workflows 聚焦任务边界、验证证据、review 和可恢复 work 文档,而不是 checkout 管理。
- worktree policy 可以独立演进,不需要修改每个 CS skill。
- 现有项目不需要强制删除历史 worktree 文档或 hooks。
- review 和测试守卫必须监控当前 skill 文档中的默认契约残留,例如 `worktree-gate`
`branch-guard``codestable-finish-worktree``worktree-conventions`
- review 和测试守卫必须监控当前 skill 文档中的默认 worktree/branch 契约残留。
## Rejected alternatives
- 继续把 worktree gates 放在每个 CS skill 内。拒绝原因:这会把 workflow correctness 绑定到
本应属于宿主或 owner 的 checkout strategy。
- runtime refresh 时删除旧 worktree 和 branch-guard artifacts。拒绝原因当前迁移目标是
非破坏性兼容。
- v2 升级时删除旧 worktree 和 branch-guard artifacts。拒绝原因当前迁移目标是非破坏性兼容。
- 立刻用新的内建 worktree policy 替代旧 policy。拒绝原因目标边界是未来独立 skill而不是
另一个嵌入式默认策略。
+7 -6
View File
@@ -6,11 +6,10 @@ date: 2026-07-06
applies-to:
- ".claude/skills/eval-cs-skill/"
- "experiments/"
- "plugins/codestable/skills/cs-feedback/scripts/feedback_to_fixture.py"
- ".claude/skills/eval-cs-skill/scripts/promote_feedback_fixture.py"
enforcement: test
stage: [author, eval, optimize, release]
lint: "python3 -m pytest tests/test_cs_skill_eval.py tests/test_cs_skill_convergence.py tests/test_cs_skill_release.py tests/test_cs_skill_bootstrap.py tests/test_cs_skill_selfref.py"
lint: "python3 -m pytest tests/test_cs_skill_eval.py tests/test_cs_skill_convergence.py tests/test_cs_skill_release.py tests/test_cs_skill_selfref.py"
---
# ADR-003: eval-cs-skill 评测与自研迭代闭环
@@ -32,20 +31,22 @@ CodeStable 原有 `tests/test_skill_*` 只验证 skill **写得对不对**(路
5. **release 两步走**`knowledge-extractor` 产草稿 → `adapt_extracted_skill.py` 翻译成 CS 合规结构(禁止 extractor 直写 plugins/),再 `regression.py` + `bump_version.py`
6. **自治默认轻量 cron**`enqueue_experiment.py`BAIME `loop-backlog` 为可选宿主。
7. **自指**`experiments/eval-cs-skill-001/` 用同一 runner/scorer 评 `eval-cs-skill` 自身。
8. **反馈交接边界**shipped `cs-feedback` 只把 local-private `triage.json` 转成同目录 candidate正式 fixture repo-local promotion 工具读取 experiment config校验 profile/input/privacy/scorer/harness/judge 后 fail-closed 落盘。两单元只通过 JSON artifact 连接,运行时互不 import。
8. **v2 输入边界**评测 fixture repo-local 维护者资产,不依赖任何 shipped runtime
skill。`promote_feedback_fixture.py` 只保留为 v1 `cs-feedback` candidate 的 legacy-only
导入器,不构成 v2 production feedback 入口。
## Consequences
- skill 效果可跨 model/harness 量化,改进有硬 verdict 而非直觉。
- 新 skill 接入只需加 `experiments/` 数据(自举);加 harness 只需加一个 adapter。
- 生产失败先经 `cs-feedback/feedback_to_fixture.py` 形成未入库 candidate只有 readiness、隐私与目标 experiment gates 全过repo-local promotion 才写 regression fixture
- 冻结的 v1 feedback candidate 仍可显式导入历史 experimentv2 不承诺 production feedback promotion
- eval-cs-skill 自身可被同一闭环评测优化(自指)。
- 真实多模型运行需 API/CLI 鉴权并产生成本,受 `--dry-run` + `budget_usd` 护栏约束。
- **评测效度是头等风险**(首轮真实 campaign 教训):必须复现 skill 的设计运行环境(`inject_context` 补 onboard 上下文)、用语义 oracle`recall_judge`)判散文 answerfixture 内嵌被操作的 subject matter否则测到的是「skill 在残缺环境下的反应」而非真实能力。核查须分模型看 + 手工读原始输出 + 认 k=1 variance。细则见 `references/eval/protocol.md` 效度三铁律。
- **评测效度是头等风险**(首轮真实 campaign 教训):必须复现被测 skill 实际声明的 context contract而不是注入统一 onboard runtime同时用语义 oracle`recall_judge`)判散文 answer,并让 fixture 内嵌被操作的 subject matter否则测到的是「skill 在残缺环境下的反应」而非真实能力。核查须分模型看 + 手工读原始输出 + 认 k=1 variance。细则见 `references/eval/protocol.md` 效度三铁律。
## Rejected alternatives
- **import cs-onboard/tools 到 eval-cs-skill**。拒绝:违反 skill 独立性CLAUDE.mddod_gate 改为自包含 + CLI 边界。
- **从另一个 shipped skill import runtime**。拒绝:违反 skill 独立性;确定性检查必须自包含或通过明确 CLI 边界调用
- **BAIME loop-backlog 作默认自治**。拒绝:绑 Node/backlog/独立 checkout跨不了 harness且与 ADR-002 有张力;改为可选宿主。
- **knowledge-extractor 直接写 plugins/**。拒绝:单数 `reference/``inventory/``README.md`、超 300 行会被 check-plugin-package fail必经适配层翻译。
- **只扩展现有结构测试**。拒绝结构测试测不了运行效果eval 是独立新层。
@@ -0,0 +1,52 @@
---
adr: "004"
title: "CodeStable v2 项目目录只承载项目知识"
status: Accepted
date: 2026-07-30
supersedes: ["001"]
applies-to:
- "plugins/codestable/skills/"
- ".codestable/"
- ".claude/skills/build-cs-skill/"
enforcement: test
stage: [author, onboard, review, release]
lint: "python3 -m pytest tests/test_v2_architecture_contract.py tests/test_skill_contracts.py tests/test_skills_cli_distribution.py"
---
# ADR-004: CodeStable v2 项目目录只承载项目知识
## Context
v1 通过 `cs-onboard` 分发共享 reference、gate、Python runtime 和 manifest并交付大量阶段
skill。升级时项目副本、已安装 runtime 与 skill 文本可能处于不同版本;简单任务也要恢复并
维护与实际工作无关的状态。v2 已收敛为 8 个独立 skill 和最小项目记忆闭环,继续保留 v1
ownership 会重新引入已经删除的耦合。
## Decision
- v2 只交付 `cs``cs-onboard``cs-feat``cs-issue``cs-refactor`
`cs-code-review``cs-epic``cs-keep`
- 新项目的 `.codestable/` 只包含 `attention.md``lessons/``work/`。项目文档和 ADR
继续由项目自己的目录结构管理。
- skill 必须独立安装。skill 专属 context 和确定性 helper 分别放在 owning skill 的
`references/``scripts/`;不得读取 sibling skill也不得依赖集中式 onboard runtime。
- 不再向项目复制或刷新通用 reference、gate、tool、hook、gitignore 或 runtime manifest
v2 skill 也不调用这些旧 runtime 入口。
- v1 项目中的历史目录与产物原样保留。新 skill 可以按任务关键词检索其中的项目知识,但不得
删除、覆盖、迁移格式或执行其中的 legacy runtime。
## Consequences
- 安装包升级即可更新行为,不再要求逐仓库 runtime refresh。
- 普通任务以 diff、测试和交付说明为证据只有跨会话工作维护一个 work 文档。
- 跨 skill 的通用规则不能藏在 sibling reference 中必须成为宿主策略、项目事实、owning
skill 的小型 hard guard或有明确接口的独立安装单元。
- 从 v1 升级会退役 24 个旧 skill 名称,是需要 major version 和升级测试的 breaking change。
## Rejected alternatives
- **恢复 `cs-onboard` 的共享 runtime**。拒绝:重新制造版本分叉,并违反独立安装边界。
- **保留 24 个兼容 shim**。拒绝:扩大触发表面,让旧阶段模型继续影响 v2 决策。
- **升级时删除 v1 项目资产**。拒绝:历史知识和用户 hook 可能仍有价值,破坏性清理无必要。
- **把所有 context 复制进每个 skill**。拒绝:增加 always-loaded token 与规则漂移;只保留每次
调用确实需要的小型 guard。
@@ -1,6 +1,6 @@
{
"name": "codestable",
"version": "1.0.4",
"version": "2.0.0",
"description": "CodeStable AI coding workflow skills.",
"author": {
"name": "CodeStable"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "codestable",
"version": "1.0.4",
"version": "2.0.0",
"description": "CodeStable AI coding workflow skills.",
"skills": "./skills/"
}
+1 -1
View File
@@ -1 +1 @@
1.0.4
2.0.0
+26 -2
View File
@@ -1,13 +1,37 @@
{
"source_commit": "skills-v2-thin-harness",
"source_ref": "v1.0.4",
"skills": [
"cs",
"cs-audit",
"cs-brainstorm",
"cs-code-review",
"cs-doc-api",
"cs-doc-tutorial",
"cs-docs",
"cs-docs-neat",
"cs-domain",
"cs-epic",
"cs-feat",
"cs-feat-accept",
"cs-feat-design",
"cs-feat-design-review",
"cs-feat-ff",
"cs-feat-impl",
"cs-feat-qa",
"cs-feedback",
"cs-goal",
"cs-issue",
"cs-issue-analyze",
"cs-issue-fix",
"cs-issue-report",
"cs-keep",
"cs-note",
"cs-onboard",
"cs-refactor"
"cs-refactor",
"cs-refactor-ff",
"cs-req",
"cs-roadmap",
"cs-roadmap-impl-goal",
"cs-roadmap-review"
]
}
+20 -10
View File
@@ -3,7 +3,6 @@
from __future__ import annotations
import json
import shutil
import sys
from pathlib import Path
@@ -42,13 +41,24 @@ def test_selfref_does_not_invoke_cs_skill_tools(tmp_path):
def test_queue_enqueue_and_run_next(tmp_path, monkeypatch):
q = tmp_path / ".queue.jsonl"
monkeypatch.setattr(eq, "_queue_path", lambda: q)
exp = tmp_path / "exp"
shutil.copytree(ROOT / "experiments/cs-code-review-001", exp,
ignore=shutil.ignore_patterns("artifacts", "iteration-*.md", "results.md"))
eq.enqueue(str(exp), "eval")
eq.enqueue(str(exp), "eval")
calls = []
def fake_run(argv):
calls.append(argv)
return 0
monkeypatch.setattr(eq.runner, "main", fake_run)
exp = str(tmp_path / "exp")
eq.enqueue(exp, "eval")
eq.enqueue(exp, "eval")
assert eq.run_next() == 0
assert eq._load()[0]["status"] == "done"
assert eq.run_next() == 0 # 第二个
assert all(i["status"] == "done" for i in eq._load())
assert eq.run_next() == 0 # 队列空,幂等
assert [item["status"] for item in eq._load()] == ["done", "queued"]
assert calls == [["--experiment", exp]]
assert eq.run_next() == 0
assert [item["status"] for item in eq._load()] == ["done", "done"]
assert calls == [["--experiment", exp], ["--experiment", exp]]
assert eq.run_next() == 0
assert len(calls) == 2
@@ -0,0 +1,81 @@
"""冻结 routing experiments 的历史完整性;不代表当前 shipped skill 契约。"""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def _routing_fixture_states(experiment: str) -> dict[str, dict[str, object]]:
fixtures = ROOT / "experiments" / experiment / "fixtures/routing"
result: dict[str, dict[str, object]] = {}
for path in sorted(fixtures.glob("*.json")):
payload = json.loads(path.read_text(encoding="utf-8"))
result[payload["id"]] = payload
return result
def test_frozen_goal_routing_fixtures_keep_recorded_state_schema() -> None:
feat = _routing_fixture_states("cs-feat-routing-001")
epic = _routing_fixture_states("cs-epic-routing-001")
deprecated = {"reviewStatus", "hasGoalPackage", "codeStatus", "qaStatus", "acceptanceStatus"}
for payload in [*feat.values(), *epic.values()]:
state = payload["task"].get("state", {})
assert deprecated.isdisjoint(state), payload["id"]
assert "终态优先" not in json.dumps(state, ensure_ascii=False), payload["id"]
assert feat["rt-f10"]["expect"]["result_type"] == "DispatchGoalDriver"
assert feat["rt-f11"]["expect"]["result_type"] == "Awaiting"
assert feat["rt-f12"]["expect"]["result_type"] == "GoalHandoff"
assert feat["rt-f13"]["expect"]["result_type"] == "NeedsHuman"
assert feat["rt-f14"]["expect"]["result_type"] == "HumanCheckpoint"
assert feat["rt-f15"]["expect"]["target"] == "FastForward"
assert feat["rt-f16"]["expect"]["target"] == "Implementation"
assert feat["rt-f16"]["expect"]["must_not_target"] == "GoalPackage"
assert feat["rt-f17"]["expect"]["target"] == "GoalPackage"
assert feat["rt-f18"]["expect"]["target"] == "FastForward"
assert epic["rt-p09"]["expect"]["result_type"] == "DispatchGoalDriver"
assert epic["rt-p11"]["expect"]["result_type"] == "Awaiting"
assert epic["rt-p12"]["expect"]["result_type"] == "Completed"
assert epic["rt-p13"]["expect"]["result_type"] == "GoalHandoff"
assert epic["rt-p14"]["expect"]["result_type"] == "NeedsHuman"
def test_frozen_cs_router_fixtures_keep_recorded_outcomes() -> None:
fixtures = _routing_fixture_states("cs-routing-001")
assert set(fixtures) == {f"rt-c{i:02d}" for i in range(1, 18)}
assert fixtures["rt-c01"]["expect"]["result_type"] == "RoutedTo"
assert fixtures["rt-c01"]["expect"]["target"] == "cs-issue"
assert fixtures["rt-c02"]["expect"]["result_type"] == "Completed"
assert fixtures["rt-c03"]["expect"]["result_type"] == "Completed"
assert fixtures["rt-c04"]["expect"]["result_type"] == "NeedsHuman"
for fixture_id, forbidden in (
("rt-c05", "cs-goal"),
("rt-c06", "cs-refactor"),
("rt-c08", "cs-keep"),
):
assert fixtures[fixture_id]["expect"]["must_not_target"] == forbidden
assert fixtures["rt-c10"]["expect"]["target"] == "cs-onboard"
assert fixtures["rt-c10"]["task"]["state"]["original_target"] == "cs-issue"
assert fixtures["rt-c11"]["expect"]["target"] == "cs-issue"
assert fixtures["rt-c12"]["expect"]["target"] == "cs-refactor"
assert fixtures["rt-c13"]["expect"]["result_type"] == "HumanCheckpoint"
assert fixtures["rt-c14"]["expect"]["result_type"] == "NeedsHuman"
assert fixtures["rt-c15"]["expect"]["result_type"] == "HumanCheckpoint"
assert fixtures["rt-c16"]["expect"]["result_type"] == "Completed"
assert "issue workflow" in fixtures["rt-c16"]["expect"]["target_any"]
assert fixtures["rt-c17"]["expect"] == {
"result_type": "RoutedTo",
"target": "cs-feedback",
}
for fixture_id in ("rt-c02", "rt-c03", "rt-c04", "rt-c13", "rt-c15"):
assert "must_not_target" not in fixtures[fixture_id]["expect"]
+63 -184
View File
@@ -1,143 +1,73 @@
"""校验 cs-* skill 的 frontmatter `contracts` 对 SKILL.md body 成立。
contracts prompt-as-code 的机器护栏`grep` 锚点保护关键骨架不被删
`not-grep` 锚点禁止危险/退化写法此前 CodeStable 本地没有校验器
contracts 只是声明本测试让它们真正生效
关键只扫 **body**剥掉 frontmatter否则 `not-grep` 会命中 frontmatter
contract 声明行自身造成假阳性
"""
"""CodeStable skills直接语义守卫与 routing fixture 回归。"""
from __future__ import annotations
import json
import re
from pathlib import Path
import pytest
import yaml
ROOT = Path(__file__).resolve().parents[1]
SKILLS = ROOT / "plugins/codestable/skills"
# 工具 skillauthoring/eval不随插件交付纳入 contracts 护栏
# 工具 skillauthoring/eval不随插件交付必须使用标准 frontmatter。
LOCAL_SKILLS = ROOT / ".claude/skills"
FRONTMATTER_RE = re.compile(r"^---\n(.*?)\n---\n(.*)$", re.S)
THIN_SKILL_SAFETY_INVARIANTS = {
"cs": ("只解释和推荐", "不启动流程", "不写文件"),
"cs-code-review": ("只读", "独立 subagent reviewer", "blocking 未解决"),
"cs-epic": ("拆解方案必须经用户确认", "不代替用户做整体验收"),
"cs-feat": ("不得代替用户确认设计", "与声明相称的可核验证据"),
"cs-issue": ("能明确变红的验证", "变红的验证必须变绿"),
"cs-keep": ("没有可追溯证据不写", "先合并"),
"cs-onboard": ("存量文件一律不动", "不复制"),
"cs-refactor": ("行为等价", "先有能自证等价的验证"),
}
# v2 交付 skill 不带 contracts frontmatterowner 决策:交付物不含自测元数据)。
# 硬门槛锚由本文件直接对 SKILL.md 正文断言,保护等价、交付更薄。
SHIPPED_HARD_GATE_ANCHORS = {
"cs-feat": ["不得代替用户确认设计", "与声明相称的可核验证据", "写入 `.codestable/work/"],
"cs-issue": ["能明确变红的验证", "变红的验证必须变绿"],
"cs-refactor": ["行为等价", "先有能自证等价的验证"],
"cs-code-review": ["只读", "blocking 未解决", "最多 2 轮"],
"cs-epic": ["拆解方案必须经用户确认", "不代替用户做整体验收"],
"cs-keep": ["没有可追溯证据不写", "先合并"],
"cs-onboard": ["存量文件一律不动", "不复制"],
THIN_SKILL_FORBIDDEN_TEXT = {
"cs": ("L0-L4",),
"cs-code-review": ("git push", "read all references"),
"cs-epic": ("ConfirmGoalCommitAuthorization", "git push", "read all references"),
"cs-feat": ("git push", "read all references"),
"cs-issue": ("git push", "read all references"),
"cs-refactor": ("git push", "read all references"),
}
def _split_frontmatter(text: str) -> tuple[str | None, str]:
"""返回 (frontmatter, body)。无 frontmatter 时 frontmatter 为 None。"""
match = FRONTMATTER_RE.match(text)
if not match:
return None, text
return match.group(1), match.group(2)
def _read_skill(path: Path) -> tuple[dict[str, object], str]:
text = path.read_text(encoding="utf-8")
assert text.startswith("---\n"), path
_, frontmatter, body = text.split("---\n", 2)
return yaml.safe_load(frontmatter), body
def _skills_with_contracts() -> list[Path]:
found: list[Path] = []
def test_active_skills_do_not_use_legacy_frontmatter_contracts() -> None:
for root in (SKILLS, LOCAL_SKILLS):
for path in sorted(root.glob("*/SKILL.md")):
frontmatter, _ = _split_frontmatter(path.read_text(encoding="utf-8"))
if frontmatter and "contracts:" in frontmatter:
found.append(path)
return found
frontmatter, _ = _read_skill(path)
assert "contracts" not in frontmatter, path
SKILLS_WITH_CONTRACTS = _skills_with_contracts()
def test_thin_skills_keep_explicit_safety_invariants() -> None:
active_skills = {path.parent.name for path in SKILLS.glob("*/SKILL.md")}
assert set(THIN_SKILL_SAFETY_INVARIANTS) == active_skills
for skill_name, invariants in THIN_SKILL_SAFETY_INVARIANTS.items():
_, body = _read_skill(SKILLS / skill_name / "SKILL.md")
for invariant in invariants:
assert invariant in body, f"{skill_name}: missing {invariant!r}"
@pytest.mark.parametrize(
"skill_md", SKILLS_WITH_CONTRACTS, ids=lambda p: p.parent.name
)
def test_frontmatter_contracts_hold_against_body(skill_md: Path) -> None:
frontmatter, body = _split_frontmatter(skill_md.read_text(encoding="utf-8"))
meta = yaml.safe_load(frontmatter)
contracts = meta.get("contracts") or []
failures: list[str] = []
for entry in contracts:
if not isinstance(entry, dict) or (
"grep" not in entry and "not-grep" not in entry
):
failures.append(f"contract 缺 grep/not-grep: {entry!r}")
continue
# literal 子串匹配;只针对 body不含 frontmatter 声明本身。
if "grep" in entry and entry["grep"] not in body:
failures.append(f"grep 锚点缺失: {entry['grep']!r}")
if "not-grep" in entry and entry["not-grep"] in body:
failures.append(f"not-grep 锚点命中: {entry['not-grep']!r}")
assert not failures, (
f"{skill_md.parent.name} contract 违反:\n " + "\n ".join(failures)
)
def test_shipped_skills_keep_hard_gate_anchors() -> None:
"""交付 skill 的硬门槛锚:防止演进/汰换时把硬约束静默削掉。
锚是行为不变量短语不是措辞快照改写措辞时同步更新此清单是
有意识的动作同时锁定外发禁令正文不得出现 git push 指令
"""
shipped = {p.parent.name for p in SKILLS.glob("*/SKILL.md")}
assert shipped == set(SHIPPED_HARD_GATE_ANCHORS) | {"cs"}, (
"交付 skill 清单变化,先更新 SHIPPED_HARD_GATE_ANCHORS"
)
for skill, anchors in SHIPPED_HARD_GATE_ANCHORS.items():
body = (SKILLS / skill / "SKILL.md").read_text(encoding="utf-8")
for anchor in anchors:
assert anchor in body, f"{skill} 缺硬门槛锚: {anchor!r}"
assert "git push" not in body, f"{skill} 出现外发指令字样"
def test_shipped_skills_carry_no_contracts_frontmatter() -> None:
"""owner 决策:交付 skill 不带 contracts frontmatter自测元数据留在仓库测试"""
for path in sorted(SKILLS.glob("*/SKILL.md")):
frontmatter, _ = _split_frontmatter(path.read_text(encoding="utf-8"))
assert frontmatter is None or "contracts:" not in frontmatter, path.parent.name
def test_not_grep_ignores_frontmatter_declaration() -> None:
"""回归护栏:校验器必须扫 body 而非全文件。
build-cs-skill frontmatter not-grep 声明行若校验器错误地扫
全文件会命中声明行自身而误报此测试锁死"只扫 body"的语义
"""
text = (LOCAL_SKILLS / "build-cs-skill" / "SKILL.md").read_text(encoding="utf-8")
frontmatter, body = _split_frontmatter(text)
assert frontmatter is not None
assert "not-grep:" in frontmatter # 声明确实在 frontmatter
meta = yaml.safe_load(frontmatter)
not_greps = [e["not-grep"] for e in meta.get("contracts", []) if "not-grep" in e]
assert not_greps, "样本 skill 需至少一条 not-grep 声明"
for phrase in not_greps:
assert phrase not in body # body 干净——not-grep 应通过
def test_thin_harness_skills_stay_free_of_v1_state_machines() -> None:
"""v2 契约:交付 skill 不得回退出现 Haskell 状态机或 v1 runtime 词汇。"""
for path in sorted(SKILLS.glob("*/SKILL.md")):
body = path.read_text(encoding="utf-8")
assert "```haskell" not in body, path.parent.name
assert "restoreFeatureStage" not in body, path.parent.name
assert "goalRunState" not in body, path.parent.name
for skill_name, forbidden_texts in THIN_SKILL_FORBIDDEN_TEXT.items():
_, body = _read_skill(SKILLS / skill_name / "SKILL.md")
for forbidden in forbidden_texts:
assert forbidden not in body, f"{skill_name}: forbidden {forbidden!r}"
def test_build_cs_skill_requires_semantic_and_host_safe_validation() -> None:
build_root = LOCAL_SKILLS / "build-cs-skill"
build = (build_root / "SKILL.md").read_text(encoding="utf-8")
openai = yaml.safe_load(
(build_root / "agents/openai.yaml").read_text(encoding="utf-8")
)
spec = (build_root / "references/cs-skill-spec-standard.md").read_text(
encoding="utf-8"
)
@@ -145,6 +75,9 @@ def test_build_cs_skill_requires_semantic_and_host_safe_validation() -> None:
encoding="utf-8"
)
for path in build_root.rglob("*.md"):
assert len(path.read_text(encoding="utf-8").splitlines()) <= 300, path
assert "$build-cs-skill" in openai["interface"]["default_prompt"]
assert "thin harness" in build
assert "thick context" in build
assert "data SkillShape" in build
@@ -153,12 +86,30 @@ def test_build_cs_skill_requires_semantic_and_host_safe_validation() -> None:
assert "ToolBackedWorkflow" in build
assert "data RulePlacement" in build
assert "placeRule :: Rule -> RulePlacement" in build
assert "not (independentSkillNeeded kind source) = NoActiveSkill" in build
assert "CompatibilityShim -> ShimSkill" in build
assert "ContextPlan" in build
assert "buildContextPlan" in build
assert "Haskell Contract Gate" in build
assert "CompatibilityShim -> ShimRoute" in build
assert "lifecycleDriven source -> LifecycleProtocol" in build
assert "algorithmic source -> AlgorithmProtocol" in build
placement = build[build.index("placeRule r"):build.index("buildContextPlan ::")]
placement_guards = (
"mechanizable r",
"nonMechanizableSafety r",
"projectSpecific r",
"stageSpecific r",
"everyInvocationNeeds r",
"otherwise",
)
positions = [placement.index(guard) for guard in placement_guards]
assert positions == sorted(positions)
assert "data RulePlacement" in spec
assert "NoActiveSkill" in spec
assert "ShimSkill" in spec
assert "ReferenceSkill" in spec
assert "placeRule :: Rule ->" not in spec
assert "Responsibility Contract" in spec
assert "Context Contract" in spec
assert "Collaboration Contract" in spec
@@ -179,76 +130,4 @@ def test_build_cs_skill_requires_semantic_and_host_safe_validation() -> None:
assert "target the canonical main entry without selecting its internal stage/lane" in gates
assert "process start identity" in gates
assert "CI or a quiet disposable host" in gates
def _routing_fixture_states(experiment: str) -> dict[str, dict[str, object]]:
fixtures = ROOT / "experiments" / experiment / "fixtures/routing"
result: dict[str, dict[str, object]] = {}
for path in sorted(fixtures.glob("*.json")):
payload = json.loads(path.read_text(encoding="utf-8"))
result[payload["id"]] = payload
return result
def test_goal_routing_fixtures_use_current_state_schema() -> None:
feat = _routing_fixture_states("cs-feat-routing-001")
epic = _routing_fixture_states("cs-epic-routing-001")
deprecated = {"reviewStatus", "hasGoalPackage", "codeStatus", "qaStatus", "acceptanceStatus"}
for payload in [*feat.values(), *epic.values()]:
state = payload["task"].get("state", {})
assert deprecated.isdisjoint(state), payload["id"]
assert "终态优先" not in json.dumps(state, ensure_ascii=False), payload["id"]
assert feat["rt-f10"]["expect"]["result_type"] == "DispatchGoalDriver"
assert feat["rt-f11"]["expect"]["result_type"] == "Awaiting"
assert feat["rt-f12"]["expect"]["result_type"] == "GoalHandoff"
assert feat["rt-f13"]["expect"]["result_type"] == "NeedsHuman"
assert feat["rt-f14"]["expect"]["result_type"] == "HumanCheckpoint"
assert feat["rt-f15"]["expect"]["target"] == "FastForward"
assert feat["rt-f16"]["expect"]["target"] == "Implementation"
assert feat["rt-f16"]["expect"]["must_not_target"] == "GoalPackage"
assert feat["rt-f17"]["expect"]["target"] == "GoalPackage"
assert feat["rt-f18"]["expect"]["target"] == "FastForward"
assert epic["rt-p09"]["expect"]["result_type"] == "DispatchGoalDriver"
assert epic["rt-p11"]["expect"]["result_type"] == "Awaiting"
assert epic["rt-p12"]["expect"]["result_type"] == "Completed"
assert epic["rt-p13"]["expect"]["result_type"] == "GoalHandoff"
assert epic["rt-p14"]["expect"]["result_type"] == "NeedsHuman"
def test_cs_router_fixtures_cover_modes_conflicts_and_recovery() -> None:
fixtures = _routing_fixture_states("cs-routing-001")
assert set(fixtures) == {f"rt-c{i:02d}" for i in range(1, 18)}
assert fixtures["rt-c01"]["expect"]["result_type"] == "RoutedTo"
assert fixtures["rt-c01"]["expect"]["target"] == "cs-issue"
assert fixtures["rt-c02"]["expect"]["result_type"] == "Completed"
assert fixtures["rt-c03"]["expect"]["result_type"] == "Completed"
assert fixtures["rt-c04"]["expect"]["result_type"] == "NeedsHuman"
for fixture_id, forbidden in (
("rt-c05", "cs-goal"),
("rt-c06", "cs-refactor"),
("rt-c08", "cs-keep"),
):
assert fixtures[fixture_id]["expect"]["must_not_target"] == forbidden
assert fixtures["rt-c10"]["expect"]["target"] == "cs-onboard"
assert fixtures["rt-c10"]["task"]["state"]["original_target"] == "cs-issue"
assert fixtures["rt-c11"]["expect"]["target"] == "cs-issue"
assert fixtures["rt-c12"]["expect"]["target"] == "cs-refactor"
assert fixtures["rt-c13"]["expect"]["result_type"] == "HumanCheckpoint"
assert fixtures["rt-c14"]["expect"]["result_type"] == "NeedsHuman"
assert fixtures["rt-c15"]["expect"]["result_type"] == "HumanCheckpoint"
assert fixtures["rt-c16"]["expect"]["result_type"] == "Completed"
assert "issue workflow" in fixtures["rt-c16"]["expect"]["target_any"]
assert fixtures["rt-c17"]["expect"] == {
"result_type": "RoutedTo",
"target": "cs-feedback",
}
# result type 的禁止分支由精确 outcome 断言完成,不能误用只检查 target 的字段。
for fixture_id in ("rt-c02", "rt-c03", "rt-c04", "rt-c13", "rt-c15"):
assert "must_not_target" not in fixtures[fixture_id]["expect"]
assert "read `.codestable/attention.md` just to author" not in build
+93 -18
View File
@@ -15,6 +15,16 @@ PACKAGE_ROOT = ROOT / "plugins/codestable"
RUN_E2E = os.environ.get("CODESTABLE_RUN_SKILLS_CLI_E2E") == "1"
CLI_COMMAND = os.environ.get("CODESTABLE_SKILLS_CLI")
LEGACY_INVENTORY = ROOT / "tests/fixtures/skills-cli/legacy-cs-inventory.json"
V2_SKILLS = {
"cs",
"cs-code-review",
"cs-epic",
"cs-feat",
"cs-issue",
"cs-keep",
"cs-onboard",
"cs-refactor",
}
SKILLS_CLI_1_5_17_PRIORITY_PREFIXES = (
"",
"skills/",
@@ -60,7 +70,7 @@ def package_skill_names(package_root: Path) -> set[str]:
def skill_md_paths(root: Path) -> set[str]:
tracked = subprocess.run(
["git", "ls-files", "-z"],
["git", "ls-files", "-z", "--cached", "--others", "--exclude-standard"],
cwd=root,
check=True,
capture_output=True,
@@ -69,7 +79,7 @@ def skill_md_paths(root: Path) -> set[str]:
return {
path
for path in tracked
if path and Path(path).name.lower() == "skill.md"
if path and Path(path).name.lower() == "skill.md" and (root / path).is_file()
}
@@ -125,12 +135,10 @@ def installed_skill_names(home: Path) -> set[str]:
}
def test_skills_cli_package_root_contains_complete_cs_family() -> None:
def test_skills_cli_package_root_contains_exact_v2_skill_family() -> None:
names = package_skill_names(PACKAGE_ROOT)
assert names
assert all(name == "cs" or name.startswith("cs-") for name in names)
assert {"cs", "cs-onboard", "cs-feat", "cs-issue", "cs-keep"} <= names
assert names == V2_SKILLS
def test_repo_root_update_discovery_misses_plugin_but_package_root_is_complete() -> None:
@@ -148,13 +156,14 @@ def test_repo_root_update_discovery_misses_plugin_but_package_root_is_complete()
assert package_discovery == canonical_paths
def test_update_deletion_detection_preserves_complete_package_siblings() -> None:
def test_v1_package_discovery_identifies_exact_retired_set() -> None:
legacy = json.loads(LEGACY_INVENTORY.read_text(encoding="utf-8"))
legacy_names = set(legacy["skills"])
current_names = package_skill_names(PACKAGE_ROOT)
retired_names = legacy_names - current_names
locked_paths = {
name: f"plugins/codestable/skills/{name}/SKILL.md"
for name in current_names
for name in legacy_names
}
root_discovery = skills_cli_1_5_17_priority_discovery(ROOT)
package_discovery = skills_cli_1_5_17_priority_discovery(ROOT, "plugins/codestable")
@@ -162,18 +171,49 @@ def test_update_deletion_detection_preserves_complete_package_siblings() -> None
deleted_from_root = skills_cli_1_5_17_deleted_skills(locked_paths, root_discovery)
deleted_from_package = skills_cli_1_5_17_deleted_skills(locked_paths, package_discovery)
assert legacy_names <= current_names
assert deleted_from_root == current_names
assert deleted_from_package == set()
assert current_names == V2_SKILLS
assert current_names <= legacy_names
assert len(legacy_names) == 32
assert len(retired_names) == 24
assert deleted_from_root == legacy_names
assert deleted_from_package == retired_names
def write_legacy_package(source: Path) -> set[str]:
legacy = json.loads(LEGACY_INVENTORY.read_text(encoding="utf-8"))
names = set(legacy["skills"])
manifest_dir = source / ".codex-plugin"
manifest_dir.mkdir(parents=True)
(manifest_dir / "plugin.json").write_text(
json.dumps(
{
"name": "codestable",
"version": legacy["source_ref"].removeprefix("v"),
"description": "CodeStable v1 upgrade fixture.",
"skills": "./skills/",
}
),
encoding="utf-8",
)
for name in names:
skill_dir = source / "skills" / name
skill_dir.mkdir(parents=True)
(skill_dir / "SKILL.md").write_text(
f"---\nname: {name}\ndescription: v1 upgrade fixture for {name}.\n---\n\n# {name}\n",
encoding="utf-8",
)
return names
@pytest.mark.skipif(
not RUN_E2E or not CLI_COMMAND,
reason="set CODESTABLE_RUN_SKILLS_CLI_E2E=1 and CODESTABLE_SKILLS_CLI for a real CLI E2E",
)
def test_full_package_reinstall_preserves_sibling_skills(tmp_path: Path) -> None:
def test_v1_full_package_reinstall_retires_removed_and_preserves_siblings(
tmp_path: Path,
) -> None:
source = tmp_path / "source/codestable"
shutil.copytree(PACKAGE_ROOT, source)
legacy_names = write_legacy_package(source)
home = tmp_path / "home"
state = tmp_path / "state"
env = os.environ.copy()
@@ -213,6 +253,14 @@ def test_full_package_reinstall_preserves_sibling_skills(tmp_path: Path) -> None
"-y",
"--copy",
]
retired_names = legacy_names - V2_SKILLS
remove_retired_command = [
*shlex.split(CLI_COMMAND),
"remove",
*sorted(retired_names),
"-g",
"-y",
]
subprocess.run(
unrelated_command,
@@ -226,14 +274,41 @@ def test_full_package_reinstall_preserves_sibling_skills(tmp_path: Path) -> None
assert "third-party-probe" in installed_skill_names(home)
subprocess.run(command, cwd=tmp_path, env=env, check=True, capture_output=True, text=True, timeout=120)
assert legacy_names <= installed_skill_names(home)
shutil.rmtree(source)
shutil.copytree(PACKAGE_ROOT, source)
subprocess.run(
remove_retired_command,
cwd=tmp_path,
env=env,
check=True,
capture_output=True,
text=True,
timeout=120,
)
assert installed_skill_names(home) == V2_SKILLS | {"third-party-probe"}
subprocess.run(command, cwd=tmp_path, env=env, check=True, capture_output=True, text=True, timeout=120)
expected = package_skill_names(source)
before = installed_skill_names(home)
assert expected <= before
after_upgrade = installed_skill_names(home)
assert after_upgrade == expected | {"third-party-probe"}
assert after_upgrade.isdisjoint(legacy_names - expected)
installed_cs = home / ".agents/skills/cs/SKILL.md"
assert installed_cs.read_text(encoding="utf-8") == (
source / "skills/cs/SKILL.md"
).read_text(encoding="utf-8")
skill_md = source / "skills/cs/SKILL.md"
skill_md.write_text(skill_md.read_text(encoding="utf-8") + "\n", encoding="utf-8")
skill_md.write_text(
skill_md.read_text(encoding="utf-8") + "\n# e2e-update-marker\n",
encoding="utf-8",
)
subprocess.run(command, cwd=tmp_path, env=env, check=True, capture_output=True, text=True, timeout=120)
after = installed_skill_names(home)
assert expected <= after
assert "third-party-probe" in after
assert after == expected | {"third-party-probe"}
assert installed_cs.read_text(encoding="utf-8") == skill_md.read_text(
encoding="utf-8"
)
+71
View File
@@ -0,0 +1,71 @@
"""CodeStable v2 的安装单元、项目骨架与历史兼容边界。"""
from __future__ import annotations
from pathlib import Path
import yaml
ROOT = Path(__file__).resolve().parents[1]
def _frontmatter(path: Path) -> dict[str, object]:
text = path.read_text(encoding="utf-8")
assert text.startswith("---\n"), path
return yaml.safe_load(text.split("---\n", 2)[1])
def test_v2_runtime_ownership_supersedes_v1_distribution() -> None:
adr1 = ROOT / "docs/adr/001-skill-global-tool-runtime.md"
adr2 = ROOT / "docs/adr/002-codestable-does-not-own-worktree-strategy.md"
adr4 = ROOT / "docs/adr/004-project-knowledge-not-runtime-distribution.md"
agents = (ROOT / "AGENTS.md").read_text(encoding="utf-8")
claude = (ROOT / "CLAUDE.md").read_text(encoding="utf-8")
assert _frontmatter(adr1)["status"] == "Superseded"
assert adr4.is_file()
assert _frontmatter(adr4)["status"] == "Accepted"
assert "supersedes: [\"001\"]" in adr4.read_text(encoding="utf-8")
assert "runtime refresh" not in adr2.read_text(encoding="utf-8")
for entry_file in (agents, claude):
assert "plugins/codestable/skills/cs-onboard/references/" not in entry_file
assert "<cs-onboard skill 目录>/tools/" not in entry_file
assert "codestable-runtime-sync.py --check --json" not in entry_file
assert "owning skill" in entry_file
assert "attention.md`、`lessons/`、`work/" in entry_file
def test_active_adrs_do_not_enforce_deleted_v1_tests_or_assets() -> None:
active_adrs = []
for path in sorted((ROOT / "docs/adr").glob("*.md")):
if _frontmatter(path)["status"] == "Accepted":
active_adrs.append(path.read_text(encoding="utf-8"))
active = "\n".join(active_adrs)
for stale in (
"tests/test_codestable_doctor.py",
"tests/test_codestable_workflow_next.py",
"tests/test_skill_entry_simplification.py",
"tests/test_cs_skill_bootstrap.py",
"plugins/codestable/skills/cs-feedback/",
"plugins/codestable/skills/cs-onboard/tools/",
"plugins/codestable/skills/cs-onboard/references/",
):
assert stale not in active
def test_v1_feedback_promoter_is_explicitly_legacy_only() -> None:
promoter = (
ROOT
/ ".claude/skills/eval-cs-skill/scripts/promote_feedback_fixture.py"
).read_text(encoding="utf-8")
eval_skill = (ROOT / ".claude/skills/eval-cs-skill/SKILL.md").read_text(
encoding="utf-8"
)
assert "LEGACY_ONLY = True" in promoter
assert "not a CodeStable v2 production-feedback entry" in promoter
assert "仅用于显式导入冻结的 v1" in eval_skill
assert "新反馈如何进入 regression 不在当前协议中定义" in eval_skill
+148
View File
@@ -0,0 +1,148 @@
"""公开文档与 authoring 规范必须描述 v2而不是已退役的 v1 runtime。"""
from __future__ import annotations
import json
import re
import shlex
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
V2_SKILLS = {
"cs",
"cs-code-review",
"cs-epic",
"cs-feat",
"cs-issue",
"cs-keep",
"cs-onboard",
"cs-refactor",
}
PUBLIC_DOCS = (
"README.md",
"README.en.md",
"WORKFLOW.md",
"WORKFLOW.en.md",
"SKILL_CATALOG.md",
"SKILL_CATALOG.en.md",
)
AUTHORING_DOCS = (
".claude/skills/build-cs-skill/SKILL.md",
".claude/skills/build-cs-skill/references/cs-skill-spec-standard.md",
".claude/skills/build-cs-skill/references/cs-skill-quality-gates.md",
".claude/skills/build-cs-skill/references/cs-skill-fixture-patterns.md",
".claude/skills/eval-cs-skill/SKILL.md",
".claude/skills/eval-cs-skill/references/author/protocol.md",
".claude/skills/eval-cs-skill/references/eval/protocol.md",
".claude/skills/eval-cs-skill/references/release/protocol.md",
)
def _read(relative: str) -> str:
return (ROOT / relative).read_text(encoding="utf-8")
def _skill_table(text: str, start: str, end: str) -> set[str]:
section = text.split(start, 1)[1].split(end, 1)[0]
return set(re.findall(r"^\|[^|]+\|\s*`(cs(?:-[a-z0-9]+)*)`\s*\|", section, re.M))
def _skills_cli_upgrade_block(text: str) -> str:
start = text.index("npx skills@latest remove")
return text[start:text.index("```", start)]
def _upgrade_remove_tokens(text: str) -> list[str]:
block = _skills_cli_upgrade_block(text).replace("\\\n", " ")
command = next(
line.strip()
for line in block.splitlines()
if line.strip().startswith("npx skills@latest remove")
)
return shlex.split(command)
def test_public_docs_present_the_exact_v2_skill_family() -> None:
zh_catalog = _read("SKILL_CATALOG.md")
en_catalog = _read("SKILL_CATALOG.en.md")
assert _skill_table(zh_catalog, "## 当前入口", "## v1.0.4") == V2_SKILLS
assert _skill_table(en_catalog, "## Current Entries", "## Retired") == V2_SKILLS
assert "8 个 skill" in _read("README.md")
assert "8 skills" in _read("README.en.md")
assert "cs--skills-8" in _read("README.md")
assert "cs--skills-8" in _read("README.en.md")
assert "已退役,不随 v2 交付" in zh_catalog
assert "retired and not shipped in v2" in en_catalog
def test_skills_cli_major_upgrade_removes_exactly_the_retired_v1_names() -> None:
legacy = json.loads(
_read("tests/fixtures/skills-cli/legacy-cs-inventory.json")
)
retired = set(legacy["skills"]) - V2_SKILLS
assert len(retired) == 24
zh = _read("README.md")
en = _read("README.en.md")
for readme in (zh, en):
block = _skills_cli_upgrade_block(readme)
tokens = _upgrade_remove_tokens(readme)
assert block.index("skills@latest remove") < block.index("skills@latest add")
assert tokens[:3] == ["npx", "skills@latest", "remove"]
assert tokens[-2:] == ["-g", "-y"]
assert len(tokens[3:-2]) == len(retired)
assert set(tokens[3:-2]) == retired
assert "--all" not in tokens
assert "--skill=*" not in tokens
assert "-s=*" not in tokens
for option in ("--skill", "-s"):
if option in tokens:
assert tokens[tokens.index(option) + 1] != "*"
assert "按名称删除,不校验安装来源" in zh
assert "name-based and does not verify the installation source" in en
def test_active_docs_do_not_publish_v1_runtime_as_current_contract() -> None:
active = "\n".join(_read(path) for path in PUBLIC_DOCS + AUTHORING_DOCS)
for stale in (
"refresh-runtime",
"cs-onboard/tools",
".codestable/reference/",
"长期兼容入口",
"Long-Term Compatibility Entries",
"long-term compatibility entries",
):
assert stale not in active
def test_eval_autonomy_example_targets_a_runnable_experiment() -> None:
autonomy = _read(
".claude/skills/eval-cs-skill/references/autonomy/protocol.md"
)
assert "experiments/cs-code-review-001" in autonomy
assert "experiments/cs-audit-001" not in autonomy
def test_authoring_docs_assign_context_and_helpers_to_real_owners() -> None:
build = _read(".claude/skills/build-cs-skill/SKILL.md")
fixtures = _read(
".claude/skills/build-cs-skill/references/cs-skill-fixture-patterns.md"
)
release = _read(".claude/skills/eval-cs-skill/references/release/protocol.md")
assert "belong to the owning skill's" in build
assert "`references/` and `scripts/`" in build
assert "do not require sibling skill files" in build
assert "retired v1 CodeStable name also selects `NoActiveSkill`" in build
assert "retired-v1-entry-remains-absent" in fixtures
assert "tests/test_skills_cli_distribution.py" in release