@pzy560117/opentest 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +51 -0
  3. package/assets/manifest.json +30 -0
  4. package/assets/skills/opentest/SKILL.md +60 -0
  5. package/assets/skills/opentest/references/acceptance-evidence.md +22 -0
  6. package/assets/skills/opentest/references/codex-harness-coverage-heuristics.md +64 -0
  7. package/assets/skills/opentest/references/command-routing.md +9 -0
  8. package/assets/skills/opentest/references/lifecycle.md +16 -0
  9. package/assets/skills/opentest/references/matrix-format.md +16 -0
  10. package/assets/skills/opentest/references/quality-gate.md +21 -0
  11. package/assets/skills/opentest/scripts/opentest-detect.sh +56 -0
  12. package/assets/skills/opentest/scripts/opentest-guard.sh +189 -0
  13. package/assets/skills/opentest/scripts/opentest-state.sh +277 -0
  14. package/assets/skills/opentest/templates/acceptance-template.md +23 -0
  15. package/assets/skills/opentest/templates/archive-layout.md +14 -0
  16. package/assets/skills/opentest/templates/matrix-template.md +5 -0
  17. package/assets/skills/opentest/templates/plan-template.md +18 -0
  18. package/assets/skills/opentest/templates/report-template.md +23 -0
  19. package/assets/skills/opentest-accept/SKILL.md +23 -0
  20. package/assets/skills/opentest-archive/SKILL.md +8 -0
  21. package/assets/skills/opentest-author/SKILL.md +23 -0
  22. package/assets/skills/opentest-heal/SKILL.md +8 -0
  23. package/assets/skills/opentest-plan/SKILL.md +23 -0
  24. package/assets/skills/opentest-run/SKILL.md +25 -0
  25. package/assets/skills/opentest-verify/SKILL.md +16 -0
  26. package/bin/opentest.js +122 -0
  27. package/package.json +36 -0
  28. package/scripts/prepublish-check.js +58 -0
  29. package/scripts/smoke-test.js +64 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 benym
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # OpenTest
2
+
3
+ OpenTest is a Codex skill bundle for planning, running, verifying, and archiving quality evidence for code changes.
4
+
5
+ It provides a small lifecycle:
6
+
7
+ ```text
8
+ plan -> author -> run -> accept -> verify -> archive
9
+ ```
10
+
11
+ `heal` is a recovery phase for stale test assets and must not hide product behavior failures.
12
+
13
+ ## Install
14
+
15
+ Install into the current project:
16
+
17
+ ```bash
18
+ npx @pzy560117/opentest install
19
+ ```
20
+
21
+ Install globally for Codex:
22
+
23
+ ```bash
24
+ npx @pzy560117/opentest install --global
25
+ ```
26
+
27
+ Overwrite an existing OpenTest install:
28
+
29
+ ```bash
30
+ npx @pzy560117/opentest install --force
31
+ ```
32
+
33
+ Restart Codex or open a new session after installation so the new skills are loaded.
34
+
35
+ ## Included Skills
36
+
37
+ - `opentest`
38
+ - `opentest-plan`
39
+ - `opentest-author`
40
+ - `opentest-run`
41
+ - `opentest-accept`
42
+ - `opentest-verify`
43
+ - `opentest-heal`
44
+ - `opentest-archive`
45
+
46
+ ## Package Contents
47
+
48
+ - `assets/skills/`: Codex skill files
49
+ - `assets/manifest.json`: published asset manifest
50
+ - `bin/opentest.js`: installer CLI
51
+
@@ -0,0 +1,30 @@
1
+ {
2
+ "version": "0.1.0",
3
+ "skills": [
4
+ "opentest/SKILL.md",
5
+ "opentest/scripts/opentest-state.sh",
6
+ "opentest/scripts/opentest-detect.sh",
7
+ "opentest/scripts/opentest-guard.sh",
8
+ "opentest/references/lifecycle.md",
9
+ "opentest/references/matrix-format.md",
10
+ "opentest/references/quality-gate.md",
11
+ "opentest/references/acceptance-evidence.md",
12
+ "opentest/references/command-routing.md",
13
+ "opentest/references/codex-harness-coverage-heuristics.md",
14
+ "opentest/templates/plan-template.md",
15
+ "opentest/templates/matrix-template.md",
16
+ "opentest/templates/acceptance-template.md",
17
+ "opentest/templates/report-template.md",
18
+ "opentest/templates/archive-layout.md",
19
+ "opentest-plan/SKILL.md",
20
+ "opentest-author/SKILL.md",
21
+ "opentest-run/SKILL.md",
22
+ "opentest-accept/SKILL.md",
23
+ "opentest-verify/SKILL.md",
24
+ "opentest-heal/SKILL.md",
25
+ "opentest-archive/SKILL.md"
26
+ ],
27
+ "platforms": [
28
+ { "id": "codex", "skillsDir": ".codex/skills" }
29
+ ]
30
+ }
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: opentest
3
+ description: "OpenTest 路由入口。自动检测 .opentest.yaml、初始化测试生命周期状态,并分发到 plan/author/run/accept/verify/heal/archive 阶段。"
4
+ ---
5
+
6
+ # OpenTest 路由入口
7
+
8
+ ## 产出语言契约
9
+
10
+ - 面向用户输出和生成文档默认使用中文。
11
+ - 命令、路径、frontmatter key、代码标识符和 API 名称保持原文。
12
+
13
+ ## Step 0: 定位脚本
14
+
15
+ 运行:
16
+
17
+ ```bash
18
+ OPENTEST_SEARCH_ROOTS=("." "$HOME/.claude/skills" "$HOME/.codex/skills" "$HOME/.cursor/skills")
19
+ OPENTEST_STATE="${OPENTEST_STATE:-$(find "${OPENTEST_SEARCH_ROOTS[@]}" -path '*/opentest/scripts/opentest-state.sh' -type f -print -quit 2>/dev/null)}"
20
+ OPENTEST_GUARD="${OPENTEST_GUARD:-$(find "${OPENTEST_SEARCH_ROOTS[@]}" -path '*/opentest/scripts/opentest-guard.sh' -type f -print -quit 2>/dev/null)}"
21
+ OPENTEST_DETECT="${OPENTEST_DETECT:-$(find "${OPENTEST_SEARCH_ROOTS[@]}" -path '*/opentest/scripts/opentest-detect.sh' -type f -print -quit 2>/dev/null)}"
22
+ ```
23
+
24
+ 若任一脚本缺失,停止并提示安装或复制 OpenTest 分发包。
25
+
26
+ ## Step 1: 状态检测
27
+
28
+ 如果 `.opentest.yaml` 不存在,运行:
29
+
30
+ ```bash
31
+ bash "$OPENTEST_STATE" init
32
+ ```
33
+
34
+ 然后运行:
35
+
36
+ ```bash
37
+ bash "$OPENTEST_STATE" check
38
+ bash "$OPENTEST_DETECT" summary
39
+ ```
40
+
41
+ ## Step 2: 阶段分发
42
+
43
+ 读取 `phase`:
44
+
45
+ ```bash
46
+ bash "$OPENTEST_STATE" get phase
47
+ ```
48
+
49
+ 分发规则:
50
+
51
+ - `archived: true`:报告 OpenTest 已完成。
52
+ - `phase: plan`:调用 `opentest-plan`。
53
+ - `phase: author`:调用 `opentest-author`。
54
+ - `phase: run`:调用 `opentest-run`。
55
+ - `phase: accept`:调用 `opentest-accept`。
56
+ - `phase: verify`:调用 `opentest-verify`。
57
+ - `phase: heal`:调用 `opentest-heal`。
58
+ - `phase: archive`:调用 `opentest-archive`。
59
+
60
+ 若状态与可验证文件冲突,以文件状态为准,并用 `opentest-state.sh set` 修正。
@@ -0,0 +1,22 @@
1
+ # Acceptance Evidence
2
+
3
+ Natural language acceptance cases must include:
4
+
5
+ - ID
6
+ - intent
7
+ - context
8
+ - actor or role
9
+ - steps
10
+ - expected outcome
11
+ - execution surface
12
+ - evidence status
13
+
14
+ Allowed evidence status:
15
+
16
+ - pending
17
+ - pass
18
+ - fail
19
+ - blocked
20
+ - risk-accepted
21
+
22
+ Blocked evidence must include a reason such as unavailable MCP, unavailable environment, missing seed data, auth requirement, or external service failure.
@@ -0,0 +1,64 @@
1
+ # Codex Harness 覆盖启发参考
2
+
3
+ ## 用途
4
+
5
+ 本文件随 OpenTest 分发,用于给 `/opentest-plan` 和 `/opentest-accept` 提供轻量覆盖启发。它不是强制 checklist,也不是 Codex Harness 全量内容副本。
6
+
7
+ OpenTest 应根据变更类型、风险和项目事实选择适用覆盖面。适用但缺少证据的点应记录为 `gap`;不适用的点应在覆盖摘要中说明,不需要展开用例。
8
+
9
+ ## 来源
10
+
11
+ 本参考从本地 Codex Harness 知识库中抽取短规则,主要对应:
12
+
13
+ - `tdd-workflow`
14
+ - `e2e-runner`
15
+ - `browser-e2e-testing`
16
+ - `verification-loop`
17
+ - `code-reviewer`
18
+ - `speckit-checklist`
19
+
20
+ ## 选择规则
21
+
22
+ | 变更类型 | 默认考虑的证据 |
23
+ | --- | --- |
24
+ | 纯函数、工具函数、状态计算 | unit evidence |
25
+ | 服务、模块协作、数据库读写 | integration evidence |
26
+ | API、契约、错误处理 | contract、integration、error evidence |
27
+ | 前端渲染或交互 | UI acceptance,必要时 component 或 E2E evidence |
28
+ | 权限、支付、安全、数据写入、跨页面闭环 | high-risk acceptance 或 E2E evidence |
29
+ | 文案、配置、小范围无行为变更 | targeted review 或 light evidence |
30
+
31
+ ## 前端验收维度
32
+
33
+ 前端或真实链路验收可从以下维度中选择适用项,不要求每次全部覆盖:
34
+
35
+ | 维度 | 含义 |
36
+ | --- | --- |
37
+ | D1 页面渲染 | 标题、文案、结构、主要元素存在 |
38
+ | D2 交互功能 | 点击、输入、选择、弹窗、菜单 |
39
+ | D3 数据展示 | API 数据、格式化、状态展示 |
40
+ | D4 表单验证 | 必填、格式、边界、关联校验 |
41
+ | D5 导航流转 | 菜单、链接、返回、跨页面传参 |
42
+ | D6 状态管理 | loading、empty、error、retry |
43
+ | D7 权限控制 | 角色、路由守卫、越权拦截 |
44
+ | D8 UI 反馈 | toast、modal、loading 指示 |
45
+ | D9 组件联动 | 筛选、tab、数量、级联变化 |
46
+ | D10 边界情况 | 空数据、长文本、特殊输入、前置数据缺失 |
47
+
48
+ ## 输出约束
49
+
50
+ `/opentest-plan` 应优先输出窄矩阵:
51
+
52
+ ```markdown
53
+ | ID | 意图 | 风险 | 必需证据 | 状态 |
54
+ | --- | --- | --- | --- | --- |
55
+ | ACC-001 | 用户保存后看到成功反馈 | medium | UI 验收 | pending |
56
+ ```
57
+
58
+ 只有当风险或变更类型需要时,才增加覆盖维度、命令、证据路径和阻塞原因列。
59
+
60
+ ## 质量门启发
61
+
62
+ 验证建议按 build、type、lint、test、security、diff 的顺序组织。构建、类型、lint、必需测试或必需验收失败时应阻塞通过;非必需证据缺口可以作为可解释风险记录。
63
+
64
+ 工具、环境、前置数据或 MCP 不可用时,记录 `blocked` evidence,不得声称验收通过。
@@ -0,0 +1,9 @@
1
+ # OpenTest Command Routing
2
+
3
+ `/opentest` is the only automatic router.
4
+
5
+ If `.opentest.yaml` is missing, initialize it and set `phase: plan`.
6
+
7
+ The router does not write tests or run product verification directly. It detects state and invokes the next phase skill.
8
+
9
+ When metadata conflicts with files, prefer verifiable files and repair `.opentest.yaml`.
@@ -0,0 +1,16 @@
1
+ # OpenTest Lifecycle
2
+
3
+ ## Phases
4
+
5
+ `plan -> author -> run -> accept -> verify -> archive`
6
+
7
+ `heal` 是失败恢复阶段,只在测试资产过期或验收执行路径失效时进入。
8
+
9
+ ## Exit Conditions
10
+
11
+ - plan: `docs/opentest/plans/*.md` and `docs/opentest/matrices/*.md` exist.
12
+ - author: required test assets or acceptance cases exist, or gaps are explicitly recorded.
13
+ - run: run report exists and records commands, exit codes, and summaries.
14
+ - accept: required acceptance evidence is pass or blocked with reason.
15
+ - verify: verification report exists and result is pass, fail, or risk-accepted.
16
+ - archive: verification passed before archive is marked complete.
@@ -0,0 +1,16 @@
1
+ # Acceptance-to-Test Matrix Format
2
+
3
+ ## Minimal Columns
4
+
5
+ | ID | 意图 | 风险 | 必需证据 | 状态 |
6
+ | --- | --- | --- | --- | --- |
7
+
8
+ ## Optional Columns
9
+
10
+ - 覆盖面
11
+ - 命令
12
+ - 证据路径
13
+ - 阻塞原因
14
+ - 风险接受说明
15
+
16
+ Only add optional columns when risk or change type needs them.
@@ -0,0 +1,21 @@
1
+ # OpenTest Quality Gate
2
+
3
+ ## Blocking
4
+
5
+ - build failure
6
+ - type failure
7
+ - lint failure
8
+ - required test failure
9
+ - required acceptance failure
10
+ - required evidence gap for high-risk behavior
11
+
12
+ ## Risk Accepted
13
+
14
+ - non-critical evidence gap with written reason
15
+ - unavailable optional tool with fallback evidence
16
+ - low-risk behavior covered by review rather than execution
17
+
18
+ ## Non-Blocking Gap
19
+
20
+ - evidence not applicable to this change
21
+ - optional coverage dimension omitted with reason
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ printf 'Usage: opentest-detect.sh summary\n' >&2
6
+ exit 1
7
+ }
8
+
9
+ package_script_present() {
10
+ local name="$1"
11
+ [ -f package.json ] || return 1
12
+ grep -Eq "\"$name\"[[:space:]]*:" package.json
13
+ }
14
+
15
+ summary() {
16
+ printf '=== OpenTest Project Facts ===\n'
17
+
18
+ if [ -f package.json ]; then
19
+ printf 'package_json: present\n'
20
+ if package_script_present test; then printf 'npm_test: present\n'; else printf 'npm_test: missing\n'; fi
21
+ if package_script_present build; then printf 'npm_build: present\n'; else printf 'npm_build: missing\n'; fi
22
+ if package_script_present lint; then printf 'npm_lint: present\n'; else printf 'npm_lint: missing\n'; fi
23
+ else
24
+ printf 'package_json: missing\n'
25
+ fi
26
+
27
+ if [ -f pyproject.toml ] || [ -f pytest.ini ] || [ -f setup.cfg ]; then
28
+ printf 'python_tests: possible\n'
29
+ else
30
+ printf 'python_tests: missing\n'
31
+ fi
32
+
33
+ if [ -f pom.xml ]; then printf 'maven_project: present\n'; else printf 'maven_project: missing\n'; fi
34
+ if [ -f Cargo.toml ]; then printf 'cargo_project: present\n'; else printf 'cargo_project: missing\n'; fi
35
+ if [ -d tests ]; then printf 'tests_dir: present\n'; else printf 'tests_dir: missing\n'; fi
36
+ if [ -d docs/opentest ]; then printf 'opentest_docs: present\n'; else printf 'opentest_docs: missing\n'; fi
37
+ if [ -d docs/opentest/acceptance ]; then printf 'acceptance_docs: present\n'; else printf 'acceptance_docs: missing\n'; fi
38
+
39
+ if git rev-parse --git-dir >/dev/null 2>&1; then
40
+ printf 'git: present\n'
41
+ printf 'changed_files: %s\n' "$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')"
42
+ else
43
+ printf 'git: missing\n'
44
+ fi
45
+ }
46
+
47
+ case "${1:-summary}" in
48
+ summary)
49
+ shift
50
+ [ $# -eq 0 ] || usage
51
+ summary
52
+ ;;
53
+ *)
54
+ usage
55
+ ;;
56
+ esac
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ PHASE="${1:-}"
5
+ APPLY="${2:-}"
6
+ STATE_FILE="${OPENTEST_STATE_FILE:-.opentest.yaml}"
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ BLOCK=0
9
+
10
+ red() { printf '\033[31m%s\033[0m\n' "$1" >&2; }
11
+ green() { printf '\033[32m%s\033[0m\n' "$1" >&2; }
12
+
13
+ usage() {
14
+ red "Usage: opentest-guard.sh <plan|author|run|accept|verify|heal|archive> [--apply]"
15
+ exit 1
16
+ }
17
+
18
+ yaml_field() {
19
+ local field="$1"
20
+ awk -v key="$field" '
21
+ $1 == key ":" {
22
+ sub(/^[^:]+:[[:space:]]*/, "", $0)
23
+ print
24
+ exit
25
+ }
26
+ ' "$STATE_FILE" | tr -d '"' | tr -d "'"
27
+ }
28
+
29
+ file_nonempty() {
30
+ [ -n "${1:-}" ] && [ "$1" != "null" ] && [ -f "$1" ] && [ -s "$1" ]
31
+ }
32
+
33
+ check_path() {
34
+ local description="$1"
35
+ local path_value="$2"
36
+ if file_nonempty "$path_value"; then
37
+ green " [PASS] $description"
38
+ else
39
+ red " [FAIL] $description"
40
+ BLOCK=1
41
+ fi
42
+ }
43
+
44
+ check_result() {
45
+ local description="$1"
46
+ shift
47
+ if "$@"; then
48
+ green " [PASS] $description"
49
+ else
50
+ red " [FAIL] $description"
51
+ BLOCK=1
52
+ fi
53
+ }
54
+
55
+ result_is_archivable() {
56
+ local result
57
+ result=$(yaml_field verification_result)
58
+ [ "$result" = "pass" ] || [ "$result" = "risk-accepted" ]
59
+ }
60
+
61
+ guard_plan() {
62
+ check_path "plan exists" "$(yaml_field plan)"
63
+ check_path "matrix exists" "$(yaml_field matrix)"
64
+ }
65
+
66
+ guard_author() {
67
+ local acceptance matrix
68
+ acceptance=$(yaml_field acceptance)
69
+ matrix=$(yaml_field matrix)
70
+
71
+ if [ -n "$acceptance" ] && [ "$acceptance" != "null" ]; then
72
+ check_path "acceptance asset exists" "$acceptance"
73
+ else
74
+ check_path "matrix exists for recorded gaps" "$matrix"
75
+ fi
76
+ }
77
+
78
+ guard_run() {
79
+ check_path "run report exists" "$(yaml_field run_report)"
80
+ }
81
+
82
+ guard_accept() {
83
+ local acceptance run_report
84
+ acceptance=$(yaml_field acceptance)
85
+ run_report=$(yaml_field run_report)
86
+
87
+ if [ -n "$acceptance" ] && [ "$acceptance" != "null" ]; then
88
+ check_path "acceptance evidence exists" "$acceptance"
89
+ else
90
+ check_path "run report exists for recorded no-acceptance path" "$run_report"
91
+ fi
92
+ }
93
+
94
+ guard_verify() {
95
+ check_path "verification report exists" "$(yaml_field verification_report)"
96
+ check_result "verification result is archivable" result_is_archivable
97
+ }
98
+
99
+ guard_heal() {
100
+ green " [PASS] heal can return to run after targeted assets are updated"
101
+ }
102
+
103
+ guard_archive() {
104
+ check_path "verification report exists" "$(yaml_field verification_report)"
105
+ check_result "verification result is archivable" result_is_archivable
106
+ }
107
+
108
+ apply_transition() {
109
+ case "$PHASE" in
110
+ plan)
111
+ bash "$SCRIPT_DIR/opentest-state.sh" transition plan-complete
112
+ ;;
113
+ author)
114
+ bash "$SCRIPT_DIR/opentest-state.sh" transition author-complete
115
+ ;;
116
+ run)
117
+ bash "$SCRIPT_DIR/opentest-state.sh" transition run-complete
118
+ ;;
119
+ accept)
120
+ bash "$SCRIPT_DIR/opentest-state.sh" transition accept-complete
121
+ ;;
122
+ verify)
123
+ local result
124
+ result=$(yaml_field verification_result)
125
+ if [ "$result" = "pass" ]; then
126
+ bash "$SCRIPT_DIR/opentest-state.sh" transition verify-pass
127
+ elif [ "$result" = "risk-accepted" ]; then
128
+ bash "$SCRIPT_DIR/opentest-state.sh" transition verify-risk-accepted
129
+ else
130
+ bash "$SCRIPT_DIR/opentest-state.sh" transition verify-fail
131
+ fi
132
+ ;;
133
+ heal)
134
+ bash "$SCRIPT_DIR/opentest-state.sh" transition heal-complete
135
+ ;;
136
+ archive)
137
+ bash "$SCRIPT_DIR/opentest-state.sh" transition archived
138
+ ;;
139
+ *)
140
+ usage
141
+ ;;
142
+ esac
143
+ }
144
+
145
+ if [ -z "$PHASE" ]; then
146
+ usage
147
+ fi
148
+
149
+ if [ ! -f "$STATE_FILE" ]; then
150
+ red "FATAL: $STATE_FILE not found"
151
+ exit 1
152
+ fi
153
+
154
+ case "$PHASE" in
155
+ plan)
156
+ guard_plan
157
+ ;;
158
+ author)
159
+ guard_author
160
+ ;;
161
+ run)
162
+ guard_run
163
+ ;;
164
+ accept)
165
+ guard_accept
166
+ ;;
167
+ verify)
168
+ guard_verify
169
+ ;;
170
+ heal)
171
+ guard_heal
172
+ ;;
173
+ archive)
174
+ guard_archive
175
+ ;;
176
+ *)
177
+ usage
178
+ ;;
179
+ esac
180
+
181
+ if [ "$BLOCK" -ne 0 ]; then
182
+ red "BLOCKED"
183
+ exit 1
184
+ fi
185
+
186
+ green "ALL CHECKS PASSED"
187
+ if [ "$APPLY" = "--apply" ]; then
188
+ apply_transition
189
+ fi