Skip to content

General: Align compliance rules and stabilize generation workflow - #2631

Open
ge94zec wants to merge 14 commits into
mainfrom
chore/2535-modularize-compliance-rules-across-prompts
Open

General: Align compliance rules and stabilize generation workflow#2631
ge94zec wants to merge 14 commits into
mainfrom
chore/2535-modularize-compliance-rules-across-prompts

Conversation

@ge94zec

@ge94zec ge94zec commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Checklist

General

Server

Motivation and Context

Translation, compliance analysis, and generation requests could overlap and finish out of order. A stale analysis or translation could then update the UI or the persisted job state after a newer generation had already completed, most visibly in the compliance score, which could briefly show the value belonging to an olderjob description.

The root cause on the server was that compliance analysis used a blocking.call(), which could not be cancelled once started; on the client, nothing invalidated callbacks belonging to superseded runs.

Description

Overall, these changes make the Generate → Translate → Analyze → Score workflow more deterministic. New generations take priority over outdated background work, stale results are discarded, and the displayed compliance score now corresponds to the latest analyzed version of the job description.

  • New AiPriorityService with foreground(jobId, flux) / background(jobId, flux),
    scoped per job. Generation is foreground; translation and compliance analysis are
    background. Starting a generation cancels ongoing background work for the same job.
  • Compliance analysis moved from .call() to .stream() so it can actually be
    cancelled mid-flight; the response is reassembled and parsed via BeanOutputConverter.
  • AbortController for generation, takeUntil for the analysis request.
  • POST /ai/jobs/analyze now returns JobAnalysisDTO (score + issues) instead of
    ComplianceIssue[]. Regenerate the API client.
  • translateTextStream takes an additional jobId.
  • updated prompt rules for compliance policy

Steps for Testing

Prerequisites:

  1. Log in to TUMApply as Prof
  2. Nav to Create position
  3. Verify generateDraft generates compliance free model text

Review Progress

Code Review

  • Code Review 1

Manual Tests

  • Test 1

Screenshots

Test Coverage

Client

Class/File Line Coverage Lines Expects Ratio
ai-run.ts 100.00% 14 4 28.6
job-creation-form.component.ts 75.03% 1286 106 8.2
ai-streaming.service.ts 88.63% 88 17 19.3
editor.component.ts 51.73% 386 28 7.3

Server

Class/File Line Coverage Lines
ComplianceIssueDTO.java 100.00% 27
JobAnalysisDTO.java 100.00% 10
TranslateComplianceDTO.java 100.00% 8
AiPriorityService.java 8.33% 48
AiService.java 8.00% 356
AiResource.java 48.00% 93
JobService.java 75.77% 372

Last updated: 2026-08-19 16:23:52 UTC

@github-actions github-actions Bot added server Pull requests that update Java code. (Added Automatically!) client Pull requests that update TypeScript code. (Added Automatically!) labels Aug 4, 2026
@codacy-production

codacy-production Bot commented Aug 4, 2026

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 high · 3 medium

Alerts:
⚠ 4 issues (≤ 0 issues of at least minor severity)

Results:
4 new issues

Category Results
UnusedCode 1 medium
ErrorProne 1 medium
Security 1 high
Complexity 1 medium

View in Codacy

🟢 Metrics 32 complexity

Metric Results
Complexity 32

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📊 Client Test Coverage Too Low

🔍 View coverage locally:

pnpm run test:ci
open build/test-results/vitest/coverage/index.html

🌐 View coverage from GitHub:
Download the "coverage-report-client" artifact from this workflow run.

@ge94zec ge94zec changed the title Chore/2535 modularize compliance rules across prompts General: Modularize compliance rules Aug 10, 2026
@ge94zec ge94zec changed the title General: Modularize compliance rules General: Modularize compliance rules Aug 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📊 Client Test Coverage Too Low

🔍 View coverage locally:

pnpm run test:ci
open build/test-results/vitest/coverage/index.html

🌐 View coverage from GitHub:
Download the "coverage-report-client" artifact from this workflow run.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 No OpenAPI or client changes needed.

@ge94zec ge94zec changed the title General: Modularize compliance rules General: Update compliance rules in generation Aug 10, 2026
…-rules-across-prompts' into chore/2535-modularize-compliance-rules-across-prompts
@github-actions

Copy link
Copy Markdown
Contributor

🤖 No OpenAPI or client changes needed.

- add AiRun to centralize AbortController ownership and stale workflow detection
- cancel active analysis and translation requests before starting generation -> prioritize generation
- scope backend AI cancellation by job ID to avoid affecting other jobs or users
- pass the job ID through generation and translation requests
- return analysis score and compliance issues directly through JobAnalysisDTO
- prevent persistence entities from leaking through the analysis API
- add a dedicated streaming editor update method
- remove redundant frontend score polling and additional job reloads
- remove redundant analyze rules in AnalyzeComplianceText.st
- add tests for cancellation, streaming, and editor behavior
@github-actions github-actions Bot added tests job Code changes in job module. labels Aug 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📊 Client Test Coverage Too Low

🔍 View coverage locally:

pnpm run test:ci
open build/test-results/vitest/coverage/index.html

