Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions DHF/items/00_uc/UC-006.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
id: UC-006
title: View Release History
content: 'A user opens the release notes page to find out what changed in the current
and previous software versions.


Actor: Radiation Therapist / Physician / Dosimetrist / Administrator


Preconditions:

- The application is running and the user can navigate to any page


Primary Flow:

1. User navigates to the release notes page from within the application

2. System displays a list of software releases in reverse-chronological order

3. Each entry shows the version number, release date, and a summary of changes

4. User reviews the change entries to understand what is new or fixed


Postconditions:

- User understands which features and fixes are included in the installed version'
type: UC
22 changes: 22 additions & 0 deletions DHF/items/01_req_crs/CRS-013.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
id: CRS-013
derives_from:
- UC-006
title: Users shall be able to view software version and release change history
content: 'As a radiation therapist, physician, dosimetrist, or administrator, I need
to access a release notes page within the application that lists each software version
with its release date and a summary of changes, so that I can confirm which features
and fixes are present in the installed version.


Verification criteria: The release notes page is reachable from the application
navigation without authentication, displays entries in reverse-chronological order,
and each entry includes at minimum a version number, release date, and change summary
text.'
user_group: Radiation Therapist / Physician / Dosimetrist / Administrator
priority: Medium
testing: 'T1: Given the application is running, a release notes page is accessible
from within the application and displays at least one release entry with a version
number and change summary.

'
type: CRS
27 changes: 27 additions & 0 deletions DHF/items/02_req_sys/SYS-016.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
id: SYS-016
satisfies:
- CRS-013
title: System shall provide an in-application release notes page displaying version
history
content: The system shall provide a dedicated release notes page accessible from within
the application. The page shall list software releases in reverse-chronological
order. Each release entry shall include the version number, release date, and a
human-readable summary of changes. The page shall be reachable via application navigation
without requiring authentication beyond normal application access.
category: Functional
verification_method:
- Test
- Demonstration
testing: 'T1: Given the application is running, the release notes page is reachable
and renders at least one release entry containing a version number and change summary.

T2: Given multiple release entries, they are ordered newest-first by version or
date.

'
critical_safety: false
verification_criteria: The release notes page renders at /release-notes with at least
one entry; each entry displays a non-empty version string, a release date, and a
non-empty change summary; entries are ordered newest-first; the page is accessible
without any authentication gate.
type: SYS
23 changes: 23 additions & 0 deletions DHF/items/03_req_srs/SRS-031.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
id: SRS-031
derives_from:
- SYS-016
title: Software shall display a dedicated release notes page listing version, date,
and change entries
content: 'The client application shall render a dedicated release notes page at the
route /release-notes. The page shall present a statically defined list of release
entries in reverse-chronological order. Each entry shall include: (1) the version
string, (2) the release date, and (3) one or more change description items. The
page shall be linked from the application navigation so users can reach it without
returning to the workspace. The release data shall be embedded in the client source
as a static constant to avoid runtime dependencies.'
verification_method:
- Test
testing: 'T1: Given the release notes page, it renders the release data with at least
one entry showing a version string and change summary.

T2: Given multiple release entries, the first rendered entry is the most recent
version.

'
critical_safety: false
type: SRS
23 changes: 23 additions & 0 deletions DHF/items/04_modules/MODULE-010.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
id: MODULE-010
title: ApplicationPages
content: 'Standalone informational and administrative pages rendered outside the clinical
workspace viewport.


Responsibilities:

- About page (/about): product description, tech stack, compliance standards, version

- Issues/Change Requests page (/issues): GitHub-backed CR submission and monitoring

- Settings page (/settings): user preferences and configuration

- Release Notes page (/release-notes): version history and change summaries


Each page is a React route component registered in App.tsx (React Router 7). Pages
share the application shell (navigation, routing) but are otherwise independent
of the clinical workspace stores (volumeStore, structureStore). Pages may use uiStore
only for global UI state (theme, sidebar). No DICOM or contouring logic belongs
in this module.'
type: MODULE
22 changes: 22 additions & 0 deletions DHF/items/05_swdd/SWDD-013.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
id: SWDD-013
implements:
- SRS-031
module:
- MODULE-010
title: Release notes static data model and ReleaseNotes page component
content: "Release data is a static constant defined in apps/client/src/pages/ReleaseNotes.tsx\
\ (or a colocated releaseData.ts). The data structure is an array of ReleaseEntry\
\ objects sorted newest-first:\n\n interface ReleaseEntry {\n version: string;\
\ // semver string, e.g. \"1.2.0\"\n date: string; // ISO-8601 date, e.g.\
\ \"2026-06-05\"\n changes: string[]; // one item per change bullet\n }\n\n\
Statically embedding the data avoids runtime network dependencies and makes the\
\ content predictable and auditable.\n\nThe ReleaseNotes page component maps over\
\ the data array and renders each entry as a card: version and date in the header,\
\ change items as a bulleted list. The component does not use any Zustand stores.\n\
\nRouting: App.tsx registers the route <Route path=\"/release-notes\" element={<ReleaseNotes\
\ />} />. Navigation: LeftSidebar.tsx adds a link to /release-notes in the System\
\ footer row alongside the existing Issues and Settings links.\n\nStyling follows\
\ the ApplicationPages pattern (bg-gray-50 page background, bg-white cards with\
\ border-gray-200, text-gray-900/600/400 — light-mode page layout used by About\
\ and Settings), not the dark clinical workspace theme."
type: SWDD
53 changes: 22 additions & 31 deletions DHF/items/06_sys_arch/SYSARCH-001.yaml
Original file line number Diff line number Diff line change
@@ -1,35 +1,4 @@
id: SYSARCH-001
title: Browser Client
status: approved
content: |-
The primary software item running in the clinical user's browser. Responsible for
all user interaction, DICOM rendering, contouring workflow, and local state management.

Technology stack:
- React 18 + TypeScript (strict) + Vite — component framework and build tooling
- Cornerstone3D — GPU-accelerated DICOM rendering via WebGL (axial/sagittal/coronal MPR)
- Zustand + Immer — reactive state management (volumeStore, structureStore, uiStore)
- Tailwind CSS — dark clinical UI theme

Key sub-components:
- ViewportManager: manages Cornerstone3D rendering contexts and MPR layout
- MPRController: coordinates multi-planar reconstruction across viewports
- ContourEngine: freehand, polygon, brush, and eraser contouring tools; undo/redo
- DICOMweb client: QIDO-RS (query), WADO-RS (retrieve), STOW-RS (store) via /dicom-web proxy
- IndexedDB adapter: browser-local auto-save of in-progress structure set drafts

External interfaces:
- /dicom-web/* → Orthanc DICOMweb (proxied by Vite in dev, reverse proxy in prod)
- /api/* → ASP.NET Core API (proxied by Vite in dev)
- /ws → WebSocket for real-time updates (proxied)

Data flows:
- Image loading: QIDO-RS query → WADO-RS metadata → Cornerstone image IDs →
VolumeBuilder → Cornerstone3D volume → ViewportManager.setVolume() → GPU render
- Contouring: user gesture → ContourEngine.addContour() → UndoRedoManager →
structureStore → React re-render
- Draft persistence: structureStore dirty → IndexedDB auto-save → restore on reload
- Structure upload: active RTSTRUCT → STOW-RS to DICOM repository
design:
- SYS-002
- SYS-003
Expand All @@ -44,3 +13,25 @@ design:
- SYS-012
- SYS-014
- SYS-015
- SYS-016
title: Browser Client
status: approved
content: "The primary software item running in the clinical user's browser. Responsible\
\ for\nall user interaction, DICOM rendering, contouring workflow, and local state\
\ management.\n\nTechnology stack:\n- React 18 + TypeScript (strict) + Vite — component\
\ framework and build tooling\n- Cornerstone3D — GPU-accelerated DICOM rendering\
\ via WebGL (axial/sagittal/coronal MPR)\n- Zustand + Immer — reactive state management\
\ (volumeStore, structureStore, uiStore)\n- Tailwind CSS — dark clinical UI theme\n\
\nKey sub-components:\n- ViewportManager: manages Cornerstone3D rendering contexts\
\ and MPR layout\n- MPRController: coordinates multi-planar reconstruction across\
\ viewports\n- ContourEngine: freehand, polygon, brush, and eraser contouring tools;\
\ undo/redo\n- DICOMweb client: QIDO-RS (query), WADO-RS (retrieve), STOW-RS (store)\
\ via /dicom-web proxy\n- IndexedDB adapter: browser-local auto-save of in-progress\
\ structure set drafts\n\nExternal interfaces:\n- /dicom-web/* → Orthanc DICOMweb\
\ (proxied by Vite in dev, reverse proxy in prod)\n- /api/* → ASP.NET Core API (proxied\
\ by Vite in dev)\n- /ws → WebSocket for real-time updates (proxied)\n\nData flows:\n\
- Image loading: QIDO-RS query → WADO-RS metadata → Cornerstone image IDs →\n VolumeBuilder\
\ → Cornerstone3D volume → ViewportManager.setVolume() → GPU render\n- Contouring:\
\ user gesture → ContourEngine.addContour() → UndoRedoManager →\n structureStore\
\ → React re-render\n- Draft persistence: structureStore dirty → IndexedDB auto-save\
\ → restore on reload\n- Structure upload: active RTSTRUCT → STOW-RS to DICOM repository"
81 changes: 81 additions & 0 deletions DHF/items/09_cr/CR-014.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
id: CR-014
affected_risk_items: []
title: Add a release note page for the software
type: CR
description: 'I want to add a release note page for the software, so I can know what
is changed.


Source issue: https://github.com/itercharles/ContourLab/issues/118'
justification: I want to know what feature or defect fix are included in the software.
priority: Medium
requested_by: itercharles
target_version: 2026-W23
category: Feature
content: there is release note page, can be accessible from the software, displaying
the summary of each changes of each version.
status: new
impact_assessment: 'Approved. Complexity: small. Affected subsystems: client. Related
CRs: CR-012. Adds a standalone release notes page following the established React
router pattern; no backend or clinical workflow changes required.'
implementation_notes: "## Overview\nCR-014 adds a dedicated Release Notes page to\
\ ContourLab, accessible from within the application. The page lists software releases\
\ in reverse-chronological order, with each entry showing a version number, release\
\ date, and change bullet points. Release data is statically embedded in the component\
\ source — no network call or backend change is needed. The feature follows the\
\ existing application-page pattern (About, Issues, Settings) used throughout the\
\ frontend.\n\n## Current State\nThe application has four routes defined in `apps/client/src/App.tsx`:\n\
- `/workspace` → MainWorkspace\n- `/settings` → Settings\n- `/issues` → Issues\n\
- `/about` → About\n\nNavigation links for Issues and Settings are in the System\
\ footer row of `apps/client/src/components/layout/LeftSidebar.tsx` (the bottom\
\ strip with `text-[11px] uppercase tracking-widest` System label and two icon-button\
\ Links).\n\nThe `About.tsx` page at `apps/client/src/pages/About.tsx` follows a\
\ light-mode card layout (bg-gray-50 page, bg-white cards, border-gray-200 borders)\
\ that the new page should match.\n\nThe app has no existing release notes data\
\ source — data must be authored at implementation time.\n\n## Changes Required\n\
- **`apps/client/src/pages/ReleaseNotes.tsx`** — new file: the page component plus\
\ the static release data constant. Follows the About.tsx layout convention (light-mode,\
\ max-w-5xl centered layout).\n- **`apps/client/src/App.tsx`** — modify existing:\
\ add `import ReleaseNotes from ./pages/ReleaseNotes` and `<Route path=\"/release-notes\"\
\ element={<ReleaseNotes />} />` before the catch-all redirect.\n- **`apps/client/src/components/layout/LeftSidebar.tsx`**\
\ — modify existing: add a Link to `/release-notes` in the System footer row icon\
\ group, alongside the Issues and Settings links.\n- **`apps/client/src/pages/ReleaseNotes.test.tsx`**\
\ — new file: Vitest component tests covering SRS-031 T1 and T2 test points.\n\n\
## Implementation Steps\n1. Author the `ReleaseEntry` interface and `RELEASE_NOTES`\
\ constant in `ReleaseNotes.tsx`. Include at least the current version entry sourced\
\ from `package.json` (already imported in About.tsx as `import { version } from\
\ ../../package.json`).\n2. Implement the `ReleaseNotes` component: header matching\
\ About.tsx style, one card per entry with version + date header and a bulleted\
\ change list.\n3. Register the route in `App.tsx`: import the component and add\
\ the route before the `<Route path=\"*\">` catch-all.\n4. Add the navigation link\
\ in `LeftSidebar.tsx`: add a `<Link to=\"/release-notes\" reloadDocument ...>`\
\ icon button in the System footer row. Use a changelog-style SVG icon (e.g., list\
\ or document icon at 13×13px, same style as the Issues icon).\n5. Write tests in\
\ `ReleaseNotes.test.tsx` covering T1 (page renders at least one entry with version\
\ and change text) and T2 (first rendered entry is the most recent — assert `RELEASE_NOTES[0].version`\
\ appears first in the DOM).\n6. Run `pnpm --filter @contourlab/client typecheck`\
\ — expect zero errors.\n7. Run `pnpm --filter @contourlab/client test` — all tests\
\ must pass.\n8. Manual check: open the app, click the release notes icon in the\
\ sidebar System footer, confirm the page loads and shows version entries.\n\n##\
\ Edge Cases & Constraints\n- `RELEASE_NOTES` must have at least one entry or the\
\ page renders an empty state — guard or ensure the constant is non-empty.\n- The\
\ `/release-notes` route must appear before the `<Route path=\"*\">` catch-all in\
\ App.tsx or it will never match.\n- Styling must follow the light-mode page pattern\
\ (bg-gray-50, bg-white cards) from About.tsx — not the dark clinical workspace\
\ theme. The page is outside the workspace viewport.\n- No `rounded-xl` or larger.\
\ No inline styles. No `any` TypeScript.\n- The `reloadDocument` prop on the Link\
\ in LeftSidebar is used by existing navigation links (Issues, Settings) to do a\
\ full navigation away from the Cornerstone3D viewport; use it here too.\n- SRS-031\
\ requirement says data is a static constant — do not fetch from GitHub Releases\
\ API or read a file at runtime.\n\n## Tests\n- **Unit / component tests** (`ReleaseNotes.test.tsx`):\n\
\ - `@links:SRS-031` annotation on the describe block\n - T1: render the component\
\ with the default static data; assert at least one version string and one change\
\ item appear in the DOM. `@testing:T1`\n - T2: assert that `RELEASE_NOTES[0].version`\
\ appears before `RELEASE_NOTES[1].version` in the rendered output (if there are\
\ ≥2 entries). `@testing:T2`\n- **Typecheck**: `pnpm --filter @contourlab/client\
\ typecheck` — zero errors\n- **Manual confirmation**: navigate to /release-notes\
\ in a running dev server; verify the page renders correctly with at least one release\
\ entry."
affected_items:
- CR-014
- SYSARCH-001
2 changes: 2 additions & 0 deletions apps/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Routes, Route, Navigate } from 'react-router-dom';
import About from './pages/About';
import Issues from './pages/Issues';
import MainWorkspace from './pages/MainWorkspace';
import ReleaseNotes from './pages/ReleaseNotes';
import Settings from './pages/Settings';

function App() {
Expand All @@ -11,6 +12,7 @@ function App() {
<Route path="/settings" element={<Settings />} />
<Route path="/issues" element={<Issues />} />
<Route path="/about" element={<About />} />
<Route path="/release-notes" element={<ReleaseNotes />} />
<Route path="*" element={<Navigate to="/workspace" replace />} />
</Routes>
);
Expand Down
15 changes: 15 additions & 0 deletions apps/client/src/components/layout/LeftSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ export default function LeftSidebar() {
System
</span>
<div className="flex items-center gap-1">
<Link
to="/release-notes"
reloadDocument
className="flex h-6 w-6 items-center justify-center rounded bg-[var(--color-elevated)] text-[var(--color-text-sec)] hover:bg-[var(--color-hover)] hover:text-[var(--color-text)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500"
title="Release Notes"
aria-label="Release Notes"
>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />
<line x1="8" y1="13" x2="16" y2="13" />
<line x1="8" y1="17" x2="16" y2="17" />
<line x1="8" y1="9" x2="10" y2="9" />
</svg>
</Link>
<Link
to="/issues"
reloadDocument
Expand Down
26 changes: 26 additions & 0 deletions apps/client/src/pages/ReleaseNotes.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import ReleaseNotes from './ReleaseNotes';
import { RELEASE_NOTES } from './releaseNotesData';

// @links:SRS-031
describe('ReleaseNotes', () => {
// @testing:T1
it('renders at least one entry with a version string and change text', () => {
render(<ReleaseNotes />);
expect(RELEASE_NOTES.length).toBeGreaterThan(0);
expect(screen.getByText(`v${RELEASE_NOTES[0].version}`)).toBeTruthy();
expect(screen.getByText(RELEASE_NOTES[0].changes[0])).toBeTruthy();
});

// @testing:T2
it('renders the most recent entry first', () => {
if (RELEASE_NOTES.length < 2) return;
render(<ReleaseNotes />);
const first = screen.getByText(`v${RELEASE_NOTES[0].version}`);
const second = screen.getByText(`v${RELEASE_NOTES[1].version}`);
expect(
first.compareDocumentPosition(second) & Node.DOCUMENT_POSITION_FOLLOWING,
).toBeTruthy();
});
});
Loading
Loading