feat(ai): enforce local-first routing
Keep external providers behind server consent, task, and prompt-cost gates while persisting actual provider provenance.
This commit is contained in:
@@ -65,16 +65,14 @@ and all-time totals; the workspace displays the monthly calls and estimated toke
|
||||
|
||||
## Provider abstraction
|
||||
|
||||
Generation goes through the existing `ISummarizerService` → ai-service, which routes to the active
|
||||
provider (`AI_PROVIDER`: ollama | gemini | groq) — production can offload a weak local GPU to a cloud
|
||||
provider. Each `AiInteraction` records the resolved provider for transparency, and `GET …/ai/modules`
|
||||
returns the current provider so the UI can show it.
|
||||
Generation goes through `ISummarizerService` to the ai-service. Ollama is primary; `AI_PROVIDER` names
|
||||
only the optional external fallback candidate. Fallback is sequential and requires administrator
|
||||
enablement, task approval, live Pro/user consent and the prompt cost/privacy ceiling. Each
|
||||
`AiInteraction` records the provider returned by the sidecar, plus bounded model/route metadata in
|
||||
`ResultJson.meta`; configuration alone is not treated as proof that a provider executed.
|
||||
|
||||
**Per-request user-selectable providers** (module 8's "users can choose provider") is a plumbing
|
||||
extension, not yet wired end-to-end: it needs (a) ai-service to accept a per-request `provider`
|
||||
override and (b) an API **key configured for each selectable provider**. Both are deployment/credential
|
||||
concerns (a live paid key per provider), so the code path is left as a documented extension point
|
||||
rather than shipped half-configured. The abstraction already isolates the change to one method.
|
||||
Per-request user-selectable providers remain intentionally unsupported. The server-side privacy
|
||||
policy selects a route, not the browser, and provider credentials remain deployment-only.
|
||||
|
||||
## Extension points
|
||||
|
||||
@@ -83,8 +81,8 @@ rather than shipped half-configured. The abstraction already isolates the change
|
||||
- **New cover-letter tone**: add to `CoverLetterModes` + `ModeGuidance`.
|
||||
- **Structured (JSON) results**: swap a module's prompt for JSON and parse into `ResultJson.meta`; the
|
||||
UI already renders `result.text` as markdown and can read `meta`.
|
||||
- **User-selectable provider**: thread a `provider` param through `ISummarizerService` →
|
||||
ai-service; gate on the provider having a configured key (see above).
|
||||
- **Provider policy**: add task types to the explicit server-side allowlist only after their payload,
|
||||
accounting and production checks pass; do not add browser provider overrides.
|
||||
|
||||
## Security
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# AI privacy and external-processing policy
|
||||
|
||||
Updated: 2026-08-03
|
||||
Updated: 2026-08-09
|
||||
|
||||
The default execution mode is local-only. External processing of `/cv/*` payloads requires all of:
|
||||
|
||||
@@ -11,9 +11,10 @@ The default execution mode is local-only. External processing of `/cv/*` payload
|
||||
5. AI enabled in the user's server-side settings; and
|
||||
6. the user's explicit `ExternalAiProcessingAllowed` opt-in.
|
||||
|
||||
The backend adds `X-Ai-External-Allowed: true` only after that live policy check. The sidecar otherwise routes `/cv/*` to Ollama even when an external provider is configured. `/summarize` always uses the local summarization model. Provider keys remain server-side and are never returned by the settings API.
|
||||
The backend adds `X-Ai-External-Allowed: true` only after that live policy check. Durable workers use the same header only from their admitted policy snapshot after a live execution-time recheck, and also send the bounded task type. The sidecar otherwise routes `/cv/*` to Ollama even when an external provider is configured. `/summarize` always uses the local summarization model. Provider keys remain server-side and are never returned by the settings API.
|
||||
|
||||
`GET/PUT /api/ai/settings` owns the user settings. Disabling AI takes effect on the next protected request and is also rechecked by the current enrichment and queued-CV workers. Existing users migrate with AI enabled to preserve current behaviour; external consent always defaults to false.
|
||||
|
||||
This is the privacy admission foundation, not the final routing system. AI-001/AI-002 must carry an immutable policy snapshot into durable operations, recheck it at execution, record the actual provider/reason, add bounded local-first fallback triggers and minimize each external payload. Background CV work currently fails safe to local because it has no HTTP user context.
|
||||
AI-002 makes provider execution local-first and sequential. External fallback additionally requires an allowed task and stays below the configured per-request prompt ceiling. Actual provider/model/route metadata is returned by the sidecar and persisted by AI Workspace or durable operations. The process-local circuit and health diagnostics expose no prompt or credential data.
|
||||
|
||||
This is not permission to enable external processing globally. New durable task types remain local until explicitly allowlisted; AI-003/004 must minimize their exact payloads and complete cross-feature monthly accounting before rollout. `EXTERNAL_AI_ENABLED=false` or `Ai:RoutingMode=local_only` is the immediate rollback switch.
|
||||
|
||||
@@ -22,4 +22,6 @@ Terminal notifications are described in `notifications.md`. Authenticated owner
|
||||
|
||||
`AiOperationAdmission` now provides the shared AI producer boundary: it rechecks live Pro/AI settings, snapshots `local_only` or `external_allowed`, applies per-user/global capacity, assigns a deadline and returns the stable `/api/operations/{id}` status URL. It stores only subject type/ID, never raw CV/email/prompt text. The current process-local admission semaphore is correct for the documented single-backend deployment; multi-replica rollout requires a database capacity reservation.
|
||||
|
||||
`AiOperationWorker` claims only registered task types by priority, enters the explicit owner scope, rechecks entitlement/privacy/cancellation, runs one inference by default, heartbeats the lease, enforces a timeout, classifies bounded retry/permanent failure and commits the existing terminal notification. `Workers:AiOperationsEnabled` defaults false and no production feature handler is registered yet. AI-003/004 add the Strategy/CV handlers and 202 producer endpoints; AI-002 adds provider/model concurrency, circuit and actual-provider provenance.
|
||||
`AiOperationWorker` claims only registered task types by priority, enters the explicit owner scope, rechecks entitlement/privacy/cancellation, runs one inference by default, heartbeats the lease, enforces a timeout, classifies bounded retry/permanent failure and commits the existing terminal notification. Its execution scope carries the rechecked privacy/task decision to the shared sidecar client. Successful and failed provider attempts persist bounded provider/model/route provenance in existing operation fields.
|
||||
|
||||
`Workers:AiOperationsEnabled` defaults false and no production feature handler is registered yet. AI-002 supplies sequential local-first routing and a process-local single-model circuit; AI-003/004 add the Strategy/CV handlers and 202 producer endpoints. The current one-worker default is the local-model concurrency limit until PROD-003 benchmarks justify anything else.
|
||||
|
||||
@@ -463,3 +463,14 @@ Status: Implemented; real-handler/browser/production verification incomplete (20
|
||||
- **Findings:** no new schema/raw private queue payload; multi-replica capacity needs a future database reservation; actual 202 producers and provider controls remain AI-003/004 and AI-002.
|
||||
- **Blockers:** browser, MariaDB and production unavailable; worker intentionally off.
|
||||
- **Next phase:** AI-002 Ollama adapter and central local-first routing.
|
||||
|
||||
## Post-audit programme execution — AI-002
|
||||
|
||||
Status: Implemented; browser/model/provider/production verification incomplete (2026-08-09).
|
||||
|
||||
- **Work completed:** revalidated every AI endpoint/caller; replaced direct configured-provider dispatch with one sequential local-first router; added task/consent/config/prompt-cost gates, bounded circuit/health state, typed sanitized failures and actual provider/model/route persistence for AI history and durable operations.
|
||||
- **Commands/evidence:** verification-log V-098–V-100; `docs/verification/ai-002-provider-routing.md`; `docs/audits/evidence/ai-002/README.md`.
|
||||
- **Findings:** no schema/dependency change and no provider race. New durable tasks fail safe to local until allowlisted. Per-request prompt ceiling exists, but complete monthly cross-feature accounting remains incomplete. Circuit state is process-local for the current single-sidecar design.
|
||||
- **Checks that remain:** real AI-003/004 handlers/producers; selected-model benchmark; browser disclosure; MariaDB; controlled synthetic external fallback; production health/restart/canary/rollback.
|
||||
- **Blockers and limitations:** no browser, production access, model benchmark or provider authority/configuration; no real/private input used.
|
||||
- **Next phase:** AI-003 Strategy Snapshot durable-operation migration.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# AI-002 evidence
|
||||
|
||||
- `../../../verification/ai-002-provider-routing.md` — route matrix, implementation and remaining gates.
|
||||
- `../../verification-log.md` entries V-098 through V-100 — exact commands and results.
|
||||
- `../../../../tools/summarizer/tests/test_app.py` — fake-transport local-first, consent, circuit, schema, cost-cap and failure tests.
|
||||
- `../../../../JobTrackerApi.Tests/AiOperationQueueTests.cs`, `AiPrivacyPolicyTests.cs`, `SummarizerServiceTests.cs` and `AiWorkspaceTests.cs` — policy propagation and provenance tests.
|
||||
|
||||
No screenshot was captured because localhost browser access remains denied by administrator policy. No model/provider call, paid service, real private data or production system was used.
|
||||
@@ -103,6 +103,9 @@ Only non-destructive commands are run. Commands that restore dependencies may po
|
||||
| V-095 | Full backend `dotnet test`; full frontend `npm test -- --runInBand`; `npm run build`; `docker compose config --quiet`; `git diff --check` | Repository root / `job-tracker-ui` | POL-002 regression, production build, deployment syntax and patch hygiene | PASS — backend 576/576; frontend 47 suites/158 tests; build/config/diff pass | Expected unset optional Compose variables and line-ending notices only. Temporary API launch command was rejected before execution by policy; no service started | Runtime/browser/production verification blocked |
|
||||
| V-096 | Source re-read plus `dotnet test ... --filter "...AiOperationQueueTests|...UserOperationStoreTests|...OperationsControllerTests"` | Repository root | AI-001 admission, capacity, priority, claim, owner execution, policy recheck and existing state/API regression | PASS — 17/17 final | Initial 15/15 and 16/16 passes preceded priority and retry/downgrade additions | N/A |
|
||||
| V-097 | Full `dotnet test`; `docker compose config --quiet`; `git diff --check` | Repository root | AI-001 full backend regression and deployment/patch syntax | PASS — backend 581/581; config/diff pass | Expected unset optional Compose variables and line-ending notices only; worker remains default-off with no real task handler | Browser/provider/production verification pending later feature packages |
|
||||
| V-098 | Bounded `rg`/`Get-Content` of every `/summarize`, `/extract-text`, `/cv/*`, `ISummarizerService`, privacy-header, operation-worker, provider-config, Compose and test path | Repository root | Revalidate AI-002 route convergence and existing mitigations before design | PASS — deterministic and summarize/extract paths remain local; all generative CV paths converge in the sidecar; configured external provider was direct rather than fallback; actual provider/failure provenance was lost | Browser/provider behavior was not inferred from source; route matrix records code-inspected status | Confirmed implementation gap |
|
||||
| V-099 | `dotnet test JobTrackerApi.Tests\JobTrackerApi.Tests.csproj --no-restore --filter "FullyQualifiedName~AiOperationQueueTests\|FullyQualifiedName~AiPrivacyPolicyTests\|FullyQualifiedName~SummarizerServiceTests\|FullyQualifiedName~AiWorkspaceTests"`; `python -m pytest -q` | Repository root / `tools/summarizer` | Verify local-first order, policy/task propagation, schema/circuit/cost/failure routing and actual provenance | PASS — backend 26/26; sidecar 22/22 | First sidecar run passed 15/18 and correctly failed three obsolete external-first expectations; tests were updated to the new approved policy, then extended. No network/provider call occurred | Expected test-contract transition resolved |
|
||||
| V-100 | Full `dotnet test JobTrackerApi.Tests\JobTrackerApi.Tests.csproj --no-restore`; `python -m pytest -q`; synthetic-token `docker compose config --quiet`; `git diff --check` | Repository root / `tools/summarizer` | AI-002 wider regression, deployment syntax and patch hygiene | PASS — backend 588/588; sidecar 22/22; Compose/diff pass | Five existing SWIG deprecation warnings; Docker config-file access and unset optional-variable warnings; line-ending notices only. Worker/external gate remain off | Browser, MariaDB, selected-model, real-provider and production checks remain blocked |
|
||||
|
||||
## Secret-scan commands
|
||||
|
||||
|
||||
@@ -33,3 +33,5 @@ PROD-002 provides a code-derived P0–P3 workload/privacy inventory and 19-case
|
||||
POL-002 now persists user AI/privacy preferences and requires independent backend/sidecar administrator gates, live Pro entitlement, AI enabled and explicit consent before `/cv/*` can use a configured external provider. The default remains local and mocked routing checks pass. No external/paid provider or production egress was exercised; durable policy snapshots, actual-provider/reason recording, payload minimization, cost controls and local-first fallback triggers remain AI-001/002 rollout gates. See `docs/verification/pol-002-ai-privacy.md`.
|
||||
|
||||
AI-001 adds the reusable bounded database-backed admission/worker layer over OPS-001A/B/C. It defaults to one worker and remains switched off; no real handler, model or external provider was invoked. Production activation remains blocked until AI-002 provider controls, AI-003/004 typed handlers, browser verification, MariaDB execution, monitoring and rollback/canary evidence pass. See `docs/verification/ai-001-durable-ai-queue.md`.
|
||||
|
||||
AI-002 now enforces sequential local-first routing, explicit task/consent/config/prompt-cost gates, a bounded process-local circuit and actual provider/model/route provenance. Backend 588/588 and sidecar fake-transport 22/22 pass. This is repository evidence only: no model/provider call or production egress occurred, the worker remains off, and PROD-001/003 plus AI-003/004 remain mandatory before any rollout. See `docs/verification/ai-002-provider-routing.md`.
|
||||
|
||||
@@ -24,8 +24,7 @@ Status: `IMPLEMENTED — NOT VERIFIED`.
|
||||
## Remaining gates
|
||||
|
||||
- AI-003/004 must register real Strategy/CV handlers and return actual 202 responses; no generic create API was exposed because it would bypass task ownership/policy.
|
||||
- AI-002 must add provider/model concurrency, circuit health, provider/reason/model recording, payload minimization and external fallback decisions.
|
||||
- AI-002 added sequential local-first routing, single-model circuit health, provider/reason/model recording and task/prompt fallback gates. AI-003/004 still own producer-specific payload minimization and accounting.
|
||||
- Browser refresh/double-click/cancel/retry must be repeated against each real producer.
|
||||
- Worker remains off; MariaDB and production canary/restart/queue telemetry are unavailable.
|
||||
- The process-local capacity gate assumes one backend replica. Add a database reservation only before multi-replica rollout.
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# AI-002 verification — local-first provider routing
|
||||
|
||||
Updated: 2026-08-09
|
||||
|
||||
Status: `IMPLEMENTED — NOT VERIFIED`.
|
||||
|
||||
## Confirmed route matrix
|
||||
|
||||
| Sidecar path | Reachable application callers | Workload/privacy | Provider policy |
|
||||
|---|---|---|---|
|
||||
| `/summarize` | job create/detail/refresh, job-enrichment worker, health probe | `JOB-SUMMARY` / `HEALTH-PROBE`; P0–P2 depending on source | local DistilBART only; never external |
|
||||
| `/extract-text` | profile-CV upload and selected application attachments | `DOC-EXTRACT`; P2 | local parser/OCR only; never external |
|
||||
| `/cv/normalize` | profile-CV reconstruction/normalization | `CV-NORMALIZE`; P2 | primary Ollama; permitted external fallback only after all gates |
|
||||
| `/cv/classify-block` | ambiguous profile-CV block classification | `CV-CLASSIFY`; P2 | primary Ollama; permitted external fallback only after all gates |
|
||||
| `/cv/rewrite` | profile/CV rewrite, CV Builder assistance, candidate fit/focus/strategy/application drafting, follow-up drafting, selected attachment context and AI Workspace modules | `PROFILE-EXTRACT`, `STRATEGY`, `CV-TAILOR`, `APPLICATION-DRAFT`, `FOLLOWUP-DRAFT`, `INTERVIEW`, `WRITING`; P2 | primary Ollama; permitted external fallback only after all gates |
|
||||
|
||||
Deterministic match, profile diff, keyword, email-classification and application-intelligence paths do not enter the provider router. Existing synchronous `/cv/*` calls use the endpoint task identifier. Durable handlers receive their typed operation task through `AiOperationExecutionScope`; a new operation task remains local until it is explicitly added to `EXTERNAL_AI_ALLOWED_TASKS`.
|
||||
|
||||
## Implemented policy
|
||||
|
||||
`tools/summarizer/app.py` is the one generation router. For each generative CV request it:
|
||||
|
||||
1. validates routing mode, task allowlist, administrator enablement, backend permission, external provider configuration and a per-request external prompt ceiling;
|
||||
2. uses Ollama first in the default `local_first` mode;
|
||||
3. validates non-empty text or structured JSON before accepting the local result;
|
||||
4. records consecutive local failures in a bounded process-local circuit;
|
||||
5. calls one external provider only after an eligible local failure/circuit-open decision and only when every gate still passes;
|
||||
6. never races local and external calls; and
|
||||
7. returns sanitized provider/model/route/fallback headers for persistence and diagnostics.
|
||||
|
||||
`local_only`, `local_first` and `external_only` are supported. Invalid modes fail closed to `local_only`. `external_only` still requires explicit backend permission and an allowed task. The default remains `local_first`, while `EXTERNAL_AI_ENABLED=false` makes it effectively local-only.
|
||||
|
||||
The external prompt ceiling is a per-request cost/privacy control, not a monthly spend ledger. Current plan-level monthly accounting covers AI Workspace interactions only; complete cross-feature accounting remains a POL-001/AI-003/AI-004 rollout gate.
|
||||
|
||||
## Backend integration
|
||||
|
||||
- `AiPrivacyHeaderHandler` uses live request policy for synchronous calls and the worker's rechecked immutable policy/task for durable calls.
|
||||
- `SummarizerService.GenerateSectionWithMetadataAsync` preserves cancellation and returns actual provider/model/fallback metadata.
|
||||
- Provider failures use a typed, sanitized `AiGenerationException`; legacy string callers retain their previous `null` behavior.
|
||||
- `AiWorkspaceService` stores the actual provider and bounded model/route metadata instead of treating deployment configuration as execution evidence.
|
||||
- `AiOperationWorker` persists provider, model and route stage on success and retryable/permanent provider failure. Existing operation APIs continue to hide provider internals while exposing the bounded progress stage.
|
||||
- No schema migration or dependency change was needed; existing nullable `UserOperations.Provider`, `Model` and `ProgressStage` columns are reused.
|
||||
|
||||
## Automated evidence
|
||||
|
||||
- Focused backend provider/privacy/queue/history tests: 26/26.
|
||||
- Full backend: 588/588.
|
||||
- Sidecar: 22/22 with fake transports only.
|
||||
- Compose configuration and `git diff --check`: pass; expected missing optional-environment and line-ending warnings only.
|
||||
- Tests cover local success, sequential fallback, missing consent/key, invalid JSON, prompt ceiling, open circuit, external outage, unapproved durable task, external-only permission, actual metadata, sanitized failures and operation persistence.
|
||||
|
||||
## Remaining gates
|
||||
|
||||
- No Ollama model, external provider, paid API, real CV/email, production service or production egress was used.
|
||||
- PROD-001/003 must identify hardware and benchmark/select the primary and optional secondary local model. No secondary local model is configured yet.
|
||||
- AI-003/004 must register real Strategy/CV handlers, choose explicit task allowlists, pass cancellation through their work and verify retry/deduplication with durable results.
|
||||
- The local circuit is intentionally process-local for the current single-sidecar deployment. Multi-replica or restart-persistent circuit coordination requires measured need and a separate design.
|
||||
- The existing named HTTP client still has a 30-second transport timeout for synchronous callers. AI-003/004 must move long work to durable handlers and align their cancellation/transport budget; increasing the synchronous timeout is not accepted as the timeout fix.
|
||||
- Browser disclosure, MariaDB execution, controlled synthetic provider fallback, production health/circuit telemetry and rollback/canary checks remain unverified.
|
||||
@@ -34,5 +34,9 @@ This is the rolling action-level evidence index. `PASS (automated/runtime)` is n
|
||||
| Durable AI | Pro admission, idempotent status URL and bounded capacity | PASS (real SQLite + synthetic subject IDs) | BLOCKED until real producer | NOT RUN; worker off | `ai-001-durable-ai-queue.md` |
|
||||
| Durable AI | priority/task-filtered atomic claim and owner-scoped success | PASS (fake handler, real operation/notification state) | N/A | NOT RUN | `ai-001-durable-ai-queue.md` |
|
||||
| Durable AI | retryable failure, downgrade recheck, lease/cancel/restart recovery | PASS (automated) | BLOCKED until real producer | NOT RUN | `ai-001-durable-ai-queue.md` |
|
||||
| AI routing | local primary success and no parallel external call | PASS (fake transports) | N/A | NOT RUN | `ai-002-provider-routing.md` |
|
||||
| AI routing | consent/admin/task/config/prompt-cap fallback denial | PASS (backend + sidecar policy tests) | BLOCKED | NOT RUN | `ai-002-provider-routing.md` |
|
||||
| AI routing | schema/local-outage/circuit fallback and external failure | PASS (fake transports) | BLOCKED | NOT RUN | `ai-002-provider-routing.md` |
|
||||
| AI routing | actual provider/model/route persistence on success/failure | PASS (real SQLite operation/history state; fake provider) | BLOCKED until real producer | NOT RUN | `ai-002-provider-routing.md` |
|
||||
|
||||
Remaining product actions are `NOT STARTED` in the master plan and will be added as their work packages enter verification. Browser localhost is currently denied by administrator policy; production access is not documented/configured.
|
||||
|
||||
@@ -33,5 +33,4 @@ Status: `IMPLEMENTED — NOT VERIFIED`.
|
||||
- No external provider, paid service, production environment or real private data was used.
|
||||
- MariaDB migration execution remains unavailable.
|
||||
- Direct clean `dotnet ef database update` fails in the pre-existing historical SQLite migration chain before this migration (`AddJobEntityAndProspectStages` expects a reconciler-added column). The application startup reconciler path was not exercised because the local process-launch command was blocked by execution policy.
|
||||
- Background operations do not yet carry a policy snapshot; they fail safe to local. AI-001/AI-002 own durable admission/recheck, actual-provider/reason recording, cost controls, payload minimization and bounded local-first fallback triggers.
|
||||
|
||||
- AI-001/002 now carry admitted/rechecked policy/task context into durable calls, enforce bounded local-first fallback and record actual provider/model/route metadata. AI-003/004 still own task-specific payload minimization, complete cross-feature monthly accounting and real producer verification.
|
||||
|
||||
@@ -279,3 +279,13 @@
|
||||
- **Consequences:** AI-003/004 only add typed handlers/producers. Current capacity serialization is process-local for the documented single-backend deployment; database reservation is required before multiple backend replicas. Provider/model semaphores and circuit/provenance remain AI-002 responsibilities.
|
||||
- **User approval required:** No; this follows both programmes' explicit instruction to reuse the smallest reliable existing infrastructure.
|
||||
- **Reversible:** Yes. Keep the worker switch false, remove admission/worker registrations, and retain operation rows/API history. No new schema was added in this slice.
|
||||
|
||||
## DEC-029 — One sequential sidecar router owns local-first fallback
|
||||
|
||||
- **Date:** 2026-08-09
|
||||
- **Decision:** Keep provider execution behind the existing sidecar boundary, make Ollama the default primary, permit at most one sequential external fallback, and carry the backend's rechecked privacy/task decision through the AI-001 execution scope. Reuse existing operation provider/model/progress fields for provenance; add no queue/provider schema or dependency.
|
||||
- **Reason/evidence:** every generative `/cv/*` path already converges on one `_provider_generate` family, while deterministic tasks and `/summarize` must remain local. Sidecar fake-transport tests prove local success, consent/config/task/cost denials, schema fallback, circuit behavior, external failure and no parallel duplicate call. Backend tests prove policy propagation and success/failure provenance.
|
||||
- **Alternatives considered:** provider selection in each controller; browser-selected providers; a second provider abstraction in .NET; simultaneous local/cloud racing; a new circuit/attempt table; increasing synchronous timeouts. These scatter policy, expose authority, duplicate the established boundary, risk double charge/output, add unneeded schema, or mask the queued-operation root cause.
|
||||
- **Consequences:** `AI_ROUTING_MODE` supports `local_only`, `local_first` and explicitly gated `external_only`; invalid values fail closed. New durable task IDs stay local until allowlisted. The current circuit is process-local and one AI worker is the effective single-model concurrency limit. Per-request prompt size limits external spend/exposure, but complete monthly cross-feature accounting and model selection remain rollout gates.
|
||||
- **User approval required:** No; this directly implements the approved local-first programme without invoking a provider or production service.
|
||||
- **Reversible:** Yes. Set `EXTERNAL_AI_ENABLED=false` or `AI_ROUTING_MODE=local_only`; the older `AI_PROVIDER`/model configuration is retained. Existing nullable operation fields and AI history remain readable.
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
# JobTracker master programme progress
|
||||
|
||||
Updated: 2026-08-03
|
||||
Updated: 2026-08-09
|
||||
|
||||
- **Overall programme status:** Active. Six packages are locally verified; eleven packages including POL-001/002 and AI-001 are implemented with automated/runtime evidence but blocked from later browser/provider/production gates; AI-002 is now in progress.
|
||||
- **Current work package:** `AI-002` — Ollama adapter and local-first provider routing (`IN PROGRESS`), extending the POL-002 boundary and AI-001 execution context.
|
||||
- **Overall programme status:** Active. Six packages are locally verified; twelve packages through AI-002 are implemented with automated/runtime evidence but blocked from later browser/provider/production gates; AI-003 is now in progress.
|
||||
- **Current work package:** `AI-003` — Strategy Snapshot durable-operation migration (`IN PROGRESS`), reusing AI-001 operations and AI-002 routing rather than creating a Strategy-specific queue.
|
||||
- **Completed work packages:** None are `DONE`; all repository security packages still have applicable browser, provider and/or production gates.
|
||||
- **Locally verified work:** SEC-001, SEC-002, SEC-003, SEC-005A, CORE-001 and PROD-002 (`VERIFIED LOCALLY`).
|
||||
- **Implemented, verification incomplete:** SEC-004, SEC-005B, SEC-008, CORE-002, BG-001, OPS-001A/B/C, POL-001/002 and AI-001 (`IMPLEMENTED — NOT VERIFIED`): foundations through durable bounded AI execution pass local checks; real handlers, browser, provider, accounting and production gates remain.
|
||||
- **Implemented, verification incomplete:** SEC-004, SEC-005B, SEC-008, CORE-002, BG-001, OPS-001A/B/C, POL-001/002, AI-001 and AI-002 (`IMPLEMENTED — NOT VERIFIED`): foundations through bounded local-first routing pass local checks; real producers, browser, selected-model/provider, accounting and production gates remain.
|
||||
- **Production-verified work:** None.
|
||||
- **Blocked work:** SEC-006 requires explicit internet/package-index permission; PROD-001/003/004 and REL-001 require documented production access and unfinished dependencies. Browser access was denied by the browser admin policy check; SMTP/MariaDB environments are unavailable.
|
||||
- **Deferred work:** None. Conditional large abstractions, model deletion, multi-replica coordination and unrelated production changes remain outside current packages.
|
||||
- **Next five work packages:** AI-002 Ollama adapter/routing; AI-003 Strategy Snapshot queue migration; AI-004 CV processing queue migration; UX-001 authentication/theme corrections; QA-001 job-analysis/keyword quality. SEC-006/007 resume after package-index permission.
|
||||
- **Status counts:** 6 `VERIFIED LOCALLY`; 11 `IMPLEMENTED — NOT VERIFIED`; 1 `IN PROGRESS`; 16 `NOT STARTED`; 5 `BLOCKED`; 0 `DONE`; 0 `DEFERRED`.
|
||||
- **Test status:** backend 581/581; AI-001 focused queue/state/API 17/17; frontend 47/47 suites and 158/158 tests plus build; Python sidecar 18/18. Prior POL/PROD/OPS/BG/SEC/CORE evidence remains green.
|
||||
- **Next five work packages:** AI-003 Strategy Snapshot queue migration; AI-004 CV processing queue migration; UX-001 authentication/theme corrections; QA-001 job-analysis/keyword quality; UX-002 Career Workspace/CV Builder redesign. SEC-006/007 resume after package-index permission.
|
||||
- **Status counts:** 6 `VERIFIED LOCALLY`; 12 `IMPLEMENTED — NOT VERIFIED`; 1 `IN PROGRESS`; 15 `NOT STARTED`; 5 `BLOCKED`; 0 `DONE`; 0 `DEFERRED`.
|
||||
- **Test status:** backend 588/588; AI-002 focused provider/privacy/queue/history 26/26; frontend baseline 47/47 suites and 158/158 tests plus build; Python sidecar 22/22. Compose and patch checks pass. Prior POL/PROD/OPS/BG/SEC/CORE evidence remains green.
|
||||
- **Deployment status:** No deployment performed. No production migrations were run.
|
||||
- **Production status:** Unchanged and unverified. Pre-existing Docker development services on 3000/5202 were observed and left untouched.
|
||||
- **Known regressions:** No known regression from the implemented packages. POL-002 preserves local AI behaviour and defaults external consent off. Direct clean EF-only SQLite migration still hits the pre-existing historical blank-chain defect before the new migration; normal startup owns reconciliation. Complete usage/provider provenance remains a known pre-rollout gap.
|
||||
- **Known regressions:** No known regression from the implemented packages. AI-002 preserves local behavior, defaults external consent off, performs no parallel provider race and records actual provenance. Direct clean EF-only SQLite migration still hits the pre-existing historical blank-chain defect before the new migration; normal startup owns reconciliation. Complete cross-feature monthly usage accounting remains a pre-rollout gap.
|
||||
- **Outstanding security findings:** JT-001 repository ownership is implemented but remains High deployment risk until migration/inventory/provider checks; production portion of JT-002; JT-006, JT-009 and associated JT-011/JT-012/JT-022 prerequisites. JT-005 owner foundation is implemented but workers remain off until persistent notification, entitlement and privacy gates. JT-007/JT-008/JT-010 repository behavior is not fully browser/provider/production verified.
|
||||
|
||||
## Current evidence
|
||||
@@ -38,5 +38,6 @@ Updated: 2026-08-03
|
||||
- `docs/verification/pol-001-free-pro-entitlements.md`
|
||||
- `docs/verification/pol-002-ai-privacy.md`
|
||||
- `docs/verification/ai-001-durable-ai-queue.md`
|
||||
- `docs/verification/ai-002-provider-routing.md`
|
||||
- `docs/verification/prod-002-ai-evaluation.md`
|
||||
- `docs/work-programmes/master-work-plan.md`
|
||||
|
||||
@@ -16,7 +16,7 @@ Allowed statuses are `NOT STARTED`, `IN PROGRESS`, `IMPLEMENTED — NOT VERIFIED
|
||||
|
||||
`DONE` requires every applicable acceptance criterion, focused and regression tests, browser/accessibility/theme/mobile checks, tenant and entitlement checks, documentation, migration/rollback evidence, and production verification. Repository-only work that still requires production is at most `VERIFIED LOCALLY`.
|
||||
|
||||
Exactly one implementation item may be `IN PROGRESS`. As of this revision it is **AI-002**.
|
||||
Exactly one implementation item may be `IN PROGRESS`. As of this revision it is **AI-003**.
|
||||
|
||||
## Consolidated dependency order
|
||||
|
||||
@@ -383,7 +383,7 @@ Ordering differences from the suggested list:
|
||||
- **Blocker:** browser localhost is denied; Stripe/MariaDB/production are unavailable. Usage accounting is complete only for AI Workspace, so provider rollout remains blocked until durable execution centralizes it.
|
||||
- **Evidence:** `docs/verification/pol-001-free-pro-entitlements.md`; focused backend 74/74; full backend 568/568; focused frontend 22/22; full frontend 47 suites/157 tests; production build.
|
||||
- **Commit:** none.
|
||||
- **Remaining work:** browser locked/Pro state checks; mocked Stripe expiry/downgrade lifecycle; central all-task usage accounting through AI-001/002; production role/config smoke. PRODUCT-001 separately removes the known landing-page price/third-tier/unlimited claims.
|
||||
- **Remaining work:** browser locked/Pro state checks; mocked Stripe expiry/downgrade lifecycle; central all-task usage accounting through AI-003/004 producers; production role/config smoke. PRODUCT-001 separately removes the known landing-page price/third-tier/unlimited claims.
|
||||
|
||||
### POL-002 — AI privacy, consent and external-fallback policy
|
||||
|
||||
@@ -398,10 +398,10 @@ Ordering differences from the suggested list:
|
||||
- **Required browser verification:** user/admin controls and disclosure/locked/failure states.
|
||||
- **Required production verification:** external egress capture with synthetic data only; no real private CV/email.
|
||||
- **Status:** `IMPLEMENTED — NOT VERIFIED`.
|
||||
- **Blocker:** browser localhost is denied; MariaDB/production/external-provider verification is unavailable. Final fallback triggers and provenance depend on AI-001/002.
|
||||
- **Blocker:** browser localhost is denied; MariaDB/production/external-provider verification is unavailable. Task-specific payload minimization/accounting depend on AI-003/004.
|
||||
- **Evidence:** `docs/verification/pol-002-ai-privacy.md`; focused backend 72/72 and final policy 28/28; sidecar 18/18; focused frontend 8/8; full backend 576/576; full frontend 47 suites/158 tests; production build; config and migration script checks.
|
||||
- **Commit:** none.
|
||||
- **Remaining work:** browser user/admin disclosure checks; MariaDB and production synthetic egress proof; AI-001 policy snapshot/recheck; AI-002 provider provenance, payload minimization, cost controls and bounded local-first fallback. Background CV calls currently fail safe to local.
|
||||
- **Remaining work:** browser user/admin disclosure checks; MariaDB and production synthetic egress proof; AI-003/004 task-specific payload minimization, accounting and real producer verification. AI-001/002 now carry rechecked policy/task context and record bounded local-first provenance.
|
||||
|
||||
### AI-001 — Durable AI queue, backpressure and operation APIs
|
||||
|
||||
@@ -416,10 +416,10 @@ Ordering differences from the suggested list:
|
||||
- **Required browser verification:** synthetic operation status across refresh/nav/double-click/offline/retry/cancel.
|
||||
- **Required production verification:** queue depth/age, one-worker canary, Ollama offline/restart and app/worker restart.
|
||||
- **Status:** `IMPLEMENTED — NOT VERIFIED`.
|
||||
- **Blocker:** real 202 producers/browser verification depend on AI-003/004; provider/model circuit and provenance depend on AI-002; MariaDB/production are unavailable and the worker remains off.
|
||||
- **Blocker:** real 202 producers/browser verification depend on AI-003/004; MariaDB/production are unavailable and the worker remains off.
|
||||
- **Evidence:** `docs/verification/ai-001-durable-ai-queue.md`; focused queue/state/API tests 17/17; full backend 581/581; Compose config and diff checks.
|
||||
- **Commit:** none.
|
||||
- **Remaining work:** AI-002 provider/model controls; AI-003/004 task handlers and 202 endpoints; browser refresh/double-click/cancel/retry; MariaDB and monitored single-worker production canary. Do not create a second CV- or Strategy-specific queue.
|
||||
- **Remaining work:** AI-003/004 task handlers and 202 endpoints; browser refresh/double-click/cancel/retry; MariaDB and monitored single-worker production canary. AI-002 supplies local-first circuit/provenance. Do not create a second CV- or Strategy-specific queue.
|
||||
|
||||
### AI-002 — Ollama adapter and local-first provider routing
|
||||
|
||||
@@ -433,11 +433,11 @@ Ordering differences from the suggested list:
|
||||
- **Required tests:** routing matrix, Ollama adapter, schema failure, local circuit, fallback allowed/prohibited/unavailable, cost limits and deduplication.
|
||||
- **Required browser verification:** provider-agnostic queued states and appropriate fallback disclosure.
|
||||
- **Required production verification:** actual selected local model and controlled synthetic fallback.
|
||||
- **Status:** `IN PROGRESS`.
|
||||
- **Blocker:** actual model/config depends on PROD-003; adapter/policy can use fakes first.
|
||||
- **Evidence:** new programme explicitly supersedes ADR-004's single-provider decision for this scope.
|
||||
- **Status:** `IMPLEMENTED — NOT VERIFIED`.
|
||||
- **Blocker:** browser and production checks, actual local-model selection and controlled provider fallback depend on administrator browser policy plus PROD-001/003 access/benchmarks. Repository behavior is not blocked.
|
||||
- **Evidence:** `docs/verification/ai-002-provider-routing.md`; V-098–V-100; focused backend 26/26, full backend 588/588, sidecar fake-transport 22/22, Compose/diff checks pass.
|
||||
- **Commit:** none.
|
||||
- **Remaining work:** design smallest central policy; preserve old provider config for rollback.
|
||||
- **Remaining work:** AI-003/004 must register typed producers/handlers and explicit external task allowlists; complete monthly cross-feature accounting; browser/MariaDB/selected-model/controlled-provider/production verification. Old provider/model configuration remains available for rollback.
|
||||
|
||||
### PROD-001 — Read-only production AI inventory and rollout safety
|
||||
|
||||
@@ -523,9 +523,9 @@ Ordering differences from the suggested list:
|
||||
- **Required tests:** endpoint/handler/provider fakes, all required states, entitlement/privacy/tenant checks, E2E.
|
||||
- **Required browser verification:** complete queue/status/error/retry/cancel/refresh/back-forward/mobile/theme flow.
|
||||
- **Required production verification:** local model success, timeout and restart recovery.
|
||||
- **Status:** `NOT STARTED`.
|
||||
- **Blocker:** dependencies.
|
||||
- **Evidence:** reported timeout not yet reproduced in this programme.
|
||||
- **Status:** `IN PROGRESS`.
|
||||
- **Blocker:** browser and production checks remain unavailable, but repository tracing, handler/API implementation and fake-provider tests can proceed.
|
||||
- **Evidence:** reported timeout was code-traced as synchronous `/cv/rewrite` work; live browser/provider reproduction remains blocked and must not be inferred.
|
||||
- **Commit:** none.
|
||||
- **Remaining work:** do not build a Strategy-specific queue.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user