Skip to content

spec validate: files-incomplete treats a dotted directory name as a file (test-runner directory args warn on complete plans) #192

Description

@buddhamangler

Follow-up to #190 — thanks for the fast turnaround on that one; the check catches the shape reported there.

Summary

pilot spec validate's files-incomplete rule treats a directory whose name contains a dot as a file. A Verify: command that hands a test directory to the runner — dotnet test tests/Widgets.Tests, pytest tests/widgets.unit — produces a warning naming that directory, and the warning does not clear when the plan lists every test file the task actually touches. Since Step 10.0 now gates on warnings ("treat each warning the same way unless it is provably wrong for this plan"), every task in a plan for a .NET-style layout (tests/<Project>.Tests/ is the conventional project directory name) carries one warning the author can only silence by listing a directory under Test: — which is not a review-scope entry and is exactly the kind of thing the rule says not to do.

The rule does not consult the filesystem (the directory exists on disk in the repro below), so the classification is purely lexical: a dot in the last path segment reads as an extension.

Reproduction

Pilot Shell v11.0.4.

mkdir -p repro/src/widgets repro/tests/widgets.unit && cd repro && git init -q
touch src/widgets/parser.py tests/widgets.unit/test_parser.py
cat > plan.md <<'EOF'
# Widget parser Implementation Plan

Created: 2026-09-17
Status: PENDING
Approved: No
Iterations: 0
Worktree: No
Type: Feature

## Summary

**Goal:** Reject malformed widget headers.

## Progress Tracking

- [ ] Task 1: Parser rejects malformed headers

## Implementation Tasks

### Task 1: Parser rejects malformed headers

**Objective:** Make the parser raise on a header without a version field.

**Files:**

- Modify: `src/widgets/parser.py`
- Test: `tests/widgets.unit/test_parser.py`

**Key Decisions / Notes:**

- None.

**Definition of Done:**

- [ ] `parse("widget:")` raises `HeaderError`
- [ ] Verify: `uv run pytest tests/widgets.unit -q`
EOF
pilot spec validate plan.md --json | jq '.warnings'

Output:

[
  {
    "rule": "files-incomplete",
    "message": "Task 1 names `tests/widgets.unit`, which no task's `Files:` block lists. Those blocks are the review scope, so the change lands out of scope or unreviewed -- list the path under Create/Modify/Delete/Rename/Test, or cite it as a `file:line` reference if the task only reads it.",
    "line": 20
  }
]

The test file the task touches is listed under Test:; the warning is for the directory that contains it.

Varying only the directory name in the Verify: line:

Verify: argument files-incomplete
tests/widgets no
tests/widgets/ no
tests/widgets-unit no
tests/widgets.unit yes
tests/Widgets.Tests yes

So the discriminator is the dot in the final segment, not whether the path is a directory.

Suggestion

Either of these would clear it without loosening the check:

  1. When the plan is validated inside a repository, resolve the path and skip it if it is a directory on disk (a directory is never a reviewable diff target on its own).
  2. Failing that, treat a path that is a strict prefix of a path already listed in some Files: block as covered — tests/widgets.unit is a prefix of the listed tests/widgets.unit/test_parser.py, which is the case a test-runner directory argument always produces.

(2) is purely lexical and consistent with the current implementation; (1) is more precise. Either way the file:line escape hatch does not fit here — the directory is neither read nor written, it is the runner's argument.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions