Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .claude/rules/agent-rules-layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
description: Agent rules directory layout and symlink conventions
alwaysApply: false
globs: .claude/rules/**,.cursor/rules/**
paths:
- ".claude/rules/**/*.md"
- ".cursor/rules/**/*.mdc"
---

# Agent rules layout

This repo shares agent rules between **Claude Code** and **Cursor** via symlinks.

## Canonical source

- **Edit rules here:** `.claude/rules/*.md`
- **Do not** put rule content directly in `.cursor/rules/` — those files are symlinks.

## Cursor bridge

Cursor requires `.mdc` files in `.cursor/rules/`. Each `.mdc` is a symlink to the matching `.md`:

```text
.claude/rules/my-rule.md ← canonical (commit this)
.cursor/rules/my-rule.mdc ← symlink → ../.claude/rules/my-rule.md
```

## Dual frontmatter

Every rule file needs frontmatter for both tools:

```yaml
---
description: Short summary for Cursor rule picker
alwaysApply: false
globs: path/to/match/**
paths:
- "path/to/match/**"
---
```

- **Cursor** uses `globs`, `alwaysApply`, and `description`.
- **Claude Code** uses `paths`; omit `paths` for always-on rules.
- Each tool ignores the other's keys.

## Adding a rule

1. Create `.claude/rules/<name>.md` with dual frontmatter and content.
2. Add the Cursor symlink:

```bash
ln -s ../.claude/rules/<name>.md .cursor/rules/<name>.mdc
```

3. List the new rule in the **Agent rules** section of `AGENTS.md`.

## Removing a rule

1. Delete `.claude/rules/<name>.md`.
2. Delete `.cursor/rules/<name>.mdc` (the symlink, not a separate file).
3. Remove its entry from `AGENTS.md`.

## Do not

- Symlink the entire `.cursor/rules` directory to `.claude/rules` (extension mismatch: `.mdc` vs `.md`).
- Duplicate rule content in both directories.
- Commit real files under `.cursor/rules/` — only symlinks belong there.
46 changes: 46 additions & 0 deletions .claude/rules/python-quality.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
description: Python code quality conventions for src/ and tests/
alwaysApply: false
globs: src/**/*.py,tests/**/*.py
paths:
- "src/**/*.py"
- "tests/**/*.py"
---

# Python code quality

## Structured data

- Use **Pydantic `BaseModel`** for all structured data — API responses, settings, internal records, result types, and domain wrappers. Do not use `@dataclass`.
- Prefer typed models over plain `dict` for structured data.
- Reuse project base models where they exist (e.g. `InspectApiBaseModel` for API shapes; inspect-internal bases for in-memory models).

## Exceptions

- Raise **specific, descriptive exceptions** rather than bare `Exception`.
- Follow domain error hierarchies: a base exception class plus typed subclasses with context attributes (see `src/videoipath_automation_tool/apps/inspect/errors.py`).

## Context managers

- Use `with` for files, locks, test spies, and connector wrappers.

## Quality gates

Before finishing Python changes, run:

```bash
poetry run ruff check --fix src/ tests/
poetry run ruff format src/ tests/
```

Pre-commit hooks mirror these commands (see `.pre-commit-config.yaml`).

## Generated and sensitive code

- Do **not** hand-edit `src/videoipath_automation_tool/apps/inventory/model/drivers.py` — regenerate with `set-videoipath-version <version>`.
- All committed data must follow the anonymization rules in `AGENTS.md`.

## Change scope

- Keep diffs minimal and focused.
- Match surrounding patterns: mixins, module docstrings, `TYPE_CHECKING` imports, and existing naming in the file you edit.
55 changes: 55 additions & 0 deletions .claude/rules/python-style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
description: Python coding style for src/ and tests/
alwaysApply: false
globs: src/**/*.py,tests/**/*.py
paths:
- "src/**/*.py"
- "tests/**/*.py"
---

# Python coding style

## Type hints

- Annotate all function parameters and return types.
- Use `from __future__ import annotations` in new modules.
- Use `TYPE_CHECKING` blocks for imports needed only for type hints.

## Formatting and naming

- Follow PEP 8 with **snake_case** for functions, variables, and modules.
- Max line length is **120** characters (ruff formatter default in this project — not Black's 88).
- Format with **ruff**, not Black:

```bash
poetry run ruff format src/ tests/
```

## Strings and paths

- Use **f-strings** for string formatting; avoid `%` formatting and `.format()`.
- Use **`pathlib.Path`** over `os.path` for filesystem operations.

## Comprehensions and readability

- Prefer list/dict/set comprehensions over explicit loops when the result stays readable.
- Do not sacrifice clarity for brevity.

## Layout and whitespace

- Group **logically related lines** together (e.g. setup, core logic, cleanup).
- Separate groups with a **single blank line**; use an extra blank line between larger sections when it aids scanning.
- Do not sprinkle blank lines randomly, and do not leave long unbroken blocks when a visual break would help.
- Within a function, keep the main path easy to follow: inputs and validation first, then the core work, then return/cleanup.

## Public before private

- Place **public** API first so readers see the most relevant surface when scrolling: public classes, methods, functions, and module-level constants.
- Place **private** members after public ones: names prefixed with `_` (attributes, methods, functions, nested helpers) and internal implementation details.
- In classes: public methods first, then `_`-prefixed helpers and internal state accessors.
- In modules: public exports and user-facing functions first; private helpers and module-internal constants at the bottom.
- A short section comment (e.g. `# --- Internal ---`) is fine when a class or module has a large private block.

## Resources

- Use **context managers** (`with`) for files, locks, and other resources that need cleanup.
81 changes: 81 additions & 0 deletions .claude/rules/python-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
description: Python testing conventions for src/ and tests/
alwaysApply: false
globs: src/**/*.py,tests/**/*.py
paths:
- "src/**/*.py"
- "tests/**/*.py"
---

# Python testing

## Framework and commands

- Use **pytest** for all tests.
- Prefer the dedicated entry points over bare `pytest`:

```bash
poetry run test-unit # offline/unit suite (CI default)
poetry run test-e2e # live-server e2e (loads .env)
poetry run test # unit then e2e sequentially

# Single file or test (extra args pass through to test-unit / test-e2e)
poetry run test-unit tests/validators/test_device_id.py
poetry run test-e2e tests/e2e/inspect/test_e2e_inspect.py::test_name
```

- `poetry run pytest` also runs unit tests only (e2e excluded via `addopts` in `pyproject.toml`).

### VS Code

Use the launch configs in `.vscode/launch.json`:

- **Unit Tests** / **Unit Tests (current file)** — offline suite
- **E2E Tests** / **E2E Tests (current file)** — live-server suite

Or run **Tests** from `.vscode/tasks.json` (`poetry run test`).

## Unit vs e2e separation

| Layer | Unit | E2E |
|-------|------|-----|
| Location | `tests/` except `tests/e2e/` | `tests/e2e/` only |
| Marker | unmarked | `@pytest.mark.e2e` |
| Env | `tests/conftest.py` (dummy values) | `.env` (copy from `.env.template`) |
| Run command | `test-unit` / `pytest` | `test-e2e` |
| CI | yes | no |

Default `addopts` run coverage on `src/` and exclude e2e (`-m "not e2e"`).

## Assertions

- Use `pytest.raises(SpecificError)` with the exact exception type.
- Do not catch or assert against bare `Exception` when a domain error exists.

## Unit and offline tests

- Mock external I/O with fake connectors and lightweight stand-ins (see `tests/inspect/test_actions.py`).
- Dummy `VIPAT_*` env vars are set in `tests/conftest.py` (autouse fixture; skipped for e2e).
- Load JSON fixtures from `tests/<app>/fixtures/<version>/` using `pathlib.Path`.
- Put shared fixtures in `conftest.py` at the appropriate directory level.
- Use session-scoped fixtures only when setup is expensive and reuse is intentional.

## E2E tests

- Live-server tests live under `tests/e2e/` only.
- Mark with `@pytest.mark.e2e`; they are excluded from the default suite.
- E2e entry points (`poetry run test-e2e`, `poetry run test`, VS Code **E2E Tests**) load `.env` and enable the suite automatically. E2e runs use `--no-cov`.
- Copy `.env.template` to `.env` (gitignored), set connection vars. The e2e conftest loads `.env` automatically when present.
- Run with `poetry run test-e2e` (no extra env vars on the command line).
- Namespace all writes with the `E2E-` label prefix and `vipat-e2e` tag.
- Do not add e2e tests to the default CI/offline run.

## Test data

- All fixture and test data must follow anonymization rules in `AGENTS.md`.
- Preserve structure and relationships; replace real hostnames, IPs, and customer identifiers with generic placeholders.

## Coverage

- Default runs report coverage on `src/`.
- Do not disable coverage flags without a clear reason.
1 change: 1 addition & 0 deletions .cursor/rules/agent-rules-layout.mdc
1 change: 1 addition & 0 deletions .cursor/rules/python-quality.mdc
1 change: 1 addition & 0 deletions .cursor/rules/python-style.mdc
1 change: 1 addition & 0 deletions .cursor/rules/python-testing.mdc
11 changes: 0 additions & 11 deletions .env.example

This file was deleted.

13 changes: 13 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copy to .env (gitignored) for local development and live-server e2e tests.

VIPAT_ENVIRONMENT=DEV
VIPAT_VIDEOIPATH_SERVER_ADDRESS=vip-server.example
VIPAT_VIDEOIPATH_USERNAME=test-user
VIPAT_VIDEOIPATH_PASSWORD=test-password
VIPAT_USE_HTTPS=true
VIPAT_VERIFY_SSL_CERT=false
VIPAT_LOG_LEVEL=INFO
VIPAT_ADVANCED_DRIVER_SCHEMA_CHECK=true
VIPAT_TIMEOUT_HTTP_GET=10
VIPAT_TIMEOUT_HTTP_PATCH=10
VIPAT_TIMEOUT_HTTP_POST=10
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
branches:
- main
tags:
- 'v*' # Trigger on version tags like v1.2.3
- "v*" # Trigger on version tags like v1.2.3
pull_request_target:
branches:
- main
Expand All @@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13", "3.14"]
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -37,7 +37,7 @@ jobs:
run: poetry install --with dev,test

- name: Run tests
run: poetry run pytest
run: poetry run test-unit

# 2️⃣ Release Job (Uses Prebuilt Package)
release:
Expand All @@ -52,7 +52,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.13"
python-version: "3.14"

- name: Install Poetry
run: pip install poetry
Expand Down
Loading
Loading