go-control-flow — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited go-control-flow (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Go gives you if, for, and switch — and one looping construct that covers them all. The idioms are small but strict: scope variables tightly, return early, keep the happy path unindented.
if x, err := f(); err != nil { ... }.if body returns/breaks/continues, drop the else.for {}).break to exit the enclosing for.| Situation | Use |
|---|---|
| New variable, scoped to the check | if v, err := f(); err != nil |
| Reusing an outer variable | plain = (avoid := to prevent shadowing) |
| At least one new + reuse of outer | := is fine (same scope only) |
| Wanted zero value | var x T |
if err := file.Chmod(0664); err != nil {
return err
}If err is needed past the if, declare it separately:
x, err := f()
if err != nil {
return err
}
// use x freelyf, err := os.Open(name)
if err != nil {
return err
}
d, err := f.Stat()
if err != nil {
f.Close()
return err
}
codeUsing(f, d)Never bury the success path inside else.
// Bug: inner ctx never escapes the if block
if *shortenDeadlines {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
}
// Fix: assign with =, declaring cancel separately
var cancel func()
ctx, cancel = context.WithTimeout(ctx, 3*time.Second)
defer cancel()Read references/blank-identifier.md for _ use cases (interface checks, side-effect imports, multi-return discards).// Condition-only (Go's "while")
for x > 0 { x = process(x) }
// Infinite
for {
if done() { break }
}
// Three-clause
for i := 0; i < n; i++ { ... }for i, v := range slice { ... } // index + value
for k, v := range myMap { ... } // non-deterministic order
for i, r := range "héllo" { ... } // i is byte offset; r is rune
for v := range ch { ... } // drains until closedGo has no comma operator. Use parallel assignment instead:
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}++ and -- are statements, not expressions — they cannot appear inside a parallel assignment.
Loop:
for _, v := range items {
switch v.Type {
case "done":
break Loop // breaks the for, not just the switch
}
}Read references/switch-patterns.md for expression-less switches, comma cases, fallthrough, and type switches.
| Anti-pattern | Why it hurts | Do this instead |
|---|---|---|
} else { return ... } after a returning if | Pointless nesting | Drop else; let the happy path stay flat |
if _, err := f(); err == nil { ... } then use the value | Value is out of scope | Move the := outside the if |
:= in inner scope reassigning outer var | Silently shadows | Use = (declare the new locals separately) |
| Iterating a map and relying on order | Order is randomized | Sort keys explicitly |
break inside switch expecting to exit for | Only exits switch | Use labeled break Label |
_ = doSomething() to silence an error | Real failures vanish | Handle it or document why |
else branch after an if that returns/breaks/continues:= in inner scopes does not shadow important outer variablesrange over strings treats the index as a byte offset, not a rune index_break is used when a switch needs to exit a surrounding loop_ for multi-return, interface compliance, side-effect imports~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.