GitHub Contribution Workflow
1 Overview
This page documents the GitHub issue-and-PR workflow used during this project. It serves as a personal reference for contributing to a repository — either your own or someone else’s — following open-source conventions.
The workflow modelled here is issue → branch → fix → PR → close. Every change is traceable back to a logged reason.
2 Step 1 — Find or raise an issue
Before touching code, the problem is recorded as a GitHub Issue. Issues are the paper trail: they explain why a change was made and link the fix back to the motivation.
# Create an issue from the command line using the GitHub CLI (gh)
gh issue create \
--title "Short, specific description of the problem" \
--body "$(cat <<'EOF'
## Description
What is broken and where.
## Expected behaviour
What should happen.
## Actual behaviour
What actually happens, including error output or screenshots.
## Steps to reproduce
Numbered list so someone else can confirm the bug.
## Suggested fix
Optional — your best guess at the solution.
## Acceptance criteria
- [ ] Checkbox list of what "done" looks like
EOF
)"In a project you don’t own, you’d browse to the repo on GitHub, click Issues → New issue, and fill in the same information. The maintainer triages it, and if they accept it you get assigned or invited to submit a PR.
3 Step 2 — Create a branch
Never commit fixes directly to main. Create a named branch that references the issue.
# Convention: type/short-description
# Common prefixes: fix/, feat/, docs/, refactor/, chore/
git checkout -b fix/tests-qmd-dynamic-renderingNaming conventions matter in collaborative repos — maintainers scan branch names to understand intent before reading code.
In someone else’s repo you would first fork the repository (top-right button on GitHub), clone your fork, then create the branch there:
# Fork happens on GitHub, then:
git clone https://github.com/YOUR-USERNAME/their-repo.git
cd their-repo
git remote add upstream https://github.com/ORIGINAL-OWNER/their-repo.git
git checkout -b fix/short-description4 Step 3 — Make the fix
Edit files, run tests, confirm the fix works. Keep commits small and focused.
# Stage specific files (never git add -A blindly)
git add quarto/tests.qmd
# Write a commit message that explains WHY, not just WHAT
git commit -m "Load test-results.json dynamically in tests.qmd
Previously the page was static markdown with hardcoded checkmarks.
Now a Python code cell loads the JSON on every render so results
reflect the actual last test run.
Refs #1, #2"Good commit messages follow the pattern:
Short imperative summary (≤72 chars)
Blank line, then a paragraph explaining the motivation — what was
wrong, why this approach was chosen, any tradeoffs.
Refs #N ← links the commit to the issue in GitHub's UI
5 Step 4 — Push and open a Pull Request
# Push branch to remote (origin = your fork or your own repo)
git push -u origin fix/tests-qmd-dynamic-rendering
# Open a PR from the command line
gh pr create \
--title "Fix: render test results dynamically from JSON" \
--body "$(cat <<'EOF'
## Summary
- Replace hardcoded markdown in `tests.qmd` with Python code cells that load `test-results.json`
- Fix pytest output path so the JSON lands where the `.qmd` expects it
## Test plan
- [ ] Run `pytest tests/test_setup.py --json-report --json-report-file=test-results.json`
- [ ] Render `quarto/tests.qmd` and confirm results table appears
- [ ] Verify failed/skipped tests show error messages
Closes #1
Closes #2
EOF
)"Closes #N is a magic keyword — when the PR is merged, GitHub automatically closes the linked issue. Other valid keywords: Fixes #N, Resolves #N.
In someone else’s repo, you’d open the PR from your fork’s branch targeting the upstream main. GitHub shows a “Compare & pull request” banner automatically after you push.
6 Step 5 — Review and merge
In a collaborative project a maintainer reviews the PR, leaves comments, and requests changes if needed. You push additional commits to the same branch — they appear in the PR automatically.
# If the reviewer requests a change:
# 1. Make the edit
git add quarto/tests.qmd
git commit -m "Address review: handle missing test-results.json gracefully"
git push # same branch, PR updates automaticallyOnce approved:
- The maintainer clicks Squash and merge (or Merge, depending on repo policy)
- The linked issues close automatically
- The branch can be deleted
# Clean up local branch after merge
git checkout main
git pull
git branch -d fix/tests-qmd-dynamic-rendering7 Quick-reference command card
| Action | Command |
|---|---|
| Create issue | gh issue create --title "..." --body "..." |
| List open issues | gh issue list |
| View issue | gh issue view 3 |
| Create branch | git checkout -b fix/description |
| Push branch | git push -u origin fix/description |
| Open PR | gh pr create --title "..." --body "..." |
| View PR status | gh pr status |
| List PRs | gh pr list |
| Merge PR | gh pr merge --squash |
| Close issue manually | gh issue close 3 |
8 CI — Continuous Integration with GitHub Actions
Every push to this repo triggers an automated test run on GitHub’s servers. This is called Continuous Integration (CI) — the idea that code is integrated (tested) continuously, not just before a release.
The workflow lives at .github/workflows/ci.yml:
on:
push:
branches: ["**"] # every branch, every push
pull_request:
branches: [main] # and every PR targeting main
jobs:
test:
runs-on: ubuntu-latest # clean virtual machine, nothing pre-installed
steps:
- uses: actions/checkout@v4 # download the code
- uses: astral-sh/setup-uv@v5 # install uv
- run: uv sync # install Python dependencies
- run: pytest tests/test_ingest.py -v --json-report --json-report-file=test-results.json
- uses: actions/upload-artifact@v4 # save test-results.json forever on this runWhat it proves: the tests pass on a clean machine, not just on a local laptop. Every commit in the git history has a green or red badge — a permanent, timestamped record of whether the code was working at that point.
Why test_setup.py is excluded from CI: those tests check for a running Ollama instance (localhost:11434), which doesn’t exist on GitHub’s servers. They remain as local smoke tests. test_ingest.py uses mocks for Ollama and ChromaDB, so it runs cleanly anywhere.
9 Branch Rulesets — enforcing the review process
A ruleset is a policy applied to a branch. For this project, main is protected by a ruleset that requires CI to pass before any merge is allowed.
How to set it up: Settings → Rules → Rulesets → New branch ruleset
- Target:
main - Enforcement: Active
- Rule: Require status checks to pass → add the
testcheck fromci.yml
Effect: the merge button on GitHub is physically blocked until the CI check is green. This means every commit that lands on main has been tested. It also forces a deliberate human decision to merge — you can’t accidentally push a broken change.
10 Human sign-off — creating an evidence trail
The CI check proves the code works. The human comment proves the code was reviewed. For the meta-AI strand of this project, it matters that Alan’s approval is clearly recorded separately from Claude’s actions.
Pattern used in this project:
- Claude opens a PR with a comment header:
> **Generated by Claude Sonnet 4.6 — actioned on Alan McDonagh's approval** - CI runs automatically — green tick or red cross appears on the PR
- Alan reviews the Actions output and leaves a comment: “Reviewed CI output — approving merge”
- Alan merges — the merge is timestamped under Alan’s GitHub account
This produces an audit trail where every actor (Claude, CI, Alan) is distinguishable and every action is timestamped.
11 Issue closing workflow (mandatory for this project)
Every issue must pass through four steps before it is closed:
| Step | What happens |
|---|---|
| 1. Tests pass | pytest green locally and in CI |
| 2. Quarto demo | A .qmd page shows the feature working with live code output |
| 3. Nav updated | The page is registered in quarto/_quarto.yml |
| 4. Close with comment | GitHub issue closed with AI attribution header + summary of what was done |
The closing comment always includes:
> **Generated by Claude Sonnet 4.6 — actioned on Alan McDonagh's approval**
What was implemented, which tests cover it, which Quarto page demonstrates it.
12 This project’s issues raised via this workflow
| Issue | Title | Status |
|---|---|---|
| #1 | Ingest: Load course PDFs with PyPDFLoader | Closed — superseded by CSV pivot |
| #2 | Ingest: Chunk documents with RecursiveCharacterTextSplitter | Closed — implemented in rag/ingest.py |
| #18 | tests.qmd renders static markdown instead of live results | Closed via PR |
| #19 | test-results.json written to wrong path | Closed via PR |
| #22 | Switch knowledge base from course PDFs to CBI speeches corpus | Closed — implemented |
| #24 | Add GitHub Actions CI workflow | Closed via PR #26 |