Adopting on an existing codebase¶
A baseline records the violations a codebase already has: the analyzer's, and what declscope shrink reports. Both then report only what is new.
The file is found by the same upward lookup as the config, so its presence is all it takes.
packages:
github.com/you/app/database:
boundary:
userRepository:
- normalizeEmail
statement:
- Statement.wheres
(core):
- open
overexported:
userRepository:
- FindUser
| Spelling | |
|---|---|
| A package-level declaration | normalizeEmail, under its file's namespace |
| A member | Type.member, under the namespace of its type's file |
| A method | Type.method, under the namespace of the file it is written in |
| The core namespace | (core) |
An entry is keyed by package, rule, namespace and declaration, never by position. It survives code moving within its file. It does not survive a declaration moving to another namespace, since the same use then crosses a different boundary.
A baseline is regenerated, never edited.
Tip
A regeneration records what one build configuration sees. A module that runs declscope under several GOFLAGS=-tags=... or GOOS settings keeps only the last run's entries.
Where the baseline is written
Each package's entries go to the file the analyzer will consult for that package.
| Target | When |
|---|---|
| The baseline named by the package's nearest config file | That config has a baseline key |
The nearest existing .declscope-baseline.yaml above the package |
No config names one |
A new .declscope-baseline.yaml in the working directory |
Neither of the above exists |
For example, with a config in internal/billing/ naming its own baseline, and a baseline already in internal/legacy/:
.declscope-baseline.yaml internal/user's entries
internal/
├── user/user.go
├── billing/
│ ├── .declscope.yaml baseline: billing-baseline.yaml
│ ├── billing-baseline.yaml internal/billing's entries
│ └── billing.go
└── legacy/
├── .declscope-baseline.yaml internal/legacy's entries
└── legacy.go
$ declscope baseline ./...
declscope: recorded 1 violation(s) in .declscope-baseline.yaml
declscope: recorded 1 violation(s) in internal/billing/billing-baseline.yaml
declscope: recorded 1 violation(s) in internal/legacy/.declscope-baseline.yaml
-o bypasses that lookup and gathers every entry into one file.
Recording shrink's reports loads the whole module once more. -shrink=false skips it, for a module that does not run shrink.
If no pattern matches a package, the run stops and writes nothing, -o included. A pattern that matches nothing beside others that do gets a warning, as it does from go vet.
Every file written is regenerated wholesale. The existing one is never read, so a baseline that fails to parse is replaced like any other. A narrower pattern therefore drops the entries of every package it does not load from the files it writes:
$ declscope baseline ./... # records a and b
$ declscope baseline ./a # the file now holds only a
$ declscope baseline -merge ./a # a is regenerated, b is kept
-merge reads each file it writes first. It replaces the entries of the packages this run loaded, their test variants included, and keeps every other entry. Under -shrink=false it keeps shrink's entries too, since the run recorded none. It also keeps them for a package shrink did not judge, which the run names on stderr as shrink does. A file that -merge cannot parse stops the run, and nothing is written.
Note
-merge also keeps the entries of a package that was deleted or renamed, since no pattern loads it any more. A run over ./... without -merge prunes them.
Warning
Some packages cannot reach the working directory by that lookup, such as one in another module. The run then refuses and names them, rather than recording entries nothing would find.
Tip
skills/declscope-adoption is a skill for an AI agent doing this work. It covers what each diagnostic shape means, and the measurement traps that produce false confidence. Its companion, skills/declscope-authoring, is for everyday code: where a declaration goes, and how to name it accurately. The binary carries both:
declscope skill install # the agents already set up in this project
declscope skill install --agent claude-code --scope user
declscope skill list # where they are, and whether they are current
Without the binary, gh skill install mpyw/declscope declscope-authoring --agent claude-code writes to the same directories, and the same with declscope-adoption. The binary's installer is go-skill-embed, which takes them from gh skill install.