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
+ )