Repository navigation
Commit 71a4b4e
fix(release): the duplicate-Release audit counts distinct ids, so a Release the moving list serves twice is one (#26559)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi92PbDQRKbS6saKBBMa9Dg8A)_
Fixes #26552
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:发版脚本的「重复 Release」审计改成按 Release id 去重,同一个 id 被翻页列两次只算一个;列表漏掉的 tag
先按 tag 重读一次再判「未核实」。
- 为什么改:18.2.0 补发 run 40464600678 里 `@object-ui/fields@18.2.0` 的同一个 id
425135424 被列了两次,审计报「2 个 Release」、surplus 列表为空,发布本身完整却红了。
- 风险与回滚:只改 `scripts/release-github-releases.mjs` 一个文件,不动工作流;真正两个不同 id
的重复照旧报错(自测有控制行)。回滚即 revert 本 PR。
- 先看:`scripts/release-github-releases.mjs` 的 `auditDuplicateReleases`(按
id 记账与 by-tag 重读),其次 `describeDuplicates`,再看自测第 16 组。
- 两分钟验证:`node scripts/release-github-releases.mjs --self-test`,预期一行 `✓ …
114 assertions`。
- 需要你做的:无,席位落地。
## What changes
`scripts/release-github-releases.mjs` only, plus its `--self-test`.
- `auditDuplicateReleases` keys each tag's rows by `rel.id`. A tag is a
duplicate only when it carries two distinct ids. The same id served on
two offset pages is one Release, counted in a new `rowsServedTwice`
field.
- When the walk completed, a tag no page showed is re-read through
`findByTag` before it is called unseen. A Release the by-tag endpoint
answers goes into a new `foundByTag` field instead of `unseen`. A walk
that stopped at its page cap re-reads nothing, because its unseen tags
may sit, twins included, on pages it never read.
- `describeDuplicates` collapses repeated ids itself and skips an entry
with fewer than two distinct ids, so it can never print an empty surplus
list or a repeated "This run created" id. It also returns `notes`: a
plain log line (not a warning) when the list moved under the walk.
`main` prints the notes before the warnings.
- The stub client gains a `listMoves` hook that reshapes the
newest-first list just before a page is cut from it.
## Why
Release run 40464600678 (job 122058101982, the 18.2.0 `force: true`
repair) published 110 of 110 packages and still exited 1:
```
##[error]@object-ui/fields@18.2.0 has 2 GitHub Releases: 425135424 (no assets), 425135424 (no assets). … the other(s), , are the surplus. This run created 425135424, 425135424 …
##[warning]… 1 tag(s) this run released never appeared in it (@object-ui/types@18.2.0) …
```
All of one version's Releases carry the same tag date, so their order
among themselves is not stable from one list request to the next, and a
create pushes every row down. Between two offset page reads one row was
served twice and another on no page. The audit pushed every matching row
without keying on the id.
## Tests
New battery `16. A list that moves between page reads is one Release per
id` (11 cases, pinned in `SELF_TEST_BATTERIES`; the roster floor is
raised 15 to 16):
- Positive control: the swap fixture really serves one id on pages 1 and
2 and one id on neither.
- A tie-order swap at the page boundary gives 0 duplicates and 0 unseen.
`rowsServedTwice` is 1, `foundByTag` names the hidden tag, there are no
errors or warnings, and there is one note.
- A create landing at the head between two reads gives 0 duplicates, 0
unseen, 0 found by tag and 1 repeated row.
- Control: a real two-id pair under the same swap is still one ERROR,
listing `9003 (no assets), 9004 (no assets)`, with the non-empty surplus
`9004` and `This run created 9004:`.
- The literal 40464600678 audit shape (one id twice, created twice)
handed to `describeDuplicates` gives no error, no warning and no
empty-surplus text.
- A capped walk sends no by-tag read for its unread tags, and they stay
UNVERIFIED.
Batteries 1–15 are unchanged. Battery 14's real-pair, pre-existing-pair,
capped and never-appeared cases all still hold.
No ablation: `.claude/agents/os-dev.md` excludes scripts from one-time
ablations. Battery 16's positive control shows the fixture really moves.
## Verification
<details><summary>check:release-body at the final head</summary>
```
$ pnpm check:release-body
✓ release-github-releases --self-test: 114 assertions (real packages/spec/CHANGELOG.md 17.0.0-rc.2 section = 344008 chars -> 123909, limit 125000)
```
Before the change, on `origin/main` ab3c776: 103 assertions.
</details>
<details><summary>Derived gate families at head 0765c51: 31 run, 31
exit 0</summary>
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 31 commands. Each exit code was
captured before any pipe and with stdin closed.
```
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-declaration-mirrors.mjs :: exit 0
node scripts/check-declaration-mirrors.mjs --self-test :: exit 0
node scripts/check-self-test-wired.mjs :: exit 0
node scripts/check-self-test-wired.mjs --self-test :: exit 0
node scripts/check-self-test-workflow-commands.mjs :: exit 0
node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0
node scripts/check-whole-set-label-write.mjs :: exit 0
node scripts/check-whole-set-label-write.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:bash32-floor :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:cli-command-ids :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:entry-guard :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:parse-guard :: exit 0
pnpm check:pnpm-filter-targets :: exit 0
pnpm check:pnpm-flag-spellings :: exit 0
pnpm check:ratchet-remedy-authority :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:release-body :: exit 0
pnpm check:scripts-symbol-anchors :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:pm-dispatch-gates :: exit 0
```
`dispatch-gates --ran`: `31 derived famil(ies) accounted for — 31 run, 0
NOT-MEASURED (a DERIVED zero — all 31 recorded an exit code and none of
them is 3).`
Not run locally, and left to CI: the type-check lanes and the `Plan Test
Core shards` step, which `--commands` lists as CI-only. The script is
plain `.mjs` and no package `typecheck` program reads it.
</details>
## Acceptance notes
- The by-tag re-read proves that a hidden tag's Release exists. It
cannot prove that no second Release on that tag was hidden by the same
move. That limit belongs to any walk over a list whose tie order is
unstable, and the note line says the list moved.
- `skip-changeset`: the diff touches only a repo-internal release tool.
No package ships it.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(not touched)
- [x] 2. Every name I added uses the casing for its kind.
- [ ] 3. No formatter ran; `pnpm lint` passes. No formatter ran; `pnpm
lint` is CI's run (Lint & Repo Gates), not run locally.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset. (publishes
nothing: `skip-changeset`; both lines are above)
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (not touched)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [ ] The *Before Submitting* list in CONTRIBUTING.md is done. The full
`pnpm test` belongs to CI; this card's local subset is the derived gate
list above.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01ADHkmT8PswRoP8QDevDFuR
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ADHkmT8PswRoP8QDevDFuR)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent f9e8401 commit 71a4b4e
1 file changed
Lines changed: 226 additions & 25 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
67 | | - | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
68 | 70 | | |
69 | 71 | | |
70 | 72 | | |
| |||
137 | 139 | | |
138 | 140 | | |
139 | 141 | | |
| 142 | + | |
140 | 143 | | |
141 | 144 | | |
142 | 145 | | |
143 | 146 | | |
144 | | - | |
145 | | - | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
146 | 150 | | |
147 | 151 | | |
148 | 152 | | |
| |||
898 | 902 | | |
899 | 903 | | |
900 | 904 | | |
901 | | - | |
902 | | - | |
| 905 | + | |
| 906 | + | |
| 907 | + | |
| 908 | + | |
| 909 | + | |
| 910 | + | |
| 911 | + | |
| 912 | + | |
| 913 | + | |
| 914 | + | |
| 915 | + | |
| 916 | + | |
| 917 | + | |
| 918 | + | |
| 919 | + | |
903 | 920 | | |
904 | 921 | | |
905 | 922 | | |
| |||
910 | 927 | | |
911 | 928 | | |
912 | 929 | | |
| 930 | + | |
| 931 | + | |
913 | 932 | | |
914 | 933 | | |
915 | 934 | | |
| |||
922 | 941 | | |
923 | 942 | | |
924 | 943 | | |
925 | | - | |
| 944 | + | |
926 | 945 | | |
927 | 946 | | |
928 | 947 | | |
929 | 948 | | |
| 949 | + | |
930 | 950 | | |
931 | 951 | | |
932 | 952 | | |
| |||
935 | 955 | | |
936 | 956 | | |
937 | 957 | | |
938 | | - | |
939 | | - | |
940 | | - | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
941 | 962 | | |
942 | 963 | | |
943 | 964 | | |
| |||
953 | 974 | | |
954 | 975 | | |
955 | 976 | | |
956 | | - | |
957 | | - | |
958 | | - | |
| 977 | + | |
| 978 | + | |
| 979 | + | |
959 | 980 | | |
960 | 981 | | |
961 | 982 | | |
| |||
972 | 993 | | |
973 | 994 | | |
974 | 995 | | |
975 | | - | |
| 996 | + | |
| 997 | + | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
| 1001 | + | |
| 1002 | + | |
| 1003 | + | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
976 | 1012 | | |
977 | 1013 | | |
978 | 1014 | | |
979 | 1015 | | |
980 | | - | |
981 | | - | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
982 | 1020 | | |
983 | 1021 | | |
984 | | - | |
| 1022 | + | |
985 | 1023 | | |
986 | 1024 | | |
987 | 1025 | | |
988 | 1026 | | |
| 1027 | + | |
989 | 1028 | | |
990 | | - | |
| 1029 | + | |
| 1030 | + | |
| 1031 | + | |
| 1032 | + | |
| 1033 | + | |
| 1034 | + | |
991 | 1035 | | |
992 | 1036 | | |
993 | 1037 | | |
994 | 1038 | | |
995 | 1039 | | |
996 | 1040 | | |
997 | 1041 | | |
998 | | - | |
999 | | - | |
| 1042 | + | |
| 1043 | + | |
1000 | 1044 | | |
1001 | 1045 | | |
1002 | 1046 | | |
1003 | | - | |
| 1047 | + | |
| 1048 | + | |
1004 | 1049 | | |
1005 | | - | |
| 1050 | + | |
1006 | 1051 | | |
1007 | 1052 | | |
1008 | 1053 | | |
| |||
1022 | 1067 | | |
1023 | 1068 | | |
1024 | 1069 | | |
1025 | | - | |
| 1070 | + | |
| 1071 | + | |
| 1072 | + | |
| 1073 | + | |
| 1074 | + | |
| 1075 | + | |
| 1076 | + | |
| 1077 | + | |
| 1078 | + | |
| 1079 | + | |
| 1080 | + | |
| 1081 | + | |
| 1082 | + | |
1026 | 1083 | | |
1027 | 1084 | | |
1028 | 1085 | | |
| |||
1091 | 1148 | | |
1092 | 1149 | | |
1093 | 1150 | | |
1094 | | - | |
1095 | | - | |
| 1151 | + | |
| 1152 | + | |
1096 | 1153 | | |
1097 | 1154 | | |
1098 | 1155 | | |
| |||
1103 | 1160 | | |
1104 | 1161 | | |
1105 | 1162 | | |
| 1163 | + | |
1106 | 1164 | | |
1107 | 1165 | | |
1108 | 1166 | | |
| |||
1174 | 1232 | | |
1175 | 1233 | | |
1176 | 1234 | | |
| 1235 | + | |
| 1236 | + | |
1177 | 1237 | | |
1178 | 1238 | | |
1179 | 1239 | | |
| |||
1183 | 1243 | | |
1184 | 1244 | | |
1185 | 1245 | | |
| 1246 | + | |
1186 | 1247 | | |
1187 | 1248 | | |
1188 | 1249 | | |
| |||
1225 | 1286 | | |
1226 | 1287 | | |
1227 | 1288 | | |
1228 | | - | |
| 1289 | + | |
1229 | 1290 | | |
1230 | 1291 | | |
1231 | 1292 | | |
| |||
1851 | 1912 | | |
1852 | 1913 | | |
1853 | 1914 | | |
| 1915 | + | |
| 1916 | + | |
| 1917 | + | |
| 1918 | + | |
| 1919 | + | |
| 1920 | + | |
| 1921 | + | |
| 1922 | + | |
| 1923 | + | |
| 1924 | + | |
| 1925 | + | |
| 1926 | + | |
| 1927 | + | |
| 1928 | + | |
| 1929 | + | |
| 1930 | + | |
| 1931 | + | |
| 1932 | + | |
| 1933 | + | |
| 1934 | + | |
| 1935 | + | |
| 1936 | + | |
| 1937 | + | |
| 1938 | + | |
| 1939 | + | |
| 1940 | + | |
| 1941 | + | |
| 1942 | + | |
| 1943 | + | |
| 1944 | + | |
| 1945 | + | |
| 1946 | + | |
| 1947 | + | |
| 1948 | + | |
| 1949 | + | |
| 1950 | + | |
| 1951 | + | |
| 1952 | + | |
| 1953 | + | |
| 1954 | + | |
| 1955 | + | |
| 1956 | + | |
| 1957 | + | |
| 1958 | + | |
| 1959 | + | |
| 1960 | + | |
| 1961 | + | |
| 1962 | + | |
| 1963 | + | |
| 1964 | + | |
| 1965 | + | |
| 1966 | + | |
| 1967 | + | |
| 1968 | + | |
| 1969 | + | |
| 1970 | + | |
| 1971 | + | |
| 1972 | + | |
| 1973 | + | |
| 1974 | + | |
| 1975 | + | |
| 1976 | + | |
| 1977 | + | |
| 1978 | + | |
| 1979 | + | |
| 1980 | + | |
| 1981 | + | |
| 1982 | + | |
| 1983 | + | |
| 1984 | + | |
| 1985 | + | |
| 1986 | + | |
| 1987 | + | |
| 1988 | + | |
| 1989 | + | |
| 1990 | + | |
| 1991 | + | |
| 1992 | + | |
| 1993 | + | |
| 1994 | + | |
| 1995 | + | |
| 1996 | + | |
| 1997 | + | |
| 1998 | + | |
| 1999 | + | |
| 2000 | + | |
| 2001 | + | |
| 2002 | + | |
| 2003 | + | |
| 2004 | + | |
| 2005 | + | |
| 2006 | + | |
| 2007 | + | |
| 2008 | + | |
| 2009 | + | |
| 2010 | + | |
| 2011 | + | |
| 2012 | + | |
| 2013 | + | |
| 2014 | + | |
| 2015 | + | |
| 2016 | + | |
| 2017 | + | |
| 2018 | + | |
| 2019 | + | |
| 2020 | + | |
| 2021 | + | |
| 2022 | + | |
| 2023 | + | |
| 2024 | + | |
| 2025 | + | |
| 2026 | + | |
| 2027 | + | |
| 2028 | + | |
| 2029 | + | |
| 2030 | + | |
| 2031 | + | |
| 2032 | + | |
| 2033 | + | |
| 2034 | + | |
| 2035 | + | |
| 2036 | + | |
| 2037 | + | |
| 2038 | + | |
| 2039 | + | |
| 2040 | + | |
| 2041 | + | |
| 2042 | + | |
| 2043 | + | |
| 2044 | + | |
| 2045 | + | |
| 2046 | + | |
| 2047 | + | |
| 2048 | + | |
| 2049 | + | |
| 2050 | + | |
| 2051 | + | |
| 2052 | + | |
| 2053 | + | |
| 2054 | + | |
1854 | 2055 | | |
1855 | 2056 | | |
1856 | 2057 | | |
| |||
0 commit comments