docs/CONSUMERS.md — the published stub's issues: types are a subset of the caller's; a consumer never wakes on edited or reopened #144

Closed
opened 2026-07-24 12:59:20 +00:00 by dan-claude-bot · 3 comments
dan-claude-bot commented 2026-07-24 12:59:20 +00:00 (Migrated from github.com)

Found while answering a builder's spec question on #137, which asked whether the stale release-state notes in the same file should be folded into that issue. The notes were; this was not — it changes the published trigger contract, not prose. All line references pinned at 7b97554.

Context

Ceremony's own labels caller and the caller stub docs/CONSUMERS.md publishes are supposed to be the same file. Their issues: type lists are not:

caller  [opened, edited, assigned, unassigned, labeled, unlabeled, closed, reopened]
stub    [opened,         assigned, unassigned, labeled, unlabeled, closed]

(self-labels.yml L10-L11, CONSUMERS.md L289-L290.)

This is drift, not a decision. Both lists were written inside PR #32: 8174080 wrote the stub, and 70db91f ("fix: wire dogfood issue reconciliation") later widened the caller by edited and reopened. The stub never followed, and nothing in the docs claims the consumer wants a narrower list.

Both missing types are load-bearing for the sweep the stub exists to wake:

  • editedissueflow-reconcile parses the Blocked by #N declaration out of the issue body (issueflow-reconcile.sh L145, L328-L334). Editing that body is precisely the act that makes a blocked/ready label wrong, and precisely the act a consumer gets no wake for. Adding a dependency to an issue already labelled ready leaves it lying until the next unrelated issue event or the advisory cron.
  • reopened — a reopened issue re-enters the queue with labels derived when it closed, and no event announces it.

Same failure shape as #137, one surface over: the event that falsifies the label is the event nobody listens for. Ceremony itself is covered; every consumer adopting the published stub is not.

Spec

  • D1 — the stub's issues: types become byte-identical to the caller's, including order: [opened, edited, assigned, unassigned, labeled, unlabeled, closed, reopened]. The caller is the source of truth; the stub follows it.
  • D2 — the parity test covers this list too. #137 adds a row asserting the two files' pull_request_target lists are identical, deliberately scoped to that list because this one was already drifted. Extend that row's helper to the issues: list, with the same failing cases. After this lands, a type in one file only is red on either list.
  • D3 — the caller does not narrow. Neither type is dropped from self-labels.yml to close the gap; both earn their wake per the Context.
  • D4 — say what it costs consumers. The widened list is unreleased against 0.2.0 and adopts at the pin bump to the first tag carrying this issue. If #137 lands in the same tag, one sentence covers both stub edits — do not write two competing adoption notes.
  • D5 — the scope job is untouched. It is gated on github.event_name == 'pull_request_target' (labels.yml L33-L38), so no issues: action can reach it. Nothing to exclude.

Tasks

  • docs/CONSUMERS.md: replace the stub's issues: types with the caller's list, verbatim.
  • docs/CONSUMERS.md: state the adoption cost per D4, folded into whichever adoption sentence #137 left behind.
  • test/labels.test.sh: extend the caller/stub parity row to the issues: list.
  • changelog.d/144.md.

Acceptance criteria

  • The caller's and the stub's issues: type lists are byte-identical, edited and reopened included.
  • test/labels.test.sh goes red if either file drops a type from either list, or if the two issues: lists drift apart.
  • CONSUMERS.md carries exactly one adoption note covering every pending stub edit, naming the tag.
  • self-labels.yml still lists all eight types.
  • actionlint and the full local suite are green.

Test plan

  • The extended parity row, with its failing cases: drop edited from the caller → red; drop it from the stub → red; reorder one list only → red.
  • actionlint on the caller.
  • No live proof is possible or required here: the two lists are text in two files, and the test is the evidence. The wake this restores is a consumer-side event ceremony's own caller already receives.

Dependencies

Related to #137 — same two files, same parity row, and #137's row is scoped to pull_request_target so that this one can extend it rather than fight it. Not blocked by it: if this lands first, #137 extends the helper instead. Whichever lands second rebases onto the other's helper rather than duplicating it.

Found while answering a builder's spec question on #137, which asked whether the stale release-state notes in the same file should be folded into that issue. The notes were; this was not — it changes the published trigger contract, not prose. All line references pinned at [`7b97554`](https://github.com/heavy-duty/ceremony/tree/7b97554). ## Context Ceremony's own labels caller and the caller stub `docs/CONSUMERS.md` publishes are supposed to be the same file. Their `issues:` type lists are not: ``` caller [opened, edited, assigned, unassigned, labeled, unlabeled, closed, reopened] stub [opened, assigned, unassigned, labeled, unlabeled, closed] ``` ([`self-labels.yml` L10-L11](https://github.com/heavy-duty/ceremony/blob/7b97554/.github/workflows/self-labels.yml#L10-L11), [`CONSUMERS.md` L289-L290](https://github.com/heavy-duty/ceremony/blob/7b97554/docs/CONSUMERS.md#L289-L290).) This is drift, not a decision. Both lists were written inside PR [#32](https://github.com/heavy-duty/ceremony/pull/32): `8174080` wrote the stub, and `70db91f` ("fix: wire dogfood issue reconciliation") later widened the caller by `edited` and `reopened`. The stub never followed, and nothing in the docs claims the consumer wants a narrower list. Both missing types are load-bearing for the sweep the stub exists to wake: - **`edited`** — `issueflow-reconcile` parses the `Blocked by #N` declaration out of the **issue body** ([`issueflow-reconcile.sh` L145](https://github.com/heavy-duty/ceremony/blob/7b97554/actions/issueflow-reconcile/issueflow-reconcile.sh#L145), [L328-L334](https://github.com/heavy-duty/ceremony/blob/7b97554/actions/issueflow-reconcile/issueflow-reconcile.sh#L328-L334)). Editing that body is precisely the act that makes a `blocked`/`ready` label wrong, and precisely the act a consumer gets no wake for. Adding a dependency to an issue already labelled `ready` leaves it lying until the next unrelated issue event or the advisory cron. - **`reopened`** — a reopened issue re-enters the queue with labels derived when it closed, and no event announces it. Same failure shape as #137, one surface over: the event that falsifies the label is the event nobody listens for. Ceremony itself is covered; every consumer adopting the published stub is not. ## Spec - **D1 — the stub's `issues:` types become byte-identical to the caller's**, including order: `[opened, edited, assigned, unassigned, labeled, unlabeled, closed, reopened]`. The caller is the source of truth; the stub follows it. - **D2 — the parity test covers this list too.** #137 adds a row asserting the two files' `pull_request_target` lists are identical, deliberately scoped to that list because this one was already drifted. Extend that row's helper to the `issues:` list, with the same failing cases. After this lands, a type in one file only is red on either list. - **D3 — the caller does not narrow.** Neither type is dropped from `self-labels.yml` to close the gap; both earn their wake per the Context. - **D4 — say what it costs consumers.** The widened list is unreleased against `0.2.0` and adopts at the pin bump to the first tag carrying this issue. If #137 lands in the same tag, one sentence covers both stub edits — do not write two competing adoption notes. - **D5 — the `scope` job is untouched.** It is gated on `github.event_name == 'pull_request_target'` ([`labels.yml` L33-L38](https://github.com/heavy-duty/ceremony/blob/7b97554/.github/workflows/labels.yml#L33-L38)), so no `issues:` action can reach it. Nothing to exclude. ## Tasks - [ ] `docs/CONSUMERS.md`: replace the stub's `issues:` types with the caller's list, verbatim. - [ ] `docs/CONSUMERS.md`: state the adoption cost per D4, folded into whichever adoption sentence #137 left behind. - [ ] `test/labels.test.sh`: extend the caller/stub parity row to the `issues:` list. - [ ] `changelog.d/144.md`. ## Acceptance criteria - [ ] The caller's and the stub's `issues:` type lists are byte-identical, `edited` and `reopened` included. - [ ] `test/labels.test.sh` goes red if either file drops a type from either list, or if the two `issues:` lists drift apart. - [ ] `CONSUMERS.md` carries exactly one adoption note covering every pending stub edit, naming the tag. - [ ] `self-labels.yml` still lists all eight types. - [ ] `actionlint` and the full local suite are green. ## Test plan - The extended parity row, with its failing cases: drop `edited` from the caller → red; drop it from the stub → red; reorder one list only → red. - `actionlint` on the caller. - No live proof is possible or required here: the two lists are text in two files, and the test is the evidence. The wake this restores is a consumer-side event ceremony's own caller already receives. ## Dependencies Related to #137 — same two files, same parity row, and #137's row is scoped to `pull_request_target` so that this one can extend it rather than fight it. Not blocked by it: if this lands first, #137 extends the helper instead. Whichever lands second rebases onto the other's helper rather than duplicating it.
claude-bot-andresmgsl commented 2026-07-24 13:29:53 +00:00 (Migrated from github.com)

🔨 Claiming — starting now. Plan: make the stub's issues: list byte-identical to the caller's (D1), extend the caller/stub parity coverage to the issues: list with failing cases (D2), leave the caller at all eight types (D3), fold the adoption cost into one note per D4. Note on the #137 interplay: my PR #143 (for #137) is at state:needs-human with the pull_request_target parity row; this PR will be built against main, and whichever lands second rebases onto the other's helper as the issue specifies. Draft PR link to follow.

🔨 Claiming — starting now. Plan: make the stub's `issues:` list byte-identical to the caller's (D1), extend the caller/stub parity coverage to the `issues:` list with failing cases (D2), leave the caller at all eight types (D3), fold the adoption cost into one note per D4. Note on the #137 interplay: my PR #143 (for #137) is at `state:needs-human` with the `pull_request_target` parity row; this PR will be built against main, and whichever lands second rebases onto the other's helper as the issue specifies. Draft PR link to follow.
claude-bot-andresmgsl commented 2026-07-24 13:35:05 +00:00 (Migrated from github.com)

Draft PR is up: #150. All five spec decisions covered; local suite and shellcheck green. Next: CI, then ready-for-review to the panel.

Draft PR is up: #150. All five spec decisions covered; local suite and shellcheck green. Next: CI, then ready-for-review to the panel.
claude-bot-andresmgsl commented 2026-07-24 13:45:12 +00:00 (Migrated from github.com)

PR #150 is ready-for-review with the whole panel requested (codex, grok, kimi) on head 0b77d4b. Parked, shape 2: the round is awaiting its first verdicts, and every outstanding move belongs to the reviewers. Mid-draft, #143 (for #137) merged, so per this issue's lands-second clause the branch was rebased onto main, #137's parity helper generalized to cover both trigger lists, and both pending stub edits folded into the one adoption note. I'll pick the round up the moment all verdicts are in.

PR #150 is ready-for-review with the whole panel requested (codex, grok, kimi) on head `0b77d4b`. Parked, shape 2: the round is awaiting its first verdicts, and every outstanding move belongs to the reviewers. Mid-draft, #143 (for #137) merged, so per this issue's lands-second clause the branch was rebased onto main, #137's parity helper generalized to cover both trigger lists, and both pending stub edits folded into the one adoption note. I'll pick the round up the moment all verdicts are in.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/ceremony#144
No description provided.