CI Drift Check
Drift happens when massu.config.yaml declares one repo shape but the filesystem has evolved into a different shape — a new language, a moved source directory, an added workspace. The drift check compares the live runDetection output against what's declared in the config and flags the deltas. It's designed to run in CI so stale configs never merge to main.
What Drift Means
The drift check reports a mismatch in any of four dimensions:
- Language set —
framework.languageskeys don't match the detected languages - Per-language framework or test framework — declared framework is not what detection sees
- Manifest set — manifests on disk differ from
canonical_paths.manifest_pathsormanifests[] - Monorepo workspace set —
monorepo.workspacesin config differs from detected workspaces
How It Works
Drift detection is built on a deterministic SHA-256 fingerprint over a stable-sorted JSON summary of DetectionResult. The summary includes: languages, per-language framework, per-language test framework, source_dirs, manifest paths, monorepo type, and workspace paths.
Two functions, both in packages/core/src/detect/drift.ts:
| Function | Signature | Returns |
|---|---|---|
computeFingerprint | (detectionResult: DetectionResult) => string | SHA-256 hex digest (stable across runs) |
detectDrift | (currentConfig, actualDetection) => DriftReport | { drifted: boolean, changes: DriftChange[] } |
DriftChange entries identify the field, the declared value, and the detected value so CI output pinpoints the exact discrepancy.
Stability Guarantee
The fingerprint is deterministic by construction:
- All keys are sorted before JSON serialization
- Array entries are sorted
- Only the fields listed above are included (no timestamps, no absolute paths)
- Identical repos produce identical fingerprints across machines
Idempotence is covered by tests in src/tests/drift-detection.test.ts — re-running detectDrift on unchanged inputs returns the same report, and fingerprints match on re-run.
CI Wiring (Planned — Not Shipped in 1.0.0)
Status: Phase 5 of the autodetect plan is NOT part of the 1.0.0 MVP cut. The computeFingerprint and detectDrift functions exist and are tested as of 1.0.0, but the CLI surface (massu drift-check) and the prebuilt GitHub Action are planned for a follow-up release. This section describes the planned API.
Planned CLI
# Exit 0 if no drift, exit 1 with diff if drift detected
npx massu drift-check
# JSON output for machine consumption
npx massu drift-check --format json
# Fix config to match reality (dangerous — writes new config)
npx massu drift-check --fixPlanned GitHub Actions Step
# .github/workflows/ci.yml — planned, not shipped in 1.0.0
jobs:
drift-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm ci
- name: Check for config drift
run: npx massu drift-checkUsing Drift Detection Today
Even without the CLI surface, the exported functions can be used from a script:
import { readFileSync } from 'fs';
import { parse as yamlParse } from 'yaml';
import { runDetection } from '@massu/core/detect';
import { detectDrift, computeFingerprint } from '@massu/core/detect/drift';
const projectRoot = process.cwd();
const configPath = `${projectRoot}/massu.config.yaml`;
const declared = yamlParse(readFileSync(configPath, 'utf-8'));
const actual = await runDetection(projectRoot);
const report = detectDrift(declared, actual);
if (report.drifted) {
console.error('Config drift detected:');
for (const change of report.changes) {
console.error(` - ${change.field}: declared=${change.declared} detected=${change.detected}`);
}
process.exit(1);
}
console.log(`Fingerprint: ${computeFingerprint(actual)}`);Drift Scenarios
New Language Added
A repo starts as pure TypeScript; a pyproject.toml is added later. Drift check reports framework.languages has changed from [typescript] to [python, typescript]. Fix: re-run massu init or hand-edit the config to add framework.languages.python.
Test Framework Switched
Repo migrates from vitest to jest. Drift check reports framework.languages.typescript.test_framework: vitest → jest. Fix: update the config entry; regenerate verification.typescript.test if it hardcoded npm run test:vitest.
New Workspace in a Monorepo
A new apps/mobile workspace is added to a turbo monorepo. Drift check reports monorepo.workspaces adds a new entry. Fix: rebuild domains with DomainInferrer or add the workspace to domains[] manually.
Related
auto-detection— what signalsDetectionResultcapturesmigration/v1-to-v2—migrateV1ToV2uses the sameDetectionResultas inputreference/config-reference—canonical_paths.manifest_pathsschema