Skip to content

fix(digest-dedup): CLUSTERING typo fallback fails closed to complete-link#3331

Merged
koala73 merged 1 commit into
mainfrom
fix/digest-dedup-clustering-typo-fallback
Apr 23, 2026
Merged

fix(digest-dedup): CLUSTERING typo fallback fails closed to complete-link#3331
koala73 merged 1 commit into
mainfrom
fix/digest-dedup-clustering-typo-fallback

Conversation

@koala73

@koala73 koala73 commented Apr 23, 2026

Copy link
Copy Markdown
Owner

Why this PR?

DIGEST_DEDUP_CLUSTERING previously fell to single on unrecognised values, which silently defeated the documented kill switch. An operator typing DIGEST_DEDUP_CLUSTERING=complet during an over-merge incident would stick with the aggressive single-link merger instead of rolling back to conservative complete-link.

Before:

const clustering =
  clusteringRaw === 'complete' ? 'complete'
  : clusteringRaw === 'single' || clusteringRaw === '' ? 'single'
  : 'single';   // ← typo fails OPEN to the aggressive merger

After — mirrors the DIGEST_DEDUP_MODE typo pattern from PR #3247:

if (clusteringRaw === '' || clusteringRaw === 'single') clustering = 'single';
else if (clusteringRaw === 'complete') clustering = 'complete';
else {
  clustering = 'complete';              // ← typo fails CLOSED to the safe algorithm
  invalidClusteringRaw = clusteringRaw; // surface the typo for operator
}

The orchestrator emits a warn line on entry whenever invalidClusteringRaw is non-null, so operators see the typo alongside the kill-switch-took-effect message. Warn fires on both embed and jaccard codepaths because the operator's intent (conservative algorithm) applies in both modes.

Behaviour change surface

Input Before After Intent
unset single single unchanged
'' single single unchanged
single / SINGLE single single unchanged
complete / Complete complete complete unchanged
complet / avarage / any typo single complete + warn fixed

Only true typos change behaviour, and the new behaviour is the kill-switch-safe one.

Scope

Intentionally narrow. No change to:

  • The dedup orchestrator algorithms (single-link / complete-link stay exactly as they were).
  • The MODE fallback logic (already correct per feat(digest): topic-grouped brief ordering (size-first) #3247).
  • The embedding path, Jaccard fallback, topic grouping, score floor, or any downstream consumer.
  • Env vars other than DIGEST_DEDUP_CLUSTERING.

Tests

  • node --test tests/brief-dedup-embedding.test.mjs61/61 pass (prior 59 unchanged + 3 new: case-insensitive valid values, typo → complete with invalidClusteringRaw, warn emission on jaccard codepath). The one prior test that codified the old buggy behaviour is updated to the new contract.
  • node --test tests/brief-dedup-embedding.test.mjs tests/brief-dedup-jaccard.test.mjs81/81 pass total.
  • npx biome lint → clean
  • npm run typecheck → clean

Rollback

Revert the commit. DIGEST_DEDUP_CLUSTERING=single (explicit) restores the previous aggressive-merger behaviour without any code change.

Related

Test plan

  • node --test tests/brief-dedup-embedding.test.mjs tests/brief-dedup-jaccard.test.mjs
  • npx biome lint on changed files
  • npm run typecheck
  • Post-merge: verify a deliberate typo (DIGEST_DEDUP_CLUSTERING=complet) on a staging Railway service emits the expected warn line

…link

DIGEST_DEDUP_CLUSTERING previously fell to 'single' on unrecognised
values, which silently defeated the documented kill switch. A typo
like `DIGEST_DEDUP_CLUSTERING=complet` during an over-merge incident
would stick with the aggressive single-link merger instead of rolling
back to the conservative complete-link algorithm.

Mirror the DIGEST_DEDUP_MODE typo pattern (PR #3247):
  - Unrecognised value → fall to 'complete' (SAFE / conservative).
  - Surface the raw value via new `invalidClusteringRaw` config field.
  - Emit a warn line on the dedup orchestrator's entry path so operators
    see the typo alongside the kill-switch-took-effect message.

Valid values 'single' (default), 'complete', unset, empty, and any
case variation all behave unchanged. Only true typos change behaviour
— and the new behaviour is the kill-switch-safe one.

Tests: updated the existing case that codified the old behaviour plus
added coverage for (a) multiple typo variants falling to complete with
invalidClusteringRaw set, (b) case-insensitive valid values not
triggering the typo path, and (c) the orchestrator emitting the warn
line even on the jaccard-kill-switch codepath (since CLUSTERING intent
applies to both modes).

81/81 dedup tests pass.
@vercel

vercel Bot commented Apr 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
worldmonitor Ignored Ignored Apr 23, 2026 6:36am

Request Review

@greptile-apps

greptile-apps Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a silent fail-open bug in DIGEST_DEDUP_CLUSTERING env-var parsing: unrecognised values (e.g. a typo like complet) previously fell back to 'single' (the aggressive merger), defeating the kill switch. They now fall to 'complete' (conservative) and surface the bad value via invalidClusteringRaw for a warning log. The change mirrors the existing DIGEST_DEDUP_MODE typo-handling pattern from PR #3247 and is well-covered by new tests.

Confidence Score: 5/5

Safe to merge — narrow, well-tested fix with no unintended scope.

All findings are P2 or absent. The logic change is correct, mirrors an established pattern, and is covered by 4 new tests plus an updated existing one. No security, data-integrity, or correctness issues found.

No files require special attention.

Important Files Changed

Filename Overview
scripts/lib/brief-dedup.mjs Clustering parse logic updated to fail-closed on typos; invalidClusteringRaw added to config shape and warn emitted in deduplicateStories — logic is correct and consistent with the MODE pattern.
tests/brief-dedup-embedding.test.mjs Existing buggy-behaviour test updated; four new tests added covering typo→complete, case-insensitivity, null-invalidClusteringRaw on valid inputs, and warn emission on jaccard path.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["env.DIGEST_DEDUP_CLUSTERING"] --> B["clusteringRaw = (value ?? '').toLowerCase()"]
    B --> C{clusteringRaw}
    C -->|"'' or 'single'"| D["clustering = 'single'\ninvalidClusteringRaw = null"]
    C -->|"'complete'"| E["clustering = 'complete'\ninvalidClusteringRaw = null"]
    C -->|"anything else (typo)"| F["clustering = 'complete' ← BEFORE: 'single'\ninvalidClusteringRaw = clusteringRaw"]
    F --> G["deduplicateStories: emit warn log"]
    D --> H["Return config"]
    E --> H
    G --> H
Loading

Reviews (1): Last reviewed commit: "fix(digest-dedup): CLUSTERING typo fallb..." | Re-trigger Greptile

@koala73
koala73 merged commit 1958b34 into main Apr 23, 2026
11 checks passed
@koala73
koala73 deleted the fix/digest-dedup-clustering-typo-fallback branch April 23, 2026 07:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant