diff --git a/README.md b/README.md
index 16dd50a..05d782b 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@ Note OED is transitioning to mostly public design documents. The private DevDocs
- [enhancementToGithubAction.md](./githubAction/enhancementToGithubAction.md): securing GitHub action information.
- [timescaleDB/timescaleDB.md](./timescaleDB/timescaleDB.md): Information on efforts to investigate TimescaleDB usage in OED.
- [infisicalIntegration/infisicalIntegration.md](./infisicalIntegration/infisicalIntegration.md): Instructions for how to utilize Infisical secrets management to store database passwords.
+- [aiIntegration/aiWorkflowCompliance.md](./aiIntegration/aiWorkflowCompliance.md): Documents OED AI usage guidance, issue-tracking recommendations, and the pull request compliance prototype.
## Information
diff --git a/aiIntegration/aiPolicyComparison.md b/aiIntegration/aiPolicyComparison.md
new file mode 100644
index 0000000..5ecb0e0
--- /dev/null
+++ b/aiIntegration/aiPolicyComparison.md
@@ -0,0 +1,166 @@
+# OED AI Policy Comparison
+
+Converted from the original three-tab workbook into a single Markdown document.
+Google Sheets: https://docs.google.com/spreadsheets/d/1XF7leNgV0ftwLTxSF8yHJUb8IIJxx806YyQnTnyd3NE/edit?usp=sharing
+
+## Comparison Table
+
+Open Source AI Policies Compared for OED
Data and policy information current as of August 2026, based on publicly available information from the cited project websites and repositories.
+
+| Project Name | Policy Link | AI Allowed
(source table) | Disclosure Required
(source table) | Copyright Stmt
(source table) | Human in Loop
(source table) | Stance | Source Notes (Source table) | Relevance to OED/ Student | Policy Relevance to OED | Contributor
Responsible? | Human Review
Required? | Comments |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| Adwaita | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Alacritty | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Apache Airflow | | Yes | Yes | Yes | Yes | Cautious | Add Copilot code review instructions to catch AI-slop PRs | Low | Low | Yes | Yes | |
+| Apache CouchDB | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Apache DataFusion | | Yes | No | No | Yes | Cautious | Better ways to contribute than an AI dump | Low | Med | Yes | Yes | |
+| Apache Kvrocks | | Yes | Yes | Yes | Yes | Cautious | | Low | Med | Yes | Yes | AI allowed, contributor accountable and must verify the output. |
+| Apache PouchDB | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Apache Software Foundation (ASF) | | Yes | Yes | Yes | - | Cautious | Applies to all ASF projects; enforcement decentralized. Requires 'Generated-by:' in commit message. | Med | High | Yes | Not explicitly stated. | Important because it gives broad open-source legal guidance on generative tooling, disclosure, and licensing. |
+| Arrow | | Yes | Yes | Yes | Yes | Cautious | | Low | Low | Yes | Yes | Strong model for disclosure, understanding generated code, and owning/debugging AI-assisted work. |
+| Asahi Linux | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| attrs | | Yes | ? | Yes | Yes | Cautious | No LLM bots in Co-authored-by:s. | Low | Low | Yes | Yes | No explicit general disclosure requirement. The policy allows LLM-assisted work only if a human owns the copyright, understands the code, and takes full responsibility. No LLM bots in Co-authored-by: and says LLM-generated summaries/review comments must be fact-checked. Shared Policy with Pip |
+| CapyPDF | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| CC Open Source | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| CCExtractor | | Yes | Yes | Yes | Yes | Cautious | Mark PRs as AI-generated/AI-assisted/No AI used | High | High | Yes | Yes | Sole Responsible Author, it has a student/GSoC context and clear PR labeling for AI-generated, AI-assisted, or no-AI work. |
+| cilium | | Yes | Yes | Yes | Yes | Restrictive | DCO signoff required for all contributions including AI-generated ones. | Low | Med | Yes | Yes | Signoff required |
+| Clojure | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| CloudNativePG | | Yes | Yes | Yes | Yes | Cautious | | Low | Low | Yes | Yes | |
+| conda | | Yes | No | Yes | Yes | Cautious | | Low | Low | Yes | Yes | |
+| CPython | | Yes | No | No | Yes | Cautious | Disclosure of AI tools appreciated, not required. | Med | High | Yes | Yes | Strong emphasis on focused changes, existing coding patterns, tests. Disclosure is only encouraged, not required |
+| CuPy | | Yes | No | No | Yes | Cautious | | Low | Low | Yes | Yes | Allows AI but has less detailed policy language. Useful mainly for warning against AI spam/testbed behavior. |
+| curl | | Yes | Yes | Yes | Yes | Cautious | A contribution should be worth more to the project than the time it takes to review it. | Low | Low | Yes | Yes | AI-assisted work must be useful enough to justify review time. Very relevant for OED maintainers. |
+| DataJourneyHQ | | Yes | Yes | No | - | Cautious | | Low | Low | Yes | Not explicitly stated. | |
+| Django | | Yes | Yes | No | Yes | Cautious | PR template includes AI disclosure; no automated AI reviews. | Med | Low | Yes | Yes | It requires AI disclosure, manual verification, architecture alignment, tests, docs, full checks, and bans automated AI reviews on submitted PRs. |
+| dlt | | Yes | No | No | No | Pro | CONTRIBUTING_AI.md ('We strongly encourage using AI coding agents') is in private repo. Two-tier system: reviewed AI output = contributor's own; unreviewed = must carry explicit disclaimer. 'Code not fully reviewed by a human will never get merged.' Source: blog.probabl.ai interview with dlt CTO, May 2026. | Low | Low | Not explicitly stated. | Not explicitly stated. | |
+| do | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Drupal | | Yes | Yes | Yes | Yes | Cautious | | Med | High | Yes | Yes | Strong responsibility language, copyright and licensing rules, disclosure thresholds, examples of bad AI use, enforcement, and a GSoC/student-learning note. |
+| Dune 3D | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| EasyBuild | | Yes | Yes | No | Yes | Cautious | Requires declaration of specific AI models/tools used. | Low | Low | Not explicitly stated. | Yes | |
+| Elastic (GNOME) | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Elementary OS | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| FastAPI | | Yes | No | No | Yes | Cautious | If human effort in PR is less than effort to review it, don't submit. | Low | Med | Yes | Yes | Do not submit AI automated PRs if the human effort is less than the review effort. |
+| Firefox | | Yes | No | No | Yes | Cautious | | Med | High | Yes | Yes | It explicitly says humans remain accountable, must understand and self-review the code, must protect sensitive data, and should not use AI to bypass learning on “Good First” / “Good Next” bugs. The learning-focused language is useful for OED. |
+| Flutter | | Yes | No | No | Yes | Cautious | | Low | Med | Yes | Yes | Review all AI code, understand and discuss it, verify AI-generated PR text, close low-quality AI PRs, and watch for AI-generated files or mismatched descriptions. |
+| Forgejo | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| GDAL | | Yes | Yes | Yes | Yes | Restrictive | | Low | Med | Yes | Yes | Human must be primary author, must understand all contributions, disclosure required, contributor responsible, agents banned, enforcement/closure/ban process included. Maintainer-burden and accountability language. |
+| Gedit | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Gentoo Linux | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Ghostty | | Yes | Yes | No | Yes | Cautious | Our reason for the strict AI policy is not due to an anti-AI stance, but instead due to the number of highly unqualified people using AI. | Low | Low | Yes | Yes | |
+| GIMP | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Gitea | | Yes | Yes | No | Yes | Cautious | | Low | Med | Yes | Yes | AI allowed with disclosure, close review, manual testing, contributors must understand/defend/revise their work, and maintainers may close low-quality or undisclosed AI-assisted work. |
+| Glasgow Interface Explorer | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| GNOME Extensions | | Yes* | - | - | Yes | Restrictive | Extension developers should be able to justify and explain the code they submit. | Low | Med | Yes | Yes | It allows AI as learning aid/autocomplete while rejecting AI-generated submissions with signs like unnecessary code, inconsistent style, imaginary API use, and LLM-prompt comments. |
+| GNOME Loupe | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| GNU Binutils | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| goose | | Yes | No | No | Yes | Pro | AGENTS.md file provided. | Low | Med | Yes | Yes | It has practical AI workflow guidance, testing expectations, security cautions, and “you are accountable” language. |
+| GoToSocial | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Homebrew | | Yes | Yes | No | Yes | Cautious | Disclose model/tool; only one AI-assisted PR open at a time. | Low | Med | Yes | Yes | Only one AI-assisted PR open at a time |
+| Icechunk | | Yes | No | No | Yes | Cautious | | Low | High | Yes | Yes | It directly addresses maintainer burden, large AI-assisted PRs, closing impractical PRs, and domain-specific documentation errors. |
+| IREE | | Yes | Yes | Yes | Yes | Cautious | Use 'Assisted-by:' or 'Co-authored-by:' | Low | Med | Yes | Yes | AI-assisted PRs, issues, and design proposals must be reviewed and understood; substantial AI content should be labeled with Assisted-by or Co-authored-by; contributors are responsible for license rights and avoiding regenerated copyrighted material. |
+| Jellyfin | | Yes | No | No | Yes | Cautious | | Med | Low | Yes | Yes | Useful for maintainer burden language. No raw LLM communication, no vibe coding. |
+| Joomla | | Yes | Yes | Yes | Yes | Cautious | | Low | Med | Yes | Yes | AI allowed, contributor fully responsible, GPL compatibility required, AI PRs must be labeled, self-review is mandatory, vibe coding is banned, and maintainers may close repeat/problem PRs. |
+| Kornia | | Yes | Yes | No | Yes | Cautious | Use AI-generated/AI-assisted/No AI labels. | Low | Med | Yes | Yes | AI usage disclosure labels, and closure for false disclosure or inability to explain. |
+| Krita | | No | - | - | - | Rejecting | AI moratorium; to be reviewed October 2026. | Low | Low | N/A | N/A | |
+| Kubernetes | | Yes | Yes | No | Yes | Cautious | 'Assisted-by:', 'Co-developed-by:' not allowed. | Low | Med | Yes | Yes | AI use must be disclosed, AI cannot be listed as co-author or commit trailer, large AI-generated PRs and AI-generated commit messages are not allowed. PRs can be closed if the author cannot explain the changes. |
+| libmanette | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| LinkML | | Yes | Yes* | No | Yes | Cautious | No co-authorship with AI tools; disclose when you don't understand the proposed changes. | Low | Med | Yes | Yes | |
+| Linux Kernel | | Yes | Yes | Yes | Yes | Cautious | 'Assisted-by:' required; DCO required. | Low | Low | Yes | Yes | |
+| Linux man-pages | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| LLVM | | Yes | Yes | Yes | Yes | Cautious | Using AI tools to fix 'good first issues' is forbidden. | High | High | Yes | Yes | No AI fixing “good first issues.” maintainer-burden language, copyright section, and violation handling. |
+| LÖVE | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Matplotlib | | Yes | Yes | No | Yes | Cautious | 'AI Disclosure' section in PR template must be filled. | Med | Low | Yes | Yes | |
+| MDAnalysis | | Yes* | Yes | No | Yes | Restrictive | No 'substantial' contributions generated by AI tools. | Low | Low | Yes | Yes | |
+| Mesa | | Yes | Yes | Yes | Yes | Cautious | Use 'Assisted-by:' or 'Generated-by:'; do not use 'Co-authored-by:' with AI tools. | Low | Low | Yes | Yes | Contributor is responsible regardless of AI use, 'Co-authored-by:' is not allowed with AI tools, autonomous tools banned, explicit oversight required, disclosure required. |
+| MicroPython | | Yes | No | No | Yes | Cautious | | Low | Low | Yes | Yes | |
+| Molecular Nodes | | Yes* | Yes | No | Yes | Restrictive | No 'substantial' contributions generated by AI tools. | Low | Med | Yes | Yes | AI use must be declared, and maintainers may close PRs that appear poorly understood. |
+| napari | | Yes | Yes | No | Yes | Cautious | | Low | Low | Yes | Yes | Shared policy between NumPy, SciPy and NumPy. |
+| NetBSD | | Yes* | Yes | No | No | Restrictive | AI-assisted contributions require explicit core approval. | Low | Low | Yes | Yes | |
+| Cataclysm: Dark Days Ahead | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| NumPy | | Yes | Yes | Yes | Yes | Cautious | | Med | Low | Yes | Yes | Shared policy between NumPy, SciPy and NumPy. |
+| nvim-tree | | Yes* | Yes | No | Yes | Restrictive | AI generated code is discouraged as this doesn't match nvim-tree values. | Low | Low | Yes | Yes | |
+| OCaml | | Yes | Yes | Yes | Yes | Cautious | | Low | Low | Yes | Yes | |
+| Open edX | | Yes* | Yes | No | Yes | Restrictive | Only specific tools with a 'sufficient reputation for proper training' are allowed. | High | Med | Yes | Yes | It is education-related, allows AI with tool restrictions, specifies which tools contributors are allowed to use, requires disclosure, emphasizes understanding and transparency, covers PRs/issues/reviewers, gives good/bad workflow examples, and lets reviewers close AI-driven low-quality review loops. |
+| OpenInfra | | Yes | Yes | Yes | Yes | Cautious | Required use of 'Assisted-by:' or 'Generated-by:'. Open Source AI models recommended. | Low | High | Yes | Yes | requires Assisted-By: / Generated-By:, license compatibility checks, reviewer checklist. AI-assisted contributions are reviewed more carefully |
+| OpenJDK | | No | - | - | - | Rejecting | Interim policy. | Low | Low | N/A | N/A | |
+| Oxide | | Yes | - | No | Yes | Cautious | Comprehensive, extensive policy. | Med | High | Yes | Yes | Supports understanding rather than replace it. Useful for its warning against relying on AI as a substitute for human code review or comprehension. Uses MPL 2.0. |
+| Pandas | | Yes | Yes | No | Yes | Cautious | | Med | Low | Yes | Yes | |
+| pgwatch | | Yes | Yes | No | Yes | Cautious | Must mention specific tools used. | Low | Low | Yes | Yes | |
+| pip | | Yes | No | Yes | Yes | Cautious | No 'Co-authored-by:' with AI tools. | Low | Med | Yes | Yes | Based on the policy of the attrs project. |
+| pip-tools | | Yes | No | No | Yes | Cautious | | Low | Low | Yes | Yes | |
+| Polars | | Yes | Yes | No | Yes | Cautious | | Low | Low | Yes | Yes | |
+| postmarketOS | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Processing/p5.js | | Yes | Yes | No | Yes | Cautious | Disclose specific tools used. | High | High | Yes | Yes | It is education adn community oriented, allows AI only assistively. |
+| PyTorch | | Yes | No | No | Yes | Cautious | | Low | Low | Yes | Yes | |
+| PyVista | | Yes | No | No | Yes | Cautious | Follows CPython's policy. | Low | Low | Yes | Yes | Based on CPython policy. |
+| QEMU | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| QGIS | | Yes | Yes | Yes | Yes | Cautious | Use 'Assisted-by:' or 'Generated-by:' labels. | Low | Med | Yes | Yes | New-contributor learning, maintainer-burden language, AI-agent limits, enforcement, and copyright responsibility. |
+| qutip | | Yes | Yes | Yes | Yes | Cautious | No AI contributions to 'good first issues'. | Low | Low | Yes | Yes | |
+| Redox OS | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| ruff / uv / ty (astral-sh) | | Yes | No | No | Yes | Cautious | | Low | Low | Yes | Yes | |
+| SciActive | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| scikit-image | | Yes | Yes | No | Yes | Cautious | | Low | Med | Yes | Yes | Strong policy, contributors must review code line-by-line. |
+| scikit-learn | | Yes | Yes | No | Yes | Cautious | | Med | Med | Yes | Yes | no automated AI PRs/issues, contributors must review/test/explain AI changes, and AI use must be stated in the PR. |
+| SciPy | | Yes | Yes | Yes | Yes | Cautious | | Med | High | Yes | Yes | Required AI disclosure, rejection of AI slop, copyright responsibility, no AI speaking for contributors, and no AI-agent PRs. |
+| SDL | | No | - | - | - | Rejecting | AGENTS.md file bans AI contributions. | Low | Low | N/A | N/A | |
+| SearXNG | | Yes | Yes | No | Yes | Cautious | | Low | Low | Yes | Yes | |
+| Servo | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| STAC | | Yes | Yes | No | Yes | Cautious | Use 'Assisted-by:' and similar labels. | Low | Low | Yes | Yes | |
+| stb | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| SymPy | | Yes | Yes | Yes | Yes | Cautious | | Med | Low | Yes | Yes | Shared policy between NumPy, SciPy and NumPy |
+| Telegraf | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| typescript-eslint | | Yes | No | No | Yes | Cautious | | Low | Low | Yes | Yes | |
+| Unison | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| University of Alaska Anchorage (GSoC) | | Yes* | No | No | Yes | Restrictive | No vibe code. | High | High | Yes | Yes | It is explicitly student/GSoC-focused. It allows limited AI help but rejects vibe coding, AI slop, untested code, bloated AI communication, and AI-heavy research/proposals. Very useful for OED’s educational mission. |
+| Vim Classic | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Wagtail | | Yes | Yes | No | Yes | Cautious | | Med | Low | Yes | Yes | |
+| Wikipedia (German/English) | | No* | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| WP1 | | Yes | No | No | Yes | Cautious | | Low | Low | Yes | Yes | |
+| XScreenSaver | | No | - | - | - | Rejecting | | Low | Low | N/A | N/A | |
+| Zig | | No | - | - | - | Rejecting | Contributor Poker and Zig's AI Ban — rationale for ban documented separately. | Low | Low | N/A | N/A | |
+| Zulip | | Yes | No | Yes | Yes | Cautious | | Low | High | Yes | Yes | Contributors must understand/explain/test changes, avoid vibe coding, avoid AI slop, communicate clearly, and maintainers may close AI-generated PRs that waste review time. GSoC. |
+
+## High-Med Relevant Policies
+
+NOTE: Filtered list of AI policies with Medium/High relevance to OED’s student-focused context and Medium/High usefulness for OED policy design.
Data and policy information current as of August 2026, based on publicly available information from the cited project websites and repositories.
+
+| Project Name | Policy Link | AI Allowed
(source table) | Disclosure Required
(source table) | Copyright Stmt
(source table) | Human in Loop
(source table) | Stance | Source Notes (Source table) | Relevance to OED/ Student | Policy Relevance to OED | Contributor
Responsible? | Human Review
Required? | Comments |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| Apache Software Foundation (ASF) | | Yes | Yes | Yes | - | Cautious | Applies to all ASF projects; enforcement decentralized. Requires 'Generated-by:' in commit message. | Med | High | Yes | Not explicitly stated. | Important because it gives broad open-source legal guidance on generative tooling, disclosure, and licensing. |
+| CCExtractor | | Yes | Yes | Yes | Yes | Cautious | Mark PRs as AI-generated/AI-assisted/No AI used | High | High | Yes | Yes | Sole Responsible Author, it has a student/GSoC context and clear PR labeling for AI-generated, AI-assisted, or no-AI work. |
+| CPython | | Yes | No | No | Yes | Cautious | Disclosure of AI tools appreciated, not required. | Med | High | Yes | Yes | Strong emphasis on focused changes, existing coding patterns, tests. Disclosure is only encouraged, not required |
+| Drupal | | Yes | Yes | Yes | Yes | Cautious | | Med | High | Yes | Yes | Strong responsibility language, copyright and licensing rules, disclosure thresholds, examples of bad AI use, enforcement, and a GSoC/student-learning note. |
+| Firefox | | Yes | No | No | Yes | Cautious | | Med | High | Yes | Yes | It explicitly says humans remain accountable, must understand and self-review the code, must protect sensitive data, and should not use AI to bypass learning on “Good First” / “Good Next” bugs. The learning-focused language is useful for OED. |
+| LLVM | | Yes | Yes | Yes | Yes | Cautious | Using AI tools to fix 'good first issues' is forbidden. | High | High | Yes | Yes | No AI fixing “good first issues.” maintainer-burden language, copyright section, and violation handling. |
+| Open edX | | Yes* | Yes | No | Yes | Restrictive | Only specific tools with a 'sufficient reputation for proper training' are allowed. | High | Med | Yes | Yes | It is education-related, allows AI with tool restrictions, specifies which tools contributors are allowed to use, requires disclosure, emphasizes understanding and transparency, covers PRs/issues/reviewers, gives good/bad workflow examples, and lets reviewers close AI-driven low-quality review loops. |
+| Oxide | | Yes | - | No | Yes | Cautious | Comprehensive, extensive policy. | Med | High | Yes | Yes | Supports understanding rather than replace it. Useful for its warning against relying on AI as a substitute for human code review or comprehension. Uses MPL 2.0. |
+| Processing/p5.js | | Yes | Yes | No | Yes | Cautious | Disclose specific tools used. | High | High | Yes | Yes | It is education adn community oriented, allows AI only assistively. |
+| scikit-learn | | Yes | Yes | No | Yes | Cautious | | Med | Med | Yes | Yes | no automated AI PRs/issues, contributors must review/test/explain AI changes, and AI use must be stated in the PR. |
+| SciPy | | Yes | Yes | Yes | Yes | Cautious | | Med | High | Yes | Yes | Required AI disclosure, rejection of AI slop, copyright responsibility, no AI speaking for contributors, and no AI-agent PRs. |
+| University of Alaska Anchorage (GSoC) | | Yes* | No | No | Yes | Restrictive | No vibe code. | High | High | Yes | Yes | It is explicitly student/GSoC-focused. It allows limited AI help but rejects vibe coding, AI slop, untested code, bloated AI communication, and AI-heavy research/proposals. Very useful for OED’s educational mission. |
+
+## Findings & Synthesis
+
+Research Findings - OED AI Policy Comparison
Data and policy information current as of August 2026, based on publicly available information from the cited project websites and repositories.
+
+### OVERALL POLICY LANDSCAPE
+
+| Finding | Count | Total reviewed | Percentage | Scope |
+| --- | --- | --- | --- | --- |
+| Projects allowing AI-assisted contributions | 71 | 110 | 64.5% | All projects |
+| Projects rejecting AI-assisted contributions | 39 | 110 | 35.5% | All projects |
+
+### CONTRIBUTOR ACCOUNTABILITY & HUMAN REVIEW & DISCLOSURE
+
+| Finding | Count | Total reviewed | Percentage | Scope |
+| --- | --- | --- | --- | --- |
+| Contributor responsibility required | 69 | 71 | 97.2% | All AI Allowing projects |
+| Human review required | 68 | 71 | 95.8% | All AI Allowing projects |
+| Disclosure required | 47 | 71 | 66.2% | All AI Allowing projects |
+
+### Relevance
+
+| Finding | Count | Total reviewed | Percentage | Scope |
+| --- | --- | --- | --- | --- |
+| Relevent projects require human review | 11 | 12 | 92.0% | High-Med relevance |
+| Relevent projects require contributor accountability | 12 | 12 | 100.0% | High-Med relevance |
\ No newline at end of file
diff --git a/aiIntegration/aiPolicyDraft.md b/aiIntegration/aiPolicyDraft.md
new file mode 100644
index 0000000..1786542
--- /dev/null
+++ b/aiIntegration/aiPolicyDraft.md
@@ -0,0 +1,75 @@
+**Open Energy Dashboard**
+
+# AI Usage Policy for Contributors - Draft
+
+## Enforcement
+
+If OED determines that a contribution did not follow this policy, OED will attempt to resolve the issue with the contributor. If that isn't possible, OED may take remedial action, which may include public disclosure as part of the project's permanent record. Contributors have the right to present their case and appeal a decision, using the same reporting channel described in [OED's Code of Conduct](https://github.com/OpenEnergyDashboard/OED/blob/development/CODE_OF_CONDUCT.md).
+
+## Purpose
+
+Open Energy Dashboard is a student-focused open-source project. Contributors may use AI tools as support, but AI tools do not replace human judgment, learning, testing, or responsibility. AI-assisted work must still follow OED’s normal contribution, licensing, documentation, testing, and review standards.
+
+This policy applies to AI-assisted code, documentation, tests, pull request text, issue comments, translations, and review comments submitted to OED.
+
+## Principle
+
+AI tools may assist a contribution as long as the human contributor fully understands, tests, and takes responsibility for what they submit, consistent with OED's standard contribution process.
+
+AI output is only a starting point. The contributor must decide whether the output is correct, useful, and appropriate for OED.
+
+## Reputable Tool Use
+
+Contributors must use AI tools in good faith, making a reasonable effort to use ones that are reputable and unlikely to introduce unsafe or malicious output.
+
+## Required Disclosure
+
+Contributors must disclose AI use. The disclosure should include the AI tool used, how it was used, what part of the contribution it affected, and any other information the project should be aware of. The majority must come from the human contributor; if it does not, the contributor must disclose this and explain why. AI tools must not be listed as authors or co-authors.
+
+## Human Responsibility
+
+The human contributor(s) are fully responsible for all submitted work, including AI-assisted portions. AI tools may assist, but they are not responsible parties.
+
+If multiple people contributed to a pull request, each human contributor remains responsible for the parts they contributed and together those parts must account for the entire contribution, no part of a submission may lack an accountable contributor. The pull request submitter is responsible for making sure AI use is disclosed accurately for the contribution as a whole, and that at least one contributor has taken responsibility for all parts of the submitted code.
+
+**Before submitting, the contributor must confirm that they:**
+
+- reviewed and understood all AI-assisted output
+- can explain the submitted changes without relying on AI
+- confirmed that any packages, libraries, or dependencies referenced in AI-assisted output actually exist and are correct, and reviewed the change for security implications
+- checked that the output is accurate and not fabricated
+- confirmed that the contribution follows OED’s CLA and other OED project requirements, including having the rights needed to submit AI-assisted work under the CLA
+
+Using AI does not reduce contributor responsibility.
+
+## Data and Privacy Protection
+Contributors must not enter secrets, credentials, private keys, access tokens, private user data, non-public security information, private deployment details, or confidential OED information into AI tools. If in doubt about whether something is safe to share, ask a maintainer first.
+
+## OED Workflow Requirements
+AI-assisted contributions must follow the same contribution, testing, documentation, licensing, technical, and review [requirements](https://github.com/OpenEnergyDashboard/OED/blob/development/CONTRIBUTING.md) as all other OED contributions. AI tools may assist contributors or reviewers, but they must not replace OED's required human review.
+
+## Higher-Risk Changes
+Any AI-assisted change that a contributor or OED considers higher-risk may require additional explanation, information and review before accepting.
+
+## Student Learning
+For issues labeled `i-good-first-issue`, AI use is limited to learning support, such as understanding concepts, error messages, documentation, or project context. AI must not generate or substantially implement the solution. Contributors must be able to explain the problem, the files changed, their solution approach, and how the work was verified.
+
+Students are welcome to do one \`i-good-first-issue\` but need to consult with the project before doing an additional one, to verify appropriate work.
+
+## Unacceptable uses
+
+**Do not submit:**
+
+- fabricated commands, test results, citations, screenshots, or explanations
+- large AI-generated rewrites that are not clearly scoped to an issue
+- fully automated or fully autonomous AI actions (pull requests, code, issues, or review replies) submitting without meaningful human involvement
+
+## Maintainer Review
+
+Maintainers may ask how AI was used, request testing evidence, ask the contributor to explain their work, request revisions, or close AI-assisted work that is inaccurate, untested, unsupported, overly broad, or not understood by the contributor. Contributors should not submit AI-assisted work with these problems, as doing so creates an unreasonable review burden for maintainers.
+
+## Other resources
+
+Link to PR template
+
+Link to [aiUsageGuidance.md]
\ No newline at end of file
diff --git a/aiIntegration/aiUsageGuidance.md b/aiIntegration/aiUsageGuidance.md
new file mode 100644
index 0000000..c6f6bff
--- /dev/null
+++ b/aiIntegration/aiUsageGuidance.md
@@ -0,0 +1,54 @@
+# AI Usage Guidance for Contributors
+
+*This document is informational and does not establish binding requirements. See the AI Usage Policy Draft for the governing rules.*
+
+## Principle
+
+AI tools are most appropriate when they support a contributor’s own understanding, judgment, and review. They are not appropriate when they replace the contributor’s work or produce a contribution that the contributor has not reviewed, tested where applicable, or cannot explain it yourself.
+
+Examples of appropriate supportive use include:
+
+- Understanding OED code or documentation
+- Brainstorming possible approaches
+- Debugging errors or explaining error messages
+- Drafting documentation or improving wording
+- Suggesting tests or edge cases
+- Summarizing issues or pull requests
+- Translation support
+- Self-review before submitting work
+
+## AI Tools
+
+As of July 2026, the following tools were reviewed against OED code and issues and found generally reputable, with reliable output quality:
+
+- GitHub Copilot / Microsoft Copilot
+- Anthropic Claude
+- OpenAI, including ChatGPT and OpenAI API models
+- Amazon Web Services AI tools, including Kiro
+- Google AI tools, including Gemini
+
+This list will change as tools evolve — untested tools aren't excluded; any tool used in good faith, per Reputable Tool Use, is acceptable.
+
+## Higher-Risk Changes
+
+Areas that often warrant extra care include:
+
+- Database migrations
+- Authentication
+- Authorization
+- Security
+- Unit conversions
+- Readings logic
+- Graphing calculations
+- Docker
+- CI
+- Dependencies
+- Production configuration
+
+This list is illustrative, not exhaustive — other areas may also require extra care.
+
+## Student Learning
+
+**Example of assistive use:** asking AI to explain an error message or an unfamiliar concept while you work through a good-first-issue yourself.
+
+**Example of generative use (not permitted here):** asking AI to write the fix or implementation for you.
diff --git a/aiIntegration/aiWorkflowCompliance.md b/aiIntegration/aiWorkflowCompliance.md
new file mode 100644
index 0000000..6b03050
--- /dev/null
+++ b/aiIntegration/aiWorkflowCompliance.md
@@ -0,0 +1,289 @@
+# AI Workflow and Compliance
+
+
+## Overview
+
+
+This project focuses on creating AI usage guidelines, issue-tracking workflow recommendations, and a pull request compliance prototype for OpenEnergyDashboard. The issue-tracking prototype explores how existing AI tools can support contributors, while the compliance prototype uses GitHub Actions to validate AI disclosure and Contributor License Agreement requirements.
+
+
+The separate OED AI Usage Policy for Contributors defines the contributor-facing requirements for AI use, while this design document explains the workflows, prototypes, testing, and design decisions developed during the project.
+
+
+## Introduction
+
+
+OpenEnergyDashboard is an open-source project that helps users monitor and understand resource usage data, including energy and other measurable resources. Since OED is maintained by human maintainers and supported by developers and contributors, clear issue tracking is important for organizing bugs, feature requests, documentation tasks, and beginner-friendly contributions.
+
+
+AI can help support this process by summarizing GitHub issues, recommending next steps, and generating risk-based questions.
+
+
+## Students and OED
+
+
+Students are an important part of the OED project because they may contribute to development, testing, documentation, and issue resolution through coursework or project-based learning. Because students may have different levels of experience with open-source development, the project considered how AI-assisted tools could make issues easier to understand and support contributors with different experience levels.
+
+
+## AI Use Cases Considered
+
+
+Although this document focuses mainly on issue tracking, AI may also be useful in other parts of the OED workflow. Possible use cases include summarizing GitHub issues, generating clarifying questions for maintainers, assisting with pull request summaries, helping reviewers identify risky code changes, and checking whether a contribution may need closer human review. Issue tracking provides a lower-risk area for testing AI assistance before expanding to higher-risk areas such as pull request analysis. Other AI use cases can be considered later after the team evaluates whether AI-generated issue summaries are accurate and useful.
+
+
+These use cases should be introduced carefully. OED should begin with low-risk uses, such as issue summaries and internal review support, before considering more automated or public-facing AI features. Any AI-assisted workflow should include human review before it affects contributors, maintainers, or project decisions.
+
+
+## Reviewing GitHub Issues
+
+
+The AI-assisted issue tracking workflow begins with selecting existing issues from the OED GitHub repository [4]. Each issue is reviewed by an AI tool to identify the main problem, the type of task, and any missing information. The AI may produce a short summary of the issue, suggest possible next steps, and generate risk-based questions for contributors. The purpose of this step is to make issues easier to understand, especially for new contributors. The AI does not change the issue directly. Instead, it provides recommendations that a human maintainer can review before making any decisions.
+
+
+## Issue-Tracking Prototype Plan
+
+
+The issue-tracking prototype was tested using a small sample of existing OED GitHub issues. The team selected several issues from the OED repository, and for each issue, the AI generated a short issue summary, suggested next steps, and risk-based questions.
+
+
+After the AI output was generated, the team reviewed the results manually. Each output was checked for accuracy, usefulness, clarity, and whether it correctly reflected the original GitHub issue. The team also identified AI mistakes such as incorrect assumptions, vague recommendations, missing technical details, or suggestions that did not match OED’s project goals.
+
+
+The results of this review helped inform the development of the separate OED AI Usage Policy for Contributors.
+
+
+## Issue-Tracking Prototype Testing Results
+
+
+The prototype was tested using three user roles: student contributor, developer contributor, and maintainer. The goal was to determine whether the AI could adjust its issue-tracking output based on the user’s role and experience level. In all three tests, the AI recognized the selected role and followed the expected structure by providing an issue summary, suggested next steps, and risk-based questions.
+
+
+However, the responses remained very similar across all three roles and did not clearly adjust the language or level of detail based on experience. This indicates that the prototype can recognize a user role and follow the required output structure, but it does not yet provide sufficiently different guidance for each type of contributor. Stronger role-based prompts or more specific instructions may be needed to improve this behavior.
+
+
+### AI Evaluation Checklist
+
+
+The prototype outputs were reviewed using the following checklist:
+
+
+1. Did the AI recognize the user role correctly?
+2. Did the AI adjust its language and level of detail based on the user role?
+3. Did the AI keep the human-in-the-loop rule?
+
+
+## Pull Request Compliance Prototype
+
+
+As a practical extension of the OED AI Usage Policy for Contributors, the team created a pull request compliance prototype. The prototype includes automated GitHub Actions that support contributor responsibility and project policy requirements.
+
+
+The prototype includes:
+
+
+- AI disclosure validation, which checks whether contributors completed the required AI Assistance Disclosure section.
+- Signed CLA verification, which checks the pull request author and any listed contributor GitHub usernames against OED’s CLA response records.
+- CLA date verification, which checks whether the recorded CLA submission satisfies the required date condition.
+
+
+These checks provide contributors with clear feedback, but they do not replace human maintainer review or determine whether the information submitted by a contributor is truthful.
+
+
+### Prototype Workflow
+
+
+When the configured pull request events occur, GitHub Actions starts the relevant compliance workflows.
+
+
+1. The AI disclosure workflow reads the pull request description.
+2. The AI disclosure script checks the disclosure options, required fields, and human-review acknowledgments.
+3. The signed CLA workflow reads the pull request author’s GitHub username and any additional contributor usernames listed in the pull request description.
+4. The CLA script compares those usernames with the CLA response records stored in the Google Sheet.
+5. The CLA date workflow checks the applicable CLA submission date information.
+6. Each workflow passes when its requirements are satisfied or fails and displays feedback explaining what must be corrected.
+
+
+### Main Implementation Files
+
+
+- `.github/workflows/check-ai-disclosure.yml` — Runs the AI disclosure validation workflow.
+- `.github/workflows/check-cla.yml` — Runs the signed CLA verification workflow.
+- `.github/workflows/check-cla-date.yml` — Runs the CLA date verification workflow.
+- `scripts/check-ai-disclosure.js` — Validates the AI disclosure fields and human-review checkboxes.
+- `scripts/check-cla.js` — Compares contributor GitHub usernames with the CLA response records.
+- `scripts/check-cla-date.js` — Validates the relevant CLA submission date requirements.
+- `pull_request_template.md` — Collects AI disclosure, human-review, CLA, and contributor information.
+
+
+### Setup and Integration Requirements
+
+
+To use the prototype in OED:
+
+
+1. Add the workflow files to the repository’s `.github/workflows` directory.
+2. Add the validation scripts to the `scripts` directory.
+3. Add or update the pull request template.
+4. Configure the required GitHub repository secrets for Google Sheets access.
+5. Share the CLA response Sheet with the Google service account.
+6. Confirm that each workflow has only the permissions required to perform its checks.
+7. Confirm that the CLA verification and CLA date verification workflows use the correct Google Sheet columns and ranges.
+8. Test the checks in a fork or separate test repository before enabling them in the main OED repository.
+
+
+### Security Considerations
+
+
+The signed CLA workflow uses `pull_request_target` because it needs access to repository secrets used for the Google Sheets connection. Since this event can run with access to trusted repository resources, the workflow must not execute code taken directly from an untrusted contributor branch.
+
+
+To reduce this risk, the workflow checks out the trusted base-branch version of the validation script rather than the contributor’s pull request branch. This helps prevent a contributor from modifying the script in a pull request and using it to expose repository secrets.
+
+
+Sensitive information must be stored in GitHub Actions secrets and must not be written directly into the workflow, source code, documentation, or workflow logs. This includes the Google Sheet ID, service account email, and service account private key.
+
+
+The Google service account should have access only to the CLA response Sheet required by the workflow. Repository workflow permissions should also remain limited to the read access needed to inspect the pull request and repository contents.
+
+
+Before the workflows are enabled in the main OED repository, OED maintainers should review the workflow files, secret configuration, Google Sheet permissions, checked-out Git reference, and use of `pull_request_target`. The workflows should first be tested in a fork or separate prototype repository.
+
+
+### Testing and Current Limitations
+
+
+The AI disclosure validation script was manually tested from the command line by providing sample pull request descriptions through the `PR_BODY` environment variable.
+
+
+The following AI disclosure cases were tested:
+
+
+- “No AI assistance was used” selected — Pass
+- “AI assistance was used” selected with all required information completed — Pass
+- Neither AI assistance option selected — Fail
+- Both AI assistance options selected — Fail
+- AI tool entered but the explanation of how AI was used left blank — Fail
+- Explanation entered but the AI tool field left blank — Fail
+
+
+The human-review acknowledgments were also tested:
+
+
+- Human review checkbox left blank — Fail
+- Project requirements verification checkbox left blank — Fail
+- Responsibility checkbox left blank — Fail
+- All three human-review checkboxes left blank — Fail
+- Updated responsibility checkbox selected — Pass
+- Updated responsibility checkbox left blank — Fail
+
+
+The improved error message was tested with both failing and passing cases. When required information was missing, the script listed the incomplete item and provided instructions for editing the pull request description from the Conversation tab. When “No AI assistance was used” was selected correctly, the validation passed.
+
+
+All of the AI disclosure tests described above produced the expected results.
+
+
+Current limitations include:
+
+
+- The AI disclosure tests described above were performed manually using local command-line commands.
+- The AI disclosure script checks only the text contained in the pull request description.
+- It cannot determine whether a contributor’s disclosure or human-review acknowledgment is truthful.
+- Validation depends on the expected section headings, field labels, and checkbox wording remaining consistent with the pull request template.
+- Changes to the pull request template may require corresponding changes to the validation script.
+- The AI disclosure tests do not test the Google Sheets connection used by the CLA workflows.
+- CLA verification depends on correct GitHub secret configuration, Google Sheet access, and correct GitHub usernames in the CLA response records.
+- CLA date verification depends on the expected date information being present and correctly formatted in the Google Sheet.
+- The workflows do not prevent a pull request from being opened. They provide status checks that OED may configure as merge requirements.
+- First-time outside contributors may require maintainer approval before a workflow runs.
+- Additional integration testing should be completed in a GitHub fork before the workflows are proposed for use in the main OED repository.
+
+
+### Compliance Prototype Repository and Documentation
+
+- [OED Pull Request Compliance Prototype](oed-pr-compliance-prototype/)
+
+
+The repository contains the complete prototype, including:
+
+
+- GitHub Actions workflows
+- AI disclosure validation script
+- Signed CLA verification script
+- CLA date verification script
+- Pull request template
+- Testing and implementation notes
+- Secrets and API setup documentation
+
+
+**Key documentation:**
+
+- [AI Disclosure Validation Notes](oed-pr-compliance-prototype/docs/ai-disclosure-validation-notes.md)
+- [Signed CLA Verification Notes](oed-pr-compliance-prototype/docs/cla-verification-notes.md)
+- [CLA Date Verification Notes](oed-pr-compliance-prototype/docs/cla-date-verification-notes.md)
+- [Secrets and API Setup](oed-pr-compliance-prototype/docs/secrets-and-api-setup.md)
+
+
+## Issue-Tracking Risks and Limitations
+
+
+One risk of using AI for issue tracking is that the AI may misunderstand the issue. The AI may also miss important technical details, assign incorrect priority levels, or suggest next steps that are not appropriate for the OED project. Another limitation is that AI tools do not fully understand the project history, maintainer decisions, or all parts of the OED codebase. Because of this, AI-generated recommendations should only be used as support. Human maintainers must review all suggestions before making decisions about an issue. Furthermore, AI tools may not be equally effective for all contributors, since their benefits can vary depending on experience level and familiarity with the project.
+
+
+One additional risk is that AI tools may not understand the specific domain or project context of OED. In the ZoomInfo GitHub Copilot case study, developers reported that the tool struggled with domain-specific logic and sometimes produced inconsistent results. This is important for OED because GitHub issues may involve project-specific decisions, energy data concepts, database design, or maintainer preferences that an AI tool may not fully understand. As a result, AI-generated issue summaries or suggested next steps may be incomplete, too general, or incorrect.
+
+
+## Research Support
+
+
+A BIS field experiment found that generative AI improved coding productivity, with the largest gains observed among less experienced developers. However, the benefits varied across skill levels and were less pronounced for senior engineers [1].
+
+
+Research on GitHub Copilot at ZoomInfo provides useful support for using AI as an assistant in software development workflows. In the study, GitHub Copilot was deployed across more than 400 developers, and the researchers measured both quantitative usage data and developer feedback. The results showed an average suggestion acceptance rate of 33%, a line acceptance rate of 20%, and a developer satisfaction score of 72%. Developers also reported time savings of around 20%, especially for repetitive coding tasks, documentation, comments, and unit test generation [2].
+
+
+Another issue with AI is that it can create extra work for developers by producing low-quality, unclear, or untested suggestions. For OED, this is a risk because maintainers may spend more time reviewing AI-generated output than benefiting from useful work. There is also a risk that contributors may submit low-quality code generated by AI without fully understanding or testing it. Open-source AI contribution policies show that AI can increase the burden on maintainers when contributors submit AI-generated work without proper review [3].
+
+
+## Future Work
+
+
+Future work may include addressing how AI can adjust its recommendations based on a contributor’s experience level. One possible solution is to allow contributors to choose their level of expertise, such as beginner, intermediate, or advanced. Based on that selection, the AI could adjust the detail and complexity of its summaries, explanations, and suggested next steps. If the AI output is too complicated or too simple, contributors could provide feedback so the recommendations better fit their needs.
+
+
+Another area for future work is determining how much AI assistance should be used in the OED project. Current research shows that AI can be helpful, but it should be applied carefully so that it does not encourage contributors to take shortcuts or rely too heavily on AI-generated suggestions. One of the main challenges for OED is finding the right balance between useful AI support and responsible human decision-making. This is why the proposed workflow uses AI in a limited but practical way. The AI can assist with issue summaries, possible next steps, and risk-based questions, but human maintainers still make the final decisions.
+
+
+Future work may also include testing the AI-assisted issue tracking workflow with more OED GitHub issues. This would help determine whether the AI suggestions are useful, accurate, and consistent across different types of issues. Maintainers and contributors could also provide feedback on whether the summaries and beginner-friendly recommendations are helpful. In the future, the workflow could be expanded to support other areas of the OED project, such as code review and pull request summaries.
+
+
+## References
+
+
+[1] L. Gambacorta, H. Qiu, S. Shan, and D. M. Rees,
+"Generative AI and labour productivity: a field experiment on coding,"
+BIS Working Papers, no. 1208, Bank for International Settlements,
+Sep. 2024. [Online]. Available:
+https://www.bis.org/publ/work1208.pdf
+[Accessed: May. 27, 2026].
+
+
+[2] G. Bakal, A. Dasdan, Y. Katz, M. Kaufman, and G. Levin,
+"Experience with GitHub Copilot for Developer Productivity at ZoomInfo,"
+arXiv preprint arXiv:2501.13282, Jan. 2025. [Online].
+Available: https://arxiv.org/pdf/2501.13282
+[Accessed: Jun. 5, 2026].
+
+
+[3] M. Weber, “open-source-ai-contribution-policies,” GitHub repository. [Online]. Available: https://github.com/melissawm/open-source-ai-contribution-policies [Accessed: Jun. 14, 2026].
+
+
+[4] OpenEnergyDashboard, “Issues · OpenEnergyDashboard/OED,” GitHub. [Online]. Available: https://github.com/OpenEnergyDashboard/OED/issues?page=2 [Accessed: Jun. 14, 2026].
+
+
+[5] OpenEnergyDashboard, “OED Pull Request Compliance Prototype.” [Online]. Available: [oed-pr-compliance-prototype](oed-pr-compliance-prototype/).
+
+
+
+
+
diff --git a/aiIntegration/oed-pr-compliance-prototype/.github/workflows/check-ai-disclosure.yml b/aiIntegration/oed-pr-compliance-prototype/.github/workflows/check-ai-disclosure.yml
new file mode 100644
index 0000000..921c8ec
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/.github/workflows/check-ai-disclosure.yml
@@ -0,0 +1,26 @@
+name: Check AI Disclosure
+
+on:
+ pull_request:
+ types: [opened, edited, synchronize, reopened]
+
+permissions:
+ contents: read
+
+jobs:
+ check-ai-disclosure:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v4
+
+ - name: Set up Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Check PR body for AI disclosure
+ run: node scripts/check-ai-disclosure.js
+ env:
+ PR_BODY: ${{ github.event.pull_request.body }}
\ No newline at end of file
diff --git a/aiIntegration/oed-pr-compliance-prototype/.github/workflows/check-cla-date.yml b/aiIntegration/oed-pr-compliance-prototype/.github/workflows/check-cla-date.yml
new file mode 100644
index 0000000..7b324ef
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/.github/workflows/check-cla-date.yml
@@ -0,0 +1,42 @@
+name: CLA Date Verification
+
+on:
+ pull_request_target:
+ types: [opened, edited, synchronize, reopened]
+
+permissions:
+ contents: read
+ pull-requests: read
+
+jobs:
+ verify-cla-date:
+ name: Verify CLA signing date
+ runs-on: ubuntu-latest
+
+ steps:
+ # Check out the trusted base branch, not the contributor's PR branch.
+ # This is important because pull_request_target can access repository secrets.
+ - name: Check out trusted base branch code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.base.sha }}
+
+ - name: Set up Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ # Install the Google Sheets API client used by scripts/check-cla-date.js.
+ - name: Install Google Sheets client
+ run: npm install googleapis
+
+ # Check that the manual date entered in the CLA form is close to
+ # the automatic Google Form timestamp for the same GitHub username.
+ - name: Verify CLA signing date
+ run: node scripts/check-cla-date.js
+ env:
+ GITHUB_LOGIN: ${{ github.event.pull_request.user.login }}
+ PR_BODY: ${{ github.event.pull_request.body }}
+ CLA_SHEET_ID: ${{ secrets.CLA_SHEET_ID }}
+ CLA_RANGE: "Form Responses 1!A:Z"
+ GOOGLE_SERVICE_ACCOUNT_JSON: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }}
\ No newline at end of file
diff --git a/aiIntegration/oed-pr-compliance-prototype/.github/workflows/check-cla.yml b/aiIntegration/oed-pr-compliance-prototype/.github/workflows/check-cla.yml
new file mode 100644
index 0000000..7bb5038
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/.github/workflows/check-cla.yml
@@ -0,0 +1,42 @@
+name: Signed CLA Verification
+
+on:
+ pull_request_target:
+ types: [opened, edited, synchronize, reopened]
+
+permissions:
+ contents: read
+ pull-requests: read
+
+jobs:
+ verify-signed-cla:
+ name: Verify PR contributors signed the CLA
+ runs-on: ubuntu-latest
+
+ steps:
+ # Check out the trusted base branch, not the contributor's PR branch.
+ # This is important because pull_request_target can access repository secrets.
+ - name: Check out trusted base branch code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.base.sha }}
+
+ - name: Set up Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ # Install the Google Sheets API client used by scripts/check-cla.js.
+ - name: Install Google Sheets client
+ run: npm install googleapis
+
+ # Compare the PR author's username and any listed additional contributors against the CLA response Sheet.
+ # Sensitive values are stored in GitHub Actions secrets, not in the repo.
+ - name: Verify PR contributors signed the CLA
+ run: node scripts/check-cla.js
+ env:
+ GITHUB_LOGIN: ${{ github.event.pull_request.user.login }}
+ PR_BODY: ${{ github.event.pull_request.body }}
+ CLA_SHEET_ID: ${{ secrets.CLA_SHEET_ID }}
+ CLA_RANGE: "Form Responses 1!A:Z"
+ GOOGLE_SERVICE_ACCOUNT_JSON: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }}
\ No newline at end of file
diff --git a/aiIntegration/oed-pr-compliance-prototype/README.md b/aiIntegration/oed-pr-compliance-prototype/README.md
new file mode 100644
index 0000000..f14f594
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/README.md
@@ -0,0 +1,17 @@
+# OED PR Compliance Prototype
+
+Prototype GitHub Actions for Open Energy Dashboard pull request compliance.
+
+The prototype includes:
+
+- AI assistance disclosure validation
+- Signed CLA verification for the PR author and listed additional contributors
+- CLA signing-date verification
+- A proposed pull request template
+
+## Documentation
+
+- `docs/ai-disclosure-validation-notes.md`
+- `docs/cla-verification-notes.md`
+- `docs/cla-date-verification-notes.md`
+- `docs/secrets-and-api-setup.md`
diff --git a/aiIntegration/oed-pr-compliance-prototype/docs/ai-disclosure-validation-notes.md b/aiIntegration/oed-pr-compliance-prototype/docs/ai-disclosure-validation-notes.md
new file mode 100644
index 0000000..89f5293
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/docs/ai-disclosure-validation-notes.md
@@ -0,0 +1,54 @@
+# AI Disclosure Validation Notes
+
+## Purpose
+
+This prototype adds a GitHub Action that checks whether a pull request author completed the required AI Assistance Disclosure section.
+
+The goal is to provide contributors with clear feedback when the AI disclosure is missing or incomplete and to support consistent human review of AI-assisted work.
+
+## How It Works
+
+When a pull request is opened, edited, synchronized, or reopened, the workflow runs the `scripts/check-ai-disclosure.js` script.
+
+The script:
+
+1. Reads the pull request description.
+2. Checks for the AI Assistance Disclosure section.
+3. Verifies that exactly one AI-assistance option is selected.
+4. If AI assistance was used, checks that the AI tool is identified.
+5. Checks that the contributor explains how AI was used.
+6. Verifies the required human-review acknowledgments.
+7. Passes when the required disclosure is complete.
+8. Fails and lists missing or incomplete items when validation is unsuccessful.
+
+## Files Added
+
+- `.github/workflows/check-ai-disclosure.yml`
+- `scripts/check-ai-disclosure.js`
+
+## Current Behavior
+
+The current version validates the AI disclosure information entered in the pull request description.
+
+## Testing Notes
+
+The script was manually tested with the following cases:
+
+- No AI assistance selected — Pass
+- AI assistance selected with complete details — Pass
+- Neither option selected — Fail
+- Both options selected — Fail
+- AI tool entered, usage explanation blank — Fail
+- AI tool blank, usage explanation entered — Fail
+- AI output review box unchecked — Fail
+- Project requirements box unchecked — Fail
+- Responsibility box unchecked — Fail
+- All human-review boxes unchecked — Fail
+
+All tests produced the expected results.
+
+## Limitations
+
+- The Action checks only the contents of the pull request description.
+- It cannot determine whether a contributor's disclosure is truthful.
+- The current tests were performed manually.
\ No newline at end of file
diff --git a/aiIntegration/oed-pr-compliance-prototype/docs/cla-date-verification-notes.md b/aiIntegration/oed-pr-compliance-prototype/docs/cla-date-verification-notes.md
new file mode 100644
index 0000000..5015e36
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/docs/cla-date-verification-notes.md
@@ -0,0 +1,89 @@
+# CLA Date Verification Notes
+
+## Purpose
+
+This prototype adds a GitHub Action that verifies the date entered on an OED Contributor License Agreement submission.
+
+The goal is to help detect CLA submissions where the manually entered signing date does not reasonably match the Google Form submission timestamp.
+
+## How It Works
+
+When a pull request is opened, edited, synchronized, or reopened, the workflow runs the `scripts/check-cla-date.js` script.
+
+The script:
+
+1. Reads the pull request author's GitHub username.
+2. Reads any additional contributor GitHub usernames listed in the pull request description.
+3. Checks whether the CLA acknowledgment checkbox is selected.
+4. If the checkbox is not selected, the check stops and asks the contributor to acknowledge that the CLA has been signed.
+5. Connects to the Google Sheet containing CLA form responses.
+6. Locates the following columns:
+ - `Timestamp`
+ - `GitHub Username (the Username shown in your profile/used for login)`
+ - `Enter today's date (not your birthday)`
+7. Finds the CLA submission records for each contributor.
+8. Compares the manually entered date with the automatic Google Form timestamp.
+9. Allows a difference of up to one day to account for possible time-zone differences.
+10. Passes only if every contributor has at least one CLA submission with a valid date.
+11. Fails and identifies contributors whose CLA record is missing or whose date is invalid.
+
+## Files Added
+
+- `.github/workflows/check-cla-date.yml`
+- `scripts/check-cla-date.js`
+
+## Current Behavior
+
+The current version verifies the pull request author and any additional contributors listed in the **Additional contributor GitHub username(s)** field.
+
+The CLA acknowledgment checkbox must be checked before the script queries the CLA records.
+
+For each contributor, the script compares:
+
+- the automatic Google Form submission timestamp, and
+- the date manually entered by the contributor.
+
+The manually entered date is accepted when it is:
+
+- the same calendar day as the submission timestamp,
+- one day before the timestamp, or
+- one day after the timestamp.
+
+If a contributor has submitted the CLA more than once, the contributor passes if at least one matching submission contains a valid date.
+
+## Testing Notes
+
+Tests should include:
+
+- CLA acknowledgment box unchecked - fails before checking CLA records.
+- CLA acknowledgment box checked with valid CLA date - passes.
+- CLA acknowledgment box checked with date matching the timestamp exactly - passes.
+- Manual date one day before the timestamp - passes.
+- Manual date one day after the timestamp - passes.
+- Manual date more than one day from the timestamp - fails.
+- Contributor not found in CLA records - fails.
+- Additional contributor with valid CLA date - passes.
+- Additional contributor with invalid CLA date - fails.
+- Multiple CLA submissions where one record has a valid date - passes.
+- Missing required Google Sheet column - fails.
+- Missing Google Sheet or service account configuration - fails.
+
+## Limitations
+
+- GitHub usernames in the CLA records must match the usernames used in the pull request.
+
+- Additional contributors must be listed correctly in the pull request description.
+
+- The script does not automatically identify contributors from commit history.
+
+- The one-day tolerance assumes that a difference of up to one calendar day is sufficient to account for time-zone differences.
+
+- The script supports the date formats currently expected from the Google Form and Sheet. Unexpected date formats may fail validation.
+
+- The check verifies consistency between the manually entered date and the Google Form timestamp; it does not independently prove the contributor's identity.
+
+- The prototype does not prevent a pull request from being opened.
+
+- First-time outside contributors may require maintainer approval before the workflow runs.
+
+- Production use would require OED maintainer approval for GitHub Actions secrets, Google Sheet access, and workflow security.
diff --git a/aiIntegration/oed-pr-compliance-prototype/docs/cla-verification-notes.md b/aiIntegration/oed-pr-compliance-prototype/docs/cla-verification-notes.md
new file mode 100644
index 0000000..127bb88
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/docs/cla-verification-notes.md
@@ -0,0 +1,70 @@
+# Signed CLA Verification Notes
+
+## Purpose
+
+This prototype adds a GitHub Action that checks whether the pull request author and any additional contributors listed in the pull request description have signed the Contributor License Agreement.
+
+The goal is to reduce manual CLA checking and give contributors clearer feedback when their CLA status cannot be verified.
+
+## How It Works
+
+When a pull request is opened, edited, synchronized, or reopened, the workflow runs the `scripts/check-cla.js` script.
+
+The script:
+
+1. Reads the pull request author's GitHub username.
+2. Reads any additional contributor GitHub usernames listed in the pull request description.
+3. Checks whether the CLA acknowledgment checkbox is selected.
+4. If the checkbox is not selected, the check stops and asks the contributor to acknowledge that the CLA has been signed.
+5. Connects to the Google Sheet that stores CLA form responses.
+6. Looks for the column containing GitHub usernames.
+7. Checks the pull request author and each listed additional contributor against the CLA records.
+8. Passes only if a matching CLA record is found for every contributor.
+9. Fails and identifies any contributor whose CLA record cannot be found.
+
+## Files Added
+
+- `.github/workflows/check-cla.yml`
+- `scripts/check-cla.js`
+
+## Current Behavior
+
+The current version verifies:
+
+- The pull request author.
+- Any additional contributors listed in the **Additional contributor GitHub username(s)** field.
+- That the CLA acknowledgment checkbox has been selected before CLA records are checked.
+
+Additional contributor usernames may be separated by commas, spaces, or new lines. Duplicate usernames are checked only once.
+
+The current version does not automatically identify contributors from commit history. Additional contributors must be listed in the pull request description.
+
+## Testing Notes
+
+Tests:
+
+- Missing Google service account secret - failed as expected.
+- Missing Sheet ID - failed as expected.
+- Google Sheet not shared with the service account - failed as expected.
+- CLA box unchecked, contributor unsigned - failed as expected.
+- CLA box unchecked, contributor signed - failed as expected because acknowledgment is required.
+- CLA box checked, contributor unsigned - failed as expected.
+- CLA box checked, PR author signed - passed.
+- CLA box checked, PR author and additional contributor signed - passed.
+- CLA box checked, PR author signed but additional contributor unsigned - failed and identified the missing contributor.
+
+## Limitations
+
+- The GitHub username in the CLA records must match the contributor's GitHub username.
+
+- Additional contributors must be listed correctly in the pull request description.
+
+- The prototype does not automatically identify contributors from commit history.
+
+- The prototype does not prevent a pull request from being opened.
+
+- First-time outside contributors may require maintainer approval before the workflow runs.
+
+- It provides a status check that can be used as part of the pull request review and merge process.
+
+- Production use would require OED maintainer approval for secrets, Google Sheet access, and workflow security.
\ No newline at end of file
diff --git a/aiIntegration/oed-pr-compliance-prototype/docs/secrets-and-api-setup.md b/aiIntegration/oed-pr-compliance-prototype/docs/secrets-and-api-setup.md
new file mode 100644
index 0000000..4f77f3b
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/docs/secrets-and-api-setup.md
@@ -0,0 +1,126 @@
+# Secrets and API Setup
+
+## Purpose
+
+The Signed CLA Verification workflow needs access to the Google Sheet that stores CLA form responses. To keep credentials secure, the workflow uses GitHub Actions secrets instead of storing private information in the repository.
+
+## Creating the Google Service Account JSON Key
+
+1. Go to the Google Cloud Console.
+
+2. Create a new project or select an existing project.
+
+3. In the left menu, go to:
+
+ `APIs & Services → Library`
+
+4. Search for:
+
+ `Google Sheets API`
+
+5. Click **Google Sheets API**, then click **Enable**.
+
+6. Go to:
+
+ `APIs & Services → Credentials`
+
+7. Click **Create credentials**.
+
+8. Choose **Service account**.
+
+9. Give the service account a name, such as:
+
+ `cla-checker`
+
+10. After the service account is created, go to:
+
+ `IAM & Admin → Service Accounts`
+
+11. Open the service account you created.
+
+ Example:
+
+ `cla-checker@your-project-id.iam.gserviceaccount.com`
+
+12. Click the **Keys** tab.
+
+13. Click:
+
+ `Add key → Create new key`
+
+14. Choose **JSON**.
+
+15. Click **Create**.
+
+16. Download the JSON file.
+
+The contents of this JSON file will be stored in the GitHub secret named:
+
+`GOOGLE_SERVICE_ACCOUNT_JSON`
+
+Do not commit the JSON file to the repository.
+
+## Sharing the Google Sheet with the Service Account
+
+Open the downloaded JSON file and find the `client_email` value.
+
+Example:
+
+```json
+{
+ "client_email": "cla-checker@your-project-id.iam.gserviceaccount.com"
+}
+```
+
+Copy that email address.
+
+Open the Google Sheet connected to the CLA form. Click **Share** and give the service account email **Viewer** access.
+
+The workflow needs this access so it can read the CLA response sheet.
+
+## Adding GitHub Repository Secrets
+
+In the GitHub repository, go to:
+
+`Settings → Secrets and variables → Actions → New repository secret`
+
+Add the following secrets.
+
+### `GOOGLE_SERVICE_ACCOUNT_JSON`
+
+For the value, paste the entire contents of the downloaded JSON file.
+
+Do not paste only part of the file.
+
+Do not commit the JSON file to the repository or include its contents in workflow logs.
+
+### `CLA_SHEET_ID`
+
+For the value, paste only the Google Sheet ID from the Sheet URL.
+
+Example Google Sheet URL:
+
+```text
+https://docs.google.com/spreadsheets/d/thisPart123/edit#gid=0
+```
+
+The Sheet ID is:
+
+```text
+thisPart123
+```
+
+Do not paste the complete Google Sheet URL.
+
+## GitHub Username Column
+
+`scripts/check-cla.js` looks for a Google Sheet column whose header contains both `GitHub` and `username` (case-insensitive).
+
+For example:
+
+- `GitHub username`
+- `Contributor GitHub Username`
+
+If the Sheet uses a different column name, the script must be updated accordingly.
+
+
diff --git a/aiIntegration/oed-pr-compliance-prototype/pull_request_template.md b/aiIntegration/oed-pr-compliance-prototype/pull_request_template.md
new file mode 100644
index 0000000..24cdf8a
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/pull_request_template.md
@@ -0,0 +1,36 @@
+## AI Assistance Disclosure
+
+(Check one option by replacing the space in `[ ]` with an `x`.)
+
+- [ ] No AI assistance was used.
+- [ ] AI assistance was used.
+
+(If AI assistance was used, both fields below are required. Identify the AI tool or tools and briefly explain how they were used. Examples include assistance with code, documentation, debugging, testing, translation, explanation, or review.)
+
+(If the check fails, click the three-dot menu in the upper-right corner of the pull request description, select **Edit**, complete the required fields, and click **Update comment**.)
+
+**AI tool(s) used:**
+
+**How AI was used:**
+
+## Human Review of AI-Assisted Work
+
+(If AI assistance was used, check these boxes only after the review is complete — including review by any listed contributors. See [OED project requirements](https://openenergydashboard.org/developer/prs/) for what "follows requirements" means.)
+
+- [ ] I reviewed all AI-assisted output before submitting this pull request.
+- [ ] I verified that the AI-assisted work follows OED project requirements.
+- [ ] I confirm that I and any listed contributors reviewed our work and followed OED quality and licensing requirements.
+
+## Contributor License Agreement
+(See the [OED Contributor License Agreement](https://openenergydashboard.org/developer/cla/) for details.)
+
+- [ ] I acknowledge that I have signed the OED Contributor License Agreement.
+(If no matching CLA record is found for you or an additional contributor, you will be notified.)
+
+**Additional contributor GitHub username(s):**
+
+(Leave blank if none. If others contributed, list GitHub username(s), separated by commas.)
+
+## Limitations
+
+(Describe any issues that remain or work that should still be completed.)
\ No newline at end of file
diff --git a/aiIntegration/oed-pr-compliance-prototype/scripts/check-ai-disclosure.js b/aiIntegration/oed-pr-compliance-prototype/scripts/check-ai-disclosure.js
new file mode 100644
index 0000000..2647740
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/scripts/check-ai-disclosure.js
@@ -0,0 +1,170 @@
+/*this script checks if pull request AI assistant disclosure section was completed correctly*/
+
+
+//constant variable that will contain full pull request description
+const prBody = process.env.PR_BODY || "";
+
+//normalize checkbox spacing before validation, so [x], [x ], [ x], [ x ], [ x ] are accepted.
+const normalizedPrBody = prBody.replace(/\[\s*([xX])\s*\]/g, "[$1]");
+
+//empty array to store the names of any required items that are missing or incomplete
+const missing = [];
+
+
+/**
+ *escape regex function allows labels containing characters such as parentheses or periods
+ * to be safely inserted into a regular expression.
+ * @param {string} value The text to escape.
+ * @returns {string} The escaped text.
+ */
+function escapeRegex(value) {
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+}
+
+/**
+ *checks whether the pull request body contains a level-two Markdown heading.
+ * @param {string} title The section title to find.
+ * @returns {boolean} True when the section exists.
+ */
+function hasSection(title) {
+ const escapedTitle = escapeRegex(title);
+
+ const regex = new RegExp(
+ `^##[ \\t]+${escapedTitle}[ \\t]*$`,
+ "im"
+ );
+
+ return regex.test(prBody);
+}
+
+/**
+ *checks whether a specific Markdown checkbox is checked.
+ * @param {string} label The exact text following the checkbox.
+ * @returns {boolean} True when the checkbox is checked.
+ */
+function hasCheckedBox(label) {
+ const escapedLabel = escapeRegex(label);
+
+ const regex = new RegExp(
+ `^[ \\t]*-[ \\t]+\\[[xX]\\][ \\t]+${escapedLabel}[ \\t]*$`,
+ "im"
+ );
+
+ return regex.test(normalizedPrBody);
+}
+/**
+ *gets the text entered after a bold Markdown field label.
+ * The value continues until the next section heading, bold field,
+ * or the end of the pull request body.
+ * @param {string} label The field label without Markdown or a colon.
+ * @returns {string} The entered value, or an empty string if none exists.
+ */
+function getFieldValue(label) {
+ const escapedLabel = escapeRegex(label);
+
+ const regex = new RegExp(
+ `\\*\\*${escapedLabel}:\\*\\*[ \\t]*([\\s\\S]*?)(?=\\r?\\n##|\\r?\\n\\*\\*|$)`,
+ "i"
+ );
+
+ const match = prBody.match(regex);
+
+ return match ? match[1].trim() : "";
+}
+
+//AI Assistance Disclosure
+
+//confirm required AI disclosure section exists
+if (!hasSection("AI Assistance Disclosure")) {
+ missing.push("AI Assistance Disclosure section");
+}
+
+//check if checkbox was selected
+const noAiChecked = hasCheckedBox("No AI assistance was used.");
+const aiUsedChecked = hasCheckedBox("AI assistance was used.");
+
+//makes sure contributor selects one option
+if (!noAiChecked && !aiUsedChecked) {
+ missing.push(
+ 'Check either "No AI assistance was used" or "AI assistance was used"'
+ );
+}
+
+//contributor cannot select both options
+if (noAiChecked && aiUsedChecked) {
+ missing.push("Do not check both AI disclosure options");
+}
+
+//additional checks if AI is used
+if (aiUsedChecked) {
+ const toolsUsed = getFieldValue("AI tool(s) used");
+ const howUsed = getFieldValue("How AI was used");
+
+ //requires ai tool name
+ if (!toolsUsed) {
+ missing.push("AI tool(s) used field");
+ }
+
+ //require explanation of how AI was used
+ if (!howUsed) {
+ missing.push("How AI was used field");
+ }
+
+ //Human Review of AI-Assisted Work
+
+ //checks if reviewed all AI-assisted output before submitting this pull request box was checked.
+ if (
+ !hasCheckedBox(
+ "I reviewed all AI-assisted output before submitting this pull request."
+ )
+ ) {
+ missing.push("Human review checkbox");
+ }
+
+ //checks if I verified that the AI-assisted work follows OED project requirements box was checked.
+ if (
+ !hasCheckedBox(
+ "I verified that the AI-assisted work follows OED project requirements."
+ )
+ ) {
+ missing.push("Project requirements verification checkbox");
+ }
+
+ //Checks that the pull request author and any listed contributors
+ //reviewed their work and followed OED quality and licensing requirements.
+ if (
+ !hasCheckedBox(
+ "I confirm that I and any listed contributors reviewed our work and followed OED quality and licensing requirements."
+ )
+ ) {
+ missing.push("Responsibility checkbox");
+ }
+}
+//AI disclosure error message
+//returns number of items in the array missing. If one error exist then error is printed.
+//fails github action if any required information is missing
+if (missing.length > 0) {
+ console.error("AI disclosure check failed.");
+ console.error("The pull request description is missing or has incomplete AI disclosure information:");
+
+ //goes through every item in the array missing and prints each error
+ for (const item of missing) {
+ console.error(`- ${item}`);
+ }
+
+ console.error("");
+ console.error("How to fix the pull request:");
+ console.error("1. Open the pull request Conversation tab.");
+ console.error("2. Click the three-dot menu (...) on the pull request description.");
+ console.error("3. Select Edit.");
+ console.error("4. Complete the missing AI disclosure information.");
+ console.error("5. Click Update comment.");
+ console.error("");
+ console.error("The AI disclosure check will run again after the description is updated.");
+
+ //ends loop
+ process.exit(1);
+}
+
+//if no required items are missing, allow the GitHub Action to pass.
+console.log("AI disclosure check passed.");
\ No newline at end of file
diff --git a/aiIntegration/oed-pr-compliance-prototype/scripts/check-cla-date.js b/aiIntegration/oed-pr-compliance-prototype/scripts/check-cla-date.js
new file mode 100644
index 0000000..5dc4b60
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/scripts/check-cla-date.js
@@ -0,0 +1,391 @@
+// Expected Google Sheet headers.
+const TIMESTAMP_COLUMN = "Timestamp";
+const GITHUB_USERNAME_COLUMN =
+ "GitHub Username (the Username shown in your profile/used for login)";
+const MANUAL_DATE_COLUMN = "Enter today's date (not your birthday)";
+
+// Read a required environment variable from GitHub Actions.
+// If it is missing, fail the check with a clear message.
+function requiredEnv(name) {
+ const value = process.env[name];
+
+ if (!value) {
+ console.error(`Missing required environment variable: ${name}`);
+ process.exit(1);
+ }
+
+ return value;
+}
+
+// Normalize GitHub usernames so comparisons are consistent.
+function normalizeUsername(value) {
+ return String(value || "")
+ .trim()
+ .toLowerCase()
+ .replace(/^@/, "");
+}
+
+// Normalize Sheet headers so small capitalization or spacing differences do not break matching.
+function normalizeHeader(value) {
+ return String(value || "")
+ .trim()
+ .toLowerCase()
+ .replace(/\s+/g, " ");
+}
+
+// Some labels include special characters like parentheses.
+// This makes the label safe to use inside a regular expression.
+function escapeRegex(value) {
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+}
+// Check whether the CLA acknowledgment checkbox is selected in the PR body.
+function hasCheckedBox(prBody, label) {
+ const escapedLabel = escapeRegex(label);
+
+ // Normalize checked boxes such as [x], [ x ], and [X].
+ const normalizedPrBody = prBody.replace(
+ /\[\s*([xX])\s*\]/g,
+ "[$1]"
+ );
+
+ const regex = new RegExp(
+ `^[ \\t]*-[ \\t]+\\[[xX]\\][ \\t]+${escapedLabel}[ \\t]*$`,
+ "im"
+ );
+
+ return regex.test(normalizedPrBody);
+}
+// The template says to leave the field blank if there are no extra contributors.
+// This also handles common answers like "N/A" or "none".
+function isIgnoredContributorValue(value) {
+ const ignoredValues = new Set([
+ "n/a",
+ "na",
+ "none",
+ "no additional contributors",
+ ]);
+
+ return ignoredValues.has(normalizeUsername(value));
+}
+
+// Make sure the entered value looks like a GitHub username,
+// not a full name or email address.
+function isValidGitHubUsername(username) {
+ return /^[a-z\d](?:[a-z\d-]{0,37}[a-z\d])?$/i.test(username);
+}
+
+// Read extra contributor usernames from the PR description.
+// Expected format:
+// **Additional contributor GitHub username(s):**
+// username1, username2
+// (Leave blank if none. If others contributed, list GitHub username(s), separated by commas.)
+// The instruction line in parentheses is ignored.
+function parseAdditionalContributors(prBody) {
+ const label = "Additional contributor GitHub username(s):";
+ const escapedLabel = escapeRegex(label);
+
+ const regex = new RegExp(
+ `\\*\\*${escapedLabel}\\*\\*\\s*([\\s\\S]*?)(?=\\r?\\n##|$)`,
+ "i"
+ );
+
+ const match = prBody.match(regex);
+
+ if (!match) {
+ return [];
+ }
+
+ // If the label is accidentally typed again in the answer area,
+ // remove the label and keep only the username.
+ const repeatedLabelRegex = new RegExp(
+ `^\\*{0,2}${escapedLabel}\\*{0,2}\\s*`,
+ "i"
+ );
+
+ const rawValue = match[1]
+ .split(/\r?\n/)
+ .map((line) => line.trim())
+ .filter(Boolean)
+ // Ignore the template instruction line.
+ .filter((line) => !line.startsWith("("))
+ // Ignore example lines if examples are added later.
+ .filter((line) => !/^example:/i.test(line))
+ // Remove repeated label text if it was accidentally copied into the field.
+ .map((line) => line.replace(repeatedLabelRegex, "").trim())
+ .filter(Boolean)
+ .join(" ")
+ .trim();
+
+ if (!rawValue || isIgnoredContributorValue(rawValue)) {
+ return [];
+ }
+
+ const usernames = rawValue
+ .split(/[,\s]+/)
+ .map((username) => normalizeUsername(username))
+ .filter(Boolean)
+ .filter((username) => !isIgnoredContributorValue(username));
+
+ const invalidUsernames = usernames.filter(
+ (username) => !isValidGitHubUsername(username)
+ );
+
+ if (invalidUsernames.length > 0) {
+ console.error("Invalid additional contributor GitHub username(s):");
+
+ for (const username of invalidUsernames) {
+ console.error(`- ${username}`);
+ }
+
+ console.error("Use GitHub usernames only, separated by commas.");
+ process.exit(1);
+ }
+
+ return usernames;
+}
+
+// Find a Sheet column by its expected header.
+function findColumnIndex(headers, expectedHeader) {
+ const expected = normalizeHeader(expectedHeader);
+
+ return headers.findIndex((header) => {
+ return normalizeHeader(header) === expected;
+ });
+}
+
+// Convert a calendar date to a day number.
+// This lets us compare only the date, not the exact time.
+function toUtcDayNumber(year, month, day) {
+ return Math.floor(Date.UTC(year, month - 1, day) / 86400000);
+}
+
+// Parse common date formats from Google Forms / Google Sheets.
+//
+// Supported examples:
+// 7/23/2026
+// 07/23/2026
+// 7/23/2026 10:15:30
+// 2026-07-23
+//
+// For slash dates, this assumes US format: month/day/year.
+function parseDateToDayNumber(value) {
+ const text = String(value || "").trim();
+
+ if (!text) {
+ return null;
+ }
+
+ // Match ISO format: YYYY-MM-DD
+ let match = text.match(/^(\d{4})-(\d{1,2})-(\d{1,2})/);
+
+ if (match) {
+ const year = Number(match[1]);
+ const month = Number(match[2]);
+ const day = Number(match[3]);
+
+ return toUtcDayNumber(year, month, day);
+ }
+
+ // Match US format: MM/DD/YYYY or MM-DD-YYYY.
+ // This also works when the timestamp includes time after the date.
+ match = text.match(/^(\d{1,2})[/-](\d{1,2})[/-](\d{2,4})/);
+
+ if (match) {
+ const month = Number(match[1]);
+ const day = Number(match[2]);
+ let year = Number(match[3]);
+
+ if (year < 100) {
+ year += 2000;
+ }
+
+ return toUtcDayNumber(year, month, day);
+ }
+
+ return null;
+}
+
+// The manually entered date is valid if it is the same day as the automatic
+// Google Form timestamp, or one day before/after.
+// The one-day buffer helps with time zone differences.
+function datesAreCloseEnough(timestampValue, manualDateValue) {
+ const timestampDay = parseDateToDayNumber(timestampValue);
+ const manualDay = parseDateToDayNumber(manualDateValue);
+
+ if (timestampDay === null || manualDay === null) {
+ return false;
+ }
+ // buffer day
+ return Math.abs(timestampDay - manualDay) <= 1;
+}
+
+async function main() {
+ // GitHub username of the person who opened the pull request.
+ const prAuthor = normalizeUsername(requiredEnv("GITHUB_LOGIN"));
+
+ // Pull request body used to verify the CLA acknowledgment
+ // and read additional contributor usernames.
+ const prBody = process.env.PR_BODY || "";
+
+ const claAcknowledgment =
+ "I acknowledge that I have signed the OED Contributor License Agreement.";
+
+ // Require the contributor to acknowledge the CLA before checking CLA dates.
+ if (!hasCheckedBox(prBody, claAcknowledgment)) {
+ console.error("CLA date verification cannot run yet.");
+ console.error("");
+ console.error(
+ 'Check "I acknowledge that I have signed the OED Contributor License Agreement."'
+ );
+ console.error(
+ "After checking the box, save the pull request description. The CLA date verification will run again."
+ );
+
+ console.error(
+ "::error title=CLA Acknowledgment Required::Check the Contributor License Agreement acknowledgment box in the pull request description before CLA date verification can run."
+ );
+
+ process.exit(1);
+ }
+
+ // Load the Google Sheets client only after the CLA acknowledgment is confirmed.
+ const { google } = require("googleapis");
+
+ // Google Sheet information passed from GitHub Actions secrets/environment.
+ const spreadsheetId = requiredEnv("CLA_SHEET_ID");
+ const range = process.env.CLA_RANGE || "Form Responses 1!A:Z";
+
+ // Google service account credentials stored in GitHub Actions secrets.
+ const credentials = JSON.parse(requiredEnv("GOOGLE_SERVICE_ACCOUNT_JSON"));
+
+ // Fix private key formatting if GitHub stores newline characters as "\n".
+ if (credentials.private_key) {
+ credentials.private_key = credentials.private_key.replace(/\\n/g, "\n");
+ }
+
+ // Authenticate with Google Sheets using read-only access.
+ const auth = new google.auth.GoogleAuth({
+ credentials,
+ scopes: ["https://www.googleapis.com/auth/spreadsheets.readonly"],
+ });
+
+ const sheets = google.sheets({ version: "v4", auth });
+
+ // Read the CLA response Sheet.
+ const response = await sheets.spreadsheets.values.get({
+ spreadsheetId,
+ range,
+ });
+
+ const rows = response.data.values || [];
+
+ if (rows.length === 0) {
+ console.error("CLA sheet is empty or could not be read.");
+ process.exit(1);
+ }
+
+ const headers = rows[0];
+ const dataRows = rows.slice(1);
+
+ // Find the required columns in the CLA response Sheet.
+ const timestampColumnIndex = findColumnIndex(headers, TIMESTAMP_COLUMN);
+ const githubColumnIndex = findColumnIndex(headers, GITHUB_USERNAME_COLUMN);
+ const manualDateColumnIndex = findColumnIndex(headers, MANUAL_DATE_COLUMN);
+
+ if (timestampColumnIndex === -1) {
+ console.error(`Could not find required column: ${TIMESTAMP_COLUMN}`);
+ process.exit(1);
+ }
+
+ if (githubColumnIndex === -1) {
+ console.error(`Could not find required column: ${GITHUB_USERNAME_COLUMN}`);
+ process.exit(1);
+ }
+
+ if (manualDateColumnIndex === -1) {
+ console.error(`Could not find required column: ${MANUAL_DATE_COLUMN}`);
+ process.exit(1);
+ }
+
+ // Build a set of everyone who needs a valid CLA date.
+ // This includes the PR author and any additional contributors listed in the PR body.
+ const contributorsToCheck = new Set();
+
+ contributorsToCheck.add(prAuthor);
+
+ const additionalContributors = parseAdditionalContributors(prBody);
+
+ for (const contributor of additionalContributors) {
+ contributorsToCheck.add(contributor);
+ }
+
+ const usersNotFound = [];
+ const usersWithInvalidDates = [];
+
+ for (const contributor of contributorsToCheck) {
+ // Allow duplicate CLA entries.
+ // A contributor passes if any one row for their GitHub username has a valid date.
+ const rowsForContributor = dataRows.filter((row) => {
+ return normalizeUsername(row[githubColumnIndex]) === contributor;
+ });
+
+ if (rowsForContributor.length === 0) {
+ usersNotFound.push(contributor);
+ continue;
+ }
+
+ const hasValidDate = rowsForContributor.some((row) => {
+ const timestampValue = row[timestampColumnIndex];
+ const manualDateValue = row[manualDateColumnIndex];
+
+ return datesAreCloseEnough(timestampValue, manualDateValue);
+ });
+
+ if (!hasValidDate) {
+ usersWithInvalidDates.push(contributor);
+ }
+ }
+
+ if (usersNotFound.length > 0 || usersWithInvalidDates.length > 0) {
+ console.error("CLA date verification failed.");
+
+ if (usersNotFound.length > 0) {
+ console.error("The following GitHub username(s) were not found in the CLA response records:");
+
+ for (const username of usersNotFound) {
+ console.error(`- ${username}`);
+ }
+
+ }
+
+ // Error when user enters invalid date
+ if (usersWithInvalidDates.length > 0) {
+ console.error("The following GitHub username(s) were found, but the date is invalid:");
+
+ for (const username of usersWithInvalidDates) {
+ console.error(`- ${username}`);
+ }
+
+ console.error("The date entered in the CLA is invalid!");
+ console.error("Please resubmit the CLA form.");
+ }
+
+ console.error(
+ "::error title=CLA Date Verification Failed::One or more contributors are missing a valid CLA signing date."
+ );
+
+ process.exit(1);
+ }
+
+ console.log("CLA date verification passed.");
+ console.log(`Verified valid CLA date for username(s): ${[...contributorsToCheck].join(", ")}`);
+}
+
+main().catch((error) => {
+ console.error("Unexpected error while checking CLA dates:");
+ console.error(error);
+ process.exit(1);
+});
+
+
+
diff --git a/aiIntegration/oed-pr-compliance-prototype/scripts/check-cla.js b/aiIntegration/oed-pr-compliance-prototype/scripts/check-cla.js
new file mode 100644
index 0000000..56db78f
--- /dev/null
+++ b/aiIntegration/oed-pr-compliance-prototype/scripts/check-cla.js
@@ -0,0 +1,230 @@
+
+// Read a required variable (user name and secrets)
+function requiredEnv(name) {
+ const value = process.env[name];
+ if (!value) {
+ console.error(`Missing required environment variable: ${name}`);
+ process.exit(1);
+ }
+ return value;
+}
+
+// Normalize GitHub usernames.
+function normalize(value) {
+ return String(value || "").trim().toLowerCase().replace(/^@/, "");
+}
+
+// Escape text before inserting it into a regular expression.
+function escapeRegex(value) {
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+}
+
+// Check whether the CLA acknowledgment checkbox is selected in the PR body.
+function hasCheckedBox(prBody, label) {
+ const escapedLabel = escapeRegex(label);
+
+ // Normalize checkbox spacing so [x], [ x ], and [X] work.
+ const normalizedPrBody = prBody.replace(
+ /\[\s*([xX])\s*\]/g, "[$1]");
+
+ const regex = new RegExp(
+ `^[ \\t]*-[ \\t]+\\[[xX]\\][ \\t]+${escapedLabel}[ \\t]*$`, "im");
+
+ return regex.test(normalizedPrBody);
+}
+
+// This handles common answers like "N/A" or "none" in The PR template.
+function isIgnoredContributorValue(value) {
+ const ignoredValues = new Set([
+ "n/a", "none", "no additional contributors",]);
+ return ignoredValues.has(normalize(value));
+}
+
+// Validate GitHub username format.
+function isValidGitHubUsername(username) {
+ return /^[a-z\d](?:[a-z\d-]{0,37}[a-z\d])?$/i.test(username);
+}
+
+// Extracts usernames from the "Additional contributor GitHub username(s):" field.
+// Ignores the template's parenthetical instruction line so contributors
+// don't have to delete it before submitting.
+function parseAdditionalContributors(prBody) {
+ const label = "Additional contributor GitHub username(s):";
+ const escapedLabel = escapeRegex(label);
+
+ const regex = new RegExp(`\\*\\*${escapedLabel}\\*\\*\\s*([\\s\\S]*?)(?=\\r?\\n##|$)`, "i");
+ const match = prBody.match(regex);
+ if (!match) {
+ return [];
+ }
+
+ // If someone accidentally repeats the field label, Additional contributor GitHub username(s):
+ const repeatedLabelRegex = new RegExp(
+ `^\\*{0,2}${escapedLabel}\\*{0,2}\\s*`, "i"
+ );
+
+ const rawValue = match[1]
+ .split(/\r?\n/)
+ .map((line) => line.trim())
+ .filter(Boolean)
+ // Ignore the template instruction line.
+ .filter((line) => !line.startsWith("("))
+ // Ignore example lines if examples are added later.
+ .filter((line) => !/^example:/i.test(line))
+ // Remove repeated label text if it was accidentally copied into the field.
+ .map((line) => line.replace(repeatedLabelRegex, "").trim())
+ .filter(Boolean)
+ .join(" ")
+ .trim();
+
+ if (!rawValue || isIgnoredContributorValue(rawValue)) return [];
+
+ // Allow usernames to be separated by commas, spaces, or new lines.
+ const usernames = rawValue
+ .split(/[,\s]+/)
+ .map((username) => normalize(username))
+ .filter(Boolean)
+ .filter((username) => !isIgnoredContributorValue(username));
+
+ const invalidUsernames = usernames.filter((username) => !isValidGitHubUsername(username));
+
+ if (invalidUsernames.length > 0) {
+ console.error("Invalid additional contributor GitHub username(s):");
+
+ for (const username of invalidUsernames) {
+ console.error(`- ${username}`);
+ }
+
+ console.error("Use GitHub usernames only, separated by commas.");
+ process.exit(1);
+ }
+
+ return usernames;
+}
+
+async function main() {
+ const githubLogin = normalize(requiredEnv("GITHUB_LOGIN"));
+ const prBody = process.env.PR_BODY || "";
+ const claAcknowledgment =
+ "I acknowledge that I have signed the OED Contributor License Agreement.";
+
+ // Require the contributor to acknowledge the CLA before checking CLA records.
+ if (!hasCheckedBox(prBody, claAcknowledgment)) {
+ console.error("Signed CLA verification cannot run yet.");
+ console.error("");
+ console.error(
+ 'Check "I acknowledge that I have signed the OED Contributor License Agreement."'
+ );
+ console.error(
+ "After checking the box, save the pull request description. The CLA verification will run again."
+ );
+ console.error(
+ "::error title=CLA Acknowledgment Required::Check the Contributor License Agreement acknowledgment box in the pull request description before CLA verification can run."
+ );
+ process.exit(1);
+ }
+ // Load the Google Sheets client only after the CLA acknowledgment is confirmed.
+ const { google } = require("googleapis");
+ // Google Sheet info.
+ const spreadsheetId = requiredEnv("CLA_SHEET_ID");
+ const range = process.env.CLA_RANGE || "Form Responses 1!A:Z";
+ // Google Service account credentials.
+ const credentials = JSON.parse(requiredEnv("GOOGLE_SERVICE_ACCOUNT_JSON"));
+ // Fix private key formatting if GitHub stores newline characters as "\n".
+ if (credentials.private_key) {
+ credentials.private_key = credentials.private_key.replace(/\\n/g, "\n");
+ }
+ // Authenticate with Google Sheets.
+ const auth = new google.auth.GoogleAuth({
+ credentials,
+ scopes: ["https://www.googleapis.com/auth/spreadsheets.readonly"],
+ });
+ const sheets = google.sheets({ version: "v4", auth });
+ // Read the CLA response Sheet.
+ const response = await sheets.spreadsheets.values.get({
+ spreadsheetId,
+ range,
+ });
+ const rows = response.data.values || [];
+ if (rows.length === 0) {
+ console.error("CLA sheet is empty or could not be read.");
+ process.exit(1);
+ }
+ const headers = rows[0];
+ const dataRows = rows.slice(1);
+
+ // Column header must contain both "github" and "username" (case-insensitive).
+ const githubColumnIndex = headers.findIndex((header) => {
+ const normalizedHeader = normalize(header);
+ return (
+ normalizedHeader.includes("github") &&
+ normalizedHeader.includes("username")
+ );
+ });
+
+ if (githubColumnIndex === -1) {
+ console.error("Could not find a GitHub username column in the CLA sheet.");
+ console.error("Add a required Google Form field named: GitHub username.");
+ process.exit(1);
+ }
+
+ // Build a set of all GitHub usernames found in the CLA records.
+ const signedUsers = new Set();
+ for (const row of dataRows) {
+ const username = normalize(row[githubColumnIndex]);
+
+ if (username) {
+ signedUsers.add(username);
+ }
+ }
+
+ // Build a set of everyone who needs to be checked.
+ const contributorsToCheck = new Set();
+
+ contributorsToCheck.add(githubLogin);
+
+ const additionalContributors = parseAdditionalContributors(prBody);
+
+ for (const contributor of additionalContributors) {
+ contributorsToCheck.add(contributor);
+ }
+
+ // Find any required contributor who is not listed in the CLA records.
+ const missingUsers = [...contributorsToCheck].filter(
+ (username) => !signedUsers.has(username)
+ );
+
+ if (missingUsers.length > 0) {
+ console.error("Signed CLA verification failed.");
+ console.error("");
+ console.error("No matching CLA submission was found for the following GitHub username(s):");
+
+ for (const username of missingUsers) {
+ console.error(`- ${username}`);
+ }
+
+ console.error("");
+ console.error("How to fix this:");
+ console.error("1. Open the OED Contributor License Agreement link in the pull request description.");
+ console.error("2. Each contributor listed above must complete and submit the CLA form.");
+ console.error("3. Each contributor must enter their exact GitHub username in the CLA form.");
+ console.error("4. Check the Additional contributor GitHub username(s) field and correct any missing or incorrect usernames.");
+ console.error("5. After the CLA is submitted or contributor usernames are corrected, edit and save the pull request description to run the verification again.");
+
+ const annotationMessage =
+ `No matching CLA submission was found for: ${missingUsers.join(", ")}. ` +
+ "Each listed contributor must submit the OED CLA using their exact GitHub username. " +
+ "After signing or correcting contributor usernames, edit and save the pull request description to rerun verification.";
+ console.error(`::error title=Signed CLA Verification Failed::${annotationMessage}`);
+ process.exit(1);
+ }
+
+ console.log("Signed CLA verification passed.");
+ console.log(`Verified contributor username(s): ${[...contributorsToCheck].join(", ")}`);
+}
+
+main().catch((error) => {
+ console.error("Unexpected error while checking signed CLA verification.");
+ console.error(error);
+ process.exit(1);
+});
\ No newline at end of file