You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Release: post-upload reconcile cuts no tag, so the next run re-plans the same version, tags it at a later commit, and pypi-publish fails #1237
Observed 2026-10-02 when #1228 (12c0cec) merged. Original premise (two concurrent runs planning the same versions) was wrong; the comment below has the logs. Corrected description:
What happened
Run 37033996914 (75179f3, perf(rust): one walk per path table per statement, cheaper rows #1212 merge, 16:26Z) published dirsql 0.4.61 to PyPI at 16:46Z. Its pypi-tag / tag job ran reconcile three seconds later and returned {"ok":true,"actions":[]}: no dirsql-py-v0.4.61 tag was cut, presumably because the registry had not yet reflected the upload.
Run 37042741616 (12c0cec, docs: correct seven stale claims (output format, init table, root, no_ignore, docs host) #1228, 17:44Z) therefore derived 0.4.61 again from the newest tag (0.4.60), got verdict skip from PyPI, but still built the wheels, and release-github created the dirsql-py-v0.4.61 release and tag at 12c0cec. pypi-publish downloads every *-sdist/*-wheel-* artifact regardless of verdict and failed with 400 File already exists.
Net state: the dirsql-py-v0.4.61 tag points at 12c0cec, whose run.rs help text is not in the 0.4.61 wheel on PyPI (uvx dirsql==0.4.61 query --help prints the old wording). Every later dirsql-py bump will diff from a tag that overstates what shipped.
Run 37042756273 (9a5dc6d, #1227) planned only the plugin and is unrelated, except that its reconcile also cut no plugin tag; the newest plugin tag on origin is still v0.1.9 while PyPI has 0.1.21.
Fix options (see the comment for detail): tag from the upload step rather than one post-upload reconcile read; make pypi-publish honor the skip verdict; do not create a GitHub release for a package whose verdict was skip; add a workflow concurrency group as a belt. Needs a decision on which.
Also outstanding: the #1228 help text must still reach PyPI, which happens on the next dirsql-py bump.
release.yml delegates to thekevinscott/putitoutthere/.github/workflows/release.yml@v0; packages come from putitoutthere.toml. The plan job checks out with the full +refs/tags/* refspec and runs putitoutthere@v0 command: plan.
Two separate inputs:
the version comes from git tags ({name}-v{version}): the newest tag for the package sets the base commit, the package's globs are diffed from that commit to HEAD, and a hit bumps the patch.
the verdict (publish / skip) comes from the live registry.
So a missing tag makes the next plan recompute the same version, and the registry check then marks it skip.
The two runs
37033996914 (75179f3, plan 16:36:58Z) planned rust 0.4.50 / py 0.4.61 / npm 0.4.63, all publish. pypi-publish uploaded 6 dirsql-0.4.61 files, 16:46:26–16:47:05Z. pypi-tag / tag ran command: reconcile 16:47:08–16:47:17Z and returned {"ok":true,"dryRun":false,"actions":[]} — no dirsql-py-v0.4.61 tag was cut. That run's release-github created only dirsql-npm-v0.4.63 and dirsql-rust-v0.4.50.
37042741616 (12c0cec, plan 17:44:22Z) therefore still saw dirsql-py-v0.4.60 (469c8f6, 16:36Z) as newest, diffed from there, and computed 0.4.61 again. Verdicts: rust 0.4.51 publish, npm 0.4.64 publish, plugin 0.1.21 publish, dirsql-py 0.4.61 skip. release / publish logged publish: dirsql-py@0.4.61 already published; skipping, then release-github created dirsql-npm-v0.4.64, dirsql-py-v0.4.61, dirsql-rust-v0.4.51 at 17:54:31–36Z. git ls-remote now resolves dirsql-py-v0.4.61^{} to 12c0cec, while the files on PyPI are 75179f3's build. The matrix still built the py wheels despite the skip, pypi-publish downloads *-sdist + *-wheel-* unfiltered, so it re-uploaded them → 400 File already exists; pypi-tag skipped.
37042756273 (9a5dc6d, plan 17:54:42Z) planned onlydirsql-plugin-embeddings 0.1.21 — rust/npm/py had been tagged at 12c0cec three seconds earlier. Correction to the report above: it never planned py/npm/rust. Its upload ran 17:55:30–17:55:55Z and its reconcile also returned actions: []; the newest plugin tag on origin is still dirsql-plugin-embeddings-v0.1.9.
The plan never chose a stale base because of a concurrent run. It chose the correct base for the tag state it was given; the 16:47Z reconcile is what left no tag.
Refuted — the change is absent. uvx --exclude-newer-package "dirsql=2026-10-03T00:00:00Z" dirsql==0.4.61 query --help prints print the result rows as JSON on stdout. On main, 12c0cec rewrote that to print the result rows on stdout (a table on a terminal, a JSON array when piped). 0.4.61 is 75179f3's build and no later dirsql version exists.
Fix options
No concurrency: block exists in .github/workflows/release.yml.
concurrency: { group: release, cancel-in-progress: false } at workflow level. Removes the possibility of two plans racing a publish, at the cost of queueing every merge behind the previous ~12-minute release.
Cut the PyPI tag from what the upload job just pushed (or poll the registry until the version is visible) instead of a single reconcile read three seconds after upload. Fixes the gap that actually happened here; needs a change upstream in putitoutthere.
Filter pypi-publish's download to packages the plan marked publish. Stops the 400 and the wasted upload, but leaves the missing-tag drift that caused it.
changed the title [-]Release: concurrent runs planned the same version; #1228's dirsql-py publish failed with 'file already exists'[/-][+]Release: post-upload reconcile cuts no tag, so the next run re-plans the same version, tags it at a later commit, and pypi-publish fails[/+]on Oct 2, 2026
Follow-up from the next merge, #1206 (43e5987, run 37044943501, 18:13Z), which shows the same reconcile step misfiring in a second way.
dirsql-py planned 0.4.62, published to PyPI at 18:14:07Z, and pypi-tag / tag this time did create a tag: {"package":"dirsql-py","version":"0.4.62","tag":"dirsql-py-v0.4.62","commit":"469c8f64","source":"sibling","created":true}. But 469c8f6 is the commit dirsql-py-v0.4.60 already points at, not 43e5987, the commit the wheel was built from. So the py tags now read: v0.4.60 → 469c8f6, v0.4.61 → 12c0cec (wheel built from 75179f3), v0.4.62 → 469c8f6 (wheel built from 43e5987). None of the last two tags names the commit that produced its wheel.
dirsql-plugin-embeddings planned 0.1.21 again (PyPI already had it from 17:55Z, verdict skip), yet release-github re-created the dirsql-plugin-embeddings-v0.1.21 release and moved its tag from the 9a5dc6d-era commit to 43e5987.
Net: the registry is the truth and is correct; the tags are derived twice (reconcile after upload, and release-github) and both derivations pick commits by rule rather than from the build. A fix that tags the exact commit the build job used, once, would remove both failure modes.
Activity
thekevinbot commentedon Oct 2, 2026
Version planning
release.ymldelegates tothekevinscott/putitoutthere/.github/workflows/release.yml@v0; packages come fromputitoutthere.toml. Theplanjob checks out with the full+refs/tags/*refspec and runsputitoutthere@v0 command: plan.Two separate inputs:
{name}-v{version}): the newest tag for the package sets the base commit, the package'sglobsare diffed from that commit toHEAD, and a hit bumps the patch.publish/skip) comes from the live registry.So a missing tag makes the next plan recompute the same version, and the registry check then marks it
skip.The two runs
37033996914 (75179f3, plan 16:36:58Z) planned rust 0.4.50 / py 0.4.61 / npm 0.4.63, all
publish.pypi-publishuploaded 6dirsql-0.4.61files, 16:46:26–16:47:05Z.pypi-tag / tagrancommand: reconcile16:47:08–16:47:17Z and returned{"ok":true,"dryRun":false,"actions":[]}— nodirsql-py-v0.4.61tag was cut. That run'srelease-githubcreated onlydirsql-npm-v0.4.63anddirsql-rust-v0.4.50.37042741616 (12c0cec, plan 17:44:22Z) therefore still saw
dirsql-py-v0.4.60(469c8f6, 16:36Z) as newest, diffed from there, and computed 0.4.61 again. Verdicts: rust 0.4.51publish, npm 0.4.64publish, plugin 0.1.21publish,dirsql-py0.4.61skip.release / publishloggedpublish: dirsql-py@0.4.61 already published; skipping, thenrelease-githubcreateddirsql-npm-v0.4.64,dirsql-py-v0.4.61,dirsql-rust-v0.4.51at 17:54:31–36Z.git ls-remotenow resolvesdirsql-py-v0.4.61^{}to 12c0cec, while the files on PyPI are 75179f3's build. The matrix still built the py wheels despite theskip,pypi-publishdownloads*-sdist+*-wheel-*unfiltered, so it re-uploaded them →400 File already exists;pypi-tagskipped.37042756273 (9a5dc6d, plan 17:54:42Z) planned only
dirsql-plugin-embeddings0.1.21 — rust/npm/py had been tagged at 12c0cec three seconds earlier. Correction to the report above: it never planned py/npm/rust. Its upload ran 17:55:30–17:55:55Z and itsreconcilealso returnedactions: []; the newest plugin tag on origin is stilldirsql-plugin-embeddings-v0.1.9.The plan never chose a stale base because of a concurrent run. It chose the correct base for the tag state it was given; the 16:47Z
reconcileis what left no tag.Is #1228 on PyPI
Refuted — the change is absent.
uvx --exclude-newer-package "dirsql=2026-10-03T00:00:00Z" dirsql==0.4.61 query --helpprintsprint the result rows as JSON on stdout. Onmain, 12c0cec rewrote that toprint the result rows on stdout (a table on a terminal, a JSON array when piped). 0.4.61 is 75179f3's build and no laterdirsqlversion exists.Fix options
No
concurrency:block exists in.github/workflows/release.yml.concurrency: { group: release, cancel-in-progress: false }at workflow level. Removes the possibility of two plans racing a publish, at the cost of queueing every merge behind the previous ~12-minute release.reconcileread three seconds after upload. Fixes the gap that actually happened here; needs a change upstream inputitoutthere.pypi-publish's download to packages the plan markedpublish. Stops the400and the wasted upload, but leaves the missing-tag drift that caused it.release-githubfor a pypi package whose verdict isskip. Stops a tag from pointing at a commit whose code is not in the published artifact; does not by itself get docs: correct seven stale claims (output format, init table, root, no_ignore, docs host) #1228 onto PyPI.[-]Release: concurrent runs planned the same version; #1228's dirsql-py publish failed with 'file already exists'[/-][+]Release: post-upload reconcile cuts no tag, so the next run re-plans the same version, tags it at a later commit, and pypi-publish fails[/+]thekevinbot commentedon Oct 2, 2026
Follow-up from the next merge, #1206 (43e5987, run 37044943501, 18:13Z), which shows the same reconcile step misfiring in a second way.
pypi-tag / tagthis time did create a tag:{"package":"dirsql-py","version":"0.4.62","tag":"dirsql-py-v0.4.62","commit":"469c8f64","source":"sibling","created":true}. But 469c8f6 is the commitdirsql-py-v0.4.60already points at, not 43e5987, the commit the wheel was built from. So the py tags now read: v0.4.60 → 469c8f6, v0.4.61 → 12c0cec (wheel built from 75179f3), v0.4.62 → 469c8f6 (wheel built from 43e5987). None of the last two tags names the commit that produced its wheel.release-githubre-created thedirsql-plugin-embeddings-v0.1.21release and moved its tag from the 9a5dc6d-era commit to 43e5987.Net: the registry is the truth and is correct; the tags are derived twice (reconcile after upload, and release-github) and both derivations pick commits by rule rather than from the build. A fix that tags the exact commit the build job used, once, would remove both failure modes.