Title: Project Status Version: 1.1 Owner: [TENANT_CONFIGURATION_REQUIRED — Engineering Lead] Status: Living document Last reviewed: 2026-09-08 Next review: [TENANT_CONFIGURATION_REQUIRED — recommend every 2 weeks or at every milestone] Reviewers: Engineering, Product, Architecture
The authoritative, maintained record of what is actually built versus planned in this repository, as evidence-based as possible: every “Implemented” claim below reflects a real, run command (dotnet build, dotnet test, live HTTP calls against a running API and a real database) or direct code inspection performed in the session that recorded it — not an inference from documentation. Update this file whenever implementation status changes; it is the single place other documents point to for “is X actually done.”
| Label | Meaning |
|---|---|
| Implemented | Real code exists, builds, and has been exercised (test or live call) against a real dependency (real database, real HTTP server) |
| Partially implemented | Some real code/data path exists but a materially significant piece is missing, stubbed, or unverified |
| Scaffolded | A project/folder/file exists (possibly with a README) but contains no real logic — a placeholder for future work |
| Planned | Described in documentation/config schemas but no code exists |
| Deferred | Explicitly out of scope for the current phase |
| Requires decision / HR / security / legal / DBA review | See DECISIONS_REQUIRED.md for the specific open item |
First vertical slice of the recruitment workflow (CV ingestion → Master CV Bank; TAN creation → approval) implemented database-first against a real SQL Server schema, and now the React frontend (HrAutomation.Web) is genuinely wired to it end-to-end for that same slice: TAN list/create/approve and CV upload/candidate list/candidate profile all make real HTTP calls to HrAutomation.Api, persist into HrAutomationDb, and read back server-authoritative data — verified via a real headless-browser run against the live API and confirmed independently with a direct SQL query (see docs/09-quality-evaluation/live-sql-server-integration-verification.md). MSW is now OFF by default at runtime; a VITE_AUTH_MODE=devToken provider obtains a real signed JWT from the backend’s dev-only token endpoint. New this pass: the first slice of the Administration/Master-Management module — real, database-backed effective-permission checking (iam.RolePermission) and User read/list/detail (/admin/users) — see docs/09-quality-evaluation/master-management-gap-analysis.md for the full 22-area gap analysis and priority order. Next milestone: user creation/role assignment (with anti-escalation checks) and role/permission management, per that gap analysis’s P0 list.
| Capability | Status | Evidence / detail |
|---|---|---|
| CV upload, parsing, Master CV Bank | Implemented, frontend-connected | POST /api/v1/candidates/cvs → parse_cv_skill → recruitment.usp_CreateCandidate + CandidateCv/CandidateCvVersion/CvParsingResult/CvExtractionField. Duplicate detection is suggestion-only (usp_CreateDuplicateReview), never auto-merged. CvUploadPage uploads one file per real API call (the endpoint takes a single IFormFile) and shows the server’s actual per-file result. Verified via live HTTP call, dotnet test, and a real headless-browser run. |
| Candidate profile read/list/patch | Implemented, frontend-connected (list/get only) | GET /api/v1/candidates, GET /api/v1/candidates/{id}, PATCH /api/v1/candidates/{id} (RowVersion/If-Match optimistic concurrency, no frontend UI for this yet). Cursor pagination verified live. CurrentLocation field accepted but not persisted — see DEC-004. GET /api/v1/candidates/{id}/cvs (a per-candidate CV list) does not exist — CandidateProfilePage shows a truthful “not available yet” state instead of calling it. |
| TAN / Job Requisition creation | Implemented, frontend-connected | POST /api/v1/tans → create_tan_skill → recruitment.usp_CreateTalentAcquisitionNumber + usp_CreateJobRequisition + JD version rows, auto-submitted for approval (usp_SubmitJobRequisitionForApproval). Location/Grade/Budget*/InterviewStagesCount/ClientInterviewRequired/criteria-JSON fields accepted by the API but not persisted — see DEC-004. TanFormPage submits real values and navigates to the server-generated tan_id on success, then re-fetches. |
| TAN approval / rejection | Implemented, frontend-connected | POST /api/v1/tans/{id}/approve → tan_approval_skill → recruitment.usp_ApproveJobRequisition / usp_RejectJobRequisition (the latter added this pass — no reject procedure existed before). “Returned for info” is audit-only, no state change, by design. TanDetailPage’s Approve button calls this for real, behind a confirmation dialog. |
| TAN list | Implemented, frontend-connected | GET /api/v1/tans (added this pass — did not exist before; TanListPage was calling a route that 404’d against the real backend). Cursor-paginated, mirrors ListCandidates’s pattern. |
| Current user profile | Implemented, frontend-connected | GET /api/v1/users/me (added this pass) — returns tenant/user/role/display name from the validated JWT, enriched with iam.User.DisplayName. Used by devTokenAuthProvider right after login. |
| Admin: effective permissions | Implemented, frontend-connected | GET /api/v1/users/me/permissions (new) — real iam.UserRole→iam.Role→iam.RolePermission→iam.Permission resolution, deny-by-default. First endpoint gated by the new IEffectivePermissionService rather than a role-string list; used by RequirePermissionAsync (new HrControllerBase helper) on every /admin/* endpoint, and by useHasPermission() on the frontend. |
| Admin: user read/list/detail | Implemented, frontend-connected | GET /api/v1/admin/users (cursor-paginated, search by name/email, filter by status), GET /api/v1/admin/users/{userId} (profile, department/location names, active role list, server-computed allowed_actions). Gated on the real user.read permission (403 + audit event on denial, verified live). AdminUsersListPage/AdminUserDetailPage under /admin/users, verified via a live headless-browser run against the real API — 17 real seeded users render, detail page shows real department/role data. User create/edit/activate/deactivate/role-assignment are not yet implemented — next batch. |
| Health check | Implemented | GET /health (added this pass, unauthenticated by design) — verifies HrAutomationDb reachability via db.Database.CanConnectAsync(), never leaks connection string/server name/exception text. Polled by the frontend’s dev-only status indicator every 30s. |
| TAN / candidate read + audit log | Implemented (audit log not yet frontend-connected) | GET /api/v1/audit-logs, GET /api/v1/workflows/{approvalRequestId} (repurposed to read workflow.ApprovalRequest, since no generic WorkflowInstance table exists — see ADR-006). AuditPage still shows MSW-only mock data — not touched this pass. |
| Candidate matching (AI) | Scaffolded | match_candidates_skill registered but is a stub — always returns blocked/”not yet implemented.” No matching logic, no CandidateMatchScore writes. Shortlist approval (the human decision once a match exists) is implemented — see next row. |
| Shortlist approval | Implemented, frontend-connected | POST /api/v1/applications/{id}/shortlist-approval → ShortlistApprovalSkill → recruitment.usp_ApproveCandidateShortlist. CandidateMatchesPage’s “Approve for shortlist” button calls this for real. |
| Interview scheduling / feedback | Implemented, frontend-connected | GET/POST /api/v1/interviews, POST .../{id}/feedback → CreateInterviewSkill/InterviewFeedbackSkill (new) → 2 new stored procedures (usp_ScheduleInterview, usp_SubmitInterviewFeedback). progression_approval_skill remains a stub (depends on a generic workflow-transition concept not modeled here). |
| Offer drafting / approval / send / acceptance | Implemented, frontend-connected | GET/POST /api/v1/offers, .../approve, .../send, .../acceptance → 4 real skills calling offer.usp_CreateOfferDraft/usp_SubmitOfferForApproval/usp_ApproveOffer/usp_MarkOfferSent (pre-existing, previously unused) + 1 new procedure (usp_RecordOfferAcceptance). Compensation entry (offer.OfferCompensation) intentionally has no write path — restricted table, no procedure exists, not fabricated. |
| Green Form / document upload / verification | Partially implemented, frontend-connected (Green Form only) | GET/POST /api/v1/green-forms/by-token/{token}, .../submission/{id}, .../submissions, POST .../{applicationId}/issue-link all real — 3 new stored procedures, plus a new RLS-exemption role (db_hr_public_token_resolver) since the candidate flow is anonymous/token-only (see ADR-… none yet — documented directly in 07-create-stored-procedures.sql and 05-create-security.sql). document_verify_skill remains a stub — no VerificationCheck/VerificationResult write path exists. |
| Verification queue | Implemented, frontend-connected (read-only) | GET /api/v1/verification, GET .../{applicationId} (new controller) — real reads over onboarding.VerificationCase. No write action yet (see document-verify row above). |
| Discrepancy management | Implemented, frontend-connected | GET/POST /api/v1/discrepancies, .../resolve, .../reupload-request → real DiscrepancyResolveSkill/DiscrepancyReuploadRequestSkill → onboarding.usp_ResolveDiscrepancy (pre-existing, previously unused) + 1 new procedure (usp_RequestDocumentReupload). |
| Employee conversion / Employee ID | Implemented, frontend-connected | GET /api/v1/employee-conversion (new controller, real eligibility checklist), POST /api/v1/applications/{id}/convert-to-employee → EmployeeConversionSkill chaining employee.usp_ApproveEmployeeConversion → usp_CreateEmployeeFromCandidate (both pre-existing, previously unused, both independently re-validate eligibility server-side). |
| Approvals work queue | Implemented, frontend-connected (read + partial decision routing) | GET /api/v1/approvals (new controller) — real cross-entity-type read of workflow.ApprovalRequest. POST .../{id}/decision real for offer.Offer/onboarding.Discrepancy; other entity types (TAN, shortlist, employee-conversion) still use their own dedicated endpoints and return an honest 422 if routed here instead. |
| Dashboard summary | Implemented, frontend-connected | GET /api/v1/dashboard/summary (new controller) — every tile is a real, live-computed count against real tables. sla_breaches is honestly always 0 — no SLA tracking mechanism exists in this schema. |
| Downstream integrations (HRMS, payroll, calendar, email, e-signature, background-verification, IT provisioning) | Not present | No adapter code, no MCP tool servers. integration.* tables (outbox, external-system mapping) exist, unused. |
| RAG (policy retrieval) | Not present | HrAutomation.Rag project is an empty scaffold. No ingestion pipeline, no retrieval, no vector store integration. ai.Rag* tables exist, unused. |
| MCP tool servers | Not present | HrAutomation.Mcp project is an empty scaffold. mcp/ folder holds only manifests/schemas as documentation, not a running server. |
Database (HrAutomationDb) |
Implemented | SQL Server, database-first, ~243 tables across 11 schemas, RLS via SESSION_CONTEXT, ~30+ stored procedures, triggers, functions, views, seeded reference/demo data. Deployed and verified against LocalDB this session. Far ahead of the application code that uses it — most tables have no C# code wired to them yet. |
| Authentication | Partially implemented | Dev-only symmetric-key JWT via /api/v1/dev/token, gated to Development environment. Row-level security enforced via TenantSessionContextMiddleware setting SESSION_CONTEXT('TenantId') per request. No production OIDC/OAuth 2.1 provider — oidcAuthProvider.ts (frontend) is an intentional stub that throws. |
Frontend (HrAutomation.Web) |
Mostly frontend-connected — TAN, CV Bank/candidates, Interviews, Offers, Green Form, Verification, Discrepancies, Employee Conversion, Approvals, and Dashboard all real | Real React/TypeScript app, builds and runs (Vite), typechecks and lints clean. MSW is now off by default (VITE_ENABLE_MSW=false); VITE_AUTH_MODE=devToken (new) obtains a real signed JWT from /api/v1/dev/token and every subsequent call hits the real API. Verified via a real headless-browser run: login → TAN list → create TAN (real POST, server-generated tan_id/TAN number, re-fetched detail page) → approve TAN → CV Bank list/upload, all against the real database, cross-checked with a direct SQL query; and via live curl smoke tests confirming HTTP 200 from /v1/interviews, /v1/offers, /v1/discrepancies, /v1/verification, /v1/employee-conversion, /v1/approvals, /v1/dashboard/summary against a running instance. A dev-only status strip shows API reachability/MSW state/auth state. ErrorState/DataTable now show kind-specific messages (401/403/404/409/422/429/5xx/network) app-wide instead of one generic “Something went wrong” string; a RouteErrorBoundary and wildcard NotFoundPage route now exist. Only Audit still shows MSW/mock data (not attempted this pass); admin user create/edit/activate/deactivate/role-assignment is not wired (see gap-analysis doc). MSW handlers/fixtures are retained for Vitest only; nothing in the normal runtime path imports them. |
| Testing | Partially implemented | tests/HrAutomation.Tests: 72 xUnit tests, all passing, run against a real HrAutomationDb instance (not mocked) — covers CV upload, TAN create/approve/reject/wrong-role flows, health/current-user/TAN-list, admin effective-permissions/user-list/detail/tenant-isolation, Interview/Offer/Green Form/Discrepancy/Verification/Employee-conversion/Approvals/Dashboard suites, and the new AdminConfigurationTests (13 tests — tenant-profile update/concurrency-conflict/wrong-role, numbering-rule update/invalid-padding, approval-matrix-rule add/delete/floor-of-one-mandatory-step-enforced/wrong-role, workflow-definitions read). Frontend: 40 Vitest tests passing (npm run test), typecheck/lint clean. No contract, e2e (Playwright test:e2e exists but unverified this pass — browser binaries not installed for the e2e project, though Playwright was used ad hoc for live UI verification screenshots this pass), performance, or security test run confirmed. No test isolation between runs beyond manual/idempotency-aware test design (see DEC-007). |
Config-first (config/) |
Scaffolded; runtime config now admin-editable via HrAutomationDb, not config/ |
Schemas and defaults in config/ exist and pass syntax validation (scripts/validate-config.sh) but are still not wired to any running application code — this remains a real, unreconciled duplication (DEC-008). What changed: ref.NumberingRule (TAN/EmployeeId/OfferNumber prefix/suffix/padding) and ref.ApprovalMatrix/ApprovalMatrixRule (real approval-step counts) are now genuinely read AND admin-editable via /api/v1/admin/* (AdminConfigurationController) and the /admin/configuration UI — previously seed-data-only, now a real, audited, optimistic-concurrency-safe write path. org.Tenant profile (name/legal name/domain) is also now admin-editable the same way. ref.WorkflowDefinition/WorkflowStateDefinition/WorkflowTransitionDefinition are exposed read-only only — confirmed by inspection that no stored procedure consults them (ADR-006: real transitions are hardcoded per-procedure), so no edit UI was built for them (would be a fake control). |
API contract (openapi/) |
Partially implemented | openapi/hr-onboarding-api.openapi.yaml validates (npm run api:validate, 29 paths). Response DTO shapes for the implemented endpoints are already string-based (not enum-coupled), so they did not need to change during this session’s database migration. Not confirmed to be 100% in sync with every implemented endpoint’s actual behavior (e.g. GET /api/v1/workflows/{id} semantics changed — see ADR-006). |
| CI/CD | Scaffolded | .github/workflows/ci.yml, docs-validation.yml, security-scan.yml exist; none confirmed to run green against the current solution. |
docs/, config/, openapi/ in part) was generated independently of, and before, the real database/backend build-out. Several documents described a different data model, a different engine (Postgres as an option), and a generic workflow-engine table that was never built. Reconciliation notices have been added to the highest-drift documents (see git history / CHANGELOG for this pass) but not every document under docs/ has been individually re-verified against the real system — treat any document without an “Implementation reality notice” as unverified, not necessarily accurate.config/ and the database’s own seed data (src/HrAutomation.Infrastructure/Database/seed/) both claim to own tenant/role/approval-matrix configuration, with no reconciliation between them (DEC-008).DELETE script, not automated — a shared dev database will accumulate stray rows without discipline (DEC-007).See DECISIONS_REQUIRED.md for the full register with owners and defaults. Highlights: JD/TAN structured fields (Location/Grade/Budget) have no schema home yet (DEC-004); config/ vs. database-seed-data ownership of tenant configuration is unresolved (DEC-008); production auth strategy (OIDC vs. BFF) is unresolved (DEC-002); whether/when to build a generic workflow-transition table vs. continuing the per-entity-procedure pattern (DEC-001, tracked from ADR-006).
net10.0, pinned via global.json), Node.js 24+ (src/HrAutomation.Web/.nvmrc) for local development.DECISIONS_REQUIRED.md.Every item in DECISIONS_REQUIRED.md marked HR/Legal/Security/DBA review is outstanding. No formal review of this repository has been recorded — Last reviewed dates across docs/ reflect authoring/reconciliation passes, not specialist sign-off.
CreateTanRequest’s Location/Grade/Budget fields have a real column to land in.dotnet test runs against the shared LocalDB instance leave stray “Jane Doe”/”Senior Backend Engineer”/”QA Engineer” rows in the demo tenant that then show up in the real UI’s TAN/CV Bank lists. Manual cleanup was required twice during this pass.GET /api/v1/dashboard/summary endpoint so DashboardPage can show real counts instead of its current honest “not available” state.HrAutomationDb schema to replace the superseded originals under docs/03-data/.devToken mode is explicitly dev-only.| Version | Date | Author | Change |
|---|---|---|---|
| 1.0 | 2026-09-08 | Documentation reconciliation pass | Initial creation |
| 1.1 | 2026-09-09 | Platform upgrade — Phase 2/3 (Claude Code) | Updated stated local-dev prerequisite from .NET 8 SDK/Node 20+ to .NET 10 SDK/Node 24+; see platform-upgrade-gap-analysis.md |
| 1.2 | 2026-09-09 | Navigation/feature reliability pass (Claude Code) | Moved Shortlist approval, Interview scheduling/feedback, Offers, Green Form (candidate flow), Verification (read), Discrepancy management, and Employee conversion from Scaffolded to Implemented/frontend-connected; added Verification queue and Approvals work queue rows; corrected Frontend and Testing rows. match_candidates_skill, document_verify_skill, progression_approval_skill, and admin user CRUD remain Scaffolded/unbuilt. See navigation-feature-gap-analysis.md and production-readiness-report.md |
| 1.3 | 2026-09-10 | Admin configuration feature (Claude Code) | Implemented the /admin/configuration page for real: tenant profile, document-numbering rules, and approval-matrix rules are now genuinely read/write against HrAutomationDb (new AdminConfigurationController, 5 new stored procedures, optimistic concurrency, audit logging, tenant.manage/configuration.manage/workflow.manage permission gating) — previously an EmptyState placeholder. Workflow definitions are exposed read-only by design (confirmed no stored procedure consults them — ADR-006). Also fixed a pre-existing, unrelated bug found during verification: the client-side usePermissions() UX map granted PLATFORM_ADMIN zero permissions, including not admin.access, hiding the Administration nav entirely from the one role with real tenant.manage access. 13 new backend tests (72 total). Updated openapi/hr-onboarding-api.openapi.yaml (9 new paths) and the Config-first/Testing capability rows. |