Add the plan baseline: business case, stakeholders, plan, design and milestones

Business Case, Stakeholder Analysis, Project Plan, use case chain
(UCD, US, UC, DM, SSD, OC, SD, DCD, DICT), three ADRs, milestones
MIL-001 to MIL-007, the traceability matrix and 22 review records
(autonomous mode, AI as stand-in for S01).
This commit is contained in:
2026-10-14 21:33:25 +08:00
parent 97eec837a4
commit c3fbcbcfaf
49 changed files with 3484 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
# Operation Contracts: Study Flash Cards
## Metadata
| Key | Value |
| --- | --- |
| ID | OC-001 |
| CrossReference | [SSD-001], [DM-001], [DICT-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-14 | Accepted | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | pending |
---
The turn-over of the card after 3000 ms is started by the system's own timer, not by a message from the Learner, so it has no contract; it is realized in [SD-001].
## Contract: startStudySession
| Item | Value |
| --- | --- |
| Operation | `startStudySession(): CardDto \| None` |
| Traces to | `startStudySession` in [SSD-001] |
| Domain Model concepts | Study Session, Words To Learn, Word List, Flash Card ([DM-001]); IT terms `StudySession`, `Deck`, `Vocabulary`, `Card` ([DICT-001]) |
**Preconditions**
- A `Vocabulary` exists in the word list file.
**Postconditions**
- A `StudySession` instance was created.
- A `Deck` instance was created and associated with the `StudySession`.
- The `Deck` holds the cards of the progress file when it exists, otherwise a copy of the cards of the `Vocabulary`.
- When the `Deck` is not empty, `StudySession.current_card` was set to a card of the `Deck` and a `CardDto` of it is returned; otherwise `current_card` is `None` and `None` is returned.
**Exceptions**
| Condition (failing precondition) | Outcome |
| --- | --- |
| The word list file does not exist | `VocabularyNotFoundError` is raised; no session is created |
| The word list or progress file has no header of two cells, or a row with other than two cells | `VocabularyFormatError` is raised; no session is created and no file is changed |
## Contract: markKnown
| Item | Value |
| --- | --- |
| Operation | `markKnown(): None` |
| Traces to | `markKnown` in [SSD-001] |
| Domain Model concepts | Study Session, Words To Learn, Flash Card ([DM-001]); IT terms `StudySession`, `Deck`, `Card` ([DICT-001]) |
**Preconditions**
- A `StudySession` exists and its `current_card` is not `None`.
**Postconditions**
- The `Card` of `current_card` was removed from the `Deck` (the association was destroyed).
- The `Deck` was stored in the progress file.
- `StudySession.current_card` was set to `None`.
**Exceptions**
| Condition (failing precondition) | Outcome |
| --- | --- |
| `current_card` is `None` (no card is shown) | Nothing is changed and nothing is stored |
## Contract: markUnknown
| Item | Value |
| --- | --- |
| Operation | `markUnknown(): CardDto \| None` |
| Traces to | `markUnknown` in [SSD-001] |
| Domain Model concepts | Study Session, Words To Learn, Flash Card ([DM-001]); IT terms `StudySession`, `Deck`, `Card` ([DICT-001]) |
**Preconditions**
- A `StudySession` exists.
**Postconditions**
- `StudySession.current_card` was set to a card of the `Deck` that differs from the previous `current_card` when the `Deck` holds two or more cards, and a `CardDto` of it is returned.
- When the `Deck` is empty, `current_card` is `None` and `None` is returned.
- The `Deck` and the progress file are unchanged.
- The Malay word of the new `current_card` was passed to the `SpeechPort`.
**Exceptions**
| Condition (failing precondition) | Outcome |
| --- | --- |
| The `Deck` is empty | `None` is returned; nothing is spoken |
---
[SSD-001]: ./ssd.md
[DM-001]: ../domain-model.md
[DICT-001]: ../dictionary.md
[SD-001]: ./sd.md
+170
View File
@@ -0,0 +1,170 @@
# Sequence Diagrams: Study Flash Cards
## Metadata
| Key | Value |
| --- | --- |
| ID | SD-001 |
| CrossReference | [OC-001], [DCD-001], [DICT-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-14 | Accepted | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | pending |
---
## Sequence: startStudySession
**Realizes:** `startStudySession` in [OC-001]
### Diagram
```plantuml
@startuml
participant "main : app" as M
participant ":CsvVocabularyRepository" as VR
participant ":CsvProgressRepository" as PR
participant ":StudySession" as S
participant ":Deck" as D
participant ":SpeechPort" as SP
M -> PR : load()
PR --> M : deck | None
alt deck is None
M -> VR : load()
VR --> M : vocabulary
create D
M -> D : from_vocabulary(vocabulary)
D --> M : deck
end
create S
M -> S : create(deck, progress, speech, choose)
M -> S : next_card()
activate S
opt deck is not empty
S -> D : draw(choose, exclude)
D --> S : card
S -> SP : speak(card.front, front_code)
end
S --> M : CardDto | None
deactivate S
@enduml
```
### Pattern Annotations
| Pattern (GRASP / GoF) | Applied to | Rationale |
| --- | --- | --- |
| Creator | `app.main` creates `StudySession` | It holds the adapters the session needs |
| Information Expert | `Deck` draws and removes its own cards | It owns the list of cards |
| Null Object | `NullSpeech` as `SpeechPort` | The session never tests for speech |
### Postcondition Coverage
| Postcondition (from contract) | Satisfied by message |
| --- | --- |
| A `StudySession` instance was created | `create(deck, progress, speech, choose)` |
| A `Deck` was created from the progress file or a copy of the `Vocabulary` | `PR.load()`, `alt` fragment, `from_vocabulary` |
| `current_card` set and a `CardDto` returned, or `None` | `next_card()` with the `opt` fragment |
### Responsibility Check
The session coordinates, the deck holds the cards, the repositories hold the file format; no object receives all messages.
## Sequence: markKnown
**Realizes:** `markKnown` in [OC-001]
### Diagram
```plantuml
@startuml
participant ":FlashCardView" as V
participant ":FlashCardViewModel" as VM
participant ":StudySession" as S
participant ":Deck" as D
participant ":CsvProgressRepository" as PR
V -> VM : is_known()
activate VM
VM -> S : mark_known()
activate S
opt current_card is not None
S -> D : remove(card)
S -> PR : save(deck)
end
deactivate S
VM -> S : next_card()
S --> VM : CardDto | None
VM --> V : side, language name, text
deactivate VM
@enduml
```
### Pattern Annotations
| Pattern (GRASP / GoF) | Applied to | Rationale |
| --- | --- | --- |
| MVVM | `FlashCardView`, `FlashCardViewModel` | The view raises the event and shows the result; the rule is behind the view model |
| Repository | `CsvProgressRepository` | The session stores the deck through a port |
### Postcondition Coverage
| Postcondition (from contract) | Satisfied by message |
| --- | --- |
| The card was removed from the `Deck` | `remove(card)` |
| The `Deck` was stored | `save(deck)` |
| `current_card` was set to `None` | `mark_known()` ends with no current card; `next_card()` then draws the next |
### Responsibility Check
The view model only sequences two session calls and maps the result for the view.
## Sequence: markUnknown
**Realizes:** `markUnknown` in [OC-001]
### Diagram
```plantuml
@startuml
participant ":FlashCardView" as V
participant ":FlashCardViewModel" as VM
participant ":StudySession" as S
participant ":Deck" as D
participant ":SpeechPort" as SP
V -> VM : next_card()
VM -> S : next_card()
activate S
opt deck is not empty
S -> D : draw(choose, exclude=current)
D --> S : card
S -> SP : speak(card.front, front_code)
end
S --> VM : CardDto | None
deactivate S
VM --> V : side, language name, text
@enduml
```
### Pattern Annotations
| Pattern (GRASP / GoF) | Applied to | Rationale |
| --- | --- | --- |
| Information Expert | `Deck.draw` | The deck knows its cards |
### Postcondition Coverage
| Postcondition (from contract) | Satisfied by message |
| --- | --- |
| A different card was chosen when two or more exist | `draw(choose, exclude=current)` |
| Deck and file unchanged | no `remove` or `save` message |
| The Malay word was passed to the `SpeechPort` | `speak(card.front, front_code)` |
### Responsibility Check
Same as `markKnown`; the session is the only object that speaks and draws.
---
[OC-001]: ./oc.md
[DCD-001]: ../dcd.md
[DICT-001]: ../dictionary.md
+62
View File
@@ -0,0 +1,62 @@
# System Sequence Diagram: Study Flash Cards
## Metadata
| Key | Value |
| --- | --- |
| ID | SSD-001 |
| CrossReference | [UC-001], [DM-001], [OC-001] |
| Language | en |
| Domain | it |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-14 | Accepted | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | pending |
---
## Source Use Case
Study Flash Cards ([UC-001]) — scenario: main success scenario, with the unknown button as one repetition.
## Diagram
```plantuml
@startuml
actor Learner as L
participant ":System" as S
L -> S : startStudySession()
S --> L : card (word, language name)
... 3000 ms pass, the system turns the card over ...
S --> L : card (translation, language name)
L -> S : markKnown()
S --> L : next card (word, language name)
L -> S : markUnknown()
S --> L : next card (word, language name)
@enduml
```
## System Operations
| Step | Message | Parameters | Return | Use case step |
| --- | --- | --- | --- | --- |
| 1 | startStudySession | none | card to show, or "nothing left to learn" | 1, 2, 3 |
| 2 | (timer) turn the card over | none | the same card, translation side | 4 |
| 3 | markKnown | none | next card, or "nothing left to learn" | 5, 6, 7 |
| 4 | markUnknown | none | next card | 5a, 7 |
The turn-over in step 2 is started by the system's own timer, not by a message from the Learner; it is shown as a return without a call, which is the one deviation from "every return answers a message".
## Alternate Flows
The extensions 2a, 3a, 5b and 7a of [UC-001] add no system operation of their own (they are errors, speech and timer handling inside the three operations above); their behaviour is in [OC-001] as exceptions. They are out of scope of this diagram, which shows the main success scenario with `markUnknown` as the one alternate answer.
## Lifecycle Notes
The system instance lives for one run of the program. The words to learn are created when the session starts and saved after each `markKnown`.
---
[UC-001]: ./uc.md
[DM-001]: ../domain-model.md
[OC-001]: ./oc.md
+77
View File
@@ -0,0 +1,77 @@
# UC-001: Study Flash Cards
## Metadata
| Key | Value |
| --- | --- |
| ID | UC-001 |
| CrossReference | [UCD-001], [US-001], [SA-001], [DM-001], [SSD-001] |
| Language | en |
| Domain | it |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-14 | Accepted | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | pending |
---
**Format:** Fully Dressed
## Fully Dressed
- **Scope:** Flash Card program
- **Level:** user-goal
- **Primary Actor:** Learner
- **Stakeholders and Interests:**
- S01 — learns the Malay words and wants only unknown words to come up
- S02 — wants to see the assignment's behaviour step by step
- **Preconditions:**
- A word list exists.
- A display is available.
- **Postconditions (success guarantee):**
- The words the Learner marked as known are no longer in the words to learn.
- The words to learn are kept, so that the next session starts from them.
### Main Success Scenario
1. The Learner starts the program.
2. The system loads the words to learn: from the kept progress when it exists, otherwise from the word list.
3. The system shows a random card with the word in Malay.
4. After three seconds the system turns the card over and shows the translation in Danish.
5. The Learner states that the word is known.
6. The system removes the word from the words to learn and keeps the words to learn.
7. The system shows a new random card, and the Learner repeats from step 4 until no word is left.
### Extensions (Alternative / Exception Flows)
- 3a. Spoken words are available on the Learner's machine:
1. The system speaks the Malay word. If speaking fails, the system logs it and goes on.
- 5a. The Learner states that the word is not known:
1. The system keeps the word in the words to learn and shows a new random card; the Learner goes on at step 4.
- 5b. The Learner answers before three seconds have passed:
1. The system cancels the turn-over of the card being shown and starts a new one for the next card.
- 7a. No word is left:
1. The system tells the Learner that there is nothing left to learn, and further answers change nothing.
- 2a. The kept progress or the word list is unreadable:
1. The system reports the problem to the Learner and ends; it does not overwrite the kept progress.
### Special Requirements / Business Rules
| Step | Rule |
| --- | --- |
| 3, 7 | The next card is chosen at random from the words to learn |
| 4 | The turn-over comes three seconds after the card appears |
| 6 | A word is identified by its Malay and Danish text; the first equal word is removed |
| 2 | The word list names its two languages and holds one word and one translation per card |
### Open Issues
- Starting again (resetting the kept progress) is not offered in the program; deleting the kept progress does it.
---
[UCD-001]: ../use-case-diagram.md
[US-001]: ../user-stories.md
[SA-001]: ../stakeholder-analysis.md
[DM-001]: ../domain-model.md
[SSD-001]: ./ssd.md
+38
View File
@@ -0,0 +1,38 @@
# Wireframe: Study Flash Cards
The Product Owner gave this screen in the first prompt (the course's target screenshot). The presentation layer implements it and does not redesign it. Source use case: [UC-001](./uc.md).
## Screen
```text
+----------------------------------------------------------+ background #B1DDC6
| padding 50 px |
| +--------------------------------------------------+ |
| | | |
| | Malay (italic, 40 pt) | | card image 800 x 526
| | | | card_front.png (white)
| | aku (bold, 60 pt) | | card_back.png (green)
| | | |
| +--------------------------------------------------+ |
| [ X unknown ] [ v known ] | buttons: wrong.png, right.png
+----------------------------------------------------------+
```
## Layout
| Element | Position | Details |
| --- | --- | --- |
| Window | whole | Title "Flashy", padding 50 px, background `#B1DDC6` |
| Card | grid row 0, columns 0 and 1 (both) | `Canvas` 800 x 526, one image, two texts |
| Language name | centre of card, y = 150 | Arial 40 italic |
| Word | centre of card, y = 263 | Arial 60 bold |
| Unknown button | grid row 1, column 0 | `wrong.png`, no border |
| Known button | grid row 1, column 1 | `right.png`, no border |
## States
| State | Card image | Language name | Word | Text colour |
| --- | --- | --- | --- | --- |
| Front (first 3000 ms) | `card_front.png` | Malay | the Malay word | black |
| Back (after 3000 ms) | `card_back.png` | Danish | the Danish translation | white |
| Finished | `card_front.png` | (empty) | "Nothing left to learn" | black |