# AGENTS.md — AI Coding Agent Instructions

<!-- BEGIN: canonical-behavior-pointer (auto-managed) -->

> **Read first (ecosystem-wide agent behavior):**
> [`AI.dev/SKILLS/agent-behavior-guidelines.md`](AI.dev/SKILLS/agent-behavior-guidelines.md)
>
> The link above is the single source of truth for agent behavior across every
> ByteLyst repo (Karpathy + ByteLyst rules: tests sacred, verify before done,
> no shared-infra hand-edits, no `console.log`/`print`, productId on every
> Cosmos doc, conventional commits, style preservation).
>
> The per-repo content below extends — never duplicates — the canonical rules.

<!-- END: canonical-behavior-pointer -->

> **For:** Claude Code, OpenAI Codex, Cursor, GitHub Copilot, Windsurf Cascade, and any AI coding agent.
> **Repo:** `learning_ai_common_plat` — Shared platform packages + microservices for the ByteLyst ecosystem.
> **See also:** [`README.md`](README.md) for quick start, [`docs/ECOSYSTEM_ARCHITECTURE.md`](docs/ECOSYSTEM_ARCHITECTURE.md) for full architecture, [`docs/ecosystem/README.md`](docs/ecosystem/README.md) for canonical ecosystem strategy/contract docs, [`docs/ecosystem/ECOSYSTEM_AGENT_OPERATING_MODEL.md`](docs/ecosystem/ECOSYSTEM_AGENT_OPERATING_MODEL.md) for delegation/review/merge process.

---

## 1. Project Identity

| Key                   | Value                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Root package**      | `@bytelyst/root`                                                                                                                                                                                                                                                                                                                                                                                                                             |
| **Library scope**     | `@bytelyst/*` (packages/)                                                                                                                                                                                                                                                                                                                                                                                                                    |
| **Service scope**     | `@lysnrai/*` (services/)                                                                                                                                                                                                                                                                                                                                                                                                                     |
| **Product consumers** | [LysnrAI](../learning_voice_ai_agent), [MindLyst](../learning_multimodal_memory_agents), [ChronoMind](../learning_ai_clock), [JarvisJr](../learning_ai_jarvis_jr), [NomGap](../learning_ai_fastgap), [PeakPulse](../learning_ai_peakpulse), [FlowMonk](../learning_ai_flowmonk), [NoteLett](../learning_ai_notes), [ActionTrail](../learning_ai_trails), [LocalMemGPT](../learning_ai_local_memory_gpt), [EffoRise](../learning_ai_efforise) |
| **Product-agnostic**  | Yes — every Cosmos doc includes `productId`                                                                                                                                                                                                                                                                                                                                                                                                  |
| **Runtime**           | Node.js (ESM), TypeScript 5.7+                                                                                                                                                                                                                                                                                                                                                                                                               |
| **Package manager**   | pnpm (workspace)                                                                                                                                                                                                                                                                                                                                                                                                                             |
| **Test runner**       | Vitest                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| **Prototype stack**   | Docker Compose with Cosmos emulator, Azurite, Mailpit, Traefik, Loki, and Grafana                                                                                                                                                                                                                                                                                                                                                            |

## 2. Monorepo Layout

