Files
jobtrackingapp/docs/release-checklist.md
cesnimda ce76046a29 feat: complete release readiness work
- 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
2026-07-31 16:54:16 +02:00

6.0 KiB

Release checklist

Historical snapshot (2026-07-19). The current release gate and external dependencies are documented in .gitea/workflows/ci-deploy.yml, docs/implementation-roadmap.md, and BLOCKERS.md.

2026-07-19. State of the build before the first production deployment after the Phase 4/5 architecture changes. Companion to deploy/first-production-deployment.md (how to deploy), docs/production-readiness-review.md (what was audited) and docs/phase-5-completion-report.md (what was built and why).

Completed architecture work

Area State
Career Profile Single source of truth. Relational children with stable item keys; ProfileJson is a derived projection. Nothing downstream writes to it.
CV Builder Data-driven theme engine — themes are data, not hardcoded templates. Eight themes; adding one is appending an object. Variants are lenses over the master profile, never copies. Autosave, version history, restore.
Public CV /cv/{slug}, off by default, per-variant opt-in, unguessable slug, noindex. SPA deep-link routing fixed so direct navigation, refresh and shared links resolve.
Application Workspace /applications/{id}: Overview, Checklist, Timeline, Analysis, Match, CV, Cover Letter, Documents, Interview Prep, Communication. Owns almost no data — it composes systems that already existed.
AI integrations Suggestion-only. Every generation appends to AiInteraction (that history is the versioning). Interview generation now receives the analysis and match context. Nothing AI-driven writes to the profile, a variant, a cover letter or prep content without the user saving it.
Interview preparation User-owned InterviewPrepItem, distinct from the two AI caches that regenerate. Six categories in one table.
Production hardening Explicit [Authorize] on every controller with a reflection test; clean MariaDB initialization; database ownership documented; pre-deploy backups; restore and rollback docs; health checks on backend and frontend.

Local verification

Check Result
Backend tests (Release) 393 passed
Frontend tests 128 passed, 36 suites
TypeScript clean
Frontend production build clean
Docker images backend and frontend build
Fresh empty MariaDB 11 42 tables, starts healthy
Populated MariaDB restart idempotent, rows preserved
Existing SQLite dev database upgraded 34 → 44 tables, rows preserved
Backup and restore dump verified, restored into a clean database, rows identical

Unresolved external blocker

CI runner — deployment is gated on this

CI is red for a reason proven to be outside the repository: commit 8f73548 changed a single 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.

Consequence: everything above is local verification. Nothing has been proven in CI, and the deployment pipeline will not promote a build until this is fixed.

Blocked on, and needing you:

  • Job logs — the API returns 401 unauthenticated; a read-scoped Gitea token would unblock this
  • journalctl -u act_runner --since '2 hours ago' on the runner host
  • The act_runner container/service configuration

Full evidence: docs/infrastructure/runner-investigation.md.

Known risks

Risk Severity Notes
Production is many commits behind High The first deploy applies the whole Phase 4/5 change at once. Reconciler builds ~12 tables against real data for the first time.
Reconciler path unproven on your data Medium Verified on containers and on a real SQLite dev database — not on production. This is what the backup is for.
Production journey unwalked Medium The CI-gated local browser suite covers login, saved-job creation, Career Workspace, and public CV/PDF. The same authenticated and OAuth checks still require production credentials.
Rollback loses new-table data Medium A code rollback keeps everything. A database restore discards rows created since the dump. Keep the deploy window quiet.
Logging is structured JSON on stdout Low Compose retains three 10 MB files per service; aggregation remains deployment-owned.
No global exception handler Low Unhandled errors return a bare 500 with no correlation id. No stack leaks outside Development.
ClientErrorsController is anonymous Low By design — browser error reports must work before sign-in. Size-limited and truncated; stores nothing.
Prompt quality unmeasured Low Interview generation gets richer context; whether the questions are better is a judgement no test makes.

First deployment warnings

  1. Take a manual backup as well, and restore it once to a scratch database. deploy.sh backs up automatically, but the first deploy is the wrong time to discover a backup does not restore.
  2. Watch docker compose logs -f backend live. Database.Migrate() throws on failure, so a schema problem exits the container. The runbook lists the exact log lines that mean stop.
  3. DATABASE_PROVIDER defaults to sqlite. If it goes missing from .env, the backend does not quietly serve an empty database — it crashes with no such table: INFORMATION_SCHEMA.TABLES. Loud, but confusing if you have not seen it before.
  4. The connection string resolves from inside the container. Server=127.0.0.1 means the backend container, not the host.
  5. Empty new sections are expected. Existing applications will show an empty Checklist, Timeline and Interview Prep until used. The checklist seeds on first open. That is correct behaviour.
  6. Verify row counts before walking away. Applications and companies must match the pre-deploy numbers. This is the check that matters most.
  7. Do not restore the database for a code problem. Roll the code back first; it is almost always the whole fix, and it costs no data.