DEV Community

Cover image for The Test Suite Was Green. Scout Was Still Wrong.
Bradley Matera
Bradley Matera

Posted on

The Test Suite Was Green. Scout Was Still Wrong.

I found a bug in Scout that looked, at first, like a small classification mistake.

The assistant was being asked:

Is LeetCode documented as a gap?

The knowledge base clearly contained that relationship. The model had enough evidence to answer yes. The generated answer could even be semantically correct.

The system still treated the request like a different question:

Does Bradley know LeetCode?

That distinction is the entire story.

Scout did not fail because it lacked a fact. It failed because the fact was attached to the wrong proposition somewhere between the user’s sentence and the final response.

Scout meaning preservation

The job is not finished when the model produces a fluent sentence. The original question has to survive the entire trip.

This is the next stage of Scout: not just retrieving information and asking a model to phrase it, but preserving what the user actually asked long enough for the model to answer that question.

The question looked simple because the failure was upstream

The knowledge graph contains a relationship equivalent to:

{
  subject: "Bradley",
  relation: "has_gap",
  object: "LeetCode"
}
Enter fullscreen mode Exit fullscreen mode

That relationship supports one answer very clearly:

Yes, LeetCode is documented as a gap.

It does not support this answer:

Yes, Bradley knows LeetCode.

Those sentences mention the same object, but they make different claims about it. One asks about a documented gap. The other asks about verified proficiency.

The correct semantic states are different:

User question What Scout must determine Allowed answer
Does Bradley know LeetCode? Is verified proficiency documented? Unknown or qualified
Is LeetCode documented as a gap? Is has_gap(Bradley, LeetCode) verified? Yes, affirmatively

The first version of this path allowed the second question to fall into a generic negative-assessment route. That route made sense for unsupported skills. It did not make sense for a verified gap relationship.

Scout proposition versus entity evidence

Resolving the entity is only the beginning. Scout still has to resolve the relationship being asked about.

The important change was not a better sentence in the prompt. It was carrying the requested proposition as structured state:

requested proposition: has_gap(Bradley, LeetCode)
direct answer: YES
fact state: TRUE
required stance: AFFIRM
Enter fullscreen mode Exit fullscreen mode

Once that contract exists, the model has room to write naturally without having to guess what “documented as a gap” means.

The difference between an entity and a proposition

This distinction sounds obvious when it is written down. It is much easier to lose inside a live system.

Entity resolution answers questions like:

  • Which project does “that app” refer to?
  • Is “LeetCode” the same object as the item in the gap list?
  • Is the user asking about Bradley, a project, a company, or a technology?

Proposition resolution answers a different question:

  • What relationship is the user asking me to evaluate?

The entity can be correct while the proposition is wrong. Scout can resolve LeetCode perfectly and still answer the wrong question about LeetCode.

That is why a verified entity cannot be treated as blanket permission to make every claim involving it. Evidence has scope. A project’s React evidence does not automatically prove its deployment history. A gap record does not automatically prove proficiency. A source title does not automatically prove every sentence that contains a capitalized word from that title.

The contract has to carry the relationship, not only the nouns.

Scout semantic reliability pipeline

Scout’s request path: resolve the conversation, identify the proposition, retrieve evidence, generate, validate, and recover without changing the question.

Conversation state is part of the answer

The proposition cannot always be read from the latest message alone.

Consider this conversation:

What roles fit me?

What about DevOps?

QA too.

Which is the strongest fit?

The last question does not name the comparison set. The assistant has to know that DevOps and QA were introduced as alternatives, not as properties of one project. It also has to avoid inventing a winner if one of the alternatives has incomplete evidence.

The same problem appears in shorter follow-ups:

Follow-up Likely interpretation
“What about DevOps?” Add or switch to another role
“What about its deployment?” Ask for a facet of the current project
“The other project” Resolve an existing comparison member
“No, I meant QA” Correct the current frame

This is why Scout has a server-owned discourse frame. It tracks alternatives, corrections, ordinals, and facets instead of forcing the model to reconstruct the entire conversation from raw text on every request.

The model still writes the answer. It should not be responsible for inventing the conversation state that determines what the answer means.

Scout discourse frame

A discourse frame keeps alternatives, corrections, and facets from collapsing into one vague “active topic.”

Three kinds of evidence were being collapsed into one

Scout now keeps three separate axes:

  1. Conversation membership: did the user introduce this entity or alternative?
  2. Entity knowledge: what does the knowledge package document?
  3. Proposition support: does that evidence support this exact relationship?

An entity can be part of the conversation while a property is unknown. A project can be documented while its deployment history is not. A technology can appear in a gap list without being a verified skill.

Scout open-world evidence states

Supported, unknown, and contradicted are different evidence states, not different tones for the same answer.

This is an open-world rule. If Scout cannot find evidence that Bradley has used Rust, that does not automatically prove he has never used Rust. If Scout finds an explicit gap relationship, that is a supported statement about the gap, not an excuse to infer every other skill state.

For comparisons, the planner uses three-valued coverage:

  • FULL: comparable evidence exists for every member.
  • PARTIAL: evidence exists for some members, but not enough for a complete comparison.
  • UNKNOWN: the requested comparison is not supported by the available evidence.

That prevents the assistant from turning missing documentation into a ranking.

The evidence packet was teaching the model the wrong language

Scout also had a problem that was obvious in generated answers but easy to miss in code.

The model was receiving internal labels similar to:

FACT 2: LeetCode is documented as a learning gap
Enter fullscreen mode Exit fullscreen mode

Sometimes it copied the label into the response. The validator rejected that correctly. A visitor should not receive a sentence that says “according to FACT 2.”

The deeper issue was architectural. The system was showing the model syntax that it did not want the model to imitate.

The evidence now has two representations. Internally, Scout keeps source identity, relationship ownership, and block boundaries for validation. The model receives clean factual text:

Evidence:
- LeetCode is documented as a learning gap.
- ProjectHub uses React and Node.js.
Enter fullscreen mode Exit fullscreen mode

Scout evidence provenance

Provenance remains structured for the validator while the model receives readable evidence instead of internal labels.

That separation is important for same-block validation. If one block says Atlas uses React and another says Orion uses Vue, the validator must not conclude that Atlas uses Vue because both facts appeared in one prompt.

The model should receive facts. The validator should retain ownership. Neither component should have to reverse-engineer the other’s representation from a string full of internal markers.

Scout evidence packet

The evidence packet is a transport boundary: enough context to generate, enough structure to validate, and no internal scaffolding for the model to imitate.

Why a fluent answer can still fail validation

Some failures were not model hallucinations at all.

An early relationship validator could see a project name, a relation verb, and a technology-looking word in the same sentence. It could then interpret the later word as a project technology even when the model had not actually made that claim.

That is a clause-boundary problem. The fix is not to disable relationship validation. The fix is to understand where one claim ends and another begins.

The same principle applies to entity grounding. A long source title can be a legitimate entity. A sentence-initial discourse word can be ordinary English. Expanding capitalization heuristics forever is not a substitute for structured provenance and better extraction rules.

Every remaining validation failure needs a cause, not just a label:

Failure Possible cause Correct response
entity_not_grounded hallucination, missing alias, or extractor false positive reject, normalize, or fix extraction
unsupported_relationship model claim, missing graph edge, or validator mistake trace subject, relation, object, and evidence
unsupported_tech_claim missing provenance or real hallucination repair the evidence path, not a global whitelist
overclaim claim exceeds tenant evidence or contract enforce the claim ceiling

This is why I do not want to weaken validators just to raise a score. A validator that rejects supported language is broken, but a validator that accepts unsupported claims is also broken.

Scout validation and repair loop

Validation is not a final decoration. It is the boundary that decides whether generated language still matches the supported meaning.

Repair must preserve the same contract

