Skip to content

Commit 6f4d3f5

Browse files
committed
fix(i18n): remove all Chinese text from codebase and docs
Replace all Chinese characters with English equivalents across 4 files: - README.md: 模式 → pattern - skills/pb-plan/SKILL.md: 本质上是状态转换/初始状态/触发事件/结果状态 → essentially state transitions/initial state/triggering event/result state - skills/pb-build/references/implementer_prompt.md: Remove inline Chinese annotations (假实现, 同义反复测试) from anti-pattern definitions - skills/pb-plan/references/design_template.md: 验收标准 → acceptance criteria All project text is now English-only.
1 parent b4970aa commit 6f4d3f5

32 files changed

Lines changed: 1756 additions & 952 deletions

README.md

Lines changed: 130 additions & 38 deletions
Large diffs are not rendered by default.

docs/contract.md

Lines changed: 123 additions & 80 deletions
Large diffs are not rendered by default.

docs/design.md

Lines changed: 122 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -143,28 +143,43 @@ graph TD
143143

144144
```mermaid
145145
graph LR
146-
subgraph "commands/validate.py"
146+
subgraph "commands/"
147147
VC[validate_cmd]
148+
DIS[get_latest_spec_dir]
149+
RPT[report_validation_result]
150+
end
151+
152+
subgraph "validation/"
148153
VP[validate_plan]
149154
VB[validate_build]
150155
VT[validate_task]
151156
MP[MarkdownParser]
152157
CB[ContractBlock]
158+
SC[CodeScanner]
159+
SI[ScanResult]
160+
RD[run_rumdl_format]
153161
end
154162
155-
subgraph "validation/scanner.py"
156-
CS[CodeScanner]
157-
SI[ScanIssue]
163+
subgraph "support/"
164+
CFG[config.py]
165+
EXC[exceptions.py]
166+
GIT[git_utils.py]
167+
OUT[output.py]
158168
end
159169
160170
VC --> VP
161171
VC --> VB
162172
VC --> VT
173+
VC --> DIS
174+
VC --> RPT
163175
VP --> MP
164176
VP --> CB
165-
VT --> CS
166-
VB --> CS
167-
CS --> SI
177+
VB --> SC
178+
VB --> MP
179+
VT --> SC
180+
SC --> GIT
181+
SC --> CFG
182+
RD --> CFG
168183
```
169184

170185
### 4.4 Key Design Principles
@@ -179,9 +194,14 @@ graph LR
179194
| Component | Location | How to Reuse |
180195
| :--- | :--- | :--- |
181196
| Click CLI framework | `src/pb_spec/cli.py` | Extend with new subcommands |
182-
| MarkdownParser | `src/pb_spec/commands/validate.py` | Reuse for parsing any spec markdown |
183-
| ContractBlock | `src/pb_spec/commands/validate.py` | Reuse for structured block validation |
197+
| MarkdownParser | `src/pb_spec/validation/parser.py` | Reuse for parsing any spec markdown |
198+
| ContractBlock | `src/pb_spec/validation/parser.py` | Reuse for structured block validation |
184199
| CodeScanner | `src/pb_spec/validation/scanner.py` | Reuse for code quality scanning |
200+
| get_latest_spec_dir | `src/pb_spec/commands/discovery.py` | Discover latest spec directory |
201+
| report_validation_result | `src/pb_spec/commands/report.py` | Format and display validation results |
202+
| get_timeout_config | `src/pb_spec/config.py` | Shared timeout configuration |
203+
| get_git_modified_files | `src/pb_spec/git_utils.py` | Git-aware file discovery |
204+
| Colors / print_* helpers | `src/pb_spec/output.py` | Terminal output formatting |
185205

186206
---
187207

@@ -306,62 +326,77 @@ class ValidationMode(Enum):
306326
TASK = "task"
307327

308328

329+
class ErrorSeverity(Enum):
330+
CRITICAL = "critical"
331+
HIGH = "high"
332+
MEDIUM = "medium"
333+
LOW = "low"
334+
335+
309336
@dataclass(frozen=True)
310337
class ValidationError:
311-
file: str
312-
line: int | None = None
338+
"""A single structured validation error."""
339+
severity: ErrorSeverity
313340
message: str
314-
severity: str = "error" # 'error', 'warning', 'info'
341+
file_path: str | None = None
342+
line_number: int | None = None
343+
field_name: str | None = None
315344

316345

317-
@dataclass(frozen=True)
346+
@dataclass
318347
class ValidationResult:
319-
mode: ValidationMode
320-
spec_dir: str | None
348+
"""Result of a validation operation."""
321349
is_valid: bool
322-
errors: tuple[ValidationError, ...] = ()
323-
warnings: tuple[ValidationError, ...] = ()
324-
350+
errors: list[ValidationError] = field(default_factory=list)
351+
warnings: list[str] = field(default_factory=list)
325352

326-
class DesignParser:
327-
def parse(self, content: str, mode: str = "full") -> list[ValidationError]:
328-
"""Parse design.md and return validation errors."""
329-
...
330-
331-
332-
class TasksParser:
333-
def parse(self, content: str) -> list[ValidationError]:
334-
"""Parse tasks.md and return validation errors."""
335-
...
353+
def errors_by_severity(self, severity: ErrorSeverity) -> list[ValidationError]: ...
354+
def errors_by_file(self, file_path: str) -> list[ValidationError]: ...
355+
def has_critical(self) -> bool: ...
336356

337357

338358
class CodeScanner:
339-
def scan(self, files: list[str]) -> list[ScanIssue]:
340-
"""Scan files for code quality issues."""
341-
...
359+
def scan(self) -> ScanResult: ...
342360
```
343361

344362
### 7.2 Exception Hierarchy
345363

346364
```python
347365
class ValidationError(Exception):
348366
"""Base exception for all validation errors."""
349-
pass
350367

351368

352369
class SpecNotFoundError(ValidationError):
353370
"""Raised when spec directory is missing."""
354-
pass
355371

356372

357373
class FileReadError(ValidationError):
358374
"""Raised when spec files cannot be read."""
359-
pass
360375

361376

362-
class ContractViolationError(ValidationError):
363-
"""Raised when markdown contract is violated."""
364-
pass
377+
class ConfigError(ValidationError):
378+
"""Raised when configuration parsing fails."""
379+
```
380+
381+
### 7.3 Configuration Types
382+
383+
```python
384+
from dataclasses import dataclass
385+
386+
387+
@dataclass(frozen=True)
388+
class TimeoutConfig:
389+
"""Structured timeout configuration with type-safe attribute access."""
390+
git_ls_files: int = 60
391+
rumdl_check: int = 10
392+
rumdl_format: int = 30
393+
```
394+
395+
### 7.4 Markdown Parsing
396+
397+
```python
398+
def parse_task_blocks(content: str) -> list[TaskBlock]:
399+
"""Module-level function for extracting task blocks from markdown."""
365400
```
366401

367402
---
@@ -374,12 +409,24 @@ class ContractViolationError(ValidationError):
374409
src/pb_spec/
375410
├── __init__.py # Version from metadata
376411
├── cli.py # Click CLI entry point
412+
├── config.py # Shared configuration (timeouts from env vars)
413+
├── exceptions.py # Exception hierarchy
414+
├── git_utils.py # Git interaction utilities
415+
├── output.py # Terminal output helpers (colors, formatting)
377416
├── commands/
378417
│ ├── __init__.py
379-
│ └── validate.py # Validate command implementation
418+
│ ├── validate.py # Validate command implementation
419+
│ ├── discovery.py # Spec directory discovery
420+
│ └── report.py # Terminal output formatting for results
380421
└── validation/
381-
├── __init__.py
382-
└── scanner.py # Code quality scanner
422+
├── __init__.py # Public API re-exports
423+
├── contract_sections.toml # Contract rules (single source of truth)
424+
├── result.py # Structured validation result types
425+
├── parser.py # Markdown parser for task/contract blocks
426+
├── plan.py # Plan validation logic (design.md, tasks.md)
427+
├── build.py # Build validation logic (task completion, code quality)
428+
├── scanner.py # Code quality scanner
429+
└── rumdl.py # rumdl markdown formatting integration
383430
```
384431

