Spec-driven development on OpenSpec, with mechanical spec-as-source enforcement: a custom 'spec-as-source' OpenSpec schema adds file-ownership (targets) and test-verification ([@test]) metadata to every capability spec, three scripts (link check, ownership check, manifest build) keep code and specs from drifting apart, plus requirement-gathering, spec-writer, work-review, and a session-handoff skill with a proactive context-warning hook.
71
89%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Advisory
Suggest reviewing before use
Regenerate .github/workflows/spec-verification.yml so it runs exactly the test files the specs declare, guarded by the three spec-consistency checks. Run after adding a spec or changing the stack.
The workflow must end up with four things right. Treat them as a checklist:
check-spec-links, check-target-ownership, build-spec-manifest);First match wins: pytest.ini / setup.cfg / pyproject.toml [tool.pytest] → pytest; package.json test script → npm (jest/vitest if in devDependencies); Cargo.toml → cargo; go.mod → go. No match → ask the user.
Read .spec-source-manifest.json (run python3 scripts/build-spec-manifest.py first if missing) and collect every unique path under requirements[*].tests:
python3 -c "import json,itertools; m=json.load(open('.spec-source-manifest.json')); print('\n'.join(sorted({t for s in m.values() for r in s['requirements'].values() for t in r['tests']})))"If the manifest has no requirements, fall back to grep -rhoE '\[@test\] [^ ]+' openspec/specs/ | sed 's/\[@test\] //'. These exact paths go into the test command in Step 3 — do not substitute a directory glob.
name: Spec Verification
on:
push: { branches: [main] }
pull_request: { branches: [main] }
jobs:
verify-specs:
name: Run spec verification
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
<SETUP_AND_INSTALL>
- name: Check [@test] links resolve
run: bash scripts/check-spec-links.sh
- name: Check target ownership
run: bash scripts/check-target-ownership.sh
- name: Build spec manifest
run: python3 scripts/build-spec-manifest.py
- name: Run spec-linked tests
run: <TEST_CMD>| Runner | <SETUP_AND_INSTALL> | <TEST_CMD> |
|---|---|---|
| pytest | - uses: actions/setup-python@v5 with: { python-version: "3.11" } - run: pip install -r requirements.txt | pytest <the exact files from Step 2> -v --tb=short |
| npm/jest/vitest | - uses: actions/setup-node@v4 with: { node-version: "20" } - run: npm ci | npm test |
| cargo | (omit the line) | cargo test |
| go | (omit the line) | go test ./... |
For pytest, the test command must name every file from Step 2 (space- or \-separated), e.g. pytest tests/auth/test_login.py tests/auth/test_rate_limit.py -v --tb=short. Keep the three spec-check steps exactly as shown. Add steps only for the detected runner — never for runners the project does not use.
Always show the diff first, in a fenced ```diff block, so the change is reviewable before anything is written — full content as a + diff for a new file; changed lines only for an existing one. Then ask: "Update the workflow? (yes/no)" and write the file only after an explicit "yes". If it is already in sync, say so and do not overwrite. Showing the diff is unconditional; only the write waits on confirmation.
Report the runner detected, how many test files came from how many specs, and whether the workflow was written / updated / unchanged. Then: "Next step: commit and push to trigger CI."
.tessl-plugin
skills
handoff
openspec-apply-change
openspec-archive-change
openspec-explore
openspec-propose
openspec-sync-specs
requirement-gathering
spec-as-source-setup
templates
openspec-schema
spec-as-source
templates
spec-ci-sync
spec-rebuild
spec-verify
spec-writer
work-review