.github/ISSUE_TEMPLATE/ — ship the proposal door CONTRIBUTING.md already promises #51
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:ci
scope:cli
scope:docs
scope:manifests
scope:packaging
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/stoke#51
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
CONTRIBUTING.mdL54says, in this repository's own voice:
There is no proposal to file. Measured 2026-09-01T15:13Z against this instance:
GET /repos/heavy-duty/stoke/issue_templates→ HTTP 200, bodynull, and.github/holds exactly two files,
labeler.ymlandlabels.conf. NoISSUE_TEMPLATE/, noconfig.yml, no chooser. A contributor who follows that sentence lands on a blankissue 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.mdL15-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.mdL34/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.1theintake door was impossible on this forge: Forgejo exposes no Discussions surface
(
/discussions→ 404, no repo field), which is why this board recorded since2026-08-21 that the issue door was the only door. The 2026-08-30 re-vendor to
0.6.3replaced discussion with a YAML issue form, and this Forgejo renders them —
GET /repos/heavy-duty/ceremony/issue_templates→ 200, servingProposal (anyone)(4 fields) andWork order (triage only)(7 fields), parsed, onthis 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.3ships exactly three files —
config.yml(714 B),proposal.yml(1204 B),work-order.yml(2754 B). Both forms are repository-agnostic: they name doctrinefiles (
TRIAGE.md,LABELS.md) that stoke vendors at.ceremony/, and no repository.Only
config.ymlcarries 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.mdand the repository beganasserting 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.
0.6.3's set — notproposal.ymlalone. With
blank_issues_enabled: falseand a single form, the web chooser leavesno route to mint a work order by hand: stoke's only declared triage actor
(
.github/labels.conftriage-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.proposal.ymlis copied byte-identical. It names no repository. Do not reword it.work-order.ymlis copied with exactly one adaptation. Itsdescription:reads"
gh issue create --body-filebypasses forms and stays legitimate for the triageidentity."
ghtargets github.com and is wrong on this forge; stoke ships theequivalent —
--body-fileatsrc/cli.jsL763,documented at
README.mdL515.Change that phrase to
stoke issue create --body-fileand nothing else. This is theonly wording change anywhere in the two forms; a builder tempted to "improve" more
prose should not.
config.ymlkeepsblank_issues_enabled: falseand re-points its one contactlink to
https://forgejo.heavyduty.builders/heavy-duty/stoke/issues/new?template=proposal.yml.The
name:andabout:text is repository-agnostic and is copied as-is.blank_issues_enabled: falsedoes not disable API issue creation. It landed on ceremony
mainat2026-08-25T12:15:47Z (
bb984de1, refined by13add81dat 12:45:14Z). Eightissues 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_at2026-08-31T20:27:37Z, thesame second, which no human web-form path produces. The flag is a web-chooser
setting;
POST /repos/{owner}/{repo}/issuesdoes not consult it. Stated at thestrength 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.
labels:by design (ceremony#24 D2: queue labels are triage's explicit act), verifiedfrom the live endpoint. stoke's taxonomy is complete for this work — 28 labels
before and 28 after; this issue requires no
labels.confrow and therefore nobootstrap=yesdispatch..github/labeler.ymlmaps.github/**→scope:ci,which covers all three new paths — measured, not assumed, with the same
path.matchesGlobthatscripts/check-governance.jsL115-L118uses: all three return
true.CONTRIBUTING.mdis covered by*.md→scope:docs..ceremony/byte-identity regime. They live in.github/, whichtest/governance.test.jsreads only forlabels.confandlabeler.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.
.github/ISSUE_TEMPLATE/, andCONTRIBUTING.mdis outside it. One line is folded inanyway: 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
.github/ISSUE_TEMPLATE/proposal.ymlas a byte-identical copy ofceremony
0.6.3(1204 B, including its leading three-line comment about applying no labels)
.github/ISSUE_TEMPLATE/work-order.ymlfromceremony
0.6.3with the single phrase change in spec decision 3, and no other edit
.github/ISSUE_TEMPLATE/config.ymlfromceremony
0.6.3with the contact-link URL re-pointed at stoke per spec decision 4
CONTRIBUTING.mdL54 so the sentence names the doorit promises
changelog.d/fragment — this is a contributor-facing change to how work isfiled, not internal plumbing
npm testandnpm run check:governancelocally before opening the PRheavy-duty/stoke, not a fork (fork PRsstall on the CI approval gate — see !28/!29)
state:needs-human, request@andresby hand — do notwait for the engine (heavy-duty/ceremony#276:
HUMAN_REVIEWERhas no plumbing, sothe sweep logs
HTTP 404 … requested_reviewersand still reports success)Refs #50, notCloses #50— the post-mergecriterion below outlives the merge, and this issue's checkboxes stay current while
it is open, because the derived transition comment publishes every unticked line
published, not private (see the note under Acceptance criteria)
Acceptance criteria
Pre-merge, reviewable on the PR:
.github/ISSUE_TEMPLATE/contains exactlyconfig.yml,proposal.ymlandwork-order.yml, and nothing elseproposal.ymlis byte-identical to ceremony0.6.3's, proven on the PR bycurl -sS "$API/repos/heavy-duty/ceremony/raw/.github/ISSUE_TEMPLATE/proposal.yml?ref=0.6.3" | md5summatching
md5sum .github/ISSUE_TEMPLATE/proposal.ymlwork-order.ymldiffers from ceremony0.6.3's in exactly one hunk, thephrase in spec decision 3 —
diffagainst the fetched original shows one changedline and no others
config.ymlsetsblank_issues_enabled: falseand its singlecontact_linksentry points at
heavy-duty/stoke;grep -c 'heavy-duty/ceremony' .github/ISSUE_TEMPLATE/config.ymlreturns
0CONTRIBUTING.mdL54's sentence links the proposal formnpm testandnpm run check:governanceare green on the PR head, withcheck:governancestill printinggovernance: 4 identities resolved; 5 scope rows valid—five rows, not six: no scope row is added by this work
ci / testgreen on the PR headscope:ciandscope:docs, derived by thelabels-scopejob fromthe base map — not hand-set. Read it off a
synchronizerun'slabels-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 thisissue to
post-merge, and triage owns the close:GET /repos/heavy-duty/stoke/issue_templatesreturns HTTP 200 with bothforms parsed —
Proposal (anyone)with 4 fields andWork order (triage only)with 7 — and each with an empty
labelsarray. Wake condition: the merge. Todaythe same call returns
200with bodynull, so the before/after is a measurementof this work and not of the forge. Do not substitute a fetch of
/heavy-duty/stoke/issues/new?template=proposal.ymlfor this — see the failingcase in the test plan; that URL cannot tell the two states apart
Test plan
The case that must fail — coverage is real, not incidental. Delete the
.github/**glob from
.github/labeler.ymland re-runnpm run check:governance: it must exit 1with
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:cirow 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:
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_enabledback totruein a freshreadyissue — 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 andGET /heavy-duty/stoke/issues/new?template=proposal.yml→ 303, identical, eventhough 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, whichdistinguishes the two today.
Dependencies
None. Each edge was checked rather than assumed, 2026-09-01T15:13Z:
.ceremony/TRIAGE.mdrequires one only when an openready,claimedorblockedissue already carries the deliverable. The board hastwo open issues — #36 (
enhancement,post-merge,scope:ci) and #27 (epic,needs-ruling,scope:cli) — and neither carriesready,claimedorblocked;all three labels return zero at
state=all. There is no open PR.releaselabel is carried only byclosed items — #40, #32 and #1 — so no standing window is open;
v1.4.0shipped2026-08-31.
intake governance, not a CLI gap — the same call that correctly kept #48 out of it.
state=allforISSUE_TEMPLATE,proposal,intakeandtemplate: 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.
HUMAN_REVIEWERhas no plumbing)is why this issue's Tasks carry a by-hand reviewer request. It gates nothing here.
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 #50That owes a collision edge, and #288 makes it unconditional: a deliverable
already carried by an open
ready,claimedorblockedissue owesBlocked by #Non the newer issue, naming the newest open carrier, so eachclose releases exactly one successor. Disjoint regions do not waive it —
readymust mean claimable concurrently with every otherreadyissue,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, anda
+-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.
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_at2026-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 ownRefs #50line names, and it is the one a builder should claim.readyhas 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.mdrequires aBlocked byedge to prevent.Nothing was lost: no work, no comment and no claim ever attached here.
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. ABlocked byedge 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/=50at 15:15:46Z; run 815, after the close, logs no collision line at all. #50 is the sole open carrier and isready.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.