Summary
SecurityScanService.triggerScan is transactional, but it publishes the Redis Stream scan task inline before the database transaction commits.
The consumer can receive the task before the new skill_version / security_audit rows are visible. It then fails with SkillVersion not found or SecurityAudit not found. The current stream consumer retries immediately and acknowledges each failed message; all retries can be exhausted while the publishing transaction is still open, leaving the committed version stuck in SCANNING.
Current code:
Steps To Reproduce
- Enable the security scanner and Redis Stream consumer.
- Add a transaction delay after
triggerScan publishes the task but before the publish transaction commits, or run with a fast consumer and a slow database transaction.
- Publish a public or namespace-only skill version.
- Observe the consumer receive the task before the version/audit rows are committed.
- The consumer retries the same task immediately; under a sufficiently long transaction, all retries fail.
- The publish request commits, but the version remains
SCANNING with no successful scan result.
A deterministic regression test can use a transaction barrier/latch and assert that ScanTaskProducer.publishScanTask is not called until after commit.
Expected Behavior
The scan task should become visible only after the transaction that creates/updates the version and audit rows commits.
Typical options:
- register an
afterCommit synchronization before publishing; or
- use an outbox/event mechanism with commit-coupled delivery.
When no transaction is active, immediate publishing can remain supported.
Environment
- Branch:
main
- Commit:
9f602f8184f23347124b3606ffce1a6ac5f19c0a
- Scanner mode: both local and upload modes are affected by task timing
API Contract Impact
No API shape change. This fixes an asynchronous lifecycle race after publish.
Logs Or Screenshots
Representative errors:
SkillVersion not found: <versionId>
SecurityAudit not found for versionId=<versionId>
Summary
SecurityScanService.triggerScanis transactional, but it publishes the Redis Stream scan task inline before the database transaction commits.The consumer can receive the task before the new
skill_version/security_auditrows are visible. It then fails withSkillVersion not foundorSecurityAudit not found. The current stream consumer retries immediately and acknowledges each failed message; all retries can be exhausted while the publishing transaction is still open, leaving the committed version stuck inSCANNING.Current code:
triggerScanpublishes inside the transactionSteps To Reproduce
triggerScanpublishes the task but before the publish transaction commits, or run with a fast consumer and a slow database transaction.SCANNINGwith no successful scan result.A deterministic regression test can use a transaction barrier/latch and assert that
ScanTaskProducer.publishScanTaskis not called until after commit.Expected Behavior
The scan task should become visible only after the transaction that creates/updates the version and audit rows commits.
Typical options:
afterCommitsynchronization before publishing; orWhen no transaction is active, immediate publishing can remain supported.
Environment
main9f602f8184f23347124b3606ffce1a6ac5f19c0aAPI Contract Impact
No API shape change. This fixes an asynchronous lifecycle race after publish.
Logs Or Screenshots
Representative errors: