JuliusBrussee/caveman · error · Error
${where} cohort family unit count disagrees with the task fa
Error message
${where} cohort family unit count disagrees with the task family What it means
cohort.family_unit_count must equal the number of unique analysis-unit ids on the referenced task family — the validator builds Set(family.analysis_unit_ids) and compares sizes (validate-continuous-improvement.mjs:140-141). The cohort's later population arithmetic (error 607) is anchored on this number, so it must agree with the family's own membership list before anything else is checked.
Source
Thrown at packages/shared/contracts/scripts/validate-continuous-improvement.mjs:141
}
if (motif.support_run_count !== runs) throw new Error(`${where} support run count ${motif.support_run_count} != ${runs}`);
const expectedShare = runs > 0 ? weighted / runs : 0;
if (Math.abs(motif.structural_cost_share - expectedShare) > 1e-6) {
throw new Error(`${where} structural_cost_share ${motif.structural_cost_share} != ${expectedShare}`);
}
}
motifCount += report.motifs.length;
// The causal investigation of every case: the cohort's arms, the traces it
// selected from them, and the backward hard-dependency slice.
const unitsByID = new Map(report.analysis_units.map((unit) => [unit.id, unit]));
const familiesByID = new Map(report.task_families.map((family) => [family.id, family]));
for (const item of report.cases) {
const where = `report fixture ${reportPaths[index]}: case ${item.id}`;
const family = familiesByID.get(item.cohort.task_family_id);
if (!family) throw new Error(`${where} cohort references a task family that is not in this report`);
const familyUnits = new Set(family.analysis_unit_ids);
if (item.cohort.family_unit_count !== familyUnits.size) throw new Error(`${where} cohort family unit count disagrees with the task family`);
const roles = item.cohort.arms.map((arm) => arm.role);
if (roles.join(",") !== "baseline,alternative") throw new Error(`${where} cohort arms are not baseline then alternative`);
const armVariants = new Map();
let comparedUnits = 0;
for (const arm of item.cohort.arms) {
const variant = variantsByID.get(arm.variant_id);
if (!variant) throw new Error(`${where} cohort arm ${arm.role} references a workflow variant that is not in this report`);
if (variant.task_family_id !== family.id) throw new Error(`${where} cohort arm ${arm.role} uses a variant of another task family`);
armVariants.set(arm.role, variant);
comparedUnits += arm.unit_count;
}
const excluded = item.cohort.excluded_units.reduce((total, exclusion) => total + exclusion.unit_count, 0);
if (comparedUnits + excluded !== item.cohort.family_unit_count) {
throw new Error(`${where} cohort arms (${comparedUnits}) plus exclusions (${excluded}) do not account for its ${item.cohort.family_unit_count} family units`);
}
const selectedPerArm = new Map();
for (const trace of item.representative_traces) {View on GitHub (pinned to 766dce6b13)
Solutions
- Set family_unit_count to the exact number of unique ids in family.analysis_unit_ids.
- Deduplicate family.analysis_unit_ids if it repeats an id.
- If you edit one cohort, re-check every other case that references the same family, then re-run the validator.
Example fix
// before — family.analysis_unit_ids holds 14 unique ids
"cohort": { "task_family_id": "family-codegen", "family_unit_count": 12, ... }
// after
"cohort": { "task_family_id": "family-codegen", "family_unit_count": 14, ... } Defensive patterns
Strategy: validation
Validate before calling
const familyCountDrift = (report) => {
const byID = new Map(report.task_families.map((f) => [f.id, f]));
return report.cases.filter((c) =>
c.cohort.family_unit_count !== new Set(byID.get(c.cohort.task_family_id)?.analysis_unit_ids ?? []).size);
}; Try / catch
try {
execFileSync(process.execPath, [VALIDATOR, reportPath, spansPath]);
} catch (err) {
if (/family unit count disagrees/.test(err.message)) {
failCI(`cohort count out of sync with family membership: ${err.message}`);
} else throw err;
} Prevention
- Edit family.analysis_unit_ids and cohort.family_unit_count in the same change.
- Deduplicate analysis_unit_ids — the validator counts unique ids only.
- When several cohorts share a family, re-check them all after touching the family.
When it happens
Trigger: Units were added to or removed from family.analysis_unit_ids without updating cohort.family_unit_count; analysis_unit_ids contains duplicate entries (the Set deduplicates, shrinking the expected count); two cases share one family but only one cohort's count was refreshed.
Common situations: Growing a fixture by appending unit ids to the family but editing counts by hand; fixtures with several cohorts over one family where one went stale; generator writing the family list and the cohort counts in different passes.
Related errors
- ${where} cohort arm ${arm.role} uses a variant of another ta
- ${where} cohort arms (${comparedUnits}) plus exclusions (${e
- ${where} support run count ${motif.support_run_count} != ${r
- ${where} cohort references a task family that is not in this
- ${where} cohort arms are not baseline then alternative
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/e2ac05eac8063828.
Report an issue: GitHub.