Skip to content

Stop the diagnostics module handing 0071's rule to an issue it applies in its own emit - #482

Merged
iderex merged 1 commit into
mainfrom
diagnostics-names-where-the-redaction-rule-lives
Sep 18, 2026
Merged

iderex merged 1 commit into
mainfrom
diagnostics-names-where-the-redaction-rule-lives

Conversation

@iderex

@iderex iderex commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

The issue this belongs to

Closes #481

What changed

The FieldValue paragraph in src/diagnostics/mod.rs stops saying that #71 is
where 0071's per-field rule is built and that this module decides none of it. It
names redaction, which this file registers and imports from, as where the rule
lives, and Diagnostics::emit, in this same file, as where it is applied before
the client's sink is called. What the paragraph exists for is kept: the rule
reads the field NAME and never the variant, so what decides none of it is this
TYPE rather than this module. The half of #71 that is genuinely absent is named
and stays negative.

Nothing else moves. No behaviour, signature, variant or registration.

What failure it prevents

The module said of itself that it decides none of what may leave through an
event, while its own emit drops an excluded field, replaces a reduced one with
a correlator, and carries the rest whole. Somebody asking whether the core
treats a field before a sink sees it read that sentence, followed #71, found an
open issue carrying a waiting label, and concluded that nothing here does. That
is wrong in the direction that reads as covered, and the subject is what may
leave a person's device.

It was found by reading this module's account of 0071 against the submodule it
registers and against its own emit.

Evidence

The commit being pushed:

git rev-parse HEAD
921319ad227d2929f916a6a03aa4156fa5c19c54

What the paragraph said, at the parent:

git show 921319a^:src/diagnostics/mod.rs | sed -n '248,259p'
/// What a field carries.
///
/// The shapes of data 0100 names: a count, an identifier, a duration, a kind
/// from 0004, an address. Four variants rather than five, because an identifier,
/// a kind and an address are all text the core already holds and nothing here
/// treats them differently.
///
/// THAT IS NOT THE REDACTION RULE AND DOES NOT WEAKEN IT. 0100 requires the rule
/// in 0071 to decide per field NAME, because 0068 places a field carrying an
/// item identifier under its personal data list while the count beside it is
/// not, so what the rule reads is the name and never the variant. #71 is where
/// that rule is built and this module decides none of it.

The rule, at the parent, in a file this one registers and imports four names
from:

git show 921319a^:src/diagnostics/redaction.rs | sed -n '1,7p'
//! What may leave through a diagnostic event, decided per field name and applied
//! before the client's sink is called.
//!
//! 0071 is the record and #71 is the issue. Three treatments and nothing else:
//! a field is excluded outright, reduced to a correlator that means nothing
//! outside the run that produced it, or carried whole. Which of the three a field
//! gets does not vary with severity.
git show 921319a^:src/diagnostics/mod.rs | sed -n '60p;65p'
pub mod redaction;
use redaction::{Correlator, CorrelatorSalt, FieldName, Treatment};

And the application, at the parent, in the same file as the sentence:

