Skip to content

fix(powershell): bind positional scan paths to -Paths - #316

Merged
PhysShell merged 1 commit into
mainfrom
claude/complex-project-tasks-viyycs
Jul 28, 2026
Merged

fix(powershell): bind positional scan paths to -Paths#316
PhysShell merged 1 commit into
mainfrom
claude/complex-project-tasks-viyycs

Conversation

@PhysShell

Copy link
Copy Markdown
Owner

Что и зачем

Обёртка сканировала не то дерево, о котором её просили. Документированный вызов из её же .EXAMPLE:

own-check.ps1 -Format msbuild -- src\MyApp

-- завершает разбор параметров в PowerShell, дальше аргументы связываются позиционно, а $Root был объявлен первым — поэтому src\MyApp становился корнем чекаута Own.NET, а сканирование уходило в .. Голая позиционная форма делала то же самое. Корректно связывал только явный -Paths.

Отказ тихий, и это главное. Просишь каталог A — анализируется каталог B, и если в B чисто, пользователь получает зелёный результат без единой ошибки. Это та же ложь, которую арки A1 и A4 выжигали два среза подряд: «не посмотрел» под видом «посмотрел и ничего не нашёл». Здесь, правда, инструмент посмотрел — просто не туда, куда его отправили.

Как починено

Контракт объявлен, а не унаследован из порядка объявления:

[CmdletBinding(PositionalBinding = $false)]
param(
    ...
    [Parameter(Position = 0, ValueFromRemainingArguments = $true)]
    [string[]]$Paths
)
Root   → только именованный -Root
Paths  → явный -Paths или позиция 0

Перестановка параметров дала бы тот же результат сегодня и оставила бы смысл висеть на той же неявной механике, которая уже дважды устроила представление.

Проверено на настоящем скрипте (пробник собран из его же блока параметров, обрезанного перед первой стадией):

Вызов Было Стало
-Format github -- /t/a Root=/t/a Paths=[/t/a]
-Format github /t/a Root=/t/a Paths=[/t/a]
-Format github -Paths /t/a Paths=[/t/a] без изменений
-Root /checkout -Format github -Paths /t/a корректно без изменений
-Format github Paths=[.] без изменений
-Format github -- /t/a /t/b Root=/t/a Paths=[/t/a|/t/b]

Граница: pwsh -File

При запуске через pwsh -File script.ps1 ... -- <path> литерал -- перехватывается хостом ещё до связывания параметров и отвергается как неоднозначное имя. Это поведение PowerShell для -File, а не дефект скрипта и не повод раздувать срез. Поддерживаемые формы для такого запуска:

pwsh -File own-check.ps1 -Format github <target>
pwsh -File own-check.ps1 -Format github -Paths <target>

В интерактивной сессии и при & own-check.ps1 работают все три формы, включая --.

Строка, отбрасывающая литерал -- из $Paths, сохранена: в сессии PowerShell съедает токен сам, но при сплаттинге (& own-check.ps1 @args) он реально доходит как значение. Комментарий приведён в соответствие.

Что добавлено в CI

Сквозной тест именно тихой подмены цели, а не наличия какой-то находки: стоя в чистом рабочем каталоге, просим цель с гарантированным OWN001 и требуем, чтобы находка вернулась, а файл из cwd не появился в выводе вовсе. Плюс:

  • формы -- <target> и -Paths <target> обязаны дать идентичный вывод — эквивалентность публичных форм, а не «обе что-то сказали»;
  • два позиционных пути обязаны дойти оба ([string[]] заявлен как тип, доказательство на одной строке его не устанавливает);
  • без цели по-прежнему сканируется ..

Заодно исправлен устаревший комментарий над джобой: обёртка исполняется на Linux достаточно далеко, чтобы доказать ярус отказа первой стадии, — Windows требуется её успешному extraction path.

Тип изменения

  • feat — новая возможность
  • fix — исправление бага
  • docs — документация
  • refactor / chore / test / ci — без изменения поведения

