mirror of
https://github.com/codestable/CodeStable.git
synced 2026-09-19 09:03:09 +08:00
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:
@@ -3,7 +3,7 @@
|
||||
"plugins": [
|
||||
{
|
||||
"name": "codestable",
|
||||
"version": "1.0.4",
|
||||
"version": "2.0.0",
|
||||
"description": "CodeStable AI coding workflow skills.",
|
||||
"source": {
|
||||
"source": "local",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"plugins": [
|
||||
{
|
||||
"name": "codestable",
|
||||
"version": "1.0.4",
|
||||
"version": "2.0.0",
|
||||
"description": "CodeStable AI coding workflow skills.",
|
||||
"source": "./plugins/codestable"
|
||||
}
|
||||
|
||||
@@ -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 case;resume/terminal 各有正反例 |
|
||||
| `ToolBackedWorkflow` | 真实 runtime conformance;unknown/schema mismatch fail closed |
|
||||
| `ShimSkill` | canonical target 与 legacy preset passthrough 各一项断言 |
|
||||
| `ShimSkill` | 仅在新发布契约明确交付 alias 时验证 canonical target;v2 退役入口选择 `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
|
||||
|
||||
|
||||
@@ -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/git(cs-code-review haiku bare 0.31 → 补上下文 0.92)。
|
||||
1. **复现被测 skill 的实际 context contract**:fixture 提供任务/diff,并按声明补可选 attention、相关 lessons、项目文档或 ADR;不注入集中式 runtime。缺失必要输入会让模型正确拒绝,形成假 gap(历史 cs-code-review campaign:haiku 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/API、design/plan 可只从需求推导。
|
||||
3. **fixture 必须内嵌 subject matter**:转换/文档型任务需要被操作的对象。v1 `cs-docs` 历史 campaign 中,给「写配置文档」却不给配置时模型会正确要材料而非捏造(sonnet 0.75)。review 需要 diff,文档任务需要 code/config/API,design/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 conformance;package 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 调用。
|
||||
|
||||
@@ -4,18 +4,19 @@
|
||||
|
||||
- 默认用中文写面向人的回复、报告和文档;代码、命令、路径、协议字段、YAML/JSON key 保持原格式。
|
||||
- 先读仓库事实和已存在的设计/ADR,再改规则或流程;不要只凭记忆改 skill 契约。
|
||||
- 增加或更新 skill 时,同步检查相关 skill、SKILL_CATALOG、WORKFLOW、README、测试和 ADR 中的表述。
|
||||
- 增加或更新 skill 时,同步检查相关 skill、README/reference、测试和 ADR 中的表述。
|
||||
- 保持规则言简意赅,优先写可执行约束,不写口号。
|
||||
|
||||
## CodeStable Skill 边界(v2:thin 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`。
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 边界(v2:thin harness, thick context)
|
||||
## Skill 边界
|
||||
|
||||
- 交付 skill 共 8 个,位于 `plugins/codestable/skills/`;每个 SKILL.md 是薄责任契约(约 30–60 行正文),不写流程状态机、不写 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
@@ -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.
|
||||
|
||||
@@ -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-07,7 个主入口 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
@@ -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
@@ -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 / work);v1 存量无损保留 |
|
||||
| `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-升级边界)。
|
||||
|
||||
+43
-19
@@ -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
@@ -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 runtime;worktree、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)。
|
||||
|
||||
@@ -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,而不是
|
||||
另一个嵌入式默认策略。
|
||||
|
||||
@@ -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 仍可显式导入历史 experiment;v2 不承诺 production feedback promotion。
|
||||
- eval-cs-skill 自身可被同一闭环评测优化(自指)。
|
||||
- 真实多模型运行需 API/CLI 鉴权并产生成本,受 `--dry-run` + `budget_usd` 护栏约束。
|
||||
- **评测效度是头等风险**(首轮真实 campaign 教训):必须复现 skill 的设计运行环境(`inject_context` 补 onboard 上下文)、用语义 oracle(`recall_judge`)判散文 answer、fixture 内嵌被操作的 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.md);dod_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,6 +1,6 @@
|
||||
{
|
||||
"name": "codestable",
|
||||
"version": "1.0.4",
|
||||
"version": "2.0.0",
|
||||
"description": "CodeStable AI coding workflow skills.",
|
||||
"skills": "./skills/"
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.0.4
|
||||
2.0.0
|
||||
|
||||
+26
-2
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
@@ -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"
|
||||
# 工具 skill(authoring/eval,不随插件交付)也纳入 contracts 护栏
|
||||
# 工具 skill(authoring/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 frontmatter(owner 决策:交付物不含自测元数据)。
|
||||
# 硬门槛锚由本文件直接对 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
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user