Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions content/docs/permissions/tenant-audit-census.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ index is built from TRACKED sources only, deliberately. An untracked, generated
or dependency-owned declaration is one this census never saw, and «never saw it»
must not be spelled the same way as «read it, not an engine».

⚠️ One arm here says something else again: `type-text-not-round-trippable` is a
receiver whose declared type the census STORED whitespace-collapsed and could
not read back — the source parsed, the re-serialisation of it did not, so the
door rule could never be read off it. That is a fault in this tool rather than
a fact about the corpus, and it is the one row here that also fails the gate.

| what | count |
| :--- | ---: |
| write calls subtracted with no defensible reason | **1** |
Expand Down Expand Up @@ -290,11 +296,11 @@ holds still. They are required to be HERE and to say WHEN they were true;
their values are not compared. The reasoning, and the measurement behind it,
are in `scripts/check-tenant-audit-census.mjs`.

Measured on 2026-09-18 at `30def652e`.
Measured on 2026-09-20 at `215840f43`.

| corpus scale (not enforced) | count |
| :--- | ---: |
| tracked non-test sources scanned | 573 |
| tracked non-test sources scanned | 576 |
| engine-shaped types recognised | 63 |
| declared objects in the registry | 117 |
| same-named calls subtracted as non-engine | 144 |
Expand Down
10 changes: 8 additions & 2 deletions docs/audits/2026-08-tenant-audit-write-call-sites.counts.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ index is built from TRACKED sources only, deliberately. An untracked, generated
or dependency-owned declaration is one this census never saw, and «never saw it»
must not be spelled the same way as «read it, not an engine».

⚠️ One arm here says something else again: `type-text-not-round-trippable` is a
receiver whose declared type the census STORED whitespace-collapsed and could
not read back — the source parsed, the re-serialisation of it did not, so the
door rule could never be read off it. That is a fault in this tool rather than
a fact about the corpus, and it is the one row here that also fails the gate.

| what | count |
| :--- | ---: |
| write calls subtracted with no defensible reason | **1** |
Expand All @@ -84,11 +90,11 @@ holds still. They are required to be HERE and to say WHEN they were true;
their values are not compared. The reasoning, and the measurement behind it,
are in `scripts/check-tenant-audit-census.mjs`.

Measured on 2026-09-18 at `30def652e`.
Measured on 2026-09-20 at `215840f43`.

| corpus scale (not enforced) | count |
| :--- | ---: |
| tracked non-test sources scanned | 573 |
| tracked non-test sources scanned | 576 |
| engine-shaped types recognised | 63 |
| declared objects in the registry | 117 |
| same-named calls subtracted as non-engine | 144 |
Expand Down
57 changes: 53 additions & 4 deletions scripts/check-tenant-audit-census.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
'A DRIFT': 5,
'B PROSE': 4,
'⭐ THE SPLIT': 9,
'census refusals': 5,
'census refusals': 7,
'refusals': 1,
});

Expand Down Expand Up @@ -222,6 +222,7 @@ import {
END_MARKER,
PAGE,
corpusScaleRows,
notRoundTrippableSites,
renderCountsFile,
renderGeneratedRegion,
runCensus,
Expand Down Expand Up @@ -587,10 +588,21 @@ function firstDifference(committed, expected) {
/**
* ⭐ Check C -- the refusals the CENSUS raises, given an exit.
*
* `runCensus()` reports two failures about the TREE rather than about the
* `runCensus()` reports three failures about the RUN rather than about the
* artefacts: a write call site whose receiver is erased and that none of the
* three placement rules reaches (`census.unledgered`), and an `UNTYPED_RECEIVERS`
* row that no longer matches any call in the corpus (`census.staleLedgerRows`).
* three placement rules reaches (`census.unledgered`), an `UNTYPED_RECEIVERS`
* row that no longer matches any call in the corpus (`census.staleLedgerRows`),
* and a receiver whose declared type the census stored whitespace-collapsed and
* cannot read back ({@link notRoundTrippableSites}).
*
* ⭐ The third one is why this function is load-bearing rather than tidy. That
* class used to end the whole process inside `runCensus` -- `parseSourceFile`
* refuses by `process.exit`, so this gate died with it and CI went red. It is now
* localised to the one site, which is the repair; but a localisation that let CI
* go GREEN would have traded a loud takedown for a quiet subtraction, and the
* subtraction is the thing the census exists not to do in silence. ⇒ the site is
* declared in both artefacts AND refused here. ⛔ Neither half alone is the
* repair.
*
* The generator's own `main()` prints both and exits 1 -- but `lint.yml` invokes
* THIS gate and never the generator, so until this function existed those two
Expand Down Expand Up @@ -628,6 +640,17 @@ export function censusRefusals(census) {
+ '`UNTYPED_RECEIVERS` row in `scripts/tenant-audit-census.mjs` saying so.',
);
}
for (const u of notRoundTrippableSites(census)) {
problems.push(
`[type-text-not-round-trippable] ${u.file}:${u.line} \`${u.receiver}\`.${u.verb}() -- the census `
+ `stored this receiver's declared type whitespace-collapsed and cannot re-parse it as a type `
+ `alias, so the door rule could not be read off it and the census cannot say whether this site `
+ `is an engine write at all: \`${u.type}\`. The SOURCE parsed -- what did not is the census's own `
+ 're-serialisation of a fragment of it, so this is a fault in the tool and not a fact about the '
+ 'corpus. Give the receiver a NAMED type the engine type index can be keyed on, or spell the '
+ "literal's members with `;` separators so the stored text round-trips.",
);
}
for (const r of census.staleLedgerRows ?? []) {
problems.push(
`[stale-ledger-row] UNTYPED_RECEIVERS names ${r.file} (receiver \`${r.receiver}\`) but no `
Expand Down Expand Up @@ -965,6 +988,32 @@ export function selfTest() {
t('the stale-row refusal names the row it could not match',
refuse([], [orphanedRow]).some((p) => p.includes(orphanedRow.file) && p.includes(orphanedRow.receiver)));

// ⭐ The third class (#19077): a receiver whose stored type text the census
// cannot read back. Localising it is the repair; letting CI go green on it
// would be the same floor drop in a different costume, so it is refused HERE
// as well as declared in both artefacts.
const notRoundTrippable = {
file: 'packages/services/service-fixture/src/seed.ts',
line: 71,
receiver: 'engine',
verb: 'insert',
reason: 'type-text-not-round-trippable',
type: '{ insert(object: string, data: unknown): Promise<void> find(object: string): Promise<void> }',
names: [],
doorShaped: false,
};
const refuseUndefended = (undefendedSubtractions) =>
censusRefusals({ ...census, unledgered: [], staleLedgerRows: [], undefendedSubtractions });

t('⭐ a receiver whose stored type text does not round-trip is a finding, not a silent subtraction',
refuseUndefended([notRoundTrippable]).some((p) => p.startsWith('[type-text-not-round-trippable]')));
t('the round-trip refusal names the site AND the text that could not be read back',
refuseUndefended([notRoundTrippable]).some(
(p) => p.includes(`${notRoundTrippable.file}:${notRoundTrippable.line}`) && p.includes(notRoundTrippable.type)));
t('⛔ CONTROL: a subtraction on a DIFFERENT undefended arm is declared but NOT refused',
refuseUndefended([{ ...notRoundTrippable, reason: 'type-not-in-corpus' }]).length === 0,
refuseUndefended([{ ...notRoundTrippable, reason: 'type-not-in-corpus' }]).join(' | '));

// ⛔ ...and the control, without which all four cases above are equally passed
// by a function that simply reports everything it is handed: a census that
// placed every site and matched every row is clean.
Expand Down
Loading
Loading