# V026: Path-handling convention (portability)

## Overview

Dogfooding rule that checks reveal's own source for the two path-handling bug
classes fixed by BACK-493, so they can't silently creep back in:

1. `str(x.relative_to(...))` — emits backslash separators on Windows.
2. Hardcoded POSIX system-root literal comparisons (`'/tmp'`, `'/var'`, ...) —
   silently no-ops on Windows/macOS (different temp/home paths).

Only fires via `reveal reveal://. --check` (self-check of the dev checkout);
it does not run against user code.

## Rule Details

- **Code**: V026
- **Category**: validation
- **Severity**: HIGH — a silent cross-platform correctness bug, not a style
  nit; passes every test on Linux and only breaks on Windows/macOS.
- **File Patterns**: `reveal://` self-check only (`internal = True`)

## Examples

### ❌ Bad (Triggers V026)

```python
# Class A: str() wrapping a .relative_to() call
display_path = str(file.relative_to(project_root))

# Class B: hardcoded root literal comparison
if path == '/tmp':
    ...
```

### ✅ Good (Passes V026)

```python
from reveal.utils.path_utils import to_posix, is_unsafe_scan_root

display_path = to_posix(file.relative_to(project_root))

if is_unsafe_scan_root(path):
    ...
```

## Rationale

`str(Path)` and hand-maintained root-literal sets both work correctly on
Linux/CI but fail silently on Windows (`C:\...`) and macOS (`/tmp` →
`/private/tmp`, temp dir under `/var/folders/...`). BACK-493 found and fixed
every site of this kind that existed at the time; this rule is the durable
enforcement (BACK-494) so a new one doesn't ship unnoticed until the next
Windows/macOS CI run.

## See Also

- `reveal/utils/path_utils.py` — canonical `to_posix()` / `is_unsafe_scan_root()`
- `internal-docs/design/WINDOWS_PATH_HANDLING_2026-07-06.md` — full narrative
  of the bug classes and the fix

## Development

### Testing

```bash
pytest tests/test_v026_rule.py -v
```

### Implementation Notes

- Exempts `path_utils.py` itself (the canonical implementation) and its own
  source file (whose docstring/comments quote the violating patterns as
  examples, which would otherwise self-flag).
- Deliberately excludes the bare anchor `'/'` from the root-literal check —
  indistinguishable by regex from a URL/URI path root (e.g. an nginx
  `location == '/'` check); `is_unsafe_scan_root()` handles the platform
  anchor separately via `Path(path).anchor`.
- Regex-based, not AST-based — like the other V0xx self-check rules, this
  trades some precision (can't see through multi-line calls) for simplicity;
  false negatives are possible but false positives were verified at zero
  against reveal's own current source.
