Skip to content

feat(toolchain): add lychee-offline to cross-cutting ecosystem default - #867

Merged
kyle-sexton merged 4 commits into
mainfrom
feat/833-toolchain-lychee-offline
Jul 22, 2026
Merged

feat(toolchain): add lychee-offline to cross-cutting ecosystem default#867
kyle-sexton merged 4 commits into
mainfrom
feat/833-toolchain-lychee-offline

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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

@kyle-sexton kyle-sexton added do-not-merge Hard merge gate: do not merge while applied. automated Opened by automation. labels Jul 21, 2026
@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Claude Code is working…

I'll analyze this and get back to you.

View job run

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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


Security Review — PR #867

  • Gather PR diff and identify changed files
  • Read changed files in full
  • Analyze cross-cutting.yaml check-cmd for injection surfaces
  • Check plugin.json / CHANGELOG for supply-chain or trust issues
  • Check evals.json and SKILL.md for unsafe patterns
  • Post findings

No security issues found.

Reviewed across the standard threat categories:

Command injection / shell injection (cross-cutting.yaml check-cmd)

The added line is:

lychee --offline --no-progress './**/*.md'

All arguments are static — no user-controlled input is interpolated. The glob is single-quoted, so the shell does not expand it; lychee receives it literally and handles glob expansion itself. The $EC_BIN substitution that already existed in this file is resolved by the skill to one of three known binary names (no user-controlled value flows into it). No injection surface introduced.

Network egress / data exfiltration

--offline is passed unconditionally on the command line. Per lychee's CLI contract, command-line flags take precedence over lychee.toml config, so a consumer's repo-root lychee.toml cannot silently re-enable network access and cause link targets to be exfiltrated. The --offline guard is robust against consumer config override.

Supply-chain / auto-install

The install-hint block (winget install lycheeverse.lychee | brew install lychee) is informational text shown to the user when lychee is not found on PATH. It is not auto-executed by the skill — the skill reports skip with the hint and stops. No package is pulled automatically.

Plugin isolation / path traversal

No ../ references. All paths reference ${CLAUDE_PLUGIN_ROOT} or are relative to the consumer's working tree. The glob ./**/*.md scans downward from the repo root, not upward.

Secrets / PII

None introduced. No credentials, tokens, or environment variables carrying sensitive values are referenced.

GitHub Actions / workflow changes

No workflow files were modified.

Version skip (0.5.10.7.0)

Not a security issue. The PR notes this is intentional to avoid a collision with sibling PR #859 which claims 0.6.0. Consumers with loose version constraints will pick this up on next update, which is expected behavior.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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


PR #867 Review — feat(toolchain): add lychee-offline to cross-cutting ecosystem default

  • Gather PR diff and changed files
  • Read changed files in full
  • Review cross-cutting.yaml for correctness and alignment
  • Review plugin.json and CHANGELOG.md for versioning correctness
  • Review evals.json for eval correctness
  • Review SKILL.md for accuracy
  • Post findings

Overall the change is clean, well-scoped, and follows the existing cross-cutting pattern faithfully. No blocking issues. Three findings below, all minor.


Finding 1 — CHANGELOG.md: missing version-gap note (minor)