🌐 View coverage from GitHub:
Download the "coverage-report-client" artifact from this workflow run.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 OpenAPI spec and client code auto-updated and committed.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 No OpenAPI or client changes needed.

@ge94zec
ge94zec marked this pull request as ready for review August 11, 2026 22:18
@ge94zec
ge94zec requested a review from az108 as a code owner August 11, 2026 22:18
@ge94zec ge94zec changed the title General: Update compliance rules in generation General: Align compliance rules and stabilize generation workflo Aug 11, 2026
@ge94zec ge94zec changed the title General: Align compliance rules and stabilize generation workflo General: Align compliance rules and stabilize generation workflow Aug 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🤖 No OpenAPI or client changes needed.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 No OpenAPI or client changes needed.

@helios-aet
helios-aet Bot deployed to test-server August 17, 2026 11:33 Active
@github-actions

Copy link
Copy Markdown
Contributor

🤖 No OpenAPI or client changes needed.

@az108 az108 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes — the branch does not merge into main, and that needs sorting before the rest is worth acting on.

Blocking: 14 merge conflicts, three of them add/add

Last merge from main was 08-14. git merge-tree origin/main HEAD exits 1:

CONFLICT (add/add):   ai/dto/ComplianceIssueDTO.java        <- now exists on main
CONFLICT (add/add):   ai/dto/JobAnalysisDTO.java            <- now exists on main
CONFLICT (add/add):   generated/model/job-analysis-dto.ts
CONFLICT (content):   AiService.java, AiResource.java, JobService.java,
                      TranslateComplianceDTO.java, openapi.yaml,
                      job-creation-form.component.ts, editor.component.ts,
                      ai-resource-api.ts, translate-compliance-dto.ts,
                      AiResourceTest.java, job-creation-form.component.spec.ts

ComplianceIssueDTO and JobAnalysisDTO were created independently on main (via #2470/#2465) and here — two implementations of the same DTOs that need reconciling rather than textual merging.

Worth being deliberate about the resolution: add/add and modify/delete are exactly the conflict class that left two 0-byte files committed on #2520, so it is worth diffing against main deliberately rather than accepting either side wholesale.

I would rebase before acting on the inline point below — all three AiPriorityService call sites live in AiService.java, which is itself conflicted.

AiPriorityService has no test coverage

No test anywhere in src/test/ references it. AiResourceTest structurally cannot cover it: setUp() swaps in a Mockito mock via ReflectionTestUtils.setField(aiResource, "aiService", aiService), so neither the real AiService nor AiPriorityService ever runs. The new shouldReturnConflictWhenAnalysisIsCancelled stubs the CancellationException itself and only asserts the exception-to-409 mapping in AiResource.

That leaves untested: registration/deregistration, whether foreground actually cancels a live background, the unregister cleanup path, and the null-jobId bypass — i.e. all of the concurrency semantics, including the race below. A StepVerifier test over foreground/background would cover it.

Fine as-is

ai-run.spec.ts is good — both names start with should, and it covers both states of the run. The AiRun abstraction itself reads well.

}
return Flux.defer(() -> {
Sinks.Empty<Void> cancellation = Sinks.empty();
backgroundCancellations.computeIfAbsent(jobId, _ -> ConcurrentHashMap.newKeySet()).add(cancellation);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check-then-act: computeIfAbsent returns the set, then .add(cancellation) runs outside any map lock — while cancelBackground (line 60) does backgroundCancellations.remove(jobId). If the remove lands between the two, the new cancellation goes into a set that is no longer in the map, so that background stream is never cancelled — which is the whole purpose of this class.

Being straight about severity: the window is a few bytecode instructions, so a hit is unlikely. Worth fixing anyway because the fix is a risk-free one-liner and the consequence is not cosmetic — an orphaned cancellation means a full background LLM call runs alongside the generation, and in the compliance path jobService.updateAiAnalysis (AiService.java:523) then writes a score computed from the pre-generation text.

compute holds the bin lock across the entire mapping function, so it is mutually exclusive with remove on the same key:

backgroundCancellations.compute(jobId, (_, existing) -> {
    Set<Sinks.Empty<Void>> set = existing != null ? existing : ConcurrentHashMap.newKeySet();
    set.add(cancellation);
    return set;
});

No memory leak either way — unregister uses computeIfPresent, so in the race case it degrades to a safe no-op and the orphaned set is collected.

Separate, lower-severity point on the same class: cancelBackground runs exactly once, at foreground subscribe, and leaves no marker behind. Generation streams for seconds, so background work registered after that point runs unimpeded. The client closes the obvious path — executeAutoSave bails on isGeneratingDraft() (job-creation-form.component.ts:1662) and re-checks run.isStale() after the save round-trip — but two paths remain, both with millisecond rather than nanosecond windows: two tabs on the same draft (no shared activeAiRun, which is presumably why this state is keyed by jobId server-side), and latency skew where generate is sent later but arrives first. Note the compute fix above does not close this one. A foreground marker that background checks at registration would.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client Pull requests that update TypeScript code. (Added Automatically!) job Code changes in job module. ready for review server Pull requests that update Java code. (Added Automatically!) tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants