fix: validate schema version on read; guard zero user id in pipeline indexing

Problem

Two cross-stage data-integrity gaps:

  1. Schema version drift, unvalidated on read. The collector wrote Metadata.SchemaVersion = "1.0" while the analyzer and testdata wrote "1.0.0". Nothing validated the version when the analyzer read collector output, so a stale or incompatible artifact silently mis-parsed to zero values instead of failing loud.
  2. Ghost user-0 in teams. A pipeline with no attributable user carries UserID: 0. That is harmless in per-user scoring because user 0 is never in the roster, but internal/analyzer/teams.go (userPrimaryProjects) ranges PipelinesByUser directly. A UserID:0 pipeline therefore seeded a ghost "user 0" team member that inflated member counts and dragged team averages.

Fix

Schema version: canonicalize plus validate on read.

  • Add schema.CurrentSchemaVersion = "1.0.0" and schema.IsSupportedSchemaVersion(v) to internal/schema/metadata.go. The helper accepts the canonical version and the legacy "1.0" so existing artifacts still load during the transition.
  • Every write site stamps schema.CurrentSchemaVersion: collector (internal/collector/collector.go), analyzer (internal/analyzer/analyze.go), testdata (internal/testdata/generate.go).
  • Validate on read with a clear error: the analyzer load path loadCollectedData (internal/analyzer/analyze.go) and the collector incremental read path loadEnvelopeData (internal/collector/incremental.go) reject an unsupported version (artifact schema version %q unsupported by this manifold build; re-run collect) instead of proceeding.
  • Watermark (internal/collector/watermark.go) carries its own WatermarkVersion, not a schema version, and already validates non-empty. Left unchanged.

Guard UserID == 0 in pipeline indexing.

  • Add a p.UserID != 0 guard where PipelinesByUser is built in internal/analyzer/scorer.go, mirroring the existing != 0 guards on MRsByMergeUser and IssuesByCloser in the same constructor.

Validation

make check (vet plus test) and go test -race ./... pass on Go 1.24.2. No data races.

Fails-on-old / passes-on-fix proofs (temporary test, reverted each fix in turn):

  • Schema read. With validation in place: an artifact stamped "9.9" is rejected with the clear error, "1.0.0" loads, legacy "1.0" loads. With the read validation reverted: the "9.9" artifact loads silently with no error. Restored: rejected again.
  • User-0 guard. With the guard in place: a PipelinesByUser built from a pipeline set including a UserID:0 pipeline has no key 0 (keys present: [1]). With the guard reverted: key 0 appears. Restored: absent again.

🤖 Generated with Claude Code

Merge request reports

Loading