The changelog jumps from 0.5.1 directly to 0.7.0 with no entry for 0.6.0. The PR description explains this (sibling PR #859 reserves 0.6.0), but a consumer reading the changelog after both PRs land will see a mysterious hole. The do-not-merge label helps during review, but that context disappears post-merge.

Suggestion: add a brief inline note in the 0.7.0 entry:

## [0.7.0]

Permalink


Finding 2 — SKILL.md: backtick inconsistency on the inference-candidate line (nit)

Line 84:

- **cross-cutting** — repo-root config for `typos`/`gitleaks`/editorconfig-checker/`lychee-offline` present (lint-only).

typos and lychee-offline are wrapped in backticks; gitleaks and editorconfig-checker are not. The pre-existing line had typos backtick-wrapped but not the others; this PR adds lychee-offline with backticks, making the inconsistency slightly more visible. Fine to fix in this PR or in a follow-up.


Finding 3 — cross-cutting.yaml: glob scopes to .md only — confirm intent (observation)

lychee --offline --no-progress './**/*.md'

Permalink

Lychee can check links in other text formats (HTML, reStructuredText, TOML/YAML files that contain URLs). Scoping to *.md is a conservative and practical starting point — broken links in docs are the common case, and broader coverage risks noise on codebases with intentional placeholder URLs. Calling this out only for visibility; if the narrow scope is intentional (it appears to be), a brief comment in the check-cmd block or notes field would make the decision explicit for future maintainers.


What looks correct

Area Assessment
--offline robustness CLI flag takes precedence over lychee.toml; a consumer config cannot silently re-enable network access. Confirmed.
Single-quoted glob Shell does not expand ./**/*.md; lychee handles it internally. Correct quoting for the skill's execution context.
install-hint entries lycheeverse.lychee (winget) / lychee (brew) match lychee's upstream README. Consistent with existing tool format.
Version skip 0.5.1 → 0.7.0 Intentional to avoid collision with #859. Documented in PR body. Not a defect.
Eval 3 expected_output Minimal, accurate update — adds lychee-offline to the enumerated tool set; id/prompt unchanged.
opt-in / install-hint pattern Matches exactly the pattern used by typos/gitleaks/editorconfig-checker.
Plugin isolation / path traversal No ../ references; no plugin-form violations.
CLAUDE.md design rules Repo-agnostic, no hardcoded paths, configurable without editing the plugin, no PII.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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


Security Review — PR #867

  • Get PR diff and identify changed files
  • Read cross-cutting.yaml — check-cmd injection surfaces
  • Read plugin.json — supply-chain / version issues
  • Read CHANGELOG.md — no-op security check
  • Read evals.json — unsafe eval patterns
  • Read SKILL.md — command construction / injection
  • Post findings

No security issues found.

Reviewed across the standard threat categories:

Command injection / shell injection (cross-cutting.yaml L16)

The added line is:

lychee --offline --no-progress './**/*.md'

All arguments are static — no user-controlled input is interpolated. The glob is single-quoted, so the shell does not expand it; lychee receives the literal string ./**/*.md and handles globbing itself. The $EC_BIN substitution that already existed in this file is resolved by the skill to one of three known binary names — no user-controlled value flows into it. No injection surface introduced.

Network egress / data exfiltration

--offline is passed unconditionally on the command line. Per lychee's CLI contract, command-line flags take precedence over lychee.toml config, so a consumer's repo-root lychee.toml cannot silently re-enable network access. The --offline guard is robust against consumer config override.

Supply-chain / auto-install

The install-hint block (L23) is informational text shown to the user when lychee is absent from PATH. It is not auto-executed — the skill reports skip with the hint and stops. No package is pulled automatically.

Plugin isolation / path traversal

No ../ references. The glob ./**/*.md anchors to the consumer's working directory and traverses downward only. No upward traversal possible.

Secrets / PII

None introduced.

GitHub Actions / workflow changes

No workflow files were modified.

evals.json / SKILL.md

Both are prose/metadata — no code execution path. The expected_output text addition and the cross-cutting inference-candidate line addition are documentation only.

Version skip (0.5.10.7.0)

Not a security issue. The PR notes this is intentional to avoid a collision with sibling PR #859 claiming 0.6.0.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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


PR #867 Review — feat(toolchain): add lychee-offline to cross-cutting ecosystem default

  • Gather PR diff and changed files
  • Read changed files in full
  • Review cross-cutting.yaml for correctness and alignment
  • Review plugin.json and CHANGELOG.md for versioning correctness
  • Review evals.json for eval correctness
  • Review SKILL.md for accuracy
  • Post findings

The change set is focused and correctly executed. Five files touched, all for the right reasons. No correctness defects found. Security was already independently confirmed (no injection surface; --offline is positional and cannot be overridden by a consumer's lychee.toml).

cross-cutting.yaml

The added block follows the existing per-tool pattern exactly — an inline comment on the check-cmd line, a matching opt-in entry (, lychee.toml appended), and an install-hint line using the same tool: winget … | brew … format. No structural deviations.

One thing worth being explicit about: adding this to the bundled rung-4 default means lychee will run on every consumer repo that hasn't overridden cross-cutting.yaml, not only on repos that opt in via lychee.toml. This mirrors the existing behavior for typos / gitleaks / editorconfig-checker — they also run unconditionally from the bundled default. If lychee is absent from PATH, the skill reports skip with the install-hint (per eval 4's contract), so no new FAIL risk from missing binaries. If lychee is present and a consumer's markdown has broken local anchors or file references, those will now surface. That's the intended behavior, and the CHANGELOG entry documents it clearly — just worth flagging as a consumer-visible functional change.

Glob scope: './**/*.md' covers Markdown only. HTML files with local links are out of scope, which is a reasonable default for offline-only checking.

plugin.json + CHANGELOG.md

Version jump from 0.5.10.7.0, skipping 0.6.0. The PR description explains this correctly (sibling PR #859 claims 0.6.0), the PR is appropriately marked do-not-merge, and the CHANGELOG entry for [0.7.0] is accurate. The gap is intentional and will resolve when the serialization between this PR and #859 is settled. No action needed here before that resolution.

evals.json — eval 3

expected_output now reads typos/gitleaks/editorconfig-checker/lychee-offline, which matches the updated check-cmd. The expectations array for eval 3 was not changed — it doesn't enumerate specific tools, only the behavioral contract (opt-in honored, cross-cutting runs when any text file changed). That's correct; the enumeration belongs in expected_output, not in the reusable expectations.

skills/setup/SKILL.md

Line 84: cross-cutting inference signal gains /lychee-offline. This is the signal for apply to suggest configuring the cross-cutting ecosystem when a lychee.toml is present at repo root — consistent with how the other tools are listed. The inference signal is about when to offer configuration, not when the tool runs (it runs from the bundled default regardless), so this is semantically correct.

Summary

No blocking correctness issues. The only pending gate is the version-collision serialization with #859, which is already tracked and the PR is correctly marked do-not-merge for that reason. Once that resolves (and the version is renumbered if #859 lands first), this is ready to merge.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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


PR #867 Review — feat(toolchain): add lychee-offline to cross-cutting ecosystem default

  • Gather PR diff and identify changed files
  • Read changed files in full
  • Review cross-cutting.yaml for correctness and alignment
  • Review plugin.json and CHANGELOG.md for versioning correctness
  • Review evals.json and SKILL.md for accuracy
  • Post findings

Post-merge-sync confirmation: the a2fe186 merge commit brought in 9533a14 chore: sync standards components (#873) from main. That commit does not touch any of the 5 PR files — the diff against origin/main is identical in shape to the pre-sync state. No conflicts, no regressions introduced.

No correctness or security issues found. The change set is focused, follows the existing cross-cutting pattern faithfully, and is CLAUDE.md-compliant. Three prior findings remain unaddressed (all carried over from earlier review passes); none are blocking.


Finding 1 — CHANGELOG.md: version gap has no in-file explanation (minor)

The changelog jumps 0.7.00.5.2 with no 0.6.0 entry and no inline note explaining the skip:

## [0.7.0]
...
## [0.5.2]

The PR body explains this (sibling PR #859 reserves 0.6.0), but that context vanishes post-merge. A one-line callout in the [0.7.0] header would make the changelog self-contained for future maintainers:

## [0.7.0]

Permalink · Fix this →


Finding 2 — SKILL.md: backtick inconsistency on the inference-candidate line (nit)

Line 84:

- **cross-cutting** — repo-root config for `typos`/`gitleaks`/editorconfig-checker/`lychee-offline` present (lint-only).

typos and lychee-offline are backtick-wrapped; gitleaks and editorconfig-checker are not. Either wrap all four or none. The pre-existing line had typos wrapped but not the others; this PR adds lychee-offline with backticks, making the inconsistency slightly more visible.

Fix this →


Finding 3 — cross-cutting.yaml: .md-only glob scope — confirm intent (observation)

lychee --offline --no-progress './**/*.md'

Permalink

Lychee can also check local links in HTML and plain-text files. *.md-only is a sensible conservative default — broken links in docs are the common case, and broader coverage risks noise from intentional placeholder URLs in source. The inline comment (--offline = no network (external URLs skipped)) correctly documents the flag, but doesn't explain why *.md was chosen over a wider glob. If this scope is intentional (it appears to be), a brief addition to the notes field or a second inline comment would make the decision explicit. Fine to address in a follow-up if preferred.


What looks correct

Area Assessment
--offline robustness CLI flag takes precedence over lychee.toml; consumer config cannot silently re-enable network access. Confirmed.
Single-quoted glob Shell does not expand ./**/*.md; lychee handles globbing. Correct for the skill's execution context.
install-hint entries lycheeverse.lychee (winget) / lychee (brew) match lychee upstream. Consistent format with existing tools.
opt-in field lychee.toml appended following the same per-tool-config pattern.
Version skip 0.5.2 → 0.7.0 Intentional to avoid collision with #859. Marked do-not-merge. Not a defect.
Eval 3 expected_output Accurate minimal update; id/prompt/expectations unchanged.
CLAUDE.md design rules Repo-agnostic, configurable without editing, plugin-form-safe, no PII, explicitly versioned.
Merge-sync impact No conflicts; 9533a14 (standards sync from main) does not touch any of the 5 PR files.

The only pending gate is the version-collision serialization with #859, already tracked and correctly reflected in the do-not-merge label. Once that resolves, this is ready to merge.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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


Security Review — PR #867

  • Gather PR diff via git diff origin/main...HEAD
  • Read changed files in full
  • Analyze cross-cutting.yaml check-cmd for injection surfaces
  • Check install-hint for supply-chain / auto-execution risk
  • Check --offline flag robustness against consumer config override
  • Check plugin isolation / path traversal
  • Check plugin.json, evals.json, SKILL.md for unsafe patterns
  • Post findings

No security issues found.

Command injection — cross-cutting.yaml L16

lychee --offline --no-progress './**/*.md'

All arguments are static literals — no user-controlled input is interpolated. The glob is single-quoted, preventing shell word-splitting and expansion at the shell layer; lychee receives the literal string ./**/*.md and handles it internally. No injection surface introduced.

--offline flag robustness

--offline is a CLI flag passed positionally on the command line. Per lychee's CLI contract, command-line flags take precedence over lychee.toml config file values, so a consumer's repo-root lychee.toml containing offline = false cannot silently re-enable network access. The guard is robust against consumer config override.

Supply-chain / auto-install — cross-cutting.yaml L23

lychee: winget install lycheeverse.lychee | brew install lychee

The install-hint field is informational text displayed to the user when lychee is absent from PATH; it is not auto-executed by the skill — the skill reports skip with the hint and stops. The | here is display prose, not a shell pipe. No package is pulled automatically. The package IDs (lycheeverse.lychee / lychee) match lychee's upstream README.

Path traversal / plugin isolation

The glob ./**/*.md anchors to the consumer's working directory and traverses downward only. No ../ references appear in any changed file. No ${CLAUDE_PLUGIN_ROOT} violations. Fully within plugin-form bounds.

Secrets / PII

None introduced. No credentials, tokens, or environment variables carrying sensitive values are referenced.

GitHub Actions / workflow changes

No workflow files were modified.

plugin.json, evals.json, SKILL.md, CHANGELOG.md

Purely metadata/documentation — no code execution paths. The evals.json change is a single prose string update; SKILL.md is a one-line inference-signal addition; plugin.json is a version field bump; CHANGELOG.md is a Keep-a-Changelog entry. No unsafe patterns.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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


PR #867 Review — feat(toolchain): add lychee-offline to cross-cutting ecosystem default

  • Get current diff and identify changes since last review
  • Read changed files at HEAD SHA
  • Assess whether prior findings were addressed
  • Check for new issues introduced by the sync merge
  • Post findings

Post-sync state (ca85e25): The latest merge commit resolves a 3-way conflict in plugin.json / CHANGELOG.md against #890's real merge (dotnet opt-in gate, which landed as 0.6.0), and re-derives this PR's version to 0.9.0 — one past #859's re-rebased 0.8.0 claim. The functional change itself (lychee --offline --no-progress './**/*.md' in cross-cutting.yaml) is unchanged from previous review passes and remains correct. Security is clean (confirmed by prior passes; the new sync commit touches only plugin.json and CHANGELOG.md).

Two carried findings and one new finding.


Finding 1 — NEW: PR body is now stale relative to the code (minor, needs update before merge)

The PR description's version note reads:

"bumped to 0.7.0 rather than the next sequential 0.6.0 — sibling PR #859 (issue #834, also targeting plugins/toolchain) already claims 0.6.0."

But the code now has 0.9.0, and the serialization picture has changed significantly since that was written:

A reviewer reading only the PR body will see a mismatch: the stated version (0.7.0) and the actual code (0.9.0) diverge, and the stated collision source (0.6.0 claimed by #859) is no longer accurate. The do-not-merge label is still appropriate, but the PR body's serialization note should be updated to match current reality.

Fix this →


Finding 2 — CARRIED: CHANGELOG gap now spans 0.6.0 → 0.9.0 with no inline explanation (minor)

The changelog in this PR reads:

## [0.9.0]
...
## [0.6.0]

The gap now skips both 0.7.0 (unclaimed — was this PR's prior target) and 0.8.0 (claimed by in-flight #859). A brief note in the [0.9.0] header would make the changelog self-contained for consumers who read it post-merge and see a two-version hole:

## [0.9.0]

Permalink · Fix this →


Finding 3 — CARRIED: SKILL.md backtick inconsistency (nit)

Line 84 still reads:

- **cross-cutting** — repo-root config for `typos`/`gitleaks`/editorconfig-checker/`lychee-offline` present (lint-only).

typos and lychee-offline are backtick-wrapped; gitleaks and editorconfig-checker are not. Either wrap all four or none — unchanged since prior reviews.

Fix this →


What looks correct

Area Assessment
Functional change (cross-cutting.yaml) Unchanged and correct — lychee --offline --no-progress './**/*.md' added following the existing per-tool pattern exactly.
--offline robustness CLI flag takes precedence over lychee.toml; consumer config cannot silently re-enable network access. Confirmed.
Single-quoted glob Shell does not expand ./**/*.md; lychee handles globbing. Correct.
install-hint entries lycheeverse.lychee (winget) / lychee (brew) match lychee upstream.
opt-in field lychee.toml appended following the same per-tool-config pattern.
Version re-derivation to 0.9.0 Logic is sound — one past #859's re-rebased 0.8.0 claim. Merge commit message documents the conflict resolution.
do-not-merge label Still appropriate — serialization with #859 not yet settled.
Eval 3 expected_output Unchanged since last review; accurate.
Merge-sync impact ca85e25 only touched plugin.json and CHANGELOG.md (version fields). All other PR files unchanged. No regressions.
CLAUDE.md design rules Repo-agnostic, plugin-form-safe, no PII, no hardcoded paths.

The pending gate remains the #859 serialization. Once that resolves and the PR body is updated to reflect current version history (Finding 1), this is ready to merge. Findings 2 and 3 are minor polish items that can be addressed here or in a follow-up.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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


Security Review — PR #867

  • Load tools
  • Get PR diff
  • Read changed files
  • Analyze cross-cutting.yaml for command injection / shell injection
  • Analyze --offline flag robustness against consumer config override
  • Analyze install-hint for supply-chain / auto-execution risk
  • Check plugin isolation / path traversal
  • Check plugin.json, evals.json, SKILL.md, CHANGELOG.md for unsafe patterns
  • Check GitHub Actions / workflow changes

No security issues found.

Reviewed across all applicable threat categories:

Command injection — cross-cutting.yaml L16

lychee --offline --no-progress './**/*.md'

All arguments are static literals — no user-controlled input is interpolated into this command. The glob is single-quoted, so the shell does not perform word-splitting or pathname expansion on it; lychee receives the literal pattern ./**/*.md and handles glob expansion internally. No injection surface introduced.

Network egress / --offline robustness

--offline is passed as a positional CLI flag. Per lychee's CLI contract (clap-based), command-line flags take precedence over values in lychee.toml. A consumer's repo-root lychee.toml containing offline = false (or no offline key at all) cannot silently re-enable network access. The --offline guard is robust against consumer config override.

Supply-chain / auto-install — cross-cutting.yaml L23

lychee: winget install lycheeverse.lychee | brew install lychee

The install-hint field is informational prose displayed to the user when lychee is absent from PATH. It is not auto-executed — the skill reports skip with the hint and stops. The | here is a display separator, not a shell pipe. Package identifiers (lycheeverse.lychee / lychee) match lychee's upstream README. No package is pulled automatically.

Path traversal / plugin isolation

The glob ./**/*.md anchors downward from the consumer's working directory. No ../ references appear in any of the 5 changed files. No ${CLAUDE_PLUGIN_ROOT} violations. Fully within plugin-form bounds.

Secrets / PII

None introduced. No credentials, tokens, or sensitive environment variables referenced in any changed file.

GitHub Actions / workflow changes

No .github/workflows/ files appear in the PR's 5 changed files. (Workflow changes visible in the branch history are from the origin/main merge commit, not from this PR's own commits.)

plugin.json, evals.json, SKILL.md, CHANGELOG.md

Purely metadata and documentation — no code execution paths. Version field bump, Keep-a-Changelog prose entry, single expected_output string update, one-line inference-candidate addition. No unsafe patterns.


@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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


PR #867 Review — feat(toolchain): add lychee-offline to cross-cutting ecosystem default

  • Get current diff and identify changes since last review
  • Read changed files at HEAD SHA 9543e71
  • Assess whether prior findings were addressed
  • Check for new issues
  • Post findings

Sync commit 9543e71 (docs(toolchain): make cross-cutting tool backtick wrapping consistent) addresses the one nit carried across all prior review passes. Nothing new introduced.


Finding resolved — SKILL.md backtick inconsistency (was Finding 3)

Line 84 now reads:

- **cross-cutting** — repo-root config for `typos`/`gitleaks`/`editorconfig-checker`/`lychee-offline` present (lint-only).

All four tool names are consistently backtick-wrapped. Resolved.


Remaining open item — CHANGELOG version gap has no inline explanation (minor)

The changelog jumps [0.9.0][0.6.0] with 0.7.0 and 0.8.0 missing and no in-file note:

## [0.9.0]
...
## [0.6.0]

The PR body explains the serialization story (0.6.0 landed via #890, #859 claims 0.8.0, this PR steps to 0.9.0), but that context disappears post-merge. A one-line callout in the [0.9.0] entry would make the changelog self-contained.

Permalink · Fix this →


What looks correct

Area Assessment
Functional change (cross-cutting.yaml) lychee --offline --no-progress './**/*.md' — static args, single-quoted glob, --offline is CLI-precedent-safe. Confirmed correct across prior passes.
install-hint lycheeverse.lychee (winget) / lychee (brew) match lychee's upstream README.
opt-in field lychee.toml follows the same per-tool-config pattern as existing tools.
Version 0.9.0 Correctly derived — one past #859's re-rebased 0.8.0 claim.
evals.json eval 3 typos/gitleaks/editorconfig-checker/lychee-offline matches updated check-cmd.
SKILL.md line 84 Backtick wrapping now consistent across all four tool names. ✓
do-not-merge label Still appropriate — #859 serialization not settled.
Security No injection surface, no auto-install, no path traversal, no PII. Confirmed by prior security passes.
CLAUDE.md compliance Repo-agnostic, plugin-form-safe, configurable without editing, explicitly versioned.

The only pending gate remains the #859 serialization. The CHANGELOG gap note is minor polish that can be addressed here or in a follow-up. Once #859 resolves, this is ready to merge.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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


Security Review — PR #867

  • Get PR diff and identify changed files
  • Read changed files at HEAD (9543e71)
  • Analyze cross-cutting.yaml for injection surfaces
  • Analyze supply-chain / auto-install risk
  • Analyze --offline flag robustness
  • Analyze for secrets / PII / egress
  • Analyze GitHub Actions patterns
  • Post findings

No security issues found.

This review pass covers the synchronize commit 9543e71 (docs(toolchain): make cross-cutting tool backtick wrapping consistent), which is a one-line prose edit to SKILL.md (the backtick-consistency nit raised in prior passes). No new attack surface introduced.

Reviewed across all applicable threat categories against the 5 changed files:

Command injection — cross-cutting.yaml L16

lychee --offline --no-progress './**/*.md'

All arguments are static literals — no user-controlled input is interpolated. The glob is single-quoted, so the shell does not perform word-splitting or pathname expansion on it; lychee receives the literal string ./**/*.md and handles glob expansion internally. No injection surface introduced.

--offline flag robustness

--offline is a positional CLI flag. Per lychee's CLI contract (clap-based), command-line flags take precedence over values in lychee.toml. A consumer's repo-root lychee.toml containing offline = false (or no offline key) cannot silently re-enable network access. The guard is robust against consumer config override.

Supply-chain / auto-install — cross-cutting.yaml L23

lychee: winget install lycheeverse.lychee | brew install lychee

The install-hint field is informational prose displayed to the user when lychee is absent from PATH. It is not auto-executed — the skill reports skip with the hint and stops. The | here is a display separator, not a shell pipe. No package is pulled automatically. Package IDs match lychee's upstream README.

Path traversal / plugin isolation

The glob ./**/*.md anchors downward from the consumer's working directory. No ../ references appear in any of the 5 changed files. No ${CLAUDE_PLUGIN_ROOT} violations. Fully within plugin-form bounds.

Secrets / PII

None introduced. No credentials, tokens, or sensitive environment variables referenced.

GitHub Actions / workflow changes

No .github/workflows/ files appear in the 5 changed files.

plugin.json, evals.json, SKILL.md, CHANGELOG.md

Purely metadata and documentation — no code execution paths. Version field bump, Keep-a-Changelog prose entry, one expected_output string update, one-line backtick-consistency fix in SKILL.md. No unsafe patterns.


@kyle-sexton

Copy link
Copy Markdown
Contributor Author
# Finding Classification Evidence Reacted
1 PR body version note is stale (says bumped to 0.7.0, code is now 0.9.0, references superseded #859/0.6.0 collision framing) VALID — not fixed here Confirmed plugin.json has "version": "0.9.0" (plugins/toolchain/.claude-plugin/plugin.json:4) vs. the PR description's 0.7.0 claim. The PR body is GitHub metadata, not a repo file — out of scope for a branch commit, and the version is still moving alongside sibling #859, so an edit now would likely re-stale again before this merges. Surfacing as a remaining item for the PR author/orchestrator rather than editing description text. 👍
2 CHANGELOG.md has a 0.9.00.6.0 gap with no inline note explaining the skipped 0.7.0/0.8.0 VALID (defer) Confirmed in plugins/toolchain/CHANGELOG.md: ## [0.9.0] at line 6, next entry ## [0.6.0] at line 19, no explanatory note between them. Declining the bot's suggested fix as-written: it embeds live PR/issue numbers (#859, #834) in a committed changelog comment, which is exactly the kind of rot-prone cross-reference this repo's conventions avoid in comments/docs. If a note is added, it should describe the gap without naming in-flight PR numbers — deferring to the PR author's judgment on wording, or letting the gap self-resolve once serialization with #859 settles. 👍
3 SKILL.md:84 backtick wrapping inconsistent — typos/lychee-offline wrapped, gitleaks/editorconfig-checker not VALID — fixing Confirmed at plugins/toolchain/skills/setup/SKILL.md:84. Fixed in 9543e71: all four tool names now backtick-wrapped consistently. 👍

Also re-verified the failing do-not-merge check: it fails by design because the PR carries the do-not-merge label (label-gate workflow, not a code defect) — expected while serialization with #859 is unresolved, not a regression to fix.

kyle-sexton and others added 3 commits July 22, 2026 09:10
On-disk link/anchor integrity check with no network dependency —
--offline skips external URLs and only verifies local file/fragment
targets, matching the other rung-4 cross-cutting tools' opt-in pattern.

Per Brief item 3, docs/topics/lint-static-analysis-gaps/PLAN.md.
Closes #833.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: 0f69ae76-07e0-4aae-9c4a-11688fc0e8ea
The automated review check on the prior push failed with an infra
error ("no execution file was produced") rather than a real review
verdict; gh run rerun could not retry it directly. Empty commit to
get a fresh review pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: 0f69ae76-07e0-4aae-9c4a-11688fc0e8ea
Wrap gitleaks and editorconfig-checker in backticks to match typos
and lychee-offline in the cross-cutting inference-candidate line.
@kyle-sexton kyle-sexton removed the do-not-merge Hard merge gate: do not merge while applied. label Jul 22, 2026
@kyle-sexton
kyle-sexton force-pushed the feat/833-toolchain-lychee-offline branch from 9543e71 to 84a9aaf Compare July 22, 2026 13:46
@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

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


PR #867 Review — feat(toolchain): add lychee-offline to cross-cutting ecosystem default

  • Get current diff at HEAD 84a9aaf
  • Read changed files in full
  • Assess prior findings against current state
  • Check for new issues
  • Post findings

All prior findings resolved. Commit 84a9aaf (docs(toolchain): make cross-cutting tool backtick wrapping consistent) is the last synchronize — it addresses the sole remaining nit and leaves the branch clean.

Prior findings — current status

Finding Prior state Current state
SKILL.md:84 backtick inconsistency gitleaks/editorconfig-checker unwrapped while typos/lychee-offline were Resolved in 84a9aaf — all four tool names are now consistently backtick-wrapped (L84)
CHANGELOG version gap Jumped 0.9.0 → 0.6.0 with 0.7.0/0.8.0 missing Resolved by rebase — CHANGELOG now reads 0.9.0 → 0.8.0 → 0.7.0 → 0.6.0 → 0.5.2 → 0.5.1 → 0.5.0 — fully sequential, no gaps
PR body version note stale Described 0.7.0 and referenced superseded #859/0.6.0 framing Acknowledged by author as GitHub metadata (deferred, not a file change)

What looks correct at HEAD

Area Assessment
cross-cutting.yamlcheck-cmd lychee --offline --no-progress './**/*.md' — static args, single-quoted glob (shell does not expand it), --offline takes CLI precedence over lychee.toml. Correct.
cross-cutting.yamlopt-in lychee.toml appended, matching the per-tool-config pattern of the three existing tools. Correct.
cross-cutting.yamlinstall-hint lycheeverse.lychee (winget) / lychee (brew) — informational prose, not auto-executed; matches lychee's upstream README. Correct.
plugin.json version 0.9.0 — sequential one past 0.8.0 (Go ecosystem, #910). Correct.
CHANGELOG.md Fully sequential; new [0.9.0] entry accurately documents the lychee-offline addition.
evals.json eval 3 expected_output typos/gitleaks/editorconfig-checker/lychee-offline matches updated check-cmd. id/prompt/expectations unchanged.
SKILL.md:84 All four tool names backtick-wrapped. Consistent.
Security No injection surface; --offline is CLI-precedence-safe; install-hint is not auto-executed; no path traversal; no PII. Confirmed by prior security passes and unchanged since.
CLAUDE.md compliance Repo-agnostic, plugin-form-safe, configurable without editing the plugin, explicitly versioned.

No issues found. The change is focused, well-scoped, and follows the existing cross-cutting pattern faithfully. Ready to merge once the do-not-merge label is cleared.

@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

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


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


Security Review — PR #867

  • Load tools
  • Get diff and identify changed files (5 files: cross-cutting.yaml, plugin.json, CHANGELOG.md, evals.json, SKILL.md)
  • Read changed files at HEAD 84a9aaf
  • Analyze cross-cutting.yaml for command injection / shell injection
  • Analyze --offline flag robustness against consumer config override
  • Analyze install-hint for supply-chain / auto-execution risk
  • Analyze path traversal / plugin isolation
  • Analyze plugin.json, evals.json, SKILL.md, CHANGELOG.md
  • Check for GitHub Actions / workflow changes
  • Post findings

No security issues found.

This pass covers HEAD 84a9aaf (docs(toolchain): make cross-cutting tool backtick wrapping consistent). The only change in this commit is a one-line prose edit to SKILL.md line 84 — backtick wrapping made consistent across all four tool names. No new attack surface introduced.

Command injection — cross-cutting.yaml L16

lychee --offline --no-progress './**/*.md'

All arguments are static literals. No user-controlled input is interpolated. The glob is single-quoted, so the shell does not perform word-splitting or pathname expansion on it; lychee receives the literal string ./**/*.md and handles glob expansion internally. The "$EC_BIN" substitution that was already present in this file is an existing pattern (not introduced by this PR) and resolves to one of three known binary names.

--offline flag robustness

--offline is a positional CLI flag. Per lychee's CLI contract (clap-based), command-line flags take precedence over values in lychee.toml. A consumer's repo-root lychee.toml cannot silently re-enable network access. The guard is robust against consumer config override.

Supply-chain / auto-install — cross-cutting.yaml L23

lychee: winget install lycheeverse.lychee | brew install lychee

The install-hint field is informational prose shown to the user when lychee is absent from PATH. It is not auto-executed — the skill reports skip with the hint and stops. The | is a display separator matching the same format as the existing typos/gitleaks/editorconfig-checker entries, not a shell pipe. No package is pulled automatically.

Path traversal / plugin isolation

The glob ./**/*.md anchors downward from the consumer's working directory. No ../ references appear in any of the 5 changed files. No ${CLAUDE_PLUGIN_ROOT} violations.

Secrets / PII

None introduced across all 5 changed files.

GitHub Actions / workflow changes

No .github/workflows/ files appear in the 5 changed files.

plugin.json, evals.json, SKILL.md, CHANGELOG.md

Purely metadata and documentation — no code execution paths. Version field bump, Keep-a-Changelog prose entry, one expected_output string update, one-line backtick-consistency fix in SKILL.md. No unsafe patterns.

…ding

markdownlint MD022/MD032: a heading and the list above it need a blank line
between them. Lost during the 0.9.0 rebase conflict resolution.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

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


PR #867 Review — feat(toolchain): add lychee-offline to cross-cutting ecosystem default

  • Get current diff and identify changes since last review
  • Read changed files at HEAD SHA 32d0ddc
  • Assess whether prior findings were addressed
  • Check for new issues
  • Post findings

All prior findings resolved. Commit 32d0ddc (fix(toolchain): restore missing blank line before CHANGELOG 0.5.1 heading) is the latest synchronize — a one-line markdownlint compliance fix, nothing else. No new issues.

Prior findings — current status

Finding Prior state Current state
CHANGELOG version gap (0.9.0 → 0.6.0) Jumped two versions with no entries for 0.7.0/0.8.0 Resolved — CHANGELOG now reads 0.9.0 → 0.8.0 → 0.7.0 → 0.6.0 → 0.5.2 → 0.5.1 → ... — fully sequential, no gaps
SKILL.md:84 backtick inconsistency typos/lychee-offline wrapped, gitleaks/editorconfig-checker not Resolved in 84a9aaf — all four tool names consistently backtick-wrapped: `typos`/`gitleaks`/`editorconfig-checker`/`lychee-offline`
.md-only glob scope Observation only — narrow but intentional Acknowledged; no change needed

What the latest commit does

-  precompute convention — bash-only pipelines must not fall through to a
-  PowerShell host.
+
## [0.5.1]

Restores a blank line between the prior section's list and the ## [0.5.1] heading, satisfying markdownlint MD022 (headings surrounded by blank lines) / MD032 (lists surrounded by blank lines). The line was dropped during the 0.9.0 rebase conflict resolution. Correct fix, no other content touched.

What looks correct at HEAD

Area Assessment
cross-cutting.yamlcheck-cmd lychee --offline --no-progress './**/*.md' — static args, single-quoted glob, --offline takes CLI precedence over lychee.toml. Correct.
cross-cutting.yamlopt-in / install-hint lychee.toml + lycheeverse.lychee/lychee follow the same per-tool-config pattern as existing tools. Correct.
plugin.json version 0.9.0 — sequential one past 0.8.0 (Go ecosystem, #910). Correct.
CHANGELOG.md Fully sequential; blank-line compliance restored; [0.9.0] entry accurately documents the lychee-offline addition.
evals.json eval 3 typos/gitleaks/editorconfig-checker/lychee-offline matches updated check-cmd. id/prompt/expectations unchanged.
SKILL.md:84 All four tool names backtick-wrapped consistently.
Security No injection surface; --offline is CLI-precedence-safe; install-hint is not auto-executed; no path traversal; no PII. Confirmed across prior security passes; unchanged.
CLAUDE.md compliance Repo-agnostic, plugin-form-safe, configurable without editing the plugin, explicitly versioned.

No issues found. The change is focused, well-scoped, and correctly executed. Ready to merge once the do-not-merge / #859 serialization gate resolves.

@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

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


Security Review — PR #867

  • Get PR diff and identify changed files
  • Read changed files at HEAD 32d0ddc
  • Analyze cross-cutting.yaml for command injection / shell injection
  • Analyze --offline flag robustness against consumer config override
  • Analyze install-hint for supply-chain / auto-execution risk
  • Analyze path traversal / plugin isolation
  • Analyze plugin.json, evals.json, SKILL.md, CHANGELOG.md
  • Check for GitHub Actions / workflow changes

No security issues found.

Reviewed across all applicable threat categories against the 5 files changed by the 4 PR-specific commits (c25a72e, 84a9aaf, 731b487, 32d0ddc).

Command injection — cross-cutting.yaml L16

lychee --offline --no-progress './**/*.md'

All arguments are static literals — no user-controlled input is interpolated. The glob is single-quoted, so the shell does not perform word-splitting or pathname expansion on it; lychee receives the literal string ./**/*.md and handles glob expansion internally. The "$EC_BIN" substitution already present in this file (pre-existing, not introduced by this PR) resolves to one of three known binary names. No injection surface introduced.

Severity: N/A — Confidence: High

Network egress — --offline flag robustness

--offline is a positional CLI flag. Per lychee's CLI contract (clap-based), command-line flags take precedence over values in lychee.toml. A consumer's repo-root lychee.toml containing offline = false cannot silently re-enable network access. The guard is robust against consumer config override.

Severity: N/A — Confidence: High

Supply-chain / auto-install — cross-cutting.yaml L23

lychee: winget install lycheeverse.lychee | brew install lychee

The install-hint field is informational prose displayed to the user when lychee is absent from PATH — it is not auto-executed by the skill; the skill reports skip with the hint and stops. The | is a display separator matching the format of the three existing entries, not a shell pipe. Package identifiers (lycheeverse.lychee / lychee) match lychee's upstream README. No package is pulled automatically.

Severity: N/A — Confidence: High

Path traversal / plugin isolation

The glob ./**/*.md anchors downward from the consumer's working directory. No ../ references appear in any of the 5 changed files. No ${CLAUDE_PLUGIN_ROOT} violations. Fully within plugin-form bounds.

Severity: N/A — Confidence: High

Secrets / PII

None introduced. No credentials, tokens, or sensitive environment variables referenced in any changed file.

GitHub Actions / workflow changes

The 4 PR-specific commits touch no .github/workflows/ files. Confirmed via git show c25a72e 84a9aaf 731b487 32d0ddc --name-only.

plugin.json, evals.json, SKILL.md, CHANGELOG.md

Purely metadata and documentation — no code execution paths. Version field bump, Keep-a-Changelog prose, one expected_output string update, backtick-consistency fix, and a blank-line restore for markdownlint compliance. No unsafe patterns in any of these files.


@kyle-sexton
kyle-sexton marked this pull request as ready for review July 22, 2026 13:57
@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

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


PR #867 Review — feat(toolchain): add lychee-offline to cross-cutting ecosystem default

  • Read changed files at HEAD 32d0ddc
  • Assess prior findings against current state
  • Review cross-cutting.yaml for correctness and alignment
  • Review plugin.json and CHANGELOG.md for versioning
  • Review evals.json and SKILL.md for accuracy
  • Post findings

All prior findings resolved. No issues found. Ready to merge.

Prior findings — current status

Finding Resolution
CHANGELOG version gap (0.9.0 → 0.6.0, skipping 0.7.0/0.8.0) Resolved — CHANGELOG is now fully sequential: 0.9.0 → 0.8.0 → 0.7.0 → 0.6.0 → 0.5.2 → 0.5.1 → 0.5.0
SKILL.md:84 backtick inconsistency Resolved in 84a9aaf — all four tool names consistently backtick-wrapped: `typos`/`gitleaks`/`editorconfig-checker`/`lychee-offline`
.md-only glob scope (observation) Acknowledged; no change needed — narrow scope is intentional
Version collision with #859 (do-not-merge) Resolved — label removed, serialization settled at 0.9.0

What looks correct

Area Assessment
cross-cutting.yamlcheck-cmd lychee --offline --no-progress './**/*.md' — static args, single-quoted glob (shell does not expand it), --offline takes CLI precedence over lychee.toml. Correct.
cross-cutting.yamlopt-in lychee.toml appended to the existing list, matching the per-tool-config pattern of the three existing tools. Correct.
cross-cutting.yamlinstall-hint lycheeverse.lychee (winget) / lychee (brew) — informational prose, not auto-executed; matches lychee's upstream README format. Correct.
plugin.json — version 0.9.0 — sequential one past 0.8.0 (Go ecosystem, #910). Correct.
CHANGELOG.md Fully sequential; blank-line markdownlint compliance restored in 32d0ddc; [0.9.0] entry accurately documents the addition.
evals.json eval 3 expected_output typos/gitleaks/editorconfig-checker/lychee-offline matches the updated check-cmd. id/prompt/expectations unchanged.
SKILL.md:84 All four tool names consistently backtick-wrapped.
Security No injection surface; --offline is CLI-precedence-safe against lychee.toml override; install-hint is not auto-executed; no path traversal; no PII. Confirmed across prior security passes; functional change unchanged since first review.
CLAUDE.md compliance Repo-agnostic, plugin-form-safe, configurable without editing the plugin, explicitly versioned, no hardcoded paths.

@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

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


Security Review — PR #867

  • Load tools
  • Get PR diff and identify changed files
  • Read changed files at HEAD 32d0ddc
  • Analyze cross-cutting.yaml for command injection / shell injection
  • Analyze --offline flag robustness against consumer config override
  • Analyze install-hint for supply-chain / auto-execution risk
  • Analyze path traversal / plugin isolation
  • Analyze plugin.json, evals.json, SKILL.md, CHANGELOG.md
  • Check for GitHub Actions / workflow changes
  • Post findings

No security issues found.

Reviewed across all applicable threat categories against the 5 files changed by this PR's 4 commits (c25a72e, 84a9aaf, 731b487, 32d0ddc).

Command injection — cross-cutting.yaml L16

lychee --offline --no-progress './**/*.md'

All arguments are static literals — no user-controlled input is interpolated into this command. The glob is single-quoted: in the bash execution context declared by this plugin, single quotes prevent all shell interpretation (variable expansion, command substitution, pathname expansion); lychee receives the literal string ./**/*.md and handles glob expansion internally. The pre-existing "$EC_BIN" substitution on L14 is not introduced by this PR and resolves to one of three known binary names set by the skill itself — no user-controlled value flows into it.

Severity: N/A — Confidence: High

Network egress — --offline flag robustness

--offline is a positional CLI flag. Per lychee's CLI contract (clap-based argument parsing), command-line flags take precedence over values in lychee.toml. A consumer's repo-root lychee.toml containing offline = false (or omitting the key entirely) cannot silently re-enable network access. The guard is robust against consumer config override — including the opt-in scenario documented in the opt-in field where a consumer's lychee.toml customizes the ruleset.

Severity: N/A — Confidence: High

Supply-chain / auto-install — cross-cutting.yaml L23

lychee: winget install lycheeverse.lychee | brew install lychee

The install-hint field is informational prose displayed to the user when lychee is absent from PATH. It is not auto-executed by the skill — the skill reports skip with the hint and stops. The | here is a display separator matching the format used by the three existing entries (typos, gitleaks, editorconfig-checker), not a shell pipe. Package identifiers (lycheeverse.lychee / lychee) are consistent with lychee's upstream README. No package is pulled automatically.

Severity: N/A — Confidence: High

Path traversal / plugin isolation

The glob ./**/*.md anchors to the consumer's working directory and traverses downward only — no upward traversal is possible from this pattern. No ../ references appear in any of the 5 changed files. No ${CLAUDE_PLUGIN_ROOT} violations. Fully within plugin-form bounds.

Severity: N/A — Confidence: High

Secrets / PII

None introduced. No credentials, tokens, API keys, or sensitive environment variables are referenced in any changed file.

GitHub Actions / workflow changes

The 4 PR-specific commits touch no .github/workflows/ files. The 5 changed files are entirely confined to the plugins/toolchain/ subtree.

plugin.json, evals.json, SKILL.md, CHANGELOG.md

Purely metadata and documentation — no code execution paths in any of these files. The changes are: a version field bump (0.8.00.9.0), a Keep-a-Changelog prose entry, a single expected_output string update in evals, a one-line backtick-consistency fix in SKILL.md, and a blank-line markdownlint compliance restore in CHANGELOG.md. No unsafe patterns.


@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: 32d0ddcfc3

ℹ️ 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/cross-cutting.yaml
@kyle-sexton
kyle-sexton merged commit 5f026f3 into main Jul 22, 2026
28 checks passed
@kyle-sexton
kyle-sexton deleted the feat/833-toolchain-lychee-offline branch July 22, 2026 14:06
kyle-sexton added a commit that referenced this pull request Jul 25, 2026
#926) (#1020)

## Summary

`docs/conventions/ecosystem-commands/ecosystem.schema.json` defines a
structured `gates` array (`name`/`cmd`/`trigger-globs`/`remediation`)
that any ecosystem config — bundled default or consumer-tracked — can
declare, and both `plugins/toolchain/reference/ecosystems/go.yaml`
(`go-mod-tidy-drift`) and
`docs/conventions/ecosystem-commands/examples/dotnet.yaml`
(`nuget-lockfile-drift`) ship one. The array was resolved as part of
each ecosystem's command surface (`check/SKILL.md` §1.5) but **never
invoked by any workflow step** — so every declared gate was inert. The
only gate-execution mechanism `check/SKILL.md` documented was a prose,
consumer-CLAUDE.md-only step whose closing sentence ("this plugin ships
none of its own") was itself stale once `go.yaml` shipped a bundled
gate.

## Fix

Scoped to `plugins/toolchain/skills/check/SKILL.md`, precise and
additive (no restructuring of unrelated parts):

- **New structured gate-execution step** in §2 "Run checks", replacing
the stale prose step at the old `:121`. After an affected ecosystem's
build → test → lint, iterate its resolved `gates` array (§1.5 ladder —
bundled default or consumer file). Per gate:
- **Fire condition** — `trigger-globs` present → run only when ≥1
changed file matches, against the **full** changed-files set (a gate's
trigger files need not classify into the ecosystem's own `globs`);
omitted → run whenever the ecosystem runs; present-but-no-match (e.g.
`check all` on a clean tree) → does not fire.
- **Independent of the build/test/lint short-circuit** — a fired gate
runs even when an earlier phase failed and stopped (a lockfile / `go mod
tidy` gate is meaningful regardless of build success).
- **Tool presence** — missing tool → `skip` with the ecosystem's
`install-hint`, never `FAIL`.
- **Outcome** — `pass`/`FAIL` by name; on `FAIL` surface
`gate.remediation`; a fired-and-failed gate counts toward the run's FAIL
verdict.
- **Overall verdict** now covers gate-only failures — a failed fired
gate flips Overall to `FAIL` and is counted even when every ecosystem's
build/test/lint cell passed (e.g. `Overall: FAIL (0 of 2 ecosystems
failed, 1 gate failed)`).
- **Stale sentence removed** — the "this plugin ships none of its own"
framing is replaced with an accurate ladder pointer (bundled default
e.g. `go.yaml`'s `go-mod-tidy-drift`; consumer-declared in
`.claude/ecosystems/<ecosystem>.yaml`).
- **Cross-reference reconciled** at the old `:119` ("CI-parity gates …
run independent of `check-cmd`") and the report scaffold (`:140`).

**`lint/SKILL.md` deliberately unchanged** (the one maintainer-vetoable
fork, per the planning comment): every schema-described gate and both
shipped examples are CI-parity checks beyond build/test/lint (lockfile
drift, module drift, generated-artifact freshness) — none is a
lint/format op. They belong with the heavier `check` step and
`/verification:confirm` (the sanctioned pre-commit gate); adding them to
`/toolchain:lint` would break its "fast" contract. Veto before merge if
a maintainer wants gates on the lint path.

**Version bump (rule 6d):** fresh `origin/main` had `plugins/toolchain`
at `0.9.0` (claimed by the lychee PR #867); this is a new executable
behavior, so bumped to `0.10.0` with a matching `## [0.10.0]` → `###
Added` CHANGELOG entry. (The planning comment's `0.8.0 → 0.9.0` predated
#867 landing on main.)

## Verification

No skill-behavior test harness exists (`find plugins/toolchain -iname
'*test*'` and a repo-level search for `toolchain:check`/`gates` tests
both empty), so verification is static gate + hand-trace:

- **`/skill-quality:check` on the edited `check` skill:** PASS — 0
errors, markdownlint clean, 165/500 lines, all base-ref trigger phrases
preserved (frontmatter `description` untouched). The lone WARN
(description lacks `Use when:` phrasing) is pre-existing and out of
scope.
- **Hand-trace — bundled default (`go.yaml`, a real rung-4 default that
runs for this repo):** a change set including `go.sum` (or any `*.go`)
makes `go` affected; §1.5 resolves `go.yaml`; the new §2 step reaches
its `gates` and matches `go-mod-tidy-drift`'s `trigger-globs:
["go.mod","go.sum","*.go"]` against the full changed-files set → gate
fires → runs `go mod tidy -diff` from `$REPO_ROOT`. Drift → `FAIL` +
remediation + Overall FAIL; clean → `pass`; missing Go toolchain →
`skip` with `install-hint`.
- **Hand-trace — consumer-declared shape (`examples/dotnet.yaml`, an
example fixture under `docs/`, not a bundled default):** for a consumer
whose tracked `.claude/ecosystems/dotnet.yaml` carries the
`nuget-lockfile-drift` gate, a changed `*.csproj` matches
`trigger-globs:
["*.csproj","Directory.Packages.props","packages.lock.json"]` → gate
fires → runs `dotnet restore --locked-mode`. Same pass/FAIL/skip
semantics.

Both gate entries are invoked by the new step given a matching
changed-files set; neither required a per-ecosystem change.

Closes #926

## Related

- PR #910 (Go ecosystem) — **merged** `2026-07-22`; added `go.yaml`'s
`go-mod-tidy-drift` gate whose Codex review surfaced this wiring gap.
This change is the cross-cutting follow-up that makes that gate (and
every other declared gate) actually run — additive, no `go.yaml` change.
- Planning-subagent comment on #926 (`2026-07-22T03:10:48Z`) — the
primary implementation plan followed here; the only deviation is the
version target (`0.10.0`, re-derived from fresh `origin/main` per rule
6d, since `0.9.0` was taken by #867 after the plan was written).

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

https://claude.ai/code/session_019xSPACJvbsqGsjYCP91DoX

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

---------

Co-authored-by: Claude Sonnet 5 <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 lychee-offline to cross-cutting ecosystem default

1 participant