- Use /spec in Claude Pilot to start a spec-driven development workflow.
+ Use{" "}
+
+ /spec
+ {" "}
+ in Claude Pilot to start a spec-driven development workflow.
- For bug fixes and small tasks
+ For small tasks and quick questions
Just chat — no plan file, no approval gate. All quality hooks and
- TDD enforcement still apply. Great for quick fixes, questions, and
- exploration.
+ TDD enforcement still apply. Great for small changes, questions,
+ and exploration.
- Spec-Driven Development — plan, approve, implement, verify. The
- full structured workflow for features and complex changes.
+ Spec-Driven Development — plan, approve, implement, verify.
+ Features and bug fixes. Auto-detects bugfix intent from the task
+ description.
- > Fix the null pointer bug
- in user.py
+ > Add a loading spinner to
+ the submit button
- > Add pagination to the
- products API
+ > Write tests for the
+ OrderService class
- > Refactor the auth
- middleware to use JWT
+ > Explain how the auth
+ middleware works
- > Write tests for the
- OrderService class
+ > Rename the
+ "products" table to "items" across the codebase
@@ -87,10 +87,9 @@ const QuickModeSection = () => {
/spec
{" "}
- when the task is complex enough that you want to review a plan
- before implementation begins, when multiple developers need
- visibility into what's being built, or when you're tackling a
- refactor with many interdependent changes.
+ for bug fixes (structured bug analysis with Behavior Contract and
+ test-before-fix), complex features that need a plan before
+ implementation, or refactors with many interdependent changes.
diff --git a/docs/site/src/pages/docs/SpecSection.tsx b/docs/site/src/pages/docs/SpecSection.tsx
index 73933738..d04d7b8d 100644
--- a/docs/site/src/pages/docs/SpecSection.tsx
+++ b/docs/site/src/pages/docs/SpecSection.tsx
@@ -5,6 +5,8 @@ import {
Code2,
Shield,
ArrowRight,
+ Wrench,
+ Zap,
} from "lucide-react";
import { useInView } from "@/hooks/use-in-view";
@@ -106,8 +108,13 @@ const SpecSection = () => {
API to GraphQL"
- > /spec "Refactor the order
- processing pipeline for parallel execution"
+ >{" "}
+
+ /spec "Fix the crash when deleting nodes with two children"
+
+
+ {/* bugfix auto-detected */}
+
+ Full exploration workflow for new functionality, refactoring, or
+ any work where architecture decisions matter.
+
+
+ {[
+ "Codebase exploration with Vexor semantic search",
+ "Architecture design decisions via Q&A",
+ "Full plan with scope, risks, and DoD",
+ "Three verification agents (compliance, quality, goal)",
+ ].map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+
+
+
+
+
+
+
+ Bugfix Spec{" "}
+
+ auto-detected
+
+
+
+
+ Lighter, property-aware flow for targeted fixes. Defines the bug
+ precisely before touching any code.
+
+
+ {[
+ "Bug Condition (C): exact broken state description",
+ "Postcondition (P): what must be true after the fix",
+ "Behavior Contract: Must Change / Must NOT Change",
+ "Test-before-fix: bug test FAILS → preservation tests PASS → fix",
+ ].map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+
+
{/* Three phases */}
{phases.map((phase) => {
diff --git a/installer/steps/dependencies.py b/installer/steps/dependencies.py
index 62f9d8dd..a69fb4c2 100644
--- a/installer/steps/dependencies.py
+++ b/installer/steps/dependencies.py
@@ -543,6 +543,34 @@ def install_ccusage() -> bool:
return _run_bash_with_retry(npm_global_cmd("npm install -g ccusage@latest"))
+def _is_fast_check_installed() -> bool:
+ """Check if fast-check is installed globally via npm."""
+ try:
+ result = subprocess.run(
+ ["npm", "list", "-g", "fast-check", "--depth=0"],
+ capture_output=True,
+ text=True,
+ )
+ return result.returncode == 0 and "fast-check" in result.stdout
+ except Exception:
+ return False
+
+
+def install_pbt_tools() -> bool:
+ """Install property-based testing packages: hypothesis (Python) and fast-check (TypeScript).
+
+ Go PBT is handled by the built-in 'go test -fuzz' (Go 1.18+) — no install needed.
+ Both packages are best-effort: failure does not block installation.
+ """
+ if not command_exists("hypothesis"):
+ _run_bash_with_retry("uv tool install hypothesis")
+
+ if not _is_fast_check_installed():
+ _run_bash_with_retry(npm_global_cmd("npm install -g fast-check"))
+
+ return True
+
+
def _get_playwright_cache_dirs() -> list[Path]:
"""Get possible Playwright cache directories for the current platform."""
import platform
@@ -903,6 +931,9 @@ def run(self, ctx: InstallContext) -> None:
if _install_with_spinner(ui, "golangci-lint (Go linter)", install_golangci_lint):
installed.append("golangci_lint")
+ if _install_with_spinner(ui, "PBT tools (hypothesis, fast-check)", install_pbt_tools):
+ installed.append("pbt_tools")
+
if _install_with_spinner(ui, "ccusage (usage tracking)", install_ccusage):
installed.append("ccusage")
diff --git a/installer/steps/finalize.py b/installer/steps/finalize.py
index 3cf6f856..bef4af12 100644
--- a/installer/steps/finalize.py
+++ b/installer/steps/finalize.py
@@ -85,7 +85,7 @@ def _display_success(self, ctx: InstallContext) -> None:
steps.append(("🚀 Start Pilot", "Run: pilot (in your project folder)"))
steps.append(("🔄 /sync", "Learn your codebase conventions and generate project rules"))
- steps.append(("📋 /spec", "Plan, implement & verify features with TDD and code review"))
+ steps.append(("📋 /spec", "Plan, implement & verify features and bug fixes with TDD and code review"))
steps.append(("🧠 /learn", "Extract reusable knowledge into skills from sessions"))
steps.append(("🏦 /vault", "Optional: Pull shared rules and skills from your team"))
steps.append(("🔵 Pilot Console", "Open in your browser at: http://localhost:41777"))
diff --git a/installer/tests/unit/steps/test_dependencies.py b/installer/tests/unit/steps/test_dependencies.py
index 4aa5b9e0..bdfa8c43 100644
--- a/installer/tests/unit/steps/test_dependencies.py
+++ b/installer/tests/unit/steps/test_dependencies.py
@@ -31,6 +31,7 @@ def test_dependencies_check_returns_false(self):
)
assert step.check(ctx) is False
+ @patch("installer.steps.dependencies.install_pbt_tools", return_value=True)
@patch("installer.steps.dependencies.install_golangci_lint", return_value=True)
@patch("installer.steps.dependencies.install_prettier", return_value=True)
@patch("installer.steps.dependencies._precache_npx_mcp_servers", return_value=True)
@@ -53,6 +54,7 @@ def test_dependencies_run_installs_core(
_mock_precache,
_mock_prettier,
_mock_golangci_lint,
+ _mock_pbt_tools,
):
"""DependenciesStep installs all dependencies including Python tools."""
from installer.context import InstallContext
@@ -970,3 +972,66 @@ def test_install_golangci_lint_returns_false_on_failure(self, mock_check, mock_c
result = install_golangci_lint()
assert result is False
+
+
+class TestInstallPbtTools:
+ """Tests for install_pbt_tools() — property-based testing packages."""
+
+ @patch("installer.steps.dependencies._run_bash_with_retry", return_value=True)
+ @patch("installer.steps.dependencies._is_fast_check_installed", return_value=False)
+ @patch("installer.steps.dependencies.command_exists", return_value=False)
+ def test_install_pbt_tools_installs_hypothesis_when_missing(self, _mock_cmd, _mock_fc, mock_run):
+ """install_pbt_tools installs hypothesis when not already installed."""
+ from installer.steps.dependencies import install_pbt_tools
+
+ install_pbt_tools()
+
+ calls = [str(c) for c in mock_run.call_args_list]
+ assert any("hypothesis" in c for c in calls)
+
+ @patch("installer.steps.dependencies._run_bash_with_retry", return_value=True)
+ @patch("installer.steps.dependencies._is_fast_check_installed", return_value=False)
+ @patch("installer.steps.dependencies.command_exists", return_value=True)
+ def test_install_pbt_tools_skips_hypothesis_when_present(self, _mock_cmd, _mock_fc, mock_run):
+ """install_pbt_tools skips hypothesis install when already present."""
+ from installer.steps.dependencies import install_pbt_tools
+
+ install_pbt_tools()
+
+ calls = [str(c) for c in mock_run.call_args_list]
+ assert not any("hypothesis" in c for c in calls)
+
+ @patch("installer.steps.dependencies._run_bash_with_retry", return_value=True)
+ @patch("installer.steps.dependencies._is_fast_check_installed", return_value=False)
+ @patch("installer.steps.dependencies.command_exists", return_value=True)
+ def test_install_pbt_tools_installs_fast_check_when_missing(self, _mock_cmd, _mock_fc, mock_run):
+ """install_pbt_tools installs fast-check when not already installed."""
+ from installer.steps.dependencies import install_pbt_tools
+
+ install_pbt_tools()
+
+ calls = [str(c) for c in mock_run.call_args_list]
+ assert any("fast-check" in c for c in calls)
+
+ @patch("installer.steps.dependencies._run_bash_with_retry", return_value=True)
+ @patch("installer.steps.dependencies._is_fast_check_installed", return_value=True)
+ @patch("installer.steps.dependencies.command_exists", return_value=True)
+ def test_install_pbt_tools_skips_fast_check_when_present(self, _mock_cmd, _mock_fc, mock_run):
+ """install_pbt_tools skips fast-check install when already present."""
+ from installer.steps.dependencies import install_pbt_tools
+
+ install_pbt_tools()
+
+ calls = [str(c) for c in mock_run.call_args_list]
+ assert not any("fast-check" in c for c in calls)
+
+ @patch("installer.steps.dependencies._run_bash_with_retry", return_value=True)
+ @patch("installer.steps.dependencies._is_fast_check_installed", return_value=True)
+ @patch("installer.steps.dependencies.command_exists", return_value=True)
+ def test_install_pbt_tools_returns_true_when_all_present(self, _mock_cmd, _mock_fc, _mock_run):
+ """install_pbt_tools returns True when all packages already installed."""
+ from installer.steps.dependencies import install_pbt_tools
+
+ result = install_pbt_tools()
+
+ assert result is True
diff --git a/launcher/statusline/formatter.py b/launcher/statusline/formatter.py
index 272d0af7e52b8de518f4927186f1932abfd2581b..8b8887caad4cd4d5972fb9b1d55b5000bb717f78 100644
GIT binary patch
literal 18012
zcmV(pK=8i+M@dveQdv+`0E8C3m)^ETql1D=Y?~?Wa?bR0gyN-D!0q)vbw{3RPhiHS
zLdN13n20eh*r-GnoCfC8XB%-Iv9rK$D^(#YaG?ecydMmrr|k(Bk1yX%vh9!Z?G@V`
zI4+DyQ0H$BSc$hx02Yl})`z9i1+*G$&|*CGT_w{56hRtK!{lw7%)Ykz&|z|yT2
zSUKg))p414tck`Qa`n}cG>jw{Y0BL$
zYq;{lCg*_j5;UvUva38_EVvTuUC${2>K%_X6uJmQ;m2@%UI{!M?H`iUS)#tkT4PST
zI!-JF|Fe+5);V&>D67!1!@7pMK9z%LDRUMHXIwuqYh3L`BhLtrH@Gp5=h9j3=<)tH
z%M;>#uV(#6k}!a?&&0DmcZO#9C?uYZ>)rfp8b=gHV;D^7sp4hjmXzF_f+pf2OmC!I
z6tF#1U6KTYWk_}OsupS`;>}&AE=}<)Ise*zP+?SP=C47xt8*gLzKVp*%F|mAYuAAL
z54-%L8lj}j@6g5|r2vzC3F;@bPw1I`m4-4VxpUYd=#YZjW}xYySVMQ2p3bK{cc{pd
zO5iO>%I_cEdYk702`)eOdP{VL527_Mb~Uv`Lv*=Br0M|WWzrQaL1y^Kjg{rC*tb<
zFGRnZ&v&NM*%)usZ~hRTBIC+wXjja2^9?OTi{6nCU$!3QGCX!rQKsT6|#x#-lYafdpHAA
z+*gKzl~GiRSbC3q82