Introduction to Software Engineering · Chapter 07
Lab-07: Agile Software Development Model
Your team runs Sprint 1 of ITC Club Hub with Scrum. You set up a GitHub Projects board and refine the Must stories from Lab-05, plan the sprint with planning poker, implement the first stories in C++ test-first with GoogleTest (RegistrationService::registerStudent and clubhub list-events), log the daily scrums and draw the burndown from the board data, and close the sprint with a review in front of the client and a retrospective. The challenge adds one Extreme Programming practice for the whole sprint and measures its effect.
Setup
20 min- Tools. Git, the GitHub CLI
gh2.40 or later (for Projects commands), CMake 3.28 or later, a C++20 compiler (GCC 13, Clang 17 or MSVC 2022 or later) and your editor. GoogleTest is downloaded by CMakeFetchContent; nothing to install. Check the toolchain and start the lab branch from an up-to-datemain:
If the last line fails because no test target exists yet, continue: Task 3 adds it.git --version && gh --version && cmake --version gh auth login # once per machine gh auth refresh -s project # allow gh to manage GitHub Projects cd clubhub # your team repository git switch main && git pull git switch -c lab-07 cmake -S . -B build && cmake --build build && ctest --test-dir build - Case-study brief. The box below contains everything you need to know about the product for this lab.
Case-study brief: ITC Club Hub
- Purpose
- An application for student clubs at the Institute of Technology of Cambodia. The full system is analysed and modelled as a web and mobile application; the team implements its core in C++20: domain model, business rules and a command-line front end, with CSV or JSON file storage.
- Users
- Club leader registers a club and publishes events · Administrator approves clubs and sees a monthly activity report · Student browses events, registers, cancels and receives a reminder · Organiser checks registered students in at the door and sees attendance.
- Business rules
- Capacity cannot be exceeded (a waiting list is a Should feature) · a student cannot register twice for the same event · cancellation closes 24 hours before the start · events in the past cannot be registered for · only approved clubs can publish events.
- Event data
- Title, date and time, location, capacity, description, owning club. Personal data: student ID, name, email, phone (optional), club membership, attendance records.
- Fallback backlog
- Use your own Lab-05 backlog. Only if it is missing, use these ids: Must US-01 register a club · US-02 publish an event (approved club only) · US-04 list upcoming events · US-05 register for an event · US-06 cancel a registration · US-07 check a student in · US-08 see attendance · US-09 monthly activity report · US-10 approve or reject a club. Should US-03 edit an event · US-11 reminder before the event · waiting list. Could US-12 student profile.
- C++ names
- Namespace
clubhub;Club,Event,Student,Registration;ClubStatus(Pending,Approved,Rejected);RegistrationStatus(Registered,Cancelled,CheckedIn,Waitlisted);RegistrationServicewithregisterStudent,cancel,checkIn; interfacesEventRepositoryandRegistrationRepositorywith CSV-backed implementations. Layout:CMakeLists.txt,src/,include/clubhub/,tests/,data/. - Rhythm
- Sprint 1 = weeks 9 and 10 (this lab), Sprint 2 = weeks 11 and 12, project submission and demo in week 14. Sprints are two weeks (10 working days); planning in the week-9 lab session, review and retrospective in the week-10 lab session.
- Team roles
- One Product Owner for the whole project, who liaises with the client (the instructor or teaching assistant); a Scrum Master who rotates every sprint; everyone else, and in practice the SM too, are Developers.
- Inputs from earlier labs. This lab reuses the artifacts below. If one is incomplete, use the fallback and note it in
lab07/README.md(Task 5); do not stop to repair the earlier lab.Input From What you need from it Fallback if missing Team and roles Lab-01 ( lab01/README.md, team charter)Member names and GitHub accounts, who is PO, working agreement (meeting times) PO = the member who led the Lab-05 client interview; Sprint 1 SM = another volunteer; dailies at a fixed time agreed in Task 2. Process decision Lab-04 ( lab04/)Why Scrum, sprint length, events calendar Scrum with two-week sprints, events as in the brief. Product backlog and issues Lab-05 ( lab05/backlog and the GitHub issues created there)User stories with ids, MoSCoW priority and Given/When/Then acceptance criteria The fallback backlog in the brief; write the acceptance criteria in Task 1. Class diagram and headers Lab-06 ( lab06/class diagram,include/clubhub/*.hpp)Event,Registration,RegistrationStatus, repository interfacesThe fallback headers in Task 3, step 1. Build Earlier labs ( CMakeLists.txt)A library target, an executable, a GoogleTest target The CMake snippet in Task 3. - Deliverable folder. Create
lab07/; every task fills some of these files. Code goes tosrc/,include/clubhub/,tests/anddata/as usual.clubhub/ ├── CMakeLists.txt src/ include/clubhub/ tests/ data/events.csv ├── lab01/ … lab06/ # earlier labs, read-only here └── lab07/ ├── board.md + board-start.png board-end.png # Task 1 (+ Task 5) ├── definition-of-done.md # Task 1 ├── sprint-planning.md # Task 2 ├── tdd-log.md # Task 3 ├── daily-scrum-log.md # Task 4 ├── burndown-data.csv + burndown.md # Task 4 ├── sprint-review.md # Task 5 ├── retrospective.md # Task 5 ├── README.md # Task 5 (record what changed) └── xp-experiment.md + xp-data.csv # Challenge - Conventions. Story ids come from Lab-05 (
US-05); commit messages start with the story id or the lab task (test(US-05): …,Lab-07 task 2: …); every Markdown file starts with a title, a version and a date. Work on feature branches (feature/US-05-register) and merge tomainthrough pull requests reviewed by another member.
Board, backlog refinement and definition of done
Easy Lead: Product Owner 60 min · week 9Goal
Make the product backlog transparent: a GitHub Projects board with the Scrum workflow columns, every Must story from Lab-05 on it with story points and acceptance criteria, ordered by the PO, and a definition of done the whole team has agreed to.
Steps
- Create a GitHub Project (Board layout) named
Club Hub board, owned by your team organisation or the repository owner, and link it to the repository. Edit the built-in Status field so its options are exactlyBacklog,Sprint backlog,In progress,In review,Done(one board column each). - Add the custom fields Story points (Number), Priority (Single select:
Must,Should,Could,Won't) withgh project field-create, and Sprint (Iteration, 2 weeks, first iteration starting on Monday of week 9) in the web interface (the CLI cannot create iteration fields). - Import every Must story: each one must be a GitHub issue created from the
user-storyissue template (starter below); add all of them to the project withgh project item-add, set Priority and put them inBacklog. Should and Could stories may be added too, below the Musts. - Refinement meeting (30 min, whole team, PO leads): check that every item has at least two Given/When/Then scenarios (one success, one rule violation) and is small enough for one sprint; split anything bigger. Size every item relatively against a reference story (for example US-04 = 3) and fill Story points. Planning poker for the top items follows in Task 2.
- The PO orders the
Backlogcolumn by value, risk and dependencies (for example US-04 before US-05 before US-06, because you cannot register for an event you cannot list). - Write
lab07/definition-of-done.md(template in the chapter, slide 12): at least six checkable items covering build without warnings, GoogleTest tests for every acceptance criterion, review in a pull request, documentation and PO acceptance. Every member approves the pull request that adds it. - Write
lab07/board.md(project URL, fields, one-line policy per column: what must be true for a card to enter it) and save a screenshotlab07/board-start.png. Commit asLab-07 task 1: board, refined backlog, definition of done.
Starter (issue template and gh commands)
<!-- .github/ISSUE_TEMPLATE/user-story.md -->
---
name: User story
about: A product backlog item for ITC Club Hub
labels: user-story
---
**US-__** As a <role>, I want <goal> so that <benefit>.
### Acceptance criteria
```gherkin
Scenario: <success case>
Given …
When …
Then …
Scenario: <rule violated>
Given …
When …
Then …
```
Priority: Must | Should | Could Story points: __ Traces to: FR-__
OWNER=itc-clubhub-team3 # your organisation or user
gh project create --owner "$OWNER" --title "Club Hub board"
# note the project number that is printed, for example 1
gh project field-create 1 --owner "$OWNER" --name "Story points" --data-type NUMBER
gh project field-create 1 --owner "$OWNER" --name "Priority" \
--data-type SINGLE_SELECT --single-select-options "Must,Should,Could,Won't"
# add every issue labelled user-story (Lab-05) to the project
for url in $(gh issue list --label user-story --state open --limit 100 \
--json url --jq '.[].url'); do
gh project item-add 1 --owner "$OWNER" --url "$url"
done
gh project item-list 1 --owner "$OWNER" --limit 100
Expected output (excerpt of lab07/board.md)
# Club Hub board · v1.0 · 2026-11-02
Project: https://github.com/orgs/itc-clubhub-team3/projects/1
| Column | Policy: a card may enter when … |
|----------------|----------------------------------------------------------|
| Backlog | it has an id, a user story sentence and a priority |
| Sprint backlog | it was selected at sprint planning and has tasks |
| In progress | a named developer works on it now |
| In review | a pull request is open and ctest passes on the branch |
| Done | every definition-of-done item is ticked and PO accepted |
| Order | Issue | Story | Pri | Pts | Scenarios |
|-------|-------|-----------------------------------------|------|-----|-----------|
| 1 | #14 | US-04 List upcoming events | Must | 3 | 2 |
| 2 | #15 | US-05 Register for an event | Must | 5 | 4 |
| 3 | #16 | US-06 Cancel a registration | Must | 5 | 3 |
| 4 | #11 | US-01 Register a club | Must | 3 | 2 |
| … | … | 9 Must items, 41 points in total | | | |
- If
gh projectanswers "missing required scopes", rungh auth refresh -s projectagain and accept in the browser. - In the board view, Group by: Status gives you the five columns; the Status options are edited in the project settings, not per view.
- A story that needs 13 or more points, or that the team cannot explain in one sentence, is not ready: split it along a business rule (register with the capacity rule, then the duplicate rule) or along a data variation.
- If Lab-05 wrote the stories only in Markdown, create the issues with
gh issue create --template user-story.mdor paste them in the web form; keep the Lab-05 ids in the titles.
Acceptance checklist
Sprint planning with planning poker
Medium Lead: Scrum Master (facilitates), PO (goal and order) 75 min · week 9Goal
Hold sprint planning for Sprint 1: agree on a Sprint Goal, estimate the top stories with planning poker, select a sprint backlog that fits the team's real capacity, and break every selected story into tasks. The result is recorded in lab07/sprint-planning.md and on the board.
Steps
- Capacity. Each member states the hours they can really give over the 10 working days (subtract other exams, deadlines and travel). Multiply by a focus factor (0.7 is a good start for students) and subtract the time of the Scrum events. Record the table.
- Sprint Goal. The PO proposes why this sprint matters; the team rewrites it until it is one sentence describing an outcome the client can try (not a list of ids). Sprint 1 must enable at least listing events and registering.
- Planning poker for at least the top five backlog items: the PO reads the story and criteria, questions are answered, everyone picks a card from 0, 1, 2, 3, 5, 8, 13, 21, ? in secret, cards are revealed together. If the lowest and highest differ by more than one card, both explain and the team votes again. Record every member's vote per round, the key argument, and the consensus; update the Story points field.
- Selection. Pull items from the top of the backlog into
Sprint backloguntil the next item would exceed the forecast. Sprint 1 must contain US-04 (list events) and US-05 (register, with the capacity and duplicate rules), because Task 3 implements them. Set the Sprint field to Sprint 1. - Task breakdown. Split every selected story into tasks of at most 4 hours (write test, implement rule, CSV storage, CLI command, review) as a task list or sub-issues in the story issue, each with an estimate in hours. The total task hours is the start of your burndown (Task 4) and must not exceed the capacity from step 1.
- Write everything into
lab07/sprint-planning.mdand commit asLab-07 task 2: sprint 1 planning; the SM commits, the PO reviews.
Starter (lab07/sprint-planning.md)
# Sprint 1 planning · v1.0 · 2026-11-02
PO: __ Scrum Master: __ Sprint: weeks 9-10 (__ to __)
## Capacity
| Member | Hours available | Focus 0.7 | Notes |
|--------|-----------------|-----------|------------------|
| __ | __ | __ | exam on day __ |
| Scrum events (planning, dailies, review, retro): -__ h
| **Total capacity** | | **__ h** | |
## Sprint Goal
<one sentence: what the client can do at the end of the sprint>
## Planning poker
| Story | Round | __ | __ | __ | __ | __ | Key argument / outcome |
|-------|-------|----|----|----|----|----|------------------------|
| US-__ | 1 | | | | | | |
| US-__ | 2 | | | | | | consensus: __ points |
## Sprint backlog (forecast: __ points)
| Order | Story | Points | Tasks (hours) | Total h |
|-------|-------|--------|---------------|---------|
Expected output (excerpt)
Capacity: 5 members, 91 h stated x 0.7 = 64 h, minus 12 h of events = 52 h
Sprint Goal: a student can see the upcoming events, register for one and
cancel again from the command line.
| Story | Round | Dara | Sokha | Vicheka | Rithy | Malis | Outcome |
| US-05 | 1 | 5 | 5 | 3 | 8 | 5 | Vicheka forgot the CSV storage |
| US-05 | 2 | 5 | 5 | 5 | 5 | 5 | consensus: 5 |
| US-06 | 1 | 3 | 3 | 5 | 13 | 2 | 24-hour rule needs time input |
| US-06 | 2 | 5 | 5 | 5 | 8 | 5 | consensus: 5 |
| 1 | US-04 List upcoming events | 3 | test upcomingEvents 1, implement 2, CSV 3, CLI 2, review 1 | 9 |
| 2 | US-05 Register for an event | 5 | 2 tests 2, registerStudent 3, CSV repo 4, CLI 2, review 2 | 13 |
| 3 | US-06 Cancel a registration | 5 | tests 2, cancel 3, CSV rewrite 3, CLI 2, review 2 | 12 |
| 4 | US-01 Register a club | 3 | test 1, Club + status 3, CSV 3, CLI 2, review 1 | 10 |
Forecast 16 points · 44 task hours of 52 h capacity (8 h buffer)
- No cards? Use fingers (1, 2, 3, 5), a free online planning poker room, or write the number on paper and flip together. What matters is that nobody sees the others' votes first.
- The PO answers questions but, if also a developer, votes like everyone else; the SM facilitates and keeps each story under 10 minutes.
- Sprint 1 has no velocity history, so forecast from capacity: if the task hours exceed about 85 % of capacity, drop the last story instead of hoping.
- A task like "implement US-05" is too big; a good task names one class or one command and fits in one sitting.
Acceptance checklist
Implement the first stories test-first with GoogleTest
Medium Lead: Developers, in pairs 3–4 h per pair · during the sprintGoal
Deliver US-05 and US-04 as working, tested C++: RegistrationService::registerStudent enforcing the capacity and duplicate rules, and a clubhub list-events command showing upcoming events from data/events.csv. Work test-first: for every rule the Git history shows the red test commit before the green code commit.
Steps
- Check that your Lab-06 headers provide
Event(withid,startasstd::chrono::sys_secondsandcapacity),Registration,RegistrationStatusand theRegistrationRepositoryinterface. If not, add the fallback headers below; keep your own names if they differ, and adapt the tests. - Add the test double
tests/InMemoryRegistrationRepository.hpp(aRegistrationRepositorythat stores rows in astd::vector) and the GoogleTest target toCMakeLists.txt(starter). Create the branchfeature/US-05-register. - Red: write
TEST(RegistrationService, StudentCannotRegisterTwice)intests/registration_service_test.cpp, plus only the declarations needed to compile (the header below and aregisterStudentthat saves without checking). Runctest, see it fail, paste the failure intolab07/tdd-log.md, committest(US-05): student cannot register twice [red]. - Green: add the simplest code in
src/RegistrationService.cppthat passes; runctest; commitfeat(US-05): reject duplicate registration [green]. Refactor if the code is unclear (extractisRegistered), tests still green; commitrefactor(US-05): …. - Repeat red → green → refactor for
RejectsWhenCapacityReached(throwsEventFull) and forCancelledSeatCanBeTakenAgain(aCancelledregistration neither blocks the student nor uses a seat). Every acceptance criterion of US-05 has at least one test that asserts something. - US-04 on
feature/US-04-list-events, test-first again:upcomingEvents(events, now)hides events that have started and sorts by start time (passnowas a parameter so the test does not depend on the clock); thenCsvEventRepository::findAlland the CLI command insrc/main.cpp. Adddata/events.csvwith at least one past and two future events. - Open a pull request per story, reviewed by a member who did not write it; tick the definition of done in the PR description; merge; move the card to
Done. Completelab07/tdd-log.md: one row per rule with the red, green and refactor commit hashes.
Starter (fallback headers, service header, first test, CMake)
// include/clubhub/Event.hpp, Registration.hpp, RegistrationRepository.hpp
// (fallback only: use your Lab-06 headers if you have them)
namespace clubhub {
struct Event {
std::string id, clubId, title, location;
std::chrono::sys_seconds start{};
int capacity{0};
};
enum class RegistrationStatus { Registered, Cancelled, CheckedIn, Waitlisted };
struct Registration {
std::string eventId, studentId;
RegistrationStatus status{RegistrationStatus::Registered};
};
class RegistrationRepository {
public:
virtual ~RegistrationRepository() = default;
virtual std::vector<Registration> findByEvent(const std::string& eventId) const = 0;
virtual void save(const Registration& registration) = 0;
};
} // namespace clubhub
// include/clubhub/RegistrationService.hpp
#pragma once
#include <stdexcept>
#include <string>
#include "clubhub/Event.hpp"
#include "clubhub/RegistrationRepository.hpp"
namespace clubhub {
class RegistrationError : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};
class DuplicateRegistration : public RegistrationError {
public:
using RegistrationError::RegistrationError;
};
class EventFull : public RegistrationError {
public:
using RegistrationError::RegistrationError;
};
class RegistrationService {
public:
explicit RegistrationService(RegistrationRepository& registrations)
: registrations_{registrations} {}
// Registers studentId for event.
// Throws DuplicateRegistration or EventFull (rules of US-05).
Registration registerStudent(const Event& event, const std::string& studentId);
private:
RegistrationRepository& registrations_;
};
} // namespace clubhub
// tests/registration_service_test.cpp
#include <gtest/gtest.h>
#include "InMemoryRegistrationRepository.hpp"
#include "clubhub/RegistrationService.hpp"
using namespace clubhub;
using clubhub::testing::InMemoryRegistrationRepository;
namespace {
Event workshop(int capacity) {
Event e;
e.id = "E1";
e.title = "C++ Workshop";
e.capacity = capacity;
return e;
}
} // namespace
TEST(RegistrationService, StudentCannotRegisterTwice) {
InMemoryRegistrationRepository repo;
RegistrationService service{repo};
const Event event = workshop(30);
service.registerStudent(event, "S001");
EXPECT_THROW(service.registerStudent(event, "S001"), DuplicateRegistration);
}
// TODO red: RejectsWhenCapacityReached (capacity 2, third student -> EventFull)
// TODO red: CancelledSeatCanBeTakenAgain
# CMakeLists.txt (add what is missing; keep your earlier targets)
add_library(clubhub_core src/RegistrationService.cpp src/EventQueries.cpp
src/CsvEventRepository.cpp)
target_include_directories(clubhub_core PUBLIC include)
if(MSVC)
target_compile_options(clubhub_core PRIVATE /W4 /WX)
else()
target_compile_options(clubhub_core PRIVATE -Wall -Wextra -Werror)
endif()
add_executable(clubhub src/main.cpp)
target_link_libraries(clubhub PRIVATE clubhub_core)
include(FetchContent)
FetchContent_Declare(googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # needed for MSVC
FetchContent_MakeAvailable(googletest)
enable_testing()
add_executable(clubhub_tests tests/registration_service_test.cpp
tests/event_queries_test.cpp)
target_link_libraries(clubhub_tests PRIVATE clubhub_core GTest::gtest_main)
include(GoogleTest)
gtest_discover_tests(clubhub_tests)
Expected output
$ git log --oneline --reverse main -- src tests
3f1c2aa test(US-05): student cannot register twice [red]
8b04d1e feat(US-05): reject duplicate registration [green]
c7e9a10 refactor(US-05): extract isRegistered
d21f5b3 test(US-05): capacity cannot be exceeded [red]
0a9e6c4 feat(US-05): throw EventFull when capacity reached [green]
5d77b2f test(US-05): cancelled registration frees the seat [red]
91ac0e8 feat(US-05): ignore cancelled registrations [green]
e4b3f19 test(US-04): upcomingEvents hides past events, sorts by start [red]
6fd2c01 feat(US-04): upcomingEvents and CsvEventRepository [green]
b8a4d55 feat(US-04): list-events command
$ ctest --test-dir build
1/6 Test #1: RegistrationService.RegistersStudentWhenSeatsLeft ... Passed
2/6 Test #2: RegistrationService.StudentCannotRegisterTwice ...... Passed
3/6 Test #3: RegistrationService.RejectsWhenCapacityReached ...... Passed
4/6 Test #4: RegistrationService.CancelledSeatCanBeTakenAgain .... Passed
5/6 Test #5: EventQueries.ParsesAndFormatsDateTime ............... Passed
6/6 Test #6: EventQueries.UpcomingSkipsPastAndSortsByStart ....... Passed
100% tests passed, 0 tests failed out of 6
$ ./build/clubhub list-events
ID START TITLE CAP
E01 2026-11-05 18:00 Intro to Git workshop 40
E02 2026-11-07 09:00 Robotics open day 120
- Red commits live on the feature branch only;
mainreceives the branch through a pull request when everything is green, so the red commit is still in the history after the merge (use a merge commit or a rebase merge, not squash). - A compile error is not a useful red: add just enough declaration (header plus a
registerStudentthat saves and returns) so the test compiles and fails on its assertion. - Count seats with
std::ranges::count_ifoverfindByEvent(event.id), skippingCancelled; check the duplicate rule before the capacity rule, so a student who is already registered for a full event gets the right error. - Parsing
2026-11-05 18:00portably: read the numbers with astd::istringstream, build astd::chrono::year_month_day, check.ok(), and addhoursandminutestosys_days. The chapter's TDD slides show the style. - When pairing, add
Co-authored-by: Name <email>as the last line of the commit message so both partners appear in the history.
Acceptance checklist
Daily scrums, board updates and the sprint burndown
Medium Lead: Scrum Master 15 min per day + 45 minGoal
Inspect and adapt every day: hold a 15-minute daily scrum on at least 8 of the 10 sprint days, keep the board true, record the remaining work each day, and draw the sprint burndown (ideal versus actual) from that data with Mermaid.
Steps
- Fix the time and channel of the daily (for example 8:00, in person or a 15-minute call). Before it starts, every member moves their cards and updates the remaining hours of their tasks on the board.
- Run the daily around the Sprint Goal: each member answers yesterday, today, blockers. The SM notes it in
lab07/daily-scrum-log.mdwith the template below and stops at 15 minutes; discussions happen right after, with only the people involved. - Every blocker gets an owner and a follow-up in the log. A blocker still open at the next daily is escalated by the SM (to the PO for scope questions, to the TA for tooling problems).
- After each daily, append one row to
lab07/burndown-data.csv: day, date, remaining task hours summed from the board, ideal hours (total from Task 2 × (10 − day) / 10), and a note when tasks were added or removed. - At the end of the sprint, write
lab07/burndown.mdwith a Mermaidxychart-betadrawn from the CSV (first line ideal, second line actual) and three sentences interpreting it: where the team fell behind or caught up, and why. - Commit the log and the CSV at least every two days; different members commit them (the SM of the day, or whoever took the notes), so the history shows the team, not one person.
Starter (log template, CSV, chart skeleton)
## Day 4 · 2026-11-05 · 08:00-08:14 · SM: Sokha
Sprint Goal: students can list, register for and cancel events from the CLI
| Member | Yesterday | Today | Blockers |
|--------|-----------|-------|----------|
| __ | __ | __ | none |
Follow-ups: <blocker> -> <owner>, <action>, <resolved on day __>
Board: __ h remaining (ideal __ h)
day,date,remaining_h,ideal_h,note
0,2026-11-02,44,44.0,after sprint planning
1,2026-11-03,,39.6,
xychart-beta
title "Sprint 1 burndown, remaining task hours"
x-axis [D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10]
y-axis "Remaining hours" 0 --> 50
line [44, 39.6, 35.2, 30.8, 26.4, 22, 17.6, 13.2, 8.8, 4.4, 0]
line [44, __, __, __, __, __, __, __, __, __, __]
Expected output (rendered burndown and interpretation; grey = ideal, blue = actual)
---
config:
themeVariables:
xyChart:
plotColorPalette: "#6c757d, #0d6efd"
---
xychart-beta
title "Sprint 1 burndown, remaining task hours"
x-axis [D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10]
y-axis "Remaining hours" 0 --> 50
line [44, 39.6, 35.2, 30.8, 26.4, 22, 17.6, 13.2, 8.8, 4.4, 0]
line [44, 44, 46, 41, 37, 33, 27, 20, 14, 8, 3]
Days 1-2: flat, then +2 h: CSV rewrite
task found for US-06 (logged day 2).
Days 3-6: behind by about 10 h; the
daily on day 5 moved Malis from US-01
to pair on US-05.
Days 7-10: nearly caught up after
US-01 went back to the backlog (day 7,
logged). 3 h left on day 10: the
24-hour rule test of US-06, so US-06
is not done.
- Burn down task hours, not story points: with three or four stories a points burndown is a staircase that tells you nothing until the last day.
- Never edit past rows to make the curve look better; a line that goes up on the day you discovered work is honest and useful.
- GitHub Projects has built-in Insights charts; you may add a screenshot, but the Mermaid chart must come from your CSV so the data is in the repository.
- If the daily keeps running long, the SM asks "does this help us reach the Sprint Goal today?" and moves the topic to after the daily.
Acceptance checklist
Sprint review, retrospective and record what changed
Hard Lead: PO (review), Scrum Master (retrospective) 90 min · week 10Goal
Close Sprint 1: demonstrate the increment to the client, judge honestly what is done against the definition of done, turn the feedback into backlog changes, run a Start/Stop/Continue retrospective with two concrete actions, and record what this lab produced in lab07/README.md.
Steps
- Write the demo script in
lab07/sprint-review.md: the Sprint Goal, then numbered demo steps with the exact commands and data (git clone, build,clubhub list-events, a registration, a duplicate attempt, a full event), and who presents each step. Demo only from a clean build ofmain. - Hold the review with the client (instructor or TA), at most 45 minutes: the PO states the goal and what was selected, developers run the script, the client tries it. No slides.
- Fill the done / not done table: for each sprint backlog item, every definition-of-done criterion ticked or not, and the verdict. Velocity = points of items that are fully done. Not-done items go back to
Backlogwith their remaining work written in the issue. - Record the client's feedback; each point becomes a new issue or a change to an existing one (give the issue numbers). The PO reorders the backlog and writes a Sprint 2 forecast range from the velocity.
- Retrospective (SM facilitates, 45 minutes, team only): everyone writes Start / Stop / Continue notes silently for 5 minutes, groups them, dot-votes (3 votes each), and the team chooses two actions, each with an owner, a due date and how you will know it worked. Put them in
lab07/retrospective.mdand as issues labelledimprovementin Sprint 2. - Hand over the Scrum Master role for Sprint 2 and save
lab07/board-end.png. - Record what changed: create
lab07/README.mdwith a table of every Lab-07 file and a one-line description, a table Inputs reused (the Lab-01, Lab-04, Lab-05 and Lab-06 files or issues you used, with their version or commit hash, or "fallback" if you used the Setup fallback), the code files added or changed insrc/,include/clubhub/,tests/, and a change-log row (date, version, what changed). Commit asLab-07 task 5: sprint review, retrospective, README, push and open the pull request.
Starter (review, retrospective and README skeletons)
# Sprint 1 review · v1.0 · 2026-11-13
Sprint Goal: __ Client: __ Attendees: __
## Demo script
1. (__) git clone … && cmake -S . -B build && cmake --build build
2. (__) ./build/clubhub list-events -> expect 2 upcoming events
3. (__) register S001 for E01 twice -> second attempt refused
## Done / not done against the definition of done
| Item | Pts | Builds, 0 warn | Tests | Reviewed | Docs | PO ok | Verdict |
|-------|-----|----------------|-------|----------|------|-------|---------|
Velocity: __ points (done items only)
## Client feedback -> backlog
| Feedback | Issue | Change |
## Sprint 2 forecast: __ to __ points
# Sprint 1 retrospective · v1.0 · 2026-11-13 · facilitator: __
| Start | Stop | Continue | (votes in brackets)
## Actions
| # | Action | Owner | Due | How we will know it worked | Issue |
# Lab-07 · Sprint 1 · README
| File | Description |
|------|-------------|
| board.md, board-start.png, board-end.png | … |
## Inputs reused
| Input | Version or commit |
|-------|-------------------|
| lab05 backlog / issues #11-#24 | commit __ |
## Code added or changed
| Path | Change |
## Change log
| Date | Version | Change |
|------|---------|--------|
| 2026-11-13 | 1.0 | Sprint 1 closed: velocity __, SM handed over to __ |
Expected output (excerpt)
| US-04 | 3 | ✓ | ✓ 2 tests | ✓ PR #31 | ✓ | ✓ | Done |
| US-05 | 5 | ✓ | ✓ 4 tests | ✓ PR #29 | ✓ | ✓ | Done |
| US-06 | 5 | ✓ | ✗ 24 h rule untested | ✓ PR #35 | ✗ | ✗ | Not done |
Velocity: 8 points. US-06 back to the backlog: "24-hour rule + test, 4 h".
Client feedback: show remaining seats in list-events (#38, new, 2 pts);
cancelled students should be able to re-register (already done).
Sprint 2 forecast: 8 to 12 points (one sprint of history, so a wide range).
Retrospective actions
| 1 | WIP limit 2 in "In review"; SM checks at each daily | Vicheka | every daily | no PR waits > 1 day | #40 |
| 2 | "ctest passed" line in the PR template | Rithy | Mon wk 11 | 0 red merges | #41 |
- An item that fails one DoD criterion is not done, even if it "basically works". Saying so openly at the review is exactly what Scrum asks for, and it costs no marks here.
- Rehearse the demo script once on a fresh clone the day before: missing
data/events.csvor a hard-coded path is the most common demo failure. - "Communicate better" is not an action. "Post blockers in the team chat before 20:00, SM summarises them at the daily" is.
lab07/README.mdlists the earlier-lab files you actually used, with a commit hash (git log -1 --format=%h -- lab05/); if you used a Setup fallback, write "fallback" instead of repairing the earlier lab.
Acceptance checklist
Challenge: one XP practice for the whole sprint, measured
Hard 90–120 min spread over the sprint · optional, extra creditScenario
Your team suspects that work gets stuck: pull requests wait for days, and knowledge of the CSV code sits in one head. Instead of arguing, run an experiment for the whole of Sprint 1 with one practice, collect simple data, and decide with evidence whether to keep it in Sprint 2.
Requirements
- Choose A · pair programming rotation (pairs change every two sprint days, driver and navigator swap every 25 minutes, every commit made in a pair carries a
Co-authored-by:trailer) or B · Kanban WIP limit (at most one card per developer inIn progressand at most 2 inIn review; when a column is full you help finish instead of starting). - Before day 1, write in
lab07/xp-experiment.mda hypothesis with a measurable prediction (for example "median cycle time per task drops below 1 day") and the exact rule the team follows. - Collect data in
lab07/xp-data.csvduring the sprint. For A: per task, pair or solo, cycle time, defects found in review; share of co-authored commits (git log --format=%b | grep -c Co-authored-by). For B: per task, the timestamps when it enteredIn progressandDone, the daily count of cards in progress, the hours each PR waited for review. Use days 1 to 3 as the baseline if you need a before/after comparison. - Analyse: a results table (median and maximum, baseline versus experiment) and one Mermaid chart (
xychart-betabars or lines) drawn from the CSV. - Conclude in five to eight sentences: was the hypothesis supported, what else could explain the numbers (small sample, exam week, different story sizes), and a recommendation for Sprint 2 that you then add as a retrospective action.
Skeleton
# XP experiment · Sprint 1 · v1.0
Practice: B · Kanban WIP limit Rule: 1 card per developer in progress, 2 in review
Hypothesis: with the limit, the median cycle time per task falls from about 2 days
to at most 1 day, and no pull request waits more than 1 day for review.
## Data (xp-data.csv)
task,story,phase,started,done,cycle_days,review_wait_h
T-05,US-05,baseline,2026-11-03,2026-11-05,2,30
## Results
| Measure | Baseline (days 1-3) | Experiment (days 4-10) |
|--------------------------|---------------------|------------------------|
| Median cycle time (days) | | |
| Max review wait (hours) | | |
## Conclusion and recommendation for Sprint 2
Expected output (excerpt)
| Median cycle time (days) | 2.0 (n = 6) | 1.0 (n = 14) |
| Max review wait (hours) | 30 | 9 |
| Cards in progress (avg) | 7.3 | 4.1 |
Supported, with caveats: n is small, and days 1-3 held the unfamiliar setup work.
Recommendation: keep the review limit of 2 in Sprint 2 (retro action #40); drop
the in-progress limit to 1 per developer only for stories above 5 points.
- Decide the measures before the sprint starts; choosing them afterwards invites picking the numbers that look good.
- Cycle time is easiest to collect if the person who moves a card also writes the date in the issue (or read the timestamps from the project's item history).
- Use the median, not the mean: one task blocked for five days would dominate an average of fifteen tasks.
- A result that does not support the hypothesis is still a full-mark result if the data and the reasoning are honest.
Acceptance checklist
Grading rubric
| Component | Points | Full marks when… |
|---|---|---|
| Task 1 · Board, refinement, DoD | 15 | Five workflow columns and three fields; every Must story on the board with points and at least two scenarios; ordered backlog; column policies; DoD with six or more checkable items approved by all. |
| Task 2 · Sprint planning | 15 | Per-member capacity; outcome-based Sprint Goal; poker votes per member and round with arguments for five stories; sprint backlog within capacity including US-04 and US-05; tasks of at most 4 h. |
| Task 3 · First stories with TDD | 25 | Duplicate and capacity rules implemented and tested; red-before-green commits for every rule; list-events filters and sorts with a test; zero warnings, ctest green; reviewed pull requests; tdd-log.md complete. |
| Task 4 · Dailies and burndown | 15 | Eight or more daily entries for every member; blockers with owners; CSV consistent with board and log; Mermaid burndown with ideal and actual lines and an interpretation. |
| Task 5 · Review, retrospective, README | 20 | Demo script and review with the client; honest done / not-done table and velocity; feedback linked to issues; two actions with owner, date and measure; complete lab07/README.md. |
| Teamwork and code quality | 10 | Every member has commits and reviews in the sprint; modern C++ (no raw new/delete, const correctness, exceptions for rule violations); clear commit messages with story ids. |
| ★ Challenge (extra credit) | +20 | Hypothesis and rule written before the sprint, data collected during it, table and chart from the CSV, honest conclusion with a Sprint 2 action. |
| Total | 100 (+20) |
registerStudent · −5 per test that asserts nothing · −5 if the sprint backlog exceeds the stated capacity without a written reason · −10 if the burndown does not match the board and the daily log (a curve drawn afterwards) · −5 if a retrospective action has no owner or no date · −10 if cmake build or ctest fails from a clean checkout of lab-07 · −5 per member with no commit in the sprint.Submission
- Repository layout at the end of Sprint 1:
clubhub/ ├── CMakeLists.txt ├── data/events.csv ├── include/clubhub/ Event.hpp Registration.hpp RegistrationRepository.hpp │ EventRepository.hpp EventQueries.hpp RegistrationService.hpp ├── src/ main.cpp RegistrationService.cpp EventQueries.cpp │ CsvEventRepository.cpp ├── tests/ InMemoryRegistrationRepository.hpp │ registration_service_test.cpp event_queries_test.cpp ├── .github/ISSUE_TEMPLATE/user-story.md └── lab07/ board.md board-start.png board-end.png definition-of-done.md sprint-planning.md tdd-log.md daily-scrum-log.md burndown-data.csv burndown.md sprint-review.md retrospective.md README.md (xp-experiment.md xp-data.csv) - Self-check from a clean checkout, then check the TDD order in the history:
git clone <your-repo-url> check && cd check && git switch lab-07 cmake -S . -B build && cmake --build build && ctest --test-dir build ./build/clubhub list-events git log --oneline --reverse -- src tests | grep -E "\[red\]|\[green\]" git shortlog -sn --since="2 weeks ago" # every member appears - Branch
lab-07, pull request titledLab-07 – <team name>, with the project board URL and the sprint velocity in the description. The instructor reviews the pull request, the board and the review record.