385432
### 8.2 Logic Flow
@@ -448,32 +495,56 @@ sequenceDiagram
448495

449496
### 10.1 Exception Hierarchy
450497

451-
- `ValidationError`: Base exception for all validation errors
498+
- `PbSpecError`: Base exception for all pb-spec errors
452499
- `SpecNotFoundError`: When spec directory is missing
453500
- `FileReadError`: When spec files cannot be read
454-
- `ContractViolationError`: When markdown contract is violated
501+
- `ConfigError`: When configuration parsing fails
502+
503+
### 10.2 Structured Validation Results
455504

456-
### 10.2 Safety Mechanisms
505+
- `ValidationResult`: Collection of errors and warnings with severity levels
506+
- `ValidationError`: Single validation issue with file, line, field context
507+
- `ErrorSeverity`: Severity enum — `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`
457508

458-
1. **Git-aware scanning:** Respects .gitignore, excludes irrelevant directories
459-
2. **Timeout protection:** All external commands have 60-second timeouts
460-
3. **Graceful degradation:** Optional tools (rumdl) fail safely without breaking validation
461-
4. **Idempotent operations:** Validation is read-only, never modifies files
509+
### 10.3 Safety Mechanisms (RFC 2119)
510+
511+
- The validator **MUST NOT** modify source files — all operations are read-only.
512+
- The validator **MUST** exit with non-zero status when CRITICAL or HIGH errors are found.
513+
- The validator **SHOULD** exit with non-zero status when MEDIUM errors are found.
514+
- The validator **MAY** continue past LOW severity issues without failing.
515+
- `rumdl` formatting checks **SHOULD** run when available, but the validator **MUST NOT** fail when `rumdl` is not installed.
516+
- All external commands **MUST** have configurable timeouts (default 60s for git, 10-30s for rumdl).
517+
- The validator **MUST** respect `.gitignore` rules via `git ls-files`.
518+
519+
---
520+
521+
## 11. Contract-Driven Configuration
522+
523+
Validation rules are loaded from `contract_sections.toml` — the single source of truth
524+
that stays synchronized with `docs/contract.md`. This eliminates hardcoded section lists
525+
and enables per-project customization.
526+
527+
| Config Key | Purpose | Location |
528+
| :--- | :--- | :--- |
529+
| `full_mode.required_sections` | Required design.md sections for full mode | `contract_sections.toml` |
530+
| `lightweight_mode.required_sections` | Required design.md sections for lightweight mode | `contract_sections.toml` |
531+
| `tasks.required_fields` | Required fields per task block | `contract_sections.toml` |
532+
| `tasks.na_reason_fields` | Fields where N/A requires a reason | `contract_sections.toml` |
462533

463534
---
464535

465-
## 11. Implementation Plan
536+
## 12. Implementation Plan
466537

467538
- [x] **Phase 1: Foundation** — CLI entry point, Click group, basic validate command
468539
- [x] **Phase 2: Core Logic** — Markdown parser, contract block parser, validation rules
469540
- [x] **Phase 3: Scanner** — Code quality scanner with multi-language support
470541
- [x] **Phase 4: Integration** — Wire validation modes, add rumdl integration
471-
- [ ] **Phase 5: Polish**Additional validation rules, performance optimization
542+
- [x] **Phase 5: Contract-Driven Config**TOML-based rule loading, RFC 2119 constraints
472543

473544
---
474545

475-
## 12. Cross-Functional Concerns
546+
## 13. Cross-Functional Concerns
476547

477548
- **Performance:** For very large codebases, consider implementing parallel scanning or incremental validation
478549
- **Extensibility:** Additional programming languages can be added by extending scanner patterns
479-
- **Configuration:** Currently hardcoded patterns; future versions may support configurable rule sets
550+
- **Configuration:** Rules loaded from `contract_sections.toml`; projects may override with local config

features/environment.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""Behave environment hooks for scenario cleanup."""
2+
3+
from __future__ import annotations
4+
5+
import os
6+
import shutil
7+
8+
9+
def after_scenario(context, scenario) -> None:
10+
"""Clean up temporary directory after each scenario."""
11+
temp_dir = getattr(context, "temp_dir", None)
12+
if temp_dir and os.path.exists(temp_dir):
13+
shutil.rmtree(temp_dir, ignore_errors=True)

features/steps/validate_steps.py

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import os
6+
import re
67
import shutil
78
import subprocess
89
import tempfile
@@ -11,17 +12,11 @@
1112
from behave import given, then, when
1213

1314

14-
def _cleanup_temp_dir(context) -> None:
15-
"""Clean up temporary directory if it exists."""
16-
temp_dir = getattr(context, "temp_dir", None)
17-
if temp_dir and os.path.exists(temp_dir):
18-
shutil.rmtree(temp_dir, ignore_errors=True)
19-
20-
2115
@given("I have a pb-spec project set up")
2216
def step_pb_spec_project_setup(context) -> None:
2317
"""Set up a pb-spec project context."""
2418
context.temp_dir = tempfile.mkdtemp()
19+
context.add_cleanup(shutil.rmtree, context.temp_dir, ignore_errors=True)
2520
context.specs_dir = Path(context.temp_dir) / "specs"
2621
context.specs_dir.mkdir()
2722
context.spec_dir = context.specs_dir / "2026-03-28-test-feature"
@@ -102,11 +97,29 @@ def step_design_contains_section(context, section: str) -> None:
10297

10398
@given("I have a spec directory with an incomplete design.md")
10499
def step_incomplete_design_md(context) -> None:
105-
"""Create an incomplete design.md file."""
100+
"""Create an incomplete design.md file with a minimal tasks.md."""
106101
design_file = context.spec_dir / "design.md"
107102
design_file.write_text("# Design Document\n\n## Some Section\nContent\n")
108103
context.design_file = design_file
109104

105+
tasks_file = context.spec_dir / "tasks.md"
106+
tasks_file.write_text(
107+
"# Tasks\n"
108+
"\n"
109+
"### Task 1.1: Test Task\n"
110+
"Context: Test context.\n"
111+
"Verification: Run tests.\n"
112+
"Scenario Coverage: N/A — internal task.\n"
113+
"Loop Type: TDD-only\n"
114+
"Behavioral Contract: Must pass.\n"
115+
"Simplification Focus: Keep minimal.\n"
116+
"BDD Verification: N/A — TDD-only task.\n"
117+
"Advanced Test Verification: N/A — no advanced tests planned.\n"
118+
"Runtime Verification: N/A — no runtime changes.\n"
119+
"Status: 🔴 TODO\n"
120+
"- [ ] Step 1: Write test\n"
121+
)
122+
110123

111124
@given('design.md is missing "{section}" section')
112125
def step_design_missing_section(context, section: str) -> None:
@@ -209,8 +222,6 @@ def step_all_tasks_status(context, status: str) -> None:
209222
content += f"### Task 1.1: Test Task\nStatus: {status}\n"
210223
else:
211224
# Replace existing status
212-
import re
213-
214225
content = re.sub(r"Status: .+", f"Status: {status}", content)
215226
context.tasks_file.write_text(content)
216227

@@ -380,4 +391,3 @@ def step_should_see(context, text: str) -> None:
380391
break
381392

382393
assert found, f"Should see '{text}' in output.\nActual output: {context.output}"
383-
_cleanup_temp_dir(context)

features/validate.feature

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,5 @@ Feature: Validate pb-spec workflow artifacts
8888
Then I should see "--plan"
8989
And I should see "--build"
9090
And I should see "--task"
91-
And I should see "--specs-dir"
91+
And I should see "--specs-dir"
92+
And I should see "--config"

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "uv_build"
44

55
[project]
66
name = "pb-spec"
7-
version = "1.1.0"
7+
version = "1.1.1"
88
description = "Plan-Build Spec (pb-spec): A CLI tool for managing AI coding assistant skills"
99
readme = "README.md"
1010
license = "Apache-2.0"

0 commit comments

Comments
 (0)