# tf-0002: DAG EdgesTo O(E) full-edge scan inside O(V) vertex loop — O(V×E) CBD transform

**Severity:** MEDIUM
**CWE:** CWE-407 (Algorithmic Complexity — linear membership test in hot loop)
**Speedup:** ~100x at V=200 chain graph (verified by unit test)
**Target:** Terraform (hashicorp/terraform)
**Files:**
- `internal/dag/graph.go:79-90` — `EdgesTo()` iterates all edges O(E) per call
- `internal/terraform/transform_destroy_cbd.go:122,136` — `EdgesTo(v)` called inside `for _, v := range g.Vertices()`

## Description

`CBDEdgeTransformer.Transform()` iterates all vertices and calls `EdgesTo(v)`
for each destroyer vertex:

```go
for _, v := range g.Vertices() {         // O(V)
    ...
    for _, e := range g.EdgesTo(v) {     // O(E) — scans ALL edges
```

`EdgesTo()` scans the full edge list filtering by target:

```go
func (g *Graph) EdgesTo(v Vertex) []Edge {
    var result []Edge
    search := hashcode(v)
    for _, e := range g.Edges() {        // O(E) linear scan
        if hashcode(e.Target()) == search {
            result = append(result, e)
        }
    }
    return result
}
```

Total: O(V × E). For V=500 resources and E=1000 edges, this is 500,000
comparisons per destroy plan instead of ~1,500.

## Root Cause

`EdgesTo` queries the full edge set rather than the `upEdges` index that
the graph already maintains. `upEdgesNoCopy(v)` returns the set of source
vertices for target `v` in O(1) via map lookup.

Fix: rewrite `EdgesTo` to use `upEdgesNoCopy(v)`:

```go
func (g *Graph) EdgesTo(v Vertex) []Edge {
    sources := g.upEdgesNoCopy(v)
    result := make([]Edge, 0, sources.Len())
    for _, src := range sources {
        result = append(result, BasicEdge(src.(Vertex), v))
    }
    return result
}
```

Cost becomes O(in-degree(v)) per call; O(E) total across all vertices.

## Patch

See `patch/tf-0002-dag-graph-edgesto-upedges.patch`

## Complexity Before

`EdgesTo(v)` per call: **O(E)**
`CBDEdgeTransformer.Transform()`: **O(V × E)**

## Complexity After

`EdgesTo(v)` per call: **O(in-degree(v))**
`CBDEdgeTransformer.Transform()`: **O(E)** total

## Reproduction

```
cd defects/terraform/unit && javac -d . TerraformDagTest.java && java -ea unit.TerraformDagTest
```

test3: V=200 chain, defect=39800 ops, fixed=399 ops, ratio≈100x
