Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agents/skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ This directory contains specialized skills (recipes) to guide contributors and A
- [Add Task Pipeline](./add-task-pipeline/SKILL.md) — TypeScript task pipelines and React hooks.
- [Model Schema Validation](./model-schema-validation/SKILL.md) — SymbolicTensor schemas and validation.
- [Verify and Build](./verify-and-build/SKILL.md) — TypeScript typechecking, native rebuilding, and troubleshooting.
- [Skills Maintenance](./skills-maintenance/SKILL.md) — Keeping skills synchronized with core primitives.
67 changes: 25 additions & 42 deletions .agents/skills/add-native-extension/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,23 @@ namespace rnexecutorch::extensions::<domain>

#### 2. Source (`cpp/extensions/<domain>/operations.cpp`)

- Extract input and output tensors as `TensorHostObject` pointers.
- Check bounds, shapes, types, and verify that the output tensor is **not the same instance** as the input (no unsafely managed in-place mutation).
- Lock tensors using `std::shared_lock` (for inputs) and `std::unique_lock` (for outputs).
- Validate, type-check, and extract input/output tensors using `tensor::fromJs`, specifying expected `DType` and shape constraints where possible.
- Convert primitive parameters using `conversions::asType<T>`.
- Prevent in-place mutations (aliasing) using `tensor::checkNotSameTensor`.
- Lock tensors for thread-safe access using `tensor::tryLockShared` (for inputs) and `tensor::tryLockUnique` (for outputs), which also ensure the underlying memory buffer has not been disposed.

