How this skill is triggered — by the user, by Claude, or both
Slash command
/dirigent:create-contractThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You define the acceptance criteria that the REVIEWER will check after the EXECUTOR finishes a phase. The criteria you write are the definition of "done" — if they pass, the phase ships. If they fail, the executor has to fix.
You define the acceptance criteria that the REVIEWER will check after the EXECUTOR finishes a phase. The criteria you write are the definition of "done" — if they pass, the phase ships. If they fail, the executor has to fix.
The output file is validated by Pydantic. Any deviation = silent rejection. Use EXACTLY these field names:
{
"phase_id": "01",
"phase_name": "Phase Name",
"objective": "One sentence: what this phase achieves",
"acceptance_criteria": [
{
"id": "AC-{PHASE_ID}-01",
"description": "What must be true",
"verification": "Run: <executable command>",
"layer": "structural|behavioral|boundary"
}
],
"quality_gates": ["All new/modified files compile without errors", "No regressions in existing functionality", "Code follows project conventions"],
"out_of_scope": ["What this phase does NOT cover"],
"expected_files": [{"path": "src/foo.py", "change": "Add new class"}]
}
Hard constraints enforced by Pydantic validation:
acceptance_criteria — NOT criteria, NOT tests, NOT checksobjective — NOT description, NOT verification_strategyverification — NOT verify, NOT command, NOT checkid format: AC-{PHASE_ID}-{NN} (e.g., AC-01-01) — NOT S01, B01, etc.verification MUST start with "Run: " followed by an executable shell commandlayer must be one of: "structural", "behavioral", "boundary"Every contract MUST contain criteria from three layers:
Quick sanity checks — does the code compile, are files in place?
Purpose: Catch obvious build breaks before wasting time on behavioral tests.
Examples:
"Run: npm run build" — project compiles"Run: python -m py_compile src/routes/users.py" — new file is valid Python"Run: npx tsc --noEmit" — TypeScript type-checksLimit: Max 2 structural criteria per contract. These are not the substance.
The feature WORKS when a user exercises it. This is the core of the contract.
Purpose: Prove the feature does what the spec says by actually running it.
Format: Given/When/Then, verified by executable commands against the running system.
Examples:
Given: authenticated admin user
When: GET /api/tenants/1/settings
Then: HTTP 200 with JSON containing all settings fields
Verification: Run: curl -sf http://localhost:3000/api/tenants/1/settings \
-H "Authorization: Bearer $(curl -sf http://localhost:3000/api/auth/login \
-d '{"email":"[email protected]","password":"test123"}' | jq -r .token)" \
| jq 'keys | length >= 5'
What behavioral criteria MUST test:
Error paths and edge cases — what happens when things go wrong?
Purpose: Prove the system handles bad input, missing data, and unauthorized access gracefully.
Examples:
Given: no authentication
When: GET /api/admin/users
Then: HTTP 401 or 403
Verification: Run: HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' \
http://localhost:3000/api/admin/users); test "$HTTP_CODE" = "401" -o "$HTTP_CODE" = "403"
Given: valid auth, nonexistent resource
When: GET /api/tenants/999999/settings
Then: HTTP 404
Verification: Run: curl -s -o /dev/null -w '%{http_code}' \
http://localhost:3000/api/tenants/999999/settings \
-H "Authorization: Bearer $TOKEN" | grep -q 404
These test CODE STRUCTURE, not USER BEHAVIOR. They are forbidden in behavioral and boundary criteria:
| Pattern | Why it's bad | What to use instead |
|---|---|---|
grep "def func" src/file.py | Proves string exists in source, not that func works | curl the endpoint that calls func |
test -f src/routes/users.py | File existing ≠ feature working | curl /api/users and check response |
cat src/config.py | grep SETTING | Reads source, not runtime behavior | Hit an endpoint that uses the setting |
wc -l src/models.py | Line count says nothing about correctness | Test the model via API CRUD |
rg "router.include" src/main.py | Registration ≠ working endpoint | curl the registered route |
The litmus test: "If this verification passes, would a USER notice?" If no, it's structural, not behavioral.
.dirigent/PLAN.json — find the phase matching $ARGUMENTS (the phase ID).dirigent/SPEC.md — understand what the user wants.dirigent/test-harness.json — if this exists, it tells you EXACTLY how to verifyYou MUST use these fields:
| Harness Field | How to Use |
|---|---|
base_url | Base for ALL curl commands (e.g., curl -sf {base_url}/api/users) |
auth.login_command | Get a token/session BEFORE authenticated tests |
seed.users | Reference test users in criteria (email, role, password) |
verification_commands | Use at least one directly as a criterion verification |
e2e_framework.run_command | At least one criterion MUST use this if framework ≠ "none" |
health_checks | Use one as a structural criterion (system is alive) |
Example flow for an authenticated endpoint test:
# Step 1: Get auth token using harness login_command
TOKEN=$(curl -sf http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"test123"}' | jq -r .token)
# Step 2: Hit the endpoint under test
curl -sf http://localhost:3000/api/tenants/1/settings \
-H "Authorization: Bearer $TOKEN" | jq .default_currency
Combine both into a single verification command using && or subshells.
Use the project's test runner for behavioral verification:
Run: cd {repo} && python -m pytest tests/test_feature.py -v -k "test_name"Run: cd {repo} && npx jest --testPathPattern="feature" --verboseRun: cd {repo} && go test ./pkg/feature/... -v -run TestNameWrite behavioral criteria that map to specific test functions. If no tests exist yet, write criteria that use the framework's test client:
Run: cd {repo} && python -c "
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
resp = client.get('/api/users', headers={'Authorization': 'Bearer test'})
assert resp.status_code == 200
assert len(resp.json()) > 0
"
Write .dirigent/contracts/phase-{PHASE_ID}.json:
{
"phase_id": "01",
"phase_name": "User Management API",
"objective": "Implement CRUD endpoints for user management with role-based access",
"acceptance_criteria": [
{
"id": "AC-01-01",
"description": "Project compiles and lints without errors",
"verification": "Run: npm run build && npm run lint",
"layer": "structural"
},
{
"id": "AC-01-02",
"description": "GET /api/users returns a list of users with id, email, and role fields",
"verification": "Run: TOKEN=$(...login...) && curl -sf http://localhost:3000/api/users -H \"Authorization: Bearer $TOKEN\" | jq '.[0] | has(\"id\", \"email\", \"role\")'",
"layer": "behavioral"
},
{
"id": "AC-01-03",
"description": "POST /api/users creates a user and returns it with a generated ID",
"verification": "Run: TOKEN=$(...) && curl -sf -X POST http://localhost:3000/api/users -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" -d '{\"email\":\"[email protected]\",\"role\":\"viewer\"}' | jq '.id'",
"layer": "behavioral"
},
{
"id": "AC-01-04",
"description": "Data persists: POST then GET returns the created user",
"verification": "Run: TOKEN=$(...) && ID=$(curl -sf -X POST .../api/users ... | jq -r .id) && curl -sf .../api/users/$ID -H ... | jq '.email' | grep -q '[email protected]'",
"layer": "behavioral"
},
{
"id": "AC-01-05",
"description": "Viewer role cannot access admin endpoints — returns 403",
"verification": "Run: VIEWER_TOKEN=$(...login as viewer...) && HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/api/admin/users -H \"Authorization: Bearer $VIEWER_TOKEN\") && test \"$HTTP_CODE\" = \"403\"",
"layer": "boundary"
},
{
"id": "AC-01-06",
"description": "POST with duplicate email returns 409 Conflict",
"verification": "Run: TOKEN=$(...) && curl -s -o /dev/null -w '%{http_code}' -X POST .../api/users -H ... -d '{\"email\":\"[email protected]\",\"role\":\"viewer\"}' | grep -q 409",
"layer": "boundary"
}
],
"quality_gates": [
"All new/modified files compile without errors",
"No regressions in existing functionality",
"Code follows project conventions"
],
"out_of_scope": ["User profile editing", "Password reset flow"],
"expected_files": [
{"path": "src/routes/users.py", "change": "New CRUD route handlers"},
{"path": "src/models/user.py", "change": "User model definition"}
]
}
After writing the JSON file, validate it:
python ${CLAUDE_SKILL_DIR}/scripts/validate_schema.py .dirigent/contracts/phase-$ARGUMENTS.json
If validation fails, read the error messages, fix the JSON, and re-run until it passes.
npx claudepluginhub bidequity/outbid-dirigent --plugin dirigentTransforms requirements.md into an executable plan (plan.json + contracts.md) using contract-first planning. Sits between /specify and /execute for greenfield or bugfix work.
Emits goal contracts, deviation notices, phase checks, and final audits to keep agent execution aligned with the original goal.
Enforces a gated Spec → Plan → Build → Test → Review → Ship lifecycle for multi-file features and projects, preventing AI coding agents from skipping specification and verification steps.