• 0.6.3 8f0ef79620

    0.6.3
    All checks were successful
    CI / release-exercise (push) Has been skipped
    CI / self-guards (push) Successful in 5s
    CI / action-exercise (push) Successful in 4s
    CI / docs-sync-exercise (push) Successful in 4s
    release / release (push) Successful in 10s
    CI / test (push) Successful in 4m15s
    Stable

    andres released this 2026-08-26 20:20:18 +00:00 | 25 commits to main since this release

    Changed

    • The shipped 0.6.2 changelog section now carries #238's entry, which its release PR's merge base could not see; the published 0.6.2 release body is left as tagged, so tree and publication differ by that one line (#238, #231).
    • Replace the unavailable intake rule with a proposal form that triage converts into work or refuses with reasons (#247).

    Fixed

    • The needs-triage label now directs untriaged issues toward work normalization or a reasoned refusal instead of an unavailable discussion path (#265).
    • Release checks now refuse a target-head fragment that the candidate did not consume, preventing late merges from misattributing shipped changes (#253).
    • Kept drill doctrine's release-path instructions aligned with the executable manifest by removing its duplicate path list (#251).
    • Preserve Forgejo workflow names in status rollups so the label reconciler excludes only its own checks. (#243).
    • Fork-headed label runs stay green without attempting forbidden writes, while same-repository heads keep instant scope and reconciliation wakes (#241).
    • Read Forgejo timelines to exhaustion so busy issues retain their newest label events despite dishonest total-count headers (#240).
    • Refs-based issue-flow transitions now bind each declaration to its immediately following reference token, so later issue prose cannot release or preserve unrelated claims (#234).
    Downloads
  • 0.6.2 5a8fce8375

    0.6.2
    Some checks failed
    release / release (push) Successful in 25s
    CI / test (push) Successful in 3m45s
    CI / release-exercise (push) Has been skipped
    CI / self-guards (push) Failing after 20s
    CI / action-exercise (push) Successful in 20s
    CI / docs-sync-exercise (push) Successful in 20s
    Stable

    andres released this 2026-08-24 16:10:40 +00:00 | 70 commits to main since this release

    Changed

    • upstream-0.6.1 was ported in #229: CONTRIBUTING routes the vendored set
      through docs/VENDORED.txt — upstream#316 and upstream#311; BUILDER.md
      scopes the green-check precondition to its act, upstream#330; RELEASES.md
      adds the post-merge gate-member split, upstream#329 (#246).

    • upstream-0.6.2 was ported in #229: BUILDER.md orders parked claims so an
      operator-owned remainder parks the claim, never the handoff — upstream#336
      (#246).

    • upstream-0.6.3 was ported in #230: release windows read ## Members with
      no gate fallback, carriers leave their own gates, stale board flags stay
      silent — upstream#343 and upstream#327 — and the parser accepts CommonMark
      rows (#246).

    • Upstream logic was ported onto this forge's Forgejo-adapted issue-flow
      reconciler, test, and CONTRIBUTING; those files were never overwritten with
      upstream bytes (#246).

    • Upstream's drill-record fixes and the upstream 0.7.00.7.4 line are
      deferred to the next sync campaign (#246).

    • No upstream ancestry moves in this release: .upstream-ref remains
      8c3a4d1 from upstream 0.6.0, merged by #198; upstream-0.6.3 is the
      content baseline, not a merge-base (#246).

    • Release windows now read membership from a dedicated ## Members record, with CommonMark-bounded rows and no fallback to predecessor gates (#230).

    • Forge consumers now receive the upstream 0.6.1 and 0.6.2 doctrine for vendored-set routing, review-round signalling, operator-owned remainders, producer liveness, and post-merge release edges (#229).

    • docs/RUNNER-PROBES.md records the delivered 0.6.1 consumer exercise in the standing Forgejo runner venue (#217).

    Fixed

    • Forgejo drafts and fast-forward conflict-check windows no longer surface as merge conflicts when the API has not distinguished one (#236).
    • Forgejo review requests no longer count as verdicts, while its blocking and comment states now grade like their GitHub equivalents (#235).
    Downloads
  • 0.6.1 338cf5f754

    0.6.1
    All checks were successful
    CI / test (push) Successful in 3m18s
    CI / release-exercise (push) Has been skipped
    CI / self-guards (push) Successful in 10s
    CI / action-exercise (push) Successful in 9s
    CI / docs-sync-exercise (push) Successful in 9s
    release / release (push) Successful in 14s
    Stable

    andres released this 2026-08-09 19:46:56 +00:00 | 109 commits to main since this release

    Added

    • test/labels-bootstrap.test.sh pins the bridge at every hop: the declared
      boundary, both gate sites as the identity, no expression reading
      github.event.inputs, and the caller and stub pass-throughs
      byte-exact (#215).

    • The same test drives the four value paths — schedule-empty, no, yes,
      invalid — through the shipped expressions into the action's real
      validator (#215).

    • The taxonomy bootstrap keys on the BOOTSTRAP input, never the event name.
      It tested GITHUB_EVENT_NAME = workflow_dispatch — correct while an
      operator's manual dispatch was the only dispatch there was, inert-by-
      construction from #209 on, when every machine wake became a dispatch
      event (#215).

    • The venue drill caught that: with the bridge delivering no perfectly,
      drill runs 16/17 still bootstrapped, because the script never read the
      input the whole chain existed to deliver (#215).

    • test/labels-reconcile.test.sh pins the regression pair exactly: a
      workflow_dispatch event with BOOTSTRAP=no (or unset) creates and
      deletes nothing; only BOOTSTRAP=yes bootstraps (#215).

    • A gather-level case drives the real board read against a Forgejo-shaped
      fixture — every entry carrying the key. The existing discriminator cases
      assert jq expressions in isolation and passed throughout this regression
      (#210).

    • A source pin forbids has("pull_request") on this surface, because the rule
      was stated in a comment and violated forty lines below it. It strips comments,
      so the #188 warning that explains the trap is allowed to stay (#210).

    • All three sites are covered behaviourally, not only by the pin: the board
      gather, the release-body gather through an observable window flag, and the
      per-issue payload check (#210).

    • test/forge-backends.test.sh pins each backend's path and field, because
      a stubbed forge_api cannot catch a wrong path — which is how this shipped
      and why a live sweep was what found it (#209).

    • test/labels-dispatch.test.sh extracts the shipped step and executes it
      against a recording stub, asserting the method, endpoint, ref and
      inputs.bootstrap actually sent (#205).

    • That test also drives the failure path: any non-204 still fails the job, so
      the misconfiguration alarm the trigger exists to be cannot decay into a
      warning (#205).

    • An unset GITHUB_API_URL refuses before any request instead of defaulting
      to api.github.com. Guessing sent this forge's dispatch to GitHub and
      reported success — the "Never 'probably github'" rule, and the same
      unset-environment refusal #201 established for docs-sync (#205).

    • A dispatch that never reaches the forge names the failure. Letting set -e
      carry curl's exit code out did fail the job, but with a bare status and no
      sentence (#205).

    • docs/CONSUMERS.md and both caller comments describe the REST dispatch, and
      the manual bootstrap command carries a forge-neutral form beside the gh
      one — a cross-forge runbook that directs this forge to a missing binary is
      wrong even where the surrounding prose is right (#205).

    • docs/RUNNER-PROBES.md documents the standing runner-probe venue,
      heavy-duty/ceremony-runner-probe — the place runner-only facts are measured
      on demand, ruled as option A by the operator (#202).

    • drills/README.md cross-links it beside the disposal rule, so the exception
      is visible where the dangerous habit lives (#202).

    • The runbook states that the drill disposal rule does not apply to it.
      Archiving it defeats its purpose, and that is exactly how the three existing
      drill repos each became unavailable (#202).

    • It records that a probe must run as an Actions job under the workflow token:
      the same call answers 500 there and 204 under a PAT, so a probe run any other
      way produces a confident wrong answer (#202).

    • Creating the repo is recorded as the operator's step, measured rather than
      assumed: a fleet identity gets 403 on org repo creation and 201 in its own
      namespace (#202).

    • It carries an executable two-layer arming procedure: an immutable candidate
      code SHA and an armed workflow commit on top of it. A single layer is
      self-referential — rewriting a workflow makes a new commit, and a commit
      cannot contain its own object ID (#202).

    • Callers are pinned by layer: composite actions to the candidate code SHA,
      reusable workflows to the armed SHA, which is the only revision whose inner
      checkout points at the fork (#202).

    • The arming gate asserts what each carrier IS, not only that the old literal
      is gone: every repository: equals the fork, every CEREMONY_SELF_REF value
      equal the candidate code SHA, and callers match the layer they belong to
      (#202).

    • It enumerates the carriers from the tree rather than encoding a count, and
      distinguishes ceremony's internal self-checkouts from the consumer checkouts
      that must stay ${{ github.repository }} (#202).

    • Both published snippets are ShellCheck-clean when extracted and linted
      directly, not merely as part of the repository sweep (#202).

    • The checker validates the MANIFEST against the target it was given, so a
      manifest that describes a wrong arming consistently — wrong fork, or the
      armed SHA where the candidate belongs — refuses instead of matching a tree
      rewritten to the same wrong value (#202).

    • The manifest is generated from the PRE-arming tree, which is the only order
      that enumerates the carriers that must change (#202).

    • Both published snippets were driven against a constructed candidate/probe
      pair: deletion, both role swaps, wrong owner, wrong
      SHA, wrong path, a deleted caller class and an extra carrier all refuse, and
      the armed control passes (#202).

    • The manifest records complete caller coordinates, so a path swapped under the
      right owner and SHA is caught (#202).

    • Generator and checker share one domain — ceremony callers — so a third-party
      actions/checkout is neither manifested nor reported as unrecognised (#202).

    • Probe results are written to an issue in the probe repo and carried to the
      ceremony issue by a human, so the probe holds no path that can write to the
      live board (#202).

    • test/docs-sync.test.sh drives the fetch path, which had no coverage at all:
      every existing row passes --source, which overrides the fetch entirely
      (#201).

    • A stubbed curl records the requested URL and serves a tarball, so which
      forge a pin resolves against is a tested decision rather than plumbing (#201).

    • docs/UPSTREAM-SYNC.md — the recurring upstream sync as a runbook: the
      standing resolutions, which side wins each and the issue that decided it
      (#200).

    • It names the step the 0.6.0 sync nearly shipped without: auditing what the
      merge brought in that did not conflict. git merge asks no question
      about a function upstream added to a file this tree owns (#200).

    • It records that the same mechanic applies to state, not just to call sites: a
      resolved region can silently remove a producer whose consumers auto-merged,
      and every one of those consumers degrades to empty rather than erroring
      (#200).

    • It says to verify with the runner's tooling, because "green locally" was
      wrong three times in one sync — untracked files, a pinned linter, and a
      pinned jq whose empty-input exit code differs (#200).

    • It says every branch open across a sync is stale afterwards — Forgejo never
      re-tests an open PR when main moves, so a prior approval is evidence about a
      tree that no longer exists (#200).

    • It says to audit post-merge runs by executed steps rather than colour, and to
      inventory what the sync changed about workflow triggers and jobs first (#200).

    • .upstream-ref records the upstream commit this tree carries, in
      machine-readable form beside the CHANGELOG's prose (#200).

    • test/upstream-delta.test.sh fails the PR that scatters a forge decision
      into a file the inventory does not name. Discovery is derived from the tree,
      so a composite action.yml or a .yaml workflow is seen without anyone
      remembering to add a glob (#200).

    • Discovery is git's, not the filesystem's: ls-files, so the tarballs ci.yml
      extracts into the checkout and any developer cache are not parsed as source
      (#200).

    • It refuses when the recorded commit is missing, absent from the object store,
      or not an ancestor — three distinct refusals, none of them a skip. ci.yml
      fetches that exact object so the test reads local evidence without CI
      omitting it (#200).

    • Its mutation cases drive the real check against a constructed tree, so
      replacing the guard with return 0 reds five of them (#200).

    • docs/CONSUMERS.md states that two ceremonies answer to the same version
      number, and how a consumer says which one it pinned (#200).

    • lib/issue_references.sh — the LOCAL/CROSS classifier, moved out of
      actions/issueflow-reconcile's executable so a second caller can use it
      without sourcing a reconciler, which would run one (#199, #61).

    • test/refs-not-closing.test.sh drives the action's boundary on both
      backends with stubs at the transport, proving one fixture yields the same
      verdict on each — including a closing keyword that appears only in a commit
      message (#199).

    • This tree carries upstream ceremony through 8c3a4d1 (upstream 0.6.0):
      lib/attention.sh, lib/read.sh, actions/refs-not-closing, the guarded
      reads, and the ruling and window rules (#198).

    • test/no-runtime-gh.test.sh — the forge-portability guard: no runtime gh
      outside lib/forge-github.sh unless the file declares
      CEREMONY_FORGE_CLIENT=gh (#198).

    • CHANGELOG.md names the upstream commit this tree carries, so a drill
      record can say which 0.6.0 it exercised (#197, #198).

    • test/labels.test.sh holds the conf's roster and CONTRIBUTING.md's roster
      table to the same set, in both directions, so a roster edit that touches one
      file and not the other goes red instead of drifting quietly (#195).

    • test/forge-backends.test.sh pins the replacement contract: preserve
      unrelated labels across a combined add+remove, an absent removal as a
      successful no-op, the empty set as a full clear, and forge_labels_add
      still POST-only, per ceremony#128 (#192).

    • test/labels-reconcile.test.sh drives a failing write through main() — the
      swallow was in the loop, where a fixture-level probe cannot reach (#192).

    Changed

    • The review panel restores kimi-reviewer-andresmgsl alongside GLM. The
      four-identity panel now requires three cross-vendor approvals after the PR
      author recuses (#224).

    • The review panel names glm-reviewer-andresmgsl in place of
      kimi-reviewer-andresmgsl while that identity is unavailable. The panel
      stays three, so a PR still requires two cross-vendor approvals (#222).

    • test/labels.test.sh's table-side roster mutation names an identity the
      table actually carries. Naming a departed one would mutate nothing and the
      case would pass while proving nothing — #195's rot class one layer down
      (#222).

    • This forge's release line runs 0.4.1 → 0.6.1: versions 0.5.0 and 0.6.0
      arrived here by merge from the read-only upstream and were never released
      on this forge (#220).

    • The ## 0.6.0 section this changelog carries is upstream's — its entries
      describe upstream's work under upstream's issue numbers. The forge port's
      own work ships first in 0.6.1 (#220).

    • docs/RUNNER-PROBES.md records the venue's first delivered drills — the
      #192 asymmetry re-observed on demand under the workflow token, the dispatch
      route's 204 under both identities, and #215's boundary finding — each with
      the probe-issue URL it is recorded in (#202).

    • Two venue lessons join the runbook where the next probe author will look:
      findings must be written to issues because the venue's log route 404s for
      non-admin reads, and report content sent to the forge must never contain a
      credential expression or value (#202).

    Fixed

    • The sweep's bootstrap value crosses the workflow_call boundary as a
      declared input, explicitly passed by the caller — the one channel measured
      to work; the called workflow did not see the caller's event inputs as an
      implicit substitute on this instance (#215).

    • Before the bridge, github.event.inputs was empty inside the called
      workflow, so every dispatch-woken sweep bootstrapped: ~20 label upserts on
      each board event (#215).

    • The caller maps an empty top-level value to no explicitly, so a
      cron-woken sweep can never bootstrap; the declared input also defaults to
      no, so a consumer that passes nothing gets the safe path (#215).

    • The gate feeds the declared input to labels-reconcile unchanged, so an
      invalid value meets the action's own yes|no refusal instead of being
      silently coerced (#215).

    • docs/CONSUMERS.md's published sweep stub carries the same pass-through —
      without it every consumer inherits the defect ceremony fixed for
      itself (#215).

    • issueflow-reconcile sees this forge's issues again. The board gather used
      has("pull_request"), and every Forgejo entry carries that key — so it
      selected zero rows on every sweep while printing reconciled. (#210).

    • Three sites take .pull_request == null, the discriminator the file's own
      comment already specified and that one of its four call sites already used
      (#210).

    • post-merge transitions can fire again: they could not, because the sweep
      saw no issues to transition (#210).

    • blocker:unrequested is judged on this forge again. The head-commit date was
      read from repos/{o}/{r}/commits/{sha}, which Forgejo answers 404 — so
      every sweep degraded and left the blocker unjudged (#209).

    • forge_commit_at is a verb on both backends: GitHub serves a single commit at
      the bare path with the date nested, Forgejo at git/commits/{sha} with it
      under .created. The caller asks for one timestamp and knows neither shape
      (#209).

    • .github/workflows/labels.yml wakes the sweep over REST instead of
      gh workflow run, so a board event reconciles within seconds on any forge
      rather than waiting up to an hour for the scheduled sweep (#205).

    • The workflow-dispatch endpoint has the same shape on both forges, so that
      step no longer decides one: the CEREMONY_FORGE_CLIENT=gh declaration and
      both inline refusals are gone rather than ported (#205).

    • The dispatch supplies its ref explicitly, because REST has no default
      branch where gh workflow run had one, and refuses without it (#205).

    • It takes that ref from the repository, never from GITHUB_REF_NAME — on a
      pull_request_target run that is <n>/merge, which is not a branch (#205).

    • A failed dispatch names the endpoint, the ref and the status, and says that
      an empty 500 body from Forgejo means the workflow name or the ref did not
      resolve — a bare status sends the reader after a server fault that is not
      there (#205).

    • actions/docs-sync fetches the doctrine mirror from the forge named by
      GITHUB_SERVER_URL instead of a hard-coded github.com (#201).

    • The same pin ref names a different tree on each forge, so a consumer's mirror
      was verified against a tree it never pinned — and with HTTP 200, so --check
      reported drift that could not be fixed (#201).

    • A fetch that cannot name its forge now refuses instead of guessing: no
      GITHUB_SERVER_URL and no --source exits naming the variable, having
      reached for no network (#201).

    • A failed fetch names the URL it actually tried, and asks whether the ref
      exists on that forge rather than in the abstract (#201).

    • actions/refs-not-closing gathers over REST through the forge shim instead
      of one GraphQL query, so it produces a real verdict on Forgejo — which
      serves no GraphQL surface at all — rather than refusing (#199).

    • The closing set is parsed by lib/closes_references.sh over the PR body
      and every commit message, unioned. Forgejo honours closing keywords in
      commit messages, so a body-only port would miss a PR that closes an issue
      from a commit subject (#199).

    • The hasNextPage refusal is carried onto the paginated commit read: an
      incomplete gather refuses instead of returning a partial verdict, reusing
      the backend's x-total-count completeness proof (#199).

    • A failed read never reaches the parser. An unread body parses to an empty
      closing set, which is a passing verdict the action never earned (#199).

    • .github/workflows/refs-guard.yml no longer gates the job on the forge.
      A portable action behind a github-only gate is a guard that passes by never
      running (#199, #198).

    • The CEREMONY_FORGE_CLIENT=gh declaration is gone, and the contract test
      asserts its absence: an opt-out with no gh behind it is a standing
      permission slip (#199).

    • Eight runtime gh call sites arrived with the merge outside every conflict
      hunk, in functions upstream added to files this tree already owned. Seven
      are ported onto the shim; the eighth is named with its reason (#198).

    • The open-PR gather reads Refs, not only closing keywords. Reading one side
      for closing links and the other for Refs is what released a live claim in
      crew#321, and this tree carried that shape (#198).

    • The merged record gains merged_at, so post_merge_pr_for_issue answers
      the PR that merged last rather than the highest-numbered one. Without the
      column every sort key ties and the old order returns silently (#198).

    • The open gather feeds open_pr_issues one record per physical body line. A
      whole decoded body as one record loses every declaration including the
      first, and reclaims a claim a live PR was holding (#198).

    • The post-merge nudge links the issue on the forge in play rather than a
      hard-coded github.com (#198).

    • actions/refs-not-closing reports and skips on a forge it cannot speak,
      naming the client and #199, instead of standing red on every PR. It reaches
      the forge zero times, so no verdict is produced either way (#198).

    • .github/workflows/labels.yml's sweep dispatch declares the client it
      speaks and decides the FORGE before the binary, so a Forgejo runner that
      happens to ship gh cannot dispatch against a forge that cannot serve it.
      #205 ports it to REST (#198).

    • actions/refs-not-closing fails closed on a forge it cannot speak, and
      .github/workflows/refs-guard.yml carries the scheduling decision — the
      action never reports a success it did not earn (#198).

    • issue_payload_valid refuses an empty payload on jq 1.6 as well as 1.7.
      jq -e exits 4 on empty input under 1.7 and 0 under 1.6, and this
      instance's runner carries 1.6 — so the guard #247 D3 added to refuse an
      unreadable read was accepting one here (#198).

    • The post-merge nudge strips a trailing slash from the server URL, so a forge
      URL carrying one does not render //owner/repo (#198).

    • .github/scripts/release-path.sh names lib/forge.sh: #191 put the shim on
      the release doors' executable path here, so a doors-unchanged record that
      omitted it was measuring the wrong set (#198).

    • .github/labels.conf names identities that exist on the forge this repo
      lives on. All five it named before were absent, so panel= could never
      converge a review round and triage-actors= made every issue a stray mint
      nobody could normalize (#195).

    • CONTRIBUTING.md's roster table matches the conf: three identities, the
      human row is andres, and the approval count states what panel-minus-author
      actually resolves to on this roster rather than a stale three (#195).

    • Label removal on Forgejo is a full-set PUT, not a per-label DELETE. The
      workflow token gets HTTP 500 on every DELETE .../labels/{id} on this
      instance, so the state machine could only ever ADD labels (#192).

    • Every state:* transition that needs the previous state cleared, and every
      blocker:* that should lift, can now actually clear. They were inert (#192).

    • A label edit that fails is fatal to labels-reconcile, matching
      issueflow-reconcile. One cause had two contradictory policies (#192).

    • A failed write reaches the sweep's exit code: per-PR tolerance is kept for
      READS, but a sweep that could not write exits non-zero and its output carries
      no reconciled. token at all (#192).

    • Every label mutation goes through one checked helper, so clearing
      merge-next or either stale edit fails the sweep too — not only the
      primary state edit (#192).

    • A preserved label keeps the id the issue payload already carried, so
      preservation does not depend on a repository-wide list that has nothing to do
      with the issue (#192).

    • A removal that changes nothing writes nothing, rather than replacing the set
      with itself and opening a race for no state change (#192).

    • Every failure diagnostic on the forgejo backend names the verb as well as the
      path and the status. A read used to say HTTP 500 from 'repos/…', which
      cannot be told from a failed write of the same path (#192).

    • The diagnostic names what was attempted and that it did not happen, instead
      of blaming a missing label and telling the operator to bootstrap — a cause it
      had not established (#192, #101).

    • An add-label the repo does not carry refuses before any write, so a
      replacement PUT can never drop a label nobody asked to remove (#192).

    Downloads
  • 0.4.1 0003fa1d1c

    0.4.1
    All checks were successful
    CI / test (push) Successful in 1m29s
    CI / release-exercise (push) Has been skipped
    CI / self-guards (push) Successful in 6s
    CI / action-exercise (push) Successful in 5s
    CI / docs-sync-exercise (push) Successful in 5s
    release / release (push) Successful in 11s
    Stable

    andres released this 2026-08-04 16:09:39 +00:00 | 393 commits to main since this release

    Added

    • forge_release_exists, forge_commit_pulls, forge_tag_create,
      forge_release_create and forge_pr_create on both backends, so the
      release path names no client (#191).

    • The forgejo backend serves one PR object at /commits/{sha}/pull where
      GitHub serves an array at /pulls; both verbs emit the array shape, so
      the call site carries one expression (#191).

    • Forgejo creates tags at POST /tags — it serves /git/refs GET-only,
      so GitHub's ref-POST would have 404'd there forever (#191).

    • forgejo_api_base refuses when REPO is empty. Every verb interpolates
      it and every call reaches the network through there, so repos//…
      whose 404 reads as "no release" and "no PRs" — is now impossible (#191).

    • Release asset names are percent-encoded. The hook contract permits any
      filename, and the name travels as a query value: a space made curl reject
      the URL and &/#/+/% silently renamed the asset (#191).

    • lib/forge.sh — the forge selector: forge_detect names the forge from
      the runner's own environment, forge_client names the client it needs, and
      forge_preflight refuses loudly before any sweep when the two disagree
      (#188).

    • The reconcilers and labels-scope run that preflight first, so a
      GitHub-shaped client on a Forgejo instance is a named refusal instead of a
      sweep that reads nothing and reports success (#188).

    • lib/closes_references.sh — the closing-keyword parser, sibling of
      refs_references, so "which issues does this PR close" is answered from a
      PR body rather than from GitHub's GraphQL API (#188).

    • lib/forge-github.sh and lib/forge-forgejo.sh — one call surface, two
      backends, selected by forge_select; no forge branching at the call sites
      (#188).

    • The forgejo backend proves each paginated gather complete against the
      server's x-total-count and refuses loudly when it cannot — a missing
      header is a refusal, not a pass (#188).

    Changed

    • docs/CONSUMERS.md's artifact-hook recovery no longer tells operators to
      run gh release create by hand — on a Forgejo runner there is no gh.
      It names the forge-neutral tag-door path first, with both clients shown
      (#191).

    • issueflow-reconcile gathers open and merged PRs over REST instead of
      gh api graphql. Forgejo serves no GraphQL at all, so the two queries were
      replaced rather than translated; both forges return number and body
      from /pulls in the same shape (#188).

    • forge_api owns the page size, because each forge silently ignores the
      other's parameter: per_page=100 reads 30 items on Forgejo and limit=100
      reads 30 on GitHub, both HTTP 200. No call site names one (#188).

    • Outstanding review requests are derived from the reviews on the current head
      rather than from requested_reviewers, which Forgejo never clears — read
      raw there, a PR would sit at state:bots-reviewing forever (#188).

    Fixed

    • The release doors run on a Forgejo consumer. lib/facts.sh and
      release.yml gathered and published through gh, which the runner image
      does not ship, so the merge door read labeled=no for a correctly
      labeled ceremony PR and the tag door died at the publish (#191).

    • A release fact that could not be read is no longer reported as a definite
      no. A completed read finding no label is still no and still
      fail-closed; a read that did not complete refuses and emits no fact
      (#191).

    • labels-scope no longer fails to compile its jq program on jq 1.6, which
      the Forgejo runner image ships: label is a reserved word in jq's grammar,
      so $label is a syntax error there and every scope derivation died before
      reading the config (#188).

    • labels-reconcile and labels-scope no longer exit 0 on a Forgejo
      consumer having read zero facts — measured on heavy-duty/rig, where the
      sweep printed reconciled. over an empty PR list and scope reported "no
      labeler.yml" for a file that exists (#188).

    • forge_timeline projects Forgejo's label events (.type / .body /
      .user.login) into the GitHub shape (.event / .actor.login) so the
      ruling ladder reads the same board on both forges (#188).

    • forge_pr_activity no longer calls /pulls/{n}/comments on Forgejo
      (HTTP 404); inline review comments come from reviews with
      comments_count > 0 (#188).

    • CI installs shellcheck before linting, matching actionlint — the Forgejo
      runner image does not ship it (#188).

    Downloads
  • 0.4.0 0a54e31972

    0.4.0
    Some checks failed
    release / release (push) Has been cancelled
    Stable

    claude-bot-andresmgsl released this 2026-07-29 12:27:32 +00:00 | 427 commits to main since this release

    Added

    • changelog.d/shape — an optional one-line sentinel, flat or grouped,
      that pins the fragment set's shape and outranks the newest-published-section
      inference; absent, the inference binds unchanged (#182).
    • Add post-merge issue state for merged Refs work awaiting triage-owned verification.
    • changelog_fragment_problem bounds every entry at 300 normalized
      characters, red on the PR that writes the fragment; the armed guard and
      the assembler inherit the one definition (#167).
    • BUILDER.md and CHANGELOG.md state the bound and the split rule: a long
      change ships several short entries, never one long one (#167).

    Changed

    • Labels automation docs now make sweep cadence a consumer-owned tradeoff,
      retain hourly as the engine-less default, and document manual dispatch as
      the operator's immediate full-board sweep (#203).
    • labels — the reconcile cron relaxes from */15 to hourly (#199), cutting a
      private consumer's schedule-triggered full-board sweeps ~4× at GitHub's
      1-minute billing floor.
    • labels — the hourly cron is the sweep's only wake for transitions no
      subscribed event carries — a verdict landing, blocker:ci-red, a
      blocker:conflict when another PR merges, the time-based stale/reclaim — so it
      bounds their latency to ≤1h, delaying no event-carried transition (#199).
    • labels — the caller's issues: trigger narrows to
      [opened, closed, edited, reopened] (#199), the actions that carry a
      queue-state change the cron cannot wait a cadence for. The churn/validation
      actions — labeled/unlabeled/assigned/unassigned — come off; the PR handoff
      wake is unaffected.
    • labels — each caller trigger now carries a comment saying why it is
      subscribed, and reconcile keeps cancel-in-progress: false (#199) —
      cancelling a sweep mid-board is the race that guard exists to prevent.
    • CONTRIBUTING.md now points to BUILDER.md for the shared PR flow instead
      of restating doctrine that can drift, while retaining ceremony's roster and
      other repo-specific facts (#198).
    • Builder doctrine makes each whole-round reply the durable Round log record
      mirrored by the engine, leaving handoff as a mechanical facts-only step
      instead of a newly composed summary (#196).
    • FLEET.md removes its duplicate bench roster, records crew as a general
      operator-configured tool, and advances its whole-file audit stamp to
      crew@eaeb302 with every surviving crew link re-pinned (#193).
    • FLEET.md keeps the registry's authorization rule and its crew#16/crew#66
      provenance, while replacing duplicated mechanism and path claims with a
      pinned pointer to crew's registry header (#192).
    • BUILDER.md gates both review-request points on a green check at the
      head, carries crew#45's argued exception for failures outside the PR,
      and states the ruled classification: cancelled and stale are not a
      green head; skipped and neutral are (#189).
    • BUILDER.md documents CI-red recovery in pickup precedence: a red head
      of your own PR is picked up before claiming another issue, is never a
      parked claim, and follows crew#17's recovery path (#189).
    • FLEET.md writes the ci-red wake into the duty order between resume
      and build, now as deployed engine rather than on paper: the
      reconciliation stamp advances to the crew SHA carrying crew#64 (#189).
    • FLEET.md describes the build wake's check gate as the engine
      implements it: a green head, or one with no checks configured, opens a
      round; a red head and an unfinished one are held and reported
      separately (#189).
    • FLEET.md corrects the attention wake to the crew#66 ruling: the query
      is cross-repo, the action is registry-bounded, and an out-of-scope
      demand is reported and escalated to the operator rather than worked. It
      no longer claims attention is exempt from the registry (#189).
    • FLEET.md distinguishes an attention session that dies before acking,
      which relaunches, from one that completes without acking, which is a
      decline a ledger keeps from re-firing (#189).
    • BUILDER.md re-requests by head, not by verdict: a push while
      answering a round stales every approval, so every panelist is
      re-requested; only an unchanged head re-requests the non-approvers
      alone (#190).
    • FLEET.md's duty-loop mechanism is a pointer to crew's shared engine; the
      wake lists follow the engine's duty order, the roster keeps the as-built
      bench beside fleet.roster's target, and the reconciliation stamp names
      crew@01fb49c (#187).
    • Ceremony's changelog is grouped from this release forward: the pending
      fragments carry ### headings under a grouped sentinel (#182).
    • BUILDER.md: a park declaration stands until its facts change — a
      nothing-changed resumption posts nothing; only a no-open-PR park owes a
      refresh, inside the 48-hour reclaim window (#178).
    • Private-repository label callers document actions: read alongside checks and statuses for workflow-run check-rollup nodes (#173).

    Fixed

    • FLEET.md no longer says a review request outside the registry is
      authorization: repos.txt is the scope for the review queue, out-of-scope
      requests are logged and never acted on, and the attention wake is stated
      as the one registry-independent exception, by design (#187).
    • blocked_reference_records unions every Blocked by clause in the body
      instead of binding to the first marker occurrence — a repeated declaration
      no longer promotes on its first sentence alone, and earlier prose that
      merely mentions being blocked no longer hijacks the parse (#184).
    • decide_state() refuses state:needs-human while the hand-set blocked
      label stands — the PR falls to state:addressing, exactly parallel to the
      needs-ruling exclusion; never emitted by blockers() (#180).
    Downloads
  • 0.3.0 bc469d9de5

    0.3.0 Stable

    github-actions[bot] released this 2026-07-24 17:39:38 +00:00 | 480 commits to main since this release

    • Make changelog-armed reject fragment shape drift on the PR that introduces it.
    • A directive hold now has a written ending, not just a beginning: BUILDER.md's shape 5 says the hold ends where it began — on the labels — with the hold owner's most recent queue-label event governing over any stale prose, the timeline read (gh api .../issues/{n}/timeline) named as the move before standing down or up on a hold, a claim against stale prose required to cite the events it read, and a refused claim given its two exits. TRIAGE.md now requires re-reading label events before asserting label-borne state in prose, and makes correcting a lifted hold's stale body header triage's move in the same tick. On 2026-07-24 the unranked signals split two builders reading one board (#149, #151); both acted defensibly — the doctrine, not the builders, lacked the rule (#154).
    • Doctrine names the second Closes #N exception: a same-repo PR whose
      authorizing issue marks an acceptance criterion post-merge uses Refs #N,
      and triage closes the issue by hand on the evidence — merging #143
      auto-closed #137 with exactly such a criterion unmet, and no role had been
      told otherwise. TRIAGE.md now requires a post-merge criterion to carry its
      own mechanism (post-merge, triage closes, Refs #N), REVIEWER.md lists
      Refs #N beside Closes #N and Part of <owner>/<repo>#N and stops
      treating the reference-only PR as a defect, and CONTRIBUTING.md points at
      BUILDER.md as the rule's one home (#151).
    • FLEET.md — the Reviewers wake describes the deployed sweep, not the gh search trigger the bench replaced: the pulls-API requested_reviewers sweep across the org plus the named bot forks is source 1, the repos.txt/search poll an adds-only backstop, and the two are merged and deduplicated by (repo, PR) before acting. Only the notifier's needs-ruling queue remains on paper; repos.txt is the registry only on the triage box; and the Status block now stamps the crew ref the file was last reconciled against (#149).
    • REVIEWER.md now carries the review mechanics every box had been re-deriving from an incident: the queue comes from the API and not the search index, every write is one-shot per (reviewer, PR, head), heads are reviewed in throwaway checkouts, a pinned consumer's config is verified at its pin, and a verdict names the checks its box could not run (#145).
    • The docs/CONSUMERS.md labels-caller stub lists the same issues: types
      as ceremony's own caller — edited and reopened included — so a consumer
      adopting the stub wakes when an issue body's Blocked by #N declaration is
      edited, and when a closed issue re-enters the queue wearing labels derived
      at close. The two lists drifted apart inside PR #32; a parity test now pins
      them together, red if either file drops a type or the lists diverge.
      Adopting the widened list is a stub edit riding the pin bump to the first
      tag carrying this change (#144).
    • labels-reconcile — a queue-cancelled duplicate check is discarded when its context holds a real verdict, so a sibling PR's eviction no longer reds a green PR; an all-cancelled context still blocks (#139).
    • blocker:unrequested now clears the moment the panel is asked: the labels
      caller (and the docs/CONSUMERS.md stub) listens on review_requested and
      review_request_removed, so the one event that falsifies the label — or
      makes it true again — wakes the reconcile sweep instead of waiting for an
      unrelated push or the advisory cron. The scope job skips both events:
      they change no paths, and running the labeler on them widens the #130
      clobber window. Adopting the new triggers is a stub edit riding the pin
      bump to the first tag carrying this change (#137).
    • drills/README.md no longer tells the builder to delete the scratch repo —
      a step no fleet identity can perform, because delete_repo is deliberately
      absent from bot tokens. The builder's end state is archive
      (archived: true, inside the repo scope); the delete is the operator's,
      and cleanup gates nothing — not ready-for-review, not the panel, not the
      merge. The drill record now names the scratch repo by owner/name and
      states the disposal its author actually observed, never one that has not
      happened: both 0.2.0 drills hit the missing-scope wall independently, one
      stalling a release draft on an impossible 403, the other shipping a record
      asserting a delete that never ran (#135).
    • lib/facts.sh — a repository's first push to main (a root commit with no first parent) now reads base_ver=(none) and lets decide's table govern, instead of dying at exit 128 before establishing a fact; the no-base path skips the base fetch and git show, and an unresolvable head still fails loudly (#134).
    • The changelog rule now explains why release PRs write no fragment and how entry-worthy changes land instead (#131).
    • actions/labels-scope replaces actions/labeler@v5 in the labels workflow's scope job: labeler wrote the whole label set (PUT) even under sync-labels: false, silently removing any label applied while it ran — #128 lost its release that way — so the scope job now derives from the same .github/labeler.yml mapping (the changed-files/any-glob-to-any-file shape, block or flow; anything else refuses loudly) and its only write is an additive POST. The reconcile sweep also warns — never sets — when a non-draft PR is release-shaped (bare version differing from its base) but carries no release label (#130).
    Downloads
  • 0.2.0 94e019b3fc

    0.2.0 Stable

    github-actions[bot] released this 2026-07-24 12:36:07 +00:00 | 523 commits to main since this release

    • test/changelog-assembled.test.sh — keep the trio interaction aligned with fragment mode: a dropped entry makes armed red too, while a hand-edited section leaves assembled as the sole red (#126).
    • actions/changelog-assembled — a release PR's stamped section must be byte-for-byte what the fragments it consumed assemble to, replayed from the merge base; inapplicable trees pass with a NOTICE (#116).
    • changelog-armed — treat changelog.d/ as the arming, validate every development fragment, and require bare releases to consume the directory into their exact publishable section (#115).
    • lib/changelog.sh + bin/changelog-assemble — read the changelog.d/ fragments, assemble one release section (canonical group order, one shape per repo), and consume exactly what was published (#114).
    • BUILDER.md — the directed hold is the parked claim's fifth shape, its attention demand is acknowledged in the declaration comment, and its board bookkeeping covers in-flight work; TRIAGE.md no longer excludes it (#113).
    • Ceremony adopts changelog.d/ — a PR writes one fragment per issue instead of editing CHANGELOG.md, the release PR assembles the section, and ## Unreleased is gone (#112).
    • BUILDER.md — the handed-off PR is the parked claim's fourth shape, its handoff is its declaration, and shape 2 covers the round awaiting its first verdicts (#109).
    • labels-reconcile — warn once per sweep when a repository lacks labels declared by the pinned core taxonomy (#105).
    • LABELS.md — drop the vendored scope-table enumeration; the per-repo set lives in .github/labels.conf and the repo's own CONTRIBUTING (#104).
    • labels-reconcile — a degraded mergeability/checks read now logs gh's actual stderr (collapsed, bounded) beside the byte-identical counted line, and the blind-sweep warning leads with the observed reason instead of asserting the permissions cause (#101).
    • Changelog publication — count entries instead of bytes, refuse dangling grouped headings, and seed grouped re-arms with Added/Changed/Fixed (#98).
    • labels-reconcile — grant callers private-repo check reads and warn when an entire PR sweep is blind (#95).
    • labels-reconcile — the bootstrap now retires the six GitHub defaults LABELS.md publishes as deleted, tolerating both an already-absent label and a refused delete (#93).
    • issueflow-reconcile — a triage-authored issue arrival stands down with exit 0 instead of killing the run before the sweep (#91).
    • FLEET.md — the assignee's attention wake: one role-independent trigger ahead of every per-role list, one acked session per demand; a spec on paper until duty.sh polls it (#86).
    • attention doctrine — define its assignee-owned pickup, ack, queue and clock semantics across labels, triage, and builder roles (#85).
    • attention — add the issue-only, hand-set assignee-demand flag to the core label taxonomy (#84).
    • One issue at a time counts build work in flight: the parked claim's three shapes, its declared-never-inferred comment, and triage's duty to name a directed hold as a park (#77).
    • FLEET.md — the operator notifier's needs-ruling queue (one tracked message per item, edited in place across the rungs) and triage's past-24h wake condition; a spec on paper until an operator updates the box (#74).
    • The sweep observes the escalation contract: a malformed escalation is named field-by-field, and the ladder's 12h/24h rungs each draw one comment to the flag-setter — comment-only, per-episode, both surfaces (#73).
    • Ruling doctrine — define every human-owned trigger, the fixed escalation shape, and the 0–24h builder-to-triage ladder (#72).
    • issueflow-reconcile — nudge once when an offsite flag outlives every visible cross-referenced PR (#69).
    • offsite — protect claimed issues whose PR lives in another repository from the claim-reclaim clock (#68).
    • issueflow-reconcile — keep cross-repo references out of local dependency decisions and require triage to resolve cross-repo blockers by hand (#61).
    • actions/runner-isolated — a pull_request-triggered job may never run on a self-hosted runner (#58).
    • Cross-repo doctrine: the panel is the PR's repo's roster, a review request is authorization but not panel membership, and Part of <repo>#N replaces the Closes #N that cannot cross repos (#57).
    • The sweep's needs-ruling invariants, one implementation for both surfaces: the issue-side staleness exemption, the bare-flag check (comment-only, the label is never removed), and the 7-day nudge to the decider (#52).
    • needs-ruling — the cross-cutting flag for a pending human decision, excluded from state:needs-human and from the staleness sweep (#51).
    Downloads
  • 0.1.0 24ed9ad05b

    0.1.0 Stable

    github-actions[bot] released this 2026-07-23 00:12:15 +00:00 | 641 commits to main since this release

    • lib/version.sh — one version abstraction, file and package-json backends (#3).
    • lib/changelog.sh + bin/changelog-section — the one canonical changelog-section extractor (#4).
    • actions/changelog-armed — the version-keyed arming guard (#5).
    • actions/changelog-monotonic — shipped release headings are append-only: no deletion, no duplication (#6).
    • actions/drill-recorded — a release tree must carry its drill record (#7).
    • lib/decide.sh — the merge door's five-state decision, pure and exhaustively tested (#8).
    • .github/workflows/release.yml + lib/facts.sh — the reusable two-door release workflow (#9).
    • .github/workflows/labels.yml + actions/labels-reconcile — label taxonomy bootstrap and PR-state reconciliation (#10).
    • Ceremony adopts its own ceremony: VERSION, this changelog, the drill doctrine, the self-callers, and the self-guards in CI (#11).
    Downloads