`src` holds 321 TypeScript files and no JavaScript, so four settings that
existed only to let the two coexist have nothing left to act on. Each was
removed on its own and validated before the next.
allowJs / checkJs Removed together, because they are coupled: `checkJs`
without `allowJs` is TS5052, a configuration error
rather than a code one. Verified first that nothing
under `src` imports a `.js` module. The program still
resolves the same 321 files and still reports 0 errors.
This also closes a door. With `allowJs` on, a `.js`
file added under `src` would be compiled and bundled
silently; now it is a resolution failure, which is the
right outcome for a codebase that has finished
migrating.
jsconfig.json Deleted. It had been inert since `tsconfig.json`
appeared — TypeScript ignores a jsconfig wherever a
tsconfig sits beside it — and it still carried the old
`types: []` and the three stale excludes. A second,
unread copy of the options is an invitation to edit the
wrong file.
components.json `"tsx": false` -> `true`, so `npx shadcn add` emits
TSX. No runtime effect; it would have quietly
reintroduced `.jsx` into a repository that has none.
`README.md` documented `npm run typecheck` as `tsc -p ./jsconfig.json`
with `checkJs`, which named a file that no longer exists. Corrected, along
with a stale assertion count in the same table (835, against 1691 today).
No file under `src` changed, so there is nothing for a per-file emitted-JS
comparison to compare; the bundle hash is the check that matters and it is
unmoved.
typecheck 0 errors, 321 files in the program
lint exit 0
npm test 1684/1691, the same 7 failures
owliver unchanged; baselines still pinned, not recaptured
build exit 0, bundle 74d17e2d… identical
Left in place deliberately, reported rather than removed:
`scripts/ssr-resolve.mjs`. Its own comment says it can go once `src` holds
no `.js`, but that is only half the condition — the 203 literal `.js`
paths inside `skill-check.mjs` would all have to be renamed first, and
that is a large diff against the file that guards this migration.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
211 lines
9.4 KiB
Markdown
211 lines
9.4 KiB
Markdown
# 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 <App/>, 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.<Name>.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 `<HotToaster>` 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` — 1691 assertions over the skill and agent systems |
|
|
| `npm run typecheck` | `tsc -p ./tsconfig.json` — 0 errors across 321 TypeScript files |
|
|
|
|
### 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.
|