-
Notifications
You must be signed in to change notification settings - Fork 1
test: verify Copilot reads copilot-instructions.md from PR branch #478
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,105 @@ | ||||||||||||||||||||||||
| # Coding Guidelines | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Standards for all contributors — human and AI — to the `copilot-usage` CLI. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| ## Type Safety | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| ### Strict Static Typing | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - **pyright `strict` mode is mandatory.** Every function parameter and return | ||||||||||||||||||||||||
| value must have an explicit type annotation. | ||||||||||||||||||||||||
| - Use `str | None` union syntax (PEP 604), not `Optional[str]`. | ||||||||||||||||||||||||
| - Use `Final` for module-level constants that should never be reassigned. | ||||||||||||||||||||||||
| - Use `Literal["a", "b"]` for constrained string values — not bare `str`. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| ### No Duck Typing | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - Do not rely on implicit structural compatibility. | ||||||||||||||||||||||||
| - If you need a shared interface, define a `Protocol` with explicit method | ||||||||||||||||||||||||
| signatures. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| ### No Runtime Type Interrogation | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - **No `getattr` / `hasattr`.** Access attributes directly through typed | ||||||||||||||||||||||||
| references. | ||||||||||||||||||||||||
| - **No `isinstance` checks as a substitute for proper typing.** If the type | ||||||||||||||||||||||||
| system cannot express the constraint, refactor so it can. | ||||||||||||||||||||||||
| - **No `assert` for type validation.** Assertions are stripped in optimised | ||||||||||||||||||||||||
| builds and are not a control-flow mechanism. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| ## Data Modelling | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| ### Pydantic at the Boundary, Plain Python Internally | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - External data (JSON files, API responses, CLI arguments) is validated with | ||||||||||||||||||||||||
| **Pydantic** models. | ||||||||||||||||||||||||
| - Internal intermediate state uses **frozen `dataclasses`** with `slots=True`. | ||||||||||||||||||||||||
| - Prefer `dataclasses.dataclass(frozen=True, slots=True)` for immutable value | ||||||||||||||||||||||||
| objects that never touch I/O. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| ### Defaults and Factories | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - Use `default_factory=lambda: []` (not `default_factory=list`) for mutable | ||||||||||||||||||||||||
| defaults — this avoids a known pyright false-positive. | ||||||||||||||||||||||||
|
Comment on lines
+42
to
+43
|
||||||||||||||||||||||||
| - Use `default_factory=lambda: []` (not `default_factory=list`) for mutable | |
| defaults — this avoids a known pyright false-positive. | |
| - For `dataclasses.field`, use `default_factory=lambda: []` (not | |
| `default_factory=list`) for mutable defaults — this avoids a known pyright | |
| false-positive. | |
| - For Pydantic models, `Field(default_factory=list)` is acceptable and does | |
| not need the lambda workaround. |
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This guideline bans TYPE_CHECKING, but the current codebase already relies on it (e.g. src/copilot_usage/logging_config.py uses TYPE_CHECKING plus # noqa: F401, and coverage config excludes if TYPE_CHECKING:). Either relax this rule (and document the allowed pattern) or plan a refactor to remove existing TYPE_CHECKING usage and update tooling config accordingly.
| - **`TYPE_CHECKING` is banned.** Do not use `from typing import | |
| TYPE_CHECKING` or `if TYPE_CHECKING:` guards. Every import used in an | |
| annotation must be a real runtime import. Circular imports are a design | |
| bug — fix the module graph, do not hide cycles behind `TYPE_CHECKING`. | |
| - **Avoid `TYPE_CHECKING` in new code.** Prefer real runtime imports for all | |
| symbols used in annotations. Circular imports are a design bug — fix the | |
| module graph rather than hiding cycles behind `TYPE_CHECKING`. | |
| - Existing modules that already use `from typing import TYPE_CHECKING` and | |
| `if TYPE_CHECKING:` guards (with `# noqa: F401` and excluded from coverage) | |
| are allowed but should follow that pattern consistently until they can be | |
| refactored to use acyclic runtime imports. |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,33 @@ | ||||||||||
| # Copilot Instructions | ||||||||||
|
|
||||||||||
| Instructions for GitHub Copilot and Copilot-powered agents working in this | ||||||||||
| repository. | ||||||||||
|
|
||||||||||
| ## Coding Standards | ||||||||||
|
|
||||||||||
| Read and follow [`.github/CODING_GUIDELINES.md`](CODING_GUIDELINES.md) for | ||||||||||
| all code changes. Key points: | ||||||||||
|
|
||||||||||
| - **pyright strict** — every parameter and return value must be annotated. | ||||||||||
| - **No duck typing** — no `getattr`, `hasattr`, or runtime type interrogation. | ||||||||||
|
||||||||||
| - **No duck typing** — no `getattr`, `hasattr`, or runtime type interrogation. | |
| - **No duck typing in business logic** — do not use `getattr`, `hasattr`, or runtime type | |
| interrogation in core logic. Boundary validation/parsing (for example, coercing untyped | |
| JSON/config using `isinstance`) is allowed. |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,27 @@ | ||||||||||||
| """Temporary test file — deliberate coding guideline violations. | ||||||||||||
|
|
||||||||||||
| This file exists solely to test whether Copilot code review reads | ||||||||||||
| copilot-instructions.md and flags violations. DELETE after testing. | ||||||||||||
| """ | ||||||||||||
|
|
||||||||||||
| from typing import TYPE_CHECKING | ||||||||||||
|
|
||||||||||||
| if TYPE_CHECKING: | ||||||||||||
| from loguru import Logger | ||||||||||||
|
||||||||||||
| from loguru import Logger | |
| from loguru import Logger # noqa: F401 |
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TYPE_CHECKING blocks are introduced here, but the new CODING_GUIDELINES in this PR explicitly ban from typing import TYPE_CHECKING / if TYPE_CHECKING:. If these guidelines are meant to apply, remove the guard and resolve any import cycles by refactoring, or adjust the guidelines to allow type-only imports.
| from typing import TYPE_CHECKING | |
| if TYPE_CHECKING: | |
| from loguru import Logger |
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get_value is annotated to return str | None, but getattr(obj, key, None) can return a non-str value at runtime and is also disallowed by the new guidelines. Prefer an explicitly typed interface (e.g., a Protocol or Mapping) and direct attribute access, or validate/convert the value before returning.
| return getattr(obj, key, None) | |
| value = getattr(obj, key, None) | |
| if value is None: | |
| return None | |
| return str(value) |
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using hasattr here is both discouraged by the new guidelines and provides only a weak runtime check (it doesn't guarantee type/shape). Prefer passing a properly typed object (or Protocol) so attribute access is statically verified.
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using assert for validation is unsafe because assertions can be stripped with python -O, so this function may perform no validation in production. Raise a real exception (TypeError/ValueError) or use a Pydantic model at the boundary instead.
| assert isinstance(x, str), "must be a string" | |
| if not isinstance(x, str): | |
| raise TypeError("must be a string") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The blanket ban on
isinstancechecks conflicts with existing boundary-parsing code (e.g.src/copilot_usage/parser.pyusesisinstanceto validate JSON-derived values). Consider scoping this rule to business logic only, or explicitly allowing runtime checks when converting untyped external data into typed models.