---
path: /studio/neith/inverse-modeling
surface: studio
domain: neith
auth: signed-in + studio entitlement (fit-quality lane is admin-scoped: admin:* | admin:studio, fail-closed)
source: apps/oshun/web/src/app/studio/neith/inverse-modeling/page.tsx
status: walked
last_walked:
  '2026-06-25 by Codex (V1 reference-image feature extraction, four-model parameter estimates, confidence scoring, export manifest, domain tests, and E2E), against commit HEAD'
  '2026-06-25 by Codex (recoverable catalog retry, submit busy state, mobile tap targets, and live-BFF evaluator coverage), against commit HEAD'
  '2026-06-23 by Codex (direct-route auth readiness + live E2E), against commit HEAD'
---

# Studio Neith · Inverse Modeling

## Purpose

Studio-only Neith inverse procedural modeling console. The route now has two
lanes:

- **Reference Inversion Lane** — paste a compact RGBA reference-image payload,
  extract the V1 feature vector (`mean`, `contrast`, `edgeEnergy`, `entropy`,
  `warmBias`), estimate parameters for all four Neith model families (`terrain`,
  `vegetation`, `urban`, `noise`), score confidence, and emit a deterministic
  export manifest for downstream Studio handoff.
- **Goodness-of-Fit Lane** — admin-scoped JSON sample evaluator backed by the
  real BFF (`/v1/admin/studio/fit-quality`) that reports per-sample residuals,
  RMSE, MAE, R², and the worst-fitting sample.

The page mounts `ShellLayout` (active=studio), the
`StudioNeithInverseModelingWorkspace` body, the canonical route-map, and a
quick-action lane to two Isis sibling workspaces.

## Entry points

- **Linked from `/studio` body** — Domain-bridge studios section lists Neith
- **Quick action lanes from siblings**: isis/reference-image-processing,
  isis/live-preview (cross-domain links into Isis)
- **Direct URL / bookmark** — yes; metadata sets `alternates.canonical` to
  `/studio/neith/inverse-modeling`

## Layout regions

`page.tsx` renders inside `<ShellLayout active="studio">`:

- **Header**: shell header (active=studio)
- **Main**: `<StudioNeithInverseModelingWorkspace />` — client component
  (`'use client'`) with `useAuth()` access-token readiness plus state hooks for
  `outcome` (catalog load result), fit-quality `payloadText`, `result`,
  `evaluateError`, reference-image `referenceText`, `inverseReport`, and
  `inverseError`
  - Eyebrow "Neith Procgen"
  - `<h1>` "Inverse Modeling Workspace" (via `WorkspaceHeading`)
  - Summary `<p data-neith-inverse-modeling-summary>` describing the
    feature-extraction, four-model estimate, export-manifest, and
    goodness-of-fit flow
  - **Reference Inversion Lane** (`data-neith-reference-lane`) with
    `<h2 data-neith-reference-lane-heading>` "Reference Inversion Lane":
    - `<form data-neith-reference-form>` with
      `<textarea data-neith-reference-payload>`
      (`aria-label="Neith reference image json"`, `rows={14}`, monospace, seeded
      with `DEFAULT_NEITH_REFERENCE_IMAGE`) and
      `<button data-neith-reference-submit>` "Run inverse modeling"
    - Error: `<div data-neith-reference-error role="alert">` with parser or
      validation error; prior `inverseReport` remains rendered
    - Feature grid: `<div data-neith-feature-grid>` with cells keyed by
      `data-neith-feature="mean|contrast|edgeEnergy|entropy|warmBias"`
    - Model grid: four
      `<article data-neith-model-card data-neith-model="terrain|vegetation|urban|noise">`
      cards with `data-neith-model-confidence`, parameter rows
      (`data-neith-param-row data-neith-param-name`), and rationale copy
    - Export manifest: keyboard-focusable
      `<pre data-neith-export-manifest tabindex="0" aria-label="Neith inverse modeling export manifest">`
  - **Goodness-of-Fit Lane** (`WorkspaceSection`) with
    `<h2 data-fq-lane-heading>` "Goodness-of-Fit Lane". Its body depends on the
    catalog-load outcome:
    - Loading: `<p data-fq-loading>` "Loading fit-quality evaluator…"
    - Unauthorized: `<div data-fq-unauthorized>` "Access restricted." + reason
    - Error: `<div data-fq-error>` "Could not load the fit-quality evaluator." +
      reason
    - Ready (catalog present):
      - `<p data-fq-enums>` "<n> metrics" (from `catalog.metrics.length`)
      - `<form data-fq-form>` with a labelled `<textarea data-fq-payload>`
        (`aria-label="fit quality samples json"`, `rows={16}`, monospace, seeded
        with `DEFAULT_PAYLOAD`) and a `<button data-fq-submit type="submit">`
        "Evaluate fit quality" / "Evaluating fit quality…"
      - On catalog error: `<div data-fq-error role="alert">` with the message
        and `<button data-fq-retry>` "Retry evaluator"
      - On evaluate error: `<div data-fq-evaluate-error role="alert">` with the
        message
      - On result: `<div data-fq-result role="status">` containing a
        `<div data-fq-headline>` ("RMSE … · MAE … · R² … · worst …" with
        `data-fq-rmse` / `data-fq-mae` / `data-fq-r2` / `data-fq-worst` spans)
        and a `<table data-fq-samples>` of
        `<tr data-fq-sample-row data-sample-id>` rows (sample /
        `data-fq-sample-residual` / `data-fq-sample-abs`)
