Skip to content
Skillv1.0.0

helmet

Full repo onboarding — bootstraps test infrastructure (Phase A), wires the CI/CD pipeline (Phase B), and generates a project CLAUDE.md (Phase C). Use when onboarding a new repo, setting up tests + CI

by aibot88(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from aibot88/sec_skill_store (skills/claudskills/helmet/SKILL.md). Install upstream with npx skills add aibot88/sec_skill_store --skill helmet. Copyright stays with the author.

Repo Pipeline Setup

Three-phase repo onboarding: Phase A bootstraps test infrastructure (language detection, framework detection, test runner + coverage, smoke tests, gold-standard templates). Phase B wires the CI/CD pipeline (Codecov, SHA pinning, SBOM, vulnerability scanning, security backstop, Dependabot, commit signing, OpenSSF Scorecard, CodeScene, GitGuardian). Phase C generates a project CLAUDE.md by analyzing the repo's tech stack, structure, commands, conventions, and CI configuration — so every new Claude Code session starts with full project context.

When to Use

Phase A (Test Infrastructure):

  • Onboarding an existing repo that has application code but no test suite
  • Starting a new project and want test infrastructure from the start
  • CI audit found missing test infrastructure (e.g., Codecov marked N/A)

Phase B (CI/CD Pipeline):

  • Onboarding a new repo into the CI pipeline
  • Adding or fixing Codecov, pinact, or GitGuardian for existing repos
  • Deploying pipeline changes across multiple repos at once
  • Fixing cross-platform CI failures (lightningcss, npm ci, vitest coverage, Swift iOS-only)
  • Auditing CI pipeline completeness across the portfolio
  • Adding SBOM generation or build provenance attestations
  • Setting up SSH commit signing or troubleshooting signature issues
  • Configuring Dependabot security alerts or version updates
  • Deploying OpenSSF Scorecard or SECURITY.md
  • Setting up CodeScene behavioral code analysis on PRs
  • Adding security scanning CI backstop (Semgrep, Checkov, Zizmor) for defense-in-depth

Phase C (CLAUDE.md):

  • Onboarding a new repo — auto-runs after Phase B completes
  • Repo has no .claude/CLAUDE.md or it contains only boilerplate
  • User asks to generate or refresh a project's CLAUDE.md
  • Significant infrastructure changes (new test framework, CI additions) made CLAUDE.md stale

Phase A: Test Infrastructure

Bootstrap test infrastructure for repos with testable code but no tests. Detects language and framework, installs the test runner + coverage provider, creates config, and generates a smoke test + gold-standard template test.

When to Use

  • Onboarding an existing repo that has application code but no test suite
  • Starting a new project and want test infrastructure from the start
  • CI audit found missing test infrastructure (e.g., Codecov marked N/A)

A0. Precondition Check

Before running detection, verify the repo has testable application code.

A repo is "testable" when BOTH conditions are met:

  1. At least one language config file exists: package.json, go.mod, Cargo.toml, pyproject.toml, setup.py, requirements.txt, Package.swift, or a *.xcodeproj directory
  2. At least one non-test source file exists in that language (.ts, .tsx, .js, .jsx, .py, .go, .rs, .swift)

Exclude from source file count: node_modules/, vendor/, .git/, dist/, build/, generated files.

If NEITHER condition is met (no config file AND no source files), stop and report:

"This repo has no testable application code. Test infrastructure is not applicable. Consider shellcheck for shell scripts or JSON schema validation for config files."

A1. Detection

A1a. Language Detection

Detect languages in order of confidence. Check config files first (highest signal), then fall back to file extension counts.

Primary signal — config files:

Config File Language
package.json TypeScript/JavaScript
go.mod Go
Cargo.toml Rust
pyproject.toml, setup.py, requirements.txt Python
Package.swift, *.xcodeproj (directory, not file) Swift

Fallback — file extension count (when no config file found for a language):

Extensions Language
.ts, .tsx, .js, .jsx TypeScript/JavaScript
.go Go
.rs Rust
.py Python
.swift Swift

Mixed repos: Detect ALL languages present. Scope each language's setup to its root directory:

  • Find the nearest config file (package.json, go.mod, etc.) and treat that directory as the language root.
  • Example: package.json at repo root + go.mod in services/api/ -> run TS setup at root, Go setup scoped to services/api/.
  • Each language gets independent detection, installation, and output. They do not share test directories or configs.

A1b. Framework Detection

After detecting the language, inspect dependency declarations for framework-specific packages. The detected framework determines which test patterns the template test will demonstrate.

TypeScript/JavaScript (check dependencies + devDependencies in package.json):

Dependency Framework Template test approach
express Express supertest route tests
next Next.js Route handler tests, API route tests
hono Hono Hono test client
fastify Fastify app.inject() tests
None matched Generic Export/function-level unit tests

Python (check pyproject.toml [project.dependencies] or requirements.txt):

Dependency Framework Template test approach
fastapi FastAPI TestClient, dependency overrides
django Django TestCase, Client, model tests
flask Flask Test client, route tests
typer Typer (CLI) CliRunner, exit codes, output assertions
click Click (CLI) CliRunner, exit codes, output assertions
None matched Generic Module/function-level tests

Go (check require block in go.mod):

Dependency Framework Template test approach
github.com/gin-gonic/gin Gin httptest + gin test context
github.com/go-chi/chi Chi httptest + chi router
net/http imports in .go source files (not in go.mod — stdlib packages don't appear there) Stdlib httptest handler tests
None matched Generic Table-driven function tests

Rust (check [dependencies] in Cargo.toml):

Dependency Framework Template test approach
actix-web Actix actix_web::test, TestRequest
axum Axum Tower service tests
None matched Generic #[cfg(test)] module tests

Swift (check Package.swift dependencies or project structure):

Signal Framework Template test approach
import Testing in source files (Xcode 16+ / Swift 6) Swift Testing @Test functions, #expect assertions (note: Phase 2/3 templates use XCTest as fallback until Swift Testing templates are added)
SwiftUI imports + *.xcodeproj dir SwiftUI app ViewInspector, @Observable state tests
Package.swift (library) Swift package XCTest module tests
Vapor in dependencies Vapor XCTVapor request tests

A1c. Existing Test Detection

Before installing, check if test infrastructure already exists for each detected language. Skip or fill gaps as needed.

Signals to check:

Signal Means
Test directories (__tests__/, tests/, test/, *_test.go files) Tests may exist
Test config files (vitest.config.*, jest.config.*, pytest.ini, pyproject.toml with [tool.pytest]) Test framework configured
Test scripts in package.json ("test", "test:coverage") or Makefile (test: target) Test runner registered
Coverage config (.coveragerc, .nycrc, codecov.yml) Coverage already set up

Decision rules:

Config exists Test dir exists Test script exists Action
Yes Yes Yes Skip -- fully set up
Yes No -- Create directory only, keep existing config
No Yes -- Create config only, keep existing directory
-- -- No (but config + dir exist) Add script only
No No No Full setup

Proceed automatically in all cases (no user prompt). Report what was created vs. what was skipped.

A2. Installation

Install the test framework and coverage provider for each detected language. If installation fails (network, permissions, version conflict), stop and report the error -- do not proceed to Phase 3.

Package Manager Detection (TypeScript/JavaScript)

Detect the package manager from the lock file. Fall back to npm.

Lock File Package Manager Install Command
bun.lockb or bun.lock bun bun add -D vitest @vitest/coverage-v8
pnpm-lock.yaml pnpm pnpm add -D vitest @vitest/coverage-v8
yarn.lock yarn yarn add -D vitest @vitest/coverage-v8
package-lock.json or none npm npm install -D vitest @vitest/coverage-v8

Per-Language Installation

TypeScript/JavaScript

  1. Install vitest + coverage provider via detected package manager
  2. Install framework-specific test helpers based on detected framework:
Framework Additional dev dependency
Express supertest
Hono (built-in test client, no extra dep)
Fastify (built-in app.inject(), no extra dep)
Next.js (no extra dep for route handler tests)
Generic (no extra dep)
  1. Create vitest.config.ts:
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'lcov'],  // lcov for Codecov compatibility
      exclude: ['node_modules/', 'dist/', '**/*.config.*'],
    },
  },
})
  1. Add to package.json scripts:
    • "test": "vitest run"
    • "test:coverage": "vitest run --coverage"
  2. Create __tests__/ directory

Python

  1. Determine installation method:
    • uv.lock present -> add pytest and pytest-cov to dev dependencies, run uv sync --dev or uv pip install -e ".[dev]"
    • pyproject.toml with PEP 621 [project] section -> add pytest and pytest-cov to [project.optional-dependencies] dev group, run pip install -e ".[dev]"
    • pyproject.toml with Poetry ([tool.poetry]), PDM, or other non-PEP-621 format -> fall back to requirements-dev.txt approach
    • No pyproject.toml -> create requirements-dev.txt with pytest and pytest-cov, run pip install -r requirements-dev.txt
  2. If $VIRTUAL_ENV is unset and no uv.lock, warn: "No virtual environment detected. Consider python -m venv .venv first." Proceed anyway.
  3. Add pytest config to pyproject.toml (create or append):
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--cov=<package> --cov-report=xml --cov-report=term"  # Replace <package> with actual package name (e.g., src, app)
  1. Create tests/ directory with __init__.py and conftest.py

Go

  1. No installation needed (testing is built-in)
  2. If Makefile exists, add targets:
test:
	go test ./...

test-coverage:
	go test -coverprofile=coverage.out ./... && go tool cover -html=coverage.out -o coverage.html
  1. No separate test directory -- Go test files go alongside source files (*_test.go)

Rust

  1. No test framework installation needed (built-in #[test])
  2. Attempt coverage tool install:
    cargo install cargo-llvm-cov
    If install fails, warn: "cargo-llvm-cov not installed. Tests will work but coverage reports require it. Install manually or use CI-only coverage." Continue with setup.
  3. Create tests/ directory for integration tests

Swift

  1. XCTest is built-in -- no installation needed
  2. For Package.swift projects: add test target if missing:
    .testTarget(name: "AppTests", dependencies: ["App"])
  3. For Xcode projects: verify test target exists, warn if missing (cannot auto-create Xcode test targets reliably)
  4. Create Tests/AppTests/ directory structure

A3. Output Files

Generate up to three test files per detected language:

  1. Smoke test (always) — proves the app can be imported.
  2. Gold-standard template test (always) — heavily commented pattern for example-based tests.
  3. Property test template (opt-in) — ask the user first: "Does this repo have parsers, validators, serializers, crypto, state machines, or financial logic?" If yes, generate; if no, skip and don't install the framework.

A. Smoke Test

Generate one real, runnable test that proves the app can be imported without crashing.

Scope: Import-only. Does NOT start servers, connect to databases, or trigger side effects. If the app performs side effects on import (e.g., mongoose.connect() at module level), the smoke test will fail -- report this with the suggestion: "Your app performs side effects on import. Consider wrapping startup logic in a function."

File placement:

Language Smoke test file
TypeScript/JS __tests__/smoke.test.ts
Python tests/test_smoke.py
Go smoke_test.go (in root package)
Rust tests/smoke.rs
Swift Tests/AppTests/SmokeTests.swift

Templates:

import { describe, it, expect } from 'vitest'

describe('smoke', () => {
  it('main module imports without error', async () => {
    const mod = await import('../src/index')
    expect(mod).toBeDefined()
  })
})

Adjust the import path (../src/index) to match the actual entry point found in package.json "main" or "exports" field.

def test_smoke():
    """Verify the main package can be imported."""
    import app  # noqa: F401

Adjust import app to match the actual package name (the top-level directory containing __init__.py, or the module name from pyproject.toml).

package main

import "testing"

func TestSmoke(t *testing.T) {
    // Verify the package compiles and main symbols are accessible.
    // If this test fails, the package has a build error.
    t.Log("smoke test: package compiles successfully")
}

Place in the root package directory. Adjust package main to match the actual package name if different.

#[test]
fn smoke() {
    // Verify the crate compiles and can be used as a dependency.
    // If this fails, there is a build error in the main crate.
    assert!(true, "crate compiles successfully");
}

Place as tests/smoke.rs (integration test). The crate name is auto-resolved from Cargo.toml.

import XCTest
@testable import App

final class SmokeTests: XCTestCase {
    func testSmoke() {
        // Verify the module can be imported without error.
        XCTAssertTrue(true, "Module imports successfully")
    }
}

Adjust @testable import App to match the actual module/target name from Package.swift or the Xcode project.

B. Gold-Standard Template Test

Generate one heavily commented test file showing the right patterns for the detected language+framework. Contains 2-3 real implemented tests (not TODOs) demonstrating:

  1. Happy path -- basic operation with expected input
  2. Error case -- how to test error handling
  3. Framework pattern -- one idiomatic framework-specific test (e.g., authenticated route, middleware)

Comments explain: import conventions, test structure, mocking approach, and where to find more patterns.

File placement:

Language Template test file Naming rationale
TypeScript/JS __tests__/_template.test.ts Underscore sorts first
Python tests/test_template.py Follows pytest test_ convention
Go template_test.go (root package) Matches template naming in other languages (example_test.go is reserved for godoc examples)
Rust tests/template.rs Integration test in tests/
Swift Tests/AppTests/TemplateTests.swift XCTest naming convention

Generate the template based on the detected framework. Use the framework detection from Phase 1b to select the right test patterns. The template must use the actual framework's test helpers (e.g., supertest for Express, TestClient for FastAPI, httptest for Go stdlib).

References to include in template comments:

  • busdriver:tdd -- for generating tests for specific modules
  • Language-specific testing skill -- busdriver:golang-testing, busdriver:python-testing, busdriver:rust-testing, etc.
/**
 * TEMPLATE TEST -- Copy this file as a starting point for new test files.
 *
 * Pattern: supertest + vitest for Express route testing.
 * Run: npm test
 * Coverage: npm run test:coverage
 *
 * For full TDD workflow, use `busdriver:tdd` to generate tests for specific modules.
 * For more patterns, see `busdriver:tdd`.
 */
import { describe, it, expect } from 'vitest'
import request from 'supertest'
import { app } from '../src/app'

describe('GET /health', () => {
  // Happy path: verify the endpoint returns expected shape
  it('returns 200 with status ok', async () => {
    const res = await request(app).get('/health')
    expect(res.status).toBe(200)
    expect(res.body).toEqual({ status: 'ok' })
  })

  // Error case: verify proper error response format
  it('returns 404 for unknown routes', async () => {
    const res = await request(app).get('/nonexistent')
    expect(res.status).toBe(404)
  })

  // Framework pattern: testing with auth header
  it('authenticated route returns 401 without token', async () => {
    const res = await request(app).get('/api/protected')
    expect(res.status).toBe(401)
  })
})
/**
 * TEMPLATE TEST -- Copy this file as a starting point for new test files.
 *
 * Pattern: vitest for unit testing exported functions.
 * Run: npm test
 * Coverage: npm run test:coverage
 *
 * For full TDD workflow, use `busdriver:tdd`.
 */
import { describe, it, expect } from 'vitest'
// import { yourFunction } from '../src/utils'

describe('yourFunction', () => {
  // Happy path
  it.todo('returns expected result for valid input')
  // it('returns expected result for valid input', () => {
  //   const result = yourFunction('valid')
  //   expect(result).toBe(expected)
  // })

  // Error case
  it.todo('throws on invalid input')
  // it('throws on invalid input', () => {
  //   expect(() => yourFunction(null)).toThrow()
  // })
})
"""
TEMPLATE TEST -- Copy this file as a starting point for new test files.

Pattern: pytest + TestClient for FastAPI endpoint testing.
Run: pytest
Coverage: pytest --cov

For full TDD workflow, use `busdriver:tdd`.
For more patterns, see `busdriver:python-testing`.
"""
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)


# Happy path: verify endpoint returns expected shape
def test_health_returns_ok():
    response = client.get("/health")
    assert response.status_code == 200
    assert response.json() == {"status": "ok"}


# Error case: verify proper error response
def test_unknown_route_returns_404():
    response = client.get("/nonexistent")
    assert response.status_code == 404


# Framework pattern: dependency override for testing
def test_with_dependency_override():
    """Example of overriding a FastAPI dependency for testing."""
    # from app.dependencies import get_db
    # def mock_db():
    #     return FakeDB()
    # app.dependency_overrides[get_db] = mock_db
    # response = client.get("/items")
    # app.dependency_overrides.clear()
    pass  # Replace with real test
"""
TEMPLATE TEST -- Copy this file as a starting point for new test files.

Pattern: pytest for unit testing functions and classes.
Run: pytest
Coverage: pytest --cov

For full TDD workflow, use `busdriver:tdd`.
For more patterns, see `busdriver:python-testing`.
"""
# from your_module import your_function


# Happy path: verify function returns expected result
def test_happy_path():
    # result = your_function("valid input")
    # assert result == expected
    pass  # Replace with real test


# Error case: verify error handling
def test_error_case():
    # with pytest.raises(ValueError):
    #     your_function(None)
    pass  # Replace with real test
"""
TEMPLATE TEST -- Copy this file as a starting point for new test files.

Pattern: pytest + Typer's CliRunner for CLI command testing.
Run: pytest
Coverage: pytest --cov

For full TDD workflow, use `busdriver:tdd`.
For more patterns, see `busdriver:python-testing`.
"""
from typer.testing import CliRunner
from app.main import app  # Adjust to your Typer app import

runner = CliRunner()


# Happy path: verify command runs and produces expected output
def test_command_succeeds():
    result = runner.invoke(app, ["--help"])
    assert result.exit_code == 0
    assert "Usage" in result.output


# Error case: verify proper error exit on bad input
def test_command_bad_input():
    result = runner.invoke(app, ["nonexistent-command"])
    assert result.exit_code != 0


# Framework pattern: test a subcommand with arguments
def test_subcommand_with_args(tmp_path):
    """Use tmp_path for any file I/O to keep tests isolated."""
    # result = runner.invoke(app, ["process", "--input", str(tmp_path / "data.csv")])
    # assert result.exit_code == 0
    # assert "Processed" in result.output
    pass  # Replace with real test
"""
TEMPLATE TEST -- Copy this file as a starting point for new test files.

Pattern: pytest + Click's CliRunner for CLI command testing.
Run: pytest
Coverage: pytest --cov

For full TDD workflow, use `busdriver:tdd`.
For more patterns, see `busdriver:python-testing`.
"""
from click.testing import CliRunner
from app.main import cli  # Adjust to your Click group/command import

runner = CliRunner()


# Happy path: verify command runs and produces expected output
def test_command_succeeds():
    result = runner.invoke(cli, ["--help"])
    assert result.exit_code == 0
    assert "Usage" in result.output


# Error case: verify proper error exit on bad input
def test_command_missing_required():
    result = runner.invoke(cli, ["process"])  # Missing required arg
    assert result.exit_code != 0
    assert "Error" in result.output or "Missing" in result.output


# Framework pattern: test with isolated filesystem
def test_command_with_files(tmp_path):
    """Use tmp_path for file I/O; use runner.isolated_filesystem() for CWD isolation."""
    # with runner.isolated_filesystem(temp_dir=tmp_path):
    #     result = runner.invoke(cli, ["init"])
    #     assert result.exit_code == 0
    pass  # Replace with real test
// Template test -- copy this file as a starting point for new test files.
//
// Pattern: table-driven tests with httptest for HTTP handler testing.
// Run: make test (or go test ./...)
// Coverage: make test-coverage
//
// For full TDD workflow, use `busdriver:tdd`.
// For more patterns, see `busdriver:golang-testing`.

package main

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

// Happy path: verify handler returns expected status.
func TestHealthHandler(t *testing.T) {
	req := httptest.NewRequest(http.MethodGet, "/health", nil)
	w := httptest.NewRecorder()
	healthHandler(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("expected 200, got %d", w.Code)
	}
}

// Error case: table-driven test pattern for multiple inputs.
func TestHealthHandler_EdgeCases(t *testing.T) {
	tests := []struct {
		name   string
		method string
		want   int
	}{
		{"GET returns 200", http.MethodGet, http.StatusOK},
		{"POST returns 405", http.MethodPost, http.StatusMethodNotAllowed},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			req := httptest.NewRequest(tt.method, "/health", nil)
			w := httptest.NewRecorder()
			healthHandler(w, req)
			if w.Code != tt.want {
				t.Errorf("expected %d, got %d", tt.want, w.Code)
			}
		})
	}
}
// Template test -- copy this file as a starting point for new test files.
//
// Pattern: table-driven tests for pure functions.
// Run: go test ./...
// Coverage: go test -coverprofile=coverage.out ./...
//
// For full TDD workflow, use `busdriver:tdd`.
// For more patterns, see `busdriver:golang-testing`.

package main

import "testing"

// Happy path: verify function returns expected result.
func TestYourFunction(t *testing.T) {
	// result := YourFunction("valid input")
	// if result != expected {
	//     t.Errorf("expected %v, got %v", expected, result)
	// }
	t.Log("Replace with real test")
}

// Error case: table-driven test pattern.
func TestYourFunction_EdgeCases(t *testing.T) {
	tests := []struct {
		name    string
		input   string
		wantErr bool
	}{
		{"valid input", "hello", false},
		{"empty input", "", true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// _, err := YourFunction(tt.input)
			// if (err != nil) != tt.wantErr {
			//     t.Errorf("wantErr=%v, got err=%v", tt.wantErr, err)
			// }
			t.Log("Replace with real test")
		})
	}
}
//! TEMPLATE TEST -- Copy this file as a starting point for new integration tests.
//!
//! Pattern: integration test in tests/ directory.
//! Run: cargo test
//! Coverage: cargo llvm-cov
//!
//! For full TDD workflow, use `busdriver:tdd`.
//! For more patterns, see `busdriver:rust-testing`.

// use your_crate::your_function;

// Happy path: verify function returns expected result
#[test]
fn test_happy_path() {
    // let result = your_function("valid input");
    // assert_eq!(result, expected);
}

// Error case: verify error handling
#[test]
fn test_error_case() {
    // let result = your_function("");
    // assert!(result.is_err());
}
/// TEMPLATE TEST -- Copy this file as a starting point for new test files.
///
/// Pattern: XCTest for unit testing.
/// Run: swift test (SPM) or xcodebuild test (Xcode)
///
/// For full TDD workflow, use `busdriver:tdd`.
import XCTest
@testable import App

final class TemplateTests: XCTestCase {

    // Happy path: verify function returns expected result
    func testHappyPath() throws {
        // let result = yourFunction("valid")
        // XCTAssertEqual(result, expected)
        throw XCTSkip("Template — replace with real test")
    }

    // Error case: verify error handling
    func testErrorCase() throws {
        // XCTAssertThrowsError(try yourFunction(nil))
        throw XCTSkip("Template — replace with real test")
    }
}

Adapt all import paths and function names to match the actual codebase. The template is a starting point -- the tests should compile and pass as-is, so the developer can immediately see the pattern and replace with real tests.

Placeholder tests: Avoid always-passing no-op assertions (assert True, expect(true).toBe(true), XCTAssertTrue(true)) in template tests -- they inflate pass counts and trigger automated reviewer warnings. Instead:

  • Python: Use pass for placeholder bodies
  • TypeScript/JS: Use it.todo('description') (vitest/jest mark them as pending, not passing)
  • Swift: Use throw XCTSkip("Template — replace with real test")
  • Go: t.Log(...) is fine (informational, not a false assertion)
  • Rust: Commented-out assertions are fine (no placeholder needed)

For tests that demonstrate a real pattern (e.g., importing the app, hitting a real endpoint), use actual assertions -- only use placeholders for commented-out examples the developer hasn't wired up yet.

C. Property-Based Test Template (Optional)

Property-based testing complements example-based testing by generating random inputs and checking that invariants hold across all of them. It catches edge cases that example tests miss (empty strings, unicode boundaries, integer overflow, concurrent-operation orderings).

When to use property tests:

Good fit Poor fit
Parsers / serializers — round-trip invariants (parse(serialize(x)) == x) CRUD endpoints / HTTP glue
Validators — rejected inputs stay rejected after canonicalization UI rendering logic
Crypto / hashing — output length, determinism, collision properties Database migrations
State machines — sequences of operations preserve invariants Configuration loaders
Financial calculators — commutative / associative / zero-sum properties Pure plumbing / pass-throughs
Sort / search / data structures — algorithmic invariants Pure presentation-layer code

If a repo has no modules matching "good fit," skip this template — adding property tests to CRUD handlers is noise, not signal.

Three gold-standard property patterns each template demonstrates:

  1. Invariant — a property that always holds (reverse(reverse(x)) == x, parse(serialize(x)) == x)
  2. Oracle — compare new implementation against a simple reference (my_sort(xs) == sorted(xs))
  3. Model/state-machine — a sequence of operations preserves a higher-level invariant (push then pop on stack returns same element)

File placement:

Language Property test file Framework Install
Python tests/test_properties_template.py Hypothesis pip install hypothesis
TypeScript/JS __tests__/properties.test.ts fast-check npm install -D fast-check
Go properties_test.go rapid (preferred — has shrinking) or testing/quick (stdlib, no shrinking) go get -t pgregory.net/rapid
Rust tests/properties.rs proptest cargo add --dev proptest
Swift Tests/AppTests/PropertyTests.swift Pragmatic mix — see Swift template notes See notes

Opt-in flow: During Phase A, after detecting language + modules, ask the user:

"Does this repo have parsers, validators, serializers, crypto, state machines, or financial logic? (y/n)"

If yes → generate property test template. If no → skip; add no framework dependency.

Default is no — don't install framework dependencies speculatively.

"""
PROPERTY TEST TEMPLATE -- Copy this file as a starting point.

When to use: parsers, serializers, validators, crypto, state machines,
financial calculators. NOT for CRUD, HTTP glue, or pure plumbing.

Pattern: Hypothesis generates random inputs; you assert invariants.
Run: pytest tests/test_properties_template.py
Install: pip install hypothesis

For more patterns, see https://hypothesis.readthedocs.io/en/latest/quickstart.html
"""
import pytest
from hypothesis import given, strategies as st

# from your_module import serialize, deserialize, validate, canonicalize


# 1. INVARIANT -- round-trip property: parse(serialize(x)) == x
@given(st.dictionaries(st.text(), st.integers()))
def test_serialize_roundtrip_is_identity(data):
    """Serializing then parsing should always return the original."""
    pytest.skip("Template — replace body with real serialize/deserialize round-trip")
    # assert deserialize(serialize(data)) == data


# 2. ORACLE -- compare implementation to a known-correct reference
@given(st.lists(st.integers()))
def test_custom_sort_matches_python_sorted(xs):
    """Your sort should match Python's built-in sorted()."""
    pytest.skip("Template — replace body with real my_sort vs sorted() comparison")
    # assert my_sort(list(xs)) == sorted(xs)


# 3. MODEL/STATE-MACHINE -- sequences of ops preserve an invariant
@given(st.lists(st.integers()))
def test_push_then_pop_returns_same_element(items):
    """Stack push/pop is a two-way mapping for each element."""
    pytest.skip("Template — replace body with real stack LIFO check")
    # stack = Stack()
    # for item in items:
    #     stack.push(item)
    #     assert stack.pop() == item


# Shrinking example -- Hypothesis will minimize a failing input.
# Uncomment to see: the test will "fail" with a minimal counterexample.
# @given(st.lists(st.integers()))
# def test_intentionally_fails_to_show_shrinking(xs):
#     assert sum(xs) < 1_000_000  # Shrinker finds [1_000_000] as minimal fail
/**
 * PROPERTY TEST TEMPLATE -- Copy this file as a starting point.
 *
 * When to use: parsers, serializers, validators, crypto, state machines,
 * financial calculators. NOT for CRUD, HTTP glue, or pure plumbing.
 *
 * Pattern: fast-check generates random inputs; you assert invariants.
 * Run: npm test -- properties
 * Install: npm install -D fast-check
 *
 * For more patterns, see https://fast-check.dev/docs/tutorials/quick-start/
 */
import { describe, it } from 'vitest'
// import fc from 'fast-check'
// import { serialize, deserialize, mySort, Stack } from '../src/module'

describe('property tests', () => {
  // 1. INVARIANT -- round-trip property
  it.todo('serialize + deserialize is identity')
  // it('serialize + deserialize is identity', () => {
  //   fc.assert(
  //     fc.property(fc.dictionary(fc.string(), fc.integer()), (data) => {
  //       expect(deserialize(serialize(data))).toEqual(data)
  //     })
  //   )
  // })

  // 2. ORACLE -- compare to a reference implementation
  it.todo('custom sort matches Array.prototype.sort')
  // it('custom sort matches Array.prototype.sort', () => {
  //   fc.assert(
  //     fc.property(fc.array(fc.integer()), (xs) => {
  //       const mine = mySort([...xs])
  //       const reference = [...xs].sort((a, b) => a - b)
  //       expect(mine).toEqual(reference)
  //     })
  //   )
  // })

  // 3. MODEL/STATE-MACHINE -- sequence of ops preserves invariant
  it.todo('stack push/pop preserves last-in-first-out')
  // it('stack push/pop preserves last-in-first-out', () => {
  //   fc.assert(
  //     fc.property(fc.array(fc.integer()), (items) => {
  //       const stack = new Stack<number>()
  //       for (const item of items) {
  //         stack.push(item)
  //         expect(stack.pop()).toBe(item)
  //       }
  //     })
  //   )
  // })

  // Shrinking example -- fast-check minimizes a failing input.
  // Uncomment to see: failure report will show the minimal counterexample.
  // it('intentionally fails to demonstrate shrinking', () => {
  //   fc.assert(
  //     fc.property(fc.array(fc.integer()), (xs) => {
  //       expect(xs.reduce((a, b) => a + b, 0)).toBeLessThan(1_000_000)
  //     })
  //   )
  // })
})

Recommended: pgregory.net/rapid. It provides shrinking (automatic minimization of failing inputs), stateful testing, and rich generators. testing/quick works but lacks shrinking — debugging failures on nested structs becomes painful.

// Package myapp_test contains property-based tests using pgregory.net/rapid.
//
// When to use: parsers, serializers, validators, crypto, state machines,
// financial calculators. NOT for CRUD, HTTP glue, or pure plumbing.
//
// Pattern: rapid generates random inputs and SHRINKS failures to minimal cases.
// Run: go test -run TestProperty ./...
// Install: go get -t pgregory.net/rapid
//
// For more patterns, see https://pkg.go.dev/pgregory.net/rapid
// Fallback (stdlib, no shrinking): use testing/quick — see commented section below.
package myapp_test

import (
	"testing"
	// Uncomment when you replace t.Skip() bodies with real property checks:
	// "sort"
	// "pgregory.net/rapid"
)

// 1. INVARIANT -- round-trip property
func TestPropertySerializeRoundtrip(t *testing.T) {
	t.Skip("template — replace body with real serialize/deserialize round-trip check")
	// rapid.Check(t, func(t *rapid.T) {
	//     data := rapid.MapOf(rapid.String(), rapid.Int()).Draw(t, "data")
	//     encoded := serialize(data)
	//     decoded := deserialize(encoded)
	//     if !reflect.DeepEqual(decoded, data) {
	//         t.Fatalf("roundtrip mismatch: got %v, want %v", decoded, data)
	//     }
	// })
}

// 2. ORACLE -- compare custom impl against sort.Ints
func TestPropertyCustomSortMatchesStdlib(t *testing.T) {
	t.Skip("template — replace body with real mySort vs sort.Ints comparison")
	// rapid.Check(t, func(t *rapid.T) {
	//     xs := rapid.SliceOf(rapid.Int()).Draw(t, "xs")
	//     mine := mySort(append([]int{}, xs...))
	//     reference := append([]int{}, xs...)
	//     sort.Ints(reference)
	//     if !slices.Equal(mine, reference) {
	//         t.Fatalf("sort mismatch: got %v, want %v", mine, reference)
	//     }
	// })
}

// 3. MODEL/STATE-MACHINE -- stack push/pop invariant
func TestPropertyStackPushPopLIFO(t *testing.T) {
	t.Skip("template — replace body with real stack LIFO invariant check")
	// rapid.Check(t, func(t *rapid.T) {
	//     items := rapid.SliceOf(rapid.Int()).Draw(t, "items")
	//     stack := NewStack[int]()
	//     for _, item := range items {
	//         stack.Push(item)
	//         if got := stack.Pop(); got != item {
	//             t.Fatalf("LIFO violated: push %d then pop %d", item, got)
	//         }
	//     }
	// })
}

// ── Stdlib fallback (no shrinking) ───────────────────────────────────────────
// If you cannot add rapid as a dependency, testing/quick from the stdlib works.
// Failure reports show raw generated inputs (no minimization), so debugging is
// harder — especially for maps and nested structs.
//
// import "testing/quick"
//
// func TestPropertyQuickSerializeRoundtrip(t *testing.T) {
//     f := func(data map[string]int) bool {
//         return reflect.DeepEqual(deserialize(serialize(data)), data)
//     }
//     if err := quick.Check(f, nil); err != nil {
//         t.Error(err)
//     }
// }
//! PROPERTY TEST TEMPLATE -- Copy this file as a starting point.
//!
//! When to use: parsers, serializers, validators, crypto, state machines,
//! financial calculators. NOT for CRUD, HTTP glue, or pure plumbing.
//!
//! Pattern: proptest generates random inputs; you assert invariants.
//! Run: cargo test --test properties
//! Install: cargo add --dev proptest
//!
//! For more patterns, see https://proptest-rs.github.io/proptest/

use proptest::prelude::*;

// use std::collections::HashMap;  // uncomment when you wire up the serialize test
// use my_crate::{serialize, deserialize, my_sort};

proptest! {
    // 1. INVARIANT -- round-trip property
    // Remove #[ignore] once you wire up real serialize/deserialize.
    #[test]
    #[ignore = "template — replace body with real round-trip check"]
    fn serialize_roundtrip_is_identity(_data in prop::collection::hash_map(".*", any::<i64>(), 0..10)) {
        // let encoded = serialize(&_data);
        // let decoded: HashMap<String, i64> = deserialize(&encoded).unwrap();
        // prop_assert_eq!(decoded, _data);
    }

    // 2. ORACLE -- compare custom impl against stdlib sort
    #[test]
    #[ignore = "template — replace body with real my_sort vs stdlib comparison"]
    fn custom_sort_matches_stdlib(_xs in prop::collection::vec(any::<i32>(), 0..100)) {
        // let mut mine = _xs.clone();
        // my_sort(&mut mine);
        // let mut reference = _xs.clone();
        // reference.sort();
        // prop_assert_eq!(mine, reference);
    }

    // 3. MODEL/STATE-MACHINE -- stack LIFO invariant
    #[test]
    #[ignore = "template — replace body with real stack LIFO check"]
    fn stack_push_pop_is_lifo(_items in prop::collection::vec(any::<i32>(), 0..50)) {
        // let mut stack: Vec<i32> = Vec::new();
        // for &item in &_items {
        //     stack.push(item);
        //     prop_assert_eq!(stack.pop(), Some(item));
        // }
    }
}

Swift's property-testing story is weaker than other languages. The classic choice, SwiftCheck (typelift/SwiftCheck), has had no releases since 2019 and has open build failures on Swift 5.9+/6.0. Options:

Option Status Tradeoff
Parameterized tests (XCTest / swift-testing) + hand-rolled generators Stable, works with toolchain No shrinking; manual generator code
swift-gen (Point-Free) Actively maintained Data generation only — you write the test loop yourself
SwiftCheck Unmaintained since 2019 Full property API but likely fails to compile on modern Swift

The template below uses option 1 (parameterized tests + custom generators) because it works without external dependencies. Upgrade to swift-gen if you need richer combinators.

// PROPERTY TEST TEMPLATE -- Copy this file as a starting point.
//
// When to use: parsers, serializers, validators, crypto, state machines,
// financial calculators. NOT for CRUD, UI glue, or pure plumbing.
//
// Pattern: XCTest with hand-rolled random generators. No external dependency.
// Run: swift test
//
// For richer combinators (without full property framework):
//   .package(url: "https://github.com/pointfreeco/swift-gen", from: "0.4.0")
// For stateful testing and advanced patterns, consider swift-testing's
// `@Test(arguments:)` parameterized tests (Swift 5.10+).

import XCTest

// @testable import MyApp

final class PropertyTests: XCTestCase {
    private let iterations = 100
    private var rng = SystemRandomNumberGenerator()

    // Hand-rolled generators — extend as needed
    private func randomInts(count: Int = Int.random(in: 0...50)) -> [Int] {
        (0..<count).map { _ in Int.random(in: -1000...1000) }
    }

    private func randomString(maxLen: Int = 32) -> String {
        let chars = "abcdefghijklmnopqrstuvwxyz "
        let len = Int.random(in: 0...maxLen)
        return String((0..<len).map { _ in chars.randomElement()! })
    }

    // 1. INVARIANT -- round-trip property
    func testSerializeRoundtripIsIdentity() throws {
        throw XCTSkip("Template — replace body with real serialize/deserialize round-trip")
        // for _ in 0..<iterations {
        //     let xs = randomInts()
        //     let encoded = serialize(xs)
        //     let decoded: [Int] = try deserialize(encoded)
        //     XCTAssertEqual(decoded, xs, "roundtrip failed for: \(xs)")
        // }
    }

    // 2. ORACLE -- compare custom impl against stdlib sort
    func testCustomSortMatchesStdlib() throws {
        throw XCTSkip("Template — replace body with real mySort vs sorted() comparison")
        // for _ in 0..<iterations {
        //     let xs = randomInts()
        //     let mine = mySort(xs)
        //     let reference = xs.sorted()
        //     XCTAssertEqual(mine, reference, "sort mismatch for: \(xs)")
        // }
    }

    // 3. MODEL/STATE-MACHINE -- stack LIFO invariant
    func testStackPushPopIsLIFO() throws {
        throw XCTSkip("Template — replace body with real stack LIFO invariant check")
        // for _ in 0..<iterations {
        //     let items = randomInts(count: Int.random(in: 0...20))
        //     var stack = Stack<Int>()
        //     for item in items {
        //         stack.push(item)
        //         XCTAssertEqual(stack.pop(), item, "LIFO violated after pushing \(items)")
        //     }
        // }
    }
}

Key points:

  • Opt-in, never speculative — don't install framework deps unless the user confirms there are modules that benefit
  • Placeholders follow the existing Phase A guidancepass (Python), it.todo(...) (TypeScript/JS — marks as pending, not passing), t.Skip(...) (Go), XCTSkip(...) (Swift), commented-out assertions (Rust). Avoid always-passing placeholders like return true or expect(true).toBe(true) — they inflate pass counts
  • No dangling unused imports — comment out imports alongside their usages. Templates must compile under strict settings (noUnusedLocals, -D warnings, etc.)
  • Three patterns per template — invariant, oracle, state-machine — covers the high-value property categories
  • Framework docs linked — developers will extend templates using framework-specific features (shrinking strategies, custom generators, stateful testing)
  • Discovered by normal test runners — files matching the naming conventions (tests/test_properties*.py, __tests__/properties*.test.ts, properties_test.go, tests/properties.rs, PropertyTests.swift) are picked up by the default test commands. They run alongside example-based tests; no separate CI job needed
  • NOT a PR gate — property tests are advisory signal, not required checks. Missing property tests should never block merge

A4. Post-Setup

After generating all files, verify the setup works end-to-end.

A4a. Run the Tests

Execute the test command for the detected language:

Language Command
TypeScript/JS npm test (or yarn test / pnpm test / bun test per detected package manager)
Python pytest tests/
Go go test ./...
Rust cargo test
Swift swift test (SPM) or xcodebuild test (Xcode)

A4b. Handle Results

Result Action
All tests pass Report success, show coverage baseline, proceed to 4c
Smoke test fails -- import side effects Report: "Your app performs side effects on import (e.g., DB connections, env vars). Consider wrapping startup logic in a function. The smoke test verifies import-only."
Smoke test fails -- missing dependencies Report: "Install missing dependencies first, then re-run Phase A."
Template test fails Report as informational (not an error): "The template test references example endpoints/functions. Adapt it to your actual code."
Installation failed Report the error (network, permissions, version conflict). Do not generate output files.

A4c. Report Summary

Show a summary of everything that happened:

## Phase A Complete: Test Infrastructure

**Language:** TypeScript (Express)
**Package manager:** npm

**Created:**
- vitest.config.ts (coverage: v8, reporter: lcov)
- package.json scripts: test, test:coverage
- __tests__/smoke.test.ts (1 test, passing)
- __tests__/_template.test.ts (3 tests, passing)

**Skipped:** (nothing -- full setup)

**Test results:** 4 tests passing
**Coverage baseline:** 12.3%

**Next steps:**
- Proceed to Phase B to wire CI/CD pipeline
- Phase C (CLAUDE.md) will auto-run after Phase B completes
- Use `busdriver:tdd` when ready to write tests for specific modules

Phase B: CI/CD Pipeline

Set up the full CI pipeline for new or existing repos: tests + coverage, action pinning, SBOM generation, build provenance attestations, security scanning backstop (Semgrep, Checkov, Zizmor), Dependabot, SSH commit signing, OpenSSF Scorecard, CodeScene behavioral analysis, and GitGuardian secrets detection.

When to Use

  • Onboarding a new repo into the CI pipeline
  • Adding or fixing Codecov, pinact, or GitGuardian for existing repos
  • Deploying pipeline changes across multiple repos at once
  • Fixing cross-platform CI failures (lightningcss, npm ci, vitest coverage, Swift iOS-only)
  • Auditing CI pipeline completeness across the portfolio
  • Adding SBOM generation or build provenance attestations
  • Setting up SSH commit signing or troubleshooting signature issues
  • Configuring Dependabot security alerts or version updates
  • Deploying OpenSSF Scorecard or SECURITY.md
  • Setting up CodeScene behavioral code analysis on PRs
  • Adding security scanning CI backstop (Semgrep, Checkov, Zizmor) for defense-in-depth

Pipeline Components

Component What It Does Config Files
Codecov Diff-coverage on PRs (80% target for new code) codecov.yml + .github/workflows/tests.yml
Pinact Auto-pin GitHub Actions to full SHA + precise version comments .github/workflows/pinact.yml
GitGuardian Secrets detection on push/PR (catches Gitleaks misses, different engine) GitHub App (ggshield)
Syft SBOM Generate Software Bill of Materials (dependency list) compliance job in tests.yml
SBOM Attestation Cryptographic SBOM provenance (GitHub Sigstore) compliance job in tests.yml
Release Attestation Attest source archives on GitHub Release (Sigstore) attest job in release.yml
Trivy Vuln Dependency vulnerability scanning (CRITICAL+HIGH) compliance job in tests.yml
Trivy License Dependency license compliance (CRITICAL only) compliance job in tests.yml
LICENSE Proprietary repo license (all rights reserved) LICENSE file at repo root
Cosign Keyless binary signing (forge release only) .github/workflows/release.yml
Harden-Runner Monitor network egress + detect code overwrite in Actions (Ubuntu only) First step in every ubuntu job
Commitlint Enforce Conventional Commits format (open-source repos only) commitlint.config.js + commitlint job in tests.yml
semantic-release Auto version bump + changelog + GitHub Release (open-source repos only) .releaserc.json + .github/workflows/release.yml
OpenSSF Scorecard Security health score (18 checks, weekly cron + push) .github/workflows/scorecard.yml
SECURITY.md Vulnerability disclosure policy SECURITY.md at repo root
Dependabot Security alerts + automated version update PRs (GitHub-native) .github/dependabot.yml
Dependabot auto-merge On opted-in repos (vars.DEPENDABOT_AUTO_APPROVE=true): approves AND enqueues auto-merge for patch (any) + safe minor (dev/indirect/github_actions). On opted-out / enterprise-restricted repos: annotate-only — posts manual-review comment for major + production-direct minor; safe bumps merged by hand .github/workflows/dependabot-auto-merge.yml
Commit Signing (SSH) Verified commits with SSH key signatures ~/.gitconfig (global) + GitHub signing key
checkov (local) IaC misconfiguration scan — BLOCK on commit ~/.claude/hooks/pre-commit-iac-scan.sh
zizmor (local) GitHub Actions workflow security — WARN on commit ~/.claude/hooks/pre-commit-iac-scan.sh
trivy (local) Dependency vuln scan — WARN on commit (CI trivy is the real gate) ~/.claude/hooks/pre-commit-iac-scan.sh
Semgrep CI Code security scanning backstop — SQLi, XSS, cmd injection (push+PR) semgrep job in security.yml
Checkov CI IaC misconfiguration CI backstop — Dockerfile, Terraform, k8s, workflows (push+PR) checkov job in security.yml
Zizmor CI GitHub Actions workflow security CI backstop (push+PR) zizmor job in security.yml
CodeScene Behavioral code analysis — code health, hotspots, complexity on PRs (student account) GitHub App + .codescene/custom-quality-gates.json
Admin Bypass Audit Detect direct-push bypass of required checks — creates admin-bypass issue (repos with enforce_admins: false) .github/workflows/bypass-audit.yml

Default Behavior: Audit Current Repo

When this skill is invoked without a specific task (e.g., user runs /ci-pipeline-setup or says "audit CI"), run a full checklist against the current repo. Check every component below using the exact commands shown. Present results as a table with pass/fail/N-A status.

Repo Settings (via API)

OWNER=$(gh repo view --json owner -q '.owner.login')
REPO=$(gh repo view --json name -q '.name')

# Repo settings
gh api "repos/$OWNER/$REPO" --jq '{
  allow_merge_commit, allow_squash_merge, allow_rebase_merge,
  allow_update_branch, delete_branch_on_merge, allow_auto_merge,
  visibility, default_branch
}'

# Actions permissions (also check selected_actions_url for allowlist if allowed_actions is "selected")
gh api "repos/$OWNER/$REPO/actions/permissions" --jq '{allowed_actions, sha_pinning_required}'

# Branch protection (use detected default branch, not hardcoded "main")
DEFAULT_BRANCH=$(gh repo view --json defaultBranchRef -q '.defaultBranchRef.name')
gh api "repos/$OWNER/$REPO/branches/$DEFAULT_BRANCH/protection/required_status_checks" --jq '{strict, contexts}' 2>&1

File Checks

Check Command Pass condition
Tests workflow [ -f .github/workflows/tests.yml ] File exists
Security backstop [ -f .github/workflows/security.yml ] File exists
Pinact workflow [ -f .github/workflows/pinact.yml ] File exists
Scorecard workflow [ -f .github/workflows/scorecard.yml ] File exists
Release workflow [ -f .github/workflows/release.yml ] File exists (N/A for non-release repos)
SHA pin script [ -f .github/scripts/check-pinned-uses.sh ] File exists
Dependabot [ -f .github/dependabot.yml ] File exists
Dependabot auto-merge [ -f .github/workflows/dependabot-auto-merge.yml ] File exists (N/A if no Dependabot config)
Scanners required `gh api repos/$OWNER/$REPO/branches/$DEFAULT_BRANCH/protection/required_status_checks --jq '.contexts as $c (["Actions security","Code security","Dependency CVEs","IaC misconfig"] - $c
Codecov config See Codecov detection logic below Three-way check
LICENSE [ -f LICENSE ] File exists
SECURITY.md [ -f SECURITY.md ] File exists
Release config [ -f .releaserc.json ] File exists (N/A for non-release repos)
Commitlint config [ -f commitlint.config.js ] File exists (N/A for non-release repos)
CodeScene config [ -f .codescene/custom-quality-gates.json ] File exists (N/A if CodeScene App not installed)
Property tests Any of tests/test_properties*.py, __tests__/properties*.test.ts, properties_test.go, tests/properties.rs, PropertyTests.swift File exists (advisory; N/A if repo has no parser/validator/crypto/state-machine modules)

Codecov detection logic:

  1. Has test script ("test" in package.json / Makefile test: target / go test / cargo test) AND coverage config (codecov.yml, .coveragerc, vitest coverage config) -> wire Codecov
  2. Has source files (.ts/.tsx/.js/.jsx/.py/.go/.rs/.swift) but no test infrastructure -> Codecov | ❌ — set up test infrastructure first
  3. No source files (markdown, JSON, shell only) -> Codecov | N/A

Workflow Hardening (check each workflow file)

For each .github/workflows/*.yml, verify:

Check How to verify
timeout-minutes on every job For each workflow, verify every job has timeout-minutes set
permissions declared grep -L 'permissions' .github/workflows/*.yml — should return nothing
defaults.run.shell Check defaults.run.shell: bash is declared (not just any shell: key in step-level overrides)
Concurrency group Check push/PR workflows only (not cron-only workflows like scorecard)
Harden-Runner For each ubuntu job (not macOS), verify harden-runner step exists. Check per-job, not per-file
SHA-pinned actions bash .github/scripts/check-pinned-uses.sh — exit 0 = pass
No paths + paths-ignore on same trigger Verify no workflow uses both paths and paths-ignore on the same trigger event (GitHub ignores paths-ignore when paths is present)
persist-credentials: false Check all checkout steps except release/pinact (which need push access)

Content Checks (grep inside files)

Check Command Pass condition
Compliance job (SBOM+license+vuln) grep -q 'sbom-action' .github/workflows/tests.yml Found (N/A for no-dep repos)
Trivy in compliance grep -q 'trivy-action|scanners.*vuln' .github/workflows/tests.yml Found (N/A for no-dep repos)
Commitlint job grep -q 'commitlint' .github/workflows/tests.yml Found (N/A for non-release repos)
Semgrep in security.yml grep -q 'semgrep' .github/workflows/security.yml Found
Checkov in security.yml grep -q 'checkov' .github/workflows/security.yml Found
Zizmor in security.yml grep -q 'zizmor' .github/workflows/security.yml Found
Trivy vuln scan grep -q 'trivy-action' .github/workflows/tests.yml OR grep -q 'trivy' .github/workflows/security.yml Found in either (Trivy runs in compliance job in tests.yml; security.yml auto-skips if compliance exists)
Reports summary job grep -q 'GITHUB_STEP_SUMMARY' .github/workflows/security.yml Found
Artifact retention set grep -q 'retention-days' .github/workflows/scorecard.yml Found where upload-artifact is used

Present Results

Show a summary table:

| Component | Status | Notes |
|-----------|--------|-------|
| Repo: squash-only merge | ✅/❌ | |
| Repo: auto-merge | ✅/❌ | |
| Repo: branch protection | ✅/❌ | contexts: [...] |
| Repo: Actions selected | ✅/❌ | |
| Repo: SHA pinning required | ✅/❌ | |
| Tests workflow | ✅/❌ | |
| Security backstop | ✅/❌ | semgrep+checkov+zizmor+trivy+reports |
| Pinact | ✅/❌ | |
| Scorecard | ✅/❌ | |
| Release | ✅/❌/N-A | |
| Dependabot | ✅/❌ | ecosystems: [...] |
| Codecov | ✅/❌/N-A | |
| Compliance (SBOM+vuln+license) | ✅/❌/N-A | |
| Commitlint | ✅/❌/N-A | |
| LICENSE | ✅/❌ | |
| SECURITY.md | ✅/❌ | |
| SHA pin script | ✅/❌ | |
| Harden-Runner (all ubuntu jobs) | ✅/❌ | |
| Workflow hardening | ✅/❌ | timeouts, permissions, shell, concurrency |
| Commit signing | ✅/❌ | SSH or GPG |
| CodeScene | ✅/❌/N-A | quality gates config |

After showing results, suggest fixes for any ❌ items referencing the specific section in this skill.


B1. Process

B1a. Detect Stack

cd /path/to/repo  # <- Replace with the actual repository path
ls package.json pyproject.toml go.mod Cargo.toml Package.swift build.gradle.kts pom.xml 2>/dev/null

B1b. Configure Repo Settings (before deploying workflows)

Configure repo settings via API BEFORE deploying workflows. Without this, allowed_actions: "local_only" causes silent startup_failure on all workflows using external actions.

Important: If the org controls Actions permissions, repo-level API calls return 409 Conflict. Check org-level first: gh api orgs/ORG/actions/permissions/selected-actions. If org-level is set, modify it there instead of per-repo.

OWNER="owner"
REPO="repo"

# ── Repo settings (merge, auto-merge, bra

*Truncated - read the full file at https://github.com/aibot88/sec_skill_store/blob/608120788167d56f99e31449e411e603ed250dc6/skills/claudskills/helmet/SKILL.md.*

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/aibot88-sec-skill-store-helmet/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

aibot88-sec-skill-store-helmet.ocm.jsonjson
{
  "ocm": "1",
  "id": "aibot88-sec-skill-store-helmet",
  "kind": "skill",
  "name": "helmet",
  "description": "Full repo onboarding — bootstraps test infrastructure (Phase A), wires the CI/CD pipeline (Phase B), and generates a project CLAUDE.md (Phase C). Use when onboarding a new repo, setting up tests + CI from scratch, adding Codecov/pinact/SBOM/security scanning, auditing pipeline completeness, fixing CI failures, deploying pipeline changes across multiple repos, or generating/refreshing a repo's CLAUDE.md. Replaces ci-pipeline-setup and test-setup.",
  "publisher": "aibot88",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "hr",
      "coding"
    ],
    "tags": [
      "skill-md",
      "github"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Full repo onboarding — bootstraps test infrastructure (Phase A), wires the CI/CD pipeline (Phase B), and generates a project CLAUDE.md (Phase C). Use when onboarding a new repo, setting up tests + CI from scratch, adding Codecov/pinact/SBOM/security scanning, auditing pipeline completeness, fixing CI failures, deploying pipeline changes across multiple repos, or generating/refreshing a repo's CLAUDE.md. Replaces ci-pipeline-setup and test-setup."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "github",
      "repository": "https://github.com/aibot88/sec_skill_store",
      "path": "skills/claudskills/helmet/SKILL.md",
      "ref": "608120788167d56f99e31449e411e603ed250dc6",
      "url": "https://github.com/aibot88/sec_skill_store/blob/608120788167d56f99e31449e411e603ed250dc6/skills/claudskills/helmet/SKILL.md",
      "key": "aibot88/sec_skill_store/skills/claudskills/helmet/SKILL.md"
    }
  },
  "instructions": "# Repo Pipeline Setup\n\nThree-phase repo onboarding: **Phase A** bootstraps test infrastructure (language detection, framework detection, test runner + coverage, smoke tests, gold-standard templates). **Phase B** wires the CI/CD pipeline (Codecov, SHA pinning, SBOM, vulnerability scanning, security backstop, Dependabot, commit signing, OpenSSF Scorecard, CodeScene, GitGuardian). **Phase C** generates a project CLAUDE.md by analyzing the repo's tech stack, structure, commands, conventions, and CI configuration — so every new Claude Code session starts with full project context.\n\n## When to Use\n\n",
  "cost": {
    "context_tokens": 46523
  }
}

Fetch it by URL: GET /api/v1/registry/aibot88-sec-skill-store-helmet/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.