diff --git a/.gitignore b/.gitignore
index d32b0f2..8ee86f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@ coverage/
solver-c/solver_main
solver-c/solver_fea_main
solver-c/test_solver
+solver-c/test_solver_sanitize
*.o
*.out
diff --git a/Agents/COMPUTE_PLAN.md b/Agents/COMPUTE_PLAN.md
index c33d6e0..3d72abd 100644
--- a/Agents/COMPUTE_PLAN.md
+++ b/Agents/COMPUTE_PLAN.md
@@ -57,6 +57,10 @@ User goals: SROD-like transparency, measured card → downhole + pump movement,
| Fourier analytical baseline | §3 | **Deferred** |
| Full tube–tube contact (Eisner) | §6 | **Deferred** |
+### Tubing gradient (GUI helper)
+
+The **Fluid** tab can fill `TubingGradient` from a **heuristic** bulk-liquid hydrostatic estimate (water cut, oil API, water SG), aligned with the simplified mixture density used in `solver-api/src/xmlParser.js#computeFluidDensityKgM3`. `TubingGradient` is still parsed to `tubingGradientPaM` in Node but is **not** forwarded on the C JSON stdin payload today (`docs/engineering/field-traceability.md`). Wiring it into the damped-wave / pressure BC model would require `MATH_SPEC.md` + C changes + golden refresh.
+
---
## 4. API (quick reference)
@@ -189,14 +193,14 @@ This section is the execution plan for the next pass, optimized for "feature-ric
### 8.4 Priority 4 — Contract hardening
- Keep `schemaVersion: 2` additive contract stable by default.
-- Enforce option-gated heavy payloads (`profiles`, `diagnostics`, `fourier`).
-- Add traceability metadata endpoint/payload support for GUI and audits.
+- Enforce option-gated heavy payloads (`profiles`, `diagnostics`, `fourier`) — **implemented in C stdout** (`enableProfiles`, `enableDiagnosticsDetail`, `enableFourierBaseline`); default API responses omit heavy blocks.
+- Traceability metadata — **`fieldTraceability` on `GET /case/default`, `POST /case/parse`, `POST /solve`, `GET /solve/default`** via [`solver-api/src/fieldTraceability.js`](../solver-api/src/fieldTraceability.js).
**Acceptance gate:** backward-compat tests pass on default endpoints.
### 8.5 Priority 5 — CI/release readiness
-- Add sanitizer runs (ASan/UBSan) for C paths.
+- Add sanitizer runs (ASan/UBSan) for C paths — **`make test-solver-sanitize`** in root [`Makefile`](../Makefile).
- Add runtime/performance budgets on representative cases.
- Enforce quality artifact generation in CI (comparison summaries + drift reports).
diff --git a/Makefile b/Makefile
index 12ab2cb..403ae3f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
SHELL := /bin/bash
-.PHONY: run down logs test smoke
+.PHONY: run down logs test smoke test-solver-sanitize
run:
docker compose up --build
@@ -16,6 +16,14 @@ test:
cd gui-ts && npm test
./solver-c/test_solver
+# ASan/UBSan regression for solver-c (rebuilds a throwaway binary under solver-c/)
+test-solver-sanitize:
+ cd solver-c && gcc -std=c99 -fsanitize=address,undefined -g -O1 \
+ -Iinclude \
+ src/solver_common.c src/json_stdin.c src/trajectory.c src/solver_diagnostic.c \
+ src/solver.c src/solver_fea.c src/solver_fourier.c \
+ tests/test_solver.c -lm -o test_solver_sanitize && ./test_solver_sanitize
+
smoke:
@echo "Checking API health..."
@for i in {1..30}; do \
diff --git a/README.md b/README.md
index 6f54c03..bbcc70d 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Rods Cursor — Rod-string solver (math-first)
+# Majic Rod Solver — Rod-string solver stack (math-first)
Deterministic **C** numerical core (FDM + FEA), **Node** API for XML and orchestration, **TypeScript** GUI for workflow. See **[AGENTS.md](AGENTS.md)** for agent rules and **[Agents/MATH_SPEC.md](Agents/MATH_SPEC.md)** for equations and paper citations.
@@ -107,7 +107,8 @@ make down
## Validation
```bash
-make test # solver-api vitest + gui-ts tests + solver-c test_solver
+make test # solver-api vitest + gui-ts tests + solver-c test_solver
+make test-solver-sanitize # optional: ASan/UBSan build of solver-c test harness
./solver-c/test_solver
```
diff --git a/data/cases/base-case.xml b/data/cases/base-case.xml
index 6c4f13c..4dad1e1 100644
--- a/data/cases/base-case.xml
+++ b/data/cases/base-case.xml
@@ -15,7 +15,7 @@
Need at least 2 survey stations to render 3D wellbore.
; } - const maxDls = Math.max(...geom.segments.map((segment) => segment.dls), 0); - const highDlsCount = geom.segments.filter( - (segment) => segment.dls >= DLS_BAD_SECTION_THRESHOLD - ).length; const totalLen = geom.segments[geom.segments.length - 1].b.md; return ( @@ -258,10 +254,11 @@ export function Wellbore3DView({ sideLoadProfile && sideLoadProfile.length ? sideLoadProfile[Math.min(idx, sideLoadProfile.length - 1)] ?? 0 : 0; + const midMd = (segment.a.md + segment.b.md) * 0.5; const stroke = overlayMode === "sideLoad" ? colorForSideLoad(sideLoad, geom.sideLoadMax) - : colorForDls(segment.dls); + : colorForDepth(midMd, geom.mdMax); const active = highlightedSegmentIndex === idx; return (