Skip to content

queue add: Follow-up of SN 2025wny I: Space-based Observations of the First Multiply-imaged Superluminous Supernova #18

queue add: Follow-up of SN 2025wny I: Space-based Observations of the First Multiply-imaged Superluminous Supernova

queue add: Follow-up of SN 2025wny I: Space-based Observations of the First Multiply-imaged Superluminous Supernova #18

Workflow file for this run

name: Queue actions
# The dashboard's mechanical buttons land here — five per-paper, one
# per-batch. Six of them:
#
# ✅ `queue-read` marks a reading-queue.md line `DONE <date> — <title>`
# (never deleted — the reading history)
# ➕ `queue-add` moves an arxiv-inbox.md line into its reading-queue
# section, unread
# ✖️ `queue-dismiss` drops an arxiv-inbox.md line — an un-acted suggestion
# is not reading history, and git history holds it
# ➕ `interests-add` the same move out of arxiv-interests.md, routed by
# the line's own `[Topic]` rather than to one section
# ✖️ `interests-dismiss` the same drop, out of arxiv-interests.md
# 🧹 `interests-clear` drops a whole day's batch from arxiv-interests.md,
# revealing the next day — the interests list is a
# backlog to walk, not a seven-day timer, so this is
# the button that moves it along
#
# Each commits the file it touched, re-renders the dashboard, and closes the
# issue. 📥 `queue-intake` and 📑 `queue-cite` are NOT handled here — they stay
# open as filing work items and are driven by queue_filing.yml.
#
# Only owner/member/collaborator-authored issues act: this is a public repo,
# and a stranger's issue must never mutate the queue (they cannot apply the
# label either, but the gate does not rely on that).
on:
issues:
types: [opened]
# contents: write → the queue commit; issues: write → comment + close;
# actions: write → re-dispatch the Dashboard workflow (token pushes do not
# retrigger `on: push` workflows).
permissions:
contents: write
issues: write
actions: write
concurrency:
group: queue-actions
cancel-in-progress: false
jobs:
queue-action:
# One job, six labels. The gate is unchanged: this is a public repo, and
# a stranger's issue must never mutate the queue, the inbox or the
# interests list.
if: >-
(contains(github.event.issue.labels.*.name, 'queue-read') ||
contains(github.event.issue.labels.*.name, 'queue-add') ||
contains(github.event.issue.labels.*.name, 'queue-dismiss') ||
contains(github.event.issue.labels.*.name, 'interests-add') ||
contains(github.event.issue.labels.*.name, 'interests-dismiss') ||
contains(github.event.issue.labels.*.name, 'interests-clear')) &&
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'),
github.event.issue.author_association)
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
ISSUE: ${{ github.event.issue.number }}
LABELS: ${{ join(github.event.issue.labels.*.name, ',') }}
steps:
- uses: actions/checkout@v4
- name: Apply the action
id: mark
env:
# env, never inline interpolation — the body is untrusted text.
ISSUE_BODY: ${{ github.event.issue.body }}
run: |
printf '%s' "$ISSUE_BODY" > /tmp/issue_body.txt
# The label picks the script; the push retry below re-runs the same
# command on a moved main, so the mapping is stated once and the
# command travels as a step output.
case ",$LABELS," in
*,queue-add,*)
action=add
cmd="scripts/inbox_actions.py add" ;;
*,queue-dismiss,*)
action=dismiss
cmd="scripts/inbox_actions.py dismiss" ;;
*,interests-add,*)
action=interests-add
cmd="scripts/interests_actions.py add" ;;
*,interests-dismiss,*)
action=interests-dismiss
cmd="scripts/interests_actions.py dismiss" ;;
*,interests-clear,*)
action=interests-clear
cmd="scripts/interests_actions.py clear" ;;
*)
action=read
cmd="scripts/queue_mark_done.py --queue reading-queue.md" ;;
esac
status=$(python $cmd --body-file /tmp/issue_body.txt) || true
# A status the scripts do not define means they died before printing;
# normalise so the close step reports rather than mis-closing.
case "$status" in
marked|already-done|added|already-there|dismissed|already-gone) ;;
already-clear|cleared:*) ;;
*) status="${status:-error}" ;;
esac
echo "action=${action}" >> "$GITHUB_OUTPUT"
echo "cmd=${cmd}" >> "$GITHUB_OUTPUT"
echo "status=${status}" >> "$GITHUB_OUTPUT"
echo "queue action ${action}: ${status}"
# Did anything actually change on disk? Cheaper and more honest than
# inferring it from the status token.
if [ -n "$(git status --porcelain -- reading-queue.md arxiv-inbox.md arxiv-interests.md)" ]; then
echo "changed=yes" >> "$GITHUB_OUTPUT"
else
echo "changed=no" >> "$GITHUB_OUTPUT"
fi
- name: Commit + push (explicit paths only)
id: push
if: steps.mark.outputs.changed == 'yes'
env:
ACTION: ${{ steps.mark.outputs.action }}
CMD: ${{ steps.mark.outputs.cmd }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
case "$ACTION" in
add) msg="queue: add inbox paper to the reading queue" ;;
dismiss) msg="queue: dismiss inbox paper" ;;
interests-add) msg="interests: add paper to the reading queue" ;;
interests-dismiss) msg="interests: dismiss paper" ;;
interests-clear) msg="interests: clear the day's batch" ;;
*) msg="queue: mark paper read" ;;
esac
git add reading-queue.md arxiv-inbox.md arxiv-interests.md
git commit -m "${msg} (#${ISSUE}) [skip ci]"
# A rejected push means a sibling run landed a commit first.
# Rebasing the stale commit conflicts on adjacent queue lines and
# used to discard the action entirely (#69) — rebuild instead:
# reset to the moved tip and re-run the action script, which is
# idempotent (already-there / already-gone / already-done).
for attempt in 1 2 3; do
if git push; then exit 0; fi
[ "$attempt" = 3 ] && break
git fetch origin main
git reset --hard origin/main
retry_status=$(python $CMD --body-file /tmp/issue_body.txt) || true
# A clean tree only means "done" if the re-run actually succeeded:
# an error status (no-section, a crash) also leaves no diff, and
# exiting 0 on it would close the issue on the first run's stale
# status — the same silent-loss class this retry exists to fix.
case "$retry_status" in
marked|already-done|added|already-there|dismissed|already-gone|already-clear|cleared:*) ;;
*)
echo "::error::re-apply on the moved main returned '${retry_status:-error}'"
exit 1 ;;
esac
# The close step reports what actually landed, not attempt 1's
# status (last write wins).
echo "status=${retry_status}" >> "$GITHUB_OUTPUT"
if [ -z "$(git status --porcelain -- reading-queue.md arxiv-inbox.md arxiv-interests.md)" ]; then
echo "change already on main after the concurrent commit — nothing left to push"
exit 0
fi
git add reading-queue.md arxiv-inbox.md arxiv-interests.md
git commit -m "${msg} (#${ISSUE}) [skip ci]"
done
echo "::error::could not push the queue commit after 3 attempts"
exit 1
- name: Refresh the dashboard
if: steps.mark.outputs.changed == 'yes'
run: gh workflow run knowledge_board.yml --ref main
- name: Close or report on the issue
env:
STATUS: ${{ steps.push.outputs.status || steps.mark.outputs.status }}
ACTION: ${{ steps.mark.outputs.action }}
run: |
# Two suggestion tiers share these statuses, so the comment names
# the one the human actually tapped.
case "$ACTION" in
interests-*) tier="arXiv interests list" ;;
*) tier="arXiv inbox" ;;
esac
case "$STATUS" in
marked)
gh issue close "$ISSUE" --comment \
"Marked read in reading-queue.md — the line stays, DONE-prefixed (the reading history). Dashboard refresh dispatched." ;;
already-done)
gh issue close "$ISSUE" --comment \
"Already marked read in reading-queue.md — nothing to change." ;;
added)
gh issue close "$ISSUE" --comment \
"Added to the reading queue and cleared from the ${tier}. Dashboard refresh dispatched." ;;
already-there)
gh issue close "$ISSUE" --comment \
"Already in the reading queue — cleared the ${tier} line and left the queue alone." ;;
dismissed)
gh issue close "$ISSUE" --comment \
"Dropped from the ${tier}. Git history still holds the line. Dashboard refresh dispatched." ;;
already-gone)
gh issue close "$ISSUE" --comment \
"Not in the arXiv inbox or interests list any more — nothing to change (it may already have lapsed or been cleared)." ;;
cleared:*)
gh issue close "$ISSUE" --comment \
"Cleared ${STATUS#cleared:} paper(s) from the arXiv interests list — the next day's batch is now on the dashboard. Git history still holds the batch. Dashboard refresh dispatched." ;;
already-clear)
gh issue close "$ISSUE" --comment \
"That interests batch is already clear — nothing to change." ;;
no-section)
gh issue comment "$ISSUE" --body \
"Could not process this queue action — neither the named section nor the \`## Interests\` fallback exists in reading-queue.md. Handle manually; leaving the issue open." ;;
*)
gh issue comment "$ISSUE" --body \
"Could not process this queue action (status: ${STATUS:-error}) — the paper's line was not found. Handle manually; leaving the issue open." ;;
esac
# The close step above is implicitly `if: success()`, so a died push
# used to leave the issue open and completely silent (#69 read as
# "never swept up" rather than "failed"). Same shape as
# queue_filing.yml's failure report.
- name: Report failure on the issue
if: failure()
run: |
gh issue comment "$ISSUE" --body \
"Queue action failed — see the workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}. The issue stays open; handle manually if needed." || true