@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 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 ──→ /comet-design ──→ /comet-build ──→ /comet-verify ──→ /comet-archive
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 ──→ build ──→ verify ──→ archive
116
+ open ——→ build ——→ verify ——→ archive
110
117
 
111
118
  /comet-tweak (preset path, skips brainstorming and full plan)
112
- open ──→ lightweight build ──→ light verify ──→ archive
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
@@ -1,13 +1,15 @@
1
- {
2
- "version": "0.1.0",
3
- "skills": [
4
- "comet/SKILL.md",
5
- "comet-open/SKILL.md",
6
- "comet-design/SKILL.md",
7
- "comet-build/SKILL.md",
8
- "comet-verify/SKILL.md",
9
- "comet-archive/SKILL.md",
10
- "comet-hotfix/SKILL.md",
11
- "comet-tweak/SKILL.md"
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>/.openspec.yaml` 中的 `comet` 元数据。若该字段不存在,再回退到 `openspec status --change "<name>" --json`、`tasks.md` 和 `docs/superpowers/` 文件检查。
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
- comet:
74
- workflow: full
75
- phase: build
76
- design_doc: docs/superpowers/specs/YYYY-MM-DD-topic-design.md
77
- plan: docs/superpowers/plans/YYYY-MM-DD-feature.md
78
- build_mode: subagent-driven-development
79
- verify_mode: light
80
- verify_result: pending
81
- verified_at: null
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. **`comet.archived: true` 或 change 已移入 archive** → 流程已完成
104
- 2. **`comet.verify_result: pass` 且 `comet.archived` 不是 `true`** → 调用 `/comet-archive`
105
- 3. **`comet.phase: verify` 或 tasks.md 全部勾选** → 调用 `/comet-verify`
106
- 4. **`comet.phase: build` 或已有 Design Doc 但计划/执行未完成** → 调用 `/comet-build`
107
- 5. **`comet.phase: design` 或有 change 但无 Design Doc** → 调用 `/comet-design`
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
- 如果元数据与文件状态冲突,以可验证的文件状态为准,并在继续阶段前修正 `.openspec.yaml` 中的 `comet` 字段。
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
- | `.openspec.yaml` 格式异常或缺失 | 以文件状态为准(tasks.md、docs/superpowers/),修正元数据后继续 |
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>/.openspec.yaml` 中 `comet.verify_result: pass`
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
- 归档前如 `comet.verify_result` 不是 `pass`,停止归档并返回 `/comet-verify`。
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
- - `.openspec.yaml` 中 `comet.archived` 已记录为 `true`
133
+ - `.comet.yaml` 中 `archived` 已记录为 `true`
134
+ - **阶段守卫**:运行 `bash $COMET_GUARD <change-name> archive`,全部 PASS 后确认归档完整
96
135
 
97
- 归档完成后,在归档目录的 `.openspec.yaml` 中合并更新:
136
+ 归档完成后,在归档目录的 `.comet.yaml` 中更新:
98
137
 
99
138
  ```yaml
100
- comet:
101
- phase: archive
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`。