Skip to content

Refuse a logger installed in the core - #273

Merged
iderex merged 1 commit into
mainfrom
refuse-a-logger-installed-in-the-core-266
Sep 1, 2026
Merged

Refuse a logger installed in the core#273
iderex merged 1 commit into
mainfrom
refuse-a-logger-installed-in-the-core-266

Conversation

@iderex

@iderex iderex commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

The issue this belongs to

Closes #266

What changed

One rule, no-logger-installed, added to .github/invariants/rules. It refuses a
line under src/ that registers a logger behind the logging facade 0243 admits,
and it names the five registration calls it knows: set_logger,
set_boxed_logger, set_logger_racy, set_global_default and
set_default_dispatch.

Nothing else moved. No script changed, no workflow changed, and the rule is data
in the register that .github/invariants/invariants.sh already loads, which is
what CONTRIBUTING.md says a rule should be where the rules are the thing that
changes rather than the logic applying them.

What failure it prevents

0243 narrows 0103's fourth refused behaviour from linking a logging facade to
writing to a log, and it does so on a standing condition: that the core installs
no logger and states that it installs none. The whole narrowing rests on the
facade's default sink writing nothing. Register something behind it and 0103's
fourth behaviour is back in force - a second exit for the values 0071 classifies
field by field - with nothing having caught the change and a record two levels
away saying the case was considered.

That failure has not happened. What has already happened is the absence: 0243 says
in its own text that nothing refuses a logger installed tomorrow, and it names
#266 as where a rule is asked for.

Evidence

The condition 0243 rests on still holds, at the commit being pushed:

git grep -n 'set_logger\|set_boxed_logger' -- src/ ; echo "exit=$?"
exit=1

git grep -nE '^log =|^tracing' -- Cargo.toml ; echo "exit=$?"
exit=1

so this rule is added over a tree that already satisfies it, and the register goes
from nine rules to ten:

bash .github/invariants/invariants.sh check | grep 'rule(s)'
      10 rule(s)

What a guard here refuses, and the proof it bites

Three runs on one machine, in one go, on this branch.

Run 1 - the rule in the register, and one line under src/ that registers a
logger.
The line was appended to src/diagnostics/mod.rs on purpose and is not
in this change:

printf '%s\n' 'log::set_boxed_logger(Box::new(EventSinkLogger))?;' >> src/diagnostics/mod.rs
bash .github/invariants/invariants.sh check

ok    bites: no-logger-installed
ok    passes: the near miss beside no-logger-installed
...
      10 rule(s)
...
      no-logger-installed: src/
::error::no-logger-installed: src/diagnostics/mod.rs:1036:log::set_boxed_logger(Box::new(EventSinkLogger))?;
        REFUSED: src/diagnostics/mod.rs:1036:log::set_boxed_logger(Box::new(EventSinkLogger))?;
...
::error::1 line(s) broke an invariant above.
EXIT=1

One rule refused and no other. That is the run's own leg 1 as well as this
paragraph's claim: ok bites: no-logger-installed is the judge applying the whole
rule set to the fixture and requiring the verdict to be exactly this rule.

Run 2 - the same line, with the rule deleted from the register. Nothing else
changed:

awk '/^id: no-logger-installed$/{skip=1} skip&&/^$/{skip=0;next} !skip' rules.withrule > .github/invariants/rules
grep -c 'no-logger-installed' .github/invariants/rules
0
bash .github/invariants/invariants.sh check

      9 rule(s)
...
Every rule above was applied to its subject and refused nothing.
EXIT=0

So the line goes green the moment the rule is taken away, which is the direction
the guard is meant to have and the thing a run of the suite alone would not show.

Run 3 - the rule restored, the planted line removed, which is the tree being
pushed.

cp rules.withrule .github/invariants/rules
cp mod.rs.orig src/diagnostics/mod.rs
git status --porcelain
 M .github/invariants/rules
bash .github/invariants/invariants.sh check

      10 rule(s)
...
      no-logger-installed: src/
        clean
...
Every rule above was applied to its subject and refused nothing.
EXIT=0

The register is back to ten rules, the new one has a subject and judged it, and
the only thing left modified is the register itself.

The near miss is the one 0243 makes expensive to get wrong. The rule's clean
line is a diagnostic event handed to the client's sink, which is what 0100 says
the core does, carrying the word a careless pattern would key on:

sink.event(&event); // the sink is the client's, and no logger stands behind it

A rule written as logger rather than as the registration calls would refuse
that line, and it would refuse the sentence 0243 requires the core to state about
itself, which is a rule that makes its own record unsatisfiable. Every run above
proves it passes.

What this does not cover

It is a name list rather than a purpose test, which is the same bound the four
rules already in the register carry and which the run prints beside its verdict. A
registration written through a name nobody listed walks past it. The five are the
registration calls of the facade this graph carries and of its neighbour; a third
facade, a wrapper that registers on the core's behalf, or a call reached through
an alias is not refused.

The subject is src/ and nothing else. A logger installed in a test, in a
fixture or in an example is untouched, and 0243's condition is about the core
rather than about the suite. A logger implementation arriving in the dependency
graph is 0103's question and is refused there or not at all; this rule reads no
manifest and no lockfile.

Nothing was compiled or run. The planted line is not valid Rust in the place it
was planted, and it did not need to be: this check reads text out of git ls-files
and never builds. cargo build and cargo test were not run for this change, and
no claim above rests on them.

The measurement above is one machine's. The three runs were made on Windows
with the repository's own bash, not on the runner, and what the gate says is the
invariants check on this pull request.

Who has read it

Nobody other than the author has read this change. The three runs above stand in
place of a second reader, and the second of them is the one worth checking: it is
the run that says the guard has a direction.

0243 admits a logging facade on the standing condition that the core installs
no logger and states that it installs none, and it says in its own text that
nothing refuses one installed tomorrow. The narrowing it executes on 0103's
fourth refused behaviour rests entirely on the facade's default sink writing
nothing, so a registration behind it reopens a second exit for the values 0071
classifies field by field, with nothing having caught the change.

no-logger-installed refuses the five registration calls under src/. It is a name
list rather than a purpose test, which the block says of itself, and the near
miss beside it is a diagnostic event handed to the client's sink carrying the
word a careless pattern would key on - so a rule written as 'logger' would refuse
the sentence 0243 requires the core to state about itself.

Proven by planting the fixture under src/ and running the check three times: with
the rule it refuses exactly that line and nothing else, with the rule deleted the
same line is green, and with the line removed the register judges ten rules and
refuses nothing.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex
iderex merged commit 35517df into main Sep 1, 2026
24 checks passed
@iderex
iderex deleted the refuse-a-logger-installed-in-the-core-266 branch September 1, 2026 04:40
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.

Nothing refuses a logger being installed in the core, and 0243's narrowing rests on there being none

1 participant