Skip to content
Merged
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
3 changes: 2 additions & 1 deletion .cursor/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
We setup Cursor context rules to help give useful information to agents. For more info see:
https://docs.cursor.com/en/context/rules

https://cursor.com/docs/context/rules
95 changes: 95 additions & 0 deletions .cursor/rules/coding.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
description: Core coding standards - explicit naming and fail-fast error handling
alwaysApply: true
---

# Coding Standards

## Use Explicit Variable Names

Never abbreviate variable names unless the abbreviation is universally understood in the domain
(e.g. `i` for a loop index, `e` in a catch block, `url`, `id`, `config`).
Shortened names reduce readability and make code harder to search and refactor.
It is ok to shorten or abbreviate names to avoid shadowing other variables.

```python
# BAD - unclear abbreviation
for nt in node_types:
et = edge_type_map.get(nt)

# GOOD - explicit and readable
for node_type in node_types:
edge_type = edge_type_map[node_type]
```

When in doubt, spell it out.

## Fail Fast on Invalid State

Surface errors immediately when invariants are violated.
Do not silently swallow errors, return defaults, or skip logic when the situation is genuinely unexpected.

### Dict Access

Use `dict[key]` (bracket access) when the key **must** exist.
Only use `dict.get(key, default)` when the absence of a key is a valid, expected case with a meaningful default.

```python
# BAD - silently returns None when the key is missing, hiding bugs downstream
value = config.get("required_setting")

# GOOD - raises KeyError immediately, surfacing the real problem
value = config["required_setting"]

# OK - .get() is correct when absence is an expected, handled case
value = optional_overrides.get("timeout", DEFAULT_TIMEOUT)
```

### Validate Preconditions Early

Check invariants at function entry. Prefer raising an explicit exception over silently continuing
with bad data.

```python
# BAD - silently skips work on invalid input
def process_batch(batch):
if batch:
...

# GOOD - fails immediately with a clear message
def process_batch(batch):
if not batch:
raise ValueError("process_batch received an empty batch")
...
```

## Verify Features Before Submitting

Once a feature is code complete, verify it works by running the type checker and relevant tests.
Do not skip these steps or suppress errors with workarounds like `# type: ignore`.

### Type Checking

Run the type checker to ensure all type annotations are correct:

```bash
make type_check
```

If there are type errors, fix them properly. Do **not** add `# type: ignore` comments to silence them.

### Unit Tests

Run the unit tests for the files affected by your change:

```bash
make unit_test_py PY_TEST_FILES="path/to/test_file.py"
```

### Integration Tests

If the change touches cross-component behavior, also run integration tests:

```bash
make integration_test PY_TEST_FILES="path/to/test_file.py"
```
16 changes: 15 additions & 1 deletion .cursor/rules/python-patterns.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ class TestArgs:
Example with >>> for doctests, Args, Returns, and Raises if applicable. Docstrings should be clear, consistent,
testable, and Sphinx-compatible.

Example:

```python
from typing import Final, Optional
Expand Down Expand Up @@ -82,6 +81,21 @@ def add_to_magic_number(
return MAGIC_NUMBER + number_1 + sum(numbers)
```

#### Empty-Initialized Containers Must Be Typed

Always annotate empty containers at the point of initialization. Without an annotation the type checker (and the reader)
cannot infer the element type.

```python
# BAD - type is ambiguous
names = []
counts = {}

# GOOD - element type is explicit
names: list[str] = []
counts: dict[str, int] = {}
```

### Error Handling & Logging

- Use the GiGL logger: `from gigl.common.logger import Logger`
Expand Down