git show 921319a^:src/diagnostics/mod.rs | sed -n '497,500p'
        if fields
            .iter()
            .all(|field| field.name().treatment() == Treatment::CarriedWhole)
        {
git show 921319a^:src/diagnostics/mod.rs | sed -n '519,535p'
            .map(|field| match field.name().treatment() {
                Treatment::Reduced => Some(Correlator::of(&self.salt, field.value())),
                Treatment::Excluded | Treatment::CarriedWhole => None,
            })
            .collect();

        let mut kept = Vec::with_capacity(fields.len());
        for (field, correlator) in fields.iter().zip(&correlators) {
            if let Some(correlator) = correlator {
                kept.push(Field::new(
                    field.name(),
                    FieldValue::Text(correlator.as_str()),
                ));
            } else if field.name().treatment() == Treatment::CarriedWhole {
                kept.push(*field);
            }
        }

The dates. The sentence landed on 2026-08-26 and the submodule, the import and
the branches above arrived on 2026-08-28, in one commit that edited this file:

git blame -L 258,259 --date=short 921319a^ -- src/diagnostics/mod.rs | cat
6933b040 (Nils Lehnen 2026-08-26 258) /// not, so what the rule reads is the name and never the variant. #71 is where
6933b040 (Nils Lehnen 2026-08-26 259) /// that rule is built and this module decides none of it.
git blame -L 60,60 --date=short 921319a^ -- src/diagnostics/mod.rs | cat
1aa81703 (Nils Lehnen 2026-08-28 60) pub mod redaction;
git blame -L 499,499 --date=short 921319a^ -- src/diagnostics/mod.rs | cat
1aa81703 (Nils Lehnen 2026-08-28 499)             .all(|field| field.name().treatment() == Treatment::CarriedWhole)
git log --format='%h %cI %s' --diff-filter=A 921319a^ -- src/diagnostics/redaction.rs
1aa8170 2026-08-28T17:30:05+02:00 Carry the treatment on the field name, and apply it before the sink (#224)

Another register in this tree had already said the per-field half is built, and
made it the condition for putting this directory on the pinned coverage surface:

git show 921319a^:.github/coverage/pinned-surface | sed -n '53,58p'
# SRC/DIAGNOSTICS/ WAS IN THAT LIST AND IS AN AREA NOW, ON THE CONDITION THIS
# FILE ITSELF NAMED. What stood here said the facility is what the core reports
# through rather than what it decides with, and that what would move it onto the
# surface is #71 deciding what may leave through a diagnostic event at all. That
# is built. A field name now carries one of three treatments, and the facility
# drops the excluded, reduces the reduced and carries the rest whole before the

The absence that survives, which the change names rather than deletes. #71 is
open, and the module that holds the rule says which half is missing:

gh issue view 71 --repo Flowfin/core --json number,state --jq '"\(.number) \(.state)"'
71 OPEN
git show 921319a^:src/diagnostics/redaction.rs | sed -n '31,34p'
//! output for every named field, is what would catch one afterwards. THAT TEST
//! IS NOT HERE, AND THIS SENTENCE SAID IT WAS WAITING ON THE FAKE SERVER IN #21.
//! That landed four hours after this paragraph did, and the suite drives it.
//! What the test has no way to reach is the session: nothing in this library

What the paragraph says now:

git show HEAD:src/diagnostics/mod.rs | sed -n '255,267p'
/// THAT IS NOT THE REDACTION RULE AND DOES NOT WEAKEN IT. 0100 requires the rule
/// in 0071 to decide per field NAME, because 0068 places a field carrying an
/// item identifier under its personal data list while the count beside it is
/// not, so what the rule reads is the name and never the variant. THIS SENTENCE
/// SAID #71 IS WHERE THAT RULE IS BUILT AND THAT THIS MODULE DECIDES NONE OF IT.
/// The rule is in [`redaction`], which this module registers and imports from,
/// and [`Diagnostics::emit`] applies it before the client's sink is called: a
/// field the name excludes is dropped, a field the name reduces is replaced by a
/// correlator, and the rest is carried whole. What decides none of it is this
/// TYPE, which is what the paragraph is about: a variant here is a shape of data
/// and never a treatment. What of #71 is still absent is the test that drives a
/// session, which [`redaction`] states in its own header. #481 is where it was
/// found.

What the change is:

git diff --stat origin/main...HEAD
 src/diagnostics/mod.rs | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)
git diff --name-only origin/main...HEAD
src/diagnostics/mod.rs

The two commands the gate runs, at this commit. The build was run first and
exited 0; its own summary line is not pasted, because this board's hygiene check
reads the word in it as a speed claim:

cargo build --locked --all-targets ; echo "exit=$?"
exit=0
cargo test --locked ; echo "exit=$?"
exit=0
cargo test --locked 2>&1 | grep -c '^test result: ok'
10
cargo test --locked 2>&1 | grep -c 'test result: FAILED'
0

The two links the paragraph adds resolve, which the ordinary build does not
judge:

cargo doc --no-deps --locked ; echo "exit=$?"
exit=0

Four of this board's own legs, run on this machine at this commit:

bash .github/invariants/invariants.sh check | tail -1
Every rule above was applied to its subject and refused nothing.
bash .github/format/format.sh check | tail -1
Every tracked source file above is written the way the formatter would write it.
bash .github/doc-paths/doc-paths.sh check | tail -1
Every path these documents name resolves against the tracked set.
bash .github/lint/lint.sh check | tail -1
Every lint the groups above carry was refused, outside the register printed with it.

What this does not cover

  • src/diagnostics/redaction.rs. Read as evidence and not edited. It
    already says what it holds and which half of Keep personal data out of logs, errors and anything a person is asked to send #71 it does not.

  • Diagnostics::emit and the code below it. Read as evidence and not
    edited. No production line changes.

  • The module header's own hand-off. Line 6 of this file says what may leave
    through an event is 0071 and Keep personal data out of logs, errors and anything a person is asked to send #71, and the header never names the submodule
    that answers it. That sentence is not false, and whether a module header owes
    an index of its own submodules is a separate reading. It was not made here.

  • The sink's paragraph. DiagnosticsSink says what may appear in an event
    is 0071 and Keep personal data out of logs, errors and anything a person is asked to send #71 and that nothing there decides it. That trait receives an
    event which has already been treated, so the sentence is about a different
    subject and is left alone.

  • Keep personal data out of logs, errors and anything a person is asked to send #71 itself. It stays open, it keeps its waiting label, and nothing here
    changes what it is waiting on or claims any part of it done beyond what the
    tree already holds.

  • 0071, 0100 and 0068. All three records are read and none is edited. A
    record is added or superseded rather than edited in place, which
    docs/decisions/0001-decision-records.md fixes.

  • Whether any field is classified correctly. The compiler asks whether
    somebody chose a treatment, never whether they chose the right one, which the
    redaction module states about itself. This change reads no field name and
    moves no classification.

  • A check that would refuse this. Nothing in this tree reads a doc comment
    against the module beside it or against the code below it, and the document
    check reads only .md files. This adds no rule, so the next sentence of this
    shape is caught by a reader or not at all.

  • The shell analysis. It was not run on this machine, because the analyser
    is not on this machine's path:

      command -v shellcheck ; echo "exit=$?"
      exit=1
    

    What the run on this pull request reports is the reading, and nothing is
    claimed for it from here. This change touches no shell file.

Who has read it

Nobody other than the author. There is no second reader on this board tonight,
and the readings pasted above stand in place of one rather than a review having
happened.

…s in its own emit (#481)

`src/diagnostics/mod.rs` said on `FieldValue` that #71 is where 0071's per-field
rule is built and that this module decides none of it. The rule is in
`src/diagnostics/redaction.rs`, which this file registers and imports four names
from, and `Diagnostics::emit` in this same file reads the treatment off each
field name: an event of whole fields goes to the sink untouched, a reduced field
is replaced by a correlator, and an excluded field is not kept at all. The
paragraph now names both places and keeps the point it exists for, which is that
the rule reads the field NAME and never the variant, so what decides none of it
is this type rather than this module.

What that prevents is an absence read as a decision. Somebody asking whether the
core decides what may leave through a diagnostic event read on the type carrying
a field's value that this module decides none of it, followed #71, found an open
issue carrying a waiting label, and concluded that nothing here treats a field
before a sink sees it.

The sentence landed on 2026-08-26 and was refuted two days later by the commit
that added the submodule, the import and the branches to this very file. The
coverage register has said since then that the per-field half is built, which is
the condition it named for putting `src/diagnostics/` on the pinned surface, so
this file was the one place still saying otherwise.

The half of #71 that is genuinely not here is unchanged and stays negative: the
test that drives a full session at the most verbose level, which the redaction
module states in its own header and which nothing in this change claims.

Nothing else moves. No behaviour, signature, variant or registration.

Closes #481

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex
iderex merged commit ceccb98 into main Sep 18, 2026
28 checks passed
@iderex
iderex deleted the diagnostics-names-where-the-redaction-rule-lives branch September 18, 2026 09:14
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.

The diagnostics module says #71 builds the redaction rule and that it decides none of it, and it applies that rule in its own emit

1 participant