Official agent skill

Testing With Boc

by microsoft in microsoft/bocpy

Write tests for bocpy Behavior-Oriented Concurrency code. An agent skill from microsoft/bocpy.

OfficialMITAuto-check passedTesting & QA

Install Testing With Boc

skills CLI
$ npx skills add microsoft/bocpy --skill testing-with-boc -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install microsoft/bocpy testing-with-boc --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/microsoft/bocpy.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/testing-with-boc .claude/skills/testing-with-boc && rm -rf skills-src

Use ~/.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/

Facts

Skill name
testing-with-boc
GitHub stars
200
Token cost
~6.1k tokens
SKILL.md length
2,193 words
Files
1
Skills in repo
9
Repo updated
First seen
Licence
MIT

At a glance

Write tests for bocpy Behavior-Oriented Concurrency code. An agent skill from microsoft/bocpy.

  • : writing pytest tests for @when behaviors
  • SKILL.md covers Key Concepts, Project Test Setup, Pattern 1 — quiesce() +… and Pattern 2 — Testing Nested /…, plus 8 more sections
  • Calls pip and pytest
  • Cown scheduling

What it does

Testing With Boc is an agent skill from microsoft/bocpy, published by the product's own GitHub organization. Write tests for bocpy Behavior-Oriented Concurrency code. Use when: writing pytest tests for @when behaviors, Cown scheduling, send/receive messaging, cown grouping, chained behaviors, exception propagation. Covers parameter-count rules, module-level class requirements, and the quiesce()+unwrap() result-reading pattern.

Its SKILL.md is about 6.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 Testing & QA, covering Test generation and Unit testing. It works with pytest. The repository describes itself as: Behavior-Oriented Concurrency in Python. The licence is MIT.

When your agent uses it

  • : writing pytest tests for @when behaviors
  • Cown scheduling
  • Send/receive messaging
  • Chained behaviors

Example prompts

  • “/testing-with-boc”

Requirements

  • Python 3

What it can do on your machine

Read from SKILL.md and the folder at commit c8f3ceb. It shows what the files ask for, not the result of running them.

  • Tool permissions

    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.

  • Runs code

    Shell commands in SKILL.md call:

    • pip
    • pytest

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    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.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Testing With Boc loads about 6.1k tokens when it runs. Until then it costs about 85 tokens; SKILL.md has 2,193 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~85
When it runs · the whole SKILL.md, loaded when a task matches
~6.1k

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.

Safety

Auto-check passed

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.

SKILL.md

The full file from microsoft/bocpy at commit c8f3ceb, republished under its MIT licence (© microsoft). 2,193 words, ~6,058 tokens.

Download SKILL.mdSave it as .claude/skills/testing-with-boc/SKILL.md (or your agent's skills folder).
name
testing-with-boc
description
Write tests for bocpy Behavior-Oriented Concurrency code. Use when: writing pytest tests for @when behaviors, Cown scheduling, send/receive messaging, cown grouping, chained behaviors, exception propagation. Covers parameter-count rules, module-level class requirements, and the quiesce()+unwrap() result-reading pattern.

Testing with Behavior-Oriented Concurrency (BOC)

This skill describes how to write tests for code that uses the bocpy library for behavior-oriented concurrency. BOC schedules work as behaviors — decorated functions that run once all required cowns (concurrently-owned data) are available. Testing BOC programs requires specific patterns because behaviors execute asynchronously on worker interpreters.

Design guidance: if you find yourself reaching for time.sleep, threading.Event, polling loops, or wait_for_* helpers inside a behavior or in test code that drives one, stop and read thinking-in-boc first. The right answer is almost always to express the dependency through the cown graph, not through a classical synchronization primitive.

Key Concepts

ConceptDescription
Cown(value)A concurrently-owned wrapper. Behaviors receive exclusive temporal access to the cown's .value.
@when(*cowns)Decorator that schedules the function as a behavior. The decorator replaces the function with a Cown holding the return value. The first N parameters bind to the N cowns; every trailing parameter must carry a default, whose value is snapshotted as a capture at schedule time (see below).
quiesce(timeout=None)Blocks until all in-flight behaviors complete without tearing down the workers. The preferred test-thread barrier before reading results.
Cown.unwrap()After quiescence, returns the behavior's result value, or re-raises the exception the behavior captured (verbatim, on the test thread). Raises RuntimeError if called while behaviors are still in flight.
send(tag, contents)Sends a cross-interpreter message with the given tag.
receive(tags, timeout)Blocks until a message with a matching tag arrives (or times out). Returns (TIMEOUT, None) on timeout.
TIMEOUTSentinel string returned as the tag by receive when a timeout elapses.
wait(timeout)Blocks until all scheduled behaviors have completed, then tears the runtime down.
Cown count, parameter count, and captures

The first N parameters of the decorated function bind positionally to the N arguments of @when. Every additional trailing parameter must carry a default value; that default is evaluated when the def runs and snapshotted as the capture at schedule time:

  • def b(c, factor=factor) — captures factor (read from the enclosing scope at schedule time).
  • def b(c, i=i) — captures i (the canonical loop-snapshot idiom).
  • def b(c, x=y) — captures y and binds it into param x.

The runtime enforces this at decoration time:

  • A bare extra parameter (def b(c, factor) — no default) raises TypeError: the capture count must equal the trailing-parameter count.
  • A closure over an enclosing-function local raises SyntaxError — a behavior runs in another interpreter and cannot capture by closure. The error names the variable and suggests the x=x fix. (A plain module-level global is not a closure: the bindings reducer keeps module-level assignments, so a behavior may read them directly. The closure rule only bites names bound in an enclosing function — the common case when a behavior is written inside a test method.)
  • async def / generator behaviors raise SyntaxError.

Computed defaults (def b(c, k=expensive())) are allowed — the value is evaluated once and snapshotted like any other capture. A default on a cown position (def b(c=c) under @when(x)) is just a capture-count mismatch and raises TypeError.

python
# CORRECT — 1 @when arg, 1 function param
@when(x)
def good(x):
    return x.value * 2

# CORRECT — extra params beyond the cown count are captures and must
# carry a default. The default is snapshotted at schedule time.
def schedule(x):
    factor = 2
    @when(x)
    def with_extra(x, factor=factor):   # capture ``factor`` via default
        return x.value * factor

# WRONG — bare extra parameter (no default) raises TypeError
@when(x)
def missing_default(x, factor):         # TypeError at decoration
    return x.value * factor

# WRONG — closing over an enclosing-function local raises SyntaxError
def schedule_bad(x):
    factor = 2
    @when(x)
    def via_closure(x):                  # SyntaxError: closes over 'factor'
        return x.value * factor          # pass it as ``factor=factor`` instead
The def _(c, i=i) loop-capture idiom is supported

The canonical Python idiom for snapshotting a loop variable as a default argument works transparently:

python
for i, c in enumerate(cowns):
    @when(c)
    def _(c, i=i):                      # ``i`` captured at schedule time
        send("done", i)

Parameters beyond the cown count are captures, snapshotted at schedule time. A capture is a trailing parameter carrying a default whose value is read when the behavior is scheduled: def b(c, i=i) captures i (the rename form def b(c, x=y) captures y). A bare extra parameter (def b(c, factor)) raises TypeError at decoration time, and a closure over a free variable raises SyntaxError — a behavior runs in another interpreter and cannot capture by closure.

A behavior cannot reference a loop variable by closure; you must capture it explicitly:

python
for i, c in enumerate(cowns):
    @when(c)
    def _(c, i=i):                      # capture i; bare `i` reference would fail
        send("done", i)

See the "Inspecting the Worker Bindings Module" section of .github/copilot-instructions.md for how @when captures work and how to inspect the bindings module workers import.

If you want a fresh scope per iteration (e.g. to avoid sharing mutable state between iterations), use a helper function:

python
def _schedule(c, i):                    # fresh scope per iteration
    @when(c)
    def _(c):
        send("done", i)

for i, c in enumerate(cowns):
    _schedule(c, i)
Critical rule: classes and functions must be declared at module level

Behaviors run in separate sub-interpreters. Workers import the module's bindings, which means any class or function referenced inside a @when behavior must be defined at module level. A class defined inside a test method or local function cannot be resolved by the worker and will crash.

python
# CORRECT — class at module level
class Accumulator:
    def __init__(self):
        self.items = []
    def add(self, item):
        self.items.append(item)

def test_accumulator(self):
    acc = Cown(Accumulator())

    @when(acc)
    def _(a):
        a.value.add(42)       # Accumulator is importable ✓

# WRONG — class inside test method
def test_accumulator_bad(self):
    class Accumulator:        # local class — worker can't import it
        ...
    acc = Cown(Accumulator())

    @when(acc)
    def _(a):
        a.value.add(42)       # will crash

Project Test Setup

  • Tests use pytest and live in the test/ directory.
  • Install test dependencies: pip install -e .[test]
  • Run: pytest -vv

Pattern 1 — quiesce() + unwrap() (default)

This is the preferred pattern for result-shipping assertions: the behavior computes a value (or raises), and the test thread verifies it. A behavior returns its result, so @when hands back a Cown holding that result. The test blocks once on quiesce() (which lets every in-flight behavior finish without tearing down the workers), then reads the result with Cown.unwrap().

unwrap() returns the stored value on success, and re-raises any exception the behavior captured — verbatim, on the test thread — so a failing assertion inside a behavior surfaces as a real AssertionError in the test. It also guards against misuse: calling it while behaviors are still in flight raises RuntimeError, so always quiesce() first.

Always pass a timeout to quiesce(). If the barrier is not reached in time (e.g. a behavior never fires because of a @when arg-count mismatch) it raises TimeoutError, so the test fails fast instead of hanging forever. Use a module-level constant such as QUIESCE_TIMEOUT = 5.

python
from bocpy import Cown, when, quiesce, wait

QUIESCE_TIMEOUT = 5  # seconds; quiesce() raises TimeoutError if exceeded


class TestExample:
    @classmethod
    def teardown_class(cls):
        wait()  # tear the runtime down after all tests

    def test_double(self):
        x = Cown(3)

        @when(x)
        def result(x):
            return x.value * 2

        quiesce(QUIESCE_TIMEOUT)
        assert result.unwrap() == 6

Tuple results read just as naturally:

python
    def test_pair(self):
        v = Cown(Matrix(1, 2, [3.0, 4.0]))

        @when(v)
        def result(v):
            n = v.value.normalize()
            return (n[0, 0], n[0, 1])

        quiesce(QUIESCE_TIMEOUT)
        n0, n1 = result.unwrap()
        assert n0 == pytest.approx(0.6)
        assert n1 == pytest.approx(0.8)
Asserting a behavior raised

Because unwrap() re-raises the captured exception, use pytest.raises to assert that a behavior failed:

python
    def test_raises(self):
        x = Cown(1)

        @when(x)
        def boom(x):
            raise ValueError("bad input")

        quiesce(QUIESCE_TIMEOUT)
        with pytest.raises(ValueError, match="bad input"):
            boom.unwrap()

Once unwrap() consumes an exception it clears the cown's exception flag, so the runtime will not later report it as unhandled, and a second unwrap() returns the (now None) value rather than re-raising.

Why this pattern?

@when returns immediately — the behavior has not executed yet. quiesce() is the test-thread barrier that lets it (and any behaviors it chains) finish. Unlike wait(), it leaves the workers alive, so the result cowns remain readable and further behaviors can still be scheduled. Reading results directly avoids the message-queue round-trip and per-assert timeout polling that a send/receive-based assertion would require.

Pattern 2 — Testing Nested / Chained Behaviors

Behaviors can schedule further behaviors. A behavior that chains more work can return the inner result cown; after quiesce() the whole chain has run, so you unwrap the final result on the test thread.

python
def test_nested(self):
    x = Cown(1)

    @when(x)
    def step1(x):
        x.value *= 2          # x is now 2

        @when(x)
        def step2(x):
            x.value *= 3      # x is now 6
            return x.value

        return step2

    quiesce(QUIESCE_TIMEOUT)
    # step1 returns the inner step2 cown; unwrap it to read the final value.
    assert step1.unwrap().unwrap() == 6

Pattern 3 — Multi-Cown Coordination

Pass multiple cowns to @when to atomically operate on several pieces of data. The scheduler guarantees deadlock-free acquisition.

python
def test_transfer(self):
    x = Cown(100)
    y = Cown(0)

    @when(x, y)
    def _(x, y):
        y.value += 50
        x.value -= 50

    @when(x)
    def read_x(x):
        return x.value

    @when(y)
    def read_y(y):
        return y.value

    quiesce(QUIESCE_TIMEOUT)
    assert read_x.unwrap() == 50
    assert read_y.unwrap() == 50

Pattern 4 — Cown Grouping

When you have a dynamic number of cowns (e.g., a list), you can pass them to @when as a list (or slice) rather than individual arguments. Inside the behavior, that parameter is delivered as a list[Cown] — each element is an acquired cown whose .value you can read or write.

You can mix single cowns and groups freely in any order. Each distinct argument to @when becomes its own parameter in the decorated function:

@when(...) argumentsBehavior parameters
@when(list_of_cowns)(group: list[Cown])
@when(cowns[:9], cowns[9])(group: list[Cown], single: Cown)
@when(cowns[0], cowns[1:])(single: Cown, group: list[Cown])
@when(cowns[:4], cowns[4], cowns[5:])(g0: list[Cown], single: Cown, g1: list[Cown])
@when(cowns[0], cowns[1:9], cowns[9])(s0: Cown, group: list[Cown], s1: Cown)
Full group example
python
from bocpy import Cown, when, quiesce

cowns = [Cown(i) for i in range(10)]  # values 0..9, sum = 45

# All cowns as a single group
@when(cowns)
def group_sum(group: list[Cown[int]]):
    return sum(c.value for c in group)

# Group + single cown
@when(cowns[:9], cowns[9])
def group_then_single(group: list[Cown[int]], single: Cown[int]):
    return sum(c.value for c in group) + single.value

# Single cown + group
@when(cowns[0], cowns[1:])
def single_then_group(single: Cown[int], group: list[Cown[int]]):
    return single.value + sum(c.value for c in group)

# Group + single + group
@when(cowns[:4], cowns[4], cowns[5:])
def group_single_group(g0: list[Cown[int]], single: Cown[int], g1: list[Cown[int]]):
    return sum(c.value for c in g0) + single.value + sum(c.value for c in g1)
Testing grouped results

The results are all cowns, so collect them after quiesce() with unwrap(). You can also pass a list of result cowns as a group to a follow-up @when:

python
def test_cown_grouping(self):
    expected = 45
    results = [group_sum, group_then_single, single_then_group, group_single_group]

    quiesce(QUIESCE_TIMEOUT)
    for r in results:
        assert r.unwrap() == expected
Key rules for grouping
  • Pass a list (or slice) of cowns to @when — the behavior receives the corresponding parameter as list[Cown].
  • Pass a single cown — the parameter receives that Cown directly.
  • You can interleave singles and groups in any order. The positional mapping between @when(...) arguments and the decorated function's parameters is 1:1.
  • Type-annotate grouped parameters as list[Cown[T]] for clarity.
Show full SKILL.md (915 more words)Show less

Pattern 5 — Exception Propagation

If a behavior raises, the exception is captured in the returned cown's .value and the cown's .exception flag is set to True. The simplest way to assert a behavior raised is unwrap() under pytest.raises, which re-raises the captured exception verbatim on the test thread:

python
def test_exception_in_behavior(self):
    x = Cown(1)

    @when(x)
    def bad(x):
        x.value /= 0          # ZeroDivisionError

    quiesce(QUIESCE_TIMEOUT)
    with pytest.raises(ZeroDivisionError):
        bad.unwrap()

An Exception object a behavior returns (rather than raises) is just a value: .exception stays False and unwrap() returns it normally.

python
def test_returned_exception_is_not_flagged(self):
    """An Exception object *returned* from a behavior is just a value."""
    x = Cown(1)

    @when(x)
    def returns_exc(x):
        return ValueError("not really an error")

    quiesce(QUIESCE_TIMEOUT)
    result = returns_exc.unwrap()
    assert isinstance(result, ValueError)

If you need to inspect the flag from inside a downstream behavior (rather than unwrap on the test thread), the same rules apply:

  • cown.exception distinguishes a thrown exception from a returned Exception value — assert on it before isinstance(.value, Exception).
  • Writing cown.value = ... from inside a behavior clears .exception.
  • cown.exception is also writable inside a behavior, to manually mark or unmark a cown as carrying an error.

Pattern 6 — Noticeboard

The noticeboard is a global key-value store (up to 64 keys) that behaviors can read and write without acquiring any cowns. Writes are non-blocking; reads return a snapshot taken once per behavior execution.

FunctionPurpose
notice_write(key, value)Non-blocking write.
notice_update(key, fn, default=None)Atomic read-modify-write. fn and default must be picklable. Returning REMOVED deletes the entry.
notice_delete(key)Non-blocking delete.
noticeboard()Read-only mapping — snapshot of the noticeboard, cached for the duration of the current behavior.
notice_read(key, default=None)Convenience: one key from the snapshot.
Key rule: snapshot per behavior

Within a single behavior, noticeboard() and notice_read() always return data from the same snapshot — even if other behaviors write in the meantime. To see a write made by another behavior, schedule a follow-up behavior (typically by chaining via a cown returned from @when).

Because the snapshot can only be read from inside a behavior, store the read into the result cown returned by @when and unwrap() it on the test thread after quiesce() (Pattern 1). The result cown is used only by that one behavior, so it does not affect scheduling.

python
def test_noticeboard_roundtrip(self):
    x = Cown(0)

    @when(x)
    def step1(x):
        notice_write("greeting", "hello")

    # The chain on `step1` ensures step2 runs *after* the write has been
    # applied and step2's snapshot sees it.
    @when(x, step1)
    def step2(x, _):
        return notice_read("greeting")

    quiesce(QUIESCE_TIMEOUT)
    assert step2.unwrap() == "hello"
Atomic update

notice_update runs fn(current_value) on the scheduler and writes the result back atomically. Lambdas and closures are not picklable — use a module-level function (optionally wrapped with functools.partial) or an operator function.

python
from functools import partial
from operator import add

def _bump(n, by):
    return n + by

class TestCounter:
    @classmethod
    def teardown_class(cls):
        wait()

    def test_atomic_increment(self):
        x = Cown(0)

        @when(x)
        def init(x):
            notice_write("count", 0)

        @when(x, init)
        def bump(x, _):
            notice_update("count", partial(_bump, by=5))
            notice_update("count", partial(add, 3))

        @when(x, bump)
        def check(x, _):
            return notice_read("count")

        quiesce(QUIESCE_TIMEOUT)
        assert check.unwrap() == 8
Delete via REMOVED

Returning the REMOVED sentinel from a notice_update callback deletes the entry. notice_delete(key) is the direct form.

python
def _drop_if_zero(n):
    return REMOVED if n == 0 else n - 1

def test_remove_via_update(self):
    x = Cown(0)

    @when(x)
    def init(x):
        notice_write("lives", 1)

    @when(x, init)
    def tick(x, _):
        notice_update("lives", _drop_if_zero)   # 1 -> 0
        notice_update("lives", _drop_if_zero)   # 0 -> REMOVED

    @when(x, tick)
    def check(x, _):
        return "lives" in noticeboard()

    quiesce(QUIESCE_TIMEOUT)
    assert check.unwrap() is False
Common noticeboard pitfalls
PitfallFix
Reading a value back inside the same behavior that wrote itThe snapshot was taken at the start of the behavior. Chain a follow-up @when to observe the write.
Passing a lambda or closure to notice_updateThey are not picklable. Use a module-level function with functools.partial, or an operator function.
Asserting in the test body that noticeboard() contains a keyRead inside a behavior, return the result, then quiesce() and unwrap() it — noticeboard() and notice_read() outside any behavior return a snapshot that is never refreshed.
Writing more than 64 distinct keysExcess writes are dropped with a logged warning — they do not raise. Keep tests within the limit (and notice_delete keys you no longer need).

Pattern 7 — Parameterized Tests

Use @pytest.mark.parametrize to sweep inputs. Each invocation gets its own cowns so tests are isolated.

python
@pytest.mark.parametrize("n", [1, 10, 15])
def test_fibonacci(self, n):
    result = fib_parallel(n)
    expected = fib_sequential(n)

    quiesce(QUIESCE_TIMEOUT)
    assert result.unwrap() == expected

Pattern 8 — Testing send/receive Messaging Directly

For code that uses the lower-level messaging API without behaviors:

python
from bocpy import send, receive, TIMEOUT

def test_basic_messaging():
    send("tag", "payload")
    tag, value = receive("tag", 1)
    assert tag != TIMEOUT
    assert value == "payload"

def test_receive_timeout():
    tag, value = receive("tag", 0.1)
    assert tag == TIMEOUT
    assert value is None

def test_timeout_with_after_callback():
    tag, value = receive("tag", 0.1, lambda: ("fallback", 42))
    assert tag == "fallback"
    assert value == 42

Pattern 9 — Complex Objects in Cowns

Mutable objects (e.g., class instances) work inside cowns. Behaviors mutate them in-place under exclusive access.

python
class Counter:
    def __init__(self):
        self.n = 0
    def increment(self):
        self.n += 1

def test_object_in_cown(self):
    c = Cown(Counter())

    for _ in range(10):
        @when(c)
        def _(c):
            c.value.increment()

    @when(c)
    def final(c):
        return c.value.n

    quiesce(QUIESCE_TIMEOUT)
    assert final.unwrap() == 10

Common Pitfalls

PitfallFix
Parameter count mismatch in @whenThe decorated function must have exactly as many cown parameters as @when arguments; any further parameters must be captures (trailing parameters with defaults). A mismatch raises TypeError at decoration.
Classes/functions defined inside a testBehaviors run in sub-interpreters that import the module. Define all classes and functions used in behaviors at module level so workers can resolve them.
Asserting in the test body right after @whenThe behavior hasn't run yet. Call quiesce() first, then read results with unwrap() (Pattern 1).
receive without a timeoutIf a behavior crashes silently, the test hangs forever. Always pass a timeout (e.g. RECEIVE_TIMEOUT = 10) and assert the result is not TIMEOUT.
Forgetting wait() in teardownPending behaviors may leak into the next test class. Always call wait() in teardown_class.
Reading cown.value outside a behaviorA cown must be acquired first. After quiesce(), use unwrap() (which acquires for you); otherwise read inside @when.
Trying to capture a loop variable by closureA behavior runs in another interpreter and cannot close over free variables (raises SyntaxError). Capture it as a trailing default instead: def _(c, i=i): ....
Non-XIData-compatible objects in cowns across interpretersStick to built-in types or objects that support cross-interpreter data.
Importing unittest.mock in a BOC testWorker sub-interpreters import the test module's bindings. unittest.mock transitively imports asyncio, which can deadlock during PEP 684 per-interpreter init (observed on macOS arm64 + Python 3.12/3.13). Use the in-house mockreplacement.patch_attr / Recorder helpers (see test/mockreplacement.py), and import them inside the test method — never at module scope, because workers also fail to find mockreplacement on their sys.path during bootstrap.
Test function names with uppercase letters (N802)Test names must be lowercase. E.g., test_t_equals_transpose, not test_T_equals_transpose, even when testing a property like .T.
Assigning Cown(m) to an unused variable (F841)When the return value isn't needed (e.g., releasing a resource), use bare Cown(m) without assignment.
Using single quotes (Q000)The project enforces inline-quotes = double. Use "nan" not 'nan'.
Multi-line class docstring formatting (D205/D209)Summary line, then blank line, then body. Closing """ on its own line.
Placing # noqa: B023 on the def line instead of the violation line# noqa: B023 must go on the line that references the loop variable, not the def _(a): line above it.

© microsoft, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .github/skills/testing-with-boc of microsoft/bocpy.

Open the folder on GitHubat commit c8f3ceb

Compare with similar skills

Testing With Boc 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.

Testing With Boc compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Testing With Boc this skillmicrosoft/bocpy200—~6.1kAutomated safety check: PassMIT
Adk Verify Snippetsgoogle/adk-python22k—~1.4kAutomated safety check: PassApache-2.0
Hermetic Python Unit TestsdimensionalOS/dimos4.6k—~1.4kAutomated safety check: PassCustom licence
Test Coverage Reviewareed1192/finance-news-aggregator149—~2.6kAutomated safety check: PassMIT
Write Test389ds/389-ds-base294—~1.8kAutomated safety check: PassCustom licence
MoAI TDD Workflowmodu-ai/moai-adk1.2k—~3.1kAutomated safety check: PassApache-2.0

Similar skills

  • Adk Verify Snippets

    google/adk-python

    Official

    Checks that every Python code block in a Markdown file actually compiles and runs, by extracting each block to a temporary file, executing it in an isolated subprocess, and writing a pass/fail…

    22k GitHub stars~1.4k tokensUpdated today
    Testing & QAAuto-check passed
  • Hermetic Python Unit Tests

    dimensionalOS/dimos

    Rules for writing, fixing and reviewing pytest unit tests that are hermetic: behavior-focused, deterministic, isolated and cheap to run.

    4.6k GitHub stars~1.4k tokensUpdated today
    Testing & QAAuto-check passed
  • Test Coverage Review

    areed1192/finance-news-aggregator

    Audit, plan, write, and verify unit tests for Python projects using pytest.

    149 GitHub stars~2.6k tokensUpdated 5 mo ago
    Testing & QAAuto-check passed
  • Write Test

    389ds/389-ds-base

    Add or extend a pytest integration test for 389 Directory Server under dirsrvtests/.

    294 GitHub stars~1.8k tokensUpdated yesterday
    Testing & QAAuto-check passed
  • MoAI TDD Workflow

    modu-ai/moai-adk

    Drives test-first development through the RED, GREEN, REFACTOR cycle, with a config switch that selects between TDD and a DDD workflow for existing code.

    1.2k GitHub stars~3.1k tokensUpdated today
    Testing & QAAuto-check passed
  • Testing

    meleantonio/ChernyCode

    Testing conventions using pytest. An agent skill from meleantonio/ChernyCode.

    516 GitHub stars~408 tokensUpdated 1 mo ago
    Testing & QAAuto-check passed

More from microsoft/bocpy

All 9 skills in this repo
  • Branch Review

    microsoft/bocpy

    Official

    Multi-perspective code review for a branch before merging. An agent skill from microsoft/bocpy.

    200 GitHub stars~3.2k tokensUpdated 9 days ago
    Auto-check passed
  • Official

    Write a C extension whose custom types can live inside a bocpy Cown and travel between worker sub-interpreters.

    200 GitHub stars~5.1k tokensUpdated 9 days ago
    Auto-check passed
  • Official

    Follow bocpy commenting and documentation conventions. An agent skill from microsoft/bocpy.

    200 GitHub stars~3.3k tokensUpdated 9 days ago
    Auto-check passed
  • Finalize PR

    microsoft/bocpy

    Official

    Finalize a feature branch for merge. An agent skill from microsoft/bocpy.

    200 GitHub stars~3.5k tokensUpdated 9 days ago
    Auto-check passed
  • Multi Perspective Plan

    microsoft/bocpy

    Official

    Multi-perspective planning with rebuttal rounds and adversarial review loop.

    200 GitHub stars~2.8k tokensUpdated 9 days ago
    Auto-check passed
  • Testing Message Queue

    microsoft/bocpy

    Official

    Write tests for the bocpy message queue — the lock-free tag-based MPSC ring buffer.

    200 GitHub stars~2.3k tokensUpdated 9 days ago
    Auto-check passed

Works with

Categories

Questions about Testing With Boc

What does Testing With Boc do?

Write tests for bocpy Behavior-Oriented Concurrency code. An agent skill from microsoft/bocpy. Testing With Boc is an agent skill from microsoft/bocpy, published by the product's own GitHub organization. Write tests for bocpy Behavior-Oriented Concurrency code.

When should I use Testing With Boc?

Testing With Boc fits situations like: : writing pytest tests for @when behaviors; cown scheduling; send/receive messaging; chained behaviors.

How do I install Testing With Boc in Claude Code?

Run `npx skills add microsoft/bocpy --skill testing-with-boc -a claude-code`. Or copy the skill folder (.github/skills/testing-with-boc in microsoft/bocpy) into .claude/skills/testing-with-boc in your project. Claude Code loads it when a task matches its description.

How do I install Testing With Boc in Codex?

Run `npx skills add microsoft/bocpy --skill testing-with-boc -a codex`. Or copy the skill folder (.github/skills/testing-with-boc in microsoft/bocpy) into .agents/skills/testing-with-boc in your project. Codex loads it when a task matches its description.

Can I use Testing With Boc in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add microsoft/bocpy --skill testing-with-boc -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/testing-with-boc, .gemini/skills/testing-with-boc, .github/skills/testing-with-boc and .opencode/skills/testing-with-boc in your project.

What does Testing With Boc need to run?

Going by SKILL.md and its folder, Testing With Boc needs the command-line tools its instructions call (pip and pytest). Our summary lists: Python 3.

Does Testing With Boc access the network?

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.

Is Testing With Boc safe to install?

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.

What licence does Testing With Boc use?

Testing With Boc is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Testing With Boc use?

About 6.1k tokens (SKILL.md is roughly 24k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Testing With Boc?

Skills that share tags, products or a category with Testing With Boc: Adk Verify Snippets (google/adk-python, 22k stars), Hermetic Python Unit Tests (dimensionalOS/dimos, 4.6k stars), Test Coverage Review (areed1192/finance-news-aggregator, 149 stars) and Write Test (389ds/389-ds-base, 294 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Testing With Boc?

microsoft (a GitHub organization, an official publisher) maintains it in microsoft/bocpy, which has 200 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.