Skip to content

Commit 3dd0a41

Browse files
committed
chore(wheelhouse): cascade template@5a97706a
Auto-applied by socket-wheelhouse sync-scaffolding into cascade-socket-bin-2036. 46 file(s) touched: - .claude/skills/fleet/_shared/compound-lessons.md - .claude/skills/fleet/_shared/env-check.md - .claude/skills/fleet/_shared/multi-agent-backends.md - .claude/skills/fleet/_shared/path-guard-rule.md - .claude/skills/fleet/_shared/report-format.md - .claude/skills/fleet/_shared/scripts/git-default-branch.mts - .claude/skills/fleet/_shared/scripts/logger-guardrails.mts - .claude/skills/fleet/_shared/scripts/resolve-tools.mts - .claude/skills/fleet/_shared/security-tools.md - .claude/skills/fleet/_shared/skill-authoring.md - .claude/skills/fleet/_shared/variant-analysis.md - .claude/skills/fleet/_shared/verify-build.md - .claude/skills/fleet/agent-ci/SKILL.md - .claude/skills/fleet/agent-ci/reference.md - .claude/skills/fleet/auditing-gha-settings/SKILL.md - .claude/skills/fleet/auditing-gha-settings/run.mts - .claude/skills/fleet/cascading-fleet/SKILL.md - .claude/skills/fleet/cascading-fleet/lib/cascade-template.mts - .claude/skills/fleet/cascading-fleet/lib/fleet-repos.json - .claude/skills/fleet/cascading-fleet/lib/fleet-repos.txt ... and 26 more
1 parent b223065 commit 3dd0a41

46 files changed

Lines changed: 7134 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
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: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Environment Check
2+
3+
Shared prerequisite validation for all pipelines. Run at the start of every skill.
4+
5+
## Steps
6+
7+
1. Run `git status` to check working directory state
8+
2. Detect CI mode: check for `GITHUB_ACTIONS` or `CI` environment variables
9+
3. Verify `node_modules/` exists (run `pnpm install` if missing)
10+
4. Verify on a valid branch (`git branch --show-current`)
11+
12+
## Behavior
13+
14+
- **Clean working directory**: proceed normally
15+
- **Dirty working directory**: warn and continue (most skills are read-only or create their own commits)
16+
- **CI mode**: set `CI_MODE=true` — skills should skip interactive prompts and local-only validation
17+
- **Missing node_modules**: run `pnpm install` before proceeding
18+
19+
## Queue Tracking
20+
21+
Write a run entry to `.claude/ops/queue.yaml` with:
22+
23+
- `id`: `{pipeline}-{YYYY-MM-DD}-{NNN}`
24+
- `pipeline`: the invoking skill name
25+
- `status`: `in-progress`
26+
- `started`: current UTC timestamp
27+
- `current_phase`: `env-check`
28+
- `completed_phases`: `[]`
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Multi-agent backends
2+
3+
Shared policy for skills that delegate work to multiple AI CLIs (codex, claude, opencode, kimi, …). Any skill that calls out to another agent should follow this contract so the user gets a uniform experience across skills.
4+
5+
> Looking for _when_ to hand work off to another agent (CLI subprocess vs. mid-conversation `Agent(subagent_type=…)`)? See [`docs/references/agent-delegation.md`](../../../docs/references/agent-delegation.md). This file covers the _how_ for the CLI-subprocess path.
6+
7+
## Goals
8+
9+
- **Graceful detection.** Skills don't hard-fail when a preferred backend isn't installed. They fall back through a documented preference order, then skip the pass with a recorded note if nothing usable is available.
10+
- **Consistent attribution.** When a backend produces output, the skill labels the section / report / commit message with the backend name (`Codex Verification`, not just `Verification`) so the reader knows which model said what.
11+
- **No silent provider routing.** Hybrid backends like `opencode` (which dispatch to other providers internally per their own config) are only used when **explicitly** selected, never auto-picked. Direct backends (codex, claude, kimi) are preferred so model attribution stays accurate.
12+
13+
## Backend registry
14+
15+
| Backend | CLI binary | Hybrid? | Default role preference |
16+
| -------- | ---------- | ------- | ---------------------------------------------------- |
17+
| codex | `codex` | no | discovery, discovery-secondary, remediation |
18+
| claude | `claude` | no | verify |
19+
| kimi | `kimi` | no | any role (fallback) |
20+
| opencode | `opencode` | **yes** | only when `--pass <role>=opencode` explicitly chosen |
21+
22+
Adding a new backend = one entry in the registry: `{ name, bin, hybrid, run(promptFile, outFile) -> { argv, outMode } }`. No other call site changes.
23+
24+
## Detection policy
25+
26+
Detect availability via `command -v <bin>` at runtime, never hardcode "claude is always there." A skill that wants Codex but only has Kimi should run on Kimi (fallback), not bail out.
27+
28+
```
29+
For each role:
30+
preferred = explicit override (--pass role=backend) or first match in role.preferenceOrder
31+
if preferred is hybrid AND not explicitly selected -> skip preferred, try next
32+
if preferred is installed -> use it
33+
if no backend installed for this role -> skip the pass with a note in the output
34+
```
35+
36+
Document skips inline in whatever output the skill produces (`> Skipped pass: <role> — no available backend`) so the reader sees the gap.
37+
38+
## Env-var conventions
39+
40+
| Var | Default | Purpose |
41+
| ----------------- | ------------- | ---------------------------------------------- |
42+
| `CODEX_MODEL` | `gpt-5.4` | Codex model when codex is the active backend |
43+
| `CODEX_REASONING` | `xhigh` | Codex reasoning effort |
44+
| `CLAUDE_MODEL` | `opus` | Claude model when claude is the active backend |
45+
| `KIMI_MODEL` | `kimi-latest` | Kimi model when kimi is the active backend |
46+
47+
Don't invent per-skill env var names — reuse these. Skills that need a non-default model for a specific run accept a `--model` flag rather than introducing new env vars.
48+
49+
## Canonical implementation
50+
51+
`.claude/skills/reviewing-code/run.mts` is the reference implementation. New skills that need multi-agent delegation should import the same registry shape and detection function (or copy the small block until extraction is worth doing) — don't roll a parallel pattern.
52+
53+
## When NOT to use
54+
55+
- Skills that only need _one_ agent (the current Claude session driving the user). No detection needed; just do the work.
56+
- Skills that need a specific model unconditionally (e.g. a benchmark that compares two models — those use direct API calls, not the CLI registry).
57+
- Per-repo fix scripts that rely on a single tool (`pnpm`, `git`, `cargo`). Tooling, not agents.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!--
2+
Shared snippet — the canonical "1 path, 1 reference" rule text.
3+
Synced byte-identical across the Socket fleet via socket-wheelhouse's
4+
sync-scaffolding.mts (SHARED_SKILL_FILES).
5+
6+
This file is the source of truth for the rule's wording. Three artifacts
7+
embed (or paraphrase) it:
8+
9+
1. CLAUDE.md — every Socket repo's instructions to Claude.
10+
2. .claude/hooks/fleet/path-guard/README.md — what the hook blocks.
11+
3. .claude/skills/guarding-paths/SKILL.md — what the skill enforces.
12+
13+
If the wording changes here, re-run `node scripts/sync-scaffolding.mts
14+
--all --fix` from socket-wheelhouse to propagate.
15+
-->
16+
17+
## 1 path, 1 reference
18+
19+
**A path is _constructed_ exactly once. Everywhere else _references_ the constructed value.**
20+
21+
Referencing a single computed path many times is fine — that's the whole point of computing it once. What's banned is _re-constructing_ the same path in multiple places, because that's where drift is born. Three concrete shapes:
22+
23+
1. **Within a package** — every script, test, and lib file that needs a build path imports it from the package's `scripts/paths.mts` (or `lib/paths.mts`). No `path.join('build', mode, ...)` outside that module.
24+
25+
2. **Across packages** — when package B consumes package A's output, B imports A's `paths.mts` via the workspace `exports` field. Never `path.join(PKG, '..', '<sibling>', 'build', ...)`. The R28 yoga/ink bug — ink hand-building yoga's wasm path and missing the `wasm/` segment — is the canonical failure mode this rule prevents.
26+
27+
3. **Workflows, Dockerfiles, shell scripts** — they can't `import` TS, so they construct the string once and reference it everywhere downstream. Workflows: a "Compute paths" step exposes `steps.paths.outputs.final_dir`; later steps read `${{ steps.paths.outputs.final_dir }}`. Dockerfiles/shell: assign once to a variable, reference by name thereafter. Each canonical construction carries a comment naming the source-of-truth `paths.mts` so the YAML can't drift from TS without a flagged change. **Re-building** the same path in a second step is the violation, not referring to the constructed value many times.
28+
29+
Comments that re-state a full path are forbidden. The import statement IS the comment. Docs and READMEs may describe the structure ("output goes under the Final dir") but should not encode a complete `build/<mode>/<platform-arch>/out/Final/binary` string — encoded paths get parsed by tools and silently rot.
30+
31+
Code execution takes priority over docs: violations in `.mts`/`.cts`, Makefiles, Dockerfiles, workflow YAML, and shell scripts are blocking. README and doc-comment violations are advisory unless they contain a fully-qualified path with no parametric placeholders.
32+
33+
### Three-level enforcement
34+
35+
- **Hook**`.claude/hooks/fleet/path-guard/` blocks `Edit`/`Write` calls that would introduce a violation in a `.mts`/`.cts` file. Refusal at edit time stops new duplication from landing.
36+
- **Gate**`scripts/check-paths.mts` runs in `pnpm check`. Fails the build on any violation that isn't allowlisted.
37+
- **Skill**`/guarding-paths` audits the repo and fixes findings; `/guarding-paths check` reports only; `/guarding-paths install` drops the gate + hook + rule into a fresh repo.
38+
39+
The mantra is intentionally short so it sticks: **1 path, 1 reference**. When in doubt, find the canonical owner and import from it.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Report Format
2+
3+
Shared output format for all scan and review pipelines.
4+
5+
## Finding Format
6+
7+
Each finding:
8+
9+
```
10+
- **[SEVERITY]** file:line — description
11+
Fix: how to fix it
12+
```
13+
14+
Severity levels: CRITICAL, HIGH, MEDIUM, LOW
15+
16+
## Grade Calculation
17+
18+
Based on finding severity distribution:
19+
20+
- **A** (90-100): 0 critical, 0 high
21+
- **B** (80-89): 0 critical, 1-3 high
22+
- **C** (70-79): 0 critical, 4+ high OR 1 critical
23+
- **D** (60-69): 2-3 critical
24+
- **F** (< 60): 4+ critical
25+
26+
## Pipeline HANDOFF
27+
28+
When a skill completes as part of a larger pipeline (e.g., scanning-quality within release),
29+
output a structured handoff block:
30+
31+
```
32+
=== HANDOFF: {skill-name} ===
33+
Status: {pass|fail}
34+
Grade: {A-F}
35+
Findings: {critical: N, high: N, medium: N, low: N}
36+
Summary: {one-line description}
37+
=== END HANDOFF ===
38+
```
39+
40+
The parent pipeline reads this to decide whether to proceed (gate check) or abort.
41+
42+
## Queue Completion
43+
44+
When the final phase completes, update `.claude/ops/queue.yaml`:
45+
46+
- `status`: `done` (or `failed`)
47+
- `completed`: current UTC timestamp
48+
- `current_phase`: `~` (null)
49+
- `completed_phases`: full list
50+
- `findings_count`: `{critical: N, high: N, medium: N, low: N}`
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* Default-branch resolution for fleet skill runners.
3+
*
4+
* Per CLAUDE.md "Default branch fallback" rule: prefer main, fall back to
5+
* master. Never hard-code one or the other — fleet repos are mostly on main,
6+
* but a few legacy / vendored repos still use master, and a script that
7+
* hard-codes main silently no-ops on those.
8+
*
9+
* Cross-platform: shells out to git via @socketsecurity/lib/spawn, which works
10+
* the same on macOS / Linux / Windows.
11+
*/
12+
import { isSpawnError } from '@socketsecurity/lib/process/spawn/errors'
13+
import { spawn } from '@socketsecurity/lib/process/spawn/child'
14+
15+
export type ResolveDefaultBranchOptions = {
16+
/**
17+
* Working directory; defaults to process.cwd().
18+
*/
19+
readonly cwd?: string | undefined
20+
/**
21+
* Remote name; defaults to 'origin'.
22+
*/
23+
readonly remote?: string | undefined
24+
}
25+
26+
/**
27+
* Resolve the remote's default branch, preferring `main` and falling back to
28+
* `master`. Returns `'main'` as a final fallback when the remote has neither
29+
* branch (e.g., fresh clone before `git fetch`).
30+
*
31+
* Resolution order:
32+
*
33+
* 1. `git symbolic-ref refs/remotes/<remote>/HEAD` — most reliable.
34+
* 2. Probe `refs/remotes/<remote>/main` — true on the vast majority of fleet
35+
* repos.
36+
* 3. Probe `refs/remotes/<remote>/master` — legacy / vendored repos.
37+
* 4. Assume `main` and let the next git command fail loudly.
38+
*/
39+
export async function resolveDefaultBranch(
40+
options: ResolveDefaultBranchOptions = {},
41+
): Promise<string> {
42+
const { cwd = process.cwd(), remote = 'origin' } = options
43+
44+
// Step 1: ask the remote what its HEAD points to.
45+
try {
46+
const ref = await runGit(
47+
['symbolic-ref', '--quiet', '--short', `refs/remotes/${remote}/HEAD`],
48+
cwd,
49+
)
50+
if (ref) {
51+
// Strip the "<remote>/" prefix.
52+
return ref.startsWith(`${remote}/`) ? ref.slice(remote.length + 1) : ref
53+
}
54+
} catch {
55+
// Fall through.
56+
}
57+
58+
// Step 2 + 3: probe main, then master.
59+
for (const branch of ['main', 'master']) {
60+
if (await branchExists(branch, cwd, remote)) {
61+
return branch
62+
}
63+
}
64+
65+
// Step 4: last resort.
66+
return 'main'
67+
}
68+
69+
async function branchExists(
70+
branch: string,
71+
cwd: string,
72+
remote: string,
73+
): Promise<boolean> {
74+
try {
75+
await runGit(
76+
['show-ref', '--verify', '--quiet', `refs/remotes/${remote}/${branch}`],
77+
cwd,
78+
)
79+
return true
80+
} catch {
81+
return false
82+
}
83+
}
84+
85+
async function runGit(args: readonly string[], cwd: string): Promise<string> {
86+
try {
87+
const result = await spawn('git', args, { cwd, stdioString: true })
88+
return String(result.stdout ?? '').trim()
89+
} catch (e) {
90+
if (isSpawnError(e)) {
91+
throw e
92+
}
93+
throw e
94+
}
95+
}

0 commit comments

Comments
 (0)