Aravind eaa677f815 refactor(ts-migration): Phase 11 batch 1 — the UI-node engine
Renames the ten modules under `src/lib/ui/` to TypeScript and annotates
them. No logic is touched: no reordered statements, no changed defaults,
no altered branches, no renamed locals, no edited strings.

The proof is mechanical rather than argued. esbuild's output for each of
the ten files, minified, is byte-for-byte what the `.js` file produced at
`ecf5e75`, and the production bundle hashes to `74d17e2d…` before and
after. `operations` and `patch` are pure renames — R100, not one
character changed.

What the annotations actually are:

  - Four `/** @type */` and `@param` JSDoc hatches the author had already
    written, restated as real annotations. These stop applying at the
    extension boundary, which is where most of the errors came from.
    `makeNode` keeps its declared `@returns {any}`; dropping it in favour
    of an inferred shape would have quietly narrowed a contract the
    author had deliberately left open.
  - `NodeTypeRegistry.types` as `declare`, not a field declaration. A
    plain one would emit a `defineProperty` under
    `useDefineForClassFields`, i.e. a change to the shipped JS. `declare`
    emits nothing, which the per-file comparison above confirms.
  - Two accumulators (`wants`, `params`) given the shape their own
    following lines build.
  - `Object.entries<any>(…)` at two sites. Inferring `any` into the
    union parameter of `entries` yields `unknown`, not `any`, so the
    rule objects arrived unreadable; an explicit type argument restores
    what JavaScript had, without a cast.
  - `UiEditMatch`, an open interface, as `matchUiEdit`'s return.

`UiEditMatch` is the one judgement call and it is deliberately weak.
`kind` is optional and the rest is an index signature, because the
nineteen return shapes share field names carrying different meanings and
the suite reads these objects in around forty places. Writing the real
discriminated union is a schema this phase has no mandate to invent, and
`kind` stays `string` rather than a literal union partly so that no
node-type name is ever written into a type — the engine check forbids
exactly that.

`kind` is optional for a reason worth recording: at run time the planners
guard with `if (subject.kind) return subject`, so a `kind`-less object
never escapes. TypeScript cannot see it, because every `kind` widens to
`string` and a property that is `string` in every member is not a
discriminant, so truthiness narrowing leaves a shape the function cannot
produce. Marking it required would have been true of the runtime and
rejected at five return sites, and the only fixes are edits to agent
logic. The weaker claim is the honest one.

Renaming these files also surfaced twenty errors in `uiEdit.js`, which is
still JavaScript: `.js` and `.ts` infer this union differently. Verified
as a property of the rename and not of any edit, by compiling the
verbatim `ecf5e75` contents under a `.ts` extension — same twenty. The
return annotation clears them.

Measured against `ecf5e75`, all unchanged:

  typecheck   20 errors, same files      (no new error anywhere)
  lint        exit 0, 269 files, 0 errors, 289 warnings
  npm test    1684/1691, the same 7 failures verbatim
  build       exit 0, identical bundle hash
  emitted JS  10/10 byte-identical

The 7 failures and the `owliver-baseline.mjs` drift both pre-date this
commit — they are the parallel feature session's, present at `ecf5e75`
and measured there before this batch was applied. No baseline artifact
is touched; recapture waits until the agent migration is complete.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
2026-09-18 13:50:33 +05:30
2026-08-07 23:53:15 +05:30
2026-08-28 13:52:59 +05:30
2026-09-05 10:46:06 +05:30
2026-08-19 17:36:27 +05:30
2026-08-10 14:37:51 +05:30
2026-08-25 18:56:07 +05:30
2026-08-28 11:02:02 +05:30
2026-08-28 11:02:02 +05:30
2026-08-24 20:06:11 +05:30
2026-08-07 23:53:15 +05:30
2026-08-28 11:02:02 +05:30
2026-08-17 17:33:56 +05:30
2026-08-10 15:53:53 +05:30
2026-08-07 23:53:15 +05:30
2026-08-13 21:41:14 +05:30
2026-08-25 18:56:07 +05:30

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.

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:

// 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 — 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 ssrLoadModules 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.

Description
No description provided
Readme 7.2 MiB
Languages
TypeScript 81.1%
JavaScript 15.7%
HTML 2.6%
CSS 0.5%