Fold PromoteSource into Update

Every PromoteSource caller already loaded the tracker pattern under
the same transaction, so a dedicated single-column UPDATE only
duplicated machinery and forced callers to learn a second mutation
verb. Add `source = @source` to Update's SET clause, mutate
Source/UpdatedAt on the receiver, and call Update at the three
promotion sites (worker merge loop, worker adoption loop, and
reportDetectedTracker). The shouldPromoteSource gate still ranks the
candidate against the loaded value; Update is now the single write
path that can advance source, with a doc comment spelling out the
load-first contract.

Re-cast the coredata tests around Update: WritesSource pins the
round-trip from receiver to DB, NotFoundForMissingRow preserves the
ErrResourceNotFound contract callers rely on. The old
OnlyTouchesSourceAndUpdatedAt test was a property of the narrow
PromoteSource UPDATE and no longer applies — Update intentionally
rewrites the full editable column set from the receiver.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-05-26 18:04:13 +02:00
parent 27a9f83825
commit dc92fd238f
5 changed files with 78 additions and 114 deletions

View File

@@ -202,7 +202,10 @@ func (h *patternAnalysisHandler) Process(ctx context.Context, banner coredata.Co
}
if shouldPromoteSource(globPattern.Source, source) {
if err := globPattern.PromoteSource(ctx, tx, scope, *source, now); err != nil {
globPattern.Source = source
globPattern.UpdatedAt = now
if err := globPattern.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot promote source on glob pattern %q: %w", key.template, err)
}
}
@@ -784,12 +787,15 @@ func (h *patternAnalysisHandler) adoptUncategorisedPatterns(
// non-uncategorised category. Without this, a
// PRE_EXISTING glob never advances to SCRIPT/EXTENSION
// even though new SDK-observed exacts confirm the
// stronger signal. PromoteSource mutates match.Source
// in place, so subsequent adoptions against the same
// glob ratchet correctly (PRE_EXISTING → EXTENSION →
// SCRIPT) without redundant writes.
// stronger signal. We mutate match.Source in place
// before calling Update so subsequent adoptions against
// the same glob ratchet correctly (PRE_EXISTING →
// EXTENSION → SCRIPT) without redundant writes.
if shouldPromoteSource(match.Source, ep.Source) {
if err := match.PromoteSource(ctx, tx, scope, *ep.Source, time.Now()); err != nil {
match.Source = ep.Source
match.UpdatedAt = time.Now()
if err := match.Update(ctx, tx, scope); err != nil {
return false, fmt.Errorf("cannot promote source on glob pattern %q: %w", match.Pattern, err)
}
}