Skip to content

feat(rules)!: regroup, demote and retire rules after the audit - #267

Merged
noah-emp merged 7 commits into
mainfrom
ccr-5017db59-iewncz
Nov 1, 2026
Merged

noah-emp merged 7 commits into
mainfrom
ccr-5017db59-iewncz

Conversation

@ngngardner

Copy link
Copy Markdown
Contributor

The audit of every rule but style, against public Bend 2 code, sorted each
rule into keep, guard, move, demote or retire. This applies the moves:

  • perf, a new group at warn: strings and chars, whose cost is real but
    which never cause a failure (out of correctness).
  • pedantic, off by default: foreign (0 of 30 sampled hits were defects),
    fuel (23% precise), index (38%), strict (its fix is 1.56x slower on
    JS) and rewalk (its fix is 45-60% slower on native).
  • opt-in: coverage and closed, a project's policy rather than a defect;
    only their own setting turns them on.
  • retired, codes never reused: table (U008) and hoist (U009), with no
    public hits; ring (U010), 1 of 4 real and no mechanical fix; unit
    (U012), 0 of 7; scan (U013), 50% precise with a false positive tokens
    cannot tell apart; unsafe (L003), which bend reports itself since 2.0.32.

A code names the rule, not its group, so moved rules keep their codes, and
each module moves to the directory of its new group. index still leaves
a get on a fixed table alone: that test is src/rules/table.bend now. The
import-closure laws coverage's proof leans on, which lived in unsafe's law
file, are their own guarantee (laws/ImportReachIsClosed.bend), and the
digest no longer carries each file's unsafe defs. The rule criteria gain a
guard exception for rare shapes whose miss is silently wrong (arms,
escape, setting). This repo's bolt.bend turns perf, pedantic (but
tail), coverage and closed on, so its gate grades as before.

BREAKING CHANGE: table, hoist, ring, unit, scan and unsafe are
removed; a bolt.bend that still sets one gets a setting finding (C011).
strings, chars and foreign are no longer errors by default, strict,
fuel, index, rewalk and foreign are off unless pedantic (or the
rule) is set, and coverage and closed are off unless set by name: a
project that relied on them must add def coverage() / def closed().

Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_0186KEnw5sg7heUQZTJJU89N

The audit of every rule but style, against public Bend 2 code, sorted each
rule into keep, guard, move, demote or retire. This applies the moves:

- perf, a new group at warn: `strings` and `chars`, whose cost is real but
  which never cause a failure (out of correctness).
- pedantic, off by default: `foreign` (0 of 30 sampled hits were defects),
  `fuel` (23% precise), `index` (38%), `strict` (its fix is 1.56x slower on
  JS) and `rewalk` (its fix is 45-60% slower on native).
- opt-in: `coverage` and `closed`, a project's policy rather than a defect;
  only their own setting turns them on.
- retired, codes never reused: `table` (U008) and `hoist` (U009), with no
  public hits; `ring` (U010), 1 of 4 real and no mechanical fix; `unit`
  (U012), 0 of 7; `scan` (U013), 50% precise with a false positive tokens
  cannot tell apart; `unsafe` (L003), which bend reports itself since 2.0.32.

A code names the rule, not its group, so moved rules keep their codes, and
each module moves to the directory of its new group. `index` still leaves
a get on a fixed table alone: that test is src/rules/table.bend now. The
import-closure laws coverage's proof leans on, which lived in unsafe's law
file, are their own guarantee (laws/ImportReachIsClosed.bend), and the
digest no longer carries each file's unsafe defs. The rule criteria gain a
guard exception for rare shapes whose miss is silently wrong (`arms`,
`escape`, `setting`). This repo's bolt.bend turns perf, pedantic (but
`tail`), coverage and closed on, so its gate grades as before.

BREAKING CHANGE: `table`, `hoist`, `ring`, `unit`, `scan` and `unsafe` are
removed; a bolt.bend that still sets one gets a `setting` finding (C011).
`strings`, `chars` and `foreign` are no longer errors by default, `strict`,
`fuel`, `index`, `rewalk` and `foreign` are off unless `pedantic` (or the
rule) is set, and `coverage` and `closed` are off unless set by name: a
project that relied on them must add `def coverage()` / `def closed()`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186KEnw5sg7heUQZTJJU89N
… named files

Two bugs put the wrong role on files, so findings were made up or hidden.

A file was on the laws' side only when a root LAWS.bend or PROOF.bend in
the run reached it. A guarantee kept one per file under laws/ and proofs/,
linted without those root files (files named on the line, a run from inside
laws/, the editor), was linted as code: most of fromrev's false positives
and some of strict's, eager's, concat's and pick's. A file now has a role of
its own (Role.own): a LAWS.bend or PROOF.bend by name; else, but for a
package's main.bend, a proof file when it fills a law and a law file when it
states one and fills none. Over the whole tree that seeds the walk, a file
that imports a law file joins the laws' side, and code still wins: a file a
main.bend reaches is code, laws and all.

A run over a LAWS.bend without the package's main.bend took the code that
LAWS.bend imports for law files and dropped its findings (bend2craft's
entities.bend: 174 findings alone, none beside its LAWS.bend). Over files
named on the line the planner no longer walks the imports: each file is
told its own role, as the editor tells it, so the LSP's findings are the
CLI's by construction (laws/LspDiagnosticsMatchCli.bend now states the
named run's per-file findings, and its proof no longer needs a one-file
walk).

Laws: laws/RoleOwn.bend states own; the planner's per-file findings are
on_run's (the marks over the whole tree, each own role over named files).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186KEnw5sg7heUQZTJJU89N
…alk over named files"

This reverts commit 918d358.

Role by content and no walk over named files were the wrong fix: few
projects keep the laws/ and proofs/ layout, and roles must follow the
imports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186KEnw5sg7heUQZTJJU89N
A file's role follows the imports: a file a LAWS.bend reaches is a law
file unless a package's main.bend reaches it too. The walk only saw the
files of the run, so naming a LAWS.bend without the main.bend that imports
the same code took that code for laws and hid its findings (bend2craft's
world/entities.bend: 174 findings alone, none beside world/LAWS.bend), and
naming laws/<Name>.bend files without their LAWS.bend linted them as code.

Over files named on the line the planner now lists the tree under the
working directory, as a whole-tree run does, and reads every .bend file in
it for its imports alone (Role.fact.text: lexed and outlined, never
parsed); the marks are taken over that graph (Plan.marks.run), and only the
named files are linted. A whole-tree run is unchanged.

Laws: the planner's per-file findings are each source's told its role among
Plan.marks.run's marks (laws/defs/GradedFindingCounts.bend). The editor
reads one file and not the tree, so laws/LspDiagnosticsMatchCli.bend now
compares it with `bolt <path>` as that command really runs, for a file the
tree's imports leave at the role its name gives it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186KEnw5sg7heUQZTJJU89N
The graph a named run reads its roles from was the run's sources, then
every source the tree's walk finds, so a named file under the working
directory was in it twice (`a.bend` and `./a.bend`) and read twice. It is
now the walk's sources, then each source of the run whose normalized path
the walk did not find: one named outside the working directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186KEnw5sg7heUQZTJJU89N
… does

`bolt <path>` takes a file's role from the imports of the whole tree, but
the editor linted the one open file by its name alone, so a file a
LAWS.bend reaches was a law file to the CLI and code in the editor, and
laws/LspDiagnosticsMatchCli.bend had to assume the two agreed.

On open and save, beside the checker's run, the server now gathers the
World `bolt <path>` reads (src/lint/plan.bend's questions answered through
the Files service, which can now list a directory) and reads the role
marks from it (Plan.marks.named); the document keeps them, and each change
is linted with the marks of the last open or save. The editor law now
states the editor's lint, told those marks, is the CLI's, with no
assumption on the file's role. laws/LspKeepsRoleMarks.bend states that open
and save keep the marks they read and a change keeps them as they were.

The graph is now walked over resolved paths, so a file named by an absolute
path (as the editor names it) or as `./a.bend` is matched to its file in
the tree; `bolt /w/laws/x.bend` from /w took it for code before.

src/lsp/tests/checker.bend runs native only: each open now reads every
file of the tree, and bolt's proofs are past the JS lane's stack.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186KEnw5sg7heUQZTJJU89N
@noah-emp
noah-emp enabled auto-merge (squash) November 1, 2026 16:04

ngngardner commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor Author

check / check was red only because check / flake (macos-14) was cancelled, not because a check failed.

  • On 25ced75, nix flake check -L passed on ubuntu-latest and ubuntu-24.04-arm.
  • The macOS job was cancelled about 1 s after creation, on both attempts, before any step ran and with no runner assigned.

Cause: GitHub retires the macos-14 image on 2026-11-02 and runs scheduled brownouts before then (today, 14:00–00:00 UTC), during which macos-14 jobs end before a runner is assigned. The shared nix-flake-check.yml defaults to macos-14.

Fix in this PR: 9ffbe5c passes runners with macos-15 (arm64, so aarch64-darwin is still checked). The shared workflow's default in Emerging-Patterns/actions should move off macos-14 as well, or every repo using it will go red on Nov 2.

(Edited: an earlier version of this comment guessed at billing, which was wrong; this is a public repo.)


Generated by Claude Code

The shared nix-flake-check workflow defaults to macos-14, which GitHub
retires on 2026-11-02 and fails during scheduled brownouts before then: the
job is cancelled before any runner is assigned, so `check / check`, the
check main requires, went red with no step run. Pass the runner list with
macos-15, also arm64, so aarch64-darwin is still checked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186KEnw5sg7heUQZTJJU89N
@noah-emp
noah-emp merged commit ca72d74 into main Nov 1, 2026
4 checks passed
@noah-emp
noah-emp deleted the ccr-5017db59-iewncz branch November 1, 2026 16:23
ngngardner pushed a commit that referenced this pull request Nov 1, 2026
Resolve the rule regroup (#267): `empty` (U018) stays in `suspicious`
beside `prefix` and `fromrev`; strings and chars are `perf` as main has
them. The code table proof keeps main's arms, which already reach past
the 30 rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186KEnst9PZ9GVQD8BtKhu4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants