Skip to content

bug(roles): dead-code verdicts emitted for parse-only languages (HCL) where call resolution is absent by design #2385

Description

@carlos-alm

Summary

For languages where call resolution is intentionally absent, role classification still emits dead-unresolved for every symbol. The verdict is 100% false-positive by construction, and it invites destructive action — an agent or human reading "132 dead symbols" in a Terraform repo may try to delete live infrastructure.

This is not a request for HCL call resolution. The README support matrix is explicit and correct that Terraform/HCL is parse-only ( for symbols, —³ for heritage/type-inference/call-graph, footnoted "declarative — no functions, classes, or type system"), and resolution-benchmark.test.ts sets hcl: { precision: 0.0, recall: 0.0 } under the comment "New fixture languages — no parser or call resolution yet". Absence of call edges for HCL is by design. Emitting a dead-code verdict anyway is the defect.

Reproduction

Minimal Terraform file where every resource is actively referenced:

resource "aws_kms_key" "state" { description = "state encryption key" }
resource "aws_s3_bucket" "state" { bucket = "my-state-bucket" }

resource "aws_s3_bucket_server_side_encryption_configuration" "state" {
  bucket = aws_s3_bucket.state.id                      # references aws_s3_bucket.state
  rule {
    apply_server_side_encryption_by_default {
      kms_master_key_id = aws_kms_key.state.arn        # references aws_kms_key.state
    }
  }
}

output "bucket_name" { value = aws_s3_bucket.state.bucket }   # references aws_s3_bucket.state
$ codegraph build
Native build orchestrator completed: 6 nodes, 5 edges, 1 files

$ codegraph roles
Node roles (4 symbols):
  dead-unresolved: 4
## dead-unresolved (4)
  - aws_kms_key.state  main.tf:1
  - aws_s3_bucket.state  main.tf:5
  - aws_s3_bucket_server_side_encryption_configuration.state  main.tf:9
  - output.bucket_name  main.tf:18

All 4 flagged dead; aws_s3_bucket.state is referenced three times. All 5 edges are contains — there are zero reference edges, as expected for a parse-only language.

Confirmed independently on optave/tenant-optave-autopilot-svc during the org rollout: 129 of its 132 dead-unresolved symbols are .tf, including KMS keys and buckets wired together in the same file.

Also worth noting: the HCL fixture's expected edges never materialize

tests/benchmarks/resolution/fixtures/hcl/expected-edges.json declares 2 calls edges (module.user_servicemodule.repository, → module.validators). Building that fixture yields 31 nodes, 27 edges, all contains — 0 of 2 expected edges. The benchmark passes only because the HCL threshold is 0.0/0.0. That's self-consistent with parse-only support, but it means the fixture and its manifest currently encode an aspiration no test enforces. Either wire the module-reference edges up, or annotate the manifest as pending so it isn't mistaken for a passing capability.

Suggested fix

Gate dead-code classification on whether the language has call-resolution support. For parse-only languages, either omit dead-* roles entirely or emit a distinct, clearly non-actionable role (e.g. unanalyzed). The language capability is already known — LANGUAGE_REGISTRY in domain/parser.ts is the single source of truth, and the benchmark thresholds already encode which languages have no resolution.

Whatever shape is chosen, apply it to the mirrored Rust path in crates/codegraph-core/src/graph/classifiers/roles.rs so the engines don't diverge.

Why this matters now

The org-wide rollout writes a .codegraph/basics.md health baseline into every Optave repo, including Terraform ones. Right now those baselines have to carry an explicit "ignore this number, it's a false positive" caveat — which is exactly the "documenting a bug as expected behavior" pattern CLAUDE.md warns against.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions