Regenerate the skill manifest so the new micro and deployments-cicd validate rules use the canonical escaping emitted by build:manifest.
The prior shared-index commit captured non-canonical escaping in generated/skill-manifest.json.
Verified: bun run build:manifest
Verified: git diff --cached -- generated/skill-manifest.json shows only the 2 regex escaping normalizations
Swarm-Agent: codex-rules-wave2b
Add focused fixture tests for each current upgradeToSkill validate rule.
Each case asserts the positive trigger and the skipIfFileContains path so future SKILL.md changes do not silently break upgrade guidance.
Verified: bun test tests/upgrade-rules.test.ts
How to test: bun test tests/upgrade-rules.test.ts
Swarm-Agent: codex-test-fixtures
Add top-level validate blocks to react-best-practices, auth, and routing-middleware so the manifest can recommend SWR, Sign in with Vercel, and Vercel Firewall/WAF when legacy patterns are detected.
Verified: bun run build:manifest
Verified: git diff -- generated/skill-manifest.json shows only these three skills gained validate entries
Swarm-Agent: codex-rules-wave2a
Add three Next.js validate rules that recommend upgrading legacy auth, Pages Router API routes, and in-memory caches to dedicated Vercel skills.
Regenerate the static skill manifest so the new upgrade metadata is available at runtime; this also syncs a few pre-existing source-of-truth manifest entries that were stale in the generated file.
Verified: bun run build:manifest (isolated clean checkout based on HEAD + skills/nextjs/SKILL.md)
Verified: git diff --cached --check
Swarm-Agent: codex-commit-nextjs
Instead of injecting full SKILL.md bodies as additionalContext, hooks now
inject "You must run the Skill(<name>) tool." — leveraging the conventional
Skill tool mechanism for context loading.
Instead of injecting the full SKILL.md body as additionalContext, inject
"You must run the Skill(<name>) tool." — a more conventional way of
telling the agent to use the Skill tool for context loading.
- Add stemmer and shared contractions modules for lexical prompt matching
- Enhance lexical index and prompt patterns with stemming support
- Add promptSignals metadata to all 43 skill frontmatter files
- Add comprehensive documentation site (docs/)
- Add .claude-plugin marketplace and plugin metadata
- Add benchmark scenarios script
- Update skill manifest with prompt signal data
- Add lexical-index and stemmer tests, expand prompt-patterns tests
Eval analysis of 9 real sessions showed 10 skills never triggering despite being
requested. Root causes: pathPatterns too narrow (agents write to lib/email-template.tsx
not emails/), promptSignals containing regex instead of plain text (vercel-sandbox),
and missing promptSignals entirely (v0-dev, vercel-firewall).
Skills updated: email, vercel-queues, edge-runtime, vercel-firewall, chat-sdk,
v0-dev, vercel-sandbox. New skill: next-forge (bootstrap detection).
- SubagentStart bootstrap hook injects project context (likely skills, summaries) into spawned subagents
with budget scaling by agent type (minimal for Explore/Plan, standard for general-purpose)
- SubagentStop sync hook writes agent metadata to a session-scoped JSONL ledger for observability
- SessionEnd cleanup extended to remove subagent ledger files
- Updated ai-elements/nextjs skills, benchmark-agents and eval skill definitions
Skill frontmatter (pathPatterns, bashPatterns, promptSignals, etc.)
was being injected alongside the skill body, wasting token budget on
metadata only useful for hook matching. Now uses extractFrontmatter()
to emit only the markdown body.
The PreToolUse hook restores path regexes from generated/skill-manifest.json when a v2 manifest is present, so the brace-expansion fix also needs regenerated pathRegexSources. This refresh updates the affected extension-list patterns from literal brace matches to alternations.
Verified: bun run build:manifest
Verified: bun test tests/pretooluse-skill-inject.test.ts -t "matches src/middleware\.(mjs|mts) to routing-middleware skill"
Swarm-Agent: codex-brace-expand
New ai-generation-persistence skill (priority 6) injects guidance for treating
AI generations as first-class persistent resources — unique IDs, addressable
URLs, database/Blob storage, cost tracking, and generate-then-redirect UX
patterns. Triggers on AI SDK imports and broad prompt signals.
New verification skill added. Updated ecosystem graph, catalog, manifest,
fixtures, and snapshots.
Refine SKILL.md content across 29 skills based on live eval findings from 16
benchmark sessions. Add .claude/skills/vercel-plugin-eval/ for running real-world
plugin eval sessions with debug log monitoring. Update posttooluse-validate and
validate-rules tests to match refined skill content.
Parallel PreToolUse hooks were racing on the same seen-skills file, causing 2-6x
duplicate injections. UserPromptSubmit hook didn't share dedup state at all.
Fix by implementing atomic per-skill claim files (Oracle session: fix-dedup-race-condition-plan):
1. In hooks/src/hook-env.mts, add tryClaimSessionKey(sessionId, kind, key) using
openSync(path, 'wx') for atomic exclusive create. Add listSessionKeys() to read
claim dir, syncSessionFileFromClaims() to derive the txt snapshot, and
removeSessionClaimDir() for cleanup. Claim files live in
<tmpdir>/vercel-plugin-<sessionId>-seen-skills.d/<skill-name>.
2. In hooks/src/patterns.mts, add mergeSeenSkillStates(...values) to union env var,
session file, and claim dir state. Add serializeSeenSkills(set) helper.
3. In hooks/src/pretooluse-skill-inject.mts, load merged state from env+file+claims.
Before injecting each skill, call tryClaimSessionKey — first process wins, others
get EEXIST and skip. Use same claim path for once-only keys (agent-browser warning,
vercel-env-help). Pass sessionId through InjectOptions.
4. In hooks/src/user-prompt-submit-skill-inject.mts, preserve sessionId from parsed
input, load merged seen state, pass sessionId to injection flow so it uses the
shared claim backend. Skills injected via promptSignals now visible to PreToolUse.
5. In hooks/src/session-end-cleanup.mts, add rmSync deletion of claim directories.
6. Update CLAUDE.md dedup contract to document atomic claim mechanism.
7. Bump .plugin/plugin.json to 0.6.0, rebuild.
The 0.5.4 release added session-scoped temp files for dedup persistence, but this
was wrong — CLAUDE_ENV_FILE env vars ARE propagated to hook subprocesses by Claude
Code. The temp file approach littered tmpdir with hundreds of small files.
Revert to the original env-var-only mechanism:
1. Remove readSessionFile/writeSessionFile/dedupFilePath from hooks/src/hook-env.mts
2. Remove all writeSessionFile calls from pretooluse-skill-inject.mts — revert to
pure process.env.VERCEL_PLUGIN_SEEN_SKILLS = appendSeenSkill(...) pattern
3. Remove sessionId from InjectOptions and the injectSkills call site
4. Remove sessionId from posttooluse-validate.mts ParsedInput — revert
isAlreadyValidated/markValidated to pure env-var signatures
5. Revert all test expectations back to "env-var" and "memory-only" strategies
6. Restore CLAUDE.md dedup contract to env-var-only documentation
Skill dedup is completely broken because CLAUDE_ENV_FILE vars are only available to Bash
tool commands, not hook subprocesses. Each PreToolUse invocation starts a fresh Node.js
process that never sees VERCEL_PLUGIN_SEEN_SKILLS.
To fix this:
1. In hooks/src/hook-env.mts, add readSessionFile(sessionId, kind) and
writeSessionFile(sessionId, kind, value) helpers that read/write
/tmp/vercel-plugin-<sessionId>-<kind>.txt for persistent dedup state.
2. In hooks/src/pretooluse-skill-inject.mts, read seen skills from the session file
at startup using readSessionFile(sessionId, "seen-skills"). Add a persistSeen()
closure inside injectSkills() that calls both appendSeenSkill and writeSessionFile.
Replace all process.env.VERCEL_PLUGIN_SEEN_SKILLS = appendSeenSkill(...) calls with
persistSeen() or inline writeSessionFile calls. Add "file" as the primary dedup
strategy when session_id is present, falling back to "env-var" then "memory-only".
Pass sessionId through InjectOptions to injectSkills().
3. In hooks/src/posttooluse-validate.mts, add sessionId to ParsedInput and extract it
from input.session_id in parseInput(). Pass sessionId to isAlreadyValidated() and
markValidated(), which should read/write via readSessionFile/writeSessionFile for
the "validated-files" kind.
4. Update tests: pretooluse-skill-inject.test.ts — rename "no env var" test to expect
file-based dedup persistence, update strategy assertions from "env-var" to "file",
use omitSessionId for memory-only test. subagent-fresh-env.test.ts — use a fresh
session ID for subagent calls, update strategy expectations to "file".
session-timeline-subagent.test.ts — add sessionId option to runHookEnv, use a
different session ID for the subagent call.
5. Update CLAUDE.md dedup contract section to document file-based mechanism.
6. Bump .plugin/plugin.json version to 0.5.4, run bun run build.