create-ccc-tutor 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.
- package/README.md +41 -0
- package/bin/cli.js +76 -0
- package/package.json +28 -0
- package/template/.claude/commands/abandon.md +7 -0
- package/template/.claude/commands/add-anti-flag.md +7 -0
- package/template/.claude/commands/add-constitution-clause.md +7 -0
- package/template/.claude/commands/audit-spec.md +7 -0
- package/template/.claude/commands/commit.md +7 -0
- package/template/.claude/commands/constitution-edit.md +7 -0
- package/template/.claude/commands/db-schema.md +7 -0
- package/template/.claude/commands/exam.md +66 -0
- package/template/.claude/commands/execution-plan.md +7 -0
- package/template/.claude/commands/feature-draft.md +7 -0
- package/template/.claude/commands/handoff.md +7 -0
- package/template/.claude/commands/implement.md +7 -0
- package/template/.claude/commands/init.md +7 -0
- package/template/.claude/commands/next.md +7 -0
- package/template/.claude/commands/offload.md +7 -0
- package/template/.claude/commands/pickup.md +7 -0
- package/template/.claude/commands/recall.md +7 -0
- package/template/.claude/commands/remember.md +7 -0
- package/template/.claude/commands/slide.md +87 -0
- package/template/.claude/commands/spec-finalize.md +7 -0
- package/template/.claude/commands/test-fix.md +7 -0
- package/template/.claude/commands/uninstall.md +7 -0
- package/template/.claude/settings.json +161 -0
- package/template/.claude-plugin/plugin.json +41 -0
- package/template/.codex/config.toml +24 -0
- package/template/.codex/hooks.json +4 -0
- package/template/.codex/install-skills.sh +18 -0
- package/template/.codex/skills/exam/SKILL.md +61 -0
- package/template/.codex/skills/slide/SKILL.md +69 -0
- package/template/.harness/agents/README.md +70 -0
- package/template/.harness/agents/_template/junior-agent-template.md +116 -0
- package/template/.harness/agents/backend-reviewer.md +153 -0
- package/template/.harness/agents/frontend-reviewer.md +158 -0
- package/template/.harness/agents/security-reviewer.md +148 -0
- package/template/.harness/agents/test-fixer.md +147 -0
- package/template/.harness/docs/doc-sync.md +29 -0
- package/template/.harness/docs/git-hygiene.md +56 -0
- package/template/.harness/docs/spec-model.md +47 -0
- package/template/.harness/docs/tool-map.md +120 -0
- package/template/.harness/docs/workflow.md +59 -0
- package/template/.harness/scripts/README.md +70 -0
- package/template/.harness/scripts/auditor-gate.sh +388 -0
- package/template/.harness/scripts/bootstrap-check.sh +103 -0
- package/template/.harness/scripts/budget-monitor.sh +223 -0
- package/template/.harness/scripts/check-prereqs.sh +165 -0
- package/template/.harness/scripts/checkpoint-recall.sh +136 -0
- package/template/.harness/scripts/checkpoint-write.sh +281 -0
- package/template/.harness/scripts/decision-log-append.sh +90 -0
- package/template/.harness/scripts/env-check.sh +286 -0
- package/template/.harness/scripts/format-edit.sh +80 -0
- package/template/.harness/scripts/lint-bans.sh +110 -0
- package/template/.harness/scripts/memory-archive.sh +129 -0
- package/template/.harness/scripts/memory-recall.sh +197 -0
- package/template/.harness/scripts/memory-snapshot.sh +124 -0
- package/template/.harness/scripts/post-migration.sh +58 -0
- package/template/.harness/scripts/precommit-cycles.sh +74 -0
- package/template/.harness/scripts/precommit-typecheck.sh +69 -0
- package/template/.harness/scripts/scratchpad-recall.sh +83 -0
- package/template/.harness/scripts/scratchpad-update.sh +39 -0
- package/template/.harness/scripts/standalone-bootstrap.md +443 -0
- package/template/.harness/skills/abandon/SKILL.md +157 -0
- package/template/.harness/skills/add-anti-flag/SKILL.md +205 -0
- package/template/.harness/skills/add-constitution-clause/SKILL.md +244 -0
- package/template/.harness/skills/audit-spec/SKILL.md +395 -0
- package/template/.harness/skills/commit/SKILL.md +270 -0
- package/template/.harness/skills/constitution-edit/SKILL.md +292 -0
- package/template/.harness/skills/db-schema/SKILL.md +145 -0
- package/template/.harness/skills/db-schema/references/methodology.md +202 -0
- package/template/.harness/skills/execution-plan/SKILL.md +346 -0
- package/template/.harness/skills/feature-draft/SKILL.md +426 -0
- package/template/.harness/skills/handoff/SKILL.md +211 -0
- package/template/.harness/skills/implement/SKILL.md +355 -0
- package/template/.harness/skills/init/SKILL.md +805 -0
- package/template/.harness/skills/next/SKILL.md +245 -0
- package/template/.harness/skills/offload/SKILL.md +134 -0
- package/template/.harness/skills/pickup/SKILL.md +213 -0
- package/template/.harness/skills/recall/SKILL.md +159 -0
- package/template/.harness/skills/remember/SKILL.md +205 -0
- package/template/.harness/skills/spec-finalize/SKILL.md +196 -0
- package/template/.harness/skills/test-fix/SKILL.md +363 -0
- package/template/.harness/skills/uninstall/SKILL.md +370 -0
- package/template/.harness/state/install.json +83 -0
- package/template/AGENTS.md +262 -0
- package/template/CCC_MAGI_LICENSE +201 -0
- package/template/CCC_MAGI_README.md +986 -0
- package/template/CLAUDE.md +658 -0
- package/template/codex.md +39 -0
- package/template/constitution.md +164 -0
- package/template/course/README.md +15 -0
- package/template/course/course_code(example)/exam/README.md +2 -0
- package/template/course/course_code(example)/slide/slide_example-1.pdf +40 -0
- package/template/course/course_code(example)/slide/slide_example-2.pdf +40 -0
- package/template/docs/features/slide-query-implementation.md +79 -0
- package/template/docs/features/slide-query.md +211 -0
- package/template/docs-harness/README.md +42 -0
- package/template/docs-harness/adoption-playbook.md +373 -0
- package/template/docs-harness/ccc-step1-driver-template.md +288 -0
- package/template/docs-harness/cli-configs-README.md +78 -0
- package/template/docs-harness/context-architecture-v2.md +249 -0
- package/template/docs-harness/design-spec.md +437 -0
- package/template/docs-harness/memory-layer.md +135 -0
- package/template/docs-harness/retrospective-notes.md +204 -0
- package/template/gitignore +106 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: feature-draft
|
|
3
|
+
description: This skill should be used when starting a new feature. It produces stage 1 (new-feature mode) of the dual-mode feature workflow — paraphrasing CEO intent, running an edge-case round using the configured `{{edge_case_categories}}`, and a different-model auditor external review — landing a plain-language spec at {{spec_dir}}<name>.md plus an optional implementation notes file. Always use this skill to start a feature rather than writing specs freely, so every feature enters the pipeline with the same shape. Trigger when the user invokes /feature-draft, says "start a new feature", "draft a spec for X", "I want to build <feature>", "let's build <feature>", or similar intent.
|
|
4
|
+
argument-hint: [feature-name]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /feature-draft
|
|
8
|
+
|
|
9
|
+
Drive Stage 1 of the workflow in **new-feature mode**: convert CEO intent into a finalized plain-language spec, with edge cases discovered collaboratively and validated by a different model.
|
|
10
|
+
|
|
11
|
+
The output is the **two-file model**:
|
|
12
|
+
|
|
13
|
+
- `{{spec_dir}}<name>.md` — CEO domain. Plain language, no tech terms. Always produced.
|
|
14
|
+
- `{{implementation_dir}}<name>-implementation.md` — manager domain. Tech detail. Produced only when the feature warrants it.
|
|
15
|
+
|
|
16
|
+
## Why this flow
|
|
17
|
+
|
|
18
|
+
Two layers of validation remove the implementer-grades-own-work bias from intent itself:
|
|
19
|
+
|
|
20
|
+
1. **Conversation-level (paraphrase + edge-case sweep)** — Main Claude paraphrases the CEO's happy path back to confirm understanding, then walks the categories in `{{edge_case_categories}}` together with the CEO. Edge-case discovery is collaborative; intent decisions stay the CEO's.
|
|
21
|
+
2. **Model-level (auditor external review)** — the auditor ({{auditor_model}}) independently reads the consensus spec for missing scenarios, hidden assumptions, and contradictions. Different priors catch things shared-model authorship misses.
|
|
22
|
+
|
|
23
|
+
CEO has the final word on intent; the auditor flags concerns but cannot override CEO decisions (Constitution § 3).
|
|
24
|
+
|
|
25
|
+
## Authoritative sources
|
|
26
|
+
|
|
27
|
+
Load before drafting:
|
|
28
|
+
|
|
29
|
+
1. **`constitution.md`** — project identity (Section 2) + universal core (Section 1); shapes acceptable feature scope
|
|
30
|
+
2. **Root `CLAUDE.md`** — two-file model, lane definitions
|
|
31
|
+
3. **i18n convention** — anything user-visible needs translation entries for all locales in `{{supported_locales}}`
|
|
32
|
+
4. **`{{rule_sources}}`** — scoped rule files that may constrain this feature's design
|
|
33
|
+
5. **Existing files under `{{spec_dir}}`** — to stay consistent with prior CEO-spec shape
|
|
34
|
+
|
|
35
|
+
The CEO spec must NOT pull in tech-term language from prior implementation notes during Stage 1. Tech detail belongs in the implementation file, written later.
|
|
36
|
+
|
|
37
|
+
## Invocation
|
|
38
|
+
|
|
39
|
+
- Typical: `/feature-draft <feature-name>` (e.g., `/feature-draft messaging`)
|
|
40
|
+
- `$ARGUMENTS` becomes the filename (`{{spec_dir}}$ARGUMENTS.md`) and the feature folder name (under `{{feature_folder_pattern}}`).
|
|
41
|
+
|
|
42
|
+
If `$ARGUMENTS` is missing, ask the user. Do not guess.
|
|
43
|
+
|
|
44
|
+
## Pre-stage check
|
|
45
|
+
|
|
46
|
+
Before Step 1, run a 30-second pre-flight:
|
|
47
|
+
|
|
48
|
+
1. `git status` — clean? If uncommitted changes exist, surface them and ask: commit first, stash, or proceed (and accept noise).
|
|
49
|
+
2. Existing `{{spec_dir}}<name>.md` — does it already exist? If yes, ask whether this is a redo (delete and start over), an audit (`/audit-spec <name>` instead), or an add-on to a finalized spec.
|
|
50
|
+
3. **Write initial checkpoint** (CRITICAL — see § Step 0 below): so /pickup can restore state if user interrupts mid-Step-1.
|
|
51
|
+
4. State recap to user: "Pre-stage clear. Lane: Full workflow (new feature). Proceeding with Stage 1 in new-feature mode. Anything to adjust before we start?"
|
|
52
|
+
|
|
53
|
+
Do not proceed without an explicit go-ahead.
|
|
54
|
+
|
|
55
|
+
## Step 0 — Initial checkpoint (MANDATORY before Step 1)
|
|
56
|
+
|
|
57
|
+
Before asking the CEO anything, write a placeholder checkpoint. This is the **insurance** against interruptions: if the user closes Claude mid-Step-1, `/pickup` and the SessionStart welcome-back UX still know "user-login was started, was at Stage 1, was on paraphrase/edge-case round".
|
|
58
|
+
|
|
59
|
+
Without this, an interrupted Step 1 leaves no trace → user comes back next session, sees nothing about the feature, has to remember they were doing it.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Detect mode + lane (defaults: new-feature, full)
|
|
63
|
+
.harness/scripts/checkpoint-write.sh \
|
|
64
|
+
--feature <name> \
|
|
65
|
+
--create-if-missing \
|
|
66
|
+
--mode new-feature \
|
|
67
|
+
--lane full \
|
|
68
|
+
--stage 1 \
|
|
69
|
+
--stage-in-progress "$(jq -nc '{stage_number:1, files_total:0, files_done_list:[], files_remaining_list:[], last_action:\"Starting /feature-draft\", resume_hint:\"You were at Stage 1 (paraphrase + edge-case round). Run /feature-draft to continue.\"}')"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Verify the checkpoint exists before continuing:
|
|
73
|
+
```bash
|
|
74
|
+
test -f .harness/state/workflow-checkpoints/<name>.json && echo "✓ checkpoint placed" || echo "❌ checkpoint write failed — investigate before proceeding"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
If write failed (jq missing, permissions, etc.), HALT and surface to user — don't proceed without checkpoint protection.
|
|
78
|
+
|
|
79
|
+
**Updates during Step 1-4**: after each substep that produces value (paraphrase confirmed, edge-case round complete, spec written, audit verdict received), update the checkpoint's `resume_hint` field to reflect what state to resume from:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# After paraphrase confirmed
|
|
83
|
+
.harness/scripts/checkpoint-write.sh --feature <name> \
|
|
84
|
+
--stage-in-progress "$(jq -nc '{stage_number:1, files_total:0, files_done_list:[], files_remaining_list:[], last_action:\"Paraphrase confirmed\", resume_hint:\"Resume from edge-case round (you confirmed the happy path)\"}')"
|
|
85
|
+
|
|
86
|
+
# After edge-case round complete
|
|
87
|
+
.harness/scripts/checkpoint-write.sh --feature <name> \
|
|
88
|
+
--stage-in-progress "$(jq -nc '{...resume_hint:\"Resume from spec writing (edge cases gathered)\"}')"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This ensures `/pickup` reports are specific: not just "Stage 1 in progress" but "Stage 1 — edge-case round complete, ready to write spec".
|
|
92
|
+
|
|
93
|
+
## Step 1 — CEO happy path + paraphrase
|
|
94
|
+
|
|
95
|
+
Ask the CEO for the happy path in one sentence-or-paragraph, plain language (the example below uses a generic scenario; adapt to the actual feature):
|
|
96
|
+
|
|
97
|
+
> "Users open the app, type a phone number, get a code, type the code, and they're in."
|
|
98
|
+
|
|
99
|
+
If the CEO doesn't know the happy path, offer **2–3 generic patterns** ("for a feature like this, common shapes are: A, B, C — pick or describe your own"), let the CEO choose or modify, and document that the CEO selected from offered patterns (this stays in `## Decision history`).
|
|
100
|
+
|
|
101
|
+
Then **paraphrase** back: "I'm hearing X. Did I get that right?" Wait for confirmation or correction. Do not infer past what the CEO said.
|
|
102
|
+
|
|
103
|
+
> **Language mode reminder:** `{{language_mode}}`. If `plain` (default), every question is phrased so a non-engineer can answer. If `professional`, technical vocabulary is allowed in CEO-facing prompts.
|
|
104
|
+
|
|
105
|
+
## Step 2 — Edge-case round
|
|
106
|
+
|
|
107
|
+
Walk all categories listed in `{{edge_case_categories}}`. For each, raise 3–5 concrete scenarios that **could** apply to this feature, then ask the CEO which behaviors apply and how each should resolve. Keep questions in plain language; refuse to ask the CEO tech-flavored questions ("useState vs useReducer?" — never; "how should the app behave if the user double-taps the submit button?" — yes).
|
|
108
|
+
|
|
109
|
+
**MANDATORY: Show progress before each category** (CEO needs to know "how much more?"). Format (in CEO's OS locale):
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
🔍 边界场景检查 — N/8 完成
|
|
113
|
+
✅ 已完成: <已答的类别用 ① ② ③ 列出>
|
|
114
|
+
▶ 当前: <第 N+1 类>
|
|
115
|
+
▢ 接下来: <剩下的类别>
|
|
116
|
+
|
|
117
|
+
(你可以随时说「跳过这类」、「下一个」、「这类详细问」)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
After each category, also accept these meta-commands from the CEO:
|
|
121
|
+
- 「**跳过**」/「**skip**」 → mark this category as "no behaviors apply" and move to next
|
|
122
|
+
- 「**全部跳过**」/「**skip all**」 → finish the round, move to Step 3 (write spec)
|
|
123
|
+
- 「**重新问**」/「**re-ask**」 → re-raise the current category with different scenarios
|
|
124
|
+
|
|
125
|
+
**Default categories** (these ship with the harness; the user can edit `{{edge_case_categories}}` at /init or via `/constitution-edit` to add domain-specific ones):
|
|
126
|
+
|
|
127
|
+
1. **Input validation** — empty, too long, special characters, wrong format
|
|
128
|
+
2. **Network / external dependency** — disconnected, slow, mid-failure, timeout, third-party down
|
|
129
|
+
3. **Concurrency** — same action attempted twice, two devices at once, race conditions
|
|
130
|
+
4. **Permission / authentication** — not logged in, no permission, expired session, blocked, anonymous vs authenticated
|
|
131
|
+
5. **Lifecycle** — app force-quit, backgrounded, returned, mid-screen-transition
|
|
132
|
+
6. **Data state** — none, too many, corrupted, stale, fresh vs cached
|
|
133
|
+
7. **User mistake / abuse** — wrong order, repeating the same action, intentional misuse
|
|
134
|
+
8. **Domain / market specifics** — fill in at /init; covers norms, expectations, regulatory nuances unique to your domain
|
|
135
|
+
|
|
136
|
+
Question shape:
|
|
137
|
+
|
|
138
|
+
> "[Network / external dependency] If the user starts entering an OTP and their network drops mid-submit, options:
|
|
139
|
+
>
|
|
140
|
+
> a. Show 'connection failed', let them tap retry
|
|
141
|
+
> b. Auto-retry up to 3 times silently, then show error
|
|
142
|
+
> c. Treat as fatal, return them to the previous screen
|
|
143
|
+
>
|
|
144
|
+
> Which fits the experience you want?"
|
|
145
|
+
|
|
146
|
+
Move category by category. Skip categories that don't apply (e.g., concurrency rarely matters for a static welcome screen). Do not ask category-spanning questions; keep them tight.
|
|
147
|
+
|
|
148
|
+
## Step 3 — Write spec v1
|
|
149
|
+
|
|
150
|
+
After all applicable categories are walked, surface to the CEO a one-paragraph recap of the consensus (happy path + the edge-case behaviors decided) and ask: "Anything to add, change, or revisit before I draft the spec?"
|
|
151
|
+
|
|
152
|
+
**Wait for user response before continuing.**
|
|
153
|
+
|
|
154
|
+
Then write `{{spec_dir}}<name>.md` using the template below. This is **plain language only** — no library names, no code identifiers, no file paths.
|
|
155
|
+
|
|
156
|
+
```markdown
|
|
157
|
+
# Feature: <Feature Name>
|
|
158
|
+
|
|
159
|
+
## Status: DRAFT <YYYY-MM-DD>
|
|
160
|
+
|
|
161
|
+
## 1. What this feature is for
|
|
162
|
+
|
|
163
|
+
One paragraph, plain language: who uses it, what value it delivers.
|
|
164
|
+
|
|
165
|
+
## 2. Happy path
|
|
166
|
+
|
|
167
|
+
### 2.1 <Scenario name in plain language>
|
|
168
|
+
|
|
169
|
+
When the user does X, the system does Y, and the screen shows Z.
|
|
170
|
+
|
|
171
|
+
### 2.2 <Other scenario>
|
|
172
|
+
|
|
173
|
+
...
|
|
174
|
+
|
|
175
|
+
## 3. Edge-case behavior
|
|
176
|
+
|
|
177
|
+
### 3.1 <Situation in plain language>
|
|
178
|
+
|
|
179
|
+
#### Behavior (CEO sign-off)
|
|
180
|
+
|
|
181
|
+
- When the user does X
|
|
182
|
+
- The system does Y (different from happy path because ...)
|
|
183
|
+
- The user sees Z
|
|
184
|
+
|
|
185
|
+
#### Classification
|
|
186
|
+
|
|
187
|
+
[Required automated test] or [Smoke test only]
|
|
188
|
+
|
|
189
|
+
(Test IDs are NOT recorded in the CEO spec. After Stage 6 the resolved IDs land in `{{implementation_dir}}<feature>-implementation.md` § "Scenario → automated test map". The source-of-truth scenario↔test binding is the `// Verifies scenario X.Y` comment at the top of each test file.)
|
|
190
|
+
|
|
191
|
+
#### Smoke test procedure
|
|
192
|
+
|
|
193
|
+
**Reproduce:**
|
|
194
|
+
|
|
195
|
+
1. ...
|
|
196
|
+
2. ...
|
|
197
|
+
|
|
198
|
+
**Pass criteria:**
|
|
199
|
+
|
|
200
|
+
- ...
|
|
201
|
+
|
|
202
|
+
**Failure signals:**
|
|
203
|
+
|
|
204
|
+
- ...
|
|
205
|
+
|
|
206
|
+
### 3.2 <next scenario>
|
|
207
|
+
|
|
208
|
+
...
|
|
209
|
+
|
|
210
|
+
## 4. Who can use this
|
|
211
|
+
|
|
212
|
+
- Who has access (anonymous, signed-in, specific role)
|
|
213
|
+
- What conditions must be met
|
|
214
|
+
- When access is blocked
|
|
215
|
+
|
|
216
|
+
## 5. External dependencies (plain language)
|
|
217
|
+
|
|
218
|
+
- Which outside services this relies on — described in plain terms
|
|
219
|
+
- What happens when an external dependency fails
|
|
220
|
+
|
|
221
|
+
## 6. Deferred / unresolved
|
|
222
|
+
|
|
223
|
+
- Decisions intentionally postponed, with the reason
|
|
224
|
+
|
|
225
|
+
## 7. Out of scope
|
|
226
|
+
|
|
227
|
+
- What this feature explicitly does not cover
|
|
228
|
+
|
|
229
|
+
## 8. Decision history
|
|
230
|
+
|
|
231
|
+
- One line per significant decision: what + why (plain language)
|
|
232
|
+
|
|
233
|
+
## 9. (Audit mode only — leave empty in new-feature mode) Code vs spec delta
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Scenario classification rule:**
|
|
237
|
+
|
|
238
|
+
- **Required automated test** if the scenario hinges on data correctness, security/permission, business logic, error handling, or user data protection.
|
|
239
|
+
- **Smoke test only** if it's UI / animation / device-integration / timing-sensitive and the cost of automating outweighs the value.
|
|
240
|
+
|
|
241
|
+
Classification is **CEO-confirmed**, not silently assigned by Main Claude.
|
|
242
|
+
|
|
243
|
+
If the feature has tech detail worth recording (routing, components, state, access-control policies, library decisions, i18n keys), also write `{{implementation_dir}}<name>-implementation.md`. For a simple feature, skip it. Use this shape:
|
|
244
|
+
|
|
245
|
+
```markdown
|
|
246
|
+
# <Feature> — Implementation Notes
|
|
247
|
+
|
|
248
|
+
**Spec:** {{spec_dir}}<name>.md (canonical, CEO domain)
|
|
249
|
+
**This file:** technical detail, manager domain.
|
|
250
|
+
|
|
251
|
+
## Routing contract
|
|
252
|
+
|
|
253
|
+
...
|
|
254
|
+
|
|
255
|
+
## UI surface
|
|
256
|
+
|
|
257
|
+
...
|
|
258
|
+
|
|
259
|
+
## Data requirements
|
|
260
|
+
|
|
261
|
+
...
|
|
262
|
+
|
|
263
|
+
## External dependencies
|
|
264
|
+
|
|
265
|
+
<library + version + functions used>
|
|
266
|
+
|
|
267
|
+
## i18n
|
|
268
|
+
|
|
269
|
+
<key-by-key table covering all of {{supported_locales}}>
|
|
270
|
+
|
|
271
|
+
## Boundary contracts
|
|
272
|
+
|
|
273
|
+
<interfaces with other features>
|
|
274
|
+
|
|
275
|
+
## File locations
|
|
276
|
+
|
|
277
|
+
<code-location map per {{feature_folder_pattern}}>
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**For complex features with a manager-domain file**: When drafting the initial `<name>-implementation.md`, write functional requirements in **EARS notation** from the start. See `CLAUDE.md § Two-file feature spec model > EARS notation` for the format and variants.
|
|
281
|
+
|
|
282
|
+
Don't retroactively EARS-ify simple features (those without a manager file) — EARS is for the manager domain only, and simple features may not need one.
|
|
283
|
+
|
|
284
|
+
Surface both files to the CEO before invoking the auditor.
|
|
285
|
+
|
|
286
|
+
## Step 4 — Auditor external review
|
|
287
|
+
|
|
288
|
+
Run the auditor gate on the CEO spec with the spec-tuned adversarial preset (`.harness/scripts/auditor-prompts/adversarial-spec.md`). The preset wraps the focus text below with skeptical-review framing and a spec-shaped attack surface (ambiguous user paths, undefined error states, silent state transitions, multi-user races, undocumented dependencies, PII gaps, lifecycle/offline gaps).
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
AUDITOR_GATE_PRESET=adversarial-spec \
|
|
292
|
+
AUDITOR_GATE_TARGET_LABEL="<feature> Stage 1 new-feature spec" \
|
|
293
|
+
bash .harness/scripts/auditor-gate.sh review <feature> 1 \
|
|
294
|
+
"Review this Stage 1 (new-feature mode) spec. Main Claude and the CEO have walked an edge-case round through {{edge_case_categories}}; the spec reflects their consensus. Your job is the external-model layer. Beyond the preset's spec attack surface, also weigh: subtle scenarios within the categories that were missed (e.g., the spec covers 'network drop mid-OTP' but not 'network drop after OTP accepted, before session lands'); concerns outside the configured categories (domain norms specific to this project per constitution.md Section 2 — Project Identity; business-logic contradictions; cross-feature implications); spec-internal contradictions between sections. Do NOT flag: writing style, section ordering, suggestions to extend scope beyond declared, opinions on technical architecture (the spec is intentionally tech-term-free). Stay in plain-language territory." \
|
|
295
|
+
{{spec_dir}}<feature>.md
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Read the gate's exit code:
|
|
299
|
+
|
|
300
|
+
- **Exit 0 (PASS / CONCERNS / WAIVED)** — surface any advisory items to the CEO. For CONCERNS, also surface the logged warning path (`.harness/audits/concerns-*.json`) so the CEO weighs it before commit. For WAIVED, surface the `waiver_reason`. Stage 1 is one round closer to done. Proceed to Step 5.
|
|
301
|
+
- **Exit 2 (FAIL)** — surface every blocking item verbatim. Proceed to Step 5.
|
|
302
|
+
- **Exit 1 (script error / Universal Core WAIVED rejected / missing waiver_reason / legacy verdict)** — surface stderr, halt.
|
|
303
|
+
|
|
304
|
+
## Step 5 — CEO answers, spec v2
|
|
305
|
+
|
|
306
|
+
For each auditor finding, present to the CEO:
|
|
307
|
+
|
|
308
|
+
> "{{auditor_model}} flagged: [verbatim quote of the finding].
|
|
309
|
+
>
|
|
310
|
+
> Three resolutions:
|
|
311
|
+
> a. Accept and update the spec (here's the proposed change)
|
|
312
|
+
> b. Reject and document why (the concern is real but acceptable for our context)
|
|
313
|
+
> c. Defer to a future round (mark as known limitation)
|
|
314
|
+
>
|
|
315
|
+
> Which?"
|
|
316
|
+
|
|
317
|
+
CEO chooses. Update the spec. Repeat for each finding.
|
|
318
|
+
|
|
319
|
+
If the CEO disagrees with the auditor on something marked CRITICAL, follow the **CEO escalation pattern** (Constitution § 3):
|
|
320
|
+
|
|
321
|
+
> "Auditor disagreement on [item]:
|
|
322
|
+
>
|
|
323
|
+
> - Auditor view: A (reasoning: ...)
|
|
324
|
+
> - CEO view: B (reasoning: ...)
|
|
325
|
+
> User-result impact: [yes/no]
|
|
326
|
+
> Cost / maintenance impact: [yes/no]
|
|
327
|
+
> Security / risk trade-off: [yes/no]
|
|
328
|
+
>
|
|
329
|
+
> CEO decision overrides; documenting the reasoning in `## Decision history`. (Note: per Constitution § 3, CEO cannot override Universal Core items — if the auditor's finding maps to a Section 1 item, reformulation is required.)"
|
|
330
|
+
|
|
331
|
+
CEO decides; the decision goes into `## Decision history` regardless of which side wins.
|
|
332
|
+
|
|
333
|
+
## Step 6 — Run another auditor round if needed
|
|
334
|
+
|
|
335
|
+
Re-run the auditor gate after each spec revision. Stage 1 is **finalized** when:
|
|
336
|
+
|
|
337
|
+
1. The auditor's most recent round returns no new BLOCKING / STRONG findings, AND
|
|
338
|
+
2. CEO declares "OK, proceed."
|
|
339
|
+
|
|
340
|
+
Round count is unbounded — CEO has stop authority. The gate is the structural defense against premature finalization.
|
|
341
|
+
|
|
342
|
+
## Step 7 — Hand off to Stage 2
|
|
343
|
+
|
|
344
|
+
Once finalized:
|
|
345
|
+
|
|
346
|
+
- Surface to the CEO: "Stage 1 complete. CEO spec at `{{spec_dir}}<feature>.md`; implementation notes at `{{implementation_dir}}<feature>-implementation.md` (or 'not produced — feature simple enough')."
|
|
347
|
+
- Recommend: "Run `/spec-finalize <feature>` to mark FINALIZED with a final auditor cross-check."
|
|
348
|
+
|
|
349
|
+
Do not advance to Stage 2 silently — CEO triggers it.
|
|
350
|
+
|
|
351
|
+
## Trust contract
|
|
352
|
+
|
|
353
|
+
- **CEO owns intent.** Tech Lead paraphrases, surfaces options, runs the edge-case sweep, and writes the spec — but every behavior in the spec is CEO-confirmed.
|
|
354
|
+
- **Plain language is non-negotiable in the CEO file.** Tech terms (library names, code identifiers, framework jargon) belong in the implementation file or the code.
|
|
355
|
+
- **Auditor review is unconditional.** No skipping because "the spec looks fine" or "the feature is small." (Constitution § 1.)
|
|
356
|
+
- **Verdict is parsed from the gate's exit code**, not interpreted from prose.
|
|
357
|
+
- **CEO escalation has a fixed shape** so auditor disagreements are surfaced uniformly.
|
|
358
|
+
|
|
359
|
+
## Anti-patterns the skill blocks
|
|
360
|
+
|
|
361
|
+
- **Tech-term creep into the CEO spec.** Catch on review; rewrite as plain language.
|
|
362
|
+
- **Main Claude inferring past CEO intent.** If the CEO didn't say it, it's not in the spec — surface as a question instead.
|
|
363
|
+
- **Skipping the edge-case sweep "because the feature is small."** All applicable categories are walked; categories that don't apply produce no questions, but the walk is mandatory.
|
|
364
|
+
- **Silent classification of scenarios.** Each scenario's [Required automated test] vs [Smoke test only] tag is CEO-confirmed.
|
|
365
|
+
- **Bundling implementation detail into the CEO file.** The implementation file is where it lives.
|
|
366
|
+
- **Treating the auditor as advisory at Stage 1.** BLOCKING/STRONG items must be surfaced and resolved (accept / reject-with-reason / defer); ADVISORY items can pass.
|
|
367
|
+
|
|
368
|
+
## Completion criteria
|
|
369
|
+
|
|
370
|
+
Stage 1 in new-feature mode is complete when:
|
|
371
|
+
|
|
372
|
+
- `{{spec_dir}}<feature>.md` exists with every template section present (the CEO spec does NOT carry test IDs — that index lives in the implementation file's "Scenario → automated test map" once Stage 6 lands)
|
|
373
|
+
- `{{implementation_dir}}<feature>-implementation.md` exists when the feature warrants it (CEO and Tech Lead agree it does), or is explicitly skipped. If present, it may carry an empty "Scenario → automated test map" stub at this stage; the map fills in at Stage 6.
|
|
374
|
+
- The CEO spec is plain language end-to-end (no tech terms, no test file paths)
|
|
375
|
+
- Every scenario is classified [Required automated test] or [Smoke test only] with CEO sign-off
|
|
376
|
+
- The auditor's most recent round returned no new BLOCKING / STRONG findings
|
|
377
|
+
- CEO has declared "OK, proceed"
|
|
378
|
+
- The next step is `/spec-finalize <feature>`
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Checkpoint + decision-log integration (MAGI Archivist)
|
|
383
|
+
|
|
384
|
+
At successful completion, write the checkpoint (first stage = bootstraps the file):
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
.harness/scripts/checkpoint-write.sh \
|
|
388
|
+
--feature <feature-slug> \
|
|
389
|
+
--create-if-missing \
|
|
390
|
+
--mode new-feature \
|
|
391
|
+
--lane <full|stability-fix|trivial> \
|
|
392
|
+
--stage 2 \
|
|
393
|
+
--stage-complete 1 \
|
|
394
|
+
--artifact-spec docs/features/<feature-slug>.md \
|
|
395
|
+
[--artifact-implementation docs/features/<feature-slug>-implementation.md]
|
|
396
|
+
|
|
397
|
+
# If CEO made a material intent decision during paraphrase or edge-case rounds:
|
|
398
|
+
.harness/scripts/decision-log-append.sh \
|
|
399
|
+
--feature <feature-slug> --stage 1 --by "CEO" \
|
|
400
|
+
--decision "<one-line summary, e.g. 'edge case #3 race-condition is in scope'>"
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Skipping this step breaks `/pickup`** — MAGI Archivist depends on every stage writing here.
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## Final message to CEO (natural-language, not slash-command)
|
|
408
|
+
|
|
409
|
+
After completing Stage 1, do NOT print "next step: /spec-finalize". Instead, display (in CEO's OS locale):
|
|
410
|
+
|
|
411
|
+
```
|
|
412
|
+
✅ Stage 1 完成 — <feature> 的需求文档写好了
|
|
413
|
+
位置: docs/features/<feature>.md
|
|
414
|
+
MAGI Verdict 的初评: <PASS/CONCERNS/FAIL>, risk = N
|
|
415
|
+
|
|
416
|
+
接下来可以:
|
|
417
|
+
👉 「继续」/「下一步」/「OK」 — 我来做最终审查 (Stage 2)
|
|
418
|
+
👉 「看一下」 — 我把刚写的需求念给你听
|
|
419
|
+
👉 「改一下 + 你想改的内容」 — 重做需求,比如「再加一个边界场景」
|
|
420
|
+
👉 「放弃」 — 不做这个功能了
|
|
421
|
+
👉 直接告诉我下一件事 — 我会想办法理解
|
|
422
|
+
|
|
423
|
+
(我不需要你记 slash command — 用大白话和我说就行)
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
If CEO responds with "继续" / "OK" / "下一步" / "go" / "approve" → invoke `/spec-finalize <feature>` directly (transparent translation per CLAUDE.md § MAGI Core's Natural-Language Intent Translation). Don't ask "do you want me to run /spec-finalize?" — just do it after their go-ahead.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: handoff
|
|
3
|
+
description: |
|
|
4
|
+
Generate a rich 5-slot session-snapshot entry so a fresh terminal can resume this session's work. Trigger at ~95% context as one of the three options offered by budget-monitor; also user-invokable any time.
|
|
5
|
+
|
|
6
|
+
Trigger when the user:
|
|
7
|
+
- Selects "[2] /handoff" from the 95% budget menu
|
|
8
|
+
- Says "handoff / 转交会话 / 移交 / hand this off / fresh start with context / 开干净的接着干"
|
|
9
|
+
- Invokes /handoff explicitly
|
|
10
|
+
|
|
11
|
+
After this skill runs, the user typically /clear's or opens a new terminal. The next session's memory-recall will surface this snapshot at top of the manifest.
|
|
12
|
+
allowed-tools: Bash(git status:*), Bash(git rev-parse:*), Bash(git branch:*), Bash(git log:*), Bash(cat:*), Bash(jq:*), Bash(echo:*), Bash(mkdir:*), Bash(date:*), Read, Edit, Write
|
|
13
|
+
argument-hint: [optional note text]
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# /handoff
|
|
17
|
+
|
|
18
|
+
User-invoked session snapshot. Writes ONE structured entry to
|
|
19
|
+
`.harness/memory/sessions/recall/snapshots.jsonl` with kind `session-snapshot`.
|
|
20
|
+
|
|
21
|
+
> *Companion to the auto-snapshot path (`memory-snapshot.sh` PreCompaction hook). That fires automatically with limited context; this one is user-invoked when the user knows the session is about to end and wants a deliberate, rich snapshot.*
|
|
22
|
+
|
|
23
|
+
## When this skill is the right answer
|
|
24
|
+
|
|
25
|
+
- Context is at ~95% and you want a fresh terminal but don't want to lose state
|
|
26
|
+
- You're switching machines (e.g., laptop → desktop) and want resume on the other side
|
|
27
|
+
- You're about to step away for hours/days and want a clean handover note
|
|
28
|
+
- A long Stage 5 implementation needs to span 2+ sessions
|
|
29
|
+
|
|
30
|
+
## When this skill is NOT the right answer
|
|
31
|
+
|
|
32
|
+
- Below ~75% context → just continue (no need)
|
|
33
|
+
- Working on a totally different unrelated task → that's not a handoff, that's a new feature
|
|
34
|
+
- Want to permanently capture a one-off decision → use `/remember` instead
|
|
35
|
+
|
|
36
|
+
## What this skill produces
|
|
37
|
+
|
|
38
|
+
A single new line appended to `.harness/memory/sessions/recall/snapshots.jsonl`:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"id": "SS-2026053018a",
|
|
43
|
+
"ts": "2026-05-30T18:30:00Z",
|
|
44
|
+
"kind": "session-snapshot",
|
|
45
|
+
"feature": "auth",
|
|
46
|
+
"focus": "Resolve OTP race condition in middleware",
|
|
47
|
+
"decisions": [
|
|
48
|
+
{"id": "d-001", "rule": "WHEN form submits with code, THE SYSTEM SHALL validate before navigation"}
|
|
49
|
+
],
|
|
50
|
+
"open_problems": [
|
|
51
|
+
{"id": "p-001", "what": "Concurrent submissions cause double-charge", "blocked_by": "need DB advisory lock"}
|
|
52
|
+
],
|
|
53
|
+
"next_intent": "Implement advisory lock in src/auth/middleware.ts",
|
|
54
|
+
"files_touched": [
|
|
55
|
+
{"path": "src/auth/middleware.ts", "why": "added validation hook"}
|
|
56
|
+
],
|
|
57
|
+
"prev_session_id": "SS-2026052801",
|
|
58
|
+
"source": "handoff"
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Step-by-step
|
|
63
|
+
|
|
64
|
+
### Step 0 — Detect feature and prior snapshot
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
68
|
+
# Derive FEATURE the same way memory-recall.sh does (feat/X-... → X)
|
|
69
|
+
|
|
70
|
+
# Find prior snapshot for same feature (for prev_session_id)
|
|
71
|
+
PREV_ID=$(jq -r 'select(.feature == "'"$FEATURE"'") | .id' \
|
|
72
|
+
.harness/memory/sessions/recall/snapshots.jsonl 2>/dev/null | tail -1)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Step 1 — Draft each of the 5 slots
|
|
76
|
+
|
|
77
|
+
You (the AI) draft each slot based on:
|
|
78
|
+
- **focus** (≤200 chars, 1 line): What was THIS session about? Single sentence.
|
|
79
|
+
- **decisions[]** (0+ entries): Significant decisions made this session. Each entry has `id` (`d-001`...) and `rule` (preferably EARS-form: WHEN/IF... THE SYSTEM SHALL ...).
|
|
80
|
+
- **open_problems[]** (0+ entries): Things noticed but not solved. Each has `id` (`p-001`...), `what`, and `blocked_by`.
|
|
81
|
+
- **next_intent** (≤200 chars, 1 line): What does the next session need to do FIRST?
|
|
82
|
+
- **files_touched[]** (0+ entries): Files actually modified this session. Each: `{path, why}` where why is ≤80 chars.
|
|
83
|
+
|
|
84
|
+
Pull source material from:
|
|
85
|
+
- TodoWrite state (if any active list)
|
|
86
|
+
- `git status --short` (files in flight)
|
|
87
|
+
- `git log -5 --oneline` (recent commits this session)
|
|
88
|
+
- Conversation memory of decisions made
|
|
89
|
+
|
|
90
|
+
If `$ARGUMENTS` is non-empty, prepend it as extra context to focus.
|
|
91
|
+
|
|
92
|
+
### Step 2 — Show full draft to CEO for confirmation
|
|
93
|
+
|
|
94
|
+
Present in CEO's OS locale (per `CLAUDE.md § Language Awareness`):
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
─── Handoff Snapshot Draft ─────────────────────────────
|
|
98
|
+
|
|
99
|
+
feature : auth
|
|
100
|
+
focus : Resolve OTP race condition in middleware
|
|
101
|
+
|
|
102
|
+
decisions:
|
|
103
|
+
[d-001] WHEN form submits with code, THE SYSTEM SHALL validate before navigation
|
|
104
|
+
[d-002] WHEN code expired, THE SYSTEM SHALL surface error within 200ms
|
|
105
|
+
|
|
106
|
+
open_problems:
|
|
107
|
+
[p-001] Concurrent submissions cause double-charge
|
|
108
|
+
blocked_by: need DB advisory lock
|
|
109
|
+
|
|
110
|
+
next_intent : Implement advisory lock in src/auth/middleware.ts
|
|
111
|
+
|
|
112
|
+
files_touched:
|
|
113
|
+
src/auth/middleware.ts ← added validation hook
|
|
114
|
+
src/auth/types.ts ← OTP state enum
|
|
115
|
+
|
|
116
|
+
prev_session_id: SS-2026052801
|
|
117
|
+
|
|
118
|
+
Snapshot will be saved to:
|
|
119
|
+
.harness/memory/sessions/recall/snapshots.jsonl
|
|
120
|
+
|
|
121
|
+
Confirm?
|
|
122
|
+
[1] Yes, save and continue
|
|
123
|
+
[2] Edit slot: focus / decisions / open_problems / next_intent / files_touched
|
|
124
|
+
[3] Cancel (do not save)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Wait for user response.**
|
|
128
|
+
|
|
129
|
+
If `[2]`, ask which slot, accept edit, re-show. Loop until `[1]` or `[3]`.
|
|
130
|
+
|
|
131
|
+
### Step 3 — Write the entry
|
|
132
|
+
|
|
133
|
+
On `[1]`:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
mkdir -p .harness/memory/sessions/recall
|
|
137
|
+
|
|
138
|
+
TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
139
|
+
DATE_PART=$(echo "$TS" | cut -c1-10 | tr -d '-')
|
|
140
|
+
# Hash-based suffix avoids collisions when multiple snapshots same day
|
|
141
|
+
SUFFIX=$(echo "$TS$RANDOM" | shasum | cut -c1-3)
|
|
142
|
+
SS_ID="SS-${DATE_PART}${SUFFIX}"
|
|
143
|
+
|
|
144
|
+
ENTRY=$(jq -c -n \
|
|
145
|
+
--arg id "$SS_ID" \
|
|
146
|
+
--arg ts "$TS" \
|
|
147
|
+
--arg feature "$FEATURE" \
|
|
148
|
+
--arg focus "$FOCUS" \
|
|
149
|
+
--argjson decisions "$DECISIONS_JSON" \
|
|
150
|
+
--argjson open_problems "$OPEN_PROBLEMS_JSON" \
|
|
151
|
+
--arg next_intent "$NEXT_INTENT" \
|
|
152
|
+
--argjson files_touched "$FILES_TOUCHED_JSON" \
|
|
153
|
+
--arg prev_id "$PREV_ID" \
|
|
154
|
+
'{
|
|
155
|
+
id: $id,
|
|
156
|
+
ts: $ts,
|
|
157
|
+
kind: "session-snapshot",
|
|
158
|
+
feature: (if $feature=="" then null else $feature end),
|
|
159
|
+
focus: $focus,
|
|
160
|
+
decisions: $decisions,
|
|
161
|
+
open_problems: $open_problems,
|
|
162
|
+
next_intent: $next_intent,
|
|
163
|
+
files_touched: $files_touched,
|
|
164
|
+
prev_session_id: (if $prev_id=="" then null else $prev_id end),
|
|
165
|
+
source: "handoff"
|
|
166
|
+
}')
|
|
167
|
+
|
|
168
|
+
echo "$ENTRY" >> .harness/memory/sessions/recall/snapshots.jsonl
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Step 4 — Squash-merge against prior same-feature snapshot
|
|
172
|
+
|
|
173
|
+
If `PREV_ID` is set AND prior snapshot is <7 days old:
|
|
174
|
+
|
|
175
|
+
- Read the prior snapshot's `decisions[]` and `open_problems[]`.
|
|
176
|
+
- Identify items in prior that are NOT contradicted/superseded by current snapshot's items.
|
|
177
|
+
- Append non-contradicted items to current snapshot's lists (preserving id, marking `from: <prev_id>`).
|
|
178
|
+
- Mark prior snapshot as `superseded_by: <current_id>` by APPENDING a new override line (don't edit-in-place; jsonl is append-only).
|
|
179
|
+
|
|
180
|
+
This keeps chains bounded — a long-running feature accumulates current state in one rolling snapshot, not a growing chain.
|
|
181
|
+
|
|
182
|
+
### Step 5 — Tell CEO next steps
|
|
183
|
+
|
|
184
|
+
In CEO's locale:
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
✓ Snapshot saved (id=SS-2026053018a)
|
|
188
|
+
|
|
189
|
+
Next:
|
|
190
|
+
→ /clear — clears current session, fresh start with this snapshot at top of manifest
|
|
191
|
+
→ Or open a new terminal — same effect, snapshot will be in the recall manifest
|
|
192
|
+
|
|
193
|
+
In the next session:
|
|
194
|
+
- SessionStart will show "[SS-2026053018a] feature=auth ..." in manifest
|
|
195
|
+
- If you (or AI) need the body: /recall SS-2026053018a
|
|
196
|
+
- The snapshot's next_intent is your starting point
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Do NOT auto-invoke `/clear` — that's the CEO's choice.
|
|
200
|
+
|
|
201
|
+
## Trust contract
|
|
202
|
+
|
|
203
|
+
- Writes to **exactly one file**: `.harness/memory/sessions/recall/snapshots.jsonl`
|
|
204
|
+
- Squash-merge (Step 4) only appends; never edits older lines in place
|
|
205
|
+
- Never auto-clears the session (CEO's prerogative)
|
|
206
|
+
- Never writes if Step 2 is cancelled
|
|
207
|
+
|
|
208
|
+
## Completion criteria
|
|
209
|
+
|
|
210
|
+
- Snapshot entry appended (Step 3) and CEO has seen Step 5 next-steps message, OR
|
|
211
|
+
- CEO cancelled at Step 2 (nothing written)
|