Every JobApplication gets a dedicated workspace at /applications/{id} — a
surface, not a new data store. It owns no data and duplicates none: CV comes
from the Phase 4 CvVariant lens, analysis/match/interview from the existing
AiWorkspacePanel, documents from Attachments, communication from
Correspondence, activity from JobEvent, stage semantics from JobPipeline. No
career data is copied and nothing here writes.
- GET /api/jobapplications/{id}/workspace: one aggregate read (role, company,
stage, dates, attached CV variant, cover letter, documents, AI history,
recent activity) replacing the page fanning out across endpoints
- Next recommended action: ordered rules answering "what do I do next?", the
core product principle for this phase
- ApplicationWorkspacePage: left nav + linkable ?section=, reusing the existing
component for each domain; later-milestone sections say so rather than faking
- Entry point from the job dialog via an optional onOpenWorkspace callback —
the dialog must not depend on router context (it is mounted without a
<Router> in several suites), so the caller owns navigation
- 8 backend tests (aggregate, CV variant surfacing, counts, activity ordering,
next-step rules, tenant scoping)
Local: 314 backend, 88/88 frontend (31 suites), tsc clean, production build ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two post-reboot runs: the first reproduced the failure identically (smoke 1s
pass, suite 3s fail), the second failed earlier at `dotnet restore` in 0s — a
step that succeeded in 3-4s on every previous run, same commit, same runner.
That rules out stuck state (reboot changed nothing) and rules out a
deterministic sandbox policy such as seccomp/W^X blocking runtime IL emission,
which was the leading remaining hypothesis. Combined with host telemetry
showing no disk/memory/PID pressure, confidence in any specific mechanism drops
to ~25%; confidence that application code is not the cause stays high.
Removes the pure-vs-Moq diagnostic scaffolding (it never executed). No test
skipped or weakened.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Server shows 62G free (71% used), inodes 14%, 16G /dev/shm (~32G RAM), ulimit
-u 127749, no cgroup pids.max, and no fail2ban installed. That falsifies both
resource-exhaustion hypotheses at the host level and the fail2ban explanation
for the deploy failure.
Notes the caveat that Gitea act_runner usually runs jobs inside a Docker
container, so host figures do not describe the environment the tests ran in
(separate cgroup limits, and a 64MB /dev/shm by Docker default).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Moves the report to docs/infrastructure/runner-investigation.md with the
requested structure (evidence, experiments, hypothesis, confidence, required
infrastructure changes, why application code is no longer suspected).
Decisive new experiment: the suite was run from a clean `git archive HEAD`
tree — byte-identical to CI's checkout, without the gitignored runtime dirs
(jobtracker.db, keys/, CvArtifacts/, backups/) that earlier local runs had
silently included. 10/10 pass in 1s. That removes the last difference between
the local tree and the runner, eliminating application code (~95% confidence).
Also establishes, by route probe, that production is healthy but stale:
/api/public-cv/{unknown} returns 404 locally (route exists, AllowAnonymous) but
401 on prod, same as a nonsense path — PublicCvController is absent, so Phase 4
and Phase 5 have never deployed. Production therefore never ran the faulty
migration: no half-built tables exist there and no data cleanup is needed.
Deploy is a second, separate infrastructure failure: the first attempt reached
deploy.sh (37s, consistent with the MariaDB crash since fixed), every attempt
since dies at 3s at SSH connection time while the host serves traffic normally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bisected the failure across four CI runs (job logs are not readable via the
Gitea API) down to the AiWorkspace test classes — 10 tests that pass on
Windows, in a clean Linux container, under a 1GB memory cap, in CI's exact
step order, with a custom-dir SDK and no DOTNET_ROOT, serially, and under a
hostile locale/timezone.
Ruled out: Linux behaviour, case sensitivity, path separators, locale/culture,
time zone, environment variables, parallel execution, test ordering, shared
state, memory. Not testable remotely: host permissions/limits.
Assessment is environmental: the workflow already documents three failure
modes on this same runner with an identical signature (processes dying with no
error output — SDK cache corruption, npm ci SIGSEGV, CRA build OOM/SIGSEGV).
Report includes evidence table and recommended infrastructure fix.
Removes the temporary bisection scaffolding; keeps the restore/build/test split
and the host smoke. No test was weakened, skipped, or filtered.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ai-career-assistant.md (modules, prompt flow, provider abstraction, append-only
history model, extension points, security). Master guide + roadmap Phase 5
updated with the shipped workspace and the open provider-selection extension.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cv-builder.md (variant model, rendering pipeline, API, builder workflow,
extension points, known deep-link limitation) + cv-theme-engine.md (how a
theme is data and how to add one). Roadmap Phase 4 marked foundation-shipped
with the remaining polish itemised.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 3 foundation: entities, relationships, ownership, source-of-truth, and
snapshot rules for the structured career profile. Relational children
(Experience/Education/Skill/Project/Certification/Language) under CareerProfile;
long tail as JSON; blob (ProfileCvStructureJson) becomes a derived projection for
legacy read paths; lazy non-destructive backfill.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add section 4a to docs/architecture/current.md: request flow, data ownership,
API responsibilities, and future extension points for the /profile vs /career
separation completed in Phase 2/2.2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bring the four Career Workspace documents onto main as the target architecture
for Phases 2-4, and point MASTER_IMPLEMENTATION_GUIDE.md at them. Taken from the
branch tip (later commits refined them). Pure additions — none previously existed
on main.
- cv-builder-competitor-deep-research.md (Novoresume, Reactive Resume, FlowCV,
Teal, Enhancv, Canva, Resume.io, Kickresume; matrix; pricing intelligence).
- cv-builder-product-teardown.md
- career-workspace-product-strategy.md
- career-workspace-implementation-roadmap.md (F0-F5)
MASTER_IMPLEMENTATION_GUIDE.md v1.1: adds a Source-Of-Truth Documents section and
restates the "profile is the source of truth; documents reference snapshots" rule.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Active docs/ was stub scaffolding while the real docs sat in docs/_archive/.
Restore and correct them, and record the Phase 0 work.
- docs/architecture/current.md: verified system map (from archived SYSTEM_OVERVIEW,
9 corrections against code).
- docs/research/competitors.md: sourced competitor analysis (from archived
PRODUCT_RESEARCH, feature matrix corrected).
- docs/decisions/ADR-002-job-application-model.md: the Job/JobApplication split.
- docs/application-discovery-report.md, docs/implementation-roadmap.md,
docs/phase-0-foundation-report.md, docs/career-workspace-branch-assessment.md.
- Remove 10 zero-byte placeholder files that advertised content that never existed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve conflicts from main's Wave 0 (PR #1) landing after this branch was cut:
- useViewResource.ts: main's e352aae already fixes the render loop the same way
(load in a ref, dropped from deps) — took main's canonical version. My
independent fix is superseded (my branch predated e352aae, which is why the
loop reproduced live).
- JobApplicationsController.cs: keep BOTH main's IJobCvMatchService and my
AnalyticsService (ctor gets both optional params). GetAnalyticsOverview stays
delegated to AnalyticsService.
- Fold main's H3 additions into the extracted AnalyticsService: pipeline-driven
funnel (JobPipeline.Normalize/Stages) + time-in-stage (StageAnalytics) and add
StageDurationDto + TimeInStage to Models/AnalyticsDtos.cs, preserving the API
contract the frontend expects.
Build clean; backend suite 135/135 green.
useViewResource built `reload` with `load` in its useCallback deps, and the
fetch effect depended on `reload`. Callers routinely pass an inline `load`
closure (e.g. JobTable), so `load` — and therefore `reload` and the effect —
changed every render, calling setState and re-rendering: an unbounded
"Maximum update depth exceeded" loop that froze the renderer on /jobs and every
other list view (DashboardView, RemindersView, CompaniesTable).
Fix: hold `load` in a ref (like the existing hasLoadedRef) and drop it from the
dependency arrays. Re-fetching is still driven by `deps`/`enabled`; the ref
always points at the latest closure. No API/behaviour change for callers.
Runtime-verified live: /jobs went from a render storm (frozen renderer, 100s of
console errors) to 0 errors in a 2s window and a clean render. Suites that drive
JobTable→useViewResource pass in isolation; the remaining full-run flakiness is
pre-existing (state-pollution/timing in the heavy RTL suites, unrelated).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The axios 401 interceptor calls clearAuthClientState() on every 401, which
dispatched "auth-changed"; the App handler re-fetched /auth/me, which 401'd
again → interceptor → clearAuthClientState() → "auth-changed" → ... an unbounded
request storm (observed live: 100+ GET /auth/me and climbing) that ran whenever
the user was logged out (login page, expired session) — burning CPU, network and
battery and flooding the server.
Fix: make clearAuthClientState idempotent — only emit "auth-changed" when it
actually removes a stored user key (a real signed-in→out transition), so
repeated 401s can no longer re-trigger the fetch.
Runtime-verified in a live stack: /auth/me went from 100+ & growing to 0 &
stable. login-page/settings tests green. Documented in
docs/performance/PERFORMANCE_IMPROVEMENTS.md (Phase 3.5 runtime finding).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Evidence-based investigation across every leak vector (timers, listeners, object
URLs, observers, websockets, static server collections, IMemoryCache, Python
caches). Verdict: no confirmed memory leak — the codebase has disciplined
cleanup. One resource-release correctness bug (over-eager blob-URL revocation in
the CV carousel) was found and fixed (eed9b1f).
Adds docs/performance/: MEMORY_LEAK_REPORT.md, ROOT_CAUSE_ANALYSIS.md,
PERFORMANCE_IMPROVEMENTS.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1-3 deliverables: full architecture/security/tech-debt map,
2026 market research with feature matrix, and tiered execution roadmap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>