Skip to content

GitHub Projects

Infrastructure work for scandora.net is tracked in GitHub Projects.

Project board: https://github.com/users/scandora/projects/1

Repository: https://github.com/scandora/scandora.net


Swim Lanes

Work items flow through five swim lanes:

Backlog → Ready → In Progress → In Review → Done
Lane Meaning
Backlog Idea captured, not yet actionable (may need scoping, labeling, or a body)
Ready Actionable, no blockers, has labels + description
In Progress Branch created, active work underway
In Review PR open, awaiting merge
Done Merged and closed

Issue Hygiene Requirements

Every issue must have before it moves to Ready:

  • At least one label
  • A body with enough context to pick up and work (50+ characters)
  • A Priority set on the project board (P0–P4)

The /start-issue skill enforces this — it auto-infers and applies labels when missing (using GitHub MCP), warns on empty bodies, and only hard-stops if it cannot confidently infer labels from the issue content.


Labels

Label Use
infrastructure Gateway, network, hardware work
automation IaC, Ansible, Terraform, CI/CD
documentation Docs improvements and architecture decisions
bug Things that are broken
enhancement Improvements to existing functionality
cloud AWS, GCP cloud infrastructure
monitoring Prometheus, Grafana, alerting

Priority

Set on the project board (not as a GitHub label):

Priority Use
P0 Critical — broken right now
P1 High — significant impact, do soon
P2 Medium — important, planned work
P3 Normal — standard backlog
P4 Low — nice to have

GitHub Flow (Enforced)

All changes go through a branch — no exceptions, including skill files, hooks, and docs.

1. /start-issue <N>     → creates branch, moves issue to In Progress
2. plan + implement     → write plan, then /publish-plan to persist it on the issue's
                          "Proposed approach" section before starting implementation
3. commits on branch
4. /review              → walks the diff, runs preflight checks, stops for agreement
                          no push, no PR — just inspection and discussion
5. /ready               → pushes branch, creates PR with "Closes #N", posts verification comment
                          GitHub automation moves issue to In Review automatically
6. merge PR via GitHub UI
                          GitHub automation moves issue to Done automatically
7. /post-merge          → verify board → Done (worktree: safe; classic: pulls main)
                          worktree path: does NOT remove worktree or delete branch here
                          classic path: also deletes local branch
                          check issue state: Auto-close workflow closes it automatically
                          when Closes #N is present — no manual action needed in normal path
                          worktree path ends with: "Run /restart to close and remove worktree"
8. /restart             → end-of-session: audit state from git/gh (never memory), clean
                          stale branches, trim MEMORY.md, rewrite HANDOFF from live data,
                          bounded docs audit, final clean-state report
                          Phase 5 (worktree only): git worktree remove + branch delete
                          as final step (remove worktree FIRST, then branch) — CWD gone, user closes session

Closes #N is the automation trigger. Every PR body must include it. Without it, the Pull request linked to issue, Pull request merged, and Auto-close issue workflows don't fire — board moves and issue closure must be done manually. With it, the issue is closed automatically when the PR merges; no manual close is needed or expected.

Enforcement:

  • Pre-push hook (.githooks/pre-push) blocks git push origin main
  • /start-issue hard-stops if issue has no labels, warns if body is empty
  • /review is the agreement gate — run it before /ready, not after
  • /ready enforces Closes #N as mandatory, not advisory
  • /ready runs three preflight checks before creating the PR (see below)
  • GitHub auto-deletes branches on merge (makes post-merge pushes fail loudly)

GitHub Projects Automation Workflows

Seven workflows are configured and enabled on this project (Project Settings → Workflows):

Workflow Trigger Action
Auto-add to project Issue or PR created in repo Adds item to board at Backlog
Auto-add sub-issues to project Sub-issue created Adds sub-issue to board
Item added to project Item added manually Sets initial status
Pull request linked to issue PR opened with Closes #N Moves issue → In Review
Pull request merged PR merged Moves linked issue → Done
Auto-close issue PR merged Closes the linked issue
Item closed Issue closed directly Moves item → Done

Gap — no built-in workflow for PR closed without merge: GitHub Projects does not offer a "Pull request closed without merge" trigger. If a PR is abandoned (closed without merging), move the board item back to In Progress using the wrapper script:

scripts/github/set-project-status.sh <N> "In Progress"

/review and /ready Preflight Checks

The /review skill is the agreement checkpoint — it walks the diff interactively, runs all four gates below, and stops without creating a PR. Run it when you think work is done but before committing to a PR.

The /ready skill enforces the same gates before creating a PR.

PREFLIGHT — is the branch complete?

  • All changes committed, git status clean
  • No follow-on fixes anticipated
  • Once gh pr create is called the branch is final — commits to a merged branch go nowhere

SCOPE CHECK — one PR = one concern

  • Diff addresses exactly one issue/question
  • Unrelated bug fixes in pre-existing code must be extracted to their own branch first, then rebased in
  • Bug fixes in new code you're adding are fine to include

DRIFT CHECK — is main ahead of this branch?

git fetch origin && git log HEAD..origin/main --oneline

If main has commits touching your changed files, rebase before creating the PR:

git rebase origin/main

Rebasing before PR creation is clean. Rebasing after a conflict is reported costs more time and risks mixing concerns during resolution.

VALIDATION GATE — evidence that the change was actually tested, not just planned.

One of the following is required before /review can produce its summary:

  • Evidence — actual command output run this session demonstrating the changed behavior works. "ShellCheck passed" is syntax validation only and does not count as behavioral evidence.
  • Explicit deferral — if the change targets an exact failure condition that cannot be reproduced pre-merge, state this explicitly with a reason and the exact post-merge test command.

If neither is present, /review stops and prompts for validation before continuing.

Minimum validation by change type:

Change type Minimum pre-merge validation
Shell script (new function/path) Source the function, run it, show output
SSH/key handling ssh-keygen -l -f <key_file> — prove key extracted correctly
Ansible playbook/role run-*.sh --check --tags <tag> on any reachable host
Config/template Show rendered output; assert expected values present
Docs/comments only No runtime validation required — state this explicitly
Workflow/skill files Invoke the modified skill to verify it loads

Documentation Sync Rule

Docs and code ship together in the same PR.

When a PR touches workflow files, the corresponding documentation must be updated in the same commit:

If you change... Also update...
.claude/commands/ready.md, start-issue.md, review.md, or post-merge.md docs/operations/github-projects.md
.githooks/pre-push docs/operations/git-hooks.md
CLAUDE.md (workflow sections) Relevant docs/operations/ page

The /review skill Step 4 and /ready skill Step 2 both prompt explicitly for this check.

Skill invocation check (enforced in /review Step 4, part of doc-sync) — if any .claude/commands/*.md skill was added or modified in the diff, invoke it before proceeding to /ready. Skill ! prefix commands must be single operations: no | pipes, no && chains. A skill that fails to load is broken regardless of how clean the diff looks in code review. Reading the diff is not a substitute for running the skill.


Board Status Updates

Use scripts/github/set-project-status.sh to move issues between columns:

scripts/github/set-project-status.sh <issue-number> <status>

Status values (case-insensitive): Backlog | Ready | "In Progress" | "In Review" | Done

# Examples
scripts/github/set-project-status.sh 75 "In Progress"
scripts/github/set-project-status.sh 37 "In Review"
scripts/github/set-project-status.sh 62 Done

The script retrieves the GitHub PAT from 1Password automatically (requires desktop app unlocked). It hard-codes the stable project/field node IDs and looks up the item node ID at runtime. If the issue is not yet on the board, the script automatically adds it before setting the status — no manual board-add step required.

When to use this manually:

  • /start-issue calls it automatically (moves to In Progress)
  • /post-merge calls it when Closes #N was missing from the PR body (moves to Done)
  • PR abandoned without merge: move back to In Progress manually

Using the GitHub CLI

Note: gh on luna is aliased to op plugin run -- gh which requires an interactive TTY. In Claude Code sessions, use the PAT directly:

GH_PAT=$(op item get "github_pat_mcp_claude" --vault scandora-automation --fields credential --reveal 2>/dev/null)
GITHUB_TOKEN="$GH_PAT" /opt/homebrew/bin/gh issue list --repo scandora/scandora.net

For raw Projects v2 GraphQL (advanced use — prefer the wrapper script for board moves):

GITHUB_TOKEN="$GH_PAT" /opt/homebrew/bin/gh api graphql -f query='{ user(login: "scandora") { projectV2(number: 1) { title } } }'
# Create an issue
gh issue create --title "Brief title" --label "infrastructure" --body "Details..."

# List open issues
gh issue list --repo scandora/scandora.net --state open

# View an issue
gh issue view 57 --repo scandora/scandora.net

GitHub MCP Integration

Claude Code interacts with GitHub Issues and the project board via the GitHub MCP server (user-level, available in all sessions).

See: MCP Authentication for setup details.


Commit and Branch Conventions

Commit messages:

feat(scope): brief description
fix(scope): brief description
docs(scope): brief description

Branch names:

feat/227-short-description
fix/99-short-description
docs/42-short-description

Note: /start-issue uses the EnterWorktree tool, which prefixes the actual git branch with worktree- (e.g. feat/60-zerotier-bootstrap → git branch worktree-feat/60-zerotier-bootstrap). The worktree name you provide to EnterWorktree follows the conventions above; the worktree- prefix is added automatically and is visible in git branch --show-current inside the worktree.