149 lines
11 KiB
Markdown
149 lines
11 KiB
Markdown
# Repository Inspection Evidence — verified facts
|
||
|
||
> Working note. Every claim below was verified by direct inspection on 2026-07-29.
|
||
> Authoring agents MUST use these facts and cite these file paths. Do not re-derive
|
||
> or contradict them. Do not invent files, frameworks, or config that is not listed.
|
||
|
||
## A. What the repository actually is
|
||
|
||
A **static, browser-only frontend prototype**. There is no backend of any kind.
|
||
|
||
Complete file inventory of the **prototype as inspected on 2026-07-29** (excluding
|
||
`.git/`, `.backup-prebrand/`). Files added afterwards by this design package are listed
|
||
separately below, so the prototype's own line counts stay comparable:
|
||
|
||
```
|
||
index.html 287 lines
|
||
css/styles.css 1269 lines (design system, brand-tokenised)
|
||
js/ (22 files, ~4,780 lines total — `wc -l js/*.js` = 4,779)
|
||
data.js 512 charts.js 347 candidates.js 441 inbox.js 332
|
||
app.js 275 jobs.js 252 aiassistant.js 218 interviews.js 209
|
||
misc.js 207 import.js 176 jobboard.js 172 pipeline.js 166
|
||
dashboard.js 170 ui.js 252 offers.js 139 assessments.js 126
|
||
recruiterhub.js 121 rbac.js 117 analytics.js 114 reports.js 109
|
||
settings.js 193 tasks.js 131
|
||
devserver.py 36 lines (no-cache static server, added for preview only)
|
||
docs/TalentFlow-ATS-Business-Requirements-v1.0.docx
|
||
.gitignore 42 lines
|
||
.claude/launch.json
|
||
.audit.js (dev-only QA helper, gitignored)
|
||
```
|
||
|
||
Added after inspection by this design package, and **not** part of the prototype or of
|
||
any line count quoted anywhere in these documents:
|
||
|
||
```
|
||
docs/architecture/ this package
|
||
tools/check_evidence_citations.py documentation gate: verifies every `path:line`
|
||
citation in docs/architecture against the source
|
||
```
|
||
|
||
## B. ABSENT — verified by `find` / direct check
|
||
|
||
None of the following exist anywhere in the repository:
|
||
|
||
- `package.json`, any lockfile, `node_modules/`
|
||
- `requirements.txt`, `pyproject.toml`, `Pipfile`
|
||
- `composer.json`, `go.mod`, `Gemfile`, `pom.xml`, `build.gradle`
|
||
- `Dockerfile`, `docker-compose.yml`, `Makefile`
|
||
- `.env`, `.env.example` (none present; `.gitignore` anticipates them)
|
||
- Any migration directory or migration tool
|
||
- Any ORM, query builder, or database driver
|
||
- Any test file, test runner, or CI configuration (`.github/` absent)
|
||
- Any build tooling (`tsconfig.json`, `vite.config.js`, `webpack.config.js`)
|
||
- Any API route, controller, service, or server-side code
|
||
- **Any meeting transcript or recruitment document** (searched repo and
|
||
`~/Documents` to depth 2 — only the BRD `.docx` this project produced)
|
||
|
||
**Consequence:** there is no backend stack to preserve. Operating Rule 9
|
||
("do not replace the existing stack") therefore constrains only the frontend
|
||
and design system, not the backend, which is a greenfield choice.
|
||
|
||
## C. Frontend architecture — verified
|
||
|
||
| Fact | Evidence |
|
||
|---|---|
|
||
| Zero network calls. `grep` for `fetch(`, `XMLHttpRequest`, `axios`, `$.ajax`, `WebSocket`, `EventSource` across `js/` and `index.html` returns **nothing**. The UI is fully self-contained. | repo-wide grep |
|
||
| All data is generated in-browser at load by a seeded LCG PRNG (`seed = 88123`), so the dataset is stable across reloads but exists only in memory. Nothing persists. | `js/data.js:8-10` |
|
||
| 100 candidates, plus jobs/interviews/assessments/offers/inbox generated at load. | `js/data.js:110-131` |
|
||
| Hardcoded "today" — `new Date('2026-07-09')` used as the current date in multiple places. | `js/data.js:237`, `js/candidates.js:18`, `js/jobboard.js:167` |
|
||
| `localStorage` used **only** for the theme preference. No session or app state. | `js/app.js:64,193,198` |
|
||
| Client-side hash router over `location.hash`; views are functions returning HTML strings plus an `onMount` hook. 23 routes. | `js/app.js:20-57` (`Router.go`, `Router.render`), `ROUTES` map `js/app.js:7-16` |
|
||
| Global namespaces on `window`: `DB`, `UI`, `Charts`, `App`, `Router`, `Views`, plus per-module globals. No modules, no bundler, 22 `<script>` tags in order. | `index.html:264-285` |
|
||
|
||
## D. Authentication and authorization — verified ABSENT
|
||
|
||
| Fact | Evidence |
|
||
|---|---|
|
||
| No authentication whatsoever. No login screen, no token, no session. | repo-wide grep |
|
||
| Security settings are **inert UI chrome** — 2FA, SSO, session timeout and password policy render as toggles/selects with no handlers and no persistence. | `js/settings.js:148-154` |
|
||
| The RBAC permission matrix is a **display widget only**. Clicking a cell mutates an in-memory array; nothing reads the matrix to gate behaviour. There is no `can()`, `hasPermission()`, or equivalent anywhere. | `js/rbac.js:78`, `js/rbac.js:83-85`, `js/rbac.js:111-112` |
|
||
| Roles/permissions are demo data: 8 roles, 13 modules, 8 permission types, matrix derived from a single `level` cutoff index. | `js/data.js:425-446` (`rbacModules`, `permTypes`, `rbacRoles`, `buildMatrix`) |
|
||
|
||
## E. SECURITY — P0 finding: systemic XSS exposure
|
||
|
||
| Fact | Evidence |
|
||
|---|---|
|
||
| **No HTML escaping exists anywhere.** `grep` for `escapeHtml`, `sanitiz`, `DOMPurify` returns nothing. | repo-wide grep |
|
||
| 34 `innerHTML` assignments across 14 files; every view builds HTML by template-string interpolation of data values. | `grep -c innerHTML js/*.js` |
|
||
| Candidate-controlled fields are interpolated raw into markup — e.g. `${c.name}`, `${c.currentTitle}`, `${c.location}`. | `js/candidates.js:68` |
|
||
| Inline event handlers with interpolated values, e.g. `onclick="Candidates.openProfile('${c.id}')"`. | `js/candidates.js:121` |
|
||
|
||
**Why this is P0 and not theoretical:** today the data is synthetic and generated
|
||
locally, so nothing is exploitable. The moment real data flows in — and the two
|
||
primary Phase 1 sources are **CV files and inbound email**, both attacker-supplied —
|
||
every screen becomes a stored-XSS sink. A CV containing
|
||
`<img src=x onerror=...>` in its name field would execute in a recruiter's session
|
||
with full application privileges. This must be fixed before any real data is
|
||
rendered, and it is a rendering-layer change, not a backend one.
|
||
|
||
## F. Data-model gaps in the prototype (all confirm the prompt's principles)
|
||
|
||
| Gap | Evidence | Implication |
|
||
|---|---|---|
|
||
| **No candidate/application separation.** One flat `candidates` array carries `jobId`, `jobTitle`, `stage`, `aiScore`, `recruiter` directly on the candidate. One candidate cannot hold two applications. | `js/data.js:117-127` | Confirms prompt §5.2 — must be split. |
|
||
| **ATS score is a random integer**, `aiScore: int(52,98)`. No components, no evidence, no model, no version. A separate client-side "relevance" blend also exists. | `js/data.js:123`, `js/candidates.js:18` | Confirms prompt §5.6 — nothing reusable. |
|
||
| **No raw intake layer.** The inbox is a pre-resolved array already joined to candidate and job; there is no unresolved/failed state that cannot become a candidate. | `js/data.js:284-300` (`processingStatuses`, `inbox`) | Confirms prompt §5.1. |
|
||
| **No versioning of jobs or requirements.** Jobs are mutable single records. | `js/data.js:85-108` | Confirms prompt §5.3. |
|
||
| **Recruiter assignment is a single scalar** (`recruiter`, `recruiterId`) on the job/candidate. No history, no primary/supporting distinction. | `js/data.js:96`, `js/data.js:123` | Confirms prompt §5.4. |
|
||
| **No history tables.** Current values only — no stage history, assignment history, or status history. | `js/data.js` throughout | Confirms prompt §5.5. |
|
||
| **Money is a bare integer.** `salary: int(90,190)*1000`. No currency field anywhere in the dataset; offer validation only checks `> 0`. | `js/data.js:126`, `js/offers.js:129` | Needs decimal + ISO currency. |
|
||
| **No timezone discipline.** JS `Date` objects, `toLocaleDateString` for display, hardcoded "today". No UTC storage, no tz-aware scheduling. | `js/data.js:54,237` | Interview scheduling will need real tz handling. |
|
||
| No requisition concept at all. No director role scoping. | absent from `js/data.js` | Phase 2 greenfield. |
|
||
|
||
## G. Genuinely reusable assets (retain)
|
||
|
||
| Asset | Why it is worth keeping | Evidence |
|
||
|---|---|---|
|
||
| **Design system** — 1269 lines of tokenised CSS: dual light/dark themes, Utopia brand palette and type hierarchy, responsive 320px→ultrawide, WCAG 2.1 AA verified across 23 routes × 2 themes (8,459 text nodes, 0 failures), 44px touch targets, safe-area/`dvh` handling. | Substantial, verified, brand-compliant work. Rebuilding it would be pure loss. | `css/styles.css` |
|
||
| **`js/ui.js` primitives** — `modal`, `toast`, `dataTable` (sort + paginate + render hooks), `badge`, `avatar`, `avatarStack`, `scoreChip`, `pbar`, `fieldError`, `clearErrors`, icon set. | A coherent component vocabulary already matching the design system. | `js/ui.js:251` (export list) |
|
||
| **`js/charts.js`** — dependency-free canvas chart engine (line/area, bar, grouped bar, doughnut, horizontal bar, sparkline) reading colours from CSS custom properties so it re-themes automatically. | Avoids adding a charting dependency; already theme-aware. | `js/charts.js:339` |
|
||
| **23-route information architecture** — the module breakdown, navigation grouping and screen inventory are a validated UX artefact even though the data layer behind them is fake. | Useful as the Phase 1+ screen backlog. | `js/app.js:7-16` |
|
||
|
||
## H. Frontend liabilities (refactor)
|
||
|
||
| Liability | Evidence | Note |
|
||
|---|---|---|
|
||
| String-template `innerHTML` rendering with no escaping — see §E. | 34 sites | P0 before real data. |
|
||
| No build step, no modules, 22 ordered `<script>` tags, everything on `window`. Complex forms (requisitions, offers, scorecards) in this pattern will not scale to 25 modules with two developers. | `index.html:264-285` | Real tension: retain the CSS/UX, reconsider the rendering layer. |
|
||
| No client-side validation library; ad-hoc per-form checks. | `js/offers.js:129`, `js/ui.js:241-249` | |
|
||
| No tests of any kind. | absent | |
|
||
| `.gitignore` contains a Python section and `.env` rules but **no Node section**. This postdates `devserver.py` (added for local preview), so it is **weak/ambiguous evidence of stack intent** and must not be presented as a decision input. | `.gitignore` | Do not over-read this. |
|
||
|
||
## I. Team
|
||
|
||
- **Talha Ahmed** — senior; ATS scanning, candidate matching, AI workflows, architecture, integrations.
|
||
- **Ahmed Mujtaba** — junior; capable coder. Must get small, modular, varied, independently
|
||
demonstrable work across frontend, backend APIs, validation, testing, AI UX, dashboards
|
||
and workflow logic. Explicitly **not** only repetitive CRUD or data cleaning.
|
||
Every task needs a Talha review checkpoint.
|
||
|
||
## J. Hard constraints for all authors
|
||
|
||
- Internal Utopia Brands system. **Not** multi-tenant SaaS.
|
||
- One platform, one master data model, one relational database. No regional databases.
|
||
- Do not invent repository contents. If something does not exist, say so and cite §B.
|
||
- Do not print or copy secret values (none exist to leak — no `.env` present).
|
||
- No transcript exists; the assignment prompt is the authoritative requirements source.
|
||
State this explicitly rather than implying a transcript was read.
|