Skip to content

Commit 3566e55

Browse files
os-helpclaude
andauthored
chore(devx): measure the #6635 partial-retirement-annotation signal over the repo (#6635) (#7190)
The maintainer ruling of 2026-08-09 made this a measurement card, not a gate card: run the proposed signal once over the whole repo and report the numbers that select a decision branch. No gate is wired here and no existing gate changes. Adds the one-shot instrument and the audit record. The instrument is deliberately named `measure-*` rather than `check-*`/`gen-*`, so the #4203 script ledger has nothing to classify (verified: `check:generated --reconcile-only` is green and does not list it). Headline numbers, at `origin/main` bf32d4a: - 197 hits across 65 files, all tiers, no exclusions - positive control reproduces #6630 mechanically at the pre-fix commit (flagged) and is clear on current main (fixed) -- red before, green after - 34 hits hand-verified: 0 true positives, 34 false positives - best-case rule (tier A+B, record surfaces path-excluded): 20 hits / 16 files, all 20 hand-verified false positives - five structurally legitimate partial-citation classes, the ledgers and retirement pin tests foremost The decision the numbers feed is the maintainer's, per the ruling. Claude-Session: https://claude.ai/code/session_01KJATVrh6V2ysutYUJigh3B Co-authored-by: Claude <noreply@anthropic.com>
1 parent f1ab039 commit 3566e55

2 files changed

Lines changed: 706 additions & 0 deletions

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Partial-retirement-annotation signal — corpus measurement (#6635)
2+
3+
**Measured at** `origin/main` `bf32d4a0ec1cebca4f636b26c040ad6903c54287` (2026-08-10).
4+
`main` takes roughly 18 merges a day, so every number below belongs to that sha.
5+
6+
**Instrument**: `scripts/measure-partial-retirement-annotation.mjs`. Not a gate, not
7+
wired into any workflow, not a `check:`/`gen:` script. Committed so the numbers are
8+
reproducible rather than asserted.
9+
10+
**Mandate**: the maintainer ruling of 2026-08-09 on #6635 — measurement first, no gate
11+
is built yet. This document reports total hits, a spot-verified true/false-positive
12+
split, and the exemption count a warning-tier rule would need on day one. **It does not
13+
select a decision branch**; the ruling reserves that.
14+
15+
---
16+
17+
## 1. The signal as implemented
18+
19+
For each (file, retired symbol): flag the file when at least one mention of the symbol
20+
cites its retirement issue number and at least one other mention does not and does not
21+
frame the symbol historically. Annotation-presence, never name-presence — which is what
22+
lets a tombstone error string, whose whole job is to name the retired key, pass.
23+
24+
The annotation window is the **stanza**: a maximal run of lines with non-empty content
25+
once comment furniture is stripped. In TSDoc that is the paragraph; in markdown it is
26+
the paragraph.
27+
28+
Inventory (141 symbols) is the repo's own declaration surface — `RETIRED_DEFS_BY_MAJOR`,
29+
`RETIRED_KEYS_BY_MAJOR` and the `retiredKey()` tombstone guidance strings — tiered by how
30+
distinctive the name is:
31+
32+
| tier | what | example | count |
33+
|---|---|---|---|
34+
| A | retired def names | `ETLPipeline`, `WidgetManifest` | 45 |
35+
| B | retired keys, qualified | `Manifest.loading`, `crypto.hash` | 81 |
36+
| C | retired keys, bare | `transform`, `type`, `cursor` | 15 |
37+
38+
## 2. Positive control — the scanner is proven to see before any number is believed
39+
40+
The specimen fixes landed before this measurement, so the control was rebuilt from
41+
history rather than read off `main`:
42+
43+
| tree | file | result |
44+
|---|---|---|
45+
| `4e271b2c6` (= the #6630 fix commit's parent) | `packages/spec/src/shared/retry-policy.zod.ts` | **FLAGGED**`ETLPipeline`, cited at L30 (`#6414`), bare at L81 and L133 |
46+
| `bf32d4a0e` (`origin/main`) | same file | **clear** — 0 hits |
47+
48+
That is the #6630 finding reproduced mechanically, and the expected direction in both
49+
legs: red before the fix, green after it.
50+
51+
**Recall bound, measured on the same specimen.** #6630 landed in two parts. The signal
52+
catches part 1 (PR #6701) and **structurally cannot catch part 2** (PR #6753): that site
53+
had a single mention, phrased as prose (`an ETL pipeline's retry`) rather than as the
54+
symbol name. The signal needs at least two mentions with at least one already annotated,
55+
so it is blind both to single-mention drift and to the pure-miss case where a retirement
56+
pass touched nothing in the file at all — the more common shape of a missed retirement.
57+
58+
## 3. Total hits
59+
60+
| scope | hits (file x symbol) | files |
61+
|---|---|---|
62+
| all tiers, no exclusions | **197** | **65** |
63+
| tier A only | 42 | 10 |
64+
| tier B only | 99 | 45 |
65+
| tier C only | 56 | 22 |
66+
67+
Window sensitivity — the burden is not an artifact of the stanza choice:
68+
69+
| window | hits | files |
70+
|---|---|---|
71+
| stanza (default) | 197 | 65 |
72+
| plus/minus 3 lines | 184 | 66 |
73+
| plus/minus 6 lines | 179 | 56 |
74+
75+
## 4. True/false-positive split
76+
77+
**34 of 197 hits were verified by hand** (file opened, mention and its stanza read in
78+
context). Sampling was not random: every hit in the residual set of section 5 was
79+
verified exhaustively (20/20), plus 14 more drawn one-per-class from the record surfaces
80+
and tier C, chosen to cover each structural class rather than to estimate a rate.
81+
82+
**Result: 0 true positives, 34 false positives.**
83+
84+
No hit was a retired symbol taught in the present tense as if it still existed. The
85+
closest candidate — `packages/spec/docs/SYNC_ARCHITECTURE.md` L399, an
86+
`import type { ETLPipeline }` — is the deliberate "Before" specimen of a migration
87+
guide, in a plain fence rather than a `typescript` one, with the file stating in prose
88+
that it must not compile.
89+
90+
## 5. What a warning-tier rule would emit on day one
91+
92+
Building the most generous structural exclusions into the rule itself — drop tier C
93+
entirely, path-exclude every record surface (the two registries, `CHANGELOG.md`,
94+
`.changeset/`, `docs/audits/`, `docs/protocol-upgrade-guide.md`,
95+
`content/docs/releases/`) — leaves:
96+
97+
| rule shape | hits | files |
98+
|---|---|---|
99+
| all tiers, no exclusions | 197 | 65 |
100+
| tier A+B only | 141 | 51 |
101+
| **tier A+B, record surfaces excluded** | **20** | **16** |
102+
| tier A only, record surfaces excluded | 8 | 5 |
103+
104+
**All 20 were hand-verified. All 20 are false positives.** So the day-one exemption
105+
count for the best-case rule is **20 entries across 16 files, against 0 true positives**
106+
— every warning it emits on the current tree would have to be exempted.
107+
108+
## 6. Structurally legitimate partial-citation classes
109+
110+
Five classes where partial citation is correct authoring, not drift. These are not
111+
"files that happen to be noisy" — in each, annotating every mention would make the prose
112+
worse.
113+
114+
1. **The retirement ledgers themselves** (62 hits / 2 files). `RETIRED_DEFS_BY_MAJOR`'s
115+
entries are bare string literals — `'ui/I18nObject',` — that can never carry an issue
116+
number, and the D2/D3 evidence strings enumerate the retired names by design
117+
(`'No source imports \`ServerEvent\`, \`ServerEventType\`, ...'`).
118+
2. **Retirement pin tests.** A removal's pin test asserts the name is gone —
119+
`expect(name in httpServer).toBe(false)`,
120+
`expect(HookBodyCapability.options).not.toContain('crypto.hash')`,
121+
`it('rejects the retired \`body\` with the rename prescription')`. The name must
122+
appear bare, once per assertion. This is a genre the repo requires of every
123+
retirement.
124+
3. **Migration guides with a "Before" specimen.** The retired shape must be spelled out
125+
un-annotated or the guide teaches nothing.
126+
4. **Changelogs, changesets and generated projections** (74 hits / 34 files). Historical
127+
by construction: the release note that removed the symbol names it, and so do the
128+
earlier entries that introduced it.
129+
5. **Annotated header plus enumeration stanza.** The repo's own correct pattern is a
130+
section header carrying `#NNNN` followed by a markdown table or bullet list naming one
131+
retired shape per row (`system/http-server.zod.ts` L198-215 is the model). Any
132+
stanza-scoped window splits the citation from the rows it governs.
133+
134+
## 7. Two detector-level defects found while building the instrument
135+
136+
Reported because they price the gate, not as findings against any file.
137+
138+
**a. Bare-key matching collides, including with other retirements.** Tier C keys —
139+
`type`, `body`, `layout`, `multiple`, `transform`, `cursor`, `distinct` — are ordinary
140+
words. Sharpest case: `distinct` was retired **twice by different issues**
141+
(`query.distinct` at #4286, `AggregationNode:distinct` at #6815), so a paragraph
142+
correctly annotated `#4286` reads as un-annotated when checked against `#6815`
143+
(`content/docs/protocol/objectql/query-syntax.mdx` L102).
144+
145+
**b. There is no machine-readable retirement-to-issue mapping in the repo.** A gate
146+
needs one as data. The registry tables are flat string arrays whose attribution lives
147+
only in free prose above each group, and parsing that prose is wrong in two silent ways:
148+
it picks the wrong number when a comment names several (the `transform` trio's header
149+
reads "The first entries since #4659 built this table (#5552)" — #4659 built the table,
150+
#5552 is the retirement), and it cannot see repo boundaries (the #6946 group cites
151+
`objectui#3829` and `objectui#3818`, indistinguishable from local numbers once the
152+
prefix is dropped). The mapping in the instrument is therefore hand-curated and stated
153+
explicitly so it can be audited.
154+
155+
## 8. Reproducing
156+
157+
```bash
158+
node scripts/measure-partial-retirement-annotation.mjs # summary
159+
node scripts/measure-partial-retirement-annotation.mjs --hits # every hit with context
160+
node scripts/measure-partial-retirement-annotation.mjs --tier A # one tier
161+
node scripts/measure-partial-retirement-annotation.mjs --window 3 # window sensitivity
162+
node scripts/measure-partial-retirement-annotation.mjs --inventory # the 141 symbols
163+
```
164+
165+
The positive control needs a tree at the pre-fix commit:
166+
167+
```bash
168+
git worktree add --detach ../objectstack-6635-cmp 4e271b2c670a1262b83dc50450c1244639df6115
169+
cd ../objectstack-6635-cmp
170+
node <path-to>/scripts/measure-partial-retirement-annotation.mjs \
171+
--tier A --file packages/spec/src/shared/retry-policy.zod.ts --hits
172+
```

0 commit comments

Comments
 (0)