Nx Generate
nomcopter/react-mosaic
Generate code using nx generators. An agent skill from nomcopter/react-mosaic.
Write a C extension whose custom types can live inside a bocpy Cown and travel between worker sub-interpreters.
$ npx skills add microsoft/bocpy --skill c-extensions-with-bocpy -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install microsoft/bocpy c-extensions-with-bocpy --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/microsoft/bocpy.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/c-extensions-with-bocpy .claude/skills/c-extensions-with-bocpy && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "c-extensions-with-bocpy" agent skill from https://github.com/microsoft/bocpy/tree/main/.github/skills/c-extensions-with-bocpy into .claude/skills/c-extensions-with-bocpy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "c-extensions-with-bocpy", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/microsoft/bocpy/tree/main/.github/skills/c-extensions-with-bocpyType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add microsoft/bocpy --skill c-extensions-with-bocpy -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install microsoft/bocpy c-extensions-with-bocpy --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/microsoft/bocpy.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.github/skills/c-extensions-with-bocpy .agents/skills/c-extensions-with-bocpy && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "c-extensions-with-bocpy" agent skill from https://github.com/microsoft/bocpy/tree/main/.github/skills/c-extensions-with-bocpy into .agents/skills/c-extensions-with-bocpy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "c-extensions-with-bocpy", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add microsoft/bocpy --skill c-extensions-with-bocpy -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install microsoft/bocpy c-extensions-with-bocpy --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/microsoft/bocpy.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.github/skills/c-extensions-with-bocpy .cursor/skills/c-extensions-with-bocpy && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "c-extensions-with-bocpy" agent skill from https://github.com/microsoft/bocpy/tree/main/.github/skills/c-extensions-with-bocpy into .cursor/skills/c-extensions-with-bocpy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "c-extensions-with-bocpy", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/microsoft/bocpy.git --path .github/skills/c-extensions-with-bocpy--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add microsoft/bocpy --skill c-extensions-with-bocpy -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install microsoft/bocpy c-extensions-with-bocpy --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/microsoft/bocpy.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.github/skills/c-extensions-with-bocpy .gemini/skills/c-extensions-with-bocpy && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "c-extensions-with-bocpy" agent skill from https://github.com/microsoft/bocpy/tree/main/.github/skills/c-extensions-with-bocpy into .gemini/skills/c-extensions-with-bocpy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "c-extensions-with-bocpy", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install microsoft/bocpy c-extensions-with-bocpyInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add microsoft/bocpy --skill c-extensions-with-bocpy -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/microsoft/bocpy.git skills-src && mkdir -p .github/skills && cp -r skills-src/.github/skills/c-extensions-with-bocpy .github/skills/c-extensions-with-bocpy && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "c-extensions-with-bocpy" agent skill from https://github.com/microsoft/bocpy/tree/main/.github/skills/c-extensions-with-bocpy into .github/skills/c-extensions-with-bocpy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "c-extensions-with-bocpy", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add microsoft/bocpy --skill c-extensions-with-bocpy -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install microsoft/bocpy c-extensions-with-bocpy --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/microsoft/bocpy.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.github/skills/c-extensions-with-bocpy .opencode/skills/c-extensions-with-bocpy && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "c-extensions-with-bocpy" agent skill from https://github.com/microsoft/bocpy/tree/main/.github/skills/c-extensions-with-bocpy into .opencode/skills/c-extensions-with-bocpy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "c-extensions-with-bocpy", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
c-extensions-with-bocpyWrite a C extension whose custom types can live inside a bocpy Cown and travel between worker sub-interpreters.
C Extensions With Bocpy is an agent skill from microsoft/bocpy, published by the product's own GitHub organization. Write a C extension whose custom types can live inside a bocpy Cown and travel between worker sub-interpreters. Use when: writing a native type (matrix, buffer, GPU handle, opaque C resource) that needs to round-trip through @when, send/receive, or any other bocpy primitive that crosses interpreter boundaries; designing the producer/consumer XIData callbacks; implementing proto-Region ownership semantics; setting up multi-phase init and per-interpreter type registration. Covers the bocpy public C ABI (bocpy.h…
Its SKILL.md is about 5.1k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Development, covering Project scaffolding. The repository describes itself as: Behavior-Oriented Concurrency in Python. The licence is MIT.
5 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit c8f3ceb. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
pipFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use pip, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
C Extensions With Bocpy loads about 5.1k tokens when it runs. Until then it costs about 174 tokens; SKILL.md has 2,040 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from microsoft/bocpy at commit c8f3ceb, republished under its MIT licence (© microsoft). 2,040 words, ~5,144 tokens.
.claude/skills/c-extensions-with-bocpy/SKILL.md (or your agent's skills folder).This skill is for writing a downstream C extension whose custom
types must travel through bocpy's runtime — placed inside a
Cown, scheduled with @when, or shipped via send/receive
across worker sub-interpreters.
Read
thinking-in-bocfirst. The C ABI does not change the BOC mental model. Your C type is still wrapped in aCown, behaviors are still scheduled with@when, and ordering still comes from the cown graph — not from anything you do in C. The C ABI only buys you a safe, zero-copy way to cross the interpreter boundary; the choreography between behaviors is unchanged.
You need a C extension that uses the bocpy public C ABI only when all three of the following are true:
Cown and be
acquired by @when behaviors on worker sub-interpreters.If your type is pure-Python, pickleable, or you are happy with a copy
on each interpreter crossing, you do not need this skill —
Cown(my_obj) already works. Read the BOC primer in the project
copilot instructions and stop here.
The canonical worked example shipped by bocpy lives in
templates/c_abi_consumer/ of the bocpy source tree — copy it as the
starting point for a new extension. The Matrix type in
src/bocpy/_math.c is the in-tree reference implementation.
A bocpy-aware C type implements proto-Region ownership: a single
atomic owner field on the impl identifies the interpreter that may
read or write the payload. The producer-side XIData callback CASes
that field from bocpy_interpid() to BOCPY_NO_OWNER; the
consumer-side callback CASes it back from BOCPY_NO_OWNER to
bocpy_interpid(). Any data accessor that reads or writes the
payload first asserts bocpy_interpid() == atomic_load(&impl->owner)
and raises RuntimeError otherwise.
This is not the full Lungfish region model — there are no nested regions, no freeze, no merge, no borrow tracking — but it is enough to turn "a shareable pointer" into "a resource owned by exactly one interpreter at a time", which is what BOC needs.
What the proto-Region contract gives you in practice:
RuntimeError until ownership returns. This catches races that
pointer-only sharing would miss.setup.pyUse bocpy.get_include() for the header search path and
bocpy.get_sources() for the MSVC out-of-line atomics shim (no-op
elsewhere). Copy this verbatim from templates/c_abi_consumer/setup.py
and change the module name:
from setuptools import Extension, setup
import bocpy
setup(
ext_modules=[
Extension(
"_your_extension",
sources=["src/_your_extension.c"] + bocpy.get_sources(),
include_dirs=[bocpy.get_include()],
),
],
)pyproject.tomlDeclare bocpy in both [build-system].requires (so an isolated PEP 517
build can satisfy import bocpy in setup.py) and
[project].dependencies (so installing your wheel installs bocpy at
runtime).
Always install with pip install --no-build-isolation so the build
resolves headers against the bocpy install actually being tested,
not whatever PyPI happens to publish.
#include <bocpy/bocpy.h>
/* Compile-time guard: bocpy.h must not leak Py_BUILD_CORE. */
#ifdef Py_BUILD_CORE
#error "Py_BUILD_CORE leaked from bocpy.h"
#endifRules:
<bocpy/bocpy.h> includes <Python.h> internally. It is
order-insensitive with respect to <Python.h> itself.<stdio.h>,
<string.h>, ...) in the same translation unit, the same way
<Python.h> must — CPython forbids system headers before
Python.h.<bocpy/bocpy.h> is not supported from C++ in this
release. C++ consumers must wrap the ABI in a thin C translation
unit.Everything below is exposed by <bocpy/bocpy.h>. Treat anything else
under the bocpy package directory as private.
| Symbol | Purpose |
|---|---|
BOCPY_ABI | Integer macro. Gate code on BOCPY_ABI >= N if you need a minimum revision. |
BOCPY_NO_OWNER | Sentinel -2 meaning "no interpreter owns this impl right now". Use as initial / in-flight value of the owner field. |
bocpy_interpid() | static inline int_least64_t. Returns the running interpreter's ID, pre-typed for the atomic CAS parameter list. Must be called with the GIL held / attached. |
atomic_int_least64_t | 64-bit atomic integer type. Sequentially consistent on every supported target. |
atomic_load(p), atomic_store(p, v), atomic_fetch_add(p, v), atomic_compare_exchange_strong(p, &exp, des) | The four atomic ops you will need. SC on every supported MSVC target (x86, x64, ARM64); plain <stdatomic.h> elsewhere. |
thread_local | Macro for thread-local storage. Use to cache the per-interpreter LOCAL_STATE so callbacks don't walk PyModule_GetState every call. |
XIDATA_T | Opaque struct holding a serialised cross-interpreter handoff. |
XIDATA_NEW(), XIDATA_GETXIDATA(value, xidata), XIDATA_FREE(xidata), XIDATA_SET_FREE(xidata, fn) | Lifecycle ops. You normally only call XIDATA_INIT from your producer callback; the rest is called by bocpy. |
XIDATA_INIT(xidata, interp, data, obj, new_object) | Initialise an XIDATA_T. interp must be the interpreter that currently owns data. Buffer must be freshly allocated. |
XIDATA_NEWOBJECT | Type alias for the consumer-side reconstruction callback. |
XIDATA_REGISTERCLASS(type, cb) | Register a Python type as cross-interpreter shareable with producer callback cb. Per-interpreter — call from the exec slot. |
XIDATA_GETDATA_FUNC(name) | Macro that declares a producer callback with a portable (tstate, obj, xidata) signature. Hides the legacy-CPython signature change. |
Internal headers and surfaces (boc_compat.h, boc_cown.h,
boc_sched.h, boc_tags.h, boc_terminator.h, boc_noticeboard.h,
the typed atomics, the BOC mutex/condvar types, boc_yield, boc_now_*,
boc_sleep_ns, etc.) are not public. Do not depend on them.
Counter-sized walkthroughThe full annotated source is templates/c_abi_consumer/src/_bocpy_probe.c.
The skeleton below shows the five places you must get right.
A heap-allocated C struct that lives outside any single Python object's lifetime. Carries its own atomic refcount and its atomic owner field.
typedef struct {
atomic_int_least64_t refcount; /* number of wrappers holding this impl */
atomic_int_least64_t owner; /* interpreter ID, or BOCPY_NO_OWNER in flight */
/* ... your payload here ... */
} your_impl;
static your_impl *your_impl_new(void) {
your_impl *impl = PyMem_RawMalloc(sizeof(*impl));
if (impl == NULL) return NULL;
atomic_store(&impl->refcount, 1);
atomic_store(&impl->owner, bocpy_interpid()); /* born owned */
return impl;
}
static void your_impl_incref(your_impl *impl) {
atomic_fetch_add(&impl->refcount, 1);
}
static void your_impl_decref(your_impl *impl) {
if (atomic_fetch_add(&impl->refcount, -1) == 1) {
/* last holder, free the payload */
PyMem_RawFree(impl);
}
}
static bool your_impl_check_acquired(your_impl *impl, bool set_error) {
if (bocpy_interpid() != atomic_load(&impl->owner)) {
if (set_error)
PyErr_SetString(PyExc_RuntimeError,
"the current interpreter does not own this type");
return false;
}
return true;
}Refcounting and ownership are independent. Any interpreter holding a wrapper drops its ref on dealloc, regardless of who currently owns the impl. The proto-Region check guards data accessors, not the lifetime of the impl itself.
Use PyType_FromModuleAndSpec (not the static-type pattern). Store
the type on per-module state and cache it in a thread_local
LOCAL_STATE so callbacks can find it without walking module state.
typedef struct {
PyTypeObject *your_type;
} your_module_state;
static thread_local your_module_state *LOCAL_STATE;
typedef struct {
PyObject_HEAD
your_impl *impl;
} YourObject;Data accessors must call your_impl_check_acquired before reading
the payload. Identity-only accessors (e.g. an address getter that
returns (uintptr_t)impl) may skip the check — printing the address
of a Region handle without being inside the Region is allowed.
XIDATA_GETDATA_FUNCRuns on the interpreter that currently owns the impl, every time
something asks XIData to package one of your objects. CAS the owner
field from this interpreter to BOCPY_NO_OWNER, then call
XIDATA_INIT. Failing the CAS surfaces as a RuntimeError and
aborts the handoff.
XIDATA_GETDATA_FUNC(_your_shared) {
YourObject *self = (YourObject *)obj;
your_impl *impl = self->impl;
if (impl == NULL) {
PyErr_SetString(PyExc_RuntimeError, "type not initialised");
return -1;
}
int_least64_t expected = bocpy_interpid();
int_least64_t desired = BOCPY_NO_OWNER;
if (!atomic_compare_exchange_strong(&impl->owner, &expected, desired)) {
PyErr_Format(PyExc_RuntimeError,
"cannot share: owned by interpreter %lld",
(long long)expected);
return -1;
}
XIDATA_INIT(xidata, tstate->interp, impl, obj, _new_your_object);
return 0;
}Why XIDATA_GETDATA_FUNC and not a hand-written signature? On older
CPython the callback is (obj, xidata) only — no tstate. The macro
emits a small trampoline so the body is portable across every
supported CPython.
new_object reconstructionRuns on the interpreter that is taking ownership. CAS the owner
from BOCPY_NO_OWNER to bocpy_interpid(), allocate a fresh
wrapper from the local heap type, and bump the impl refcount.
static PyObject *_new_your_object(XIDATA_T *xidata) {
your_impl *impl = (your_impl *)xidata->data;
int_least64_t expected = BOCPY_NO_OWNER;
int_least64_t desired = bocpy_interpid();
if (!atomic_compare_exchange_strong(&impl->owner, &expected, desired)) {
PyErr_Format(PyExc_RuntimeError,
"cannot acquire (expected BOCPY_NO_OWNER, observed %lld)",
(long long)expected);
return NULL;
}
PyTypeObject *type = LOCAL_STATE->your_type;
YourObject *self = (YourObject *)type->tp_alloc(type, 0);
if (self == NULL) {
/* CRITICAL: roll the owner back so a retry can succeed and
* the impl is not stranded with us as owner without a wrapper. */
atomic_store(&impl->owner, BOCPY_NO_OWNER);
return NULL;
}
self->impl = impl;
your_impl_incref(impl);
return (PyObject *)self;
}The wrapper-allocation rollback is easy to forget and impossible to recover from at runtime. Write it the same time you write the success path.
XIDATA_REGISTERCLASS registers into a per-interpreter registry.
It must run in every interpreter that will reconstruct one of your
objects — which means every worker sub-interpreter, not just the main
one. Single-phase PyModule_Create modules load in the main
interpreter but cannot satisfy Py_MOD_PER_INTERPRETER_GIL_SUPPORTED,
and the registration never runs in worker interpreters; the consumer
callback then dereferences a NULL LOCAL_STATE and segfaults.
Use multi-phase init with a Py_mod_exec slot:
static int _your_module_exec(PyObject *module) {
your_module_state *state =
(your_module_state *)PyModule_GetState(module);
state->your_type = (PyTypeObject *)PyType_FromModuleAndSpec(
module, &YourType_Spec, NULL);
if (state->your_type == NULL) return -1;
if (PyModule_AddType(module, state->your_type) < 0) return -1;
if (XIDATA_REGISTERCLASS(state->your_type, _your_shared)) {
PyErr_SetString(PyExc_RuntimeError,
"could not register type for cross-interpreter sharing");
return -1;
}
LOCAL_STATE = state; /* prime the thread-local cache */
return 0;
}
static PyModuleDef_Slot _your_module_slots[] = {
{Py_mod_exec, (void *)_your_module_exec},
#if PY_VERSION_HEX >= 0x030C0000
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
#endif
#if PY_VERSION_HEX >= 0x030D0000
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
#endif
{0, NULL},
};Also implement m_traverse, m_clear, and m_free to manage the
type stored on per-module state — this is standard multi-phase
hygiene, not bocpy-specific.
import of your extension in every Python file that
schedules @when bodies which observe your types. The
transpiler propagates module-scope import statements into the
exported per-worker module. Runtime helpers like
importlib.import_module(...), __import__(...), or
pytest.importorskip(...) are invisible to the transpiler — a
worker without your extension loaded will skip the exec slot and
the consumer callback will dereference a NULL LOCAL_STATE.XIDATA_REGISTERCLASS belongs in Py_mod_exec, never in PyInit.
The registry is per-interpreter.bocpy_interpid() -> BOCPY_NO_OWNER. Consumer:
BOCPY_NO_OWNER -> bocpy_interpid(). Anything else strands the
impl.@when, never send("tag", c.value) of a proto-Region
resource. send would atomically move the impl out of the cown
mid-behavior and leave the worker unable to release the cown
afterwards. Send a copy (c.value.copy()) or send primitive
summary data (c.value.address, a hash, a slice). The cown itself
is the right primitive for handing the resource to another
behavior — schedule a downstream @when on the same cown.@when. If you find yourself reaching for a threading.Event,
atomic flag, or polling loop to coordinate two behaviors on the
same C type, re-read thinking-in-boc.Py_BUILD_CORE leakage. Guard for it with #error at the
top of your translation unit; a future bocpy refactor that forgets
the #undef should fail your build loudly, not at runtime.| Pitfall | Symptom | Fix |
|---|---|---|
Single-phase PyModule_Create | Loads in main interpreter; segfaults in worker. | Switch to multi-phase init with Py_mod_exec and declare Py_MOD_PER_INTERPRETER_GIL_SUPPORTED. |
XIDATA_REGISTERCLASS in PyInit | Consumer callback sees no type registered in its registry. | Move to the exec slot. |
| Forgetting to roll back owner on alloc failure | Impl stranded with no wrapper; future handoffs fail their CAS. | Add atomic_store(&impl->owner, BOCPY_NO_OWNER); before returning NULL from the consumer callback. |
pytest.importorskip("_your_ext") instead of top-level import | Workers skip the exec slot; consumer dereferences NULL LOCAL_STATE. | Use a plain top-level import _your_ext in any file that schedules @when bodies. |
send("tag", c.value) of a proto-Region object | Worker cannot release the cown afterwards; runtime stalls. | Send a copy or summary data. |
Reading the payload outside a @when | RuntimeError: the current interpreter does not own this type. | Read inside a @when that holds the cown, or use an identity-only accessor (.address). |
| Identity-only getter calling the ownership check | .address raises RuntimeError from interpreters that hold a stale wrapper. | Skip the ownership check on identity/lifetime accessors. |
Static-type pattern (PyTypeObject MyType = {...}) | Type cannot be per-interpreter; LOCAL_STATE cache pattern won't fit. | Use PyType_FromModuleAndSpec and store the type on per-module state. |
Including <stdio.h> before <bocpy/bocpy.h> | Compile error on some toolchains; Python.h ordering rule. | Move <bocpy/bocpy.h> to the very top of the translation unit. |
Hand-written (obj, xidata) producer callback | Compile error on CPython <3.12 (legacy (tstate, obj, xidata)) or vice versa. | Declare the callback with XIDATA_GETDATA_FUNC(name). |
Building without --no-build-isolation | Wheel resolves against PyPI bocpy headers instead of your local install. | Always pip install --no-build-isolation .. |
| Refcount and ownership conflated | __dealloc__ raises RuntimeError from interpreters that hold a stale wrapper. | Keep refcount independent of ownership — drop refs on dealloc regardless of who owns the impl. |
Before declaring a bocpy-aware C extension done, verify all of:
setup.py uses bocpy.get_include() and bocpy.get_sources().pyproject.toml lists bocpy in both [build-system].requires
and [project].dependencies.<bocpy/bocpy.h> at the top, with an
#error guard against Py_BUILD_CORE leakage.Py_MOD_PER_INTERPRETER_GIL_SUPPORTED.PyType_FromModuleAndSpec and stored
on per-module state.XIDATA_REGISTERCLASS is called from the Py_mod_exec slot.XIDATA_GETDATA_FUNC.bocpy_interpid() to
BOCPY_NO_OWNER before XIDATA_INIT.BOCPY_NO_OWNER to
bocpy_interpid() before allocating the wrapper.BOCPY_NO_OWNER if
wrapper allocation fails._check_acquired-style helper
and raise RuntimeError on mismatch.importorskip) and exercises a @when chain that round-trips
a cown carrying one of your types through at least one worker.
Use the testing-with-boc send/receive assertion pattern.pip install --no-build-isolation.See templates/c_abi_consumer/test/test_consumer.py in the bocpy
source tree for the canonical test pattern — a tail-recursive @when
chain that ships a counter cown between workers, with assertions
fired back via send / receive.
© microsoft, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .github/skills/c-extensions-with-bocpy of microsoft/bocpy.
Open the folder on GitHubat commit c8f3ceb
C Extensions With Bocpy next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| C Extensions With Bocpy this skillmicrosoft/bocpy | 201 | — | ~5.1k | Automated safety check: Pass | MIT | |
| Nx Generatenomcopter/react-mosaic | 4.8k | 7 repos | ~1.9k | Automated safety check: Pass | Custom licence | |
| PonytailDavidObando/gsharp | 564 | 8 repos | ~1.7k | Automated safety check: Pass | MIT | |
| Run Nx Generatornrwl/nx | 29k | 2 repos | ~592 | Automated safety check: Notes | MIT | |
| Conductor Setupgemini-cli-extensions/conductor | 3.8k | — | ~4.2k | Automated safety check: Pass | Apache-2.0 | |
| Mirage VFS Adapter Authoringstrukto-ai/mirage | 3.7k | — | ~2.5k | Automated safety check: Pass | Apache-2.0 |
nomcopter/react-mosaic
Generate code using nx generators. An agent skill from nomcopter/react-mosaic.
DavidObando/gsharp
Forces the laziest solution that actually works, simplest, shortest, most minimal.
nrwl/nx
Run Nx generators with prioritization for workspace-plugin generators.
gemini-cli-extensions/conductor
Scaffolds the project and sets up the Conductor environment.
strukto-ai/mirage
Builds or extends a custom Mirage virtual filesystem adapter for an API, database, object store or app data, with a working mount configuration and filesystem tests.
siteboon/claudecodeui
Enforces this repository's TypeScript backend module architecture under server/: feature folders, barrel exports, and where shared types and utilities belong.
microsoft/bocpy
Multi-perspective code review for a branch before merging. An agent skill from microsoft/bocpy.
microsoft/bocpy
Follow bocpy commenting and documentation conventions. An agent skill from microsoft/bocpy.
microsoft/bocpy
Finalize a feature branch for merge. An agent skill from microsoft/bocpy.
microsoft/bocpy
Multi-perspective planning with rebuttal rounds and adversarial review loop.
microsoft/bocpy
Write tests for the bocpy message queue — the lock-free tag-based MPSC ring buffer.
microsoft/bocpy
Think in Behavior-Oriented Concurrency, not threads-and-locks.
Categories
Write a C extension whose custom types can live inside a bocpy Cown and travel between worker sub-interpreters. C Extensions With Bocpy is an agent skill from microsoft/bocpy, published by the product's own GitHub organization. Write a C extension whose custom types can live inside a bocpy Cown and travel between worker sub-interpreters.
C Extensions With Bocpy fits situations like: : writing a native type (matrix; opaque C resource) that needs to round-trip through @when; any other bocpy primitive that crosses interpreter boundaries; designing the producer/consumer XIData callbacks.
Run `npx skills add microsoft/bocpy --skill c-extensions-with-bocpy -a claude-code`. Or copy the skill folder (.github/skills/c-extensions-with-bocpy in microsoft/bocpy) into .claude/skills/c-extensions-with-bocpy in your project. Claude Code loads it when a task matches its description.
Run `npx skills add microsoft/bocpy --skill c-extensions-with-bocpy -a codex`. Or copy the skill folder (.github/skills/c-extensions-with-bocpy in microsoft/bocpy) into .agents/skills/c-extensions-with-bocpy in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add microsoft/bocpy --skill c-extensions-with-bocpy -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/c-extensions-with-bocpy, .gemini/skills/c-extensions-with-bocpy, .github/skills/c-extensions-with-bocpy and .opencode/skills/c-extensions-with-bocpy in your project.
Going by SKILL.md and its folder, C Extensions With Bocpy needs the command-line tools its instructions call (pip). Our summary lists: Python 3.
SKILL.md contains no URLs. Its commands use pip, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
C Extensions With Bocpy is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.1k tokens (SKILL.md is roughly 21k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with C Extensions With Bocpy: Nx Generate (nomcopter/react-mosaic, 4.8k stars), Ponytail (DavidObando/gsharp, 564 stars), Run Nx Generator (nrwl/nx, 29k stars) and Conductor Setup (gemini-cli-extensions/conductor, 3.8k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
microsoft (a GitHub organization, an official publisher) maintains it in microsoft/bocpy, which has 201 GitHub stars. The repository holds 9 skills in this directory. The repository was last updated on September 28, 2026.
Source: microsoft/bocpy on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.