Reserve the DOI on the request, so nothing has to come back afterwards - #43
Merged
Merged
Conversation
A deposit sat unrecorded for a fortnight. Nothing was broken and nothing complained: the only thing that would have was the NEXT deposit, which refused and made the backlog somebody's problem at the worst moment. The weakness is not the manual steps -- a person merging the request to deposit is the right design -- it is that a step can fail to happen and leave no trace. scripts/outstanding.py names the half-finished states: identifiers written but never merged, a deposit asked for and never run, a note whose version has moved past the copy on its DOI, an archival note published without one, and a DOI with no record id. `pixi run outstanding` locally; exit 1 if anything is outstanding. A report nobody runs has the failure mode it was built to fix, so the `outstanding` workflow runs it weekly and keeps ONE issue: opened when something is outstanding, edited while it stays that way, closed when it clears, and silent otherwise. Detecting a note that has outrun its deposit needs a fact we were not recording. `archived_at` says when a copy was taken, and a git timestamp cannot tell a rewrite from a typo, so the deposit now stamps `archived_version` -- the note's own version at the moment it was deposited. `version != archived_version` is then exactly the state where the DOI serves older text than the site. Backfilled for the 43 deposited notes and added to the schema. Running it found a second defect, unrelated to the first and invisible until now: deposit-ready re-asks on every metadata change, because it checked deposit-queue.txt (what was MERGED) and not what was already open. One note had four open requests. Duplicate reminders are how a reminder becomes something you scroll past, so it now counts an open request as having asked. Underworld development team with AI support from Claude Code
The identifiers write-back was a gate with no decision in it. By the time that pull request opened the DOI was already public; merging it only told the repository what had happened, and the fortnight it once sat unmerged left the duplicate-mint guard blind the whole time. So the identifiers now go IN rather than come back. A note reaching main without a record gets a request that has already created the figshare draft, reserved the DOI and committed it into that note's metadata.yml -- both private, both reversible. Merging that request approves the deposit AND records it, in one act. The publish that follows finds everything it needs already on main, including archived_at, which the PDF prints and the package README states. That also removes the race. The approval used to live in one shared deposit-queue.txt appended to by every note at the same line, so two notes in flight conflicted and merging one broke the other -- measured, not feared, and the reason four duplicate requests piled up for one note. Per note metadata cannot collide. The trigger moves with it, from the queue file to articles/**/metadata.yml, narrowed to --approved: notes holding a reserved record that is not yet published. That set can only be entered by merging a request, so an ordinary metadata edit mints nothing. --all keeps its old meaning behind the manual deposit-all mode, where a person has chosen it. One thing still comes back: archive_published_at. It is bookkeeping -- the guard is satisfied without it -- and the outstanding report lists it until it is merged. The design test that covered the old mechanism is rewritten rather than dropped: it still asserts that nothing mints a DOI without somebody merging something, now by checking the push trigger uses --approved and never --all, and that the request workflow can reserve but never publish. Underworld development team with AI support from Claude Code
This was referenced Sep 2, 2026
# Conflicts: # .github/workflows/deposit-ready.yml # scripts/outstanding.py # tests/test_outstanding.py
Contributor
|
Preview: https://underworld-technical-notes.github.io/underworldcode.org-preview/4ace55d00b/ No article changed on this branch. Built from |
This was referenced Sep 2, 2026
lmoresi
added a commit
that referenced
this pull request
Sep 2, 2026
I dropped this guard when I rewrote the step in #43. It had been added in #42 for the queue design, and the rewrite replaced the whole step without carrying it across. `unreserved()` reads main, and a reserved record only reaches main when a request is MERGED. So between opening a request and merging it the note still looks unreserved, and the next run of the reminder creates a second figshare draft, reserves a second DOI, and opens a second request -- abandoning the first draft. That is the duplicate-mint hazard the whole flow exists to prevent. It is already wrong on the push trigger, where two metadata pushes before a merge are enough. It would be worse on a schedule, which is what prompted looking: a weekly run would mint a fresh DOI for every waiting request, every week. So an OPEN request counts as having asked, and the reminder subtracts those before it reserves anything. The test asserts the guard rather than its wording, since this is the second time the mechanism has been rewritten around it. Underworld development team with AI support from Claude Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #42 — review that first, or read this diff alone with
git diff docs/outstanding-report...docs/deposit-flow.The identifiers write-back was a gate with no decision in it. By the time that
pull request opened, the DOI was already public; merging it only told the
repository what had happened. The fortnight it sat unmerged left the
duplicate-mint guard blind the whole time.
The identifiers go in, rather than come back
A note reaching
mainwithout a record now gets a request that has alreadycreated the figshare draft, reserved the DOI, and committed it into that note's
own
metadata.yml. Both are private and reversible. Merging that requestapproves the deposit and records it, in one act — and
deposit-pdfattachesthe archival PDF, so the DOI on the title page is the one being asked about.
The publish that follows finds everything already on
main, includingarchived_at, which the PDF prints and the package README states. That is whythe reserve stamps it: stamped at publish, it would exist only on the runner.
The race goes with it
The approval used to live in one shared
deposit-queue.txt, appended to byevery note at the same line. Two notes in flight conflicted and merging one
broke the other — measured, not feared, and the reason four duplicate requests
piled up for one note. Per-note metadata cannot collide.
The trigger moves from the queue file to
articles/**/metadata.yml, narrowed to--approved(holds a reserved record, not yet published). That set can only beentered by merging a request, so an ordinary metadata edit — a keyword, a
corrected credit — mints nothing.
--allkeeps its old meaning behind themanual
deposit-allmode, where a person has chosen it.What still comes back
archive_published_at, and only that. It is bookkeeping: the record id and DOIare on
mainalready, so the guard is satisfied whether or not it lands, and itonly decides which notes a batch re-version offers. The report lists it until
merged. Automating it away needs a stored credential — the ruleset bypass that
would have avoided one is refused by GitHub, because the Actions app is not an
actor of this organisation.
Checked
168 tests. The design test covering the old mechanism is rewritten, not
dropped — it still asserts that nothing mints a DOI without somebody merging
something, now by checking the push trigger uses
--approvedand never--all,and that the request workflow can reserve but never publish. Two new tests cover
the per-note isolation and that the reserve stamps what the publish needs.
preview.ymlalready excludesdeposit/**by name rather than by path, whichbecomes load-bearing here: request branches now touch
articles/**, so a pathsfilter alone would have started building a preview site for every one.
Not covered
The reserve path has not been exercised against the live figshare API — the
tests assert the wiring, not a round trip. The first real request is the test,
and it is reversible at every step: the draft can be deleted, and nothing
resolves publicly until the merge.
Underworld development team with AI support from Claude Code