# KROW Demo AI-powered hiring for event and hospitality staffing — a standalone demo build of KROW Forge. Runs with no backend, no API keys and no real login. `npm install && npm run dev` and the app is fully populated. ```bash npm install npm run dev # http://localhost:5173 — opens on /admin/login ``` ## Stack Vite 6 · React 19 · React Router 6 · React Query 5 · Tailwind + shadcn/ui · Framer Motion · Recharts + MUI X Charts · react-hot-toast. ## Structure ``` src/ main.jsx entry — mounts , imports index.css App.jsx providers + the entire route table index.css design tokens (HSL CSS variables) + utility layers api/ the backend seam — see below base44Client.js the contract the production app talks to store.js in-memory entity store, mirrored to localStorage aiEngine.js local, deterministic InvokeLLM seed.js demo dataset attendanceSeed.js shift/attendance dataset lib/ domain logic and data hooks krowHooks.js React Query hooks over the entity store krowAi.js AI workflows krowScore.js score engine AuthContext.jsx auth provider admin/ admin session, permissions, position insights skills/ the Owliver skill system (registry, resolver, flows) agents/ the agent system (registry, runtime, lifecycle) skills/ *.md skill definitions — loaded by import.meta.glob agents/ *.md agent definitions — loaded by import.meta.glob components/ ui/ 23 shadcn primitives ds/ 30 design-system components (the app's real vocabulary) admin/ admin page shell + role glyphs krow/ domain components (+ talent/, proving/) charts/ MUI X Charts wrappers and theme agents/ agent configure / canvas / test panel forge/ KROW Forge skill + challenge components skills/ skill authoring surfaces ai-assistant/ Owliver — panel, routing, capabilities, history layouts/ AdminLayout — header nav, ⌘K search, assistant panel pages/ route components (admin/ holds the live product) hooks/ shared hooks assets/ brand asset references ``` ## Routes The app opens on **`/admin/login`**. Everything else lives under `/admin/*` behind two guards: `ProtectedRoute` (auth) and `AdminRoute` (admin session), rendered inside `AdminLayout`. | Path | Page | | --- | --- | | `/admin/login` | Admin sign-in — outside the protected boundary | | `/admin` | Control Center | | `/admin/positions`, `positions/new`, `positions/:id` | Positions, Create, Detail | | `/admin/candidates`, `candidates/:id` | Candidates, Candidate Intelligence profile | | `/admin/hired` · `/admin/talent-pool` | Hired History, Talent Pool | | `/admin/university`, `university/:id` | KROW Forge, challenge detail | | `/admin/analytics` · `/admin/activity` | Analytics, Activity | | `/admin/profile` · `/admin/settings` | Profile, Settings | | `/admin/workspace` | Workspace hub | | `/admin/workspace/agents`, `agents/new`, `agents/:id` | Agent list, agent editor | | `/admin/workspace/skills`, `skills/new`, `skills/:id` | UI skill list and editor | | `/admin/workspace/skills/owliver/new`, `owliver/:id` | Owliver skill editor | | `/admin/workspace/skill-development` | Skill Development | | `*` | `lib/PageNotFound.jsx` | Legacy top-level paths (`/overview`, `/positions`, `/candidates`, `/hired`, `/talent-pool`, `/university`, `/analytics`, `/activity`, `/tracking`) all `Navigate` into their `/admin/*` equivalent, id preserved. Static route segments are declared before dynamic ones so `workspace/agents/new` cannot be read as an agent whose id is `"new"`. > **Note.** `App.jsx` still imports a set of pre-redesign Employer/Talent pages > (`Overview`, `Apply`, `WorkerProfile`, `KrowIdentity`, `EmployeeDashboard`, `DesignSystem`, …) > and `layouts/Layout.jsx`. None of them are mounted on a route — they are unreachable at runtime > and retained pending a decision on whether to re-route or remove them. ## The backend seam The production app talks to a Base44 backend through `src/api/base44Client.js`. This demo keeps that module path, export name and full method contract, and swaps only the transport: | Contract | Demo implementation | | --- | --- | | `base44.entities..list/filter/get/create/update/delete` | `api/store.js` — in-memory records, mirrored to `localStorage` | | `base44.integrations.Core.InvokeLLM` | `api/aiEngine.js` — local, deterministic | | `base44.integrations.Core.UploadFile` | blob URLs, session-scoped | | `base44.auth.*` | seeded employer/admin user, always signed in | Because the seam did not move, everything above it — `lib/krowHooks.js`, `lib/krowAi.js`, `lib/krowScore.js`, every page and component — is unchanged from the reference implementation. ## The AI engine Every AI workflow funnels through one call, `InvokeLLM({ prompt, response_json_schema })`. `api/aiEngine.js` reimplements it locally: it recognises each workflow by the phrase its prompt opens with, reads the structured fields the prompt already carries (`Years Experience: 6`, `Required Certifications: …`, the talent-pool JSON block) and scores them deterministically. Nine workflows are implemented: candidate screening, job-description generation, résumé building, interview questions, interview evaluation, talent matching, Owliver's conversation, Career DNA building and Proving Ground evaluation. Results respond to the data rather than being canned — the same input always returns the same output. ## Skills and agents Two registries discover their definitions from markdown at build time: | Registry | Glob | Files | | --- | --- | --- | | `lib/skills/registry.js` | `import.meta.glob('/src/skills/**/*.md')` | 18 Owliver skills, 5 workforce training skills | | `lib/agents/registry.js` | `import.meta.glob('/src/agents/**/*.md')` | 9 agent definitions | **These globs are absolute paths.** Moving or renaming `src/skills/` or `src/agents/` makes the registry silently return nothing — no build error, no import failure, just an empty registry. Add definitions by dropping a new `.md` file into the right folder; nothing else needs to change. ## Demo data Seeded in `src/api/seed.js` — positions, applicants, AI-scored candidates, interviews, hires, talent-pool profiles, Proving Ground courses and an activity log; shift data in `src/api/attendanceSeed.js`. Edits persist to `localStorage`. To restore the shipped dataset: ```js // in the browser console localStorage.removeItem('krow_demo_db'); location.reload(); ``` Bumping `STORAGE_VERSION` in `src/api/store.js` (currently `8`) invalidates saved snapshots automatically, so changing the seed does not require a manual clear. ## Design system Tokens live in `src/index.css` as HSL CSS variables consumed by `tailwind.config.js`. | Token | Value | | --- | --- | | Primary — KROW Blue | `#0A39DF` | | Accent — KROW Yellow | `#F9E547` | | Pale Yellow | `#F8E08E` | | Dark Navy | `#333F48` | | Mint | `#D1E0D7` | | Radius | `0.75rem` | | Headings | Sora | | Body | Inter | `src/components/ds/` is the vocabulary the app actually writes in — `Surface`, `KpiCard`, `DataTable`, `PageHeader`, `Modal`, `toast` and the rest, exported through `ds/index.js`. `src/components/ui/` holds the shadcn primitives those are built on. Surfaces use `glass` and `glass-card`; the page background is the `gradient-mesh` utility. Toasts go through `ds/toast` (a wrapper over `react-hot-toast`), rendered by `` in `App.jsx`. ## Scripts | Command | Purpose | | --- | --- | | `npm run dev` | Dev server (honors `PORT`, default 5173) | | `npm run build` | Production build | | `npm run preview` | Serve the build | | `npm run lint` | ESLint — currently clean | | `npm run lint:fix` | ESLint with `--fix` | | `npm test` | `scripts/skill-check.mjs` — 835 assertions over the skill and agent systems | | `npm run typecheck` | `tsc -p ./jsconfig.json` with `checkJs` | ### The test harness `npm test` boots a real Vite dev server in middleware mode and `ssrLoadModule`s the actual application modules, so it exercises the real `@/` alias and `import.meta.glob` pipeline rather than mocks. It also diffs Owliver's behaviour across eleven page contexts against a committed snapshot, `scripts/__baseline__/owliver-baseline.json`; regenerate that deliberately with `node scripts/owliver-baseline.mjs --write`, never to turn a red check green. Because it loads modules by absolute path, the harness pins the locations of `src/api/`, `src/lib/`, `src/lib/skills/`, `src/lib/agents/`, `src/components/ai-assistant/`, `src/App.jsx`, `src/layouts/AdminLayout.jsx` and a handful of agent files. Move any of them and update `scripts/owliver-capture.mjs` and `scripts/skill-check.mjs` in the same change. ### Known-failing checks Two checks fail on a clean checkout and are tracked as separate work, not regressions: - `npm test` — 834/835 pass; `the seeded overtime climb is found` fails. - `npm run typecheck` — 59 errors, all pre-existing JSDoc/inference gaps in `lib/krowAi.js`, `lib/skills/*`, `lib/positionModel.js` and a few components. ## Deployment `Dockerfile` builds with `node:22-alpine` (capped at 1 GB heap for small hosts) and serves the static `dist/` from `nginx:alpine` using `nginx.conf` — gzip, immutable `/assets/` caching and an SPA fallback to `index.html` so client-side routes resolve on refresh.