Skip to content

Commit 61fcbac

Browse files
committed
feat(pb-improve): consolidate multiple specs into single directory
- Change pb-improve to generate ONE spec directory for all findings instead of separate directories per finding - Update design.md template with Findings section and per-finding structure - Update tasks.md template with cross-finding sequential numbering - Add Approach section to lightweight design template (required by contract) - Add 6 BDD scenarios for consolidated and full-mode spec validation - Add 10 unit tests for consolidated spec and full-mode spec flows - Update closing-the-loop.md and plan-template.md for single-spec output All 63 unit tests and 18 BDD scenarios pass.
1 parent 6f4d3f5 commit 61fcbac

11 files changed

Lines changed: 1110 additions & 122 deletions

File tree

features/steps/validate_steps.py

Lines changed: 382 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,388 @@ def step_task_unchecked_step(context, checkbox: str) -> None:
252252
context.tasks_file.write_text(content)
253253

254254

255+
@given("I have a consolidated spec directory with valid design.md and tasks.md")
256+
def step_consolidated_valid_spec(context) -> None:
257+
"""Create a consolidated spec directory with all required sections."""
258+
design_file = context.spec_dir / "design.md"
259+
design_file.write_text(
260+
"# Design: Codebase Quality Improvements\n"
261+
"\n"
262+
"## Summary\n"
263+
"Consolidated improvements across correctness, security, and performance.\n"
264+
"\n"
265+
"## Approach\n"
266+
"Implement findings sequentially, starting with highest-impact items.\n"
267+
"\n"
268+
"## Findings\n"
269+
"\n"
270+
"### Finding 1: Fix N+1 query\n"
271+
"- **Category:** performance\n"
272+
"- **Impact:** HIGH\n"
273+
"\n"
274+
"#### Approach\n"
275+
"Add batch loading.\n"
276+
"\n"
277+
"## Architecture Decisions\n"
278+
"### AD-01: Use batch loading\n"
279+
"- **Status:** Accepted\n"
280+
"\n"
281+
"**Context:** N+1 queries detected.\n"
282+
"**Decision:** Use DataLoader pattern.\n"
283+
"**Consequences:** Reduces query count.\n"
284+
"\n"
285+
"## BDD/TDD Strategy\n"
286+
"BDD+TDD with behave.\n"
287+
"\n"
288+
"## Code Simplification Constraints\n"
289+
"Keep minimal.\n"
290+
"\n"
291+
"## BDD Scenario Inventory\n"
292+
"- features/performance.feature — Batch loading: reduces queries → Task 1.1\n"
293+
"\n"
294+
"## Existing Components to Reuse\n"
295+
"None.\n"
296+
"\n"
297+
"## Verification\n"
298+
"Run full test suite.\n"
299+
)
300+
301+
tasks_file = context.spec_dir / "tasks.md"
302+
tasks_file.write_text(
303+
"# Tasks\n"
304+
"\n"
305+
"### Task 1.1: Fix N+1 query\n"
306+
"Context: Address performance issue in order listing.\n"
307+
"Verification: Run tests.\n"
308+
"Scenario Coverage: features/performance.feature — Batch loading.\n"
309+
"Loop Type: BDD+TDD\n"
310+
"Behavioral Contract: Preserve existing behavior.\n"
311+
"Simplification Focus: Reduce nesting.\n"
312+
"BDD Verification: uv run behave features/performance.feature\n"
313+
"Advanced Test Verification: N/A — no advanced tests planned.\n"
314+
"Runtime Verification: N/A — no runtime changes.\n"
315+
"Status: 🔴 TODO\n"
316+
"- [ ] Step 1: Write failing test\n"
317+
)
318+
319+
features_dir = context.spec_dir / "features"
320+
features_dir.mkdir(exist_ok=True)
321+
(features_dir / "performance.feature").write_text(
322+
"Feature: Performance improvements\n"
323+
" Scenario: Batch loading reduces queries\n"
324+
" Given a list of orders\n"
325+
" When orders are fetched\n"
326+
" Then only one query is executed\n"
327+
)
328+
329+
330+
@given('I have a consolidated spec directory with design.md missing "{section}" section')
331+
def step_consolidated_missing_design_section(context, section: str) -> None:
332+
"""Create a consolidated spec with a missing design section."""
333+
design_file = context.spec_dir / "design.md"
334+
design_file.write_text(
335+
"# Design: Improvements\n"
336+
"\n"
337+
"## Summary\n"
338+
"Summary.\n"
339+
"\n"
340+
"## Approach\n"
341+
"Approach.\n"
342+
"\n"
343+
"## Architecture Decisions\n"
344+
"Decision.\n"
345+
"\n"
346+
"## Code Simplification Constraints\n"
347+
"Keep minimal.\n"
348+
"\n"
349+
"## BDD Scenario Inventory\n"
350+
"Scenarios.\n"
351+
"\n"
352+
"## Existing Components to Reuse\n"
353+
"None.\n"
354+
"\n"
355+
"## Verification\n"
356+
"Run tests.\n"
357+
)
358+
359+
tasks_file = context.spec_dir / "tasks.md"
360+
tasks_file.write_text(
361+
"# Tasks\n"
362+
"\n"
363+
"### Task 1.1: Test Task\n"
364+
"Context: Test.\n"
365+
"Verification: Run tests.\n"
366+
"Scenario Coverage: N/A — internal task.\n"
367+
"Loop Type: TDD-only\n"
368+
"Behavioral Contract: Must pass.\n"
369+
"Simplification Focus: Keep minimal.\n"
370+
"BDD Verification: N/A — TDD-only task.\n"
371+
"Advanced Test Verification: N/A — no advanced tests.\n"
372+
"Runtime Verification: N/A — no runtime changes.\n"
373+
"Status: 🔴 TODO\n"
374+
"- [ ] Step 1: Write test\n"
375+
)
376+
377+
378+
@given('I have a consolidated spec directory with tasks.md missing "{field}" field')
379+
def step_consolidated_missing_task_field(context, field: str) -> None:
380+
"""Create a consolidated spec with a missing task field."""
381+
design_file = context.spec_dir / "design.md"
382+
design_file.write_text(
383+
"# Design: Improvements\n"
384+
"\n"
385+
"## Summary\n"
386+
"Summary.\n"
387+
"\n"
388+
"## Approach\n"
389+
"Approach.\n"
390+
"\n"
391+
"## Architecture Decisions\n"
392+
"Decision.\n"
393+
"\n"
394+
"## BDD/TDD Strategy\n"
395+
"Strategy.\n"
396+
"\n"
397+
"## Code Simplification Constraints\n"
398+
"Keep minimal.\n"
399+
"\n"
400+
"## BDD Scenario Inventory\n"
401+
"Scenarios.\n"
402+
"\n"
403+
"## Existing Components to Reuse\n"
404+
"None.\n"
405+
"\n"
406+
"## Verification\n"
407+
"Run tests.\n"
408+
)
409+
410+
tasks_file = context.spec_dir / "tasks.md"
411+
# Write a task that is missing the specified field
412+
tasks_file.write_text(
413+
"# Tasks\n"
414+
"\n"
415+
"### Task 1.1: Test Task\n"
416+
"Context: Test.\n"
417+
"Verification: Run tests.\n"
418+
"Scenario Coverage: N/A — internal task.\n"
419+
"Behavioral Contract: Must pass.\n"
420+
"Simplification Focus: Keep minimal.\n"
421+
"BDD Verification: N/A — TDD-only task.\n"
422+
"Advanced Test Verification: N/A — no advanced tests.\n"
423+
"Runtime Verification: N/A — no runtime changes.\n"
424+
"Status: 🔴 TODO\n"
425+
"- [ ] Step 1: Write test\n"
426+
)
427+
428+
429+
@given("I have a full-mode spec directory with all required sections")
430+
def step_full_mode_valid_spec(context) -> None:
431+
"""Create a full-mode spec directory with all required sections."""
432+
design_file = context.spec_dir / "design.md"
433+
design_file.write_text(
434+
"# Design: Full Feature\n"
435+
"\n"
436+
"## Executive Summary\n"
437+
"Complete feature implementation.\n"
438+
"\n"
439+
"## Requirements & Goals\n"
440+
"- **[REQ-01]:** The system *shall* validate inputs when form is submitted.\n"
441+
"\n"
442+
"## Architecture Overview\n"
443+
"```mermaid\n"
444+
"graph TD\n"
445+
" A[Client] --> B[Server]\n"
446+
"```\n"
447+
"\n"
448+
"## Architecture Decisions\n"
449+
"### AD-01: Use REST API\n"
450+
"- **Status:** Accepted\n"
451+
"\n"
452+
"**Context:** Need external API.\n"
453+
"**Decision:** Use REST.\n"
454+
"**Consequences:** Simple integration.\n"
455+
"\n"
456+
"## Data Models\n"
457+
"```dbml\n"
458+
"Table users {\n"
459+
" id integer [pk]\n"
460+
" name varchar\n"
461+
"}\n"
462+
"```\n"
463+
"\n"
464+
"## Interface Contracts\n"
465+
"```python\n"
466+
"class UserProto(Protocol):\n"
467+
" def get_name(self) -> str: ...\n"
468+
"```\n"
469+
"\n"
470+
"## Detailed Design\n"
471+
"Implementation details.\n"
472+
"\n"
473+
"## Verification & Testing Strategy\n"
474+
"BDD + unit tests.\n"
475+
"\n"
476+
"## Implementation Plan\n"
477+
"- [ ] Phase 1: Core\n"
478+
)
479+
480+
tasks_file = context.spec_dir / "tasks.md"
481+
tasks_file.write_text(
482+
"# Tasks\n"
483+
"\n"
484+
"### Task 1.1: Implement feature\n"
485+
"Context: Build the feature.\n"
486+
"Verification: Run tests.\n"
487+
"Scenario Coverage: Test scenario.\n"
488+
"Loop Type: BDD+TDD\n"
489+
"Behavioral Contract: Must pass.\n"
490+
"Simplification Focus: Keep minimal.\n"
491+
"BDD Verification: uv run behave\n"
492+
"Advanced Test Verification: N/A — no advanced tests.\n"
493+
"Runtime Verification: N/A — no runtime changes.\n"
494+
"Status: 🔴 TODO\n"
495+
"- [ ] Step 1: Write test\n"
496+
)
497+
498+
features_dir = context.spec_dir / "features"
499+
features_dir.mkdir(exist_ok=True)
500+
(features_dir / "test.feature").write_text(
501+
"Feature: Full feature\n"
502+
" Scenario: Happy path\n"
503+
" Given a user\n"
504+
" When they submit\n"
505+
" Then success\n"
506+
)
507+
508+
509+
@given('I have a full-mode spec directory missing "{section}" section')
510+
def step_full_mode_missing_section(context, section: str) -> None:
511+
"""Create a full-mode spec missing a required section."""
512+
design_file = context.spec_dir / "design.md"
513+
# Write all sections except the missing one
514+
sections = {
515+
"Executive Summary": "## Executive Summary\nSummary.\n",
516+
"Requirements & Goals": "## Requirements & Goals\n- **[REQ-01]:** The system *shall* validate.\n",
517+
"Architecture Overview": "## Architecture Overview\n```mermaid\ngraph TD\n A-->B\n```\n",
518+
"Architecture Decisions": "## Architecture Decisions\n### AD-01: Decision\n- **Status:** Accepted\n\n**Context:** C\n**Decision:** D\n**Consequences:** E\n",
519+
"Data Models": "## Data Models\n```dbml\nTable t { id integer [pk] }\n```\n",
520+
"Interface Contracts": "## Interface Contracts\n```python\nproto...\n```\n",
521+
"Detailed Design": "## Detailed Design\nDetails.\n",
522+
"Verification & Testing Strategy": "## Verification & Testing Strategy\nStrategy.\n",
523+
"Implementation Plan": "## Implementation Plan\n- [ ] Phase 1\n",
524+
}
525+
content = "# Design: Full Feature\n\n"
526+
for sec_name, sec_content in sections.items():
527+
if sec_name != section:
528+
content += sec_content + "\n"
529+
design_file.write_text(content)
530+
531+
tasks_file = context.spec_dir / "tasks.md"
532+
tasks_file.write_text(
533+
"# Tasks\n"
534+
"\n"
535+
"### Task 1.1: Implement\n"
536+
"Context: Build.\n"
537+
"Verification: Run tests.\n"
538+
"Scenario Coverage: Scenario.\n"
539+
"Loop Type: TDD-only\n"
540+
"Behavioral Contract: Must pass.\n"
541+
"Simplification Focus: Keep minimal.\n"
542+
"BDD Verification: N/A — TDD-only.\n"
543+
"Advanced Test Verification: N/A — no advanced tests.\n"
544+
"Runtime Verification: N/A — no runtime.\n"
545+
"Status: 🔴 TODO\n"
546+
"- [ ] Step 1: Write test\n"
547+
)
548+
549+
features_dir = context.spec_dir / "features"
550+
features_dir.mkdir(exist_ok=True)
551+
(features_dir / "test.feature").write_text(
552+
"Feature: Test\n Scenario: Test\n Given a\n When b\n Then c\n"
553+
)
554+
555+
556+
@given("I have a consolidated tasks.md with tasks across multiple findings")
557+
def step_consolidated_cross_finding_tasks(context) -> None:
558+
"""Create a consolidated tasks.md with tasks numbered across findings."""
559+
design_file = context.spec_dir / "design.md"
560+
design_file.write_text(
561+
"# Design: Multiple Findings\n"
562+
"\n"
563+
"## Summary\n"
564+
"Multiple improvements.\n"
565+
"\n"
566+
"## Approach\n"
567+
"Approach.\n"
568+
"\n"
569+
"## Architecture Decisions\n"
570+
"Decision.\n"
571+
"\n"
572+
"## BDD/TDD Strategy\n"
573+
"Strategy.\n"
574+
"\n"
575+
"## Code Simplification Constraints\n"
576+
"Keep minimal.\n"
577+
"\n"
578+
"## BDD Scenario Inventory\n"
579+
"Scenarios.\n"
580+
"\n"
581+
"## Existing Components to Reuse\n"
582+
"None.\n"
583+
"\n"
584+
"## Verification\n"
585+
"Run tests.\n"
586+
)
587+
588+
tasks_file = context.spec_dir / "tasks.md"
589+
tasks_file.write_text(
590+
"# Tasks\n"
591+
"\n"
592+
"### Task 1.1: Finding 1 — Fix bug\n"
593+
"Context: Fix the bug.\n"
594+
"Verification: Run tests.\n"
595+
"Scenario Coverage: features/correctness.feature — Bug fix.\n"
596+
"Loop Type: BDD+TDD\n"
597+
"Behavioral Contract: Must pass.\n"
598+
"Simplification Focus: Keep minimal.\n"
599+
"BDD Verification: uv run behave features/correctness.feature\n"
600+
"Advanced Test Verification: N/A — no advanced tests.\n"
601+
"Runtime Verification: N/A — no runtime changes.\n"
602+
"Status: 🔴 TODO\n"
603+
"- [ ] Step 1: Write test\n"
604+
"\n"
605+
"### Task 2.1: Finding 2 — Add feature\n"
606+
"Context: Add new feature.\n"
607+
"Verification: Run tests.\n"
608+
"Scenario Coverage: features/security.feature — Auth check.\n"
609+
"Loop Type: BDD+TDD\n"
610+
"Behavioral Contract: Must pass.\n"
611+
"Simplification Focus: Keep minimal.\n"
612+
"BDD Verification: uv run behave features/security.feature\n"
613+
"Advanced Test Verification: N/A — no advanced tests.\n"
614+
"Runtime Verification: N/A — no runtime changes.\n"
615+
"Status: 🔴 TODO\n"
616+
"- [ ] Step 1: Write test\n"
617+
)
618+
619+
features_dir = context.spec_dir / "features"
620+
features_dir.mkdir(exist_ok=True)
621+
(features_dir / "correctness.feature").write_text(
622+
"Feature: Correctness\n"
623+
" Scenario: Bug fix\n"
624+
" Given a bug\n"
625+
" When fixed\n"
626+
" Then works\n"
627+
)
628+
(features_dir / "security.feature").write_text(
629+
"Feature: Security\n"
630+
" Scenario: Auth check\n"
631+
" Given a user\n"
632+
" When authenticated\n"
633+
" Then access granted\n"
634+
)
635+
636+
255637
def _init_git_repo(context) -> None:
256638
"""Initialize a git repo with an initial commit so scanner can find files."""
257639
subprocess.run(["git", "init"], cwd=context.temp_dir, capture_output=True, check=True)

0 commit comments

Comments
 (0)