1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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}"
|