learning_ai_common_plat/AGENTS.md

586 lines
40 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AGENTS.md — AI Coding Agent Instructions
> **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.
---
## 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 (400429)
│ ├── 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`
### 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
- 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
```
### 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
### 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.