# `applies.mjs` — applies-гейт правила `python`

## Огляд

Модуль `applies.mjs` реалізує **applies-гейт** для правила `python` (`python.mdc`) у системі правил `n-cursor`. Його єдина роль — відповісти на питання: «чи варто застосовувати правило `python.mdc` до поточного репозиторію?».

Критерій застосовності — наявність файлу `pyproject.toml` у корені репозиторію. Якщо файл відсутній, CLI `n-cursor` пропускає всі concerns цього правила (як JS-перевірки, так і policy-перевірки Rego). Це усуває хибне спрацювання rego-правила `python/package_json`, яке вимагало б наявність npm-скрипта `scripts.lint-python` навіть у репозиторіях, що не пов'язані з Python.

Сам JS-файл не виконує жодних інших FS-перевірок: усі подальші перевірки (`uv.lock`, `package.json`, `lint-python.yml`, заборона Poetry тощо) розміщені окремо у `tooling.mjs`. Окрім гейта `applies()`, файл містить тонкий стаб `check()`, який друкує context-pass через стандартний reporter.

## Експорти / API

Модуль експортує дві named-функції (ESM):

| Експорт   | Тип / сигнатура                      | Призначення                                                           |
| --------- | ------------------------------------ | --------------------------------------------------------------------- |
| `applies` | `(cwd?: string) => Promise<boolean>` | Гейт застосовності правила: `true`, якщо в корені є `pyproject.toml`. |
| `check`   | `() => number`                       | Друкує context-pass через `check-reporter`, повертає exit-code (`0`). |

Default-export відсутній.

## Функції

### `applies(cwd = process.cwd())`

Гейт застосовності правила `python.mdc` для конкретного репозиторію.

- **Сигнатура:** `export function applies(cwd?: string): Promise<boolean>`
- **Параметри:**
  - `cwd` _(string, опційний)_ — абсолютний (або відносний) шлях до кореня репозиторію, у якому виконується перевірка. За замовчуванням — результат `process.cwd()`. У звичайному прогоні CLI це робочий каталог, з якого запущено команду.
- **Повертає:** `Promise<boolean>`
  - `true` — у `cwd` існує файл `pyproject.toml`, правило `python.mdc` слід застосовувати (CLI запустить решту concerns).
  - `false` — `pyproject.toml` відсутній, правило треба повністю пропустити.
- **Алгоритм:**
  1. Будує абсолютний шлях `join(cwd, 'pyproject.toml')`.
  2. Викликає синхронну `existsSync()` з `node:fs`.
  3. Загортає синхронний boolean у `Promise.resolve(...)`, щоб відповідати очікуваному CLI-контракту `Promise<boolean>`.
- **Side effects:** немає — лише читання метаданих файлової системи (`stat`-like виклик). Файл не відкривається й не читається; запис не виконується; глобальний стан не змінюється.
- **Помилки:** функція не очікує винятків; `existsSync` сам по собі не кидає для відсутніх шляхів — повертає `false`. Промісована обгортка зберігає таку ж поведінку.

### `check()`

Тонкий стаб, що друкує єдиний context-pass і завершується успіхом. Фактичні порушення (на кшталт відсутності `lint-python`-job у CI, заборони Poetry тощо) перевіряють інші concerns правила, тож тут жодних реальних перевірок немає.

- **Сигнатура:** `export function check(): number`
- **Параметри:** немає.
- **Повертає:** `number` — exit-code, отриманий з `reporter.getExitCode()`. Оскільки викликається лише `reporter.pass(...)`, код завжди `0` (немає фейлів).
- **Алгоритм:**
  1. Створює інстанс reporter-а через `createCheckReporter()`.
  2. Викликає `reporter.pass('pyproject.toml знайдено в корені — застосовую python.mdc')`, що друкує позитивний рядок у стандартному форматі n-cursor reporter-а.
  3. Повертає `reporter.getExitCode()`.
- **Side effects:** пише в `stdout` (через reporter). Не виконує FS-операцій, не змінює стану процесу, не кидає винятків.
- **Передумова:** функцію має сенс викликати лише після того, як `applies()` повернула `true` (інакше повідомлення про «знайдено» було б оманливим). CLI `n-cursor` забезпечує цей контракт.

## Залежності

### Зовнішні (стандартна бібліотека Node.js)

- `node:fs`
  - `existsSync(path)` — синхронна перевірка існування шляху. Використовується для виявлення `pyproject.toml`.
- `node:path`
  - `join(...segments)` — нормалізована побудова шляху, незалежна від OS-роздільника.

### Внутрішні (репозиторій)

- `../../../scripts/lib/check-reporter.mjs`
  - `createCheckReporter()` — фабрика стандартного reporter-а для `check.mjs`-скриптів правил. Очікувані методи: `pass(message)` (друк позитивного рядка) та `getExitCode()` (повертає накопичений exit-код, `0` для успіху).
  - Відносний шлях вказує на спільну бібліотеку скриптів правил у тому ж `npm/`-пакеті (`npm/scripts/lib/check-reporter.mjs` відносно файлу `npm/rules/python/js/applies.mjs`).

Інших залежностей (зокрема — TOML-парсера, інших правил, `tooling.mjs`) у цьому файлі немає.

## Потік виконання / Використання

### Місце в архітектурі правил

У системі правил `n-cursor` кожне правило (наприклад, `python.mdc`) описується трьома шарами:

1. **`*.mdc`** — людинозрозумілий опис правила, без алгоритму перевірки.
2. **`js/applies.mjs`** — гейт застосовності правила до конкретного репозиторію (поточний файл).
3. **`js/<concern>.mjs`** і Rego-policy — фактичні перевірки (`tooling.mjs`, `package_json.rego` тощо).

CLI `n-cursor` під час прогону правил викликає `applies()` для кожного правила перед тим, як запускати решту concerns. Якщо `applies()` повертає `false`, **усі** перевірки правила (JS і policy) пропускаються. Це й розв'язує проблему хибних спрацювань rego-правила `python/package_json` у не-Python репозиторіях.

### Послідовність викликів (happy path)

1. Користувач запускає `n-cursor check` (або еквівалент) у корені репо.
2. CLI обходить набір правил, серед яких `python`.
3. CLI імпортує `npm/rules/python/js/applies.mjs` і викликає `applies(process.cwd())`.
4. Якщо `pyproject.toml` присутній — CLI продовжує: запускає `check()` (друк context-pass) та інші concerns правила (`tooling.mjs`, Rego-policy).
5. Якщо `pyproject.toml` відсутній — CLI повністю пропускає `python` для цього прогону. `check()` у такому разі не викликається.

### Приклад прямого використання (без CLI)

```js
import { applies, check } from './applies.mjs'

if (await applies(process.cwd())) {
  const code = check()
  process.exitCode = code
}
```

### Інваріанти й контракти

- `applies(cwd)` ніколи не модифікує файлову систему.
- `applies(cwd)` повертає Promise навіть попри синхронну реалізацію — для уніфікованого CLI-контракту з іншими applies-гейтами.
- `check()` не дублює перевірку `pyproject.toml`: вона вже відбулась у `applies()`. Якщо `check()` викликається без попереднього `applies()`, повідомлення «pyproject.toml знайдено в корені» може ввести в оману, але exit-code від цього не залежить.
- Файл свідомо не імпортує `tooling.mjs` чи інші concerns — кожен concern завантажується CLI окремо.
