Skip to content

feat: add ScenarioRank Phase 3A evaluation harness - #8

Draft
abdo2006-dev wants to merge 6 commits into
mainfrom
v2/phase-3a-evaluation-harness
Draft

feat: add ScenarioRank Phase 3A evaluation harness#8
abdo2006-dev wants to merge 6 commits into
mainfrom
v2/phase-3a-evaluation-harness

Conversation

@abdo2006-dev

@abdo2006-dev abdo2006-dev commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Draft — do not merge.

Phase 3A remains an evaluation-only harness over the real production pipeline. This PR does not change production scoring, ranking, pairing, prompts, provider behavior, HTTP behavior, frontend behavior, or shared response contracts. SR-P3A-001 remains intentionally unfixed. No live evaluation or OpenAI request was made; Phase 3B remains unstarted.

Final hardening

  • Structured findings are the source of truth for every grader detail. Known-defect suppression requires complete, exact scoped matching, so SR-P3A-001 plus any unrelated contract/score finding remains a required failure.
  • The conservative estimator derives its frozen token/retry policy from the production export (12 worst-case pairing attempts; 8 non-pairing). Budget is verified before provider construction and before every execution.
  • Run-state precedence is unexpected_failure, then baseline_change_required, pass_with_known_defects, and clean_pass; Markdown reports full fixture and defect-comparison counts.
  • Released benchmark validation cross-checks a repository-level release registry. eval:update-integrity is explicit, local-only, requires a reason and version/metadata change, and writes digest provenance without committing.
  • Comparison identity preserves execution/scenario/variant/repetition. CLI numeric parsing is strict; live budget is capped at $100 and repetitions at 20.

Current offline baseline

pass_with_known_defects: 16/16 cases complete without unexpected failure; 12 clean cases; 8 expected observations across 4 affected executions; 0 unexpected failures and 0 unexpected defect resolutions.

Verification

  • npm run lint, npm run lint:server, npm run typecheck, all project guards, npm run build, and node --check server.mjs passed.
  • npm run eval:validate and repeated npm run eval:fixtures -- --no-write passed.
  • npm test passed: 646 tests (103 frontend + 217 server + 326 evaluation).
  • npm ls passed; git diff --check passed. npm audit could not contact npm's audit endpoint in the restricted environment.

Adds decision-benchmark-v1: 16 fully synthetic decision cases (21 scenario
executions) covering basic ranking, multi-scenario behaviour, evidence
quality, input-permutation robustness, and pairing.

The benchmark separates two things that are routinely conflated. Each case
carries deterministic_expectations (objectively checkable: candidate and
scenario coverage, pair completeness, logical-stage accounting, whether the
reported winner matches the deterministic ranking) and rubric_dimensions
(human judgment: grounding, trade-off clarity, uncertainty handling). No case
hardcodes one "correct" natural-language answer: where several winners are
defensible, allowed_winner_ids lists all of them, and where the evidence is
too thin or contradictory to justify any winner, the case makes no claim at
all and checks instead that every candidate is flagged for human review.

Versioning is enforced rather than documented: case IDs are immutable, a
meaning change requires a new benchmark_version, a cosmetic change increments
metadata_revision only, and a loader refuses an unsupported schema_version
rather than attempting a best-effort read of an unknown format.

Every case declares synthetic: true and data_policy: "synthetic-only" as
schema literals, not booleans a later case could quietly flip. Every
candidate, company, and record is invented.

Case inputs are validated against the production evaluationRequestSchema, so
the benchmark cannot describe a request the server would reject.
Adds the harness that executes decision-benchmark-v1 against the real
production pipeline: real prompts, real schemas, real deterministic scoring,
real batch-identity validation.

Eleven deterministic graders cover contract validity, candidate coverage,
scenario coverage, ranking consistency, score integrity, pairing integrity,
pipeline accounting, not_measured honesty, winner expectations, unsupported
claims, and uncertainty acknowledgement. Score integrity recomputes every
recomputable deterministic value using the production formulas in
server/domain/scoring.js; weighted_fit_score is excluded and the reason is
stated, because normalised criterion weights are not part of the public
response.

The unsupported-claim checks are deliberately conservative and scoped to
model-authored narrative fields only, so the pipeline's own honest "has not
been measured" wording cannot trip them. The narrative-contradiction check is
name-based and reports itself skipped when the winner's display name is shared
by another candidate, rather than guessing.

