1. I joined
  2. Frontend lead on BioNav
  3. FoxHog and Agenecy
  4. Frontend Strategy released!
  5. FoxCub
  6. TargetNexus
  7. Frontend Tech Solutions
  8. Netrya for Frontends (Monet)
Context

From five apps to one plugin

I've been the Frontend lead on

BioNav
FoxHog
Agenecy
FoxCub
TargetNexusnow
#
#help-frontendThe current, manual way to get Frontend help is Slack.

The same patterns came up on every one of them, so I'm turning the repeatable ones into a plugin:

App scaffoldInfra, auth, CI and deployment
Testing and quality checksAccessibility, visual regression, design system, dead code
Prototyping supportPer-PR previews to try ideas with stakeholders and iterate before shipping
Add a Frontend Lead to your project: Autolab, Janus, Tycho, Monet, CIDM*, ServiceNow*, Workday*
* Pending future RDT collaboration
A RoPilot, on your project too.
Oprah Winfrey, arms outstretched, in the you-get-a-car meme
You get a Frontend,everybody gets a Frontend!!
CS CoE Frontend ยท Monet

Netrya

Netrya: Sanskrit, roughly “medicine for your eyes”

A Claude Code plugin that acts as the Frontend lead on your project. It encodes the CS CoE stack, the @cs-coe registry rules, the Genentech theme and the quality bar, so an agent builds it right the first time.

12skills
8commands
3subagents
1design-system hook
19eval cases
INSTALL: COMING SOON
/plugin install netrya@cscoe-frontend

1What you can ask it

CommandWhat it does
/netrya:kickoffIntake questions and a stack decision before anything is scaffolded
/netrya:modernizePhased migration onto the stack: tests first, existing look and feel preserved by contract
/netrya:pairPrototype in code with stakeholders instead of Figma, then iterate on feedback
/netrya:reviewCode review, plus a design review at 1440, 768 and 375 if anything visual changed
/netrya:doctorThe full quality gate, with failures triaged
/netrya:shipGate, then draft the PR with screenshots and test evidence
/netrya:standupWhere the project stands against the bar, and the three things worth doing next
/netrya:driftWhat changed upstream since the last check, and a draft PR for the safe parts

2It catches problems as Claude writes them

A typical first draft of a plate-QC dashboard: MUI, Recharts, a native select, a hard-coded brand colour. The moment Claude saves the file, Netrya's hook reviews it and hands the findings straight back to Claude, which fixes them in the same turn. It never blocks the edit.

src/PlateQCDashboard.tsx, as first written

import { useEffect, useState } from "react";
import { Button } from "@mui/material";
import { LineChart } from "recharts";

export function PlateQCDashboard({ plates }: { plates: Plate[] }) {
  const [passRate, setPassRate] = useState(0);
  useEffect(() => {
    setPassRate(plates.filter((p) => p.ok).length / plates.length);
  }, [plates]);

  return (
    <div className="bg-blue-500 font-medium" style={{ color: "#005596" }}>
      <select>{plates.map((p) => <option key={p.id}>{p.id}</option>)}</select>
      <LineChart data={plates} />
      <Button onClick={() => window.confirm("Reject plate?")}>Reject</Button>
      Pass rate {passRate}
    </div>
  );
}

What the hook tells Claude, immediately

Design system check โ€” app/src/PlateQCDashboard.tsx
  โ€ข Material UI import โ€” use @cs-coe registry components (npx shadcn@latest add @cs-coe/<name>)
  โ€ข Chart library import โ€” use @tanstack/charts
  โ€ข <select> โ€” use <Select> from @cs-coe/select
  โ€ข window.confirm() โ€” use <AlertDialog> from @cs-coe/alert-dialog
  โ€ข Hardcoded #005596 โ€” use bg-primary / text-primary
  โ€ข Tailwind color-scale class โ€” use semantic tokens (bg-primary, text-muted-foreground)
  โ€ข font-medium โ€” Gene Sans has no weight 500. Use font-normal (400), font-semibold (600), or font-bold (700)
  Run 'npm run check:design-system' for the full, authoritative pass.

3The same gate before every PR

/netrya:doctor and /netrya:ship run the full quality gate. Two of its checks are shown here: the design-system check, and the monorepo's mandatory useEffect rule, which Netrya now wires into every new app.

npm run check:design-system

Design system check โ€” 1 files in: src

โœ— BANNED_IMPORT  src/PlateQCDashboard.tsx:2
  Material UI import
  โ†’ Use @cs-coe registry components: npx shadcn@latest add @cs-coe/<name>
โœ— BANNED_IMPORT  src/PlateQCDashboard.tsx:3
  Banned chart library import
  โ†’ Use @tanstack/charts
โœ— RAW_HTML  src/PlateQCDashboard.tsx:13
  Native <select> element
  โ†’ Use <Select> from @cs-coe/select
โœ— RAW_HTML  src/PlateQCDashboard.tsx:15
  window.confirm()
  โ†’ Use <AlertDialog> from @cs-coe/alert-dialog
โš  HARDCODED_COLOR  src/PlateQCDashboard.tsx:12
  Hardcoded Genentech Blue
  โ†’ Use bg-primary / text-primary
โš  TAILWIND_COLOR  src/PlateQCDashboard.tsx:12
  Tailwind color-scale class
  โ†’ Use semantic tokens (bg-primary, text-muted-foreground)
โš  FONT_WEIGHT  src/PlateQCDashboard.tsx:12
  font-medium โ€” Gene Sans has no weight 500
  โ†’ Use font-normal (400), font-semibold (600, house style), or font-bold (700)

Violations: 4  Warnings: 3

Install registry components with:
  npx shadcn@latest add @cs-coe/<component>

See AGENTS.md for the full component mapping and token list.
Note: .astro files are not scanned โ€” review those templates by eye.

npm run check:useeffect

โ”โ”โ” useEffect Guard โ”โ”โ”

Scanning for unjustified useEffect calls...

  โœ— src/PlateQCDashboard.tsx:7
      useEffect(() => {
    โ†’ Add: // useEffect: <describe the external system being synchronized>

โ”โ”โ” useEffect Guard Summary โ”โ”โ”

โ—‹        1 file(s) with useEffect in exempt paths (ui/, gyde/, tests)
โœ— 1 unjustified useEffect call(s)

Every useEffect must have a comment on the line above:

  // useEffect: sync chart resize observer with container dimensions
  useEffect(() => { ... }, [width, height]);

Common replacements (https://react.dev/learn/you-might-not-need-an-effect):
  โ€ข Derived state        โ†’ compute during render
  โ€ข Data fetching        โ†’ TanStack Query, loader, or server component
  โ€ข User-caused work     โ†’ event handler
  โ€ข Reset on prop change โ†’ key the component
  โ€ข External store       โ†’ useSyncExternalStore
Violations fail CI, warnings inform. Every finding says what to use instead, and the fix is always a registry component or a theme token.

4Staying current with the design system

A month after adopting v0.3.0, upstream ships v0.4.0. /netrya:drift compares every installed component against the registry and a saved baseline, so it knows which side moved.

Drift check against v0.4.0

Registry items: 4   Local ui files: 4

! upstream-update (1)
    safe to reinstall: npx shadcn@latest add @cs-coe/<name>
      button                   src/components/ui/button.tsx

! conflict (1)
    both sides changed โ€” merge by hand, then --write-baseline --resolve <name>
      card                     src/components/ui/card.tsx

  local-customization (1)
    allowed โ€” confirm it is still intentional
      badge                    src/components/ui/badge.tsx

  in-sync (1)
      dialog                   src/components/ui/dialog.tsx

Actionable: 2

After merging card by hand: --resolve card

drift-scan: card resolved at its current hashes

Registry items: 4   Local ui files: 4

! upstream-update (1)
    safe to reinstall: npx shadcn@latest add @cs-coe/<name>
      button                   src/components/ui/button.tsx

  local-customization (2)
    allowed โ€” confirm it is still intentional
      badge                    src/components/ui/badge.tsx
      card                     src/components/ui/card.tsx

  in-sync (1)
      dialog                   src/components/ui/dialog.tsx

Actionable: 1
button is safe: only upstream changed, so reinstall it. card changed on both sides, so a person merges it and then records the merge. badge is our deliberate customization, and it stays. Nothing is silently overwritten, and nothing silently drops off the list.

5DevOps and DevEx: from first commit to production

Netrya doesn't stop at the code. It knows the environments, pipelines and deploys the CS CoE runs, and /netrya:ship wires the same CI and deployment into a new app, so it has the monorepo's path to production from day one.

Ephemeral dev environments in the monorepo

  • One click opens a fresh Ona workspace from .devcontainer, on Roche's Ona base image
  • Node 22, the GitHub CLI and the pinned Playwright Chromium builds are baked in; ffmpeg and Xvfb are there for screenshots and demo GIFs
  • Automations install all three apps, verify the browsers, and install @cs-coe components into prototypes from a registry served locally
  • Dev servers start with health checks on :4320 components, :4321 showcase and :4322 prototypes, and are thrown away when you're done

CI that is a real gate in new apps

  • An 8-job pipeline template: lockfile sync, design-system and useEffect checks, lint, typecheck, unit tests with coverage, accessibility, visual regression, build
  • Every action is pinned to a commit SHA, and every workflow has least-privilege permissions
  • react-doctor as advisory, knip for dead code, CODEOWNERS and a PR template with screenshot and test evidence
  • /netrya:ship runs the gate, then drafts the PR

Continuous deployment live

  • main goes to production on every merge; tags mark the versions teams upgrade to
  • Cloudflare Pages at cscoe-frontend.ghpages.roche.com: every Roche employee via SSO, no GitHub licence needed
  • Deploys use GitHub OIDC exchanged for a Vault token, so no cloud secret lives in the repo
  • GitHub Pages mirror; each app gets its own ASTRO_BASE subpath, with the registry JSON served alongside

Protected review environments ready to enable

  • Model evals run on Amazon Bedrock through OIDC: no API keys anywhere
  • netrya-evals is locked to main for the unattended weekly run
  • netrya-evals-review requires a second reviewer before any branch run gets credentials
  • Built and in review; switches on once the Bedrock role is created
  • Pull requests get an offline validation job and never touch paid infrastructure

Per-PR preview environments, for review and for prototyping proven on TargetNexus part of Netrya

Every pull request gets its own live, short-lived environment with a shareable link, torn down on merge. It's the same mechanism whether the change is production code or a prototype.

Prototype in code, not in decksTry a real, clickable idea on a real link the same day, instead of a mockup that has to be rebuilt later
Review the thing itselfReviewers click through the change, not screenshots of it
Global collaborationA link works across time zones, so teams in different regions review asynchronously without a hand-off meeting
Non-technical stakeholders includedScientists, PMs and leadership give feedback from a browser, with no clone, no setup and no GitHub needed
How TargetNexus uses it: each iteration ships to its own preview. Stakeholders around the world react to the live version, the team adjusts, and the next preview follows. The loop runs many times before anything reaches production, so what ships has already been used and agreed on.
Example A drive-by UX suggestion from a new engineer

A new engineer on TargetNexus spotted rough edges in the Reports table. Instead of filing a ticket or drawing a mockup, they used the AI coding setup to prototype table changes in a preview environment the team could try for themselves.

The TargetNexus Reports table, current version, annotated with UX suggestions
Their notes on the current version: the starting point for the prototype
Give the target more visual weightDropdown filtersDifferent colours for Completed and RunningDurations as 02:20:13Clearer action iconsResizable columns for truncated textFull-word therapeutic-area pillsNo dash in empty cells

A drive-by idea became something working to react to, without touching production.

6TargetNexus as a benchmark

Our team works in the openโ€ฆ in private Slack channels. So the iteration history lived in threads, not in any tracker. I built a skill to read it back and measure how often each feature was reworked: the UX/Product Rework-Rate Baseline Report.

8โ€“14estimated rework cycles per feature (high-bound)
0 of 6feature areas shipped right first time
55iteration cycles counted
Marโ€“Sep 20267 months, 10+ contributors
TargetNexus UX/Product Rework-Rate Baseline report: 8 to 14 average rework cycles per feature across 6 feature areas, none shipped right first time, March to September 2026, with iteration cycles per feature area
The report the skill generated. Report Layout & Overview Structure led with 15 cycles, then Claims Review UI (12) and Feedback Capture (11).
Why it matters: this is the baseline. Every rework cycle was a round of feedback, and per-PR previews are how those rounds happened fast and in the open with stakeholders. Now there's a number to compare against as the patterns move into Netrya.

7Netrya on a real app: DDC Forge

DDC Forge is built on the Frontend Tech Solutions stack and was AI-coded from the app starter prompts. I ran Netrya's quality gate, useEffect guard and drift check against its frontend (roche-innersource/cscoe-ddc-forge, commit c4f1534), read-only, with no install and none of its own code executed.

Right from the startTech stack and themeThe starter prompts get the foundations right on day one.
โ†’
Grows in organicallyArchitecture and CI patternsGaps build up as features land and the app grows.
โ†’
The opportunityNetrya and ClaudeContinuous checks catch the drift; Claude fixes it at scale.
DDC Forge compound search: identity, chemistry and provenance filters above a results table with structure drawings
DDC Forge (UAT): compound search, built on the Frontend Tech Solutions stack
575source files scanned
10.8 minclone to report, including ~7 min lost to a slow script (below)
~21k ยท $1.82new tokens, and usage cost for the run
34 sdesign-system scan
3 suseEffect scan
AreaResultWhat Netrya found
Right from the start: the starter prompts at work
StackStrongReact 19, TypeScript, Tailwind v4 CSS-first, Base UI, TanStack Query and Table, lucide, and the @cs-coe registry already configured. Vite and React Router instead of an Astro shell, which is reasonable for a single-page app.
ThemeStrongAligned: --primary is Genentech Blue, on a CSS-first token system. Two refinements: the font is Inter rather than Gene Sans (which is why font-medium appears in 60 files: fine under Inter, invalid under Gene Sans), and status colours are hard-coded Tailwind scales in 44 files (text-green-700 ร—20, bg-red-50 ร—15, โ€ฆ) where --success, --warning and --info tokens now exist. 8 files override colours with dark:.
TestsStrong173 unit test files, 11 Playwright specs, an axe accessibility spec and visual regression. Gap: the e2e, accessibility and visual suites run nightly only, so a PR can merge a regression they would catch.
Grown in organically: architecture and CI patterns
EffectsGap97 effects without a // useEffect: justification, and 13 suppressed exhaustive-deps warnings. The monorepo makes this mandatory; nothing in forge checks it.
ComponentsPartial23 of the 61 registry components are installed, all edited locally. The biggest difference is Prettier style: forge drops semicolons, so every file differs from upstream. Eight components are hand-built, and the registry now covers three of them: multi-select-filter โ†’ the chip multi-select combobox (#515), pagination-bar โ†’ pagination, date-range-picker โ†’ calendar. Also 1 native <select> and 1 direct Base UI import.
CI gatesPartialPR CI runs lint, typecheck and unit tests with coverage. It doesn't run the design-system check, the useEffect check, a format check, knip or react-doctor.
The pattern: AI-coding from good starter prompts gets the stack and theme right. What slips is what emerges over time: effects added one feature at a time, components hand-built before the registry had them, gates never added to CI. Nobody decides to let those drift. That drift is what Netrya's continuous checks and drift scans catch, and what Claude can fix at scale.

The three things worth doing next

1. Add check:useeffect and check:design-system to PR CI, and run the accessibility suite on PRs, not nightly.

2. Move status colours onto the success, warning and info tokens, and decide on Gene Sans vs Inter.

3. Swap the hand-built multi-select, pagination and date-range picker for the registry versions, then re-run the drift check with a baseline.

What running it taught us about Netrya

Dogfooding on a real codebase found four fixes to make in the plugin itself:

โ€ข The batch design-system script ran over 7 minutes on 349 files without finishing; the hook's rules covered 575 files in 34 s.
โ€ข The drift check counted .test and .stories files as components.
โ€ข --ignore-whitespace doesn't absorb Prettier style such as semicolons, so a formatted fork reads as all-drift.
โ€ข The font-medium rule assumes Gene Sans; it should follow the app's actual font.

8Netrya on a real app: Autolab

A different starting point. Autolab's web frontend (roche-innersource/cscoe-openlab, src/autolab/web, commit a72af34) is hand-built and doesn't use the Frontend Tech Solutions stack. Where DDC Forge needs a drift check, Autolab needs /netrya:modernize. Same read-only run: no install, and none of its code executed.

Autolab (OpenLab) automation page: agent conversations beside a spec panel that builds as you talk
Autolab (OpenLab): the automation agent, where the spec builds as you talk
133source files scanned (29k lines)
3.6 minclone to findings
~9k ยท $0.97new tokens, and usage cost for the analysis
11 sdesign-system scan
2 suseEffect scan
AreaResultWhat Netrya found
Already in good shape
FootprintStrongOnly six runtime dependencies. TypeScript throughout, Janus auth (@janus/core) wired in, and dedicated deploy workflows for the web app, Ona and promotion between environments.
ThemeOwn systemA well-engineered token layer: CSS custom properties, light and dark via data-theme, Tailwind colours mapped to tokens. But it's Autolab's own warm monochrome palette in Inter, not the Genentech theme.
Where modernization pays off
StackBehindReact 18, Tailwind 3 with a JavaScript config, Vite 5 and React Router 6. The stack is React 19, Tailwind v4 CSS-first, Base UI and TanStack.
ComponentsGapNo @cs-coe registry. Its own primitives (Btn, Card, Field, โ€ฆ) are used in only 2 files; the rest is inline markup: 9 raw <table>s, hand-built dialogs in 4 files, 17 inline SVG icons, 2 window.confirm() and a native <select>, each with a registry equivalent.
EffectsGap94 unjustified useEffect calls in 133 files, plus 5 suppressed exhaustive-deps warnings: several times Forge's density.
TestsPartial10 unit test files, all for logic (utils, hooks, parsers). No component, end-to-end, accessibility or visual regression tests.
CI gatesPartialCI installs, runs the unit tests and builds (which type-checks). No ESLint (lint is just tsc), no Prettier, and no design-system, useEffect or accessibility check.

The plan /netrya:modernize would produce

1. Test hardening first: Playwright journeys that query by role and name, and visual baselines that lock in today's look.
2. React 18 โ†’ 19, then a Tailwind v4 token layer carrying Autolab's current palette.
3. Registry components screen by screen: dialogs, tables, tooltips, select, alert dialog.
4. Justify or replace the 94 effects, and add the gates to CI.
5. Optional, last: adopt the Genentech theme, as a deliberate and reviewed visual change.

Two apps, two ways in

DDC Forge started on the stack via starter prompts, so Netrya's job is keeping it there: drift checks, gates, and effect discipline.

Autolab predates the stack, so Netrya's job is getting it there without breaking it: tests first, look and feel preserved by contract, one phase at a time.

Both scans ran in seconds; each analysis cost one to two dollars.
A roadside neon sign shaped like the Claude pixel mascot, reading CLAUDE, with a marquee below saying WE HAVE THE TOKENS

9Built to the bar it enforces

39 / 39Plugin script tests: drift scanner, CLI, and the hook under macOS /bin/bash
25 / 25CI script tests: eval summary, retry logic, offline case validator
14 / 14Repository CI checks green on merge, including CodeQL
14 rowsPlaces this repo's docs contradict its code, which Netrya knows and won't repeat. The drift check re-verifies them monthly.

What's next

So far this is built from new apps. The next step is the harder half.

Apply it to existing applications

  • Point Netrya at apps already in production and bring them onto the stack
  • The modernization skill already exists: tests first, look and feel preserved by contract, one phase at a time

A modernization factory, with Lech

  • Likely collaboration with Lech on a modernization factory
  • Exploring how Netrya's modernization skill can feed repeatable migrations at scale
Also coming: marketplace install, per-PR preview environments in the plugin (proven on TargetNexus), and the first live eval run on Bedrock.