Build1 publisher3 min readPublished
The third answer: a dead-code tool allowed to say "not traced yet"
A dev.to write-up describes a statement-level dead-code tracer whose useful feature is an unresolved state. Two runs of the same skill still disagreed by 66 statements.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction
What happened
- The author states that every dead-code tool they have used has exactly two answers: used, or unused.
- When the tool cannot see a link - because the name is built at runtime, because a class is addressed through a variable, or because a framework reads a value instead of calling a function - it still has to pick one of its two answers, and it picks wrong.
- The author says tools split into two camps: some hedge with a confidence percentage, others commit, delete live code, and hand the user an exception list to maintain by hand for ever.
- SPIDER is an agent skill; it does not search for names, it traces.
- Method: split the codebase into individual top-level statements, each with a number and an exact address (file, first line, last line); find the entry points that something outside the program starts; walk from each entry point along the links one statement at a time; whatever the walk reaches is alive, whatever it never reaches is not needed.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
A dev.to post describes a dead-code analyser called SPIDER, built as an agent skill, whose design choice is a third answer alongside used and unused: unresolved [4][9]. That third answer is the whole engineering argument, because a two-answer tool has to file a verdict whenever reflection or framework indirection hides a link, and according to the author it files the wrong one [1][2].
The failure mode is familiar to anyone who has run one of these over a real repository. When a name is built at runtime, a class is addressed through a variable, or a framework reads a value instead of calling a function, the analyser cannot see the edge but still has to answer [2]. The author says tools then split into two camps: some emit a confidence percentage and leave the operator to it, others commit, delete live code, and hand back an exception list to be maintained by hand for ever [3].
The method is tracing rather than name search [4]. The codebase is split into individual top-level statements, each numbered with an exact address of file, first line and last line; entry points are identified; the walk proceeds from each entry point along links one statement at a time; whatever the walk reaches is alive, and whatever it never reaches is not needed [5]. It runs one statement at a time across Python, TypeScript, JavaScript and CSS in a single list under one rule [6], so a stylesheet can be reported as three live rules and one dead one instead of judged as a file [7], and when a TypeScript file addresses a CSS class both ends of the link sit in the same model [8]. Statements whose links cannot be established are set aside as unresolved, then opened in the real source and settled one by one before they are given a final verdict [9]. One rule outranks the rest: a name assembled at runtime is never declared unused, which covers styles[status], getattr(obj, name), and classes built by string concatenation [10].
On a production Next.js application, the reported run covered 858 statements across 66 files [11] and returned 121 statements nothing in the running program reaches, described as two entire stylesheets, one abandoned screen with its stylesheet, and nine separate dead rules, or one statement in seven [12] - about 14 percent [1]. Five style rules that a name-based search had wrongly condemned were addressed through an assembled name and survived because of the unresolved state [13]. The 121-item list, each entry carrying its address, is presented as the deliverable: bounded work of known size handed back to an agent, rather than "audit my repository" [14].
The honest part of the post is the part that undercuts it. Execution is by a model, not a compiler [15], and the same skill run by two different models over the same project produced 121 unneeded statements and 55 [16] - a gap of 66 statements, or 55 percent of the larger count [2]. The author traces the whole difference to one decision at step two, where one run concluded from its own general knowledge of browsers that every CSS rule is an entry point, which the skill never says [17]. Both reports read as equally sound [18]. Adding more prose to the instructions did not fix it [19]; what the repository ships instead is an evals folder with a small project carrying six deliberate traps, including a stylesheet nobody imports, a name assembled at runtime, a bundler directive, and data the framework reads rather than code somebody calls [20].
Worth watching whether that answer key actually catches an invented entry-point rule, and whether the unresolved queue gets worked or quietly becomes permanent limbo. All of the above is one self-reported project.