Checkpoint Lab — Issues, Labels, Milestones, Iterations, Boards, Epics, and Work Planning
Plan and execute a miniature delivery slice from issue to branch and merge request, prove that planning views reflect underlying metadata, collect evidence, and clean up synthetic work safely.
Learning objectives
- Define a lightweight planning policy before creating work and record Free/tier/version assumptions explicitly.
- Create a miniature delivery slice with issues, labels, a milestone, a board, a branch, and a merge request while predicting key state transitions first.
- Prove from independent surfaces that board columns reflect underlying work-item metadata and that merge-request closing behavior depends on the default-branch event.
- Produce a compact evidence packet that distinguishes observed GitLab state from paid-feature simulations and does not contain credentials or unnecessary private content.
- Clean up synthetic work safely and state how Chapter 05 strengthens the production GitLab operating model before moving to collaboration utilities in Chapter 06.
1. Checkpoint mission and safety preflight
You are the maintainer of a disposable project preparing a tiny “CH05 Demo” delivery slice. Your job is not to build a feature-rich planning system. Your job is to prove the invariants behind one: metadata has defined meaning, board views reflect that metadata, Git objects and planning records remain distinct, and work-to-MR relationships behave as predicted.
- Offering/tier: GitLab.com Free or equivalent Free Self-Managed/Dedicated access where the documented Free features are available.
- Role: enough project permission to create/edit issues, labels, milestones, board configuration, branches, and a draft MR in a disposable project you control.
-
Tools: Git,
glab, andjq. - Not required: runner/CI minutes, Premium/Ultimate, Dedicated tenancy, Self-Managed admin, cloud, Kubernetes, AI/Duo, registry, or second account.
-
Safety: all titles use
[CH05 LAB]; no real backlog, secrets, customer data, or production repository.
2. Write the policy before the first issue
Record this contract in a local ch05-policy.md file or
your lab notes:
# CH05 planning policy
- type labels: exactly one of type-bug | type-docs | type-feature (team convention)
- flow labels: at most one of flow-ready | flow-doing (team convention)
- delivery goal: one project milestone named CH05 Demo
- board: projects flow labels; it does not own separate work state
- ownership: assignee means current accountable person
- closure: only a deliberate closing pattern reaching the default branch may auto-close
- paid fields: scoped labels, iteration, status, epic are optional/fixture only on Free
Prediction 1: moving an issue from the Ready label list to Doing will replace the team-convention flow label in the source issue (assuming the board is configured with those label lists).
Prediction 2: creating a draft MR containing
Closes #IID will not immediately close the issue; the
issue should remain open until a qualifying merge/commit reaches the
project default branch.
3. Capture target and current state
glab auth status
glab repo view --output json --jq '{path_with_namespace,visibility,default_branch,web_url}' | tee ch05-project-before.json
git status --short --branch
git remote -v
glab issue list --all --output json --jq '[.[] | select(.title | startswith("[CH05 LAB]")) | {iid,title,state}]'
If synthetic items from a prior attempt exist, reuse or clean them deliberately instead of creating duplicates. Record the current default branch; the checkpoint later compares it with the MR target.
4. Create the planning resources
Use the same metadata policy as Lesson 2. Inspect before each create in a real run; the commands below show the intended mutations.
PROJECT_ID="123456" # disposable project numeric ID
REPO="group/example-project" # disposable full path
# Labels (create only when absent).
for spec in 'type-bug|#D9534F|CH05 lab defect' 'type-docs|#428BCA|CH05 lab docs' 'type-feature|#5CB85C|CH05 lab feature' 'flow-ready|#F0AD4E|CH05 lab ready' 'flow-doing|#8E44AD|CH05 lab active'; do
IFS='|' read -r name color desc <<EOF
$spec
EOF
if ! glab api "projects/$PROJECT_ID/labels?search=$name" --jq '.[].name' | grep -Fxq "$name"; then
glab api "projects/$PROJECT_ID/labels" -X POST -f name="$name" -f color="$color" -f description="$desc" >/dev/null
fi
done
# Milestone (create only when absent).
MILESTONE_ID="$(glab api "projects/$PROJECT_ID/milestones?state=active&search=CH05%20Demo" --jq '.[] | select(.title=="CH05 Demo") | .id' | head -n1)"
if [ -z "$MILESTONE_ID" ]; then
MILESTONE_ID="$(glab api "projects/$PROJECT_ID/milestones" -X POST -f title='CH05 Demo' -f description='Disposable Chapter 05 milestone' --jq '.id')"
fi
printf 'milestone_id=%s
' "$MILESTONE_ID"
# Three synthetic issues.
glab issue create -R "$REPO" -t '[CH05 LAB] Fix parser edge case' -d 'Synthetic checkpoint defect.' -l type-bug,flow-ready -m 'CH05 Demo' --yes
glab issue create -R "$REPO" -t '[CH05 LAB] Add operator note' -d 'Synthetic checkpoint docs.' -l type-docs,flow-ready -m 'CH05 Demo' --yes
glab issue create -R "$REPO" -t '[CH05 LAB] Add dry-run option' -d 'Synthetic checkpoint feature.' -l type-feature,flow-ready -m 'CH05 Demo' --yes
# Capture the three IIDs.
glab api "projects/$PROJECT_ID/issues?scope=all&state=opened&per_page=100" --paginate --jq '.[] | select(.title | startswith("[CH05 LAB]")) | {iid,title,labels,milestone:(.milestone.title // null)}' | tee ch05-issues-created.ndjson
5. Build the board and prove Prediction 1
Create CH05 Flow if needed and add label lists for
flow-ready and flow-doing. Use the current
issue-board UI or Boards API. Then move only the parser issue from
Ready to Doing.
# Current glab supports creating a project issue board.
glab issue board create 'CH05 Flow' -R "$REPO"
# Read board structure; identify the intended board/list types.
glab api "projects/$PROJECT_ID/boards" --paginate --jq '.[] | {id,name,lists:[.lists[]? | {id,label:(.label.name // null)}]}' | tee ch05-boards.json
# After the drag in the UI, verify the source issue independently.
PARSER_IID="1" # replace with actual IID
glab api "projects/$PROJECT_ID/issues/$PARSER_IID" --jq '{iid,title,state,labels,milestone:(.milestone.title // null)}' | tee ch05-parser-after-board.json
Pass condition: the issue contains
flow-doing and no longer carries the lab’s
flow-ready workflow label. If both remain, inspect the
board configuration and remember that Free ordinary labels rely on
team convention rather than scoped-label exclusivity.
6. Create the branch/MR and prove Prediction 2
Use a harmless documentation change. This proves traceability without introducing CI or deployment complexity.
DEFAULT_BRANCH="$(glab repo view -R "$REPO" --output json --jq '.default_branch')"
BRANCH="$PARSER_IID-ch05-checkpoint"
git switch "$DEFAULT_BRANCH"
git pull --ff-only
git switch -c "$BRANCH"
printf '
CH05 checkpoint marker.
' >> CH05_LAB.md
git add CH05_LAB.md
git commit -m "docs: chapter 05 checkpoint marker"
git push -u origin "$BRANCH"
glab mr create -R "$REPO" --source-branch "$BRANCH" --target-branch "$DEFAULT_BRANCH" --title '[CH05 LAB] Parser delivery slice' --description "Closes #$PARSER_IID
Synthetic checkpoint MR; do not merge into a valuable branch." --draft --yes
MR_IID="$(glab mr list -R "$REPO" --source-branch "$BRANCH" --output json --jq '.[0].iid')"
glab mr view "$MR_IID" -R "$REPO" --output json --jq '{iid,state,title,source_branch,target_branch,description}' | tee ch05-mr.json
glab issue view "$PARSER_IID" -R "$REPO" --output json --jq '{iid,state,title,labels,milestone}' | tee ch05-parser-before-merge.json
Pass condition: the issue is still open. The MR has a closing relationship, but it has not been integrated into the default branch.
Optional live close proof: only in this disposable project, remove Draft status and merge the MR through the normal project policy if you want to observe automatic closure. No force push, bypass, or admin override is needed. Then verify the issue changed to closed. The mandatory checkpoint can instead stop at the predicted pre-merge state and use the official documented behavior as the final event model.
7. Compare UI, glab, API, and Git evidence
| Invariant | UI evidence | CLI/API/Git evidence |
|---|---|---|
| Correct work record | Issue/work-item page | Issues API iid/title/state |
| Workflow move changed metadata | Board + issue sidebar | Issues API labels |
| Delivery goal is attached | Milestone on item | Issues API milestone.title |
| Branch is separate Git state | Repository branch view | git branch -vv, commit SHA |
| MR links delivery to planning | MR Work items/description | glab mr view + issue state |
| Board is a view | Card appears in label list | Boards API list type + issue labels |
8. Optional portfolio fixture
On Free, save this as ch05-paid-fixture.json and label
it explicitly as a simulation:
{
"not_live_gitlab_state": true,
"iteration": {"title": "Sprint 12", "tier": "Premium/Ultimate"},
"epic": {"title": "Checkout reliability", "tier": "Premium/Ultimate"},
"status": {"value": "In progress", "tier": "Premium/Ultimate"},
"scoped_label": {"value": "workflow::doing", "tier": "Premium/Ultimate"}
}
If you do have the paid tier, replace the fixture with read-only live inspection. For epics on modern GitLab, use the current work-item interface/API path; do not build new automation against the deprecated Epics REST API.
9. Evidence packet and verification checklist
Collect only planning fields needed to prove the checkpoint. Avoid full confidential descriptions or user email addresses.
mkdir -p ch05-evidence
cp ch05-project-before.json ch05-evidence/
cp ch05-issues-created.ndjson ch05-evidence/
cp ch05-boards.json ch05-evidence/
cp ch05-parser-after-board.json ch05-evidence/
cp ch05-mr.json ch05-evidence/
cp ch05-parser-before-merge.json ch05-evidence/
[ -f ch05-paid-fixture.json ] && cp ch05-paid-fixture.json ch05-evidence/
python - <<'PY'
from pathlib import Path
import hashlib, json, datetime
root=Path('ch05-evidence')
items=[]
for f in sorted(root.iterdir()):
if f.is_file():
items.append({'file':f.name,'sha256':hashlib.sha256(f.read_bytes()).hexdigest(),'bytes':f.stat().st_size})
out={'captured_at_utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'files':items}
(root/'manifest.json').write_text(json.dumps(out,indent=2)+'
')
PY
cat ch05-evidence/manifest.json
-
Three synthetic issues have one type label and the
CH05 Demomilestone. - The parser issue’s flow metadata matches the board column.
- The board API proves the relevant list is label-backed.
- The branch and MR are identifiable separately from the issue.
- The MR target equals the recorded default branch.
- The issue state before merge matches Prediction 2.
- Any paid-feature data is clearly marked live or simulated.
10. Cleanup / rollback
Cleanup should preserve useful evidence and avoid destructive shortcuts. The safest mandatory cleanup is to close synthetic work and remove the temporary branch/MR without deleting the whole project.
# If the draft MR was not merged, close it explicitly.
glab mr close "$MR_IID" -R "$REPO" || true
# Close only synthetic Chapter 05 issues.
glab issue list -R "$REPO" --all --output json --jq '.[] | select(.title | startswith("[CH05 LAB]")) | .iid' | while read -r iid; do
glab issue close "$iid" -R "$REPO" || true
done
# Remove local branch after switching away. Delete remote branch only if it is the synthetic lab branch.
git switch "$DEFAULT_BRANCH"
git branch -D "$BRANCH" || true
git push origin --delete "$BRANCH" || true
# Verify no synthetic open issues/MRs remain.
glab issue list -R "$REPO" --opened --output json --jq '[.[] | select(.title | startswith("[CH05 LAB]")) | {iid,title}]'
glab mr list -R "$REPO" --state opened --output json --jq '[.[] | select(.title | startswith("[CH05 LAB]")) | {iid,title}]'
11. What this chapter adds to a production GitLab operating model
Chapter 01 established platform boundaries, Chapter 02 identities, Chapter 03 project governance, and Chapter 04 authorization inheritance. Chapter 05 adds the work-control plane: a small taxonomy, explicit delivery goals, traceable ownership, view/source separation, API-readable evidence, and a clear bridge from planned work to code change.
The production invariant is: every planning view must be explainable from source work-item metadata, every automation must have an explicit state-transition contract, and every paid/portfolio assumption must be labeled before it becomes a dependency.
Knowledge check
You drag the parser issue to Doing, but the API still shows flow-ready. What should you do first?
Inspect the board/list configuration and the exact issue. Do not repeat mutations or create a duplicate item until you know which metadata the list is supposed to change.
The draft MR contains Closes #12. Why is issue #12 expected to remain open?
The MR has not merged into the default branch. The closing relationship is declared, but the qualifying integration event has not occurred.
A teammate wants iterations and epics in the mandatory Free checkpoint. What is the correct response?
Keep them optional/read-only or simulate them with clearly labeled fixtures; do not require a paid tier.
Why capture board configuration as well as issue labels?
Issue labels prove source state; board configuration proves how the view maps that state into columns. Both are needed to explain the visual result.
What does the evidence manifest hash prove?
It proves the local evidence files have not changed since the manifest was created. It does not prove GitLab state remained unchanged after capture.
What is the safest cleanup if the project will be reused in Chapter 06?
Close synthetic issues/MR, delete only the synthetic branch if appropriate, preserve evidence, and keep shared lab planning resources if they remain clearly isolated and useful.
Checkpoint summary
You defined policy before data, created a Free-compatible backlog, predicted two state transitions, proved board/source consistency, connected one issue to Git and a merge request, distinguished closing intent from the default-branch close event, and captured auditable evidence without requiring paid features. That planning discipline now supports collaboration rather than replacing it.
Official references
- GitLab Docs — Work items
- GitLab Docs — Manage issues
- GitLab Docs — Labels
- GitLab Docs — Milestones
- GitLab Docs — Iterations
- GitLab Docs — Issue boards
- GitLab Docs — Manage epics
- GitLab Docs — Issues API
- GitLab Docs — Project issue boards API
- GitLab Docs — REST API pagination
- GitLab Docs — glab issue
- GitLab Docs — glab work-items list
- GitLab Docs — glab mr create
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.