Skip to content

tools(wildcard): apply the Wildcard Track's routing labels on every filing path - #711

Open
isHeSatoshi wants to merge 4 commits into
limitlessprojects:mainfrom
isHeSatoshi:tooling/wildcard-intake-labels
Open

isHeSatoshi wants to merge 4 commits into
limitlessprojects:mainfrom
isHeSatoshi:tooling/wildcard-intake-labels

Conversation

@isHeSatoshi

Copy link
Copy Markdown
Contributor

tools(wildcard): apply the Wildcard Track's routing labels on every filing path

BOUNTY.md Track 3 runs a separate $500/cycle pool, and MOONSHOT.md is its
ledger. The proposal form declares its own routing labels:

title: "[wildcard] "
labels: ["wildcard", "💎 Bounty"]

GitHub applies those labels only when an issue is created through the web
template chooser. The same template-shaped body filed through the API —
gh issue create --body-file, which is how a script or an agent files an
issue — skips the frontmatter and lands with no labels at all.

That is not cosmetic, because the label is the routing key into triage.
Measured on 2026-10-18 against the ledger in MOONSHOT.md:

in ledger not in ledger
labeled 10 5
unlabeled 0 12

Not one unlabeled proposal has ever reached the ledger, and several unlabeled
ones are older than labeled proposals that did. A proposal's chance of being
read should not depend on which client filed it.

What this adds

  • .github/workflows/wildcard-intake.yml — two jobs. On issues: opened
    it applies the form's labels; on workflow_dispatch it backfills every open
    proposal that is missing them. Neither job hardcodes a label or a title
    prefix: both read them from the form at runtime, so the workflow cannot
    drift from the template. That is also why the on-open job has no cheap
    YAML title gate — the predicate lives in the tool, not in a second copy.
  • tools/wildcard_intake.py — the single source of truth.
    declared_labels() and title_prefix() read the form; needs_labels() is
    the pure selection predicate shared by both jobs. Stdlib only, no YAML
    dependency.
  • tools/test_wildcard_intake.py — 17 tests, wired into ci.yml's quick
    job. A missing or malformed form is an error rather than an empty label
    list, because silently applying nothing would be this same bug in a new
    costume.

Why the predicate is tested rather than eyeballed

Both failure directions are silent. Over-eager matching labels a non-proposal
and misroutes triage; under-eager matching leaves a proposal unlabeled, which
is the bug being fixed. The tests pin the boundaries: prefix must match at the
start, pull requests are never selected, a partially-labeled proposal gets
only the label it lacks, a fully-labeled one gets nothing, and a labels: key
inside the form's body must not shadow the frontmatter.

Two things found while building it

  • The shell parsing is where this could have gone wrong, because one label
    contains a space and the line format uses both : and , as separators.
    Verified against a stub gh that records argv:

    GH-CALL: [issue] [edit] [565] [--repo] [owner/repo] [--add-label] [wildcard] [--add-label] [💎 Bounty]
    GH-CALL: [issue] [edit] [308] [--repo] [owner/repo] [--add-label] [💎 Bounty]
    

    The second line is the partial case: [wildcard] Replace task-boundary alias tracking with snapshot-isolated messages #308 already carries wildcard, so it
    gets only 💎 Bounty.

  • Feeding the selector non-UTF-8 data reports every labeled proposal as
    unlabeled.
    Piping gh issue list through a Windows console mangles 💎
    into ??, and the comparison then fails for the entire corpus. On
    ubuntu-latest the pipe is UTF-8 and this does not arise, but it is worth
    knowing before reading a surprising result as a real one.

Not fixed here

The workflow labels on opened and on manual dispatch, so the 12 already-filed
proposals need the backfill run once. I cannot apply labels myself (no triage
permission on this repository), which is the whole reason this exists as a
workflow rather than a one-time edit.

Refs #565.

@isHeSatoshi

Copy link
Copy Markdown
Contributor Author

Verified against live payloads, and one bug caught doing it

Re-ran both jobs' exact gh invocations against the real repository rather
than fixtures, which surfaced a defect in the first commit:

$ gh issue view 565 --repo ... --json number,title,labels \
    | python3 tools/wildcard_intake.py --select
usage error: stdin JSON must be a list of issues

gh issue view returns a single object; gh issue list returns a list.
--select required a list, so the on-open job would have failed the first time
a real issue arrived. The unit tests missed it because they fed it lists.

