.github/ISSUE_TEMPLATE/ — ship the proposal door CONTRIBUTING.md already promises #51

Closed
opened 2026-09-01 15:15:29 +00:00 by claude-bot-andresmgsl · 3 comments

Context

CONTRIBUTING.md L54
says, in this repository's own voice:

Only triage mints work issues; anyone may file a proposal, which triage converts
or refuses.

There is no proposal to file. Measured 2026-09-01T15:13Z against this instance:
GET /repos/heavy-duty/stoke/issue_templatesHTTP 200, body null, and
.github/
holds exactly two files, labeler.yml and labels.conf. No ISSUE_TEMPLATE/, no
config.yml, no chooser. A contributor who follows that sentence lands on a blank
issue box with no shape and no route, and the sentence is the only instruction the
repository gives them.

The doctrine this repository vendors assumes the door exists. .ceremony/TRIAGE.md
L15-L19
names triage's inputs as "Every open proposal in the repo you serve" and
"Stray issues — anything filed outside the proposal form by a non-triage
actor"
, and outcome 4 instructs triage to "convert its substance into a proposal
and close it"
— none of which is executable here today. .ceremony/AGENTS.md
L34/L40
draws the same pipeline (proposal ──▶ triage ──▶ work issue) and tells every agent
"Found work? File or extend a proposal."

The measurement that makes this buildable rather than aspirational. At 0.6.1 the
intake door was impossible on this forge: Forgejo exposes no Discussions surface
(/discussions → 404, no repo field), which is why this board recorded since
2026-08-21 that the issue door was the only door. The 2026-08-30 re-vendor to 0.6.3
replaced discussion with a YAML issue form, and this Forgejo renders them
GET /repos/heavy-duty/ceremony/issue_templates200, serving
Proposal (anyone) (4 fields) and Work order (triage only) (7 fields), parsed, on
this same instance, re-measured 2026-09-01T15:11Z. The door was not renamed from
unreachable to unreachable; an impossible one was replaced with a buildable one.

Prior art, and it is a copy job. ceremony at
0.6.3
ships exactly three files — config.yml (714 B), proposal.yml (1204 B),
work-order.yml (2754 B). Both forms are repository-agnostic: they name doctrine
files (TRIAGE.md, LABELS.md) that stoke vendors at .ceremony/, and no repository.
Only config.yml carries a ceremony-qualified URL.

Why this is being minted now, having been declined once. Triage recorded this gap
on #36 (comment
30456)
as a live mint candidate and deliberately did not mint it: at the time it was a
doctrine-versus-repo gap in which stoke itself claimed nothing, and minting on that
basis would have been board-shaping rather than repair. That changed at
2026-09-01T13:08Z, when #46/!47 merged CONTRIBUTING.md and the repository began
asserting the door exists. A promise the repository authors and cannot keep is a
different defect from a doctrine it has not yet adopted, and repairing it is triage's
ordinary work.

Spec

Decisions, made here so the builder makes none of them.

  1. Ship all three files, mirroring ceremony 0.6.3's set — not proposal.yml
    alone. With blank_issues_enabled: false and a single form, the web chooser leaves
    no route to mint a work order by hand: stoke's only declared triage actor
    (.github/labels.conf triage-actors=claude-bot-andresmgsl) files through the API,
    but a human standing in for triage would be locked into the proposal form. Shipping
    the full set also keeps a future re-sync a straight copy instead of a diff, which
    matters in a repository whose .ceremony/ mirror is hand-vendored.
  2. proposal.yml is copied byte-identical. It names no repository. Do not reword it.
  3. work-order.yml is copied with exactly one adaptation. Its description: reads
    "gh issue create --body-file bypasses forms and stays legitimate for the triage
    identity."
    gh targets github.com and is wrong on this forge; stoke ships the
    equivalent — --body-file at
    src/cli.js L763,
    documented at
    README.md L515.
    Change that phrase to stoke issue create --body-file and nothing else. This is the
    only wording change anywhere in the two forms; a builder tempted to "improve" more
    prose should not.
  4. config.yml keeps blank_issues_enabled: false and re-points its one contact
    link
    to
    https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/new?template=proposal.yml.
    The name: and about: text is repository-agnostic and is copied as-is.
  5. The hazard that gated this for eight days is settled: blank_issues_enabled: false
    does not disable API issue creation.
    It landed on ceremony main at
    2026-08-25T12:15:47Z (bb984de1, refined by 13add81d at 12:45:14Z). Eight
    issues have been created on that board since (ceremony #263, #265, #268, #269, #271,
    #273, #275, #276), all by fleet agents — and
    ceremony#275 and ceremony#276 both carry created_at 2026-08-31T20:27:37Z, the
    same second
    , which no human web-form path produces. The flag is a web-chooser
    setting; POST /repos/{owner}/{repo}/issues does not consult it. Stated at the
    strength of the evidence: this is a fingerprint of scripted API creation on a board
    that has the flag live, not a direct test — so the direct test is in the test plan
    below and is owed before the close, because every fleet agent files through the
    API and this repository would be unusable if it were wrong.
  6. The forms apply no labels, and none are added. Both ceremony forms carry an empty
    labels: by design (ceremony#24 D2: queue labels are triage's explicit act), verified
    from the live endpoint. stoke's taxonomy is complete for this work — 28 labels
    before and 28 after
    ; this issue requires no labels.conf row and therefore no
    bootstrap=yes dispatch.
  7. No new scope row is owed. .github/labeler.yml maps .github/**scope:ci,
    which covers all three new paths — measured, not assumed, with the same
    path.matchesGlob that
    scripts/check-governance.js L115-L118
    uses: all three return true. CONTRIBUTING.md is covered by *.mdscope:docs.
  8. These files are not under the .ceremony/ byte-identity regime. They live in
    .github/, which test/governance.test.js reads only for labels.conf and
    labeler.yml. No governance test asserts their contents, and none should be added —
    the adaptation in decision 3 is deliberate and byte-identity would forbid it.
  9. Scope addition, named rather than slipped in. This issue's subject is
    .github/ISSUE_TEMPLATE/, and CONTRIBUTING.md is outside it. One line is folded in
    anyway: L54's sentence is the promise this issue repairs, and shipping the door while
    leaving the sentence that points nowhere would fix the mechanism and leave the
    instruction. It is one link on an existing line; splitting it would create two issues
    that must land together to be coherent.

Tasks

  • Create .github/ISSUE_TEMPLATE/proposal.yml as a byte-identical copy of
    ceremony 0.6.3
    (1204 B, including its leading three-line comment about applying no labels)
  • Create .github/ISSUE_TEMPLATE/work-order.yml from
    ceremony 0.6.3
    with the single phrase change in spec decision 3, and no other edit
  • Create .github/ISSUE_TEMPLATE/config.yml from
    ceremony 0.6.3
    with the contact-link URL re-pointed at stoke per spec decision 4
  • Add the proposal-form link to CONTRIBUTING.md L54 so the sentence names the door
    it promises
  • Add a changelog.d/ fragment — this is a contributor-facing change to how work is
    filed, not internal plumbing
  • Run npm test and npm run check:governance locally before opening the PR
  • Open the PR from a same-repo branch on heavy-duty/stoke, not a fork (fork PRs
    stall on the CI approval gate — see !28/!29)
  • When the PR reaches state:needs-human, request @andres by hand — do not
    wait for the engine (heavy-duty/ceremony#276: HUMAN_REVIEWER has no plumbing, so
    the sweep logs HTTP 404 … requested_reviewers and still reports success)
  • Reference this issue with Refs #50, not Closes #50 — the post-merge
    criterion below outlives the merge, and this issue's checkboxes stay current while
    it is open, because the derived transition comment publishes every unticked line
  • Keep this issue's checkboxes current as you go — on this machinery they are
    published, not private (see the note under Acceptance criteria)

Acceptance criteria

Pre-merge, reviewable on the PR:

  • .github/ISSUE_TEMPLATE/ contains exactly config.yml, proposal.yml and
    work-order.yml, and nothing else
  • proposal.yml is byte-identical to ceremony 0.6.3's, proven on the PR by
    curl -sS "$API/repos/heavy-duty/ceremony/raw/.github/ISSUE_TEMPLATE/proposal.yml?ref=0.6.3" | md5sum
    matching md5sum .github/ISSUE_TEMPLATE/proposal.yml
  • work-order.yml differs from ceremony 0.6.3's in exactly one hunk, the
    phrase in spec decision 3 — diff against the fetched original shows one changed
    line and no others
  • config.yml sets blank_issues_enabled: false and its single contact_links
    entry points at heavy-duty/stoke; grep -c 'heavy-duty/ceremony' .github/ISSUE_TEMPLATE/config.yml
    returns 0
  • CONTRIBUTING.md L54's sentence links the proposal form
  • npm test and npm run check:governance are green on the PR head, with
    check:governance still printing governance: 4 identities resolved; 5 scope rows valid
    five rows, not six: no scope row is added by this work
  • ci / test green on the PR head
  • The PR carries scope:ci and scope:docs, derived by the labels-scope job from
    the base map — not hand-set. Read it off a synchronize run's labels-scope:
    line, never off the PR's label set: on this forge a label event re-derives scopes
    from the head map (defect 8 on #36), so a label set is not evidence

Post-merge — this cannot be checked before the merge because the endpoint reads the
default branch, the PR references this issue with Refs #50, the merge moves this
issue to post-merge, and triage owns the close:

  • GET /repos/heavy-duty/stoke/issue_templates returns HTTP 200 with both
    forms parsed — Proposal (anyone) with 4 fields and Work order (triage only)
    with 7 — and each with an empty labels array. Wake condition: the merge. Today
    the same call returns 200 with body null, so the before/after is a measurement
    of this work and not of the forge. Do not substitute a fetch of
    /heavy-duty/stoke/issues/new?template=proposal.yml for this
    — see the failing
    case in the test plan; that URL cannot tell the two states apart

Test plan

API=https://forgejo.heavyduty.builders/api/v1

# 1. the gap, before (200 + null today; both forms after the merge)
curl -sS -H "Authorization: token $FORGE_TOKEN" "$API/repos/heavy-duty/stoke/issue_templates" \
  | jq -r '.[]? | "\(.name) fields=\(.body|length) labels=\(.labels // [] | length)"'

# 2. byte-identity of proposal.yml, and the single-hunk diff of work-order.yml
for f in proposal.yml work-order.yml; do
  curl -sS -H "Authorization: token $FORGE_TOKEN" \
    "$API/repos/heavy-duty/ceremony/raw/.github/ISSUE_TEMPLATE/$f?ref=0.6.3" > "/tmp/$f.orig"
  diff -u "/tmp/$f.orig" ".github/ISSUE_TEMPLATE/$f"
done
# proposal.yml: no output. work-order.yml: one changed line, the `gh` → `stoke` phrase.

# 3. no ceremony-qualified URL survives in config.yml
grep -c 'heavy-duty/ceremony' .github/ISSUE_TEMPLATE/config.yml   # 0

# 4. scope coverage holds with three new tracked paths
npm run check:governance   # governance: 4 identities resolved; 5 scope rows valid
npm test

The case that must fail — coverage is real, not incidental. Delete the .github/**
glob from .github/labeler.yml and re-run npm run check:governance: it must exit 1
with tracked paths have no scope mapping: naming all three new files. Restore the glob.
If it exits 0 with the glob removed, the three files are being covered by something other
than the scope:ci row and spec decision 7 is wrong.

The case that must fail — the API door stays open under blank_issues_enabled: false.
This is the hazard spec decision 5 settles by inference and this step settles directly,
and it is owed before triage closes this issue, not before the merge. After the merge,
file a throwaway issue through the API and close it immediately:

curl -sS -X POST -H "Authorization: token $FORGE_TOKEN" -H 'Content-Type: application/json' \
  -d '{"title":"probe: API issue creation under blank_issues_enabled=false","body":"Throwaway; closing immediately."}' \
  "$API/repos/heavy-duty/stoke/issues" -w '\nHTTP %{http_code}\n'

It must return 201. If it returns 4xx, the API intake door has been closed for every
fleet agent, and the fix is to flip blank_issues_enabled back to true in a fresh
ready issue — the chooser interception is worth less than the door.

A measurement that does NOT work, recorded so nobody repeats it. Fetching the web
chooser URL proves nothing on this instance: measured 2026-09-01T15:12Z,
GET /heavy-duty/ceremony/issues/new?template=proposal.yml303 and
GET /heavy-duty/stoke/issues/new?template=proposal.yml303, identical, even
though ceremony has the form and stoke does not. It redirects to login before it ever
consults templates. Use /api/v1/repos/{owner}/{repo}/issue_templates, which
distinguishes the two today.

Dependencies

None. Each edge was checked rather than assumed, 2026-09-01T15:13Z:

  • No collision edge is owed. .ceremony/TRIAGE.md requires one only when an open
    ready, claimed or blocked issue already carries the deliverable. The board has
    two open issues — #36 (enhancement, post-merge, scope:ci) and #27 (epic,
    needs-ruling, scope:cli) — and neither carries ready, claimed or blocked;
    all three labels return zero at state=all. There is no open PR.
  • No release-window membership call is owed. The release label is carried only by
    closed items — #40, #32 and #1 — so no standing window is open; v1.4.0 shipped
    2026-08-31.
  • Not a member of epic #27. #27 organizes the five CLI gaps, and this is repository
    intake governance, not a CLI gap — the same call that correctly kept #48 out of it.
  • Not a duplicate, and nothing to extend or reopen. Searched state=all for
    ISSUE_TEMPLATE, proposal, intake and template: the only hits are #46 (closed —
    its deliverable was CONTRIBUTING.md's repo-specific facts, and it is the issue whose
    merge created this gap), #48 (closed — the scope map) and #36 (open — the ceremony
    re-pin, which records this gap in comment 30456 and explicitly places it out of scope).
    None of the three should absorb this.
  • Related, not blocking: heavy-duty/ceremony#276 (HUMAN_REVIEWER has no plumbing)
    is why this issue's Tasks carry a by-hand reviewer request. It gates nothing here.
## Context `CONTRIBUTING.md` [L54](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/bef059d7b750179b44334e156dd7bc1a8db9dbd8/CONTRIBUTING.md#L54) says, in this repository's own voice: > Only triage mints work issues; anyone may file a proposal, which triage converts > or refuses. **There is no proposal to file.** Measured 2026-09-01T15:13Z against this instance: `GET /repos/heavy-duty/stoke/issue_templates` → **HTTP 200, body `null`**, and [`.github/`](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/bef059d7b750179b44334e156dd7bc1a8db9dbd8/.github) holds exactly two files, `labeler.yml` and `labels.conf`. No `ISSUE_TEMPLATE/`, no `config.yml`, no chooser. A contributor who follows that sentence lands on a blank issue box with no shape and no route, and the sentence is the only instruction the repository gives them. The doctrine this repository vendors assumes the door exists. `.ceremony/TRIAGE.md` [L15-L19](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/bef059d7b750179b44334e156dd7bc1a8db9dbd8/.ceremony/TRIAGE.md#L15-L19) names triage's inputs as *"Every open **proposal** in the repo you serve"* and *"**Stray issues** — anything filed outside the **proposal form** by a non-triage actor"*, and outcome 4 instructs triage to *"convert its substance into a proposal and close it"* — none of which is executable here today. `.ceremony/AGENTS.md` [L34/L40](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/bef059d7b750179b44334e156dd7bc1a8db9dbd8/.ceremony/AGENTS.md#L34) draws the same pipeline (`proposal ──▶ triage ──▶ work issue`) and tells every agent *"Found work? File or extend a proposal."* **The measurement that makes this buildable rather than aspirational.** At `0.6.1` the intake door was *impossible* on this forge: Forgejo exposes no Discussions surface (`/discussions` → 404, no repo field), which is why this board recorded since 2026-08-21 that the issue door was the only door. The 2026-08-30 re-vendor to `0.6.3` replaced *discussion* with a **YAML issue form**, and **this Forgejo renders them** — `GET /repos/heavy-duty/ceremony/issue_templates` → **200**, serving `Proposal (anyone)` (4 fields) and `Work order (triage only)` (7 fields), parsed, on this same instance, re-measured 2026-09-01T15:11Z. The door was not renamed from unreachable to unreachable; an impossible one was replaced with a buildable one. **Prior art, and it is a copy job.** ceremony at [`0.6.3`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/tag/0.6.3/.github/ISSUE_TEMPLATE) ships exactly three files — `config.yml` (714 B), `proposal.yml` (1204 B), `work-order.yml` (2754 B). Both forms are **repository-agnostic**: they name doctrine files (`TRIAGE.md`, `LABELS.md`) that stoke vendors at `.ceremony/`, and no repository. Only `config.yml` carries a ceremony-qualified URL. **Why this is being minted now, having been declined once.** Triage recorded this gap on #36 (comment [30456](https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/36#issuecomment-30456)) as a live mint candidate and deliberately did **not** mint it: at the time it was a doctrine-versus-repo gap in which stoke itself claimed nothing, and minting on that basis would have been board-shaping rather than repair. That changed at **2026-09-01T13:08Z**, when #46/!47 merged `CONTRIBUTING.md` and the repository began asserting the door exists. A promise the repository authors and cannot keep is a different defect from a doctrine it has not yet adopted, and repairing it is triage's ordinary work. ## Spec Decisions, made here so the builder makes none of them. 1. **Ship all three files, mirroring ceremony `0.6.3`'s set** — not `proposal.yml` alone. With `blank_issues_enabled: false` and a single form, the web chooser leaves **no route to mint a work order by hand**: stoke's only declared triage actor (`.github/labels.conf` `triage-actors=claude-bot-andresmgsl`) files through the API, but a human standing in for triage would be locked into the proposal form. Shipping the full set also keeps a future re-sync a straight copy instead of a diff, which matters in a repository whose `.ceremony/` mirror is hand-vendored. 2. **`proposal.yml` is copied byte-identical.** It names no repository. Do not reword it. 3. **`work-order.yml` is copied with exactly one adaptation.** Its `description:` reads *"`gh issue create --body-file` bypasses forms and stays legitimate for the triage identity."* `gh` targets github.com and is wrong on this forge; stoke ships the equivalent — `--body-file` at [`src/cli.js` L763](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/bef059d7b750179b44334e156dd7bc1a8db9dbd8/src/cli.js#L763), documented at [`README.md` L515](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/bef059d7b750179b44334e156dd7bc1a8db9dbd8/README.md#L515). Change that phrase to `stoke issue create --body-file` and nothing else. This is the only wording change anywhere in the two forms; a builder tempted to "improve" more prose should not. 4. **`config.yml` keeps `blank_issues_enabled: false` and re-points its one contact link** to `https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/new?template=proposal.yml`. The `name:` and `about:` text is repository-agnostic and is copied as-is. 5. **The hazard that gated this for eight days is settled: `blank_issues_enabled: false` does not disable API issue creation.** It landed on ceremony `main` at **2026-08-25T12:15:47Z** (`bb984de1`, refined by `13add81d` at 12:45:14Z). **Eight** issues have been created on that board since (ceremony #263, #265, #268, #269, #271, #273, #275, #276), all by fleet agents — and **ceremony#275 and ceremony#276 both carry `created_at` `2026-08-31T20:27:37Z`, the same second**, which no human web-form path produces. The flag is a web-chooser setting; `POST /repos/{owner}/{repo}/issues` does not consult it. Stated at the strength of the evidence: this is a fingerprint of scripted API creation on a board that has the flag live, not a direct test — so the direct test is in the test plan below and is owed before the close, because **every fleet agent files through the API and this repository would be unusable if it were wrong.** 6. **The forms apply no labels, and none are added.** Both ceremony forms carry an empty `labels:` by design (ceremony#24 D2: queue labels are triage's explicit act), verified from the live endpoint. stoke's taxonomy is complete for this work — **28 labels before and 28 after**; this issue requires no `labels.conf` row and therefore no `bootstrap=yes` dispatch. 7. **No new scope row is owed.** `.github/labeler.yml` maps `.github/**` → `scope:ci`, which covers all three new paths — measured, not assumed, with the same `path.matchesGlob` that [`scripts/check-governance.js` L115-L118](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/bef059d7b750179b44334e156dd7bc1a8db9dbd8/scripts/check-governance.js#L115-L118) uses: all three return `true`. `CONTRIBUTING.md` is covered by `*.md` → `scope:docs`. 8. **These files are not under the `.ceremony/` byte-identity regime.** They live in `.github/`, which `test/governance.test.js` reads only for `labels.conf` and `labeler.yml`. No governance test asserts their contents, and none should be added — the adaptation in decision 3 is deliberate and byte-identity would forbid it. 9. **Scope addition, named rather than slipped in.** This issue's subject is `.github/ISSUE_TEMPLATE/`, and `CONTRIBUTING.md` is outside it. One line is folded in anyway: L54's sentence is the promise this issue repairs, and shipping the door while leaving the sentence that points nowhere would fix the mechanism and leave the instruction. It is one link on an existing line; splitting it would create two issues that must land together to be coherent. ## Tasks - [ ] Create `.github/ISSUE_TEMPLATE/proposal.yml` as a byte-identical copy of [ceremony `0.6.3`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/tag/0.6.3/.github/ISSUE_TEMPLATE/proposal.yml) (1204 B, including its leading three-line comment about applying no labels) - [ ] Create `.github/ISSUE_TEMPLATE/work-order.yml` from [ceremony `0.6.3`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/tag/0.6.3/.github/ISSUE_TEMPLATE/work-order.yml) with the single phrase change in spec decision 3, and no other edit - [ ] Create `.github/ISSUE_TEMPLATE/config.yml` from [ceremony `0.6.3`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/tag/0.6.3/.github/ISSUE_TEMPLATE/config.yml) with the contact-link URL re-pointed at stoke per spec decision 4 - [ ] Add the proposal-form link to `CONTRIBUTING.md` L54 so the sentence names the door it promises - [ ] Add a `changelog.d/` fragment — this is a contributor-facing change to how work is filed, not internal plumbing - [ ] Run `npm test` and `npm run check:governance` locally before opening the PR - [ ] Open the PR from a **same-repo branch** on `heavy-duty/stoke`, not a fork (fork PRs stall on the CI approval gate — see !28/!29) - [ ] When the PR reaches `state:needs-human`, request `@andres` **by hand** — do not wait for the engine (heavy-duty/ceremony#276: `HUMAN_REVIEWER` has no plumbing, so the sweep logs `HTTP 404 … requested_reviewers` and still reports success) - [ ] Reference this issue with **`Refs #50`**, not `Closes #50` — the post-merge criterion below outlives the merge, and this issue's checkboxes stay current while it is open, because the derived transition comment publishes every unticked line - [ ] Keep this issue's checkboxes current as you go — on this machinery they are published, not private (see the note under Acceptance criteria) ## Acceptance criteria Pre-merge, reviewable on the PR: - [ ] `.github/ISSUE_TEMPLATE/` contains exactly `config.yml`, `proposal.yml` and `work-order.yml`, and nothing else - [ ] `proposal.yml` is byte-identical to ceremony `0.6.3`'s, proven on the PR by `curl -sS "$API/repos/heavy-duty/ceremony/raw/.github/ISSUE_TEMPLATE/proposal.yml?ref=0.6.3" | md5sum` matching `md5sum .github/ISSUE_TEMPLATE/proposal.yml` - [ ] `work-order.yml` differs from ceremony `0.6.3`'s in **exactly one hunk**, the phrase in spec decision 3 — `diff` against the fetched original shows one changed line and no others - [ ] `config.yml` sets `blank_issues_enabled: false` and its single `contact_links` entry points at `heavy-duty/stoke`; `grep -c 'heavy-duty/ceremony' .github/ISSUE_TEMPLATE/config.yml` returns `0` - [ ] `CONTRIBUTING.md` L54's sentence links the proposal form - [ ] `npm test` and `npm run check:governance` are green on the PR head, with `check:governance` still printing `governance: 4 identities resolved; 5 scope rows valid` — five rows, not six: no scope row is added by this work - [ ] `ci / test` green on the PR head - [ ] The PR carries `scope:ci` and `scope:docs`, derived by the `labels-scope` job from the **base** map — not hand-set. Read it off a `synchronize` run's `labels-scope:` line, never off the PR's label set: on this forge a label event re-derives scopes from the head map (defect 8 on #36), so a label set is not evidence Post-merge — this cannot be checked before the merge because the endpoint reads the **default branch**, the PR references this issue with `Refs #50`, the merge moves this issue to `post-merge`, and **triage owns the close**: - [ ] `GET /repos/heavy-duty/stoke/issue_templates` returns **HTTP 200** with **both** forms parsed — `Proposal (anyone)` with 4 fields and `Work order (triage only)` with 7 — and each with an empty `labels` array. Wake condition: the merge. Today the same call returns `200` with body `null`, so the before/after is a measurement of this work and not of the forge. **Do not substitute a fetch of `/heavy-duty/stoke/issues/new?template=proposal.yml` for this** — see the failing case in the test plan; that URL cannot tell the two states apart ## Test plan ```bash API=https://forgejo.heavyduty.builders/api/v1 # 1. the gap, before (200 + null today; both forms after the merge) curl -sS -H "Authorization: token $FORGE_TOKEN" "$API/repos/heavy-duty/stoke/issue_templates" \ | jq -r '.[]? | "\(.name) fields=\(.body|length) labels=\(.labels // [] | length)"' # 2. byte-identity of proposal.yml, and the single-hunk diff of work-order.yml for f in proposal.yml work-order.yml; do curl -sS -H "Authorization: token $FORGE_TOKEN" \ "$API/repos/heavy-duty/ceremony/raw/.github/ISSUE_TEMPLATE/$f?ref=0.6.3" > "/tmp/$f.orig" diff -u "/tmp/$f.orig" ".github/ISSUE_TEMPLATE/$f" done # proposal.yml: no output. work-order.yml: one changed line, the `gh` → `stoke` phrase. # 3. no ceremony-qualified URL survives in config.yml grep -c 'heavy-duty/ceremony' .github/ISSUE_TEMPLATE/config.yml # 0 # 4. scope coverage holds with three new tracked paths npm run check:governance # governance: 4 identities resolved; 5 scope rows valid npm test ``` **The case that must fail — coverage is real, not incidental.** Delete the `.github/**` glob from `.github/labeler.yml` and re-run `npm run check:governance`: it must exit **1** with `tracked paths have no scope mapping:` naming all three new files. Restore the glob. If it exits 0 with the glob removed, the three files are being covered by something other than the `scope:ci` row and spec decision 7 is wrong. **The case that must fail — the API door stays open under `blank_issues_enabled: false`.** This is the hazard spec decision 5 settles by inference and this step settles directly, and it is owed **before triage closes this issue**, not before the merge. After the merge, file a throwaway issue through the API and close it immediately: ```bash curl -sS -X POST -H "Authorization: token $FORGE_TOKEN" -H 'Content-Type: application/json' \ -d '{"title":"probe: API issue creation under blank_issues_enabled=false","body":"Throwaway; closing immediately."}' \ "$API/repos/heavy-duty/stoke/issues" -w '\nHTTP %{http_code}\n' ``` It must return **201**. If it returns 4xx, the API intake door has been closed for every fleet agent, and the fix is to flip `blank_issues_enabled` back to `true` in a fresh `ready` issue — the chooser interception is worth less than the door. **A measurement that does NOT work, recorded so nobody repeats it.** Fetching the web chooser URL proves nothing on this instance: measured 2026-09-01T15:12Z, `GET /heavy-duty/ceremony/issues/new?template=proposal.yml` → **303** and `GET /heavy-duty/stoke/issues/new?template=proposal.yml` → **303**, identical, even though ceremony has the form and stoke does not. It redirects to login before it ever consults templates. Use `/api/v1/repos/{owner}/{repo}/issue_templates`, which distinguishes the two today. ## Dependencies **None.** Each edge was checked rather than assumed, 2026-09-01T15:13Z: - **No collision edge is owed.** `.ceremony/TRIAGE.md` requires one only when an open `ready`, `claimed` or `blocked` issue already carries the deliverable. The board has two open issues — #36 (`enhancement`, `post-merge`, `scope:ci`) and #27 (`epic`, `needs-ruling`, `scope:cli`) — and **neither carries `ready`, `claimed` or `blocked`**; all three labels return zero at `state=all`. There is no open PR. - **No release-window membership call is owed.** The `release` label is carried only by closed items — #40, #32 and #1 — so no standing window is open; `v1.4.0` shipped 2026-08-31. - **Not a member of epic #27.** #27 organizes the five CLI gaps, and this is repository intake governance, not a CLI gap — the same call that correctly kept #48 out of it. - **Not a duplicate, and nothing to extend or reopen.** Searched `state=all` for `ISSUE_TEMPLATE`, `proposal`, `intake` and `template`: the only hits are #46 (closed — its deliverable was CONTRIBUTING.md's repo-specific facts, and it is the issue whose merge created this gap), #48 (closed — the scope map) and #36 (open — the ceremony re-pin, which records this gap in comment 30456 and explicitly places it out of scope). None of the three should absorb this. - **Related, not blocking:** heavy-duty/ceremony#276 (`HUMAN_REVIEWER` has no plumbing) is why this issue's Tasks carry a by-hand reviewer request. It gates nothing here.
claude-bot-andresmgsl added the
documentation
ready
scope:ci
scope:docs
labels 2026-09-01 15:15:29 +00:00

This issue and the issue named beside each key below are both open and
unblocked, and their titles name the same deliverable:

  • issue_template/ — also carried by #50

That owes a collision edge, and #288 makes it unconditional: a deliverable
already carried by an open ready, claimed or blocked issue owes
Blocked by #N on the newer issue, naming the newest open carrier, so each
close releases exactly one successor. Disjoint regions do not waive it —
ready must mean claimable concurrently with every other ready issue,
and an undeclared collision sends two builders at one deliverable.

The key is the title's em-dash prefix, normalized: one leading actions/,
lib/, bin/ or .github/ segment comes off, then every extension, and
a +-joined title matches on any segment. That is what the machine read,
never a judgment about what the deliverable is — if two spellings normalized
to one deliverable that is really two, say so and no edge is owed.

Comment only: nothing on this path writes a label or changes a state. The
marker carries the collision itself, so an unchanged one never re-posts.

<!-- issueflow:collision-issue-template-50-c5ddb8a2d72d --> This issue and the issue named beside each key below are both open and unblocked, and their titles name the same deliverable: - `issue_template/` — also carried by #50 That owes a **collision edge**, and #288 makes it unconditional: a deliverable already carried by an open `ready`, `claimed` or `blocked` issue owes `Blocked by #N` on the newer issue, naming the newest open carrier, so each close releases exactly one successor. Disjoint regions do not waive it — `ready` must mean claimable concurrently with every other `ready` issue, and an undeclared collision sends two builders at one deliverable. The key is the title's em-dash prefix, normalized: one leading `actions/`, `lib/`, `bin/` or `.github/` segment comes off, then every extension, and a `+`-joined title matches on any segment. That is what the machine read, never a judgment about what the deliverable is — if two spellings normalized to one deliverable that is really two, say so and no edge is owed. *Comment only: nothing on this path writes a label or changes a state. The marker carries the collision itself, so an unchanged one never re-posts.*
claude-bot-andresmgsl removed the
ready
label 2026-09-01 15:15:50 +00:00
Author
Member

Triage, 2026-09-01T15:15Z — closing this as a duplicate of #50. My error, not a board event.

I issued the create call twice in one command; #50 and #51 carry created_at 2026-09-01T15:15:29Z, the same second, with identical titles and bodies. #50 is the live issue — it is the lower number, it is what the body's own Refs #50 line names, and it is the one a builder should claim.

ready has been removed from this issue before closing. Every other label stays: this is a duplicate, not an epitaph, and leaving it claimable for even a moment would put two concurrently claimable issues on the board for one deliverable — exactly the collision .ceremony/TRIAGE.md requires a Blocked by edge to prevent.

Nothing was lost: no work, no comment and no claim ever attached here.

**Triage, 2026-09-01T15:15Z — closing this as a duplicate of #50. My error, not a board event.** I issued the create call twice in one command; #50 and #51 carry `created_at` `2026-09-01T15:15:29Z`, the same second, with identical titles and bodies. **#50 is the live issue** — it is the lower number, it is what the body's own `Refs #50` line names, and it is the one a builder should claim. `ready` has been removed from this issue before closing. Every other label stays: this is a duplicate, not an epitaph, and leaving it claimable for even a moment would put two concurrently claimable issues on the board for one deliverable — exactly the collision `.ceremony/TRIAGE.md` requires a `Blocked by` edge to prevent. Nothing was lost: no work, no comment and no claim ever attached here.
Author
Member

Triage, 2026-09-01T15:16Z — resolving the machine's collision flag above. It posted at 15:15:46Z, four seconds before my close comment, which was composed without having seen it; this answers it explicitly rather than leaving a machine flag unaddressed.

The flag is correct and needs no edge. It offers two resolutions — declare Blocked by #50, or say the two normalized spellings are really two deliverables. Neither applies, because there is only one issue's worth of work here: issue_template/ is one deliverable carried by two issues because I posted the create call twice, and this one is now closed. A Blocked by edge would encode a successor relationship between an issue and its own accidental copy.

The collision is gone, measured rather than asserted: sweep run 814 logged issueflow: #51: collision flag — issue_template/=50 at 15:15:46Z; run 815, after the close, logs no collision line at all. #50 is the sole open carrier and is ready.

Worth recording for whoever reads this thread later: this is the first time #288's collision detector has fired on this board, and it was right within seventeen seconds of the duplicate existing.

**Triage, 2026-09-01T15:16Z — resolving the machine's collision flag above.** It posted at 15:15:46Z, four seconds before my close comment, which was composed without having seen it; this answers it explicitly rather than leaving a machine flag unaddressed. **The flag is correct and needs no edge.** It offers two resolutions — declare `Blocked by #50`, or say the two normalized spellings are really two deliverables. Neither applies, because there is only one issue's worth of work here: `issue_template/` is one deliverable carried by two issues because I posted the create call twice, and this one is now closed. A `Blocked by` edge would encode a successor relationship between an issue and its own accidental copy. **The collision is gone, measured rather than asserted:** sweep run 814 logged `issueflow: #51: collision flag — issue_template/=50` at 15:15:46Z; run 815, after the close, logs no collision line at all. #50 is the sole open carrier and is `ready`. Worth recording for whoever reads this thread later: **this is the first time #288's collision detector has fired on this board**, and it was right within seventeen seconds of the duplicate existing.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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/stoke#51
No description provided.