```
learning_ai_common_plat/
├── packages/                       # @bytelyst/* shared libraries
│   ├── errors/                     # Typed HTTP service errors (400–429)
│   ├── cosmos/                     # Azure Cosmos DB client singleton + container registry
│   ├── config/                     # Zod-based env loader + product identity + AKV resolver
│   ├── auth/                       # JWT utilities, auth middleware, password hashing
│   ├── api-client/                 # Configurable fetch wrapper with auth token injection
│   ├── fastify-core/               # createServiceApp() factory + startService() helper
│   ├── react-auth/                 # React auth context factory (typed provider + hook)
│   ├── logger/                     # Structured logging wrapper (pino-based)
│   ├── testing/                    # Shared test mocks, fixtures, Fastify inject helpers
│   ├── blob/                       # Azure Blob Storage client + SAS token helpers
│   ├── extraction/                 # createExtractionClient(), shared types for extraction consumers
│   ├── monitoring/                 # Health-check utilities, Loki/Grafana helpers
│   ├── llm-router/                 # Deterministic LLM router: provider/model selection, fallback, health
│   └── design-tokens/              # Cross-platform tokens (JSON → CSS/TS/Kotlin/Swift)
│       ├── tokens/bytelyst.tokens.json   # ← CANONICAL SOURCE
│       ├── scripts/generate.ts           # Token generator
│       └── generated/                    # Output: tokens.css, tokens.ts, MindLystTokens.kt, MindLystTheme.swift
├── dashboards/                     # Product-agnostic web dashboards (Next.js)
│   ├── admin-web/                  # Platform admin console (port 3001)
│   └── tracker-web/                # Issue tracker + public roadmap (port 3003)
├── services/                       # @lysnrai/* product-agnostic microservices
│   ├── platform-service/           # Product-agnostic platform: auth, audit, flags, notifications, blob,
│   │                               #   invitations, referrals, promos, subscriptions, usage, plans,
│   │                               #   licenses, stripe, items, comments, votes, public (port 4003)
│   │                               #   NOTE: Product-specific modules migrated to product repos' backend/
│   ├── extraction-service/         # LangExtract text extraction + Python sidecar (port 4005)
│   └── monitoring/                 # Loki + Grafana config, health-check script
├── scripts/                        # DevOps scripts (pnpm workspace member)
│   ├── encrypt-migrate.ts          # Batch-encrypt plaintext Cosmos docs (10 products, 40+ fields)
│   └── package.json                # @bytelyst/scripts — deps: @azure/cosmos, @bytelyst/field-encrypt
├── docs/                           # Architecture docs, roadmap, analysis
├── package.json                    # Root scripts: build, test, typecheck, clean, prototype:self-test
├── pnpm-workspace.yaml             # Workspace: packages/* + services/* + dashboards/* + scripts
├── tsconfig.base.json              # Shared TS config (ES2022, NodeNext, strict)
├── vitest.config.ts                # Root vitest config (passWithNoTests)
├── docker-compose.yml              # Prototype stack: services + Cosmos emulator + Azurite + Mailpit + monitoring
├── .env.example                    # Required env vars template
└── .editorconfig                   # Editor settings
```

## 2A. Prototype Runtime Conventions

- The current single-host prototype is defined by [`docker-compose.yml`](docker-compose.yml).
- Prototype state currently lives in:
  - Cosmos DB Emulator
  - Azurite blob storage
  - Mailpit SMTP sandbox
- Reuse the existing prototype diagnostics instead of adding parallel health endpoints:
  - `GET /health`
  - `GET /api/health/dependencies`
  - `GET /api/self-test`
  - `GET /api/self-test.json`
- The canonical host-side smoke test command is `pnpm prototype:self-test`.
- The underlying implementation is [`scripts/prototype-self-test.sh`](scripts/prototype-self-test.sh). Extend it instead of creating duplicate one-off prototype scripts.
- If you change prototype infra, also update:
  - [`README.md`](README.md)
  - [`docs/PROTOTYPE_DEPLOYMENT.md`](docs/PROTOTYPE_DEPLOYMENT.md)
  - [`docker-compose.yml`](docker-compose.yml)
  - [`.env.example`](.env.example) when tracked defaults change

### Current local prototype endpoints

- `platform-service`: `http://localhost:4003`
- `extraction-service`: `http://localhost:4005`
- `mcp-server`: `http://localhost:4007`
- Cosmos Data Explorer: `http://localhost:1234`
- Azurite blob endpoint: `http://localhost:10000`
- Mailpit SMTP: `localhost:1025`
- Mailpit inbox UI: `http://localhost:8025`

## 3. Tech Stack Rules

### TypeScript (all packages + services)

- **Module system:** ESM (`"type": "module"` everywhere)
- **TS target:** ES2022, `moduleResolution: NodeNext`
- **Base config:** All packages/services extend `../../tsconfig.base.json`
- **Builds:** `tsc` — outputs to `dist/`
- **Tests:** Vitest with `passWithNoTests: true` at root

### Service framework (Fastify)

- **Framework:** Fastify 5 with Zod validation
- **Module pattern:** `types.ts` → `repository.ts` → `routes.ts` per module
- **Auth:** JWT via `jose` — platform-service issues, all others validate
- **Database:** Azure Cosmos DB via `@azure/cosmos`
- **Logging:** Fastify built-in `req.log` / `app.log` (never `console.log`)
- **Health:** Every service exposes `GET /health` → `{ status: "ok", service, requestId }`
- **Request tracing:** `x-request-id` header propagated across all services
- **Dev mode:** `tsx watch src/server.ts`

### Package conventions

- All packages export from `src/index.ts` → `dist/index.js`
- Use `exports` field in `package.json` (not just `main`)
- Peer dependencies for heavy/shared deps (`@azure/cosmos`, `jose`, `bcryptjs`, `react`, `zod`)
- Workspace deps: `"@bytelyst/errors": "workspace:*"`
- If a dashboard or app consumes a local package outside the pnpm workspace, build the package first and import from `dist/`, not raw `src/`

## 4. Coding Conventions

### MUST follow

