Checkpoint Lab — Issues, Labels, Milestones, Templates, Forms, Discussions, and Triage
Run a complete intake-and-triage checkpoint: install a small taxonomy and bug form, open and classify test issues, handle a duplicate with canonical linkage, verify queries, produce a triage runbook, and clean up disposable state.
Learning objectives
- Design and implement a compact issue taxonomy plus one reproducible bug form in a fresh disposable repository.
- Open several synthetic issues and triage each with explicit labels, milestone, assignee, state, and canonical linkage decisions.
- Predict at least two hosted/Git state changes before mutation and verify the prediction independently.
- Handle a duplicate without losing unique context and prove the canonical relationship.
- Produce a triage runbook covering intake criteria, response states, escalation, stale-work policy, and cleanup.
-
Verify the final queue through
ghand versioned REST queries, then clean up only the disposable resources.
1. Checkpoint mission and acceptance evidence
The deliverable is an evidence package, not screenshots of a busy issue list. At the end you should have: taxonomy definition, form/config commit OID, three or four issue numbers, milestone, assignee decisions, one duplicate-to-canonical relationship, structured query output, and a written triage runbook.
2. Preflight and collision guard
gh auth status --active --hostname github.com
OWNER=$(gh api user --jq .login)
REPO="atlas-c05-checkpoint"
gh repo view "$OWNER/$REPO" >/dev/null 2>&1 && {
echo "STOP: $OWNER/$REPO already exists" >&2
exit 2
}
printf 'owner=%s repo=%s
' "$OWNER" "$REPO"
Required role assumption: you own the disposable personal repository, so you can create labels/milestones, assign yourself, edit repository files, and delete the lab at the end. No organization/enterprise admin permission is assumed.
3. Make four predictions before changing state
- P1: committing the form files changes Git history; it does not itself create an Issue.
-
P2: creating an issue with the form creates
hosted issue state and auto-applies
kind:bug; it does not create a commit. - P3: marking one issue as a duplicate changes lifecycle/timeline state while preserving the issue record and canonical link.
- P4: deleting the disposable repository during cleanup removes hosted Issues/settings as well as the GitHub repository; therefore evidence must be captured first.
4. Create repository and taxonomy
gh repo create "$OWNER/$REPO" --public --add-readme --clone
cd "$REPO"
DEFAULT_BRANCH=$(git branch --show-current)
gh label create "kind:bug" --repo "$OWNER/$REPO" --description "Reproducible defect" --color D73A4A --force
gh label create "kind:docs" --repo "$OWNER/$REPO" --description "Documentation work" --color 0075CA --force
gh label create "priority:p1" --repo "$OWNER/$REPO" --description "Critical lab impact" --color B60205 --force
gh label create "priority:p2" --repo "$OWNER/$REPO" --description "Normal important work" --color FBCA04 --force
gh label create "status:needs-info" --repo "$OWNER/$REPO" --description "Waiting for evidence" --color D4C5F9 --force
gh label list --repo "$OWNER/$REPO" --limit 100 --json name,description --jq '.[]'
Taxonomy policy for this checkpoint: one kind label when known; at most one priority; temporary status labels only while the condition exists.
5. Create the checkpoint milestone and verify it
In the Issues UI create milestone v0.1 reliability with
description “Checkpoint fixes required for the first reliability
baseline.” This is deliberately one bounded goal.
MILESTONE_JSON=$(gh api -H "X-GitHub-Api-Version: 2026-03-10" "/repos/$OWNER/$REPO/milestones?state=all&per_page=100" --paginate --jq '.[] | select(.title=="v0.1 reliability")')
printf '%s
' "$MILESTONE_JSON"
test -n "$MILESTONE_JSON"
6. Install the bug form and chooser policy
# .github/ISSUE_TEMPLATE/bug.yml
name: Bug report
description: Report a reproducible Atlas checkpoint defect.
title: "[bug] "
labels: ["kind:bug"]
body:
- type: textarea
id: observed
attributes:
label: Observed behavior
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Steps to reproduce
description: Use synthetic, secret-free lab steps.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
validations:
required: true
- type: input
id: version
attributes:
label: Version
placeholder: 0.1.0
validations:
required: true
- type: checkboxes
id: safety
attributes:
label: Data safety
options:
- label: I removed credentials and sensitive data.
required: true
# .github/ISSUE_TEMPLATE/config.yml
blank_issues_enabled: false
contact_links:
- name: Questions
url: https://github.com/OWNER/REPO/discussions
about: Use Discussions if enabled; otherwise ask a maintainer where questions belong.
mkdir -p .github/ISSUE_TEMPLATE
# Save the two files above and replace OWNER/REPO with the actual checkpoint repo.
git add .github/ISSUE_TEMPLATE
git diff --cached --check
git commit -m "chore: define checkpoint issue intake"
git push origin HEAD
FORM_OID=$(git rev-parse HEAD)
printf 'form_oid=%s default_branch=%s
' "$FORM_OID" "$DEFAULT_BRANCH"
Verify P1:
gh issue list --repo "$OWNER/$REPO" --state all is
still empty immediately after the configuration commit.
7. Open canonical bug A through the form
- In the web UI choose the Bug report form.
- First leave one required field empty and confirm submission is blocked.
-
Submit synthetic data: observed “reload endpoint returns 503”;
reproduction “start 0.1.0 → reload → query /health”; expected
“health remains 200”; version
0.1.0. - Record the new issue number as
BUG_A.
Verify P2 and add triage metadata:
BUG_A=REPLACE_WITH_NUMBER
gh issue edit "$BUG_A" --repo "$OWNER/$REPO" --add-assignee "@me" --add-label "priority:p1" --milestone "v0.1 reliability"
gh issue view "$BUG_A" --repo "$OWNER/$REPO" --json number,title,state,labels,assignees,milestone,url --jq .
8. Open docs issue B through CLI and triage it
DOC_URL=$(gh issue create --repo "$OWNER/$REPO" --title "Docs: document health semantics" --body "Clarify expected health behavior during reload. Related to #$BUG_A." --label "kind:docs" --label "priority:p2" --assignee "@me" --milestone "v0.1 reliability")
DOC_B=${DOC_URL##*/}
gh issue view "$DOC_B" --repo "$OWNER/$REPO" --json number,title,state,labels,assignees,milestone,body,url --jq .
The body’s #$BUG_A creates a cross-reference when
rendered in the same repository. It documents relationship, not
dependency semantics.
9. Open needs-info issue C and show a temporary workflow label
INFO_URL=$(gh issue create --repo "$OWNER/$REPO" --title "Bug: intermittent timeout with no reproduction" --body "Synthetic report intentionally omits exact reproduction." --label "kind:bug" --label "status:needs-info")
INFO_C=${INFO_URL##*/}
gh issue comment "$INFO_C" --repo "$OWNER/$REPO" --body "Please provide version, minimal reproduction steps, and observed vs expected behavior. Do not paste secrets or private logs."
Do not assign P1 merely because “timeout” sounds serious. The issue lacks impact/reproduction evidence. The status label communicates the next required state transition.
10. Create duplicate D and preserve the canonical relationship
DUP_URL=$(gh issue create --repo "$OWNER/$REPO" --title "Reload endpoint is 503" --body "Duplicate checkpoint record; same synthetic reproduction as #$BUG_A.")
DUP_D=${DUP_URL##*/}
gh issue close "$DUP_D" --repo "$OWNER/$REPO" --duplicate-of "$BUG_A"
gh issue view "$DUP_D" --repo "$OWNER/$REPO" --json number,state,stateReason,comments,url --jq .
Verify P3: the duplicate issue still exists and is queryable, but it is closed with duplicate context pointing readers toward the canonical bug.
11. Verify the queue through multiple independent views
printf '%s
' '--- open issues by structured CLI ---'
gh issue list --repo "$OWNER/$REPO" --state open --limit 100 --json number,title,labels,assignees,milestone --jq '.[] | {number,title,labels:[.labels[].name],assignees:[.assignees[].login],milestone:(.milestone.title // null)}'
printf '%s
' '--- all ordinary issues via REST ---'
gh api -H "X-GitHub-Api-Version: 2026-03-10" "/repos/$OWNER/$REPO/issues?state=all&per_page=100" --paginate --jq '.[] | select(.pull_request == null) | {number,title,state,state_reason,labels:[.labels[].name]}'
printf '%s
' '--- milestone progress ---'
gh api -H "X-GitHub-Api-Version: 2026-03-10" "/repos/$OWNER/$REPO/milestones?state=all&per_page=100" --paginate --jq '.[] | {title,state,open_issues,closed_issues}'
Your expected queue: canonical Bug A open and owned; Docs B open and owned; needs-info C open without unjustified priority; duplicate D closed and linked. If the output differs, diagnose before mutating more metadata.
12. Produce the triage runbook
Write TRIAGE_RUNBOOK.md locally as your checkpoint
artifact. It does not need to be committed unless you want the
repository itself to demonstrate the runbook. Include:
- Intake criteria: what information makes a bug actionable and what must never be pasted publicly.
- Response states: open/actionable, needs-info, blocked, duplicate, completed/not planned.
- Priority criteria: observable impact and urgency; reporter adjectives are not sufficient.
- Ownership: when to assign and how to escalate when no owner exists.
- Discussion boundary: questions/ideas versus tracked work.
- Duplicate policy: preserve canonical link and unique evidence.
- Stale-work policy: time window, warning/comment, exemptions, human reopen path; no silent bot deletion.
- Moderation: when locking is appropriate and who may perform it.
- Cleanup: archive/close versus delete; destructive operations require explicit authorization.
13. Design the stale-work policy without deploying automation
For the checkpoint, do not install a stale bot. Write a policy such as: “After 30 days waiting for reporter information, add a reminder; after another 30 days, a human may close as not planned with a comment; security/incidents/release blockers are exempt; reopening is allowed when evidence arrives.” The exact timing is a team choice. The important controls are warning, exemptions, human override, and preserved history.
Chapter 13+ will show how Actions permissions/events affect implementation. This chapter proves the policy can stand on its own before code automates it.
14. Final verification checklist
-
The issue form/config files are on the default branch and
$FORM_OIDis recorded. - The bug form blocks a missing required response in the current web UI.
- Labels have non-overlapping meanings and the issue list demonstrates those meanings.
- The milestone groups only checkpoint work aimed at one bounded goal.
- Bug A, Docs B, and needs-info C are open with intentional metadata.
- Duplicate D remains accessible and points to the canonical issue.
-
gh issue listand REST output agree after accounting for state and PR filtering. - The runbook defines intake, ownership, escalation, duplicate, stale, moderation, and cleanup behavior.
- No real credential, customer data, private key, token, or sensitive log was posted.
15. Cleanup / rollback
Copy the final structured output and runbook out of the repository first. Then delete only the disposable checkpoint repository and local clone:
cd ..
printf 'about to delete: %s/%s
' "$OWNER" "$REPO"
gh repo delete "$OWNER/$REPO" --yes
rm -rf "$REPO"
Verify P4 by confirming gh repo view "$OWNER/$REPO" no
longer resolves after cleanup. Your external evidence/runbook should
remain.
16. What Chapter 05 adds to a production GitHub operating model
Chapters 01–04 established platform boundaries, credentials, repository design, and contribution topology. Chapter 05 adds the work-intake control plane: actionable records, classification, ownership, bounded goals, reproducible forms, routing to Discussions, duplicate preservation, and least-privilege triage. This makes the repository’s incoming work observable before the team introduces richer planning automation.
17. Bridge to Chapter 06: GitHub Projects
Issues now have disciplined semantics. Chapter 06 can therefore place them into Projects, roadmaps, custom fields, views, and automation without first trying to repair an incoherent intake queue. Projects should organize trustworthy work records—not compensate for missing triage policy.
Knowledge check
What two different state changes happen when you commit an issue form and later submit that form?
The commit changes Git history/configuration files; the submission creates a separate GitHub-hosted issue record.
Why is status:needs-info temporary?
It describes a current workflow condition, not the enduring kind of work. Remove it when the requested evidence arrives.
What proves duplicate handling succeeded?
The duplicate remains queryable/closed with a canonical relationship to the surviving issue, and unique context is not lost.
Why should the stale policy be written before adding a stale bot?
Automation should implement an explicit human policy with exemptions/recovery, not invent lifecycle decisions implicitly.
A REST issue count is higher than gh issue list.
What should you inspect first?
State/limit/pagination filters and whether the REST Issues collection included pull requests.
What is the bridge from this checkpoint to GitHub Projects?
Projects can now plan and visualize issues whose intake, taxonomy, ownership, and lifecycle semantics are already consistent.
Authoritative references
About issues
Configuring issue templates
Syntax for issue forms
Managing labels
About milestones
Marking duplicates
Repository roles
gh issue
REST API endpoints for issues
REST API endpoints for milestones
REST API versions
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.