Skip to content

[Bug]: buildRegistryChangeReport only diffs 5 of the 8 registry config fields — changes to fixedBaseScore / defaultLabelMultiplier / eligibilityMode are reported as "no change" #280

Description

@joaovictor712

Summary

buildRegistryChangeReport (the live "what changed between the two latest registry snapshots" report) compares only five of the eight comparable fields on RegistryRepoConfig:

// src/signals/engine.ts:3021-3028
const changes = [
  ...(repo.emissionShare !== old.emissionShare ? [`emission_share ${old.emissionShare} -> ${repo.emissionShare}`] : []),
  ...(repo.issueDiscoveryShare !== old.issueDiscoveryShare ? [`issue_discovery_share ${old.issueDiscoveryShare} -> ${repo.issueDiscoveryShare}`] : []),
  ...(repo.maintainerCut !== old.maintainerCut ? [`maintainer_cut ${old.maintainerCut} -> ${repo.maintainerCut}`] : []),
  ...(JSON.stringify(repo.labelMultipliers) !== JSON.stringify(old.labelMultipliers) ? ["label_multipliers changed"] : []),
  ...(repo.trustedLabelPipeline !== old.trustedLabelPipeline ? [`trusted_label_pipeline ${old.trustedLabelPipeline ?? false} -> ${repo.trustedLabelPipeline ?? false}`] : []),
];
return changes.length > 0 ? [{ repoFullName, changes }] : [];   // empty changes -> repo dropped from changedRepos

It omits fixedBaseScore, defaultLabelMultiplier, and eligibilityMode. The authoritative drift comparator enumerates all eight:

// src/upstream/ruleset.ts:606-615  (the correct reference)
const REGISTRY_DRIFT_COMPARABLE_FIELDS: RegistryComparableHyperparameterField[] = [
  "emissionShare", "issueDiscoveryShare", "maintainerCut",
  "fixedBaseScore", "eligibilityMode", "trustedLabelPipeline",
  "defaultLabelMultiplier", "labelMultipliers",
];

RegistryRepoConfig (src/types.ts:225-236) defines and the normalizer (src/registry/normalize.ts) parses all eight. Because a repo with an empty changes array is dropped from changedRepos (engine.ts:3029), any registry update that touches only an omitted field is reported as "no change" for that repo, and the summary count is wrong.

Why the omitted fields matter

The three omitted fields are exactly the ones that most directly move scoring:

  • fixedBaseScore overrides the entire base score — buildScorePreview uses it in place of the computed base when present (src/scoring/preview.ts:276,287-288). A change here changes every score preview for the repo.
  • defaultLabelMultiplier is the fallback label multiplier (src/scoring/preview.ts:293).
  • eligibilityMode drives lane-fit / scoreability assumptions (it is a first-class drift area in ruleset.ts).

So the report silently hides the highest-impact registry changes while faithfully reporting the lower-impact ones (maintainer_cut, trusted_label_pipeline).

Failure mode (concrete example)

Between two registry snapshots, upstream changes JSONbored/gittensory from fixed_base_score: 2 to fixed_base_score: 50 and changes nothing else.

  • Correct: changedRepos includes { repoFullName: "JSONbored/gittensory", changes: ["fixed_base_score 2 -> 50"] }, and the summary reads … 1 changed repo(s).
  • Current: all five compared fields are equal → changes is [] → the repo is dropped → changedRepos is empty and the summary reads 0 changed, even though every score preview for that repo now differs (base score 2 → 50).

The same false "no change" happens for a pure default_label_multiplier or eligibility_mode update.

Affected surfaces

buildRegistryChangeReport is public-facing:

  • GET /v1/registry/changes (src/api/routes.ts:1173).
  • The registry-changes MCP tool (src/mcp/server.ts:762).

Maintainers/contributors using these to see "what changed in the registry" get a false all-clear for the fields that most affect scoring and eligibility.

Steps to reproduce

  1. Persist two registry snapshots where the newer one differs from the older only in fixedBaseScore (or defaultLabelMultiplier, or eligibilityMode) for one repo.
  2. Call buildRegistryChangeReport(await listLatestRegistrySnapshots(env, 2)) (or hit GET /v1/registry/changes).
  3. Observe the repo is absent from changedRepos and the summary says 0 changed, instead of reporting the fixed_base_score change.

Expected behavior

The change report compares the same full set of comparable RegistryRepoConfig fields as REGISTRY_DRIFT_COMPARABLE_FIELDS (ruleset.ts:606-615), so a change to fixedBaseScore, defaultLabelMultiplier, or eligibilityMode produces a changes entry and counts the repo as changed.

Actual behavior

Only emissionShare, issueDiscoveryShare, maintainerCut, labelMultipliers, and trustedLabelPipeline are diffed; changes to the other three comparable fields are invisible and the repo is dropped from changedRepos.

Suggested fix

  • Make buildRegistryChangeReport diff the full comparable-field set. Rather than adding three more ad-hoc branches (which is how the two implementations drifted in the first place), drive the comparison from a single field-descriptor table — { label, render(config) } for each of the eight RegistryRepoConfig comparable fields — and iterate it to build changes. This covers fixed_base_score, default_label_multiplier, and eligibility_mode, and keeps the live report and the upstream drift comparator from diverging again as new config fields are added.
  • Add fail-on-revert coverage: a snapshot pair differing only in fixedBaseScore (and again only in eligibilityMode / defaultLabelMultiplier) must yield a changedRepos entry with the corresponding field old -> new change line and a 1 changed summary. The existing tests only exercise the five currently-compared fields, so they ratify the omission.

Metadata

Metadata

Assignees

No one assigned

    Labels

    slopAI slop and/or attempts to game additional points via manipulation or alt profiles.

    Projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions