fix(llm-rubric): decline instead of fabricating on cross-artifact predicates (closes #291) - #292
Merged
Merged
Conversation
…dicates (closes #291) When a rule's predicate ranges over artifacts absent from the rendered target (e.g. "classify every module under src/" judged against only the doc), prompt v7 emitted a confident pass/fail with fabricated within-target grounding rather than declining. The #291 dogfood run missed real drift (13 unclassified modules), false-flagged a correct `deferred` classification, and false-failed two code-invariant rules against a doc target. Fix direction (a): broaden the decline contract. v8 adds a second authorised `unsupported` reason, `unsupported_reason: "cross_artifact_predicate"`, valid for any rule (annotated or not). The model returns `unsupported` and names the absent artifacts instead of guessing; the backend maps this to `Status.UNSUPPORTED` with `evidence.kind=cross_artifact_predicate` and a remediation pointing at the existing S5 remedy (`params.target_scope` / multi-target, PR #290 / multi-target.md §9). Fail-closed: an explicit, evidence-bearing non-verdict, never a silent pass. Over-trigger guard: the instruction fires only when the absent artifacts are essential to the predicate — a rule that merely names another file but is decidable from the target is judged normally. Self-contained rules keep judging pass/fail. Aggregation (following the target-kind-mismatch precedent): a cross-artifact decline counts as an ordinary `unsupported` vote under consensus, and is conclusive without escalation under review/adaptive (a re-run against the same insufficient target cannot recover it). PROMPT_VERSION v7 -> v8 (eval-cache keys on it by design; old entries miss). Tests: parser accepts the new shape; single-strategy maps to UNSUPPORTED for annotated and unannotated rules; self-contained pass is not declined; consensus counts the decline as an unsupported vote; stray reason on pass is dropped. Two bench fixtures model the #291 scenario (a doc table + a src-tree completeness rule -> unsupported; a self-contained companion -> pass). Docs: docs/semantic-rules.md (cross-artifact predicates) and docs/llm-rubric.md (decline verdicts) document the behavior and the target_scope remedy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wx8qUs1ipy6Nf82hu2eDhg
Owner
|
@codex please review this PR |
gate-keeper dogfooding
Tokens: in=9546 out=615, latency: 7306 ms, model: gpt-5.4, prompt_version: v8 See |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 948d4c25f0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…-bearing Codex P2 (PR #292): a bench entry expecting `unsupported` that receives the new `cross_artifact_predicate` decline evidence was marked PASS, but the telemetry loop only recognised `llm_judgment` / `target_kind_mismatch` / `llm_quote_fabrication`, so those #291 fixtures reported tokens_in/out=0, latency_ms=0, and model/prompt_version=None despite the evidence carrying those fields. Add `cross_artifact_predicate` to `_TELEMETRY_BEARING_KINDS` and cover it with a bench test mirroring the target-kind-mismatch telemetry test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wx8qUs1ipy6Nf82hu2eDhg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Closes #291 (dogfood finding on trunk #63 — semantic rubric quality).
When an
llm-rubricrule's predicate ranges over artifacts not present in the rendered target (e.g. "the scaffold-status table should classify every module undersrc/", judged against only the doc), promptv7emitted a confident pass/fail with fabricated within-target grounding instead of declining. In the #291 live run againstyomotsusaka'srepo-rules.md(gpt-5.4), this:src/tree;deferredstub (transfer.py) as "already implemented";Fix — direction (a): decline rather than fabricate
Extends the existing
unsupporteddecline contract. Before v8 the only authorised decline was the target-kind-mismatch case (gated on atarget_kindannotation). v8 adds a second reason discriminator:cross_artifact_predicateis valid for any rule (annotated or not): the model returnsunsupportedand names the absent artifacts inprimary_reasoninstead of guessing. The backend maps it toStatus.UNSUPPORTEDwithevidence.kind=cross_artifact_predicateand a remediation pointing at the existing S5 remedy —params.target_scope/ multi-target (PR #290,docs/design/multi-target.md§9). Direction (b) already shipped; this fix points authors at it rather than rebuilding it.Fail-closed:
UNSUPPORTEDis a non-pass status — an explicit, evidence-bearing non-verdict, never a silent pass.Over-trigger guard: the instruction fires only when the absent artifacts are essential to the predicate. A rule that merely names another file but is decidable from the target itself is judged normally. Encoded in the prompt wording and tested on both sides.
Aggregation (following the target-kind-mismatch precedent): a cross-artifact decline counts as an ordinary
unsupportedvote underconsensus, and is conclusive without escalation underreview/adaptive(a re-run against the same insufficient target cannot recover it).PROMPT_VERSIONv7→v8(the eval-cache keys on it by design, so old entries correctly miss).Before / after (live, gpt-5.4)
Before (v7, #291 verdict against the doc):
status: failwith a fabricated reason ("transfer.py… already implements its MVP responsibility") — wrong, and silent about the 13 missing modules.After (v8, #291-shaped bench fixture):
status: unsupportedValidation
uv run pytest— 1751 passed (7 newTestCrossArtifactPredicateDeclinetests + updated v7→v8 assertions).uv run ruff check .,uv run ruff format --check .,uv run pyright— all green.completeness-06-cross-artifact-src-tree→unsupported(new decline reproduces on the real model)completeness-07-self-contained-doc-header→pass(over-trigger guard holds on the same target)clarity-01→ pass,clarity-02→ fail (no regression on grounded rules)target-kind-mismatch-01→ unsupported (existing decline path intact)Files
src/gate_keeper/backends/llm_rubric.py— schema field + parser, both prompt templates (v8), decline helpers, single/consensus/review wiring, adaptive docstring.src/gate_keeper/bench.py— recognisecross_artifact_predicateevidence for expected-unsupportedentries.tests/fixtures/semantic/— two llm-rubric: completeness/cross-artifact rules get fabricated evidence + missed drift (dogfood, #63) #291-modeled fixtures + target.docs/semantic-rules.md,docs/llm-rubric.md— cross-artifact-predicate guidance +target_scoperemedy.🤖 Generated with Claude Code
https://claude.ai/code/session_01Wx8qUs1ipy6Nf82hu2eDhg