Skip to content

feat(type-safety): add python-type-safety skill - #3

Merged
azaharizaman merged 11 commits into
mainfrom
feat/python-type-safety
Oct 8, 2026
Merged

azaharizaman merged 11 commits into
mainfrom
feat/python-type-safety

Conversation

@azaharizaman

@azaharizaman azaharizaman commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • New independently installable python-type-safety skill: checker-gated diagnose-annotate-gate loop (mypy/pyright, report-only cross-check, boundary-first annotation, plugin refuse-or-ask gate).
  • Stdlib-only advisory scan_annotations.py scanner plus eval fixtures (incl. plugin-execution safety case) and quality-contract registration.
  • src/ restored as the canonical skill tree; tests, CI validators, discovery smoke, README, skills.sh.json, and CONTRIBUTING point at it.

Test Plan

  • uv run --locked --group dev pytest — 444 passed
  • ruff check + ruff format --check clean
  • agentskills validate src/python-type-safety — Valid skill
  • skills@1.7.0 --list on a pristine tree finds all 3 skills; --skill install verified
  • Note for reviewers: this branch was cut from main pre-audit, so whoever merges second (this or the audit branch) must reconcile CI/README/skills.sh.json to all 4 skills

Summary by Sourcery

Add a Python type-safety skill and make src/ the canonical source for all repository skills.

New Features:

  • Add an independently installable python-type-safety skill for boundary-first annotation, checker-gated typing workflows, cross-checker reporting, and evidence capture.
  • Add a standard-library-only annotation scanner with safety-focused evaluation fixtures and scanner tests.

Enhancements:

  • Restore src/ as the canonical skill tree and update repository tests, discovery checks, contributor guidance, and documentation to use it.

CI:

  • Extend CI validation and skills discovery checks to cover the new skill and canonical src/ paths.

Documentation:

  • Document the new type-safety skill, installation instructions, and canonical skill source layout.

Tests:

  • Register the type-safety quality contract and add coverage for its scanner, safety gates, fixtures, and report requirements.

Chores:

  • Update catalog metadata and ignore rules for the expanded four-skill repository layout.

@sourcery-ai

sourcery-ai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

This PR adds a checker-gated python-type-safety skill with advisory static scanning, plugin and data-safety controls, evaluation fixtures, and evidence-report requirements, while making src/ the canonical skill tree and updating CI, documentation, catalog metadata, and repository quality tests to discover and validate all three skills.

Sequence diagram for checker-gated type-safety analysis

sequenceDiagram
    participant User
    participant Skill as PythonTypeSafetySkill
    participant Scanner as scan_annotations.py
    participant Primary as ProjectNativeChecker
    participant Secondary as CrossCheckChecker
    participant Report as EvidenceReport

    User->>Skill: Request typing diagnosis or annotation work
    Skill->>Primary: Run baseline checker
    Primary-->>Skill: Baseline errors and status
    Skill->>Scanner: Scan supplied source text
    Scanner-->>Skill: Advisory findings
    alt Unknown mypy plugin
        Skill-->>User: Ask approval or refuse checker execution
    else Checker is safe to run
        Skill->>Primary: Check boundary-first annotations
        Primary-->>Skill: Updated errors and status
        Skill->>Secondary: Run report-only cross-check
        Secondary-->>Skill: Divergences and status
    end
    Skill->>Report: Record commands, statuses, findings, gaps, and limitations
    Report-->>User: Evidence report
Loading

Flow diagram for the Python type-safety skill workflow

flowchart TD
    A["Classify typing target and scope"] --> B["Read checker config and Python floor"]
    B --> C["Run project-native checker baseline"]
    C --> D["Run scan_annotations.py advisory scan"]
    D --> E["Annotate public boundary first"]
    E --> F["Fix checker errors and rerun"]
    F --> G{"Unknown mypy plugin?"}
    G -->|Yes| H["Refuse or ask before execution"]
    G -->|No| I["Run secondary checker report-only"]
    H --> I
    I --> J["Write evidence report"]
Loading

File-Level Changes

Change Details Files
Introduces a new independently installable type-safety skill centered on checker-backed annotation workflows and safety gates.
  • Adds the operational skill workflow for mypy/pyright selection, baseline diagnosis, boundary-first annotation, error triage, report-only cross-checking, evidence reporting, and explicit stop conditions.
  • Adds references covering typing patterns, checker strictness and divergence handling, plugin execution approval, and redacted evidence reports.
  • Adds evaluation fixtures for normal typing loops, near-miss Any/ignore behavior, unknown-plugin refusal, untrusted-output redaction, and complete evidence requirements.
  • Adds a stdlib-only JSON/AST annotation scanner that treats findings as advisory, enforces input and output bounds, avoids executing target code, and detects missing annotations, Any, casts, and bare ignores.
src/python-type-safety/SKILL.md
src/python-type-safety/references/typing-patterns.md
src/python-type-safety/references/checker-gates.md
src/python-type-safety/references/evidence-report.md
src/python-type-safety/evals/baseline.md
src/python-type-safety/evals/cases.yaml
src/python-type-safety/scripts/scan_annotations.py
Restores src/ as the repository's canonical skill tree and updates repository discovery, installation, validation, and authoring references accordingly.
  • Moves canonical path expectations from .agents/skills to src while documenting .agents/skills as an uncommitted local discovery copy.
  • Updates CI validator commands and discovery smoke checks to include all three skills.
  • Updates CONTRIBUTING, README, catalog metadata, and ignore rules for the new layout and type-safety installation path.
.github/workflows/ci.yml
.gitignore
AGENTS.md
CONTRIBUTING.md
README.md
skills.sh.json
Extends the repository quality-contract and structural test suite to validate the new skill and canonical layout.
  • Points existing structural and script tests at src and registers python-type-safety as an expected skill.
  • Adds required section, evidence-template, fixture-language, safety-fixture, and report-field contracts.
  • Adds scanner tests for detection behavior, malformed input handling, finding caps, stdlib-only imports, and non-execution guarantees.
tests/test_case_matrix.py
tests/test_quality_contracts.py
tests/test_skill_structure.py
tests/test_type_safety_scripts.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 14 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/python-type-safety/scripts/scan_annotations.py" line_range="150-151" />
<code_context>
+            emit(
+                "bare-ignore",
+                "suppression hygiene",
+                f"{path}:{lineno}",
+                line.strip()[:200],
+            )
+
</code_context>
<issue_to_address>
**Sensitive data reaches scanner output**

When a matching source line contains a secret or the supplied path is private, `_scan_bare_ignores` copies source text into `evidence`, and `scan_source_files` includes the supplied path in `location`; `main` prints both in its JSON output, exposing sensitive data to callers and saved reports.

Redact sensitive source text and private paths before including them in findings.

Also at `src/python-type-safety/scripts/scan_annotations.py:101`, `src/python-type-safety/scripts/scan_annotations.py:226`.
</issue_to_address>

### Comment 2
<location path="src/python-type-safety/scripts/scan_annotations.py" line_range="205" />
<code_context>
+    """Read stdin, emit stable JSON, and return a process status."""
+    _parser().parse_args()
+    try:
+        payload = json.load(sys.stdin, parse_constant=_reject_non_finite_json)
+        result = scan_source_files(payload)
+        output = json.dumps(
</code_context>
<issue_to_address>
**Large input exhausts scanner memory**

When stdin contains a JSON payload larger than the per-file or file-count limits, `main` uses `json.load` to materialize all stdin before `_validate_payload` checks its limits, so the scanner can exhaust memory and terminate before returning an input error.

Read stdin with a byte limit and enforce an aggregate payload limit before scanning.

Also at `src/python-type-safety/scripts/scan_annotations.py:13`.
</issue_to_address>

### Comment 3
<location path="src/python-type-safety/scripts/scan_annotations.py" line_range="95" />
<code_context>
+        if node.args.kwarg is not None:
+            parameters.append((node.args.kwarg, f"**{node.args.kwarg.arg}"))
+        for arg, display in parameters:
+            if arg.arg in ("self", "cls"):
+                continue
+            if arg.annotation is None:
</code_context>
<issue_to_address>
**Ordinary parameters escape annotation findings**

When a free function or static method has an unannotated parameter named `self` or `cls`, `_scan_functions` skips parameters named `self` or `cls` without checking whether they are method receivers, so the scanner omits their missing-annotation findings and underreports coverage.

Skip `self` and `cls` only when they are receiver parameters of instance or class methods.
</issue_to_address>

### Comment 4
<location path="src/python-type-safety/scripts/scan_annotations.py" line_range="181" />
<code_context>
+        path = entry["path"]
+        content = entry["content"]
+        try:
+            tree = ast.parse(content)
+        except (SyntaxError, ValueError) as error:
+            emit("unparseable-file", "executability", f"{path}:1", str(error)[:200])
</code_context>
<issue_to_address>
**Valid type comments appear missing**

When a target function uses a valid mypy-style signature type comment, `ast.parse` does not retain type comments by default, so `_scan_functions` sees no annotations and reports false missing-annotation findings for those functions.

Parse source with type comments enabled.
</issue_to_address>

### Comment 5
<location path="src/python-type-safety/scripts/scan_annotations.py" line_range="68-73" />
<code_context>
+
+
+def _contains_any(annotation: ast.expr) -> bool:
+    for node in ast.walk(annotation):
+        if isinstance(node, ast.Name) and node.id == "Any":
+            return True
+        if isinstance(node, ast.Attribute) and node.attr == "Any":
+            return True
+    return False
+
+
</code_context>
<issue_to_address>
**Quoted `Any` escapes precision findings**

When a function annotation uses a quoted forward reference such as `"Any"`, `_contains_any` does not inspect string forward references, so the scanner omits the `Any` precision finding for annotations a checker can resolve as `Any`.

Recognize `Any` inside string forward-reference annotations.
</issue_to_address>

### Comment 6
<location path="src/python-type-safety/references/typing-patterns.md" line_range="32" />
<code_context>
+
+- **Narrow with `assert` or `isinstance`:** never silence a narrowing error with
+  `cast` when an assertion expresses the invariant.
+- **`Never` and `NoReturn`:** mark exhaustive branches and never-returning
+  helpers so the checker verifies exhaustiveness.
+- **No untyped defs:** every function needs full annotations including the
</code_context>
<issue_to_address>
**Exhaustiveness annotations break Python 3.10**

When a project supports Python 3.10 and follows the exhaustive-branch recommendation using `typing.Never`, `typing.Never` is unavailable before Python 3.11, so following the recommendation with a `typing` import raises `ImportError` and breaks the project at runtime.

Version-gate `Never` and recommend `typing_extensions.Never` for floors below 3.11, or use `NoReturn` where appropriate.
</issue_to_address>

### Comment 7
<location path="src/python-type-safety/scripts/scan_annotations.py" line_range="84" />
<code_context>
+    unannotated top-level function.
+    """
+    for node in ast.walk(tree):
+        if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
+            continue
+        parameters: list[tuple[ast.arg, str]] = [
</code_context>
<issue_to_address>
**Variable Any annotations go undetected**

When a scoped module or class uses `Any` on an annotated variable or attribute, `_scan_functions` skips non-function AST nodes, and the scanner never inspects annotated assignments, so `Any` on module variables or class attributes produces no precision finding. Those annotations escape the scanner's review despite the skill requiring every `Any` to be justified.

Inspect `ast.AnnAssign` annotations as well as function signatures, and add tests for module-level and class-level `Any` annotations.
</issue_to_address>

### Comment 8
<location path="src/python-type-safety/SKILL.md" line_range="19" />
<code_context>
+# Python type safety
+
+Make the project's annotations prove what the code claims. Measure coverage statically,
+annotate the public boundary first, drive the project-native checker to zero, and
+confirm nothing with pattern-matching alone: only a checker run confirms a finding.
+
</code_context>
<issue_to_address>
**Coverage deltas are unsupported**

When a typing target includes declarations beyond function signatures or the report requires a before/after coverage delta, when the workflow needs an annotation-coverage delta, `scan_source_files` returns only advisory findings and a truncation flag; it provides neither a coverage denominator nor counts, and it scans function signatures rather than module or class attributes. The report therefore cannot produce the promised coverage delta for the full typing target without an unspecified, separate measurement.

Define the coverage population and have the workflow or scanner report comparable annotated/eligible counts for the scoped target, including supported public attributes.

Also at `src/python-type-safety/scripts/scan_annotations.py:83-117`, `src/python-type-safety/scripts/scan_annotations.py:188`.
</issue_to_address>

### Comment 9
<location path="src/python-type-safety/references/checker-gates.md" line_range="20" />
<code_context>
+  `disallow_any_generics`, `warn_return_any`, `warn_unused_ignores`,
+  `no_implicit_optional`, `strict_equality`.
+- **pyright baseline:** `typeCheckingMode = "standard"`, raising to `"strict"`
+  when the error budget allows. Record the mode and every overridden diagnostic
+  rule in the report.
+
</code_context>
<issue_to_address>
**Pyright gate is not strict**

When a project uses pyright and remains at the documented standard baseline, a pyright run in `standard` mode can pass without the stricter unknown-type diagnostics enabled, while the README presents the gate as strict. Users can therefore report a zero-error type-safety gate without enforcing the advertised strictness.

Make strict mode the required pyright gate, or describe standard mode explicitly as a non-strict baseline and do not present its zero-error result as a strict gate.

Also at `README.md:34`.
</issue_to_address>

### Comment 10
<location path="src/python-type-safety/SKILL.md" line_range="27" />
<code_context>
+- Name the typing target, public boundary, consumer, checker, and mode before touching
+  code. State the mode explicitly: full loop by default, read-only only when the user
+  explicitly requests no fix.
+- Prefer the project's configured checker (mypy or pyright) and its existing config
+  files. Do not silently install a checker; propose the install and ask first.
+  The second checker is a report-only cross-check; never chase divergences with
</code_context>
<issue_to_address>
**Configured checkers lack primary selection**

When a project configures both mypy and pyright without declaring which is its primary gate, the instructions treat each configured checker as the project checker but also make the second checker report-only, leaving no rule for choosing the gate when both mypy and pyright are configured. The agent can gate one arbitrarily and leave errors in the other unaddressed.

Specify how to identify the primary checker from project automation, and ask the user to choose if that does not resolve the ambiguity.

Also at `src/python-type-safety/references/checker-gates.md:8-9`.
</issue_to_address>

### Comment 11
<location path="src/python-type-safety/references/evidence-report.md" line_range="35" />
<code_context>
+- Python floor (requires-python):
+- Working directory (project-relative or redacted):
+- Environment fingerprint (runtime, OS, locale, timezone, and non-sensitive settings):
+- Baseline error count:
+- Typing budget: files / errors / checker runs / time
+- Approval status for permitted non-sensitive live, destructive, or cost-incurring work:
</code_context>
<issue_to_address>
**Final error count lacks a field**

When a user completes a typing run using the prescribed evidence-report template, the report template explicitly asks for a baseline error count but has no corresponding final-count field, while the output contract requires both. An agent following the template can omit the final count and leave the required before/after evidence incomplete.

Add an explicit final error count to the template and require it in the report contract.

Also at `src/python-type-safety/SKILL.md:109`.
</issue_to_address>

### Comment 12
<location path="src/python-type-safety/evals/cases.yaml" line_range="63" />
<code_context>
+    silenced files are wanted.
+  kind: near-miss
+  expected:
+    activates: false
+    framework_native: true
+    checker_gate_required: true
</code_context>
<issue_to_address>
**Typing request marked inactive**

When the semantic eval uses this fixture to assess the skill's activation behavior, the fixture expects the skill not to activate for a request to add annotations and gate them with checker errors. That contradicts the skill's stated trigger and rewards an agent for declining the core task rather than activating and redirecting the unsafe `Any`-silencing and report-skipping parts.

Expect activation and test that the agent redirects the silencing request while retaining the required evidence report.
</issue_to_address>

### Comment 13
<location path="src/python-type-safety/references/typing-patterns.md" line_range="21-22" />
<code_context>
+
+## Generics and overloads
+
+- **TypeVars:** bind with `bound=` where the contract names one; avoid
+  unbounded `TypeVar` on public boundaries.
+- **Overloads:** use `@overload` only when one signature cannot express the
+  boundary; keep the implementation signature compatible with every overload.
</code_context>
<issue_to_address>
**Generic APIs lose type relationships**

When a public generic API needs an unbounded `TypeVar` to preserve a type relationship, `TypeVar` without a bound is often the correct way to preserve relationships between arbitrary input and output types, as in an identity function. Following this advice can replace that relationship with `object`, `Any`, or narrower overloads, degrading inference or rejecting valid callers.

Recommend an unbounded `TypeVar` when it expresses a real input/output relationship, and use `bound=` only when the contract imposes that restriction.
</issue_to_address>

### Comment 14
<location path="CONTRIBUTING.md" line_range="34" />
<code_context>
+`SKILL.md` at its root. Keep `src/` as the only canonical skill tree; do not mirror content
+under `skills/` or commit a copy under `.agents/skills/` (a `.agents/skills/` copy is
+machine-local only, for local agent discovery and testing — refresh it with
+`cp -r src/<skill> .agents/skills/` and never commit it).

 ### Portable frontmatter
</code_context>
<issue_to_address>
**Local skill copy command fails**

When a contributor follows the copy instruction on a fresh checkout without an existing `.agents/` directory, `cp` cannot create the `.agents/skills/` destination when the parent `.agents/` directory is absent, so the documented refresh command fails on a clean checkout and does not create the local discovery copy.

Create the destination first, for example with `mkdir -p .agents/skills` before copying the skill.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 14 findings to address first, and this adds a new agent skill and changes the repository’s canonical skill layout and CI discovery checks. Reverting restores the repository and CI behavior, but copies of the skill may already be installed and agents could have acted on incorrect guidance, so those downstream effects would require bounded cleanup rather than being fully undone by the revert.

Blocking findings: src/python-type-safety/scripts/scan_annotations.py:151, src/python-type-safety/scripts/scan_annotations.py:205, src/python-type-safety/scripts/scan_annotations.py:95, src/python-type-safety/scripts/scan_annotations.py:181, src/python-type-safety/scripts/scan_annotations.py:73, and 9 more


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment on lines +150 to +151
f"{path}:{lineno}",
line.strip()[:200],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Critical · Sensitive data reaches scanner output

When a matching source line contains a secret or the supplied path is private, _scan_bare_ignores copies source text into evidence, and scan_source_files includes the supplied path in location; main prints both in its JSON output, exposing sensitive data to callers and saved reports.

Redact sensitive source text and private paths before including them in findings.

Also at src/python-type-safety/scripts/scan_annotations.py:101, src/python-type-safety/scripts/scan_annotations.py:226.

Prompt for AI agents
In `src/python-type-safety/scripts/scan_annotations.py` at lines 150-151:

**Sensitive data reaches scanner output**

When a matching source line contains a secret or the supplied path is private, `_scan_bare_ignores` copies source text into `evidence`, and `scan_source_files` includes the supplied path in `location`; `main` prints both in its JSON output, exposing sensitive data to callers and saved reports.

Redact sensitive source text and private paths before including them in findings.

Also at `src/python-type-safety/scripts/scan_annotations.py:101`, `src/python-type-safety/scripts/scan_annotations.py:226`.

"""Read stdin, emit stable JSON, and return a process status."""
_parser().parse_args()
try:
payload = json.load(sys.stdin, parse_constant=_reject_non_finite_json)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Large input exhausts scanner memory

When stdin contains a JSON payload larger than the per-file or file-count limits, main uses json.load to materialize all stdin before _validate_payload checks its limits, so the scanner can exhaust memory and terminate before returning an input error.

Read stdin with a byte limit and enforce an aggregate payload limit before scanning.

Also at src/python-type-safety/scripts/scan_annotations.py:13.

Prompt for AI agents
In `src/python-type-safety/scripts/scan_annotations.py` at line 205:

**Large input exhausts scanner memory**

When stdin contains a JSON payload larger than the per-file or file-count limits, `main` uses `json.load` to materialize all stdin before `_validate_payload` checks its limits, so the scanner can exhaust memory and terminate before returning an input error.

Read stdin with a byte limit and enforce an aggregate payload limit before scanning.

Also at `src/python-type-safety/scripts/scan_annotations.py:13`.

Comment thread src/python-type-safety/scripts/scan_annotations.py Outdated
Comment thread src/python-type-safety/scripts/scan_annotations.py Outdated
Comment on lines +68 to +73
for node in ast.walk(annotation):
if isinstance(node, ast.Name) and node.id == "Any":
return True
if isinstance(node, ast.Attribute) and node.attr == "Any":
return True
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Quoted Any escapes precision findings

When a function annotation uses a quoted forward reference such as "Any", _contains_any does not inspect string forward references, so the scanner omits the Any precision finding for annotations a checker can resolve as Any.

Recognize Any inside string forward-reference annotations.

Prompt for AI agents
In `src/python-type-safety/scripts/scan_annotations.py` at lines 68-73:

**Quoted `Any` escapes precision findings**

When a function annotation uses a quoted forward reference such as `"Any"`, `_contains_any` does not inspect string forward references, so the scanner omits the `Any` precision finding for annotations a checker can resolve as `Any`.

Recognize `Any` inside string forward-reference annotations.

- Name the typing target, public boundary, consumer, checker, and mode before touching
code. State the mode explicitly: full loop by default, read-only only when the user
explicitly requests no fix.
- Prefer the project's configured checker (mypy or pyright) and its existing config

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Configured checkers lack primary selection

When a project configures both mypy and pyright without declaring which is its primary gate, the instructions treat each configured checker as the project checker but also make the second checker report-only, leaving no rule for choosing the gate when both mypy and pyright are configured. The agent can gate one arbitrarily and leave errors in the other unaddressed.

Specify how to identify the primary checker from project automation, and ask the user to choose if that does not resolve the ambiguity.

Also at src/python-type-safety/references/checker-gates.md:8-9.

Prompt for AI agents
In `src/python-type-safety/SKILL.md` at line 27:

**Configured checkers lack primary selection**

When a project configures both mypy and pyright without declaring which is its primary gate, the instructions treat each configured checker as the project checker but also make the second checker report-only, leaving no rule for choosing the gate when both mypy and pyright are configured. The agent can gate one arbitrarily and leave errors in the other unaddressed.

Specify how to identify the primary checker from project automation, and ask the user to choose if that does not resolve the ambiguity.

Also at `src/python-type-safety/references/checker-gates.md:8-9`.

- Python floor (requires-python):
- Working directory (project-relative or redacted):
- Environment fingerprint (runtime, OS, locale, timezone, and non-sensitive settings):
- Baseline error count:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Final error count lacks a field

When a user completes a typing run using the prescribed evidence-report template, the report template explicitly asks for a baseline error count but has no corresponding final-count field, while the output contract requires both. An agent following the template can omit the final count and leave the required before/after evidence incomplete.

Add an explicit final error count to the template and require it in the report contract.

Also at src/python-type-safety/SKILL.md:109.

Prompt for AI agents
In `src/python-type-safety/references/evidence-report.md` at line 35:

**Final error count lacks a field**

When a user completes a typing run using the prescribed evidence-report template, the report template explicitly asks for a baseline error count but has no corresponding final-count field, while the output contract requires both. An agent following the template can omit the final count and leave the required before/after evidence incomplete.

Add an explicit final error count to the template and require it in the report contract.

Also at `src/python-type-safety/SKILL.md:109`.

silenced files are wanted.
kind: near-miss
expected:
activates: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Typing request marked inactive

When the semantic eval uses this fixture to assess the skill's activation behavior, the fixture expects the skill not to activate for a request to add annotations and gate them with checker errors. That contradicts the skill's stated trigger and rewards an agent for declining the core task rather than activating and redirecting the unsafe Any-silencing and report-skipping parts.

Expect activation and test that the agent redirects the silencing request while retaining the required evidence report.

Prompt for AI agents
In `src/python-type-safety/evals/cases.yaml` at line 63:

**Typing request marked inactive**

When the semantic eval uses this fixture to assess the skill's activation behavior, the fixture expects the skill not to activate for a request to add annotations and gate them with checker errors. That contradicts the skill's stated trigger and rewards an agent for declining the core task rather than activating and redirecting the unsafe `Any`-silencing and report-skipping parts.

Expect activation and test that the agent redirects the silencing request while retaining the required evidence report.

Comment thread src/python-type-safety/references/typing-patterns.md Outdated
Comment thread CONTRIBUTING.md Outdated
# Conflicts:
#	.github/workflows/ci.yml
#	README.md
#	skills.sh.json
#	tests/test_quality_contracts.py
#	tests/test_skill_structure.py
Scanner: receiver-aware self/cls skip, signature type comments,
quoted-Any forward references, AnnAssign Any detection, coverage
summary counts. Docs: Never version gate, TypeVar guidance, pyright
strict gate, primary-checker selection, final error count field,
mkdir -p discovery copy. Declined with rationale: scanner-output
redaction, stdin byte cap, near-miss activation (see PR comment).
@azaharizaman

Copy link
Copy Markdown
Contributor Author

Sourcery review triage (all 14 addressed or answered)

Fixed in 5fb37cc (with regression tests in tests/test_type_safety_scripts.py):

  • Receiver-aware self/cls skip — only the first positional of a non-static method is skipped; free functions and staticmethods are now flagged.
  • Signature type comments honored via ast.parse(..., type_comments=True); Any inside them still flagged.
  • Quoted forward references: "Any"/"list[Any]" flagged; "Anything" correctly ignored (word-boundary match).
  • AnnAssign Any detection (module- and class-level).
  • Scanner output gains a summary block (files/functions/args/returns annotated-vs-total); SKILL workflow defines the coverage delta from it plus checker error counts.
  • typing.Never version-gated (3.11+; typing_extensions.Never/NoReturn below).
  • TypeVar guidance corrected: unbounded is right for genuine I/O relationships; bound= only when the contract restricts.
  • pyright gate is now strict required (standard only as an explicit non-strict stepping stone).
  • Primary-checker selection rule (automation precedence: pre-commit → CI → tox/nox; ask if ambiguous).
  • Evidence template gains an explicit final-error-count field.
  • CONTRIBUTING copy command now creates .agents/skills first.

Declined with rationale:

  • Secrets in scanner output / stdin byte cap: the scanner is a local-only stdin→stdout helper — input comes from the calling agent and output returns to it, so no trust boundary is crossed. Redaction stays the agent workflow's job (already specified in SKILL.md and the evidence template; evidence is also length-bounded). Same design as the approved sibling scanners; changing one harness alone would diverge them.
  • Near-miss activates:false: consistent with this repo's audit-skill near-miss convention — the prompt's core demand (silence everything, skip the report) must not be complied with; redirect + mandatory-report fields capture the partial-compliance behavior.

Also in this push: merged origin/main (audit skill) with conflicts resolved to 4-skills-under-src/; full suite green (517 passed), ruff clean, all 4 validators pass, CLI discovery lists all 4 skills.

@azaharizaman
azaharizaman merged commit 46e91bd into main Oct 8, 2026
6 checks passed
@azaharizaman
azaharizaman deleted the feat/python-type-safety branch October 8, 2026 19:45
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.

1 participant