@gordon.gan/specflow 1.0.2 → 1.1.1

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.1",
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.
@@ -5,6 +5,15 @@ description: "Two-phase apply — task rewrite + subagent TDD execution"
5
5
 
6
6
  # SpecFlow: Apply
7
7
 
8
+ ## Invocation mode
9
+
10
+ Inspect the current user invocation before any stage work:
11
+
12
+ - **Default mode:** `/specflow:apply` preserves every existing user confirmation gate and the default interactive behavior.
13
+ - **Yes mode:** `/specflow:apply --yes` records non-interactive confirmation mode for the current invocation only. It auto-accepts only successful acknowledgement gates; it never bypasses prerequisites, gap detection, failing verification, or a review `Block`.
14
+
15
+ Report the selected mode once at the start of the session. Do not persist it to future invocations.
16
+
8
17
  > **HARD GATE (prerequisite)**: phase must be `refined`. Run /specflow:refine first if not.
9
18
  > **HARD GATE (Phase A)**: rewritten tasks.md must be user-confirmed before Phase B.
10
19
  > **HARD GATE (Phase B)**: each task must be reviewed (spec + code quality) and user-confirmed before next task.
@@ -56,7 +65,10 @@ Present the audit summary to the user: per-group task count (coarse → atomic),
56
65
 
57
66
  ### Gate A: Rewrite Confirmation
58
67
 
59
- Present the rewritten `tasks.md` to the user. **Ask the user to confirm the rewrite.** Do NOT proceed to Phase B until the user explicitly confirms.
68
+ - **Default mode:** Present the rewritten `tasks.md` and ask the user to confirm the rewrite. Do NOT proceed to Phase B until the user explicitly confirms.
69
+ - **Yes mode:** Present the rewrite audit and continue directly to Phase B after recording the rewrite as automatically accepted for this invocation.
70
+
71
+ If Phase A reports a reorganization choice or a design gap, stop regardless of mode; `--yes` does not choose a reorganization or fill a missing design decision.
60
72
 
61
73
  ---
62
74
 
@@ -108,22 +120,32 @@ ECC reviewer verdict routing:
108
120
 
109
121
  #### Gate B: Per-task Confirmation
110
122
 
111
- Present the task output and both review reports to the user. **Ask the user to confirm the task is complete.** Do NOT proceed to the next task until confirmation is received.
123
+ - **Default mode:** Present the task output and both review reports. Ask the user to confirm the task is complete. Do NOT proceed to the next task until confirmation is received.
124
+ - **Yes mode:** Present the task output and both review reports. If spec review passes and code-quality review returns `Approve` or `Warning`, record the task as automatically accepted and start the next task without waiting.
112
125
 
113
- If the user rejects, return to Stage B2a for the same task.
126
+ If either review blocks, return to Stage B2a for the same task. `--yes` never advances past a blocked review.
114
127
 
115
128
  ### Stage B3: Phase Transition
116
129
 
117
- Once all tasks are confirmed:
118
- - Invoke `specflow change phase <name> --set built` to transition the phase.
130
+ Once all tasks are confirmed in default mode or automatically accepted in yes mode:
131
+ - Invoke `specflow change phase <name> --set apply` to transition the phase.
119
132
  - Summarize the build results and overall coverage.
120
- - Inform the user they can now run `/specflow:review` or `/specflow:verify`.
133
+
134
+ #### Yes-mode downstream sequence
135
+
136
+ When yes mode reaches this point:
137
+ 1. Invoke `/specflow:review`. Stop if it reports a CRITICAL or HIGH finding.
138
+ 2. Invoke `/specflow:test` only after review has no CRITICAL/HIGH findings. Stop if tests cannot be made green.
139
+ 3. Invoke `/specflow:verify` only after test succeeds. Stop if verify returns `FAIL`.
140
+ 4. Report review, test, and verification evidence. Do NOT invoke `/specflow:archive` automatically; archive remains the user's manual choice.
141
+
142
+ When default mode reaches this point, inform the user that they can now run `/specflow:review` or `/specflow:verify`.
121
143
 
122
144
  ---
123
145
 
124
146
  ## Phase Transition Notes
125
147
 
126
- - **On Phase B success**: phase → `built` (automatic via Stage B3 CLI call).
148
+ - **On Phase B success**: phase → `apply` (automatic via Stage B3 CLI call).
127
149
  - **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
150
  - **On Phase A reorganization choice C**: phase remains `refined`. No transition. User edits groups manually, then re-runs `/specflow:apply`.
129
151
  - **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