Fixed in 737d08a — --select now accepts either shape, so both jobs share one
tested code path instead of the on-open job needing its own untested one. Seven
new CLI-boundary tests cover both shapes, a non-wildcard object, empty stdin,
non-JSON, a bare scalar, and that the emoji survives the round trip (24 tests
total, all wired into ci.yml).

Confirmed after the fix, against the live issue:

$ gh issue view 565 --repo ... --json number,title,labels \
    | python3 tools/wildcard_intake.py --select
565:wildcard,💎 Bounty

What the backfill would do today

Run against live data, the selector finds 14 open proposals missing labels:
12 carrying neither (#710 #636 #585 #565 #544 #537 #482 #471 #458 #448 #428 #394) and 2 carrying wildcard but not 💎 Bounty (#308, #175).

One caveat if you reproduce this by hand: piping gh through a Windows console
mangles 💎 into ??, which makes every already-labeled proposal look
unlabeled and reports 37 instead of 14. On ubuntu-latest the pipe is UTF-8 and
this does not arise — worth knowing before reading a surprising count as real.

Still cannot be fixed from here

I have no triage permission on this repository (permissions: none), so I
cannot apply the labels to the 14 myself — which is why this is a workflow
rather than a one-time edit. Once merged, a single manual run of the backfill
closes the gap for all 14 and for every proposal filed through an API client
after that.

@isHeSatoshi

Copy link
Copy Markdown
Contributor Author

Update: the backfill no longer needs anyone to press a button

The first version made the backfill workflow_dispatch-only, which meant the 14
unlabeled proposals still depended on a maintainer noticing them. That is not a
fix — it is a fix-shaped dependency. Adding an hourly schedule trigger makes the
workflow self-healing: a proposal that arrives unlabeled on any path is corrected
within the hour, with no decision required from anyone.

It now runs three ways, and the two jobs cannot collide (if: event_name == 'issues' / != 'issues', plus a concurrency group):

trigger job
issues: opened labels that issue
schedule: 17 * * * * backfills every open proposal missing labels
workflow_dispatch same backfill, on demand

This does not change which proposals get read — the form already declares these
labels, so applying them is the template's own intent executed on the paths the
template cannot reach. It only stops a filing client from deciding visibility.

Evidence tightened

The claim in the first commit was weaker than it looked: "0 of 12 unlabeled
proposals reached the ledger" is fully explained by timing, because every
unlabeled proposal was filed after the first harvest. The second intake batch is
the real test — ledger commit 2026-10-17T20:24, with ten proposals available to
the same pass:

taken skipped
labeled #375 #378 #417 #462 —
unlabeled — #394 #428 #448 #458 #471 #482

#375 was hand-labeled at 20:23, one minute before that commit, and lands in it as
W-021. This still does not prove the label is the gate rather than a byproduct
of being read, and the header comment now says so explicitly instead of implying
more than the data supports.

Status

CI green on all three commits (quick + compiler), mergeable, 4 files. The only
remaining step is a merge — I have no write access to this repository, so I cannot
apply a label to a single issue myself.

Posted to the wildcard review thread
(discussions/365)
as well, since the participant list for the 2026-11-01 window is what this affects.

@isHeSatoshi
isHeSatoshi force-pushed the tooling/wildcard-intake-labels branch from 1aebb13 to 7c581ee Compare October 18, 2026 21:27
…iling path

`BOUNTY.md` Track 3 runs a separate $500/cycle pool, and `MOONSHOT.md` is its
ledger. The proposal form declares its own routing labels:

```yaml
title: "[wildcard] "
labels: ["wildcard", "💎 Bounty"]
```

GitHub applies those labels **only** when an issue is created through the web
template chooser. The same template-shaped body filed through the API —
`gh issue create --body-file`, which is how a script or an agent files an
issue — skips the frontmatter and lands with no labels at all.

That is not cosmetic, because the label is the routing key into triage.
Measured on 2026-10-18 against the ledger in `MOONSHOT.md`:

| | in ledger | not in ledger |
|---|---|---|
| **labeled** | 10 | 5 |
| **unlabeled** | **0** | **12** |

Not one unlabeled proposal has ever reached the ledger, and several unlabeled
ones are *older* than labeled proposals that did. A proposal's chance of being
read should not depend on which client filed it.

### What this adds

- **`.github/workflows/wildcard-intake.yml`** — two jobs. On `issues: opened`
  it applies the form's labels; on `workflow_dispatch` it backfills every open
  proposal that is missing them. Neither job hardcodes a label or a title
  prefix: both read them from the form at runtime, so the workflow cannot
  drift from the template. That is also why the on-open job has no cheap
  YAML title gate — the predicate lives in the tool, not in a second copy.
- **`tools/wildcard_intake.py`** — the single source of truth.
  `declared_labels()` and `title_prefix()` read the form; `needs_labels()` is
  the pure selection predicate shared by both jobs. Stdlib only, no YAML
  dependency.
- **`tools/test_wildcard_intake.py`** — 17 tests, wired into `ci.yml`'s quick
  job. A missing or malformed form is an error rather than an empty label
  list, because silently applying nothing would be this same bug in a new
  costume.

### Why the predicate is tested rather than eyeballed

Both failure directions are silent. Over-eager matching labels a non-proposal
and misroutes triage; under-eager matching leaves a proposal unlabeled, which
is the bug being fixed. The tests pin the boundaries: prefix must match at the
start, pull requests are never selected, a partially-labeled proposal gets
only the label it lacks, a fully-labeled one gets nothing, and a `labels:` key
inside the form's body must not shadow the frontmatter.

### Two things found while building it

- **The shell parsing is where this could have gone wrong**, because one label
  contains a space and the line format uses both `:` and `,` as separators.
  Verified against a stub `gh` that records argv:

  ```
  GH-CALL: [issue] [edit] [565] [--repo] [owner/repo] [--add-label] [wildcard] [--add-label] [💎 Bounty]
  GH-CALL: [issue] [edit] [308] [--repo] [owner/repo] [--add-label] [💎 Bounty]
  ```

  The second line is the partial case: limitlessprojects#308 already carries `wildcard`, so it
  gets only `💎 Bounty`.

- **Feeding the selector non-UTF-8 data reports every labeled proposal as
  unlabeled.** Piping `gh issue list` through a Windows console mangles `💎`
  into `??`, and the comparison then fails for the entire corpus. On
  `ubuntu-latest` the pipe is UTF-8 and this does not arise, but it is worth
  knowing before reading a surprising result as a real one.

### Not fixed here

The workflow labels on `opened` and on manual dispatch, so the 12 already-filed
proposals need the backfill run once. I cannot apply labels myself (no triage
permission on this repository), which is the whole reason this exists as a
workflow rather than a one-time edit.

Refs limitlessprojects#565.
The two workflow jobs pipe different shapes: the on-open job runs
`gh issue view --json ...`, which is ONE object, while the backfill runs
`gh issue list --json ...`, which is a LIST. The tool required a list, so
the on-open job would have died with

    usage error: stdin JSON must be a list of issues

the first time a real issue arrived -- the failure only shows up against a
live payload, not in the unit tests, which fed it lists.

Verified against the live issue: `gh issue view 565 --json ...` now yields
`565:wildcard,💎 Bounty`.

Seven tests cover the CLI boundary: both shapes, a non-wildcard object, empty
stdin, non-JSON, a bare scalar, and that the emoji survives the round trip.
…click

A fix that only works when someone remembers to press a button is not a fix.
The backfill was `workflow_dispatch` only, which left the 14 unlabeled
proposals dependent on a maintainer noticing them. Adding an hourly schedule
makes the workflow self-healing: an unlabeled proposal is corrected within the
hour, on every filing path, without anyone deciding to run it.

The form already declares these labels, so applying them is not a new decision
— it is the template's own intent executed on the paths the template cannot
reach. Nothing about which proposals get read changes; they just become
visible to whatever query reads them.

Also tightens the evidence in the header comment. The earlier "0 of 12
unlabeled reached the ledger" was true but proved nothing on its own, since
every unlabeled proposal was filed after the first harvest. The second intake
batch (ledger commit 2026-10-17T20:24) is the real test: ten proposals were
available to the same pass, four were taken (limitlessprojects#375 limitlessprojects#378 limitlessprojects#417 limitlessprojects#462 — all
labeled, limitlessprojects#375 hand-labeled at 20:23, one minute before the commit) and six
were skipped (limitlessprojects#394 limitlessprojects#428 limitlessprojects#448 limitlessprojects#458 limitlessprojects#471 limitlessprojects#482 — all unlabeled). Every proposal
ever processed carries the label; no unlabeled proposal has ever been
processed. The label may be a byproduct of processing rather than the gate,
but an unlabeled proposal is indistinguishable from an unprocessed one.

Both job bodies syntax-checked with `bash -n`. 24 unit tests pass.
@isHeSatoshi
isHeSatoshi force-pushed the tooling/wildcard-intake-labels branch from 7c581ee to 7f23965 Compare October 18, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant