ce76046a29
- consolidate API ownership and remove dead vendor code - add Stripe billing, learning paths, and public CV hardening - add migration, recovery, security, audit, and browser gates
152 lines
8.2 KiB
Markdown
152 lines
8.2 KiB
Markdown
# Phase 5 — Intelligent Application Workspace: completion report
|
|
|
|
> **Historical snapshot (2026-07-19).** Current release dependencies are tracked in `BLOCKERS.md`.
|
|
|
|
> 2026-07-19. What was built, what was decided, what is still risky, and what should come next.
|
|
> Companion to `docs/architecture/application-workspace.md` and
|
|
> `docs/infrastructure/database-ownership.md`.
|
|
|
|
## Core principle
|
|
|
|
*"The user should never ask: what should I do next?"*
|
|
|
|
The overview answers it from the checklist — the first pending item in category priority order. Not a
|
|
second ruleset, and not something that can recommend work the user already finished.
|
|
|
|
## What shipped
|
|
|
|
| Milestone | Delivered |
|
|
|---|---|
|
|
| 5.1 Foundation | `/applications/{id}` route, nav shell, one aggregate overview read, next recommended action |
|
|
| 5.2 Checklist | Persisted `ApplicationChecklistItem`, auto-completion from readiness signals, custom items, reordering, dismissal; **readiness refactored into a projection of it** |
|
|
| 5.3 Intelligence | Timeline interpretation over `JobEvent`, structured job analysis, career matching with evidence — all deterministic and read-only |
|
|
| 5.4 Assets | CV variant association, tailoring suggestions, cover letter workflow with version history, documents |
|
|
| 5.5 Interview & follow-up | User-owned `InterviewPrepItem`, follow-up over the existing `FollowUpAt` + checklist, five more timeline event types |
|
|
| Follow-up | Interview AI generation now receives the analysis and match context |
|
|
| Infrastructure | Clean MariaDB initialization fixed; database ownership model documented |
|
|
|
|
## Architecture decisions
|
|
|
|
**1. The checklist absorbed readiness rather than competing with it.**
|
|
`/readiness` keeps its DTO shape but now *projects* the checklist: score is completion percentage,
|
|
completed/missing are live items by status. One workflow surface, two views. The alternative — two
|
|
trackers kept in sync — was the thing the milestone brief explicitly forbade, and it would have
|
|
drifted within a release.
|
|
|
|
**2. Intelligence is deterministic; AI is a separate, explicit act.**
|
|
Analysis, matching, tailoring and timeline are computed from data the user already owns. The same
|
|
input always gives the same number, so the score is checkable. Opening a section costs nothing and
|
|
changes nothing. AI narrative stays in `AiWorkspaceService`, runs only when asked, and is versioned by
|
|
the append-only `AiInteraction` history.
|
|
|
|
**3. Suggestion-only is enforced by tests, not by convention.**
|
|
`Match_never_writes_to_the_career_profile`,
|
|
`Tailoring_never_writes_to_the_career_profile_or_the_variant`,
|
|
`Generating_ai_history_does_not_create_prep_items`,
|
|
`Existing_prep_items_are_never_overwritten_by_generation`.
|
|
|
|
**4. Two "interview prep" concepts exist, deliberately.**
|
|
`InterviewPrepNote` and `AiWorkspaceNote` are AI **caches** — they regenerate when their context
|
|
signature changes. `InterviewPrepItem` is the **user's** durable content. Discovering that the first
|
|
two would eventually overwrite anything a user typed is what forced the third. The DTO name
|
|
`InterviewPrepBoardDto` exists because the cache already owned `InterviewPrepDto`.
|
|
|
|
**5. Restore is always additive.**
|
|
Cover letter restore brings old text back as a *new* version, so what you restored from still exists.
|
|
Nothing in this phase destroys history.
|
|
|
|
**6. Reuse over new storage.**
|
|
Follow-up dates write `JobApplication.FollowUpAt`, the field `RulesEngine` and the reminder hosted
|
|
service already read — so reminders work with no new wiring. Follow-up tasks are checklist items.
|
|
Communication stayed entirely on `Correspondence`. Documents stayed on `Attachment`.
|
|
|
|
## Ownership verification
|
|
|
|
Audited mechanically across all six Phase 5 services:
|
|
|
|
| Service | SaveChanges | Reads profile | Writes profile | Writes variant content |
|
|
|---|---|---|---|---|
|
|
| `ApplicationWorkspaceService` | 0 | no | no | no |
|
|
| `ApplicationChecklistService` | 6 | yes | **no** | no |
|
|
| `ApplicationTimelineService` | 0 | no | no | no |
|
|
| `ApplicationIntelligenceService` | 0 | yes | **no** | no |
|
|
| `ApplicationAssetsService` | 3 | no | **no** | no |
|
|
| `InterviewPrepService` | 4 | no | **no** | no |
|
|
|
|
The rules hold:
|
|
|
|
- **`JobEvent`** — history source. Interpreted, never replaced, never written by the timeline service.
|
|
- **Checklist** — workflow guidance. Owns completion state only.
|
|
- **Readiness** — a projection of the checklist.
|
|
- **`CareerProfile`** — source of truth. Read by two services, written by none.
|
|
- **`CvVariant`** — derived output. The workspace moves a pointer; it never edits settings.
|
|
- **AI** — assistant only. Appends to `AiInteraction` and nothing else.
|
|
|
|
## Verification (local, 2026-07-19)
|
|
|
|
| Check | Result |
|
|
|---|---|
|
|
| Backend tests (Release) | 379 passed |
|
|
| Frontend tests | 128 passed, 36 suites |
|
|
| TypeScript | clean |
|
|
| Frontend production build | clean |
|
|
| Backend Docker image | built |
|
|
| Frontend Docker image | built |
|
|
| Fresh empty MariaDB 11 | 42 tables, starts, 0 exceptions |
|
|
| Existing populated MariaDB | 42 tables, starts, rows preserved |
|
|
| Existing SQLite dev DB | upgraded 34 → 44 tables, all rows preserved |
|
|
|
|
## Remaining risks
|
|
|
|
### 1. CI is red for an environmental reason — **external blocker**
|
|
|
|
Unresolved and outside the repository. The decisive evidence: run 531 on commit `8f73548` changed
|
|
**one markdown file** — no code, no test, no dependency, no workflow — and its test job failed in the
|
|
same duration band as every other run. A change that cannot affect compilation cannot fail a test job.
|
|
|
|
Everything above is therefore **local verification only**. Blocked on job logs (a read-scoped Gitea
|
|
token), `journalctl -u act_runner`, and the runner container config. See
|
|
`docs/infrastructure/runner-investigation.md`.
|
|
|
|
### 2. Production is behind — **deployment blocker**
|
|
|
|
Prod still runs the pre-Phase-4 build, because deployment is gated on CI. When it does deploy it will
|
|
apply several no-op migrations and let the reconciler build the new tables. That path is verified on
|
|
containers, **not on your data**. Take a database backup before the first deploy.
|
|
|
|
### 3. Authentication depends on a configuration flag — **defence-in-depth gap**
|
|
|
|
Authentication is enforced by a fallback policy that only activates when `Auth:Require=true`. It
|
|
defaults to `false`, and `docker-compose.yml` hardcodes `true`, so every compose deployment is
|
|
protected. But several core controllers — `JobApplicationsController`, `CompaniesController`,
|
|
`CorrespondenceController`, `RulesController`, `JobImportController` — carry **no explicit
|
|
`[Authorize]`**, so a deployment that lost that flag would expose them entirely.
|
|
|
|
Every Phase 5 controller has an explicit `[Authorize(AuthenticationSchemes = "local")]` and is safe
|
|
regardless. Adding explicit attributes to the older controllers is the right fix, but it changes local
|
|
development behaviour (where the flag is off), so it is flagged rather than applied unilaterally.
|
|
|
|
`PublicCvController` is intentionally anonymous — it serves `/cv/{slug}` only for variants the user
|
|
explicitly made public, and is `noindex` by default.
|
|
|
|
### 4. Prompt-quality is unmeasured
|
|
|
|
The interview module now receives the analysis and match context, and tests prove the context is
|
|
*present* and correctly scoped. Whether it produces better questions is a judgement no test here
|
|
makes.
|
|
|
|
## Future improvements
|
|
|
|
This dated list is superseded by the current roadmap and debt ledger. Authorization and lifecycle
|
|
event emission are implemented; the placeholder Portfolio/Notes workspace tabs were removed because
|
|
projects belong in public CVs and application notes remain in the existing application editor.
|
|
|
|
- **Resolve the runner**, then verify the full stack in CI and deploy. Everything else is downstream.
|
|
- ~~**Explicit `[Authorize]`** on the pre-Phase-5 controllers~~ — implemented.
|
|
- **AI cover-letter actions** — separate improve/shorten/expand actions remain intentionally unscoped;
|
|
the shipped flow generates by tone and keeps user editing/version history authoritative.
|
|
- ~~**Portfolio section**~~ — removed; public CV projects/custom sections cover the current scope.
|
|
- ~~**Emit the new lifecycle events**~~ — implemented through `JobLifecycleEvents`.
|
|
- **Checklist GET writes on read** (seeding + auto-completion sync). Correct and idempotent, but it
|
|
makes a GET non-cacheable; worth revisiting if the workspace ever needs to scale reads.
|