This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 723b06b38a9 Add optional AI agent rules to React plugin template 
(#69994)
723b06b38a9 is described below

commit 723b06b38a95608bb082494b973d48d25e7a6808
Author: Shae Alhusayni <[email protected]>
AuthorDate: Tue Jul 21 07:58:52 2026 -0700

    Add optional AI agent rules to React plugin template (#69994)
    
    * Add optional AI agent rules to React plugin template
    
    * Address React plugin AI rule feedback
---
 dev/react-plugin-tools/README.md                   |  8 +++
 dev/react-plugin-tools/bootstrap.py                | 23 +++++-
 .../react_plugin_template/ai-agent-rules/README.md | 43 +++++++++++
 .../ai-agent-rules/airflow-plugin.md}              | 48 ++++---------
 .../ai-agent-rules/react-typescript.md}            | 49 +++++--------
 .../ai-agent-rules/testing.md}                     | 42 +++--------
 scripts/tests/test_react_plugin_bootstrap.py       | 83 ++++++++++++++++++++++
 7 files changed, 196 insertions(+), 100 deletions(-)

diff --git a/dev/react-plugin-tools/README.md b/dev/react-plugin-tools/README.md
index 9dc95e7dacb..4f3ac45d165 100644
--- a/dev/react-plugin-tools/README.md
+++ b/dev/react-plugin-tools/README.md
@@ -25,6 +25,7 @@
   - [Overview](#overview)
   - [Files](#files)
   - [Quick Start](#quick-start)
+  - [Optional AI Agent Rules](#optional-ai-agent-rules)
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
@@ -57,3 +58,10 @@ python bootstrap.py my-awesome-plugin
 # Or specify a custom directory
 python bootstrap.py my-awesome-plugin --dir 
/path/to/my-projects/my-awesome-plugin
 ```
+
+## Optional AI Agent Rules
+
+The bootstrap tool asks whether to include AI agent coding rules. Answering 
yes adds an
+`ai-agent-rules/` directory to the generated project with Airflow React plugin 
conventions and
+validation commands. The files use tool-agnostic Markdown, so follow the 
generated directory's
+README to adapt them to the format supported by your AI coding tool.
diff --git a/dev/react-plugin-tools/bootstrap.py 
b/dev/react-plugin-tools/bootstrap.py
index 3406b26fe92..a64014e6f3c 100755
--- a/dev/react-plugin-tools/bootstrap.py
+++ b/dev/react-plugin-tools/bootstrap.py
@@ -33,6 +33,8 @@ import shutil
 import sys
 from pathlib import Path
 
+AI_AGENT_RULES_DIR = "ai-agent-rules"
+
 
 def get_template_dir() -> Path:
     """Get the template directory path."""
@@ -46,6 +48,12 @@ def get_template_dir() -> Path:
     return template_dir
 
 
+def should_include_ai_agent_rules() -> bool:
+    """Ask whether AI agent coding rules should be included."""
+    answer = input("Include AI agent coding rules? [y/N]: ")
+    return answer.strip().casefold() in {"y", "yes"}
+
+
 def replace_template_variables(content: str, project_name: str) -> str:
     """Replace template variables in file content."""
     return content.replace("{{PROJECT_NAME}}", project_name)
@@ -66,11 +74,15 @@ def remove_apache_license_header(content: str, 
file_extension: str) -> str:
     return content
 
 
-def copy_template_files(template_dir: Path, project_path: Path, project_name: 
str) -> None:
+def copy_template_files(
+    template_dir: Path, project_path: Path, project_name: str, *, 
include_ai_agent_rules: bool
+) -> None:
     for item in template_dir.rglob("*"):
         if item.is_file():
             # Calculate relative path from template root
             rel_path = item.relative_to(template_dir)
+            if not include_ai_agent_rules and rel_path.parts[0] == 
AI_AGENT_RULES_DIR:
+                continue
             target_path = project_path / rel_path
 
             target_path.parent.mkdir(parents=True, exist_ok=True)
@@ -105,6 +117,8 @@ def bootstrap_react_plugin(args) -> None:
         print("Error: Project name should only contain letters, numbers, 
hyphens, and underscores")
         sys.exit(1)
 
+    include_ai_agent_rules = should_include_ai_agent_rules()
+
     print(f"Creating React plugin project: {project_name}")
     print(f"Target directory: {project_path}")
     print(f"Template directory: {template_dir}")
@@ -114,7 +128,12 @@ def bootstrap_react_plugin(args) -> None:
     try:
         # Copy template files
         print("Copying template files...")
-        copy_template_files(template_dir, project_path, project_name)
+        copy_template_files(
+            template_dir,
+            project_path,
+            project_name,
+            include_ai_agent_rules=include_ai_agent_rules,
+        )
 
         print(f"\n✅ Successfully created {project_name}!")
         print("\nNext steps:")
diff --git 
a/dev/react-plugin-tools/react_plugin_template/ai-agent-rules/README.md 
b/dev/react-plugin-tools/react_plugin_template/ai-agent-rules/README.md
new file mode 100644
index 00000000000..77a1f3ec020
--- /dev/null
+++ b/dev/react-plugin-tools/react_plugin_template/ai-agent-rules/README.md
@@ -0,0 +1,43 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+
+<!-- START doctoc generated TOC please keep comment here to allow auto update 
-->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents**  *generated with 
[DocToc](https://github.com/thlorenz/doctoc)*
+
+- [AI agent rules](#ai-agent-rules)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+# AI agent rules
+
+These Markdown files describe the coding and validation conventions for this 
Airflow React plugin.
+They are deliberately tool-agnostic and are not loaded automatically by AI 
coding tools.
+
+Adapt the files to the format supported by your tool:
+
+- Cursor project rules belong in `.cursor/rules/` and use the `.mdc` 
extension. Add any metadata
+  required for the desired rule scope. See the [Cursor rules 
documentation](https://docs.cursor.com/context/rules).
+- GitHub Copilot can use a repository-wide `.github/copilot-instructions.md` 
file or path-specific
+  `.github/instructions/*.instructions.md` files. Path-specific files require 
`applyTo` frontmatter.
+  See the [GitHub Copilot custom instructions 
documentation](https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions).
+- For other tools, consult their documentation and copy or convert these files 
as needed.
+
+Keep the adapted rules version-controlled and update them when the plugin's 
dependencies, scripts,
+or build configuration change.
diff --git a/dev/react-plugin-tools/README.md 
b/dev/react-plugin-tools/react_plugin_template/ai-agent-rules/airflow-plugin.md
similarity index 51%
copy from dev/react-plugin-tools/README.md
copy to 
dev/react-plugin-tools/react_plugin_template/ai-agent-rules/airflow-plugin.md
index 9dc95e7dacb..9742b2b6f2f 100644
--- a/dev/react-plugin-tools/README.md
+++ 
b/dev/react-plugin-tools/react_plugin_template/ai-agent-rules/airflow-plugin.md
@@ -21,39 +21,21 @@
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
 **Table of Contents**  *generated with 
[DocToc](https://github.com/thlorenz/doctoc)*
 
-- [React Plugin Development Tools](#react-plugin-development-tools)
-  - [Overview](#overview)
-  - [Files](#files)
-  - [Quick Start](#quick-start)
+- [Airflow plugin integration](#airflow-plugin-integration)
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
-# React Plugin Development Tools
-
-This directory contains tools for developing React-based Airflow plugins that 
can be dynamically loaded into the Airflow UI.
-
-## Overview
-
-These tools help you create React plugin projects that:
-
-- Build as libraries compatible with dynamic imports
-- Share React instances with the host Airflow application
-- Follow Airflow's UI development patterns and standards
-- Include proper TypeScript configuration and build setup
-
-## Files
-
-- `bootstrap.py` - CLI tool to create new React plugin projects
-- `react_plugin_template/` - Template directory with all the necessary files
-
-## Quick Start
-
-### Create a New Plugin Project
-
-```bash
-# From the dev/react-plugin-tools directory
-python bootstrap.py my-awesome-plugin
-
-# Or specify a custom directory
-python bootstrap.py my-awesome-plugin --dir 
/path/to/my-projects/my-awesome-plugin
-```
+# Airflow plugin integration
+
+- Preserve the library build and the default component export from 
`src/main.tsx`; Airflow loads the
+  generated bundle dynamically.
+- Keep React, React DOM, React Router, and the JSX runtime external in 
`vite.config.ts`. They are
+  shared with the Airflow host application and bundling another copy can break 
hooks or routing.
+- Keep the `AirflowPlugin` UMD global name unless the host integration changes 
with it.
+- Build UI with Chakra components and semantic theme tokens. Avoid raw color 
values and styling that
+  bypasses the inherited Airflow theme.
+- Integrate through Airflow's documented public plugin and REST API surfaces. 
Plugins should use the
+  Public API, not private APIs such as the UI API, which is intended for 
Airflow's Core UI and does
+  not follow SemVer.
+- Treat the React plugin interface as experimental and verify compatibility 
when upgrading external
+  dependencies shared with Airflow.
diff --git a/dev/react-plugin-tools/README.md 
b/dev/react-plugin-tools/react_plugin_template/ai-agent-rules/react-typescript.md
similarity index 51%
copy from dev/react-plugin-tools/README.md
copy to 
dev/react-plugin-tools/react_plugin_template/ai-agent-rules/react-typescript.md
index 9dc95e7dacb..f34b5cd49c0 100644
--- a/dev/react-plugin-tools/README.md
+++ 
b/dev/react-plugin-tools/react_plugin_template/ai-agent-rules/react-typescript.md
@@ -21,39 +21,22 @@
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
 **Table of Contents**  *generated with 
[DocToc](https://github.com/thlorenz/doctoc)*
 
-- [React Plugin Development Tools](#react-plugin-development-tools)
-  - [Overview](#overview)
-  - [Files](#files)
-  - [Quick Start](#quick-start)
+- [React and TypeScript](#react-and-typescript)
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
-# React Plugin Development Tools
-
-This directory contains tools for developing React-based Airflow plugins that 
can be dynamically loaded into the Airflow UI.
-
-## Overview
-
-These tools help you create React plugin projects that:
-
-- Build as libraries compatible with dynamic imports
-- Share React instances with the host Airflow application
-- Follow Airflow's UI development patterns and standards
-- Include proper TypeScript configuration and build setup
-
-## Files
-
-- `bootstrap.py` - CLI tool to create new React plugin projects
-- `react_plugin_template/` - Template directory with all the necessary files
-
-## Quick Start
-
-### Create a New Plugin Project
-
-```bash
-# From the dev/react-plugin-tools directory
-python bootstrap.py my-awesome-plugin
-
-# Or specify a custom directory
-python bootstrap.py my-awesome-plugin --dir 
/path/to/my-projects/my-awesome-plugin
-```
+# React and TypeScript
+
+- Write function components and hooks. Do not introduce class components.
+- Keep TypeScript strict. Define explicit component props and domain types; do 
not use `any` or
+  suppress type errors.
+- Use the configured `src` path alias instead of deep relative imports.
+- Calculate derived values while rendering instead of synchronizing them with 
`useState` and
+  `useEffect`.
+- Use `useEffect` only to synchronize with an external system. Keep 
dependencies complete and make
+  cleanup symmetrical with setup.
+- Handle loading, error, empty, and success states for asynchronous work.
+- Preserve keyboard behavior, focus management, accessible names, and semantic 
elements when adding
+  or changing interactions.
+- Keep reusable components focused and place shared application code under 
`src/` rather than in the
+  library entry point.
diff --git a/dev/react-plugin-tools/README.md 
b/dev/react-plugin-tools/react_plugin_template/ai-agent-rules/testing.md
similarity index 51%
copy from dev/react-plugin-tools/README.md
copy to dev/react-plugin-tools/react_plugin_template/ai-agent-rules/testing.md
index 9dc95e7dacb..a2a2380426f 100644
--- a/dev/react-plugin-tools/README.md
+++ b/dev/react-plugin-tools/react_plugin_template/ai-agent-rules/testing.md
@@ -21,39 +21,17 @@
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
 **Table of Contents**  *generated with 
[DocToc](https://github.com/thlorenz/doctoc)*
 
-- [React Plugin Development Tools](#react-plugin-development-tools)
-  - [Overview](#overview)
-  - [Files](#files)
-  - [Quick Start](#quick-start)
+- [Testing and validation](#testing-and-validation)
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
-# React Plugin Development Tools
+# Testing and validation
 
-This directory contains tools for developing React-based Airflow plugins that 
can be dynamically loaded into the Airflow UI.
-
-## Overview
-
-These tools help you create React plugin projects that:
-
-- Build as libraries compatible with dynamic imports
-- Share React instances with the host Airflow application
-- Follow Airflow's UI development patterns and standards
-- Include proper TypeScript configuration and build setup
-
-## Files
-
-- `bootstrap.py` - CLI tool to create new React plugin projects
-- `react_plugin_template/` - Template directory with all the necessary files
-
-## Quick Start
-
-### Create a New Plugin Project
-
-```bash
-# From the dev/react-plugin-tools directory
-python bootstrap.py my-awesome-plugin
-
-# Or specify a custom directory
-python bootstrap.py my-awesome-plugin --dir 
/path/to/my-projects/my-awesome-plugin
-```
+- Add a colocated `.test.tsx` file for new components and changed behavior.
+- Use Vitest and Testing Library to test behavior visible to users. Prefer 
accessible queries and
+  user interactions over implementation details.
+- Cover relevant loading, error, empty, and success states as well as 
important interactions.
+- Do not test React, Chakra UI, React Router, or other third-party internals.
+- Keep tests deterministic and restore mocks between tests.
+- Before finishing a change, run `pnpm lint`, `pnpm test`, and `pnpm build`. 
Run `pnpm format` when
+  source formatting changes.
diff --git a/scripts/tests/test_react_plugin_bootstrap.py 
b/scripts/tests/test_react_plugin_bootstrap.py
new file mode 100644
index 00000000000..435e8e113bb
--- /dev/null
+++ b/scripts/tests/test_react_plugin_bootstrap.py
@@ -0,0 +1,83 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import importlib.util
+import sys
+from pathlib import Path
+from types import SimpleNamespace
+from unittest import mock
+
+import pytest
+
+MODULE_PATH = Path(__file__).resolve().parents[2] / "dev" / 
"react-plugin-tools" / "bootstrap.py"
+
+
[email protected]
+def bootstrap_module():
+    module_name = "test_react_plugin_bootstrap_module"
+    sys.modules.pop(module_name, None)
+    spec = importlib.util.spec_from_file_location(module_name, MODULE_PATH)
+    assert spec is not None and spec.loader is not None
+    module = importlib.util.module_from_spec(spec)
+    sys.modules[module_name] = module
+    try:
+        spec.loader.exec_module(module)
+        yield module
+    finally:
+        sys.modules.pop(module_name, None)
+
+
[email protected](
+    "answer,expected",
+    [
+        ("y", True),
+        ("YES", True),
+        (" yes ", True),
+        ("", False),
+        ("n", False),
+        ("anything else", False),
+    ],
+)
[email protected]("builtins.input")
+def test_should_include_ai_agent_rules(mock_input, bootstrap_module, answer, 
expected):
+    mock_input.return_value = answer
+
+    assert bootstrap_module.should_include_ai_agent_rules() is expected
+
+
[email protected](("answer", "include_ai_agent_rules"), [("yes", True), 
("", False)])
[email protected]("builtins.input")
+def test_bootstrap_conditionally_includes_ai_agent_rules(
+    mock_input, bootstrap_module, tmp_path, monkeypatch, answer, 
include_ai_agent_rules
+):
+    mock_input.return_value = answer
+    template_dir = tmp_path / "template"
+    rules_dir = template_dir / bootstrap_module.AI_AGENT_RULES_DIR
+    rules_dir.mkdir(parents=True)
+    (template_dir / "README.md").write_text("project", encoding="utf-8")
+    (rules_dir / "react.md").write_text("rules", encoding="utf-8")
+    project_path = tmp_path / "project"
+    get_template_dir = mock.create_autospec(bootstrap_module.get_template_dir, 
return_value=template_dir)
+    monkeypatch.setattr(bootstrap_module, "get_template_dir", get_template_dir)
+
+    bootstrap_module.bootstrap_react_plugin(SimpleNamespace(name="project", 
dir=project_path))
+
+    assert (project_path / "README.md").is_file()
+    assert (project_path / bootstrap_module.AI_AGENT_RULES_DIR / 
"react.md").exists() is (
+        include_ai_agent_rules
+    )

Reply via email to