@gordon.gan/specflow 1.0.2 → 1.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.
@@ -39,7 +39,7 @@ export function registerChangeArchiveCommand(changeCmd) {
39
39
  changeCmd
40
40
  .command('archive <name>')
41
41
  .description('Archive a completed change')
42
- .option('--force', 'Archive even if the change is not in phase=built')
42
+ .option('--force', 'Archive even if the change is not in phase=apply')
43
43
  .action(async (name, opts) => {
44
44
  const projectRoot = requireProjectRoot();
45
45
  const result = await archiveChangeCommand(name, projectRoot, {
@@ -34,7 +34,7 @@ export async function createChange(name, projectRoot) {
34
34
  const metadata = {
35
35
  schema: 'specflow',
36
36
  created: todayDate(),
37
- phase: 'plan',
37
+ phase: 'propose',
38
38
  };
39
39
  await writeChangeMetadata(name, metadata, projectRoot);
40
40
  }
@@ -28,6 +28,6 @@ export declare function setPhase(name: string, phase: string, projectRoot: strin
28
28
  *
29
29
  * Usage:
30
30
  * specflow change phase <name> # print current phase
31
- * specflow change phase <name> --set propose # update phase
31
+ * specflow change phase <name> --set refined # update phase
32
32
  */
33
33
  export declare function registerChangePhaseCommand(changeCmd: Command): void;
@@ -3,13 +3,13 @@
3
3
  *
4
4
  * Read or update the lifecycle phase of a change.
5
5
  */
6
- import { CHANGE_PHASES, getChangeMetadata, updatePhase, } from '../../utils/change-utils.js';
6
+ import { CHANGE_PHASES, getChangeMetadata, normalizeChangePhase, updatePhase, } from '../../utils/change-utils.js';
7
7
  import { requireProjectRoot } from '../../utils/project-root.js';
8
8
  /**
9
9
  * Returns true if the candidate value is a valid ChangePhase.
10
10
  */
11
- function isChangePhase(value) {
12
- return CHANGE_PHASES.includes(value);
11
+ function resolveChangePhase(value) {
12
+ return normalizeChangePhase(value);
13
13
  }
14
14
  /**
15
15
  * Reads the current phase of a change.
@@ -35,18 +35,19 @@ export async function getPhase(name, projectRoot) {
35
35
  * @throws When the phase is invalid or the change does not exist
36
36
  */
37
37
  export async function setPhase(name, phase, projectRoot) {
38
- if (!isChangePhase(phase)) {
38
+ const resolved = resolveChangePhase(phase);
39
+ if (resolved === null) {
39
40
  const valid = CHANGE_PHASES.join(' | ');
40
41
  throw new Error(`Invalid phase "${phase}": expected one of ${valid}`);
41
42
  }
42
- await updatePhase(name, phase, projectRoot);
43
+ await updatePhase(name, resolved, projectRoot);
43
44
  }
44
45
  /**
45
46
  * Registers the `change phase` subcommand with Commander.
46
47
  *
47
48
  * Usage:
48
49
  * specflow change phase <name> # print current phase
49
- * specflow change phase <name> --set propose # update phase
50
+ * specflow change phase <name> --set refined # update phase
50
51
  */
51
52
  export function registerChangePhaseCommand(changeCmd) {
52
53
  changeCmd
@@ -13,7 +13,7 @@ export interface ArchiveResult {
13
13
  }
14
14
  export interface ArchiveOptions {
15
15
  /**
16
- * If true, archive even when the change is not in phase=built.
16
+ * If true, archive even when the change is not in phase=apply.
17
17
  * A warning is emitted to stderr when force is applied.
18
18
  */
19
19
  readonly force?: boolean;
@@ -22,7 +22,7 @@ export interface ArchiveOptions {
22
22
  * Archive a completed change.
23
23
  *
24
24
  * Steps:
25
- * 0. Validate phase gate: require phase=built unless options.force is true
25
+ * 0. Validate phase gate: require phase=apply unless options.force is true
26
26
  * 1. Find all delta spec files in `specflow/changes/<name>/specs/`
27
27
  * 2. Validate each delta spec
28
28
  * 3. For each delta spec, find the corresponding main spec in `specflow/specs/`
@@ -54,7 +54,7 @@ function todayDatePrefix() {
54
54
  * Archive a completed change.
55
55
  *
56
56
  * Steps:
57
- * 0. Validate phase gate: require phase=built unless options.force is true
57
+ * 0. Validate phase gate: require phase=apply unless options.force is true
58
58
  * 1. Find all delta spec files in `specflow/changes/<name>/specs/`
59
59
  * 2. Validate each delta spec
60
60
  * 3. For each delta spec, find the corresponding main spec in `specflow/specs/`
@@ -72,19 +72,19 @@ export async function archiveChange(changeName, projectRoot, options = {}) {
72
72
  const changeDir = join(projectRoot, 'specflow', 'changes', changeName);
73
73
  const deltaSpecsDir = join(changeDir, 'specs');
74
74
  const mainSpecsDir = join(projectRoot, 'specflow', 'specs');
75
- // 0. Phase gate: require phase=built unless --force
75
+ // 0. Phase gate: require phase=apply unless --force
76
76
  const metadata = await readChangeMetadata(changeDir);
77
77
  const currentPhase = metadata?.phase;
78
- if (currentPhase !== 'built' && !options.force) {
78
+ if (currentPhase !== 'apply' && !options.force) {
79
79
  const phaseLabel = currentPhase ?? 'unknown';
80
80
  return {
81
81
  success: false,
82
82
  errors: [
83
- `Cannot archive: change '${changeName}' is in phase '${phaseLabel}', expected 'built'. Complete '/specflow:apply' first, or pass '--force' to archive anyway.`,
83
+ `Cannot archive: change '${changeName}' is in phase '${phaseLabel}', expected 'apply'. Complete '/specflow:apply' first, or pass '--force' to archive anyway.`,
84
84
  ],
85
85
  };
86
86
  }
87
- if (options.force && currentPhase !== 'built') {
87
+ if (options.force && currentPhase !== 'apply') {
88
88
  const phaseLabel = currentPhase ?? 'unknown';
89
89
  console.warn(`Warning: archiving "${changeName}" in phase ${phaseLabel} with --force. ` +
90
90
  `Consider running /specflow:apply first.`);
@@ -86,12 +86,12 @@ export declare const SchemaYamlSchema: z.ZodObject<{
86
86
  requires: string[];
87
87
  instruction?: string | undefined;
88
88
  }[];
89
- description?: string | undefined;
90
89
  apply?: {
91
90
  requires: string[];
92
91
  instruction?: string | undefined;
93
92
  tracks?: string | null | undefined;
94
93
  } | undefined;
94
+ description?: string | undefined;
95
95
  }, {
96
96
  name: string;
97
97
  version: number;
@@ -102,12 +102,12 @@ export declare const SchemaYamlSchema: z.ZodObject<{
102
102
  instruction?: string | undefined;
103
103
  requires?: string[] | undefined;
104
104
  }[];
105
- description?: string | undefined;
106
105
  apply?: {
107
106
  requires: string[];
108
107
  instruction?: string | undefined;
109
108
  tracks?: string | null | undefined;
110
109
  } | undefined;
110
+ description?: string | undefined;
111
111
  }>;
112
112
  /** A single artifact definition. */
113
113
  export type Artifact = z.infer<typeof ArtifactSchema>;
@@ -4,6 +4,7 @@ import { COMMAND_CATALOG } from '../shared/command-catalog.js';
4
4
  import { directoryExists, hashDirectoryTree } from '../shared/asset-hash.js';
5
5
  import { deriveCapabilityIds } from '../shared/capability-evidence.js';
6
6
  import { copyRuntimeAssets } from '../shared/runtime-assets.js';
7
+ import { removeRetiredManagedAssets } from '../shared/retired-commands.js';
7
8
  import { renderIdeContent } from '../shared/skill-renderer.js';
8
9
  function extractSkillDescription(content, fallback) {
9
10
  const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
@@ -56,6 +57,7 @@ async function generateClaude(ctx) {
56
57
  await fs.writeFile(join(commandsDestDir, `${command.id}.md`), generateCommandAlias(command.id, description), 'utf-8');
57
58
  }
58
59
  const runtimePaths = await copyRuntimeAssets(packageRoot, projectRoot, 'claude');
60
+ await removeRetiredManagedAssets(projectRoot, 'claude');
59
61
  return {
60
62
  ide: 'claude',
61
63
  generatedPaths: ['.claude/skills', '.claude/commands/specflow', ...runtimePaths],
@@ -5,7 +5,9 @@ import { directoryExists, hashDirectoryTree, hashFile } from '../shared/asset-ha
5
5
  import { deriveCapabilityIds } from '../shared/capability-evidence.js';
6
6
  import { managedBlockPresent, upsertManagedBlock } from '../shared/marker-write.js';
7
7
  import { copyRuntimeAssets } from '../shared/runtime-assets.js';
8
+ import { removeRetiredManagedAssets } from '../shared/retired-commands.js';
8
9
  import { renderIdeContent, validateSkillFrontmatter } from '../shared/skill-renderer.js';
10
+ import { renderPhaseGateBullet } from '../shared/phase-context.js';
9
11
  const CODEX_AGENTS_MARKER_START = '# Added by specflow init (Codex context)';
10
12
  const CODEX_AGENTS_MARKER_END = '# End specflow init (Codex context)';
11
13
  function extractSkillDescription(content, fallback) {
@@ -33,7 +35,7 @@ function renderCodexAgentsBlock() {
33
35
  return [
34
36
  '# SpecFlow Codex Context',
35
37
  '',
36
- '- Respect specflow phase gates: plan -> refined -> built -> archived',
38
+ renderPhaseGateBullet(),
37
39
  '- Never bypass CLI validation and archive checks',
38
40
  '- Use artifacts under specflow/changes/<change>/ before coding',
39
41
  '- Workflow skills: $specflow-explore, $specflow-propose, $specflow-refine, $specflow-apply, $specflow-review, $specflow-test, $specflow-verify, $specflow-archive, $specflow-fix, $specflow-snap',
@@ -82,6 +84,7 @@ async function generateCodex(ctx) {
82
84
  }
83
85
  const runtimePaths = await copyRuntimeAssets(packageRoot, projectRoot, 'codex');
84
86
  await upsertManagedBlock(join(projectRoot, 'AGENTS.md'), CODEX_AGENTS_MARKER_START, CODEX_AGENTS_MARKER_END, renderCodexAgentsBlock().split('\n'));
87
+ await removeRetiredManagedAssets(projectRoot, 'codex');
85
88
  return {
86
89
  ide: 'codex',
87
90
  generatedPaths: ['.agents/skills', 'AGENTS.md', ...runtimePaths],
@@ -4,7 +4,9 @@ import { COMMAND_CATALOG } from '../shared/command-catalog.js';
4
4
  import { directoryExists, fileExists, hashDirectoryTree, hashFile } from '../shared/asset-hash.js';
5
5
  import { deriveCapabilityIds } from '../shared/capability-evidence.js';
6
6
  import { copyRuntimeAssets } from '../shared/runtime-assets.js';
7
+ import { removeRetiredManagedAssets } from '../shared/retired-commands.js';
7
8
  import { renderIdeContent } from '../shared/skill-renderer.js';
9
+ import { renderPhaseGateBullet } from '../shared/phase-context.js';
8
10
  function extractSkillDescription(content, fallback) {
9
11
  const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
10
12
  if (!frontmatterMatch) {
@@ -26,7 +28,7 @@ function renderCursorRule() {
26
28
  return [
27
29
  '# SpecFlow Cursor Context',
28
30
  '',
29
- '- Respect specflow phase gates: propose -> refined -> built -> archived',
31
+ renderPhaseGateBullet(),
30
32
  '- Never bypass CLI validation and archive checks',
31
33
  '- Use artifacts under specflow/changes/<change>/ before coding',
32
34
  '',
@@ -69,6 +71,7 @@ async function generateCursor(ctx) {
69
71
  }
70
72
  await fs.writeFile(join(rulesDir, 'specflow-context.mdc'), renderCursorRule(), 'utf-8');
71
73
  const runtimePaths = await copyRuntimeAssets(packageRoot, projectRoot, 'cursor');
74
+ await removeRetiredManagedAssets(projectRoot, 'cursor');
72
75
  return {
73
76
  ide: 'cursor',
74
77
  generatedPaths: [
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Human-readable phase gate chain for IDE context blocks (AGENTS.md, Cursor rules).
3
+ * Must stay aligned with {@link CHANGE_PHASES}.
4
+ */
5
+ export declare const PHASE_GATE_CHAIN: string;
6
+ export declare function renderPhaseGateBullet(): string;
@@ -0,0 +1,9 @@
1
+ import { CHANGE_PHASES } from '../../utils/change-metadata.js';
2
+ /**
3
+ * Human-readable phase gate chain for IDE context blocks (AGENTS.md, Cursor rules).
4
+ * Must stay aligned with {@link CHANGE_PHASES}.
5
+ */
6
+ export const PHASE_GATE_CHAIN = CHANGE_PHASES.join(' -> ');
7
+ export function renderPhaseGateBullet() {
8
+ return `- Respect specflow phase gates: ${PHASE_GATE_CHAIN}`;
9
+ }
@@ -0,0 +1,10 @@
1
+ import type { IdeTarget } from './types.js';
2
+ /**
3
+ * Command IDs removed from the public catalog (v1.0.1+ rename, v1.0.2 scan removal).
4
+ * Init/sync must delete these managed assets so upgraded projects do not keep stale skills.
5
+ */
6
+ export declare const RETIRED_COMMAND_IDS: readonly ["plan", "build", "done", "scan"];
7
+ /**
8
+ * Removes obsolete specflow command aliases and skills left from pre-v1.0.1 installs.
9
+ */
10
+ export declare function removeRetiredManagedAssets(projectRoot: string, ide: IdeTarget): Promise<void>;
@@ -0,0 +1,42 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ /**
4
+ * Command IDs removed from the public catalog (v1.0.1+ rename, v1.0.2 scan removal).
5
+ * Init/sync must delete these managed assets so upgraded projects do not keep stale skills.
6
+ */
7
+ export const RETIRED_COMMAND_IDS = ['plan', 'build', 'done', 'scan'];
8
+ async function removeIfExists(path) {
9
+ try {
10
+ await fs.rm(path, { recursive: true, force: true });
11
+ }
12
+ catch {
13
+ // ignore missing paths
14
+ }
15
+ }
16
+ /**
17
+ * Removes obsolete specflow command aliases and skills left from pre-v1.0.1 installs.
18
+ */
19
+ export async function removeRetiredManagedAssets(projectRoot, ide) {
20
+ if (ide === 'claude') {
21
+ const commandsDir = join(projectRoot, '.claude', 'commands', 'specflow');
22
+ const skillsDir = join(projectRoot, '.claude', 'skills');
23
+ for (const id of RETIRED_COMMAND_IDS) {
24
+ await removeIfExists(join(commandsDir, `${id}.md`));
25
+ await removeIfExists(join(skillsDir, `specflow-${id}`));
26
+ }
27
+ return;
28
+ }
29
+ if (ide === 'cursor') {
30
+ const commandsDir = join(projectRoot, '.cursor', 'commands', 'specflow');
31
+ const skillsDir = join(projectRoot, '.cursor', 'skills');
32
+ for (const id of RETIRED_COMMAND_IDS) {
33
+ await removeIfExists(join(commandsDir, `${id}.md`));
34
+ await removeIfExists(join(skillsDir, `specflow-${id}`));
35
+ }
36
+ return;
37
+ }
38
+ const skillsDir = join(projectRoot, '.agents', 'skills');
39
+ for (const id of RETIRED_COMMAND_IDS) {
40
+ await removeIfExists(join(skillsDir, `specflow-${id}`));
41
+ }
42
+ }
@@ -1,12 +1,16 @@
1
1
  /**
2
2
  * All valid lifecycle phases a change can be in.
3
3
  *
4
- * - `plan`: Initial phase, created by `specflow change new`.
4
+ * Aligned with workflow command names (propose / refine / apply / archive).
5
+ *
6
+ * - `propose`: Initial phase, created by `specflow change new`.
5
7
  * - `refined`: After `/specflow:refine` iterative deep review completes.
6
- * - `built`: After `/specflow:apply` Phase B execution finishes.
8
+ * - `apply`: After `/specflow:apply` Phase B execution finishes.
7
9
  * - `archived`: Post-archive state; assigned automatically on successful archive.
8
10
  */
9
- export declare const CHANGE_PHASES: readonly ["plan", "refined", "built", "archived"];
11
+ export declare const CHANGE_PHASES: readonly ["propose", "refined", "apply", "archived"];
12
+ /** Legacy phase values from v1.0.x and earlier. */
13
+ export declare const LEGACY_CHANGE_PHASES: readonly ["plan", "built"];
10
14
  /**
11
15
  * Union type of valid lifecycle phases.
12
16
  */
@@ -20,6 +24,10 @@ export interface ChangeMetadata {
20
24
  readonly phase?: ChangePhase;
21
25
  readonly [key: string]: unknown;
22
26
  }
27
+ /**
28
+ * Normalizes a phase string for CLI input (accepts legacy aliases).
29
+ */
30
+ export declare function normalizeChangePhase(phase: string): ChangePhase | null;
23
31
  /**
24
32
  * Reads change metadata from .specflow.yaml in the change directory.
25
33
  *
@@ -6,12 +6,28 @@ const METADATA_FILENAME = '.specflow.yaml';
6
6
  /**
7
7
  * All valid lifecycle phases a change can be in.
8
8
  *
9
- * - `plan`: Initial phase, created by `specflow change new`.
9
+ * Aligned with workflow command names (propose / refine / apply / archive).
10
+ *
11
+ * - `propose`: Initial phase, created by `specflow change new`.
10
12
  * - `refined`: After `/specflow:refine` iterative deep review completes.
11
- * - `built`: After `/specflow:apply` Phase B execution finishes.
13
+ * - `apply`: After `/specflow:apply` Phase B execution finishes.
12
14
  * - `archived`: Post-archive state; assigned automatically on successful archive.
13
15
  */
14
- export const CHANGE_PHASES = ['plan', 'refined', 'built', 'archived'];
16
+ export const CHANGE_PHASES = ['propose', 'refined', 'apply', 'archived'];
17
+ /** Legacy phase values from v1.0.x and earlier. */
18
+ export const LEGACY_CHANGE_PHASES = ['plan', 'built'];
19
+ function normalizePhaseValue(value) {
20
+ if (typeof value !== 'string') {
21
+ return value;
22
+ }
23
+ if (value === 'plan') {
24
+ return 'propose';
25
+ }
26
+ if (value === 'built') {
27
+ return 'apply';
28
+ }
29
+ return value;
30
+ }
15
31
  /**
16
32
  * Zod schema for validating change metadata.
17
33
  *
@@ -22,9 +38,21 @@ const changeMetadataSchema = z
22
38
  .object({
23
39
  schema: z.string(),
24
40
  created: z.string(),
25
- phase: z.enum(CHANGE_PHASES).optional(),
41
+ phase: z.preprocess(normalizePhaseValue, z.enum(CHANGE_PHASES).optional()),
26
42
  })
27
43
  .passthrough();
44
+ /**
45
+ * Normalizes a phase string for CLI input (accepts legacy aliases).
46
+ */
47
+ export function normalizeChangePhase(phase) {
48
+ const normalized = normalizePhaseValue(phase);
49
+ if (typeof normalized !== 'string') {
50
+ return null;
51
+ }
52
+ return CHANGE_PHASES.includes(normalized)
53
+ ? normalized
54
+ : null;
55
+ }
28
56
  /**
29
57
  * Formats a Zod error into a user-friendly message.
30
58
  *
@@ -34,7 +62,7 @@ const changeMetadataSchema = z
34
62
  function formatZodError(error, metaPath) {
35
63
  const hasInvalidPhase = error.issues.some((issue) => issue.path[0] === 'phase' && issue.code === 'invalid_enum_value');
36
64
  if (hasInvalidPhase) {
37
- return new Error('Invalid phase in metadata: expected one of plan|refined|built|archived');
65
+ return new Error('Invalid phase in metadata: expected one of propose|refined|apply|archived');
38
66
  }
39
67
  return new Error(`Invalid metadata format in ${metaPath}: ${error.message}`);
40
68
  }
@@ -1,5 +1,5 @@
1
1
  import type { ChangeMetadata, ChangePhase } from './change-metadata.js';
2
- export { CHANGE_PHASES } from './change-metadata.js';
2
+ export { CHANGE_PHASES, normalizeChangePhase } from './change-metadata.js';
3
3
  export type { ChangePhase } from './change-metadata.js';
4
4
  /**
5
5
  * Validates that a change name uses only lowercase alphanumeric characters
@@ -1,7 +1,7 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { readChangeMetadata, writeChangeMetadata as writeMetaInternal } from './change-metadata.js';
4
- export { CHANGE_PHASES } from './change-metadata.js';
4
+ export { CHANGE_PHASES, normalizeChangePhase } from './change-metadata.js';
5
5
  const CHANGES_REL_PATH = 'specflow/changes';
6
6
  const VALID_CHANGE_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/;
7
7
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gordon.gan/specflow",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "SpecFlow — unified spec-driven development: OpenSpec planning + Superpowers execution in one CLI and cross-IDE workflow",
6
6
  "keywords": [
@@ -7,7 +7,7 @@
7
7
  ## Purpose Declaration
8
8
 
9
9
  **This prompt is for REWRITING an existing tasks.md into writing-plans precision.**
10
- **NOT for generating tasks.md from scratch.** The tasks.md already exists from the `plan` or `refine` phase. Your job is to take a coarse-grained first-iteration propose (or a refine-updated version of it) and transform each checkbox into a 2-5 minute atomic task that a fresh subagent can execute without additional context.
10
+ **NOT for generating tasks.md from scratch.** The tasks.md already exists from the `propose` or `refine` phase. Your job is to take a coarse-grained first-iteration propose (or a refine-updated version of it) and transform each checkbox into a 2-5 minute atomic task that a fresh subagent can execute without additional context.
11
11
 
12
12
  **Announce at start:** "I'm using specflow:apply Phase A to rewrite the existing propose into writing-plans precision."
13
13
 
@@ -67,7 +67,7 @@ Signals that you've found a real decision:
67
67
  - "The naive approach is X, but it breaks when Z"
68
68
  - "The proposal implies X but there's a tension with Y"
69
69
 
70
- If during this first iteration you are genuinely uncertain about an option, **mark the decision `(plan-phase analysis, may revise in refine)`**. Honesty about uncertainty is better than false certainty — refine exists precisely to close these gaps. Do NOT use this marker as an escape hatch for every decision; use it only where you actually lack information.
70
+ If during this first iteration you are genuinely uncertain about an option, **mark the decision `(propose-phase analysis, may revise in refine)`**. Honesty about uncertainty is better than false certainty — refine exists precisely to close these gaps. Do NOT use this marker as an escape hatch for every decision; use it only where you actually lack information.
71
71
 
72
72
  ### 4. Risks / Trade-offs
73
73
 
@@ -111,13 +111,13 @@ Save the document to:
111
111
  specflow/changes/<change-name>/design.md
112
112
  ```
113
113
 
114
- **Do NOT ask the user to confirm this draft at this stage.** The end-of-propose summary (handled by the orchestrating SKILL) will present all four plan-phase artifacts together for one cohesive user review. Write the best first-iteration design you can, save it, and move on to the next plan-phase artifact.
114
+ **Do NOT ask the user to confirm this draft at this stage.** The end-of-propose summary (handled by the orchestrating SKILL) will present all four propose-phase artifacts together for one cohesive user review. Write the best first-iteration design you can, save it, and move on to the next propose-phase artifact.
115
115
 
116
116
  ## Remember
117
117
 
118
118
  - First iteration, not placeholder
119
119
  - Substance over structure — skip sections with "N/A" rather than fake-filling them
120
120
  - Reference example is a depth guide, not a copy target
121
- - Honest uncertainty > false certainty — use the `(plan-phase analysis, may revise in refine)` marker when genuinely uncertain
121
+ - Honest uncertainty > false certainty — use the `(propose-phase analysis, may revise in refine)` marker when genuinely uncertain
122
122
  - No quantity gates — quality is judged by user review
123
- - No confirmation gate here — the SKILL handles overall plan-phase review
123
+ - No confirmation gate here — the SKILL handles overall propose-phase review
@@ -112,7 +112,7 @@ Save the document to:
112
112
  specflow/changes/<change-name>/tasks.md
113
113
  ```
114
114
 
115
- **Do NOT ask the user to confirm this draft at this stage.** The orchestrating SKILL will present all plan-phase artifacts together at the end of the propose phase. Refine will challenge and restructure; Apply Phase A will rewrite to precision. Your job is to hand off a substantive first pass.
115
+ **Do NOT ask the user to confirm this draft at this stage.** The orchestrating SKILL will present all propose-phase artifacts together at the end of the propose phase. Refine will challenge and restructure; Apply Phase A will rewrite to precision. Your job is to hand off a substantive first pass.
116
116
 
117
117
  ## Remember
118
118
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  # Refine-Phase Brainstorming: Attacking Deep Review
6
6
 
7
- > This prompt is for **ATTACKING DEEP REVIEW** of plan-phase artifacts.
7
+ > This prompt is for **ATTACKING DEEP REVIEW** of propose-phase artifacts.
8
8
  > NOT from-scratch brainstorming. The 4 artifacts (proposal, specs, design, tasks)
9
9
  > already exist and must be challenged, deepened, and possibly updated.
10
10
 
@@ -48,7 +48,7 @@ injected; steps 3–5 carry the discussion into artifacts; steps 6–8 close the
48
48
 
49
49
  ### Step 1 — Examine existing artifacts (inject Challenge Behavior #1)
50
50
 
51
- Re-read all 4 plan-phase artifacts with fresh eyes. This is not a summary exercise — look for
51
+ Re-read all 4 propose-phase artifacts with fresh eyes. This is not a summary exercise — look for
52
52
  what the propose *did not* say. Explicitly INJECT **Challenge Behavior #1 (challenge plan's
53
53
  assumptions)** here: list assumptions the propose glossed over and explain why each matters. Each
54
54
  round MUST produce this list of challenged assumptions, even if shorter than the previous round.
@@ -115,7 +115,7 @@ If the user rejects, return to Stage B2a for the same task.
115
115
  ### Stage B3: Phase Transition
116
116
 
117
117
  Once all tasks are confirmed:
118
- - Invoke `specflow change phase <name> --set built` to transition the phase.
118
+ - Invoke `specflow change phase <name> --set apply` to transition the phase.
119
119
  - Summarize the build results and overall coverage.
120
120
  - Inform the user they can now run `/specflow:review` or `/specflow:verify`.
121
121
 
@@ -123,7 +123,7 @@ Once all tasks are confirmed:
123
123
 
124
124
  ## Phase Transition Notes
125
125
 
126
- - **On Phase B success**: phase → `built` (automatic via Stage B3 CLI call).
126
+ - **On Phase B success**: phase → `apply` (automatic via Stage B3 CLI call).
127
127
  - **On Phase A gap halt (Outcome 3)**: phase remains `refined`. No transition. User runs `/specflow:refine` to close gaps, then re-runs `/specflow:apply`.
128
128
  - **On Phase A reorganization choice C**: phase remains `refined`. No transition. User edits groups manually, then re-runs `/specflow:apply`.
129
129
  - **On Phase B interruption or failing tests**: phase remains `refined`. User resumes Phase B later.
@@ -11,7 +11,7 @@ description: "Archive change + merge specs + git branch cleanup"
11
11
 
12
12
  - An active change must exist with completed implementation.
13
13
  - `specflow` CLI must be available on PATH.
14
- - **Phase must be `built`** in `.specflow.yaml`. This is set automatically when `/specflow:apply` Phase B completes. `specflow change archive` refuses to archive changes whose phase is not `built` unless the caller passes `--force` explicitly. Do NOT pass `--force` from this skill — the flag is reserved for explicit user discretion. If archive fails with a phase check error, route the user back to `/specflow:apply` to complete the missing phase transition rather than forcing past the guard.
14
+ - **Phase must be `apply`** in `.specflow.yaml`. This is set automatically when `/specflow:apply` Phase B completes. `specflow change archive` refuses to archive changes whose phase is not `apply` unless the caller passes `--force` explicitly. Do NOT pass `--force` from this skill — the flag is reserved for explicit user discretion. If archive fails with a phase check error, route the user back to `/specflow:apply` to complete the missing phase transition rather than forcing past the guard.
15
15
 
16
16
  ## Stage 1: Test Gate
17
17
 
@@ -41,7 +41,7 @@ If no active change directory exists for this work:
41
41
  specflow change new <name>
42
42
  ```
43
43
 
44
- The CLI sets `phase=plan` in `.specflow.yaml`. Explore does not change phase — `explore.md` with `Status: confirmed` signals that exploration completed.
44
+ The CLI sets `phase=propose` in `.specflow.yaml`. Explore does not change phase — `explore.md` with `Status: confirmed` signals that exploration completed.
45
45
 
46
46
  If `explore.md` already exists and is confirmed, suggest `/specflow:propose` instead of re-exploring unless the user wants to revise.
47
47
 
@@ -61,9 +61,9 @@ If the fix addresses a genuinely new scenario that existing specs did not cover,
61
61
 
62
62
  ### Stage 7a: Mark Phase Built
63
63
 
64
- Before archive, invoke `specflow change phase fix-<desc> --set built` to mark the fix as built.
64
+ Before archive, invoke `specflow change phase fix-<desc> --set apply` to mark the fix as apply-complete.
65
65
 
66
- Rationale: a fix change is created with `phase=plan` (same as any other change) but the fix flow covers the equivalent of the propose → refine → apply pipeline end-to-end inside Stages 3–6 (debug → TDD → review → verify). Archive rejects non-`built` phases unless `--force` is passed, so setting the phase explicitly here keeps the guard meaningful without needing `--force`.
66
+ Rationale: a fix change is created with `phase=propose` (same as any other change) but the fix flow covers the equivalent of the propose → refine → apply pipeline end-to-end inside Stages 3–6 (debug → TDD → review → verify). Archive rejects non-`apply` phases unless `--force` is passed, so setting the phase explicitly here keeps the guard meaningful without needing `--force`.
67
67
 
68
68
  ### Stage 7b: Run Archive
69
69
 
@@ -49,7 +49,7 @@ ls specflow/changes/<name>/explore.md 2>/dev/null
49
49
 
50
50
  Run `specflow change new <name>` to initialize a new change directory.
51
51
 
52
- The CLI automatically sets `phase=plan` in `.specflow.yaml` on creation (no separate phase call needed here).
52
+ The CLI automatically sets `phase=propose` in `.specflow.yaml` on creation (no separate phase call needed here).
53
53
 
54
54
  ## Stage 2: Generate Proposal
55
55
 
@@ -16,8 +16,8 @@ emits a round diff summary.
16
16
 
17
17
  ## Prerequisites
18
18
 
19
- - Active change with `.specflow.yaml` `phase=plan` (refine refuses if phase is not `plan`).
20
- - All 4 plan-phase artifacts exist in `specflow/changes/<name>/`:
19
+ - Active change with `.specflow.yaml` `phase=propose` (refine refuses if phase is not `propose`).
20
+ - All 4 propose-phase artifacts exist in `specflow/changes/<name>/`:
21
21
  - `proposal.md`
22
22
  - `specs/**/*.md` (at least one delta spec)
23
23
  - `design.md`
@@ -107,18 +107,18 @@ Stage 4 round-end output.
107
107
  Only reached after Stage 5 declared convergence AND the user did not request another round.
108
108
 
109
109
  1. Present a **cross-round summary**: what changed in each round, which artifacts now differ
110
- from the plan-phase baseline, which decisions were resolved, which questions remained
110
+ from the propose-phase baseline, which decisions were resolved, which questions remained
111
111
  open (if any).
112
112
  2. **HARD GATE**: Wait for the user's explicit confirmation of the final refined state.
113
113
  A mere acknowledgement of a round's diff summary is NOT confirmation of the overall
114
114
  refined state — ask explicitly: "Confirm this refined state is final and I should mark
115
115
  the phase as `refined`?"
116
116
  3. On explicit confirmation: invoke `specflow change phase <name> --set refined` to
117
- advance `.specflow.yaml` `phase` from `plan` to `refined`.
117
+ advance `.specflow.yaml` `phase` from `propose` to `refined`.
118
118
  4. Suggest `/specflow:apply` as the next slash command.
119
119
 
120
120
  If the user declines to confirm and requests more exploration, treat it as a user-requested
121
- extra round (loop back to Stage 2, phase stays `plan`).
121
+ extra round (loop back to Stage 2, phase stays `propose`).
122
122
 
123
123
  ## Not covered by refine
124
124
 
@@ -39,7 +39,7 @@ Do NOT archive until confirmed.
39
39
 
40
40
  ## Stage 4: Mark Phase Built
41
41
 
42
- Before archive, invoke `specflow change phase <name> --set built` since snap produces a complete retroactive record equivalent to a built change. The CLI creates the snap change with `phase=plan`, but snap's entire flow (analyze → generate → infer → confirm) is post-hoc documentation of already-built work, so setting the phase explicitly satisfies the archive guard without needing `--force`.
42
+ Before archive, invoke `specflow change phase <name> --set apply` since snap produces a complete retroactive record equivalent to an apply-complete change. The CLI creates the snap change with `phase=propose`, but snap's entire flow (analyze → generate → infer → confirm) is post-hoc documentation of already-implemented work, so setting the phase explicitly satisfies the archive guard without needing `--force`.
43
43
 
44
44
  ## Stage 5: Archive
45
45