- **Route Map panel** (`data-neith-inverse-modeling-route-map`): renders 5
  entries from `STUDIO_NEITH_INVERSE_MODELING_ROUTE_MAP` — primary,
  `sessions/[sessionId]`, `presets/[presetId]`, `exports/[exportId]`,
  `audit/[auditId]`
- **Quick-action lane**: three `Link.quickAction` anchors — Isis Reference Image
  Processing, Isis Live Preview, Back to Studio workspace index

## States

- [x] **Reference ready (default known image)** —
      `analyzeNeithReferenceImage(DEFAULT_NEITH_REFERENCE_IMAGE)` runs at mount.
      The feature grid shows the known reference vector: mean `0.373`, contrast
      `0.118`, edgeEnergy `0.135`, entropy `0.583`, warmBias `0.004`.
      Domain-covered in `neith-inverse-modeling.test.ts` and E2E-covered in
      `studio-neith-inverse-modeling.spec.ts`.
- [x] **Reference estimate populated** — four `data-neith-model-card` cards
      render for `terrain`, `vegetation`, `urban`, and `noise`. The known
      reference estimates include terrain erosionStrength `0.565`, vegetation
      canopyDensity `0.502`, urban blockDensity `0.649`, and noiseKind `perlin`.
- [x] **Reference export manifest** — `data-neith-export-manifest` renders
      `manifestVersion: neith.inverse.v1`, `modelCount: 4`, checksum
      `fnv1a-82598ffa`, source dimensions, feature vector, four estimates, and
      Studio handoff targets. The manifest `<pre>` is focusable and labelled so
      its scrollable region has keyboard access.
- [x] **Reference error (malformed/invalid image payload)** — invalid JSON or
      validation failures set `<div data-neith-reference-error role="alert">`;
      the prior inverse report stays visible so an operator does not lose the
      last known-good feature vector.
- [x] **Loading** — server component for outer page; workspace mounts with
      `outcome === null` and renders `<p data-fq-loading>` "Loading fit-quality
      evaluator…" while auth hydrates and then while `fetchCatalog()` runs (GET
      `/v1/admin/studio/fit-quality`). Unit-covered in
      `StudioNeithInverseModelingWorkspace.test.tsx`
- [x] **Ready (catalog loaded)** — GET returns a `Catalog`
      (`{ metrics: string[] }`); `<p data-fq-enums>` shows "<n> metrics" and the
      `data-fq-form` payload form renders, seeded with `DEFAULT_PAYLOAD` (4
      sample rows)
- [x] **Gated (unauthorized)** — GET returns 401/403;
      `<div data-fq-unauthorized>` "Access restricted." renders with the server
      message, or the fallback "Studio admin scope required to evaluate fit
      quality." The form is NOT rendered (no `catalog`)
