Chapter 06Lesson 02~165 minutes

GitHub Projects, Roadmaps, Custom Fields, Views, Automation, and Planning: Guided Hands-On Workflow and Core Operations

Create a disposable release-planning Project, add real Issues, a pull request, and a draft item, define Priority and Iteration data, build table/board/roadmap views, configure one safe built-in workflow, and verify state through gh plus API inspection.

gh projectRoadmapBuilt-in workflowsAPI evidence

Learning objectives

  • Create a fresh public repository and a separate user-owned Project without depending on paid organization features.
  • Add real issue/PR items plus a draft item and distinguish their resource identities.
  • Define Priority and Iteration planning data, then build table, board, and roadmap views for different questions.
  • Configure a safe built-in closed-item → Done workflow and verify the resulting field transition.
  • Use gh project and the current versioned Projects REST API for before/after evidence.
  • Clean up only disposable Project/repository resources and document what each deletion affects.
Availability: Required: GitHub.com personal account, GitHub CLI, Git, one disposable public repository, and a user-owned Project. If your CLI credential lacks Project authorization, the lab calls out the scope change before it happens. Organization Projects are optional.

1. Scenario: Atlas 0.6 release planning

You will create atlas-c06-planning-lab with two Issues and one small pull request, plus a user-owned Project named Atlas 0.6 Planning Lab. The Project will contain those records and one draft item. Priority answers “what matters most?”, Status answers “where is the planning flow?”, and Iteration answers “when are we planning to work on it?”

Disposable-only lab. Project deletion and repository deletion are separate destructive operations. The cleanup section verifies both names before deletion.

2. Preflight: identity, collisions, and credential scope

gh auth status --active --hostname github.com
OWNER=$(gh api user --jq .login)
REPO="atlas-c06-planning-lab"
PROJECT_TITLE="Atlas 0.6 Planning Lab"

gh repo view "$OWNER/$REPO" >/dev/null 2>&1 && {
  echo "STOP: repository already exists" >&2; exit 2;
}

gh project list --owner "$OWNER" --format json

If the final command reports insufficient Project authorization, inspect the active account first. On a personal disposable identity, use gh auth refresh -s project to add the scope. This is a credential-permission change, not a harmless formatting option. Do not broaden an employer-managed token for a course lab.

Security-sensitive: gh auth refresh -s project grants the CLI token Project access. Run it only on the intended personal lab account, then review gh auth status.

3. Create source work: repository, issues, and a pull request

gh repo create "$OWNER/$REPO" --public --add-readme --clone
cd "$REPO"
DEFAULT_BRANCH=$(git branch --show-current)

ISSUE_A=$(gh issue create --title "Release: document rollback check"   --body "Add one rollback verification step to the release checklist.")
ISSUE_B=$(gh issue create --title "Bug: retry counter missing from output"   --body "Synthetic bug for Project planning practice.")

# Create a tiny PR so the Project contains both Issue and PR resources.
git switch -c docs/c06-release-note
printf '
## 0.6 planning lab
Synthetic release note.
' >> README.md
git add README.md
git commit -m "docs: add 0.6 planning note"
git push -u origin docs/c06-release-note
PR_URL=$(gh pr create --base "$DEFAULT_BRANCH" --head docs/c06-release-note   --title "docs: add 0.6 planning note" --body "Synthetic PR for Chapter 06.")

printf 'issueA=%s
issueB=%s
pr=%s
' "$ISSUE_A" "$ISSUE_B" "$PR_URL"

The issues and PR are source resources. Their URLs are stable identities you can add to a Project. No Project exists yet, so none of these records has Priority/Iteration planning data.

4. Create the Project and prove its identity

PROJECT=$(gh project create --owner "$OWNER" --title "$PROJECT_TITLE"   --format json --jq '.number')

test -n "$PROJECT"
printf 'projectNumber=%s
' "$PROJECT"
gh project view "$PROJECT" --owner "$OWNER" --format json

# Independent read through the current versioned Projects REST API.
gh api   -H "Accept: application/vnd.github+json"   -H "X-GitHub-Api-Version: 2026-03-10"   "/users/$OWNER/projectsV2/$PROJECT"   --jq '{number,title,state,public}'

The create command returns structured JSON, so the script captures the new Project number directly instead of guessing from a title search. The following versioned REST GET is an independent read of that exact Project resource. For user-owned Project REST endpoints, verify the endpoint token-compatibility table; GitHub currently notes that some fine-grained/App token types are not accepted even when similar organization endpoints support them.

6. Add issue/PR items and one draft item

gh project item-add "$PROJECT" --owner "$OWNER" --url "$ISSUE_A"
gh project item-add "$PROJECT" --owner "$OWNER" --url "$ISSUE_B"
gh project item-add "$PROJECT" --owner "$OWNER" --url "$PR_URL"
gh project item-create "$PROJECT" --owner "$OWNER"   --title "Draft: decide maintenance-window wording"   --body "Planning-only idea; no repository issue yet."

gh project item-list "$PROJECT" --owner "$OWNER" --limit 100 --format json

The first three items point to existing engineering records. The draft item exists only in the Project. If you delete the draft item, no repository issue is affected; if you remove a linked issue item from the Project, the issue itself still exists.

7. Add a Priority field with the CLI

gh project field-create "$PROJECT" --owner "$OWNER"   --name "Priority" --data-type SINGLE_SELECT   --single-select-options "P0,P1,P2"

gh project field-list "$PROJECT" --owner "$OWNER" --format json

gh project item-edit "$PROJECT" --owner "$OWNER" --url "$ISSUE_A"   --field "Priority" --value "P1"
gh project item-edit "$PROJECT" --owner "$OWNER" --url "$ISSUE_B"   --field "Priority" --value "P0"
gh project item-edit "$PROJECT" --owner "$OWNER" --url "$PR_URL"   --field "Priority" --value "P2"

Current gh project field-create supports TEXT, SINGLE_SELECT, DATE, and NUMBER fields. It does not expose Iteration creation as a field-create data type, so the next step uses the current web UI for Iteration rather than pretending the CLI has a flag that it does not.

8. Create Iteration in the web UI, then verify it

  1. Open the Project with gh project view "$PROJECT" --owner "$OWNER" --web.
  2. In a table view, add a new field named Iteration and choose the Iteration field type.
  3. Use a short lab cadence such as one week. GitHub creates the initial iteration windows automatically.
  4. Assign the two Issues and PR to the current or next iteration. Leave the draft unassigned to make missing scheduling visible.
gh project field-list "$PROJECT" --owner "$OWNER" --format json

gh api   -H "Accept: application/vnd.github+json"   -H "X-GitHub-Api-Version: 2026-03-10"   "/users/$OWNER/projectsV2/$PROJECT/fields?per_page=100" --paginate

The REST field response exposes the iteration field and its configuration, including iteration IDs/date windows. This is strong evidence that the planning cadence is Project state, not a label hidden in an issue body.

9. Build three saved views with one model

  1. Table — Planning inventory: show Title, Status, Priority, Iteration, Assignees, Repository. Sort by Priority and use it for data-quality inspection.
  2. Board — Flow: group by Status. The board should answer what is Todo/In Progress/Done, not “which card looks important.”
  3. Roadmap — Release horizon: use Iteration as the time axis and display Priority. The draft without an Iteration should remain visibly unscheduled rather than silently disappearing from every view.

Save each view with a purpose-oriented name. A view is a query/presentation contract. If the roadmap filters out items with no Iteration, create a companion “Unscheduled” view or a filter that makes missing scheduling visible.

10. Configure one safe built-in workflow and observe causality

Open the Project menu → Workflows. Select the workflow that reacts when an issue or pull request is closed, edit it so the Project Status becomes Done, then Save and turn it on. Current GitHub documentation says close/merge-to-Done workflows are enabled by default in newly initialized Projects, but inspect the actual state rather than assuming.

# Before closing: capture both source and planning state.
gh issue view "$ISSUE_A" --json number,title,state,url
gh project item-list "$PROJECT" --owner "$OWNER" --limit 100   --field Status --field Priority --field Iteration --format json

# Trigger the source event.
gh issue close "$ISSUE_A" --comment "Closing to test Project workflow."

# Re-inspect source and Project state.
gh issue view "$ISSUE_A" --json number,title,state,url
gh project item-list "$PROJECT" --owner "$OWNER" --limit 100   --field Status --field Priority --field Iteration --format json

Causality should read: issue close event → built-in Project workflow → Status becomes Done. The Project field did not close the issue. That direction is deliberately easier to reason about and recover from.

11. Restore the lab issue and make planning state explicit

gh issue reopen "$ISSUE_A" --comment "Reopened after workflow verification."

gh project item-edit "$PROJECT" --owner "$OWNER" --url "$ISSUE_A"   --field "Status" --value "Todo"

gh project item-list "$PROJECT" --owner "$OWNER" --limit 100   --field Status --field Priority --field Iteration --format json

Reopening the issue is source-state recovery. Resetting Project Status is planning-state recovery. Treat them as two explicit operations unless you have a documented workflow that synchronizes both directions.

12. Challenge: choose the correct surface

Need Correct surface/control Why
Change which release window work is planned for Project Iteration field Planning schedule, not issue category
Prove code has merged Pull request state / merge commit Engineering source of truth
See all P0 work regardless of Status Project filter/view Cross-cutting planning query
Classify “bug” inside repository workflows Repository label Repository-level reusable categorization
Track an idea before selecting a repository Draft Project item No issue resource is justified yet

13. Cleanup and rollback

First decide whether you want to preserve screenshots/JSON evidence outside the disposable resources. Then remove the Project before deleting the repository so you can observe the independent lifecycles.

cd ..
printf 'delete project=%s owner=%s
' "$PROJECT" "$OWNER"
gh project delete "$PROJECT" --owner "$OWNER"

printf 'delete repo=%s/%s
' "$OWNER" "$REPO"
gh repo delete "$OWNER/$REPO" --yes
rm -rf "$REPO"
Destructive: Project deletion removes the planning model/views/fields/drafts, while repository deletion removes Git history plus hosted Issues/PRs. Confirm both identities separately.

14. Lesson summary

You built one Project from real source records, added planning-only fields, rendered three views, and observed one built-in workflow. The important result is not the board; it is the verified separation between Project state and engineering state.

Knowledge check

Why did the lab create Iteration in the UI instead of gh project field-create?

What survives if you remove an issue item from a Project?

What was the authoritative trigger in the workflow test?

Why is the unscheduled draft useful in the roadmap exercise?

Why are Project and repository deletion separate warnings?

Next lesson

Choose the model, not just the layout

Lesson 03 evaluates user vs organization ownership, fields vs labels/milestones, iteration cadence, view design, and automation ownership.

Authoritative references

 Creating a project
 About iteration fields
 Changing the layout of a view
 Built-in Project automations
 gh project
 gh project create
 gh project field-create
 gh project item-edit
 gh project item-list
 gh project link
 REST API for Projects
 REST Project field endpoints
 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.