Seven offline fake-provider profiles: three valid, and four deliberately
invalid so the graders are proven to catch real defects rather than only ever
observed passing. Scores are keyed by candidate ID, never by array position,
which is what makes the candidate-order permutation a test of the pipeline
instead of a test of the fixture.

Live mode is gated hard and was never executed: it requires --live, an API
key, an explicit positive budget, and a deliberate case selection; it refuses
CI by default; it refuses a model with no recorded pricing, because a budget
that cannot be computed cannot be enforced; and it stops before starting any
execution whose worst case would breach the limit. The provider factory is
imported lazily, so a refused invocation never constructs an OpenAI client.

Comparison returns improved/regressed/unchanged/inconclusive. Only required
invariants move the verdict; cost, token, and duration deltas are reported raw
and explicitly marked not_assessed, because two runs cannot support a
significance claim. An output change with no invariant change is inconclusive,
not unchanged.

Artifacts go to .eval-runs/ and are schema-validated and scanned for secrets
and absolute paths before they are written, not after.

A case may declare known_defects for documented, pre-existing product defects.
Those failures stop gating the exit status, but a case-level check raises a
required failure if a known defect ever stops reproducing, so a suppression
cannot outlive the defect it describes.
Adds 309 evaluation tests in their own Vitest project, so frontend, backend,
and evaluation counts stay independently reportable. None of them makes a
network request and none of them calls OpenAI.

Every grader is proven twice: once that it passes a genuinely good result
produced by executing the real pipeline, and once that it catches a specific,
realistic defect. A grader that has only ever been seen to pass is not
evidence of anything.

Coverage includes benchmark schema rejection paths (duplicate case IDs,
unsupported schema versions, invalid tags, malformed candidate IDs, missing
synthetic-data policy, mismatched pair counts and stage counts, winner
expectations naming unknown candidates); case validation against the
production request contract; runner behaviour with every network primitive
disabled and no API key present; determinism of decision content across
repeated runs; live-mode refusals and budget arithmetic driven entirely by
injected values; comparison verdicts including the incompatible-benchmark
refusals; and permutation-variant linkage.

Repository-protection tests enforce the boundaries rather than trusting them:
production imports nothing from evals/, .eval-runs is ignored by git itself,
no committed evaluation file contains a secret or an absolute machine path,
the request observer records no payloads, every CLI supports --help and exits
nonzero on a required failure, and no dependency was added.

Wires npm run eval:validate/fixtures/live/compare, adds evals/ to lint:server,
includes test:evals in npm test, and ignores .eval-runs/.
Adds docs/evaluation/ (architecture, benchmark v1, human review guide,
runbook) and ADR-0009 recording the local-first evaluation decision and the
benchmark-versioning policy.

ADR-0009 explains why the hosted OpenAI Evals API was deliberately not
integrated in this phase: a hosted service sees prompts and completions but
cannot observe the deterministic layer most of these checks target, it
requires network access and spend for every run, and it would reintroduce, at
the evaluation layer, the vendor coupling the provider contract removed. The
seams are drawn so it can be added later as a provider factory plus a
reporter.

Documents why evaluation precedes prompt optimisation, what a fixture run does
and does not prove, deterministic versus qualitative grading, live-run safety
and budget controls, artifact privacy, and the explicit limits of the
comparison command.

Records the real defect the harness found on its first run and deliberately
did not fix. SR-P3A-001: computeRiskAdjustedScore can return a negative value
for a weak candidate, while completedPipelineResponseSchema bounds
risk_adjusted_score to 0-100 and server/http/routes.js validates its own
response before sending — so a run containing a weak enough candidate returns
a generic 500 after every OpenAI call has already been paid for. Phase 3A is
scoped to measurement and forbidden from changing scoring or contracts, and
either candidate fix moves the baseline the harness measures from, so it is
tracked as a known defect (KNOWN_LIMITATIONS.md P0.7) rather than patched.

Also records a second gap surfaced while building case-002: ScenarioRank has
no near-tie uncertainty signal at all, so a decision separated by noise is
never flagged for human review (P2.5).

States plainly, in the docs and inside every run artifact, that this is a
development benchmark: not scientifically validated, not representative of
real hiring decisions, not evidence of fairness or demographic neutrality, not
a legal-compliance test, not a calibrated-confidence benchmark, and not a
production service-level objective. Phase 3B has not started.
@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
scenario-rank-ai Ready Ready Preview Aug 3, 2026 7:44pm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant