Skip to content

Commit 77c14d4

Browse files
committed
chore(sync): cascade fleet skills + helpers from socket-repo-template
Pulls in the latest fleet-canonical skill changes from socket-repo-template via `node scripts/sync-scaffolding.mts --fix`. Highlights of what's new from upstream: - _shared/ taxonomy: prose modules + scripts/ for shared TS helpers. - New shared docs: variant-analysis.md, compound-lessons.md, skill-authoring.md. - New shared TS: scripts/git-default-branch.mts (main → master fallback). - worktree-management/ skill (new / pr-fanout / prune modes). - scanning-quality/scans/ split: variant-analysis, insecure-defaults, differential. - guarding-paths restructured: reference.md (prose) + templates/ (.tmpl scaffolding) instead of mixed reference/. - Skill orchestrator/reference split across updating, updating-lockstep, driving-cursor-bugbot, refreshing-history, guarding-paths. - refreshing-history/run.sh → run.mts (cross-platform via @socketsecurity/lib/spawn). - CLAUDE.md fleet block: new rules for variant analysis, compound lessons, plan review, default-branch fallback, .mts-over-.sh runners.
1 parent 072ce66 commit 77c14d4

21 files changed

Lines changed: 2063 additions & 596 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Compound lessons
2+
3+
How a fleet skill or review turns a finding into a durable rule, instead of fixing it once and forgetting.
4+
5+
## The principle
6+
7+
Each unit of engineering work should make subsequent units **easier**, not harder. A bug fix that doesn't update the rule that allowed the bug is a half-finished job: the next change in the same area will hit the same class of bug, and the cycle repeats.
8+
9+
Three places a lesson can land in this fleet:
10+
11+
| Where | When | Effect |
12+
|---|---|---|
13+
| **CLAUDE.md fleet rule** | The mistake recurs across repos or is a fleet-wide invariant | Every fleet repo inherits the rule on next sync |
14+
| **`.claude/hooks/*` block** | The mistake is mechanical and can be detected from tool input/output | Hook blocks the next attempt before the file is written |
15+
| **Skill prompt update** | The mistake is judgment-shaped (review pass missed a class of finding) | Future runs of that skill catch the variant |
16+
17+
## When to compound
18+
19+
Compound a lesson **only** when one of these is true:
20+
21+
1. **Recurrence** — the same kind of bug has now appeared 2+ times. Write down the rule that would have caught both.
22+
2. **High blast radius** — the bug shipped, broke a downstream user, or required a revert. The rule prevents the next shipping incident.
23+
3. **Drift signal** — fleet repos disagreed on the answer. The rule reconciles which answer wins.
24+
25+
Don't compound for one-off fixes that won't recur. Don't write a "lesson" doc when the lesson is just "we fixed it." The fleet rule **is** the lesson; if you can't crystallize it into a rule, the lesson isn't ready.
26+
27+
## How to compound
28+
29+
1. **Name the rule** — one sentence, imperative voice. "Never X." "Always Y."
30+
2. **Cite the incident** — one-line `**Why:**` line referencing the commit, PR, or finding. Don't write a paragraph.
31+
3. **State the application** — one-line `**How to apply:**` line saying when the rule fires.
32+
4. **Land it where it'll fire** — CLAUDE.md, hook, or skill prompt. Pick the lowest-friction surface that catches the next occurrence.
33+
34+
Skip the retrospective doc. Skip the post-mortem template. The rule is the artifact.
35+
36+
## Anti-patterns
37+
38+
- **The "lessons learned" graveyard** — a `docs/lessons/` folder where dated markdown files rot. Don't. The rule belongs in the live config that fires on the next run.
39+
- **Vague rules** — "be careful with X." Useless. If you can't write the rule as a `rg` pattern or a CLAUDE.md `🚨` line, it isn't a rule yet.
40+
- **Rules without why** — future readers can't judge edge cases without the original incident. Always cite.
41+
42+
## Source
43+
44+
Borrowed from Every Inc.'s _Compound Engineering_ playbook (https://every.to/chain-of-thought/compound-engineering-how-every-codes-with-agents). Their `/ce-compound` slash command is the verb form of this principle; we encode the same discipline as a fleet convention rather than a slash command.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Default-branch resolution for fleet skill runners.
3+
*
4+
* Per CLAUDE.md "Default branch fallback" rule: prefer main, fall back
5+
* to master. Never hard-code one or the other — fleet repos are mostly
6+
* on main, but a few legacy / vendored repos still use master, and a
7+
* script that hard-codes main silently no-ops on those.
8+
*
9+
* Cross-platform: shells out to git via @socketsecurity/lib/spawn, which
10+
* works the same on macOS / Linux / Windows.
11+
*/
12+
import { isSpawnError, spawn } from '@socketsecurity/lib/spawn'
13+
14+
export type ResolveDefaultBranchOptions = {
15+
/** Working directory; defaults to process.cwd(). */
16+
readonly cwd?: string | undefined
17+
/** Remote name; defaults to 'origin'. */
18+
readonly remote?: string | undefined
19+
}
20+
21+
/**
22+
* Resolve the remote's default branch, preferring `main` and falling back
23+
* to `master`. Returns `'main'` as a final fallback when the remote has
24+
* neither branch (e.g., fresh clone before `git fetch`).
25+
*
26+
* Resolution order:
27+
* 1. `git symbolic-ref refs/remotes/<remote>/HEAD` — most reliable.
28+
* 2. Probe `refs/remotes/<remote>/main` — true on the vast majority of fleet repos.
29+
* 3. Probe `refs/remotes/<remote>/master` — legacy / vendored repos.
30+
* 4. Assume `main` and let the next git command fail loudly.
31+
*/
32+
export async function resolveDefaultBranch(
33+
options: ResolveDefaultBranchOptions = {},
34+
): Promise<string> {
35+
const { cwd = process.cwd(), remote = 'origin' } = options
36+
37+
// Step 1: ask the remote what its HEAD points to.
38+
try {
39+
const ref = await runGit(
40+
['symbolic-ref', '--quiet', '--short', `refs/remotes/${remote}/HEAD`],
41+
cwd,
42+
)
43+
if (ref) {
44+
// Strip the "<remote>/" prefix.
45+
return ref.startsWith(`${remote}/`) ? ref.slice(remote.length + 1) : ref
46+
}
47+
} catch {
48+
// Fall through.
49+
}
50+
51+
// Step 2 + 3: probe main, then master.
52+
for (const branch of ['main', 'master']) {
53+
if (await branchExists(branch, cwd, remote)) {
54+
return branch
55+
}
56+
}
57+
58+
// Step 4: last resort.
59+
return 'main'
60+
}
61+
62+
async function branchExists(
63+
branch: string,
64+
cwd: string,
65+
remote: string,
66+
): Promise<boolean> {
67+
try {
68+
await runGit(
69+
['show-ref', '--verify', '--quiet', `refs/remotes/${remote}/${branch}`],
70+
cwd,
71+
)
72+
return true
73+
} catch {
74+
return false
75+
}
76+
}
77+
78+
async function runGit(args: readonly string[], cwd: string): Promise<string> {
79+
try {
80+
const result = await spawn('git', args, { cwd, stdioString: true })
81+
return String(result.stdout ?? '').trim()
82+
} catch (e) {
83+
if (isSpawnError(e)) {
84+
throw e
85+
}
86+
throw e
87+
}
88+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Skill authoring patterns
2+
3+
Conventions every fleet skill follows. Reference from new-skill scaffolds and from auditor agents.
4+
5+
## Modular structure
6+
7+
A skill's `SKILL.md` is the **orchestrator**, not the encyclopedia. When a skill grows past ~300 lines or covers more than one phase / tool / domain, push the depth into siblings:
8+
9+
```
10+
.claude/skills/
11+
├── _shared/
12+
│ ├── <topic>.md # shared prose loaded on demand by multiple skills
13+
│ └── scripts/
14+
│ └── <helper>.mts # shared TS helpers used by per-skill run.mts files
15+
└── my-skill/
16+
├── SKILL.md # ≤ 300 lines, table of contents + decision flow
17+
├── reference.md # long-form prose Claude reads (single file, growable to a dir)
18+
├── scans/<type>.md # one file per scan type / phase / tool (when many)
19+
├── templates/ # file scaffolding copied verbatim by install/setup modes
20+
│ └── <name>.tmpl
21+
└── run.mts # skill-specific executable runner
22+
```
23+
24+
Two naming conventions are load-bearing:
25+
26+
- **`lib/` vs `scripts/`** matches the fleet's public-vs-private convention. `lib/` names a public, importable, stable surface (think `@socketsecurity/lib`); `scripts/` names private, internal automation that's not consumed outside the host repo. Skill helpers under `_shared/scripts/` are internal automation — no external consumers — so `scripts/` is the right name. (No `_shared/lib/` exists in this tree.)
27+
- **`reference.md` vs `reference/`** — single file by default; grow to a directory only when a skill genuinely has multiple distinct reference docs. Don't preemptively wrap a single doc in a dir.
28+
- **`templates/`** is reserved for file scaffolding (`.tmpl` files copied verbatim by `install` / `setup` modes). Don't mix templates into `reference/` — readers can't tell prose from scaffolding by directory name alone.
29+
30+
The same File-size rule from CLAUDE.md applies — soft cap 500, hard cap 1000 — but for skills the trigger is usually **shape**, not lines: as soon as the SKILL.md is "this and also that and also the other thing," extract.
31+
32+
What goes where:
33+
34+
| Path | Purpose |
35+
|---|---|
36+
| `<skill>/SKILL.md` | Orchestrator: when to use, modes, phase list, links to deeper files. Reads top-to-bottom in one screen. |
37+
| `<skill>/reference.md` | Long-form depth: bash blocks, full validation rules, sample outputs, recovery procedures. Loaded by the orchestrator when a phase needs it. |
38+
| `<skill>/scans/`, `phases/`, `tools/` | One file per discrete unit when the skill enumerates many (e.g., `scanning-quality/scans/<type>.md`). Adding a new unit = one new file, no SKILL.md touch. |
39+
| `<skill>/templates/<name>.tmpl` | File scaffolding (`.tmpl` files copied verbatim by `install` / `setup` modes — gate scripts, allowlist starters, etc.). Distinct from `reference.md` which is prose, not scaffolding. |
40+
| `<skill>/run.mts` | Skill-specific executable runner. Inline prompts so prompts and code can't drift. Per CLAUDE.md _Tooling — Runners are `.mts`, not `.sh`_. |
41+
| `_shared/<topic>.md` | Shared **prose** (variant-analysis discipline, compound-lessons workflow, multi-agent backends). Cross-skill load surface. |
42+
| `_shared/scripts/<helper>.mts` | Shared **TypeScript** helpers imported by per-skill `run.mts` (default-branch resolution, report formatting, spawn wrappers). Internal automation — not a public library, hence `scripts/` not `lib/`. Use `@socketsecurity/lib/spawn` for subprocesses, never raw `node:child_process`. |
43+
44+
## Auditor agents
45+
46+
Skills that author other artifacts (skills, hooks, slash commands, subagents) should ship an auditor sibling. The pattern:
47+
48+
1. The authoring skill emits a draft.
49+
2. An auditor agent (separate prompt, narrower tool surface) reviews against a checklist.
50+
3. The authoring skill applies the auditor's feedback before shipping.
51+
52+
Three audit dimensions per artifact:
53+
54+
| Artifact | Auditor checks |
55+
|---|---|
56+
| Skill | frontmatter complete, when-to-use unambiguous, tool surface minimal, no buried opinions |
57+
| Hook | matcher tight, command exits fast, doesn't depend on session state, can't deadlock |
58+
| Slash command | argument shape clear, idempotent, doesn't touch shared state without confirmation |
59+
| Subagent | prompt self-contained (no "based on the conversation"), tool surface matches the task, return shape documented |
60+
61+
A fleet skill that does this well is the canonical reference; the auditor is a `Task` agent spawned by the authoring skill, not a long-running daemon.
62+
63+
## Compound-lessons capture
64+
65+
When a fleet skill discovers a recurring failure mode — a lint rule that catches the same kind of bug, a hook that blocks the same antipattern, a review pass that flags the same regression — codify it once:
66+
67+
1. Open a follow-up to add the rule to CLAUDE.md, the hook, or the skill prompt.
68+
2. Reference the original incident (commit, PR, finding ID) in a one-line `**Why:**` so future readers know the rule is load-bearing.
69+
3. Resist the urge to write a full retrospective doc — the fleet rule **is** the retrospective.
70+
71+
This is the fleet's equivalent of a post-mortem: every recurring bug becomes a rule, every rule earns its place by closing a class of bugs. The principle is _compound engineering_: each unit of work makes the next unit easier.
72+
73+
## When to NOT extract
74+
75+
- One-off skill (≤ 100 lines, single phase, single tool) — keep it monolithic.
76+
- Code unique to one repo that can't be shared — keep it in that repo's `unique` skill.
77+
- Prompt that's tightly coupled to its caller — inline, don't split.
78+
79+
The principle: **a reader should be able to predict what's in a skill from its name, and find what they need without scrolling past three other concerns.** Same as the File-size rule, applied to skills.
80+
81+
## Frontmatter requirements (from upstream)
82+
83+
The Anthropic docs codify several rules; honor them:
84+
85+
- `name`: ≤ 64 chars, lowercase letters / numbers / hyphens only. No `anthropic` / `claude` substring.
86+
- `description`: ≤ 1024 chars, third-person voice (`"Manages X"`, not `"I help with X"` or `"You can use this to X"`). Include both **what** and **when to use**.
87+
- Prefer **gerund form** for the name (`processing-pdfs`, `scanning-quality`); noun-phrase (`pdf-processing`) and verb-imperative (`process-pdfs`) are acceptable alternatives, but pick one and be consistent across the fleet.
88+
- Use forward slashes in any path the skill references — never backslashes, even in docs that target Windows users.
89+
90+
## References
91+
92+
Authoritative upstream docs — keep these as the source of truth, mirror their guidance here only when fleet specifics demand it:
93+
94+
- [Anthropic — Skill authoring best practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices) — frontmatter rules, progressive disclosure, evaluation-driven development.
95+
- [Anthropic — Claude Code best practices: writing an effective CLAUDE.md](https://code.claude.com/docs/en/best-practices#write-an-effective-claude-md) — CLAUDE.md scope, pruning discipline, when to push knowledge into a skill instead.
96+
- [Anthropic — Prompt engineering best practices](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices) — model-tuning, response-length calibration, examples-over-descriptions.
97+
98+
Real-world plugin reference (not fleet-canonical, useful as a worked example of skills + hooks + templates working together): [`arscontexta`](https://github.com/agenticnotetaking/arscontexta) — knowledge-system plugin that derives skills/hooks/templates from a conversational setup. Useful as a study of the "skills compose into a system" pattern.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Variant analysis
2+
3+
When a finding lands — a bug, a regression, a security issue — the next question is always: **does this same shape exist anywhere else in the repo?** Variant analysis is the systematic answer.
4+
5+
## Why this exists
6+
7+
A bug is rarely unique. The mental model that produced it usually produced siblings. The reviewer who didn't catch it once usually missed the rest. Treating each finding as one-off leaks variants into production.
8+
9+
This file is referenced by `scanning-quality` (variant-analysis scan type), `scanning-security`, and `reviewing-code`.
10+
11+
## The pattern
12+
13+
For every confirmed finding, run three searches before closing it out:
14+
15+
1. **Same file, different lines** — the antipattern often clusters within the file that exhibits it. Read the whole file, not just the diff.
16+
2. **Sibling files, same shape**`rg`/`grep` for the same call, the same condition, the same data flow. If the bug was `if (foo == null)`, search for that exact shape.
17+
3. **Cross-package, same concept** — does another package own a parallel implementation? If `socket-cli` has the bug, does `socket-registry` have it too? Fleet drift loves to hide variants.
18+
19+
## What counts as "the same shape"
20+
21+
| Bug class | What to search for |
22+
|---|---|
23+
| Missing null check | the call before the access — `foo.bar()` where `foo` could be undefined |
24+
| Race condition | the lock primitive + the call sequence |
25+
| Path construction | literal `path.join('build', …)` outside the canonical `paths.mts` |
26+
| Insecure default | the option name, the boolean default, the env-var fallback |
27+
| Token leak | the field name (`token`, `api_key`, …), the log statement, the error message |
28+
| Promise.race leak | `Promise.race(`, `Promise.any(` inside a `for`/`while` |
29+
| Forbidden API | `fetch(`, `fs.rm(`, `fs.access(`, raw `npx` / `pnpm dlx` |
30+
31+
## Outputs
32+
33+
For each variant found, emit:
34+
35+
```
36+
- file:line — variant of <original-finding-id>
37+
Pattern: <one-line shape>
38+
Severity: <propagate from original, or LOWER if context differs>
39+
Fix: <reference original fix, or note where it diverges>
40+
```
41+
42+
Variants should be batched into the same fix commit when mechanical (one find/replace), or filed as sibling commits on the same branch when each needs review.
43+
44+
## Don't
45+
46+
- Don't variant-hunt for style nits. Reserve this for correctness / security / fleet-drift findings.
47+
- Don't expand the search radius past one repo without writing it down — cross-fleet variants get a `chore(sync): cascade <fix>` PR per the _Drift watch_ rule.
48+
- Don't skip the search because the finding "looks unique." Looking unique is exactly when the search pays off.
49+
50+
## Trail-of-Bits influence
51+
52+
This pattern is borrowed from Trail of Bits' `variant-analysis` plugin (https://github.com/trailofbits/skills) and adapted to the fleet's drift-watch discipline. Their version is Semgrep-rule-driven for security; ours is `rg`-driven for general correctness. Same idea, lighter machinery.

0 commit comments

Comments
 (0)