Chapter 05Lesson 05~175 minutes

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.

CheckpointTriage runbookQuery evidenceCleanup

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 gh and versioned REST queries, then clean up only the disposable resources.
Availability: GitHub.com + personal GitHub Free account + GitHub CLI + Git are sufficient. The repository is disposable and public. Issue forms are public preview. Discussions and organization issue types are optional and not required to pass the checkpoint.

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.

Fresh resources only. Do not reuse the Lesson 02 repository. The checkpoint has its own resource name and its own cleanup boundary.

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

  1. P1: committing the form files changes Git history; it does not itself create an Issue.
  2. P2: creating an issue with the form creates hosted issue state and auto-applies kind:bug; it does not create a commit.
  3. P3: marking one issue as a duplicate changes lifecycle/timeline state while preserving the issue record and canonical link.
  4. 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

  1. In the web UI choose the Bug report form.
  2. First leave one required field empty and confirm submission is blocked.
  3. Submit synthetic data: observed “reload endpoint returns 503”; reproduction “start 0.1.0 → reload → query /health”; expected “health remains 200”; version 0.1.0.
  4. 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_OID is 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 list and 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"
Destructive: repository deletion removes the Git repository plus hosted Issues, milestone, labels, form settings, and any optional Discussions. Confirm the exact owner/name. There is no reason to perform this against a valuable repository.

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?

Why is status:needs-info temporary?

What proves duplicate handling succeeded?

Why should the stale policy be written before adding a stale bot?

A REST issue count is higher than gh issue list. What should you inspect first?

What is the bridge from this checkpoint to GitHub Projects?

Next chapter

GitHub Projects, Roadmaps, Custom Fields, Views, Automation, and Planning: Concepts, Architecture, and Mental Model

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.