diff --git a/DHF/items/00_uc/UC-006.yaml b/DHF/items/00_uc/UC-006.yaml
new file mode 100644
index 0000000..ef4f606
--- /dev/null
+++ b/DHF/items/00_uc/UC-006.yaml
@@ -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
diff --git a/DHF/items/01_req_crs/CRS-013.yaml b/DHF/items/01_req_crs/CRS-013.yaml
new file mode 100644
index 0000000..5df6cf6
--- /dev/null
+++ b/DHF/items/01_req_crs/CRS-013.yaml
@@ -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
diff --git a/DHF/items/02_req_sys/SYS-016.yaml b/DHF/items/02_req_sys/SYS-016.yaml
new file mode 100644
index 0000000..1f3adad
--- /dev/null
+++ b/DHF/items/02_req_sys/SYS-016.yaml
@@ -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
diff --git a/DHF/items/03_req_srs/SRS-031.yaml b/DHF/items/03_req_srs/SRS-031.yaml
new file mode 100644
index 0000000..4f1ab10
--- /dev/null
+++ b/DHF/items/03_req_srs/SRS-031.yaml
@@ -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
diff --git a/DHF/items/04_modules/MODULE-010.yaml b/DHF/items/04_modules/MODULE-010.yaml
new file mode 100644
index 0000000..8e3d309
--- /dev/null
+++ b/DHF/items/04_modules/MODULE-010.yaml
@@ -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
diff --git a/DHF/items/05_swdd/SWDD-013.yaml b/DHF/items/05_swdd/SWDD-013.yaml
new file mode 100644
index 0000000..502ea38
--- /dev/null
+++ b/DHF/items/05_swdd/SWDD-013.yaml
@@ -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 } />. 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
diff --git a/DHF/items/06_sys_arch/SYSARCH-001.yaml b/DHF/items/06_sys_arch/SYSARCH-001.yaml
index 2b893bf..3bb6e73 100644
--- a/DHF/items/06_sys_arch/SYSARCH-001.yaml
+++ b/DHF/items/06_sys_arch/SYSARCH-001.yaml
@@ -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
@@ -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"
diff --git a/DHF/items/09_cr/CR-014.yaml b/DHF/items/09_cr/CR-014.yaml
new file mode 100644
index 0000000..762e697
--- /dev/null
+++ b/DHF/items/09_cr/CR-014.yaml
@@ -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 `} />` 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 `` catch-all.\n4. Add the navigation link\
+ \ in `LeftSidebar.tsx`: add a ``\
+ \ 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 `` 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
diff --git a/apps/client/src/App.tsx b/apps/client/src/App.tsx
index bf6ccdb..19f2748 100644
--- a/apps/client/src/App.tsx
+++ b/apps/client/src/App.tsx
@@ -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() {
@@ -11,6 +12,7 @@ function App() {
} />
} />
} />
+ } />
} />
);
diff --git a/apps/client/src/components/layout/LeftSidebar.tsx b/apps/client/src/components/layout/LeftSidebar.tsx
index 01e2c57..b278c76 100644
--- a/apps/client/src/components/layout/LeftSidebar.tsx
+++ b/apps/client/src/components/layout/LeftSidebar.tsx
@@ -103,6 +103,21 @@ export default function LeftSidebar() {
System
+
+
+
{
+ // @testing:T1
+ it('renders at least one entry with a version string and change text', () => {
+ render();
+ 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();
+ 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();
+ });
+});
diff --git a/apps/client/src/pages/ReleaseNotes.tsx b/apps/client/src/pages/ReleaseNotes.tsx
new file mode 100644
index 0000000..83c2f57
--- /dev/null
+++ b/apps/client/src/pages/ReleaseNotes.tsx
@@ -0,0 +1,42 @@
+import { RELEASE_NOTES } from './releaseNotesData';
+
+function ReleaseNotes() {
+ return (
+
+
+
+
Release Notes
+
+ What's changed in each version of ContourLab
+
+
+
+
+
+ {RELEASE_NOTES.map((entry) => (
+
+
+
+ v{entry.version}
+
+ {entry.date}
+
+
+ {entry.changes.map((change) => (
+
+
+ {change}
+
+ ))}
+
+
+ ))}
+
+
+ );
+}
+
+export default ReleaseNotes;
diff --git a/apps/client/src/pages/releaseNotesData.ts b/apps/client/src/pages/releaseNotesData.ts
new file mode 100644
index 0000000..4f145b3
--- /dev/null
+++ b/apps/client/src/pages/releaseNotesData.ts
@@ -0,0 +1,31 @@
+import { version } from '../../package.json';
+
+export interface ReleaseEntry {
+ version: string;
+ date: string;
+ changes: string[];
+}
+
+export const RELEASE_NOTES: ReleaseEntry[] = [
+ {
+ version,
+ date: '2026-06-05',
+ changes: [
+ 'Added dedicated release notes page accessible from the sidebar system footer',
+ 'Added navigation icon in the left sidebar system footer row for release notes',
+ ],
+ },
+ {
+ version: '0.1.0',
+ date: '2026-05-01',
+ changes: [
+ 'Initial release of ContourLab',
+ 'DICOM repository browser with patient/series worklist',
+ 'Multi-viewport image display with Cornerstone3D rendering',
+ 'RTSTRUCT import, structure list, and per-structure visibility toggles',
+ 'Real-time collaborative contour editing over WebSocket',
+ 'Issues and change request tracking page',
+ 'Settings page with display and preference controls',
+ ],
+ },
+];