Skip to content

One row mapper, one job loader, one failure policy for the enrichment repositories #144

Description

@cursor

Spec: Fowler-style refactoring review (cloud agent, 2026-08-05). Refactor only — no behavior change.

What to change

lib/enrichment/neon-repository.ts repeats itself, and reimplements policy owned elsewhere:

  1. Row → domain mapping ×3. The same camelCase + ?? [] / ?? null block appears in listThreadEnrichments (~274–293) and twice in completeJob (~512–531, ~564–583); memory-repository.ts repeats the created-path shape (~193–212). Extract mapEnrichment(row) beside the existing mapJob, with a single shared column list.
  2. Job SELECT ×6. SELECT id, idempotency_key, thread_id, basis_revision, … plus its inline Array<{...}> cast appears six times. Extract loadJob(userId, jobId) / loadJobByKey(...).
  3. Snapshot ordering duplicated. The basis_revision + 0.5 + index * 0.001 ordering exists in neon listPendingThreads (~219–221) and memory buildSnapshot (~77–87). Share one buildEnrichmentSnapshot.
  4. Permanent-failure policy drift. requeueFailed reimplements isPermanentEnrichmentError from lib/enrichment/failures.ts as SQL LIKE clauses (~641–671) with a comment admitting it "mirrors" the TS. Make one module own the policy (filter in TS after fetch, or generate the SQL predicate from the same source).

Out of scope: splitting EnrichmentRepository into job/embedding interfaces; the optional-method cleanup; classification typing (separate ticket).

Acceptance criteria

  • One mapEnrichment(row) used by every enrichment read/write path in the Neon repo
  • One job loader replaces the six duplicated SELECT + cast blocks
  • One snapshot builder shared by the Neon and memory repos
  • isPermanentEnrichmentError has a single authoritative implementation with no mirrored SQL copy
  • mise run lint passes; enrichment specs (tests/enrichment-*.spec.ts) pass unchanged

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions