
Product
PHP and Composer Support Is Now in Beta
Socket’s PHP and Composer support is now in Beta for all customers, with PHP reachability analysis generally available.
@third-option/comment-budget
Advanced tools
A gate on comment volume: how much prose must a reader wade through to reach the code.
A gate on comment volume. One question: how much prose must a reader wade through to reach the code?
comment_lines / (comment_lines + code_lines) blank lines ignored
So 0.15 means "1 line in 7 is prose" — not comments-per-code-line. Two other
signals sit beside the ratio: an over-long unbroken run of comment, and an
over-long .md.
Linters flag what a comment says, not how much of it there is; comment volume goes unmeasured. This measures only that.
cargo install comment-budget # or, for a prebuilt binary:
bun add -d @third-option/comment-budget # npm/pnpm/yarn work too
comment-budget # the gate: only the lines this branch adds over `main`
comment-budget --all # the standing backlog, repo-wide
comment-budget --report # surface table and worst files, no finding list
comment-budget --format json # findings as an array, for CI to consume
comment-budget --surface web # one surface, for a session spent fixing it
comment-budget --new-from-merge-base release # gate against a branch other than main
comment-budget --new-from-rev HEAD~3 # gate against a revision itself
Exit status is 0 when nothing errored, 1 when something did, 2 on a bad
invocation. Warnings never fail the run — they are the standing hit list.
Repo-wide, an established codebase reports hundreds of errors, and a gate that fails every run is one nobody reads. So the default judges only what a branch adds, and the ratio is the added lines' own — a branch can neither add prose nor inherit the file's existing debt.
An over-long run is the exception: it is measured whole and merely has to touch an added line to be reported, because a reader wades through all of it however much of it you wrote.
The comparison is against the working tree, so a local run judges what you
are about to push, not only what you have committed. --whole-files opts back
into judging every touched file whole. The --new-from-* flag names are
golangci-lint's, which is where the idea is best known from.
Everything measured — and how hard, and why — lives in comment-budget.toml at
the root of the tree, found by searching upward from the working directory.
exempt comment prefixes are
invisible to every signal, neither comment nor code, so a Rust //! header
can be as long as the decision it records. counted is the bloat being
measured. A comment matching neither counts, so a new syntax can't slip
through unmeasured.surface.file fires only when ratio and line count both exceed a tier.
Mass alone flags a big well-commented file; ratio alone flags a tiny stub
whose three doc lines are 40% of nothing. Neither is the thing being hunted.A file may opt out with a top-of-file comment-budget: allow(<reason>). The
reason is required — an unexplained opt-out is the failure mode the directive
exists to prevent.
A minimal config:
skip = ["node_modules", "target", "dist"]
[kinds.rust]
grammar = "rust" # the tree-sitter grammar whose comment nodes are read
extensions = ["rs"]
exempt = ["//!"] # module docs: where hard-won "why" lives, never capped
counted = ["///", "//"] # item docs and narration: the bloat being measured
[kinds.markdown]
grammar = "prose" # parses nothing; measures length instead
extensions = ["md"]
[[surface]]
name = "crates"
paths = ["crates/*/src/**/*.rs"]
goal = "Document the module and the crossing points; not every pub item."
target = 0.15 # reported per surface, not enforced
[surface.file]
warn = { ratio = 0.20, lines = 50 }
error = { ratio = 0.30, lines = 100 }
[surface.run]
warn = 8
error = 14
[[surface]]
name = "docs"
paths = ["**/*.md"]
goal = "Prose has no code to sit against, so length is the only signal it offers."
target = { lines = 150 }
warn = { lines = 150 }
error = { lines = 250 }
[escape]
directive = "comment-budget: allow(<reason>)"
The binary is a thin shell over the crate; Finding keeps its fields rather
than only a rendered line, so a consumer can emit GitHub annotations or editor
diagnostics without parsing text back out.
let (cfg, root) = Config::discover(&std::env::current_dir()?)?;
let diff = Diff::open(&root, &Since::MergeBase("main".into()), false)?;
let analysis = comment_budget::analyze(&root, &cfg, Some(&diff))?;
for finding in comment_budget::judge(&cfg, &analysis.stats) {
println!("{finding}");
}
Delete, don't reflow. Cut history — git already holds it — and keep only what looks forward: the why, and the how where the code leaves it unclear. Squeezing under a threshold just moves an error onto the warning list, and the budgets are not the thing to lower.
MIT OR Apache-2.0, at your option.
FAQs
A gate on comment volume: how much prose must a reader wade through to reach the code.
We found that @third-option/comment-budget demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Product
Socket’s PHP and Composer support is now in Beta for all customers, with PHP reachability analysis generally available.

Product
Socket is bringing experimental protection to Firefox, scanning 97,000+ extensions in Mozilla's official directory for malware and risky updates.

Research
/Security News
Three compromised Rust crates pulled in a malicious dependency that downloaded and executed cross-platform malware during Cargo builds.