Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
node_modules/
dist/
*.tsbuildinfo
.DS_Store
coverage/
Expand Down
77 changes: 77 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Contributing to spec-ci-plugin

Thanks for your interest in contributing! This guide covers how to get started.

## Prerequisites

- Node.js >= 18
- npm

## Setup

```bash
git clone https://github.com/UnityInFlow/spec-ci-plugin.git
cd spec-ci-plugin
npm install
```

## Development workflow

```bash
npm run lint # Type check (tsc --noEmit)
npm test # Run all tests
npm run test:watch # Watch mode
npm run build # Produce dist/index.js (CJS bundle)
npm run format # Check formatting
npm run format:fix # Fix formatting
```

## Making changes

1. Fork the repo and create a feature branch from `main`
2. Make your changes in `src/`
3. Add or update tests in `tests/`
4. Run `npm run lint && npm test && npm run build` to verify
5. Commit with a descriptive message following [Conventional Commits](https://www.conventionalcommits.org/):
- `feat: add new checker for X`
- `fix: handle empty spec file in scope checker`
- `test: add edge cases for criteria matcher`
- `docs: update README with new input`
6. Open a PR against `main`

## Project structure

```
src/
index.ts Action entry point (4-step pipeline)
types.ts Shared interfaces
spec-linter.ts Run spec-linter via npx
injection-scanner.ts Download + run injection-scanner binary
scope-checker.ts Extract scope from spec, compare to PR diff
criteria-checker.ts Parse test descriptions, match to criteria
comment-builder.ts Build markdown PR comment
github.ts Post/update PR comment via GitHub API
tests/
fixtures/ Test fixture files
*.test.ts Test files (one per module)
```

## Code style

- TypeScript strict mode -- no `any` types
- `const` everywhere, named exports
- Early returns over nested `if` blocks
- Tests use vitest (`describe`, `it`, `expect`)

## Releasing

Only maintainers can release. The process:

1. Bump version in `package.json`
2. Run `npm run build` and commit `dist/`
3. Tag with `git tag vX.Y.Z`
4. Push tag and create GitHub Release

## License

By contributing, you agree that your contributions will be licensed under the MIT License.
149 changes: 136 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,164 @@
# spec-ci-plugin

Spec compliance CI/CD plugin for the [UnityInFlow](https://github.com/UnityInFlow) ecosystem. Enforces spec-linter, injection-scanner, scope, and criteria checks on every PR.
A GitHub Action that enforces spec compliance on every pull request. Runs spec-linter, injection-scanner, scope checker, and criteria checker -- then posts a structured compliance report as a PR comment.

## Usage
## The problem

Spec-driven development tools like GSD and Superpowers enforce specs during the coding session, but there is zero enforcement at the CI boundary. A PR that diverges from the spec merges freely. The spec is advisory, not enforced.

**spec-ci-plugin closes the loop** -- the spec becomes a contract enforced at every PR.

## Quick start

```yaml
# .github/workflows/spec-check.yml
name: Spec Compliance
on: [pull_request]

jobs:
check:
spec-check:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: UnityInFlow/spec-ci-plugin@v0
- uses: UnityInFlow/spec-ci-plugin@v0.0.1
with:
spec-file: CLAUDE.md
fail-on: errors
```

## What it checks

The action runs a 4-step validation pipeline on your spec file:

1. **Spec Validation** -- runs `@unityinflow/spec-linter` to check required sections, no secrets, file size, no wildcard permissions, no duplicate headers
2. **Security Scan** -- runs `injection-scanner` to detect prompt injection patterns in the spec file
3. **Scope Compliance** -- extracts declared scope from your spec (via `## Scope` section or GSD `<files>` tags) and compares against the PR's changed files
4. **Criteria Coverage** -- parses acceptance criteria from `## Acceptance Criteria` and matches them to test descriptions (`describe`/`it`/`test`) found in your test files

## PR comment output

The action posts (or updates) a single PR comment with results:

```
## Spec Compliance Report

### Spec Validation
PASS

### Security Scan
PASS
- No injection patterns detected

### Scope Compliance
All changed files are within spec-declared scope.
Scope: src/auth/, src/middleware/

### Acceptance Criteria Coverage
"Users can log in with email/password" -- test found: auth.test.ts ("allows user login with email and password")
"Rate limiting enforced on login" -- test found: middleware.test.ts ("enforces rate limit on login endpoint")
"Session expires after 24h" -- no matching test found

**Coverage: 2/3 criteria matched to tests**

---
**Overall: WARN**
```

## Inputs

| Input | Description | Default |
|---|---|---|
| `github-token` | GitHub token for PR comments | `${{ github.token }}` |
| `spec-file` | Path to spec file | `CLAUDE.md` |
| `fail-on` | When to fail: `errors`, `warnings`, `never` | `errors` |
| `github-token` | GitHub token for posting PR comments and reading PR files | `${{ github.token }}` |
| `spec-file` | Path to spec file (CLAUDE.md, REQUIREMENTS.md, etc.) | `CLAUDE.md` |
| `fail-on` | When to fail the check: `errors`, `warnings`, or `never` | `errors` |
| `post-comment` | Post compliance report as PR comment | `true` |
| `injection-scanner-version` | injection-scanner version to download | `v0.0.1` |
| `injection-scanner-version` | injection-scanner release version to download | `v0.0.1` |

## What it checks
## Outputs

| Output | Description |
|---|---|
| `status` | Overall status: `pass`, `warn`, or `fail` |
| `report` | Full compliance report as JSON |

## Exit codes

| Code | Meaning |
|---|---|
| 0 | All checks passed (or `fail-on: never`) |
| 1 | Checks failed based on `fail-on` setting |

When `fail-on` is `errors`, only `fail` status triggers a non-zero exit. When `fail-on` is `warnings`, both `warn` and `fail` trigger non-zero. When `fail-on` is `never`, the action always exits 0.

## Spec file format

The action works with any Markdown spec file. For best results, include these sections:

```markdown
## Project Overview
What the project does.

## Constraints
- TypeScript strict mode
- No wildcard permissions

## Acceptance Criteria
- [ ] Users can log in with email
- [ ] Dashboard loads in under 2 seconds

## Scope
- src/auth/
- src/dashboard/
```

GSD-style `<files>` tags are also supported for scope declaration.

## Examples

### Fail on warnings

```yaml
- uses: UnityInFlow/spec-ci-plugin@v0.0.1
with:
fail-on: warnings
```

### Custom spec file

```yaml
- uses: UnityInFlow/spec-ci-plugin@v0.0.1
with:
spec-file: REQUIREMENTS.md
```

### Skip PR comment

```yaml
- uses: UnityInFlow/spec-ci-plugin@v0.0.1
with:
post-comment: "false"
```

### Use output in subsequent steps

```yaml
- uses: UnityInFlow/spec-ci-plugin@v0.0.1
id: spec-check
with:
fail-on: never

- run: echo "Spec status: ${{ steps.spec-check.outputs.status }}"
```

## Part of the UnityInFlow ecosystem

spec-ci-plugin is tool #4 in the [UnityInFlow](https://github.com/UnityInFlow) AI agent tooling ecosystem. It builds on:

1. **Spec Validation** -- runs `@unityinflow/spec-linter` on your spec file
2. **Security Scan** -- runs `injection-scanner` for prompt injection patterns
3. **Scope Compliance** -- compares PR changed files against spec-declared scope
4. **Criteria Coverage** -- matches acceptance criteria to test descriptions
- [spec-linter](https://github.com/UnityInFlow/spec-linter) -- validates spec file structure
- [injection-scanner](https://github.com/UnityInFlow/injection-scanner) -- detects prompt injection patterns

## License

Expand Down
Loading
Loading