You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(plugin): give force a reachable meaning on definition delete #107
DELETE /jobs/definitions/{id}?force=true (#94, PR #105) is documented as bypassing the running-execution guard, but in practice it never does. DynamicJobLoaderService.deleteJob checks execution history first and returns 409 JobDeleteConflictException when any job_executions_audit row exists, and force does not bypass that check. JobExecutionAuditListener.beforeJob writes an audit row as soon as a plugin job starts, so every running execution already has history. The running-execution guard behind force is therefore unreachable for plugin jobs.
Proposed Solution
Decide and implement one meaning for force on delete. Options to evaluate:
Remove the force parameter from DELETE and document that deletion requires a definition with no execution history.
Keep force and give it a reachable meaning, for example deleting despite history once a history-retention strategy exists (see the related replacement/rollback follow-up).
Align the API contract, Javadoc, and docs with the chosen meaning.
Acceptance Criteria
The DELETE contract states one reachable behavior for force, or the parameter is removed.
Javadoc in DynamicJobLoaderService and JobManagementController, the developer guide, the runbook, and the production checklist match the implementation.
Tests cover the chosen force behavior through the service and over HTTP.
Affected Area
Plugin lifecycle: job definition deletion
Out of Scope
Changing the execution-history retention model itself.
Distributed locking.
Additional Context
Follow-up to #94 / PR #105. Raised by the native review finding R3-history-guard-shadows-force.
The description says JobExecutionAuditListener.beforeJob writes an audit row when a plugin job starts, so every running execution already has history and force is unreachable. The listener code does that, but the listener is never attached to any job: it is a @Component and no job registers it with .listener(...). The only writer of job_executions_audit is JobsManagerService.saveAudit, called after a synchronous single-job run completes (JobsManagerService.java:246). Asynchronous runs write no row.
Consequences:
force=true is reachable today: while a job is running there is usually no history row yet, so the history guard does not shadow the running-execution guard.
This issue should be re-scoped once #111 decides how history is recorded. The native review finding R3-history-guard-shadows-force was based on the same incorrect assumption.
Activity
fronzec commentedon Oct 17, 2026
Correction: the premise of this issue is wrong.
The description says
JobExecutionAuditListener.beforeJobwrites an audit row when a plugin job starts, so every running execution already has history andforceis unreachable. The listener code does that, but the listener is never attached to any job: it is a@Componentand no job registers it with.listener(...). The only writer ofjob_executions_auditisJobsManagerService.saveAudit, called after a synchronous single-job run completes (JobsManagerService.java:246). Asynchronous runs write no row.Consequences:
force=trueis reachable today: while a job is running there is usually no history row yet, so the history guard does not shadow the running-execution guard.This issue should be re-scoped once #111 decides how history is recorded. The native review finding
R3-history-guard-shadows-forcewas based on the same incorrect assumption.