Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d3d3383764 | |||
| 1756273bc1 | |||
| 557efde073 | |||
| db818e5ebd | |||
| 4840870f0c | |||
| 6f29ed4cdb | |||
| 2a4acc179e |
-41
@@ -1,41 +0,0 @@
|
||||
# Jobjakt Security Policy
|
||||
|
||||
## System and scope
|
||||
|
||||
Jobjakt manages private career profiles, CVs, letters, applications, correspondence and related account data. Review the repository-owned React/Next static-export UI, ASP.NET Core API, EF data and background processing, Python AI/document sidecar, browser extension, deployment/CI configuration and their trust boundaries. Architecture documentation describes nginx, MariaDB, private files and internal services; actual deployed exposure and identity must be verified separately. Retained SQLite and legacy document paths remain in scope while supported.
|
||||
|
||||
This policy supplies review context, not permission to run commands or test live targets. It does not certify existing controls. Root and component policies compose; nearest-path guidance takes precedence.
|
||||
|
||||
## Threat model and trust boundaries
|
||||
|
||||
Consider anonymous callers, two distinct authenticated owners, administrators, revoked or limited sessions, malicious document authors, incoming webhook senders and compromised external services. Treat route/body identifiers, uploaded files, HTML, job advertisements, external callbacks, logs and model output as untrusted.
|
||||
|
||||
Assets include canonical Career Profile facts/history, CV overrides and frozen revisions, application records, attachments, authentication/recovery material, provider tokens, billing entitlements, audit records and recoverable backups. Trace browser-to-API, API-to-database/files/workers, API-to-sidecar and provider callback/outbound fetch boundaries. Do not assume a role, a client UI flag or an internal-network label proves authorization.
|
||||
|
||||
## Security invariants
|
||||
|
||||
- Establish authenticated identity and owner scope before disclosing or changing private data. Caller-supplied owner IDs cannot replace server identity. Apply ownership to nested resources, lists/search, downloads, exports and background work, not only direct item reads.
|
||||
- Enforce administrator and sensitive-operation permissions server-side, including self/last-admin restrictions. Session revocation, recovery, MFA and OAuth state/account-linking controls must preserve the intended identity boundary.
|
||||
- Preserve canonical Career Profile ownership, null-as-inherit CV settings and immutable submitted/frozen snapshots. All applicable application/CV/letter writes must enforce existing revision preconditions and atomic persistence; stale operations must not partially commit or overwrite newer data.
|
||||
- Bound untrusted document/upload/parser work. Validate type, size, owned paths and lifecycle state; reject traversal and unauthorized retrieval. Rendering/import must not execute active content, dereference unapproved URLs, expose local files or change another owner's data.
|
||||
- Treat AI output as a proposal. Keep consent, entitlement, manual fallback, source/revision checks and explicit review/apply boundaries. Private content must not reach an unapproved model/provider; model or document text cannot authorize tool execution.
|
||||
- Validate outbound destinations and redirects at network boundaries. Preserve job-provider rights/attribution and mailbox consent separately from sign-in identity. Validate webhook authenticity, identity binding and replay/idempotency where applicable.
|
||||
- Preserve browser output encoding, safe HTML/rendering boundaries, origin/cookie/CSRF protections and validated return/navigation targets. UI visibility never substitutes for API authorization.
|
||||
- Protect secrets, tokens and private document content in logs, diagnostics, browser bundles, exports and backups. Privacy deletion/retention must account for files, queued work and recovery copies; successful recovery claims require tested database/file/key consistency.
|
||||
- Treat CI/deployment inputs and dependencies as supply-chain boundaries. Review least privilege, secret handling, provenance, image/runtime exposure and reversible recovery; test fixtures or historical reports do not prove production configuration.
|
||||
|
||||
## Reportable findings and severity context
|
||||
|
||||
Report reachable violations with attacker preconditions, affected assets and evidence. Cross-owner private-data disclosure/mutation, account takeover, privilege escalation, unsafe parser/rendering execution, exposed credentials and unauthorized external data transfer warrant impact-based investigation. Separate confirmed reproduction, reasoned likelihood and unverified assumptions.
|
||||
|
||||
Evaluate actual source/configuration and supported deployment modes. A package advisory, missing attribute or hypothetical path alone is not a reproduced exploit; equally, a passing mock test or limited deployment mitigation does not close a reachable issue. Record advisory/reachability and runtime evidence separately. Preserve failed, blocked, skipped and unexecuted checks explicitly.
|
||||
|
||||
## Exclusions and accepted risk
|
||||
|
||||
No vulnerability class, repository component or existing defect is implicitly suppressed. No new risk acceptance, severity downgrade or finding exclusion is granted by this policy. Historical deferred work and compatibility support remain reviewable.
|
||||
|
||||
Production, adjacent networks, real personal data and third-party systems are excluded from active testing unless separately and explicitly authorized. This execution restriction does not exclude review of repository-owned integration or deployment code. Synthetic local fixtures are distinct from approved staging and live-service qualification.
|
||||
|
||||
## Known limitations and qualification
|
||||
|
||||
The independent audit is incomplete. Source analysis, package checks, local fixtures, hosted CI, native accessibility, provider/model behavior, isolated staging and production recovery are separate evidence lanes. Final integrated security review follows relevant application changes. Baseline/deep/final scan results, cost allowance, staging scope and human release acceptance must be recorded separately; none is established by creating this file.
|
||||
@@ -3,3 +3,7 @@
|
||||
The deployment script and Gitea workflow live in this directory and `.gitea/workflows/`. The current deployment, health/readiness, rollback, and secret-handling guidance is in the repository [infrastructure guide](../docs/infrastructure/README.md), especially [deployment](../docs/infrastructure/deployment.md) and [disaster recovery](../docs/infrastructure/disaster-recovery.md).
|
||||
|
||||
Keep the server `.env` outside the repository. Do not run production deployment, restore, migration, or credential rotation as part of local documentation work.
|
||||
|
||||
MariaDB backup connection parsing uses `scripts/parse-backup-connection.py` (Python 3, already required by deployment preflight). Keys are case-insensitive and support the documented host/user/password/database aliases. Single/double-quoted values may contain semicolons and doubled quote characters. The dump contract requires one TCP host, a database and user, with port 1-65535 (default 3306); an empty password is retained. Duplicate aliases, control characters, host lists/socket paths and database path separators are rejected before dumping. This deliberately bounded parser is not the full MySqlConnector configuration engine. Additional connection options do not change existing dump-client TLS behavior; that qualification remains separate.
|
||||
|
||||
Run `python scripts/test-deploy-backup.py` for the offline synthetic dump-boundary regression. It extracts only the backup function and never executes the deployment entry point, contacts a database or reads the production environment.
|
||||
|
||||
+11
-11
@@ -326,19 +326,19 @@ backup_database() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Parse the ADO.NET connection string. Keys are case-insensitive in .NET, so match that.
|
||||
local db_host db_port db_name db_user db_pass
|
||||
db_host="$(sed -n 's/.*[Ss]erver=\([^;]*\).*/\1/p' <<<"$cs")"
|
||||
db_port="$(sed -n 's/.*[Pp]ort=\([^;]*\).*/\1/p' <<<"$cs")"
|
||||
db_name="$(sed -n 's/.*[Dd]atabase=\([^;]*\).*/\1/p' <<<"$cs")"
|
||||
db_user="$(sed -n 's/.*[Uu]ser[ ]*[Ii]*[Dd]*=\([^;]*\).*/\1/p' <<<"$cs")"
|
||||
db_pass="$(sed -n 's/.*[Pp]assword=\([^;]*\).*/\1/p' <<<"$cs")"
|
||||
db_port="${db_port:-3306}"
|
||||
|
||||
if [ -z "$db_host" ] || [ -z "$db_name" ] || [ -z "$db_user" ]; then
|
||||
echo "Could not parse host/database/user from JOBTRACKER_CONNECTION_STRING. Aborting deploy."
|
||||
# Keep secrets off argv; parse aliases/quoted separators without evaluating shell text.
|
||||
local db_host db_port db_name db_user db_pass parsed_connection
|
||||
local -a connection_fields=()
|
||||
if ! parsed_connection="$(printf '%s' "$cs" | python3 scripts/parse-backup-connection.py)"; then
|
||||
echo "Could not parse JOBTRACKER_CONNECTION_STRING for backup. Aborting deploy."
|
||||
return 1
|
||||
fi
|
||||
mapfile -t connection_fields <<< "$parsed_connection"
|
||||
db_host="${connection_fields[0]}"
|
||||
db_port="${connection_fields[1]}"
|
||||
db_name="${connection_fields[2]}"
|
||||
db_user="${connection_fields[3]}"
|
||||
db_pass="${connection_fields[4]}"
|
||||
|
||||
local target="$BACKUP_DIR/jobtracker-${db_name}-${BACKUP_STAMP}.sql.gz"
|
||||
echo "Backing up MariaDB database '${db_name}' on ${db_host}:${db_port} to ${target}"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Direct roadmap integration — 2 November 2026
|
||||
|
||||
Published implementation checkpoint `557efde073a380030a39066fab2e6d961ce717f4` was independently read back on origin/codex/direct-roadmap-integration. Follow-up test-only catalogue QA adds three invariants and runs with existing provider/persistence stability tests: 10 passed in three additional suites; see ../i18n/EN_NB_CATALOGUE_QA.md. No runtime application change after the passing build.
|
||||
|
||||
Subsequent bounded localisation fix: supported-language control now names Bokmål explicitly and shows NB, retaining preference values and handlers. Seventeen shell/language tests passed; scoped lint, full typecheck and a new production static build passed after this change. See ../i18n/LANGUAGE_CONTROL_QA.md. This later run overlaps i18n-provider coverage in the 10-test QA run; keep those counts separate. No final UI design was implemented.
|
||||
|
||||
Validated isolated review branch `codex/direct-roadmap-integration`. Base Phase 10 `db818e5ebd19eeedc05ae58dc3f7a908ce0e713d`; selected application paths and docs/i18n from published `4172c23d7ce13e8327a5e51bbb09f296c24fb9b8`; backup parser/deploy function and release-readiness reports from `f19e7ebf4aeae350b43fa84cf115c393030280f0`. Content was integrated by explicit owned paths, preserving the Phase 10 accepted handoff records and approved App.css deletion. No full-branch history overwrite, orchestrator access, main merge, final UI design or deployment.
|
||||
|
||||
## Fresh qualification
|
||||
|
||||
- PASSED: 195 tests in 16 explicitly selected frontend suites, zero failures/pending, native exit 0, 40.06 seconds. Includes session/tenant client boundary, attachment preview, inherited-name fallbacks, pipeline/board, checklist/workspace and document/interview workflows. [Native JSON](integration-evidence/frontend-native.json.gz). This is a new combined execution; do not add preceding branch runs to its total.
|
||||
- PASSED: full TypeScript typecheck, full ESLint with zero warnings, native exits 0.
|
||||
- PASSED: Next 16.4.1 production static build, including TypeScript and all three generated pages, native exit 0. Local API base at build time, no live API/provider/model qualification.
|
||||
- PASSED: five actual backup-function parser tests using synthetic strings and mocked dump/Docker boundaries, native exit 0. Git Bash syntax check of deploy/deploy.sh also exit 0. No deployment script was sourced or run in full; no dump, database, model or production operation.
|
||||
- PASSED: portable register/inventory/source/owner dependency hashes match the integrated working tree; approved stylesheet remains absent. Raw inventory hash initially differed after Git CRLF checkout. Register schema 3 now declares LF-normalised reviewed-text hashes, preserving historical raw source hashes and original census content. This corrected evidence portability; no runtime failure was hidden.
|
||||
- npm ci used the locked offline cache, 825 packages; lockfile unchanged. Existing deprecation and unapproved unrs-resolver install-script notices were retained; no dependency/script approval changes.
|
||||
- Backend/API/test/configuration and automation handoff records have no diff from the integration base. Reuse the 103 selected backend / 92 frontend Phase 10 evidence only within its original scope; those are not extra integration tests.
|
||||
|
||||
## Scope and remaining gates
|
||||
|
||||
This branch makes the approved ordinary fixes reviewable together. The original 188-call dynamic key-domain census is accounted for; full I18N-00, every-string context/backend extraction, European locale registry/release, grammar/native review and all runtime coverage are not accepted. No additional locale or country is published.
|
||||
|
||||
Phase 10 P10-05 remains OPEN: full suite/preservation reconciliation, independent audit and required human/environment qualifications remain. Product Review and Global Jobs entry/provider/data gates remain. Public homepage proposals on feature/launch-site-redesign are design-neutral documentation, not included visual implementation. Five application concepts and comparison remain available in the separate design worktree; no final unapproved design integrated.
|
||||
|
||||
New browser/native-device, final integrated security/plugin scans, hosted CI, real provider, backup/restore/rollback and production tests NOT RUN here. Security source/relational attachment review remains separately scoped. No production qualification is inferred from this branch or static build. Publish only to this review branch and independently verify the ref; no main merge/deployment.
|
||||
|
||||
Next: review this combined checkpoint; continue locale context/literal QA and safe source audit, then execute affected browser and final integrated qualifications when their scope and approvals are satisfied. Do not repeat successful unchanged targeted runs merely to resume the chat.
|
||||
@@ -1,5 +1,19 @@
|
||||
# Direct execution continuation
|
||||
|
||||
Current continuation after 2a4acc17: Phase 10 acceptance register published/read back at `6f29ed4cdb7b259318282f365d737f6cfdb6a682` (P10-05 remains OPEN). Fresh integrity check: 263 indexed artifact hashes and 1,279 retained source hashes match; only approved App.css absent. No extra deletion candidate.
|
||||
|
||||
I18N branch latest published/read-back `ebd33fcf0040ed7ebe659c75171873a88cdfc025`. Actual fixes: `2108ea50` session-change message uses existing EN/NB catalogue outside private providers; `7b132ec4` own-property guards for completeness/timeline unknown labels; `36db9322` inherited-name pipeline alias fallback. Selected runs separately: 17 session tests, 41 label/workflow tests, 29 pipeline/board tests, all passing. Typecheck and scoped lint passed for each implementation package. Static production build passed before the final one-line pipeline patch; do not claim a fresh final-pipeline build. Local AST graph update completed, 18,560 nodes / 46,020 edges; generated graphs untracked. Sanitized 17/41 native reports committed; pipeline native JSON remains ignored locally. DYNAMIC_RESOLUTION_REGISTER.json binds 18 individually matched supplemental call sites and reviewed source hashes to the original inventory: 126 resolved / 62 pending of 188, no duplicate counts. Final integrated security tests remain open until relevant application changes are complete.
|
||||
|
||||
Five rendered UI concepts are review-ready: product-modernisation local-only commit `c678ebdb`, docs/product-modernisation/REVIEW_PACK.md. Gallery http://127.0.0.1:4317/gallery.html; preferred expanded A3 http://127.0.0.1:4317/expanded.html. All 70 original screenshot files and ten gallery previews confirmed present; accepted 75-screenshot execution reused, not rerun. Earlier owner choices retained; no final unapproved design implementation. Design branch not pushed under its specific publishing restriction.
|
||||
|
||||
Global Jobs eligibility rechecked against planning branch docs/global-jobs/00/DEPENDENCIES_AND_GATES.md, PROVIDER_RIGHTS_MATRIX.md and OWNER_ACTIONS.md. Their old blanket no-implementation authority is superseded only by later direct-chat authorisation for independent ordinary work; required Phase 10/Product Review acceptance, stage/data/lifecycle/provider grants remain open. GJ-00 evidence is dated 28 October, not a fresh provider check. No credentials, registrations, scraping or provider calls occurred. European localisation registry/market/native-review choices, public homepage visual/claims approval, final design, hosted/live recovery/CI and security scan/staging gates remain separate; do not silently mark them complete.
|
||||
|
||||
Next eligible work: continue the 62 unresolved dynamic calls using the machine register to avoid repeats; review processing-monitor external-key maps for inherited-property behavior, then finite interview/prep/action maps; finish European catalogue context/legal evidence and eligible fixture checks; continue security owner-filter/source matrix review without final integrated scanning. Prepare fresh build/browser checks when relevant app scope stabilises, retaining all baseline/source boundaries. No immediate owner input is required for this offline work. No orchestrator access, pre-P10 edits, main merge or production deployment.
|
||||
|
||||
Newest continuation receipts: Phase 10 `ba826de1e5c959352e4442ab2fec5d02445dd32d` (89 backend + 92 frontend selected cases, separately counted); I18N `bab240914e30fb1e8e3756fbe568cbd60e39db32` (120/188 dynamic calls resolved; 68 pending); security offline preflight `0a17a33db7b9944693976df432f1e5a319277279`. All three refs were independently read back after normal publication. Security worktree: C:\Users\Connor\.codex\worktrees\security-offline-preflight\Job tracker, branch codex/security-offline-preflight. It contains partial Phase 00/01 inventory, ROE, eight-row source endpoint matrix and honest scan register. No scan or active staging attack executed; no policy file written. Continue there from docs/security/01-threat-model.md, completing owner-filter/sibling-path/source matrix review and actual plugin capability preflight. Read further subordinate prompts before entering later audit stages. Audit acceptance remains open.
|
||||
|
||||
Decisions parked for owner catch-up: exact proposed root security policy before any policy write; separately bounded deep-scan cost/runtime; named isolated staging hosts/accounts/rates before active attacks; final comprehensive product design and implementation roadmap; native-language/market and release/human/device gates. None is required for the next offline source review. Session-boundary English text remains an implementation item requiring privacy/unmount regression checks; preserve old-provider teardown. Orchestrator and pre-P10 worktree remain untouched.
|
||||
|
||||
Latest continuation: P10-04 selected regression has 89 backend cases and 92 frontend tests passing, zero failures/skips/pending. Native evidence is archived under docs/redesign/phase-10/p10-04-evidence; P10-04.md records exact filters, source boundaries and remaining qualification. Backend checkpoint `525b5fe4d74ab75f711651fe3a36b988b07abc4d` was independently remotely verified. I18N finite-map review was published/read back at `f577565f7146c6e0cba6edd00b4e804b032ad670`: 112 dynamic calls resolved / 76 pending. It verified 15 letter/completeness keys in both catalogues; checklist default review verified all 14 current defaults but retained the legacy unknown-key gap. No additional cleanup or production change.
|
||||
|
||||
Next: continue independent audit, remaining i18n owner maps/literals, and P10-05 evidence reconciliation. Keep final design/implementation approval, market/language decisions, provider/licensing access, live recovery, hosted CI and human/device gates open; ordinary local work need not wait for these unrelated gates. No user input is required for the next static review package.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,26 @@
|
||||
# I18N-00 baseline audit
|
||||
|
||||
Date: 2026-11-02. Verdict: **IN_PROGRESS; partial research checkpoint, not full I18N-00 acceptance. I18N-01 through 08 BLOCKED. Production NO-GO.**
|
||||
|
||||
Research only, authorised by master prompt and continuous-progress instruction. Branch `feature/i18n-00-audit`, worktree `C:/Users/Connor/.codex/worktrees/i18n-00-audit/Job tracker`, baseline `5cb903b989742b7a96cb22ddf4c0e24993b18980`. Application/API/tests/UI/sidecar diff against `ba04d817` was empty. This is the accepted conditional development handoff, not blanket runtime, language or production acceptance. No controller or approval state was changed. All ten supplied I18N prompts were read. Their implementation requirements are future specifications, not authority to execute them.
|
||||
|
||||
Architecture authority is `docs/architecture/current.md`, which redirects to `overview.md`. Existing React/MUI, ASP.NET/EF/MariaDB, Python sidecar and rendering ownership remain. Graph absent in this baseline: bounded source inspection used; no graph rebuild needed for documentation.
|
||||
|
||||
## Verified source findings
|
||||
|
||||
* `job-tracker-ui/src/i18n/translations.ts`: custom two-language `en | nb` object; it is not an i18next or FormatJS installation.
|
||||
* `I18nProvider.tsx`: local storage accepts `nb`, `nb-NO`, `no`; otherwise English. Auth hydration and cross-tab storage update language. `{word}` substitution handles simple values, not ICU plural/select grammar. Failed preference save offers retry; sequence tracking avoids stale error handling.
|
||||
* `JobTrackerApi/Controllers/AuthController.cs:601`: language endpoint accepts `en` or Bokmål aliases, writes `nb-NO`; unsupported input returns hardcoded English error. Do not rename canonical English to `en-GB` without migration tests.
|
||||
* `CoverLetterDocumentRenderer.cs:36`: labels distinguish `nb-NO` from English; document HTML has its own language. `ThemedCvRenderer.cs:60` derives HTML language from document settings. UI language must not become the output-language owner.
|
||||
* `HumanLanguageCatalog.cs` is CV skill-language recognition, including environment-independent seeds and variant collapsing. It is not an eligible UI-locale registry; reusing it would lose script/region distinctions.
|
||||
* Manifest dependencies: React `^19.2.4`, Next `16.4.1`, MUI `^7.3.9`, TypeScript `^5.9.3`, date-fns `^4.1.0`. These are manifest ranges, not freshly installed/runtime versions. No i18next/FormatJS direct dependency. Lockfile/runtime compatibility must be qualified before implementation.
|
||||
|
||||
## Gate evidence
|
||||
|
||||
`docs/pre-phase10-ux/ACCEPTANCE.md` grants conditional development review, not Phase 10 implementation. `docs/automation/handoffs/pre-phase10/FINAL_DEVELOPMENT_ENTRY.md` separates P10-00 assessment from candidate-specific P10-01 permission. This checkpoint cannot grant either. Product-review and launch-site accepted decisions are absent from this baseline; absence is not permission. Global Jobs planning exists separately at `0ec9f98` in the main workspace (`docs/global-jobs/00/DEPENDENCIES_AND_GATES.md` inspected read-only); it is not integrated here and does not authorise live providers or GJ-01.
|
||||
|
||||
## Honest limits
|
||||
|
||||
The TypeScript AST inventory contains 3,119 existing English catalogue entries and 3,164 syntactically identified static call sites across 134 non-test frontend files. It records 188 dynamic/unknown calls separately as an unresolved count; 853 catalogue entries have no identified static caller. These are not deletion candidates: wrappers, dynamic keys and direct object access remain unresolved. A subsequent partial JSX/accessibility literal inventory now contains 26 source-context-reviewed candidates. CONTEXT_TRIAGE.md records the current 80 unresolved dynamic calls after 108 finite-branch resolutions. This is still not a complete non-catalogue inventory. Every-string attribution, translator context, accessible states and country-specific legal recognition require additional review. Therefore the stage's complete-inventory criterion has not passed. No translations, native approval, screenshots, model results, browser results or locale release are manufactured.
|
||||
|
||||
The country matrix covers an initial 59 territory/market contexts from versioned CLDR 49, with targeted official-record checks for Norway, Switzerland and Serbia. The remaining legal cross-checks are pending. The draft catalogue contains 60 planned locale candidates, not 60 supported languages. Schema, uniqueness, BCP-47 parsing and rejection of a forged released status passed using existing local tools. Node/ICU canonicalises `cnr-ME` to `sr-ME`; retain this unresolved identity issue rather than accepting an automatic product alias. See BASELINE_TESTS.md.
|
||||
@@ -0,0 +1,19 @@
|
||||
# Planning evidence and baseline checks
|
||||
|
||||
2026-11-02. Baseline `5cb903b989742b7a96cb22ddf4c0e24993b18980`, branch `feature/i18n-00-audit`. Documentation only. No runtime code, test code, lockfiles or settings changed.
|
||||
|
||||
| Check | Result | Scope |
|
||||
| --- | --- | --- |
|
||||
| `git diff ba04d817fed26d659bfa2a56b089d6cac851dc34 HEAD --stat -- JobTrackerApi JobTrackerApi.Tests JobTrackerApi.MariaDb.Tests job-tracker-ui tools/summarizer` | PASSED: empty diff, exit 0 | Source identity only; no fresh runtime assertion |
|
||||
| English catalogue AST extraction using existing TypeScript | PASSED: 3,119 unique keys | Property assignments required to be string literals; duplicate/unsupported members throw; 134 non-test TS/TSX files scanned |
|
||||
| Static syntactic `t` call association | 3,164 sites; 188 dynamic/unknown calls; 853 entries without static caller | Partial inventory, not symbol resolution or coverage proof; no dead-code inference |
|
||||
| Existing Ajv draft-07 validation | PASSED: 60 entries | `LOCALE_SCHEMA.json` compiled and validated `LOCALE_CATALOG.json`; no installed dependency changes |
|
||||
| Uniqueness and `Intl.getCanonicalLocales` parsing | PASSED: 60 unique parseable tags | Node v24.19.0, ICU 78.3; syntax acceptance is not runtime translation availability |
|
||||
| Negative publication fixture | PASSED | In-memory copy changed first `surfaces.ui` from `planned` to `released`; validator rejected it |
|
||||
| Montenegrin identity | OPEN | Intl maps `cnr-ME` to `sr-ME`; product semantics require explicit resolution before registry implementation |
|
||||
| Product/unit/browser/MariaDB/Python suites | NOT RUN | No implementation changes; existing accepted evidence remains historical |
|
||||
| Native/legal/AT/device/PDF/hosted/production/provider/model qualification | NOT RUN | No authority or claim conferred by planning checks |
|
||||
|
||||
The inventory records its baseline, parser version, exact source SHA-256 and source line for each English message. Callers retain expressions and file/line, making manual review possible. Extraction covers the `en` object in `translations.ts`, excludes `*.test.*`, and associates literal first arguments of expressions named `t` or ending in `.t`. Dynamic keys, aliases, direct property reads, C#/Python messages and hardcoded JSX are not completely captured. Recompute after source changes; do not extrapolate counts to full application completeness.
|
||||
|
||||
Research sources checked on this date: CLDR 49 territory table, EU official-language list, i18next plural documentation, FormatJS ICU syntax, Norwegian Language Act, Swiss federal multilingualism guidance and Serbian government constitution. Specific links and limitations are in the matrix and implementation plan. No production data or private user text was sent to translation services.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Source-context triage continuation
|
||||
|
||||
2026-11-02, unchanged application baseline `5cb903b989742b7a96cb22ddf4c0e24993b18980`. No translation or runtime implementation.
|
||||
|
||||
## Completed inventory refinement
|
||||
|
||||
- All 188 previously counted dynamic/unknown `t` calls now have source file/line/hash and exact expression in DYNAMIC_MESSAGE_CALLS.json.
|
||||
- 108 calls resolve to finite literal conditional branches, accounting for 240 possible-key references. Each referenced key exists in the English catalogue. Nested ternaries are included. This is key resolution, not translator or runtime acceptance.
|
||||
- 80 calls still need lookup-table, wrapper, template-token or runtime-key review. Type assertions such as `as any` or a single asserted literal must not be treated as proof of actual possible values.
|
||||
- Four direct catalogue reads were located in I18nProvider: save-failure/retry feedback and the evaluator/fallback. These are active consumers missed by a `t("key")`-only search; preserve them during migration.
|
||||
- 26 JSX text/selected accessibility-label candidates were extracted and individually reviewed in source context. The JSON records preservation/extraction decisions and excludes adjacent private runtime values. This scan does not cover every computed JSX expression, object-held label, string-returning helper, backend string or app-shell document text.
|
||||
|
||||
## Findings to carry into approved implementation
|
||||
|
||||
| ID | Source | Finding / required handling |
|
||||
| --- | --- | --- |
|
||||
| I00-13 | `App.tsx:488` | Session-invalidated alert is hardcoded English. Localisation must preserve disposal of the old application tree and must not depend on its discarded session/provider state |
|
||||
| I00-14 | `JobFlowBar.tsx:147` | `Flow` is controlled UI copy outside the catalogue; extract with workflow context |
|
||||
| I00-15 | `Attachments.tsx:361` | Preview iframe title is only `pdf`; use descriptive localised purpose while keeping private filenames out of translator exports |
|
||||
| I00-16 | `AppShell.tsx:692` | `EN`/`NO` with `English`/`Norsk` labels conflates country-like abbreviation and Norwegian variant. Future selector needs native/English names and explicit Bokmal/Nynorsk distinction; preserve existing nb aliases |
|
||||
| I00-17 | `ApplicationAssets.tsx:360` | File-size display uses `toFixed(1)` with KB. Review locale number formatting; filename and underlying byte count remain unchanged |
|
||||
| I00-18 | `I18nProvider.tsx:43` | Save-failure feedback accesses catalogue directly. Migration must preserve same-language retry and stale-save sequencing; static t-call inventory alone misses this contract |
|
||||
| I00-19 | `LandingPage.tsx:133` | Technology-name list is not ordinary translatable prose. Coordinate user-outcome copy with homepage design work; do not translate trademarks or add unverified feature claims |
|
||||
|
||||
Brand/provider names, host examples and technical identifiers were not blindly classified as missing translations. Unit/version fragments require formatting and accessibility context rather than mechanical dictionary substitution. All findings are planning tasks, not permission to edit application files.
|
||||
|
||||
## Verification and remaining work
|
||||
|
||||
The same 134 non-test TS/TSX files were scanned using the installed TypeScript AST parser. Assertions confirmed exactly 188 dynamic calls and that all 240 resolved branch-key references exist. No full application typecheck or browser suite was run. The 26 literal records were reviewed against adjacent source lines; broader semantic census remains IN_PROGRESS.
|
||||
|
||||
Next: resolve the remaining 80 table/template/wrapper calls; cover computed frontend values and app document metadata; inventory backend errors, queued communication text and Python processing messages. Country legal records, reviewer allocation and runtime-library choice remain separate unfinished tasks.
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"baseline": "5cb903b989742b7a96cb22ddf4c0e24993b18980",
|
||||
"method": "TypeScript AST syntactic triage; no symbol resolution or runtime coverage claim",
|
||||
"filesScanned": 134,
|
||||
"entries": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/i18n/I18nProvider.tsx",
|
||||
"line": 43,
|
||||
"sourceSha256": "95a930915cb2240547cc1a91af7cb0a475bc8f46ac75304b3241a401928ed3e4",
|
||||
"kind": "direct-catalogue-access",
|
||||
"text": "translations[l].settingsLanguageSaveFailed",
|
||||
"ownerComponent": "job-tracker-ui/src/i18n/I18nProvider.tsx",
|
||||
"contextStatus": "pending",
|
||||
"sensitivity": "unclassified-template; do-not-export-runtime-values"
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/i18n/I18nProvider.tsx",
|
||||
"line": 44,
|
||||
"sourceSha256": "95a930915cb2240547cc1a91af7cb0a475bc8f46ac75304b3241a401928ed3e4",
|
||||
"kind": "direct-catalogue-access",
|
||||
"text": "translations[l].retry",
|
||||
"ownerComponent": "job-tracker-ui/src/i18n/I18nProvider.tsx",
|
||||
"contextStatus": "pending",
|
||||
"sensitivity": "unclassified-template; do-not-export-runtime-values"
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/i18n/I18nProvider.tsx",
|
||||
"line": 67,
|
||||
"sourceSha256": "95a930915cb2240547cc1a91af7cb0a475bc8f46ac75304b3241a401928ed3e4",
|
||||
"kind": "direct-catalogue-access",
|
||||
"text": "translations[language][key]",
|
||||
"ownerComponent": "job-tracker-ui/src/i18n/I18nProvider.tsx",
|
||||
"contextStatus": "pending",
|
||||
"sensitivity": "unclassified-template; do-not-export-runtime-values"
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/i18n/I18nProvider.tsx",
|
||||
"line": 67,
|
||||
"sourceSha256": "95a930915cb2240547cc1a91af7cb0a475bc8f46ac75304b3241a401928ed3e4",
|
||||
"kind": "direct-catalogue-access",
|
||||
"text": "translations.en[key]",
|
||||
"ownerComponent": "job-tracker-ui/src/i18n/I18nProvider.tsx",
|
||||
"contextStatus": "pending",
|
||||
"sensitivity": "unclassified-template; do-not-export-runtime-values"
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,87 @@
|
||||
# Europe country and language research matrix
|
||||
|
||||
Research checkpoint 2026-11-02; not a market-launch list or legal opinion. All entries are candidate scope. CLDR 49 provides an initial language relationship dataset; national/regional official-record cross-checks remain incomplete. Blank CLDR status is not proof of absence or recognition. No tag decides sovereignty, EU membership, provider rights or support. XK is a CLDR user-assigned territory code, not ISO 3166-1; do not silently persist it as an ISO identifier.
|
||||
|
||||
Source: [Unicode CLDR 49 territory-language data](https://www.unicode.org/cldr/charts/49/tsv/territory_language_information.tsv); retrieved SHA-256 `29bcadbb4362e1a7a3685f4bf199152809635acca58fab10a3576a0f7b55868c`. Population percentages are deliberately omitted; they are not product usage evidence.
|
||||
|
||||
| Territory | Code | CLDR official/de-facto | CLDR regional/recognised | Legal cross-check |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Andorra | AD | ca (official) | not recorded; further review required | PENDING |
|
||||
| Albania | AL | sq (official) | not recorded; further review required | PENDING |
|
||||
| Austria | AT | de (official) | hr (official_regional), sl (official_regional), hu (official_regional) | PENDING |
|
||||
| Åland Islands | AX | sv (official) | not recorded; further review required | PENDING |
|
||||
| Bosnia & Herzegovina | BA | bs (official), bs-Cyrl (official), hr (official), sr (official), sr-Latn (official) | not recorded; further review required | PENDING |
|
||||
| Belgium | BE | nl (official), fr (official), de (official) | not recorded; further review required | PENDING |
|
||||
| Bulgaria | BG | bg (official) | not recorded; further review required | PENDING |
|
||||
| Belarus | BY | ru (official), be (official) | not recorded; further review required | PENDING |
|
||||
| Switzerland | CH | de (official), gsw (de_facto_official), fr (official), it (official) | rm (official_regional) | PENDING |
|
||||
| Cyprus | CY | el (official), tr (official) | not recorded; further review required | PENDING |
|
||||
| Czechia | CZ | cs (official) | not recorded; further review required | PENDING |
|
||||
| Germany | DE | de (official) | frr (official_regional) | PENDING |
|
||||
| Denmark | DK | da (official) | de (official_regional), kl (official_regional) | PENDING |
|
||||
| Estonia | EE | et (official) | not recorded; further review required | PENDING |
|
||||
| Spain | ES | es (official) | ca (official_regional), gl (official_regional), eu (official_regional), ast (official_regional), oc (official_regional) | PENDING |
|
||||
| Finland | FI | fi (official), sv (official) | sms (official_regional) | PENDING |
|
||||
| Faroe Islands | FO | fo (official) | not recorded; further review required | PENDING |
|
||||
| France | FR | fr (official) | not recorded; further review required | PENDING |
|
||||
| United Kingdom | GB | en (official) | cy (official_regional), ga (official_regional), gd (official_regional) | PENDING |
|
||||
| Guernsey | GG | en (official) | not recorded; further review required | PENDING |
|
||||
| Gibraltar | GI | en (official) | not recorded; further review required | PENDING |
|
||||
| Greece | GR | el (official) | not recorded; further review required | PENDING |
|
||||
| Croatia | HR | hr (official) | it (official_regional), vec (official_regional) | PENDING |
|
||||
| Hungary | HU | hu (official) | not recorded; further review required | PENDING |
|
||||
| Ireland | IE | en (official), ga (official) | not recorded; further review required | PENDING |
|
||||
| Isle of Man | IM | en (official), gv (official) | not recorded; further review required | PENDING |
|
||||
| Iceland | IS | is (official) | not recorded; further review required | PENDING |
|
||||
| Italy | IT | it (official) | fr (official_regional), vec (official_regional) | PENDING |
|
||||
| Jersey | JE | en (official) | not recorded; further review required | PENDING |
|
||||
| Liechtenstein | LI | de (official), gsw (de_facto_official) | not recorded; further review required | PENDING |
|
||||
| Lithuania | LT | lt (official) | not recorded; further review required | PENDING |
|
||||
| Luxembourg | LU | fr (official), lb (official), de (official) | not recorded; further review required | PENDING |
|
||||
| Latvia | LV | lv (official) | not recorded; further review required | PENDING |
|
||||
| Monaco | MC | fr (official) | not recorded; further review required | PENDING |
|
||||
| Moldova | MD | ro (official) | not recorded; further review required | PENDING |
|
||||
| Montenegro | ME | sr-Latn (official) | not recorded; further review required | PENDING |
|
||||
| North Macedonia | MK | mk (official) | sq (official_regional) | PENDING |
|
||||
| Malta | MT | mt (official), en (official) | not recorded; further review required | PENDING |
|
||||
| Netherlands | NL | nl (official) | fy (official_regional) | PENDING |
|
||||
| Norway | NO | nb (official), no (official), nn (official) | se (official_regional) | PENDING |
|
||||
| Poland | PL | pl (official) | de (official_regional), csb (official_regional), lt (official_regional) | PENDING |
|
||||
| Portugal | PT | pt (official) | not recorded; further review required | PENDING |
|
||||
| Romania | RO | ro (official) | not recorded; further review required | PENDING |
|
||||
| Serbia | RS | sr (official), sr-Latn (official) | hu (official_regional), ro (official_regional), hr (official_regional), sk (official_regional), uk (official_regional) | PENDING |
|
||||
| Russia | RU | ru (official) | tt (official_regional), ce (official_regional), ba (official_regional), av (official_regional), kbd (official_regional), myv (official_regional), kum (official_regional), sah (official_regional), inh (official_regional), bua (official_regional), az-Cyrl (official_regional), lez (official_regional), mdf (official_regional), krc (official_regional), tyv (official_regional), udm (official_regional), ady (official_regional), kv (official_regional), lbe (official_regional), koi (official_regional) | PENDING |
|
||||
| Sweden | SE | sv (official) | fi (official_regional) | PENDING |
|
||||
| Slovenia | SI | sl (official) | vec (official_regional) | PENDING |
|
||||
| Svalbard & Jan Mayen | SJ | nb (official) | not recorded; further review required | PENDING |
|
||||
| Slovakia | SK | sk (official) | not recorded; further review required | PENDING |
|
||||
| San Marino | SM | it (official) | not recorded; further review required | PENDING |
|
||||
| Türkiye | TR | tr (official) | not recorded; further review required | PENDING |
|
||||
| Ukraine | UA | uk (official), ru (de_facto_official) | not recorded; further review required | PENDING |
|
||||
| Vatican City | VA | it (de_facto_official) | not recorded; further review required | PENDING |
|
||||
| Kosovo | XK | sq (official), sr (official), sr-Latn (official) | not recorded; further review required | PENDING |
|
||||
| Armenia | AM | hy (official) | not recorded; further review required | PENDING |
|
||||
| Azerbaijan | AZ | az (official), az-Cyrl (official) | not recorded; further review required | PENDING |
|
||||
| Georgia | GE | ka (official) | ab (official_regional), os (official_regional) | PENDING |
|
||||
| Kazakhstan | KZ | ru (official), kk (official) | not recorded; further review required | PENDING |
|
||||
| Greenland | GL | kl (official) | not recorded; further review required | PENDING |
|
||||
|
||||
## Scope and legal cross-check queue
|
||||
|
||||
Russia, Turkey, Cyprus, Armenia, Azerbaijan, Georgia and Kazakhstan are explicit geographic/market-review cases; inclusion here does not approve a market. Greenland, Faroe Islands, Aland, Crown dependencies, Gibraltar and Svalbard/Jan Mayen are separately considered territory contexts. This is an initial 59-code research envelope, not a claim to have exhausted every dependency or minority language.
|
||||
|
||||
Norway: [Language Act](https://lovdata.no/dokument/NLE/lov/2021-05-21-42) distinguishes Bokmal/Nynorsk and protected Sami/minority languages. Plan distinct nb-NO/nn-NO and investigate Sami/Kven review capacity; do not infer their status from the abbreviated CLDR row.
|
||||
|
||||
Switzerland: [federal multilingualism explanation](https://www.aboutswitzerland.eda.admin.ch/en/multilingualism) distinguishes national languages and official usage. Plan de-CH, fr-CH, it-CH and rm-CH with separate reviewers.
|
||||
|
||||
Serbia: [government constitution](https://www.srbija.gov.rs/tekst/56753/ustav-republike-srbije.php) establishes Serbian/Cyrillic official use. sr-Latn-RS remains an explicit product candidate, separately from sr-Cyrl-RS; not a claim of identical legal status. These source checks inform planning, not exhaustive legal clearance.
|
||||
|
||||
Regional/minority queue includes ca-ES, eu-ES, gl-ES, cy-GB, gd-GB, se-NO, smj-NO, sma-NO and fkv-NO. Country-specific recognition, applicable areas, reviewer capacity, orthography, fonts and locale-data support must be reviewed separately. No automatic mapping of Montenegrin to Serbian; cnr-ME remains a candidate requiring runtime/data validation.
|
||||
|
||||
## EU 24 language baseline
|
||||
|
||||
[EU official language list](https://european-union.europa.eu/principles-countries-history/languages_en), checked 2026-11-02: Bulgarian bg-BG; Croatian hr-HR; Czech cs-CZ; Danish da-DK; Dutch nl-NL; English en (preserve existing canonical ID; en-GB candidate); Estonian et-EE; Finnish fi-FI; French fr-FR; German de-DE; Greek el-GR; Hungarian hu-HU; Irish ga-IE; Italian it-IT; Latvian lv-LV; Lithuanian lt-LT; Maltese mt-MT; Polish pl-PL; Portuguese pt-PT; Romanian ro-RO; Slovak sk-SK; Slovenian sl-SI; Spanish es-ES; Swedish sv-SE. These locale choices are proposals, not mandated by the EU list.
|
||||
|
||||
## Proposed waves
|
||||
|
||||
Foundation candidates: preserve existing en/nb behavior, then nn-NO, sv-SE, da-DK, fi-FI and is-IS if reviewers exist. EU-24 next; broader national/script/region candidates thereafter. Actual usage analytics and reviewer allocation are unavailable, so no evidence-backed commercial ranking or promised dates are asserted. English fallback remains available irrespective of market.
|
||||
@@ -0,0 +1,319 @@
|
||||
{
|
||||
"baseline": "5cb903b989742b7a96cb22ddf4c0e24993b18980",
|
||||
"method": "TypeScript AST syntactic triage; no symbol resolution or runtime coverage claim",
|
||||
"filesScanned": 134,
|
||||
"entries": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/App.tsx",
|
||||
"line": 488,
|
||||
"sourceSha256": "3b5645d12f2c92ed8999d7a072bd7d47fac77023d460a19a9b3bfc2a4191962f",
|
||||
"kind": "jsx-text",
|
||||
"text": "Session changed. Clearing this page…",
|
||||
"ownerComponent": "job-tracker-ui/src/App.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "localise-with-session-boundary-preservation",
|
||||
"translatorContext": "Alert rendered while old application tree is invalidated; preserve unmount/privacy behavior and avoid dependencies on the discarded session/provider tree."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/assets/JobbjaktMark.tsx",
|
||||
"line": 7,
|
||||
"sourceSha256": "5a539514876a66eaf17ec68eb7ce3d28c558384bc24ab76b61f73be8df754473",
|
||||
"kind": "jsx-attribute:aria-label",
|
||||
"text": "Jobbjakt",
|
||||
"ownerComponent": "job-tracker-ui/src/assets/JobbjaktMark.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "preserve-proper-name-or-example",
|
||||
"translatorContext": "Public brand/provider label or synthetic host example; not user content. Retain original spelling; localise surrounding guidance separately."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"line": 198,
|
||||
"sourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"kind": "jsx-text",
|
||||
"text": "· v",
|
||||
"ownerComponent": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "version-or-unit-formatting-review",
|
||||
"translatorContext": "Keep numeric/version identity; review locale number formatting and descriptive accessible labels. Do not translate user file/name data."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"line": 360,
|
||||
"sourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"kind": "jsx-text",
|
||||
"text": "KB",
|
||||
"ownerComponent": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "version-or-unit-formatting-review",
|
||||
"translatorContext": "Keep numeric/version identity; review locale number formatting and descriptive accessible labels. Do not translate user file/name data."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"line": 699,
|
||||
"sourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"kind": "jsx-text",
|
||||
"text": "v",
|
||||
"ownerComponent": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "version-or-unit-formatting-review",
|
||||
"translatorContext": "Keep numeric/version identity; review locale number formatting and descriptive accessible labels. Do not translate user file/name data."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/Attachments.tsx",
|
||||
"line": 361,
|
||||
"sourceSha256": "fe706526f36d92c10d2a0adfd7abb634d0ebee28f5d7e1794c590179b4fd884c",
|
||||
"kind": "jsx-attribute:title",
|
||||
"text": "pdf",
|
||||
"ownerComponent": "job-tracker-ui/src/components/Attachments.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "descriptive-localised-accessible-title",
|
||||
"translatorContext": "Attachment preview iframe title; describe document preview purpose, without exposing private filename in exported translator context."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/CoverLetterDocument.tsx",
|
||||
"line": 157,
|
||||
"sourceSha256": "17ea0c608db7bc05171fb3c44f378c1acaa78fbdd337f9330002ef6346b92546",
|
||||
"kind": "jsx-text",
|
||||
"text": "pt ·",
|
||||
"ownerComponent": "job-tracker-ui/src/components/CoverLetterDocument.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "version-or-unit-formatting-review",
|
||||
"translatorContext": "Keep numeric/version identity; review locale number formatting and descriptive accessible labels. Do not translate user file/name data."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/CoverLetterDocument.tsx",
|
||||
"line": 157,
|
||||
"sourceSha256": "17ea0c608db7bc05171fb3c44f378c1acaa78fbdd337f9330002ef6346b92546",
|
||||
"kind": "jsx-text",
|
||||
"text": "mm ·",
|
||||
"ownerComponent": "job-tracker-ui/src/components/CoverLetterDocument.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "version-or-unit-formatting-review",
|
||||
"translatorContext": "Keep numeric/version identity; review locale number formatting and descriptive accessible labels. Do not translate user file/name data."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/EmailProviderConnections.tsx",
|
||||
"line": 227,
|
||||
"sourceSha256": "b71228ee28ee73525014c8ece8690364e0f70ddaca3ee7211e9076123d120b1d",
|
||||
"kind": "jsx-attribute:label",
|
||||
"text": "Gmail",
|
||||
"ownerComponent": "job-tracker-ui/src/components/EmailProviderConnections.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "preserve-proper-name-or-example",
|
||||
"translatorContext": "Public brand/provider label or synthetic host example; not user content. Retain original spelling; localise surrounding guidance separately."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/EmailProviderConnections.tsx",
|
||||
"line": 239,
|
||||
"sourceSha256": "b71228ee28ee73525014c8ece8690364e0f70ddaca3ee7211e9076123d120b1d",
|
||||
"kind": "jsx-attribute:label",
|
||||
"text": "Outlook / Microsoft 365",
|
||||
"ownerComponent": "job-tracker-ui/src/components/EmailProviderConnections.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "preserve-proper-name-or-example",
|
||||
"translatorContext": "Public brand/provider label or synthetic host example; not user content. Retain original spelling; localise surrounding guidance separately."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/EmailProviderConnections.tsx",
|
||||
"line": 264,
|
||||
"sourceSha256": "b71228ee28ee73525014c8ece8690364e0f70ddaca3ee7211e9076123d120b1d",
|
||||
"kind": "jsx-attribute:placeholder",
|
||||
"text": "imap.example.com",
|
||||
"ownerComponent": "job-tracker-ui/src/components/EmailProviderConnections.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "preserve-proper-name-or-example",
|
||||
"translatorContext": "Public brand/provider label or synthetic host example; not user content. Retain original spelling; localise surrounding guidance separately."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/JobFlowBar.tsx",
|
||||
"line": 147,
|
||||
"sourceSha256": "643053f6ac883f23ef74c66def7a42678cd7917ed11ca802b11be25ec6df0cb2",
|
||||
"kind": "jsx-text",
|
||||
"text": "Flow",
|
||||
"ownerComponent": "job-tracker-ui/src/components/JobFlowBar.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "extract-controlled-label",
|
||||
"translatorContext": "Workflow overline in JobFlowBar; ordinary user-facing UI label, not a persisted status."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/QuickCommandDialog.tsx",
|
||||
"line": 166,
|
||||
"sourceSha256": "27cbbb8b8596fdf4e7474eccfdb6d9a8a219f4baff6a9b8013b825ce5fc2c411",
|
||||
"kind": "jsx-text",
|
||||
"text": "Ctrl/Cmd + K",
|
||||
"ownerComponent": "job-tracker-ui/src/components/QuickCommandDialog.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "version-or-unit-formatting-review",
|
||||
"translatorContext": "Keep numeric/version identity; review locale number formatting and descriptive accessible labels. Do not translate user file/name data."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"line": 312,
|
||||
"sourceSha256": "e75a58f2984bf47d7edc3c2c51439242aba2df32675a30b1f17088095ea1d634",
|
||||
"kind": "jsx-text",
|
||||
"text": "Jobbjakt",
|
||||
"ownerComponent": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "preserve-proper-name-or-example",
|
||||
"translatorContext": "Public brand/provider label or synthetic host example; not user content. Retain original spelling; localise surrounding guidance separately."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"line": 415,
|
||||
"sourceSha256": "e75a58f2984bf47d7edc3c2c51439242aba2df32675a30b1f17088095ea1d634",
|
||||
"kind": "jsx-text",
|
||||
"text": "Jobbjakt",
|
||||
"ownerComponent": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "preserve-proper-name-or-example",
|
||||
"translatorContext": "Public brand/provider label or synthetic host example; not user content. Retain original spelling; localise surrounding guidance separately."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"line": 563,
|
||||
"sourceSha256": "e75a58f2984bf47d7edc3c2c51439242aba2df32675a30b1f17088095ea1d634",
|
||||
"kind": "jsx-text",
|
||||
"text": "commit",
|
||||
"ownerComponent": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "localise-label-preserve-identifier",
|
||||
"translatorContext": "Technical display label may be translated; commit/job identifier remains invariant and must not enter translation resources."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"line": 692,
|
||||
"sourceSha256": "e75a58f2984bf47d7edc3c2c51439242aba2df32675a30b1f17088095ea1d634",
|
||||
"kind": "jsx-attribute:aria-label",
|
||||
"text": "English",
|
||||
"ownerComponent": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "locale-selector-contract",
|
||||
"translatorContext": "Replace ambiguous country-like abbreviation/undifferentiated Norwegian label only within approved locale-selector work; preserve current nb behavior and native labels."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"line": 692,
|
||||
"sourceSha256": "e75a58f2984bf47d7edc3c2c51439242aba2df32675a30b1f17088095ea1d634",
|
||||
"kind": "jsx-text",
|
||||
"text": "EN",
|
||||
"ownerComponent": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "locale-selector-contract",
|
||||
"translatorContext": "Replace ambiguous country-like abbreviation/undifferentiated Norwegian label only within approved locale-selector work; preserve current nb behavior and native labels."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"line": 693,
|
||||
"sourceSha256": "e75a58f2984bf47d7edc3c2c51439242aba2df32675a30b1f17088095ea1d634",
|
||||
"kind": "jsx-attribute:aria-label",
|
||||
"text": "Norsk",
|
||||
"ownerComponent": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "locale-selector-contract",
|
||||
"translatorContext": "Replace ambiguous country-like abbreviation/undifferentiated Norwegian label only within approved locale-selector work; preserve current nb behavior and native labels."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"line": 693,
|
||||
"sourceSha256": "e75a58f2984bf47d7edc3c2c51439242aba2df32675a30b1f17088095ea1d634",
|
||||
"kind": "jsx-text",
|
||||
"text": "NO",
|
||||
"ownerComponent": "job-tracker-ui/src/layout/AppShell.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "locale-selector-contract",
|
||||
"translatorContext": "Replace ambiguous country-like abbreviation/undifferentiated Norwegian label only within approved locale-selector work; preserve current nb behavior and native labels."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/AdminAuditPage.tsx",
|
||||
"line": 146,
|
||||
"sourceSha256": "b78d0c5f6ec86a4ee3d2214e6854e638d9f73a5c69c8d5b01023d8c60b46ac81",
|
||||
"kind": "jsx-text",
|
||||
"text": "jobId=",
|
||||
"ownerComponent": "job-tracker-ui/src/views/AdminAuditPage.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "localise-label-preserve-identifier",
|
||||
"translatorContext": "Technical display label may be translated; commit/job identifier remains invariant and must not enter translation resources."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/AdminUsersPage.tsx",
|
||||
"line": 274,
|
||||
"sourceSha256": "674290a26fc279a25e43176194716dea87f427d1885ecce70dea7c368ab0c6f8",
|
||||
"kind": "jsx-attribute:label",
|
||||
"text": "—",
|
||||
"ownerComponent": "job-tracker-ui/src/views/AdminUsersPage.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "empty-state-accessibility-review",
|
||||
"translatorContext": "No-role placeholder; visual glyph can stay but verify meaningful accessible context."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/LandingPage.tsx",
|
||||
"line": 99,
|
||||
"sourceSha256": "2c550aa126e1db8786ed10b895d2403491ce50bf5073fc95b8a0fcb0cb0e083e",
|
||||
"kind": "jsx-text",
|
||||
"text": "Jobbjakt",
|
||||
"ownerComponent": "job-tracker-ui/src/views/LandingPage.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "preserve-proper-name-or-example",
|
||||
"translatorContext": "Public brand/provider label or synthetic host example; not user content. Retain original spelling; localise surrounding guidance separately."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/LandingPage.tsx",
|
||||
"line": 133,
|
||||
"sourceSha256": "2c550aa126e1db8786ed10b895d2403491ce50bf5073fc95b8a0fcb0cb0e083e",
|
||||
"kind": "jsx-text",
|
||||
"text": "React · TypeScript · ASP.NET Core · EF Core · FastAPI AI · Gmail",
|
||||
"ownerComponent": "job-tracker-ui/src/views/LandingPage.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "launch-copy-review",
|
||||
"translatorContext": "Technology list is public brand names but poor user-value copy; coordinate replacement with Launch Site design approval."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/career/CareerProfileSections.tsx",
|
||||
"line": 100,
|
||||
"sourceSha256": "0c772feb6bde8324676b0884aaf343283465c5cd3b3318128363063df4ea22ab",
|
||||
"kind": "jsx-attribute:label",
|
||||
"text": "GitHub",
|
||||
"ownerComponent": "job-tracker-ui/src/views/career/CareerProfileSections.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "preserve-proper-name-or-example",
|
||||
"translatorContext": "Public brand/provider label or synthetic host example; not user content. Retain original spelling; localise surrounding guidance separately."
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/career/ProfileCompleteness.tsx",
|
||||
"line": 55,
|
||||
"sourceSha256": "95f017b1495cf9d88a2b69e7c6ae8ef83d55f2b1ad7b6f54895e280b147743b5",
|
||||
"kind": "jsx-text",
|
||||
"text": "v",
|
||||
"ownerComponent": "job-tracker-ui/src/views/career/ProfileCompleteness.tsx",
|
||||
"contextStatus": "source-context-reviewed; implementation-pending",
|
||||
"sensitivity": "public-ui-template; adjacent runtime identifiers excluded",
|
||||
"decision": "version-or-unit-formatting-review",
|
||||
"translatorContext": "Keep numeric/version identity; review locale number formatting and descriptive accessible labels. Do not translate user file/name data."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# Partial I18N-00 research handoff
|
||||
|
||||
Branch `feature/i18n-00-audit`, based on accepted conditional handoff `5cb903b989742b7a96cb22ddf4c0e24993b18980`. Only `docs/i18n/00/` changed. This checkpoint preserves research; I18N-00 acceptance remains IN_PROGRESS and I18N-01 implementation remains BLOCKED.
|
||||
|
||||
Completed: source-owner audit draft; ordered implementation contracts; source-bound English catalogue/static callers; initial 59-territory CLDR matrix; 60 planned locale candidates and schema; planning checks documented in BASELINE_TESTS.md. No locale translated, reviewed or released here.
|
||||
|
||||
Next authorised work, without implementation approval:
|
||||
|
||||
1. Resolve 188 dynamic/unknown calls, direct translation object access and wrappers. Review the 853 entries without static callers without treating them as unused.
|
||||
2. Inventory hardcoded frontend/ARIA/empty/error/export text and backend/Python/email/queue strings. Assign real namespace, translator context, parameter sensitivity and owning component rather than inferring from keys.
|
||||
3. Complete national/regional official-record checks beyond the initial Norway/Switzerland/Serbia checks. Expand territory/minority coverage where the current research envelope omits required contexts.
|
||||
4. Evaluate actual installed/locked library compatibility and Montenegrin canonicalisation before choosing a runtime migration. Do not install packages now.
|
||||
5. Obtain reviewer/market priorities when a concrete rollout proposal is ready. Keep human/native approval separate from planning completeness.
|
||||
|
||||
The delegated researcher was interrupted by a service usage limit; the parent continued, corrected references to nonexistent deliverables and produced the actual inventory/catalogue/matrix. No agent is working in the background. Full stage completion must not be inferred from the presence of the expected filenames.
|
||||
|
||||
Continuation: CONTEXT_TRIAGE.md records all 188 dynamic calls, 108 finite-branch resolutions (240 existing-key references), four direct catalogue reads and 26 source-context-reviewed JSX/accessibility literal candidates. Seven concrete planning findings I00-13 through I00-19 added. Remaining dynamic call queue is 80; prior 188 count is the original inventory total, not the remaining queue. No application changes.
|
||||
|
||||
Product tests NOT RUN; source equality and planning checks PASSED within their documented limits. Preserve existing PDF evidence. No application models, production services, orchestrator configuration or signing/approval material accessed.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Ordered implementation specification — not execution authority
|
||||
|
||||
## Proposed architecture decision
|
||||
|
||||
Keep existing `useI18n` call sites and React/MUI owners. Replace only its message evaluation/storage behind that contract after entry approval. Compare: current substitution is smallest but cannot meet plural/select requirements; i18next offers namespace loading and CLDR plural suffixes but changes interpolation/catalogue shape; FormatJS ICU supports complete sentence plural/select messages behind the current helper with less caller churn. **Proposed preference: ICU message evaluation behind the existing API plus platform Intl formatting**, subject to actual lockfile/runtime compatibility, bundle and extraction prototype after approval. Do not install either now. Do not implement two competing message engines. .NET renders server communications from its own typed templates with shared IDs/parameter contracts; do not assume identical ICU parser support in .NET/Python.
|
||||
|
||||
Sources checked 2026-11-02: https://www.i18next.com/translation-function/plurals and https://formatjs.github.io/docs/core-concepts/icu-syntax/. These establish syntax capabilities, not repository compatibility.
|
||||
|
||||
## Independent preference contract
|
||||
|
||||
Residence `countryId`, `searchCountryIds`, typed location IDs, `uiLocale`, `adLanguageFilters`, document `outputLocale`, optional `communicationLocale` and `timeZone` are distinct. Country changes only suggestion ordering. Manual saved preference wins over URL/app choice, onboarding, browser negotiation and English fallback. Explicitly distinguish signed-out local storage from account hydration. Persist provenance and revision semantics without weakening tenant ownership. Existing `en` stays canonical until an approved compatibility migration; `en-GB` is a candidate variant, not a silent rewrite. `nb/no/nb-NO` legacy aliases preserve existing Bokmål behavior. `nn-NO` must never silently map to Bokmål. `sr-Latn` and `sr-Cyrl` retain script identity. Unsupported locale resolves to reliable English chrome; user content unchanged.
|
||||
|
||||
## Stage sequence and evidence
|
||||
|
||||
| Stage | Bounded change after prerequisites | Required exit evidence |
|
||||
|---|---|---|
|
||||
| 00 | Finish contextual census, country legal cross-check, reviewer ownership | Complete source-bound inventory; independent schema validation; planning review |
|
||||
| 01 | Registry, aliases, separate preferences, migration, formatting | Legacy roundtrip, invalid IDs, tenant/multi-device conflicts, MariaDB migration/reversal; new locales hidden |
|
||||
| 02 | Extract full UI and accessible strings into existing owners | Required-key completeness, placeholder parser, dirty-form language switch, pseudo/long-label responsive tests |
|
||||
| 03 | Stable backend codes; queued communication templates | Event-time locale snapshot proposed, retry determinism, isolated Mailpit/plain/HTML tests, legal approvals |
|
||||
| 04 | Per-document headings/fonts, import boundaries | RD21/history invariance; script/PDF extraction and layout; native/physical review distinct |
|
||||
| 05 | GJ-owned geography/provider/search integration | All-industry synthetic fixtures, explicit alias suggestions, source language and rights/retention evidence |
|
||||
| 06 | Translation supply chain and glossary | Licensed source, translator context, independent proficient review, source-change invalidation |
|
||||
| 07 | Launch Site localisation/SEO contract | Approved visual design; published-only alternates/sitemaps; truthful feature/provider claims |
|
||||
| 08 | Cross-surface/native/device/security acceptance | Locale × surface × market × profession × device matrix; exact commit and disjoint counts |
|
||||
|
||||
Phase 10 completion/appropriate accepted handoff, PRODUCT-REVIEW-01, accepted I18N-00 and explicit stage authority precede I18N-01. Sequential accepted stage integration is mandatory. Separate development commits, no main merge/deploy.
|
||||
|
||||
GJ owns source fetch, rights, stable geography/occupation IDs and search mechanics; I18N owns display labels, ad-language semantics and translated-summary labels. ESCO concept identity cannot be replaced by translated free text. Original advertisement always remains distinguishable. Provider rights are checked before requests, not inferred from locale availability. Launch Site owns visual design/routing architecture; I18N supplies market+content-locale+publication schema and claims binding to deployed capability. Future fixtures may be designed now but no dependent runtime is invented.
|
||||
|
||||
Terminology queue: Career Profile (canonical facts), CV (document variant), cover letter, application (pursuit), job (opportunity), apprenticeship, shift, seasonal work, salary, qualification, nursing/teaching licence. Define each English concept before translation; profession label never establishes accreditation. Planned fixtures include teacher, cook, nurse, electrician, construction worker, driver, cleaner, retail, office administrator and developer.
|
||||
@@ -0,0 +1,846 @@
|
||||
{
|
||||
"baseline": "5cb903b989742b7a96cb22ddf4c0e24993b18980",
|
||||
"status": "research-draft",
|
||||
"locales": [
|
||||
{
|
||||
"tag": "en",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "en-GB",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "nb-NO",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "nn-NO",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "sv-SE",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "da-DK",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "fi-FI",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "is-IS",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "bg-BG",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "hr-HR",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "cs-CZ",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "nl-NL",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "et-EE",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "fr-FR",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "de-DE",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "el-GR",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "hu-HU",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "ga-IE",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "it-IT",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "lv-LV",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "lt-LT",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "mt-MT",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "pl-PL",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "pt-PT",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "ro-RO",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "sk-SK",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "sl-SI",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "es-ES",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "uk-UA",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "sq-AL",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "sr-Latn-RS",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "sr-Cyrl-RS",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "bs-BA",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "cnr-ME",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "mk-MK",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "tr-TR",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "lb-LU",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "cy-GB",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "rm-CH",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "fo-FO",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "be-BY",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "ka-GE",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "hy-AM",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "az-Latn-AZ",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "ru-RU",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "fr-CH",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "de-CH",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "it-CH",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "nl-BE",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "fr-BE",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "de-BE",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "en-IE",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "ca-ES",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "eu-ES",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "gl-ES",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "gd-GB",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "se-NO",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "smj-NO",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "sma-NO",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "fkv-NO",
|
||||
"ownerRole": "localisation-product-owner",
|
||||
"reviewer": null,
|
||||
"status": "planned",
|
||||
"surfaces": {
|
||||
"ui": "planned",
|
||||
"backend": "planned",
|
||||
"communications": "planned",
|
||||
"documents": "planned",
|
||||
"jobDiscovery": "planned",
|
||||
"marketing": "planned"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Research-only locale catalogue; no release authority",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"baseline",
|
||||
"status",
|
||||
"locales"
|
||||
],
|
||||
"properties": {
|
||||
"baseline": {
|
||||
"const": "5cb903b989742b7a96cb22ddf4c0e24993b18980"
|
||||
},
|
||||
"status": {
|
||||
"const": "research-draft"
|
||||
},
|
||||
"locales": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"tag",
|
||||
"ownerRole",
|
||||
"reviewer",
|
||||
"status",
|
||||
"surfaces"
|
||||
],
|
||||
"properties": {
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$"
|
||||
},
|
||||
"ownerRole": {
|
||||
"const": "localisation-product-owner"
|
||||
},
|
||||
"reviewer": {
|
||||
"type": "null"
|
||||
},
|
||||
"status": {
|
||||
"const": "planned"
|
||||
},
|
||||
"surfaces": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"ui",
|
||||
"backend",
|
||||
"communications",
|
||||
"documents",
|
||||
"jobDiscovery",
|
||||
"marketing"
|
||||
],
|
||||
"properties": {
|
||||
"ui": {
|
||||
"const": "planned"
|
||||
},
|
||||
"backend": {
|
||||
"const": "planned"
|
||||
},
|
||||
"communications": {
|
||||
"const": "planned"
|
||||
},
|
||||
"documents": {
|
||||
"const": "planned"
|
||||
},
|
||||
"jobDiscovery": {
|
||||
"const": "planned"
|
||||
},
|
||||
"marketing": {
|
||||
"const": "planned"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
# Risks, blockers and independent approval
|
||||
|
||||
| ID | Risk / unresolved item | Owner and closure evidence |
|
||||
|---|---|---|
|
||||
| I00-01 | Catalogue scan is not every-string contextual inventory | UI/backend owners triage literals, all callers, runtime/ARIA/export states; no unknown required surface |
|
||||
| I00-02 | CLDR is not complete national/regional legal authority | Product research owner checks official country records, political/territory scope and versioned licence before activating suggestions |
|
||||
| I00-03 | No allocated translators/native reviewers | Product owner assigns named reviewers per locale/surface; critical legal/billing/security needs qualified review |
|
||||
| I00-04 | Implementation predecessor gates unresolved | Owner records Phase10/product review and explicit I18N entry against accepted baseline; this document cannot approve them |
|
||||
| I00-05 | Plurals, gender, inflection, word order | ICU whole-sentence contexts, Slavic/Irish/Welsh fixtures, native review; no fragment concatenation |
|
||||
| I00-06 | Turkish casing, Greek accents, Serbian script, names | Locale-aware search/display separate from invariant IDs; preserve original names and qualification titles |
|
||||
| I00-07 | Dates/timezones/DST, salary/currency | UTC storage, partial-date precision, explicit timezone; formatting never FX conversion |
|
||||
| I00-08 | Fonts/glyphs, RTL readiness, long labels | Licensed extended Latin/Greek/Cyrillic font tests, logical layout and focus; RTL activation only when approved |
|
||||
| I00-09 | Career fact loss, frozen PDF drift, stale writes | UI switch never mutates facts/history/undo; ApplicationAssetsService and revision contracts retained |
|
||||
| I00-10 | Private content external translation | No PII export; separately approved consent/contracts/data controls before external translation; no live-model calls |
|
||||
| I00-11 | Draft locale exposed as supported | Per-surface publication gate, English fallback; no selector/SEO claims from catalogue presence |
|
||||
| I00-12 | Backend literal errors and queue drift | Stable error codes, typed escaped params, deterministic event-time locale and retry tests |
|
||||
|
||||
Public release requires required-key completeness, syntax/placeholders/plurals and escaping checks, whole critical journeys, native/proficient review, accessibility/device/PDF tests, country/provider truthfulness, owner release decision and actual deployed identity. Status advances separately per surface: planned → draft/machine_draft → review_required → reviewed → qa_passed → release_approved → released. No generated JSON grants review or approval. Approval evidence must be externally controlled and source-bound. All catalogue entries here are `planned`; existing en/nb code presence is independently recorded, not relabelled as newly qualified release.
|
||||
|
||||
Human/native/legal/device/physical-print review: pending, not executed. Provider and live-model tests: not run. Implementation/release: not authorised. Independent planning can continue on I00-01/02 without waiting for runtime gates.
|
||||
@@ -0,0 +1,22 @@
|
||||
# Surface inventory and message accounting
|
||||
|
||||
All rows need a source-bound contextual census before I18N-00 acceptance. Existing-catalogue inventory is in `MESSAGE_INVENTORY.json`; partial JSX/accessibility literals are now recorded in `FRONTEND_LITERAL_CANDIDATES.json`, with direct reads and dynamic calls in separate inventories. This is not a 100% coverage claim. Namespace assignments remain provisional until caller review; translator context cannot be inferred safely from key names alone. The AST inventory identifies syntactically named `t` calls, not symbol-resolved calls. See CONTEXT_TRIAGE.md for reviewed literal context and the 108 resolved conditional calls; 80 table/template/wrapper calls remain.
|
||||
|
||||
| Surface / proposed namespace | Source owner / inspection target | Content boundary and required evidence |
|
||||
|---|---|---|
|
||||
| Account, onboarding, recovery / auth | views LoginPage, ForgotPasswordPage, ResetPasswordPage, VerifyEmailPage; AuthController | Forms, ARIA, errors, consent, email templates, multi-device hydration; preserve token URLs |
|
||||
| Dashboard, navigation, mobile / app | App routing, dashboard components | Hidden/empty/loading/error states, keyboard labels, tooltips and layout expansion |
|
||||
| Jobs, discovery, applications / jobs | JobDiscoveryPage, ApplicationWorkspacePage; JobPipeline | Translate controlled status labels, never stored identity/status codes or original advertisements |
|
||||
| Career facts, imports / career | CareerProfilePage, career/CvImportReview, CvImportMappingEditor; CareerProfileService; Python app.py | Original facts and uncertainty remain; parser/script quality separate from UI coverage |
|
||||
| CV editor, templates, letters / documents | CvBuilderEditor, DocumentsPage; ThemedCvRenderer, CoverLetterDocumentRenderer, ApplicationAssetsService | Output locale independent; headings, dates, filename labels; preserve null-as-inherit and JobApplication.Version |
|
||||
| PDF, public CV / exports | PublicCvPage, PlaywrightCvPdfExporter, CvDocumentFonts | Font licence/glyph matrix, selectable text, physical print, frozen snapshots and public access |
|
||||
| Email, inbox, reminders / communications | CorrespondenceInboxPage, GmailReviewPage, SmtpEmailSender, FollowUpReminderHostedService | User correspondence is private content; subject/preheader/plain/HTML copy and queued locale policy |
|
||||
| Settings, linked accounts / settings | ConnectedAccountsPage, EmailProviderConnections | Provider names stay proper nouns; OAuth errors and cancellation need usable fallback |
|
||||
| Billing, subscriptions / billing | StripeBillingGateway, entitlement UI | Amount and currency invariant; legal/tax wording needs qualified approval |
|
||||
| Admin and operations / admin | AdminUsersPage, AdminSystemPage, AdminProcessingPage, AdminAuditPage, OperationsPage; OperationEventLog | Stable diagnostic codes; localized presentation without multilingual log parsing or PII leaks |
|
||||
| Analytics, comparisons / analytics | AnalyticsPage, OfferComparisonPage | Plurals, dates, currency and accessible chart/table labels |
|
||||
| Marketing, SEO, legal / public | LandingPage, RouteErrorPage, NotFoundPage; future Launch Site | No locale claims, hreflang or indexing for draft pages; owner mockup gate untouched |
|
||||
| Backend/API/queues / errors | Controllers, UserNotificationStore, AiOperationQueue, CvProcessingQueue | Stable code plus typed parameters; UTC storage; deterministic queue-recipient locale |
|
||||
| Python/AI / processing | tools/summarizer/app.py | Machine codes vs user text, validation and document-language metadata; no live-model qualification |
|
||||
|
||||
For each required message record: stable ID, namespace, exact source, source hash/line, all callers, English text, context/screenshot, typed placeholders, plural/select variants, sensitivity, owner, translator and independent review state. Sensitive values are runtime parameters, never translation exports. Static public UI text is eligible; names, CV facts, messages, tokens, job ads and employer claims are not automatically translated. Existing flat keys remain stable through migration; do not bulk rename for aesthetics.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Application asset label domains — 2 November 2026
|
||||
|
||||
Nine historical dynamic sites resolved by source review; no source/runtime behavior changed.
|
||||
|
||||
| Historical line(s) | Finite domain / fallback |
|
||||
| --- | --- |
|
||||
| 389 | Package labels iterate only TailoredCvText and RecruiterMessageDraft; the assetsPackage prefix therefore yields two keys. Material body remains user/application data. |
|
||||
| 456/458 | tailoringLabels is a Map of six fixed title/detail tuples; unknown kinds preserve server-supplied title/detail as data. |
|
||||
| 687/688 | historySources and historyAiActions are Maps with five and ten fixed values. Guarded get calls preserve unknown historical source/action labels verbatim. |
|
||||
| 830 | Toolbar iterates four literal key/label pairs. |
|
||||
| 857/895 | COVER_LETTER_ACTIONS is a nine-entry literal array; selection uses find with a fixed coverActionGenerate fallback. |
|
||||
| 866 | Tone options iterate six literal key pairs. |
|
||||
|
||||
All relevant components obtain t from useI18n. The 39 distinct keys across these domains are defined in both catalogues; local assertion verified all 78 definitions. Map lookups have no inherited Object-property ambiguity. UI language and selected document-generation language remain separate. This does not activate AI, re-run PDF metrics or change canonical ownership, revision/history, entitlements, package snapshots or stored text.
|
||||
|
||||
The two error.messageKey sites remain pending until all error constructors/callers are traced. Key-domain review is not complete translator context, professional/native translation review or production capability acceptance. Existing unchanged contract tests were not rerun solely to record these static facts.
|
||||
|
||||
Machine register binds exact expressions and reviewed source hash. Reconciliation: 108 original + 37 supplemental = 145 resolved / 43 pending of 188. Do not count earlier 136/52 as additional work to add to this total.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Attachment preview accessible label — 2026-11-02
|
||||
|
||||
Status: implemented and locally verified; publication recorded in Git history.
|
||||
|
||||
The shared Attachments PDF iframe used the generic English title `pdf`. It now reuses `attachmentsPreviewTitle` with the displayed filename, matching the existing dialog title in English and Norwegian Bokmål. This addresses the generic PDF-title item from the literal review (I00-15).
|
||||
|
||||
Scope: `Attachments.tsx`, its attachment regression tests, and this evidence. ApplicationWorkspacePage remains the caller. No route, API, file-content, upload/download, object-URL lifecycle, persistence, dependency or generated-content language behavior changed.
|
||||
|
||||
Verification:
|
||||
|
||||
- RED: two new language cases failed against the previous generic title; three existing attachment cases passed.
|
||||
- PASSED: attachment and file-picker suites, 13 tests across two suites, zero failures, native exit 0. The language cases open the PDF preview through its translated button and verify the named iframe and blob URL.
|
||||
- PASSED: `npm run typecheck`, exit 0.
|
||||
- PASSED: ESLint for both changed source/test files with zero warnings, exit 0.
|
||||
- An earlier combined test execution passed all 13 assertions but exited 1 because the JSON report directory did not exist. Creating the local artifact directory and rerunning produced the successful execution above. These are overlapping runs, not 26 distinct tests.
|
||||
- Raw successful Jest evidence: local ignored `job-tracker-ui/.artifacts/attachment-label-results.json`.
|
||||
|
||||
Rollback: revert this package; no data conversion is involved. No new translation key was needed. The 80 unresolved dynamic translation calls remain open; this fix does not establish complete i18n or accessibility acceptance. Browser/native assistive-technology checks and a full production build were not rerun for this label-only change.
|
||||
|
||||
Next: continue the unresolved dynamic-call inventory and remaining literal triage; retain all independent workstream and release gates.
|
||||
@@ -0,0 +1,12 @@
|
||||
# Checklist dynamic translation review — 2026-11-02
|
||||
|
||||
Supplement to the historical dynamic inventory; no application behavior changed.
|
||||
|
||||
- RESOLVED: `ApplicationChecklist.tsx:207`, `t(keys[category])`. The local map at lines 200–206 contains five literal keys: checklistCategoryPreparation, checklistCategorySubmission, checklistCategoryFollowUp, checklistCategoryInterview, checklistCategoryCustom. Both English (translations.ts:3042–3046) and Bokmål (6163–6167) define all five. The component obtains `t` from useI18n at line 25 and passes it to the category helper. Unknown categories remain user/domain data displayed verbatim; they never become translation keys.
|
||||
- OPEN: the two checklist-item template calls at lines 214–215 and the two next-step calls in ApplicationWorkspacePage at lines 392/395. `checklistSystemTranslationToken` (applicationWorkspace.ts:94) excludes empty and learning-prefixed keys but otherwise converts arbitrary kebab-case strings. It does not restrict the input to the known translated system items. Catalogue entries alone cannot prove all provider/backend inputs are covered. Inspect the backend system-key producer and compatibility contract before declaring these four calls resolved or changing the fallback.
|
||||
|
||||
Reconciled progress: historical inventory has 188 dynamic calls, 108 previously resolved and 80 pending. This supplemental review resolves one additional call: 109 resolved, 79 pending. The original inventory remains a historical source-bound snapshot; do not add this result to a rerun total or label all translation contexts accepted.
|
||||
|
||||
Evidence: graph query located ApplicationChecklist, useI18n, applicationWorkspace and ApplicationWorkspacePage; direct source review confirmed the map, translator ownership, token behavior and ten catalogue definitions. Static review only; no runtime coverage claim.
|
||||
|
||||
Backend follow-through: ApplicationChecklistService.cs:105–132 defines 14 default system keys. A local assertion converted each through the frontend's kebab-to-camel rule and verified all 28 title/description keys occur in both catalogues (56 definitions). SeedMissingAsync:353–405 adds absent defaults and retains existing persisted rows; Project:482 passes through SystemKey. Learning keys use a separate prefix and remain excluded by the helper. Current defaults are covered; legacy/unrecognised persisted keys still prevent resolving the four calls without a compatibility/fallback decision. No database was accessed.
|
||||
@@ -0,0 +1,13 @@
|
||||
# Historical checklist text fallback
|
||||
|
||||
2 November 2026. CONFIRMED functional compatibility defect: an unknown/retired persisted system key was converted into nonexistent catalogue keys, hiding the supplied historical task text. Diagnostic RED: three unknown-token cases and EN/NB UI cases failed; nine unselected cases were not run in that diagnostic execution.
|
||||
|
||||
Minimal shared fix in checklistSystemTranslationToken: retain kebab-to-camel conversion, but return a token only when the English fallback catalogue owns both title and description keys. Otherwise return null, activating the existing original-text fallback in ApplicationChecklist and workspace next-step presentation. Learning/custom boundaries remain intact. This does not delete tasks, rewrite stored keys/text, change backend generation, enable AI, or change revision/ownership/API contracts. English is the existing reliable translation fallback; current 14 system title/description pairs also exist in Bokmål.
|
||||
|
||||
PASSED: 37 tests in checklist-translation-token, application-checklist and application-workspace-overlay, zero failures/pending, exit 0. New tests prove unknown/inherited-name keys do not manufacture labels, historical text survives EN/NB, and known/learning/null behavior remains. Full typecheck and scoped three-file ESLint passed. Native local report: job-tracker-ui/.artifacts/checklist-unknown-system-keys.json. Full build/browser and integrated security acceptance are not claimed here. Rollback: revert the helper/import and new tests; no database rollback.
|
||||
|
||||
Four dynamic checklist/next-step sites are now bounded by actual catalogue membership, not merely a presumed backend key list. The machine register binds applicationWorkspace.ts as their owner dependency.
|
||||
|
||||
Two further unchanged sites reviewed: ApplicationWorkspacePage.tsx:505 receives only WORKSPACE_SECTIONS.map(s.key), a fixed seven-key label domain; CareerEvidenceBank.tsx:83 first checks a six-value literal category array before constructing the prefixed key, leaving unknown category text untouched. All 41 distinct system/workspace/evidence keys are defined in both catalogues. This is key-domain evidence, not native language quality or complete every-string context.
|
||||
|
||||
Reconciliation: 108 original + 62 supplemental sites = 170 resolved / 18 pending of 188. This supersedes the earlier open checklist gap without reclassifying broader i18n/security acceptance.
|
||||
@@ -0,0 +1,13 @@
|
||||
# Current localisation continuation
|
||||
|
||||
Register portability correction: schema 3 explicitly normalises CRLF to LF when hashing reviewed TS/TSX/JSON text and the original inventory; historicalSourceSha256 retains the original inventory's raw-hash meaning. This avoids treating Git checkout line-ending conversion as a source edit. Integration initially detected the raw inventory mismatch; original snapshot contents and historical source hashes were preserved, and both worktrees are revalidated with the declared encoding. This is evidence portability, not renewed runtime execution.
|
||||
|
||||
2 November 2026. Historical 188 dynamic-call census finite key-domain review COMPLETE; full I18N-00 / European locale release acceptance remains INCOMPLETE. Original source-bound snapshots are retained under 00/. Machine register schema 2 binds inventory indices, source hashes and selected owner dependencies; 108 original plus 80 supplemental calls cover all 188 indices without overlap. Static review does not establish all translator sensitivity/context, every visible/backend string, grammar/plural quality, runtime coverage or native review.
|
||||
|
||||
Independent fixes now include descriptive attachment preview labels; session-change feedback outside private providers; safe inherited-name handling for completeness/timeline/pipeline/processing/interview/purpose labels; and original historical checklist-text fallback for unrecognised system keys. No user data, canonical Career Profile ownership, CV/letter revision or final design contract was rewritten. No dependencies or migrations added.
|
||||
|
||||
Latest implementation qualification: processing monitor 17 tests; checklist/workspace 37 tests; interview/document index 41 tests. Each selected execution passed with zero failures/pending, scoped lint and full typecheck. They are disjoint selected suites, not complete application acceptance. Earlier diagnostic failures and overlapping executions remain separately recorded in their reports. A fresh combined Next 16.4.1 static build after the final fixes passed, exit 0, including TypeScript and all three generated pages. No deployment, live API/provider/model or final integrated security test implied.
|
||||
|
||||
Sanitized native reports: evidence/processing-label-fallbacks.json.gz, evidence/checklist-unknown-system-keys.json.gz, evidence/interview-document-fallbacks.json.gz. Earlier scope reports and prior evidence remain intact. Generated AST graph stays untracked; optional SQL/PDF extractor gaps do not change application acceptance.
|
||||
|
||||
Next independent work: complete direct-access and literal review, every-string context/sensitivity triage, European catalogue/territory/source qualification and approved glossary/translation QA preparation. Full registry/persistence/plural engine architecture still needs prerequisite acceptance and the documented decisions; no proposed locale is published. Native translators/market priorities, Montenegrin tag identity, final design, provider rights, staging/paid scans, hosted CI and production/recovery decisions remain distinct gates. Final integrated security testing follows the relevant application changes.
|
||||
@@ -0,0 +1,13 @@
|
||||
# CV, board and field-review translation domains
|
||||
|
||||
2 November 2026. Eight more historical sites resolved by source/control-flow review; no implementation change or repeated PDF qualification.
|
||||
|
||||
- KanbanBoard.tsx:194/446 iterate the three literal PIPELINE_GROUPS labels. Custom statuses use their separately handled Other column; they do not create group translation keys. The pipeline owner dependency is hash-bound in the register.
|
||||
- CvBuilderEditor.tsx:669 iterates the literal indices 1/0/2/3/4 over five literal studio-tab keys. This lookup is independent of arbitrary tab state.
|
||||
- CvBuilderEditor.tsx:1490 maps four historical sources with an existing Object.hasOwn guard and returns unknown source text as data.
|
||||
- CvBuilderEditor.tsx:486 uses getCvExportFailureKey, which returns only cvEmptyContent/cvPdfBusy/cvPdfTimedOut/undefined after explicit status/code checks. Unrecognised errors use existing error handling. CvBuilderPage.tsx:178 has only four literal action-error constructors plus that same export helper. cvBuilder.ts is recorded as an owner dependency. No server/PDF renderer contract was changed.
|
||||
- CareerProfileSections.tsx:45/46 uses confidenceTone, whose four branches return fixed confidence labels. Confidence is interpolated as data; no confidence value becomes a key. Missing metadata is not presented as an accepted profile fact.
|
||||
|
||||
All components obtain t from useI18n. Assertion verified all 23 distinct keys in both catalogues. This is finite key/control-flow coverage, not native terminology, all placeholders/plurals, semantic CV quality or complete accessibility. Canonical profile ownership, revision-safe document history, source provenance and PDF metric/visual acceptance remain untouched.
|
||||
|
||||
Register reconciliation: 108 original + 56 supplemental sites = 164 resolved / 24 pending of 188. Existing earlier tests are historical evidence for unchanged implementations, not fresh executions by this review.
|
||||
@@ -0,0 +1,917 @@
|
||||
{
|
||||
"schemaVersion": 3,
|
||||
"inventorySha256": "79928688a7b1a4961286aaaf48c028eb13ae8286d72d132561d6b5fdd192e323",
|
||||
"historicalCalls": 188,
|
||||
"previouslyResolved": 108,
|
||||
"additionalResolved": 80,
|
||||
"resolved": 188,
|
||||
"pending": 0,
|
||||
"evidence": [
|
||||
"CHECKLIST_DYNAMIC_REVIEW.md",
|
||||
"FINITE_OWNER_MAP_REVIEW.md",
|
||||
"UNKNOWN_LABEL_FALLBACKS.md",
|
||||
"PIPELINE_ALIAS_FALLBACK.md",
|
||||
"PROCESSING_LABEL_FALLBACKS.md",
|
||||
"REMINDER_INTERVIEW_KEY_REVIEW.md",
|
||||
"ASSET_KEY_REVIEW.md",
|
||||
"LANDING_DOCUMENT_KEY_REVIEW.md",
|
||||
"CV_BOARD_KEY_REVIEW.md",
|
||||
"CHECKLIST_UNKNOWN_SYSTEM_KEYS.md",
|
||||
"ERROR_STATE_KEY_REVIEW.md",
|
||||
"IMPORT_OPERATION_KEY_REVIEW.md",
|
||||
"INTERVIEW_DOCUMENT_FALLBACKS.md"
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationChecklist.tsx",
|
||||
"historicalLine": 207,
|
||||
"expression": "t(keys[category])",
|
||||
"historicalSourceSha256": "88fdcd24914cf074aedee72a94b0c420c96a16dfe7194142daf22ec0ad8370ff",
|
||||
"reviewedSourceSha256": "0f198e90559b30952cbf7d0f4973211e3fbffcbb183e9e4b4f2ee2e2a9d02f56",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 14
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationIntelligence.tsx",
|
||||
"historicalLine": 367,
|
||||
"expression": "t(keys[category])",
|
||||
"historicalSourceSha256": "89addd1e262ac9c5e37a718acb31d270a132269fe3518d6711e91f3da76ae1a4",
|
||||
"reviewedSourceSha256": "90540576dba4d705ed9234df7091f2b725500d4301287ba151a520956c03f073",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 18
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationIntelligence.tsx",
|
||||
"historicalLine": 373,
|
||||
"expression": "t(`matchScoreBand_${band}`)",
|
||||
"historicalSourceSha256": "89addd1e262ac9c5e37a718acb31d270a132269fe3518d6711e91f3da76ae1a4",
|
||||
"reviewedSourceSha256": "90540576dba4d705ed9234df7091f2b725500d4301287ba151a520956c03f073",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 19
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/CoverLetterDocument.tsx",
|
||||
"historicalLine": 36,
|
||||
"expression": "t(fieldLabels[key])",
|
||||
"historicalSourceSha256": "17ea0c608db7bc05171fb3c44f378c1acaa78fbdd337f9330002ef6346b92546",
|
||||
"reviewedSourceSha256": "d76d0cc3a21230043e91a0d28d03953c236b731c5fecd48e2ba11fd610281053",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 30
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/CoverLetterDocument.tsx",
|
||||
"historicalLine": 153,
|
||||
"expression": "t(fieldLabels[key])",
|
||||
"historicalSourceSha256": "17ea0c608db7bc05171fb3c44f378c1acaa78fbdd337f9330002ef6346b92546",
|
||||
"reviewedSourceSha256": "d76d0cc3a21230043e91a0d28d03953c236b731c5fecd48e2ba11fd610281053",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 33
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/CvTemplateGallery.tsx",
|
||||
"historicalLine": 33,
|
||||
"expression": "t(labels[0])",
|
||||
"historicalSourceSha256": "dda733af709e419b17fb254d65fbb27c8dbba01d032a1c0266517fc27d134eb7",
|
||||
"reviewedSourceSha256": "d7f28fd9479dabd5a988b6a8b656c36c6c9b6622beb13530b7011590edb7965e",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 35
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/CvTemplateGallery.tsx",
|
||||
"historicalLine": 33,
|
||||
"expression": "t(labels[1])",
|
||||
"historicalSourceSha256": "dda733af709e419b17fb254d65fbb27c8dbba01d032a1c0266517fc27d134eb7",
|
||||
"reviewedSourceSha256": "d7f28fd9479dabd5a988b6a8b656c36c6c9b6622beb13530b7011590edb7965e",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 36
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/CvTemplateGallery.tsx",
|
||||
"historicalLine": 33,
|
||||
"expression": "t(labels[2])",
|
||||
"historicalSourceSha256": "dda733af709e419b17fb254d65fbb27c8dbba01d032a1c0266517fc27d134eb7",
|
||||
"reviewedSourceSha256": "d7f28fd9479dabd5a988b6a8b656c36c6c9b6622beb13530b7011590edb7965e",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 37
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/DashboardGrid.tsx",
|
||||
"historicalLine": 47,
|
||||
"expression": "t(WIDGETS.find(widget => widget.id === id)!.titleKey)",
|
||||
"historicalSourceSha256": "201f6d7580af81e92e1fe9c759972a4d2bc1f865cbbe651dd7977e10a130062e",
|
||||
"reviewedSourceSha256": "1f9dfb5dc17b7804c00cca823e7045ec1804d3bcf162bb56590bcd3cf85358d8",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 38
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/DashboardGrid.tsx",
|
||||
"historicalLine": 63,
|
||||
"expression": "t(WIDGETS.find(item => item.id === widget.id)!.titleKey)",
|
||||
"historicalSourceSha256": "201f6d7580af81e92e1fe9c759972a4d2bc1f865cbbe651dd7977e10a130062e",
|
||||
"reviewedSourceSha256": "1f9dfb5dc17b7804c00cca823e7045ec1804d3bcf162bb56590bcd3cf85358d8",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 39
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/DashboardLayoutEditor.tsx",
|
||||
"historicalLine": 77,
|
||||
"expression": "t(entry.titleKey)",
|
||||
"historicalSourceSha256": "faab5c4d63d650c02f4fa0a6f0e2880b1b21535b444a143e1989d9e378f0e16f",
|
||||
"reviewedSourceSha256": "86714ee75bfeddfe62e4c4c9d535c4e641d1213422615fab76838d6aa401984a",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 45
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/DashboardView.tsx",
|
||||
"historicalLine": 259,
|
||||
"expression": "t(key)",
|
||||
"historicalSourceSha256": "37952276ba7d3d0c65df734c816e317b54d50034838dc91f56e3de6f1eb17879",
|
||||
"reviewedSourceSha256": "65113412e4a21e23d093d4af52affab7d24997a3912e7dada9e3e276954d77e0",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 48
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/DashboardView.tsx",
|
||||
"historicalLine": 372,
|
||||
"expression": "t(WIDGETS.find(item => item.id === widget.id)!.titleKey)",
|
||||
"historicalSourceSha256": "37952276ba7d3d0c65df734c816e317b54d50034838dc91f56e3de6f1eb17879",
|
||||
"reviewedSourceSha256": "65113412e4a21e23d093d4af52affab7d24997a3912e7dada9e3e276954d77e0",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 57
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/pipeline.ts",
|
||||
"historicalLine": 149,
|
||||
"expression": "t(LABEL_KEYS[normalized])",
|
||||
"historicalSourceSha256": "fa755ced07a90cef79ceff1622049adc7ed7df3794c942512d9675ef04af8843",
|
||||
"reviewedSourceSha256": "f5cb6d36e81268b2533c4f5ba4e53781007f166c217a4d805c9a8f9f66c2b9ca",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 79
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/AdminProcessingPage.tsx",
|
||||
"historicalLine": 153,
|
||||
"expression": "t(tasks[value])",
|
||||
"historicalSourceSha256": "13973a963ec247c2b61230f5b96ebe1e5feb73ccc9374f15c9bd05f2b3699d1c",
|
||||
"reviewedSourceSha256": "514cbf45298e165334ab502b3833e08fc7a145c5790a80979dc4b0485857316b",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 87
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/AdminProcessingPage.tsx",
|
||||
"historicalLine": 173,
|
||||
"expression": "t(key as TranslationKey)",
|
||||
"historicalSourceSha256": "13973a963ec247c2b61230f5b96ebe1e5feb73ccc9374f15c9bd05f2b3699d1c",
|
||||
"reviewedSourceSha256": "514cbf45298e165334ab502b3833e08fc7a145c5790a80979dc4b0485857316b",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 88
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/AdminProcessingPage.tsx",
|
||||
"historicalLine": 205,
|
||||
"expression": "t(key)",
|
||||
"historicalSourceSha256": "13973a963ec247c2b61230f5b96ebe1e5feb73ccc9374f15c9bd05f2b3699d1c",
|
||||
"reviewedSourceSha256": "514cbf45298e165334ab502b3833e08fc7a145c5790a80979dc4b0485857316b",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 94
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/career/careerCompletenessLabels.ts",
|
||||
"historicalLine": 15,
|
||||
"expression": "t(key)",
|
||||
"historicalSourceSha256": "b841cc4af73307fa33e772b8a10ddfe9620a7566f2b30ef34eb2b832da02d41e",
|
||||
"reviewedSourceSha256": "9e53eab4592a341b6b369b3408843b21b2c9233e9b765115367bb3586a1b8401",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "see named focused regression reports; static review alone is not runtime coverage",
|
||||
"inventoryEntryIndex": 187
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/AdminProcessingPage.tsx",
|
||||
"historicalLine": 130,
|
||||
"expression": "t(statuses[value] ?? 'processingUnknown')",
|
||||
"historicalSourceSha256": "13973a963ec247c2b61230f5b96ebe1e5feb73ccc9374f15c9bd05f2b3699d1c",
|
||||
"reviewedSourceSha256": "514cbf45298e165334ab502b3833e08fc7a145c5790a80979dc4b0485857316b",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "PROCESSING_LABEL_FALLBACKS.md; six inherited-key/language regressions plus eleven existing cases",
|
||||
"inventoryEntryIndex": 80
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/AdminProcessingPage.tsx",
|
||||
"historicalLine": 131,
|
||||
"expression": "t(stages[value])",
|
||||
"historicalSourceSha256": "13973a963ec247c2b61230f5b96ebe1e5feb73ccc9374f15c9bd05f2b3699d1c",
|
||||
"reviewedSourceSha256": "514cbf45298e165334ab502b3833e08fc7a145c5790a80979dc4b0485857316b",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "PROCESSING_LABEL_FALLBACKS.md; six inherited-key/language regressions plus eleven existing cases",
|
||||
"inventoryEntryIndex": 81
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/AdminProcessingPage.tsx",
|
||||
"historicalLine": 175,
|
||||
"expression": "t(tasks[item.taskType] ?? 'processingOther')",
|
||||
"historicalSourceSha256": "13973a963ec247c2b61230f5b96ebe1e5feb73ccc9374f15c9bd05f2b3699d1c",
|
||||
"reviewedSourceSha256": "514cbf45298e165334ab502b3833e08fc7a145c5790a80979dc4b0485857316b",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "PROCESSING_LABEL_FALLBACKS.md; six inherited-key/language regressions plus eleven existing cases",
|
||||
"inventoryEntryIndex": 89
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/AdminProcessingPage.tsx",
|
||||
"historicalLine": 191,
|
||||
"expression": "t(workers[item.name] ?? 'processingWorkerUntracked')",
|
||||
"historicalSourceSha256": "13973a963ec247c2b61230f5b96ebe1e5feb73ccc9374f15c9bd05f2b3699d1c",
|
||||
"reviewedSourceSha256": "514cbf45298e165334ab502b3833e08fc7a145c5790a80979dc4b0485857316b",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "PROCESSING_LABEL_FALLBACKS.md; six inherited-key/language regressions plus eleven existing cases",
|
||||
"inventoryEntryIndex": 91
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/AdminProcessingPage.tsx",
|
||||
"historicalLine": 212,
|
||||
"expression": "t(kinds[item.kind] ?? 'processingUnknown')",
|
||||
"historicalSourceSha256": "13973a963ec247c2b61230f5b96ebe1e5feb73ccc9374f15c9bd05f2b3699d1c",
|
||||
"reviewedSourceSha256": "514cbf45298e165334ab502b3833e08fc7a145c5790a80979dc4b0485857316b",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "PROCESSING_LABEL_FALLBACKS.md; six inherited-key/language regressions plus eleven existing cases",
|
||||
"inventoryEntryIndex": 96
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/InterviewPrep.tsx",
|
||||
"historicalLine": 236,
|
||||
"expression": "t(selected.labelKey)",
|
||||
"historicalSourceSha256": "eadf345d3613ad867788f6a74a8fd1fbba508bb81ec7bc87fe54e4f66dadc69b",
|
||||
"reviewedSourceSha256": "ef8161cc982c9068d3c7eec80f7dd6ef565f05ba0b36bcf58e3b643285b22f0f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review only; no inference or provider calls",
|
||||
"inventoryEntryIndex": 63
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/InterviewPrep.tsx",
|
||||
"historicalLine": 261,
|
||||
"expression": "t(selected.labelKey)",
|
||||
"historicalSourceSha256": "eadf345d3613ad867788f6a74a8fd1fbba508bb81ec7bc87fe54e4f66dadc69b",
|
||||
"reviewedSourceSha256": "ef8161cc982c9068d3c7eec80f7dd6ef565f05ba0b36bcf58e3b643285b22f0f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review only; no inference or provider calls",
|
||||
"inventoryEntryIndex": 64
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/InterviewPrep.tsx",
|
||||
"historicalLine": 285,
|
||||
"expression": "t(item.labelKey)",
|
||||
"historicalSourceSha256": "eadf345d3613ad867788f6a74a8fd1fbba508bb81ec7bc87fe54e4f66dadc69b",
|
||||
"reviewedSourceSha256": "ef8161cc982c9068d3c7eec80f7dd6ef565f05ba0b36bcf58e3b643285b22f0f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review only; no inference or provider calls",
|
||||
"inventoryEntryIndex": 65
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/RemindersView.tsx",
|
||||
"historicalLine": 35,
|
||||
"expression": "t(key)",
|
||||
"historicalSourceSha256": "1eedbc443761001e86be1f322691ab4058553033e764b0c6cf9c93ce4fab05c0",
|
||||
"reviewedSourceSha256": "e550e76cb28cb4236100bb1a35158a329e4ecd3a12be56f6cdaf28fb75fa0091",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review only; no inference or provider calls",
|
||||
"inventoryEntryIndex": 76
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/RemindersView.tsx",
|
||||
"historicalLine": 38,
|
||||
"expression": "t(dayKey, { days: match[1] })",
|
||||
"historicalSourceSha256": "1eedbc443761001e86be1f322691ab4058553033e764b0c6cf9c93ce4fab05c0",
|
||||
"reviewedSourceSha256": "e550e76cb28cb4236100bb1a35158a329e4ecd3a12be56f6cdaf28fb75fa0091",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review only; no inference or provider calls",
|
||||
"inventoryEntryIndex": 77
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"historicalLine": 389,
|
||||
"expression": "t(`assetsPackage${key}` as \"assetsPackageTailoredCvText\")",
|
||||
"historicalSourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"reviewedSourceSha256": "b5f14b41de10474e2bcd709b91b1becb7b7db4aba021fba29e4e9e44a7a2947f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review; no AI inference or provider calls",
|
||||
"inventoryEntryIndex": 4
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"historicalLine": 456,
|
||||
"expression": "t(labels[0])",
|
||||
"historicalSourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"reviewedSourceSha256": "b5f14b41de10474e2bcd709b91b1becb7b7db4aba021fba29e4e9e44a7a2947f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review; no AI inference or provider calls",
|
||||
"inventoryEntryIndex": 5
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"historicalLine": 458,
|
||||
"expression": "t(labels[1])",
|
||||
"historicalSourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"reviewedSourceSha256": "b5f14b41de10474e2bcd709b91b1becb7b7db4aba021fba29e4e9e44a7a2947f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review; no AI inference or provider calls",
|
||||
"inventoryEntryIndex": 6
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"historicalLine": 687,
|
||||
"expression": "t(sourceKey)",
|
||||
"historicalSourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"reviewedSourceSha256": "b5f14b41de10474e2bcd709b91b1becb7b7db4aba021fba29e4e9e44a7a2947f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review; no AI inference or provider calls",
|
||||
"inventoryEntryIndex": 7
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"historicalLine": 688,
|
||||
"expression": "t(actionKey)",
|
||||
"historicalSourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"reviewedSourceSha256": "b5f14b41de10474e2bcd709b91b1becb7b7db4aba021fba29e4e9e44a7a2947f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review; no AI inference or provider calls",
|
||||
"inventoryEntryIndex": 8
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"historicalLine": 830,
|
||||
"expression": "t(label)",
|
||||
"historicalSourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"reviewedSourceSha256": "b5f14b41de10474e2bcd709b91b1becb7b7db4aba021fba29e4e9e44a7a2947f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review; no AI inference or provider calls",
|
||||
"inventoryEntryIndex": 9
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"historicalLine": 857,
|
||||
"expression": "t(item.labelKey)",
|
||||
"historicalSourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"reviewedSourceSha256": "b5f14b41de10474e2bcd709b91b1becb7b7db4aba021fba29e4e9e44a7a2947f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review; no AI inference or provider calls",
|
||||
"inventoryEntryIndex": 10
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"historicalLine": 866,
|
||||
"expression": "t(key as any)",
|
||||
"historicalSourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"reviewedSourceSha256": "b5f14b41de10474e2bcd709b91b1becb7b7db4aba021fba29e4e9e44a7a2947f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review; no AI inference or provider calls",
|
||||
"inventoryEntryIndex": 11
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"historicalLine": 895,
|
||||
"expression": "t(COVER_LETTER_ACTIONS.find((item) => item.key === action)?.labelKey ?? \"coverActionGenerate\")",
|
||||
"historicalSourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"reviewedSourceSha256": "b5f14b41de10474e2bcd709b91b1becb7b7db4aba021fba29e4e9e44a7a2947f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static owner-map review; no AI inference or provider calls",
|
||||
"inventoryEntryIndex": 13
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/DocumentsPage.tsx",
|
||||
"historicalLine": 222,
|
||||
"expression": "t(label)",
|
||||
"historicalSourceSha256": "fa53a826ebbdbc868c8b21a0387138c12611d3e2867de0fa0e5f6d8483471eb9",
|
||||
"reviewedSourceSha256": "0b9aaba2c102b4346b10386107b3822016b4f680bb9ab6b58c24ef80b74080fc",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static source review; no marketing or accessibility acceptance",
|
||||
"inventoryEntryIndex": 134
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/DocumentsPage.tsx",
|
||||
"historicalLine": 225,
|
||||
"expression": "t(states.find(([value]) => value === item.state)?.[1] ?? \"documentsCurrent\")",
|
||||
"historicalSourceSha256": "fa53a826ebbdbc868c8b21a0387138c12611d3e2867de0fa0e5f6d8483471eb9",
|
||||
"reviewedSourceSha256": "0b9aaba2c102b4346b10386107b3822016b4f680bb9ab6b58c24ef80b74080fc",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static source review; no marketing or accessibility acceptance",
|
||||
"inventoryEntryIndex": 135
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/DocumentsPage.tsx",
|
||||
"historicalLine": 231,
|
||||
"expression": "t(types.find(([value]) => value === item.type)?.[1] ?? \"documentsTypeAttachment\")",
|
||||
"historicalSourceSha256": "fa53a826ebbdbc868c8b21a0387138c12611d3e2867de0fa0e5f6d8483471eb9",
|
||||
"reviewedSourceSha256": "0b9aaba2c102b4346b10386107b3822016b4f680bb9ab6b58c24ef80b74080fc",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static source review; no marketing or accessibility acceptance",
|
||||
"inventoryEntryIndex": 137
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/DocumentsPage.tsx",
|
||||
"historicalLine": 264,
|
||||
"expression": "t(title)",
|
||||
"historicalSourceSha256": "fa53a826ebbdbc868c8b21a0387138c12611d3e2867de0fa0e5f6d8483471eb9",
|
||||
"reviewedSourceSha256": "0b9aaba2c102b4346b10386107b3822016b4f680bb9ab6b58c24ef80b74080fc",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static source review; no marketing or accessibility acceptance",
|
||||
"inventoryEntryIndex": 144
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/DocumentsPage.tsx",
|
||||
"historicalLine": 265,
|
||||
"expression": "t(title)",
|
||||
"historicalSourceSha256": "fa53a826ebbdbc868c8b21a0387138c12611d3e2867de0fa0e5f6d8483471eb9",
|
||||
"reviewedSourceSha256": "0b9aaba2c102b4346b10386107b3822016b4f680bb9ab6b58c24ef80b74080fc",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static source review; no marketing or accessibility acceptance",
|
||||
"inventoryEntryIndex": 145
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/DocumentsPage.tsx",
|
||||
"historicalLine": 265,
|
||||
"expression": "t(label)",
|
||||
"historicalSourceSha256": "fa53a826ebbdbc868c8b21a0387138c12611d3e2867de0fa0e5f6d8483471eb9",
|
||||
"reviewedSourceSha256": "0b9aaba2c102b4346b10386107b3822016b4f680bb9ab6b58c24ef80b74080fc",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static source review; no marketing or accessibility acceptance",
|
||||
"inventoryEntryIndex": 146
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/LandingPage.tsx",
|
||||
"historicalLine": 87,
|
||||
"expression": "t(`landingPlan${plan.id === \"free\" ? \"Free\" : \"Pro\"}Feature${index + 1}` as TranslationKey)",
|
||||
"historicalSourceSha256": "2c550aa126e1db8786ed10b895d2403491ce50bf5073fc95b8a0fcb0cb0e083e",
|
||||
"reviewedSourceSha256": "aedcdbc8bfc22f7cd4bc9737a06b07f318ca7b4970836bd6c20e805ebfa36c74",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static source review; no marketing or accessibility acceptance",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/planCatalog.ts",
|
||||
"sha256": "068871cc43669d6f8a0581fbaefe8e6d0d2ab7f1c47e0b31cb131e767c2d13b2"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 160
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/LandingPage.tsx",
|
||||
"historicalLine": 172,
|
||||
"expression": "t(f.title)",
|
||||
"historicalSourceSha256": "2c550aa126e1db8786ed10b895d2403491ce50bf5073fc95b8a0fcb0cb0e083e",
|
||||
"reviewedSourceSha256": "aedcdbc8bfc22f7cd4bc9737a06b07f318ca7b4970836bd6c20e805ebfa36c74",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static source review; no marketing or accessibility acceptance",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/planCatalog.ts",
|
||||
"sha256": "068871cc43669d6f8a0581fbaefe8e6d0d2ab7f1c47e0b31cb131e767c2d13b2"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 162
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/LandingPage.tsx",
|
||||
"historicalLine": 173,
|
||||
"expression": "t(f.body)",
|
||||
"historicalSourceSha256": "2c550aa126e1db8786ed10b895d2403491ce50bf5073fc95b8a0fcb0cb0e083e",
|
||||
"reviewedSourceSha256": "aedcdbc8bfc22f7cd4bc9737a06b07f318ca7b4970836bd6c20e805ebfa36c74",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static source review; no marketing or accessibility acceptance",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/planCatalog.ts",
|
||||
"sha256": "068871cc43669d6f8a0581fbaefe8e6d0d2ab7f1c47e0b31cb131e767c2d13b2"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 163
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/LandingPage.tsx",
|
||||
"historicalLine": 190,
|
||||
"expression": "t(s.title)",
|
||||
"historicalSourceSha256": "2c550aa126e1db8786ed10b895d2403491ce50bf5073fc95b8a0fcb0cb0e083e",
|
||||
"reviewedSourceSha256": "aedcdbc8bfc22f7cd4bc9737a06b07f318ca7b4970836bd6c20e805ebfa36c74",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static source review; no marketing or accessibility acceptance",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/planCatalog.ts",
|
||||
"sha256": "068871cc43669d6f8a0581fbaefe8e6d0d2ab7f1c47e0b31cb131e767c2d13b2"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 164
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/LandingPage.tsx",
|
||||
"historicalLine": 191,
|
||||
"expression": "t(s.body)",
|
||||
"historicalSourceSha256": "2c550aa126e1db8786ed10b895d2403491ce50bf5073fc95b8a0fcb0cb0e083e",
|
||||
"reviewedSourceSha256": "aedcdbc8bfc22f7cd4bc9737a06b07f318ca7b4970836bd6c20e805ebfa36c74",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static source review; no marketing or accessibility acceptance",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/planCatalog.ts",
|
||||
"sha256": "068871cc43669d6f8a0581fbaefe8e6d0d2ab7f1c47e0b31cb131e767c2d13b2"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 165
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/KanbanBoard.tsx",
|
||||
"historicalLine": 194,
|
||||
"expression": "t(g.labelKey as any)",
|
||||
"historicalSourceSha256": "f9e7491cd3bd82214fb081571b46b55b31fb8e135fa5050e76896759c2e79a92",
|
||||
"reviewedSourceSha256": "e83ea475bbf1f33fd262bff154d3cbe8a4b20815beaa00b48312377663f4e48e",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static control-flow review; no PDF metric or model rerun",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/pipeline.ts",
|
||||
"sha256": "f5cb6d36e81268b2533c4f5ba4e53781007f166c217a4d805c9a8f9f66c2b9ca"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 71
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/KanbanBoard.tsx",
|
||||
"historicalLine": 446,
|
||||
"expression": "t(g.labelKey as any)",
|
||||
"historicalSourceSha256": "f9e7491cd3bd82214fb081571b46b55b31fb8e135fa5050e76896759c2e79a92",
|
||||
"reviewedSourceSha256": "e83ea475bbf1f33fd262bff154d3cbe8a4b20815beaa00b48312377663f4e48e",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static control-flow review; no PDF metric or model rerun",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/pipeline.ts",
|
||||
"sha256": "f5cb6d36e81268b2533c4f5ba4e53781007f166c217a4d805c9a8f9f66c2b9ca"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 74
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/CvBuilderEditor.tsx",
|
||||
"historicalLine": 486,
|
||||
"expression": "t(key)",
|
||||
"historicalSourceSha256": "b163cbe792e98cc1afdd775ffb595c1abb8b58c5fdd9a79bbd6fa6b552f868ae",
|
||||
"reviewedSourceSha256": "67d091b653ca151be853746eaaca1fd5d159a5670924705e228d8e2e9327ad0f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static control-flow review; no PDF metric or model rerun",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/cvBuilder.ts",
|
||||
"sha256": "c021bafeabbc8ad3526d1b10b90ad8f7f0958ded8ed26eac8c1d28b53098b64d"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 116
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/CvBuilderEditor.tsx",
|
||||
"historicalLine": 669,
|
||||
"expression": "t(([\"cvEditorTemplate\", \"cvEditorContent\", \"cvEditorCustomize\", \"cvEditorAiTools\", \"cvEditorHistory\"] as const)[value])",
|
||||
"historicalSourceSha256": "b163cbe792e98cc1afdd775ffb595c1abb8b58c5fdd9a79bbd6fa6b552f868ae",
|
||||
"reviewedSourceSha256": "67d091b653ca151be853746eaaca1fd5d159a5670924705e228d8e2e9327ad0f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static control-flow review; no PDF metric or model rerun",
|
||||
"inventoryEntryIndex": 117
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/CvBuilderEditor.tsx",
|
||||
"historicalLine": 1490,
|
||||
"expression": "t(source)",
|
||||
"historicalSourceSha256": "b163cbe792e98cc1afdd775ffb595c1abb8b58c5fdd9a79bbd6fa6b552f868ae",
|
||||
"reviewedSourceSha256": "67d091b653ca151be853746eaaca1fd5d159a5670924705e228d8e2e9327ad0f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static control-flow review; no PDF metric or model rerun",
|
||||
"inventoryEntryIndex": 131
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/CvBuilderPage.tsx",
|
||||
"historicalLine": 178,
|
||||
"expression": "t(actionError.key)",
|
||||
"historicalSourceSha256": "b4cc784cadb3b820b78b7b688e3c9961ed724230de4fc5fffae166d29620f361",
|
||||
"reviewedSourceSha256": "d9b7ece2ff25d082bdd8d109f52bb2342d9071b8cc465ca5a2b15ac20aeac6c6",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static control-flow review; no PDF metric or model rerun",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/cvBuilder.ts",
|
||||
"sha256": "c021bafeabbc8ad3526d1b10b90ad8f7f0958ded8ed26eac8c1d28b53098b64d"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 132
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/career/CareerProfileSections.tsx",
|
||||
"historicalLine": 45,
|
||||
"expression": "t(tone.key, { percent: Math.round(metadata.confidence * 100) })",
|
||||
"historicalSourceSha256": "0c772feb6bde8324676b0884aaf343283465c5cd3b3318128363063df4ea22ab",
|
||||
"reviewedSourceSha256": "aa3acbe472d34074a517a566c77983e7d95f8ec1df9ee37e47537ad96beffd0e",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static control-flow review; no PDF metric or model rerun",
|
||||
"inventoryEntryIndex": 176
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/career/CareerProfileSections.tsx",
|
||||
"historicalLine": 46,
|
||||
"expression": "t(tone.key)",
|
||||
"historicalSourceSha256": "0c772feb6bde8324676b0884aaf343283465c5cd3b3318128363063df4ea22ab",
|
||||
"reviewedSourceSha256": "aa3acbe472d34074a517a566c77983e7d95f8ec1df9ee37e47537ad96beffd0e",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static control-flow review; no PDF metric or model rerun",
|
||||
"inventoryEntryIndex": 177
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationChecklist.tsx",
|
||||
"historicalLine": 214,
|
||||
"expression": "t(`checklistItem_${token}`)",
|
||||
"historicalSourceSha256": "88fdcd24914cf074aedee72a94b0c420c96a16dfe7194142daf22ec0ad8370ff",
|
||||
"reviewedSourceSha256": "0f198e90559b30952cbf7d0f4973211e3fbffcbb183e9e4b4f2ee2e2a9d02f56",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/applicationWorkspace.ts",
|
||||
"sha256": "3b00a4fe16159bdb9603eeb37bfdf5e5eb9a87d7815227fa3aa2f0da6e4f232c"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 15
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationChecklist.tsx",
|
||||
"historicalLine": 215,
|
||||
"expression": "t(`checklistItem_${token}Description`)",
|
||||
"historicalSourceSha256": "88fdcd24914cf074aedee72a94b0c420c96a16dfe7194142daf22ec0ad8370ff",
|
||||
"reviewedSourceSha256": "0f198e90559b30952cbf7d0f4973211e3fbffcbb183e9e4b4f2ee2e2a9d02f56",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/applicationWorkspace.ts",
|
||||
"sha256": "3b00a4fe16159bdb9603eeb37bfdf5e5eb9a87d7815227fa3aa2f0da6e4f232c"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 16
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/ApplicationWorkspacePage.tsx",
|
||||
"historicalLine": 392,
|
||||
"expression": "t(`checklistItem_${nextStepToken}` as any)",
|
||||
"historicalSourceSha256": "107b4db54ef15d88254e01bc404136b987755588c1d171b800c2a47f93488b50",
|
||||
"reviewedSourceSha256": "62c79d59ce31dccddefd70991db5fc26fcc84f0c58ca6219d0d36980db2050d7",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/applicationWorkspace.ts",
|
||||
"sha256": "3b00a4fe16159bdb9603eeb37bfdf5e5eb9a87d7815227fa3aa2f0da6e4f232c"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 101
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/ApplicationWorkspacePage.tsx",
|
||||
"historicalLine": 395,
|
||||
"expression": "t(`checklistItem_${nextStepToken}Description` as any)",
|
||||
"historicalSourceSha256": "107b4db54ef15d88254e01bc404136b987755588c1d171b800c2a47f93488b50",
|
||||
"reviewedSourceSha256": "62c79d59ce31dccddefd70991db5fc26fcc84f0c58ca6219d0d36980db2050d7",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/applicationWorkspace.ts",
|
||||
"sha256": "3b00a4fe16159bdb9603eeb37bfdf5e5eb9a87d7815227fa3aa2f0da6e4f232c"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 102
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/ApplicationWorkspacePage.tsx",
|
||||
"historicalLine": 505,
|
||||
"expression": "t(keys[section])",
|
||||
"historicalSourceSha256": "107b4db54ef15d88254e01bc404136b987755588c1d171b800c2a47f93488b50",
|
||||
"reviewedSourceSha256": "62c79d59ce31dccddefd70991db5fc26fcc84f0c58ca6219d0d36980db2050d7",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/applicationWorkspace.ts",
|
||||
"sha256": "3b00a4fe16159bdb9603eeb37bfdf5e5eb9a87d7815227fa3aa2f0da6e4f232c"
|
||||
}
|
||||
],
|
||||
"inventoryEntryIndex": 103
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/career/CareerEvidenceBank.tsx",
|
||||
"historicalLine": 83,
|
||||
"expression": "t(`careerEvidenceCategory_${value}` as \"careerEvidenceCategory_achievement\")",
|
||||
"historicalSourceSha256": "fc85ccfc1addd48af3a29bc263b089858765b2ac3ec7db3b66dedc55ddfab625",
|
||||
"reviewedSourceSha256": "51cab7289717c9f26006a9d34ac1f8aac9e09d19e549a3666a15adc41d5d4724",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed",
|
||||
"inventoryEntryIndex": 175
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 2,
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"historicalLine": 104,
|
||||
"expression": "t(error.messageKey)",
|
||||
"historicalSourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"reviewedSourceSha256": "b5f14b41de10474e2bcd709b91b1becb7b7db4aba021fba29e4e9e44a7a2947f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 12,
|
||||
"file": "job-tracker-ui/src/components/ApplicationAssets.tsx",
|
||||
"historicalLine": 887,
|
||||
"expression": "t(error.messageKey)",
|
||||
"historicalSourceSha256": "25216c80ef92d519baad9e428a0e77390150605c1a3529a4809819d120661f29",
|
||||
"reviewedSourceSha256": "b5f14b41de10474e2bcd709b91b1becb7b7db4aba021fba29e4e9e44a7a2947f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 97,
|
||||
"file": "job-tracker-ui/src/views/AdminSystemPage.tsx",
|
||||
"historicalLine": 333,
|
||||
"expression": "t(kind === \"status\" && status ? \"adminSystemRefreshFailed\" : readErrorKeys[kind])",
|
||||
"historicalSourceSha256": "1e76931739d779d9edb17e22846d16fffef7ebd1a0c27aebf740c2c46c79b3de",
|
||||
"reviewedSourceSha256": "64e85d9b13088b5229b6635674e9e6ff9ee6c4a62db7f66accb1dcddcedd16c0",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 98,
|
||||
"file": "job-tracker-ui/src/views/AdminSystemPage.tsx",
|
||||
"historicalLine": 448,
|
||||
"expression": "t(actionErrorKeys[actionError.kind])",
|
||||
"historicalSourceSha256": "1e76931739d779d9edb17e22846d16fffef7ebd1a0c27aebf740c2c46c79b3de",
|
||||
"reviewedSourceSha256": "64e85d9b13088b5229b6635674e9e6ff9ee6c4a62db7f66accb1dcddcedd16c0",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 99,
|
||||
"file": "job-tracker-ui/src/views/AdminSystemPage.tsx",
|
||||
"historicalLine": 449,
|
||||
"expression": "t(message)",
|
||||
"historicalSourceSha256": "1e76931739d779d9edb17e22846d16fffef7ebd1a0c27aebf740c2c46c79b3de",
|
||||
"reviewedSourceSha256": "64e85d9b13088b5229b6635674e9e6ff9ee6c4a62db7f66accb1dcddcedd16c0",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 104,
|
||||
"file": "job-tracker-ui/src/views/CareerProfilePage.tsx",
|
||||
"historicalLine": 508,
|
||||
"expression": "t(stageKeys[stage])",
|
||||
"historicalSourceSha256": "d8f302c2bd8b09544a41735b4d63818c2843ca5193f7b733a3e8447c828f181a",
|
||||
"reviewedSourceSha256": "190b8b0840fb57480985a1688160001a1e5e828639e5b632351740b6352da0b7",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 105,
|
||||
"file": "job-tracker-ui/src/views/CareerProfilePage.tsx",
|
||||
"historicalLine": 509,
|
||||
"expression": "t(importLabelKeys[value])",
|
||||
"historicalSourceSha256": "d8f302c2bd8b09544a41735b4d63818c2843ca5193f7b733a3e8447c828f181a",
|
||||
"reviewedSourceSha256": "190b8b0840fb57480985a1688160001a1e5e828639e5b632351740b6352da0b7",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 106,
|
||||
"file": "job-tracker-ui/src/views/CareerProfilePage.tsx",
|
||||
"historicalLine": 865,
|
||||
"expression": "t(runActionErrors[run.id].key)",
|
||||
"historicalSourceSha256": "d8f302c2bd8b09544a41735b4d63818c2843ca5193f7b733a3e8447c828f181a",
|
||||
"reviewedSourceSha256": "190b8b0840fb57480985a1688160001a1e5e828639e5b632351740b6352da0b7",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 153,
|
||||
"file": "job-tracker-ui/src/views/GmailReviewPage.tsx",
|
||||
"historicalLine": 231,
|
||||
"expression": "t(writeError.key)",
|
||||
"historicalSourceSha256": "3609ec12dd9619f7cbbd3541dcf2563a33e4ef3cb7cf9eab0cfc4a17a43d22d8",
|
||||
"reviewedSourceSha256": "5e87cad1c5f5118fdd6d62d3aae35b83b3ab10d8d60722ed18c1dbb4487f9890",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 154,
|
||||
"file": "job-tracker-ui/src/views/GmailReviewPage.tsx",
|
||||
"historicalLine": 231,
|
||||
"expression": "t(writeError.key)",
|
||||
"historicalSourceSha256": "3609ec12dd9619f7cbbd3541dcf2563a33e4ef3cb7cf9eab0cfc4a17a43d22d8",
|
||||
"reviewedSourceSha256": "5e87cad1c5f5118fdd6d62d3aae35b83b3ab10d8d60722ed18c1dbb4487f9890",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 155,
|
||||
"file": "job-tracker-ui/src/views/JobDiscoveryPage.tsx",
|
||||
"historicalLine": 229,
|
||||
"expression": "t(error ?? \"jobDiscoveryUnavailable\")",
|
||||
"historicalSourceSha256": "e15f5183fe6d41ecb7a53ec9806872a67f0d6ce683354d0abef53527bd397b94",
|
||||
"reviewedSourceSha256": "8055c95959b662b69f04f780993d4668e3907c53b445451c0981fa3a1436756f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 156,
|
||||
"file": "job-tracker-ui/src/views/JobDiscoveryPage.tsx",
|
||||
"historicalLine": 230,
|
||||
"expression": "t(error)",
|
||||
"historicalSourceSha256": "e15f5183fe6d41ecb7a53ec9806872a67f0d6ce683354d0abef53527bd397b94",
|
||||
"reviewedSourceSha256": "8055c95959b662b69f04f780993d4668e3907c53b445451c0981fa3a1436756f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static state-constructor and own-property review; no admin actions, external mail or AI executed"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 166,
|
||||
"file": "job-tracker-ui/src/views/OperationsPage.tsx",
|
||||
"historicalLine": 127,
|
||||
"expression": "t(taskKeys[task])",
|
||||
"historicalSourceSha256": "8cff1e08b2009ff683957849af71d6e76916a28ae218e647d9e3c04a12ad5f9b",
|
||||
"reviewedSourceSha256": "b35c793fd33dcd0638d47df2aa3bfb2926879a8fa709880983b64c1a1309bb98",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "existing Object.hasOwn guards statically reviewed; no service execution"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 168,
|
||||
"file": "job-tracker-ui/src/views/OperationsPage.tsx",
|
||||
"historicalLine": 158,
|
||||
"expression": "t(statusKeys[operation.status])",
|
||||
"historicalSourceSha256": "8cff1e08b2009ff683957849af71d6e76916a28ae218e647d9e3c04a12ad5f9b",
|
||||
"reviewedSourceSha256": "b35c793fd33dcd0638d47df2aa3bfb2926879a8fa709880983b64c1a1309bb98",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "existing Object.hasOwn guards statically reviewed; no service execution"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 183,
|
||||
"file": "job-tracker-ui/src/views/career/CvImportReview.tsx",
|
||||
"historicalLine": 114,
|
||||
"expression": "t(labelKeys[value])",
|
||||
"historicalSourceSha256": "bdecd17437d919a33d9deaa855262b1f1baf78b4a22eef0e88ac1fdbb5ebeda4",
|
||||
"reviewedSourceSha256": "dbbf0bcebd4fc46f55deaa6bb70a0abd37ecb0f01608335bcaf4de46e05f4a48",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "existing Object.hasOwn guards statically reviewed; no service execution"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 184,
|
||||
"file": "job-tracker-ui/src/views/career/CvImportReview.tsx",
|
||||
"historicalLine": 165,
|
||||
"expression": "t(warningKeys[warning])",
|
||||
"historicalSourceSha256": "bdecd17437d919a33d9deaa855262b1f1baf78b4a22eef0e88ac1fdbb5ebeda4",
|
||||
"reviewedSourceSha256": "dbbf0bcebd4fc46f55deaa6bb70a0abd37ecb0f01608335bcaf4de46e05f4a48",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "existing Object.hasOwn guards statically reviewed; no service execution"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 67,
|
||||
"file": "job-tracker-ui/src/components/InterviewPrep.tsx",
|
||||
"historicalLine": 505,
|
||||
"expression": "t(keys[category])",
|
||||
"historicalSourceSha256": "eadf345d3613ad867788f6a74a8fd1fbba508bb81ec7bc87fe54e4f66dadc69b",
|
||||
"reviewedSourceSha256": "ef8161cc982c9068d3c7eec80f7dd6ef565f05ba0b36bcf58e3b643285b22f0f",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "INTERVIEW_DOCUMENT_FALLBACKS.md; 41 full selected-suite tests, including 12 inherited-name/language cases"
|
||||
},
|
||||
{
|
||||
"inventoryEntryIndex": 138,
|
||||
"file": "job-tracker-ui/src/views/DocumentsPage.tsx",
|
||||
"historicalLine": 243,
|
||||
"expression": "t(({ resume: \"attachmentsPurposeResume\", \"cover-letter\": \"attachmentsPurposeCoverLetter\", portfolio: \"attachmentsPurposePortfolio\", \"case-study\": \"attachmentsPurposeCaseStudy\", certificate: \"attachmentsPurposeCertificate\" } as Record<string, TranslationKey>)[item.purpose.toLowerCase()] ?? \"attachmentsPurposeOther\")",
|
||||
"historicalSourceSha256": "fa53a826ebbdbc868c8b21a0387138c12611d3e2867de0fa0e5f6d8483471eb9",
|
||||
"reviewedSourceSha256": "0b9aaba2c102b4346b10386107b3822016b4f680bb9ab6b58c24ef80b74080fc",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "INTERVIEW_DOCUMENT_FALLBACKS.md; 41 full selected-suite tests, including 12 inherited-name/language cases"
|
||||
}
|
||||
],
|
||||
"limitations": [
|
||||
"key-domain review is not full translator context or native-language acceptance",
|
||||
"original inventory is retained as historical evidence",
|
||||
"reviewed hashes must be refreshed after relevant source changes"
|
||||
],
|
||||
"catalogueSha256": "ed99d129f8702181429e10ec02f65f734220409fcba4de51d0d77a0d60871ea4",
|
||||
"hashEncoding": "Reviewed text and inventory hashes use UTF-8 file bytes with CRLF normalized to LF; historicalSourceSha256 retains the original inventory encoding"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Automated EN/Bokmål catalogue QA
|
||||
|
||||
2 November 2026. Added translation-catalogue.test.ts to the existing Jest suite. Reuses the current interpolation contract and the checks demonstrated by the historical Phase 9 localisation helper; that helper/report was not modified or overwritten.
|
||||
|
||||
Three automated invariants: equal English/Bokmål key sets; equal sets of current `{word}` substitution variables per message; no blank message values. Current resources contain 3,119 English and 3,119 Bokmål keys. These are resource counts, not thousands of test cases. Identical proper names/technical labels are allowed; machine equality does not determine whether natural-language copy is translated well.
|
||||
|
||||
PASSED: 10 tests across translation-catalogue, i18n-provider and locale-fetch-stability, zero failures/pending, native exit 0. This includes the three new global invariants and existing persistence/legacy alias/fetch-stability coverage. Scoped lint passed and full typechecking completed without errors. [Native report](../direct-roadmap/integration-evidence/locale-catalogue-native.json.gz). These three suites are separate from the 16-suite/195-test integration execution; do not represent them as a single fresh full-suite run. Runtime application source is unchanged from the passing integration production build; no build was repeated for a test-only addition.
|
||||
|
||||
Limits: current simple interpolation only, not a future ICU/plural engine; no every-string/frontend/backend completeness, native translation quality, human/device or European locale release acceptance. The 60 research locale candidates remain unshipped. Native terminology, market priorities, Montenegrin identity/canonicalisation and registry/migration decisions retain their documented gates.
|
||||
@@ -0,0 +1,13 @@
|
||||
# Error-state translation inputs
|
||||
|
||||
2 November 2026. Twelve historical call sites reviewed through local state constructors; no runtime implementation changed or privileged action executed.
|
||||
|
||||
- ApplicationAssets.tsx:104/887: AssetError.messageKey originates in the private assetError helper with literal/finite conditional arguments, or three explicit cover-AI source error constructors. Conflict status selects revisionConflict; API error text stays display data rather than a catalogue key. Shell is private to this module with AssetError props from these owners.
|
||||
- AdminSystemPage.tsx:333/448/449: ReadKind/ActionKind are bounded by four local read/action entry points; readNotice is called only with status/benchmark/maintenance/email. Error-key maps have fixed entries; success state is supplied only by four local literal runAction calls. API response bodies never supply kind/success keys. No maintenance/probe/email request was run by this review.
|
||||
- CareerProfilePage.tsx:508/509/865: stage/import-label maps already use Object.hasOwn with explicit generic or raw-text fallback. Run action errors are constructed from cancel/retry/accept/discard and conflict status, with fixed key literals; server error message remains data. No import processing or canonical profile mutation was executed.
|
||||
- GmailReviewPage.tsx:231 has two distinct translator calls, both fed by only three literal write-error constructors (save/sync/create). No real mail operation or provider access.
|
||||
- JobDiscoveryPage.tsx:229/230: error state receives only five literal keys from local catches, or null; the first call has a fixed unavailable fallback and the second is guarded by non-null state. No provider request or source grant implied.
|
||||
|
||||
All components obtain t from useI18n. Assertion verified 87 distinct relevant keys, including the import label/stage maps, in both catalogues. This is finite key/owner/control-flow review, not native translation quality, all message formatting or a security/operational pass.
|
||||
|
||||
Register schema 2 now uses immutable inventoryEntryIndex (bound to the original inventory hash) to distinguish the two identical Gmail expressions on the same line. All 74 supplemental indices are unique. Earlier file/line/expression fields remain for readability; they alone cannot identify same-line repeated calls. Reconciliation: 108 original + 74 supplemental = 182 resolved / 6 pending of 188.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Finite owner-map review — 2 November 2026
|
||||
|
||||
Static source review resolves three further historical dynamic call sites; no application code changed.
|
||||
|
||||
| Call | Owner and key bounds | Result |
|
||||
| --- | --- | --- |
|
||||
| CoverLetterDocument.tsx:36, `t(fieldLabels[key])` | `detailFields` is a literal tuple of eight fields; typed fieldLabels maps to letterSenderName/Title/Email/Phone/Location, letterRecipient/Subject/Date; component obtains t from useI18n. | RESOLVED |
|
||||
| CoverLetterDocument.tsx:153, `t(fieldLabels[key])` | `contactFields` is the five-field literal subset; CopyLetterSnapshot obtains t from useI18n. User snapshot values remain interpolated data, never keys. | RESOLVED |
|
||||
| careerCompletenessLabels.ts:15, `t(key)` | Seven literal mapped TranslationKeys: profileCvCompletenessPersonal/Summary/Experience/Education/Skills/Projects/Languages. Unknown labels return verbatim. Both callers, ProfileCompleteness and CareerWorkspaceOverview, pass the useI18n translator. | RESOLVED |
|
||||
|
||||
Graph queries preceded source inspection. A local assertion verified all 15 distinct keys have definitions in both catalogues. This proves finite key coverage and translator ownership, not native translation quality or runtime acceptance. Historical call lines and reviewed source are unchanged by the attachment-title package.
|
||||
|
||||
Reconciliation: 188 historical dynamic calls; 108 previously resolved + 1 checklist category map + 3 here = 112 resolved / 76 pending. The four checklist system-key template calls remain pending. Do not rewrite user-provided contact fields or backend completeness labels merely to translate them.
|
||||
|
||||
Further review resolves CvTemplateGallery.tsx:33's three indexed calls (`labels[0]`, `[1]`, `[2]`). `templateLabels` is a nine-entry Map of typed, fixed three-key tuples; its 27 references contain 24 distinct keys, all asserted present in both catalogues. Calls occur only after a matching tuple was found for Bokmål. Unknown themes and English retain the backend theme metadata as their existing fallback. Gallery and preview-dialog callers obtain language/t from useI18n. Preserve historical theme identities and renderer versions; this review changes neither metadata policy nor rendering. No additional runtime test was required for this source-only review.
|
||||
|
||||
Latest reconciliation: 115 resolved / 73 pending (188 total). The earlier 112/76 count describes the preceding review checkpoint.
|
||||
|
||||
Dashboard follow-through resolves five additional sites: DashboardGrid.tsx:47/63, DashboardLayoutEditor.tsx:77, DashboardView.tsx:372 use the nine literal WIDGETS titleKeys; DashboardView.tsx:259 iterates the three literal table-heading keys. All 12 keys were asserted present in both catalogues; the components use useI18n. WidgetId is derived from WIDGETS; normalizeDashboardLayout discards unknown persisted identifiers and constructs widgets from the matching definition. This establishes the finite translation domain, not arbitrary forged prop safety or layout/device acceptance. Geometry, hidden positions, schema-2 persistence and revisions were not modified.
|
||||
|
||||
Current reconciled total: 120 resolved / 68 pending, still 188 historical dynamic calls. These static reviews do not close the broader every-string context, backend-message or native-language review requirements.
|
||||
|
||||
ApplicationIntelligence.tsx:367/373 review resolves two further sites. timelineCategoryLabel maps five fixed labels, with unknown categories returned as data; matchBandLabel admits only Strong/Partial/Low/Unknown before forming a key and returns unknown bands verbatim. All nine distinct catalogue keys were asserted present in both languages; caller components obtain t from useI18n. Current reconciliation: 122 resolved / 66 pending. This key-domain review does not assert native translation quality or live matching accuracy.
|
||||
|
||||
Follow-up runtime corrections are recorded in UNKNOWN_LABEL_FALLBACKS.md and PIPELINE_ALIAS_FALLBACK.md: inherited property names required own-property guards in the completeness/timeline helpers and pipeline alias normaliser. Their regression evidence supersedes the earlier unconditional static fallback claims. Pipeline's one historical call is now resolved, bringing the intermediate count to 123/65.
|
||||
|
||||
AdminProcessingPage.tsx:153/173/205 adds three source-resolved calls: task filter labels iterate Object.keys(tasks), excluding inherited properties; table headings use six literal keys; detail date labels use four literal tuple keys. useI18n supplies t. Eleven distinct catalogue keys for these domains, plus the ten pipeline label keys, were asserted present in both languages. Backend-derived status/stage/task/event/worker strings remain pending because their dictionary accesses need separate own-property and unknown-value review. Do not count those as covered by the safe literal/own-key iterators.
|
||||
|
||||
Latest reconciliation: 126 resolved / 62 pending, of 188 historical dynamic calls. The chronological counts above are historical checkpoints, not overlapping work to add again.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Import-review and operation label guards
|
||||
|
||||
2 November 2026. Four unchanged sites reviewed: OperationsPage.tsx:127/158 and career/CvImportReview.tsx:114/165. The existing task/status and field/warning dictionaries are all guarded with Object.hasOwn before translation. Unknown task/field/warning text remains data; unknown operation status uses the existing underscore-to-space display fallback. Components obtain t from useI18n. No operation, import, provider, model or profile mutation was executed.
|
||||
|
||||
Every literal dictionary value was checked for definitions in both catalogues. These existing guards prevent inherited-property names from entering the translator; no new implementation was necessary. Source/control-flow review is not native-language, full import accuracy, notification delivery or integrated security acceptance.
|
||||
|
||||
Machine register: 108 original + 78 supplemental = 186 resolved / 2 pending of 188. Remaining sites are interview category and document attachment-purpose lookups; synthetic regressions are in progress, and neither is claimed fixed by this review.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Interview category and document-purpose fallbacks
|
||||
|
||||
2 November 2026. Two confirmed functional defects: inherited Object-property names bypassed interview category raw-text fallback and document-purpose localized Other fallback. Synthetic EN/NB diagnostic run: ten failures, two passing lowercase-purpose controls, 29 unselected cases. This is not a prototype mutation or account-access finding.
|
||||
|
||||
Minimal fixes: Object.hasOwn in the existing interview category helper; a null-prototype object for the existing inline document-purpose map. Exact known labels, purpose lowercasing, existing unknown-value choices and all surrounding workflows remain intact. No role, API, CV/import acceptance, revision, model, provider, storage, database or final UI design changes.
|
||||
|
||||
GREEN: 41 tests across full interview-prep and documents-page suites, zero failures/pending, exit 0. Twelve new cases cover constructor/toString/__proto__ in EN/Bokmål, checking unknown category text and localized Other purpose. Existing cases protect normal interview/follow-up, document filtering/date/link/download and account-safety behavior. Full typecheck and four-file scoped lint passed, zero warnings. Native report: job-tracker-ui/.artifacts/interview-document-fallbacks.json. All 13 dictionary/fallback keys verified in both catalogues. No real model, mail or provider operation.
|
||||
|
||||
Rollback: revert the two lookup changes and twelve test cases; no data conversion or recovery action. Existing labels/history/content are not rewritten. Final integrated security/browser/build qualification remains separately recorded; this test report is not a full application pass.
|
||||
|
||||
The historical 188-call census is now completely accounted for: 108 prior finite-branch sites plus 80 individually reviewed supplemental sites, no overlapping inventory indices, zero pending finite key domains. Register schema 2 distinguishes same-line repeated calls. This does NOT mean complete I18N-00, every-string sensitivity/context, backend-message extraction, plural/select architecture, European locale publication, native translation, accessibility or full runtime coverage is accepted. New code and changed owner dependencies require renewed review. Preserve the original inventory as immutable historical evidence.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Landing and document-index key domains — 2 November 2026
|
||||
|
||||
Eleven additional sites reviewed without source changes. Both pages obtain t from useI18n; no UI/market/document preference or route behavior changed.
|
||||
|
||||
LandingPage.tsx:87/172/173/190/191: FEATURES and STEPS are six-entry literal arrays with fixed title/body keys (24 distinct keys). PUBLIC_PLANS contains Free with five features and Pro with six; the indexed key expression therefore currently yields 11 existing keys. All 35 are defined in both languages. The machine register binds planCatalog.ts as an owner dependency, since adding/reordering features requires renewed key/copy review even if LandingPage itself is unchanged. This resolves key existence, not the accuracy, entitlement, deployment or marketing approval of existing public claims. New public-site content remains separately gated and unpublished.
|
||||
|
||||
DocumentsPage.tsx:222/225/231/264/265 (two expressions at 265): date label is a four-value literal conditional; state/type lookups use finite arrays with explicit fixed-key fallbacks; filter title/option parameters have only three local callers using the literal types/states/visibility arrays. All 22 distinct keys are defined in both catalogues. Unknown state/type objects cannot introduce a catalogue key through array find. Date values, application names and document content remain data. The attachment-purpose inline object lookup at line 243 remains pending; do not assume it has safe arbitrary-key behavior.
|
||||
|
||||
Local assertion checked all 57 distinct keys have definitions in both catalogues, and confirmed actual 5/6 plan feature lengths. No browser or production build rerun for unchanged source-only review. Full native language, plural/formatting, accessibility and feature availability remain separate qualifications.
|
||||
|
||||
Register: 108 originally resolved + 48 individually matched supplemental sites = 156 resolved / 32 pending of 188. Catalogue/source/dependency hashes bind this checkpoint. No call is counted twice.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Supported-language control naming
|
||||
|
||||
2 November 2026. Existing compact AppShell control now displays NB for Bokmål, accessible native name Norsk bokmål, and lang=nb. English retains EN/English with lang=en. This distinguishes written-language identity from Norway's NO country code. Native self-names are intentional invariant labels, not ordinary untranslated interface prose.
|
||||
|
||||
The persisted UI value remains nb; I18nProvider still handles legacy aliases and server nb-NO. No document/ad-language preference, route, country support, new locale, navigation layout or final design implementation changed. Both buttons retain their existing handlers, aria-pressed state and two-character badge width.
|
||||
|
||||
RED: new shell test failed against the old Norsk/NO label. GREEN: 17 tests in app-shell-navigation, app-shell-notifications and i18n-provider, zero failures/pending, native exit 0. New case checks both language attributes, native name, badge, pressed state and persisted nb selection. Scoped lint and full typecheck passed. Existing language-persistence cases overlap the preceding catalogue QA run; do not add the totals as disjoint tests. Native local report: job-tracker-ui/.artifacts/language-control-qa.json. Native-device/AT/browser qualification remains distinct.
|
||||
|
||||
Literal review also checked JobFlowBar's Flow label. Bounded src search found only its declaration; it has a separate local status normaliser rather than importing the shared pipeline helper. No deletion candidate is established and no inactive/compatibility component was changed solely to reduce an inventory count. Other formatting, admin labels and backend literals remain open.
|
||||
|
||||
Fresh production static build after the language-control patch PASSED, native exit 0, including TypeScript and all three generated pages. This is development build evidence, not browser/native AT or production qualification. Native test evidence is archived at ../direct-roadmap/integration-evidence/language-control-native.json.gz.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Pipeline alias fallback — 2 November 2026
|
||||
|
||||
CONFIRMED functional defect: normalizeStatus('constructor') returned the inherited Object function and '__proto__' returned an object instead of the declared pipeline status or Other. statusLabel could consequently lose a custom status. No prototype mutation, account access or persisted-data corruption is asserted.
|
||||
|
||||
Minimal fix: only accept own properties of the existing ALIASES map. Existing trimming, case-insensitive canonical stages, known aliases, empty-as-Applied and custom-as-Other behavior are retained. Imported shared-helper callers reviewed here are KanbanBoard and ApplicationWorkspacePage. Correction: JobFlowBar has a separate local normaliser; the earlier name-based caller search incorrectly included it. That component was not changed. No statuses or stored records were renamed; no routes, migrations or dependencies changed.
|
||||
|
||||
RED diagnostic: constructor and __proto__ cases failed; toString control passed because the input is lowercased. Eleven unrelated cases were not selected in that diagnostic run. GREEN final: pipeline and grouped-board suites, 29 tests, zero failures/pending, native exit 0. New cases verify raw custom labels bypass translation; existing cases cover aliases, group membership/order, valid stage labels and board interactions. Scoped lint and full typecheck passed. Native ignored report: job-tracker-ui/.artifacts/pipeline-alias-fallback.json.
|
||||
|
||||
This resolves historical pipeline.ts:149 `t(LABEL_KEYS[normalized])`: the canonical result is one of the ten explicit LABEL_KEYS or Other, which returns raw status. The original implementation's assumed finite return domain was not true for inherited names. Latest dynamic review count: 123 resolved / 65 pending of 188. Full catalogue/native-language context acceptance is separate.
|
||||
|
||||
Rollback: revert the one-line guard and regression additions. Production build from the preceding label/session package predates this patch and is not claimed as a fresh pipeline build. Final integrated browser/security qualification remains open.
|
||||
@@ -0,0 +1,13 @@
|
||||
# Processing-monitor translation fallbacks — 2 November 2026
|
||||
|
||||
CONFIRMED functional defect: runtime status/stage/task/event/worker names matching inherited Object properties bypassed existing unknown-value fallbacks. Six EN/Bokmål cases using constructor, toString and __proto__ failed before the fix. No prototype mutation, privilege escalation or backend validation bypass is asserted.
|
||||
|
||||
Minimal shared fix: the five existing label dictionaries now use null-prototype objects, retaining their exact entries and Record typing. All callers, Object.keys filter controls and existing fallback expressions remain intact. No auth/role, API, polling, account-isolation, queue, model or production behavior changed. Known fields translate normally; unknown names reach the existing localized Unknown/Other operation/Unregistered worker/Not recorded labels.
|
||||
|
||||
PASSED: all 17 admin-processing-page tests, zero failures/pending, native exit 0. Six new cases exercise all five map domains, open the collapsed worker-details section, then check timeline event/status/stage fallbacks; eleven existing cases cover normal monitor workflows and safety. An intermediate rerun failed only because the new worker-label visibility assertion inspected a closed details element; the test now opens its summary through the normal UI. That run is not counted as passing or added to the final total.
|
||||
|
||||
PASSED: full typecheck and scoped two-file ESLint, native exits 0. Thirty-eight distinct map/fallback keys asserted present in both catalogues. Local native report: job-tracker-ui/.artifacts/processing-label-fallbacks.json. AST-only graph update required after code edits; keep generated graph separate from the implementation commit. Full build/browser and final integrated security acceptance are not claimed for this patch.
|
||||
|
||||
Five historical call sites resolved: AdminProcessingPage.tsx:130/131/175/191/212. Machine register binds each expression and reviewed source hash: 131 resolved / 57 pending of 188. The earlier safe map iterators retain their coverage with updated source hashes. This is key-domain/fallback evidence, not complete locale or native-language acceptance.
|
||||
|
||||
Rollback: revert the five dictionary constructions and six regression cases; no data migration or restoration needed. Continue remaining owner-map review; preserve unapproved final design and live/security/release gates.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Reminder and interview key review — 2 November 2026
|
||||
|
||||
Five additional historical dynamic call sites resolved by source review; no implementation or runtime inference changed.
|
||||
|
||||
- InterviewPrep.tsx:236/261/285: INTERVIEW_AI_FOCUS is a literal five-entry array. The selected value uses find plus a guaranteed first-entry fallback; menu labels iterate the same array. All five labelKeys are fixed catalogue keys. InterviewAiAssistant obtains t from useI18n. UI language and selected generated-content language remain separate; resolving these labels does not enable AI or prove semantic output quality.
|
||||
- RemindersView.tsx:35/38: generatedReasons is a Map with seven fixed catalogue values; generatedDayReasons has four literal key/regex pairs. Map.get guards the direct lookup; only anchored numeric-day matches interpolate the days placeholder. Unknown reason text returns unchanged as data. The supplied translator is the useI18n translator. Map semantics do not admit inherited Object-property keys.
|
||||
|
||||
All 16 distinct keys were asserted present in both catalogues. This proves finite key coverage and translator ownership, not grammar/native review, all every-string context, plural quality or notification delivery. European plural/formatting architecture remains separately gated; no existing working translation was rewritten. No source edit, live model/provider call, paid scan or duplicate regression rerun was needed for this static review.
|
||||
|
||||
Machine register: 108 original resolved sites + 28 individually recorded supplemental sites = 136 resolved / 52 pending of 188. Source hashes and exact historical expressions are bound in DYNAMIC_RESOLUTION_REGISTER.json. Continue unresolved category/asset/import maps and backend-owned keys; never treat this count as complete i18n acceptance.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Session-change message localisation — 2 November 2026
|
||||
|
||||
Implemented in the existing App invalidation branch. Reuses the existing `sessionChangedCleared` English/Bokmål catalogue message. The canonical document language (`nb-NO`, set by I18nProvider) selects Bokmål; other values safely select English. Reading it occurs only in the invalidated client branch. No provider, hook, storage access, request, dialog or private callback is retained to obtain the message.
|
||||
|
||||
Callers: root App renders Application only while valid; invalidation replaces it with a main alert and preserves the existing passive redirect to /login?sessionChanged=1. I18nProvider already publishes the current UI language to the document; generated-content language is unrelated. All routes, auth/CSRF/context tokens, abort logic and provider teardown remain unchanged. No new translation key or dependency.
|
||||
|
||||
RED: two new root-render tests failed against the previous literal English alert. PASSED after patch: 17 tests in session-boundary-message.test.tsx and session-boundary.test.ts, zero failures/pending, exit 0. New cases verify EN/NB feedback outside the private provider tree and zero API requests. Existing cases exercise cross-tab/cookie invalidation, abort/stale callbacks and session-context handling. These are local Jest/DOM tests; jsdom navigation is not browser redirect qualification.
|
||||
|
||||
PASSED: full TypeScript typecheck; scoped ESLint of App.tsx and the new test, zero warnings. Local ignored native Jest result: job-tracker-ui/.artifacts/session-boundary-message.json. No fresh full build/browser or complete security acceptance claimed. Rollback: revert the two source/test files in this package; no data changes. The attachment-label fix remains independently preserved.
|
||||
|
||||
The original literal-inventory App.tsx entry is now implemented; the inventory remains a historical snapshot. Final integrated security regression must run after relevant application changes are complete.
|
||||
@@ -0,0 +1,15 @@
|
||||
# Unknown translation-map labels — 2 November 2026
|
||||
|
||||
CONFIRMED functional defects, repaired locally with synthetic regressions: Career Profile completeness and application timeline category helpers used ordinary object property lookup. Unknown labels matching inherited Object properties (`constructor`, `toString`, `__proto__`) were incorrectly sent to the translator, losing the original text and producing missing visible/accessible labels. This is not a prototype-pollution or account-access finding; no such impact is claimed.
|
||||
|
||||
Minimal fix: Object.hasOwn guards in the two existing helpers. Known keys still translate normally, other strings remain data. Callers: ProfileCompleteness and CareerWorkspaceOverview; ApplicationTimeline. No API, persisted profile/category, UI language preference, generated-content language or revision behavior changes; no dependencies added.
|
||||
|
||||
RED: three inherited-name Career Profile cases failed (two known/custom control cases passed); three timeline-category cases failed before the patch (11 unselected cases were skipped in that diagnostic run). GREEN final combined execution: 41 tests across four suites, zero failures/pending, native exit 0: career-completeness-labels, career-history-view, career-workspace-page and application-intelligence. Do not add the overlapping earlier 27-case run to this total. Full typecheck and scoped four-file ESLint passed, zero warnings.
|
||||
|
||||
Native ignored JSON: job-tracker-ui/.artifacts/translation-map-fallbacks.json. Known-key test checks the translated catalogue key; timeline tests verify accessible button names for all three inherited-name canaries. No production or live provider/model used. Full integrated security testing remains deferred until relevant application changes are complete.
|
||||
|
||||
Correction to earlier static review: finite literal-key coverage alone did not prove the stated unknown-label fallback for these ordinary object maps. These regressions now verify it. The checklist category helper's current caller iterates CHECKLIST_CATEGORIES, so arbitrary unknown categories never reach its lookup; no speculative checklist change was made. Other maps with external keys, including processing-monitor maps, still need equivalent reachability/fallback review. Dynamic-call total remains 122 resolved / 66 pending; this remediation is not another resolved call count.
|
||||
|
||||
Rollback: revert the two helper lines and associated tests only; no data rollback needed. Remaining limits: no claim of native-language quality, full WCAG, all map coverage or final integrated security acceptance.
|
||||
|
||||
Final build: Next.js 16.4.1 static production build PASSED, exit 0, including TypeScript and all three generated pages. This also builds the session-message and preceding attachment-label fixes on this branch. API build-time base was local; no deployment or live service validation. Sanitized native reports are retained in evidence/translation-map-fallbacks.json.gz and evidence/session-boundary-message.json.gz (41 and 17 disjoint selected tests respectively).
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -22,3 +22,9 @@ The three filters are disjoint: 89 distinct backend cases passed, zero failed/sk
|
||||
Frontend PASSED: five suites, 92 tests, zero failures/pending, native exit 0, 41.237 seconds. Exact selected paths: `career-history-view.test.tsx`, `application-assets.test.tsx`, `cover-letter-document.test.tsx`, `cv-document-controls.test.tsx`, `cv-import-review.test.tsx`; Jest `--runInBand --runTestsByPath`, local mocked contracts. [Native JSON](p10-04-evidence/frontend-document-workflows.json.gz), uncompressed SHA256 `b919a96d94218ff166de684f6b82d3ce6f34d47b51d52480721e8955df2df257`. Browser/real PDF fidelity and semantic AI quality are separate evidence. Backend and frontend counts remain separate.
|
||||
|
||||
Migration rehearsal/rollback, MariaDB integration, sidecar, complete suites, security acceptance, hosted CI and human/device qualification remain NOT RUN here. Reuse unchanged accepted renderer evidence only within its source/fixture/environment boundaries. No migration or destructive cleanup is proposed. P10-05 remains open; production remains NO-GO.
|
||||
|
||||
## Additional attachment qualification
|
||||
|
||||
Fresh AttachmentConsistencyTests execution: 14 PASSED, zero failures/skips, native exit 0. Reused the existing Release assembly with --no-build/--no-restore after confirming no backend/test source difference between the Phase 10 and security branches. Relational temporary SQLite and synthetic files only. The named case Another_users_attachment_cannot_be_downloaded_renamed_or_deleted verifies three NotFound results and preserves the foreign file/row. Other selected cases cover upload/storage consistency. This is direct controller/local fixture evidence, not authenticated HTTP or deployed/MariaDB isolation acceptance. No application code changed.
|
||||
|
||||
[Native TRX](p10-04-evidence/attachment-ownership-and-storage.trx.gz). These 14 cases are disjoint from the earlier 89 backend cases: selected backend coverage is now 103 distinct cases, with the 92 frontend tests kept separate. Complete suites and P10-05 remain open.
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# P10-05 — Acceptance reconciliation (OPEN)
|
||||
|
||||
2 November 2026. This is an acceptance work register, not final acceptance. P10-01 CLEAN-20 is the only implemented deletion. P10-02/03 retain reviews found no additional proven-safe candidate. No further cleanup is authorised or needed merely to populate a checkpoint.
|
||||
|
||||
## Evidence applicability
|
||||
|
||||
Fresh integrity check at continuation from 2a4acc17: all 263 indexed TEST_EVIDENCE artifact hashes match; 1,279 application-source hashes match the accepted source-after manifest, with only the approved App.css absent and zero changed-file mismatches. Paths were resolved under the expected repository/evidence root before reading. This establishes historical source applicability, not fresh execution or a complete independent audit. New reports and native test artifacts are outside the application manifest.
|
||||
|
||||
| Required area | Evidence disposition | Remaining limit |
|
||||
| --- | --- | --- |
|
||||
| Cleanup eligibility | CLEAN-20 blob/callers and narrow approval recorded in P10-00/01 | No additional deletion proven |
|
||||
| Frontend types/lint/build | PASSED fresh for CSS deletion; P10-01 | Full-suite execution is not represented by these checks |
|
||||
| Focused browser EN/NB/theme/responsive | PASSED five selected cases; P10-01 | Canary is not full browser regression or device acceptance |
|
||||
| Backend document/data contracts | PASSED 89 selected cases; P10-04 | Not complete backend regression |
|
||||
| Frontend document workflows | PASSED 92 selected tests; P10-04 | Not complete frontend regression |
|
||||
| Routes/capabilities | Existing 34-route and 57-group reconciliation retained | 56 PARTIAL / one DEFERRED is not 57 accepted groups; independent closure review pending |
|
||||
| Historical CV/document/PDF | Accepted source/fixture-bound evidence retained; affected document contracts freshly checked | No fresh physical/PDF visual metric acceptance; unchanged metrics must not be reopened without affected change |
|
||||
| Complete suites/browser contracts | Historical evidence remains source-bound | No fresh full-suite result in this cleanup branch; reviewer must reconcile required acceptance scope explicitly |
|
||||
| MariaDB/migration rollback/restore | Existing qualifications remain scoped | Current independent environment and recovery qualification incomplete; no database migration added or executed |
|
||||
| Sidecar/security/accessibility/hosted CI | Existing limited evidence retained | Full independent audit, actual plugin scans, integrated qualification, hosted/human/device gates remain open |
|
||||
|
||||
## Decisions
|
||||
|
||||
- CLEAN-20 implementation: locally validated and published; no further code cleanup proposed.
|
||||
- Phase 10 completion: OPEN; full acceptance is not established by targeted counts or this report.
|
||||
- Redesign completion and post-redesign Product Review acceptance: OPEN; new full-application design also requires final owner approval.
|
||||
- Independent product/research/i18n/security offline work: may continue under the owner's direct parallel-work authorisation. Do not claim upstream acceptance or integrated release qualification for isolated branches.
|
||||
- Production release: NO-GO. No merge or deployment authorised.
|
||||
|
||||
Next: reconcile complete accepted suite evidence and remaining preservation-group qualifications, complete independent audit coverage, and record genuine reviewer/owner decisions. Keep active-host, provider, model, migration and paid-scan gates distinct from ordinary source review. Do not rerun thousands of unchanged tests solely as a restart demonstration. If a relevant implementation changes, refresh its affected checks and final integrated qualification accordingly.
|
||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
# Preliminary release discovery
|
||||
|
||||
2026-11-02. Branch `planning/release-readiness-discovery`; accepted conditional development baseline `5cb903b989742b7a96cb22ddf4c0e24993b18980`. Stage 1 IN_PROGRESS. No release candidate nominated. Production NO-GO. This report records source inspection and a bounded offline check, not full release qualification.
|
||||
|
||||
| Component / inspected owner | Dependency / failure boundary | Required release evidence |
|
||||
| --- | --- | --- |
|
||||
| `job-tracker-ui/package.json`, `Dockerfile`, `nginx.conf.template` | Next static export, React/MUI and React Router; nginx serves `out`, proxies `/api/`, `/health`, `/ready`; configured host and forwarded-header handling | Exact build/image, browser journeys and deployed proxy/TLS identity |
|
||||
| `JobTrackerApi/Program.cs`, `JobTrackerApi.csproj` | net9.0, EF 9.0.14, Pomelo 9.0.0; authentication/authorization then rate limiting/session middleware and controllers | Endpoint-by-endpoint ownership/session/CSRF/error tests; source registration alone is not security proof |
|
||||
| `JobTrackerApi/Data/DatabaseConfiguration.cs` | Defaults to MariaDB; missing connection throws; SQLite requires explicit transitional selection | Actual MariaDB migration/upgrade/rollback and preservation evidence; retain legacy paths |
|
||||
| `docker-compose.database.yml` | Development persistent volume and separate integration tmpfs service; MariaDB 12.2.2 pinned digest; loopback ports | Isolated provider tests; no claim of equality with production |
|
||||
| Career/CV domain and PDF | Program registers CareerProfileService, ApplicationAssetsService, CV/letter renderers and Playwright exporter | Canonical facts, null-as-inherit, revisions, historical artifacts and physical print remain separate contracts |
|
||||
| Background services | Program registers backup, rules, reminders, saved search, export, enrichment, summarizer probe, AI operation, retention, email recovery and account deletion workers | Verify configuration, failure recovery and tenant isolation for enabled workers; registration is not runtime activation |
|
||||
| Auth/mail/provider integrations | Architecture documents Google/Microsoft identity, Gmail/Graph/IMAP/SMTP, NAV, optional translation, local/external AI, Stripe | Synthetic local tests separate from licensed/provider/account delivery and consent |
|
||||
| `.gitea/workflows/ci-deploy.yml` | Test pipeline; deployment limited by source condition to push on `refs/heads/main` | Fresh hosted artifact/commit/runner evidence; development branch push is not hosted qualification |
|
||||
| `.gitea/workflows/mariadb-integration.yml` | Isolated runner label, explicit initial exit-1 isolation gate | Operator isolation evidence and authorised workflow review before enabling; do not remove gate during discovery |
|
||||
| Existing backup/deployment runbooks | Database, attachments/CV artifacts, keys and deletion tombstones; code rollback differs from data restore | Disposable restore with independent content/decryption/deletion checks; approved RPO/RTO and failure rehearsal |
|
||||
|
||||
Public boundary currently inspected: nginx host routing, `/health`, `/ready`, `/api/`; API controllers are mapped but a complete anonymous/authenticated endpoint census is still PENDING. External mail/job/AI requests, uploaded documents, browser credentials, database records and stored artifacts are separate trust boundaries. No live endpoints, Docker engines, databases, credentials, models or production services were accessed.
|
||||
|
||||
Local development, test and production are not interchangeable: integration uses a generated project and disposable service; production needs the external network, server-side environment, actual ingress and operator controls. Current architecture/runbooks describe these differences; exact intended staging/production configuration and image identity remain unverified.
|
||||
|
||||
The latest handoff's source-bound PDF metric/visual approvals supersede the older acceptance paragraph describing unchanged S01 coordinate failures. They are preserved for the approved development scope. Physical print, device, AT and language review remain open. No export was rerun because no renderer/font/fixture/application source changed.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Discovery baseline and actual checks
|
||||
|
||||
Date 2026-11-02. Source `5cb903b989742b7a96cb22ddf4c0e24993b18980`; application freeze `ba04d817fed26d659bfa2a56b089d6cac851dc34`. No accepted post-roadmap release candidate, build identifier or release image exists in this discovery checkpoint.
|
||||
|
||||
Fresh check: `rtk proxy python scripts/test-mariadb-integration.py` exited 0; **5 tests passed** in 0.030 seconds. This existing offline suite mocks subprocess execution. The printed cleanup failure/exit 17 and setup error/exit 1 are intentional negative-test outcomes asserted by the suite, not a real container failure. No MariaDB/Docker execution occurred. This proves five runner guards, not database behavior, hosted isolation or a full regression.
|
||||
|
||||
Read-only source checks confirmed the MariaDB default/missing-connection fail-closed behavior and the hosted workflow's explicit isolation blocker. Refer to SYSTEM_INVENTORY for inspected paths. `git diff --check` is a documentation hygiene check, not product qualification.
|
||||
|
||||
Historical test totals, PDF exports, security scans and provider runs remain attributed to their original evidence. They are not fresh runs here. `docs/automation/handoffs/pre-phase10/FINAL_DEVELOPMENT_ENTRY.md` records the latest approved unchanged-source applicability and narrow PDF metric/visual closure. Direct chat does not adopt or activate its historical autonomous-controller instructions.
|
||||
|
||||
NOT RUN: full backend/frontend/browser/Python/provider suites, builds, container validation, security scan/advisory refresh, performance, native review, deployment smoke, restore or rollback rehearsal. These require the actual release-stage source/environment and their own applicable gates. Discovery does not mark any mandatory release gate PASS merely because historical tests exist.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Recovery implementation review and rehearsal plan
|
||||
|
||||
2026-11-02; source `5cb903b989742b7a96cb22ddf4c0e24993b18980`. Read-only discovery. The entire 765-line `deploy/deploy.sh` was inspected, never executed or sourced. No operational keys, credentials, environment files, databases, containers or remote storage were accessed.
|
||||
|
||||
## Distinct recovery mechanisms
|
||||
|
||||
| Owner | Verified source behavior | Qualification boundary |
|
||||
| --- | --- | --- |
|
||||
| `SqliteDatabaseBackupRunner` | SQLite-only VACUUM snapshot and count retention; explicitly unsupported for MariaDB | Does not prove MariaDB backups exist |
|
||||
| `BackupController` | Authenticated, encrypted user-data export using application Data Protection; includes structured records and attachment metadata | Not a server-wide database/files/keys recovery set; do not use it as disaster recovery proof |
|
||||
| `deploy/deploy.sh:backup_database` | Requires explicit provider; SQL dump for MariaDB, volume archive for SQLite; rejects missing/empty/malformed/trailerless dumps | Format checks do not restore or compare records; emergency skip exists and is not approved here |
|
||||
| `backup_recovery_volumes` | MariaDB additionally archives required application data volume; independently archives tombstones when present | Consistency between dump/files/tombstones and current key availability still needs isolated rehearsal |
|
||||
| Retention/off-host copy | Daily/monthly retention and optional rclone copy | Source message calls remote encrypted but source does not establish actual remote encryption configuration; off-host configuration and recovery remain unverified |
|
||||
| `finish_deploy` | On failure after replacement, restarts previous backend/frontend image references when both exist; retains failed exit | No database reversal, no previous AI image restoration, and no post-rollback health check in this handler |
|
||||
| Tombstone store and replay | Validates ledger records; Program invokes `StageRestoredAccountsAsync` at startup; deleted identities are staged again | Test restored data with latest independent tombstones; corrupt ledger must fail closed. Existing AccountDeletionTests include restore replay, not a complete operational restore |
|
||||
|
||||
Build retry paths prune Docker build/system cache and deployment may warm an Ollama model. Therefore even a proposed "local deploy smoke" must not run this script against the shared workstation. No such action was taken.
|
||||
|
||||
## RR-11: backup connection parser mismatch - reproduced
|
||||
|
||||
The script's host `sed` expression accepts `Server=` but returns no host for `SERVER=` and `Host=`. The latter is a documented MySqlConnector alias ([official connection options](https://mysqlconnector.net/connection-options/), checked 2026-11-02). The script then fails its required-host check before dump execution. Impact: a valid application connection spelling can prevent deployment backup/deployment; no observed data loss or actual production impact. Engineering priority proposed P2; not a security vulnerability rating.
|
||||
|
||||
Three synthetic cases used only loopback and dummy identifiers. Exact source expression was extracted from the `db_host` assignment, then executed through Git Bash with `echo <shell-quoted synthetic input> | sed -n <shell-quoted source expression>`. Results: canonical `Server` -> `127.0.0.1`; uppercase `SERVER` -> empty; alias `Host` -> empty. Assertions confirmed all three results. No connection was opened. Initial direct Windows-to-sed invocations failed the positive-control assertion and were discarded as invalid harness evidence; the shell-native rerun passed its positive control.
|
||||
|
||||
Proposed future remediation: parse with the same supported connection-string semantics as the API, or explicitly validate/document a narrower deploy contract before any operational step. Cover aliases/case, quoted separators, duplicate keys, missing required values and secret-redaction behavior. Do not print real input. Any implementation belongs to an authorised release remediation scope, not P10 cleanup.
|
||||
|
||||
## Isolated rehearsal specification - NOT RUN
|
||||
|
||||
1. Bind accepted candidate, image digests, migration history and disposable host ownership. Use synthetic accounts and a separate temporary MariaDB/files/key/tombstone set; never copy production data here without separate authority.
|
||||
2. Seed two owners with canonical profiles, CV overrides, letter history, submitted packages, attachments, encrypted provider placeholders and completed deletion tombstones. Record independent counts and artifact hashes, without recording plaintext secrets.
|
||||
3. Produce the actual supported dump/files/tombstone recovery set. Demonstrate a failed dump or missing required volume prevents promotion. Verify off-host retrieval/decryption separately if that integration is approved.
|
||||
4. Restore into a second isolated environment. Independently compare rows/ownership/revisions/artifact bytes; exercise application decryption and document reopening. Restore an older database with newer tombstones and prove erased accounts cannot regain access. Inject a malformed ledger and assert fail-closed startup.
|
||||
5. Rehearse prior-image rollback after failed replacement, then after a representative additive migration and writes. Check `/health`, `/ready`, exact version and authenticated read/write preservation. Document when image rollback is insufficient and database restore would lose newer writes.
|
||||
6. Measure recovery duration and data-loss window against owner-approved RTO/RPO. No target or measurement exists in this checkpoint. Preserve cleanup and evidence identities; no production execution or release approval implied.
|
||||
|
||||
Fresh checks: Git Bash `bash -n deploy/deploy.sh` PASSED (syntax only); `python scripts/test-mariadb-workflow.py` PASSED 4/4 (source quarantine guards only). These are separate from the earlier 5/5 offline runner tests. Restore, failure injection, hosted isolation, live encryption and rollback health remain NOT TESTED.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Preliminary release blocker reconciliation
|
||||
|
||||
Discovery only, 2026-11-02. These are qualification gaps unless a concrete defect is stated. No inferred P0/P1 vulnerability ratings or owner risk acceptance.
|
||||
|
||||
| ID | Historical item / current finding | Status / closure evidence needed |
|
||||
| --- | --- | --- |
|
||||
| RR-01 | Phase 9/10 and production NO-GO | Phase 10 assessment now independently published, implementation still awaits exact approval. Later roadmap acceptance absent. BLOCKED for final RC |
|
||||
| RR-02 | Old Linux 11/24 and later S01 failure narratives | Latest handoff records approved S01 36/36 per platform and owner visual acceptance for unchanged development output. Do not reopen these metrics without affected change; physical print/device remain NOT TESTED |
|
||||
| RR-03 | Python advisory/native parser/image qualification | Latest scoped conditional disposition retained; current advisories and production-equivalent native isolation NOT TESTED. No new scan or risk waiver |
|
||||
| RR-04 | Hosted MariaDB/isolation | Source explicitly fails before execution until isolated runner proof. BLOCKED; fresh local 5 guard tests do not close it |
|
||||
| RR-05 | Human/device/provider | Actual AT/contrast, physical devices/print, native language and live provider accounts not qualified here. BLOCKED/NOT TESTED by lane |
|
||||
| RR-06 | Old known-issues F19 SQLite fallback statement | Current DatabaseConfiguration defaults to MariaDB and throws without connection. Old wording is superseded for this source. SQLite retirement still BLOCKED on transfer/restore/parity gates |
|
||||
| RR-07 | Old F01 stale-write statement | MariaDB acceptance documents bounded RD21 resolution; later handoff carries it. Do not reopen all old F01 findings based on stale index wording; final affected API census/regression remains required |
|
||||
| RR-08 | MySQL TLS / dependency / production identity | Latest handoff retains MYSQL-TLS-01 and OPS-01. No live configuration read; current advisory disposition and deployed equivalence unverified |
|
||||
| RR-09 | Backup/restore and rollback | Existing procedures and historical seeded rehearsal are not a current restore. Need isolated recovery-set completeness, decryption, tombstone behavior, post-write rollback compatibility and agreed RPO/RTO |
|
||||
| RR-10 | AI quality/activation | Deterministic Docling fixtures are distinct from real-model semantic accuracy. Optional unqualified adapter remains disabled; no AI invocation authorised here |
|
||||
| RR-11 | Backup host parsing rejects supported `Host=` alias; uppercase spelling also unrecognised | REPRODUCED with synthetic input and shell-native positive control. Deployment aborts before backup; proposed P2 compatibility defect. Fix scope awaits authorised release remediation |
|
||||
| RR-12 | Rollback restores core images without post-rollback health qualification; no schema/data/AI reversal | VERIFIED source limitation, runtime impact NOT TESTED. Requires isolated post-migration/post-write recovery rehearsal; no rollback guarantee |
|
||||
| RR-13 | Off-host encryption and recovery-set consistency not established by script success messages | NOT TESTED operational qualification. Verify remote encryption, retrieval/decryption, database/files/key/tombstone consistency in approved isolation |
|
||||
|
||||
Next remediation is evidence work: complete the source/endpoint/environment census and resolve historical records against their exact latest evidence. RR-11 now has a bounded future fix proposal in 11_BACKUP_RESTORE_ROLLBACK.md; no runtime implementation is authorised by this discovery checkpoint. Every fix needs minimal scope, targeted verification and the applicable stage approval.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Preliminary acceptance matrix
|
||||
|
||||
2026-11-02. **NO-GO for production.** This is a discovery snapshot, not Stage 7 final acceptance. No accepted final RC or production-equivalent build is nominated. PASS applies only to an executed gate, not historical source evidence.
|
||||
|
||||
| Mandatory gate | Current release verdict | Missing release evidence / dependency |
|
||||
| --- | --- | --- |
|
||||
| Functional regression | BLOCKED | Accepted final roadmap source; fresh applicable critical journeys |
|
||||
| Frontend/UX | BLOCKED | Accepted design/implementation and final source-bound browser evidence |
|
||||
| Accessibility | NOT TESTED | Actual AT/device/contrast/manual evidence plus automation |
|
||||
| Authentication/authorization | NOT TESTED | Final endpoint/tenant/session qualification |
|
||||
| Security assessment | BLOCKED | Dedicated security initiative start and predecessor gates; no scan run |
|
||||
| Backend APIs | NOT TESTED | Final API census and applicable regression |
|
||||
| MariaDB/migrations | BLOCKED | Hosted isolation and actual RC upgrade/recovery evidence; five offline guards are not this gate |
|
||||
| CV/PDF fidelity | BLOCKED | Final RC source equivalence and retained scoped approvals; physical print still missing |
|
||||
| AI integrations | BLOCKED | Explicit isolated-model qualification and semantic evidence |
|
||||
| Email/OAuth | BLOCKED | Actual configured provider/consent/account verification |
|
||||
| Performance/reliability | NOT TESTED | Approved load targets and bounded final-source measurements |
|
||||
| Docker/infrastructure | NOT TESTED | Exact images/environment/configuration/ingress parity |
|
||||
| CI/CD | BLOCKED | Hosted isolation and fresh actual run/artifacts |
|
||||
| Backup/restore | NOT TESTED | Disposable complete recovery rehearsal with content assertions |
|
||||
| Deployment/rollback | NOT TESTED | Authorised isolated rehearsal; production execution remains prohibited |
|
||||
| Monitoring | NOT TESTED | Failure detection, delivery and redaction evidence |
|
||||
| Documentation | NOT TESTED | Complete release-source/runbook reconciliation, beyond this partial discovery |
|
||||
| Human/device/provider | BLOCKED | Real reviewers/devices/accounts and source-bound records |
|
||||
|
||||
No gate is marked FAIL without fresh evidence of unacceptable behavior. No missing qualification is marked PASS. Historical source-specific acceptance may be reused where permitted and unchanged, but cannot establish final release readiness by itself.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Release discovery handoff
|
||||
|
||||
Branch `planning/release-readiness-discovery`, baseline `5cb903b989742b7a96cb22ddf4c0e24993b18980`; documentation only. Stage 1 IN_PROGRESS, not complete. Full authoritative release prompt read. No final GO/NO-GO completion report or deployable release claimed.
|
||||
|
||||
Completed: initial source inventory, historical blocker reconciliation, preliminary 18-gate matrix and five fresh offline MariaDB runner guard tests. Preserved scoped PDF acceptance and separate model/provider/human/hosted/production gates.
|
||||
|
||||
Next authorised work: complete endpoint and environment census, map each failure boundary to existing test/runbook owners, and inspect remaining backup/restore/deployment implementation without executing it. Then prepare a concrete isolated-rehearsal plan. Final baseline/qualification depends on accepted roadmap source; do not run thousands of unchanged tests to fill this report.
|
||||
|
||||
Continuation: recovery implementation inspection and isolated rehearsal specification are now recorded in 11_BACKUP_RESTORE_ROLLBACK.md. RR-11 parser mismatch reproduced using three synthetic cases; RR-12/13 retain rollback/encryption qualification limits. Four source quarantine guard tests and shell syntax check passed. No deployment script execution. Next: endpoint/environment census, then remaining independent I18N/homepage audit work. Stage 1 remains IN_PROGRESS.
|
||||
|
||||
No production credentials/environment files were opened; no infrastructure, models, main branch or autonomous orchestrator changed. The earlier delegated researcher was interrupted by a service limit; parent supplied this partial checkpoint. No background agent remains active.
|
||||
@@ -0,0 +1,19 @@
|
||||
# RR-11 bounded remediation
|
||||
|
||||
Authorised by the owner's Continuous Roadmap Execution instruction explicitly prioritising this fix. Branch `codex/release-backup-parser`, based on published discovery `653f8d04d469dcd52c883d4b98bea76b8adacada`. Separate from CLEAN-20, full release qualification and infrastructure operations.
|
||||
|
||||
Replaced five inconsistent sed expressions with one stdlib Python parser and a shell-safe field transfer. Case-insensitive aliases, quoted separators/doubled quotes, Unicode and empty passwords survive to the dump command. Invalid ports, incomplete/ambiguous/control-bearing values and unsupported multi-host/socket/path forms fail before dumping. No eval; connection input travels via stdin, not argv; errors never include input. No dependency added. Existing dump/TLS/retention/rollback behavior is otherwise unchanged.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Regression first: four test methods ran against the original backup function; eight subcase failures reproduced alias/case rejection and quoted-password truncation. Canonical/negative controls executed. No real dump or deployment.
|
||||
- Final targeted suite: **5/5 methods PASSED**, including 7 host spellings, quoted semicolon/equal/doubled quote/shell characters, Unicode/single quotes, empty password, and six invalid/ambiguous input cases. Tests run the actual backup function and mock only external dump/Docker boundaries.
|
||||
- Full offline script unittest set: **31/31 PASSED**, including targeted cases, ingress Compose rendering with synthetic env, workflow/runner guards, retry guard, supply-chain fixtures and offline model-evaluation helpers. No model invocation. Printed exit17/exit1 are expected mocked runner negative cases. Do not add targeted five to the overlapping total.
|
||||
- Ordinary unittest discovery found zero tests because repository filenames contain hyphens; recorded as NOT A PASS. Explicit importlib loading of every `scripts/test-*.py` unittest module (excluding standalone Docker topology runner) executed the 31 tests. Docker topology qualification NOT RUN.
|
||||
- Git Bash `bash -n deploy/deploy.sh`: PASSED.
|
||||
- Graphify AST update completed locally: 18,504 nodes / 45,920 edges; missing optional PDF/SQL extractors reported. Newly generated untracked graph output retained locally, not a runtime verification or introduced repository artifact.
|
||||
- Product/backend/frontend/full database/browser suites NOT RUN: this package changes only deploy parsing and its offline tests. Real dump/restore, Linux-host operational behavior, hosted CI and production validation remain NOT TESTED.
|
||||
|
||||
The first Unicode test attempt failed in the Windows test harness encoding before reaching product code; the harness was corrected to UTF-8, then passed. Parser stdin/stdout are explicitly UTF-8. No application user data or production credentials used.
|
||||
|
||||
Rollback: revert this bounded implementation commit to restore the prior parser (and its known limitation). No database/schema change needs reversal. RR-11 is FIXED AND OFFLINE-TESTED on this development branch; RR-12/13 and production remain open. No main merge/deploy authorised.
|
||||
@@ -1,14 +0,0 @@
|
||||
# Codex Security scan register
|
||||
|
||||
3 November2026 update: owner approved exact root policy creation/publication. Define-security-policy workflow COMPLETED for root policy only; source base88bcd6d9, exact candidate and resolver results in ../security/ROOT_POLICY_APPROVAL.md. Managed standalone artifact ID/path is documented there; there is no scan ID. Root now resolves for API/UI, nested CV-doc policy remains closest. Baseline/deep/final scans below remain NOT RUN/BLOCKED; no charges, active targets or release acceptance approved. Earlier policy-pending entries are historical.
|
||||
|
||||
2 November 2026. Source baseline `ba826de1e5c959352e4442ab2fec5d02445dd32d`, branch codex/security-offline-preflight. Owner confirms installation; actual tool metadata/skill files verified. Production/third parties excluded. Paid/deep compute allowance pending. ROE: ../security/00-rules-of-engagement.md.
|
||||
|
||||
| Phase/workflow | Status | Actual evidence and limits |
|
||||
| --- | --- | --- |
|
||||
| 00 define-security-policy review | NOT RUN (full workflow); resolver substep COMPLETED | Installed helper resolve-security-md --list exited 0, returned two docs-only policies. No root policy created; proposal/review/approval remains. No scan ID. |
|
||||
| 01 baseline standard scan | NOT RUN | Skill/tool exposure checked; threat-model/policy and capability preflight pending. No scan artifacts or findings. |
|
||||
| 03 deep scan | BLOCKED | Baseline and separately approved compute/cost scope missing. No scan launched. |
|
||||
| 15 final standard and diff scans | NOT RUN | Await baseline, remediation range and independent verification. |
|
||||
|
||||
No scan findings, closure verdicts or human release acceptance are implied by tool availability or regression tests.
|
||||
@@ -1,11 +0,0 @@
|
||||
# Security Phase 00 — offline engagement draft
|
||||
|
||||
2 November 2026. Owner: Connor, direct-chat roadmap authorisation. Source baseline `ba826de1e5c959352e4442ab2fec5d02445dd32d`; isolated branch `codex/security-offline-preflight`. Phase 00 is PARTIAL, not accepted.
|
||||
|
||||
Authorised now: source inspection, harmless synthetic local fixtures, minimal ordinary remediation within approved roadmap boundaries, sanitized reports and branch publication. No network attack target has been approved. Production, adjacent LAN services, real accounts/data, identity/payment/email providers, live AI/models, orchestrator state/keys/configuration/queue and the pre-P10 acceptance worktree are excluded.
|
||||
|
||||
Active staging testing is BLOCKED until exact host/IP, test users/roles, rate/concurrency/duration limits, independent database/storage, disabled egress and recovery/stop controls are approved and verified. Do not infer those from example localhost URLs or repository credentials. Emergency stop: terminate only the named audit-owned fixture process; never stop existing production/user services.
|
||||
|
||||
Proposed fixture boundary: disposable synthetic tenant A, tenant B and administrator; separate temporary data root and database; inert CV files and canary records; mocked OAuth, billing, email, job providers and AI. No routable credentials. Snapshot and restore qualification must precede staged mutation. This plan has not been instantiated or approved for active network testing.
|
||||
|
||||
Evidence: only sanitized reports/test artifacts in Git. Do not inspect real .env values, auth/signing stores or personal documents. Findings require calibrated confidence and safe reproduction; absence of a scan result proves nothing. No root SECURITY.md policy edits, suppressed findings or accepted risks are authorised by this draft.
|
||||
@@ -1,17 +0,0 @@
|
||||
# Phase 01 — partial source-backed threat model
|
||||
|
||||
2 November 2026, baseline ba826de1e5c959352e4442ab2fec5d02445dd32d. PARTIAL; baseline scan NOT RUN. Read the complete Phase 01 prompt and architecture frontend/backend/data-flow/integrations, API reference, security notes and deployment overview. The graph query failed because the new isolated worktree has no graph; direct scoped source review followed. No active network request or live-service test.
|
||||
|
||||
Verified dependency and documented data-flow inventory is in ASSESSMENT_INDEX.md. Threat actors to test: anonymous caller, tenant A, tenant B, administrator, revoked/limited session, malicious document author, incoming webhook sender and compromised external service. Assets: canonical career profile and history, CV/letter snapshots, application records, attachments, OAuth/mail tokens, sessions, billing entitlements, backups and administrative privileges. Secret values have not been inspected.
|
||||
|
||||
Source boundary observations (not proof of a secure deployment):
|
||||
|
||||
- AttachmentsController requires the local authentication scheme. List first queries the requested job; download/rename call FindOwnedAttachmentAsync. Downloads reject unmanaged paths and pending/missing files. Global filter/helper implementation, alternate upload paths and negative tests still need independent audit.
|
||||
- UsersController declares Admin role authorization. Role mutation and reset/email operations require endpoint-specific review; class attributes alone do not establish safe privilege transitions.
|
||||
- BillingController status/checkout/portal explicitly use local authentication; webhook explicitly allows anonymous requests and caps request size at 1 MB. Webhook verifies the Stripe signature via ConstructEvent, rejects invalid signatures, then refreshes subscription state. This can trigger external Stripe egress when configured; do not execute it against a real provider during offline tests. Review identity binding, replay, price qualification and mock negative coverage before acceptance.
|
||||
|
||||
Initial attack tree: reach another tenant's data via object IDs, background execution or shared files; obtain higher privilege via role/session/OAuth flows; cross file/network/model boundaries through uploaded content; forge billing authority through webhook or stale subscription state; expose secrets through public bundles/logs/backups. Each branch requires source tracing plus synthetic negative proof. These are hypotheses, not confirmed vulnerabilities.
|
||||
|
||||
ASVS Level 2 is a proposed planning target for private employment data, not a compliance claim. No unverified requirement IDs or vulnerability identifiers are assigned. Partial endpoint matrix is ENDPOINT_AUTH_MATRIX.csv; tests listed there are candidate existing suites, NOT executed audit evidence. Full route enumeration, middleware/global filter review, deployed exposure and complete endpoint/test mapping remain open.
|
||||
|
||||
Next permitted review: resolve owner filters and attachment sibling paths, inspect auth/session middleware, and complete the source matrix. Baseline standard scan requires the documented capability preflight and recorded results; no scan pass is inferred from this threat model. Root policy changes, if proposed, require exact-diff approval under the policy skill; keep review-only work progressing meanwhile.
|
||||
@@ -1,21 +0,0 @@
|
||||
# Independent security assessment continuation
|
||||
|
||||
3 November2026 — root policy proposal explicitly approved and applied; ROOT_POLICY_APPROVAL.md records exact text identity and successful API/UI/nested policy resolution. This closes the root-policy decision only. Baseline/deep/final scans, current integration target/capability/cost preflight, full matrix and staging/human/release qualifications remain open. No new application/security fix or vulnerability closure is claimed.
|
||||
|
||||
Continuation: ATTACHMENT_OWNERSHIP_REVIEW.md traces the parent ownership filter and corrects the reflection-only test assumption. Fourteen existing relational fixture tests freshly passed on identical Phase 10 backend/test sources; native evidence published at db818e5e. Matrix distinguishes direct controller denials from unexecuted HTTP/list/upload/missing-owner/MariaDB checks. No full security scan or final integrated acceptance is implied.
|
||||
|
||||
Phase 00 offline preflight, 2 November 2026. Branch `codex/security-offline-preflight`, application baseline `ba826de1e5c959352e4442ab2fec5d02445dd32d`. No application/security implementation changed. No staging attacks or production access.
|
||||
|
||||
Read the external master, complete Phase 00 prompt, ROE/finding/release/scan-register templates, repository auditor skill, Codex Security policy and standard-scan skill instructions. Remaining stage prompts must be read before entering their stages. The old master heading “DO NOT START THIS NOW” is superseded for permitted offline work by the owner's later direct execution instruction; protected gates remain.
|
||||
|
||||
Verified inventory: package.json identifies Next 16.4.1, React 19/MUI/TypeScript; API csproj targets .NET 9 with EF SQLite and Pomelo MariaDB, Identity/JWT, Stripe, MailKit; Python requirements identify FastAPI/transformers/document dependencies. These are source dependencies, not deployed identities or proven reachable features. Architecture overview documents nginx/static export -> API -> DB/files/internal AI and provider boundaries; runtime exposure remains unverified. Preserve canonical Career Profile, CV null-as-inherit overrides, letter history and JobApplication.Version/RD21 contracts. No technology-version vulnerability claim is made without advisory/reachability analysis.
|
||||
|
||||
Baseline evidence reused from this exact source ancestry: P10-04 has 89 backend and 92 frontend tests passing, independently source-bound native artifacts. CLEAN-20 has five focused browser checks plus type/lint/build evidence. These are regression evidence, not a completed security audit; this new worktree did not rerun them. Backup parser repair exists on a separate published branch and is not integrated here.
|
||||
|
||||
Plugin exposure: standard/deep scan, draft/artifact/progress tools and documented skill files are available. The actual policy resolver ran successfully and found only docs/cv-import/SECURITY.md and docs/redesign/phase-9/p9-requal-01/SECURITY.md. Neither is an inherited root policy for API/UI source. No baseline/deep scan has run. Root policy creation needs exact proposed diff and separate owner approval under define-security-policy; defer that decision while independent source review continues. No new charge approved; deep scan remains blocked.
|
||||
|
||||
Execution order: finish inventory/threat boundaries and baseline policy review; standard scan after required preflight/gates; deep scan only with cost scope; phases 02–12 offline review/fixture work in source order; active phase 13 only with approved isolated target; remediation and independent retest; final full standard/diff scans; human release gate. Read every applicable subordinate prompt before its work. Do not treat these milestones as complete based on this plan.
|
||||
|
||||
Open: approved ROE/staging isolation, complete trust-boundary/endpoint matrix, root policy decision, baseline/final scans, deep-scan allowance, full independent audit, provider/device/CI/deployment identity and live backup/restore. Security release recommendation: BLOCKED/NO-GO pending evidence. No new vulnerability has been asserted in Phase 00.
|
||||
|
||||
Next permitted action: read Phase 01 and the linked architecture topics, verify endpoint/actor boundaries from source, and prepare the policy proposal without writing an unapproved root policy. No user input is immediately required for that review.
|
||||
@@ -1,11 +0,0 @@
|
||||
# Attachment ownership follow-through
|
||||
|
||||
2 November 2026. Source review plus bounded existing relational fixtures; not complete Phase 05, integrated security testing or deployed acceptance.
|
||||
|
||||
AttachmentsController.FindOwnedAttachmentAsync:42–47 includes JobApplication while selecting the raw child ID. JobTrackerContext:169–170 denies missing owners and filters JobApplication by CurrentUserId; the Attachment relation at 264–268 uses the parent foreign key. Attachment itself does not have a separate owner filter. Download, Rename and Delete all call this helper. ListForJob first checks the owned JobApplication exists, then limits child rows to that ID. Maintain these relationships; raw child queries without equivalent parent checks are not automatically safe.
|
||||
|
||||
AttachmentsControllerTests contains attribute/extension/size reflection tests, not cross-tenant behavioral proof. OwnershipGuardTests similarly checks helper presence. The stronger existing AttachmentConsistencyTests.Another_users_attachment_cannot_be_downloaded_renamed_or_deleted seeds a foreign job/file, clears tracking, checks three NotFound results and verifies the row/file remain.
|
||||
|
||||
Fresh targeted execution on the Phase 10 working tree: all 14 AttachmentConsistencyTests passed, zero failures/skips, exit 0, temporary SQLite and synthetic files only. Before execution, git diff between security and Phase 10 branches showed no JobTrackerApi/JobTrackerApi.Tests source difference. Existing Release assembly used --no-build/--no-restore. Evidence published and independently verified on Phase 10 at `db818e5ebd19eeedc05ae58dc3f7a908ce0e713d`, docs/redesign/phase-10/p10-04-evidence/attachment-ownership-and-storage.trx.gz. Uncompressed SHA256 `b589ad1139cb200d9de64b863d65e3304cea0230eda55d89476e5f79347fe84a`.
|
||||
|
||||
Result: selected local controller/storage behavior PASSED. Not an authenticated HTTP attack, complete negative list/upload/missing-owner matrix, MariaDB retest, production probe or final scan. Keep those gaps open. No new vulnerability or production exception is asserted; no backend fix was needed for these reviewed paths. Final integrated scan/testing stays deferred until relevant application changes are complete.
|
||||
@@ -1,10 +0,0 @@
|
||||
method,route,source,declared_auth,control_observed,sensitive_asset_or_effect,candidate_test_suite,audit_execution
|
||||
GET,/api/attachments/{jobId:int},AttachmentsController.cs:87,local scheme,parent owner-filtered existence check before child listing,attachment metadata,AttachmentConsistencyTests,source reviewed; negative list and HTTP NOT RUN
|
||||
GET,/api/attachments/download/{id:int},AttachmentsController.cs:103,local scheme,parent Include ownership filter and managed path checks,attachment bytes,AttachmentConsistencyTests,PASSED foreign-owner controller rejection; HTTP NOT RUN
|
||||
PATCH,/api/attachments/{id:int},AttachmentsController.cs:125,local scheme,parent Include ownership filter via shared helper,metadata and AI-use flag,AttachmentConsistencyTests,PASSED foreign-owner controller rejection; HTTP NOT RUN
|
||||
DELETE,/api/attachments/{id:int},AttachmentsController.cs:170,local scheme,parent Include ownership filter via shared helper,attachment removal,AttachmentConsistencyTests,PASSED foreign-owner controller rejection with row/file retained; HTTP NOT RUN
|
||||
GET,/api/billing/status,BillingController.cs:42,local scheme,method declares local authentication,entitlement status,BillingControllerTests,NOT RUN
|
||||
POST,/api/billing/checkout,BillingController.cs:58,local scheme,method declares local authentication; provider mocked only,checkout session,BillingControllerTests,NOT RUN
|
||||
POST,/api/billing/portal,BillingController.cs:104,local scheme,method declares local authentication; provider mocked only,billing portal,BillingControllerTests,NOT RUN
|
||||
POST,/api/billing/webhook,BillingController.cs:132,AllowAnonymous,signature verification and size limit; full binding audit pending,subscription entitlement,BillingControllerTests,NOT RUN
|
||||
PUT,/api/users/{id}/roles,UsersController.cs:142,Admin role at class,privilege transition review pending,account roles,UsersControllerTests,NOT RUN
|
||||
|
@@ -1,13 +0,0 @@
|
||||
# Root policy approval and resolver verification
|
||||
|
||||
3 November2026. Owner explicitly answered **"Approve policy creation and publication"** to the exact41-line SECURITY.md proposal and target in this chat. Approval includes validating the chain and committing/publishing on the security review branch; it does not approve a scan, charge, active testing or production action.
|
||||
|
||||
Target was reread and remained absent; HEAD was88bcd6d91c6823ffc6bbc6a6de176ab7769e729f and worktree was clean before creation. Applied text matches the approved candidate exactly after normal newline decoding. Policy LF SHA256: `4ec5365bdc1f84410b596b893463572b035f03d1260f51af722d1c32c8fa3eb3`.
|
||||
|
||||
Installed define-security-policy workflow: policy inventory and root/nearest-path guidance reviewed; both existing docs-only policies resolved inside the repository, regular files below1MiB. API had no inherited policy before the change. After creation the actual resolver exited0 for JobTrackerApi and job-tracker-ui with the new root policy, and for docs/cv-import with root plus its existing nearer policy. No inherited policy overwritten. Diff whitespace check passed. No application tests run for this Markdown-only change.
|
||||
|
||||
Exact proposal retained through Codex Security managed standalone artifact storage: `artifacts/root-policy-proposal.md`, artifact SHA256 `5d00af57cfa55de2792218916fd9cf646b470d821db6e8afee5c59c76dba116b`, at `C:\Users\Connor\.codex\state\plugins\codex-security\scans\Job-tracker\artifacts-ad2afb630ff68e10e32f349b87541ffb5778071babb98965362c085936a3f385\artifacts\root-policy-proposal.md`. Persistent readback succeeded. This is a standalone document collection, not a scan ID or result.
|
||||
|
||||
No new finding exclusion, risk acceptance or severity downgrade. Canonical-profile, tenant, revision, parser/file, AI-consent, provider and recovery invariants remain review requirements, not assertions that all controls already pass. No secret values or private user content read/exported. Source-security and final integrated acceptance remain open.
|
||||
|
||||
Next: reconcile the intended immutable integration revision before a baseline scan, finish capability/cost/scope preflight and record actual results. The older offline audit branch is not a final scan of A3 UI changes. Deep allowance and isolated staging remain distinct protected decisions; final integrated scanning follows relevant application changes.
|
||||
@@ -29,6 +29,7 @@ import JobTable from "./components/JobTable";
|
||||
import NotificationsPopover from "./components/NotificationsPopover";
|
||||
import type { JobTableColumns } from "./components/JobTable";
|
||||
import { I18nProvider, useI18n } from "./i18n/I18nProvider";
|
||||
import { translations } from "./i18n/translations";
|
||||
import LoginPage from "./views/LoginPage";
|
||||
import LandingPage from "./views/LandingPage";
|
||||
import ForgotPasswordPage from "./views/ForgotPasswordPage";
|
||||
@@ -485,7 +486,7 @@ export default function App() {
|
||||
}, [invalidated]);
|
||||
// Unmount providers as well as routes: confirmations/toasts can retain private text and
|
||||
// callbacks. Passive effect navigation runs after the old forms' unload guards are removed.
|
||||
return invalidated ? <main role="alert">Session changed. Clearing this page…</main> : <Application />;
|
||||
return invalidated ? <main role="alert">{translations[document.documentElement.lang === "nb-NO" ? "nb" : "en"].sessionChangedCleared}</main> : <Application />;
|
||||
}
|
||||
|
||||
function Application() {
|
||||
|
||||
@@ -20,6 +20,32 @@ beforeEach(() => {
|
||||
});
|
||||
afterEach(() => jest.useRealTimers());
|
||||
|
||||
test.each([
|
||||
['en', 'constructor', 'Other operation', 'Unknown', 'Unregistered worker', 'Not recorded'],
|
||||
['en', 'toString', 'Other operation', 'Unknown', 'Unregistered worker', 'Not recorded'],
|
||||
['en', '__proto__', 'Other operation', 'Unknown', 'Unregistered worker', 'Not recorded'],
|
||||
['nb', 'constructor', 'Annen oppgave', 'Ukjent', 'Uregistrert bakgrunnsprosess', 'Ikke registrert'],
|
||||
['nb', 'toString', 'Annen oppgave', 'Ukjent', 'Uregistrert bakgrunnsprosess', 'Ikke registrert'],
|
||||
['nb', '__proto__', 'Annen oppgave', 'Ukjent', 'Uregistrert bakgrunnsprosess', 'Ikke registrert'],
|
||||
])('uses safe %s fallbacks for inherited processing key %s', async (language, key, other, unknown, unregistered, notRecorded) => {
|
||||
localStorage.setItem('uiLanguage', language);
|
||||
const unknownItem = { ...item, taskType: key, status: key, stage: key };
|
||||
get.mockImplementation((url: string) => Promise.resolve({ data: url.includes('/events')
|
||||
? { items: [{ id: '1', kind: key, status: key, stage: key, attempt: 1, failureCode: null, atUtc: item.createdAtUtc }], hasMore: false, retentionDays: 30 }
|
||||
: url.includes(item.id) ? unknownItem
|
||||
: { ...listing, items: [unknownItem], counts: { [key]: 1 }, workers: [{ ...listing.workers[0], name: key }] } }));
|
||||
open();
|
||||
const details = await screen.findByText(other, { selector: 'button' });
|
||||
expect(screen.getByText(`${unknown}: 1`)).toBeVisible();
|
||||
const workerLabel = screen.getByText(new RegExp(`^${unregistered}:`));
|
||||
fireEvent.click(workerLabel.closest('details')!.querySelector('summary')!);
|
||||
expect(workerLabel).toBeVisible();
|
||||
fireEvent.click(details);
|
||||
const events = await screen.findByRole('list');
|
||||
expect(events).toHaveTextContent(`${unknown} · ${unknown}`);
|
||||
expect(events).toHaveTextContent(notRecorded);
|
||||
});
|
||||
|
||||
test('shows safe job metadata and real events with a copyable correlation ID', async () => {
|
||||
open();
|
||||
expect(await screen.findByText('Synthetic owner')).toBeVisible();
|
||||
|
||||
@@ -24,6 +24,17 @@ function Providers({ children }: { children: React.ReactNode }) {
|
||||
|
||||
afterEach(() => localStorage.clear());
|
||||
|
||||
test('language controls identify Bokmål with its native name and language code', () => {
|
||||
render(<Providers><AppShell pageTitle="Jobs" breadcrumbs={[]} pathname="/jobs" nav={nav} navBottom={[]} onNavigate={jest.fn()} onToggleDrawer={jest.fn()} drawerOpen={false}><div>Jobs content</div></AppShell></Providers>);
|
||||
const bokmal = screen.getAllByRole('button', { name: 'Norsk bokmål' })[0];
|
||||
expect(bokmal).toHaveTextContent('NB');
|
||||
expect(bokmal).toHaveAttribute('lang', 'nb');
|
||||
expect(screen.getAllByRole('button', { name: 'English' })[0]).toHaveAttribute('lang', 'en');
|
||||
fireEvent.click(bokmal);
|
||||
expect(bokmal).toHaveAttribute('aria-pressed', 'true');
|
||||
expect(localStorage.getItem('uiLanguage')).toBe('nb');
|
||||
});
|
||||
|
||||
test("[P5-01 PRES-E07 E08] grouped navigation exposes the current child without marking the parent as the page", () => {
|
||||
render(<Providers><AppShell pageTitle="Board" breadcrumbs={[]} pathname="/kanban" nav={nav} navBottom={[]} onNavigate={jest.fn()} onToggleDrawer={jest.fn()} drawerOpen={false}><div>Board content</div></AppShell></Providers>);
|
||||
expect(screen.getByRole("navigation", { name: "Navigation" })).toBeInTheDocument();
|
||||
|
||||
@@ -68,6 +68,14 @@ test("localizes system checklist content without changing user-authored tasks",
|
||||
expect(screen.getByText("Ask Sara for a referral")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test.each(['en', 'nb'])('preserves unknown historical system task text in %s', async language => {
|
||||
window.localStorage.setItem('uiLanguage', language);
|
||||
mockedApi.get.mockResolvedValue({ data: checklist([item({ systemKey: 'retired-system-task', title: 'Reviewed historical task', description: 'Original task details' })]) } as any);
|
||||
renderChecklist();
|
||||
expect(await screen.findByText('Reviewed historical task')).toBeInTheDocument();
|
||||
expect(screen.getByText('Original task details')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders the checklist grouped by category with progress", async () => {
|
||||
renderChecklist();
|
||||
|
||||
|
||||
@@ -78,6 +78,12 @@ const match = {
|
||||
|
||||
beforeEach(() => jest.clearAllMocks());
|
||||
|
||||
test.each(["constructor", "toString", "__proto__"])("timeline preserves unknown category %s as a label", async category => {
|
||||
mockedApi.get.mockResolvedValue({ data: { ...timeline, categories: ["stage", category] } } as any);
|
||||
render(<ApplicationTimeline jobId={7} />);
|
||||
expect(await screen.findByRole("button", { name: category })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// ---------- Timeline ----------
|
||||
|
||||
test("timeline renders grouped days, milestones and readable summaries", async () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { api } from "./api";
|
||||
import { translations } from "./i18n/translations";
|
||||
|
||||
// Mirrors WorkspaceOverviewDto. Read-only aggregate — the workspace owns no data.
|
||||
export type WorkspaceCv = {
|
||||
@@ -93,7 +94,9 @@ export const WORKSPACE_SECTIONS: { key: WorkspaceSectionKey; label: string }[] =
|
||||
|
||||
export function checklistSystemTranslationToken(systemKey?: string | null): string | null {
|
||||
if (!systemKey || systemKey.startsWith("learning:")) return null;
|
||||
return systemKey.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase());
|
||||
const token = systemKey.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase());
|
||||
return Object.hasOwn(translations.en, `checklistItem_${token}`)
|
||||
&& Object.hasOwn(translations.en, `checklistItem_${token}Description`) ? token : null;
|
||||
}
|
||||
|
||||
const LEGACY_WORKSPACE_SECTIONS: Record<string, WorkspaceSectionKey> = {
|
||||
|
||||
@@ -22,6 +22,33 @@ const mockedApi = api as jest.Mocked<typeof api>;
|
||||
|
||||
beforeEach(() => jest.clearAllMocks());
|
||||
|
||||
test.each([
|
||||
['en', 'Preview: resume.pdf', 'Preview: resume.pdf'],
|
||||
['nb', 'Forhåndsvis: resume.pdf', 'Forhåndsvisning: resume.pdf'],
|
||||
])('PDF preview identifies the document in %s', async (language, buttonName, frameTitle) => {
|
||||
const previousLanguage = localStorage.getItem('uiLanguage');
|
||||
const originalCreate = URL.createObjectURL;
|
||||
const originalRevoke = URL.revokeObjectURL;
|
||||
localStorage.setItem('uiLanguage', language);
|
||||
URL.createObjectURL = jest.fn(() => 'blob:attachment-preview');
|
||||
URL.revokeObjectURL = jest.fn();
|
||||
mockedApi.get.mockResolvedValueOnce({ data: [{ id: 7, fileName: 'resume.pdf', fileType: 'application/pdf', fileSize: 2048, uploadDate: '2026-11-02T10:00:00Z' }] } as any);
|
||||
mockedApi.get.mockResolvedValueOnce({ data: new Blob(['sample'], { type: 'application/pdf' }) } as any);
|
||||
const view = render(<ToastProvider><I18nProvider><ConfirmProvider><PromptProvider><Attachments jobId={42} /></PromptProvider></ConfirmProvider></I18nProvider></ToastProvider>);
|
||||
try {
|
||||
fireEvent.click(await screen.findByRole('button', { name: buttonName }));
|
||||
const frame = await screen.findByTitle(frameTitle);
|
||||
expect(frame.tagName).toBe('IFRAME');
|
||||
expect(frame).toHaveAttribute('src', 'blob:attachment-preview');
|
||||
} finally {
|
||||
view.unmount();
|
||||
URL.createObjectURL = originalCreate;
|
||||
URL.revokeObjectURL = originalRevoke;
|
||||
if (previousLanguage === null) localStorage.removeItem('uiLanguage');
|
||||
else localStorage.setItem('uiLanguage', previousLanguage);
|
||||
}
|
||||
});
|
||||
|
||||
test('attachments empty state renders drag and drop guidance', async () => {
|
||||
mockedApi.get.mockResolvedValueOnce({ data: [] } as any);
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { localizeCompletenessLabel } from "./views/career/careerCompletenessLabels";
|
||||
|
||||
test.each(["constructor", "toString", "__proto__", "Custom section"])("preserves unknown completeness label %s as data", label => {
|
||||
const translate = jest.fn(() => "translated");
|
||||
expect(localizeCompletenessLabel(label, translate)).toBe(label);
|
||||
expect(translate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("translates a known completeness label using its catalogue key", () => {
|
||||
const translate = jest.fn(() => "Personopplysninger");
|
||||
expect(localizeCompletenessLabel("Personal details", translate)).toBe("Personopplysninger");
|
||||
expect(translate).toHaveBeenCalledWith("profileCvCompletenessPersonal");
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { checklistSystemTranslationToken } from './applicationWorkspace';
|
||||
|
||||
test.each(['retired-system-task', 'constructor', '__proto__'])('does not manufacture translation keys for %s', key => {
|
||||
expect(checklistSystemTranslationToken(key)).toBeNull();
|
||||
});
|
||||
|
||||
test('retains the known system translation and learning/custom boundaries', () => {
|
||||
expect(checklistSystemTranslationToken('prepare-cv')).toBe('prepareCv');
|
||||
expect(checklistSystemTranslationToken('learning:prepare-cv')).toBeNull();
|
||||
expect(checklistSystemTranslationToken(null)).toBeNull();
|
||||
});
|
||||
@@ -364,7 +364,7 @@ function timelineCategoryLabel(t: (key: any) => string, category: string): strin
|
||||
communication: "intelligenceCategoryCommunication",
|
||||
ai: "intelligenceCategoryAi",
|
||||
};
|
||||
return keys[category] ? t(keys[category]) : category;
|
||||
return Object.hasOwn(keys, category) ? t(keys[category]) : category;
|
||||
}
|
||||
|
||||
function matchBandLabel(t: (key: any) => string, band?: string): string {
|
||||
|
||||
@@ -358,7 +358,7 @@ export default function Attachments({ jobId }: { jobId: number }) {
|
||||
<img src={preview.url} alt={preview.name} style={{ maxWidth: "100%", maxHeight: "70vh", borderRadius: 12 }} />
|
||||
</Box>
|
||||
) : isPdfType(preview.type) ? (
|
||||
<iframe title="pdf" src={preview.url} style={{ width: "100%", height: 560, border: 0, borderRadius: 12 }} />
|
||||
<iframe title={t("attachmentsPreviewTitle", { name: preview.name })} src={preview.url} style={{ width: "100%", height: 560, border: 0, borderRadius: 12 }} />
|
||||
) : (
|
||||
<Typography sx={{ color: "text.secondary" }}>{t("attachmentsNoInlinePreview")}</Typography>
|
||||
)}
|
||||
|
||||
@@ -502,5 +502,5 @@ function interviewCategoryLabel(t: (key: any) => string, category: string) {
|
||||
note: "interviewCategoryNotes",
|
||||
debrief: "interviewCategoryDebrief",
|
||||
};
|
||||
return keys[category] ? t(keys[category]) : category;
|
||||
return Object.hasOwn(keys, category) ? t(keys[category]) : category;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,15 @@ function page(items: ReturnType<typeof item>[] = [item()], currentPage = 1, tota
|
||||
return { items, page: currentPage, pageSize: 25, total, totalPages: Math.ceil(total / 25) };
|
||||
}
|
||||
|
||||
describe.each(['en', 'nb'])('%s unknown attachment purposes', language => {
|
||||
test.each(['constructor', 'toString', '__proto__'])('uses the localized Other label for %s', async purpose => {
|
||||
localStorage.setItem('uiLanguage', language);
|
||||
mockedApi.get.mockResolvedValue({ data: page([item({ type: 'attachment', purpose, visibility: 'private' })]) } as never);
|
||||
renderPage();
|
||||
expect(await screen.findByText(`${copy('attachmentsPurpose')}: ${copy('attachmentsPurposeOther')}`)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T) => void;
|
||||
let reject!: (reason: unknown) => void;
|
||||
|
||||
@@ -62,6 +62,16 @@ beforeEach(() => {
|
||||
window.localStorage.setItem("uiLanguage", "en");
|
||||
});
|
||||
|
||||
describe.each(['en', 'nb'])('%s unknown interview categories', language => {
|
||||
test.each(['constructor', 'toString', '__proto__'])('preserves %s as a category label', async category => {
|
||||
window.localStorage.setItem('uiLanguage', language);
|
||||
routeGet({ board: { ...board, groups: [{ ...board.groups[0], category }] } });
|
||||
render(<ApplicationInterviewPrep jobId={7} />);
|
||||
expect(await screen.findByText(category)).toBeInTheDocument();
|
||||
expect(screen.getByText('Funding history')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("localizes interview categories while preserving user-authored preparation", async () => {
|
||||
window.localStorage.setItem("uiLanguage", "nb");
|
||||
routeGet();
|
||||
|
||||
@@ -689,8 +689,8 @@ export function StackLanguageToggle({ language, setLanguage, onDark = false }: {
|
||||
...(onDark ? { "& .MuiButton-outlined": { color: "#fff", borderColor: "rgba(255,255,255,0.55)" } } : {}),
|
||||
}}
|
||||
>
|
||||
<Button aria-label="English" aria-pressed={language === "en"} variant={language === "en" ? "contained" : "outlined"} onClick={() => setLanguage("en")}>EN</Button>
|
||||
<Button aria-label="Norsk" aria-pressed={language === "nb"} variant={language === "nb" ? "contained" : "outlined"} onClick={() => setLanguage("nb")}>NO</Button>
|
||||
<Button lang="en" aria-label="English" aria-pressed={language === "en"} variant={language === "en" ? "contained" : "outlined"} onClick={() => setLanguage("en")}>EN</Button>
|
||||
<Button lang="nb" aria-label="Norsk bokmål" aria-pressed={language === "nb"} variant={language === "nb" ? "contained" : "outlined"} onClick={() => setLanguage("nb")}>NB</Button>
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,12 @@ import {
|
||||
} from './pipeline';
|
||||
|
||||
describe('pipeline', () => {
|
||||
test.each(['constructor', '__proto__', 'toString'])('keeps inherited alias name %s as a custom status', status => {
|
||||
const translate = jest.fn(() => 'translated');
|
||||
expect(normalizeStatus(status)).toBe('Other');
|
||||
expect(statusLabel(translate, status)).toBe(status);
|
||||
expect(translate).not.toHaveBeenCalled();
|
||||
});
|
||||
test('normalizeStatus canonicalizes casing and synonyms', () => {
|
||||
expect(normalizeStatus('applied')).toBe('Applied');
|
||||
expect(normalizeStatus(' OFFER ')).toBe('Offer');
|
||||
|
||||
@@ -102,7 +102,7 @@ export function normalizeStatus(status?: string | null): PipelineStatus | "Other
|
||||
const exact = PIPELINE_STATUSES.find((s) => s.toLowerCase() === trimmed.toLowerCase());
|
||||
if (exact) return exact;
|
||||
const alias = ALIASES[trimmed.toLowerCase()];
|
||||
return alias ?? "Other";
|
||||
return Object.hasOwn(ALIASES, trimmed.toLowerCase()) ? alias : "Other";
|
||||
}
|
||||
|
||||
/** MUI palette key for a status; both chip color and board accent derive from this. */
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import "@testing-library/jest-dom";
|
||||
import App from "./App";
|
||||
import { I18nProvider } from "./i18n/I18nProvider";
|
||||
import { api } from "./api";
|
||||
|
||||
jest.mock("./sessionBoundary", () => ({
|
||||
isSessionInvalidated: () => true,
|
||||
SESSION_INVALIDATED_EVENT: "session-invalidated",
|
||||
}));
|
||||
jest.mock("./i18n/I18nProvider", () => ({
|
||||
...jest.requireActual("./i18n/I18nProvider"),
|
||||
I18nProvider: jest.fn(() => { throw new Error("Invalidated sessions must not mount providers"); }),
|
||||
}));
|
||||
|
||||
test.each([
|
||||
["en", "Your session changed. Private data and unsaved edits were cleared from this page. Sign in again to continue."],
|
||||
["nb-NO", "Økten din ble endret. Private data og ulagrede endringer ble fjernet fra denne siden. Logg inn igjen for å fortsette."],
|
||||
])("invalidated session shows %s feedback outside the private provider tree", (language, message) => {
|
||||
const previousLanguage = document.documentElement.lang;
|
||||
document.documentElement.lang = language;
|
||||
try {
|
||||
render(<App />);
|
||||
expect(screen.getByRole("alert")).toHaveTextContent(message);
|
||||
expect(I18nProvider).not.toHaveBeenCalled();
|
||||
expect(api.get).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
document.documentElement.lang = previousLanguage;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { translations } from './i18n/translations';
|
||||
|
||||
const placeholders = (text: string) => [...new Set(text.match(/\{\w+\}/g) ?? [])].sort();
|
||||
|
||||
test('English and Bokmål expose the same UI message keys', () => {
|
||||
expect(Object.keys(translations.nb).sort()).toEqual(Object.keys(translations.en).sort());
|
||||
});
|
||||
|
||||
test('Bokmål preserves every English substitution variable', () => {
|
||||
const mismatches = Object.entries(translations.en).filter(([key, text]) =>
|
||||
JSON.stringify(placeholders(text)) !== JSON.stringify(placeholders(translations.nb[key as keyof typeof translations.en]))
|
||||
).map(([key]) => key);
|
||||
expect(mismatches).toEqual([]);
|
||||
});
|
||||
|
||||
test('supported UI catalogues contain no blank messages', () => {
|
||||
const blanks = Object.entries(translations).flatMap(([language, messages]) =>
|
||||
Object.entries(messages).filter(([, text]) => !text.trim()).map(([key]) => `${language}.${key}`)
|
||||
);
|
||||
expect(blanks).toEqual([]);
|
||||
});
|
||||
@@ -19,11 +19,11 @@ type Item = {
|
||||
type Listing = { items: Item[]; total: number; counts: Record<string, number>; workers: { name: string; flagEnabled: boolean; individualOperationsRecorded: boolean }[] };
|
||||
type Event = { id: string; atUtc: string; kind: string; status: string; stage: string | null; attempt: number; failureCode: string | null };
|
||||
type Events = { items: Event[]; hasMore: boolean; retentionDays: number };
|
||||
const statuses: Record<string, TranslationKey> = { queued: 'operationsStatusQueued', running: 'operationsStatusRunning', waiting_for_retry: 'operationsStatusWaitingForRetry', waiting_for_external_fallback: 'operationsStatusWaitingForFallback', succeeded: 'operationsStatusSucceeded', failed: 'operationsStatusFailed', cancelled: 'operationsStatusCancelled' };
|
||||
const tasks: Record<string, TranslationKey> = { 'cv.process': 'operationsTaskCv', 'strategy.snapshot': 'operationsTaskStrategy' };
|
||||
const stages: Record<string, TranslationKey> = { cv_validating_file: 'cvImportStageValidating', cv_extracting: 'cvImportStageExtracting', cv_identifying_sections: 'cvImportStageSections', cv_analysing: 'cvImportStageAnalysing', cv_validating_details: 'cvImportStageDetails', cv_preparing_review: 'cvImportStagePreparing', cv_review_ready: 'cvImportStageReady' };
|
||||
const kinds: Record<string, TranslationKey> = { Created: 'processingEventCreated', Claimed: 'processingEventClaimed', StageChanged: 'processingEventStageChanged', Completed: 'processingEventCompleted', RetryScheduled: 'processingEventRetryScheduled', Failed: 'processingEventFailed', CancellationRequested: 'processingEventCancellationRequested', Cancelled: 'processingEventCancelled', Retried: 'processingEventRetried', LeaseRecovered: 'processingEventLeaseRecovered', Expired: 'processingEventExpired' };
|
||||
const workers: Record<string, TranslationKey> = { AiOperations: 'processingWorkerAi', CvDocumentImports: 'processingWorkerDocument', JobEnrichment: 'processingWorkerEnrichment', Rules: 'processingWorkerRules', FollowUpReminders: 'processingWorkerReminders', DailyExport: 'processingWorkerExport', SavedJobSearchAlerts: 'processingWorkerSearch' };
|
||||
const statuses: Record<string, TranslationKey> = Object.assign(Object.create(null), { queued: 'operationsStatusQueued', running: 'operationsStatusRunning', waiting_for_retry: 'operationsStatusWaitingForRetry', waiting_for_external_fallback: 'operationsStatusWaitingForFallback', succeeded: 'operationsStatusSucceeded', failed: 'operationsStatusFailed', cancelled: 'operationsStatusCancelled' });
|
||||
const tasks: Record<string, TranslationKey> = Object.assign(Object.create(null), { 'cv.process': 'operationsTaskCv', 'strategy.snapshot': 'operationsTaskStrategy' });
|
||||
const stages: Record<string, TranslationKey> = Object.assign(Object.create(null), { cv_validating_file: 'cvImportStageValidating', cv_extracting: 'cvImportStageExtracting', cv_identifying_sections: 'cvImportStageSections', cv_analysing: 'cvImportStageAnalysing', cv_validating_details: 'cvImportStageDetails', cv_preparing_review: 'cvImportStagePreparing', cv_review_ready: 'cvImportStageReady' });
|
||||
const kinds: Record<string, TranslationKey> = Object.assign(Object.create(null), { Created: 'processingEventCreated', Claimed: 'processingEventClaimed', StageChanged: 'processingEventStageChanged', Completed: 'processingEventCompleted', RetryScheduled: 'processingEventRetryScheduled', Failed: 'processingEventFailed', CancellationRequested: 'processingEventCancellationRequested', Cancelled: 'processingEventCancelled', Retried: 'processingEventRetried', LeaseRecovered: 'processingEventLeaseRecovered', Expired: 'processingEventExpired' });
|
||||
const workers: Record<string, TranslationKey> = Object.assign(Object.create(null), { AiOperations: 'processingWorkerAi', CvDocumentImports: 'processingWorkerDocument', JobEnrichment: 'processingWorkerEnrichment', Rules: 'processingWorkerRules', FollowUpReminders: 'processingWorkerReminders', DailyExport: 'processingWorkerExport', SavedJobSearchAlerts: 'processingWorkerSearch' });
|
||||
const failureCodes = ['timeout', 'lease_expired', 'deadline_exceeded', 'entitlement_changed', 'document_unavailable', 'sidecar_unavailable', 'upstream_unavailable', 'invalid_response', 'circuit_open', 'rate_limited', 'permission_denied', 'cancelled', 'upload_rejected', 'parser_failed', 'malformed_output', 'provider_unavailable', 'processing_failed'];
|
||||
|
||||
export default function AdminProcessingPage() {
|
||||
|
||||
@@ -240,7 +240,7 @@ export default function DocumentsPage() {
|
||||
{item.type === "email-draft" ? <Typography variant="caption" color="text.secondary">{t("documentsEmailHelp")}</Typography> : null}
|
||||
{item.type === "attachment" ? <>
|
||||
<Typography variant="caption" color="text.secondary">{[item.fileType, item.fileSize != null ? t("documentsFileSize", { size: new Intl.NumberFormat(locale, { maximumFractionDigits: 1 }).format(item.fileSize / 1024) }) : null].filter(Boolean).join(" · ")}</Typography>
|
||||
{item.purpose ? <Typography variant="caption" color="text.secondary">{t("attachmentsPurpose")}: {t(({ resume: "attachmentsPurposeResume", "cover-letter": "attachmentsPurposeCoverLetter", portfolio: "attachmentsPurposePortfolio", "case-study": "attachmentsPurposeCaseStudy", certificate: "attachmentsPurposeCertificate" } as Record<string, TranslationKey>)[item.purpose.toLowerCase()] ?? "attachmentsPurposeOther")}</Typography> : null}
|
||||
{item.purpose ? <Typography variant="caption" color="text.secondary">{t("attachmentsPurpose")}: {t((Object.assign(Object.create(null), { resume: "attachmentsPurposeResume", "cover-letter": "attachmentsPurposeCoverLetter", portfolio: "attachmentsPurposePortfolio", "case-study": "attachmentsPurposeCaseStudy", certificate: "attachmentsPurposeCertificate" }) as Record<string, TranslationKey>)[item.purpose.toLowerCase()] ?? "attachmentsPurposeOther")}</Typography> : null}
|
||||
{item.purpose?.toLowerCase() === "certificate" ? <Typography variant="caption" color="text.secondary">{t("documentsCertificateHelp")}</Typography> : null}
|
||||
{item.useForAi != null ? <Typography variant="caption" color="text.secondary">{t("attachmentsAiUse")}: {t(item.useForAi ? "attachmentsAiEnabled" : "attachmentsAiDisabled")}</Typography> : null}
|
||||
</> : null}
|
||||
|
||||
@@ -11,6 +11,6 @@ const completenessLabelKeys: Record<string, TranslationKey> = {
|
||||
};
|
||||
|
||||
export function localizeCompletenessLabel(label: string, t: (key: TranslationKey) => string) {
|
||||
const key = completenessLabelKeys[label];
|
||||
const key = Object.hasOwn(completenessLabelKeys, label) ? completenessLabelKeys[label] : undefined;
|
||||
return key ? t(key) : label;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
"""Read a deployment connection string from stdin; emit five fields plus a sentinel.
|
||||
|
||||
This is a single-host dump contract, not a replacement for MySqlConnector. Reject
|
||||
ambiguous aliases/control characters instead of choosing a different backup target.
|
||||
Never include input values in errors or pass the connection string as an argument.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
ALIASES = {
|
||||
**dict.fromkeys(['server', 'host', 'data source', 'datasource', 'address', 'addr', 'network address'], 'host'),
|
||||
**dict.fromkeys(['user id', 'userid', 'username', 'uid', 'user name', 'user'], 'user'),
|
||||
**dict.fromkeys(['password', 'pwd'], 'password'),
|
||||
**dict.fromkeys(['database', 'initial catalog'], 'database'),
|
||||
'port': 'port',
|
||||
}
|
||||
PAIR = re.compile(r'''\s*([^=;]+?)\s*=\s*(?:"((?:""|[^"])*)"|'((?:''|[^'])*)'|([^;]*))\s*(?:;|$)''')
|
||||
|
||||
|
||||
def parse(connection):
|
||||
if any(ord(c) < 32 or ord(c) == 127 for c in connection):
|
||||
raise ValueError('Control characters are unsupported in backup connection settings.')
|
||||
fields = {}
|
||||
position = 0
|
||||
while position < len(connection):
|
||||
if connection[position] == ';' or connection[position].isspace():
|
||||
position += 1
|
||||
continue
|
||||
match = PAIR.match(connection, position)
|
||||
if not match:
|
||||
raise ValueError('Malformed backup connection settings.')
|
||||
key, double, single, plain = match.groups()
|
||||
if double is not None:
|
||||
value = double.replace('""', '"')
|
||||
elif single is not None:
|
||||
value = single.replace("''", "'")
|
||||
else:
|
||||
value = plain.strip()
|
||||
if value.startswith(('"', "'")):
|
||||
raise ValueError('Malformed quoted backup connection value.')
|
||||
canonical = ALIASES.get(key.strip().lower())
|
||||
if canonical:
|
||||
if canonical in fields:
|
||||
raise ValueError('Duplicate backup connection setting or alias.')
|
||||
fields[canonical] = value
|
||||
position = match.end()
|
||||
if any(not fields.get(key, '').strip() for key in ['host', 'database', 'user']):
|
||||
raise ValueError('Backup connection requires host, database and user.')
|
||||
port = fields.get('port', '3306')
|
||||
if not re.fullmatch(r'[0-9]+', port) or not 1 <= int(port) <= 65535:
|
||||
raise ValueError('Backup connection port must be between 1 and 65535.')
|
||||
if any(c in fields['host'] for c in ',/\\'):
|
||||
raise ValueError('Backup requires one TCP host, not a host list or socket path.')
|
||||
if any(c in fields['database'] for c in '/\\'):
|
||||
raise ValueError('Database name cannot contain backup path separators.')
|
||||
return [fields['host'], str(int(port)), fields['database'], fields['user'], fields.get('password', '')]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
values = parse(sys.stdin.buffer.read().decode('utf-8'))
|
||||
except (ValueError, UnicodeError):
|
||||
print('Invalid or unsupported backup connection settings.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
sys.stdout.reconfigure(encoding='utf-8', newline='\n')
|
||||
# Sentinel keeps an empty final password intact through shell command substitution.
|
||||
sys.stdout.write('\n'.join([*values, 'END']) + '\n')
|
||||
@@ -0,0 +1,89 @@
|
||||
"""Exercise the real backup function with a synthetic dump command; never deploy."""
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
BASH = 'C:/Program Files/Git/bin/bash.exe' if os.name == 'nt' else shutil.which('bash')
|
||||
|
||||
|
||||
class BackupConnectionTests(unittest.TestCase):
|
||||
def backup(self, connection):
|
||||
source = (ROOT / 'deploy/deploy.sh').read_text(encoding='utf-8')
|
||||
start = source.index('backup_database() {')
|
||||
function = source[start:source.index('\n}\n', start) + 3]
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
folder = Path(temporary)
|
||||
script = '\n'.join([
|
||||
'set -euo pipefail',
|
||||
'BACKUP_DIR=' + shlex.quote(folder.as_posix()),
|
||||
'BACKUP_STAMP=synthetic',
|
||||
'DATABASE_PROVIDER=mariadb',
|
||||
'JOBTRACKER_CONNECTION_STRING=' + shlex.quote(connection),
|
||||
'python3() { ' + shlex.quote(Path(sys.executable).as_posix()) + ' "$@"; }',
|
||||
'docker() { echo "Unexpected Docker call" >&2; return 99; }',
|
||||
'mariadb-dump() { printf "%s\\0" "$@" > "$BACKUP_DIR/args"; '
|
||||
'printf "%s" "$MYSQL_PWD" > "$BACKUP_DIR/password"; '
|
||||
'printf "CREATE TABLE synthetic (id int);\\n-- Dump completed\\n"; }',
|
||||
'verify_sql_backup() { gzip -t "$1"; }',
|
||||
function,
|
||||
'backup_database',
|
||||
])
|
||||
result = subprocess.run([BASH, '--noprofile', '--norc'], input=script,
|
||||
text=True, encoding='utf-8', capture_output=True, cwd=ROOT,
|
||||
env={**os.environ, 'DEPLOY_SKIP_DB_BACKUP': 'false'})
|
||||
arguments = (folder / 'args').read_bytes().split(b'\0')[:-1] if (folder / 'args').exists() else None
|
||||
password = (folder / 'password').read_text(encoding='utf-8') if (folder / 'password').exists() else None
|
||||
return result, arguments, password
|
||||
|
||||
def test_supported_host_alias_and_case_reach_dump_unchanged(self):
|
||||
for host in ['Server', 'SERVER', 'Host', 'Data Source', 'Address', 'Addr', 'Network Address']:
|
||||
with self.subTest(host=host):
|
||||
result, args, password = self.backup(f'{host}=127.0.0.1;DATABASE=synthetic;UID=reader;PWD=dummy-secret')
|
||||
self.assertEqual(0, result.returncode, result.stderr + result.stdout)
|
||||
self.assertEqual([b'--host=127.0.0.1', b'--port=3306', b'--user=reader',
|
||||
b'--single-transaction', b'--routines', b'--events', b'--quick', b'synthetic'], args)
|
||||
self.assertEqual('dummy-secret', password)
|
||||
self.assertNotIn('dummy-secret', result.stdout + result.stderr)
|
||||
|
||||
def test_quoted_separators_and_shell_characters_remain_data(self):
|
||||
result, args, password = self.backup('Server=127.0.0.1;Port=3307;Database=synthetic;User ID=reader;Password="semi;eq=quote""$(false)"')
|
||||
self.assertEqual(0, result.returncode, result.stderr + result.stdout)
|
||||
self.assertIn(b'--port=3307', args)
|
||||
self.assertEqual('semi;eq=quote"$(false)', password)
|
||||
|
||||
def test_empty_password_is_preserved(self):
|
||||
result, args, password = self.backup('Server=127.0.0.1;Database=synthetic;User ID=reader;Password=')
|
||||
self.assertEqual(0, result.returncode, result.stderr + result.stdout)
|
||||
self.assertIsNotNone(args)
|
||||
self.assertEqual('', password)
|
||||
|
||||
def test_single_quoted_unicode_password_and_database_alias_are_preserved(self):
|
||||
result, args, password = self.backup("Host=127.0.0.1;Initial Catalog=synthetic;Username=reader;Password='søknad你好;it''s';SslMode=VerifyFull")
|
||||
self.assertEqual(0, result.returncode, result.stderr + result.stdout)
|
||||
self.assertEqual(b'synthetic', args[-1])
|
||||
self.assertEqual("søknad你好;it's", password)
|
||||
|
||||
def test_invalid_or_ambiguous_input_never_invokes_dump_or_exposes_input(self):
|
||||
for connection in [
|
||||
'Host=127.0.0.1;Database=synthetic;Uid=reader;Port=0;Pwd=dummy-secret',
|
||||
'Host=127.0.0.1;Database=synthetic;Uid=reader;Port=65536;Pwd=dummy-secret',
|
||||
'Host=127.0.0.1;Server=other;Database=synthetic;Uid=reader;Pwd=dummy-secret',
|
||||
'Host=127.0.0.1;Database=synthetic;Uid=reader;Pwd="dummy-secret',
|
||||
'Host=127.0.0.1;Database=synthetic;Uid=reader;Pwd=dummy-secret\nline',
|
||||
'Host=127.0.0.1;Uid=reader;Pwd=dummy-secret',
|
||||
]:
|
||||
with self.subTest(connection=connection.replace('dummy-secret', 'REDACTED')):
|
||||
result, args, _ = self.backup(connection)
|
||||
self.assertNotEqual(0, result.returncode)
|
||||
self.assertIsNone(args)
|
||||
self.assertNotIn('dummy-secret', result.stdout + result.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user