Skip to content

feat(toolchain): add pyright to python ecosystem check-cmd - #859

Merged
kyle-sexton merged 6 commits into
mainfrom
feat/834-toolchain-pyright-python
Jul 21, 2026
Merged

feat(toolchain): add pyright to python ecosystem check-cmd#859
kyle-sexton merged 6 commits into
mainfrom
feat/834-toolchain-pyright-python

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

Summary

Adds pyright to the bundled toolchain plugin's Python ecosystem default check-cmd, closing the gap where CI gated pyright but the local /toolchain:check batch was ruff-only.

Fix

  • plugins/toolchain/reference/ecosystems/python.yaml: check-cmd now runs uv run ruff check . --no-fix && uv run ruff format . --check && uv run pyright. fix-cmd is unchanged (pyright has no fix mode). opt-in and install-hint updated to mention pyright.
  • plugins/toolchain/skills/check/context/python.md: documents pyright as part of check-cmd (not fix), and adds a gotcha for the default basic-mode noise on untyped projects absent a pyrightconfig.json/pyproject.toml [tool.pyright].
  • Rung-4 fallback only — a consumer's own .claude/ecosystems/python.yaml override still wins key-by-key and is unaffected.
  • Version bump: plugins/toolchain 0.5.10.6.0 (minor: new default check behavior), with a CHANGELOG entry.

Verification

Closes #834

Related

CI gates pyright but the local /toolchain:check batch was ruff-only,
so agents could pass locally and still fail CI on type errors.

Closes #834

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: 0f69ae76-07e0-4aae-9c4a-11688fc0e8ea
@kyle-sexton kyle-sexton added the automated Opened by automation. label Jul 21, 2026
@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 47s —— View job


PR Review — feat(toolchain): add pyright to python ecosystem check-cmd

  • Gather context and read CLAUDE.md
  • Fetch PR diff and read changed files
  • Review changed files
  • Post findings

Summary

This is a clean, well-scoped change that closes a real CI/local parity gap. The change is minimal and correct. No blocking issues. Two observations below, both minor.


What looks good

Core change is correct. Appending && uv run pyright to check-cmd is the right way to chain a read-only type checker. The fix-cmd is correctly left unchanged, and the inline comment # pyright has no fix mode is consistent with the bash.yaml precedent (# shellcheck has no fix mode). (python.yaml L8–9)

Rung-4 semantics preserved. The PR description correctly notes that consumer .claude/ecosystems/python.yaml overrides still win key-by-key; the bundled default is only a fallback. This is the correct mental model.

Version bump is right. 0.5.1 → 0.6.0 is appropriate — adding a new default tool to check-cmd is new behavior (minor bump in pre-1.0 semver), not a patch fix. Consistent with how 0.2.0 handled the /toolchain:build → check rename.

opt-in text is informative and on-pattern. The extended description about pyright's default basic-mode behavior follows the precedent set by bash.yaml's opt-in (which also describes tool activation conditions inline). (python.yaml L10)

python.md gotcha is genuinely useful. The note about reportMissingTypeStubs/reportUnknownX noise on untyped projects, and the guidance to use typeCheckingMode rather than suppressing ad hoc, is the right actionable advice. (python.md L41)

CHANGELOG is accurate and detailed. The entry correctly describes the rung-4 scope, that fix-cmd is unchanged, and the documentation addition — consistent with prior entries.


Observations

1. brew install pyright — worth a quick sanity check

brew install uv ruff pyright is listed in install-hint. ruff and uv are confirmed in Homebrew core; pyright has a formula too, but it's worth confirming it's current (pyright's Homebrew packaging has historically lagged). If the formula is stale or absent, macOS users relying on the hint would hit a silent gap. The pip install ruff pyright uv path is the safer fallback and is already first. (python.yaml L11)

No change required if the formula is confirmed — just worth a quick brew info pyright check before this ships.

2. winget install astral-sh.uv still covers only uv (pre-existing)

The winget entry doesn't add pyright (or ruff), so a Windows user who uses only winget would get uv but still need pip install pyright ruff separately. This is a pre-existing pattern — the old hint also omitted ruff from winget — so this PR doesn't regress anything. Worth noting for a follow-up (e.g., winget install astral-sh.uv Microsoft.Pyright), but not a blocker here. (python.yaml L11)


Verdict

Approve with minor follow-up. The functional change is correct and well-documented. The two observations are low-priority and don't block merge — the pip path covers all platforms, and the brew question is a quick lookup. No security, correctness, or repo-agnostic compliance issues.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 11s —— View job


Security Review — PR #859

  • Read PR diff
  • Review changed files
  • Analyze for security issues
  • Post findings

Scope

Four files changed: plugin.json (version bump), CHANGELOG.md, reference/ecosystems/python.yaml (adds uv run pyright to check-cmd), skills/check/context/python.md (documents the change). No GitHub Actions workflows touched.


Findings

No security issues found.

The surface area here is minimal and all additions are benign:

Static check-cmd string — no injection surface
The new check-cmd value ("uv run ruff check . --no-fix && uv run ruff format . --check && uv run pyright") is a static literal. It contains no ${variable} interpolation, no consumer-supplied values, and no shell metacharacter exposure. The && chaining is standard short-circuit logic with no injection vector.

Trusted dependency (pyright)
pyright is Microsoft's official Python type checker, published to PyPI under the pyright package by the microsoft organization. It is a widely-deployed, first-party tool with no known supply-chain risk. The addition mirrors how ruff and uv are already referenced.

Static analysis only — no code execution
uv run pyright performs static type analysis; it does not execute the project's Python files. A malicious pyrightconfig.json in a consumer project could cause pyright to analyze unexpected paths, but pyright produces no output other than diagnostics to stdout — no data exfiltration, no privilege escalation, no code execution path.

Plugin sandbox unaffected
No changes to path resolution, plugin variable use, or ${CLAUDE_PLUGIN_ROOT} / ${CLAUDE_PLUGIN_DATA} references. The Rung-4 fallback structure (consumer .claude/ecosystems/python.yaml overrides win key-by-key) is unchanged and correctly documented.

Version bump and changelog
Purely metadata. No security relevance.


@kyle-sexton
kyle-sexton marked this pull request as ready for review July 21, 2026 16:23
@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Post-green review classification (work lane):

Finding Source Classification Disposition
Everything substantive PR review + security review "Approve with minor follow-up" / "No security issues found" No action needed
brew install pyright formula staleness — worth a sanity check PR review Non-blocking observation, pip path already covers it as primary/safe fallback Left alone
winget entry doesn't cover pyright/ruff PR review Pre-existing pattern, not a regression from this PR Left alone

No DEFER-FORBIDDEN class applies. No inline review threads to resolve (PR-level comments only).

Flipping to ready, handing off to the babysit lane for ongoing watch.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 9s —— View job


Security Review — PR #859

  • Gather context and read CLAUDE.md
  • Fetch PR diff and read changed files
  • Analyze for security issues
  • Post findings

Scope

Four files changed: plugin.json (version bump 0.5.1 → 0.6.0), CHANGELOG.md (documentation), reference/ecosystems/python.yaml (adds uv run pyright to check-cmd), skills/check/context/python.md (documents the change and adds a gotcha). No GitHub Actions workflows touched.


Findings

No security issues found.

The surface area is minimal and all additions are benign. Each vector reviewed:

Command injection — none
The new check-cmd value is a static string literal with no ${variable} interpolation and no consumer-supplied values embedded in the command:

"uv run ruff check . --no-fix && uv run ruff format . --check && uv run pyright"

The && chaining is standard short-circuit shell logic. There is no injection surface. (python.yaml L8)

uv run script shadowing — not a regression
uv run pyright resolves pyright from the project's virtualenv. In principle, a malicious pyproject.toml could define a script entry point named pyright that executes arbitrary code. However, this exact attack surface already exists for uv run ruff and uv run pytest (both present before this PR). This change does not widen the existing trust boundary — it adds a third call following the same pre-existing pattern.

Supply chain — trusted dependency
pyright is Microsoft's official Python type checker, published to PyPI under the pyright package by the microsoft PyPI organization. It is a widely-deployed, first-party tool with no known supply-chain risk. The addition mirrors how ruff and uv are already referenced. (python.yaml L11)

Static analysis only — no code execution
uv run pyright performs static type analysis; it does not execute the project's Python source files. A consumer project's pyrightconfig.json could redirect analysis to unexpected paths, but pyright's output is diagnostics to stdout only — no data exfiltration, no privilege escalation, no code execution path.

Plugin sandbox — unaffected
No changes to path resolution, ${CLAUDE_PLUGIN_ROOT}, or ${CLAUDE_PLUGIN_DATA} references. The Rung-4 fallback structure (consumer .claude/ecosystems/python.yaml overrides win key-by-key) is unchanged and correctly documented.

Secrets / credentials — none
No tokens, secrets, or credentials appear in any changed file. plugin.json and CHANGELOG.md are purely metadata.


@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 38s —— View job


PR Review — feat(toolchain): add pyright to python ecosystem check-cmd

  • Gather context and read CLAUDE.md
  • Read changed files (diff, python.yaml, python.md, plugin.json, CHANGELOG.md, bash.yaml for pattern comparison)
  • Review for correctness, security, and guideline alignment
  • Post findings

Summary

Clean, well-scoped change that closes a real CI/local parity gap. All four files are correct. No blocking issues found.


What looks good

Core change is correct. Appending && uv run pyright to check-cmd with && short-circuit chaining is the right composition — the check aborts on ruff failure before paying pyright's startup cost. The fix-cmd is correctly left unchanged. (python.yaml L8–9)

# pyright has no fix mode comment follows the bash.yaml pattern. bash.yaml L8 already uses # shellcheck has no fix mode in the same position, so this is on-pattern, not ad hoc annotation. (bash.yaml L8)

python.md heading rename is accurate. "## Type check (when configured)" → "## Type check" is the right edit — pyright is now unconditionally part of check-cmd, not an optional gate, so removing "(when configured)" eliminates a false signal. The added lead-in ("Part of check-cmd (no fix mode…)") correctly mirrors the fix-cmd distinction. (python.md L31)

Gotcha is genuinely useful and actionable. The basic-mode noise warning points to typeCheckingMode in project config as the fix path, which is the right advice (not ad-hoc suppression). (python.md L41)

Version bump is correct. 0.5.1 → 0.6.0 is appropriate — new default behavior in a minor bump, consistent with how 0.2.0 handled the /toolchain:build → check rename. CHANGELOG entry is accurate and includes the rung-4 scope note, the unchanged fix-cmd, and the doc addition.

Rung-4 semantics preserved. The PR doesn't touch the resolution ladder; consumer .claude/ecosystems/python.yaml overrides still win key-by-key.


One observation (non-blocking)

uv run pyright vs uvx pyright for a standalone type checker. uv run pyright works when pyright is available in the project venv or on PATH (both covered by the install-hint). For projects where pyright is not a project dependency, uvx pyright is the more idiomatic uv invocation for a standalone tool — it fetches and runs without mutating the project env. That said, uv run falls back to PATH, so brew install pyright / pip install pyright users aren't broken. And changing to uvx would be a behavioral divergence from the uv run ruff pattern already in place. Worth knowing, not worth changing here.


Verdict

Approve. The functional change is correct, the documentation is accurate, and the codebase conventions are followed. No security, correctness, or repo-agnostic compliance issues. The prior review's two observations (brew formula staleness, winget gap) were correctly classified as non-blocking; nothing new to add.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e2ff2c5499

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/toolchain/reference/ecosystems/python.yaml
@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 39s —— View job


PR Review — feat(toolchain): add pyright to python ecosystem check-cmd

  • Gather context and read CLAUDE.md
  • Read changed files and diff
  • Assess the Codex/kyle-sexton finding on pyright-absent projects
  • Review for correctness, security, and guideline alignment
  • Post findings

Summary

This is a clean change that closes a real CI/local parity gap. The merge of origin/main into the branch (commit c444986) introduced a formatting-only change to plugin.json (keywords array multi-line) — no functional change there. The substantive PR content is commit e2ff2c5.


What looks good

Core change is correct. && uv run pyright appended to check-cmd with short-circuit && is the right composition — ruff failures abort early before paying pyright startup cost. fix-cmd correctly unchanged. (python.yaml L8–9)

Pattern is consistent with bash.yaml. bash.yaml L7 uses shellcheck -x -S warning <files> && shfmt -d <files> — two tools in one compound check-cmd, same && chaining, same fix-cmd comment convention. This PR follows that established pattern exactly. (bash.yaml L7–8)

python.md heading and lead-in are accurate. Removing "(when configured)" is right — pyright is now unconditional in check-cmd. The "Part of check-cmd (no fix mode…)" lead-in correctly documents the fix-cmd split. (python.md L29–35)

Gotcha is actionable. The basic-mode noise warning points to typeCheckingMode in project config, not ad-hoc suppression — right advice. (python.md L41)

Version bump is correct. 0.5.1 → 0.6.0 is appropriate for new default behavior. CHANGELOG entry is accurate, includes rung-4 scope note and unchanged fix-cmd.


On the Codex finding: pyright-absent ruff-only projects

Codex flagged that a project with ruff configured (so it activates the python ecosystem via rung-4 globs) but without pyright installed will now fail check-cmd where it previously passed. Kyle's classification in the thread was accurate — this is VALID, IMPORTANT, and non-blocking given the pre-existing design. Here's why:

The skip granularity is ecosystem-level, not per-tool. SKILL.md L110:

Tool presence: before each ecosystem runs, verify the tool is on PATH. If missing, report skip … — never report FAIL for a missing tool.

This is a single-tool check per ecosystem. With check-cmd being a compound of uv run ruff … && uv run pyright, the skill verifies uv (the primary tool), and if it's present, the full command runs. Pyright absence under uv reaches uv run pyright and exits non-zero — the skill produces a FAIL rather than a skip.

This is pre-existing behavior, not a regression unique to this PR. bash.yaml has the same shape: shellcheck … && shfmt …. A project with shellcheck on PATH but not shfmt would hit the same failure. The ecosystem-level skip was already insufficient for compound commands before this change.

What this PR does correctly: it updates install-hint to include pyright and documents the behavior in python.md. The intent is that pyright is a required part of the Python default toolchain going forward — the install-hint is the signal.

The concrete follow-up (not a blocker here, but the right next action) is a check-skill improvement to make tool-presence per-command rather than per-ecosystem — or at minimum, to parse the compound check-cmd and verify each tool. Fix this →


Verdict

Approve. Functional change is correct, follows established bash.yaml compound-command pattern, version bump is right, documentation is accurate. The Codex finding is real but correctly classified as a pre-existing skill-level design limitation — the right action is a follow-up issue for per-tool presence checking in SKILL.md, not a hold on this PR.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 36s —— View job


Security Review — PR #859

  • Fetch PR diff
  • Read changed files
  • Analyze for security issues
  • Post findings

Scope

Four files changed: plugin.json (version bump 0.5.1 → 0.6.0), CHANGELOG.md (documentation only), reference/ecosystems/python.yaml (adds uv run pyright to check-cmd, updates opt-in and install-hint), skills/check/context/python.md (documents the change, adds a basic-mode gotcha). No GitHub Actions workflows modified.


Findings

No new security issues found.

Each vector examined:

Command injection — none.
The new check-cmd value is a static string literal with no ${variable} interpolation and no consumer-supplied values embedded in the command:

"uv run ruff check . --no-fix && uv run ruff format . --check && uv run pyright"

The && chaining is standard short-circuit shell logic. There is no injection surface. (python.yaml L8)

uv run script shadowing — pre-existing, not widened.
uv run pyright resolves pyright from the project virtualenv first. A malicious pyproject.toml [project.scripts] entry named pyright could shadow the real binary and execute arbitrary code. However, this exact attack surface already exists for uv run ruff and uv run pytest, both present before this PR. This change does not widen the existing trust boundary.

Supply chain — trusted dependency.
pyright is Microsoft's official Python type checker, published to PyPI by the microsoft organization. The install-hint references pip install, brew install, and winget install — all standard package manager paths to the official package, mirroring the pre-existing pattern for ruff and uv. (python.yaml L11)

pyrightconfig.json path redirect — inherently low severity, pre-existing pattern.
A consumer project's pyrightconfig.json could use include to redirect analysis to paths outside the project directory. However, pyright performs static analysis only — it reads source files but does not execute them. Output is diagnostics to stdout; there is no data exfiltration path, no code execution, no privilege escalation. This is the same trust model that already applies to ruff reading .ruff.toml. Not a new concern.

Plugin sandbox — unaffected.
No changes to ${CLAUDE_PLUGIN_ROOT}, ${CLAUDE_PLUGIN_DATA}, or any path resolution logic. The Rung-4 fallback structure is unchanged.

GitHub Actions patterns — not applicable.
No workflow files modified.

Secrets / credentials — none.
No tokens, secrets, or credentials appear in any changed file.


Verdict: no security issues. Safe to merge from a security standpoint.

kyle-sexton added a commit that referenced this pull request Jul 21, 2026
Resolves conflicts in plugins/toolchain/.claude-plugin/plugin.json and
CHANGELOG.md against #860's fleet-wide 0.5.2 shell-declaration sweep —
kept the 0.7.0 lychee-offline entry stacked on top (one past sibling
PR #859's 0.6.0 claim on the same plugin).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: 0f69ae76-07e0-4aae-9c4a-11688fc0e8ea

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c444986bde

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/toolchain/skills/check/context/python.md Outdated
Pyright's default typeCheckingMode is `standard`, not `basic`, and the
reportMissingTypeStubs/reportUnknown* diagnostics default to `none` in
both basic and standard modes (per microsoft/pyright configuration.md).
Fix the false "basic mode" / reportUnknown-noise claim in the python
ecosystem opt-in text, the python.md gotcha, and the CHANGELOG entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 5s —— View job


PR Review — feat(toolchain): add pyright to python ecosystem check-cmd

  • Gather context and read CLAUDE.md
  • Review latest commit (bd3ff56) and changed files
  • Assess open Codex findings (P1 and P2)
  • Post findings

Latest commit — bd3ff56 (docs: correct pyright default mode to standard)

This commit directly addresses the Codex P2 finding and gets it right.

python.yaml L10 — fixed:

opt-in: "ruff config (...); pyright runs in its default standard mode absent a pyrightconfig.json or pyproject.toml [tool.pyright]"

(permalink)

python.md L41 — fixed:

pyright runs in its default standard mode absent a pyrightconfig.json or pyproject.toml [tool.pyright]
— on an untyped or partially-typed project this can surface genuine type errors; set typeCheckingMode
(e.g. basic or off) or add project config to tune the strictness rather than suppressing findings ad hoc

(permalink)

Both now correctly say standard. Pyright's default typeCheckingMode is standard per the official configuration reference, not basic. The Codex finding was accurate, the fix is correct, and the guidance to set typeCheckingMode to basic or off to reduce noise is actionable and points the right direction.

CHANGELOG L14 also updated to "standard-mode gotcha" — consistent with the correction.


Open finding status

Finding Status
P2 — Pyright default mode doc incorrect (basic vs standard) RESOLVED by bd3ff56
P1 — Pyright-absent project turns pass → fail Correctly deferred; pre-existing check-skill design limitation (ecosystem-level tool-presence check insufficient for compound check-cmd). Follow-up issue for per-tool presence checking is the right path.

One minor CHANGELOG note (non-blocking)

The CHANGELOG includes a [0.5.2] entry documenting the shell: bash additions merged in from main (7688ae7). That change was already published on main — it will never ship as a standalone 0.5.2 release; the plugin jumps directly 0.5.1 → 0.6.0. The entry correctly documents the change but could be mildly confusing to a consumer diffing releases. Not a blocker; just noting it exists.


Verdict

Approve. The P2 documentation error is resolved. P1 is correctly classified as a pre-existing design limitation and deferred. Everything else from prior review rounds stands — core change is correct, version bump is appropriate, documentation is accurate. No security or repo-agnostic compliance issues.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 37s —— View job


Security Review — PR #859 (commit bd3ff56)

  • Read PR diff for latest commit
  • Read changed files
  • Analyze for security issues
  • Post findings

Scope

The latest commit (bd3ff56) is a documentation correction across three files: CHANGELOG.md, reference/ecosystems/python.yaml (opt-in text only), and skills/check/context/python.md (gotcha text). It changes "basic mode" to "standard mode" — a purely editorial fix in static strings. No functional code changed.

The functional surface (adding uv run pyright to check-cmd) was introduced in e2ff2c5 and has been reviewed in two prior security passes in this thread. This review re-examines the full PR diff, including both commits.


Findings

No security issues found.

Each vector examined:

Command injection — none.
python.yaml L8 contains a static string literal:

"uv run ruff check . --no-fix && uv run ruff format . --check && uv run pyright"

No ${variable} interpolation, no consumer-supplied values embedded in the command, no injection surface. The && chaining is standard short-circuit shell logic.

uv run script shadowing — pre-existing, not widened.
uv run pyright resolves pyright from the project virtualenv first. A malicious pyproject.toml [project.scripts] entry named pyright could shadow the real binary. This attack surface already existed for uv run ruff and uv run pytest before this PR — this change follows the same pre-existing trust model and does not widen it.

Supply chain — trusted dependency.
pyright is Microsoft's official Python type checker, published to PyPI by the microsoft organization. The install-hint at python.yaml L11 references pip install, brew install, and winget install — all standard paths to the official package.

Static analysis only — no code execution.
uv run pyright performs static type analysis; it does not execute Python source files. Output is diagnostics to stdout only; no data exfiltration path, no privilege escalation, no code execution.

Plugin sandbox — unaffected.
No changes to ${CLAUDE_PLUGIN_ROOT}, ${CLAUDE_PLUGIN_DATA}, or any path resolution logic. The Rung-4 fallback structure is unchanged.

GitHub Actions — not applicable.
No workflow files modified.

Secrets / credentials — none.
No tokens, secrets, or credentials appear in any changed file. The latest commit touches only documentation strings.


Verdict: no security issues. Safe to merge from a security standpoint.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 2s —— View job


PR Review — feat(toolchain): add pyright to python ecosystem check-cmd

  • Gather context and read CLAUDE.md
  • Read PR diff and changed files
  • Review for correctness, security, and guideline alignment
  • Post findings

Summary

Clean, well-scoped change that closes a real CI/local parity gap. All prior review findings have been resolved or correctly deferred. One new blocking issue surfaced by this sync pass: a version gap introduced when the branch re-versioned after the dotnet PR (#890) merged ahead of it on main.


What looks good

Core change is correct. && uv run pyright appended to check-cmd with && short-circuit chaining is the right composition — ruff failures abort early before paying pyright startup cost. fix-cmd correctly left unchanged with the # pyright has no fix mode comment following the bash.yaml precedent. (python.yaml L8–9)

Mode documentation is now correct. bd3ff56 fixed the Codex P2 finding — both python.yaml opt-in and python.md gotcha now correctly say standard mode, not basic. The CHANGELOG entry was updated consistently. (python.yaml L10, python.md L41)

python.md heading and gotcha are accurate. Removing "(when configured)" from the heading is right — pyright is now unconditional in check-cmd. The gotcha is actionable: it points to typeCheckingMode rather than ad-hoc suppression.

Rung-4 semantics preserved. Consumer .claude/ecosystems/python.yaml overrides still win key-by-key; the bundled default is unaffected.


Blocking finding

Version gap: 0.6.00.8.00.7.0 is missing.

origin/main is at 0.6.0 (landed via the dotnet PR #890). This branch's plugin.json declares 0.8.0, and the CHANGELOG head entry is [0.8.0] for the pyright addition — with no [0.7.0] entry anywhere in the file.

CHANGELOG (this branch)          plugin.json (this branch)
─────────────────────────        ─────────────────────────
[0.8.0]  pyright added           "version": "0.8.0"   ← one version too high
[0.6.0]  dotnet opt-in (main)
[0.5.2]  shell: bash
...

The correct next semver after 0.6.0 is 0.7.0. The 0.8.0 bump skips a version with no corresponding entry, which means consumers upgrading from 0.6.0 will see a gap in the changelog and wonder what changed between 0.6.0 and 0.8.0 (answer: nothing, but they can't tell that). It also breaks any tooling that validates sequential minor bumps.

Fix this →


Deferred findings (status unchanged)

Finding Status
P2 — Pyright default mode doc (basic vs standard) Resolved in bd3ff56
P1 — Pyright-absent project turns ruff-only pass → fail Correctly deferred; pre-existing ecosystem-level tool-presence limitation in SKILL.md, not a regression from this diff
brew formula staleness Non-blocking; pip path is the primary/safe fallback
winget missing ruff/pyright Pre-existing pattern, not a regression

Verdict

Request changes. One blocking fix required: rename [0.8.0][0.7.0] in CHANGELOG.md and plugin.json. Everything else is correct.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 18s —— View job


Security Review — PR #859

  • Gather context and read CLAUDE.md
  • Fetch PR diff and read changed files
  • Analyze for security issues
  • Post findings

Scope

Four files changed: plugin.json (version bump), CHANGELOG.md (documentation), reference/ecosystems/python.yaml (adds uv run pyright to check-cmd, updates opt-in and install-hint), skills/check/context/python.md (documents the change, corrects default mode to standard, adds a gotcha). No GitHub Actions workflows modified.


Findings

No security issues found.

Each vector examined:

Command injection — none.
The new check-cmd value at python.yaml L8 is a static string literal:

"uv run ruff check . --no-fix && uv run ruff format . --check && uv run pyright"

No ${variable} interpolation, no consumer-supplied values embedded in the command, no injection surface. The && chaining is standard short-circuit shell logic with no injection vector.

uv run script shadowing — pre-existing, not widened.
uv run pyright resolves pyright from the project virtualenv first. A malicious pyproject.toml [project.scripts] entry named pyright could shadow the real binary and execute arbitrary code. However, this attack surface already exists for uv run ruff and uv run pytest, both present before this PR. This change follows the same pre-existing trust model and does not widen the existing trust boundary.

Supply chain — trusted dependency.
pyright is Microsoft's official Python type checker, published to PyPI by the microsoft organization. The install-hint at python.yaml L11 references pip install, brew install, and winget install — all standard package manager paths to the official package, mirroring the pre-existing pattern for ruff and uv.

Static analysis only — no code execution.
uv run pyright performs static type analysis; it does not execute Python source files. A consumer project's pyrightconfig.json could redirect analysis to paths outside the project directory via include, but pyright's output is diagnostics to stdout only — no data exfiltration, no privilege escalation, no code execution path.

Plugin sandbox — unaffected.
No changes to ${CLAUDE_PLUGIN_ROOT}, ${CLAUDE_PLUGIN_DATA}, or any path resolution logic. The Rung-4 fallback structure (consumer .claude/ecosystems/python.yaml overrides win key-by-key) is unchanged.

GitHub Actions — not applicable.
No workflow files modified.

Secrets / credentials — none.
No tokens, secrets, or credentials appear in any changed file. plugin.json and CHANGELOG.md are purely metadata.


Verdict: no security issues. Safe to merge from a security standpoint.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e6f1ab76f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/toolchain/reference/ecosystems/python.yaml
kyle-sexton added a commit that referenced this pull request Jul 21, 2026
Resolves conflicts in plugins/toolchain/.claude-plugin/plugin.json and
CHANGELOG.md against #890's real merge (dotnet opt-in gate, 0.5.2 ->
0.6.0) — re-derives the version to 0.9.0, one past sibling PR #859's
own re-rebased 0.8.0 claim on the same plugin (issue #834, pyright).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: 0f69ae76-07e0-4aae-9c4a-11688fc0e8ea
@kyle-sexton

Copy link
Copy Markdown
Contributor Author

This was generated by AI (decide lane — decision-brief pass; parked PR-thread decision surfaced via periodic scan).

Decision brief — how to gate pyright in the python default check-cmd

Re: parked review thread discussion_r3624070405 (author-deferred, IMPORTANT / medium-confidence, thread left unresolved). This surfaces the parked decision with a RECOMMENDED so it can be ruled on; it changes nothing.

Problem

Adding uv run pyright to the bundled python default check-cmd makes pyright the first tool in that command whose presence is not implied by the opt-in gate, which enrolls the ecosystem on ruff config alone. For a ruff-configured project with pyright uninstalled and no .claude/ecosystems/python.yaml override that hits the rung-4 fallback, the result is a behavior change either way — the compound && fails (green→red), or the per-ecosystem tool-presence skip drops ruff too (green→coverage-loss).

Today — how check-cmd selects/gates python tools

  • Default command (plugins/toolchain/reference/ecosystems/python.yaml:8): uv run ruff check . --no-fix && uv run ruff format . --check && uv run pyright.
  • Opt-in gate is one condition on ruff only (python.yaml:10): "ruff config (…); pyright runs in its default standard mode absent a pyrightconfig.json or pyproject.toml [tool.pyright]". Pyright is not part of the gate.
  • Tool-presence skip is per-ecosystem, keyed on the tool being on PATH (plugins/toolchain/skills/check/SKILL.md:16) — "never report FAIL for a missing tool." There is no per-tool skip inside a compound check-cmd.
  • The opt-in gate treats python as a single-condition ecosystem (SKILL.md:20, "e.g. dotnet, python") — met → run the whole check-cmd; unmet → skip the whole lint phase. The multi-tool, per-tool-condition case (SKILL.md:22, the bash shellcheck-always / shfmt-conditional precedent) explicitly does not get a partial skip: the compound string is opaque, so it runs whole. This PR moves python toward that multi-tool shape while SKILL.md:20 still classifies it as single-condition — that is the unresolved seam.

Options

A. Presence-gate pyright — run the pyright leg only when pyright is installed/configured. [RECOMMENDED]
Append && uv run pyright (or resolve it) only on detected presence — pyright on PATH, or a pyrightconfig.json / [tool.pyright] declaration; also add pyright config to the python globs (per sibling thread discussion_r3624999355). Tradeoff: this builds the per-tool resolution the skill does not yet have — bash already describes per-tool opt-in conditions but the skill runs its compound string whole (the atomicity limitation, SKILL.md:22), so this is new work in skills/check, not a one-line yaml edit and not a working precedent to copy. Reversibility: high — can be promoted to a hard default later once a setup/presence story exists.
Rationale: a default that flips a green ruff-only project to red is a portability regression, exactly the failure lane the plugin philosophy narrows against — a shipped default is legitimate only when it "cannot conflict in any repo the plugin drops into" (docs/PLUGIN-PHILOSOPHY.md:143), and "A detection-first or presence-gated use is compliant" (PLUGIN-PHILOSOPHY.md:156). Presence-gating is the lane-1 conformant shape.

B. Hard default with opt-out. Treat pyright as a hard prerequisite once ruff is opted in (matches this PR's documented intent; pyright is already in install-hint, python.yaml:11); ruff-only projects that don't want it override via .claude/ecosystems/python.yaml. Tradeoff: green→red for every ruff-only / pyright-absent project until they install pyright or override — imposes a convention the consumer can reasonably differ on, the lane-1 narrowness PLUGIN-PHILOSOPHY.md:143-146 warns against. Reversibility: high (revert the yaml leg), but the regression lands on consumers first.

C. Advisory, not failing. Run pyright but don't let its exit fail the ecosystem (report-only). Tradeoff: defeats the PR's own purpose — closing the gap where CI gates pyright but local /toolchain:check was ruff-only (PR summary); a non-failing local check drifts back from the CI gate it was meant to mirror. Reversibility: medium.

Cross-links

  • Plugin philosophy, defaults doctrine — lane 1 "Non-conflicting good-practice defaults" and "detection-first or presence-gated use is compliant": docs/PLUGIN-PHILOSOPHY.md:141-158.
  • Ecosystem-commands contract (resolution ladder, bundled portable defaults are rung-4 fallback): docs/conventions/ecosystem-commands/README.md.
  • Related open sub-threads on the same PR: pyright config globs (discussion_r3624999355); standard-mode docs correction (discussion_r3624293830, already fixed in bd3ff56).
  • Underlying seam worth a follow-up regardless of choice: per-ecosystem vs per-tool skip granularity for compound check-cmd (SKILL.md:16, :22).

main advanced to 0.6.0 (dotnet feature #890) after this branch had bumped
to 0.8.0, leaving a skipped 0.7.0 with no CHANGELOG entry. The pyright
addition is a single minor feature over 0.6.0, so 0.7.0 is the correct
next version. Retitle the CHANGELOG head entry and the plugin.json version
from 0.8.0 to 0.7.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 54s —— View job


PR Review — feat(toolchain): add pyright to python ecosystem check-cmd

  • Gather context and read CLAUDE.md
  • Read latest commit (2f0ef49) and changed files
  • Assess open decision brief (P1 gating question from kyle-sexton)
  • Review version fix (0.8.0 → 0.7.0)
  • Post findings

Latest commit — 2f0ef49 (fix: align version to 0.7.0)

This commit directly addresses the blocking finding from the prior review round and gets it right.

plugin.json — fixed:
"version": "0.8.0""version": "0.7.0" (permalink)

CHANGELOG.md — fixed:
## [0.8.0]## [0.7.0] (permalink)

The version sequence is now clean: [0.7.0] (pyright) → [0.6.0] (dotnet #890) → [0.5.2][0.5.1] → ... No skipped entries, no changelog gaps.


Open finding status

Finding Status
Blocking — version skipped 0.7.0 (was 0.8.0) RESOLVED by 2f0ef49
P2 — Pyright default mode doc incorrect (basic vs standard) RESOLVED by bd3ff56
P1 — Pyright-absent project turns ruff-only pass → fail Deferred — open decision brief pending kyle-sexton ruling

On the open decision brief (P1)

The decision brief (kyle-sexton, 2026-07-21T22:09) is well-constructed and surfaces the right tradeoff. The three options map cleanly onto the current skill design:

Why Option B (current PR state) is a defensible merge position:

The SKILL.md tool-presence check at L110 is ecosystem-level and keyed on a single binary (uv for the python ecosystem). Adding pyright to install-hint (python.yaml L11) is the mechanism the plugin uses to declare "this tool is a required part of this ecosystem's default." The behavior change (ruff-only pass → fail for pyright-absent projects) is the intended signal that pyright is now a first-class requirement of the Python default toolchain — the install-hint drives the remediation.

Why Option A is better long-term but requires new skill work:

SKILL.md L114–116 explicitly documents the "multi-tool check-cmd atomicity" gotcha: when a compound check-cmd bundles multiple tools, the opt-in gate cannot suppress just one sub-tool — "splitting a multi-tool check-cmd into separately gateable ecosystem keys would need a schema change." Option A's presence-gate for pyright is exactly that schema change. It is the right long-term solution but it's out of scope for this PR and requires a separate issue.

What Option C rules out:

The brief correctly notes Option C (advisory / non-failing) defeats the PR's own stated purpose of closing the CI/local parity gap. Not a viable option here.

My read: The PR's current state (Option B) is a valid, documented design choice with a clear install-hint remediation path. The PLUGIN-PHILOSOPHY.md "detection-first or presence-gated" guidance (cited in the brief) is a preference, not a hard prohibition — the tool presence signal (install-hint) is present, and the behavior change is intentional and documented. Option A follow-up via a separate issue for per-tool skip granularity in SKILL.md is the right path.


Verdict

Approve. The one blocking issue (version skipped 0.7.0) is resolved by 2f0ef49. All prior findings are either resolved or correctly classified. The core change is correct, the documentation is accurate, and the version sequence is clean.

The P1 decision brief is an open design question for kyle-sexton to rule on before merge — the current PR state (Option B) is a valid merge position, but if the author prefers presence-gating (Option A) that would require additional skill work and should be a separate PR or a pre-merge amendment. Nothing else blocks.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 20s —— View job


Security Review — PR #859 (commit 2f0ef49)

  • Gather context and read CLAUDE.md
  • Fetch PR diff and read changed files
  • Analyze for security issues
  • Post findings

Scope

The latest commit (2f0ef49) is a pure version correction in two files: plugin.json ("version": "0.8.0""0.7.0") and CHANGELOG.md (## [0.8.0]## [0.7.0]). No functional code changed. This review covers the full PR diff for completeness.

Four files changed across the PR:

File Nature
plugin.json Version bump only (0.5.1 → 0.7.0)
CHANGELOG.md Documentation only
reference/ecosystems/python.yaml Adds uv run pyright to check-cmd, updates opt-in and install-hint
skills/check/context/python.md Documentation: pyright section + standard-mode gotcha

No GitHub Actions workflows modified.


Findings

No security issues found.

Each vector examined:

Command injection — none.
The check-cmd value at python.yaml L8 is a static string literal:

uv run ruff check . --no-fix && uv run ruff format . --check && uv run pyright

No ${variable} interpolation, no consumer-supplied values embedded in the command, no injection surface.

uv run script shadowing — pre-existing, not widened.
uv run pyright resolves pyright from the project virtualenv before PATH. A malicious pyproject.toml [project.scripts] entry named pyright could theoretically shadow the real binary. However, this attack surface already exists for uv run ruff and uv run pytest, both present before this PR. This change does not widen the existing trust boundary.

Supply chain — trusted dependency.
pyright is Microsoft's official Python type checker, published to PyPI by the microsoft organization. The install-hint at python.yaml L11 references pip install, brew install, and winget install — all standard paths to the official package, mirroring the pre-existing pattern for ruff and uv.

Static analysis only — no code execution.
uv run pyright reads Python source files for type information; it does not execute them. Output is diagnostics to stdout only — no data exfiltration, no privilege escalation, no code execution path.

Plugin sandbox — unaffected.
No changes to ${CLAUDE_PLUGIN_ROOT}, ${CLAUDE_PLUGIN_DATA}, or any path resolution logic.

GitHub Actions — not applicable.
No workflow files modified.

Secrets / credentials — none.
No tokens, secrets, or credentials appear in any changed file. plugin.json and CHANGELOG.md are purely metadata.


Verdict: no security issues. Safe to merge from a security standpoint.

@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Automated babysit-lane classification (safe tier — reply only).

# Finding Classification Evidence Reacted
1 Version gap: plugin.json and CHANGELOG head declared 0.8.0, skipping 0.7.0 (no entry) while origin/main is at 0.6.0 VALID — fixed origin/main plugin.json = 0.6.0 (dotnet #890); branch declared 0.8.0 with CHANGELOG jumping [0.8.0] → [0.6.0], no [0.7.0]. Pyright is a single minor feature over 0.6.0, so 0.7.0 is the correct next version. 👍

Fixed in 2f0ef497216c1a62a48f58ac96187cdddeb45feb: plugin.json version and the CHANGELOG head entry both retitled 0.8.0 → 0.7.0. No other 0.8.0 references exist in the plugin (no footer link refs).

Deferred items from prior rounds are unchanged and remain author-owned: the P1 pyright-absent behavior-change (discussion_r3624070405, decision brief posted) and the P2 detection-globs scope (discussion_r3624999355) are design calls left for the author; the P2 default-mode doc error was already fixed in bd3ff56.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f0ef49721

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/toolchain/reference/ecosystems/python.yaml
@kyle-sexton
kyle-sexton merged commit 35ac4cc into main Jul 21, 2026
23 checks passed
@kyle-sexton
kyle-sexton deleted the feat/834-toolchain-pyright-python branch July 21, 2026 23:50
kyle-sexton added a commit that referenced this pull request Jul 22, 2026
#867)

## Summary

Adds `lychee-offline` to the `toolchain` plugin's bundled
`cross-cutting.yaml` ecosystem
default, alongside the existing `typos`/`gitleaks`/editorconfig-checker
tools — on-disk
link/anchor integrity checking with no network dependency.

## Fix

- `reference/ecosystems/cross-cutting.yaml`: `check-cmd` gains
`lychee --offline --no-progress './**/*.md'` (`--offline` = "Only check
local files and
block network requests" per lychee's own CLI help — external URLs are
skipped, only local
file and fragment targets are verified); `opt-in` and `install-hint`
documented following
the same per-tool-config pattern as the existing tools (optional
`lychee.toml` at repo
  root customizes the ruleset; absent means lychee's own defaults).
- `skills/setup/SKILL.md`: cross-cutting inference-candidate line
mentions `lychee-offline`,
  and all four cross-cutting tool names backtick-wrapped consistently.
- `skills/lint/evals/evals.json`: eval 3's `expected_output` updated to
include
  `lychee-offline` in the enumerated cross-cutting tool set.
- `plugins/toolchain/.claude-plugin/plugin.json` + `CHANGELOG.md`:
version bump.

**Version note**: sibling PR #859 (issue #834) landed first and took the
intervening
`0.6.0`/`0.7.0` versions; PR #910 (issue #832) then landed `0.8.0`.
Rebased onto current
`main` and renumbered to `0.9.0`, sequential CHANGELOG history intact.
Collision with #859
is resolved (merged); `do-not-merge` label removed.

## Verification

- Confirmed `--offline` and `--no-progress` are real lychee CLI flags,
and the winget
package id (`lycheeverse.lychee`) / brew formula (`lychee`) are correct,
against lychee's
own upstream README (`lycheeverse/lychee`, `master` branch, Installation
and Commandline
  usage sections).
- Smoke-tested the actual `check-cmd` (`lychee --offline --no-progress
'./**/*.md'`) against
  this repo: 0 errors across 2645 links (1079 unique, 1056 excluded).
- `evals.json` still parses as valid JSON; structural shape (required
`id`/`prompt` fields)
  unchanged — only `expected_output` prose edited.
- `skills/setup/SKILL.md` re-checked against the 500-line skill-quality
hard cap.
- Rebased onto current `origin/main`; `claude plugin validate --strict`,
`node scripts/validate-plugin-contracts.mjs`, and full `bash
scripts/run-plugin-tests.sh`
  all green.

Closes #833

## Related

- Epic: #830
- Brief item 3, `docs/topics/lint-static-analysis-gaps/PLAN.md`
- Sibling PR on the same plugin: #859 (issue #834, pyright) — merged,
serialization resolved

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
kyle-sexton added a commit that referenced this pull request Jul 25, 2026
…opt-in wording gaps (#1025)

## Summary

Three mature, well-scoped follow-up findings on the bundled python
ecosystem default
(`plugins/toolchain/reference/ecosystems/python.yaml` and its owning
check-skill context doc),
deferred from PR #859's review threads and batched here via the #657
triage sweep. All three are
scoped to the python ecosystem surface; no cross-ecosystem mechanism
changes.

## Fix

**Site 1 (P1) — pyright skip granularity.** `uv run pyright` is the
first `check-cmd` tool whose
presence is not implied by the ruff-config opt-in gate, and tool
presence is evaluated **per
ecosystem, not per tool** (per the check skill's tool-presence rule). So
a ruff-configured project
with pyright absent reports a whole-ecosystem missing-tool `skip` that
also drops ruff coverage,
rather than skipping pyright alone.

Chose the issue's **defaulted decision** — document pyright as a hard
prerequisite of the python
default once ruff opts the ecosystem in
(`skills/check/context/python.md`, Type-check section) —
over a per-tool skip-granularity code change. Rationale: per-tool skip
is a larger cross-ecosystem
mechanism change (every `check-cmd` tool would need independent-skip
semantics); the documented
prerequisite is the smaller, reversible fix that resolves the reported
fail-closed surprise without
redesigning the skip model. A maintainer wanting the larger mechanism
can veto and redirect.

**Site 2 (P2) — detection globs.** Added `pyrightconfig.json` to the
python `globs` so a standalone
pyright-config-only change is auto-detected as python-affecting by
`/toolchain:check` and
`/toolchain:lint` local runs (CI already enforces its own check). The
`[tool.pyright]` table is
already covered by the existing `pyproject.toml` glob.

**Site 3 (P2) — opt-in wording.** Reduced the `opt-in` value to its
single ruff-config gating
condition. The trailing semicolon-separated pyright-mode clause
structurally resembled the
multi-clause per-tool `opt-in` form (per the ecosystem-commands
contract, whose schema defines
`opt-in` as either a single whole-`check-cmd` condition **or** a
per-tool multi-clause value), risking
a misread of python's single-condition gate as multi-condition.
**Removed rather than reworded:**
`opt-in` is a *condition* field, not a mode note, and the descriptive
pyright-mode text already lives
verbatim in `skills/check/context/python.md` gotchas (single source of
truth — no duplication). The
reduced value is now identical to the contract's own clean example
(`docs/conventions/ecosystem-commands/examples/python.yaml`), confirming
the intended single-condition
form.

Includes the per-plugin version bump (`0.9.0` → `0.9.1`, patch — scoped
bugfix/hardening) and a
matching top-inserted `CHANGELOG.md` entry.

## Verification

Ran against the changed files in the worktree:

```
check-jsonschema --schemafile docs/conventions/ecosystem-commands/ecosystem.schema.json \
  plugins/toolchain/reference/ecosystems/python.yaml
  -> ok -- validation done

check-jsonschema --schemafile https://json.schemastore.org/claude-code-plugin-manifest.json \
  plugins/toolchain/.claude-plugin/plugin.json
  -> ok -- validation done

markdownlint-cli2 --config .markdownlint-cli2.jsonc \
  plugins/toolchain/skills/check/context/python.md plugins/toolchain/CHANGELOG.md
  -> Summary: 0 error(s)

typos --config _typos.toml <changed files>
  -> clean
```

(`/toolchain:check` and `/toolchain:lint` are model-driven skills, not
runnable scripts; the
ecosystem YAML is validated against its published
`ecosystem.schema.json` contract as the concrete
static gate. No CI lane enforces the ecosystem schema — it is a
reference contract — so the schema
run above is the authoritative structural check for the YAML edit.)

## Related

- Closes #1017
- #859 — originating PR whose review threads deferred these as
follow-ups
- #657 — triage-sweep source (T8h batch conversion)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Work-class: C3 (bug-fix-shaped) — attended triage 2026-07-23,
operator-ratified. 🤖

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automated Opened by automation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(toolchain): add pyright to python ecosystem check-cmd

1 participant