# Your tests share your blind spots. Readers don't.

> Source: <https://dev.to/siddharth_pandey_27/your-tests-share-your-blind-spots-readers-dont-524j>
> Published: 2026-09-24 05:52:04+00:00

On June 14 I shipped a resolver with a green test suite. On September 1 I published a post about it. On September 4, three days later, I filed an issue crediting three people from the comment thread, each of whom had found something my tests never would.

That is the whole hot take, so here it is up front: **your tests share your blind spots. Strangers reading your code don't.** Tests encode the questions you already thought to ask. Readers who didn't write the code ask different ones. For tools that AI agents depend on, where a wrong answer looks exactly like a right one, I now think the second group is worth more than the first.

This post is part dev diary, part argument. Everything in it comes from the public tracker of [Infrawise](https://github.com/Sidd27/infrawise) ([npm](https://www.npmjs.com/package/infrawise)), the MCP server I build that gives coding agents context about live infrastructure. Mainly [#132](https://github.com/Sidd27/infrawise/issues/132), [#133](https://github.com/Sidd27/infrawise/pull/133) and [#134](https://github.com/Sidd27/infrawise/issues/134).

An agent editing `checkout.ts` wants to know what calls that code in production and what the event looks like. The catch: deployed names almost never match source names. You write `export const handler` in `checkout.ts`; your stack deploys it as `checkout-handler-prod`. Something has to connect the two.

Infrawise has two linkers for that. One reads your Terraform or CDK and links by the declared handler path, labelled `proven`. The fallback, `HeuristicLinker`, normalizes names and compares them:

``` js
const STAGE_TOKENS = new Set([
  'prod', 'production', 'dev', 'development', 'staging', 'stage', 'test', 'qa',
]);
const NOISE_TOKENS = new Set(['handler', 'fn', 'func', 'function', 'lambda']);

export function normalizeName(raw: string): string {
  let s = raw.toLowerCase().trim();
  s = s.split('/').pop() ?? s;
  s = s.replace(/\.(ts|js|mjs|cjs)$/, '');
  let segs = s.split(/[-_.\s]+/).filter(Boolean);
  if (segs.length > 1 && STAGE_TOKENS.has(segs[segs.length - 1] ?? '')) segs.pop();
  segs = segs.filter((seg) => !NOISE_TOKENS.has(seg));
  return segs.join('');
}
```

`checkout-handler-prod` becomes `checkout`. The file `checkout.ts` becomes `checkout`. Match, link, label it `inferred`.

The linker loop at the time looked like this (trimmed, not current code):

``` js
for (const lam of lambdaNodes(graph)) {
  const target = normalizeName(lam.name);
  if (!target) continue;
  const matches = fns.filter(/* normalized name or file equals target */);
  if (matches.length !== 1) continue;
  links.push({ lambdaId: lam.id, functionId: matches[0].id, confidence: 'inferred' });
}
```

And the tests shipped alongside it had one I was quietly proud of:

``` js
it('does not link when multiple functions match (ambiguous)', () => { ... });
```

Several source functions match one deployed function? Refuse. I had thought about ambiguity. Tested it. Green.

Here is what I had not thought about, and so had not tested: the same question in the other direction.

Two deployed functions: `checkout-handler-prod` and `checkout-dev`. Both normalize to `checkout`. Each one, on its own, finds exactly one matching source function, `checkout.ts`, so each one links. The loop asks "does this deployment match exactly one function?" and never asks "does any *other* deployment match the same function?"

My test for ambiguity was the mirror image of my code. The code checked one direction; the test checked the same direction. They agreed with each other perfectly, and both were blind to the same case. That is not bad luck. It's structural: when the same person writes the code and the tests, the tests inherit the author's model of the problem, gaps included. The suite stayed green for 82 days.

In the comment thread on [Your agent trusts the first match. Should it?](https://dev.to/siddharth_pandey_27/your-agent-trusts-the-first-match-should-it-350k), [@anp2network](https://dev.to/anp2network) read the published package, traced that both linkers returned only successes with no diagnostics channel, and made the point I built the fix around: whether a match was *authored* by the resolver or *observed* in the data has to be recorded at the moment the grouping is built. Normalization deletes `handler`, `prod` and `dev`, the exact tokens that might have told the two deployments apart. After that, nothing downstream can get them back.

The subtle part: the link might even have been correct. `checkout-handler-prod` and `checkout-dev` could be the same code in two stages. Or two unrelated handlers that share a prefix. The resolver couldn't know, and it didn't say it couldn't know.

[@prpatel05](https://dev.to/prpatel05) argued that `ambiguous: true` is a measurement, not a decision, and a response should be unusable until something narrows it to one match. [@kenwalger](https://dev.to/kenwalger) separated `proven` and `inferred` as answers to two different questions, and argued that a resolver needs permission to say "undetermined", or ranking forces it to manufacture certainty.

None of those three points were in my test file. None of them could have been. I would have had to already know them.

This is the part I didn't expect. The thread didn't stop at comments.

| When (UTC) | What | 
|---|---|
| Sep 1, 09:09 | Post published | 
| Sep 4, 05:49 | I file #132: four points, all three commenters credited | 
| Sep 4, 17:57 | Ken's PR #133 lands: an optional `file` input for`analyze_function` (point 3) | 
| Sep 4, 18:14 | Ken files #134, found *while implementing* #133 | 
| Sep 4, 19:19 | v0.28.0 released with the `file` input | 
| Sep 4, 19:41 | #134 closed by the linker fix (points 1 and 2) | 
| Sep 5, 12:11 | v0.28.1 released with the linker fix | 

`analyze_function` already refused to pick one and named them as `candidateLambdas`. But the same response also sent this, unconditionally:

```
"triggers": []
```

Ken's issue put it exactly: that is "a claim about the world rather than a statement about what the tool could determine." An agent reading `triggers: []` concludes the function isn't event-driven, and writes a handler with no idea what the event looks like. My tests checked that `candidateLambdas` appeared. None of them asked what `triggers` meant next to it.

Ken also argued against their own proposed fix, which is rare and useful:

Any caller doing `result.triggers.map(...)` breaks when the key disappears.

We shipped it anyway, with a reason code alongside so the key doesn't just vanish. A caller that crashes on a missing key is annoying. A caller that confidently writes the wrong handler because an empty array lied to it costs you an incident.

The rule now sits in the linker as a comment:

```
// A refusal is recorded, never encoded as absence: a Lambda with five matches
// and a Lambda with none must not produce the same output.
export interface LinkResult {
  links: LambdaCodeLink[];
  unresolved: UnresolvedLambdaLink[];
}
```

Every refusal carries a reason: `no_match`, `multiple_functions` (candidate functions listed), or `multiple_lambdas` (the other colliding deployment names listed). The collision check lives where the grouping is built, because that's the only place the deleted tokens still exist:

```
// normalizeName deletes the tokens that tell `checkout-handler-prod` from
// `checkout-dev`, so the collision has to be caught here, where the class
// is built: nothing downstream can recover the deleted tokens.
for (const [key, lams] of byKey) {
  if (lams.length > 1) {
    for (const lam of lams) {
      out.unresolved.push({
        lambdaId: lam.id,
        reason: 'multiple_lambdas',
        candidates: lams.filter((l) => l !== lam).map((l) => l.name),
      });
    }
    continue;
  }
  // exactly one deployment for this key: match as before
}
```

Asking about `handler` in `checkout.ts` now returns (trimmed):

```
{
  "function": "handler",
  "found": true,
  "matches": [{ "file": "/repo/src/checkout.ts", "accesses": [ ... ] }],
  "unresolvedLambdas": [
    { "lambda": "checkout-handler-prod", "reason": "multiple_lambdas", "candidates": ["checkout-dev"] },
    { "lambda": "checkout-dev", "reason": "multiple_lambdas", "candidates": ["checkout-handler-prod"] }
  ],
  "triggers": []
}
```

`triggers` is still empty here, because nothing linked, and now the reason sits next to it. The agent can name both deployments and ask which one you mean. When several deployments *do* link to one shared function, `triggers` is left out of the response entirely.

And the test file finally has the case it was missing: two deployments normalizing to one key, zero links, two refusals naming each other.

I went back through the tracker to check whether I was generalizing from one lucky thread. In June, one commenter on [Why Infrawise Uses Deterministic Analysis Instead of an LLM](https://dev.to/siddharth_pandey_27/why-infrawise-uses-deterministic-analysis-instead-of-an-llm-15fk), [@ggle_in](https://dev.to/ggle_in), raised three separate ideas that became issues #55, #56 and #57 on the same day. #55, surfacing how old the analysis is, shipped two weeks later and grew into the freshness block every tool response now carries.

Two posts, two threads, and between them most of the design changes I'm proudest of this year. In both cases the comments beat my tests, because the commenters had no stake in my model of the problem being right.

I'm not arguing tests are useless. The new collision test will stop this exact bug from coming back, and nothing a commenter says does that. Tests are how you keep a lesson. They're just a poor way to learn one.

It's also not free. Publishing internals with file paths and line numbers means people will find your mistakes in public. The heuristic now links less than it did: on an account where `orders-prod` and `orders-dev` really are the same code, the old version was right by luck and the new one refuses. And two points from #132 are still open. [#140](https://github.com/Sidd27/infrawise/issues/140): the `proven` label comes from the handler *declared* in IaC, while the *deployed* handler is fetched and then thrown away, so where they disagree, `proven` sits on the stale one. [#131](https://github.com/Sidd27/infrawise/issues/131): when several deployments link to one scanning function, a high-severity finding names only one of them, picked by iteration order.

Both came straight out of that thread (#131 filed alongside #132, #140 split out of it later), and neither had a failing test.

So here's the question I actually want answered: where did the last bug your tests couldn't have caught come from, a user, a reviewer, or a stranger reading your code?
