Compare commits

..
Author SHA1 Message Date
TirsvadandClaude Sonnet 5.5 2b65618c1a Resolve pending commit links in the RC-008 change
CI / checks (push) Successful in 52s
CI / checks (pull_request) Successful in 54s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 21:46:06 +08:00
Tirsvad 861edbfa92 Re-review the Business Case and accept v4
CI / checks (push) Successful in 56s
Add RC-008 for the Business Case after v3 added objective 6. Two
defects found: objective 6 had no success criterion, and the Python
version was worded differently in the assumption and the constraint.
BC-001 v4 fixes both and is accepted; v3 is deprecated. The
traceability matrix now points to RC-008.
No issue closed: review and documentation only, no code changed.
2026-10-06 21:44:11 +08:00
Tirsvad 71a27ddc1f Re-review the Business Case and accept v4
Add RC-008 for the Business Case after v3 added objective 6. Two
defects found: objective 6 had no success criterion, and the Python
version was worded differently in the assumption and the constraint.
BC-001 v4 fixes both and is accepted; v3 is deprecated. The
traceability matrix now points to RC-008.
No issue closed: review and documentation only, no code changed.
2026-10-06 21:44:02 +08:00
Tirsvad 693cfd5b24 Merge pull request 'README: mention docs/sqa in the project layout' (#30) from docs-readme-layout into main
CI / checks (push) Successful in 53s
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 4s
Reviewed-on: Tirsvad-Udemy-100_days_of_code/015-coffee_machine#30
2026-10-06 15:16:40 +02:00
TirsvadandClaude Sonnet 5.5 93928b5f27 Describe docs/sqa in the README project layout
CI / checks (pull_request) Successful in 1m5s
CI / checks (push) Successful in 1m0s
The project layout table did not mention the review records and the
traceability matrix under docs/sqa/.

No issue closed: documentation only, no code changed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 21:16:03 +08:00
Tirsvad 58116dcb5d Merge pull request 'MIL-004: source code review and CI' (#29) from mil-004-code-review-and-ci into main
CI / checks (push) Successful in 1m7s
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Failing after 4s
Reviewed-on: Tirsvad-Udemy-100_days_of_code/015-coffee_machine#29
2026-10-06 14:59:32 +02:00
TirsvadandClaude Sonnet 5.5 f192f227e2 Resolve pending commit links in MIL-004 review records
CI / checks (push) Successful in 51s
CI / checks (pull_request) Successful in 56s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 20:56:18 +08:00
Tirsvad 00d47e244c Review source code and add CI for MIL-004
CI / checks (push) Successful in 1m5s
Add RC-006 (source code against QC-PY-001) and RC-007 (MIL-004), and
update the traceability matrix. Set lower bounds for the dev
dependencies, the one optional Fail in RC-006. Add a Gitea Actions
workflow running pytest, ruff and mypy, and describe it in the README.
Accept MIL-004 and link its Milestone in the Project Plan.

Closes #24
Closes #25
Closes #26
Closes #27
Closes #28

Task: MIL-004#1
Task: MIL-004#2
Task: MIL-004#3
Task: MIL-004#4
Task: MIL-004#5
2026-10-06 20:50:41 +08:00
Tirsvad ffffee04f4 Merge pull request 'SQA review records and MIL-004 plan (Code Review and CI)' (#23) from rc-planning-review into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Failing after 6s
Reviewed-on: Tirsvad-Udemy-100_days_of_code/015-coffee_machine#23
2026-10-06 14:44:57 +02:00
TirsvadandClaude Sonnet 5.5 2b9e46b81f Resolve pending commit links in review and planning documents
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 20:43:23 +08:00
Tirsvad 8ef00aafaa Add SQA review records and plan MIL-004 Code Review and CI
Add RC-001 to RC-005 for the Business Case, Stakeholder Analysis and
MIL-001 to MIL-003, checked against their QC checklists, and a
traceability matrix. The reviews found a missing duration constraint in
the Business Case; BC-001 v2 adds it, and SA-001 v2 records the
sign-off. Register RC and TM in the artifact registry.

Plan MIL-004 (review the source against QC-PY-001, add a CI workflow)
as Proposed, with Business Case objective 6, a Project Plan gateway
row and a matrix entry. The milestone is not yet synced to the git host.

No issue closed: planning and review documents only, no code changed.
2026-10-06 20:42:51 +08:00
Tirsvad 1403dacf0e Merge pull request 'MIL-003: tests, Doxygen check and repository metadata' (#22) from mil-003-quality-and-publication into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Failing after 3s
Reviewed-on: Tirsvad-Udemy-100_days_of_code/015-coffee_machine#22
2026-10-06 13:57:11 +02:00
Tirsvad 8a0e03a6ff Add tests and publish repository metadata for MIL-003
Add 19 pytest tests covering the report, resource check, coin
processing, refunds, change, profit, make_coffee and the main loop.
Add a tests package and put the project root on the pytest path.
Doxygen output is clean. The repository description and topics were set
on the git host.

Closes #13
Closes #14
Closes #15
Closes #16
Closes #17

Task: MIL-003#1
Task: MIL-003#2
Task: MIL-003#3
Task: MIL-003#4
Task: MIL-003#5
2026-10-06 19:56:08 +08:00
Tirsvad 9b5eff7163 Merge pull request 'MIL-002: coffee machine core' (#21) from mil-002-coffee-machine-core into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Failing after 3s
Reviewed-on: Tirsvad-Udemy-100_days_of_code/015-coffee_machine#21
2026-10-06 13:52:17 +02:00
Tirsvad d570489a33 Implement coffee machine core for MIL-002
Add the menu, coin values, starting resources and message texts to
constants.py. Implement report, is_resource_sufficient, process_coins,
is_transaction_successful, make_coffee and the main loop. Money is
counted in whole cents. The smoke test now feeds "off" so the loop
exits.

Closes #6
Closes #7
Closes #8
Closes #9
Closes #10
Closes #11
Closes #12

Task: MIL-002#1
Task: MIL-002#2
Task: MIL-002#3
Task: MIL-002#4
Task: MIL-002#5
Task: MIL-002#6
Task: MIL-002#7
2026-10-06 19:49:49 +08:00
Tirsvad 0f26d8d91e Merge pull request 'MIL-001: README with setup, run and test instructions' (#20) from mil-001-readme into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Failing after 4s
Reviewed-on: Tirsvad-Udemy-100_days_of_code/015-coffee_machine#20
2026-10-06 13:45:59 +02:00
Tirsvad c2a8767fb0 Add README with setup, run, test and Doxygen instructions
Follow the Higher Lower README template: project description, local
.venv setup with pip upgrade, running the machine, tests and code
checks, source documentation, layout and license.

Closes #5
Task: MIL-001#5
2026-10-06 19:45:09 +08:00
Tirsvad 322efae5b4 Merge pull request 'MIL-001: project skeleton and tooling' (#19) from mil-001-project-setup into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 5s
Reviewed-on: Tirsvad-Udemy-100_days_of_code/015-coffee_machine#19
2026-10-04 18:31:21 +02:00
Tirsvad 572074ae73 Set up project skeleton and tooling for MIL-001
Add pyproject.toml (Python >=3.13, no runtime dependencies, pytest, ruff
and mypy as dev extras), a Doxyfile that scans src/, the
src/coffee_machine package with stub constants and main modules, and a
pytest smoke test. Ignore Doxygen output in .gitignore.

Closes #1
Closes #2
Closes #3
Closes #4
Refs #5

Task: MIL-001#1
Task: MIL-001#2
Task: MIL-001#3
Task: MIL-001#4
2026-10-05 00:30:18 +08:00
Tirsvad c3e225b049 Merge pull request 'Add planning baseline: BC, SA, PP and MIL-001 to MIL-003' (#18) from plan-baseline into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 6s
Reviewed-on: Tirsvad-Udemy-100_days_of_code/015-coffee_machine#18
2026-10-04 18:26:38 +02:00
29 changed files with 1241 additions and 51 deletions
+26
View File
@@ -0,0 +1,26 @@
name: CI
on:
push:
pull_request:
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
- name: Tests
run: python -m pytest
- name: Lint
run: python -m ruff check src tests
- name: Format check
run: python -m ruff format --check src tests
- name: Types
run: python -m mypy src tests
+3
View File
@@ -174,3 +174,6 @@ cython_debug/
# PyPI configuration file
.pypirc
# Doxygen output
docs/doxygen/
+14
View File
@@ -0,0 +1,14 @@
# Doxygen configuration for the coffee machine. Run: doxygen Doxyfile
PROJECT_NAME = "Coffee Machine"
PROJECT_BRIEF = "Day 15 of Udemy's 100 Days of Code Python bootcamp"
OUTPUT_DIRECTORY = docs/doxygen
INPUT = src
FILE_PATTERNS = *.py
RECURSIVE = YES
OPTIMIZE_OUTPUT_JAVA = YES
EXTRACT_ALL = YES
JAVADOC_AUTOBRIEF = YES
GENERATE_HTML = YES
GENERATE_LATEX = NO
QUIET = YES
WARN_IF_UNDOCUMENTED = YES
+112 -1
View File
@@ -1,2 +1,113 @@
# 015-coffee_machine
# Coffee Machine
A console game from Udemy's *100 Days of Code: The Complete Python Pro Bootcamp*
(day 15). A virtual coffee machine serves espresso, latte and cappuccino. Pay with
US coins (quarters, dimes, nickels and pennies). The machine checks its water, milk
and coffee before it serves, refunds you if you pay too little and gives change if
you pay too much.
```
What would you like? (espresso/latte/cappuccino): latte
Please insert coins.
How many quarters?: 10
How many dimes?: 0
How many nickels?: 0
How many pennies?: 0
Here is your latte. Enjoy!
```
- Python 3.13 or newer, no runtime dependencies.
- Type `report` to see the water, milk, coffee and money in the machine, and `off`
to switch the machine off.
- The function names follow the assignment: `is_resource_sufficient`,
`process_coins`, `is_transaction_successful`, `make_coffee`.
- Money is counted in whole cents inside the program, so change is never off by a
rounding error.
## Requirements
- [Python](https://www.python.org/downloads/) 3.13 or newer
- Optional: [Doxygen](https://www.doxygen.nl/) to build the source documentation
## Set up
Create a local virtual environment in `.venv`, activate it, and upgrade `pip`.
Windows (PowerShell):
```powershell
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
```
Windows (Git Bash), Linux and macOS:
```bash
python -m venv .venv
source .venv/Scripts/activate # Linux/macOS: source .venv/bin/activate
python -m pip install --upgrade pip
```
The machine itself needs nothing more. To run the tests and the code checks, install
the development tools:
```bash
python -m pip install -e ".[dev]"
```
## Run the coffee machine
```bash
python -m coffee_machine
```
Type `espresso`, `latte` or `cappuccino` and press Enter, then enter the coins.
Type `report` for the machine's resources, or `off` to quit.
## Run the tests
```bash
python -m pytest
```
Check the code style and types:
```bash
python -m ruff check src tests
python -m ruff format --check src tests
python -m mypy src tests
```
## Continuous integration
On every push and pull request, `.gitea/workflows/ci.yml` installs the project on
Python 3.13 and runs the same checks as above: `pytest`, `ruff check`,
`ruff format --check` and `mypy`. To run all of them locally:
```bash
python -m pytest && python -m ruff check src tests && python -m ruff format --check src tests && python -m mypy src tests
```
## Build the source documentation
The source uses Doxygen comments. The HTML output goes to `docs/doxygen/html`.
```bash
doxygen Doxyfile
```
## Project layout
| Path | Content |
| --- | --- |
| `src/coffee_machine/` | The machine: `main.py` (the loop), `constants.py` (menu, coins, starting resources) |
| `tests/` | pytest tests |
| `docs/` | Planning documents (business case, plan, milestones) and SQA review records with the traceability matrix (`docs/sqa/`) |
| `pyproject.toml` | Project configuration |
| `Doxyfile` | Doxygen configuration |
| `.gitea/workflows/` | CI workflow |
## License
GNU Affero General Public License v3.0. See [LICENSE](LICENSE).
+45 -43
View File
@@ -1,43 +1,45 @@
# Artifact Registry
This project's artifact state. Types, short names and `CrossReference
Candidates` come from the framework catalog
(`framework/registry/artifact-catalog.md`); this file only records where
each document lives in *this* project and the next version to use.
Delete rows for types you don't use. Add a row the first time you create a
document of a type. `Primary File` may contain a glob (e.g.
`docs/uc-*/uc.md`); `framework/scripts/find-crossreferences.sh` reads it.
| Short Name | Artifact Type | Primary File | Next Available Version |
| --- | --- | --- | --- |
| BC | Business Case | docs/business-case.md | 002 |
| SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 |
| PP | Project Plan | docs/project-plan.md | 002 |
| MIL | Milestone / Gateway | docs/milestones/*.md | 004 |
## Languages
Set the PO language when the project starts; `project-planning` asks for it
if it is missing. A translated artifact is named `<artifact>.<language>.md`
(for example `business-case.da.md`); the English file stays the source.
| Setting | Value |
| --- | --- |
| PO language | en |
| High-level register | IT Executive English |
| Technical register | IT Professional English |
| Artifact types | Register | Also kept as a PO-language file |
| --- | --- | --- |
| BC, KPI, PP, MIL | IT Executive English | Yes |
| SA, BMC, BPMN, UCD, US, UC, SSD, DM, RA, GOV, DICT | IT Professional English | Yes |
| OC, SD, DCD, ERD, ADR, TM, RC, QC, source code | IT Professional English | No |
## Notes
- "Next Available Version" is the zero-padded (3-digit) version to use the
*next* time a new document of that type is created. Increment it only when
a brand-new document is created, not when an existing document's
`## Version History` gets a row.
- `ADR` uses 4 digits (`0001`); `RC` is sequential across all artifact types.
# Artifact Registry
This project's artifact state. Types, short names and `CrossReference
Candidates` come from the framework catalog
(`framework/registry/artifact-catalog.md`); this file only records where
each document lives in *this* project and the next version to use.
Delete rows for types you don't use. Add a row the first time you create a
document of a type. `Primary File` may contain a glob (e.g.
`docs/uc-*/uc.md`); `framework/scripts/find-crossreferences.sh` reads it.
| Short Name | Artifact Type | Primary File | Next Available Version |
| --- | --- | --- | --- |
| BC | Business Case | docs/business-case.md | 002 |
| SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 |
| PP | Project Plan | docs/project-plan.md | 002 |
| MIL | Milestone / Gateway | docs/milestones/*.md | 005 |
| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 009 |
| TM | Traceability Matrix | docs/sqa/traceability-matrix.md | 002 |
## Languages
Set the PO language when the project starts; `project-planning` asks for it
if it is missing. A translated artifact is named `<artifact>.<language>.md`
(for example `business-case.da.md`); the English file stays the source.
| Setting | Value |
| --- | --- |
| PO language | en |
| High-level register | IT Executive English |
| Technical register | IT Professional English |
| Artifact types | Register | Also kept as a PO-language file |
| --- | --- | --- |
| BC, KPI, PP, MIL | IT Executive English | Yes |
| SA, BMC, BPMN, UCD, US, UC, SSD, DM, RA, GOV, DICT | IT Professional English | Yes |
| OC, SD, DCD, ERD, ADR, TM, RC, QC, source code | IT Professional English | No |
## Notes
- "Next Available Version" is the zero-padded (3-digit) version to use the
*next* time a new document of that type is created. Increment it only when
a brand-new document is created, not when an existing document's
`## Version History` gets a row.
- `ADR` uses 4 digits (`0001`); `RC` is sequential across all artifact types.
+9 -3
View File
@@ -9,7 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-05 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [c0c3940] |
| 2026-10-06 | Deprecated | Jens Tirsvad Nielsen | S01 | Added objective 6 and scope for code review and CI | [8ef00aa] |
| 2026-10-06 | Accepted | Jens Tirsvad Nielsen | S01 | Added success criterion 6 and clarified the Python version (RC-008 actions) | [71a27dd] |
---
@@ -45,6 +46,7 @@ participants and for viewers browsing the repository.
3. Cover the behaviour with automated pytest tests.
4. Document how to set up a local `.venv` and run the program and tests.
5. Publish the repository with a description, topics and README.
6. Review the source code against the Python quality checklist and run the tests and code checks automatically on every push.
## Scope
@@ -55,6 +57,7 @@ participants and for viewers browsing the repository.
- `pyproject.toml`, Python `.gitignore`, `Doxyfile`, `README.md`.
- Project documents under `docs/`.
- Repository description and topics on the git host.
- A review record for the source code and a CI workflow (MIL-004).
### Out of Scope
@@ -89,6 +92,7 @@ professional project hygiene, and the goal of sharing readable solutions.
| 3 | Runtime dependencies | 0 | `pyproject.toml` `dependencies` is empty |
| 4 | Setup documented | A new reader can run program and tests from README | README walkthrough |
| 5 | Repository metadata | Description and at least 3 topics set | Repository page |
| 6 | Code reviewed and checked automatically | Source review record with verdict Go; latest CI run on `main` green | `docs/sqa/reviews/` and the CI run on the git host |
## Risks
@@ -105,10 +109,11 @@ professional project hygiene, and the goal of sharing readable solutions.
## Constraints
- Python greater than 3.13 as requested, `venv` for environments, pytest for tests.
- Python 3.13 or newer (the request says "greater than 3.13"; read as `>=3.13`), `venv` for environments, pytest for tests.
- Constants live in `constants.py`.
- Source files use Doxygen comments.
- Nothing is committed or pushed unless the user asks.
- Duration: one week, 2026-10-05 to 2026-10-12.
## Cost–Benefit Assessment
@@ -131,4 +136,5 @@ Proceed — the scope is small, well specified and delivers a reusable example.
---
[SA-001]: ./stakeholder-analysis.md
[c0c3940]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/c0c3940551db0d7d96492cc734976d0436051ff9
[8ef00aa]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/8ef00aafaaa193ea565f1a45238d31e963a5e05d
[71a27dd]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/71a27ddc1f5555c1b836b1556fab292ebc9deb62
@@ -0,0 +1,70 @@
# MIL-004 Code Review and CI
## Metadata
| Key | Value |
| --- | --- |
| ID | MIL-004 |
| CrossReference | [BC-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-06 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [8ef00aa] |
---
## Purpose
Decide whether the source code has been reviewed against the Python quality checklist and is checked automatically on every push.
## Deliverable
`RC-006` review record for the source code, fixes for its findings, a CI workflow running the tests and code checks, and a README section describing it.
## Go / No-Go Criteria
| # | Criterion (objectively checkable) | Go | No-Go |
| --- | --- | --- | --- |
| 1 | `docs/sqa/reviews/` has an RC record for the source against `QC-PY-001` | Present with a verdict | Missing |
| 2 | Every Fail in that record has a closed action item or a recorded deviation | All handled | Open Fail |
| 3 | The CI workflow runs pytest, ruff check, ruff format --check and mypy on push and pull request | All four run | Any missing |
| 4 | The latest CI run on `main` is green | Green | Red or none |
| 5 | README describes the CI and the matching local commands | Present | Missing |
## Dependencies
| Depends on | Reason |
| --- | --- |
| MIL-003 | Tests and metadata must exist before they are reviewed and automated |
## Traceability
| Business Case objective / KPI / user story | Reference |
| --- | --- |
| Objective 6 (review and CI), Objective 4 | [BC-001] |
## Ownership
| Role | Stakeholder ID (SA) |
| --- | --- |
| Owner | S01 |
| Approving reviewer | S01 |
## Target Date
2026-10-12 — inside the duration constraint in [BC-001].
## Tasks
| # | Task | Summary | Needs its own Use Case/User Story? | Reference |
| --- | --- | --- | --- | --- |
| 1 | Review source code against QC-PY-001 | Create RC-006 with `new-artifact.sh RC` for `src/` and `tests/` against `framework/qc/qc-programming-python.md`. Criterion 10 (Design Class Diagram) is N-A because no DCD exists. Every Fail becomes an action item. | No | Objective 6 |
| 2 | Fix code review findings | Fix each Fail from RC-006 under `src/` and `tests/`, or record a justified deviation in the record. If the review has no Fail, close this task without a code change. | No | Objective 6 |
| 3 | Add CI workflow | Add `.gitea/workflows/ci.yml` that sets up Python 3.13, installs `.[dev]`, and runs `pytest`, `ruff check`, `ruff format --check` and `mypy` on push and pull request. Check first that the git host has an Actions runner. | No | Objective 6 |
| 4 | Document CI in README | Add a short section to README.md saying what the CI runs and how to run the same checks locally. | No | Objective 4 |
| 5 | Update traceability matrix | Add the RC-006 review to `docs/sqa/traceability-matrix.md` and close the milestone's row there. | No | Objective 6 |
---
[BC-001]: ../business-case.md
[8ef00aa]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/8ef00aafaaa193ea565f1a45238d31e963a5e05d
+11 -2
View File
@@ -4,12 +4,13 @@
| Key | Value |
| --- | --- |
| ID | PP-001 |
| CrossReference | [BC-001], [SA-001], [MIL-001], [MIL-002], [MIL-003] |
| CrossReference | [BC-001], [SA-001], [MIL-001], [MIL-002], [MIL-003], [MIL-004] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-05 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [c0c3940] |
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S01 | Added MIL-004 Code Review and CI | [8ef00aa] |
---
@@ -31,6 +32,7 @@ published project, within the short assignment timeline in [BC-001].
| Project Setup | [MIL-001] | 2026-10-05 to 2026-10-07 | 2026-10-07 | S01 | none | Skeleton, tooling, README | [Milestone 40] |
| Coffee Machine Core | [MIL-002] | 2026-10-08 to 2026-10-10 | 2026-10-10 | S01 | none | Working program | [Milestone 41] |
| Quality and Publication | [MIL-003] | 2026-10-11 to 2026-10-12 | 2026-10-12 | S01 | none | Tests, Doxygen, published repo | [Milestone 42] |
| Code Review and CI | [MIL-004] | 2026-10-07 to 2026-10-12 | 2026-10-12 | S01 | none | Source review record, CI workflow | [Milestone 48] |
```plantuml
@startgantt
@@ -41,6 +43,8 @@ Project starts 2026-10-05
[MIL-001 Go/No-Go] happens 2026-10-07
[MIL-002 Go/No-Go] happens 2026-10-10
[MIL-003 Go/No-Go] happens 2026-10-12
[Code Review and CI] starts 2026-10-07 and ends 2026-10-12
[MIL-004 Go/No-Go] happens 2026-10-12
@endgantt
```
@@ -53,11 +57,12 @@ Project starts 2026-10-05
| `pyproject.toml`, `.gitignore`, `Doxyfile`, README | [MIL-001] |
| Project documents | Planning, before [MIL-001] |
| Repository description and topics | [MIL-003] |
| Source code review record and CI workflow | [MIL-004] |
## Dependencies
```
MIL-001 -> MIL-002 -> MIL-003
MIL-001 -> MIL-002 -> MIL-003 -> MIL-004
```
A No-Go moves all later dates by the same amount.
@@ -66,6 +71,7 @@ A No-Go moves all later dates by the same amount.
| Risk | Impact | Mitigation |
| --- | --- | --- |
| The git host has no Actions runner | CI cannot run | Check the host first; if none, keep the workflow file and document the local commands |
| README template not supplied | README task blocked | Ask S01 for the template before the task |
| Doxygen not installed locally | Cannot verify docs | Document the install step in README |
@@ -82,7 +88,10 @@ A No-Go moves all later dates by the same amount.
[MIL-001]: ./milestones/mil-001-project-setup.md
[MIL-002]: ./milestones/mil-002-coffee-machine-core.md
[MIL-003]: ./milestones/mil-003-quality-and-publication.md
[MIL-004]: ./milestones/mil-004-code-review-and-ci.md
[Milestone 40]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/milestone/40
[Milestone 41]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/milestone/41
[Milestone 42]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/milestone/42
[c0c3940]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/c0c3940551db0d7d96492cc734976d0436051ff9
[8ef00aa]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/8ef00aafaaa193ea565f1a45238d31e963a5e05d
[Milestone 48]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/milestone/48
+49
View File
@@ -0,0 +1,49 @@
# RC-001 Business Case Review
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-001 |
| CrossReference | [BC-001], [QC-BC-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | [8ef00aa] |
---
## Artifact Under Review
- Instance reviewed: [BC-001] (docs/business-case.md)
- Checklist used: [QC-BC-001] (`QC-BC-001`)
- Independence: S01 is the only stakeholder and is also the recorded author, so the reviewer-is-not-author rule cannot be met. The documents were drafted by Claude on S01's behalf; S01 reviews and signs. This is a known limit of a one-person project, not a clean independent review.
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | ROI/Cost-Benefit analysis is quantitative, or where qualitative, is explicitly justified | Pass | Cost-Benefit table is explicitly qualitative and says why (no money involved). |
| 2 | Risks are identified with documented impact and mitigation | Pass | Three risks, each with impact and mitigation. |
| 3 | Success criteria are measurable, stating explicit targets rather than vague aspirations | Pass | Five criteria with targets and a measure each. |
| 4 | Scope explicitly separates In Scope vs Out of Scope | Pass | `### In Scope` and `### Out of Scope` are separate. |
| 5 | Stakeholders are cross-referenced to Stakeholder Analysis IDs rather than re-described inline | Pass | Stakeholders table cites S01 to S03 only; no roles are re-described. |
| 6 | Methodology and quality-standard foundation are stated explicitly (e.g. ISO/IEC 25010, Larman) | Pass | Methodology section names the framework, ISO/IEC 25010:2023 and the coding conventions. |
| 7 | Assumptions and constraints are explicit and clearly distinguished from one another | Pass | Fixed in v2: the first draft had no duration, although MIL-001 to MIL-003 and PP-001 cite one. Constraints now state 2026-10-05 to 2026-10-12. |
| 8 | Document supports executive decision-making with a clear, unambiguous recommendation | Pass | Recommendation is a single "Proceed" with a reason. |
## Overall Verdict
Go — all mandatory criteria pass. Criterion 7 first failed (missing duration constraint) and was fixed in the same change as BC-001 v2.
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| Add the duration constraint to BC-001 (done in v2) | S01 | 2026-10-06 |
---
[BC-001]: ../../business-case.md
[QC-BC-001]: ../../../framework/qc/qc-business-case.md
[8ef00aa]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/8ef00aafaaa193ea565f1a45238d31e963a5e05d
@@ -0,0 +1,49 @@
# RC-002 Stakeholder Analysis Review
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-002 |
| CrossReference | [SA-001], [QC-SA-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | [8ef00aa] |
---
## Artifact Under Review
- Instance reviewed: [SA-001] (docs/stakeholder-analysis.md)
- Checklist used: [QC-SA-001] (`QC-SA-001`)
- Independence: S01 is the only stakeholder and is also the recorded author, so the reviewer-is-not-author rule cannot be met. The documents were drafted by Claude on S01's behalf; S01 reviews and signs. This is a known limit of a one-person project, not a clean independent review.
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | Power/Interest grid is filled for every stakeholder, with no gaps or unclassified entries | Pass | S01 to S03 each have power, interest and quadrant. |
| 2 | Each stakeholder is assigned a unique, stable ID (e.g. S01-S11 style) reusable for RACI assignments in other artifacts | Pass | IDs S01 to S03 are used by every other document. |
| 3 | Roles and organizational context are defined with explicit Power and Interest levels, not just narrative description | Pass | Levels are stated as High/Low in the table, with organisation and role. |
| 4 | Communication needs (channel, frequency, deliverable type) are mapped to project phases or milestones | Pass | Communication table maps each stakeholder to MIL-001 to MIL-003. |
| 5 | Conflicting stakeholder interests are identified with documented mitigation or resolution strategies | Pass | One conflict (assignment names vs testable structure) with a mitigation. |
| 6 | Stakeholder concerns are explicitly traced to Business Case objectives | Pass | Traceability table maps each stakeholder to [BC-001] objectives. |
| 7 | Primary concerns are expressed in both business language and a recognized quality-attribute mapping (e.g. FURPS+) | Pass | Each concern has a FURPS+ attribute. |
| 8 | Document is understandable and navigable by non-technical stakeholders reviewing their own entry | Pass | Plain language, short; each stakeholder can find their own row. |
## Overall Verdict
Go — all criteria pass. The Sign-Off row said `Pending review` and was updated to the Go in SA-001 v2.
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| Record the sign-off in SA-001 (done in v2) | S01 | 2026-10-06 |
---
[SA-001]: ../../stakeholder-analysis.md
[QC-SA-001]: ../../../framework/qc/qc-stakeholder-analysis.md
[8ef00aa]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/8ef00aafaaa193ea565f1a45238d31e963a5e05d
+47
View File
@@ -0,0 +1,47 @@
# RC-003 MIL-001 Review
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-003 |
| CrossReference | [MIL-001], [QC-MIL-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | [8ef00aa] |
---
## Artifact Under Review
- Instance reviewed: [MIL-001] (docs/milestones/mil-001-project-setup.md)
- Checklist used: [QC-MIL-001] (`QC-MIL-001`)
- Independence: S01 is the only stakeholder and is also the recorded author, so the reviewer-is-not-author rule cannot be met. The documents were drafted by Claude on S01's behalf; S01 reviews and signs. This is a known limit of a one-person project, not a clean independent review.
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | A concrete deliverable is defined for every gate | Pass | Deliverable section names the concrete output. |
| 2 | Explicit Go/No-Go criteria are stated for each gate | Pass | Go/No-Go table with one checkable criterion per row. |
| 3 | Dependencies on other milestones are explicitly mapped | Pass | Dependencies table present. |
| 4 | Each milestone is traceable to a Business Case objective or KPI | Pass | Traceability cites the Business Case objectives. |
| 5 | Milestone owner and approving reviewer are identified | Pass | Ownership names S01 as owner and approving reviewer (same person; see independence note). |
| 6 | Milestone has a defined target date consistent with project constraints | Pass | Target date lies inside the duration constraint added to BC-001 in RC-001's action (2026-10-05 to 2026-10-12). Before that fix this was a Fail. |
## Overall Verdict
Go — all criteria pass. Delivery matches: issues #1 to #5 closed, PRs 19 and 20 merged.
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | S01 | - |
---
[MIL-001]: ../../milestones/mil-001-project-setup.md
[QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md
[8ef00aa]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/8ef00aafaaa193ea565f1a45238d31e963a5e05d
+47
View File
@@ -0,0 +1,47 @@
# RC-004 MIL-002 Review
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-004 |
| CrossReference | [MIL-002], [QC-MIL-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | [8ef00aa] |
---
## Artifact Under Review
- Instance reviewed: [MIL-002] (docs/milestones/mil-002-coffee-machine-core.md)
- Checklist used: [QC-MIL-001] (`QC-MIL-001`)
- Independence: S01 is the only stakeholder and is also the recorded author, so the reviewer-is-not-author rule cannot be met. The documents were drafted by Claude on S01's behalf; S01 reviews and signs. This is a known limit of a one-person project, not a clean independent review.
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | A concrete deliverable is defined for every gate | Pass | Deliverable section names the concrete output. |
| 2 | Explicit Go/No-Go criteria are stated for each gate | Pass | Go/No-Go table with one checkable criterion per row. |
| 3 | Dependencies on other milestones are explicitly mapped | Pass | Dependencies table present. |
| 4 | Each milestone is traceable to a Business Case objective or KPI | Pass | Traceability cites the Business Case objectives. |
| 5 | Milestone owner and approving reviewer are identified | Pass | Ownership names S01 as owner and approving reviewer (same person; see independence note). |
| 6 | Milestone has a defined target date consistent with project constraints | Pass | Target date lies inside the duration constraint added to BC-001 in RC-001's action (2026-10-05 to 2026-10-12). Before that fix this was a Fail. |
## Overall Verdict
Go — all criteria pass. Delivery matches: issues #6 to #12 closed, PR 21 merged. Note: Go/No-Go rows refer to the assignment's behaviour; the exact texts are asserted in tests/.
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | S01 | - |
---
[MIL-002]: ../../milestones/mil-002-coffee-machine-core.md
[QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md
[8ef00aa]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/8ef00aafaaa193ea565f1a45238d31e963a5e05d
+47
View File
@@ -0,0 +1,47 @@
# RC-005 MIL-003 Review
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-005 |
| CrossReference | [MIL-003], [QC-MIL-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | [8ef00aa] |
---
## Artifact Under Review
- Instance reviewed: [MIL-003] (docs/milestones/mil-003-quality-and-publication.md)
- Checklist used: [QC-MIL-001] (`QC-MIL-001`)
- Independence: S01 is the only stakeholder and is also the recorded author, so the reviewer-is-not-author rule cannot be met. The documents were drafted by Claude on S01's behalf; S01 reviews and signs. This is a known limit of a one-person project, not a clean independent review.
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | A concrete deliverable is defined for every gate | Pass | Deliverable section names the concrete output. |
| 2 | Explicit Go/No-Go criteria are stated for each gate | Pass | Go/No-Go table with one checkable criterion per row. |
| 3 | Dependencies on other milestones are explicitly mapped | Pass | Dependencies table present. |
| 4 | Each milestone is traceable to a Business Case objective or KPI | Pass | Traceability cites the Business Case objectives. |
| 5 | Milestone owner and approving reviewer are identified | Pass | Ownership names S01 as owner and approving reviewer (same person; see independence note). |
| 6 | Milestone has a defined target date consistent with project constraints | Pass | Target date lies inside the duration constraint added to BC-001 in RC-001's action (2026-10-05 to 2026-10-12). Before that fix this was a Fail. |
## Overall Verdict
Go — all criteria pass. Delivery matches: issues #13 to #17 closed, PR 22 merged.
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | S01 | - |
---
[MIL-003]: ../../milestones/mil-003-quality-and-publication.md
[QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md
[8ef00aa]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/8ef00aafaaa193ea565f1a45238d31e963a5e05d
+54
View File
@@ -0,0 +1,54 @@
# RC-006 Source Code Review
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-006 |
| CrossReference | [MIL-004], [QC-PY-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | [00d47e2] |
---
## Artifact Under Review
- Instance reviewed: `src/coffee_machine/` and `tests/` at the state of MIL-003 (`main` after PR 22)
- Checklist used: [QC-PY-001] (`QC-PY-001`)
- Independence: S01 is the only stakeholder and is also the recorded author, so the reviewer-is-not-author rule cannot be met. The code and documents were written by Claude on S01's behalf; S01 reviews and signs. This is a known limit of a one-person project, not a clean independent review.
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | Packages, modules, functions, variables, classes and constants follow PEP 8 casing (`snake_case`, `PascalCase`, `UPPER_SNAKE`) | Pass | Modules, functions and variables are `snake_case`, constants `UPPER_SNAKE`, `Drink` is `PascalCase`. |
| 2 | Names state purpose in the domain's language; no unexplained abbreviations, no single-letter names outside tiny scopes | Pass | Names state purpose (`is_resource_sufficient`, `process_coins`); the only short names are loop variables. |
| 3 | Code is produced by the project's formatter and passes its linter with no unexplained suppressions | Pass | `ruff check` and `ruff format --check` pass; no suppression comments. |
| 4 | Every function and method signature is type-annotated, including `-> None` | Pass | Every function, including tests and the nested `feed`, is annotated. |
| 5 | No bare `except:`, no swallowed exceptions; specific exceptions are raised and the cause is kept (`raise ... from`) | Pass | No bare `except`. `_ask_coin_count` catches only `ValueError` and answers it by asking again, so nothing is swallowed. |
| 6 | No mutable default arguments and no shadowed builtins | Pass | No mutable defaults; no builtin is shadowed. |
| 7 | Files, locks and connections are managed with context managers | N-A | The program opens no files, locks or connections. |
| 8 | Public modules, classes and functions have docstrings that say what, not how | Pass | All modules, public functions and tests have Doxygen docstrings that say what the code does. |
| 9 | Logging uses `logging`, not `print`; no secrets or personal data in log output | N-A | No logging exists. `print` is the program's own console output, not diagnostics. |
| 10 | Classes and operations trace to the Design Class Diagram they implement; deviations are recorded | N-A | No Design Class Diagram exists; the plan treats the work as plain technical tasks. |
| 11 | Tests exist for new behaviour, are named for the behaviour, and do not depend on order or the network | Pass | 19 tests named for behaviour, independent of order, no network. |
| 12 | Type checker runs in strict mode without errors; `Any` is justified in a comment | Pass | `mypy --strict` runs clean and `Any` is not used. |
| 13 | Dependencies are declared and pinned in the project's dependency file, none unused | Fail | Runtime dependencies: none. The dev tools had no lower bounds for ruff and mypy; fixed by action item 1, which sets `pytest>=9`, `ruff>=0.16`, `mypy>=2.4` (the tested versions). |
## Overall Verdict
Go — every mandatory criterion passes or is N-A. The one Fail (13) is optional and is fixed in the same change by issue #25.
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| Set lower bounds for the dev dependencies in pyproject.toml (done) | S01 | 2026-10-06 |
---
[MIL-004]: ../../milestones/mil-004-code-review-and-ci.md
[QC-PY-001]: ../../../framework/qc/qc-programming-python.md
[00d47e2]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/00d47e244c82007bbc55e18312649189b175ec10
+47
View File
@@ -0,0 +1,47 @@
# RC-007 MIL-004 Review
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-007 |
| CrossReference | [MIL-004], [QC-MIL-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | [00d47e2] |
---
## Artifact Under Review
- Instance reviewed: [MIL-004] (docs/milestones/mil-004-code-review-and-ci.md)
- Checklist used: [QC-MIL-001] (`QC-MIL-001`)
- Independence: S01 is the only stakeholder and is also the recorded author, so the reviewer-is-not-author rule cannot be met. The code and documents were written by Claude on S01's behalf; S01 reviews and signs. This is a known limit of a one-person project, not a clean independent review.
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | A concrete deliverable is defined for every gate | Pass | Deliverable names the RC record, the fixes, the CI workflow and the README section. |
| 2 | Explicit Go/No-Go criteria are stated for each gate | Pass | Five Go/No-Go rows, each checkable (a record exists, a workflow runs four commands, a run is green). |
| 3 | Dependencies on other milestones are explicitly mapped | Pass | Depends on MIL-003 with a reason. |
| 4 | Each milestone is traceable to a Business Case objective or KPI | Pass | Traceability cites Business Case objective 6 and 4. |
| 5 | Milestone owner and approving reviewer are identified | Pass | Owner and approving reviewer are S01 (same person; see independence note). |
| 6 | Milestone has a defined target date consistent with project constraints | Pass | Target date 2026-10-12 lies inside the duration constraint in the Business Case. |
## Overall Verdict
Go — all criteria pass.
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | S01 | - |
---
[MIL-004]: ../../milestones/mil-004-code-review-and-ci.md
[QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md
[00d47e2]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/00d47e244c82007bbc55e18312649189b175ec10
@@ -0,0 +1,52 @@
# RC-008 Business Case v3 Review
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-008 |
| CrossReference | [BC-001], [QC-BC-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | [861edbf] |
---
## Artifact Under Review
- Instance reviewed: [BC-001] (docs/business-case.md, version 3 as reviewed; version 4 after the actions below)
- Checklist used: [QC-BC-001] (`QC-BC-001`)
- Independence: S01 is the only stakeholder and is also the recorded author, so the reviewer-is-not-author rule cannot be met. The document was written by Claude on S01's behalf; S01 reviews and signs. This is a known limit of a one-person project, not a clean independent review.
- This record re-reviews the Business Case after version 3 added objective 6 and scope for code review and CI. It follows [RC-001], which reviewed version 2.
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | ROI/Cost-Benefit analysis is quantitative, or where qualitative, is explicitly justified | Pass | Cost-Benefit table is explicitly qualitative and says why. |
| 2 | Risks are identified with documented impact and mitigation | Pass | Three risks, each with impact and mitigation. No new risk comes from objective 6. |
| 3 | Success criteria are measurable, stating explicit targets rather than vague aspirations | Pass | Fixed in v4: objective 6 (review and CI) had no success criterion. Criterion 6 now has a target and a measure; criteria 1 to 6 are all measurable. |
| 4 | Scope explicitly separates In Scope vs Out of Scope | Pass | `### In Scope` and `### Out of Scope` are separate; v3 added the review and CI line to In Scope and nothing contradicts Out of Scope. |
| 5 | Stakeholders are cross-referenced to Stakeholder Analysis IDs rather than re-described inline | Pass | Stakeholders table cites S01 to S03 only. |
| 6 | Methodology and quality-standard foundation are stated explicitly (e.g. ISO/IEC 25010, Larman) | Pass | Methodology section names the framework, ISO/IEC 25010:2023 and the coding conventions. |
| 7 | Assumptions and constraints are explicit and clearly distinguished from one another | Pass | Fixed in v4: the Assumption said "3.13 or newer" but the Constraint said "greater than 3.13". The Constraint now states the reading (`>=3.13`). The duration constraint added after RC-001 is still present. |
| 8 | Document supports executive decision-making with a clear, unambiguous recommendation | Pass | Recommendation is a single "Proceed" with a reason. |
## Overall Verdict
Go — all mandatory criteria pass. Two defects were found in version 3 (no success criterion for objective 6; conflicting wording of the Python version) and fixed in the same change as BC-001 v4.
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| Add success criterion 6 for objective 6 (done in v4) | S01 | 2026-10-06 |
| State the Python version reading in the constraint (done in v4) | S01 | 2026-10-06 |
---
[BC-001]: ../../business-case.md
[QC-BC-001]: ../../../framework/qc/qc-business-case.md
[RC-001]: ./rc-001-business-case.md
[861edbf]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/861edbfa92ed8b28b018c44ffd05a0b1eed035b5
+59
View File
@@ -0,0 +1,59 @@
# Traceability Matrix
## Metadata
| Key | Value |
| --- | --- |
| ID | TM-001 |
| CrossReference | [BC-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S01 | Added RC-006, RC-007 and the source code row | [00d47e2] |
| 2026-10-06 | Proposed | Jens Tirsvad Nielsen | S01 | Business Case last reviewed in RC-008 | [71a27dd] |
---
## Purpose
Tracks backward/forward links between artifact instances so that the Business Case's
cross-artifact traceability can be measured. A row is added or updated whenever an
artifact instance is created or reviewed.
## Traceability Table
| Artifact Instance | Type | Upstream (Backward Link) | Downstream (Forward Link) | Last Reviewed (RC-ID) |
| --- | --- | --- | --- | --- |
| [SA-001] | Stakeholder Analysis | - | [BC-001] | [RC-002] |
| [BC-001] | Business Case | [SA-001] | [PP-001], [MIL-001], [MIL-002], [MIL-003], [MIL-004] | [RC-008] |
| [PP-001] | Project Plan | [BC-001], [SA-001] | [MIL-001], [MIL-002], [MIL-003], [MIL-004] | - |
| [MIL-001] | Milestone | [BC-001], [PP-001] | - | [RC-003] |
| [MIL-002] | Milestone | [BC-001], [PP-001] | - | [RC-004] |
| [MIL-003] | Milestone | [BC-001], [PP-001] | - | [RC-005] |
| [MIL-004] | Milestone | [BC-001], [PP-001] | - | [RC-007] |
| Source code (`src/`, `tests/`) | Source code | [MIL-002], [MIL-003], [MIL-004] | - | [RC-006] |
## Coverage Notes
- `-` in Upstream means foundational; in Downstream, nothing is built on it yet; in Last Reviewed, no `RC-*` exists yet.
- [PP-001] has no QC checklist in the framework, so it has no `RC-*`.
- No use case, domain model, design or data artifacts exist; the plan treats all tasks as plain technical tasks. The source code is reviewed in [RC-006].
---
[SA-001]: ../stakeholder-analysis.md
[BC-001]: ../business-case.md
[PP-001]: ../project-plan.md
[MIL-001]: ../milestones/mil-001-project-setup.md
[MIL-002]: ../milestones/mil-002-coffee-machine-core.md
[MIL-003]: ../milestones/mil-003-quality-and-publication.md
[MIL-004]: ../milestones/mil-004-code-review-and-ci.md
[RC-002]: ./reviews/rc-002-stakeholder-analysis.md
[RC-003]: ./reviews/rc-003-mil-001.md
[RC-004]: ./reviews/rc-004-mil-002.md
[RC-005]: ./reviews/rc-005-mil-003.md
[RC-006]: ./reviews/rc-006-source-code.md
[RC-007]: ./reviews/rc-007-mil-004.md
[RC-008]: ./reviews/rc-008-business-case-v3.md
[00d47e2]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/00d47e244c82007bbc55e18312649189b175ec10
[71a27dd]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/71a27ddc1f5555c1b836b1556fab292ebc9deb62
+4 -2
View File
@@ -9,7 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-05 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [c0c3940] |
| 2026-10-05 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [c0c3940] |
| 2026-10-06 | Accepted | Jens Tirsvad Nielsen | S01 | Recorded S01 sign-off after RC-002 | [8ef00aa] |
---
@@ -70,9 +71,10 @@ Monitor).
| Stakeholder | Decision | Date |
| --- | --- | --- |
| S01 | Pending review | |
| S01 | Go (RC-002) | 2026-10-06 |
---
[BC-001]: ./business-case.md
[c0c3940]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/c0c3940551db0d7d96492cc734976d0436051ff9
[8ef00aa]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/015-coffee_machine/commit/8ef00aafaaa193ea565f1a45238d31e963a5e05d
+32
View File
@@ -0,0 +1,32 @@
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[project]
name = "coffee-machine"
version = "0.1.0"
description = "Console coffee machine: Day 15 of Udemy's 100 Days of Code Python bootcamp."
readme = "README.md"
requires-python = ">=3.13"
license = { file = "LICENSE" }
dependencies = []
[project.optional-dependencies]
dev = ["pytest>=9", "ruff>=0.16", "mypy>=2.4"]
[project.scripts]
coffee-machine = "coffee_machine.main:main"
[tool.setuptools.packages.find]
where = ["src"]
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src", "."]
[tool.ruff]
line-length = 88
[tool.mypy]
strict = true
mypy_path = "src"
+4
View File
@@ -0,0 +1,4 @@
"""!
@file __init__.py
@brief Console coffee machine package.
"""
+8
View File
@@ -0,0 +1,8 @@
"""!
@file __main__.py
@brief Allow `python -m coffee_machine`.
"""
from coffee_machine.main import main
main()
+54
View File
@@ -0,0 +1,54 @@
"""!
@file constants.py
@brief Constants for the coffee machine: menu, coins, starting resources, texts.
Money is stored in whole cents so that sums and change are exact.
"""
from typing import TypedDict
class Drink(TypedDict):
"""!
@brief A menu entry: the ingredients it needs and its price.
"""
ingredients: dict[str, int]
cost: int
MENU: dict[str, Drink] = {
"espresso": {"ingredients": {"water": 50, "coffee": 18}, "cost": 150},
"latte": {
"ingredients": {"water": 200, "milk": 150, "coffee": 24},
"cost": 250,
},
"cappuccino": {
"ingredients": {"water": 250, "milk": 100, "coffee": 24},
"cost": 300,
},
}
## Value of each US coin in cents, in the order the machine asks for them.
COIN_VALUES: dict[str, int] = {
"quarters": 25,
"dimes": 10,
"nickels": 5,
"pennies": 1,
}
INITIAL_RESOURCES: dict[str, int] = {"water": 300, "milk": 200, "coffee": 100}
## Unit printed after each resource in the report.
RESOURCE_UNITS: dict[str, str] = {"water": "ml", "milk": "ml", "coffee": "g"}
CENTS_PER_DOLLAR = 100
COMMAND_REPORT = "report"
COMMAND_OFF = "off"
PROMPT_ORDER = "What would you like? (espresso/latte/cappuccino): "
MESSAGE_INSERT_COINS = "Please insert coins."
MESSAGE_NOT_ENOUGH_MONEY = "Sorry that's not enough money. Money refunded."
MESSAGE_UNKNOWN_DRINK = "Sorry, we do not serve that."
MESSAGE_INVALID_NUMBER = "Please enter a whole number of coins, zero or more."
+138
View File
@@ -0,0 +1,138 @@
"""!
@file main.py
@brief Entry point and logic of the coffee machine.
"""
from coffee_machine.constants import (
CENTS_PER_DOLLAR,
COIN_VALUES,
COMMAND_OFF,
COMMAND_REPORT,
INITIAL_RESOURCES,
MENU,
MESSAGE_INSERT_COINS,
MESSAGE_INVALID_NUMBER,
MESSAGE_NOT_ENOUGH_MONEY,
MESSAGE_UNKNOWN_DRINK,
PROMPT_ORDER,
RESOURCE_UNITS,
)
def format_money(cents: int) -> str:
"""!
@brief Format an amount in cents as dollars.
@param cents Amount in whole cents.
@return The amount such as "$2.50".
"""
return f"${cents / CENTS_PER_DOLLAR:.2f}"
def report(resources: dict[str, int], profit: int) -> None:
"""!
@brief Print the resources left in the machine and the money earned.
@param resources Remaining amount of each resource.
@param profit Money earned so far, in cents.
"""
for name, amount in resources.items():
print(f"{name.capitalize()}: {amount}{RESOURCE_UNITS[name]}")
print(f"Money: {format_money(profit)}")
def is_resource_sufficient(
order_ingredients: dict[str, int], resources: dict[str, int]
) -> bool:
"""!
@brief Check that the machine has enough of every ingredient for an order.
@param order_ingredients Amount of each ingredient the drink needs.
@param resources Remaining amount of each resource.
@return True if every ingredient is available, otherwise False after
printing the first missing one.
"""
for name, needed in order_ingredients.items():
if needed > resources[name]:
print(f"Sorry there is not enough {name}.")
return False
return True
def _ask_coin_count(coin_name: str) -> int:
"""!
@brief Ask how many coins of one kind are inserted, until the answer is valid.
@param coin_name Plural coin name, such as "quarters".
@return The number of coins, zero or more.
"""
while True:
answer = input(f"How many {coin_name}?: ")
try:
count = int(answer)
except ValueError:
count = -1
if count >= 0:
return count
print(MESSAGE_INVALID_NUMBER)
def process_coins() -> int:
"""!
@brief Ask for the coins and total their value.
@return The total inserted, in cents.
"""
print(MESSAGE_INSERT_COINS)
return sum(
_ask_coin_count(coin_name) * value for coin_name, value in COIN_VALUES.items()
)
def is_transaction_successful(money_received: int, drink_cost: int) -> bool:
"""!
@brief Check the payment, refund it if too small, otherwise give change.
@param money_received Money inserted, in cents.
@param drink_cost Price of the drink, in cents.
@return True if the payment covers the price, otherwise False.
"""
if money_received < drink_cost:
print(MESSAGE_NOT_ENOUGH_MONEY)
return False
change = money_received - drink_cost
if change > 0:
print(f"Here is {format_money(change)} in change.")
return True
def make_coffee(
drink_name: str, order_ingredients: dict[str, int], resources: dict[str, int]
) -> None:
"""!
@brief Use up the drink's ingredients and serve it.
@param drink_name Name of the drink.
@param order_ingredients Amount of each ingredient the drink needs.
@param resources Remaining amount of each resource; updated in place.
"""
for name, needed in order_ingredients.items():
resources[name] -= needed
print(f"Here is your {drink_name}. Enjoy!")
def main() -> None:
"""!
@brief Run the coffee machine until the user types "off".
"""
resources = dict(INITIAL_RESOURCES)
profit = 0
while True:
choice = input(PROMPT_ORDER).strip().lower()
if choice == COMMAND_OFF:
return
if choice == COMMAND_REPORT:
report(resources, profit)
elif choice in MENU:
drink = MENU[choice]
if not is_resource_sufficient(drink["ingredients"], resources):
continue
payment = process_coins()
if is_transaction_successful(payment, drink["cost"]):
profit += drink["cost"]
make_coffee(choice, drink["ingredients"], resources)
else:
print(MESSAGE_UNKNOWN_DRINK)
+4
View File
@@ -0,0 +1,4 @@
"""!
@file __init__.py
@brief Test package.
"""
+25
View File
@@ -0,0 +1,25 @@
"""!
@file conftest.py
@brief Shared pytest helpers.
"""
from collections.abc import Callable, Iterable
import pytest
ScriptInput = Callable[[Iterable[str]], None]
@pytest.fixture
def script_input(monkeypatch: pytest.MonkeyPatch) -> ScriptInput:
"""!
@brief Provide a function that feeds the given answers to `input`.
@param monkeypatch pytest's monkeypatch fixture.
@return A function taking the answers in the order they are asked.
"""
def feed(answers: Iterable[str]) -> None:
remaining = iter(answers)
monkeypatch.setattr("builtins.input", lambda _prompt: next(remaining))
return feed
+107
View File
@@ -0,0 +1,107 @@
"""!
@file test_machine.py
@brief Tests for making drinks and the main loop.
"""
import pytest
from coffee_machine.constants import MENU
from coffee_machine.main import main, make_coffee
from tests.conftest import ScriptInput
COINS_FOR_LATTE = ["10", "0", "0", "0"]
NO_COINS = ["0", "0", "0", "0"]
FULL_REPORT = "Water: 300ml\nMilk: 200ml\nCoffee: 100g\nMoney: $0.00"
def test_make_coffee_uses_up_ingredients(capsys: pytest.CaptureFixture[str]) -> None:
"""!
@brief The drink's ingredients are deducted and the drink is served.
"""
resources = {"water": 300, "milk": 200, "coffee": 100}
make_coffee("latte", MENU["latte"]["ingredients"], resources)
assert resources == {"water": 100, "milk": 50, "coffee": 76}
assert capsys.readouterr().out == "Here is your latte. Enjoy!\n"
def test_off_ends_the_machine(script_input: ScriptInput) -> None:
"""!
@brief Typing off returns from the loop.
"""
script_input(["off"])
main()
def test_report_shows_starting_resources(
script_input: ScriptInput, capsys: pytest.CaptureFixture[str]
) -> None:
"""!
@brief A fresh machine reports full resources and no money.
"""
script_input(["report", "off"])
main()
assert FULL_REPORT in capsys.readouterr().out
def test_unknown_input_is_rejected(
script_input: ScriptInput, capsys: pytest.CaptureFixture[str]
) -> None:
"""!
@brief Anything that is not a drink or command prints a message.
"""
script_input(["tea", "off"])
main()
assert "Sorry, we do not serve that." in capsys.readouterr().out
def test_sale_updates_resources_and_profit(
script_input: ScriptInput, capsys: pytest.CaptureFixture[str]
) -> None:
"""!
@brief A paid drink is served, resources drop and profit rises by the price.
"""
script_input(["latte", *COINS_FOR_LATTE, "report", "off"])
main()
out = capsys.readouterr().out
assert "Here is your latte. Enjoy!" in out
assert "Water: 100ml\nMilk: 50ml\nCoffee: 76g\nMoney: $2.50" in out
def test_refund_changes_nothing(
script_input: ScriptInput, capsys: pytest.CaptureFixture[str]
) -> None:
"""!
@brief An underpaid order is refunded: no drink, no profit.
"""
script_input(["espresso", *NO_COINS, "report", "off"])
main()
out = capsys.readouterr().out
assert "Money refunded." in out
assert "Enjoy" not in out
assert FULL_REPORT in out
def test_overpayment_adds_only_price_to_profit(
script_input: ScriptInput, capsys: pytest.CaptureFixture[str]
) -> None:
"""!
@brief Change is returned and only the price is counted as profit.
"""
script_input(["espresso", "7", "0", "0", "0", "report", "off"])
main()
out = capsys.readouterr().out
assert "Here is $0.25 in change." in out
assert "Money: $1.50" in out
def test_order_refused_when_resources_run_out(
script_input: ScriptInput, capsys: pytest.CaptureFixture[str]
) -> None:
"""!
@brief After the water is used up, the next order is refused before payment.
"""
script_input(["latte", *COINS_FOR_LATTE, "latte", "off"])
main()
out = capsys.readouterr().out
assert out.count("Please insert coins.") == 1
assert "Sorry there is not enough water." in out
+52
View File
@@ -0,0 +1,52 @@
"""!
@file test_resources.py
@brief Tests for the report and the resource check.
"""
import pytest
from coffee_machine.constants import INITIAL_RESOURCES, MENU
from coffee_machine.main import format_money, is_resource_sufficient, report
def test_format_money_shows_two_decimals() -> None:
"""!
@brief Cents are shown as dollars with two decimals.
"""
assert format_money(250) == "$2.50"
assert format_money(0) == "$0.00"
def test_report_prints_resources_and_money(capsys: pytest.CaptureFixture[str]) -> None:
"""!
@brief The report lists each resource with its unit, then the money.
"""
report({"water": 300, "milk": 200, "coffee": 100}, 250)
assert capsys.readouterr().out == (
"Water: 300ml\nMilk: 200ml\nCoffee: 100g\nMoney: $2.50\n"
)
def test_is_resource_sufficient_when_enough() -> None:
"""!
@brief An order is accepted when every ingredient is available.
"""
assert is_resource_sufficient(MENU["latte"]["ingredients"], INITIAL_RESOURCES)
def test_is_resource_sufficient_when_exactly_enough() -> None:
"""!
@brief Using up an ingredient completely is allowed.
"""
assert is_resource_sufficient({"water": 300}, {"water": 300})
def test_is_resource_sufficient_names_missing_resource(
capsys: pytest.CaptureFixture[str],
) -> None:
"""!
@brief A short ingredient is refused and named.
"""
resources = {"water": 300, "milk": 100, "coffee": 100}
assert not is_resource_sufficient(MENU["latte"]["ingredients"], resources)
assert capsys.readouterr().out == "Sorry there is not enough milk.\n"
+16
View File
@@ -0,0 +1,16 @@
"""!
@file test_smoke.py
@brief Smoke test that the package imports.
"""
import pytest
from coffee_machine import main as main_module
def test_main_is_callable(monkeypatch: pytest.MonkeyPatch) -> None:
"""!
@brief The entry point starts and stops on "off".
"""
monkeypatch.setattr("builtins.input", lambda _prompt: "off")
main_module.main()
+56
View File
@@ -0,0 +1,56 @@
"""!
@file test_transactions.py
@brief Tests for coin processing and payment.
"""
import pytest
from coffee_machine.main import is_transaction_successful, process_coins
from tests.conftest import ScriptInput
def test_process_coins_totals_in_cents(script_input: ScriptInput) -> None:
"""!
@brief Quarters, dimes, nickels and pennies add up to the right total.
"""
script_input(["4", "3", "2", "1"])
assert process_coins() == 4 * 25 + 3 * 10 + 2 * 5 + 1
def test_process_coins_asks_again_on_invalid_count(
script_input: ScriptInput, capsys: pytest.CaptureFixture[str]
) -> None:
"""!
@brief Text and negative counts are rejected and asked again.
"""
script_input(["abc", "-1", "2", "0", "0", "0"])
assert process_coins() == 50
assert capsys.readouterr().out.count("Please enter a whole number") == 2
def test_transaction_refused_when_too_little(
capsys: pytest.CaptureFixture[str],
) -> None:
"""!
@brief Too little money is refunded with the refund message.
"""
assert not is_transaction_successful(100, 150)
assert "Sorry that's not enough money. Money refunded." in capsys.readouterr().out
def test_transaction_accepted_with_exact_payment(
capsys: pytest.CaptureFixture[str],
) -> None:
"""!
@brief Exact payment succeeds and gives no change.
"""
assert is_transaction_successful(150, 150)
assert "change" not in capsys.readouterr().out
def test_transaction_gives_change(capsys: pytest.CaptureFixture[str]) -> None:
"""!
@brief Overpaying succeeds and returns the difference.
"""
assert is_transaction_successful(175, 150)
assert capsys.readouterr().out == "Here is $0.25 in change.\n"