claim-ticket.yml's claimant input describes itself as:
description: "Who is claiming — a session URL, branch name, or handle"
A session URL cannot be used. claim-ceremony.mjs — the half that produces the
human_authorization the same workflow now requires (#264) — sends the claimant
to the keeper, which validates it against [A-Za-z0-9/._-]+. That character
class has no :, so every URL is rejected.
Measured, 2026-09-04
CLAIM_REPO=.github-private CLAIM_ISSUE=908 \
CLAIMANT=https://claude.ai/code/session_019KLp3phY7enkTmVyU2BVDL \
KEEPER_URL=https://keeper.bounded.tools node claim-ceremony.mjs
Error: the keeper refused to open a ceremony:
invalid claim request: claimant: must match [A-Za-z0-9/._-]+
Re-run with a branch name (claude/next-cjuuu1) and the ceremony opened
normally; the claim landed human-authorized, assurance aal2.
Why this is worth a line of YAML
The failure is loud and the workaround is obvious once seen, so the cost is
small — but it is paid inside the ceremony window, which the door's own
description warns "may be as short as two minutes, so fetch the token
immediately before dispatching, not ahead of time". A session that takes the
description at its word burns a ceremony discovering it, and the retry starts a
fresh window. That happened here.
It is also the one input where a session has an obvious reason to reach for a
URL: a session URL is the most specific identifier a cloud session has for
itself, and it is what Claude-Session: trailers already carry.
.github-private's CLAUDE.md documents the constraint correctly for door 2
(claimant matching [A-Za-z0-9/._-]+); only claim-ticket.yml's own input
description contradicts it.
Two ways to close it
- Correct the description — drop "a session URL", say what the charset is.
One line, no behaviour change, and the door stops advertising a form it
refuses.
- Accept a URL by normalizing it claimant-side (e.g. strip the scheme) —
larger, touches the digest the passkey signs over, and the claimant string
is recomputed by the door from its own inputs, so both sides must agree.
Not obviously worth it.
(1) unless someone wants URLs to actually work, in which case (2) needs a
deliberate look at claim-digest.mjs.
Filed from a session that hit this on the primary door; not claimed.
claim-ticket.yml'sclaimantinput describes itself as:A session URL cannot be used.
claim-ceremony.mjs— the half that produces thehuman_authorizationthe same workflow now requires (#264) — sends the claimantto the keeper, which validates it against
[A-Za-z0-9/._-]+. That characterclass has no
:, so every URL is rejected.Measured, 2026-09-04
Re-run with a branch name (
claude/next-cjuuu1) and the ceremony openednormally; the claim landed
human-authorized, assuranceaal2.Why this is worth a line of YAML
The failure is loud and the workaround is obvious once seen, so the cost is
small — but it is paid inside the ceremony window, which the door's own
description warns "may be as short as two minutes, so fetch the token
immediately before dispatching, not ahead of time". A session that takes the
description at its word burns a ceremony discovering it, and the retry starts a
fresh window. That happened here.
It is also the one input where a session has an obvious reason to reach for a
URL: a session URL is the most specific identifier a cloud session has for
itself, and it is what
Claude-Session:trailers already carry..github-private's CLAUDE.md documents the constraint correctly for door 2(
claimantmatching[A-Za-z0-9/._-]+); onlyclaim-ticket.yml's own inputdescription contradicts it.
Two ways to close it
One line, no behaviour change, and the door stops advertising a form it
refuses.
larger, touches the digest the passkey signs over, and the claimant string
is recomputed by the door from its own inputs, so both sides must agree.
Not obviously worth it.
(1) unless someone wants URLs to actually work, in which case (2) needs a
deliberate look at
claim-digest.mjs.Filed from a session that hit this on the primary door; not claimed.