mirror of
https://github.com/codestable/CodeStable.git
synced 2026-09-19 09:03:09 +08:00
bb7c22829f
Add item_progression: parallel as an owner-gated scheduling extension: the main workflow stays the sole orchestrator and cursor writer, workers execute dependency-independent items in host-provided isolated workspaces, and integration is serialized without advancing mainline history until per-item re-verification passes. Ships the parallel execution protocol reference (wavefront scheduling, contextPacket delegation, active_items recovery records with branched state transitions, item-local vs epic-global blockers, capability fallback), mirrors the contract across bilingual WORKFLOW/CATALOG, ADR-005, AGENTS.md and CLAUDE.md, and anchors it in the contract tests. Reviewed by an independent heterogeneous reviewer over three rounds (final verdict: approve; target sha256 a78d424e1d3a). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
878 lines
36 KiB
Python
878 lines
36 KiB
Python
"""公开文档与 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-review",
|
||
"cs-epic",
|
||
"cs-feat",
|
||
"cs-issue",
|
||
"cs-keep",
|
||
"cs-onboard",
|
||
"cs-refactor",
|
||
}
|
||
|
||
# 发布契约显式交付的唯一兼容别名(v1 沿用名 -> v2 新名);不算独立能力。
|
||
SHIM_SKILLS = {"cs-code-review"}
|
||
|
||
CURRENT_CONTRACT_DOCS = (
|
||
"README.md",
|
||
"README.en.md",
|
||
"WORKFLOW.md",
|
||
"WORKFLOW.en.md",
|
||
"SKILL_CATALOG.md",
|
||
"SKILL_CATALOG.en.md",
|
||
)
|
||
|
||
SUPPORTING_PUBLIC_DOCS = (
|
||
"UPGRADE.md",
|
||
"UPGRADE.en.md",
|
||
"ROADMAP.md",
|
||
"ROADMAP.en.md",
|
||
"docs/why-codestable.md",
|
||
"docs/why-codestable.en.md",
|
||
)
|
||
|
||
PUBLIC_DOCS = CURRENT_CONTRACT_DOCS + SUPPORTING_PUBLIC_DOCS
|
||
|
||
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 _contains_contract(text: str, anchor: str) -> bool:
|
||
return "".join(anchor.split()) in "".join(text.split())
|
||
|
||
|
||
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 _ordinary_prose_paragraphs(text: str) -> list[str]:
|
||
without_code = re.sub(r"```.*?```", "", text, flags=re.S)
|
||
paragraphs: list[str] = []
|
||
excluded_prefixes = ("#", "|", "<", ">", "- ", "* ")
|
||
for block in re.split(r"\n\s*\n", without_code):
|
||
lines = [line.strip() for line in block.splitlines() if line.strip()]
|
||
if not lines or any(line.startswith(excluded_prefixes) for line in lines):
|
||
continue
|
||
paragraphs.append(" ".join(lines))
|
||
return paragraphs
|
||
|
||
|
||
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
|
||
|
||
zh_readme = _read("README.md")
|
||
en_readme = _read("README.en.md")
|
||
assert "8 个 skill" in zh_readme
|
||
assert "8 skills" in en_readme
|
||
assert "cs--skills-8" in zh_readme
|
||
assert "cs--skills-8" in en_readme
|
||
for skill in V2_SKILLS:
|
||
assert f"`{skill}`" in zh_readme
|
||
assert f"`{skill}`" in en_readme
|
||
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 - SHIM_SKILLS
|
||
|
||
assert len(retired) == 24
|
||
zh = _read("UPGRADE.md")
|
||
en = _read("UPGRADE.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_readme_is_a_compact_first_evaluator_entry() -> None:
|
||
zh = _read("README.md")
|
||
en = _read("README.en.md")
|
||
headings = {
|
||
"README.md": (
|
||
"## 30 秒运行模型",
|
||
"## 5 分钟开始",
|
||
"## 三个核心原则",
|
||
"## 项目记忆",
|
||
"## 适用边界",
|
||
"## 深入文档",
|
||
),
|
||
"README.en.md": (
|
||
"## 30-Second Model",
|
||
"## Start in 5 Minutes",
|
||
"## Three Principles",
|
||
"## Project Memory",
|
||
"## Fit",
|
||
"## Go Deeper",
|
||
),
|
||
}
|
||
for filename, ordered_headings in headings.items():
|
||
text = _read(filename)
|
||
positions = [text.index(heading) for heading in ordered_headings]
|
||
assert positions == sorted(positions)
|
||
assert len(text.splitlines()) <= 300
|
||
assert all(len(paragraph) <= 240 for paragraph in _ordinary_prose_paragraphs(text))
|
||
assert "asset/PromotionalImage.png" not in text
|
||
|
||
for anchor in (
|
||
"轻量 skill 契约",
|
||
"不编排 Agent 团队",
|
||
"不为项目建立第二套文档系统",
|
||
"你只需要告诉 `cs` 想完成什么",
|
||
"还没讨论清楚的内容",
|
||
"直接执行 / 当前会话讨论 / 给出建议",
|
||
"thin harness, thick context",
|
||
"证据先于结论",
|
||
"一个事实,一个 canonical owner",
|
||
"永久 Epic 文档",
|
||
"临时 work 游标",
|
||
"叶子执行器",
|
||
):
|
||
assert anchor in zh
|
||
for anchor in (
|
||
"lightweight skill contracts",
|
||
"does not orchestrate agent teams",
|
||
"does not create a second documentation system",
|
||
"You only need to tell `cs` what you want to accomplish",
|
||
"does not persist unfinished discussions",
|
||
"execute directly / discuss in this session / advise",
|
||
"thin harness, thick context",
|
||
"Evidence before conclusions",
|
||
"One fact, one canonical owner",
|
||
"permanent Epic document",
|
||
"temporary work cursor",
|
||
"leaf executor",
|
||
):
|
||
assert anchor in en
|
||
|
||
for text in (zh, en):
|
||
for anchor in (
|
||
"codex plugin marketplace add codestable/CodeStable",
|
||
"/plugin marketplace add codestable/CodeStable",
|
||
"npx skills@latest add codestable/CodeStable/plugins/codestable",
|
||
"/cs-onboard",
|
||
"/cs",
|
||
"attention.md",
|
||
"lessons/",
|
||
"work/",
|
||
):
|
||
assert anchor in text
|
||
assert "codex plugin marketplace upgrade codestable" not in text
|
||
assert "npx skills@latest remove" not in text
|
||
|
||
assert "[升级指南](./UPGRADE.md#从-v104-升级到-v2)" in zh
|
||
assert "精确删除 24 个退役入口" in zh
|
||
assert "[upgrade guide](./UPGRADE.en.md#upgrade-from-v104-to-v2)" in en
|
||
assert "remove the 24 retired entries" in en
|
||
|
||
assert (
|
||
"作者 [@liuzhengdong](https://github.com/liuzhengdong)、"
|
||
"[@dafang](https://github.com/dafang)、Codex、Claude"
|
||
) in zh
|
||
assert (
|
||
"Authors [@liuzhengdong](https://github.com/liuzhengdong), "
|
||
"[@dafang](https://github.com/dafang), Codex, and Claude"
|
||
) in en
|
||
assert "liuzhengdongfortest" not in zh
|
||
assert "liuzhengdongfortest" not in en
|
||
|
||
|
||
def test_readme_links_to_canonical_deep_docs() -> None:
|
||
links = {
|
||
"README.md": (
|
||
("./WORKFLOW.md", "WORKFLOW.md"),
|
||
("./SKILL_CATALOG.md", "SKILL_CATALOG.md"),
|
||
("./UPGRADE.md", "UPGRADE.md"),
|
||
("./docs/why-codestable.md", "docs/why-codestable.md"),
|
||
("./ROADMAP.md", "ROADMAP.md"),
|
||
("./CHANGELOG.md", "CHANGELOG.md"),
|
||
),
|
||
"README.en.md": (
|
||
("./WORKFLOW.en.md", "WORKFLOW.en.md"),
|
||
("./SKILL_CATALOG.en.md", "SKILL_CATALOG.en.md"),
|
||
("./UPGRADE.en.md", "UPGRADE.en.md"),
|
||
("./docs/why-codestable.en.md", "docs/why-codestable.en.md"),
|
||
("./ROADMAP.en.md", "ROADMAP.en.md"),
|
||
("./CHANGELOG.md", "CHANGELOG.md"),
|
||
),
|
||
}
|
||
for readme, targets in links.items():
|
||
text = _read(readme)
|
||
for link, target in targets:
|
||
assert link in text
|
||
assert (ROOT / target).is_file()
|
||
|
||
|
||
def test_public_document_local_links_resolve() -> None:
|
||
for filename in PUBLIC_DOCS:
|
||
document = ROOT / filename
|
||
for target in re.findall(r"!?\[[^]]*\]\(([^)]+)\)", _read(filename)):
|
||
if "://" in target or target.startswith(("#", "mailto:")):
|
||
continue
|
||
relative = target.split("#", 1)[0]
|
||
if relative:
|
||
assert (document.parent / relative).resolve().exists(), (
|
||
f"{filename} links to missing local target {target}"
|
||
)
|
||
|
||
|
||
def test_upgrade_docs_keep_legacy_assets_without_owning_runtime_policy() -> None:
|
||
zh = _read("UPGRADE.md")
|
||
en = _read("UPGRADE.en.md")
|
||
|
||
assert "升级不会删除项目里的 v1 历史资产" in zh
|
||
assert "检索与写入政策以 [WORKFLOW.md](./WORKFLOW.md#v1-升级边界) 为准" in zh
|
||
assert "does not delete historical v1 project assets" in en
|
||
assert "retrieval and write policy remains owned by [WORKFLOW.en.md](./WORKFLOW.en.md#v1-upgrade-boundary)" in en
|
||
assert ".codestable/reference/" not in zh
|
||
assert ".codestable/reference/" not 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
|
||
|
||
|
||
def test_review_docs_publish_single_level_agent_orchestration() -> None:
|
||
zh_readme = _read("README.md")
|
||
en_readme = _read("README.en.md")
|
||
zh_workflow = _read("WORKFLOW.md")
|
||
en_workflow = _read("WORKFLOW.en.md")
|
||
en_workflow_compact = " ".join(en_workflow.split())
|
||
|
||
assert "叶子执行器" in zh_readme
|
||
assert "leaf executor" in en_readme
|
||
assert "外层主流程创建 reviewer" in zh_workflow
|
||
assert "outer workflow creates the reviewer" in en_workflow
|
||
assert "冻结一个明确的审查目标" in zh_workflow
|
||
assert "当前主流程创建 reviewer 前" in zh_workflow
|
||
assert "当前会话可调用的 subagent 创建与管理能力" in zh_workflow
|
||
assert "本机有界 agent CLI 回退" in zh_workflow
|
||
assert "具体后端与 model 约束属于项目上下文" in zh_workflow
|
||
assert "健康运行中的 reviewer" in zh_workflow
|
||
assert "Awaiting 携带同一可查询 run identity" in zh_workflow
|
||
assert "有 blocking 或未被用户明确接受的 important 时" in zh_workflow
|
||
assert "freeze an explicit review target" in en_workflow_compact
|
||
assert "discovers the subagent creation and management capabilities callable in the current session" in en_workflow_compact
|
||
assert "A bounded local agent CLI is only a fallback" in en_workflow_compact
|
||
assert "Exact backend and model constraints belong to project context" in en_workflow_compact
|
||
assert "When no qualified heterogeneous candidate exists" in en_workflow_compact
|
||
assert "never rely on a default model" in en_workflow_compact
|
||
assert "healthy running reviewer" in en_workflow_compact
|
||
assert "Awaiting carries the same queryable run identity" in en_workflow_compact
|
||
assert "does not consume a review round" in en_workflow_compact
|
||
assert "blocking findings or important findings not explicitly accepted by the user" in en_workflow_compact
|
||
|
||
for anchor in (
|
||
"每个独立审查阶段的首轮",
|
||
"一个独立审查阶段由单一审查目的界定",
|
||
"design review、change review、contract review 与 Epic final acceptance 是不同阶段",
|
||
"只有为本阶段 findings 所作修复的复审",
|
||
"同一 reviewer 的同一 session",
|
||
"完整当前候选与本轮修复增量",
|
||
"`resolved` / `unresolved` / `new findings`",
|
||
"累计最多 3 个有终态报告的轮次",
|
||
"更换 reviewer 不重置计数",
|
||
"独立于实现者,不要求对自身上一轮审查失忆",
|
||
"只有原 run/session 失败或不可恢复、能力不满足、目标、范围、设计或核心路径发生重大变化",
|
||
"reviewer 声明无法继续独立判断",
|
||
"owner 要求第二意见",
|
||
):
|
||
assert _contains_contract(zh_workflow, anchor)
|
||
|
||
for anchor in (
|
||
"Each independent review stage starts with one fresh reviewer",
|
||
"A review stage is defined by one review purpose",
|
||
"Design review, change review, contract review, and Epic final acceptance are separate stages",
|
||
"Only re-review driven by fixes for that stage's findings",
|
||
"the same reviewer's same session",
|
||
"the complete current candidate and the repair delta",
|
||
"classifies prior findings as resolved or unresolved",
|
||
"reports new findings",
|
||
"at most three completed rounds with terminal reports",
|
||
"Replacing a reviewer does not reset that count",
|
||
"independence from the implementer, not amnesia about its own prior review",
|
||
"original run/session fails or cannot be recovered",
|
||
"capability is insufficient",
|
||
"target, scope, design, or core path changes materially",
|
||
"reviewer says it can no longer judge independently",
|
||
"owner requests a second opinion",
|
||
):
|
||
assert anchor in en_workflow_compact
|
||
|
||
assert _contains_contract(zh_workflow, "final acceptance 是独立审查阶段")
|
||
assert _contains_contract(zh_workflow, "另建 fresh reviewer")
|
||
assert _contains_contract(zh_workflow, "不沿用子项、design review 或 contract review 的 lineage")
|
||
assert "Final acceptance is a separate review stage" in en_workflow_compact
|
||
assert "starts a new fresh-reviewer lineage" in en_workflow_compact
|
||
assert "instead of reusing an item, design-review, or contract-review lineage" in en_workflow_compact
|
||
assert _contains_contract(zh_workflow, "契约变化形成新的 contract review 审查阶段")
|
||
assert "contract changes start a separate contract-review stage with a fresh reviewer" in en_workflow_compact
|
||
assert _contains_contract(zh_workflow, "永久文档已批准后的执行中")
|
||
assert "During execution, after the permanent document has been approved" in en_workflow_compact
|
||
|
||
public = "\n".join(_read(path) for path in PUBLIC_DOCS)
|
||
assert "独立 subagent 视角" not in public
|
||
assert "independent subagent perspective" not in public
|
||
|
||
|
||
def test_cs_session_discussion_and_handoff_contract_is_bilingual() -> None:
|
||
zh_workflow = " ".join(_read("WORKFLOW.md").split())
|
||
en_workflow = " ".join(_read("WORKFLOW.en.md").split())
|
||
|
||
for anchor in (
|
||
"明确行动默认优先同轮直转",
|
||
"用户显式要求先讨论",
|
||
"讨论只存在于当前会话",
|
||
"不创建 discussion work 游标",
|
||
"未收敛讨论不跨会话恢复",
|
||
"已有执行授权时同轮移交",
|
||
"不再询问“是否继续”",
|
||
"handoff 不扩大授权",
|
||
"原始问答、未决讨论和候选分支不落盘",
|
||
"三个已确认出口之外",
|
||
):
|
||
assert anchor in zh_workflow
|
||
|
||
for anchor in (
|
||
"Explicit action dispatches in the same turn by default",
|
||
"the user explicitly asks to discuss first",
|
||
"repository facts still cannot identify",
|
||
"would materially change",
|
||
"Discussion exists only in the current session",
|
||
"does not create a discussion work cursor",
|
||
"Unresolved discussion is not recoverable across sessions",
|
||
"existing execution authorization",
|
||
"must not ask whether to continue",
|
||
"The handoff does not expand authorization",
|
||
"the owning skill's review, verification, or owner gates",
|
||
"Raw questions, answers, unresolved discussion, and candidate branches are not persisted",
|
||
"only when it is hard to reverse",
|
||
"the result of a real trade-off",
|
||
"When no canonical home exists, ask the owner to choose one",
|
||
"Outside the three confirmed handoff targets",
|
||
):
|
||
assert anchor in en_workflow
|
||
|
||
zh_readme = _read("README.md")
|
||
en_readme = _read("README.en.md")
|
||
zh_catalog = _read("SKILL_CATALOG.md")
|
||
en_catalog = _read("SKILL_CATALOG.en.md")
|
||
assert "未经明确授权,不会修改代码" in zh_readme
|
||
assert "先讨论的请求在当前会话收敛后同轮移交" in zh_catalog
|
||
assert "It will not change code" in en_readme
|
||
assert "without execution authorization" in en_readme
|
||
assert "Requests to discuss first converge in the current session and hand off in the same turn" in en_catalog
|
||
assert "稳定资产由 owning skill 按 canonical 归宿毕业" in zh_catalog
|
||
assert "stable assets graduate through the owning skill into their canonical homes" in en_catalog
|
||
|
||
|
||
def test_issue_diagnosis_and_authorized_fix_contract_is_bilingual() -> None:
|
||
zh_readme = _read("README.md")
|
||
en_readme = _read("README.en.md")
|
||
zh_catalog = _read("SKILL_CATALOG.md")
|
||
en_catalog = _read("SKILL_CATALOG.en.md")
|
||
zh_workflow = " ".join(_read("WORKFLOW.md").split())
|
||
en_workflow = " ".join(_read("WORKFLOW.en.md").split())
|
||
|
||
for document in (zh_readme, zh_catalog):
|
||
assert "诊断问题;获授权后用红到绿证据修复" in document
|
||
for document in (en_readme, en_catalog):
|
||
assert "Diagnose problems; once repair is authorized, fix with red-to-green evidence" in document
|
||
|
||
for anchor in (
|
||
"只诊断 / 排查",
|
||
"获授权修复 bug / 性能回退 / 行为异常",
|
||
"只要求诊断时保持零产品改动",
|
||
"已证实根因、可证伪假设或证据不足",
|
||
"性能回退或异常变慢",
|
||
"行为等价的主动优化",
|
||
):
|
||
assert anchor in zh_workflow
|
||
|
||
for anchor in (
|
||
"diagnose / investigate",
|
||
"authorized repair of bug / performance regression / broken behavior",
|
||
"A diagnosis-only request leaves zero product changes",
|
||
"confirmed root cause, falsifiable hypothesis, or insufficient evidence",
|
||
"performance regression or anomalous slowdown",
|
||
"proactive optimization under behavioral equivalence",
|
||
):
|
||
assert anchor in en_workflow
|
||
|
||
|
||
def test_minimum_sufficient_assurance_contract_is_bilingual() -> None:
|
||
zh_readme = _read("README.md")
|
||
en_readme = _read("README.en.md")
|
||
for anchor in (
|
||
"需求清楚时",
|
||
"直接开始,并用足够的验证交付结果",
|
||
"遇到具体风险时",
|
||
"只增加与风险对应的确认、测试或 review",
|
||
"不自动启用整套流程",
|
||
):
|
||
assert _contains_contract(zh_readme, anchor)
|
||
for anchor in (
|
||
"When the request is clear",
|
||
"delivers the result with enough verification",
|
||
"When it finds a concrete risk",
|
||
"only the confirmation, tests, or review needed for that risk",
|
||
"does not enable the whole workflow",
|
||
):
|
||
assert _contains_contract(en_readme, anchor)
|
||
|
||
zh_docs = (
|
||
_read("WORKFLOW.md"),
|
||
_read("SKILL_CATALOG.md"),
|
||
_read("docs/why-codestable.md"),
|
||
)
|
||
en_docs = (
|
||
_read("WORKFLOW.en.md"),
|
||
_read("SKILL_CATALOG.en.md"),
|
||
_read("docs/why-codestable.en.md"),
|
||
)
|
||
|
||
for document in zh_docs:
|
||
for anchor in (
|
||
"执行流程 = 最小闭环 + 每个未排除风险所要求的最少保障",
|
||
"任务类型只决定工程方法,实际风险决定保障强度",
|
||
"独立 review 不是默认步骤",
|
||
"一个风险只增加与它直接对应的保障",
|
||
):
|
||
assert _contains_contract(document, anchor)
|
||
|
||
for document in en_docs:
|
||
for anchor in (
|
||
"Execution flow = minimum complete loop + the least assurance required by each unexcluded risk",
|
||
"Task type determines the engineering method; actual risk determines assurance strength",
|
||
"Independent review is not a default step",
|
||
"Each risk adds only the assurance directly required by that risk",
|
||
):
|
||
assert anchor in document
|
||
|
||
public_workflow = _read("WORKFLOW.md") + _read("WORKFLOW.en.md")
|
||
for stale in (
|
||
"默认;仅文案级微小改动可说明后跳过",
|
||
"默认;仅单行级微小修复可说明后跳过",
|
||
"default; documentation-only tiny changes may be skipped with an explanation",
|
||
"default; single-line tiny fixes may be skipped with an explanation",
|
||
):
|
||
assert stale not in public_workflow
|
||
|
||
|
||
def test_workflow_publishes_risk_mapping_and_packet_design_targets_bilingually() -> None:
|
||
zh = " ".join(_read("WORKFLOW.md").split())
|
||
en = " ".join(_read("WORKFLOW.en.md").split())
|
||
|
||
for anchor in (
|
||
"一次静默、有界核对",
|
||
"不能排除时先按风险存在处理",
|
||
"不得以“没有注意到风险”作为降级依据",
|
||
"独立审查 / 审计 -> cs-review",
|
||
"破坏兼容性或改变多消费者依赖的公开契约",
|
||
"改变权限、安全、隐私或其他信任边界",
|
||
"改变持久化数据、schema 或迁移路径",
|
||
"改变并发、顺序或一致性语义",
|
||
"产生不可恢复的代码外副作用",
|
||
"性能回退或性能敏感路径变化",
|
||
"改动影响面广或失败可跨模块传播",
|
||
"流程太重 / 只是小改动 / 文档比代码多",
|
||
"不是无条件跳过安全门槛",
|
||
"连续性需要不是风险门槛",
|
||
"仓库内已有 design 文档版本",
|
||
"task packet 内原样全文 + SHA-256",
|
||
"reviewer 审查的目标就是该文本",
|
||
"最新全文、前后 hash 与修复摘要",
|
||
"实际触发 review 时才要求通过审查门槛",
|
||
):
|
||
assert anchor in zh
|
||
|
||
for anchor in (
|
||
"one silent, bounded check",
|
||
"If a risk still cannot be ruled out after the least-cost targeted check, treat it as present",
|
||
"not noticing a risk is not a valid reason to lower assurance",
|
||
"independent review / audit -> cs-review",
|
||
"breaks compatibility or changes a public contract used by multiple consumers",
|
||
"changes authorization, security, privacy, or another trust boundary",
|
||
"changes persisted data, schema, or a migration path",
|
||
"changes concurrency, ordering, or consistency semantics",
|
||
"creates an irreversible effect outside the codebase",
|
||
"addresses a performance regression or changes a performance-sensitive path",
|
||
"has broad impact or can propagate failure across modules",
|
||
"flow is too heavy / this is only a small change / documentation exceeds the code",
|
||
"not an unconditional bypass of safety gates",
|
||
"Continuity needs are not risk gates",
|
||
"an existing repository design document",
|
||
"verbatim design text + SHA-256 in the task packet",
|
||
"the reviewer reviews that text as the target",
|
||
"latest full text, previous and current hashes, and repair summary",
|
||
"A review gate is required only when review was actually triggered",
|
||
):
|
||
assert anchor in en
|
||
|
||
assert "公开契约、数据、权限、并发或真实方案取舍先经用户确认" not in zh
|
||
assert (
|
||
"Public contracts, data, authorization, concurrency, or real design "
|
||
"tradeoffs require owner confirmation first"
|
||
) not in en
|
||
|
||
|
||
def test_workflow_owns_epic_and_legacy_knowledge_contracts() -> None:
|
||
zh_workflow = " ".join(_read("WORKFLOW.md").split())
|
||
en_workflow = " ".join(_read("WORKFLOW.en.md").split())
|
||
|
||
legacy_dirs = (
|
||
"roadmap/",
|
||
"features/",
|
||
"issues/",
|
||
"refactors/",
|
||
"goals/",
|
||
"compound/",
|
||
"audits/",
|
||
"brainstorms/",
|
||
"feedback/",
|
||
)
|
||
for directory in legacy_dirs:
|
||
assert directory in zh_workflow
|
||
assert directory in en_workflow
|
||
|
||
for anchor in (
|
||
"只读历史知识源",
|
||
"owning task skills",
|
||
"按任务关键词覆盖",
|
||
"其他 skill 只检索自身契约明确点名的历史源",
|
||
"不得继续生成",
|
||
"原地改写",
|
||
"批量迁移",
|
||
"永久 Epic 文档",
|
||
"临时执行游标",
|
||
"按需建立 `.codestable/epics/{slug}.md`",
|
||
"`cs-onboard` 不预建 `.codestable/epics/`",
|
||
"Epic 保留三道 owner gate",
|
||
"最新 owner 已批准的验收标准",
|
||
"owner 最终接受",
|
||
"串行约束,不是每个子项的人工 gate",
|
||
"不得询问“是否继续下一项”",
|
||
"不得把它作为终态返回",
|
||
"`item_progression`",
|
||
"`item_progression: parallel` 只能搭配 `milestone_commit: authorized`",
|
||
"唯一编排者与游标 writer",
|
||
"退化为串行推进",
|
||
"全部合格 worker 创建能力或隔离能力均不可用",
|
||
"`active_items`",
|
||
"不恢复 `cs-goal` 入口",
|
||
):
|
||
assert anchor in zh_workflow
|
||
|
||
for anchor in (
|
||
"read-only historical knowledge sources",
|
||
"cover all nine by task keyword",
|
||
"Other skills retrieve only historical sources explicitly named by their own contracts",
|
||
"No skill may generate",
|
||
"rewrite in place",
|
||
"bulk-migrate",
|
||
"permanent Epic document",
|
||
"temporary execution cursor",
|
||
"create `.codestable/epics/{slug}.md` on demand",
|
||
"`cs-onboard` does not precreate `.codestable/epics/`",
|
||
"An Epic retains three owner gates",
|
||
"latest owner-approved criteria",
|
||
"owner's final acceptance",
|
||
"serialization constraint, not a per-item owner gate",
|
||
"must not ask whether to continue to the next item",
|
||
"return that completion as terminal",
|
||
"`item_progression`",
|
||
"`item_progression: parallel` requires `milestone_commit: authorized`",
|
||
"sole orchestrator and cursor writer",
|
||
"degrades to serial within the session",
|
||
"every qualified worker-creation capability is unavailable",
|
||
"`active_items`",
|
||
"Do not restore the `cs-goal` entry",
|
||
):
|
||
assert anchor in en_workflow
|
||
|
||
for workflow in (zh_workflow, en_workflow):
|
||
assert ".codestable/requirements/" in workflow
|
||
assert ".codestable/attention.md" in workflow
|
||
assert "canonical requirement" in workflow
|
||
|
||
zh_readme = _read("README.md")
|
||
en_readme = _read("README.en.md")
|
||
zh_catalog = _read("SKILL_CATALOG.md")
|
||
en_catalog = _read("SKILL_CATALOG.en.md")
|
||
assert "默认连续策略" not in zh_workflow
|
||
assert "永久 Epic 文档" in zh_catalog
|
||
assert "permanent Epic doc" in en_catalog
|
||
assert "永久 Epic 文档" in zh_readme
|
||
assert "临时 work 游标" in zh_readme
|
||
assert "permanent Epic document" in en_readme
|
||
assert "temporary work cursor" in en_readme
|
||
assert "串行连续推进" in zh_catalog
|
||
assert "serially and continuously" in en_catalog
|
||
assert "`parallel` 策略时并行推进依赖互不阻塞的子项" in zh_catalog
|
||
assert "owner-approved `parallel` policy" in en_catalog
|
||
|
||
|
||
def test_epic_wayfinding_contract_is_bilingual_and_keeps_one_document_owner() -> None:
|
||
zh_docs = (
|
||
_read("README.md"),
|
||
_read("WORKFLOW.md"),
|
||
_read("SKILL_CATALOG.md"),
|
||
_read("docs/why-codestable.md"),
|
||
)
|
||
en_docs = (
|
||
_read("README.en.md"),
|
||
_read("WORKFLOW.en.md"),
|
||
_read("SKILL_CATALOG.en.md"),
|
||
_read("docs/why-codestable.en.md"),
|
||
)
|
||
|
||
for document in zh_docs:
|
||
assert _contains_contract(document, "路线尚不清晰")
|
||
assert _contains_contract(document, "路线清晰、可审查、可执行")
|
||
assert _contains_contract(document, "永久 Epic 文档本身就是路线地图")
|
||
for document in en_docs:
|
||
assert _contains_contract(document, "route is still unclear")
|
||
assert _contains_contract(document, "clear, reviewable, and executable")
|
||
assert _contains_contract(document, "permanent Epic document itself is the route map")
|
||
|
||
zh_workflow = " ".join(zh_docs[1].split())
|
||
en_workflow = " ".join(en_docs[1].split())
|
||
for anchor in (
|
||
"永久 Epic 文档就是唯一路线文档",
|
||
"在 proposed 阶段,永久 Epic 文档本身就是路线地图",
|
||
"frontier 只由依赖已经解决的待决策派生",
|
||
"待决策",
|
||
"尚未明确",
|
||
"不得替 owner 回答 HITL 问题",
|
||
"HITL 不是新的 owner gate",
|
||
"route clear 前不得保留未解决的 HITL 节点",
|
||
"仍有效的 HITL 节点必须由 owner 明确解决或确认移入 `非目标`",
|
||
"涉及外部权限或副作用的 prerequisite 仍须另获对应权限或确认",
|
||
"不新增独立 issue、map 或第三套状态",
|
||
"route clear 不是新的 owner gate",
|
||
"现有 design review",
|
||
"`current_item` 保持 `null`",
|
||
):
|
||
assert anchor in zh_workflow
|
||
for anchor in (
|
||
"The permanent Epic document is the only route document",
|
||
"during proposed, the permanent Epic document itself is the route map",
|
||
"frontier is derived only from pending decisions whose dependencies are resolved",
|
||
"Decisions pending",
|
||
"Still unclear",
|
||
"must not answer a HITL question for the owner",
|
||
"HITL is not a new owner gate",
|
||
"No unresolved HITL node may remain at route clear",
|
||
"A still-valid HITL node must be explicitly resolved by the owner or moved out of scope with the owner's explicit confirmation",
|
||
"a prerequisite involving external authority or side effects still requires separate permission or confirmation",
|
||
"does not add issues, a separate map artifact, or a third state system",
|
||
"Route clear is not a new owner gate",
|
||
"existing design review",
|
||
"`current_item` stays `null`",
|
||
):
|
||
assert anchor in en_workflow
|
||
assert "`Decisions pending`" not in en_workflow
|
||
assert "`Still unclear`" not in en_workflow
|
||
|
||
|
||
def test_shared_language_contract_is_bilingual() -> None:
|
||
zh_docs = (
|
||
_read("README.md"),
|
||
_read("SKILL_CATALOG.md"),
|
||
_read("docs/why-codestable.md"),
|
||
)
|
||
en_docs = (
|
||
_read("README.en.md"),
|
||
_read("SKILL_CATALOG.en.md"),
|
||
_read("docs/why-codestable.en.md"),
|
||
)
|
||
for document in zh_docs:
|
||
assert "共享语言" in document
|
||
for document in en_docs:
|
||
assert "shared language" in document
|
||
|
||
zh_workflow = " ".join(_read("WORKFLOW.md").split())
|
||
en_workflow = " ".join(_read("WORKFLOW.en.md").split())
|
||
for anchor in (
|
||
"共享语言是条件式设计纪律",
|
||
"普通改动沿用已有单义术语时不增加产物",
|
||
"Feature 要求局部语义清晰",
|
||
"Epic 要求概念体系清晰",
|
||
"仓库事实由 agent 核实",
|
||
"产品含义与概念边界进入 HITL",
|
||
"不自动创建 `CONTEXT.md`",
|
||
"不是新的 owner gate",
|
||
):
|
||
assert anchor in zh_workflow
|
||
for anchor in (
|
||
"Shared language is a conditional design discipline",
|
||
"Ordinary changes that reuse existing unambiguous terms add no artifact",
|
||
"A Feature requires local semantic clarity",
|
||
"An Epic requires conceptual-system clarity",
|
||
"The agent verifies repository facts",
|
||
"product meaning and concept boundaries enter HITL",
|
||
"does not automatically create `CONTEXT.md`",
|
||
"is not a new owner gate",
|
||
):
|
||
assert anchor in en_workflow
|
||
|
||
|
||
def test_public_workflows_stay_within_document_size_limit() -> None:
|
||
assert len(_read("WORKFLOW.md").splitlines()) <= 300
|
||
assert len(_read("WORKFLOW.en.md").splitlines()) <= 300
|
||
|
||
|
||
def test_project_learning_lifecycle_is_bilingual_and_low_interruption() -> None:
|
||
zh_workflow = _read("WORKFLOW.md")
|
||
en_workflow = _read("WORKFLOW.en.md")
|
||
for anchor in (
|
||
"任务内静默观察",
|
||
"经验命中:{path}({status});核验:{fact};影响:{plan_or_check}",
|
||
"一次有界、最低成本的定向核实",
|
||
"明确排除一个具体且合理的错误路径",
|
||
"本次通过的验收证据",
|
||
"observed / validated / retired",
|
||
"普通任务最多展示一条",
|
||
"Epic 子项不新增暂停",
|
||
"机械 guard 优先",
|
||
"新 lesson 仍需显式授权",
|
||
"不保存 transcript",
|
||
):
|
||
assert _contains_contract(zh_workflow, anchor)
|
||
for anchor in (
|
||
"observes silently during the task",
|
||
"lesson hit: {path} ({status}); check: {fact}; impact: {plan_or_check}",
|
||
"one bounded, lowest-cost targeted check",
|
||
"explicitly rules out a concrete, plausible wrong path",
|
||
"the task's passing acceptance evidence",
|
||
"observed / validated / retired",
|
||
"at most one candidate",
|
||
"Epic items add no pause",
|
||
"mechanical guards first",
|
||
"New lessons still require explicit authorization",
|
||
"does not save transcripts",
|
||
):
|
||
assert _contains_contract(en_workflow, anchor)
|
||
|
||
public_pairs = (
|
||
("README.md", "README.en.md", "边做边识别晶化时刻", "recognizes crystallization moments while working"),
|
||
("SKILL_CATALOG.md", "SKILL_CATALOG.en.md", "observed / validated / retired", "observed / validated / retired"),
|
||
("docs/why-codestable.md", "docs/why-codestable.en.md", "经验不是活动日志", "Experience is not an activity log"),
|
||
(
|
||
"docs/why-codestable.md",
|
||
"docs/why-codestable.en.md",
|
||
"排除一个具体且合理的错误路径",
|
||
"rules out a concrete, plausible wrong path",
|
||
),
|
||
)
|
||
for zh_path, en_path, zh_anchor, en_anchor in public_pairs:
|
||
assert _contains_contract(_read(zh_path), zh_anchor)
|
||
assert _contains_contract(_read(en_path), en_anchor)
|
||
|
||
assert not _contains_contract(zh_workflow, "把可复用经验写成 lesson")
|
||
assert not _contains_contract(en_workflow, "reusable experience into lessons")
|
||
assert "| `cs-keep` | 管理有证据的项目事实、lesson 生命周期与 canonical 归宿 |" in _read("README.md")
|
||
assert (
|
||
"| `cs-keep` | Manage evidence-backed project facts, lesson lifecycle, and canonical homes |"
|
||
in _read("README.en.md")
|
||
)
|
||
assert _contains_contract(_read("docs/why-codestable.md"), "尚未被更强 owner 承接的经验暂存于 lessons")
|
||
assert _contains_contract(
|
||
_read("docs/why-codestable.en.md"),
|
||
"experience without a stronger owner is staged in lessons",
|
||
)
|