aw-spec-cli 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +291 -0
  2. package/assets/docs/ModularMonolith/ModularMonolith.Reference.pack.txt +25248 -0
  3. package/assets/docs/ModularMonolith/ModularMonolith.pack.txt +5726 -0
  4. package/assets/docs/ModularMonolith/pack-foundation.ps1 +145 -0
  5. package/assets/docs/ModularMonolith/pack-foundation.sh +196 -0
  6. package/assets/docs/ModularMonolith/pack-reference-foundation.ps1 +157 -0
  7. package/assets/docs/ModularMonolith/pack-reference-foundation.sh +209 -0
  8. package/assets/docs/ModularMonolith/unpack-foundation.ps1 +126 -0
  9. package/assets/docs/ModularMonolith/unpack-foundation.sh +217 -0
  10. package/assets/docs/ModularMonolith/unpack-reference-foundation.ps1 +129 -0
  11. package/assets/docs/ModularMonolith/unpack-reference-foundation.sh +218 -0
  12. package/assets/docs/UI.Visualization/PACKAGING.md +191 -0
  13. package/assets/docs/UI.Visualization/UI.Visualization.Reference.pack.txt +11768 -0
  14. package/assets/docs/UI.Visualization/UI.Visualization.pack.txt +11214 -0
  15. package/assets/docs/UI.Visualization/pack-foundation.ps1 +165 -0
  16. package/assets/docs/UI.Visualization/pack-foundation.sh +206 -0
  17. package/assets/docs/UI.Visualization/pack-reference-foundation.ps1 +159 -0
  18. package/assets/docs/UI.Visualization/pack-reference-foundation.sh +205 -0
  19. package/assets/docs/UI.Visualization/unpack-foundation.ps1 +126 -0
  20. package/assets/docs/UI.Visualization/unpack-foundation.sh +217 -0
  21. package/assets/docs/UI.Visualization/unpack-reference-foundation.ps1 +129 -0
  22. package/assets/docs/UI.Visualization/unpack-reference-foundation.sh +218 -0
  23. package/assets/docs/VerticalSlice/VerticalSlice.Reference.pack.txt +19318 -0
  24. package/assets/docs/VerticalSlice/pack-reference-foundation.ps1 +157 -0
  25. package/assets/docs/VerticalSlice/pack-reference-foundation.sh +209 -0
  26. package/assets/docs/VerticalSlice/unpack-reference-foundation.ps1 +129 -0
  27. package/assets/docs/VerticalSlice/unpack-reference-foundation.sh +218 -0
  28. package/assets/docs/components/Email/README.md +7 -0
  29. package/assets/docs/components/Email/ReferenceEndpoints/SendEmailEndpoint.cs +84 -0
  30. package/assets/docs/components/Email/integration.md +57 -17
  31. package/assets/docs/components/Jobs/integration.md +29 -15
  32. package/assets/docs/components/QrGenerator/README.md +7 -0
  33. package/assets/docs/components/QrGenerator/ReferenceEndpoints/GenerateQrEndpoint.cs +57 -0
  34. package/assets/docs/components/QrGenerator/integration.md +51 -6
  35. package/assets/docs/components/README.md +19 -2
  36. package/assets/docs/domainspecs/UIVisualization/chart-bubble.md +70 -0
  37. package/assets/docs/domainspecs/UIVisualization/chart-dimred.md +58 -0
  38. package/assets/docs/domainspecs/UIVisualization/chart-heatmap.md +62 -0
  39. package/assets/docs/domainspecs/UIVisualization/chart-network.md +69 -0
  40. package/assets/docs/domainspecs/UIVisualization/chart-parallel.md +75 -0
  41. package/assets/docs/domainspecs/UIVisualization/chart-scurve.md +66 -0
  42. package/assets/docs/domainspecs/UIVisualization/chart-survival.md +69 -0
  43. package/assets/docs/domainspecs/UIVisualization/chart-trellis.md +68 -0
  44. package/assets/docs/domainspecs/UIVisualization/chart-violin.md +61 -0
  45. package/assets/docs/domainspecs/UIVisualization/chart-volcano.md +63 -0
  46. package/assets/docs/domainspecs/UIVisualization/charting-standards.domain.md +80 -0
  47. package/assets/docs/domainspecs/UIVisualization/clean-architecture.domain.md +87 -0
  48. package/assets/docs/domainspecs/UIVisualization/coding-standards.domain.md +47 -0
  49. package/assets/docs/domainspecs/UIVisualization/comandrulemap.md +130 -0
  50. package/assets/docs/domainspecs/UIVisualization/component-contracts.domain.md +67 -0
  51. package/assets/docs/domainspecs/UIVisualization/data-source.domain.md +68 -0
  52. package/assets/docs/domainspecs/UIVisualization/domain-requirements.md +110 -0
  53. package/assets/docs/domainspecs/UIVisualization/folder-structure.domain.md +55 -0
  54. package/assets/docs/domainspecs/UIVisualization/state-management.domain.md +67 -0
  55. package/assets/docs/domainspecs/UIVisualization/testing.domain.md +52 -0
  56. package/assets/docs/domainspecs/UIVisualization/ui-viz-terms.md +39 -0
  57. package/assets/docs/domainspecs/healthcare/comandrulemap.md +47 -0
  58. package/assets/docs/domainspecs/pharma/comandrulemap.md +50 -0
  59. package/dist/commands/init.d.ts.map +1 -1
  60. package/dist/commands/init.js +54 -7
  61. package/dist/commands/init.js.map +1 -1
  62. package/dist/commands/update.d.ts +3 -0
  63. package/dist/commands/update.d.ts.map +1 -0
  64. package/dist/commands/update.js +142 -0
  65. package/dist/commands/update.js.map +1 -0
  66. package/dist/index.js +2 -0
  67. package/dist/index.js.map +1 -1
  68. package/dist/utils/manifest.d.ts +31 -0
  69. package/dist/utils/manifest.d.ts.map +1 -0
  70. package/dist/utils/manifest.js +91 -0
  71. package/dist/utils/manifest.js.map +1 -0
  72. package/dist/utils/update-engine.d.ts +52 -0
  73. package/dist/utils/update-engine.d.ts.map +1 -0
  74. package/dist/utils/update-engine.js +203 -0
  75. package/dist/utils/update-engine.js.map +1 -0
  76. package/package.json +5 -2
  77. package/assets/docs/requirement-spec.txt +0 -15
@@ -10,8 +10,12 @@
10
10
  ## Agent workflow — execute in order
11
11
 
12
12
  1. **Load context.** Read [README.md](./README.md) and this file.
13
- 2. **Locate target projects** (see [[folder-structure]]): `…Application` (injects `IQRGenerator`),
14
- `…Infrastructure` (does the registration), `…Service` (API endpoints).
13
+ 2. **Resolve the host project** `<HostProject>` (see [[folder-structure]], [[components-readme]] R0.2):
14
+ the single startup project that owns **`Program.cs`** + the ASP.NET Core web SDK —
15
+ `<SampleProjectName>.Service` (Vertical Slice foundation), `<SampleProjectName>.API` (Modular
16
+ Monolith foundation), or another name in a future template. **Find it by locating `Program.cs`, not
17
+ by assuming the name.** There is **no** separate `…Application` / `…Infrastructure` project in these
18
+ foundations; `<HostProject>` is the target for C1–C3 below and holds the API endpoints.
15
19
  3. **Present the implementation catalog (below) as a selection list and ASK the user which
16
20
  implementation(s) to integrate.** Use a multi-select prompt (`AskUserQuestion`,
17
21
  `multiSelect: true`) for consistency with the other components.
@@ -21,7 +25,10 @@
21
25
  5. **For each selected implementation, apply its block** (§Implementation blocks).
22
26
  6. **Set the provider:** `configureOptions: options => options.Provider = "<selected key>"`.
23
27
  7. **(Optional) Build the feature slice** (§Usage) — DTO + Command + Handler + Endpoint.
24
- 8. **Verify** (§Verification) and report what was added.
28
+ 8. **Offer the bundled sample endpoint (ASK).** After the wiring above, ask the user (single-select
29
+ yes/no via `AskUserQuestion`) whether to add the ready-made sample endpoint. If **yes**, copy it
30
+ into the host project — see §Sample reference endpoint. If **no**, skip silently.
31
+ 9. **Verify** (§Verification) and report what was added.
25
32
 
26
33
  > **Guardrail — namespace casing.** Package IDs use `QrGenerator` (lower `r`); **namespaces/types
27
34
  > use `QRGenerator` (upper `R`)**. `using AppWeaver.QRGenerator;` is correct — getting this wrong is
@@ -53,8 +60,8 @@
53
60
 
54
61
  ### C2 — Project references (no `Version=`)
55
62
 
56
- **File:** `src/<SampleProjectName>.Application/<SampleProjectName>.Application.csproj`
57
- **File:** `src/<SampleProjectName>.Infrastructure/<SampleProjectName>.Infrastructure.csproj`
63
+ **File:** `src/<HostProject>/<HostProject>.csproj` (the host project resolved in step 2 — single
64
+ project, no separate Application/Infrastructure).
58
65
 
59
66
  ```xml
60
67
  <ItemGroup>
@@ -65,7 +72,10 @@
65
72
 
66
73
  ### C3 — Registration scaffold
67
74
 
68
- **File:** `src/<SampleProjectName>.Infrastructure/Extensions/InfrastructureServiceRegistration.cs`
75
+ **File:** the host project `<HostProject>` (step 2) — register in its
76
+ `Extensions/InfrastructureServiceRegistration.cs` (`AddInfrastructure`) when that file exists (it does
77
+ in the Vertical Slice / Modular Monolith foundations), otherwise directly in `Program.cs` **before**
78
+ `builder.Build()`.
69
79
 
70
80
  ```csharp
71
81
  using AppWeaver.QRGenerator.Extensions; // AddQRGenerator (capital R)
@@ -78,6 +88,12 @@ services.AddQRGenerator(
78
88
  configureOptions: options => options.Provider = "<selected key>"); // step 6
79
89
  ```
80
90
 
91
+ > **Dual local/Aspire startup ([[components-readme]] R0.2a).** If the host project also has an
92
+ > `AddAspireInfrastructure` method (`Extensions/AspireInfrastructureExtensions.cs`) selected by
93
+ > `if (isAspire) … else …` in `Program.cs`, add this **same** `AddQRGenerator(...)` block to **both**
94
+ > `AddInfrastructure` and `AddAspireInfrastructure` (it is DB-agnostic — identical in both), or
95
+ > `IQRGenerator` is unresolvable when the app runs under Aspire.
96
+
81
97
  No `appsettings.json` entry is required — there is nothing to configure.
82
98
 
83
99
  ---
@@ -169,6 +185,32 @@ public class GenerateQrRequest { public string Content { get; set; } = string.Em
169
185
 
170
186
  ---
171
187
 
188
+ ## Sample reference endpoint
189
+
190
+ The §Usage slice above routes through a command + handler. For a quick, self-contained sample there
191
+ is also a ready-to-run FastEndpoint bundled in this component folder at
192
+ [`ReferenceEndpoints/GenerateQrEndpoint.cs`](./ReferenceEndpoints/GenerateQrEndpoint.cs). It injects
193
+ `IQRGenerator` directly and does the **full sequence inline** — no command/handler/mediator. It is
194
+ **not** wired automatically; it is offered in workflow step 8.
195
+
196
+ **When the user opts in (step 8) — copy procedure:**
197
+
198
+ 1. Confirm `docs/components/QrGenerator/ReferenceEndpoints/GenerateQrEndpoint.cs` exists. If it is
199
+ missing, tell the user and skip (do not hand-write a replacement).
200
+ 2. Create `src/<HostProject>/ReferenceEndpoints/` if it does not exist (`<HostProject>` = the project
201
+ resolved in step 2).
202
+ 3. Copy the file to `src/<HostProject>/ReferenceEndpoints/GenerateQrEndpoint.cs`, replacing the
203
+ namespace token `__HOSTPROJECT__` with `<HostProject>`'s root namespace (e.g.
204
+ `AppWeaver.Health.Service`). Keep the `using AppWeaver.QRGenerator;` (capital R) — do not "fix" it
205
+ to match the package id. Change nothing else.
206
+ 4. Do **not** touch the C1–C3 wiring. FastEndpoints auto-discovers the endpoint by assembly scan, so
207
+ no extra registration is required.
208
+
209
+ **Endpoint:** `POST /reference/qr-codes` — body `{ "content" }` — returns an `image/png` body.
210
+ An empty `content` string is valid (returns a small PNG); `null` is rejected with 400.
211
+
212
+ ---
213
+
172
214
  ## Verification
173
215
 
174
216
  1. **Builds** — `dotnet build` succeeds; `using` directives use `AppWeaver.QRGenerator…`; no
@@ -178,6 +220,9 @@ public class GenerateQrRequest { public string Content { get; set; } = string.Em
178
220
  4. **Generates** — `GenerateQR("test")` returns a non-empty `byte[]`; `POST /qr-codes` returns an
179
221
  `image/png` body that opens as a scannable QR image. (`null` content throws; empty string is
180
222
  valid and returns a PNG.)
223
+ 5. **Sample endpoint (only if copied in step 8)** — the build still succeeds with
224
+ `ReferenceEndpoints/GenerateQrEndpoint.cs` added, the `__HOSTPROJECT__` token was replaced, and
225
+ `POST /reference/qr-codes` returns an `image/png`.
181
226
 
182
227
  ## Related
183
228
 
@@ -42,8 +42,25 @@ Every folder contains two independent, agent-readable files:
42
42
 
43
43
  - **R0.1** — NuGet versions live in `Directory.Packages.props` only (`<PackageVersion>`); project
44
44
  files reference the package **without** a `Version=` attribute (see [[dependency-policy]]).
45
- - **R0.2** — Register the component in the **Infrastructure** registration extension, never in a
46
- feature slice or domain code (see [[clean-architecture]]).
45
+ - **R0.2** — Register the component in the **startup/host project** — the one project that owns
46
+ `Program.cs` + the ASP.NET Core web SDK. Its name is template-dependent: `<App>.Service` (Vertical
47
+ Slice foundation), `<App>.API` (Modular Monolith foundation), and possibly other names as templates
48
+ are added — so **resolve it by locating `Program.cs`, never by assuming a fixed project name**.
49
+ These foundations have **no** separate `…Application` / `…Infrastructure` project. Put the
50
+ `AddXxx(...)` registration in that project's `Extensions/InfrastructureServiceRegistration.cs`
51
+ (`AddInfrastructure`) when that extension exists, otherwise directly in `Program.cs` before
52
+ `builder.Build()`. Never register in a feature slice or domain code (see [[clean-architecture]]).
53
+ - **R0.2a** — *(dual local/Aspire startup)* Some foundations split infrastructure registration across
54
+ **two** methods that `Program.cs` selects at runtime — `if (isAspire) AddAspireInfrastructure(...)`
55
+ (the Aspire path: `Extensions/AspireInfrastructureExtensions.cs`, typically PostgreSQL + Redis)
56
+ `else AddInfrastructure(...)` (the local/dev path:
57
+ `Extensions/InfrastructureServiceRegistration.cs`, typically SQLite + in-memory cache). When
58
+ **both** methods exist, add the component registration to **both**, or the abstraction is
59
+ unresolvable in whichever run mode you skipped (e.g. `IEmailService` throws at runtime under
60
+ Aspire). The registration block is **identical** in both — copy it verbatim — *except*
61
+ provider-specific calls (connection strings, `UseSqlite(...)` vs `UseNpgsql(...)`, cache provider),
62
+ which must match each method's existing provider. If only `AddInfrastructure` (or only `Program.cs`)
63
+ exists, register once as in R0.2.
47
64
  - **R0.3** — Inject the **abstraction** (`IEmailService`, `IQRGenerator`, `IJobScheduler`) into
48
65
  handlers — never the concrete provider type (see [[cqrs]]).
49
66
  - **R0.4** — Always set `options.Provider = "<Key>"` explicitly. Do not rely on the implicit
@@ -0,0 +1,70 @@
1
+ # BubbleChart — Chart Rule
2
+
3
+ > Load **only when "Bubble" (or "all plots") is in the selection** (see [[domain-requirements]]).
4
+ > Component: `BubbleChart` in `components/charts/generic/BubbleChart.tsx`. Built on [[charting-standards.domain]].
5
+
6
+ ## Purpose
7
+ Multi-dimensional scatter — each point encodes **x, y, size, colour, label** (up to 5 dimensions).
8
+ Use for potency vs MW, portfolio risk/return, effort vs impact, market mapping.
9
+
10
+ ## Props contract
11
+ ```ts
12
+ interface BubblePoint { x: number; y: number; size: number; label: string; meta?: Record<string, string|number> }
13
+ interface BubbleSeries { seriesLabel: string; color?: string; points: BubblePoint[] }
14
+
15
+ interface BubbleChartProps {
16
+ series: BubbleSeries[] // REQUIRED — one series per colour/category
17
+ xLabel: string // REQUIRED
18
+ yLabel: string // REQUIRED
19
+ sizeLabel?: string // shown as "Bubble size ∝ …" annotation
20
+ title?: string
21
+ xAxisType?: 'linear' | 'log'
22
+ yAxisType?: 'linear' | 'log'
23
+ sizeRange?: [number, number] // [minPx, maxPx] bubble diameter (default [8,40])
24
+ xRefLine?: number; xRefLabel?: string // optional vertical reference line
25
+ yRefLine?: number; yRefLabel?: string // optional horizontal reference line
26
+ height?: number
27
+ showLabels?: boolean // annotate points with their label
28
+ }
29
+ ```
30
+
31
+ ## Rules
32
+ - Bubble sizes are **normalised across the whole dataset** to `sizeRange` — pass raw magnitudes;
33
+ the chart scales them. Don't pre-scale to pixels.
34
+ - Group points into **series by the categorical dimension** (target, region, product line) so
35
+ colour + legend come for free.
36
+ - Use `xAxisType="log"` / `yAxisType="log"` or pre-`log10` the value (e.g. IC₅₀) — pick one,
37
+ document it in the axis label.
38
+
39
+ ## Adapter example (agent reference)
40
+ ```tsx
41
+ 'use client'
42
+ import { BubbleChart } from '@/components/charts/generic'
43
+ import type { Compound } from '@/lib/types/pharma'
44
+
45
+ export function compoundsToBubbleSeries(compounds: Compound[]) {
46
+ const targets = [...new Set(compounds.map(c => c.target))]
47
+ return targets.map(t => ({
48
+ seriesLabel: t,
49
+ points: compounds.filter(c => c.target === t).map(c => ({
50
+ x: c.molecularWeight, y: Math.log10(c.ic50_nM),
51
+ size: c.selectivityRatio, label: c.name,
52
+ meta: { Status: c.status, 'IC₅₀': `${c.ic50_nM} nM` },
53
+ })),
54
+ }))
55
+ }
56
+
57
+ export default function CompoundScatterPlot({ compounds, height }: { compounds: Compound[]; height?: number }) {
58
+ return (
59
+ <BubbleChart series={compoundsToBubbleSeries(compounds)}
60
+ xLabel="Molecular Weight (Da)" yLabel="log₁₀(IC₅₀, nM)" sizeLabel="Selectivity Ratio"
61
+ xRefLine={500} xRefLabel="Ro5 MW limit" sizeRange={[7, 28]} height={height} />
62
+ )
63
+ }
64
+ ```
65
+
66
+ ## Scaffolds when selected
67
+ - generic `BubbleChart` + `app/bubble/page.tsx` + nav tab + optional adapter.
68
+
69
+ ## Related
70
+ - [[charting-standards.domain]] · [[component-contracts.domain]] · [[clean-architecture.domain]]
@@ -0,0 +1,58 @@
1
+ # DimRedPlot — Chart Rule
2
+
3
+ > Load **only when "Dim. Reduction / PCA / t-SNE / UMAP" (or "all plots") is selected** (see [[domain-requirements]]).
4
+ > Component: `DimRedPlot` in `components/charts/generic/DimRedPlot.tsx`. Built on [[charting-standards.domain]].
5
+
6
+ ## Purpose
7
+ 2-D scatter of **pre-computed** dimensionality-reduction coordinates, coloured by group, with
8
+ optional cluster-centroid labels and density contours. Use for single-cell clustering, customer
9
+ segments, NLP embeddings, chemical-space maps, anomaly scatter.
10
+
11
+ ## Props contract
12
+ ```ts
13
+ interface DimRedPoint {
14
+ x: number; y: number // reduced coordinates (caller ran PCA/t-SNE/UMAP)
15
+ label: string
16
+ group: string // category → colour + legend entry
17
+ meta?: Record<string, string|number>
18
+ }
19
+ interface DimRedPlotProps {
20
+ points: DimRedPoint[] // REQUIRED
21
+ method?: string // badge: "PCA", "t-SNE", "UMAP" (default "DR")
22
+ xLabel?: string; yLabel?: string // e.g. "PC1 (34.2% var)", "UMAP 1"
23
+ colorByLabel?: string // legend header / tooltip ("Cell type", "Segment")
24
+ showClusterLabels?: boolean // centroid label per group (default true)
25
+ showDensityContours?: boolean // KDE contour per group (default false)
26
+ markerSize?: number // default 5
27
+ title?: string; height?: number
28
+ }
29
+ ```
30
+
31
+ ## Rules
32
+ - The chart **does not run** PCA/t-SNE/UMAP — coordinates are computed upstream (Python,
33
+ backend, or a JS lib) and passed in. Document the method in `method` + axis labels.
34
+ - `group` drives colour and the legend; keep group count modest (≤ ~12) for distinct colours.
35
+ - Turn on `showDensityContours` for large, overlapping point clouds; off for small datasets.
36
+
37
+ ## Page/adapter example (agent reference)
38
+ ```tsx
39
+ 'use client'
40
+ import { DimRedPlot } from '@/components/charts/generic'
41
+
42
+ // cells already have umap1/umap2 + cellType from the pipeline
43
+ export default function CellUmap({ cells }: { cells: { id: string; umap1: number; umap2: number; cellType: string }[] }) {
44
+ return (
45
+ <DimRedPlot
46
+ points={cells.map(c => ({ x: c.umap1, y: c.umap2, label: c.id, group: c.cellType }))}
47
+ method="UMAP" xLabel="UMAP 1" yLabel="UMAP 2" colorByLabel="Cell type" showClusterLabels
48
+ />
49
+ )
50
+ }
51
+ ```
52
+
53
+ ## Scaffolds when selected
54
+ - generic `DimRedPlot` + `app/dimred/page.tsx` + nav tab + optional adapter (+ a place to compute
55
+ or fetch coordinates).
56
+
57
+ ## Related
58
+ - [[charting-standards.domain]] · [[component-contracts.domain]] · [[clean-architecture.domain]]
@@ -0,0 +1,62 @@
1
+ # Heatmap — Chart Rule
2
+
3
+ > Load **only when "Heatmap" (or "all plots") is in the selection** (see [[domain-requirements]]).
4
+ > Component: `Heatmap` in `components/charts/generic/Heatmap.tsx`. Built on [[charting-standards.domain]].
5
+
6
+ ## Purpose
7
+ Colour-encoded grid for a 2-D value matrix. Use for correlation matrices, expression grids
8
+ (log₂FC), confusion matrices, risk tables, cross-asset return grids.
9
+
10
+ ## Props contract
11
+ ```ts
12
+ type ColourPreset = 'diverging-purple-red' | 'sequential-teal' | 'sequential-heat' | 'diverging-blue-red'
13
+
14
+ interface HeatmapProps {
15
+ matrix: number[][] // matrix[row][col] — REQUIRED
16
+ rowLabels: string[] // y-axis — REQUIRED
17
+ colLabels: string[] // x-axis — REQUIRED
18
+ title?: string
19
+ unit?: string // colour-bar title + tooltip unit (e.g. "log₂FC", "r")
20
+ colorscale?: ColourPreset | [number, string][] // default 'diverging-purple-red'
21
+ zMid?: number // centre of a diverging scale (use 0 for FC / correlation)
22
+ zMin?: number; zMax?: number // override colour range (defaults to matrix min/max)
23
+ showCellValues?: boolean // annotate each cell (default true)
24
+ cellValuePrecision?: number // decimals in annotations (default 2)
25
+ cellValueContrastThreshold?: number // |value| above → white text (default 1.0)
26
+ height?: number // omit for responsive fill
27
+ reverseRows?: boolean
28
+ }
29
+ ```
30
+
31
+ ## Rules
32
+ - `matrix.length === rowLabels.length`; every row length `=== colLabels.length`. Validate in the
33
+ adapter, not the chart.
34
+ - For **diverging** data (fold-change, correlation) set `zMid={0}` so zero maps to the neutral
35
+ midpoint; for **sequential** data (counts, intensity) use a `sequential-*` preset and omit `zMid`.
36
+ - Keep cell annotations on for ≤ ~12×12 grids; turn `showCellValues` off for dense matrices.
37
+
38
+ ## Adapter example (agent reference)
39
+ ```tsx
40
+ 'use client'
41
+ import { Heatmap } from '@/components/charts/generic'
42
+ import type { BiomarkerData } from '@/lib/types/pharma'
43
+
44
+ export default function BiomarkerHeatmap({ data, height }: { data: BiomarkerData; height?: number }) {
45
+ return (
46
+ <Heatmap
47
+ matrix={data.matrix} rowLabels={data.treatments} colLabels={data.markers}
48
+ unit="log₂FC" colorscale="diverging-purple-red" zMid={0}
49
+ showCellValues cellValuePrecision={2} cellValueContrastThreshold={1.5}
50
+ height={height}
51
+ />
52
+ )
53
+ }
54
+ ```
55
+
56
+ ## Scaffolds when selected
57
+ - `components/charts/generic/Heatmap.tsx` (from the generic library — always present)
58
+ - `app/heatmap/page.tsx` (example route) + nav tab
59
+ - optional adapter `components/charts/<Domain>Heatmap.tsx`
60
+
61
+ ## Related
62
+ - [[charting-standards.domain]] · [[component-contracts.domain]] · [[clean-architecture.domain]]
@@ -0,0 +1,69 @@
1
+ # NetworkGraph — Chart Rule
2
+
3
+ > Load **only when "Network / graph / node-edge" (or "all plots") is selected** (see [[domain-requirements]]).
4
+ > Component: `NetworkGraph` in `components/charts/generic/NetworkGraph.tsx`. Built on [[charting-standards.domain]].
5
+
6
+ ## Purpose
7
+ Force-directed **node-edge** graph rendered with Plotly scatter+line traces; layout is computed
8
+ in-component (Fruchterman-Reingold spring or circular) so **no extra graph library** is needed.
9
+ Use for PPI/pathway networks, microservice/dependency maps, knowledge graphs, org/social graphs.
10
+
11
+ ## Props contract
12
+ ```ts
13
+ interface NetworkNode {
14
+ id: string // referenced by edges
15
+ label: string
16
+ group?: string // colour + legend
17
+ size?: number // marker size (importance, default 10)
18
+ meta?: Record<string, string|number>
19
+ }
20
+ interface NetworkEdge {
21
+ source: string; target: string // node ids
22
+ weight?: number // line thickness (default 1)
23
+ label?: string // hover label
24
+ }
25
+ interface NetworkGraphProps {
26
+ nodes: NetworkNode[]; edges: NetworkEdge[] // REQUIRED
27
+ layout?: 'spring' | 'circular' // default 'spring'
28
+ springIterations?: number // default 120 (higher = better layout, slower)
29
+ showEdgeLabels?: boolean // default true
30
+ directed?: boolean // arrowheads (default false)
31
+ title?: string; height?: number
32
+ }
33
+ ```
34
+
35
+ ## Rules
36
+ - Every edge `source`/`target` MUST reference an existing node `id`. Validate in the adapter;
37
+ dangling edges are dropped silently.
38
+ - Layout is **deterministic per dataset** (warm-started from a circle) but recomputes when the
39
+ node/edge set changes — memoise `nodes`/`edges` so it doesn't recompute each render.
40
+ - Keep graphs modest (≲ ~150 nodes) for readable spring layout; above that, pre-cluster or paginate.
41
+ - Set `directed` for DAGs / signalling cascades; leave off for undirected association graphs.
42
+
43
+ ## Page/adapter example (agent reference)
44
+ ```tsx
45
+ 'use client'
46
+ import { NetworkGraph } from '@/components/charts/generic'
47
+ import type { NetworkNode, NetworkEdge } from '@/components/charts/generic'
48
+
49
+ const NODES: NetworkNode[] = [
50
+ { id: 'EGFR', label: 'EGFR', group: 'Receptor', size: 26 },
51
+ { id: 'RAS', label: 'RAS', group: 'GTPase', size: 22 },
52
+ { id: 'RAF', label: 'BRAF', group: 'Kinase', size: 20 },
53
+ ]
54
+ const EDGES: NetworkEdge[] = [
55
+ { source: 'EGFR', target: 'RAS', weight: 2.5, label: 'activates' },
56
+ { source: 'RAS', target: 'RAF', weight: 2, label: 'activates' },
57
+ ]
58
+
59
+ export default function PathwayNetwork() {
60
+ return <NetworkGraph nodes={NODES} edges={EDGES} layout="spring" springIterations={150} directed showEdgeLabels />
61
+ }
62
+ ```
63
+
64
+ ## Scaffolds when selected
65
+ - generic `NetworkGraph` + `app/network/page.tsx` + nav tab + optional adapter; type the node/edge
66
+ arrays with `NetworkNode[]` / `NetworkEdge[]` to avoid `meta` literal-type errors.
67
+
68
+ ## Related
69
+ - [[charting-standards.domain]] · [[component-contracts.domain]] · [[clean-architecture.domain]]
@@ -0,0 +1,75 @@
1
+ # ParallelCoordinatesPlot — Chart Rule
2
+
3
+ > Load **only when "Parallel Coords / parcoords" (or "all plots") is selected** (see [[domain-requirements]]).
4
+ > Component: `ParallelCoordinatesPlot` in `components/charts/generic/ParallelCoordinatesPlot.tsx`.
5
+ > Built on [[charting-standards.domain]]; uses Plotly's native `parcoords` (interactive axis brushing).
6
+
7
+ ## Purpose
8
+ Multi-dimensional data as **polylines crossing parallel vertical axes** — one line per observation.
9
+ Use for multi-parameter optimisation (MPO), ADMET profiles, hyperparameter sweeps, KPI dashboards,
10
+ portfolio metrics.
11
+
12
+ ## Props contract
13
+ ```ts
14
+ interface PCDimension {
15
+ key: string // matches a key in each trace's `values`
16
+ label: string
17
+ range?: [number, number] // axis range (auto if omitted)
18
+ tickFormat?: string // d3 format, e.g. ".2f", ".0f"
19
+ invertAxis?: boolean // for "lower is better" metrics (IC₅₀, p-value)
20
+ }
21
+ interface PCTrace {
22
+ id: string; label: string
23
+ colorValue: number // drives the line colour (continuous)
24
+ values: Record<string, number> // one value per dimension key
25
+ group?: string
26
+ }
27
+ type PCColorscale = 'teal-purple' | 'blue-red' | 'viridis' | 'plasma' | [number, string][]
28
+
29
+ interface ParallelCoordinatesPlotProps {
30
+ dimensions: PCDimension[] // REQUIRED — the axes
31
+ traces: PCTrace[] // REQUIRED — one per observation
32
+ colorLabel?: string // colour-bar title
33
+ colorscale?: PCColorscale // default 'teal-purple'
34
+ lineOpacity?: number; lineWidth?: number
35
+ title?: string; height?: number
36
+ }
37
+ ```
38
+
39
+ ## Rules
40
+ - `dimensions[].key` must exist in **every** trace's `values`. Validate in the adapter.
41
+ - Use `invertAxis` for metrics where **lower is better** so "good" lines trend the same way.
42
+ - `colorValue` is a single continuous dimension (often the headline metric, e.g. IC₅₀ or score);
43
+ it maps onto `colorscale`. Brushing on any axis filters interactively — no extra wiring.
44
+
45
+ ## Page/adapter example (agent reference)
46
+ ```tsx
47
+ 'use client'
48
+ import { ParallelCoordinatesPlot } from '@/components/charts/generic'
49
+ import type { Compound } from '@/lib/types/pharma'
50
+
51
+ const DIMS = [
52
+ { key: 'mw', label: 'MW (Da)', range: [300, 560] as [number, number], tickFormat: '.0f' },
53
+ { key: 'logP', label: 'LogP', range: [1.5, 5.5] as [number, number], tickFormat: '.1f' },
54
+ { key: 'ic50', label: 'IC₅₀ (nM)', range: [0, 800] as [number, number], tickFormat: '.0f', invertAxis: true },
55
+ ]
56
+
57
+ export default function MpoParallel({ compounds }: { compounds: Compound[] }) {
58
+ return (
59
+ <ParallelCoordinatesPlot
60
+ dimensions={DIMS}
61
+ traces={compounds.filter(c => c.ic50_nM <= 800).map(c => ({
62
+ id: c.id, label: c.name, group: c.status, colorValue: c.ic50_nM,
63
+ values: { mw: c.molecularWeight, logP: c.logP, ic50: c.ic50_nM },
64
+ }))}
65
+ colorLabel="IC₅₀ (nM)" colorscale="teal-purple"
66
+ />
67
+ )
68
+ }
69
+ ```
70
+
71
+ ## Scaffolds when selected
72
+ - generic `ParallelCoordinatesPlot` + `app/parallel/page.tsx` + nav tab + optional adapter.
73
+
74
+ ## Related
75
+ - [[charting-standards.domain]] · [[component-contracts.domain]] · [[clean-architecture.domain]]
@@ -0,0 +1,66 @@
1
+ # SCurveChart — Chart Rule
2
+
3
+ > Load **only when "S-Curve / dose-response / sigmoid" (or "all plots") is selected** (see [[domain-requirements]]).
4
+ > Component: `SCurveChart` in `components/charts/generic/SCurveChart.tsx`. Built on [[charting-standards.domain]].
5
+
6
+ ## Purpose
7
+ One or more **sigmoidal (S-shaped)** curves with optional inflection markers. Use for
8
+ dose-response (4-PL / Hill), technology adoption, market penetration, enzyme kinetics, SaaS ramp.
9
+
10
+ ## Props contract
11
+ ```ts
12
+ interface SCurvePoint { x: number; y: number }
13
+ interface SCurve {
14
+ id: string; label: string; color?: string
15
+ points: SCurvePoint[] // PRE-COMPUTED by the caller (model runs outside the chart)
16
+ inflectionX?: number // e.g. IC₅₀ / midpoint — draws a marker + dashed line
17
+ inflectionLabel?: string // "IC₅₀", "EC₅₀", "Midpoint", "Tipping point"
18
+ metadata?: Record<string, string> // tooltip enrichment
19
+ }
20
+ interface SCurveChartProps {
21
+ curves: SCurve[] // REQUIRED
22
+ xLabel: string; yLabel: string // REQUIRED
23
+ xAxisType?: 'linear' | 'log'
24
+ referenceY?: number // horizontal reference (default 50)
25
+ referenceYLabel?: string
26
+ yRange?: [number, number] // default [-5, 108]
27
+ title?: string; height?: number
28
+ }
29
+ ```
30
+
31
+ ## Rules
32
+ - The chart **plots points, it does not fit models** — the caller generates curve points (4-PL,
33
+ logistic, etc.) and passes them. Provide generators in `lib/utils.ts`
34
+ (`generate4PLCurve`, `generateLogisticPoints`).
35
+ - `inflectionX` is in the **same x-units** as the points (e.g. `log10(ic50_nM)` if x is log-conc).
36
+ - Use `referenceY={50}` for "50% response/adoption"; set to `undefined` to hide.
37
+
38
+ ## Adapter example (agent reference)
39
+ ```tsx
40
+ 'use client'
41
+ import { SCurveChart } from '@/components/charts/generic'
42
+ import { PHARMA_COLORS } from '@/components/charts/PlotlyChart'
43
+ import type { DoseResponseCurve } from '@/lib/types/pharma'
44
+
45
+ export default function DoseResponseChart({ curves, height }: { curves: DoseResponseCurve[]; height?: number }) {
46
+ return (
47
+ <SCurveChart
48
+ curves={curves.map((c, i) => ({
49
+ id: c.compoundId, label: c.compoundName, color: PHARMA_COLORS[i % PHARMA_COLORS.length],
50
+ points: c.points.map(p => ({ x: p.logConc, y: p.response })),
51
+ inflectionX: Math.log10(c.ic50_nM), inflectionLabel: 'IC₅₀',
52
+ metadata: { Target: c.target, 'Hill slope': c.hillSlope.toFixed(2) },
53
+ }))}
54
+ xLabel="log₁₀[Concentration (nM)]" yLabel="% Inhibition"
55
+ referenceY={50} referenceYLabel="50% Inhibition" height={height}
56
+ />
57
+ )
58
+ }
59
+ ```
60
+
61
+ ## Scaffolds when selected
62
+ - generic `SCurveChart` + curve generators in `lib/utils.ts` + `app/scurve/` (or `dose-response/`)
63
+ route + nav tab + optional adapter.
64
+
65
+ ## Related
66
+ - [[charting-standards.domain]] · [[component-contracts.domain]] · [[clean-architecture.domain]]
@@ -0,0 +1,69 @@
1
+ # SurvivalCurveChart — Chart Rule
2
+
3
+ > Load **only when "Survival / Kaplan-Meier / retention" (or "all plots") is selected** (see [[domain-requirements]]).
4
+ > Component: `SurvivalCurveChart` in `components/charts/generic/SurvivalCurveChart.tsx`. Built on [[charting-standards.domain]].
5
+
6
+ ## Purpose
7
+ Kaplan-Meier–style **step-function** curves with optional CI shading, median markers, and a
8
+ significance annotation. Use for clinical OS/PFS, user-cohort retention, component MTTF, churn.
9
+
10
+ ## Props contract
11
+ ```ts
12
+ interface SurvivalPoint {
13
+ time: number
14
+ survival: number // 0–1 (×100 applied) or 0–100
15
+ ciLower?: number; ciUpper?: number // same scale as survival
16
+ nAtRisk?: number
17
+ }
18
+ interface SurvivalArm {
19
+ label: string; color: string // colour REQUIRED — distinguishes arms
20
+ points: SurvivalPoint[]
21
+ medianTime?: number | null // null = not reached
22
+ }
23
+ interface SurvivalCurveChartProps {
24
+ arms: SurvivalArm[] // REQUIRED
25
+ timeLabel?: string // default "Time"
26
+ survivalLabel?: string // default "Survival Probability (%)"
27
+ eventLabel?: string // "Death", "Churn", "Failure" …
28
+ medianLabel?: string // "mOS", "mPFS", "Median retention"
29
+ curveType?: string // corner badge: "OS", "KM", "Retention"
30
+ pValue?: string // "< 0.001", "n.s."
31
+ showCI?: boolean // default true (needs ciLower/ciUpper)
32
+ xMax?: number; height?: number
33
+ }
34
+ ```
35
+
36
+ ## Rules
37
+ - Curve is a **step function** (`shape: 'hv'`) — survival is constant between events. Pass points
38
+ at event/censor times; the chart draws the steps.
39
+ - `survival` accepts either 0–1 or 0–100 — the chart normalises; be consistent within an arm.
40
+ - CI shading requires `ciLower`/`ciUpper` on the points and `showCI` (default on).
41
+ - `medianTime: null` renders "Not reached" — use it, don't fake a value.
42
+
43
+ ## Adapter example (agent reference)
44
+ ```tsx
45
+ 'use client'
46
+ import { SurvivalCurveChart } from '@/components/charts/generic'
47
+ import type { TrialArm } from '@/lib/types/pharma'
48
+
49
+ export default function KaplanMeierChart({ arms, height }: { arms: TrialArm[]; height?: number }) {
50
+ return (
51
+ <SurvivalCurveChart
52
+ arms={arms.map(a => ({
53
+ label: a.name, color: a.color, medianTime: a.medianSurvival,
54
+ points: a.points.map(p => ({ time: p.time, survival: p.survival,
55
+ ciLower: p.ciLower, ciUpper: p.ciUpper, nAtRisk: p.nAtRisk })),
56
+ }))}
57
+ timeLabel="Days" survivalLabel="Survival Probability (%)" medianLabel="mOS"
58
+ curveType="OS" pValue="< 0.001" showCI height={height}
59
+ />
60
+ )
61
+ }
62
+ ```
63
+
64
+ ## Scaffolds when selected
65
+ - generic `SurvivalCurveChart` + `app/survival/` (or `trials/`) route + nav tab + optional adapter;
66
+ an optional number-at-risk table consumes `nAtRisk`.
67
+
68
+ ## Related
69
+ - [[charting-standards.domain]] · [[component-contracts.domain]] · [[clean-architecture.domain]]