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
9 changes: 9 additions & 0 deletions .changeset/9140-changeset-claims-blind-spot-census.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
---

Count the population `check-changeset-claims` cannot reach. Its `--audit` mode
now reports how many pending bodies publish a CHANGELOG paragraph but spell no
name the gate can resolve — the ones that coordinate themselves by symbol — so
the size of that blind spot is re-derived on every run instead of hand-counted
per incident (objectui#9140). The gate's coordinate is unchanged and it remains
report-only. Tooling and tests only; no package is released by this change.
96 changes: 95 additions & 1 deletion scripts/__tests__/check-changeset-claims.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

import { namedFiles, paragraphNaming, resolveNamed, treeIndex } from '../check-changeset-claims.mjs';
import { audit, namedFiles, paragraphNaming, resolveNamed, treeIndex } from '../check-changeset-claims.mjs';

/**
* objectui#9003 — a pending changeset's prose is judged by nothing.
Expand Down Expand Up @@ -418,3 +418,97 @@ describe('resolveNamed', () => {
expect(resolveNamed(index, 'package.json')).toBeNull();
});
});

// ── 6. the blind spot is COUNTED, not hand-counted per incident ──────────────

/**
* objectui#9140 — the shape this gate cannot see, made measurable.
*
* objectui#9065 was five WENT-FALSE claims at once; the gate named four and was
* blind to the fifth. The fifth's body names no file at all — it coordinates
* itself by SYMBOL — so no diff can make the diff-mode check speak about it.
* That was established by counting backticked spans BY HAND, once, on one card.
*
* `--audit` now counts the population instead, so the size of the blind spot is
* re-derived on every run rather than written down (AGENTS.md rule #9). What is
* pinned here is the COUNTER, in both directions:
*
* - a symbol-coordinated body is counted as outside the reach (it fires), and
* - the FIRING NEGATIVE CONTROL: the same body with one resolvable file name
* added is NOT counted there. Without that leg, "the census sees it" cannot
* be told apart from "the census counts everything".
*
* The limit itself is pinned too: even when the change edits the very file the
* symbol-only body is about, the diff-mode gate still reports nothing about it.
* ⛔ That is the card's finding, pinned AS A LIMIT — not a bug to be fixed by
* loosening the coordinate, which objectui#9140 measured and left open.
*/
describe('objectui#9140 — bodies this gate can never reach', () => {
/** A fixture carrying one symbol-coordinated pending body. */
function blindFixture(label: string, body: string): Fixture {
const fixture = fixtureRepo(label);
fixture.write('.changeset/9140-symbol-coordinated.md', body);
fixture.commit('a pending declaration that coordinates itself by symbol');
return fixture;
}

// The shape of `.changeset/7165-grid-dependent-values.md`: every coordinate it
// offers is a symbol, and not one of them is spelled as a file name.
const SYMBOL_ONLY =
'---\n' +
"'@fixture/alpha': patch\n" +
'---\n\n' +
'The inline editor now supplies `dependentValues`, so a `dependsOn` column is\n' +
'no longer permanently uneditable. `renderCellEditor` passes the saved row.\n';

it('counts a symbol-coordinated body as outside the reach', () => {
const fixture = blindFixture('blind', SYMBOL_ONLY);
const totals = audit(fixture.root, null);

expect(totals.silent, 'a body naming no file is outside the diff-mode reach').toBeGreaterThanOrEqual(1);
expect(totals.blind.join('\n')).toContain('.changeset/9140-symbol-coordinated.md');
});

it('FIRING NEGATIVE CONTROL — the same body naming one resolvable file is NOT counted there', () => {
// Known direction, and it must move: the ONLY edit between the two fixtures
// is one added backticked file name that resolves to exactly one tracked
// file. If the census counted every body, this leg would read identically
// to the one above and prove nothing.
const blind = audit(blindFixture('blind-ctl-a', SYMBOL_ONLY).root, null);
const seeing = audit(
blindFixture('blind-ctl-b', SYMBOL_ONLY.replace('the saved row.', 'the saved row, in `reconciliation.test.ts`.'))
.root,
null,
);

const named = '.changeset/9140-symbol-coordinated.md';
expect(blind.blind.join('\n'), 'the symbol-only body IS in the blind list').toContain(named);
expect(seeing.blind.join('\n'), 'adding one resolvable file name takes it OUT').not.toContain(named);
// Non-empty guard: both runs must have looked at the same publishing
// population, so the difference above cannot come from one side reading
// nothing at all.
expect(seeing.publishing, 'both runs read the same publishing population').toBe(blind.publishing);
expect(blind.publishing).toBeGreaterThan(0);
expect(seeing.silent, 'the control body left the silent population').toBe(blind.silent - 1);
});

it('pins the LIMIT: the diff-mode gate stays silent even when the change edits the file that body is about', () => {
const fixture = blindFixture('blind-limit', SYMBOL_ONLY);
// `renderCellEditor` is a symbol this body names. Edit the file that
// declares it — the exact shape of the falsifying merge on objectui#9065.
fixture.write('packages/alpha/src/reconciliation.test.ts', 'export const renderCellEditor = () => null;\n');
fixture.commit('edit the file the symbol-only body is about');

const run = runGate(fixture.root, lastCommitRange(fixture));

expect(run.status, 'report-only: still exit 0').toBe(0);
expect(run.output, 'the symbol-coordinated body is never reported').not.toContain(
'9140-symbol-coordinated.md',
);
// Control with a known direction that HITS in this same run: the gate did
// look, and it can still speak about a body that names that same file.
expect(run.output, 'the gate was not simply silent about everything').toContain(
'.changeset/6794-declared-default.md',
);
});
});
78 changes: 74 additions & 4 deletions scripts/check-changeset-claims.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,42 @@
* it, or carves an exemption into it: the two gates are meant to fire together on
* a correction, one asking for the read and the other recording the write.
*
* ## What objectui#9140 measured about this gate, after five live instances
*
* objectui#9065 was five WENT-FALSE claims at once. The gate was re-run on each
* falsifying merge's own range, and the answer split two ways. Both halves are
* recorded here because each one contradicts something this header assumes.
*
* HALF ONE - it named four of the five, and nobody acted on any of them. All
* four false paragraphs then sat on `main` as pending release-note input until
* objectui#9065 was filed BY HAND, and three had already been folded into the
* standing Changesets release PR's staged CHANGELOGs. ⇒ the "REQUEST TO READ"
* theory of change stated above has a measured delivery record, and on the one
* shape this gate exists for that record is zero for four. ⛔ That is NOT an
* argument for making it block - the measurement against blocking is unchallenged
* and "Why it can never block" still rules. It is the datum whoever revisits that
* trade needs, recorded rather than lost.
*
* HALF TWO - the fifth was invisible, and that is a SECOND historical instance on
* the runner-up's side of the coordinate trade measured above. The one this gate
* could not see is exactly the one whose body names no file at all: it coordinates
* itself by symbol. `--audit` now counts that population - bodies that publish but
* spell no name this gate can resolve - so the blind spot is re-derived on every
* run instead of hand-counted per incident (rule #9 in AGENTS.md).
*
* ⛔ The coordinate was NOT widened, and the reason is a measurement, not taste.
* objectui#9140 asked what it would take to bring that fifth instance into the
* census. Its body reaches a file this gate could resolve through exactly ONE
* spelling - the stem of a file name - and that one spelling is backticked by a
* large fraction of the whole pending queue, because it is the name of one of this
* repo's principal components. Admitting it admits every changeset that mentions
* that component. Re-measured over the same 25-first-parent-commit population the
* table above uses, that widening lands in the same band the runner-up was REJECTED
* in, and it lands there on a channel half one just measured at zero for four. ⇒
* widening the coordinate is a readability-budget decision, it is open, and it is
* deliberately not taken here. The numbers are on objectui#9140; ⛔ they are not
* copied into this header, which is rule #9 applied to this paragraph.
*
* ## Where it runs
*
* `changeset-presence.yml`, as a second job. That workflow carries NO path
Expand Down Expand Up @@ -380,19 +416,43 @@ export function audit(root, ref = null) {
path.endsWith('.md') &&
!NOT_A_CHANGESET.has(path.slice(path.lastIndexOf('/') + 1)),
);
const totals = { pending: pendingPaths.length, publishing: 0, naming: 0, resolved: 0, unresolved: 0, dangling: [] };
const totals = {
pending: pendingPaths.length,
publishing: 0,
naming: 0,
resolved: 0,
unresolved: 0,
dangling: [],
silent: 0,
unreachable: 0,
blind: [],
};
for (const [path, source] of readBlobs(root, ref, pendingPaths)) {
const declaration = describeDeclaration(source);
if (declaration.kind !== 'frontmatter' || declaration.entries === 0) continue;
totals.publishing += 1;
const spans = namedFiles(source);
if (spans.length === 0) continue;
if (spans.length === 0) {
// Coordinates itself by SYMBOL, or by nothing. No diff can ever make the
// diff-mode check speak about this body (objectui#9140).
totals.silent += 1;
totals.blind.push(`${path} ~ names no file at all`);
continue;
}
totals.naming += 1;
let resolvedHere = 0;
for (const span of spans) {
if (resolveNamed(index, span) === null) {
totals.unresolved += 1;
totals.dangling.push(`${path} ~ ${span}`);
} else totals.resolved += 1;
} else {
totals.resolved += 1;
resolvedHere += 1;
}
}
if (resolvedHere === 0) {
totals.unreachable += 1;
totals.blind.push(`${path} ~ every name it spells resolves to none or to many`);
}
}
return totals;
Expand Down Expand Up @@ -424,10 +484,20 @@ if (isEntrypoint(import.meta.url)) {
`File names in those bodies: ${totals.resolved} resolve to exactly one tracked file, ` +
`${totals.unresolved} resolve to none or to many.`,
);
console.log(
`\nOutside the diff-mode check's reach ENTIRELY: ${totals.silent} publishing body(ies) name no file ` +
`at all,\nand ${totals.unreachable} spell only names that resolve to none or to many. Those ` +
`${totals.silent + totals.unreachable} cannot be\nreported for ANY diff — that is the size of the ` +
'blind spot (objectui#9140), re-derived each run.',
);
if (process.argv.includes('--blind')) for (const line of totals.blind) console.log(` ${line}`);
console.log(
'\n A name that resolves to nothing is ⛔ NOT a false claim on its own — a changeset that\n' +
' retired a file names it correctly and the file is correctly gone. This count is the\n' +
" reach of the diff-mode check, not a verdict on anybody's prose.",
" reach of the diff-mode check, not a verdict on anybody's prose.\n" +
' ⛔ Nor is a body that names no file WRONG for coordinating itself by symbol — that is a\n' +
' normal way to write a changeset. It is simply a body this gate cannot carry a request\n' +
' to read about, and objectui#9140 measured one WENT-FALSE claim that sat there unseen.',
);
process.exit(0);
}
Expand Down
Loading