{"record":{"id":"fd8e562f499a53a5","repo":"JuliusBrussee/caveman","slug":"where-operations-are-not-a-contiguous-subsequen","errorCode":null,"errorMessage":"${where} operations are not a contiguous subsequence of variant ${variantID}'s signature","messagePattern":"(.+?) operations are not a contiguous subsequence of variant (.+?)'s signature","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/shared/contracts/scripts/validate-continuous-improvement.mjs","lineNumber":119,"sourceCode":"  }\n  relationshipCount += report.relationships.length;\n\n  // A motif is a structural count over variants this report carries, so every\n  // part of it must be re-derivable from those variants.\n  const variantsByID = new Map(report.workflow_variants.map((variant) => [variant.id, variant]));\n  const familyIDs = new Set(report.task_families.map((family) => family.id));\n  for (const motif of report.motifs) {\n    const where = `report fixture ${reportPaths[index]}: motif ${motif.id}`;\n    if (!familyIDs.has(motif.task_family_id)) throw new Error(`${where} references a task family that is not in this report`);\n    if (motif.support_variant_count !== motif.variant_ids.length) throw new Error(`${where} support variant count disagrees with its variant ids`);\n    let runs = 0;\n    let weighted = 0;\n    for (const variantID of motif.variant_ids) {\n      const variant = variantsByID.get(variantID);\n      if (!variant) throw new Error(`${where} references workflow variant ${variantID} that is not in this report`);\n      if (variant.task_family_id !== motif.task_family_id) throw new Error(`${where} supporting variant ${variantID} belongs to another task family`);\n      if (!contiguouslyContains(variant.signature, motif.operations)) {\n        throw new Error(`${where} operations are not a contiguous subsequence of variant ${variantID}'s signature`);\n      }\n      runs += variant.eligible_runs;\n      weighted += (motif.operations.length / variant.signature.length) * variant.eligible_runs;\n    }\n    if (motif.support_run_count !== runs) throw new Error(`${where} support run count ${motif.support_run_count} != ${runs}`);\n    const expectedShare = runs > 0 ? weighted / runs : 0;\n    if (Math.abs(motif.structural_cost_share - expectedShare) > 1e-6) {\n      throw new Error(`${where} structural_cost_share ${motif.structural_cost_share} != ${expectedShare}`);\n    }\n  }\n  motifCount += report.motifs.length;\n\n  // The causal investigation of every case: the cohort's arms, the traces it\n  // selected from them, and the backward hard-dependency slice.\n  const unitsByID = new Map(report.analysis_units.map((unit) => [unit.id, unit]));\n  const familiesByID = new Map(report.task_families.map((family) => [family.id, family]));\n  for (const item of report.cases) {\n    const where = `report fixture ${reportPaths[index]}: case ${item.id}`;","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/shared/contracts/scripts/validate-continuous-improvement.mjs#L101-L137","documentation":"Each motif's operations must occur inside every supporting variant's signature as one consecutive run — contiguouslyContains does a sliding-window, element-wise comparison over the signature. This throw means the operations appear interleaved with other steps or in a different order, so the motif is not a structural subsequence of the variant and the structural_cost_share math built on it would be invalid.","triggerScenarios":"motif.operations built by set-intersection instead of contiguous-window extraction; the variant signature changed (operations inserted, renamed, or split) while motif.operations were kept from an older extraction; a hand-written motif listing steps that do exist in the variant but separated by other operations.","commonSituations":"Pipeline version changes renaming or splitting signature operations; fixtures authored against older signatures; a motif generalized across variants where it only matches some of them contiguously.","solutions":["Regenerate motif.operations from the current variant signatures using contiguous-window extraction.","If the motif genuinely spans non-adjacent steps, split it into multiple motifs whose operations are each contiguous.","Align operation naming between the extractor and the signature builder (renames are the usual culprit).","Re-run validate-continuous-improvement.mjs until the fixture passes."],"exampleFix":"// before\nsignature: ['a','x','b'] ; motif.operations: ['a','b']  // not contiguous -> throws\n// after\nsignature: ['a','b','x'] ; motif.operations: ['a','b']  // contiguous run","handlingStrategy":"validation","validationCode":"// Pre-check contiguity exactly the way the validator does.\nconst contiguouslyContains = (haystack, needle) => {\n  for (let start = 0; start + needle.length <= haystack.length; start += 1) {\n    if (needle.every((op, i) => haystack[start + i] === op)) return true;\n  }\n  return false;\n};\n\nfunction motifsAreSubsequences(report) {\n  const byID = new Map(report.workflow_variants.map((v) => [v.id, v]));\n  return report.motifs.every((m) =>\n    m.variant_ids.every((id) => contiguouslyContains(byID.get(id).signature, m.operations)),\n  );\n}\n// if (!motifsAreSubsequences(report)) regenerateMotifs(report);","typeGuard":"function isContiguousMotif(variantSignature, motifOperations) {\n  if (motifOperations.length === 0 || motifOperations.length > variantSignature.length) return false;\n  for (let start = 0; start + motifOperations.length <= variantSignature.length; start += 1) {\n    if (motifOperations.every((op, i) => variantSignature[start + i] === op)) return true;\n  }\n  return false;\n}","tryCatchPattern":"try {\n  execFileSync('node', ['validate-continuous-improvement.mjs', reportPath, spansPath]);\n} catch (e) {\n  if (/not a contiguous subsequence/.test(String(e?.stderr ?? e?.message))) {\n    // Re-extract motif.operations with a sliding window over the current signatures,\n    // or split the motif; then recompute support counts and rerun the validator.\n  }\n  throw e;\n}","preventionTips":["Derive motif.operations by sliding-window extraction from signatures, never by set intersection.","Regenerate motifs whenever signature operations are renamed, split, or reordered.","Keep extractor and signature-builder versions pinned together so operation vocabularies match."],"tags":["validation","fixtures","subsequence","data-integrity","contracts"],"backgroundTag":"fixture-validation-failed","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}