HR-ATS-Portal/docs/architecture/_repo-findings.md

149 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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.