Help us shape the future of Go! Take the 2026 Go Developer Survey

deadset

module
v1.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 2, 2026 License: GPL-3.0

README

deadset

Go Reference Go version Mutation

One command for dead code across Go and TypeScript, resolving the references that cross the language boundary.

What it does

Dead code is a declaration nothing reaches: a function nobody calls, an export nothing imports, a file no build includes, a dependency no import needs. Each language has its own analyzer for that question. deadset is the command you run when a repository holds more than one language, and it does four things:

  1. Detects which languages the repository contains, and runs only the analyzers for those. A repository with one language never needs the other language's toolchain.
  2. Runs each analyzer as a separate process. deadset contains no parser and no type checker of its own; every finding comes from an analyzer.
  3. Resolves the references that cross the language boundary. A Go type consumed only through the TypeScript client generated from it has no reference in Go and no declaration in TypeScript, so each analyzer alone would judge it wrong. deadset reads both reports, follows the declared edge between the two symbols, and reports the pair as dead only when both sides are.
  4. Merges the reports into one and returns one exit code, so a CI step reads one result.

The analyzers are complete programs on their own. deadset-go analyzes a Go module and deadset-ts analyzes a TypeScript or JavaScript package, each from parsing to exit code. Running one directly produces the same findings as running it through deadset, so this command is never a required hop; it earns its place when there is a language boundary to resolve.

Every analyzer implements the same contract, published at deadset-spec: one vocabulary of issue kinds, one report schema, one suppression grammar, one exit-code table and one merge definition. Any analyzer that passes that repository's conformance corpus can be listed in deadset's provider list, including one written by someone else for a language neither first-party analyzer covers.

Status

Pre-release. This build implements contract version 3.2.0. deadset analyze runs every analyzer the provider list holds for a language in the target, merges their reports, prints the findings, the summary and the annotations on standard output, keeps the evidence of the run in a run directory and exits with the merged report's verdict; --scope hands each analyzer the consumers a scope document declares. It renders text and github on standard output, json as the merged report in the run directory, and sarif and template as files beside the merged report whose paths the output names; template renders the Go template --template names. deadset print-config prints the resolved configuration and where each value came from, and deadset version prints the build's versions. Every other command is reserved and exits with a usage message.

Quick start

go install github.com/cplieger/deadset/cmd/deadset@latest
deadset version

Go 1.27 or later is required to install from source. The binary is static and needs no C toolchain.

Usage

deadset analyze --target=lib --scope=scope.json

A library's published API has callers outside the library, so a finding about an exported symbol is only as certain as the set of callers the analysis loaded. --scope names a scope document that lists them: the target and its consumers, each a directory on the local filesystem.

{
  "target": { "path": "lib" },
  "consumers": [{ "path": "app" }, { "path": "web" }]
}

The document is the scope document deadset-spec publishes in contract/scope.schema.json, the same one each analyzer's own --scope reads. A relative path is resolved against the directory that holds the document, and its target must be the directory --target names.

  • Each consumer is handed to every analyzer claiming a language detected in it, and to no other, so a Go consumer reaches the Go analyzer and a TypeScript one the TypeScript analyzer. A consumer holding a language no analyzer of the run claims is refused with exit code 2. A consumer that does not exist, is not a directory or holds no language does not load, and a scope document that cannot be read or does not meet the schema is refused; each ends the run with exit code 3.
  • The analyzers run in the deepest directory holding the target and every consumer, so the merged report's target.root and each consumer's path under consumers.loaded are relative to that directory.
  • The run directory keeps the declared scope as scope.json. An analyzer handed only some of the consumers reads its own, scope.<name>.json.

A target or consumer holding both Go and TypeScript is named in every report by its Go module path, so the merge reads the two analyzers' reports as one module; the scope document may leave its id out. With every declared consumer loaded, a library's findings are certain. With no scope document the target is analyzed alone, and a finding about a library's published API is possible; a Go workspace's other modules are consumers only when the scope document declares them.

deadset never clones, fetches or checks out a consumer. Whatever runs it puts every consumer on the filesystem first: a CI workflow checks each one out before the step that runs deadset, and a container is given a scope document naming the paths mounted into it.

Security

deadset performs no network request during analysis. It executes only analyzers already installed on the local filesystem, at the path its provider list names, and exits with an error rather than fetching one that is missing. It is report-only: no command edits a source file, and any request for one is refused with exit code 2.

Dependencies

The binary uses the standard library only. The test suite requires deadset-spec, whose Contract files it checks the binary against. The analyzers it runs are separate programs with their own releases.

Contributing

Issues and pull requests are welcome. The general guidelines live in cplieger/.github.

Disclaimer

This project is built with care and follows security best practices, but it is intended for personal / self-hosted use. No guarantees of fitness for production environments. Use at your own risk.

This project was built with AI-assisted tooling using Claude, GPT, and Kiro. The human maintainer defines architecture, supervises implementation, and makes all final decisions.

License

GPL-3.0-or-later. See LICENSE.

Directories

Path Synopsis
cmd
deadset command
Command deadset is the front door of the deadset dead-code toolkit.
Command deadset is the front door of the deadset dead-code toolkit.
internal
config
Package config resolves the deadset configuration for the orchestrator.
Package config resolves the deadset configuration for the orchestrator.
detect
Package detect decides which languages a target holds, from the names of its files alone.
Package detect decides which languages a target holds, from the names of its files alone.
gomod
Package gomod reads the path a Go module publishes itself under from the go.mod file that governs a directory, the way the go command finds and reads that file, and reads nothing else of it.
Package gomod reads the path a Go module publishes itself under from the go.mod file that governs a directory, the way the go command finds and reads that file, and reads nothing else of it.
invoke
Package invoke runs an analyzer as a separate process: Describe runs its describe verb and admits or refuses the analyzer from what it prints, and Analyze runs its analyze verb and reads the report the analyzer writes to the file the invocation names.
Package invoke runs an analyzer as a separate process: Describe runs its describe verb and admits or refuses the analyzer from what it prints, and Analyze runs its analyze verb and reads the report the analyzer writes to the file the invocation names.
merge
Package merge combines the reports analyzers wrote over one target into one merged report, by the algorithm contract/grammar/merge.md states.
Package merge combines the reports analyzers wrote over one target into one merged report, by the algorithm contract/grammar/merge.md states.
present
Package present shapes the merged report a run writes and prints, once, after the merge returns: the configured severity of every finding the merge emitted itself, the configured order of the findings, and the configured maximum finding count.
Package present shapes the merged report a run writes and prints, once, after the merge returns: the configured severity of every finding the merge emitted itself, the configured order of the findings, and the configured maximum finding count.
providers
Package providers chooses the analyzers a run invokes: the entries of the provider list that claim a language in scope, each with the executable its command resolves to.
Package providers chooses the analyzers a run invokes: the entries of the provider list that claim a language in scope, each with the executable its command resolves to.
render
Package render writes the merged report in the two formats that are documents of their own rather than lines of the run's output: a SARIF 2.1.0 log for upload to code scanning, and the rendering of a template the user supplies.
Package render writes the merged report in the two formats that are documents of their own rather than lines of the run's output: a SARIF 2.1.0 log for upload to code scanning, and the rendering of a template the user supplies.
report
Package report is the deadset report document as Go values: the envelope one analyzer writes over one analysis, the envelope a merge writes over several, and the finding both carry.
Package report is the deadset report document as Go values: the envelope one analyzer writes over one analysis, the envelope a merge writes over several, and the finding both carry.
rundir
Package rundir lays out the run directory: the evidence for every exit code the orchestrator returns.
Package rundir lays out the run directory: the evidence for every exit code the orchestrator returns.
scope
Package scope reads the scope document an invocation names: the target a run reports on and the consumers whose references count against it, each a directory on the local filesystem.
Package scope reads the scope document an invocation names: the target a run reports on and the consumers whose references count against it, each a directory on the local filesystem.
summary
Package summary renders what a run prints about its merged report beside the report itself: one text line per finding and per stale suppression, the analyzers the merge read, one summary line over every language the run covered, the remediation of a failing finding, and one workflow annotation per finding and per stale suppression.
Package summary renders what a run prints about its merged report beside the report itself: one text line per finding and per stale suppression, the analyzers the merge read, one summary line over every language the run covered, the remediation of a failing finding, and one workflow annotation per finding and per stale suppression.
verdict
Package verdict is the Contract's exit-code table: the code a run returns, read from the report the run produced or from the error that ended it before a report existed.
Package verdict is the Contract's exit-code table: the code a run returns, read from the report the run produced or from the error that ended it before a report existed.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL