From 5ca1a6a5a22119d9f0478e4a5eccca2809381029 Mon Sep 17 00:00:00 2001 From: omereliy Date: Tue, 19 May 2026 10:31:01 +0300 Subject: [PATCH 1/4] v0.1.5: stop leaking "Plan is VALID" on syntax-only calls format_plain_text emitted "All goals satisfied. Plan is VALID." whenever is_valid=True, including the validate_syntax(domain[, problem]) path where no plan was executed and no goal was checked. Gate the Goal Check block on "execution" in result.phases and, for the syntax-only success case, emit a single accurate line instead. Downstream consumers (pddl-copilot pddl-validator 2.2.0) can drop their string-strip workaround once pinned to >=0.1.5. --- CHANGELOG.md | 19 ++++++++++++++ pyproject.toml | 2 +- pyval/__init__.py | 2 +- pyval/report_formatter.py | 46 ++++++++++++++++++++-------------- tests/test_report_formatter.py | 30 ++++++++++++++++++++++ 5 files changed, 78 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f8cbe4..486d786 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,25 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [S ## [Unreleased] +## [0.1.5] — 2026-05-19 + +### Fixed +- Plain-text report no longer claims `"All goals satisfied. Plan is VALID."` + on syntax-only calls (`validate_syntax(domain[, problem])`). The "Goal Check" + block in `pyval/report_formatter.py` ran unconditionally whenever + `is_valid=True`, even though no plan had been executed and no goal had been + checked. The block is now gated on `"execution" in result.phases`; the + syntax-only success path emits a single accurate line instead: + `"All syntax and consistency checks passed. No plan was executed."`. + Downstream consumers that previously string-stripped the leaked verdict + (pddl-copilot's `pddl-validator` plugin v2.2.0) can drop that workaround. + +### Added +- `tests/test_report_formatter.py::test_plain_text_syntax_only_success_domain_and_problem` + and `::test_plain_text_syntax_only_success_domain_only` lock in the no-plan + semantics: no `"Plan is VALID/INVALID"`, no `"Goal Check"`, and the new + success line present. + ## [0.1.4] — 2026-04-20 ### Fixed diff --git a/pyproject.toml b/pyproject.toml index 861f2a0..a273e96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pddl-pyvalidator" -version = "0.1.4" +version = "0.1.5" description = "Pure Python PDDL plan validator" readme = "README.md" license = "MIT" diff --git a/pyval/__init__.py b/pyval/__init__.py index f091d93..1fd85d9 100644 --- a/pyval/__init__.py +++ b/pyval/__init__.py @@ -1,6 +1,6 @@ """PyVAL — Pure Python PDDL plan validator.""" -__version__ = "0.1.4" +__version__ = "0.1.5" from pyval.models import ( GoalResult, diff --git a/pyval/report_formatter.py b/pyval/report_formatter.py index 1ae307b..ce9c68d 100644 --- a/pyval/report_formatter.py +++ b/pyval/report_formatter.py @@ -68,26 +68,34 @@ def format_plain_text(result: ValidationResult, verbose: bool = False) -> str: lines.append(f" Deficit: {failure.deficit} units") lines.append("") - # Goal check - lines.append("=== Goal Check ===") - if result.is_valid: - lines.append("All goals satisfied. Plan is VALID.") - else: - if result.failed_step is not None: - total = result.phases.get("execution", {}).get("total_steps", "?") - lines.append( - f"Plan is INVALID. Failed at step {result.failed_step} of {total}." - ) - remaining = int(total) - result.failed_step if isinstance(total, int) else "?" - lines.append(f"Remaining actions not executed: {remaining}") - elif result.unsatisfied_goals: - lines.append("Plan executed but goals are NOT satisfied.") - for goal in result.unsatisfied_goals: - lines.append(f" Unmet goal: {goal.expression}") - for k, v in goal.current_values.items(): - lines.append(f" Current value: {v}") + # Goal check — only when Phase 3 (plan execution) actually ran. + # Syntax-only calls (validate_syntax) never simulate a plan; emitting + # "Plan is VALID/INVALID" there is a misleading verdict on something + # that was not checked. + if "execution" in result.phases: + lines.append("=== Goal Check ===") + if result.is_valid: + lines.append("All goals satisfied. Plan is VALID.") else: - lines.append(f"Plan is {result.status}.") + if result.failed_step is not None: + total = result.phases.get("execution", {}).get("total_steps", "?") + lines.append( + f"Plan is INVALID. Failed at step {result.failed_step} of {total}." + ) + remaining = int(total) - result.failed_step if isinstance(total, int) else "?" + lines.append(f"Remaining actions not executed: {remaining}") + elif result.unsatisfied_goals: + lines.append("Plan executed but goals are NOT satisfied.") + for goal in result.unsatisfied_goals: + lines.append(f" Unmet goal: {goal.expression}") + for k, v in goal.current_values.items(): + lines.append(f" Current value: {v}") + else: + lines.append(f"Plan is {result.status}.") + elif result.is_valid: + lines.append( + "All syntax and consistency checks passed. No plan was executed." + ) # Summary if result.steps: diff --git a/tests/test_report_formatter.py b/tests/test_report_formatter.py index 40955f2..5333df2 100644 --- a/tests/test_report_formatter.py +++ b/tests/test_report_formatter.py @@ -115,3 +115,33 @@ def test_plain_text_syntax_error(tmp_path): text = format_plain_text(result) assert "SYNTAX_ERROR" in text assert "[ERROR]" in text + + +def test_plain_text_syntax_only_success_domain_and_problem(classical_files): + """validate_syntax(domain, problem) on clean PDDL must not claim a plan + verdict — no plan was executed.""" + v = PDDLValidator() + result = v.validate_syntax( + classical_files["domain"], classical_files["problem"] + ) + text = format_plain_text(result) + assert "Plan is VALID" not in text + assert "Plan is INVALID" not in text + assert "Goal Check" not in text + assert ( + "All syntax and consistency checks passed. No plan was executed." in text + ) + + +def test_plain_text_syntax_only_success_domain_only(classical_files): + """validate_syntax(domain) on a clean domain must not claim a plan + verdict — no plan was executed.""" + v = PDDLValidator() + result = v.validate_syntax(classical_files["domain"]) + text = format_plain_text(result) + assert "Plan is VALID" not in text + assert "Plan is INVALID" not in text + assert "Goal Check" not in text + assert ( + "All syntax and consistency checks passed. No plan was executed." in text + ) From 83618e6b85e6c2b2e833d25718aa9e80339168e3 Mon Sep 17 00:00:00 2001 From: omereliy Date: Tue, 19 May 2026 12:58:43 +0300 Subject: [PATCH 2/4] removed redundent doc --- VALIDATOR_SPEC.md | 468 ---------------------------------------------- 1 file changed, 468 deletions(-) delete mode 100644 VALIDATOR_SPEC.md diff --git a/VALIDATOR_SPEC.md b/VALIDATOR_SPEC.md deleted file mode 100644 index 053e3ee..0000000 --- a/VALIDATOR_SPEC.md +++ /dev/null @@ -1,468 +0,0 @@ -# PyVAL — Python PDDL Validator Specification - -## Overview - -PyVAL is a pure-Python PDDL plan validator designed to replace compiled VAL binaries in LLM-augmented planning pipelines. It produces rich, structured diagnostic output suitable for consumption by Large Language Models (LLMs) operating as planning copilots. - -Built on top of the [Unified Planning Framework (UPF)](https://github.com/aiplan4eu/unified-planning), PyVAL supports both classical and numeric PDDL domains and requires zero compiled dependencies — it is fully pip-installable. - ---- - -## Goals - -1. **Drop-in replacement** for VAL's `validate` binary in MCP-based planning tool servers (e.g., SPL-BGU/PlanningCopilot). -2. **Rich diagnostic output** — match or exceed VAL's verbose mode (`-v`) and error reporting (`-e`) quality, with output structured for LLM parsing. -3. **Classical + Numeric PDDL support** — handle `:strips`, `:typing`, `:numeric-fluents`, `:negative-preconditions`, `:equality`, `:conditional-effects`, and `:action-costs`. -4. **Zero compiled dependencies** — pure Python, pip-installable, no Docker, no platform-specific binaries. -5. **Programmatic API** — usable both as a CLI tool and as an importable Python library. - ---- - -## Scope - -### In Scope - -- PDDL domain syntax validation (type checking, predicate/function declarations, action schema well-formedness). -- PDDL problem validation against a domain (object types, initial state consistency, goal well-formedness). -- Sequential plan validation (precondition checking, effect application, goal verification). -- Numeric fluent tracking throughout plan execution (increase, decrease, assign, scale-up, scale-down effects). -- Numeric precondition evaluation (comparisons: `<`, `<=`, `=`, `>=`, `>` over arithmetic expressions). -- Detailed per-step diagnostic reports when validation fails. -- Repair advice generation (identifying unsatisfied preconditions and suggesting fixes). -- State trajectory extraction (fluent values at each step of the plan). -- Goal achievement analysis (which goals are met/unmet after plan execution). - -### Out of Scope (v1) - -- Temporal plan validation (durative actions, `at start`/`at end`/`over all` conditions). -- PDDL+ features (processes, events, continuous change). -- Derived predicates / axioms. -- Multi-agent plan validation. -- LaTeX report generation. -- Plan optimization metric evaluation (may be added in a future version). - ---- - -## Architecture - -### Dependencies - -| Package | Purpose | -|---|---| -| `unified-planning` | PDDL parsing (`PDDLReader`), state simulation (`SequentialSimulator`), plan validation (`PlanValidator`), expression evaluation | -| `pddl-plus-parser` | Trajectory I/O format compatibility with PlanningCopilot's `get_state_transition` tool (optional integration layer) | - -### Core Modules - -``` -pyval/ -├── __init__.py -├── cli.py # CLI entry point (mirrors VAL's Validate interface) -├── validator.py # Main validation orchestrator -├── syntax_checker.py # Domain/problem syntax and semantic validation -├── plan_simulator.py # Step-by-step plan simulation with diagnostics -├── diagnostics.py # Diagnostic message generation (RepairAdvice equivalent) -├── numeric_tracker.py # Numeric fluent value tracking across plan steps -├── report_formatter.py # Output formatting (plain text, structured JSON) -└── models.py # Data classes for validation results -``` - ---- - -## Validation Pipeline - -The validator operates in three sequential phases. Execution halts at the first phase that produces errors of severity `FATAL`. - -### Phase 1 — Syntax & Semantic Validation - -Validates domain and problem files independently and against each other. - -**Checks performed:** - -- PDDL parse success (well-formed S-expressions, valid keywords). -- All types referenced in predicates, functions, actions, and objects are declared. -- All predicates and functions used in preconditions and effects are declared with correct arity. -- Action parameters have declared types. -- No duplicate predicate, function, type, action, or object names. -- Problem domain name matches the provided domain. -- All objects in the problem have types declared in the domain. -- Initial state only references declared predicates/functions with correct arity and types. -- Goal formula only references declared predicates/functions with correct arity and types. -- Numeric function initial values are assigned where expected. - -**Output on failure:** - -``` -=== Syntax & Semantic Validation === -[ERROR] Domain: Action 'move' references undeclared predicate 'at_robot' (did you mean 'robot_at'?) -[ERROR] Domain: Function '(distance ?from ?to)' declared with types (city, city) but used in action 'drive' with types (location, location) -[WARNING] Problem: Numeric function '(fuel truck1)' has no initial value assigned — defaults to 0 -``` - -### Phase 2 — Plan Structure Validation - -Validates the plan file against the domain and problem. - -**Checks performed:** - -- Plan file parses correctly (action names and parameters per line). -- Every action name in the plan corresponds to a declared action in the domain. -- Every action parameter in the plan corresponds to a declared object in the problem. -- Parameter types match action schema parameter types. -- No empty plan when goals are not satisfied in the initial state. - -**Output on failure:** - -``` -=== Plan Structure Validation === -[ERROR] Step 3: Action 'pick_up' is not declared in domain 'logistics' -[ERROR] Step 7: Action 'drive(truck1, depot, city3)' — object 'city3' is not declared in problem -[ERROR] Step 7: Action 'drive(truck1, depot, city3)' — parameter 3 expects type 'location', got undeclared object -``` - -### Phase 3 — Plan Execution Simulation - -Simulates the plan step-by-step using UPF's `SequentialSimulator`, checking preconditions and applying effects at each step. - -**For each action in the plan:** - -1. Evaluate all preconditions against the current state. -2. If any precondition is unsatisfied, report: - - Which precondition failed. - - The current values of all fluents involved in the failed precondition. - - A human-readable explanation of what would need to change. -3. If all preconditions are met, apply the action's effects and advance the state. -4. Record all fluent values that changed (boolean and numeric). - -**After all actions:** - -5. Evaluate goal satisfaction against the final state. -6. Report which goals are met and which are unmet (with current values for numeric goals). - -**Output on success:** - -``` -=== Plan Execution === -Step 1: (drive truck1 depot loc1) ✓ - Changed: (at truck1 depot) = false, (at truck1 loc1) = true, (fuel truck1) = 92 (was 100) -Step 2: (load pkg1 truck1 loc1) ✓ - Changed: (in pkg1 truck1) = true, (at_pkg pkg1 loc1) = false -Step 3: (drive truck1 loc1 loc2) ✓ - Changed: (at truck1 loc1) = false, (at truck1 loc2) = true, (fuel truck1) = 84 (was 92) - -=== Goal Check === -All goals satisfied. Plan is VALID. -Plan length: 3 actions -Final state numeric values: (fuel truck1) = 84 -``` - -**Output on failure:** - -``` -=== Plan Execution === -Step 1: (drive truck1 depot loc1) ✓ - Changed: (at truck1 depot) = false, (at truck1 loc1) = true, (fuel truck1) = 92 (was 100) -Step 2: (drive truck1 loc1 loc3) ✗ PRECONDITION FAILURE - Unsatisfied: (>= (fuel truck1) 20) - Current value: (fuel truck1) = 8 - Required: (fuel truck1) >= 20 - Deficit: 12 units - Unsatisfied: (connected loc1 loc3) - Current value: false - Explanation: No direct connection exists between loc1 and loc3 - -Plan is INVALID. Failed at step 2 of 5. -Remaining actions not executed: 3 -``` - ---- - -## Output Modes - -### Plain Text (default) - -Human-readable output matching VAL's verbose style. This is the primary mode for LLM consumption via MCP tools. - -### Structured JSON - -Machine-readable output for programmatic integration. - -```json -{ - "status": "INVALID", - "phases": { - "syntax": {"status": "PASS", "errors": [], "warnings": []}, - "structure": {"status": "PASS", "errors": [], "warnings": []}, - "execution": { - "status": "FAIL", - "failed_step": 2, - "total_steps": 5, - "steps": [ - { - "index": 1, - "action": "(drive truck1 depot loc1)", - "status": "OK", - "changes": { - "boolean": {"(at truck1 depot)": false, "(at truck1 loc1)": true}, - "numeric": {"(fuel truck1)": {"before": 100, "after": 92}} - } - }, - { - "index": 2, - "action": "(drive truck1 loc1 loc3)", - "status": "FAILED", - "unsatisfied_preconditions": [ - { - "expression": "(>= (fuel truck1) 20)", - "type": "numeric", - "current_values": {"(fuel truck1)": 8}, - "required": ">= 20", - "deficit": 12 - }, - { - "expression": "(connected loc1 loc3)", - "type": "boolean", - "current_value": false - } - ] - } - ] - }, - "goals": null - } -} -``` - -### State Trajectory (ValueSeq equivalent) - -Numeric fluent values tracked across the entire plan execution. - -``` -=== Numeric Fluent Trajectory === -Step | Action | (fuel truck1) | (packages_delivered) ------|-----------------------------|--------------:|--------------------: - 0 | [initial state] | 100 | 0 - 1 | (drive truck1 depot loc1) | 92 | 0 - 2 | (load pkg1 truck1 loc1) | 92 | 0 - 3 | (drive truck1 loc1 loc2) | 84 | 0 - 4 | (unload pkg1 truck1 loc2) | 84 | 1 -``` - ---- - -## API Interface - -### Python API - -```python -from pyval import PDDLValidator, ValidationResult - -# Initialize validator -validator = PDDLValidator() - -# Validate domain only (syntax check) -result: ValidationResult = validator.validate_syntax(domain_path="domain.pddl") - -# Validate domain + problem (syntax + consistency check) -result: ValidationResult = validator.validate_syntax( - domain_path="domain.pddl", - problem_path="problem.pddl" -) - -# Validate domain + problem + plan (full validation) -result: ValidationResult = validator.validate( - domain_path="domain.pddl", - problem_path="problem.pddl", - plan_path="plan.txt" -) - -# Access results -print(result.is_valid) # bool -print(result.status) # "VALID" | "INVALID" | "SYNTAX_ERROR" | "STRUCTURE_ERROR" -print(result.report()) # Plain text report (for LLM consumption) -print(result.to_json()) # Structured JSON -print(result.trajectory) # List of state snapshots -print(result.failed_step) # Step index where failure occurred (or None) -print(result.unsatisfied_goals) # List of unmet goals in final state -print(result.numeric_trajectory) # Dict of numeric fluent values per step - -# Get specific step diagnostics -step = result.steps[1] -print(step.action) # "(drive truck1 loc1 loc3)" -print(step.status) # "FAILED" -print(step.unsatisfied) # List of unsatisfied precondition diagnostics -``` - -### CLI Interface - -```bash -# Syntax validation only -pyval domain.pddl - -# Domain + problem validation -pyval domain.pddl problem.pddl - -# Full plan validation (verbose) -pyval -v domain.pddl problem.pddl plan.txt - -# JSON output -pyval --json domain.pddl problem.pddl plan.txt - -# Numeric trajectory output -pyval --trajectory domain.pddl problem.pddl plan.txt - -# Specific numeric fluent tracking (ValueSeq equivalent) -pyval --track "fuel truck1" --track "packages_delivered" domain.pddl problem.pddl plan.txt -``` - -### MCP Tool Interface - -Drop-in replacement for PlanningCopilot's `validate_pddl_syntax` tool: - -```python -@mcp.tool() -def validate_pddl_syntax(domain: str, problem: str = None, plan: str = None) -> str: - """Validates PDDL domain/problem/plan using PyVAL.""" - validator = PDDLValidator() - - if plan is not None: - result = validator.validate( - domain_path=domain, - problem_path=problem, - plan_path=plan - ) - elif problem is not None: - result = validator.validate_syntax( - domain_path=domain, - problem_path=problem - ) - else: - result = validator.validate_syntax(domain_path=domain) - - return result.report() -``` - ---- - -## Data Models - -```python -@dataclass -class ValidationResult: - status: Literal["VALID", "INVALID", "SYNTAX_ERROR", "STRUCTURE_ERROR"] - is_valid: bool - phases: dict # Phase-level results - steps: list[StepResult] # Per-action results - trajectory: list[StateSnapshot] # Full state at each step - numeric_trajectory: dict[str, list] # Fluent name -> values per step - failed_step: int | None # First failure index - unsatisfied_goals: list[GoalResult] # Unmet goals after execution - warnings: list[str] # Non-fatal warnings - -@dataclass -class StepResult: - index: int - action: str - status: Literal["OK", "FAILED"] - boolean_changes: dict[str, bool] - numeric_changes: dict[str, NumericChange] - unsatisfied: list[PreconditionFailure] - -@dataclass -class NumericChange: - before: float - after: float - -@dataclass -class PreconditionFailure: - expression: str # The precondition as PDDL string - type: Literal["boolean", "numeric"] - current_values: dict[str, Any] # Current values of involved fluents - explanation: str # Human-readable explanation - deficit: float | None # For numeric: how far from satisfaction - -@dataclass -class GoalResult: - expression: str - satisfied: bool - current_values: dict[str, Any] - -@dataclass -class StateSnapshot: - step: int - action: str | None # None for initial state - boolean_fluents: dict[str, bool] - numeric_fluents: dict[str, float] -``` - ---- - -## Diagnostic Message Guidelines - -Diagnostic messages are the primary interface between PyVAL and the LLM. They must be: - -1. **Specific** — always name the exact precondition, fluent, action, and step involved. -2. **Quantitative** — for numeric failures, always report current value, required threshold, and deficit. -3. **Actionable** — suggest what would need to change for the precondition to be satisfied. -4. **Consistent** — use the same PDDL notation the LLM sees in the domain/problem files. -5. **Concise** — avoid boilerplate; every line should carry information. - -### Diagnostic Templates - -**Boolean precondition failure:** -``` -Unsatisfied: ({predicate} {args}) - Current value: false - Explanation: {predicate}({args}) is not true in the current state. - Last made true at: Step {n} by action ({action}) [if applicable] - Last made false at: Step {n} by action ({action}) [if applicable] -``` - -**Numeric precondition failure:** -``` -Unsatisfied: ({comparator} ({expression}) {threshold}) - Current value: ({fluent}) = {value} - Required: ({fluent}) {comparator} {threshold} - Deficit: {abs(threshold - value)} units -``` - -**Goal not achieved:** -``` -Unmet goal: ({goal_expression}) - Current value: {value} - Required: {description} - Nearest achieving action: {action_name} (sets this to true / modifies this fluent) -``` - ---- - -## Compatibility Notes - -- **VAL output parity**: PyVAL's plain-text output should be parseable by any system that currently parses VAL's verbose output. The format is intentionally similar but not identical — it is optimized for LLM consumption rather than strict backward compatibility. -- **PlanningCopilot integration**: The MCP tool function signature matches the existing `validate_pddl_syntax` tool in `solvers_server.py`, allowing a one-line swap. -- **Plan file format**: Accepts IPC-standard plan format (one grounded action per line, optional cost comments). - ---- - -## Implementation Reference - -The validation logic is informed by studying the following VAL source files (BSD 3-Clause licensed): - -| VAL Source File | PyVAL Equivalent | Purpose | -|---|---|---| -| `Validator.cpp` | `plan_simulator.py` | Main simulation loop: precondition check → effect apply → advance | -| `RepairAdvice.cpp` | `diagnostics.py` | Diagnostic message generation for failed preconditions | -| `State.cpp` | UPF `SequentialSimulator` | State representation and update semantics | -| `FuncExp.cpp` | UPF expression evaluation | Numeric expression evaluation | -| `typecheck.cpp` | `syntax_checker.py` | Type hierarchy and arity checking | -| `Plan.cpp` | `validator.py` | Plan parsing and action-to-schema matching | - ---- - -## Testing Strategy - -- **Unit tests** for each validation phase independently. -- **Cross-validation against VAL** on IPC benchmark domains (classical + numeric) to verify agreement on valid/invalid verdicts. -- **Diagnostic quality tests** — for known-invalid plans, assert that the diagnostic output contains the specific precondition, current value, and step number. -- **Numeric precision tests** — validate correct handling of floating-point arithmetic in numeric fluent tracking. -- **Regression suite** from VAL's known bug cases (plans that VAL incorrectly accepts/rejects). From 528401783bb58c2891ba6c3b6d037c15316ccbd9 Mon Sep 17 00:00:00 2001 From: omereliy Date: Tue, 19 May 2026 13:26:15 +0300 Subject: [PATCH 3/4] test: assert syntax-error report omits plan verdict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends test_plain_text_syntax_error to lock in the contract that a SYNTAX_ERROR result never emits "Plan is VALID/INVALID" or "Goal Check" — no plan was executed, no goals were checked. Cheap regression guard against anyone later removing the gate added in 0.1.5. --- tests/test_report_formatter.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_report_formatter.py b/tests/test_report_formatter.py index 5333df2..886ff0c 100644 --- a/tests/test_report_formatter.py +++ b/tests/test_report_formatter.py @@ -115,6 +115,11 @@ def test_plain_text_syntax_error(tmp_path): text = format_plain_text(result) assert "SYNTAX_ERROR" in text assert "[ERROR]" in text + # Regression guard: a syntax-error result must never emit a plan verdict + # — no plan was executed, no goals were checked. + assert "Plan is VALID" not in text + assert "Plan is INVALID" not in text + assert "Goal Check" not in text def test_plain_text_syntax_only_success_domain_and_problem(classical_files): From e6aa09a26d601cea5d5d6b1c906e8c61c5bad0d7 Mon Sep 17 00:00:00 2001 From: omereliy Date: Tue, 19 May 2026 13:32:31 +0300 Subject: [PATCH 4/4] docs: align references after VALIDATOR_SPEC.md removal - README, CLAUDE.md, and four .claude/skills/*.md files referenced VALIDATOR_SPEC.md (deleted in 83618e6); point them at CLAUDE.md, pyval/models.py, and the inline templates in pyval/diagnostics.py / pyval/report_formatter.py instead. - CLAUDE.md: update "Output Modes" to note that syntax-only validation omits the "Plan is VALID/INVALID" verdict (v0.1.5 behavior), fix stale tests/domains/ layout claim. - CHANGELOG: add "Removed" entry under v0.1.5 for VALIDATOR_SPEC.md so the deletion is recorded alongside the syntax-only fix. --- .claude/skills/implement.md | 10 +++++----- .claude/skills/plan-review-simplify/SKILL.md | 15 ++++++++------- .claude/skills/setup-project.md | 4 ++-- .claude/skills/simplify/SKILL.md | 5 +++-- CHANGELOG.md | 6 ++++++ CLAUDE.md | 12 ++++++------ README.md | 2 +- 7 files changed, 31 insertions(+), 23 deletions(-) diff --git a/.claude/skills/implement.md b/.claude/skills/implement.md index 79e0c5f..795dbdd 100644 --- a/.claude/skills/implement.md +++ b/.claude/skills/implement.md @@ -1,22 +1,22 @@ --- name: implement -description: Implement a module or feature from the spec. Reads VALIDATOR_SPEC.md, checks existing code, writes implementation with tests. +description: Implement a module or feature from the architecture reference. Reads CLAUDE.md and the existing pyval/ modules, then writes implementation with tests. --- ## Workflow -1. **Read the spec** — `VALIDATOR_SPEC.md` is the source of truth. Find the section relevant to the module/feature. +1. **Read the architecture reference** — `CLAUDE.md` (top-level) is the source of truth for pipeline phases, output modes, and conventions. Data-model shapes live in `pyval/models.py`; diagnostic templates live in `pyval/diagnostics.py` and `pyval/report_formatter.py`. 2. **Check existing code** — Search `pyval/` for related implementations. Don't duplicate. 3. **Check UPF API** — If the feature involves parsing, simulation, or state operations, verify the unified-planning API by reading its source or docs. Don't guess method signatures. -4. **Implement** — Follow the module structure from the spec. Use the data models from `models.py`. +4. **Implement** — Follow the module structure described in `CLAUDE.md`'s "Project Structure" section. Use the data models from `pyval/models.py`. 5. **Write tests** — Every public function gets at least one classical and one numeric test case. Use inline PDDL strings, not fixture files. 6. **Run tests** — `python3 -m pytest tests/ -v` ## Rules -- The spec's data models (`ValidationResult`, `StepResult`, etc.) are the contract. Don't change their shape without updating the spec. +- The data models in `pyval/models.py` (`ValidationResult`, `StepResult`, etc.) are the contract. Don't change their shape without updating every consumer and noting it in `CHANGELOG.md`. - All PDDL parsing goes through `unified-planning`'s `PDDLReader`. Do not write regex-based PDDL parsers. -- Diagnostic messages must follow the templates in the spec's "Diagnostic Message Guidelines" section. +- Diagnostic messages must follow the existing templates in `pyval/diagnostics.py` and `pyval/report_formatter.py` (precondition deficit reporting, repair-oriented messages, no leaking "Plan is VALID" when no plan was executed). - Handle both file paths and inline PDDL strings as input. - Numeric validation must track fluent values with float precision and report deficits. - Phase execution halts on first FATAL error — don't continue to Phase 3 if Phase 1 has FATAL errors. diff --git a/.claude/skills/plan-review-simplify/SKILL.md b/.claude/skills/plan-review-simplify/SKILL.md index 8475da4..3be5bcb 100644 --- a/.claude/skills/plan-review-simplify/SKILL.md +++ b/.claude/skills/plan-review-simplify/SKILL.md @@ -12,9 +12,10 @@ For the task described in $ARGUMENTS: ### Phase 1: Explore 1. Read all relevant existing code using Grep and Glob 2. Identify existing patterns that can be reused — especially: - - `pyval/models.py` for data model contracts + - `pyval/models.py` for data model contracts (and `to_json()` for the JSON output schema) - `pyval/validator.py` for pipeline orchestration patterns - - `VALIDATOR_SPEC.md` for output format and diagnostic templates + - `pyval/report_formatter.py` and `pyval/diagnostics.py` for output format and diagnostic templates + - `CLAUDE.md` for the architectural overview and conventions 3. Check all modules under `pyval/` to understand cross-module impact 4. Review `.claude/rules/` for validation semantics and UPF gotchas 5. **Run `python3 -m pytest tests/ -v`** to verify baseline test state before planning changes @@ -23,7 +24,7 @@ For the task described in $ARGUMENTS: Design the implementation approach covering: - **Objective**: One sentence describing the goal - **Analysis**: Current state, what needs to change, existing code to reuse -- **Spec alignment**: Which sections of VALIDATOR_SPEC.md are affected? +- **Reference alignment**: Which sections of `CLAUDE.md` (or which `pyval/` modules) are affected? Do output formats or diagnostic templates need updates in `report_formatter.py` / `diagnostics.py`? - **Pipeline phase**: Which phase does this change belong to? (Phase 1: syntax_checker, Phase 2: validator, Phase 3: plan_simulator, or cross-cutting: models/diagnostics/report_formatter) - **Models impact**: Does this change the models.py dataclass shapes? If so, which downstream modules break? - **Files to modify**: Table of file | action (create/modify/delete) | description @@ -45,10 +46,10 @@ Before presenting the plan, review it for simplification and correctness: - Does `models.py` remain the single source of truth for data shapes? - Does the change avoid duplicating logic across pipeline phases? -**Spec conformance:** -- Do output formats match VALIDATOR_SPEC.md templates? -- Do diagnostic messages follow the "Diagnostic Message Guidelines" section? -- Do data models match the spec's dataclass definitions? +**Reference conformance:** +- Do output formats match the existing templates in `pyval/report_formatter.py`? +- Do diagnostic messages follow the existing patterns in `pyval/diagnostics.py` (precondition deficits, repair advice, no leaking "Plan is VALID" when no plan was executed)? +- Do data models match the dataclass definitions in `pyval/models.py`? - Does the CLI interface stay compatible with VAL's `Validate` command? **UPF correctness:** diff --git a/.claude/skills/setup-project.md b/.claude/skills/setup-project.md index 5a22910..0ea61a1 100644 --- a/.claude/skills/setup-project.md +++ b/.claude/skills/setup-project.md @@ -15,7 +15,7 @@ description: Bootstrap the pyvalidator project structure. Creates pyproject.toml - Entry point: `[project.scripts] pyval = "pyval.cli:main"` - Include license, description, author metadata -2. **Create `pyval/` package** (all modules from VALIDATOR_SPEC.md): +2. **Create `pyval/` package** (modules per `CLAUDE.md`'s "Project Structure" section): ``` pyval/__init__.py # Public API: PDDLValidator, ValidationResult pyval/cli.py # CLI entry point @@ -55,7 +55,7 @@ description: Bootstrap the pyvalidator project structure. Creates pyproject.toml ## Rules -- Follow VALIDATOR_SPEC.md for all module names and data model shapes +- Follow `CLAUDE.md` for module names and the pipeline structure; data-model shapes come from `pyval/models.py` - Python >= 3.10 (for `match` statements and `X | Y` type union syntax) - `models.py` should be implemented first — other modules depend on its dataclasses - All `__init__.py` files should export public API names diff --git a/.claude/skills/simplify/SKILL.md b/.claude/skills/simplify/SKILL.md index 1b6b689..f4893e5 100644 --- a/.claude/skills/simplify/SKILL.md +++ b/.claude/skills/simplify/SKILL.md @@ -13,7 +13,8 @@ $ARGUMENTS If no specific target is given, review the most recent changes (check git diff or the current plan). Key reference files for correctness and convention review: -- VALIDATOR_SPEC.md — Authoritative spec for output formats, data models, and diagnostics -- pyval/models.py — Data model contract (ValidationResult, StepResult, etc.) +- CLAUDE.md — Authoritative architecture reference (pipeline phases, output modes, conventions) +- pyval/models.py — Data model contract (ValidationResult, StepResult, etc.) and JSON schema via `to_json()` +- pyval/diagnostics.py, pyval/report_formatter.py — Diagnostic message templates and output formats - .claude/rules/pddl-validation-semantics.md — Validation correctness rules - .claude/rules/upf-gotchas.md — UPF API pitfalls diff --git a/CHANGELOG.md b/CHANGELOG.md index 486d786..20c3a4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,12 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [S semantics: no `"Plan is VALID/INVALID"`, no `"Goal Check"`, and the new success line present. +### Removed +- `VALIDATOR_SPEC.md` — content was redundant with `CLAUDE.md` and the inline + module docs/diagnostic templates in `pyval/`. `CLAUDE.md` is now the single + authoritative architecture reference; data-model shapes are documented by + `pyval/models.py` and the JSON schema by `ValidationResult.to_json()`. + ## [0.1.4] — 2026-04-20 ### Fixed diff --git a/CLAUDE.md b/CLAUDE.md index 2705e73..91f4429 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -23,13 +23,13 @@ pyval/ ├── report_formatter.py # Output formatting (plain text, JSON, trajectory table) └── models.py # Dataclasses: ValidationResult, StepResult, etc. tests/ -├── domains/ # Test PDDL files (classical + numeric) -└── ... +├── conftest.py # Shared PDDL fixtures (inline strings) +└── test_*.py # Module-level tests ``` ## Specification -`VALIDATOR_SPEC.md` is the authoritative reference. It defines the 3-phase pipeline, output formats, data models, and diagnostic templates. Read it before making design decisions. +This file (`CLAUDE.md`) is the authoritative reference for architecture, pipeline phases, and conventions. Data-model shapes are defined in `pyval/models.py`; diagnostic templates live in `pyval/diagnostics.py` and `pyval/report_formatter.py`. JSON output schema is whatever `ValidationResult.to_json()` produces. Read these before making design decisions. ## Architecture @@ -46,8 +46,8 @@ tests/ ### Output Modes -- **Plain text** (default) — VAL-like verbose output, optimized for LLM consumption. -- **Structured JSON** — Machine-readable, see spec for schema. +- **Plain text** (default) — VAL-like verbose output, optimized for LLM consumption. When no plan is provided (syntax-only validation), the report omits the `Plan is VALID/INVALID` verdict and the `Goal Check` block, and emits a single success line instead (since no plan was executed). +- **Structured JSON** — Machine-readable; schema is whatever `ValidationResult.to_json()` (in `pyval/models.py`) emits. - **State trajectory** — Numeric fluent values at each plan step. ## Domain Knowledge for AI Agents @@ -147,7 +147,7 @@ VAL is the C++ reference validator. PyVAL aims for output-format similarity (not - Pure Python, zero compiled dependencies. Must stay `pip install`-able. - Use `unified-planning` for all PDDL parsing and simulation — do not write custom parsers. - Test against both classical and numeric domains. -- Diagnostic messages follow templates in `VALIDATOR_SPEC.md` section "Diagnostic Message Guidelines". +- Diagnostic messages are generated in `pyval/diagnostics.py` and `pyval/report_formatter.py` — follow the existing templates there (precondition deficit reporting, repair-oriented messages, no leaking "Plan is VALID" when no plan was executed). - CLI interface mirrors VAL's `Validate` command for familiarity. - **Update `CHANGELOG.md`** after every implementation milestone (new module, feature, or fix). Keep entries under `[Unreleased]` until a version is tagged. diff --git a/README.md b/README.md index 3e6a735..5b7fbf9 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ pip install -e ".[dev]" pytest ``` -See `VALIDATOR_SPEC.md` for the full specification and `CLAUDE.md` for architecture notes. +See `CLAUDE.md` for architecture notes and the validation pipeline overview. ## License