#!/bin/bash ############################################################################### # Copyright (c) 2026 Eclipse ThreadX contributors # # This program and the accompanying materials are made available under the # terms of the MIT License which is available at # https://opensource.org/licenses/MIT. # # AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5). # The AI-generated portions may be considered public domain (CC0-1.0) # and not subject to the project's licence. The human contributor has # reviewed and verified that the code is correct. # # SPDX-License-Identifier: MIT and CC0-1.0 ############################################################################### # # Fail the build if a file's AI disclosure comment has drifted from the one # accepted form. # # A file that was edited with AI assistance carries exactly one line: # # Portions of this file were generated with AI assistance. # # written with the comment character that file already uses. It names no # product, no model and no version, and a file carries at most one of it, ever. # # The text is fixed for a reason that is easy to miss. An earlier convention # named the product and the model -- "Some portions generated by # ()" -- and the result was not attribution but accumulation: each tool # that touched a file failed to recognise the line another tool had left, and # appended its own. Files reached three stacked lines, and one product ended # up spelled four different ways across the tree, which made the record # unusable for the one question it was meant to answer. # # Precise attribution lives on the commit instead, where the Assisted-by # trailer is dated and attached to the diff it describes: # # scripts/count_assisted_by.sh --list -- # # A file-level flag answers WHETHER; the history answers WHO. A header line # cannot hold the second honestly, because the code it names gets rewritten # and the line stays. # # The AI Disclosure paragraph in a new file's copyright header is different and # is not checked here: it keeps its product and model version, because a file # is created once and that record cannot grow. # # This script is excluded from its own scan. It has to spell the rejected # forms in order to look for them. set -euo pipefail readonly ROOT="$(cd "$(dirname "$(realpath "$0")")/.." && pwd)" readonly SELF='scripts/check_ai_disclosure.sh' readonly FIXED='Portions of this file were generated with AI assistance.' cd "${ROOT}" # Tracked files only. A build tree is not this repository's text to police, # and scanning one would make the check depend on whether somebody had built. mapfile -d '' -t FILES < <(git ls-files -z | grep -zZv "^${SELF}$") status=0 report() { printf '%s\n\n' "$1" >&2 printf '%s\n\n' "$2" >&2 status=1 } # 1. The superseded per-edit form, which names a product and a model. hits="$(grep -nI 'Some portions generated by' -- "${FILES[@]}" 2>/dev/null || true)" if [ -n "${hits}" ]; then report "AI disclosure check FAILED: superseded per-edit form. Replace each of these with the fixed line, keeping the file's comment character: ${FIXED}" "${hits}" fi # 2. A doubled comment marker, such as '; //' or '@ //'. Assembly dialects # differ -- armasm and IAR use ';', GNU as uses '@' or '//' -- and writing # both is a symptom of a tool guessing rather than reading the file. hits="$(grep -nIE '(//|[;@#])[[:space:]]*//[[:space:]]*Portions of this file were generated' \ -- "${FILES[@]}" 2>/dev/null || true)" if [ -n "${hits}" ]; then report "AI disclosure check FAILED: doubled comment marker. Use the single comment character the rest of the file uses." "${hits}" fi # 3. More than one disclosure line in a file. This is the failure the fixed # text exists to prevent, so it is worth catching directly rather than # inferring it from the form. hits="$(grep -cIF "${FIXED}" -- "${FILES[@]}" 2>/dev/null | awk -F: '$NF > 1' || true)" if [ -n "${hits}" ]; then report "AI disclosure check FAILED: more than one disclosure line. A file carries at most one, ever. Keep the first and delete the rest; the commit trailer, not the header, records which agents have touched the file." "${hits}" fi # 4. A near miss. A line that is clearly meant to be the disclosure but is # not spelled exactly right defeats every deduplication that follows it. # # Markdown is exempt from this one check. The contribution guide has to # quote the accepted text and explain when it applies, so matching the bare # phrase there reports prose rather than drift. A superseded form in a # Markdown file is still caught by check 1. mapfile -d '' -t SOURCES < <(printf '%s\0' "${FILES[@]}" | grep -zZv '\.md$' || true) hits='' if [ "${#SOURCES[@]}" -gt 0 ]; then hits="$(grep -nIF 'AI assistance' -- "${SOURCES[@]}" 2>/dev/null \ | grep -vF "${FIXED}" || true)" fi if [ -n "${hits}" ]; then report "AI disclosure check FAILED: the text is not spelled exactly. The accepted text, character for character, is: ${FIXED}" "${hits}" fi if [ "${status}" -eq 0 ]; then echo "AI disclosure check passed." fi exit "${status}"