- Every Cosmos document MUST include a `productId` field
- Every REST endpoint MUST validate input with Zod schemas
- Every service MUST propagate `x-request-id` headers
- Prototype infra changes MUST preserve `pnpm prototype:self-test`
- Prototype dependency checks belong in the shared status/self-test surfaces, not ad hoc endpoints
- Use `PRODUCT_ID` from `@bytelyst/config` (`loadProductIdentity()`) — never hardcode
- Services use self-contained Zod config schemas in `src/lib/config.ts` (avoids zod version mismatch with shared packages)
- Services re-export from `@bytelyst/*` in their `src/lib/` files (`errors.ts`, `cosmos.ts`, `product-config.ts`)
- For LLM routing, prefer `@bytelyst/llm-router` as the source of truth; do not introduce parallel routing logic unless explicitly required
- Commit messages: `type(scope): description` — types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`

### .npmrc — NEVER edit directly in product repos

**All `.npmrc` files are managed by a canonical template.** Never create or edit `.npmrc` in any product repo — use the sync tool instead.

- **Template:** `scripts/npmrc.template` in this repo (learning_ai_common_plat)
- **Sync:** `bash scripts/sync-npmrc.sh` — copies template to all 13 product repos
- **Audit:** `bash scripts/check-npmrc-drift.sh` — detects drift (CI-ready, exits 1 on mismatch)
- **Key config:** `@bytelyst:registry` uses `${GITEA_NPM_HOST:-localhost}` — never hardcode `gitea.bytelyst.com` (unreachable on corp network)
- **If adding a new repo:** add it to the `REPOS` array in both `sync-npmrc.sh` and `check-npmrc-drift.sh`, then run sync

### MUST NOT do

- Never use `console.log` in production code — use `req.log` or `app.log` in Fastify
- Never use `any` type — use Zod inference or explicit types
- Never hardcode secrets or API keys
- Never hardcode `gitea.bytelyst.com` in `.npmrc` — use `${GITEA_NPM_HOST:-localhost}` via the canonical template
- Secret guardrails: Husky runs `scripts/secret-scan-staged.sh` (pre-commit) and `scripts/secret-scan-repo.sh` (pre-push). See `docs/WINDSURF/CODEX_SESSION_SUMMARY_AND_PLAYBOOK.md`.
- Never commit real emulator keys or blob account keys in tracked files; keep placeholders in `.env.example`
- Never modify tests to make them pass — fix the actual code
- Never delete existing comments or documentation unless explicitly asked
- Never add emojis to code unless explicitly asked

## 5. Design System (Critical)

### NEVER hardcode colors

**All colors MUST come from `@bytelyst/design-tokens`.** Hardcoded hex values create inconsistency and maintenance debt.

| Platform           | ❌ DON'T                     | ✅ DO                                       |
| ------------------ | ---------------------------- | ------------------------------------------- |
| **Web**            | `color: '#5A8CFF'`           | `color: var(--ml-accent-primary)`           |
| **Web (Tailwind)** | `bg-[#5A8CFF]`               | `bg-[var(--ml-accent-primary)]`             |
| **iOS**            | `Color(hex: 0x5A8CFF)`       | `MindLystColors.darkAccentPrimary`          |
| **Android/KMP**    | `Color(0xFF5A8CFF)`          | `Color(MindLystTokens.Dark.ACCENT_PRIMARY)` |
| **React Native**   | `backgroundColor: '#5A8CFF'` | `tokens.colors.accentPrimary`               |

### Token file locations

```
packages/design-tokens/
├── tokens/bytelyst.tokens.json          # ← CANONICAL SOURCE (edit this)
├── generated/
│   ├── tokens.css                       # Web: CSS custom properties
│   ├── tokens.ts                        # TypeScript constants
│   ├── MindLystTokens.kt                # Kotlin/KMP/Android
│   ├── MindLystTheme.swift              # Swift/iOS
│   └── react-native/tokens.ts           # React Native
└── scripts/
    ├── generate.ts                      # Main generator
    ├── generate-react-native.ts         # RN generator
    ├── validate-tokens.cjs              # Validation script
    └── token-coverage.cjs               # Coverage report
```

### How to update tokens

1. **Edit** `packages/design-tokens/tokens/bytelyst.tokens.json`
2. **Run** `pnpm --filter @bytelyst/design-tokens generate`
3. **Copy** generated files to consumer repos (see per-repo AGENTS.md)
4. **Commit** both the JSON source AND generated files

### Product-specific token sections

Each product has a dedicated section in `bytelyst.tokens.json`:

| Product    | Token Section      | Example                              |
| ---------- | ------------------ | ------------------------------------ |
| MindLyst   | `color.brain`      | `BRAIN_WORK`, `BRAIN_HOME`           |
| JarvisJr   | `color.jarvisjr`   | `AGENT_COACH`, `AGENT_LINGUA`        |
| PeakPulse  | `color.peakpulse`  | `ACTIVITY_HIKE`, `SPEED_ZONE_FAST`   |
| ChronoMind | `color.chronomind` | `URGENCY_CRITICAL`, `FOCUS_MODE`     |
| NomGap     | `color.nomgap`     | `STAGE_KETOSIS`, `AUTOPHAGY_METER`   |
| LysnrAI    | `color.lysnrai`    | `RECORDING_ACTIVE`, `DICTATION_MODE` |

### Validation

Check for hardcoded colors before committing:

```bash
# In any product repo
node ../learning_ai_common_plat/packages/design-tokens/scripts/validate-tokens.cjs src/

# Get coverage report
node ../learning_ai_common_plat/packages/design-tokens/scripts/token-coverage.cjs src/
```

**CI Integration:** All PRs should run the validation script and fail if new hardcoded colors are introduced.

### Current adoption status (2026-03-03)

| Product          | Token Adoption | Status                      |
| ---------------- | -------------- | --------------------------- |
| MindLyst iOS     | 100%           | ✅ Good                     |
| MindLyst Android | 100%           | ✅ Good                     |
| ChronoMind Web   | 0%             | ❌ Critical                 |
| ChronoMind iOS   | 0%             | ❌ Critical                 |
| PeakPulse iOS    | 0%             | ❌ Needs tokens             |
| NomGap           | 0%             | ❌ Critical (466 hardcoded) |
| LysnrAI iOS      | 0%             | ❌ Critical                 |
| JarvisJr iOS     | 0%             | ❌ Critical                 |

See full audit: [`docs/design-system/DESIGN_SYSTEM_AUDIT_2026-03-03.md`](docs/design-system/DESIGN_SYSTEM_AUDIT_2026-03-03.md)

## 6. File Ownership Map

| Domain                  | Location                       | Key Files                                                                                                                     |
| ----------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| **Errors**              | `packages/errors/`             | `src/index.ts` — `BadRequestError`, `UnauthorizedError`, `ForbiddenError`, `NotFoundError`, `ConflictError`, `RateLimitError` |
| **Cosmos client**       | `packages/cosmos/`             | `src/index.ts` — `getCosmosClient()`, `getContainer()`, container registry                                                    |
| **Config / Product ID** | `packages/config/`             | `src/index.ts` — `loadEnvConfig()`, `loadProductIdentity()`, `getProductId()`                                                 |
| **JWT / Auth**          | `packages/auth/`               | `src/index.ts` — `signJwt()`, `verifyJwt()`, `hashPassword()`, `verifyPassword()`, auth middleware                            |
| **API client**          | `packages/api-client/`         | `src/index.ts` — `createApiClient()` with token injection                                                                     |
| **React auth**          | `packages/react-auth/`         | `src/index.ts` — `createAuthContext()` factory (provider + hook)                                                              |
| **LLM router**          | `packages/llm-router/`         | `src/router.ts` — route/plan/fallback, `src/registry.ts` — providers incl. local Ollama                                       |
| **Design tokens**       | `packages/design-tokens/`      | `tokens/bytelyst.tokens.json` (source), `scripts/generate.ts` (generator), `generated/` (output)                              |
| **Auth / JWT issue**    | `services/platform-service/`   | `src/modules/auth/`                                                                                                           |
| **Feature flags**       | `services/platform-service/`   | `src/modules/flags/` — FNV-1a hash for deterministic rollout                                                                  |
| **Blob storage**        | `services/platform-service/`   | `src/modules/blob/`, `src/lib/blob.ts` — SAS tokens, CRUD                                                                     |
| **Prototype status**    | `services/platform-service/`   | `src/modules/status/` — dependency health JSON, self-test JSON, self-test HTML                                                |
| **Delivery / SMTP**     | `services/platform-service/`   | `src/modules/delivery/` — SMTP delivery, Mailpit-backed prototype email flows                                                 |
| **Audit log**           | `services/platform-service/`   | `src/modules/audit/`                                                                                                          |
| **Notifications**       | `services/platform-service/`   | `src/modules/notifications/`                                                                                                  |
| **Rate limiting**       | `services/platform-service/`   | `src/modules/rate-limit/`                                                                                                     |
| **Subscriptions**       | `services/platform-service/`   | `src/modules/subscriptions/`                                                                                                  |
| **Stripe webhooks**     | `services/platform-service/`   | `src/modules/stripe/`                                                                                                         |
| **Usage tracking**      | `services/platform-service/`   | `src/modules/usage/`                                                                                                          |
| **Plans**               | `services/platform-service/`   | `src/modules/plans/`                                                                                                          |
| **Licenses**            | `services/platform-service/`   | `src/modules/licenses/`                                                                                                       |
| **Invitations**         | `services/platform-service/`   | `src/modules/invitations/`                                                                                                    |
| **Referrals**           | `services/platform-service/`   | `src/modules/referrals/`                                                                                                      |
| **Promos**              | `services/platform-service/`   | `src/modules/promos/`                                                                                                         |
| **Tracker items**       | `services/platform-service/`   | `src/modules/items/`                                                                                                          |
| **Public roadmap**      | `services/platform-service/`   | `src/modules/public/`                                                                                                         |
| **Tracker comments**    | `services/platform-service/`   | `src/modules/comments/`                                                                                                       |
| **Tracker votes**       | `services/platform-service/`   | `src/modules/votes/`                                                                                                          |
| **Extraction routes**   | `services/extraction-service/` | `src/modules/extract/` — POST /extract, /extract/batch, /extract/jobs, /extract/models                                        |
| **Extraction tasks**    | `services/extraction-service/` | `src/modules/tasks/` — predefined task library (triage, transcript, memory-insight, etc.)                                     |
| **Extraction Python**   | `services/extraction-service/` | `python/src/` — LangExtract sidecar (FastAPI :4006), extractor, task registry, language detection                             |
| **Extraction package**  | `packages/extraction/`         | `src/index.ts` — `createExtractionClient()`, shared types for consumers                                                       |
| **Monitoring**          | `services/monitoring/`         | `health-check.ts`, `loki/`, `grafana/`                                                                                        |

### Dashboard Consumers (via `file:` refs)

The following dashboards consume `@bytelyst/*` packages:

| Dashboard            | Location                              | Packages Used                                                |
| -------------------- | ------------------------------------- | ------------------------------------------------------------ |
| `admin-web`          | `dashboards/admin-web/` (this repo)   | api-client, auth, config, cosmos, errors, logger, react-auth |
| `user-dashboard-web` | `../learning_voice_ai_agent/`         | api-client, auth, config, cosmos, errors, logger, react-auth |
| `tracker-web`        | `dashboards/tracker-web/` (this repo) | api-client, config, cosmos, errors                           |

**Prerequisite:** Run `pnpm build` in this repo before running `npm install` in any dashboard.

### Local Mission Control Dashboard (Extracted)

The local LLM dashboard has been extracted to its own repo: `../learning_ai_local_llms/dashboard/`.
It consumes `@bytelyst/llm-router` from the Gitea npm registry.

## 6. How to Run Things

```bash
# ── Install ────────────────────────────────────────
pnpm install

# ── Build all packages + services ──────────────────
pnpm build

# ── Run all tests ────────────────────────────────
pnpm test

# ── Type-check everything ──────────────────────────
pnpm typecheck

# ── Clean dist/ in all packages ────────────────────
pnpm clean

# ── Run a specific service in dev mode ─────────────
pnpm --filter @lysnrai/platform-service dev    # port 4003
pnpm --filter @lysnrai/extraction-service dev  # port 4005

# ── Run tests for one workspace ────────────────────
pnpm --filter @lysnrai/platform-service test
pnpm --filter @bytelyst/errors test
pnpm --filter @bytelyst/llm-router test

# ── Generate design tokens ─────────────────────────
pnpm --filter @bytelyst/design-tokens generate

# ── Local LLM dashboard (extracted to ../learning_ai_local_llms/) ──
# cd ../learning_ai_local_llms/dashboard && npm install && npm run dev

# ── Docker Compose (all services + monitoring) ─────
docker compose up -d
docker compose down
pnpm prototype:self-test

# ── Encryption migration CLI ──────────────────────
npx tsx scripts/encrypt-migrate.ts --product all --dry-run       # Audit all 10 backends
npx tsx scripts/encrypt-migrate.ts --product lysnrai --dry-run   # Single product dry-run
npx tsx scripts/encrypt-migrate.ts --product chronomind           # Live encrypt (requires Cosmos creds)
# Requires: COSMOS_ENDPOINT, COSMOS_KEY, FIELD_ENCRYPT_KEY_PROVIDER, FIELD_ENCRYPT_KEY

# ── Portable builds for consumer repos ────────────
# Pack @bytelyst/* tarballs so Docker/CI builds don't need this sibling repo
./scripts/prep-consumer.sh /path/to/consumer-dir           # pack + rewrite
./scripts/prep-consumer.sh /path/to/consumer-dir --restore # undo

# ── Infrastructure lint (Dockerfiles + Helm charts) ─
./scripts/lint-infra.sh              # Lint all 25 Dockerfiles + any Helm charts
./scripts/lint-infra.sh --docker     # Dockerfiles only
./scripts/lint-infra.sh --helm       # Helm charts only
./scripts/lint-infra.sh path/to/Dockerfile   # Explicit path(s)
# Requires: brew install hadolint helm

# ── Cross-repo typecheck + test ──────────────────────
./scripts/typecheck-all.sh           # pnpm typecheck across all 11 repos
./scripts/test-all.sh                # pnpm test --run across all 11 repos

# ── Health check all services ──────────────────────
pnpm --filter @lysnrai/monitoring check
```

## 7. Common Patterns

### Adding a new Fastify module

1. Create `services/<service>/src/modules/<name>/types.ts` — Zod schemas + TS interfaces
2. Create `services/<service>/src/modules/<name>/repository.ts` — Cosmos CRUD
3. Create `services/<service>/src/modules/<name>/routes.ts` — REST endpoints
4. Register in `services/<service>/src/server.ts`: `await app.register(routes, { prefix: "/api" })`
5. Add tests: `services/<service>/src/modules/<name>/<name>.test.ts`

### Adding a new shared package

1. Create `packages/<name>/` with `package.json` (`name: "@bytelyst/<name>"`)
2. Add `src/index.ts` with exports
3. Add `tsconfig.json` extending `../../tsconfig.base.json`
4. Heavy deps go in `peerDependencies`, not `dependencies`
5. Consumer services add `"@bytelyst/<name>": "workspace:*"` to their `dependencies`

### Using shared packages in services

Services re-export from `@bytelyst/*` in `src/lib/` for clean internal imports:

```typescript
// src/lib/errors.ts
export { BadRequestError, NotFoundError, ConflictError } from '@bytelyst/errors';

// src/lib/cosmos.ts
export { getCosmosClient, getContainer } from '@bytelyst/cosmos';

// src/lib/product-config.ts
import { loadProductIdentity } from '@bytelyst/config';
const identity = loadProductIdentity();
export const PRODUCT_ID = identity.productId;
```

### Design token workflow

1. Edit `packages/design-tokens/tokens/bytelyst.tokens.json`
2. Run `pnpm --filter @bytelyst/design-tokens generate`
3. Copy generated files to consumer repos:
   - `MindLystTokens.kt` → `mindlyst-native/shared/.../theme/`
   - `MindLystTheme.swift` → `mindlyst-native/iosApp/`
   - Token CSS → `design-system/web/mindlyst.css` (token section only)

## 8. Environment Variables

Required by all services (see `.env.example`):

```
COSMOS_ENDPOINT=https://<account>.documents.azure.com:443/
COSMOS_KEY=<primary-key>
COSMOS_DATABASE=lysnrai
JWT_SECRET=<shared-secret>
```

Prototype Docker defaults differ from production:

- `COSMOS_ENDPOINT=http://cosmos-emulator:8081`
- `STORAGE_PROVIDER=azure` with Azurite-backed blob config
- `EMAIL_PROVIDER=smtp` with `SMTP_HOST=mailpit`
- Use placeholders in tracked files; keep real local values in `.env`

Additional per-service:

```
# platform-service
STRIPE_SECRET_KEY=<stripe-key>
STRIPE_WEBHOOK_SECRET=<webhook-secret>

# platform-service (blob storage)
AZURE_BLOB_CONNECTION_STRING=<connection-string>
AZURE_BLOB_ACCOUNT_NAME=bytelystblobs
AZURE_BLOB_ACCOUNT_KEY=<account-key>

# All services
DEFAULT_PRODUCT_ID=lysnrai
```

## 9. Corporate Network / Proxy Setup

Development happens on a corporate network with a TLS-intercepting proxy. The `NETWORK` env var controls all proxy behavior — **no manual toggling needed**.

### Quick Reference

| Tool                       | Corporate proxy mechanism                                          | Config location                               |
| -------------------------- | ------------------------------------------------------------------ | --------------------------------------------- |
| **npm / pnpm**             | `NPM_CONFIG_REGISTRY` → JFrog proxy, `NPM_CONFIG_STRICT_SSL=false` | `switch-network.sh`                           |
| **Node.js**                | `NODE_TLS_REJECT_UNAUTHORIZED=0`                                   | `switch-network.sh`                           |
| **Python (pip)**           | `PIP_TRUSTED_HOST`, `http_proxy`/`https_proxy`                     | `switch-network.sh`                           |
| **Gradle / Android / KMP** | Custom JVM truststore with corporate CA cert                       | `~/.gradle/gradle.properties` + `GRADLE_OPTS` |
| **curl / general HTTP**    | `http_proxy`/`https_proxy` → `cso.proxy.att.com:8080`              | `switch-network.sh`                           |

### How it works

1. `~/.zshrc` sets `export NETWORK=corp` (or `home`)
2. `~/.zshrc` sources `scripts/switch-network.sh` from this repo
3. The script conditionally sets/unsets all proxy env vars based on `NETWORK`
4. On new shell: `🏢 NETWORK=corp — proxy active` or `🏠 NETWORK=home — direct internet`

### Key files

| File                                                     | Purpose                                                    |
| -------------------------------------------------------- | ---------------------------------------------------------- |
| [`scripts/switch-network.sh`](scripts/switch-network.sh) | Central network switch — sets all proxy vars               |
| `~/.gradle/gradle.properties`                            | Gradle daemon JVM args (truststore) + HTTP proxy host/port |
| `~/.gradle/ssl/gradle-cacerts.jks`                       | Java truststore = default cacerts + ATT CSO proxy CA cert  |

### Gradle SSL (TLS interception)

The corporate proxy (`cso.proxy.att.com`) does TLS interception — it replaces upstream TLS certificates with its own, signed by the `ATTINTERNALROOTv2` CA. Gradle's JVM doesn't trust this CA by default.

**Solution:** A custom Java truststore (`~/.gradle/ssl/gradle-cacerts.jks`) that includes:

- All default Java CA certificates (from `$JAVA_HOME/lib/security/cacerts`)
- The AT&T CSO proxy CA certificate

The truststore is passed to Gradle via:

- `GRADLE_OPTS` env var (set by `switch-network.sh` when `NETWORK=corp`) — for the wrapper bootstrap JVM
- `org.gradle.jvmargs` in `~/.gradle/gradle.properties` — for the daemon JVM

**Recreate truststore** (needed after Java updates):

```bash
mkdir -p ~/.gradle/ssl
JAVA_HOME=$(/usr/libexec/java_home)
cp "$JAVA_HOME/lib/security/cacerts" ~/.gradle/ssl/gradle-cacerts.jks
echo | openssl s_client -connect services.gradle.org:443 \
  -proxy cso.proxy.att.com:8080 -showcerts 2>/dev/null \
  | awk 'BEGIN{c=0} /BEGIN CERT/{c++} c==2{print} /END CERT/&&c==2{exit}' \
  > /tmp/corp-ca.pem
keytool -importcert -noprompt -trustcacerts -alias att-cso-proxy \
  -file /tmp/corp-ca.pem \
  -keystore ~/.gradle/ssl/gradle-cacerts.jks -storepass changeit
```

### Docker builds behind corporate proxy

The TLS-intercepting proxy causes failures inside Docker containers even when Docker Desktop proxy is configured. See `AI.dev/SKILLS/dual-network-setup.md` § Docker Builds for the full reference. Summary:

- **Never use Alpine** — `apk` cannot bypass TLS interception; use `node:22-slim` (Debian)
- **Never use `corepack`** in Dockerfiles — use `npm install -g pnpm@10` or plain `npm`
- **Always add** `npm config set strict-ssl false` before any `npm install`
- **Always add** `ENV NODE_TLS_REJECT_UNAUTHORIZED=0` in build stages with native modules (e.g. `better-sqlite3`)
- Build-stage only — production images don't need these workarounds

### MUST follow (network-related)

- Always use `NETWORK` env var — never hardcode proxy URLs in app code
- Gradle builds require `GRADLE_OPTS` with truststore when on corp network (handled automatically by `switch-network.sh`)
- If a Gradle build fails with SSL errors, verify `echo $GRADLE_OPTS` shows the truststore path
- If adding a new tool that fetches from the internet, add its proxy config to `switch-network.sh`
- `~/.gradle/gradle.properties` is a local-only file — never commit it to any repo
- Docker builds: follow the `node:22-slim` + `strict-ssl false` + `NODE_TLS_REJECT_UNAUTHORIZED=0` pattern (see above)

### Kotlin Platform SDK (`packages/kotlin-platform-sdk/`)

The shared Kotlin SDK is consumed by Android apps as a composite build via `includeBuild()` in each product's `settings.gradle.kts`. Key details:

- `settings.gradle.kts` declares `pluginManagement` and `dependencyResolutionManagement` repos (required for composite builds to resolve their own deps)
- `build.gradle.kts` uses the Compose compiler plugin for UI components (`SurveyUI`, `InAppMessageUI`)
- Uses `kotlinOptions { jvmTarget = "17" }` (not `compilerOptions {}`) for compatibility

## 10. Dependency Graph

```
@bytelyst/errors          ← no deps (foundation)
@bytelyst/cosmos          ← peers: @azure/cosmos
@bytelyst/config          ← peers: zod; includes AKV resolver (resolveKeyVaultSecrets)
@bytelyst/auth            ← peers: jose, bcryptjs
@bytelyst/api-client      ← no deps
@bytelyst/react-auth      ← deps: @bytelyst/api-client; peers: react
@bytelyst/blob            ← peers: @azure/storage-blob
@bytelyst/extraction      ← no deps (types + client factory)
@bytelyst/monitoring      ← no deps (health-check utilities)
@bytelyst/llm-router      ← no deps (deterministic provider/model router + local Ollama plan support)
@bytelyst/fastify-core    ← deps: fastify; createServiceApp() + startService()
@bytelyst/logger          ← deps: pino
@bytelyst/testing         ← deps: vitest; shared mocks + Fastify inject helpers
@bytelyst/design-tokens   ← no deps (standalone generator)

@lysnrai/platform-service    ← @bytelyst/{fastify-core, config, cosmos, errors, auth, blob}
@lysnrai/extraction-service  ← @bytelyst/{fastify-core, config, cosmos, errors, auth}
```

Build order: packages first (they have no inter-deps), then services. `pnpm build` handles this automatically via workspace topology.

## 11. Key Documents

| When you need to...              | Read this                                                                                          |
| -------------------------------- | -------------------------------------------------------------------------------------------------- |
| Understand the full architecture | [`docs/ECOSYSTEM_ARCHITECTURE.md`](docs/ECOSYSTEM_ARCHITECTURE.md)                                 |
| See the extraction roadmap       | [`docs/ROADMAP.md`](docs/ROADMAP.md)                                                               |
| Understand why this repo exists  | [`docs/COMMON_PLATFORM_ANALYSIS.md`](docs/COMMON_PLATFORM_ANALYSIS.md)                             |
| Quick start / consuming packages | [`README.md`](README.md)                                                                           |
| LysnrAI product repo conventions | [`../learning_voice_ai_agent/AGENTS.md`](../learning_voice_ai_agent/AGENTS.md)                     |
| MindLyst native repo conventions | [`../learning_multimodal_memory_agents/AGENTS.md`](../learning_multimodal_memory_agents/AGENTS.md) |

## 12. Service Test Counts

| Service / Package  | Tests    | Runner |
| ------------------ | -------- | ------ |
| platform-service   | 800      | vitest |
| extraction-service | 46       | vitest |
| packages (various) | ~30      | vitest |
| **Total**          | **876+** |        |

> Note: billing-service, growth-service, and tracker-service were consolidated into platform-service (Feb 2026).
> Product-specific modules migrated to product repo backends (see below).

## 13. Product-Specific Backends

Product-specific API modules have been migrated from platform-service into each product repo's `backend/` directory:

| Product    | Repo                                            | Port | Modules                                                                                   | Tests |
| ---------- | ----------------------------------------------- | ---- | ----------------------------------------------------------------------------------------- | ----- |
| PeakPulse  | `../learning_ai_peakpulse/backend/`             | 4010 | peak-sessions, peak-routes                                                                | 32    |
| ChronoMind | `../learning_ai_clock/backend/`                 | 4011 | timers, routines, households, shared-timers                                               | 130   |
| JarvisJr   | `../learning_ai_jarvis_jr/backend/`             | 4012 | jarvis-agents, jarvis-sessions, jarvis-memory, jarvis-teams, marketplace                  | 198   |
| NomGap     | `../learning_ai_fastgap/backend/`               | 4013 | fasting-sessions, fasting-protocols, body-stages, social-fasting, meal-log, push-triggers | 152   |
| MindLyst   | `../learning_multimodal_memory_agents/backend/` | 4014 | brains, memory, reflections, daily-briefs, streaks                                        | 59    |

Each product backend uses `@bytelyst/*` packages via `file:` refs and follows the same Fastify module pattern.

## 14. Common Pitfalls

1. **Don't import `zod` from `@bytelyst/config` in services** — services bundle their own zod version. Use self-contained Zod schemas in `src/lib/config.ts`.
2. **Don't forget `productId`** — every Cosmos document must include it.
3. **Don't use `dependencies` for heavy libs in packages** — use `peerDependencies` so consumers control the version.
4. **Don't mix up package scopes** — libraries are `@bytelyst/*`, services are `@lysnrai/*`.
5. **Don't run `npm` commands** — this is a pnpm workspace. Always use `pnpm`.
6. **Don't modify generated files directly** — edit `bytelyst.tokens.json` and re-run the generator.
7. **Build packages before testing services** — service tests may import from `@bytelyst/*` `dist/`. Run `pnpm build` first if you get import errors.
8. **Don't duplicate LLM routing logic** — update `@bytelyst/llm-router` first, then have consumers call it.

---

## Cross-Repo Automation

For periodic maintenance tasks that span all ByteLyst repos (test audits, coverage gaps, dependency checks, secret scans, typecheck sweeps), see the **Coding Agent Automation Playbook**:

> [`docs/devops/CODING_AGENT_AUTOMATION_PLAYBOOK.md`](docs/devops/CODING_AGENT_AUTOMATION_PLAYBOOK.md)

Key tasks: workspace hygiene sweep, cross-repo test suite, backend/web coverage gap analysis, TypeScript typecheck sweep, dependency health check, secret scan, code quality audit, AGENTS.md consistency check.
