Skip to content

Say what is half-finished, without being asked - #42

Merged
lmoresi merged 4 commits into
mainfrom
docs/outstanding-report
Sep 2, 2026
Merged

lmoresi merged 4 commits into
mainfrom
docs/outstanding-report

Conversation

@lmoresi

@lmoresi lmoresi commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

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 possible moment.

The weakness is not the manual steps. A person merging the request to deposit is
the right design, and deposit-ready already catches the opposite case — a note
published without a DOI. The weakness is that a step can fail to happen and
leave no trace.

What this adds

scripts/outstanding.py names the half-finished states:

deposited, not recorded an identifiers PR is still open, so the duplicate-mint guard is blind
asked for, not deposited a request is open on the queue
note ahead of its copy version has moved past archived_version
never deposited archival, published, no DOI
guard blind archive_doi with no repository_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 maintains one issue: opened when
something is outstanding, edited while it stays that way, closed when it clears,
silent otherwise. It never opens a second issue.

A fact we were not recording

Detecting "this note has outrun its deposit" needed something new. archived_at
says when a copy was taken, and a git timestamp cannot tell a rewrite from a
typo — I tried, and it flagged 4 notes of which 2 were the identifier
write-backs themselves. 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, with no
false positives and no archaeology. Backfilled for the 43 deposited notes, added
to the schema, and pixi run validate is clean.

It found a second defect immediately

Unrelated to the first, and invisible until something looked: deposit-ready
re-asks on every metadata change, because it checked deposit-queue.txt — what
has been merged — and not what was already open. One note has four open
deposit requests
(#33, #37, #39, #41). Duplicate reminders are how a reminder
becomes something you scroll past, which is the same disease as the original
bug. It now counts an open request as having asked.

What this does not do

It cannot make the identifiers PR merge itself. Two independent things block
that: repo auto-merge is disabled (allow_auto_merge: false), and a PR opened
by the bot with GITHUB_TOKEN has its required checks sit in action_required
until a human approves them — which is why #25 could not have merged on its own
however long anyone waited. Removing that state entirely needs a repo settings
change and a token, and both are yours to decide; this makes the state loud
instead.

165 tests pass, including the control that matters most here: a check that
cannot answer reports "not checked" rather than nothing, because silence being
mistaken for good news is the whole bug.

Underworld development team with AI support from Claude Code

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 preview build died on this branch with "AMBIGUOUS: /underworld-2/
could belong to joss-publication-underworld-2 or underworld-2-10", an
ambiguity that does not exist: those are two different notes with two
different pages.

fix_slugs restores URLs MyST truncated at 50 characters, by matching a
slug against the pages that were built. A preview builds only the notes a
branch changes, so in a preview most slugs have no built page -- and this
branch's metadata backfill touched 43 articles, which made the build large
enough for three related slugs to be in play at once. Matched loosely,
`underworld-2-10` starts with `underworld-2` and
`joss-publication-underworld-2` ends with it, so both claimed the same
page and the second one to arrive raised the ambiguity.

Neither could have been truncated: both are shorter than the cap. So the
fix is to say that -- a slug at or under the cap is skipped, because there
is nothing to restore, and only a longer one is matched at all. The cap
is now a named constant rather than a literal in two places.

Underworld development team with AI support from Claude Code
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Preview

Or the whole site.

Built from f405673. Shows notes at draft and review, which the published site withholds. Not indexed, no comments, and not the citable version. Confirmed serving this commit before this was posted.

The preview died on this branch reporting an ambiguity that does not
exist: "/underworld-2/ could belong to joss-publication-underworld-2 or
underworld-2-10". Those are three separate notes.

fix_slugs restores URLs MyST mangled, by matching a slug against the
pages that were built. A preview builds only the notes a branch changes,
so most slugs have no built page at all -- and this branch's metadata
backfill touched 43 articles, which put three related slugs in play at
once. Matched loosely, `underworld-2-10` claimed `underworld-2` merely by
starting with it and `joss-publication-underworld-2` by ending with it.

My first attempt at this was wrong, and the build caught it: I assumed
the only mangling was truncation at 50 characters and skipped every
shorter slug, which broke the fifteen that are mangled a different way.
MyST also drops a LEADING NUMBER, so `2-11-scaling` is served as
/scaling/ and `30-years-of-citcom-...` as /years-of-citcom-.../.

So both manglings are now matched exactly -- truncation only when the
slug is longer than the cap, and a leading strip only when what was
removed is a number -- and anything looser is refused. Checked against a
real full build: 43 slugs already correct, 15 renamed, none unmatched, no
ambiguity, and test-dois reports 50 resolved and 0 broken.

Underworld development team with AI support from Claude Code
@lmoresi
lmoresi merged commit e702a84 into main Sep 2, 2026
3 checks passed
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant