Skip to content

feat(plugin): give force a reachable meaning on definition delete #107

Open

Description

@fronzec

Problem Description

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.

Activity

  1. fronzec commented on Oct 17, 2026

    @fronzec
    OwnerAuthor

    Correction: the premise of this issue is wrong.

    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.
    • The real problem is that execution history is incomplete, which makes the delete history guard inconsistent. That is tracked in fix(plugin): record execution history for every plugin job run #111.

    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.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions