{"record":{"id":"eb78af092902257c","repo":"we-promise/sure","slug":"source-citation-must-name-the-document-it-came-fro","errorCode":null,"errorMessage":"source citation must name the document it came from","messagePattern":"source citation must name the document it came from","errorType":"exception","errorClass":"Provenance::Citation::InvalidError","httpStatus":null,"severity":"error","filePath":"app/models/provenance/citation.rb","lineNumber":59,"sourceCode":"        value = raw.to_s.strip\n\n        raise InvalidError, \"source citation is required\" if value.blank?\n        raise InvalidError, \"source citation must be #{MAX_LENGTH} characters or fewer\" if value.length > MAX_LENGTH\n\n        if value.match?(ESTIMATED_MARKER) && !value.start_with?(ESTIMATED_PREFIX)\n          raise InvalidError, \"estimated values must start with the exact prefix #{ESTIMATED_PREFIX.inspect}\"\n        end\n\n        if (suffix = value.match(GRADE_SUFFIX)) && !GRADES.include?(suffix[:grade])\n          raise InvalidError, \"reliability grade must be one of #{GRADES.join(\", \")} (got #{suffix[:grade].inspect})\"\n        end\n\n        match = value.match(FORMAT)\n        raise InvalidError, \"source citation does not match #{grammar}\" unless match\n\n        text = match[:text].to_s.strip\n        if text.length < MIN_TEXT_LENGTH\n          raise InvalidError, \"source citation must name the document it came from\"\n        end\n\n        estimated = match[:estimated].present?\n        grade = match[:grade]\n\n        if estimated && grade.blank?\n          raise InvalidError, \"estimated values must carry a reliability grade, e.g. \\\"#{ESTIMATED_PREFIX}... (grade: C)\\\"\"\n        end\n\n        new(raw: value, text: text, estimated: estimated, grade: grade)\n      end\n\n      def valid?(raw)\n        parse!(raw)\n        true\n      rescue InvalidError\n        false\n      end","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provenance/citation.rb#L41-L77","documentation":"Provenance::Citation.parse! raises InvalidError 'source citation must name the document it came from' when the text captured by FORMAT is shorter than MIN_TEXT_LENGTH (3) after stripping. This catches citations that are technically present but content-free — e.g. 'x' or a lone character — which would give a number the appearance of being sourced without identifying anything.","triggerScenarios":"Citations like 'a', 'ab', ' x ', or a value that is only the grade suffix with near-empty text ('(grade: A)' with nothing before it).","commonSituations":"Placeholder values from forms ('-', 'n/a' survives only if 3+ chars, so mostly single/double chars); agent emitting a stub; whitespace-padded short tokens.","solutions":["Write an actual document name of at least 3 characters: 'Broker statement 2025-12'.","Reject placeholder input upstream (treat 'x', '-', 'tbd' as blank).","Check length pre-parse: value.strip.length >= 3."],"exampleFix":"# before\nProvenance::Citation.parse!(\"x\")\n\n# after\nProvenance::Citation.parse!(\"Broker statement 2025-12\")","handlingStrategy":"validation","validationCode":"value.to_s.strip.length >= Provenance::Citation::MIN_TEXT_LENGTH # 3","typeGuard":"def citation_text_long_enough?(v) = v.to_s.strip.length >= Provenance::Citation::MIN_TEXT_LENGTH","tryCatchPattern":"begin\n  Provenance::Citation.parse!(source)\nrescue Provenance::Citation::InvalidError => e\n  errors.add(:source, e.message)\nend","preventionTips":["Treat 1-2 char placeholders ('x', '-') as missing input.","Require a real document name in prompts and form hints."],"tags":["provenance","validation","min-length","ruby"],"backgroundTag":"min-length-not-met","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}