actions/refs-not-closing — the gather is gh api graphql, which Forgejo does not serve at all #199
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:docs
scope:guards
scope:labels
scope:release-flow
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#199
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Part of #197.
Blocked by #198, which lands the merge that first brings thisaction onto the tree.
actions/refs-not-closing/is new in upstream 0.6.0 (#218). It refuses a PRwhose
Refs #Npromise contradicts the closing-issue graph — a PR that saysit merely references an issue while actually being scheduled to close it.
Its entire gather is one GraphQL read,
actions/refs-not-closing/run.sh:17:Forgejo serves no GraphQL at all.
/api/graphqlon this instance returns404, and
lib/forge.sh's header records that a real forgejo-runner job evenarrives with
GITHUB_GRAPHQL_URLset to the empty string. There is noendpoint to translate this to; it has to be re-expressed, exactly as #188
re-expressed its own two GraphQL sites over REST plus a parser this repo owns.
#198 lands this action declaring
CEREMONY_FORGE_CLIENT=gh, soforge_preflightrefuses loudly here rather than producing a verdict from anunread graph. This issue removes the refusal by making the action portable.
Spec
1. The gather becomes REST, through the shim. Two reads, both already
available on both backends:
forge_pr_view/forge_api "repos/$REPO/pulls/$N", field.body;forge_api --paginate "repos/$REPO/pulls/$N/commits",field
.[].commit.message(verified present on this instance, HTTP 200).2. The closing set is computed by
lib/closes_references.sh, not by theforge.
closes_referencesis this repo's closing-keyword parser, added by#188 for precisely this reason. Run it over the body and over each commit
message, union the results.
The commit half is not optional.
refs-not-closing.sh's own header calls theGraphQL graph "authoritative because it includes both closing keywords and
sidebar links". On Forgejo the two halves of that claim resolve differently:
a keyword, not by a manual link. So nothing is lost there.
messages. A body-only parse would therefore miss a PR that closes an issue
from a commit subject, and would let exactly the contradiction this action
exists to catch through. Hence both reads.
3. The
hasNextPagerefusal is kept in spirit, relocated. Upstreamrefuses rather than issuing a partial verdict past 100 closing references.
The REST gather's equivalent failure is an incomplete paginated commit
read, so it must refuse on the same terms — reuse the forgejo backend's
existing
x-total-countcompleteness proof rather than inventing a secondone. A partial read is a refusal, never a pass.
4. The decision stays network-free.
refs-not-closing.shkeeps itscurrent signature —
<body-file> [<closing-issue-number> ...]— sotest/refs-not-closing.test.shkeeps driving the incident matrix offline andis not rewritten by this issue. Only
run.shchanges.5. The
CEREMONY_FORGE_CLIENT=ghdeclaration from #198 is removed in thesame PR, and the action's preflight then passes on both forges.
Tasks
run.sh's GraphQL gather with the two REST reads through theshim.
closes_referencesover the body and over every commit message.CEREMONY_FORGE_CLIENT=ghdeclaration #198 added..github/workflows/refs-guard.yml'sif: github.server_url == 'https://github.com'gate. #198 added it so aguard that could only refuse would not stand red on every PR on this
forge; once the gather is REST the job must run here and produce
verdicts again. Deleting the declaration without deleting the gate
leaves the action portable and never scheduled — a guard that passes
by never running, which is this repo's blind-sweep shape wearing a
different hat (@kimi-reviewer-andresmgsl, #198).
test/refs-not-closing.test.shwith the gather-level casesbelow; leave the existing offline matrix untouched.
changelog.d/fragment.Acceptance criteria
actions/refs-not-closing/run.shcontains noghinvocation and noGraphQL.
proven by one fixture driven through each.
the body, is detected — the case a body-only port would miss.
exit; it never returns a verdict.
refs-not-closing.shis unchanged, andtest/refs-not-closing.test.sh's existing cases pass untouched.test/run.shendsfailed 0; shellcheck- and actionlint-clean.Test plan
test/refs-not-closing.test.sh, unchangedand green — proof the decision layer was not disturbed.
Refs #7withCloses #7present only in a commit subject. Body-only parsing calls thisclean; the union must refuse it.
assert non-zero exit and a reason naming the read, and assert no verdict
was emitted.
Closes #NPR with noRefs, which theaction must leave untouched on both backends.
/api/graphql404s on this instance, so the reason this portexists is evidenced by the instance rather than by this issue's prose.
Dependencies
Part of #197.Blocked by #198.This issue's
Blocked bydeclarations parse to: {#198}That is the exact set this sweep gates on — what the machine read, never a
judgment about whether it is what you meant. The parse unions every clause it
finds, so a sentence like
no longer blocked by #9contributes#9likeany other; over-retaining is the deliberate direction of error, because a stale
blockedis a triage comment away and a falsereadysends a builder intowork that cannot merge. If this set names something you did not declare, or
omits something you did, edit the declaration — the next sweep echoes the
correction.
Comment only: nothing on this path writes a label. The marker carries the set
itself, so a parse unchanged since the last echo never re-posts.
Every issue named by
Blocked byis closed. The sweep is moving this issue toready.The Refs-linked PR merged with these acceptance criteria still unchecked:
run.sh's GraphQL gather with the two REST reads through thecloses_referencesover the body and over every commit message.CEREMONY_FORGE_CLIENT=ghdeclaration #198 added..github/workflows/refs-guard.yml'stest/refs-not-closing.test.shwith the gather-level caseschangelog.d/fragment.actions/refs-not-closing/run.shcontains noghinvocation and norefs-not-closing.shis unchanged, andtest/run.shendsfailed 0; shellcheck- and actionlint-clean.The merge releases the claim; no builder owes a draft. Triage owes completion in a follow-up comment that names the owner and wake condition.
Post-merge evidence — the guard's first live verdict on this forge
Run 513,
refs-guard.yml, eventpull_request, on !213's headed5ce89— the job ran and succeeded where every prior run on this forgewas
skipped. From its log:That is the ported gather executing end to end on a live PR of this
repository: two REST reads through the shim (body + paginated commits),
closes_referencesunioned over both, and a real pass verdict for a PRwhose body declares
Refs #205and whose commits close nothing — the correctanswer, produced rather than refused.
For the criteria the transition comment lists as unchecked: the build-side
items were all verified in !214's review (both-backend fixture parity, the
commit-only detection case, the completeness refusal, no
gh/GraphQL — codexand kimi at head). Run 513 supplies the live half: the action produces
verdicts on this forge.
One caveat named so it is not rediscovered: the verdict line's prose says
"GitHub's closing-issue graph" — wording from
refs-not-closing.sh, which#199's spec deliberately left unchanged. The gather behind it is the REST
port; only the sentence is stale. If triage wants the sentence updated it is a
one-line follow-up, not evidence of the old path running.
@andres — with this, every criterion on this issue has either review or live
evidence. Close is triage's; I am not closing it.