127 lines
16 KiB
Markdown
127 lines
16 KiB
Markdown
# International Job Discovery Implementation Plan
|
||
|
||
> **For agentic workers:** Use superpowers:executing-plans to implement this plan task-by-task only after explicit stage authorization. Delegation is not required by this plan. Do not start implementation from this document alone.
|
||
|
||
**Goal:** Deliver cross-industry, multilingual NO/SE/GB discovery while preserving Jobjakt's current data and document owners.
|
||
|
||
**Architecture:** Extend existing React/MUI discovery, .NET services and MariaDB. Use a source-rights registry, NAV local index, eligible live-query adapters and honest external/manual fallbacks; no generative model dependency.
|
||
|
||
**Tech stack:** React 19/Next.js static shell, TypeScript, MUI, ASP.NET/EF Core 9, MariaDB, existing background services and test tools.
|
||
|
||
**Spec:** [Master](../../planning/global-jobs-prompts/00_MASTER_ORCHESTRATOR.md), separate GJ-01–08 prompts, [architecture/contracts](ARCHITECTURE_ADRS.md), [acceptance](ACCEPTANCE_MATRIX.md).
|
||
|
||
## Global constraints
|
||
|
||
- Planning only now; Phase 10, PRODUCT-REVIEW-01, initiative approval and next-stage instruction precede implementation.
|
||
- No main merge, production operation, destructive data change, restricted scraping, consumer registration or paid-resource purchase.
|
||
- Preserve Career Profile, CV Import/Editor, applications, frozen packages, revisions, histories, all 57 groups/33 routes and MariaDB compatibility.
|
||
- Never enable live AI without independent qualification; core suggestions/matching are deterministic.
|
||
- Each stage stops after acceptance and publication. Later source branch includes accepted predecessors. Stage prompts override this proposed file breakdown if they conflict.
|
||
- Run `graphify query` before code exploration, trace every shared caller before changes, and `graphify update .` after code changes (no update needed for this docs-only stage).
|
||
|
||
## Review focus
|
||
|
||
1. Legacy FINN URL preview reaches HTML fetch before plugin selection: GJ04-F must deny it before network while keeping reviewed manual entry usable.
|
||
2. A withdrawn source moves location or loses fields: GJ04-G must remove it independent of old/new query filters and surviving duplicate sources.
|
||
3. Parent remains checked after town refinement: GJ02-C/D must query only refinements and roundtrip exact scope state.
|
||
4. Search edited/deleted while worker is sending: GJ06-B must recheck revision/owner/status and suppress obsolete delivery, including restart.
|
||
5. Source-content purge and user application evidence differ: GJ06-D/GJ07-C must enforce approved retention without deleting user notes or silently changing frozen history.
|
||
|
||
## Execution and evidence pattern
|
||
|
||
For each checkpoint below: record exact source/callers and baseline → add the specified failing contract/scenario test → run and preserve RED → make smallest complete change → run focused checks then required stage regressions → review diff/security/rights → document measured outcomes, commit/push only its authorized branch and verify remote HEAD → stop at stage end. Do not count focused reruns twice. New fixtures are synthetic or lawfully retained/redacted; no private CVs or credentials in evidence.
|
||
|
||
Representative existing commands (verify local SDK/package scripts/config at entry): `rtk dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter FullyQualifiedName~JobDiscovery`, `rtk npm test -- --runInBand job-discovery.test.tsx` from UI, and configured Playwright discovery specs. These are planned commands, not executed results. MariaDB uses the repository's existing isolated integration lane after its entry check; do not start Docker/DB or tests with unknown live service targets. TypeScript/lint/build and ordinary/separate browser lanes use current package scripts and explicit configs, serialized with PDF/build lanes. Capture native exits and discovery membership.
|
||
|
||
## GJ-01 — preferences and languages
|
||
|
||
Branch `feature/gj01-country-language-preferences`; authority [GJ-01](../../planning/global-jobs-prompts/02_GJ_01_PREFERENCES_I18N.md). Entry: all global gates above. Produces `SearchPreference` contract and persisted locale/default semantics from ADR-03.
|
||
|
||
Files: extend `JobTrackerApi/Models/ApplicationUser.cs`, `Controllers/AuthController.cs`, `Data/JobTrackerContext.cs`; proposed `Models/SearchPreference.cs`, preference controller/service and additive migration. Extend `job-tracker-ui/src/components/SettingsView.tsx`, `views/JobDiscoveryPage.tsx`, `i18n/I18nProvider.tsx`, `i18n/translations.ts`, `api.ts`/types where existing ownership requires. Test proposed `SearchPreferenceTests.cs`, UI preference/locale tests, existing settings e2e and account export/deletion contracts.
|
||
|
||
- [ ] GJ01-A: freeze DTO/schema validation, expected revision and empty legacy defaults; RED cross-owner/missing-owner/concurrent-write/old-account tests, then additive persistence. No schema deletions.
|
||
- [ ] GJ01-B: first-run/Settings defaults and optional residence distinct from work countries; test cross-device recovery and no nationality collection.
|
||
- [ ] GJ01-C: locale registry/native labels/prominence/Coming Later/fallback; complete required translations and missing-key checks; native language review stays separate.
|
||
- [ ] GJ01-D: initialize draft query from defaults, explicit Update defaults/save named profile boundary; test temporary overrides never persist accidentally. Named rich saved-search lifecycle is completed in GJ-06 using same query contract.
|
||
- [ ] GJ01-E: tenant/export/delete/revision/EN-Bokmål regressions and approved shipped-locale proof. Publish `docs/global-jobs/01/` acceptance and stop. Unqualified Swedish UI journey remains blocked, not silently counted passing.
|
||
|
||
## GJ-02 — geography
|
||
|
||
Branch `feature/gj02-geographic-search`; [GJ-02](../../planning/global-jobs-prompts/03_GJ_02_GEOGRAPHY.md). Consumes preferences; produces `LocationNode`, `LocationScope`, `LocationQueryPlan` contracts from ADR-03/geography decisions.
|
||
|
||
Files: proposed `Models/DiscoveryLocation.cs`, `Services/Discovery/LocationScopeCompiler.cs`, versioned reference importer under existing tooling conventions, `Controllers/DiscoveryLocationsController.cs`, EF mapping/migration, `src/components/discovery/LocationScopePicker.tsx` and shared discovery query types. Test `LocationScopeCompilerTests.cs`, MariaDB mapping/index tests, UI scope transitions, browser discovery specs.
|
||
|
||
- [ ] GJ02-A: obtain exact licensed snapshots/IDs/geometries, especially Warwick **town** versus district and overlapping SE/NO settlements. Block precise town query if no qualified footprint exists.
|
||
- [ ] GJ02-B: idempotent versioned import + typed relations/provider mapping + stale-ID review; tests prove no orphan/broadened saved scopes and no partial dataset swap.
|
||
- [ ] GJ02-C: implement scope/refinement controls; test every geography decision-table transition, collapse/save/reload and accessible announcements.
|
||
- [ ] GJ02-D: compile exact IDs/polygons, radius/multi-origin/remote rules; assert county-only job fails strict town/radius filter and out-of-parent radius result is disclosed.
|
||
- [ ] GJ02-E: measured lookup/query/load, keyboard/mobile/zoom tests and edition/licence ledger. Publish `docs/global-jobs/02/` and stop.
|
||
|
||
## GJ-03 — occupations
|
||
|
||
Branch `feature/gj03-occupation-search`; [GJ-03](../../planning/global-jobs-prompts/04_GJ_03_OCCUPATIONS.md). Consumes query/scopes; produces `OccupationConcept`/`OccupationQueryPlan`, without AI.
|
||
|
||
Files: proposed `Models/DiscoveryOccupation.cs`, `Services/Discovery/OccupationQueryCompiler.cs`, versioned dataset importer, `Controllers/DiscoveryOccupationsController.cs`, `src/components/discovery/OccupationAutocomplete.tsx`; integrate `JobDiscoveryPage`, preserve `JobImport/SkillTagger.cs` callers. Test taxonomy/alias/compiler suites and browser combobox fixtures.
|
||
|
||
- [ ] GJ03-A: licence/checksum/version manifest for ESCO/SSYK/SOC, reviewed aliases/crosswalk relation types and frozen ten-sector/three-language benchmark; approve metrics before measuring.
|
||
- [ ] GJ03-B/C: concept lookup and accessible cancellable typeahead; test stale response, same word/different language, deprecated ID and taxonomy outage.
|
||
- [ ] GJ03-D: exact/related/keyword modes, optional skill chips and provider mapping; test “weekend work”, free text and no qualification inference.
|
||
- [ ] GJ03-E: report per-sector/language precision/recall and p95/index size; publish `docs/global-jobs/03/` and stop.
|
||
|
||
## GJ-04 — providers and inventory
|
||
|
||
Branch `feature/gj04-provider-connectors`; [GJ-04](../../planning/global-jobs-prompts/05_GJ_04_PROVIDERS.md). Consumes canonical query/location/occupation plans; produces source-aware `DiscoveryPageV1` and permitted detail/lifecycle data.
|
||
|
||
Files: existing `Controllers/JobDiscoveryController.cs`, `Services/JobDiscoverySearchService.cs`, `Services/JobImport/JobImportService.cs`, `Plugins/FinnPlugin.cs`; proposed `Services/Discovery/ProviderRightsPolicy.cs`, `DiscoveryQueryService.cs`, `Providers/` capability-specific adapters, inventory/cursor models, EF mappings/migrations and DI registration at current host owner. Tests extend `JobDiscoveryControllerTests`, `JobImportServiceTests`, transport tests and proposed provider/lifecycle/dedupe MariaDB tests.
|
||
|
||
- [ ] GJ04-A: reverify matrix, exact API schemas/grants, numeric cache/quota/lag policy and credential references; unknown capability denied. Test every denied mode and rights expiry/revocation before adapter work.
|
||
- [ ] GJ04-B: inventory/event/cursor transaction, replay/out-of-order/crash/bootstrap/backoff tests; additive migration and search compatibility adapter.
|
||
- [ ] GJ04-C: NAV ingestion only with consumer authorization; fixtures can proceed independently, clearly labelled. Test removal/masking independent of area and no public-token production fallback.
|
||
- [ ] GJ04-D: SE JobSearch and truncated JobAd Links, exact taxonomy IDs and bounded windows; original-link/completeness/404/429 tests. JobStream only after separately qualified need.
|
||
- [ ] GJ04-E: licensed GB adapter or explicit external-link-only state; do not mark a fixture connector live. No full Adzuna body reconstruction.
|
||
- [ ] GJ04-F: FINN URL recognition/bookmark/manual workflow with zero network fetch through both new and existing import entry points; test malicious hosts, redirects and approved-source exceptions.
|
||
- [ ] GJ04-G: conservative dedupe, source loss and field-rights lineage, SSRF/XSS/rate/rights tests; separately bounded approved live contracts. Publish `docs/global-jobs/04/` with provider-by-provider decisions and stop.
|
||
|
||
## GJ-05 — unified discovery UI
|
||
|
||
Branch `feature/gj05-discovery-ui`; [GJ-05](../../planning/global-jobs-prompts/06_GJ_05_SEARCH_UI.md). Consumes page/detail contracts; produces desktop/mobile results/detail and existing workflow actions.
|
||
|
||
Files: extend `views/JobDiscoveryPage.tsx`, `App.tsx` routes, existing `PageLayout`/theme/API/types; proposed `components/discovery/JobDetailPanel.tsx` and filter/list pieces only when independently needed. Reuse existing job dialog/actions. Extend `src/job-discovery.test.tsx`, `e2e/job-discovery.spec.ts`, `e2e/phase7-discovery.spec.ts`.
|
||
|
||
- [ ] GJ05-A/B: filter rail/list/detail with independent partial-source/loading/error/empty states; tests preserve draft filters, deep link/history and return focus.
|
||
- [ ] GJ05-C: results/query semantics, sorting/pagination, exact-filter coverage and conservative alternate-source grouping; test stale/partial-source results and missing fields.
|
||
- [ ] GJ05-D: render full/truncated/link-only details with safe HTML/plain-text fallback, salary period/prediction/unknown fields and provenance. Assert blocked content never appears in DOM/API/export.
|
||
- [ ] GJ05-E: reviewed Save/Track/original-apply actions and native CV/letter document links, with AI gated.
|
||
- [ ] GJ05-F: phone full-screen details, tablet/desktop, EN/Bokmål and qualified locales, keyboard/zoom/themes, accessibility/fidelity/regression. Publish `docs/global-jobs/05/`, exact visual/axe/manual limits and stop.
|
||
|
||
## GJ-06 — searches, alerts and matches
|
||
|
||
Branch `feature/gj06-alerts-matching`; [GJ-06](../../planning/global-jobs-prompts/07_GJ_06_ALERTS_MATCHING.md). Consumes permitted inventory/query and existing profile/application owners; produces revision-safe `SavedSearch`, `SavedJob`, `SearchAlert` behavior.
|
||
|
||
Files: extend `Models/SavedJobSearch.cs`, `Services/SavedJobSearchRunner.cs`, `SavedJobSearchAlertHostedService.cs`, `Controllers/JobDiscoveryController.cs`, owned export/deletion services; proposed bookmark/subscription/delivery models and deterministic match service; UI search profile/notification/actions; no parallel application/profile writer. Tests `SavedJobSearchAlertTests`, `SavedJobSearchSqliteTests`, account export/deletion, MariaDB races and browser tracking/documents.
|
||
|
||
- [ ] GJ06-A: versioned full queries, duplicate/edit/delete/suspend/restore and legacy bare-NAV-ID migration; test revision conflict and exact scope roundtrip.
|
||
- [ ] GJ06-B: durable delivery/initial baseline/frequency/DST/pause/unsubscribe/deduplication; test duplicate workers, crash, changed/deleted search, withdrawn job before dispatch, source stale, overlapping searches and ambiguous mail send failure.
|
||
- [ ] GJ06-C: evidence-backed occupation/skills/qualification explanations; tests include nurse licence absent, cleaner empty profile, language unknown and profile revision change.
|
||
- [ ] GJ06-D: Save distinct from Track, idempotent tracked link, CV/letter native workflow; assert no canonical/CV/frozen-package mutation from source update and approved purge policy behavior.
|
||
- [ ] GJ06-E: security/fanout/accessibility/retention checks; publish `docs/global-jobs/06/` and stop.
|
||
|
||
## GJ-07 — admin and resilience
|
||
|
||
Branch `feature/gj07-discovery-operations`; [GJ-07](../../planning/global-jobs-prompts/08_GJ_07_ADMIN_SECURITY.md). Consumes feed/alert state; produces admin health, redacted correlation diagnostics and incident/rollback runbooks.
|
||
|
||
Files: extend `Controllers/AdminSystemController.cs`, `src/views/AdminSystemPage.tsx`, existing operation response/presentation contracts where appropriate; proposed system source-sync state/log projection (not synthetic tenant user), rights/retention controls. Tests admin API/UI, provider failure drills, export/delete/restore and resource limits.
|
||
|
||
- [ ] GJ07-A/B: provider rights/access/health/quota/lag/cursor/backlog/count metrics and paginated correlation events; distinguish actual system sync from real submitting user.
|
||
- [ ] GJ07-C: Admin check on every list/detail/stream/export; assert tenant/non-admin denial, redaction, no unsafe HTML/PII/raw Docker logs, retained source purge and account erasure.
|
||
- [ ] GJ07-D: provider loss, invalid schema, repeated cursor, credential revocation, index outage, lease expiry, retry storm and bounded load; verify backup/restore suppression and additive rollback. No production maintenance dispatch. Publish `docs/global-jobs/07/` and stop.
|
||
|
||
## GJ-08 — integrated acceptance
|
||
|
||
Branch `test/gj08-global-discovery-acceptance`; [GJ-08](../../planning/global-jobs-prompts/09_GJ_08_ACCEPTANCE.md). Entry: actual GJ-01–07 accepted ancestry, rights scope and source freeze, not sum of stage test counts.
|
||
|
||
- [ ] GJ08-A: freeze integrated source/config/fixture/dataset/licence identity and discover exact full-suite membership; reconcile blocked capabilities.
|
||
- [ ] GJ08-B: run all matrix journeys, backend/frontend/browser/contracts/actual MariaDB/model-disabled sidecar where applicable, TypeScript/lint/build and unchanged preservation/PDF oracles. Serialize build/browser/PDF lanes; retain native exits and failures.
|
||
- [ ] GJ08-C: authorised provider smoke separately from fixtures; per-country/source/content/language/geographic coverage report. Complete human/device/native-language/AT/print checks only with real evidence, otherwise OPEN.
|
||
- [ ] GJ08-D: source lifecycle/security/load/rollback/release gate review; publish `docs/global-jobs/08/`, per-stage/provider development decisions and production NO-GO unless separately authorized and qualified. Commit/push and stop; never deploy or merge main.
|