MANDATORY: Act as principal-level engineer. Follow these guidelines exactly.
- Identify users by git credentials; use their actual name, never "the user"
- Use "you/your" when speaking directly; use names when referencing contributions
This repo may have multiple Claude sessions running concurrently against the same checkout, against parallel git worktrees, or against sibling clones. Several common git operations are hostile to that and silently destroy or hijack the other session's work.
-
FORBIDDEN in the primary checkout (the one another Claude may be editing):
git stash— shared stash store; another session canpopyours.git add -A/git add .— sweeps files belonging to other sessions.git checkout <branch>/git switch <branch>— yanks the working tree out from under another session.git reset --hardagainst a non-HEAD ref — discards another session's commits.
-
REQUIRED for branch work: spawn a worktree instead of switching branches in place. Each worktree has its own HEAD, so branch operations inside it are safe.
# From the primary checkout — does NOT touch the working tree here. git worktree add -b <task-branch> ../<repo>-<task> main cd ../<repo>-<task> # edit, commit, push from here; the primary checkout is untouched. cd - git worktree remove ../<repo>-<task>
-
REQUIRED for staging: surgical
git add <specific-file> [<file>…]with explicit paths. Never-A/.. -
If you need a quick WIP save: commit on a new branch from inside a worktree, not a stash.
The umbrella rule: never run a git command that mutates state belonging to a path other than the file you just edited.
MANDATORY: Review CLAUDE.md before any action. No exceptions.
- Before ANY structural refactor on a file >300 LOC: remove dead code first, commit separately
- Multi-file changes: phases of ≤5 files, verify each before the next
- Study existing code before building — working code is a better spec than any description
- Work from raw error data, not theories
- On "yes", "do it", or "go": execute immediately, no plan recap
- Run the actual command — execute, don't assume
- State what you verified, not just "looks good"
- FORBIDDEN: Claiming "Done" when tests show failures
- Run type-check/lint if configured; fix ALL errors before reporting done
- Re-read every modified file; confirm nothing references removed items
- Fix warnings when you find them (lint, type-check, build, runtime) — don't leave them for later
- After 10+ messages: re-read files before editing
- Read files >500 LOC in chunks
- Before every edit: re-read. After every edit: re-read to confirm
- When renaming: search direct calls, type refs, string literals, dynamic imports, re-exports, tests
- Tool results over 50K chars are silently truncated — narrow scope and re-run if results seem incomplete
- For tasks touching >5 files: use sub-agents with worktree isolation
- Flag misconceptions before executing
- Flag adjacent bugs: "I also noticed X — want me to fix it?"
- Default to perfectionist mindset: when you have latitude to choose, pick the maximally correct option — no shortcuts, no cosmetic deferrals. Fix state that looks stale even if not load-bearing. If pragmatism is the right call, the user will ask for it explicitly. "Works now" ≠ "right."
- Do not add features or improvements beyond what was asked
- Simplest approach first; flag architectural flaws and wait for approval
- Finish 100% before reporting — never claim done at 80%
- Fix forward, don't revert (reverting requires explicit user approval)
- After EVERY code change: build, test, verify, commit as one atomic unit
- Present two views before calling done: what a perfectionist would reject vs. what a pragmatist would ship — and let the user decide. If the user gives no signal, default to perfectionist: do the fuller fix.
- If a fix fails twice: stop, re-read top-down, state where the mental model was wrong
- After ANY correction: log the pattern to memory so the same mistake is never repeated
- Convert mistakes into strict rules — enforce them
- Write intermediate results, plans, and status to files in
.claude/(gitignored) - Don't hold large analysis in context — write it down, reference it later
- Offer to checkpoint before risky changes
- Flag files >400 LOC for potential splitting
- Never create files unless necessary; always prefer editing existing files
- Forbidden to create docs unless requested
- 🚨 NEVER use
npx,pnpm dlx, oryarn dlx— usepnpm execorpnpm run - minimumReleaseAge: NEVER add packages to
minimumReleaseAgeExcludein CI. Locally, ASK before adding — the age threshold is a security control.
Allowed: README.md, CLAUDE.md, SECURITY.md, CHANGELOG.md, docs/ (max 11 files), .claude/ (functional only), */README.md for complex subsystems only.
Forbidden: Migration/planning docs after completion, redundant guides, docs duplicating code comments, tutorial content, ADRs unless requested. After migrations or major refactors, DELETE planning documents.
- Commits: Conventional Commits
<type>(<scope>): <description>— NO AI attribution - Scripts: Prefer
pnpm run foo --flagoverfoo:barscripts - Dependencies: After
package.jsonedits, runpnpm install - Backward Compatibility: 🚨 FORBIDDEN to maintain — actively remove when encountered
- Work Safeguards: MANDATORY commit + backup branch before bulk changes
- Safe Deletion: Use
safeDelete()from@socketsecurity/lib/fs(NEVERfs.rm/rmSyncorrm -rf) - HTTP Requests: NEVER use
fetch()— usehttpJson/httpText/httpRequestfrom@socketsecurity/lib/http-request - File existence: ALWAYS
existsSyncfromnode:fs. NEVERfs.access,fs.stat-for-existence, or an asyncfileExistswrapper. Import form:import { existsSync, promises as fs } from 'node:fs'. Promise.race/Promise.any: NEVER pass a long-lived promise (interrupt signal, pool member) into a race inside a loop. Each call re-attaches.thenhandlers to every arm; handlers accumulate on surviving promises until they settle. For concurrency limiters, use a single-waiter "slot available" signal (resolved by each task's.then) instead of re-racingexecuting[]. See nodejs/node#17469 and@watchable/unpromise. Race with two fresh arms (e.g. one-shotwithTimeout) is safe.
Terminal symbols (from @socketsecurity/lib/logger LOG_SYMBOLS): ✓ (green), ✗ (red), ⚠ (yellow), ℹ (blue), → (cyan). Color the icon only, not the message. Use yoctocolors-cjs (not ESM yoctocolors). Avoid emoji overload.
Core infrastructure library for Socket.dev security tools.
- Internal imports: Relative paths (e.g.,
'../constants/packages'). Path aliases are intentionally avoided. - Vendored externals:
cacache,make-fetch-happen,fast-sort,pacote,adm-zip,tar-fs,picomatchlive insrc/external/and are remapped viatsconfig.jsonpaths. Import them by bare package name.
- Build:
pnpm build| Watch:pnpm run dev - Test:
pnpm test| Coverage:pnpm run cover - Check:
pnpm run check(tsgo type check) - Lint:
pnpm run lint(oxlint) | Fix:pnpm run fix(oxfmt) - Clean:
pnpm run clean
/security-scan— AgentShield + zizmor security audit/quality-scan— comprehensive code quality analysis/quality-loop— scan and fix iteratively- Agents:
code-reviewer,security-reviewer,refactor-cleaner(in.claude/agents/) - Shared subskills in
.claude/skills/_shared/ - Pipeline state in
.claude/ops/queue.yaml
- Linting: oxlint v1.52+ with
.oxlintrc.json. Inline disable:// oxlint-disable-next-line rule-name - Formatting: oxfmt v0.37+ with
.oxfmtrc.json(Prettier v3.8 compatible, semi, single quotes, 2-space, 80 width, trailing commas)
- TypeScript → CommonJS (ES2022) via esbuild; types via tsgo (TypeScript Native Preview)
- Output:
dist/ - Build scripts: all in
scripts/as.mjs. Shell scripts (.sh) FORBIDDEN. - Main build (
pnpm build): clean → build source + types + externals in parallel → fix exports
- Files:
.tssource,.d.tstypes, kebab-case, MANDATORY@fileoverviewheader - Imports: MANDATORY
node:prefix; order: node built-ins → external →@socketsecurity/*→ internal relative → type imports (separate). Blank lines between groups, alphabetical within. - Semicolons: OMIT
- Types: FORBIDDEN
any— useunknownor specific types. Always separateimport typefrom runtime imports. - Null-prototype objects:
{ __proto__: null, ...props } - Exports: Named only.
export defaultFORBIDDEN (breaks dual CJS/ESM). Enforced by oxlintno-default-export+ build + CI validation. - Function order: Files with 3+ exports require alphabetical ordering — private first (alphabetical), then exported (alphabetical). Constants/types before functions.
All modules exported via package.json exports field. When adding modules, update exports or run scripts/generate-package-exports.mjs.
Framework: Vitest (shared config from socket-registry, main config: .config/vitest.config.mts)
- Test files in
test/, naming matches source - 🚨 NEVER use
--before test paths — runs all tests - NEVER write source-code-scanning tests — verify behavior with real function calls
Custom pipeline in .github/workflows/ci.yml: separate lint job (runs once), build caching (build once, artifacts shared), parallel execution, matrix tests Node 20/22/24 x Ubuntu/Windows. Single ci-success job for branch protection.
Access via typed getter functions in src/env/. Each module exports a pure getter. Test rewiring via src/env/rewire.ts (setEnv, clearEnv, resetEnv) without modifying process.env.
🚨 NEVER use process.chdir() — pass { cwd } options and absolute paths.