```cpp
#include "operations.h"
#include "core/tensor.h"
#include "core/conversions.h"
#include "core/tensor_helpers.h"
#include <algorithm>

namespace rnexecutorch::extensions::<domain>
{
namespace jsi = facebook::jsi;
using TensorHostObject = rnexecutorch::core::tensor::TensorHostObject;
namespace conversions = rnexecutorch::core::conversions;
namespace tensor = rnexecutorch::core::tensor;
using rnexecutorch::core::types::DType;

void install_customOp(jsi::Runtime &rt, jsi::Object &module)
{
Expand All @@ -82,47 +86,24 @@ namespace rnexecutorch::extensions::<domain>
throw jsi::JSError(rt, "Usage: customOp(src, dst, factor)");
}

// 2. Validate input and output types
auto srcObj = args[0].asObject(rt);
auto dstObj = args[1].asObject(rt);
if (!srcObj.isHostObject<TensorHostObject>(rt) || !dstObj.isHostObject<TensorHostObject>(rt))
{
throw jsi::JSError(rt, "customOp: Arguments src and dst must be Tensors");
}

auto src = srcObj.getHostObject<TensorHostObject>(rt);
auto dst = dstObj.getHostObject<TensorHostObject>(rt);
double factor = args[2].asNumber();

// 3. Prevent in-place mutations
if (src.get() == dst.get())
{
throw jsi::JSError(rt, "customOp: In-place operations (src == dst) are not supported.");
}
// 2. Validate, extract input/output tensors and check DType/Shape constraints using fromJs
auto src = tensor::fromJs(rt, "customOp: src", args[0], DType::float32, std::nullopt);
auto dst = tensor::fromJs(rt, "customOp: dst", args[1], DType::float32, src->shape_);

// 4. Validate metadata compatibility
if (src->shape_ != dst->shape_ || src->dtype_ != dst->dtype_)
{
throw jsi::JSError(rt, "customOp: src and dst shape and dtype must match");
}
// Extract and convert arguments using conversions::asType
double factor = conversions::asType<double>(rt, "customOp: factor", args[2]);

// 5. Lock underlying buffers
std::shared_lock<std::shared_mutex> src_lock(src->mutex_, std::try_to_lock);
std::unique_lock<std::shared_mutex> dst_lock(dst->mutex_, std::try_to_lock);
if (!src_lock.owns_lock() || !dst_lock.owns_lock())
{
throw jsi::JSError(rt, "customOp: Tensors are currently in use");
}
// 3. Prevent in-place mutations (aliasing)
tensor::checkNotSameTensor(rt, "customOp: src", src, "customOp: dst", dst);

if (!src->data_ || !dst->data_)
{
throw jsi::JSError(rt, "customOp: Tensor has been disposed");
}
// 4. Lock underlying buffers and verify they aren't disposed
auto srcLock = tensor::tryLockShared(rt, "customOp: src", src);
auto dstLock = tensor::tryLockUnique(rt, "customOp: dst", dst);

// 6. Perform the computation
// 5. Perform the computation
const float *srcData = reinterpret_cast<const float *>(src->data_.get());
float *dstData = reinterpret_cast<float *>(dst->data_.get());
size_t size = src->size();
size_t size = src->numel_;

for (size_t i = 0; i < size; ++i)
{
Expand Down Expand Up @@ -201,8 +182,10 @@ When adding a native extension, verify that:

- [ ] You only implemented in C++ if the operation takes `> 5%` of the total inference budget.
- [ ] No JSI Tensors are implicitly allocated and returned in the C++ code.
- [ ] Input and output tensors are locked using `std::shared_lock` and `std::unique_lock` respectively.
- [ ] In-place mutation is explicitly prevented by checking that `src != dst`.
- [ ] Input and output tensors are extracted and validated using `tensor::fromJs` with strict expected `DType` and shape constraints where possible.
- [ ] Primitive arguments are extracted and converted using `conversions::asType<T>`.
- [ ] In-place mutation is explicitly prevented using `tensor::checkNotSameTensor`.
- [ ] Input and output tensors are locked using `tensor::tryLockShared` and `tensor::tryLockUnique` respectively.
- [ ] No default parameter values are defined in the C++ header/source files.
- [ ] The custom operation install function is registered in both the domain `install` function and core [cpp/RnExecutorch.cpp](../../../packages/react-native-executorch/cpp/RnExecutorch.cpp).
- [ ] The TypeScript wrapper imports and uses `rnexecutorchJsi` instead of the global `__rnexecutorch_jsi__`.
Expand Down
1 change: 1 addition & 0 deletions .agents/skills/core-guidelines/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Use the following index to locate the specific procedural guides for your task:
| **Create a task pipeline or hook** | [SKILL.md](../add-task-pipeline/SKILL.md) | Guide to building end-to-end TS pipelines (e.g. object detection) and exposing them via React hooks. |
| **Verify, rebuild, or troubleshoot changes** | [SKILL.md](../verify-and-build/SKILL.md) | Workflows for rebuilding TS/C++ and resolving common JSI runtime errors. |
| **Validate model constraints & schemas** | [SKILL.md](../model-schema-validation/SKILL.md) | Guide on specifying SymbolicTensor constraints and shapes for model signature validation. |
| **Maintain or refactor codebase patterns** | [SKILL.md](../skills-maintenance/SKILL.md) | Guide to keeping workspace skills in sync with codebase state to prevent documentation decay. |

---

Expand Down
46 changes: 46 additions & 0 deletions .agents/skills/skills-maintenance/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
name: skills-maintenance
description: Use when introducing new patterns, refactoring core primitives, adding helper utilities, or changing standard idioms to ensure the workspace skills remain up-to-date.
metadata:
id: skills_maintenance
scope: general development
---

# Skill: Skills Maintenance & Documentation Integrity

Use this guide when you introduce, modify, or deprecate core codebase patterns, helpers, utilities, or design decisions. This skill ensures that the agent keeps workspace skills in sync with the codebase state to prevent documentation decay and outdated copy-pasting.

---

## 🚦 Core Guidelines

1. **Detect Pattern Shifts**:
- Whenever you implement a new utility, helper class, or conversion method (e.g., in `core/conversions` or `core/tensor_helpers`), or change how native bindings are structured:
- Check if these new primitives replace older manual patterns (e.g., replacing manual locking/extraction with helper library calls).
- Identify which existing skills (in `.agents/skills/`) contain code examples, templates, or instructions that are affected by this shift.

2. **Proactively Update Affected Skills**:
- Do not wait for the user to explicitly ask to update the skills. When a core API or idiom changes, update the relevant `SKILL.md` files as part of the implementation or refactoring task.
- Specifically inspect:
- [add-native-extension](../add-native-extension/SKILL.md) for C++ JSI & locking idioms.
- [add-task-pipeline](../add-task-pipeline/SKILL.md) for TypeScript pipeline orchestration, pre-allocation, and lifecycle hooks.
- [model-schema-validation](../model-schema-validation/SKILL.md) for schema verification constraints.
- [verify-and-build](../verify-and-build/SKILL.md) for compilation and troubleshooting steps.

3. **Verify Example Correctness**:
- Ensure all code blocks and examples in updated skills compile/work and match actual usage in the repository.
- Do not leave references to deprecated fields, functions, or old workflows.

4. **Update the Checklist**:
- If a new verification rule is introduced (e.g., "must use helper X instead of manual code Y"), update the **Verification Checklist** section at the bottom of the relevant skill's `SKILL.md`.

---

## 📋 Verification Checklist

When introducing or refactoring a pattern, verify that:

- [ ] You identified all workspace skills containing code snippets or instructions affected by the change.
- [ ] You updated all examples and templates to reflect the new, correct APIs and idioms.
- [ ] You updated the corresponding checklists to enforce the new conventions.
- [ ] The updated skill files are valid markdown and keep lines reasonably short.
2 changes: 1 addition & 1 deletion .agents/skills/verify-and-build/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ CLANG_TIDY=$(brew --prefix llvm)/bin/clang-tidy yarn workspace react-native-exec

### 2. `check-cpp-warnings.sh` — clangd warning set

Compiles staged (or explicitly passed) `cpp/` sources with the same flags clangd uses in the editor (from `compile_flags.txt` + `-W` flags in `.clangd`), so the editor and CI stay in sync.
Compiles staged (or explicitly passed) `cpp/` sources with the same flags clangd uses in the editor (from `compile_flags.txt` + `-W` flags in `.clangd`), so the editor and CI stay in sync. Run from the package root (`packages/react-native-executorch/`):

```bash
# The script takes file paths as positional arguments and exits 0 immediately
Expand Down
2 changes: 1 addition & 1 deletion .cspell-wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,4 @@ thresholding
binarization
bugprone
NOLINTNEXTLINE

nullopt