summaryrefslogtreecommitdiff
path: root/scripts/check_ai_disclosure.sh
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/check_ai_disclosure.sh')
-rwxr-xr-xscripts/check_ai_disclosure.sh129
1 files changed, 129 insertions, 0 deletions
diff --git a/scripts/check_ai_disclosure.sh b/scripts/check_ai_disclosure.sh
new file mode 100755
index 00000000..5a08a44c
--- /dev/null
+++ b/scripts/check_ai_disclosure.sh
@@ -0,0 +1,129 @@
+#!/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 <product>
+# (<model>)" -- 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 <rev> -- <path>
+#
+# 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}"