Как проверено

  • Локально с PowerShell 7.4.6 против настоящего скрипта — вся матрица связывания выше, включая множественные пути и дефолт .

  • Ярус отказа первой стадии после правки держится в обеих формах: LASTEXITCODE = 2

  • Фикстуры проверены на верность вердиктов до пуша: целевой файл действительно даёт OWN001, файл рабочего каталога действительно чист

  • Тело каждого шага проверено парсером PowerShell

  • CI ветки зелёный на bfcab5e — 19 джоб, ни одной не-успешной; Windows-джоба прошла всеми шестью шагами с первой попытки:

    OK: every documented form scans the requested target; the two public forms agree
    OK: stage-1 failure landed in the hard-error tier (2)
    OK: clean tree exits 0
    OK: findings -> 1 with the flag, 0 without, identical output
    
  • python tests/run_tests.py / ruff — не применимо: Python и продуктовый код не менялись

Связанные issue

Closes #315. Refs #313, #314 (где дефект был найден исполнением обёртки).

Чеклист

  • изменение покрыто тестом/селфтестом (или объяснено, почему нет)
  • README/docs обновлены при необходимости — комментарий над джобой приведён в соответствие с фактами
  • коммиты в conventional-commit стиле (feat:, fix:, docs: …)

Generated by Claude Code

The wrapper's own documented invocation scanned the wrong tree. With

    own-check.ps1 -Format msbuild -- src\MyApp

`--` ends PowerShell's parameter parsing, what follows binds POSITIONALLY,
and $Root was declared first — so src\MyApp became the Own.NET checkout root
and the scan fell through to ".". Bare positional paths did the same. Only an
explicit -Paths reached $Paths.

The failure mode is what makes this more than a papercut: it is SILENT. Ask
for directory A, get directory B analysed, and if B is clean you are handed a
green result and no error. That is the same lie A1 and A4 spent two arcs
removing — "did not look" wearing the face of "looked and found nothing" —
except here the tool did look, just not where it was told to.

Fixed by declaring the contract instead of inheriting it from declaration
order: PositionalBinding = $false turns off automatic positional binding for
every parameter, and $Paths claims Position = 0 explicitly. Reordering the
param block would have worked too, and would have left the meaning resting on
the same implicit machinery that already produced this twice.

  Root  -> named -Root only
  Paths -> explicit -Paths, or position 0

Verified against the real script, every documented form:

  -Format github -- /t/a                     -> Paths=[/t/a]    (was Root)
  -Format github /t/a                        -> Paths=[/t/a]    (was Root)
  -Format github -Paths /t/a                 -> Paths=[/t/a]
  -Root /checkout -Format github -Paths /t/a -> Root=[/checkout] Paths=[/t/a]
  -Format github                             -> Paths=[.]
  -Format github -- /t/a /t/b                -> Paths=[/t/a|/t/b]

CI gains the end-to-end half, which is the part that actually catches a
wrong-tree scan: standing in a CLEAN working directory, ask for a target that
contains a guaranteed OWN001, and require the finding to come back and the
cwd's own file never to appear. The `--` and -Paths forms must produce
IDENTICAL output — equivalence of the public forms, not merely "both said
something" — and two positional paths must both arrive, since [string[]] is
the declared type and a one-element proof would not establish it.

The stale claim above the job is corrected while here: the PowerShell wrapper
does execute on Linux far enough to prove its stage-1 failure tier; it is the
successful extraction path that needs Windows.

Closes #315.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015de4MezSeUnZBoWq1fFU5M
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@PhysShell, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 18 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1ccc94a0-c5f1-43a3-ac3f-cbc9f55fac6c

📥 Commits

Reviewing files that changed from the base of the PR and between 0e5f61e and bfcab5e.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml
  • scripts/own-check.ps1
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/complex-project-tasks-viyycs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@PhysShell
PhysShell merged commit 0ded835 into main Jul 28, 2026
47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PowerShell wrapper: documented positional paths bind to -Root and silently scan "."

2 participants