Scout has a repair path for answers that fail validation. Repair can remove a scaffold leak, correct an unsupported technology, or fix an answer that takes the wrong stance.

But a second model call is not automatically recovery. It is recovery only if it receives enough of the original semantic state to answer the same question:

  • original user question
  • resolved question
  • active entity
  • requested proposition
  • direct answer
  • fact state
  • required stance
  • claim ceiling
  • forbidden claims
  • canonical evidence
  • validation diagnostics as errors, not facts

Scout repair boundary

A rejected draft can provide wording context. It cannot become evidence just because it was generated earlier.

If the first draft invents Kubernetes, the repair must remove Kubernetes. It must not repeat the word because it appeared in the rejected answer.

The repair packet needs separate namespaces:

FACTUAL EVIDENCE
Verified facts from the tenant knowledge package.

SEMANTIC CONTRACT
What proposition the user asked about and what stance is allowed.

VALIDATION DIAGNOSTICS
What the previous draft did wrong.

REJECTED DRAFT
Optional wording context. Not evidence.
Enter fullscreen mode Exit fullscreen mode

This matters for future-capability questions too. “Could Bradley learn COBOL?” is not the same as “Is COBOL currently documented as one of his skills?” A repair that turns future potential into a current-state denial may avoid one overclaim while still answering the wrong question.

The numbers only matter when their scope is clear

The qualification work produced useful measurements:

Check Result
Local tests 1,159 / 1,159
Retrieval Recall@6 1.000
Retrieval MRR@6 0.942
Targeted live battery 22–23 / 23 across runs
Larger conversation gate 95 / 132 turns; 20 / 33 conversations

Those are not one accuracy percentage. They measure different layers of the system.

Scout qualification test layers

Unit, retrieval, conversation, and deployment checks measure different layers of the system.
  • Unit tests ask whether a rule behaves as written.
  • Retrieval tests ask whether the right evidence can be found.
  • Live conversations ask whether meaning survives state, generation, deadlines, and validation.
  • Deployment checks ask whether the intended runtime is actually serving requests.

The larger gate combines model generation, conversation state, retrieval, contract construction, validation, repair, provider behavior, and the evaluation harness. That makes it useful, but it also means a failed turn is only the beginning of the investigation.

The next useful report is a failure corpus: stance mismatches, entity-grounding errors, unsupported relationships, unsupported technologies, scaffold leakage, true prompt leakage, provider failures, deadline failures, repair attempts, and accepted repairs.

Scout is still generative

The surrounding system is becoming more deterministic because the model should not have to rediscover the boundaries of the question on every request.

The division is:

Code decides:
- what the user is asking
- which entities and relationships are involved
- what evidence is supported
- what stance is allowed
- what claims must be rejected

The model decides:
- how to express that supported meaning naturally
Enter fullscreen mode Exit fullscreen mode

There are no benchmark-specific “if the question contains LeetCode, print this paragraph” answers in the intended design. The LeetCode fix works because has_gap is represented as a supported proposition. The same machinery should handle a warranty question, a project deployment question, or a future-role question without adding a new canned response.

Scout qualification layers

Reliable generation comes from combining the layers, not pretending any one layer can replace the others.

What changed for me

Before this bug, I thought of retrieval as the grounding layer. Now I think of grounding as a chain:

  1. Resolve the conversation.
  2. Identify the exact proposition.
  3. Retrieve evidence for that proposition.
  4. Preserve the contract through generation.
  5. Validate the meaning of the generated claim.
  6. Repair the answer without changing the question.

If any link changes the proposition, the final answer can be wrong while every sentence still sounds professional.

That is more dangerous than an obvious hallucination because it can survive a quick read. The words look reasonable. The evidence is real. The answer is still answering something else.

The LeetCode case gave me a small, clear example of a larger design rule: reliable generative software is not only about giving a model better facts. It is about preserving the meaning of the request while the software around the model does its work.

That is what Scout is working on now.

References

Top comments (1)

Collapse
 
rverwey profile image
Ryan VerWey

Great article brotha! Keep em coming.