Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

Commit cbf5cdf

Browse files
os-salesclaude
andauthored
fix(scripts): the polarity census reads a table as structure, so a retired reading stops asserting (objectui#9870) (#9907)
Fixes #9870 Internal instrument only: `scripts/changeset-polarity-census.mjs` and its pin. No published package changes. ## The defect, and what measuring it changed about the repair The card names the last entry on the instrument's own limits list that still read `⛔ unrepaired` about a false-positive SOURCE: an entry may carry a table whose first column is a reading it is RETIRING and whose later columns say what falsified it and when. That reading stood in ASSERTION position with its own correction one cell to the right, unread. Quotation cannot reach it — the retired reading is not quoted, it is **tabulated**. The limits entry also **misdescribed the mechanism, in the direction that makes a defect look smaller**. It said the cells of one ROW were joined into one sentence and called that join deliberate. The code doing the joining carried the **opposite** intent in its own comment (`A table row: the cells are independent fragments, never one sentence.`) and achieved neither: - `. ` cuts a sentence only before a character that is **not lower-case** (`cutSentences`), and this corpus's later columns open with `objectui#…` more often than not — so the cells of one row stayed one sentence; - a row's **last** cell got no terminator at all, so nothing separated one row from the next. An entire table — header, delimiter row and every data row — collapsed into **one** sentence, and every key in it was offered to every schema named anywhere in it. That is the cartesian window objectui#9754 closed at the sentence level, rebuilt one level up. Measured on `.changeset/7322-object-kanban-component-props.md`, the entry the card quotes: the whole five-row table is one sentence, opening `--- the component is registered under TWO node type keys … 2026-09-10, 2026-09-17`. ## The direction taken — B, and why not A or C - **A**, exclude table rows from assertion position as a class — **refused**. - **C**, widen quotation position to cover every tabulated reading — **refused**. A and C differ in what the report calls the row and not in the verdict. - **B**, read the shape the table itself declares — **taken**. Both refusals have the same ground, and it is the ground this family already paid for once: a table that **asserts** is the ordinary case in this corpus (72 entries carry one, 74 header rows, spellings like `key | reading` and `schema (@object-ui/types/zod) | key | now also accepts`), and a claim silently never judged is the failure that does not ring. A and C are **cheap today** — 0 of the 40 current flags come from a table paragraph, measured with a lit control reading 672 table-derived sentences across 72 entries — and that cheapness is exactly what makes them dangerous: the cost lands later, on a table nobody will re-measure. B reads a declaration the **author** made: the header `reading below | falsified by | landed` says in so many words that the column it points back at is no longer a claim about today. So the rule is written on the HEADER, never on the shape of a row. POSITION gains a third value, `superseded`: a cell in a column the table's own header retires is counted, **reported under its own name**, and never flagged. Reading the header requires knowing which cell is in which column, so the collapse above had to go first: a table is now read as structure — a cell is its own unit of text, the delimiter row is dropped as syntax, the header row is kept. ## The entry fee — flag set per key, before and after `.changeset/`, parent commit `c1f039a67a` vs this branch, controls PASS on both runs (`corpusLit` 973→976, `memberLit` 1, `resolutionLit` 3/3, every absent control 0): | reading | before | after | | --- | --- | --- | | entries scanned | 1706 | 1707 (this PR's own changeset) | | sentences scanned | 18412 | 19104 | | matched, ASSERTION position | 226 | 225 | | matched, QUOTED position | 1 | 1 | | matched, SUPERSEDED position | — | 2 | | **candidate contradictions** | **40** | **40** | | contradiction entries | 22 | 22 | | schemas resolving nowhere | 3 | 2 | **Rows that stop being flagged: none.** The 40 candidate contradictions are the **same 40** `(entry, schema, key, polarity)` rows before and after — set difference empty in both directions — so there is no row to adjudicate as a correctly-dropped false positive or a lost true positive. Nothing was gained either. One row **does** leave a reported bucket, and it is adjudicated here: `7322-object-kanban-component-props.md` / `KanbanSchema` leaves *schemas resolving NOWHERE* (3 → 2). Its only assertion-position mention in the whole corpus was the retired reading in column 1 of that table, whose own row records that objectui#8802 retired the `KanbanSchema` arm. ⇒ **correctly-dropped FALSE POSITIVE**: a candidate whose adjudication was already written one cell to the right. No other entry loses a row; `KanbanSchema` had exactly one row in that bucket before and has none after. ### Why zero rows moved, stated rather than hidden The card cites three flagged rows on that entry from objectui#9754 slice 2's dev. They are gone already, and **not** because of anything about tables: running the **pre-#9863** instrument over today's corpus reads 123 contradictions with four on that entry (`kanban`, `filter`, `columns`, `cardTitle`); slice 2's window narrowing dropped them, because a clause whose subject is not a schema symbol now pairs with nothing. So on this corpus, at this ref, the tabulation defect is **latent, not live** — and the join `:157` describes still happens, which is why the card's downgrade arm does not fire. The defect is pinned against a fixture that makes it live. ## The fixture, where the shape and its counter-shapes are live `scripts/__tests__/fixtures/changeset-polarity/corpus/12-superseded-readings-table.md`, parent-commit instrument vs this one: | pair | before | after | what it is | | --- | --- | --- | --- | | `ObjectKanbanSchema.swimlaneWidth` | FLAG | — | retired reading, correctly dropped | | `SpinnerSchema.size` | FLAG | — | retired reading, correctly dropped | | `LaneSchema.titleField` | FLAG | — | cross-ROW pairing, correctly dropped | | `ObjectKanbanSchema.cards` | FLAG | — | cross-ROW pairing, correctly dropped | | `LaneSchema.laneWidth` | FLAG | FLAG | an ordinary table asserts, and is still read | | `ObjectKanbanSchema.dragHandle` | — | **FLAG** | TRUE POSITIVE FOUND: the falsifier column is a claim about today, and the collapse had swallowed it | Four false positives dropped, one true positive recovered, and the anti-blindness leg (`laneWidth`) green throughout. ## Gates - `pnpm exec vitest run scripts/__tests__/changeset-polarity-census.test.ts` — **71 passed**, exit 0 (path filter, so `passWithNoTests` is off and a zero-match would have failed). - `pnpm type-check:scripts` — exit 0, and `tsc --listFiles` confirms the program contains **both** changed files (the coverage-only `scripts-type-check.test.ts` was not relied on). - `pnpm check:control-bytes`, `check:changeset-claims`, `check:pending-changeset-literals`, `check:unreferenced-sources`, `check:test-path-roots`, `check:entry-guard`, `check:new-line-citations`, `check:comment-mask-corpus` — all exit 0. - `eslint --no-inline-config` on both changed files — exit 0. - Reverse verification and the full `scripts/` suite: see the comment below / the report. ## ⛔ Not in this PR `scripts/changeset-polarity-census.mjs`'s **other** unrepaired entry — «`without` and `fails to` are read as clause-wide negators» — is untouched and its text is byte-identical. It is a separate card and this change does not reach its path: the negator word list, `segmentClauses` and `readPolarity` are not modified here. ## Acceptance notes - The `.changeset/` corpus includes this PR's own changeset (objectui#9795 is open and unruled on that). Run rather than argued: the new instrument reads 21 sentences out of it, 1 carrying a present-tense declaration verb, and flags **nothing** — the verb-carrying sentence names no schema symbol, so it is outside the population. - Noted, not filed: `.changeset/` carries at least five `before | after`-shaped tables whose first column is retired by MEANING rather than by a word in the header (`page before | authority on origin/main | page after`, `key | mirror was | mirror is`, …). They are named as residue in the limits list; none of them flags today. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L --- _Generated by [Claude Code](https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5b21e79 commit cbf5cdf

4 files changed

Lines changed: 424 additions & 35 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
---
3+
4+
Internal instrument only (objectui#9870): `scripts/changeset-polarity-census.mjs` and its
5+
pin. No published package changes, no schema change, no runtime behaviour change.
6+
7+
**A TABLE OF SUPERSEDED READINGS.** A pending entry may carry a table whose first column is
8+
a reading it is RETIRING and whose later columns say what falsified it and when. The census
9+
read that first column as a present-tense claim about today's tree, with its own correction
10+
one cell to the right and unread. Quotation position could not reach it, because the retired
11+
reading is not quoted — it is tabulated. It was the last entry on the instrument's own limits
12+
list still marked unrepaired as a false-positive SOURCE.
13+
14+
Measuring it first moved the repair. The limits entry said the cells of one ROW were joined
15+
into one sentence and called that join deliberate; the code doing the joining carried the
16+
opposite intent in its own comment and achieved neither. `. ` cuts a sentence only before a
17+
character that is not lower-case, and a row's last cell got no terminator at all — so an
18+
entire table, header and delimiter row included, collapsed into ONE sentence, and every key
19+
in it was offered to every schema named anywhere in it. That is the cartesian window
20+
objectui#9754 closed at the sentence level, rebuilt one level up.
21+
22+
A table is therefore read as structure rather than flattened into wrapped prose: a cell is
23+
its own unit of text, the delimiter row is dropped as syntax, and the header row is kept —
24+
it is what lets a column be read as a position. POSITION gains a third value, `superseded`:
25+
a cell in a column the table's OWN HEADER retires (the column a later one declares it was
26+
«falsified by») is counted, reported under its own name, and never flagged.
27+
28+
Of the three directions the card offered, this is the narrow one, and the other two were
29+
refused for the same reason: excluding table rows from assertion position as a class, or
30+
widening quotation to cover every tabulated reading, are both cheap against this corpus and
31+
both buy a false negative in the shape this family has already paid for — a table that
32+
ASSERTS is the ordinary case here, and a claim silently never judged is the failure that
33+
does not ring.
34+
35+
READINGS, taken on `.changeset/` at the parent commit and again after, with the instrument's
36+
controls passing on both runs:
37+
38+
- candidate contradictions: 40 before, 40 after — the SAME 40 rows. No row stops being
39+
flagged, so no row needs adjudicating as a dropped false positive or a lost true positive.
40+
- sentences scanned 18412 → 19104, assertions 226 → 225, and a new bucket reporting 2
41+
readings in superseded position (both from the entry the card names).
42+
- «a schema whose symbol resolves nowhere this run can reach»: 3 → 2. The dropped row is a
43+
retired reading whose own row records the card that retired that symbol — a candidate
44+
whose adjudication was already written beside it. Correctly dropped.
45+
- the fixture corpus, where the shape and its three counter-shapes are pinned: 5 flags → 2.
46+
Four false positives dropped (two retired readings, two cross-ROW pairings) and one true
47+
positive FOUND — the falsifier column is a claim about today, and the collapse had
48+
swallowed it.
49+
50+
RESIDUE, named rather than discovered later: a `before | after` table retires its first
51+
column by MEANING and not by a word in its header, and is not read here; the header match is
52+
a word list with this file's usual cost; and a claim whose subject sits in one column and its
53+
verb in another is now two sentences and pairs with nothing — it was reachable only through
54+
the cross-row collapse that also paired it with every other row.

‎scripts/__tests__/changeset-polarity-census.test.ts‎

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
runControls,
3232
segmentClauses,
3333
segmentSentences,
34+
supersededColumns,
3435
} from '../changeset-polarity-census.mjs';
3536

3637
/**
@@ -1122,3 +1123,151 @@ describe('objectui#9754 pin 12 -- the window is the declaration clause, not the
11221123
function namesSchemaIn(text: string) {
11231124
return [...new Set([...text.matchAll(/\b([A-Z][A-Za-z0-9_]*Schema)\b/g)].map((m) => m[1]))];
11241125
}
1126+
1127+
1128+
describe('objectui#9870 pin 13 -- a table is read as structure, and a retired reading is not a claim', () => {
1129+
/**
1130+
* The last entry on the instrument's own limits list that still said
1131+
* `⛔ unrepaired` about a false-positive SOURCE: an entry may carry a table
1132+
* whose first column is a reading it is RETIRING and whose later columns are
1133+
* what falsified it. Quotation cannot reach that reading -- it is not quoted,
1134+
* it is TABULATED -- so it stood in ASSERTION position with its own correction
1135+
* one cell to the right, unread.
1136+
*
1137+
* ⭐ Measuring it first moved the repair. The entry said the cells of one ROW
1138+
* were joined into one sentence and called that join deliberate; the code
1139+
* doing the joining carried the OPPOSITE intent in its own comment, and
1140+
* achieved neither. `. ` cuts only before a character that is not lower-case,
1141+
* and a row's last cell got no terminator at all -- so an entire table,
1142+
* header and delimiter row included, collapsed into ONE sentence, and every
1143+
* key in it was offered to every schema named anywhere in it. That is
1144+
* objectui#9754's cartesian window rebuilt one level up.
1145+
*
1146+
* `12-superseded-readings-table.md` carries the shape and the three ways the
1147+
* repair could have been bought with blindness, so one run reads them all.
1148+
* The readings this pin is judged against, taken on the fixture corpus with
1149+
* the instrument as it stood at the parent commit and again after:
1150+
*
1151+
* BEFORE ObjectKanbanSchema.swimlaneWidth FLAG (retired reading)
1152+
* SpinnerSchema.size FLAG (retired reading)
1153+
* LaneSchema.titleField FLAG (cross-ROW pairing)
1154+
* ObjectKanbanSchema.cards FLAG (cross-ROW pairing)
1155+
* LaneSchema.laneWidth FLAG (an ordinary table asserts)
1156+
* ObjectKanbanSchema.dragHandle -- (missed: the collapse hid it)
1157+
* AFTER LaneSchema.laneWidth FLAG
1158+
* ObjectKanbanSchema.dragHandle FLAG
1159+
*
1160+
* ⭐ Four false positives dropped and a true positive FOUND: the falsifier
1161+
* column is a claim about today, and the collapse had swallowed it.
1162+
*/
1163+
const flags = flagsFor('12-superseded-readings-table');
1164+
const superseded = fixtureRun.superseded.filter((s: { entry: string }) =>
1165+
s.entry.startsWith('12-superseded-readings-table'),
1166+
);
1167+
1168+
it('a reading in a column the table retires is not an assertion', () => {
1169+
// Both retired readings contradict the fixture faces, which is what a
1170+
// retired reading DOES -- that is why it was retired. Read as assertions
1171+
// they were two candidates whose adjudication was already written beside
1172+
// them.
1173+
expect(flags.map((f) => f.key)).not.toContain('swimlaneWidth');
1174+
expect(flags.map((f) => f.key)).not.toContain('size');
1175+
});
1176+
1177+
it('⛔ they are SET ASIDE, not dropped -- the count is reported under its own name', () => {
1178+
// DARK LEG. A rule whose cost nobody can read is how a false negative hides:
1179+
// these are still read, still resolved to schema and key, and still counted.
1180+
// If this pin passed while the sentences went missing, the repair would be
1181+
// deleting evidence instead of positioning it.
1182+
expect(superseded.map((s: { claim: { keys: string[] } }) => s.claim.keys).flat().sort()).toEqual([
1183+
'size',
1184+
'swimlaneWidth',
1185+
]);
1186+
expect(fixtureRun.matchedSuperseded).toBe(superseded.length);
1187+
expect(fixtureRun.matchedSuperseded).toBeGreaterThan(0);
1188+
// And they are not in the quoted bucket either: the reason differs, so the
1189+
// name differs, or the report cannot say which rule set a reading aside.
1190+
expect(
1191+
fixtureRun.quoted.some((q: { entry: string }) =>
1192+
q.entry.startsWith('12-superseded-readings-table'),
1193+
),
1194+
).toBe(false);
1195+
});
1196+
1197+
it('⛔ did NOT buy it with blindness -- a table that ASSERTS is still read and still flagged', () => {
1198+
// The false negative that excluding table rows as a class, or widening
1199+
// quotation to cover every tabulated reading, would have bought. An
1200+
// ordinary table -- one whose header retires nothing -- is the common case
1201+
// in this corpus, and its rows are claims about today.
1202+
const laneWidth = flags.filter((f) => f.key === 'laneWidth');
1203+
expect(laneWidth.map((f) => f.schema)).toEqual(['LaneSchema']);
1204+
expect(laneWidth[0].polarity).toBe('positive');
1205+
});
1206+
1207+
it('only the retired COLUMN is set aside -- the falsifier column is a claim about today', () => {
1208+
// `objectui#0001 -- ... `ObjectKanbanSchema` declares `dragHandle` for that
1209+
// job today` sits in the `falsified by` column of the SAME row as a retired
1210+
// reading. It is the live half of that row and it is flagged: the fixture
1211+
// face declares no such member. Before this repair the collapse had folded
1212+
// it into the row-wide sentence and it was never judged at all.
1213+
const dragHandle = flags.filter((f) => f.key === 'dragHandle');
1214+
expect(dragHandle.map((f) => f.schema)).toEqual(['ObjectKanbanSchema']);
1215+
expect(dragHandle[0].polarity).toBe('positive');
1216+
expect(dragHandle[0].memberPresent).toBe(false);
1217+
});
1218+
1219+
it('the rows of a table are not one sentence, and neither are the cells of a row', () => {
1220+
// The cartesian window one level up. `ObjectKanbanSchema declares titleField`
1221+
// and `on LaneSchema, cards is declared` are rows 1 and 2 of one table, and
1222+
// the second opens with a lower-case word -- which is exactly where `. `
1223+
// failed to cut. Joined, each key was offered to BOTH schemas and both
1224+
// claims were flagged against the schema they were never made about.
1225+
expect(flags.map((f) => `${f.schema}.${f.key}`)).not.toContain('LaneSchema.titleField');
1226+
expect(flags.map((f) => `${f.schema}.${f.key}`)).not.toContain('ObjectKanbanSchema.cards');
1227+
// The mechanism, asserted rather than inferred from the verdict: no emitted
1228+
// sentence carries the text of two different cells.
1229+
const sentences = segmentSentences(
1230+
fs.readFileSync(path.join(FIXTURE_CORPUS, '12-superseded-readings-table.md'), 'utf8'),
1231+
);
1232+
expect(sentences.some((s) => /titleField/.test(s.text) && /cards/.test(s.text))).toBe(false);
1233+
expect(sentences.some((s) => /swimlaneWidth/.test(s.text) && /dragHandle/.test(s.text))).toBe(
1234+
false,
1235+
);
1236+
});
1237+
1238+
it('the delimiter row is syntax and the header row is text', () => {
1239+
const sentences = segmentSentences(
1240+
['| reading below | falsified by | landed |', '| --- | :---: | ---: |', '| `S` declares `k` | objectui#1 -- it does not | 2026-09-11 |'].join('\n'),
1241+
);
1242+
// What the delimiter row carries is WHICH row was the header. Its dashes are
1243+
// not a sentence, and they used to be three.
1244+
expect(sentences.map((s) => s.text)).toEqual([
1245+
'reading below',
1246+
'falsified by',
1247+
'landed',
1248+
'`S` declares `k`',
1249+
'objectui#1 -- it does not',
1250+
'2026-09-11',
1251+
]);
1252+
expect(sentences.map((s) => s.position)).toEqual([
1253+
'superseded',
1254+
'assertion',
1255+
'assertion',
1256+
'superseded',
1257+
'assertion',
1258+
'assertion',
1259+
]);
1260+
});
1261+
1262+
it('the rule is the header the table WROTE, not a shape this reader guesses at', () => {
1263+
// A header that declares nothing retires nothing -- the default is the empty
1264+
// set, so every ordinary table keeps every column in assertion position.
1265+
expect([...supersededColumns(['face', 'what it declares today'])]).toEqual([]);
1266+
expect([...supersededColumns(null)]).toEqual([]);
1267+
// The falsifier declares the columns BEFORE it retired, and it has to point
1268+
// BACK at something: a first column that falsifies has nothing behind it.
1269+
expect([...supersededColumns(['reading below', 'falsified by', 'landed'])]).toEqual([0]);
1270+
expect([...supersededColumns(['id', 'reading', 'superseded by'])]).toEqual([0, 1]);
1271+
expect([...supersededColumns(['falsified by', 'reading'])]).toEqual([]);
1272+
});
1273+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
'@object-ui/types': patch
3+
---
4+
5+
The superseded-readings pin (objectui#9870). An entry may carry a table whose
6+
first column is a reading it is RETIRING, and whose later columns say what
7+
falsified it and when. The table declares that itself, in its header.
8+
9+
| reading below | falsified by | landed |
10+
| --- | --- | --- |
11+
| `ObjectKanbanSchema` declares `swimlaneWidth` | objectui#0001 — no face ever declared it; `ObjectKanbanSchema` declares `dragHandle` for that job today | 2026-09-11 |
12+
| `SpinnerSchema` declares no `size` | objectui#0002 — `size` is declared on `SpinnerSchema`, as the protocol has it | 2026-09-12 |
13+
14+
A table whose header retires NOTHING is an ordinary table and asserts:
15+
16+
| face | what it declares today |
17+
| --- | --- |
18+
| the board | `ObjectKanbanSchema` declares `allowCollapse` |
19+
| the lane | `LaneSchema` declares `laneWidth` |
20+
21+
And the rows of one table are not one sentence, which is what the collapse made
22+
them:
23+
24+
| face | the reading |
25+
| --- | --- |
26+
| board | `ObjectKanbanSchema` declares `titleField` |
27+
| lane | on `LaneSchema`, `cards` is declared |

0 commit comments

Comments
 (0)