Skip to content

deployments-cicd injects Vercel guidance for any .github/workflows/*.yml, in repos with no Vercel surface #198

Description

@ericlovold

Summary

Writing an ordinary GitHub Actions workflow in a repository with no Vercel surface at all injects the "MANDATORY: Your training data for these libraries is OUTDATED and UNRELIABLE" block plus Vercel deployment guidance.

The cause here is not the workflow skill's globs. It is deployments-cicd's own pathPatterns, which list the generic CI files that nearly every repository has:

# skills/deployments-cicd/SKILL.md
pathPatterns:
  - '.github/workflows/*.yml'
  - '.github/workflows/*.yaml'
  - '.gitlab-ci.yml'
  - 'bitbucket-pipelines.yml'
  - 'vercel.json'
  - 'apps/*/vercel.json'

.github/workflows/*.yml matching implies nothing about Vercel. A repository can have GitHub Actions and no relationship to Vercel whatsoever, which is the common case.

This is distinct from the two existing reports, and survives whatever is done about them:

Both concern the workflow skill. deployments-cicd names these paths explicitly in its own frontmatter, so fixing the workflow skill leaves this firing.

Repro

The repository under test is a private operations repo: launchd job definitions, six .plist files, a handful of Python scripts, two shell scripts. No package.json, no lockfile, no JavaScript, no vercel.json, no .vercel/. I added a CI workflow to it and got Vercel deployment docs.

Running the hook directly, with a fresh session_id per case, because the per-session dedupe will otherwise make a second run look like a fix:

echo '{"session_id":"fresh-1","cwd":"/tmp/probe","hook_event_name":"PreToolUse",
 "tool_name":"Write","tool_input":{"file_path":".github/workflows/ci.yml",
 "content":"name: CI\non: [pull_request]\njobs: {test: {runs-on: ubuntu-latest}}"}}' \
| CLAUDE_PLUGIN_ROOT=<plugin> node hooks/pretooluse-skill-inject.mjs

Actual: two skills injected.

- "workflow" matched suffix pattern `workflows/**` on Write: .github/workflows/ci.yml
- "deployments-cicd" matched full pattern `.github/workflows/*.yml` on Write: .github/workflows/ci.yml

Expected: nothing. There is no Vercel surface in the repository or in the file.

Why it matters

The injected text is imperative: "MANDATORY", "DO NOT guess, assume, or rely on memorized APIs". In a repo where Vercel is irrelevant, that instructs an agent to go read documentation for a platform the project does not use, spending context and inviting irrelevant suggestions. A maintainer's reasonable response is to disable the plugin.

Proposed fix

The plugin already has the right precedent: resolveVercelJsonSkills (hooks/src/vercel-config.mts) refines a vercel.json match by reading the file's keys rather than trusting the path. Generic CI files deserve the same treatment, since unlike vercel.json the path carries no signal at all.

The match is currently unconditional (hooks/src/pretooluse-skill-inject.mts, around line 448 on main):

const reason = matchPathWithReason(filePath, entry.compiledPaths);

Requiring a Vercel signal in the file being written, for generic CI paths only:

const GENERIC_CI_PATH =
  /(^|\/)\.github\/workflows\/|(^|\/)\.gitlab-ci\.ya?ml$|(^|\/)bitbucket-pipelines\.ya?ml$/i;
const VERCEL_SIGNAL = /vercel|turborepo|\bnext(\.js| build|-app)/i;

function vercelSignalPresent(filePath: string, fileContent: string): boolean {
  if (VERCEL_SIGNAL.test(filePath)) return true;
  if (fileContent && VERCEL_SIGNAL.test(fileContent)) return true;
  if (!fileContent) {
    try {
      return VERCEL_SIGNAL.test(readFileSync(filePath, "utf8"));
    } catch {
      return false;
    }
  }
  return false;
}

let reason = matchPathWithReason(filePath, entry.compiledPaths);
if (reason && GENERIC_CI_PATH.test(filePath) && !vercelSignalPresent(filePath, fileContent)) {
  reason = null;
}

fileContent is already assembled a few lines above from content / old_string / new_string, and readFileSync is already imported, so this needs no new plumbing. The disk read only happens on Read, where the tool input carries no content.

I applied exactly this locally and tested four cases, each with a fresh session:

Case Before After
.github/workflows/ci.yml, no Vercel anywhere 2 skills injected nothing
.github/workflows/deploy.yml running vercel deploy --prod injected still injected
src/workflows/order.ts (Workflow DevKit) injected still injected
vercel.json injected still injected

The false positive disappears and I could not find a true positive it costs. A repo-level gate (a vercel.json, a .vercel/ directory, or a Vercel dependency anywhere in the tree) would be stronger still, and would also address the class #144 describes, but the file-level check is the smaller change.

Environment

Plugin 0.25.1 installed; patterns and the unguarded match verified unchanged on main at 0.50.0
Node v22.22.2
OS macOS 26.1, arm64

I reproduced on the installed 0.25.1. I have not run 0.50.0, so I checked the source instead: skills/deployments-cicd/SKILL.md still carries the four generic CI patterns, and pretooluse-skill-inject.mts still matches on path alone.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions