@rpamis/comet 0.1.1 → 0.1.3
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.
- package/README.md +56 -4
- package/assets/manifest.json +15 -13
- package/assets/skills/comet/SKILL.md +30 -18
- package/assets/skills/comet/scripts/comet-guard.sh +203 -0
- package/assets/skills/comet/scripts/comet-yaml-validate.sh +130 -0
- package/assets/skills/comet-archive/SKILL.md +51 -7
- package/assets/skills/comet-build/SKILL.md +53 -14
- package/assets/skills/comet-design/SKILL.md +37 -4
- package/assets/skills/comet-hotfix/SKILL.md +48 -13
- package/assets/skills/comet-open/SKILL.md +52 -15
- package/assets/skills/comet-tweak/SKILL.md +152 -117
- package/assets/skills/comet-verify/SKILL.md +49 -16
- package/package.json +50 -50
package/README.md
CHANGED
|
@@ -89,6 +89,13 @@ After `comet init`, three groups of skills are installed to the selected platfor
|
|
|
89
89
|
| `/comet-hotfix` | Preset: Quick bug fix (skips brainstorming) |
|
|
90
90
|
| `/comet-tweak` | Preset: Small change (skips brainstorming and full plan) |
|
|
91
91
|
|
|
92
|
+
### Guard Scripts
|
|
93
|
+
|
|
94
|
+
| Script | Purpose |
|
|
95
|
+
|--------|---------|
|
|
96
|
+
| `comet-guard.sh` | Phase transition guard — validates exit conditions before phase transitions |
|
|
97
|
+
| `comet-yaml-validate.sh` | Schema validator — validates `.comet.yaml` structure and field values |
|
|
98
|
+
|
|
92
99
|
### OpenSpec Skills
|
|
93
100
|
|
|
94
101
|
Spec lifecycle management: propose, explore, sync, verify, archive, and more.
|
|
@@ -102,14 +109,14 @@ Development methodology: brainstorming, TDD, subagent-driven development, code r
|
|
|
102
109
|
```
|
|
103
110
|
/comet
|
|
104
111
|
↓ auto-detect
|
|
105
|
-
/comet-open
|
|
112
|
+
/comet-open ——→ /comet-design ——→ /comet-build ——→ /comet-verify ——→ /comet-archive
|
|
106
113
|
(OpenSpec) (Superpowers) (Superpowers) (Both) (OpenSpec)
|
|
107
114
|
|
|
108
115
|
/comet-hotfix (preset path, skips brainstorming)
|
|
109
|
-
open
|
|
116
|
+
open ——→ build ——→ verify ——→ archive
|
|
110
117
|
|
|
111
118
|
/comet-tweak (preset path, skips brainstorming and full plan)
|
|
112
|
-
open
|
|
119
|
+
open ——→ lightweight build ——→ light verify ——→ archive
|
|
113
120
|
```
|
|
114
121
|
|
|
115
122
|
### Five Phases
|
|
@@ -130,12 +137,54 @@ Development methodology: brainstorming, TDD, subagent-driven development, code r
|
|
|
130
137
|
- **Commit frequently** — one commit per task, message reflects design intent
|
|
131
138
|
- **Verify before archive** — `/comet-verify` must pass before `/comet-archive`
|
|
132
139
|
|
|
140
|
+
### State Management
|
|
141
|
+
|
|
142
|
+
Comet uses a decoupled state architecture with separate YAML files:
|
|
143
|
+
|
|
144
|
+
| File | Owner | Purpose |
|
|
145
|
+
|------|-------|---------|
|
|
146
|
+
| `.openspec.yaml` | OpenSpec | Spec lifecycle, change metadata |
|
|
147
|
+
| `.comet.yaml` | Comet | Workflow phase, execution mode, verification status |
|
|
148
|
+
|
|
149
|
+
**Key Fields in `.comet.yaml`:**
|
|
150
|
+
- `workflow`: `full`, `hotfix`, or `tweak`
|
|
151
|
+
- `phase`: `design`, `build`, `verify`, `archive`
|
|
152
|
+
- `design_doc`: Path to Superpowers Design Doc
|
|
153
|
+
- `plan`: Path to implementation plan
|
|
154
|
+
- `build_mode`: `subagent-driven-development`, `executing-plans`, or `direct`
|
|
155
|
+
- `verify_mode`: `light` or `full`
|
|
156
|
+
- `verify_result`: `pending`, `pass`, or `fail`
|
|
157
|
+
- `archived`: Boolean indicating if change is archived
|
|
158
|
+
|
|
159
|
+
### Reliability Features
|
|
160
|
+
|
|
161
|
+
Comet includes three-layer defense to ensure agent execution reliability:
|
|
162
|
+
|
|
163
|
+
1. **Entry Verification** — Each phase validates preconditions before execution
|
|
164
|
+
- Checks file existence, state consistency, and phase transitions
|
|
165
|
+
- Outputs `[HARD STOP]` with actionable suggestions if validation fails
|
|
166
|
+
|
|
167
|
+
2. **Write-Then-Verify** — Every state write is immediately verified
|
|
168
|
+
- After updating `.comet.yaml`, agents must verify field values
|
|
169
|
+
- Automatic retry mechanism (up to 2 attempts) on mismatch
|
|
170
|
+
|
|
171
|
+
3. **Schema Validation** — `comet-yaml-validate.sh` ensures data integrity
|
|
172
|
+
- Validates required fields (9 fields)
|
|
173
|
+
- Validates enum values (6 enum types)
|
|
174
|
+
- Validates referenced file paths exist
|
|
175
|
+
- Detects unknown/typos fields
|
|
176
|
+
|
|
177
|
+
**Security**: Path traversal protection on all change name inputs
|
|
178
|
+
|
|
133
179
|
## Project Structure
|
|
134
180
|
|
|
135
181
|
```
|
|
136
182
|
your-project/
|
|
137
183
|
├── .claude/skills/ # Platform skills dir (Comet + OpenSpec + Superpowers)
|
|
138
184
|
│ ├── comet/SKILL.md
|
|
185
|
+
│ │ └── scripts/
|
|
186
|
+
│ │ ├── comet-guard.sh # Phase transition guard
|
|
187
|
+
│ │ └── comet-yaml-validate.sh # Schema validator
|
|
139
188
|
│ ├── comet-*/SKILL.md
|
|
140
189
|
│ ├── openspec-*/SKILL.md
|
|
141
190
|
│ └── brainstorming/SKILL.md
|
|
@@ -143,7 +192,8 @@ your-project/
|
|
|
143
192
|
│ ├── config.yaml
|
|
144
193
|
│ └── changes/
|
|
145
194
|
│ └── <name>/
|
|
146
|
-
│ ├── .openspec.yaml
|
|
195
|
+
│ ├── .openspec.yaml # OpenSpec state
|
|
196
|
+
│ ├── .comet.yaml # Comet workflow state (decoupled)
|
|
147
197
|
│ ├── proposal.md
|
|
148
198
|
│ ├── design.md
|
|
149
199
|
│ ├── specs/<capability>/spec.md
|
|
@@ -173,6 +223,8 @@ pnpm build
|
|
|
173
223
|
pnpm test
|
|
174
224
|
```
|
|
175
225
|
|
|
226
|
+
See [CHANGELOG.md](CHANGELOG.md) for version history and updates.
|
|
227
|
+
|
|
176
228
|
## Security
|
|
177
229
|
|
|
178
230
|
- Pre-publish scan for API keys, secrets, tokens, and private keys
|
package/assets/manifest.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "0.1.0",
|
|
3
|
-
"skills": [
|
|
4
|
-
"comet/SKILL.md",
|
|
5
|
-
"comet-
|
|
6
|
-
"comet-
|
|
7
|
-
"comet-
|
|
8
|
-
"comet-
|
|
9
|
-
"comet-
|
|
10
|
-
"comet-
|
|
11
|
-
"comet-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
{
|
|
2
|
+
"version": "0.1.0",
|
|
3
|
+
"skills": [
|
|
4
|
+
"comet/SKILL.md",
|
|
5
|
+
"comet/scripts/comet-guard.sh",
|
|
6
|
+
"comet/scripts/comet-yaml-validate.sh",
|
|
7
|
+
"comet-open/SKILL.md",
|
|
8
|
+
"comet-design/SKILL.md",
|
|
9
|
+
"comet-build/SKILL.md",
|
|
10
|
+
"comet-verify/SKILL.md",
|
|
11
|
+
"comet-archive/SKILL.md",
|
|
12
|
+
"comet-hotfix/SKILL.md",
|
|
13
|
+
"comet-tweak/SKILL.md"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -65,21 +65,20 @@ agent 不应跳过这些决策点;其他明确无歧义的阶段衔接可以
|
|
|
65
65
|
|
|
66
66
|
### Step 1: Comet 状态元数据读取
|
|
67
67
|
|
|
68
|
-
优先读取 `openspec/changes/<name>/.
|
|
68
|
+
优先读取 `openspec/changes/<name>/.comet.yaml`。若该文件不存在,再回退到 `openspec status --change "<name>" --json`、`tasks.md` 和 `docs/superpowers/` 文件检查。
|
|
69
69
|
|
|
70
70
|
推荐元数据结构:
|
|
71
71
|
|
|
72
72
|
```yaml
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
archived: false
|
|
73
|
+
workflow: full
|
|
74
|
+
phase: build
|
|
75
|
+
design_doc: docs/superpowers/specs/YYYY-MM-DD-topic-design.md
|
|
76
|
+
plan: docs/superpowers/plans/YYYY-MM-DD-feature.md
|
|
77
|
+
build_mode: subagent-driven-development
|
|
78
|
+
verify_mode: light
|
|
79
|
+
verify_result: pending
|
|
80
|
+
verified_at: null
|
|
81
|
+
archived: false
|
|
83
82
|
```
|
|
84
83
|
|
|
85
84
|
字段含义:
|
|
@@ -100,14 +99,14 @@ comet:
|
|
|
100
99
|
|
|
101
100
|
对选中的 change,按以下顺序判断当前状态:
|
|
102
101
|
|
|
103
|
-
1. **`
|
|
104
|
-
2. **`
|
|
105
|
-
3. **`
|
|
106
|
-
4. **`
|
|
107
|
-
5. **`
|
|
102
|
+
1. **`archived: true` 或 change 已移入 archive** → 流程已完成
|
|
103
|
+
2. **`verify_result: pass` 且 `archived` 不是 `true`** → 调用 `/comet-archive`
|
|
104
|
+
3. **`phase: verify` 或 tasks.md 全部勾选** → 调用 `/comet-verify`
|
|
105
|
+
4. **`phase: build` 或已有 Design Doc 但计划/执行未完成** → 调用 `/comet-build`
|
|
106
|
+
5. **`phase: design` 或有 change 但无 Design Doc** → 调用 `/comet-design`
|
|
108
107
|
6. **无活跃 change 或状态无法判定** → 调用 `/comet-open`
|
|
109
108
|
|
|
110
|
-
如果元数据与文件状态冲突,以可验证的文件状态为准,并在继续阶段前修正 `.
|
|
109
|
+
如果元数据与文件状态冲突,以可验证的文件状态为准,并在继续阶段前修正 `.comet.yaml`。
|
|
111
110
|
|
|
112
111
|
---
|
|
113
112
|
|
|
@@ -150,7 +149,7 @@ comet:
|
|
|
150
149
|
|------|---------|
|
|
151
150
|
| `openspec list --json` 失败 | 检查 openspec 是否已安装,提示用户运行 `openspec init` |
|
|
152
151
|
| 子 skill 不可用(如 `superpowers:brainstorming`) | 停止流程,提示安装或启用对应 skill |
|
|
153
|
-
| `.
|
|
152
|
+
| `.comet.yaml` 格式异常或缺失 | 以文件状态为准(tasks.md、docs/superpowers/),修正元数据后继续 |
|
|
154
153
|
| Maven 编译/测试失败 | 返回 build 阶段修复,不进入 verify |
|
|
155
154
|
| change 目录结构不完整 | 按 `comet-open` 的产物要求补齐缺失文件 |
|
|
156
155
|
|
|
@@ -158,6 +157,18 @@ comet:
|
|
|
158
157
|
|
|
159
158
|
## 快速参考
|
|
160
159
|
|
|
160
|
+
### 脚本定位
|
|
161
|
+
|
|
162
|
+
Comet 阶段守卫脚本 `comet-guard.sh` 随 skill 包分发,位于 `comet/scripts/` 目录。
|
|
163
|
+
**不硬编码平台路径**,运行时通过以下命令自定位:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
COMET_GUARD=$(find . -path '*/comet/scripts/comet-guard.sh' -type f -print -quit)
|
|
167
|
+
bash "$COMET_GUARD" <change-name> <phase>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
后续文档中 `bash $COMET_GUARD <change> <phase>` 均指此命令。加载 comet 后,agent 应在 shell 环境中缓存 `COMET_GUARD` 路径,避免重复 `find`。
|
|
171
|
+
|
|
161
172
|
### 文件结构
|
|
162
173
|
|
|
163
174
|
```
|
|
@@ -166,6 +177,7 @@ openspec/ # OpenSpec — WHAT
|
|
|
166
177
|
├── changes/
|
|
167
178
|
│ ├── <name>/ # 活跃 change
|
|
168
179
|
│ │ ├── .openspec.yaml
|
|
180
|
+
│ │ ├── .comet.yaml
|
|
169
181
|
│ │ ├── proposal.md # Why + What
|
|
170
182
|
│ │ ├── design.md # 高层架构决策
|
|
171
183
|
│ │ ├── specs/<capability>/spec.md # Delta 能力规格
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Comet Phase Guard — validates exit conditions before phase transitions
|
|
3
|
+
# Usage: comet-guard.sh <change-name> <from-phase>
|
|
4
|
+
# Phases: open, design, build, verify, archive
|
|
5
|
+
# Exit 0 = all checks pass, exit 1 = blocked (reasons printed to stderr)
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
red() { echo -e "\033[31m$1\033[0m" >&2; }
|
|
10
|
+
green() { echo -e "\033[32m$1\033[0m" >&2; }
|
|
11
|
+
warn() { echo -e "\033[33m$1\033[0m" >&2; }
|
|
12
|
+
|
|
13
|
+
# Input validation - prevent path traversal
|
|
14
|
+
validate_change_name() {
|
|
15
|
+
local name="$1"
|
|
16
|
+
# Reject empty names
|
|
17
|
+
if [ -z "$name" ]; then
|
|
18
|
+
red "ERROR: Change name cannot be empty" >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
# Only allow alphanumeric, hyphens, and underscores
|
|
22
|
+
if [[ ! "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
|
23
|
+
red "ERROR: Invalid change name: '$name'" >&2
|
|
24
|
+
red "Valid characters: a-z, A-Z, 0-9, -, _" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
# Reject path traversal attempts
|
|
28
|
+
if [[ "$name" =~ \.\. ]]; then
|
|
29
|
+
red "ERROR: Change name cannot contain '..' (path traversal not allowed)" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
validate_change_name "$1"
|
|
35
|
+
|
|
36
|
+
CHANGE="$1"
|
|
37
|
+
PHASE="$2"
|
|
38
|
+
CHANGE_DIR="openspec/changes/$CHANGE"
|
|
39
|
+
|
|
40
|
+
BLOCK=0
|
|
41
|
+
check() {
|
|
42
|
+
local desc="$1"
|
|
43
|
+
shift
|
|
44
|
+
if "$@" 2>/dev/null; then
|
|
45
|
+
green " [PASS] $desc"
|
|
46
|
+
else
|
|
47
|
+
red " [FAIL] $desc"
|
|
48
|
+
BLOCK=1
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# --- Helper functions ---
|
|
53
|
+
|
|
54
|
+
tasks_all_done() {
|
|
55
|
+
local tasks="$CHANGE_DIR/tasks.md"
|
|
56
|
+
[ -f "$tasks" ] || return 1
|
|
57
|
+
grep -q '\- \[x\]' "$tasks" || return 1
|
|
58
|
+
! grep -q '\- \[ \]' "$tasks"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
tasks_has_any() {
|
|
62
|
+
local tasks="$CHANGE_DIR/tasks.md"
|
|
63
|
+
[ -f "$tasks" ] && grep -q '\- \[' "$tasks"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
yaml_field_value() {
|
|
67
|
+
local field="$1"
|
|
68
|
+
local yaml="$CHANGE_DIR/.comet.yaml"
|
|
69
|
+
if [ -f "$yaml" ]; then
|
|
70
|
+
grep "^${field}:" "$yaml" | sed "s/^${field}: *//" | tr -d '"' | tr -d "'"
|
|
71
|
+
fi
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
file_nonempty() {
|
|
75
|
+
[ -f "$1" ] && [ -s "$1" ]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
preflight() {
|
|
79
|
+
local expected_phase="$1"
|
|
80
|
+
|
|
81
|
+
if [ ! -d "$CHANGE_DIR" ]; then
|
|
82
|
+
red "FATAL: change directory not found: $CHANGE_DIR"
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
if [ ! -f "$CHANGE_DIR/.comet.yaml" ]; then
|
|
86
|
+
red "FATAL: .comet.yaml not found in $CHANGE_DIR"
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
local actual_phase
|
|
91
|
+
actual_phase=$(yaml_field_value "phase" 2>/dev/null || true)
|
|
92
|
+
if [ "$actual_phase" != "$expected_phase" ]; then
|
|
93
|
+
red "FATAL: .comet.yaml phase is '$actual_phase', expected '$expected_phase'"
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# Schema validation
|
|
98
|
+
local script_dir validate_script
|
|
99
|
+
script_dir="$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")" 2>/dev/null || dirname "$0")"
|
|
100
|
+
validate_script="$script_dir/comet-yaml-validate.sh"
|
|
101
|
+
if [ -f "$validate_script" ]; then
|
|
102
|
+
if ! bash "$validate_script" "$CHANGE" 2>/dev/null; then
|
|
103
|
+
bash "$validate_script" "$CHANGE"
|
|
104
|
+
red "FATAL: .comet.yaml schema validation failed"
|
|
105
|
+
exit 1
|
|
106
|
+
fi
|
|
107
|
+
fi
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
maven_compiles() {
|
|
111
|
+
if [ "${COMET_SKIP_BUILD:-0}" = "1" ]; then
|
|
112
|
+
return 0
|
|
113
|
+
fi
|
|
114
|
+
mvn compile -q 2>/dev/null
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
verify_result_is_pass() {
|
|
118
|
+
local result
|
|
119
|
+
result=$(yaml_field_value "verify_result" 2>/dev/null || true)
|
|
120
|
+
[ "$result" = "pass" ]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
archived_is_true() {
|
|
124
|
+
local val
|
|
125
|
+
val=$(yaml_field_value "archived" 2>/dev/null || true)
|
|
126
|
+
[ "$val" = "true" ]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# --- Phase-specific checks ---
|
|
130
|
+
|
|
131
|
+
guard_open() {
|
|
132
|
+
echo "=== Guard: open → design ===" >&2
|
|
133
|
+
|
|
134
|
+
check "proposal.md exists and non-empty" file_nonempty "$CHANGE_DIR/proposal.md"
|
|
135
|
+
check "design.md exists and non-empty" file_nonempty "$CHANGE_DIR/design.md"
|
|
136
|
+
check "tasks.md exists and non-empty" file_nonempty "$CHANGE_DIR/tasks.md"
|
|
137
|
+
check "tasks.md has at least one task" tasks_has_any
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
guard_design() {
|
|
141
|
+
echo "=== Guard: design → build ===" >&2
|
|
142
|
+
|
|
143
|
+
local design_doc
|
|
144
|
+
design_doc=$(yaml_field_value "design_doc" 2>/dev/null || true)
|
|
145
|
+
|
|
146
|
+
check "proposal.md exists" file_nonempty "$CHANGE_DIR/proposal.md"
|
|
147
|
+
check "tasks.md exists" file_nonempty "$CHANGE_DIR/tasks.md"
|
|
148
|
+
|
|
149
|
+
if [ -n "$design_doc" ] && [ "$design_doc" != "null" ]; then
|
|
150
|
+
check "Design Doc ($design_doc) exists" file_nonempty "$design_doc"
|
|
151
|
+
else
|
|
152
|
+
warn " [WARN] No design_doc recorded in .comet.yaml"
|
|
153
|
+
fi
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
guard_build() {
|
|
157
|
+
echo "=== Guard: build → verify ===" >&2
|
|
158
|
+
|
|
159
|
+
check "tasks.md all tasks checked" tasks_all_done
|
|
160
|
+
check "proposal.md exists" file_nonempty "$CHANGE_DIR/proposal.md"
|
|
161
|
+
check "Maven compile passes" maven_compiles
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
guard_verify() {
|
|
165
|
+
echo "=== Guard: verify → archive ===" >&2
|
|
166
|
+
|
|
167
|
+
check "verify_result is pass" verify_result_is_pass
|
|
168
|
+
check "tasks.md all tasks checked" tasks_all_done
|
|
169
|
+
check "Maven compile passes" maven_compiles
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
guard_archive() {
|
|
173
|
+
echo "=== Guard: archive completeness ===" >&2
|
|
174
|
+
|
|
175
|
+
check "archived is true" archived_is_true
|
|
176
|
+
check "proposal.md exists" file_nonempty "$CHANGE_DIR/proposal.md"
|
|
177
|
+
check "tasks.md all tasks checked" tasks_all_done
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# --- Main ---
|
|
181
|
+
|
|
182
|
+
case "$PHASE" in
|
|
183
|
+
open) preflight "design" ; guard_open ;;
|
|
184
|
+
design) preflight "build" ; guard_design ;;
|
|
185
|
+
build) preflight "verify" ; guard_build ;;
|
|
186
|
+
verify) preflight "archive" ; guard_verify ;;
|
|
187
|
+
archive) preflight "archive" ; guard_archive ;;
|
|
188
|
+
*)
|
|
189
|
+
red "Unknown phase: $PHASE"
|
|
190
|
+
echo "Valid phases: open, design, build, verify, archive" >&2
|
|
191
|
+
exit 1
|
|
192
|
+
;;
|
|
193
|
+
esac
|
|
194
|
+
|
|
195
|
+
if [ "$BLOCK" -eq 1 ]; then
|
|
196
|
+
echo "" >&2
|
|
197
|
+
red "BLOCKED — fix failing checks before proceeding to next phase"
|
|
198
|
+
exit 1
|
|
199
|
+
else
|
|
200
|
+
echo "" >&2
|
|
201
|
+
green "ALL CHECKS PASSED — ready for next phase"
|
|
202
|
+
exit 0
|
|
203
|
+
fi
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Comet YAML Schema Validator — validates .comet.yaml structure
|
|
3
|
+
# Usage: comet-yaml-validate.sh <change-name>
|
|
4
|
+
# Exit 0 = valid, exit 1 = errors found (printed to stderr)
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
red() { echo -e "\033[31m$1\033[0m" >&2; }
|
|
9
|
+
green() { echo -e "\033[32m$1\033[0m" >&2; }
|
|
10
|
+
warn() { echo -e "\033[33m$1\033[0m" >&2; }
|
|
11
|
+
|
|
12
|
+
# Input validation - prevent path traversal
|
|
13
|
+
validate_change_name() {
|
|
14
|
+
local name="$1"
|
|
15
|
+
# Reject empty names
|
|
16
|
+
if [ -z "$name" ]; then
|
|
17
|
+
red "ERROR: Change name cannot be empty" >&2
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
# Only allow alphanumeric, hyphens, and underscores
|
|
21
|
+
if [[ ! "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
|
22
|
+
red "ERROR: Invalid change name: '$name'" >&2
|
|
23
|
+
red "Valid characters: a-z, A-Z, 0-9, -, _" >&2
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
# Reject path traversal attempts
|
|
27
|
+
if [[ "$name" =~ \.\. ]]; then
|
|
28
|
+
red "ERROR: Change name cannot contain '..' (path traversal not allowed)" >&2
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
validate_change_name "$1"
|
|
34
|
+
|
|
35
|
+
CHANGE="$1"
|
|
36
|
+
YAML="openspec/changes/$CHANGE/.comet.yaml"
|
|
37
|
+
|
|
38
|
+
ERRORS=0
|
|
39
|
+
WARNINGS=0
|
|
40
|
+
|
|
41
|
+
# Helper: get value of a top-level field (handles null, empty, quoted)
|
|
42
|
+
field_value() {
|
|
43
|
+
grep "^${1}:" "$YAML" 2>/dev/null | sed "s/^${1}: *//" | tr -d '"' | tr -d "'" || true
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fail() { red " FAIL: $1"; ERRORS=$((ERRORS + 1)); }
|
|
47
|
+
warn_msg() { warn " WARN: $1"; WARNINGS=$((WARNINGS + 1)); }
|
|
48
|
+
|
|
49
|
+
echo "[VALIDATE] $YAML" >&2
|
|
50
|
+
|
|
51
|
+
# --- Required fields ---
|
|
52
|
+
REQUIRED_FIELDS="workflow phase design_doc plan build_mode verify_mode verify_result verified_at archived"
|
|
53
|
+
for field in $REQUIRED_FIELDS; do
|
|
54
|
+
if ! grep -q "^${field}:" "$YAML" 2>/dev/null; then
|
|
55
|
+
fail "missing required field '$field'"
|
|
56
|
+
fi
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
# --- Enum validation ---
|
|
60
|
+
validate_enum() {
|
|
61
|
+
local field="$1" value="$2"
|
|
62
|
+
shift 2
|
|
63
|
+
local valid_values="$*"
|
|
64
|
+
|
|
65
|
+
# null or empty is always acceptable
|
|
66
|
+
if [ -z "$value" ] || [ "$value" = "null" ]; then
|
|
67
|
+
return 0
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
for v in $valid_values; do
|
|
71
|
+
if [ "$value" = "$v" ]; then
|
|
72
|
+
return 0
|
|
73
|
+
fi
|
|
74
|
+
done
|
|
75
|
+
fail "$field='$value' is not valid. Expected: $valid_values"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
workflow=$(field_value "workflow")
|
|
79
|
+
phase=$(field_value "phase")
|
|
80
|
+
build_mode=$(field_value "build_mode")
|
|
81
|
+
verify_mode=$(field_value "verify_mode")
|
|
82
|
+
verify_result=$(field_value "verify_result")
|
|
83
|
+
archived=$(field_value "archived")
|
|
84
|
+
design_doc=$(field_value "design_doc")
|
|
85
|
+
plan=$(field_value "plan")
|
|
86
|
+
|
|
87
|
+
validate_enum "workflow" "$workflow" "full hotfix tweak"
|
|
88
|
+
validate_enum "phase" "$phase" "design build verify archive"
|
|
89
|
+
validate_enum "build_mode" "$build_mode" "subagent-driven-development executing-plans direct"
|
|
90
|
+
validate_enum "verify_mode" "$verify_mode" "light full"
|
|
91
|
+
validate_enum "verify_result" "$verify_result" "pending pass fail"
|
|
92
|
+
validate_enum "archived" "$archived" "true false"
|
|
93
|
+
|
|
94
|
+
# --- Path validation ---
|
|
95
|
+
|
|
96
|
+
if [ -n "$design_doc" ] && [ "$design_doc" != "null" ]; then
|
|
97
|
+
if [ ! -f "$design_doc" ]; then
|
|
98
|
+
fail "design_doc='$design_doc' does not exist on disk"
|
|
99
|
+
fi
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
if [ -n "$plan" ] && [ "$plan" != "null" ]; then
|
|
103
|
+
if [ ! -f "$plan" ]; then
|
|
104
|
+
fail "plan='$plan' does not exist on disk"
|
|
105
|
+
fi
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# --- Unknown keys check ---
|
|
109
|
+
KNOWN_KEYS="workflow phase design_doc plan build_mode verify_mode verify_result verified_at archived"
|
|
110
|
+
while IFS=: read -r key _; do
|
|
111
|
+
key="${key// /}"
|
|
112
|
+
[ -z "$key" ] && continue
|
|
113
|
+
found=0
|
|
114
|
+
for known in $KNOWN_KEYS; do
|
|
115
|
+
[ "$key" = "$known" ] && found=1 && break
|
|
116
|
+
done
|
|
117
|
+
if [ "$found" -eq 0 ]; then
|
|
118
|
+
warn_msg "unknown field '$key' found"
|
|
119
|
+
fi
|
|
120
|
+
done < "$YAML"
|
|
121
|
+
|
|
122
|
+
# --- Summary ---
|
|
123
|
+
echo "" >&2
|
|
124
|
+
if [ "$ERRORS" -gt 0 ]; then
|
|
125
|
+
red "$ERRORS error(s), $WARNINGS warning(s) — validation FAILED"
|
|
126
|
+
exit 1
|
|
127
|
+
else
|
|
128
|
+
green "0 errors, $WARNINGS warning(s) — validation PASSED"
|
|
129
|
+
exit 0
|
|
130
|
+
fi
|
|
@@ -9,13 +9,37 @@ description: "Comet 阶段 5:归档。用 /comet-archive 调用。同步 delta
|
|
|
9
9
|
|
|
10
10
|
- 验证已通过(阶段 4 完成)
|
|
11
11
|
- 分支已处理
|
|
12
|
-
- `openspec/changes/<name>/.
|
|
12
|
+
- `openspec/changes/<name>/.comet.yaml` 中 `verify_result: pass`
|
|
13
13
|
|
|
14
14
|
## 步骤
|
|
15
15
|
|
|
16
|
+
### 0. 入口状态验证(Entry Check)
|
|
17
|
+
|
|
18
|
+
在执行任何操作之前,读取并验证当前状态:
|
|
19
|
+
|
|
20
|
+
**检查清单:**
|
|
21
|
+
1. `openspec/changes/<name>/.comet.yaml` 存在
|
|
22
|
+
2. `phase` 字段的值为 `"archive"`
|
|
23
|
+
3. `verify_result` 字段的值为 `"pass"`
|
|
24
|
+
4. `archived` 字段为 `"false"` 或 null(尚未归档)
|
|
25
|
+
|
|
26
|
+
**验证方式:**
|
|
27
|
+
- `cat openspec/changes/<name>/.comet.yaml` 读取全部字段
|
|
28
|
+
- 如 `verify_result` 不是 `"pass"`,必须先完成验证
|
|
29
|
+
|
|
30
|
+
**失败输出:**
|
|
31
|
+
```
|
|
32
|
+
[HARD STOP] Entry check failed for comet-archive
|
|
33
|
+
Expected: phase=archive, verify_result=pass, archived=false|null
|
|
34
|
+
Actual: phase=<实际值>, verify_result=<实际值>, archived=<实际值>
|
|
35
|
+
Suggestion: Run comet-verify first, or this change was already archived.
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
验证通过后才进入步骤 1。
|
|
39
|
+
|
|
16
40
|
### 1. 执行归档
|
|
17
41
|
|
|
18
|
-
归档前如 `
|
|
42
|
+
归档前如 `verify_result` 不是 `pass`,停止归档并返回 `/comet-verify`。
|
|
19
43
|
|
|
20
44
|
**立即执行:** 使用 Skill 工具加载 `openspec-archive-change` 技能。禁止跳过此步骤。
|
|
21
45
|
|
|
@@ -24,6 +48,19 @@ description: "Comet 阶段 5:归档。用 /comet-archive 调用。同步 delta
|
|
|
24
48
|
2. 所有任务已标记 `[x]`
|
|
25
49
|
3. delta specs 同步状态
|
|
26
50
|
|
|
51
|
+
### 1b. 移动 Comet 状态文件
|
|
52
|
+
|
|
53
|
+
`openspec-archive-change` 不感知 `.comet.yaml`,因此 Comet 需要在 OpenSpec 归档完成后自行移动该文件:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
mv openspec/changes/<name>/.comet.yaml openspec/changes/archive/YYYY-MM-DD-<name>/.comet.yaml
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
【写入验证】移动完成后必须验证:
|
|
60
|
+
test -f openspec/changes/archive/YYYY-MM-DD-<name>/.comet.yaml
|
|
61
|
+
确认归档目录中 .comet.yaml 存在
|
|
62
|
+
如文件不在预期位置,检查 mv 命令是否成功执行。
|
|
63
|
+
|
|
27
64
|
### 2. Delta Spec 同步
|
|
28
65
|
|
|
29
66
|
归档时将 delta specs 同步到主 specs:
|
|
@@ -73,6 +110,7 @@ change 移入归档目录:
|
|
|
73
110
|
```
|
|
74
111
|
openspec/changes/archive/YYYY-MM-DD-<name>/
|
|
75
112
|
├── .openspec.yaml
|
|
113
|
+
├── .comet.yaml
|
|
76
114
|
├── proposal.md
|
|
77
115
|
├── design.md
|
|
78
116
|
├── specs/<capability>/spec.md
|
|
@@ -92,16 +130,22 @@ brainstorming → delta spec → 实施(增量修改)→ 验证 → 主 spec
|
|
|
92
130
|
- 主 specs 已更新(delta → main 同步完成)
|
|
93
131
|
- 关联 design doc 已标注归档状态
|
|
94
132
|
- 关联 plan 已标注归档状态
|
|
95
|
-
- `.
|
|
133
|
+
- `.comet.yaml` 中 `archived` 已记录为 `true`
|
|
134
|
+
- **阶段守卫**:运行 `bash $COMET_GUARD <change-name> archive`,全部 PASS 后确认归档完整
|
|
96
135
|
|
|
97
|
-
归档完成后,在归档目录的 `.
|
|
136
|
+
归档完成后,在归档目录的 `.comet.yaml` 中更新:
|
|
98
137
|
|
|
99
138
|
```yaml
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
archived: true
|
|
139
|
+
phase: archive
|
|
140
|
+
archived: true
|
|
103
141
|
```
|
|
104
142
|
|
|
143
|
+
【写入验证】更新完成后必须验证:
|
|
144
|
+
cat openspec/changes/archive/YYYY-MM-DD-<name>/.comet.yaml
|
|
145
|
+
确认 phase 行的值为 "archive"
|
|
146
|
+
确认 archived 行的值为 "true"
|
|
147
|
+
如任一字段不匹配,重试写入后再次验证。最多重试 2 次,仍失败则报告错误并终止。
|
|
148
|
+
|
|
105
149
|
## 完成
|
|
106
150
|
|
|
107
151
|
Comet 流程全部完成。如需开始新工作,调用 `/comet` 或 `/comet-open`。
|