OpenSSF Best Practices Passing criteria: 100% met (self-assessment, verified 2026-10-16).
Standard evaluator result contract for evidence, provenance, uncertainty, and recovery — a provider-neutral protocol for extensions that evaluate artifact quality between Spec-Driven Development phases.
In agent loops, this contract lets tools retain conflicting findings, distinguish observations from assertions, and compose explicit recovery/block outcomes before the next step. AEE supplies evidence-centered claim assessment; this extension supplies the neutral envelope and composition. Together they can help expose unsupported completion claims and preserve uncertainty during handoffs. A composed pass does not validate sources or guarantee a hallucination-free answer. See use cases and how to evaluate the benefit.
Spec Kit has a strong lifecycle and extensible hook system, but there is no common contract for extensions that evaluate the quality or trustworthiness of artifacts between phases. This extension defines that contract.
The evaluator contract lets any extension:
- Register for one or more lifecycle events via hooks
- Receive the relevant resolved artifacts plus stable source/provenance references
- Return a versioned machine-readable result conforming to a shared schema
- Distinguish observed evidence from generated assertions
- Preserve contradictory findings instead of forcing false consensus
- Represent insufficient evidence or unresolved uncertainty explicitly
- Request a bounded next action:
pass,warn,iterate,clarify,gather_evidence, orblock - Persist enough compact state to survive pause/resume
- Compose deterministically with other evaluators
- Remain implementation-neutral: deterministic, model-backed, local, remote, private, paid, or hybrid
This repository is electrohire/spec-kit-evaluator; its Spec Kit extension ID is
evaluator. It is distributed as a GitHub archive, not a PyPI package. Its AEE
companion is repository electrohire/spec-kit-aee, extension ID aee. The AEE
engine is the PyPI distribution applied-epistemic-engineering, which installs
the Python import and CLI aee. Do not use pip install aee for that engine.
A valid result envelope and composed pass describe schema conformance and the
configured composition policy. They do not certify the truth of findings,
universal correctness, safety, or compliance. Independence of a reviewer helps
challenge evidence; it does not turn a model's response into a certificate. See
the engine's names and assurance boundaries
and this repository's development setup.
For the complete AEE workflow in an initialized Spec Kit project:
First register the ElectroHire publisher catalog once per project (or use an organization catalog containing AEE). The default community catalog is discovery-only, including in Specify 1.2.2. Then install by name:
specify extension add aeeAEE provisions the engine, this companion, and the Python composer dependency; no separate evaluator installation is needed.
For standalone AEE without Spec Kit, install only the engine:
python -m pip install applied-epistemic-engineeringThe standalone engine does not require this evaluator extension. Developers running the composer separately can find its Python/Bash/PowerShell prerequisites and component-specific installation in development setup.
Ask your agent: “Compose the evaluator results for this plan, explain conflicts,
and identify the next evidence needed.” Spec Kit renders four operation
SKILL.md files for run, compose, report, and route into the active integration's
skill directory. The agent reads those instructions and handles execution for you.
Reload the agent session after installation if the skills are not visible.
The source evaluator-contract skill also guides direct composition from a checkout without Spec Kit; this onboarding skill is included from extension v1.1.2. A composer does not produce the evidence it combines. For standalone AEE assessment, use the engine's direct agent skill. The command examples below remain useful to agents and CLI users alike.
Run an evaluator against one or more artifacts and produce a versioned machine-readable result.
/speckit.evaluator.run phase=after_plan artifacts=spec.md,plan.mdResults are written to .specify/extensions/evaluator/results/<evaluator-id>-<phase>-<timestamp>.json.
Compose multiple evaluator results at a lifecycle point with deterministic precedence.
/speckit.evaluator.compose phase=after_plan strategy=strictComposed results are written to .specify/extensions/evaluator/results/composed-<phase>-<timestamp>.json.
Render evaluator results as a human-readable report, CI annotation, or release gate.
/speckit.evaluator.report phase=after_plan format=terminal
/speckit.evaluator.report phase=after_plan format=ci-annotation
/speckit.evaluator.report phase=after_plan format=gateThe full JSON Schema is at schemas/evaluator-result.schema.json. Every evaluator result MUST conform to this schema.
{
"schema_version": "1.0",
"evaluator": {
"id": "my-evaluator",
"version": "0.1.0"
},
"phase": "after_plan",
"outcome": "pass",
"findings": []
}| Outcome | Meaning | Workflow Effect |
|---|---|---|
pass |
All checks passed | Continue to next phase |
warn |
Issues found but not blocking | Continue with warnings |
iterate |
Issues require revisiting a prior phase | Return to target phase |
clarify |
Ambiguities need human resolution | Pause for human input |
gather_evidence |
Insufficient evidence | Pause for evidence collection |
block |
Hard blocker | Stop the workflow |
| Kind | Meaning |
|---|---|
observed |
Directly observed from an artifact or command output |
inferred |
Logically derived from observed evidence |
asserted |
Claimed by a model or agent without direct observation |
contradicted |
Conflicts with other observed evidence |
unsupported |
No evidence found to support or refute |
The extension ships one deterministic reference evaluator (Spec 002 FR-7), registered in evaluators.yml — a stock install is no longer a contract without a tenant. It checks a feature directory for the contract's basics:
spec.md/plan.md/tasks.mdpresence, and required spec sections (title, requirements, acceptance/success criteria)claims.jsonpresence and strict-load validity (structural mirror of the AEE engine's strict loader: known fields only, non-emptyid/text, valid enums, list-typed fields, no duplicate ids or JSON keys)- every claim carries at least one falsification test
assessment.jsonpresence- task ↔ requirement traceability (every task line references a requirement; every requirement in
spec.mdis referenced fromtasks.md)
It is deliberately small and deterministic — no model calls; the same feature directory produces a byte-identical result modulo metadata.timestamp. Judging epistemics remains AEE's role; this evaluator proves the contract composes.
From a clean checkout, with nothing registered by hand:
# 1. Discover: evaluators.yml registers the evaluator and its script
grep -A3 'id: spec-structure' evaluators.yml
# 2. Run it against the passing fixture
mkdir -p /tmp/evaluator-results
python3 scripts/python/spec_structure_evaluator.py \
--feature-dir examples/fixtures/spec-structure/passing \
--phase after_specify \
--output /tmp/evaluator-results/spec-structure-after_specify-20260101T000000Z.json
# 3. Validate the emitted result against the contract schema
python3 -c "
import json, jsonschema
schema = json.load(open('schemas/evaluator-result.schema.json'))
result = json.load(open('/tmp/evaluator-results/spec-structure-after_specify-20260101T000000Z.json'))
jsonschema.validate(result, schema)
print('emitted result is schema-valid; outcome:', result['outcome'])
"
# 4. Compose (the run command's results-directory flow)
python3 scripts/python/compose_results.py \
--results-dir /tmp/evaluator-results --phase after_specify --strategy strict --jsonExpected: the emitted result has outcome: "pass" and the composed result has outcome: "pass" with evaluator_count: 1. Repeat with examples/fixtures/spec-structure/failing (missing plan.md and assessment.json, a claim with no falsification test, a strict-load-invalid claim, uncovered requirements) — the emitted and composed outcomes are block, and every defect is named in the findings.
The walkthrough is exercised by tests/test_spec_structure_evaluator.py (python3 -m unittest discover -s tests).
When multiple evaluators run at the same phase, their results are composed:
| Strategy | Behavior |
|---|---|
strict (default) |
Most severe outcome wins |
majority |
Most common outcome wins; ties break toward severity |
optimistic |
Least severe outcome wins |
All three composers (Python, Bash, PowerShell) implement the same contract (Spec 003 FR-6), verified by a shared fixture corpus in tests/:
- Empty fails closed. Zero usable results — missing directory, no files for the phase, or no valid files — composes to
outcome: "block"with an explicit no-results finding and exit code 2. Absence of evidence is never a pass. - Inputs are validated. The Python composer always applies the shipped JSON Schema (
jsonschema>=4.26.0, seerequirements.txt, is a hard dependency — its absence is a hard error, never a silent skip). Bash/PowerShell enforce the same minimum contract (required keys, enums, evaluatorid+version, payloadphaseequal to the requested phase). A malformed or invalid file is wrapped as a blockingschema_violationfinding and votesblockunder every strategy — never skipped toward pass, never a traceback. - Identity is the payload. Dedup and counting use
evaluator.id+version, never the filename; duplicates of one evaluator count once. Finding ids are namespaced<evaluator.id>:<finding.id>. - Composed output is schema-valid. No
_evaluator_idinjection,model_routingomitted rather thannull, summaries within the 500-char cap; the Python composer self-validates its output before emitting it. - Advisory strategies are marked.
majority/optimisticresults carrymetadata.gating: false; when they overturn ablock,metadata.block_overridden: trueis recorded and the inputs' outcomes stay visible inmetadata.evaluator_results. Onlystrictcompositions gate. - Exit codes are the gate contract: 0 =
pass/warn, 1 =iterate/clarify/gather_evidence, 2 =blockor hard error.
Decision (Spec 003 T610, recorded with Spec 002 FR-7.4):
speckit.evaluator.reportandspeckit.evaluator.routeremain prompt-only commands — this extension ships executables for composition only, and no test pins report/route output. The earlier promise of priority-ordered findings in composition is removed from the compose command (no shipped composer implements evaluator priority); seecommands/speckit.evaluator.compose.md.
The extension registers hooks at key lifecycle points:
after_specify— Evaluate spec quality, evidence, and provenanceafter_plan— Evaluate plan assumptions, risks, and coverageafter_tasks— Evaluate task completeness and traceabilityafter_implement— Evaluate implementation against spec, plan, and tasks
All hooks are optional (prompt before executing) with priority 20.
To write an evaluator that conforms to this contract:
- Create an extension that depends on
evaluator - Register hooks at the lifecycle points you want to evaluate
- In your command, read the relevant artifacts
- Produce a result JSON file conforming to
evaluator-result.schema.json - Write it to
.specify/extensions/evaluator/results/
See templates/evaluator-result-template.json for a starting point.
- Generated assertions MUST remain distinguishable from observed evidence.
- Model self-attestation MUST NOT satisfy an evidence gate by itself.
- Contradictions MUST be preserved, not collapsed into a single synthesized answer.
- Insufficient evidence MUST be represented explicitly rather than inventing certainty.
- Deterministic checks SHOULD run before probabilistic review where appropriate.
- Higher-risk work MAY require evaluator independence — the same model/family SHOULD NOT both generate and independently evaluate the result. An independent evaluation is not itself a certification.
spec-kit-evaluator/
├── evaluators.yml # Registered evaluators (spec-structure)
├── schemas/
│ └── evaluator-result.schema.json # JSON Schema for evaluator results
├── templates/
│ └── evaluator-result-template.json # Template for new evaluator results
├── scripts/
│ ├── python/spec_structure_evaluator.py # Reference evaluator (FR-7)
│ ├── python/compose_results.py # Composition (Python)
│ ├── bash/compose-results.sh # Composition (Bash)
│ └── powershell/compose-results.ps1 # Composition (PowerShell)
├── examples/
│ ├── demo.py # Quick-start demo
│ └── fixtures/spec-structure/ # Passing + failing fixture specs
└── tests/ # unittest suite (python3 -m unittest discover -s tests)
Installed into a project (.specify/extensions/evaluator/):
├── results/ # Individual evaluator result files
│ ├── <evaluator-id>-<phase>-<ts>.json
│ └── composed-<phase>-<ts>.json
└── reports/ # Human-readable reports (markdown format)
└── report-<phase>-<ts>.md
Report suspected vulnerabilities privately using the process in SECURITY.md. Ordinary bugs and enhancement requests belong in GitHub Issues.
MIT — see the LICENSE.