Skip to content

claim-ticket: normalize a leading "bounded-systems/" on the repo input (silent rejection otherwise) #223

Description

@bdelanghe

Summary

Ergonomics, not a bug — the strict input is deliberate and should stay. But the way it fails is invisible to the caller, which costs a round trip in the one place the org most wants to be sure of.

claim-ticket.yml's preflight refuses anything path-shaped:

case "$REPO" in
  (*[!A-Za-z0-9._-]*|"") echo "::error::repo '$REPO' is not a plain repository name"; exit 1 ;;
esac

with the stated reason "Inputs land in URL paths below; refuse anything path-shaped rather than escaping it." That is the right call and this issue does not propose weakening it.

The problem is the feedback path. A workflow_dispatch returns 204 No Content and no run id — the file's own header says so, and tells callers to verify on the issue rather than the run list. So a caller who passes bounded-systems/claude-box instead of claude-box sees a successful-looking dispatch and gets no signal at all. The rejection is discoverable only by hunting the run list.

Measured

2026-08-18, claiming claude-box#236. Dispatched with repo: "bounded-systems/claude-box"204, "Workflow run has been queued". Run 32166423311 failed in preflight with repo 'bounded-systems/claude-box' is not a plain repository name. Re-dispatched with repo: "claude-box" → claim recorded, comment on the issue names the claimant.

The documented mitigation worked — checking the issue for the claim comment is what surfaced it, exactly as the header prescribes. So the fail-closed contract held. The cost was one wasted dispatch and the detour to find out why.

Why it is worth a small fix anyway

The scenario this file exists to prevent is two agents working one ticket. An autonomous caller that dispatches, sees 204, and starts work without verifying on the issue lands in precisely that state — believing it holds a claim it does not. The org convention already guards this ("confirm the claim comment on the issue names your claimant"), so the guard is a convention at the moment, and this would make one common miss impossible rather than merely documented.

bounded-systems/ is a fixed, known prefix, so stripping it is not the general path-escaping problem the refusal is about:

REPO="${REPO#bounded-systems/}"   # before the case, not instead of it

Everything else still gets refused, and the check keeps its shape.

Alternatively — or additionally — widen the input description ("Repository name in bounded-systems (e.g. infra)" is accurate but easy to read past) to say plainly that an owner/repo form is rejected.

Surfaced while working claude-box#236; filed rather than fixed, and left unclaimed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions