Four declared-but-never-wired findings from the sweep's low tier, all the same defect class this run has been retiring: things typed, validated and documented that no code ever read.
bulk-update label
docs/features/pull-requests.md promises bulk-update: For PRs with 5+ package updates under Dynamic Labeling. Nothing applied it — a filter watching for the label saw every grouped PR slip past. generateLabels now adds it at five or more updates.
Notification event names are validated
EVENT_NAMES was exported with the comment "for config validation and subscription lists" — and no validator imported it. A mistyped events: ['pr.opened'] built the sink, read the credential, and filtered every real event out: notifications configured, nothing ever delivered. validateConfig gains validateNotifications, checking slack/discord/webhooks[] — event names against EVENT_NAMES, env-var names non-empty, webhook url present and http(s).
workflows.enabled
Declared and documented; generate-workflows generated the full set regardless. It now declines on explicit false only — the same reading the dashboard uses, so a repository that never wrote a workflows block keeps the command.
workflows.custom[].assignees — and the personalisation that was already theater
The field was declared and validated, and generateCustomWorkflow didn't even accept it. Worse: reviewers and labels were accepted — and then dropped by generateWorkflow, which never rendered them into the YAML. The generated workflow runs ./buddy update against whatever buddy.config.ts the checkout carries, so a per-workflow override can only travel through the command line:
buddy updategains--reviewers/--assignees/--labels(comma-separated; fold intopullRequestonly when given, so an unsetpullRequestblock stays unset).- The generated workflow passes the per-workflow overrides as those flags, on both the dry-run and live invocations, single-quoted with quotes stripped so a hostile name cannot escape the argument.
- Global
pullRequestvalues are deliberately not baked into the YAML any more (scheduled workflows used to pass them into the void): the runtime reads them from config on every run, and freezing today's values into a workflow file would shadow every later config edit.
Tests
bun test: 2165 pass / 0 fail. New: test/custom-workflows.test.ts (flags rendered on both invocations, no flags when no overrides, shell-quote hardening, globals not frozen); extensions to test/pr-labels.test.ts (5 ⇒ labelled, 4 ⇒ not), test/config-validation.test.ts (valid block passes; mistyped event, missing url, bad webhook events all reported), test/cli-surface.test.ts (decline honoured, no .github written).
Remaining low items (review --light --fix, reviewSuggestions, skipIfReviewed) wait on #1431, which owns the review files.
🤖 Generated with Claude Code