From d5f0d016aff065ead0401b10d35b29cd29378168 Mon Sep 17 00:00:00 2001 From: Jack Felke Date: Wed, 18 Mar 2026 13:41:13 -0700 Subject: [PATCH 1/6] docs: add example CLAUDE.md for automatic preflight integration Adds a ready-to-use CLAUDE.md template that makes Claude Code automatically run preflight_check on prompts. Users can copy it into their project to get preflight working without manual tool calls. Referenced from Quick Start in README and examples/README. --- README.md | 10 ++++++++++ examples/CLAUDE.md | 30 ++++++++++++++++++++++++++++++ examples/README.md | 16 ++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 examples/CLAUDE.md diff --git a/README.md b/README.md index 6d03f5d..7969d58 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,16 @@ claude mcp add preflight -- preflight-dev-serve > **Note:** `preflight-dev` runs the interactive setup wizard. `preflight-dev-serve` starts the MCP server — that's what you want in your Claude Code config. +### Make Claude use preflight automatically + +Add preflight rules to your project's `CLAUDE.md` so Claude runs `preflight_check` on every prompt without you asking: + +```bash +cp /path/to/preflight/examples/CLAUDE.md your-project/CLAUDE.md +``` + +See [`examples/CLAUDE.md`](examples/CLAUDE.md) for a ready-to-use template with recommended rules for when to preflight, session hygiene, and skip-lists. + --- ## How It Works diff --git a/examples/CLAUDE.md b/examples/CLAUDE.md new file mode 100644 index 0000000..da55f40 --- /dev/null +++ b/examples/CLAUDE.md @@ -0,0 +1,30 @@ +# CLAUDE.md — Preflight Integration Example +# +# Drop this into your project's CLAUDE.md (or .claude/CLAUDE.md) to make +# Claude Code automatically use preflight tools during your sessions. +# Customize the rules below to match your workflow. + +## Preflight Rules + +Before starting any non-trivial task, run `preflight_check` with my prompt. This catches vague instructions before they waste tokens on wrong→fix cycles. + +### When to use preflight tools: + +- **Every prompt**: `preflight_check` triages automatically — let it decide what's needed +- **Before multi-file changes**: Run `scope_work` to get a phased plan +- **Before sub-agent tasks**: Use `enrich_agent_task` to add context +- **After making a mistake**: Use `log_correction` so preflight learns the pattern +- **Before ending a session**: Run `checkpoint` to save state for next time +- **When I say "fix it" or "do the others"**: Use `sharpen_followup` to resolve what I actually mean + +### Session hygiene: + +- Run `check_session_health` if we've been going for a while without committing +- If I ask about something we did before, use `search_history` to find it +- Before declaring a task done, run `verify_completion` (type check + tests) + +### Don't preflight these: + +- Simple git commands (commit, push, status) +- Formatting / linting +- Reading files I explicitly named diff --git a/examples/README.md b/examples/README.md index 778f15d..f2fafc1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -12,6 +12,22 @@ The `.preflight/` directory contains example configuration files you can copy in └── api.yml # Manual contract definitions for cross-service types ``` +## `CLAUDE.md` Integration + +The `CLAUDE.md` file tells Claude Code how to behave in your project. Adding preflight rules here makes Claude automatically use preflight tools without you having to ask. + +```bash +# Copy the example into your project: +cp /path/to/preflight/examples/CLAUDE.md my-project/CLAUDE.md + +# Or append to your existing CLAUDE.md: +cat /path/to/preflight/examples/CLAUDE.md >> my-project/CLAUDE.md +``` + +This is the **recommended way** to integrate preflight — once it's in your `CLAUDE.md`, every session automatically runs `preflight_check` on your prompts. + +--- + ### Quick setup ```bash From c17f46344bf005464e2bd65b1f1631852a51e069 Mon Sep 17 00:00:00 2001 From: Jack Felke Date: Thu, 19 Mar 2026 08:20:24 -0700 Subject: [PATCH 2/6] feat(cli): add --help and --version flags, fix Node badge to 20+ - CLI now responds to --help/-h with usage info, profiles, and links - CLI now responds to --version/-v with package version - Previously, any flag just launched the interactive wizard - Fixed README badge from Node 18+ to Node 20+ (matches engines field) --- README.md | 2 +- src/cli/init.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7969d58..e7a385f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A 24-tool MCP server for Claude Code that catches ambiguous instructions before [![MCP](https://img.shields.io/badge/MCP-Compatible-blueviolet)](https://modelcontextprotocol.io/) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) [![npm](https://img.shields.io/npm/v/preflight-dev)](https://www.npmjs.com/package/preflight-dev) -[![Node 18+](https://img.shields.io/badge/node-18%2B-brightgreen?logo=node.js&logoColor=white)](https://nodejs.org/) +[![Node 20+](https://img.shields.io/badge/node-20%2B-brightgreen?logo=node.js&logoColor=white)](https://nodejs.org/) [Quick Start](#quick-start) · [How It Works](#how-it-works) · [Tool Reference](#tool-reference) · [Configuration](#configuration) · [Scoring](#the-12-category-scorecard) diff --git a/src/cli/init.ts b/src/cli/init.ts index dfaaa25..d1b0021 100644 --- a/src/cli/init.ts +++ b/src/cli/init.ts @@ -9,6 +9,46 @@ import { join, dirname } from "node:path"; import { existsSync } from "node:fs"; import { fileURLToPath } from "node:url"; +// Handle --help and --version before launching interactive wizard +const args = process.argv.slice(2); + +if (args.includes("--help") || args.includes("-h")) { + console.log(` +✈️ preflight-dev — MCP server for Claude Code prompt discipline + +Usage: + preflight-dev Interactive setup wizard (creates .mcp.json) + preflight-dev --help Show this help message + preflight-dev --version Show version + +The wizard will: + 1. Ask you to choose a profile (minimal / standard / full) + 2. Optionally create a .preflight/ config directory + 3. Write an .mcp.json so Claude Code auto-connects to preflight + +After setup, restart Claude Code and preflight tools will appear. + +Profiles: + minimal 4 tools — clarify_intent, check_session_health, session_stats, prompt_score + standard 16 tools — all prompt discipline + session_stats + prompt_score + full 20 tools — everything + timeline/vector search (needs LanceDB) + +More info: https://github.com/TerminalGravity/preflight +`); + process.exit(0); +} + +if (args.includes("--version") || args.includes("-v")) { + const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "../../package.json"); + try { + const pkg = JSON.parse(await readFile(pkgPath, "utf-8")); + console.log(`preflight-dev v${pkg.version}`); + } catch { + console.log("preflight-dev (version unknown)"); + } + process.exit(0); +} + const rl = createInterface({ input: process.stdin, output: process.stdout }); function ask(question: string): Promise { From b28d77c0dee19e6845fcbc84d2f4f72c1ce1c9b2 Mon Sep 17 00:00:00 2001 From: Jack Felke Date: Thu, 19 Mar 2026 14:21:08 -0700 Subject: [PATCH 3/6] docs: add TROUBLESHOOTING.md with common setup and config fixes --- README.md | 10 +++ TROUBLESHOOTING.md | 165 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 TROUBLESHOOTING.md diff --git a/README.md b/README.md index e7a385f..cdd5a58 100644 --- a/README.md +++ b/README.md @@ -744,6 +744,16 @@ curl http://localhost:11434/api/embed -d '{"model":"all-minilm","input":"test"}' --- +## Troubleshooting + +Having issues? Check **[TROUBLESHOOTING.md](TROUBLESHOOTING.md)** for solutions to common problems including: +- LanceDB setup and platform issues +- Embedding provider configuration +- `.preflight/` config parsing errors +- Profile selection guide + +--- + ## Contributing This project is young and there's plenty to do. Check the [issues](https://github.com/TerminalGravity/preflight/issues) — several are tagged `good first issue`. diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md new file mode 100644 index 0000000..77171fc --- /dev/null +++ b/TROUBLESHOOTING.md @@ -0,0 +1,165 @@ +# Troubleshooting + +Common issues and fixes for preflight. + +--- + +## Installation & Setup + +### `npx preflight-dev-serve` fails with module errors + +**Symptoms:** `ERR_MODULE_NOT_FOUND` or `Cannot find module` when running via npx. + +**Fix:** Preflight requires **Node 20+**. Check your version: +```bash +node --version +``` +If you're on Node 18 or below, upgrade via [nvm](https://github.com/nvm-sh/nvm): +```bash +nvm install 20 +nvm use 20 +``` + +### Tools don't appear in Claude Code after `claude mcp add` + +**Fix:** Restart Claude Code completely after adding the MCP server. The tool list is loaded at startup. + +If tools still don't appear, verify the server starts without errors: +```bash +npx preflight-dev-serve +``` +You should see MCP protocol output (JSON on stdout). If you see errors, check the sections below. + +--- + +## LanceDB & Timeline Search + +### `Error: Failed to open LanceDB` or LanceDB crashes on startup + +**Symptoms:** Timeline tools (`search_timeline`, `index_sessions`, etc.) fail. Other tools work fine. + +**Cause:** LanceDB uses native binaries that may not be available for your platform, or the database directory has permission issues. + +**Fixes:** +1. Make sure `~/.preflight/projects/` is writable: + ```bash + mkdir -p ~/.preflight/projects + ls -la ~/.preflight/ + ``` +2. If on an unsupported platform, use the **minimal** or **standard** profile (no LanceDB required): + ```bash + npx preflight-dev + # Choose "minimal" or "standard" when prompted + ``` +3. Clear corrupted LanceDB data: + ```bash + rm -rf ~/.preflight/projects/*/timeline.lance + ``` + Then re-index with `index_sessions`. + +### Timeline search returns no results + +**Cause:** Sessions haven't been indexed yet. Preflight doesn't auto-index — you need to run `index_sessions` first. + +**Fix:** In Claude Code, run: +``` +index my sessions +``` +Or use the `index_sessions` tool directly. Indexing reads your `~/.claude/projects/` session files. + +--- + +## Embeddings + +### `OpenAI API key required for openai embedding provider` + +**Cause:** You selected OpenAI embeddings but didn't set the API key. + +**Fixes:** + +Option A — Set the environment variable when adding the MCP server: +```bash +claude mcp add preflight \ + -e OPENAI_API_KEY=sk-... \ + -- npx -y preflight-dev-serve +``` + +Option B — Switch to local embeddings (no API key needed). Create or edit `~/.preflight/config.json`: +```json +{ + "embedding_provider": "local", + "embedding_model": "Xenova/all-MiniLM-L6-v2" +} +``` + +### Local embeddings are slow on first run + +**Expected.** The model (~80MB) downloads on first use and is cached afterward. Subsequent runs are fast. + +--- + +## `.preflight/` Config + +### `warning - failed to parse .preflight/config.yml` + +**Cause:** YAML syntax error in your project's `.preflight/config.yml`. + +**Fix:** Validate your YAML: +```bash +npx yaml-lint .preflight/config.yml +``` +Or check for common issues: wrong indentation, tabs instead of spaces, missing colons. + +### Config changes not taking effect + +**Cause:** Preflight reads config at MCP server startup, not on every tool call. + +**Fix:** Restart Claude Code after editing `.preflight/config.yml` or `.preflight/triage.yml`. + +--- + +## Profiles + +### Which profile should I use? + +| Profile | Tools | Best for | +|---------|-------|----------| +| **minimal** | 4 | Try it out, low overhead | +| **standard** | 16 | Daily use, no vector search needed | +| **full** | 20 | Power users who want timeline search | + +You can change profiles by re-running the setup wizard: +```bash +npx preflight-dev +``` + +--- + +## Platform-Specific + +### Apple Silicon (M1/M2/M3/M4): LanceDB build fails + +LanceDB ships prebuilt binaries for Apple Silicon. If `npm install` fails on the native module: +```bash +# Ensure you're using the ARM64 version of Node +node -p process.arch # should print "arm64" + +# If it prints "x64", reinstall Node natively (not via Rosetta) +``` + +### Linux: Permission denied on `~/.preflight/` + +```bash +chmod -R u+rwX ~/.preflight/ +``` + +--- + +## Still stuck? + +1. Check [open issues](https://github.com/TerminalGravity/preflight/issues) — someone may have hit the same problem +2. [Open a new issue](https://github.com/TerminalGravity/preflight/issues/new) with: + - Your Node version (`node --version`) + - Your OS and architecture (`uname -a`) + - The full error message + - Which profile you selected From 948c34777ae5faa53b5ac93838934b91b6d4179a Mon Sep 17 00:00:00 2001 From: Jack Felke Date: Thu, 19 Mar 2026 15:42:33 -0700 Subject: [PATCH 4/6] docs: add example .preflight/ config and triage YAML files Adds well-commented example config.yml and triage.yml to examples/.preflight/ so users can copy them into their project root and customize. Every field is documented inline with descriptions of valid values and defaults. --- examples/.preflight/config.yml | 50 +++++++++++++++++----------------- examples/.preflight/triage.yml | 44 ++++++++++++------------------ 2 files changed, 43 insertions(+), 51 deletions(-) diff --git a/examples/.preflight/config.yml b/examples/.preflight/config.yml index f59170f..dde3cff 100644 --- a/examples/.preflight/config.yml +++ b/examples/.preflight/config.yml @@ -1,35 +1,35 @@ -# .preflight/config.yml — Drop this in your project root -# -# This is an example config for a typical Next.js + microservices setup. -# Every field is optional — preflight works with sensible defaults out of the box. -# Commit this to your repo so the whole team gets the same preflight behavior. +# .preflight/config.yml +# Drop this directory in your project root to configure preflight per-project. +# All fields are optional — defaults are used for anything you omit. -# Profile controls how much detail preflight returns. -# "minimal" — only flags ambiguous+ prompts, skips clarification detail -# "standard" — balanced (default) -# "full" — maximum detail on every non-trivial prompt +# Tool profile: how many tools to expose +# minimal — 4 tools (preflight_check, prompt_score, clarify_intent, scope_work) +# standard — 16 tools (adds scorecards, cost estimation, corrections, contracts) +# full — 20 tools (adds LanceDB timeline search — requires Node 20+) profile: standard -# Related projects for cross-service awareness. -# Preflight will search these for shared types, routes, and contracts -# so it can warn you when a change might break a consumer. +# Related projects for cross-service contract search. +# Preflight scans these for shared types, API routes, and schemas. related_projects: - - path: /Users/you/code/auth-service - alias: auth - - path: /Users/you/code/billing-api - alias: billing - - path: /Users/you/code/shared-types + - path: ../api-service + alias: api + - path: ../shared-types alias: types -# Behavioral thresholds — tune these to your workflow +# Tuning knobs thresholds: - session_stale_minutes: 30 # Warn if no activity for this long - max_tool_calls_before_checkpoint: 100 # Suggest a checkpoint after N tool calls - correction_pattern_threshold: 3 # Min corrections before flagging a pattern + # Minutes before a session is considered "stale" (triggers session_health warnings) + session_stale_minutes: 30 -# Embedding provider for semantic search over session history. -# "local" uses Xenova transformers (no API key needed, runs on CPU). -# "openai" uses text-embedding-3-small (faster, needs OPENAI_API_KEY). + # Tool calls before preflight nudges you to checkpoint progress + max_tool_calls_before_checkpoint: 100 + + # How many times a correction pattern repeats before it becomes a warning + correction_pattern_threshold: 3 + +# Embedding config (only matters for "full" profile with timeline search) embeddings: + # "local" — runs Xenova/all-MiniLM-L6-v2 in-process, no API key needed + # "openai" — uses text-embedding-3-small, requires openai_api_key or OPENAI_API_KEY env var provider: local - # openai_api_key: sk-... # Uncomment if using openai provider + # openai_api_key: sk-... # or set OPENAI_API_KEY env var instead diff --git a/examples/.preflight/triage.yml b/examples/.preflight/triage.yml index b3d394e..32261d3 100644 --- a/examples/.preflight/triage.yml +++ b/examples/.preflight/triage.yml @@ -1,45 +1,37 @@ -# .preflight/triage.yml — Controls how preflight classifies your prompts -# -# The triage engine routes prompts into categories: -# TRIVIAL → pass through (commit, format, lint) -# CLEAR → well-specified, no intervention needed -# AMBIGUOUS → needs clarification before proceeding -# MULTI-STEP → complex task, preflight suggests a plan -# CROSS-SERVICE → touches multiple projects, pulls in contracts -# -# Customize the keywords below to match your domain. +# .preflight/triage.yml +# Controls how preflight_check triages your prompts. +# Separate from config.yml so you can tune triage rules independently. + +# Strictness level: +# relaxed — only flags clearly ambiguous prompts +# standard — balanced (default) +# strict — flags anything that could be more specific +strictness: standard rules: - # Prompts containing these words are always flagged as AMBIGUOUS. - # Add domain-specific terms that tend to produce vague prompts. + # Keywords that ALWAYS trigger a full preflight check, even for short prompts. + # Use this for high-risk areas of your codebase. always_check: - rewards - permissions - migration - schema - - pricing # example: your billing domain - - onboarding # example: multi-step user flows + - billing + - auth - # Prompts containing these words skip checks entirely (TRIVIAL). - # These are safe, mechanical tasks that don't need guardrails. + # Keywords that SKIP preflight checks entirely. + # Use this for low-risk, routine commands. skip: - commit - format - lint - prettier - - "git push" - # Prompts containing these words trigger CROSS-SERVICE classification. - # Preflight will search related_projects for relevant types and routes. + # Keywords that trigger cross-service contract scanning. + # When these appear, preflight also checks related_projects for shared types. cross_service_keywords: - auth - notification - event - webhook - - billing # matches the related_project alias - -# How aggressively to classify prompts. -# "relaxed" — more prompts pass as clear (experienced users) -# "standard" — balanced (default) -# "strict" — more prompts flagged as ambiguous (new teams, complex codebases) -strictness: standard + - payment From ed0432d2f196db3d0119e3df4f042ecc3943bf51 Mon Sep 17 00:00:00 2001 From: Jack Felke Date: Thu, 19 Mar 2026 17:20:56 -0700 Subject: [PATCH 5/6] docs: add 'verify it's working' section to quickstart --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index cdd5a58..e374148 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,37 @@ cp /path/to/preflight/examples/CLAUDE.md your-project/CLAUDE.md See [`examples/CLAUDE.md`](examples/CLAUDE.md) for a ready-to-use template with recommended rules for when to preflight, session hygiene, and skip-lists. +### Verify it's working + +After setup, open Claude Code in your project and try: + +``` +> fix the tests +``` + +If preflight is connected, you'll see a `preflight_check` tool call fire automatically (or when configured via CLAUDE.md). It should respond with something like: + +``` +🛫 Preflight Check +Triage: ambiguous (confidence: 0.70) +Reasons: short prompt without file references; contains vague verbs without specific targets + +⚠️ Clarification Needed +- Vague verb without specific file targets +- Very short prompt — likely missing context + +Git State +Branch: main | Dirty files: 3 +``` + +If you see this, you're good. If nothing happens: + +1. **Check tools are loaded:** Type `/mcp` in Claude Code — you should see `preflight` listed with its tools +2. **Check the server starts:** Run `npx preflight-dev-serve` in your terminal — it should output JSON (MCP protocol), not errors +3. **Restart Claude Code:** Tools are loaded at startup, so you need a full restart after adding the MCP server + +> **Tip:** Try `prompt_score "update the thing"` to test a specific tool directly. You should get a grade and suggestions. + --- ## How It Works From 7dce02e7cd76e13c1f81537343ee2373a3010de8 Mon Sep 17 00:00:00 2001 From: Jack Felke Date: Thu, 19 Mar 2026 17:44:31 -0700 Subject: [PATCH 6/6] test: add 16 unit tests for prompt_score scoring logic Export scorePrompt function and add comprehensive tests covering: - All four scoring dimensions (specificity, scope, actionability, done condition) - Grade boundaries (A+ for perfect, F for vague) - Feedback generation - Total calculation --- src/tools/prompt-score.ts | 2 +- tests/tools/prompt-score.test.ts | 97 ++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 tests/tools/prompt-score.test.ts diff --git a/src/tools/prompt-score.ts b/src/tools/prompt-score.ts index 1cecf01..9e91532 100644 --- a/src/tools/prompt-score.ts +++ b/src/tools/prompt-score.ts @@ -40,7 +40,7 @@ interface ScoreResult { feedback: string[]; } -function scorePrompt(text: string): ScoreResult { +export function scorePrompt(text: string): ScoreResult { const feedback: string[] = []; let specificity: number; let scope: number; diff --git a/tests/tools/prompt-score.test.ts b/tests/tools/prompt-score.test.ts new file mode 100644 index 0000000..a4d69f9 --- /dev/null +++ b/tests/tools/prompt-score.test.ts @@ -0,0 +1,97 @@ +import { describe, it, expect } from "vitest"; +import { scorePrompt } from "../../src/tools/prompt-score.js"; + +describe("scorePrompt", () => { + // ── Specificity ────────────────────────────────────────────────────── + it("gives max specificity for file paths", () => { + const r = scorePrompt("Fix the bug in src/tools/prompt-score.ts"); + expect(r.specificity).toBe(25); + }); + + it("gives max specificity for backtick identifiers", () => { + const r = scorePrompt("Rename `handleClick` to `onClick`"); + expect(r.specificity).toBe(25); + }); + + it("gives partial specificity for generic file/function keywords", () => { + const r = scorePrompt("Update the component to use hooks"); + expect(r.specificity).toBe(15); + }); + + it("gives low specificity when nothing specific is mentioned", () => { + const r = scorePrompt("Make it better"); + expect(r.specificity).toBe(5); + }); + + // ── Scope ──────────────────────────────────────────────────────────── + it("gives max scope for bounded tasks", () => { + const r = scorePrompt("Only change the return type of this function"); + expect(r.scope).toBe(25); + }); + + it("gives lower scope for 'all/every' phrasing", () => { + const r = scorePrompt("Fix every test"); + expect(r.scope).toBe(10); + }); + + // ── Actionability ──────────────────────────────────────────────────── + it("gives max actionability for specific verbs", () => { + const r = scorePrompt("Rename the variable x to count"); + expect(r.actionability).toBe(25); + }); + + it("gives partial actionability for vague verbs", () => { + const r = scorePrompt("Make the tests work"); + expect(r.actionability).toBe(15); + }); + + it("gives low actionability with no verb", () => { + const r = scorePrompt("the login page"); + expect(r.actionability).toBe(5); + }); + + // ── Done condition ─────────────────────────────────────────────────── + it("gives max done condition for outcome words", () => { + const r = scorePrompt("Fix it so the test should pass"); + expect(r.doneCondition).toBe(25); + }); + + it("gives good done condition for questions", () => { + const r = scorePrompt("Why does this throw a TypeError?"); + expect(r.doneCondition).toBe(20); + }); + + it("gives low done condition when no outcome specified", () => { + const r = scorePrompt("Refactor the code"); + expect(r.doneCondition).toBe(5); + }); + + // ── Grade boundaries ──────────────────────────────────────────────── + it("grades A+ for a perfect prompt", () => { + // specificity=25 (file path), scope=25 (long+bounded), actionability=25 (verb), doneCondition=25 (outcome) + const r = scorePrompt( + "Rename `processData` in src/utils/transform.ts to `transformRecords` — only this one function. The existing tests should still pass." + ); + expect(r.total).toBe(100); + expect(r.grade).toBe("A+"); + expect(r.feedback).toContain("🏆 Excellent prompt! Clear target, scope, action, and done condition."); + }); + + it("grades F for a vague prompt", () => { + const r = scorePrompt("help"); + expect(r.total).toBeLessThan(45); + expect(r.grade).toBe("F"); + }); + + // ── Total is sum of components ─────────────────────────────────────── + it("total equals sum of all four dimensions", () => { + const r = scorePrompt("Add a test for the `validate` function in src/lib/check.ts"); + expect(r.total).toBe(r.specificity + r.scope + r.actionability + r.doneCondition); + }); + + // ── Feedback is non-empty ──────────────────────────────────────────── + it("always returns at least one feedback item", () => { + const r = scorePrompt("do stuff"); + expect(r.feedback.length).toBeGreaterThan(0); + }); +});