Create Issue
Create a GitHub issue with a structured body and curated labels. The body uses a fixed five-section structure (Context, What, Why, Scope, Solution). Titles are plain business descriptions — no Conventional Commits or prefix conventions. Labels are pulled from the repository and only labels that exist may be selected.
When to Use
- When filing a new GitHub issue for tracking work
- When invoked from other skills that need to open an issue
Input
Arguments: $ARGUMENTS
Expected form:
[title hint or short description] — optional free-form hint that seeds the title and body generation (e.g., "users cannot reset password via email").
Input resolution
- Title hint —
$ARGUMENTS → if empty, prompt once via AskUserQuestion: "What is this issue about?" with a free-form slot. Do not abort silently.
- Repository —
gh repo view --json nameWithOwner --jq .nameWithOwner. No prompt.
Completion Requirement
This workflow is not complete until Phase 8 executes gh issue create and outputs the issue URL. Generating a title, generating a body, or running label selection does not constitute completion. Execute all eight phases in sequence.
AskUserQuestion Contract (MANDATORY)
Every AskUserQuestion call that presents content for review (the issue preview in Phase 7) is governed by the shared contract: read askuserquestion-contract.md and apply it. In this skill the preview content is the issue title + body + labels, plus an optional duplicate-warning line at the top.
Phase 0: Resolve Repository and Hint
- Parse
$ARGUMENTS as an optional title hint. If empty, prompt the user via AskUserQuestion ("What is this issue about?") with a free-form slot.
- Resolve the repository:
gh repo view --json nameWithOwner --jq .nameWithOwner
Store as <repo> (format: owner/name).
- No preflight-check is invoked. Issue creation does not depend on git branch state — the user may file an issue from any branch including
main.
Phase 1: Gather Context
Ground the generated body in real code, not hallucinated structure.
Find files/symbols related to the hint (keywords from $ARGUMENTS) with Grep and Glob.
Read only the sections the search matched with targeted Read. Do NOT sweep the tree.
Also collect git context:
git log -20 --oneline
git status --short
These inform the Context and Why sections.
External documentation lookup (best-effort). Classify keywords from $ARGUMENTS plus the file/symbol names that grep matched in step 1, then call the matching MCP(s) below. Each call is best-effort: on error, timeout, or empty result, log a warning and continue with whatever was collected. Do NOT block issue creation on MCP availability.
- Library / framework / SDK / CLI named in the hint (e.g., React, Bun, Zod,
gh CLI, Prisma) → call mcp__context7__resolve-library-id then mcp__context7__query-docs with a task-relevant topic. Run multiple library lookups in parallel.
- Official documentation URL or technology name →
mcp__Ref__ref_search_documentation, then mcp__Ref__ref_read_url for specific pages from the results.
- Code-pattern / "how do projects do X" / migration examples →
mcp__exa__web_search_exa for API patterns, changelogs, migration guides, and real-world usage.
- Recency / news / "is X deprecated" / general web Q&A →
mcp__perplexity__perplexity_search for factual lookups; mcp__perplexity__perplexity_reason for trade-off / architectural reasoning.
Each MCP provides different information; use as many as the hint warrants. Feed the collected snippets into the body generated by Phase 5 (Context and Solution sections in particular).
Fallback: if any MCP server (context7, Ref, exa, perplexity) is unavailable or returns no results, continue with whatever the remaining sources produced. Do not block the skill on MCP availability — the generated body should still ship, just with less external context.
Phase 2: Fetch Available Labels
gh label list -R <repo> --limit 100 --json name,description,color
- On success with non-empty output: store the label list for Phase 6 (suggestion matching).
- On success with empty output (
[]): continue with no label suggestions. Phase 7 preview will show Labels: (none).
- On error (non-zero exit, network failure): log a warning and continue with no labels. Do not block the skill.
Phase 3: Find Related Issues and PRs
Search the repository for related work in both directions (open + closed) so the new issue can reference duplicates, prior art, and in-flight work.
- Extract 3-5 keyword phrases from
$ARGUMENTS + Phase 1 context. Example: for "users cannot reset password via email" → password reset, email reset, reset password.
- For each keyword phrase, run both:
gh issue list -R <repo> --search "<phrase>" --state all --limit 10 --json number,title,state,url,labels,updatedAt
gh pr list -R <repo> --search "<phrase>" --state all --limit 10 --json number,title,state,url,updatedAt
- Merge and deduplicate by
number. Rank by relevance (keyword match count + recency from updatedAt).
- Keep the top 5 results across issues+PRs combined. Categorise each as
[open], [closed], or [merged] (PRs).
- On error (non-zero exit, network failure): log a warning and continue with no related items. Do not block the skill.
- Pass the related items into Phase 5 (used in the body's Context section as a
Related: #N, #M line — magic-word free so it does NOT auto-close anything). The duplicate-detection check against the planned title runs in Phase 4 (after the title exists).
Phase 4: Generate Title
Rules:
- Capitalized first letter
- ≤ 80 characters total
- No trailing period
- Business-focused, understandable by someone on their first day
- NOT Conventional Commits format (no
feat:, fix:, chore:)
- NO prefix (no
[BUG], HOTFIX:, [FEATURE])
- Describes what needs to happen or the problem being solved
Examples:
| Hint |
Generated Title |
"users cannot reset password via email" |
Users cannot reset password via email |
"refactor token streaming pipeline" |
Refactor token streaming pipeline for backpressure |
"add release notes section to PR template" |
Add release notes section to pull request template |
"audio drops every time multiple clients connect" |
Audio playback drops when multiple clients connect |
Duplicate-detection check (after the title is generated):
For each open item returned by Phase 3, compute the keyword-overlap ratio against the generated title:
- Tokenize both strings into lowercase keywords, drop English stop words (
a, the, for, to, of, in, on, etc.).
- Empty-set guard (apply BEFORE the division):
- If
titleKeywords is empty AND candidateKeywords is empty → overlap = 1.0 (both strings are stop-word-only; treat as identical).
- If exactly one of the two sets is empty →
overlap = 0 (no meaningful overlap; one side has nothing to match against).
- Otherwise:
overlap = |titleKeywords ∩ candidateKeywords| / min(|titleKeywords|, |candidateKeywords|).
- If
overlap > 0.8 for any open item, set possibleDuplicate to that item (the highest-scoring one wins on ties). Phase 7 will surface a warning line so the user can cancel and comment on the existing issue instead.
- Closed and merged items are not duplicate candidates (they only feed the
Related: line); only open items can trigger the warning.
Phase 5: Generate Body
Read issue-body-grammar.md and apply it — it defines the five-section structure, the per-section rules, and the linkability pass that runs after drafting.
Caller-specific wiring:
- The related items from Phase 3 feed the Context section's
Related: line.
- Linkability-pass links must use the absolute
<repo-blob-url> form — the body is posted outside the repo, where relative paths do not resolve.
Phase 6: Suggest Labels
Match Phase 2's label list against Phase 4 title + Phase 5 body keywords.
- Score each fetched label by: (a) presence of label name/description keywords in title (weight 2), (b) presence in body (weight 1).
- Select the top 0-3 matches.
- Validation: only labels present in the Phase 2 fetched set may be selected. NEVER invent a label name —
gh issue create --label nonexistent will fail.
- If no label scores > 0, select none and proceed with
Labels: (none).
Phase 7: Verify with User
Present the full issue using AskUserQuestion with preview. See the AskUserQuestion Contract above — all rules are mandatory.
Compose the full preview string:
AskUserQuestion parameters:
question: "Review the issue details and choose an action."
header: "Create issue"
options:[
{ label: "Create issue", description: "Create this GitHub issue", preview: "<full preview>" },
{ label: "Edit content", description: "Modify title, body, or labels", preview: "<full preview>" },
{ label: "Cancel", description: "Abort issue creation", preview: "<full preview>" }
]
multiSelect: false
All three options use the same preview content since the user is choosing an action, not content.
If user selects "Edit content": ask what to change (title / body section / labels), regenerate that part, re-present via AskUserQuestion.
If user selects "Cancel":
- If a
possibleDuplicate was surfaced, output: Issue creation cancelled. Consider commenting on #<duplicate-number> instead.
- Otherwise:
Issue creation cancelled.
- Abort.
Only proceed to Phase 8 after the user selects "Create issue".
Phase 8: Create Issue
This phase is mandatory. The skill is complete only after the issue URL is printed.
Execute via stdin so body content (which may contain backticks, $(), ASCII diagrams, quotes) is preserved exactly:
printf '%s' "<body>" | gh issue create \
--repo <owner/repo> \
--title "<title>" \
--body-file - \
--label "<label1>" --label "<label2>"
Rules:
- Pass
--repo <owner/repo> explicitly (resolved in Phase 0). Do not rely on cwd — this matters in worktrees.
- Use
--body-file - to read the body from stdin via printf '%s'. Avoids shell expansion of backticks and $(...) in the body.
- Repeat
--label once per label. Do NOT comma-join — label names may contain commas.
- If no labels were selected, omit the
--label flags entirely.
- The URL is the last line of
gh issue create stdout. Capture it.
Output the result:
✓ Created issue: <url>
Examples
Solution with ASCII diagram via ascii-schemas
/autopilot:issue-create "refactor token streaming pipeline"
Phase 5 detects that the Solution describes a flow between ≥ 2 components and invokes Skill(autopilot:ascii-schemas) to draw the new pipeline.
Phase 7 AskUserQuestion parameters:
question: "Review the issue details and choose an action."
header: "Create issue"
options: Create issue / Edit content / Cancel, with the descriptions listed in Phase 7
multiSelect: false
Preview (every option carries this same full preview string):
Refactor token streaming pipeline for backpressure
## Context
The current token streaming pipeline buffers an entire response before flushing to the client. Long completions exhaust the server-side buffer and back up upstream LLM calls.
## What
Convert the pipeline to a streaming model with explicit backpressure between the model adapter, the codec, and the SSE writer.
## Why
Long-form completions today block other in-flight requests, raising p99 latency for unrelated calls. Backpressure unblocks parallelism without raising memory.
## Scope
- **In scope:**
- Streaming model adapter → codec interface
- Codec → SSE writer with credit-based backpressure
- Integration test covering > 100k token responses
- **Out of scope:**
- Replacing SSE with WebSocket (separate proposal)
## Solution
Introduce a typed `TokenStream` reader/writer pair between each pipeline stage. Each stage applies credit-based backpressure: a downstream consumer signals `n` credits, the upstream producer sends at most `n` tokens before waiting.
\`\`\`text
┌─────────────┐ tokens ┌──────────┐ credits ┌─────────────┐
│ ModelAdapter│ ──────────▶ │ Codec │ ──────────▶ │ SseWriter │
│ │ ◀────────── │ │ ◀────────── │ │
└─────────────┘ credits └──────────┘ credits └─────────────┘
\`\`\`
Labels: refactor, performance
User selects "Create issue".
✓ Created issue: https://github.com/org/repo/issues/144
Further worked examples: read references/examples.md when a call site is ambiguous.
When you generate the issue body, apply the reference-formatting rules in reference-formatting.md (RFC-0001, read it first) to every reference it contains — link files, docs, skills, agents, sections, and commit SHAs as absolute <repo-blob-url> URLs (the body is posted outside the repo, where relative paths do not resolve), link cited external resources to their canonical source URL, and never leave a reference as bare text.
1---2name: issue-create3description: Create a GitHub issue with a structured body (Context, What, Why, Scope, Solution) and curated labels via the gh CLI. Use when filing new issues, or when invoked from other skills.4---56# Create Issue78Create a GitHub issue with a structured body and curated labels. The body uses a fixed five-section structure (Context, What, Why, Scope, Solution). Titles are plain business descriptions — no Conventional Commits or prefix conventions. Labels are pulled from the repository and only labels that exist may be selected.910## When to Use1112- When filing a new GitHub issue for tracking work13- When invoked from other skills that need to open an issue1415## Input1617Arguments: `$ARGUMENTS`1819Expected form:2021- `[title hint or short description]` — optional free-form hint that seeds the title and body generation (e.g., `"users cannot reset password via email"`).2223## Input resolution2425- **Title hint** — `$ARGUMENTS` → if empty, prompt once via AskUserQuestion: "What is this issue about?" with a free-form slot. Do not abort silently.26- **Repository** — `gh repo view --json nameWithOwner --jq .nameWithOwner`. No prompt.2728## Completion Requirement2930This workflow is not complete until [Phase 8](#phase-8-create-issue) executes `gh issue create` and outputs the issue URL. Generating a title, generating a body, or running label selection does not constitute completion. Execute all eight phases in sequence.3132## AskUserQuestion Contract (MANDATORY)3334Every AskUserQuestion call that presents content for review (the issue preview in [Phase 7](#phase-7-verify-with-user)) is governed by the shared contract: read [`askuserquestion-contract.md`](../shared-rules/references/askuserquestion-contract.md) and apply it. In this skill the preview content is the issue title + body + labels, plus an optional duplicate-warning line at the top.3536## Phase 0: Resolve Repository and Hint37381. Parse `$ARGUMENTS` as an optional title hint. If empty, prompt the user via AskUserQuestion ("What is this issue about?") with a free-form slot.392. Resolve the repository:40 ```bash41 gh repo view --json nameWithOwner --jq .nameWithOwner42 ```43 Store as `<repo>` (format: `owner/name`).443. **No preflight-check is invoked.** Issue creation does not depend on git branch state — the user may file an issue from any branch including `main`.4546## Phase 1: Gather Context4748Ground the generated body in real code, not hallucinated structure.49501. Find files/symbols related to the hint (keywords from `$ARGUMENTS`) with `Grep` and `Glob`.512. Read only the sections the search matched with targeted `Read`. Do NOT sweep the tree.523. Also collect git context:53 ```bash54 git log -20 --oneline55 git status --short56 ```57 These inform the Context and Why sections.584. **External documentation lookup (best-effort).** Classify keywords from `$ARGUMENTS` plus the file/symbol names that grep matched in step 1, then call the matching MCP(s) below. Each call is best-effort: on error, timeout, or empty result, log a warning and continue with whatever was collected. Do NOT block issue creation on MCP availability.59 - **Library / framework / SDK / CLI named in the hint** (e.g., React, Bun, Zod, `gh` CLI, Prisma) → call `mcp__context7__resolve-library-id` then `mcp__context7__query-docs` with a task-relevant topic. Run multiple library lookups in parallel.60 - **Official documentation URL or technology name** → `mcp__Ref__ref_search_documentation`, then `mcp__Ref__ref_read_url` for specific pages from the results.61 - **Code-pattern / "how do projects do X" / migration examples** → `mcp__exa__web_search_exa` for API patterns, changelogs, migration guides, and real-world usage.62 - **Recency / news / "is X deprecated" / general web Q&A** → `mcp__perplexity__perplexity_search` for factual lookups; `mcp__perplexity__perplexity_reason` for trade-off / architectural reasoning.6364 Each MCP provides different information; use as many as the hint warrants. Feed the collected snippets into the body generated by [Phase 5](#phase-5-generate-body) (Context and Solution sections in particular).6566**Fallback:** if any MCP server (context7, Ref, exa, perplexity) is unavailable or returns no results, continue with whatever the remaining sources produced. Do not block the skill on MCP availability — the generated body should still ship, just with less external context.6768## Phase 2: Fetch Available Labels6970```bash71gh label list -R <repo> --limit 100 --json name,description,color72```7374- On success with non-empty output: store the label list for [Phase 6](#phase-6-suggest-labels) (suggestion matching).75- On success with empty output (`[]`): continue with no label suggestions. [Phase 7](#phase-7-verify-with-user) preview will show `Labels: (none)`.76- On error (non-zero exit, network failure): log a warning and continue with no labels. Do not block the skill.7778## Phase 3: Find Related Issues and PRs7980Search the repository for related work in both directions (open + closed) so the new issue can reference duplicates, prior art, and in-flight work.81821. Extract 3-5 keyword phrases from `$ARGUMENTS` + [Phase 1](#phase-1-gather-context) context. Example: for `"users cannot reset password via email"` → `password reset`, `email reset`, `reset password`.832. For each keyword phrase, run both:84 ```bash85 gh issue list -R <repo> --search "<phrase>" --state all --limit 10 --json number,title,state,url,labels,updatedAt86 gh pr list -R <repo> --search "<phrase>" --state all --limit 10 --json number,title,state,url,updatedAt87 ```883. Merge and deduplicate by `number`. Rank by relevance (keyword match count + recency from `updatedAt`).894. Keep the top 5 results across issues+PRs combined. Categorise each as `[open]`, `[closed]`, or `[merged]` (PRs).905. On error (non-zero exit, network failure): log a warning and continue with no related items. Do not block the skill.916. Pass the related items into [Phase 5](#phase-5-generate-body) (used in the body's Context section as a `Related: #N, #M` line — magic-word free so it does NOT auto-close anything). The duplicate-detection check against the planned title runs in [Phase 4](#phase-4-generate-title) (after the title exists).9293## Phase 4: Generate Title9495**Rules:**9697- Capitalized first letter98- ≤ 80 characters total99- No trailing period100- Business-focused, understandable by someone on their first day101- **NOT** Conventional Commits format (no `feat:`, `fix:`, `chore:`)102- **NO** prefix (no `[BUG]`, `HOTFIX:`, `[FEATURE]`)103- Describes what needs to happen or the problem being solved104105**Examples:**106107| Hint | Generated Title |108| --------------------------------------------------- | ---------------------------------------------------- |109| `"users cannot reset password via email"` | `Users cannot reset password via email` |110| `"refactor token streaming pipeline"` | `Refactor token streaming pipeline for backpressure` |111| `"add release notes section to PR template"` | `Add release notes section to pull request template` |112| `"audio drops every time multiple clients connect"` | `Audio playback drops when multiple clients connect` |113114**Duplicate-detection check (after the title is generated):**115116For each open item returned by [Phase 3](#phase-3-find-related-issues-and-prs), compute the keyword-overlap ratio against the generated title:117118- Tokenize both strings into lowercase keywords, drop English stop words (`a`, `the`, `for`, `to`, `of`, `in`, `on`, etc.).119- Empty-set guard (apply BEFORE the division):120 - If `titleKeywords` is empty AND `candidateKeywords` is empty → `overlap = 1.0` (both strings are stop-word-only; treat as identical).121 - If exactly one of the two sets is empty → `overlap = 0` (no meaningful overlap; one side has nothing to match against).122- Otherwise: `overlap = |titleKeywords ∩ candidateKeywords| / min(|titleKeywords|, |candidateKeywords|)`.123- If `overlap > 0.8` for any open item, set `possibleDuplicate` to that item (the highest-scoring one wins on ties). [Phase 7](#phase-7-verify-with-user) will surface a warning line so the user can cancel and comment on the existing issue instead.124- Closed and merged items are not duplicate candidates (they only feed the `Related:` line); only open items can trigger the warning.125126## Phase 5: Generate Body127128Read [`issue-body-grammar.md`](../shared-rules/references/issue-body-grammar.md) and apply it — it defines the five-section structure, the per-section rules, and the linkability pass that runs after drafting.129130Caller-specific wiring:131132- The related items from [Phase 3](#phase-3-find-related-issues-and-prs) feed the Context section's `Related:` line.133- Linkability-pass links must use the absolute `<repo-blob-url>` form — the body is posted outside the repo, where relative paths do not resolve.134135## Phase 6: Suggest Labels136137Match [Phase 2](#phase-2-fetch-available-labels)'s label list against [Phase 4](#phase-4-generate-title) title + [Phase 5](#phase-5-generate-body) body keywords.1381391. Score each fetched label by: (a) presence of label name/description keywords in title (weight 2), (b) presence in body (weight 1).1402. Select the top 0-3 matches.1413. **Validation:** only labels present in the [Phase 2](#phase-2-fetch-available-labels) fetched set may be selected. NEVER invent a label name — `gh issue create --label nonexistent` will fail.1424. If no label scores > 0, select none and proceed with `Labels: (none)`.143144## Phase 7: Verify with User145146Present the full issue using AskUserQuestion with preview. See the AskUserQuestion Contract above — all rules are mandatory.1471481. Compose the full preview string:149 - If [Phase 3](#phase-3-find-related-issues-and-prs) flagged a `possibleDuplicate`, the FIRST line is:150 ```151 Possible duplicates: #123 (<title of duplicate>), #456 (<title>)152 ```153 followed by a blank line.154 - Then the title line.155 - Blank line.156 - The five-section body (literal newlines, no escaping).157 - Blank line.158 - `Labels: label1, label2` (or `Labels: (none)`).1591602. AskUserQuestion parameters:161 - `question`: "Review the issue details and choose an action."162 - `header`: "Create issue"163 - `options`:164 ```165 [166 { label: "Create issue", description: "Create this GitHub issue", preview: "<full preview>" },167 { label: "Edit content", description: "Modify title, body, or labels", preview: "<full preview>" },168 { label: "Cancel", description: "Abort issue creation", preview: "<full preview>" }169 ]170 ```171 - `multiSelect`: false172173 All three options use the same `preview` content since the user is choosing an action, not content.1741753. If user selects "Edit content": ask what to change (title / body section / labels), regenerate that part, re-present via AskUserQuestion.1761774. If user selects "Cancel":178 - If a `possibleDuplicate` was surfaced, output: `Issue creation cancelled. Consider commenting on #<duplicate-number> instead.`179 - Otherwise: `Issue creation cancelled.`180 - Abort.1811825. Only proceed to [Phase 8](#phase-8-create-issue) after the user selects "Create issue".183184## Phase 8: Create Issue185186This phase is mandatory. The skill is complete only after the issue URL is printed.187188Execute via stdin so body content (which may contain backticks, `$()`, ASCII diagrams, quotes) is preserved exactly:189190```bash191printf '%s' "<body>" | gh issue create \192 --repo <owner/repo> \193 --title "<title>" \194 --body-file - \195 --label "<label1>" --label "<label2>"196```197198**Rules:**199200- Pass `--repo <owner/repo>` explicitly (resolved in [Phase 0](#phase-0-resolve-repository-and-hint)). Do not rely on cwd — this matters in worktrees.201- Use `--body-file -` to read the body from stdin via `printf '%s'`. Avoids shell expansion of backticks and `$(...)` in the body.202- Repeat `--label` once per label. Do NOT comma-join — label names may contain commas.203- If no labels were selected, omit the `--label` flags entirely.204- The URL is the last line of `gh issue create` stdout. Capture it.205206Output the result:207208```209✓ Created issue: <url>210```211212## Examples213214### Solution with ASCII diagram via ascii-schemas215216```217/autopilot:issue-create "refactor token streaming pipeline"218```219220[Phase 5](#phase-5-generate-body) detects that the Solution describes a flow between ≥ 2 components and invokes `Skill(autopilot:ascii-schemas)` to draw the new pipeline.221222[Phase 7](#phase-7-verify-with-user) AskUserQuestion parameters:223224- `question`: "Review the issue details and choose an action."225- `header`: "Create issue"226- `options`: `Create issue` / `Edit content` / `Cancel`, with the descriptions listed in [Phase 7](#phase-7-verify-with-user)227- `multiSelect`: false228229Preview (every option carries this same full preview string):230231```232Refactor token streaming pipeline for backpressure233234## Context235236The current token streaming pipeline buffers an entire response before flushing to the client. Long completions exhaust the server-side buffer and back up upstream LLM calls.237238## What239240Convert the pipeline to a streaming model with explicit backpressure between the model adapter, the codec, and the SSE writer.241242## Why243244Long-form completions today block other in-flight requests, raising p99 latency for unrelated calls. Backpressure unblocks parallelism without raising memory.245246## Scope247248- **In scope:**249 - Streaming model adapter → codec interface250 - Codec → SSE writer with credit-based backpressure251 - Integration test covering > 100k token responses252- **Out of scope:**253 - Replacing SSE with WebSocket (separate proposal)254255## Solution256257Introduce a typed `TokenStream` reader/writer pair between each pipeline stage. Each stage applies credit-based backpressure: a downstream consumer signals `n` credits, the upstream producer sends at most `n` tokens before waiting.258259\`\`\`text260┌─────────────┐ tokens ┌──────────┐ credits ┌─────────────┐261│ ModelAdapter│ ──────────▶ │ Codec │ ──────────▶ │ SseWriter │262│ │ ◀────────── │ │ ◀────────── │ │263└─────────────┘ credits └──────────┘ credits └─────────────┘264\`\`\`265266Labels: refactor, performance267```268269User selects "Create issue".270271```272✓ Created issue: https://github.com/org/repo/issues/144273```274275Further worked examples: read [references/examples.md](./references/examples.md) when a call site is ambiguous.276277When you generate the issue body, apply the reference-formatting rules in [`reference-formatting.md`](../shared-rules/references/reference-formatting.md) (RFC-0001, read it first) to every reference it contains — link files, docs, skills, agents, sections, and commit SHAs as absolute `<repo-blob-url>` URLs (the body is posted outside the repo, where relative paths do not resolve), link cited external resources to their canonical source URL, and never leave a reference as bare text.