- [x] **Error (recoverable)** — GET non-OK (other than 401/403), malformed
      catalog body, or fetch rejection; `<div data-fq-error>` "Could not load
      the fit-quality evaluator." renders with the reason
      (`Fit-quality catalog request failed (<status>).`,
      `Fit-quality catalog response was malformed.`,
      `Could not reach the fit-quality evaluator.`, or `Network unavailable.`).
      The retry button (`data-fq-retry`) reissues the catalog request and
      returns to the ready form when the real evaluator catalog responds.
- [x] **Evaluate success** — POST `/v1/admin/studio/fit-quality/evaluate`
      returns 200 with a valid `result`; `<div data-fq-result>` renders the
      headline metrics and per-sample table
- [x] **Evaluate error (invalid JSON)** — `JSON.parse(payloadText)` throws;
      `setEvaluateError('Payload must be valid JSON.')`;
      `<div data-fq-evaluate-error>` renders; no network call is made
- [x] **Evaluate error (server)** — POST is non-200 or the body fails
      `isResult()`; error is the server `detail` string or
      `Evaluation failed (<status>).`; on fetch rejection,
      `Could not reach the fit-quality evaluator.`; `data-fq-result` is cleared
- [ ] **Network unavailable** — `typeof fetch === 'undefined'`: catalog load
      yields the error state; submit sets
      `evaluateError = 'Network unavailable.'`
- [ ] **Offline** — both catalog GET and evaluate POST require the BFF; offline,
      they surface as the error states above; PWA cache serves the shell
- [ ] **Standalone PWA** — workspace section and route-map panels stack; form
      `maxWidth: 900`, textarea full-width
- [x] **Empty** — N/A; the payload textarea is seeded with `DEFAULT_PAYLOAD`
- [x] **Populated (long)** — N/A; sample-count is driven by the user-supplied
      JSON, not a paginated list

## Interactions

### Reference Inversion Lane — image payload form

- [x] **Reference image JSON textarea**
      (`<textarea data-neith-reference-payload>`,
      `aria-label="Neith reference image json"`)
  - Function: controlled input bound to `referenceText`; accepts
    `{ width, height, sourceArtifactId, label, pixels }` where `pixels` is a
    row-major RGB/RGBA tuple array.
  - Keyboard: standard multiline editing in tab order inside
    `data-neith-reference-form`.
  - Touch target: full-width textarea; measured at 390 px in the E2E path.
  - Validation: payload must parse as JSON, include non-empty `sourceArtifactId`
    and `label`, positive integer `width` / `height`, and exactly
    `width * height` pixels with channel values in `0..=255`.
- [x] **"Run inverse modeling"** (`<button data-neith-reference-submit>`)
  - Function: submits `data-neith-reference-form`; calls
    `analyzeNeithReferenceImage()`, updates the feature vector, all model
    estimates, confidence values, and export manifest.
  - Failure: parser/validation errors render `data-neith-reference-error`
    without clearing the prior report.
  - Keyboard: Enter/Space activates; Enter inside the form submits.
  - Touch target: 44 px minimum height; measured at 390 px.
- [x] **Feature cells** (`data-neith-feature`)
  - Non-interactive; exact keys are `mean`, `contrast`, `edgeEnergy`, `entropy`,
    and `warmBias`.
- [x] **Model estimate cards** (`data-neith-model-card`)
  - Non-interactive; one card each for `terrain`, `vegetation`, `urban`, and
    `noise`; each card exposes confidence and parameter rows through
    `data-neith-model-confidence` and `data-neith-param-row`.
- [x] **Export manifest** (`data-neith-export-manifest`)
  - Non-interactive JSON manifest for downstream Studio handoff; focusable with
    an accessible label because it can scroll.

### Goodness-of-Fit Lane — payload form

- [x] **Samples JSON textarea** (`<textarea data-fq-payload>`,
      `aria-label="fit quality samples json"`)
  - Function: controlled input bound to `payloadText` via `onChange`; seeded
    with `DEFAULT_PAYLOAD` (4 `{ sampleId, observed, predicted }` rows);
    `rows={16}`, monospace
  - Keyboard: standard multiline text editing; in tab order inside
    `data-fq-form`
  - Screen reader: labelled "fit quality samples json" (also a visible `<span>`
    label "Samples (JSON: { samples[{ sampleId, observed, predicted }] })")
  - Touch target: full-width textarea with 240 px minimum height; measured at
    390 px.
  - Telemetry: not instrumented in current code
- [x] **"Evaluate fit quality"** (`<button data-fq-submit type="submit">`)
  - Function: submits `data-fq-form`; `submitEvaluate()` clears prior error +
    result, `JSON.parse`es `payloadText`, then POSTs the parsed object to
    `/v1/admin/studio/fit-quality/evaluate` with the hydrated AuthProvider
    access token (local-dev fallback via `buildBffAuthHeaders()`) +
    `content-type: application/json`, `cache: 'no-store'`
  - On 200 + valid `result`: sets `result` (renders `data-fq-result`)
  - On invalid JSON: short-circuits to `data-fq-evaluate-error` "Payload must be
    valid JSON." before any fetch
  - On non-200 / malformed / rejection: sets `evaluateError`
  - Keyboard: Enter/Space activates (also Enter within the form submits)
  - Touch target: 44 px minimum height; measured at 390 px.
  - Disabled when: while a POST is pending, native `disabled` plus
    `aria-disabled="true"` / `aria-busy="true"`; the label reads "Evaluating fit
    quality…".
  - Telemetry: not instrumented in current code
- [x] **"Retry evaluator"** (`<button data-fq-retry type="button">`)
  - Function: shown only in catalog-load error state; clears the error back to
    `data-fq-loading` and reissues `GET /v1/admin/studio/fit-quality`
  - Touch target: 44 px minimum height; measured in the recoverable-error E2E
    leg.
- [x] **Result table rows** (`<tr data-fq-sample-row data-sample-id=<id>>`)
  - Non-interactive; one row per `result.samples[]` entry with sample id,
    `data-fq-sample-residual`, and `data-fq-sample-abs` cells
- [x] **Result headline** (`<div data-fq-headline>`)
  - Non-interactive; shows `data-fq-rmse` / `data-fq-mae` / `data-fq-r2` /
    `data-fq-worst` from the `FitResult`

### Route Map panel

- [x] **Route entries** (5 × `<article>`)
  - `/studio/neith/inverse-modeling` — primary inverse-modeling workspace
  - `…/sessions/[sessionId]` — reference provenance + tuning deltas + snapshots
  - `…/presets/[presetId]` — reusable parameter packs
  - `…/exports/[exportId]` — generated manifests + Neith pipeline handoff
  - `…/audit/[auditId]` — estimator confidence + reproducibility evidence
  - Non-interactive listing

### Quick-action lane

- [x] **"Open Isis Reference Image Processing workspace"** (link →
      `/studio/isis/reference-image-processing`)
- [x] **"Open Isis Live Preview workspace"** (link →
      `/studio/isis/live-preview`)
- [x] **"Back to Studio workspace index"** (link → `/studio`)
  - Keyboard: included in tab order after route map; touch ≥ 44×44 px
    (`quickAction` class)
  - Mobile: anchors wrap (`flexWrap: 'wrap'`)

## Data & contracts

- **Local reference analysis**:
  `apps/oshun/web/src/lib/studio/neith-inverse-modeling.ts` exports
  `analyzeNeithReferenceImage()`, `parseReferenceImage()`,
  `extractReferenceFeatures()`, `estimateModelParameters()`, and
  `buildExportManifest()`.
  - Input: `{ sourceArtifactId, label, width, height, pixels }`; `pixels` is a
    row-major RGB/RGBA tuple array with channel values in `0..=255`.
  - Feature vector: `{ mean, contrast, edgeEnergy, entropy, warmBias }`.
  - Model estimates: four records keyed by `terrain`, `vegetation`, `urban`, and
    `noise`, each with confidence and model-specific parameters.
  - Manifest: `manifestVersion: neith.inverse.v1`, source metadata, feature
    vector, `modelCount`, estimates, handoff targets
    (`/studio/isis/reference-image-processing`, `/studio/bellona/interchange`,
    `/studio/hathor/world-configuration`), and deterministic FNV-1a checksum.
- **Reads**: `GET /v1/admin/studio/fit-quality` — returns `Catalog`
  (`{ metrics: string[] }`); sent with the hydrated AuthProvider access token,
  `cache: 'no-store'`. 401/403 → unauthorized state; other non-OK / malformed →
  error state
- **Writes**: `POST /v1/admin/studio/fit-quality/evaluate` — accepts the
  user-supplied JSON object (default shape
  `{ samples: [{ sampleId, observed, predicted }] }`); returns
  `{ result: FitResult }` on 200, else `{ detail }`. `FitResult` =
  `{ samples: { sampleId, residual, absError }[], worstSample, rmse, mae, r2, summary: { sampleCount, rmse, mae, r2 } }`
- **Realtime**: none
- **Caching**: static server render; both calls use `cache: 'no-store'`;
  metadata sets canonical URL
- **Auth/role check**: `/studio/*` requires signed-in (middleware); the
  fit-quality endpoints are admin-scoped (admin:\* | admin:studio) and
  fail-closed — a non-admin sees the `data-fq-unauthorized` state

## Cross-references

- Parent: [`../../studio-overview.md`](../../studio-overview.md)
- Shell:
  [`../../../shell/04-auth-session.md`](../../../shell/04-auth-session.md)
- Cross-domain partners (per quick-action lane):
  - `/studio/isis/reference-image-processing`
  - `/studio/isis/live-preview`
- Component sources:
  - `apps/oshun/web/src/components/studio/StudioNeithInverseModelingWorkspace.tsx`
    (client component: reference inversion lane + auth-ready catalog load +
    JSON-payload evaluate form wired to `/v1/admin/studio/fit-quality`)
  - `apps/oshun/web/src/lib/studio/neith-inverse-modeling.ts` (deterministic
    reference-image feature extraction, four-model parameter estimates,
    confidence scoring, export manifest checksum)
  - `apps/oshun/web/src/components/studio/StudioNeithInverseModelingRouteMap.ts`
- E2E: `apps/oshun/web/e2e/studio-neith-inverse-modeling.spec.ts` covers direct
  admin navigation, reference feature extraction, four model estimates, manifest
  JSON, live catalog/evaluate success, non-admin fail-closed, recoverable
  catalog retry, 390 px form/quick-action touch targets, pending submit
  disabled/busy semantics, invalid reference payload, invalid fit-quality JSON
  with no POST, BFF invalid-payload detail, route-map, and Isis quick links
- Domain tests:
  - `apps/oshun/web/src/lib/studio/neith-inverse-modeling.test.ts` proves the
    known reference image maps to exact feature values, model estimates, and
    manifest checksum.
  - `pnpm nx run @neith/procgen:test` passed for the underlying Neith procgen
    crate family named by V1 §34.
- Feature spec: `V1/TODOS.md` §34 and `V1/architecture/cross-domain-support.md`
  define Bellona/Hathor/Neith as Studio-only V1 substrates (confirmed in-v1
  2026-05-29); the route matrix marks `/studio/neith/inverse-modeling` as
  `in-v1`.

## Open questions / known gaps

- [ ] The header's stated auth (signed-in + studio entitlement) is broader than
      the lane's actual requirement (admin:\* | admin:studio). Confirm whether
      the whole route should be admin-gated at middleware, or only the
      fit-quality lane (current behavior: non-admins reach the page but see
      `data-fq-unauthorized`).
- [ ] The route map lists `sessions/[sessionId]`, `presets/[presetId]`,
      `exports/[exportId]`, and `audit/[auditId]` sub-routes that are absent
      from the repo — confirm whether these are V1, V2, or out of scope.
- [ ] No telemetry events fire for catalog load, payload edits, or evaluate
      submit; instrument when wiring to BFF analytics. The reference inversion
      lane also has no telemetry on payload edits or manifest generation.
- [ ] Quick-action lane crosses into Isis
      (`/studio/isis/reference-image-processing`, `/studio/isis/live-preview`);
      verify Isis is the canonical owner of image processing while Neith owns
      inverse-modeling.
