Skip to content

Half-State Patrol

Half-State Patrol #172

name: Half-State Patrol
# This board's standing caller of objectstack's half-state patrol: the composite
# action `objectstack-ai/objectstack/.github/actions/half-state-patrol`, pinned
# to an objectstack commit sha (objectui#11174, objectui's half of
# objectstack-ai/objectstack#18471).
#
# ## What lives here, and what does not
#
# This file holds only what is THIS board's: when the patrol runs, what it may
# touch, and the inputs whose values are decided here. Everything else — the
# sweeper (objectstack's `scripts/pm/check-half-states.mjs`), how its findings
# are rendered and delivered, and when a run goes red — is the action's, and
# the action's own header is the authority on it. ⛔ Do not restate that
# behaviour here. A copy of upstream prose describes upstream as of the day it
# was copied and no later: objectui#10208 retired this repository's copy of the
# sweeper for that reason, and objectui#11174 retired the copied steps and the
# prose adopted with them.
#
# ## The pin
#
# The `uses:` ref is a 40-character objectstack commit sha, ⛔ never `@main`:
# `@main` would adopt whatever landed upstream an hour ago, with no reviewed
# moment in this repository and nothing to roll back to. A composite action
# referenced at a sha makes the runner place the WHOLE objectstack repository
# at that sha, so the pin freezes the sweeper as well as `action.yml`.
#
# TO BUMP: pin objectstack `main`'s tip as read at that moment, and record the
# sha and the time it was read in the pull request. ⛔ Not the action's own last
# commit — that would roll the sweeper back to wherever it stood when
# `action.yml` last changed (objectui#11174, the seat's ruling on which sha to
# pin). The pin must contain objectstack `79114850f`, where `anchor-optional`
# was declared: an older one receives that input as an unexpected one, which
# the runner only warns about, and every scheduled run here goes red.
#
# The sha spelling is a declared exception to this repository's action-ref
# convention: `DECLARED_EXCEPTIONS` in `scripts/check-action-ref-convention.mjs`
# carries this workflow, this action and the reason.
#
# ## No anchor issue, by decision (objectui#8740)
#
# This board has no pinned anchor issue: the maintainer declined the setup and
# closed objectui#7852 for it. The step below says so BY
# NAME rather than by omission — `anchor-issue` is passed empty and
# `anchor-optional: true` declares empty to be this board's supported
# configuration (objectstack-ai/objectstack#20793, decision A). The action then
# sweeps, puts the rendered findings in the run summary, emits a `::notice::`,
# skips the anchor write and passes. An issue number is only ever meaningful in
# the repo it was minted in, which is why nothing here guesses one.
#
# ⚠️ The accepted cost, stated rather than rediscovered: the findings live only
# in run summaries, which notify nobody — objectui#5791 measured what that
# silence costs on this board. A seat reading this board's half-states reads the
# patrol's run summaries; there is no anchor to read.
#
# ⛔ The opt-in is empty-only, and the action enforces that, not this file: a
# non-numeric `anchor-issue`, or a sweep that could not run, still fails the
# run. Delivering to an anchor is one input away (a `tracking`-labeled issue's
# number as `anchor-issue`), but it reverses the maintainer's decline, so it is
# the maintainer's call.
#
# ## The closed-card floor (objectui#5985)
#
# `closed-floor` holds the sweeper's closed-card reader (`pm:*` labels left on
# cards that already closed) to cards closed on or after 2026-08-28 — the date
# the strip-on-close convention was written into the pm-dispatch protocol's
# label-discipline section. `pm:*` on a card closed before the convention
# existed is inert history, since the dispatch loop reads state on OPEN cards
# only. The measurement taken on 2026-08-24 says what an unfloored reader does
# here: 815 closed cards carried `pm:dispatched`, and ~87% of the issues in the
# reader's window carried some `pm:*` residue, against the 26% upstream
# measured — rows that report the convention rather than a defect, and that
# consume the whole body budget. Judging from the cutover forward buys the
# row's value with no backfill: ⛔ no bulk relabelling of closed cards was run
# and none is owed.
#
# ⛔ Do not move the date EARLIER without re-measuring: every day it moves back
# pulls in cards closed under no convention at all. The floor is UPSTREAM code;
# what is this repository's own is the value passed to it.
on:
schedule:
# Four times a day, six hours apart, at :37 past the hour.
#
# The minute is offset ON PURPOSE. The triage Routine that heals these same
# states fires hourly near the top of the hour, and a patrol landing at the
# same minute would keep reading the board mid-heal — reporting half-states
# the healer is in the middle of pairing, i.e. manufacturing findings that
# clear themselves. :37 puts this sweep in the quiet part of the healer's
# cycle in both directions. Four runs a day rather than hourly keeps it
# cheap on the core API quota it shares with the dispatch loop's hot path.
- cron: '37 1,7,13,19 * * *'
workflow_dispatch: {}
# A change to the patrol is exercised before it merges: on a pull_request run
# the action still sweeps, proving the pin, the inputs and the transport on a
# real runner, but it skips both anchor steps and writes no issue.
#
# ⚠️ That skip means a pull_request run cannot show the no-anchor opt-in at
# all. The first scheduled or dispatched run after a change here is the live
# reading of it: `configured=false`, a `::notice::`, the anchor write skipped,
# and the run green.
pull_request:
paths:
# The action and the sweeper both arrive at the pinned sha, so this file
# is the only path in this repository a change can break the patrol
# through — and bumping the pin is an edit to it.
- '.github/workflows/half-state-patrol.yml'
# What the action asks of its caller's token: `contents: read`, and
# `issues: write` for its single anchor-body PATCH. With no anchor configured
# here that write is never attempted, and the token is never used for labels,
# comments, assignees or state.
permissions:
contents: read
issues: write
# One patrol at a time. A scheduled run overlapping a manual dispatch would read
# the board twice on one request budget and, were an anchor ever configured,
# race to rewrite the same body — the loser's findings vanishing with no trace
# but an edit-history entry.
concurrency:
group: half-state-patrol
cancel-in-progress: false
jobs:
patrol:
name: Live half-state sweep
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
# The SUBJECT of the sweep, not a source of the code that runs it: the
# sweeper reads this board's own workflows and tracked files from this
# checkout, and refuses to run without it.
- name: Checkout repository
uses: actions/checkout@v7
# Stays in the caller: the action holds no Node pin of its own and only
# refuses a runner older than the floor its own tree declares.
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: '22'
- name: Sweep the board
uses: objectstack-ai/objectstack/.github/actions/half-state-patrol@0c5a71b0942d54e56ad365f0b2a173fd84c1c5e1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
# Empty on purpose, with the opt-in beside it: see "No anchor issue,
# by decision" above.
anchor-issue: ''
anchor-optional: true
closed-floor: '2026-08-28'