Skip to content

Repository files navigation

Play

Play is the implicit pre-harness controller for reusable outcomes. It searches authorized Play indexes first, runs an adequate Play in Use mode, or asks before entering Explore mode. The existing rote and rote-* skills remain callable specialists that can hand off through Play.

Start here

You do not need to remember organization/name slugs or lower-level rote commands. Describe the outcome in ordinary language:

$play find a Play that retrieves recent emails
$play run the PostHog daily active users report
$play create a reusable weekly customer report
$play whats new
$play birth weekly customer report
$play list my organizations and shared Plays
Handle this normally without Play

Play keeps four decisions separate so each prompt is small and honest:

You intend to… Play does… You choose…
Find something reusable Search local and authorized organization indexes Inspect one result or stop
Run a known or vaguely named Play Resolve the name, inspect it read-only, and show setup/effects Pull and run, or not now
Solve a one-off task Search first; if no adequate Play exists, offer Explore Explore with rote, or continue normally
Preserve successful exploration Verify it before preparing a candidate Private, Public, or Skip
See what’s new Pull an inbox grouped by organization and compare it with the remembered SHA Run, search, create, or finish
Revisit how a Play was born Open the owner-private, redacted birth certificate Choose an unambiguous name, reference, or birth SHA

Search selection is never execution approval. Before every run, Play shows what the exact version does, its parameters, adapters and credentials, what this machine must install or repair, declared operations and writes, and any unknown effect semantics. Only the next structured choice can authorize the exact inspected version and displayed parameters.

The Play state machine

Play is driven by one declarative machine, references/controller/machine.yaml (play.machine/v1). The controller re-reads it on every activation, executes exactly one declared prompt or entry action per state, and accepts only events declared by actions.yaml and prompts.yaml. It never jumps states from conversational intuition. Initial state: qualify. Terminals: receipt, completed, exited, blocked. Any action failure emits action_blocked and lands in blocked (or, for a failed run, the repair_offer gate).

stateDiagram-v2
    direction TB
    [*] --> qualify

    %% ── Qualify routes each request to one trajectory ──
    qualify --> search : outcome / search request
    qualify --> use_inspect : exact play request
    qualify --> awareness_collect : whats new
    qualify --> creator_search : create a Play
    qualify --> management_list : list orgs / plays
    qualify --> management_offer : ambiguous list request
    qualify --> birth_show : birth lookup
    qualify --> exited : conversation / excluded

    %% ── Search and adequacy ──
    search --> search_present : search-only request
    search --> classify : outcome request
    search_present --> search_offer
    search_offer --> use_inspect : result selected
    search_offer --> completed : dismissed
    classify --> use_inspect : full match
    classify --> explore_offer : partial / uncertain / no match

    %% ── Use (existing Play) ──
    use_inspect --> use_offer : inspected
    use_inspect --> completed : not runnable
    use_inspect --> search : reference unresolved
    use_offer --> use_run : approved
    use_offer --> completed : declined
    use_run --> use_verify : run ready
    use_run --> repair_offer : drifted / failed
    use_verify --> use_receipt : outcome verified
    use_verify --> repair_offer : not verified
    use_receipt --> receipt
    repair_offer --> explore_prepare : repair approved
    repair_offer --> exited : continue normally

    %% ── Explore (consent, route, delegated execution) ──
    explore_offer --> explore_prepare : explore approved
    explore_offer --> exited : continue normally
    explore_prepare --> explore_route
    explore_route --> explore_handoff : route within policy
    explore_route --> modality_offer : widening required
    modality_offer --> explore_route : widening approved
    modality_offer --> blocked : declined
    explore_handoff --> explore_execute : typed packet ready
    explore_execute --> explore_receipt : typed specialist receipt
    explore_receipt --> explore_verify : outcome ready
    explore_receipt --> explore_route : route exhausted, budget left
    explore_receipt --> effect_offer : rote confirmation required
    explore_receipt --> auth_repair_offer : auth repair required
    effect_offer --> explore_handoff : guarded call approved
    effect_offer --> blocked : declined
    auth_repair_offer --> auth_repair_handoff : approved
    auth_repair_offer --> blocked : declined
    auth_repair_handoff --> auth_repair_execute
    auth_repair_execute --> auth_repair_receipt
    auth_repair_receipt --> explore_handoff : validated repair
    auth_repair_receipt --> blocked : failed / invalid
    explore_verify --> crystallize : outcome verified
    explore_verify --> explore_route : not verified, budget left

    %% ── Save lifecycle (crystallize → publish → birth → index) ──
    crystallize --> save_offer : candidate ready
    crystallize --> completed : not reusable
    save_offer --> author_release : Private or Public
    save_offer --> completed : Skip
    author_release --> birth_capture : flow released
    birth_capture --> private_org : private
    birth_capture --> public_owner : public
    private_org --> private_publish
    public_owner --> public_publish : owner resolved
    public_owner --> public_owner_offer : owner ambiguous
    public_owner_offer --> public_publish : owner selected
    public_owner_offer --> blocked : declined
    private_publish --> birth_bind : published private
    public_publish --> birth_bind : published public
    birth_bind --> index
    index --> saved_inspect
    saved_inspect --> completed : readback matches

    %% ── Awareness, creator, management, birth ──
    awareness_collect --> awareness_present : new items
    awareness_collect --> completed : unchanged
    awareness_present --> awareness_offer
    awareness_offer --> use_inspect : play selected
    awareness_offer --> awareness_need : search
    awareness_offer --> creator_need : create
    awareness_offer --> completed : done
    awareness_need --> search
    creator_need --> creator_search
    creator_search --> creator_classify
    creator_classify --> creator_offer : related Play exists
    creator_classify --> explore_prepare : no match
    creator_offer --> use_inspect : use existing
    creator_offer --> explore_prepare : adapt / create distinct
    management_offer --> management_list
    management_list --> management_present
    management_present --> completed
    birth_show --> completed

    receipt --> [*]
    completed --> [*]
    exited --> [*]
    blocked --> [*]
Loading

State ownership is explicit: play owns prompts, evaluators, and verification; rote-specialist states (explore_execute, crystallize, author_release, publication, management_list) are delegated through typed play.handoff/v1 packets and validated play.handoff-receipt/v1 receipts; flow-runtime owns use_run and saved_inspect via first-class rote play run/inspect. Guards (for example search_is_complete, route_within_policy, exploration_budget_remaining, exact_published_version_is_indexed) are declared in actions.yaml, and tests/controller/test_machine_conformance.py fails when the machine, actions, prompts, or the thinking-orbs presentation mapping drift.

Install from a marketplace

Play is packaged as one self-contained plugin under plugins/play. The package includes the skill, controller references, Python runtime, harness activation tools, and the justfile recipes that configure and verify the Play-first experience. scripts/bin/package-plugin --check prevents those installed files from drifting from this repository's source of truth.

Rote is a prerequisite. Install and complete its guided setup first:

# Codex
codex plugin marketplace add modiqo/rote-skills
codex plugin add rote-onboard@rote-skills

# Claude Code
claude plugin marketplace add modiqo/rote-skills
claude plugin install rote-onboard@rote-skills

Restart the harness and invoke $rote-setup in Codex or /rote-setup in Claude Code. Play checks that the rote executable is present, an identity is authenticated, and rote play is available; it stops with the applicable setup commands when any precondition is missing. It never installs or authenticates Rote without permission.

Install Play from its public marketplace after Rote setup:

codex plugin marketplace add modiqo/play
codex plugin add play@play-skills

claude plugin marketplace add modiqo/play
claude plugin install play@play-skills

This checkout is also a valid local marketplace:

# Run from this repository root.
codex plugin marketplace add .
codex plugin add play@play-skills

claude plugin marketplace add .
claude plugin install play@play-skills

The public marketplace source is modiqo/play, so . can be replaced with that GitHub owner/repository from outside this checkout. Claude's manifest declares the rote plugin as a dependency from the separately trusted rote-skills marketplace; every harness still uses the same runtime preflight because plugin metadata alone cannot prove CLI installation or login state.

Kimi and other AGENTS.md-standard harnesses

Kimi has no plugin marketplace. It discovers skills from AGENTS.md-standard roots — the shared ~/.agents/skills directory and per-harness ~/.<harness>/skills roots. Install Play for Kimi from this checkout:

just plan
just install

install discovers every skills root containing rote skills — including ~/.agents/skills — links this Play skill into each, and applies the activation metadata in agents/openai.yaml (allow_implicit_invocation: true) so Play stays implicitly invocable and the rote specialists remain model-invocable for chained handoffs. Play's structured prompts map to Kimi's askquestion control (scripts/bin/play-question <prompt> --harness kimi), and just harness kimi / just smoke kimi start and smoke-test the harness like Codex and Claude Code.

Restart the harness after plugin installation. On first use, Play runs the bundled preflight. To make Play the preferred implicit entrypoint while keeping installed rote specialists model-invocable for chained handoffs, preview and apply the bundled reversible activation profile from the installed skill directory:

just plan
just install
just verify-profile

In marketplace mode this profile does not create a second Play link. It snapshots and updates the activation metadata of discovered rote skills so the harness can follow chained handoffs. Uninstall restores those exact snapshots and fails closed if a managed file was subsequently changed.

Update an installed Play plugin

After a new Play release is pushed, refresh the marketplace snapshot and reinstall/update the plugin. Published changes must carry a new plugin version; a push that keeps the same version is not a reliable cache invalidation mechanism.

For Codex:

codex plugin marketplace upgrade play-skills
codex plugin add play@play-skills

For Claude Code:

claude plugin marketplace update play-skills
claude plugin update play@play-skills

Restart the harness and start a new conversation after updating so it loads the refreshed skill. If you enabled the implicit Play-first profile, run the following from the newly installed Play skill directory to converge its reversible activation metadata after the plugin refresh:

just install
just verify-profile

Kimi and other AGENTS.md-standard roots (~/.agents/skills, ~/.<harness>/skills) have no plugin cache to upgrade; they always follow the source-checkout path below. If you use a cloned source checkout instead of the GitHub marketplace — or need to refresh those AGENTS.md roots — update from the repository root with:

git pull --ff-only
just package
just update

Then restart the harness and begin a new conversation. just package refreshes the self-contained marketplace payload; just update safely reapplies and verifies the source-linked Play-first profile.

Enable from a source checkout

Preview every harness root and canonical rote skill that will change:

just plan

Activate the Play-first profile and verify it:

just install
just verify-profile

After editing the source skill, confirm that every source-linked installation is still valid:

just update

The links make source edits live immediately; a running harness must still be restarted to reload the revised skill.

install discovers every installed harness skill root containing rote or rote-*, links this Play skill into each root, and makes every Rote skill model-invocable so specialist handoffs can continue without another user command. It snapshots the original Rote activation files so the change is reversible. Restart running harnesses after enabling the profile.

It is also the convergence command after rote harness setup, a plugin refresh, or a newly added harness. If Rote replaced managed skill files, just install preserves those refreshed files as the new uninstall baseline and reapplies only Play's activation metadata. It adds new roots/skills and retires removed ones without restoring stale backups. A changed or conflicting Play link still fails closed.

Inspect the active profile at any time:

just status
just status-roots

Start and test a fresh harness

Start a supported interactive harness after verifying the profile:

just harness codex
just harness claude
just harness kimi

Start a compact Codex session without changing global Codex configuration:

just harness-quiet

This launch sets model_verbosity="low", model_reasoning_summary="none", and hide_agent_reasoning=true as per-session overrides. It reduces model narration and reasoning events; the Codex UI may still render tool calls that were actually made.

Run a read-only smoke test that must reach Play's Explore-or-continue consent gate:

just smoke codex
just smoke claude
just smoke kimi

Run every locally installed supported smoke-test harness with:

just smoke-all

Smoke tests start new harness processes and may consume model credits.

Everyday Play commands

Find by outcome across local and authorized remote indexes:

$play find a Play that retrieves recent emails
$play search live status for AI services
$play run the PostHog DAU report

For a vague run request, Play searches and offers recognizable names. For an exact reference, it skips search but never skips inspection or approval. A registry-only result is labeled as available in an authorized organization and expected to need a local pull/install. The first-class run later performs that convergence after approval; Play does not manually assemble pull and Flow commands.

List organization and registry inventories:

$play list orgs
$play list plays
$play list

Create, explore, save, and share without memorizing lifecycle commands:

$play create a reusable weekly customer report
Explore this with rote and make it reusable if it works
Handle this normally without Play

Play always searches before creating. If an adequate Play exists it offers Inspect existing, Adapt existing, or Create distinct. Otherwise explicit create intent enters Explore directly. Successful exploration is verified before Play asks:

  • Private — release and publish to an authorized private organization;
  • Public — release and publish under a selected public owner;
  • Skip — keep the result without publishing or indexing a Play.

Explore execution is fail-closed around Rote ownership. CALL routes only through rote-using-adapters, SHELL through rote-shell, DRIVE through rote-browse, and combined work through rote-workspace. Play first confirms that the exact skill is callable in the current harness, then validates its typed receipt before accepting the result. If the specialist is absent, Play blocks; it never substitutes a directly exposed MCP, app, shell, or browser tool.

CALL routes also converge on an authenticated Rote adapter. Play reuses an installed adapter or asks Rote to detect OpenAPI, GraphQL, or MCP and create the appropriate adapter, then completes Rote's auth cycle before execution. The receipt records adapter, type, creation, and auth provenance, so a raw MCP call cannot masquerade as a delegated result.

DRIVE routes carry a current-version crystallization limit that Play discloses before exploration begins. Typed browser steps express navigation, waits, clicks, typing, and the canonical extract slices only; they cannot carry a raw page snapshot or arbitrary DOM/table content, and front-end accessibility trees are volatile across sites and releases. A browser outcome whose required facts exceed the canonical slices can crystallize only as a legacy stepless body, which rote play run rejects (play_run_eligible: false). Play therefore warns at the Explore offer (or the DRIVE route milestone), repeats the limit at the save offer, and treats play_run_eligible: false as a publication gate requiring explicit user approval. See the DRIVE crystallization limit guidance and the RCA that motivated it.

After release, Play captures a private birth certificate from the exploration evidence. After Private or Public publication, it binds that certificate to the minted exact reference, then indexes and inspects the canonical version before calling the save successful. Organization membership, invitations, and sharing use the organization/list surface rather than hidden local state.

Open or verify how one of your Plays was born:

$play birth weekly customer report
$play show how modiqo/weekly-customer-report was born
scripts/bin/play-birth show modiqo/weekly-customer-report@1.0.0
scripts/bin/play-birth list --json
scripts/bin/play-birth verify weekly-customer-report --json
scripts/bin/play-birth capture --workspace weekly-report-build --flow weekly-customer-report --json
scripts/bin/play-birth bind <birth-sha> --reference modiqo/weekly-customer-report@1.0.0 --json

Birth certificates live under ~/.play/births, independently of .rote. They are readable only by the local OS user, content-addressed, and captured once per released Flow fingerprint. They preserve safe counts, timings, dependency edges, modalities, token savings, artifact hashes, and the minted URI’s registry-supplied publication author provenance while excluding raw commands, parameters, queries, responses, credentials, and workspace paths. They are not uploaded to the registry and do not follow a Play to another machine. See references/publish/birth.md for capture, binding, privacy, and lookup semantics. Normal users need only $play birth …; capture and bind are controller-owned lifecycle commands shown here for diagnostics and integration testing.

Open the externally read-only Play inbox:

$play whats new
$play whats new this week
$play digest                       # compact command alias
scripts/bin/play-digest --remember --days 1 --json
scripts/bin/play-digest --since 2026-08-03T00:00:00Z --json
scripts/bin/play-digest --checkpoint host-checkpoint.json --json
scripts/bin/play-digest --org modiqo --days 7 --json

“What’s new” is intentionally framed like an inbox. It groups new and revised Plays by organization and shows each Play’s title, publication author when provenance supplies one, short description, visibility, timestamp, and canonical reference. It then shows the top 10 public Plays in authorized organizations ranked by lifetime downloads. Registry flow info does not currently expose run counts, so the UI labels downloads explicitly and never calls them runs or trending activity.

Selecting a card enters read-only inspection before execution approval. Publication authors are display metadata; Play does not equate an author string with the current signed-in identity. Ranking scope and missing global, run, or personal metrics are explicit rather than inferred. The emitted checkpoint token can be persisted by an authorized host for gap-free daily delivery; the command does not write host state unless --remember is explicit.

On normal $play whats new requests, Play uses remembered mode. It stores only a stable awareness SHA, UTC checkpoint, and authorized-scope contract in ~/.rote/play/digest-state.json. If the current snapshot has the same SHA, the next response is simply “Nothing new since your last Play check.” The moving time window is excluded from the SHA, and no inbox contents or credentials are stored.

Recurring delivery is optional and must be explicitly requested. Its host-neutral two-phase contract remains available for an authorized scheduler:

scripts/bin/play-scheduler-probe
scripts/bin/play-delivery prepare --target-key daily-self --channel harness --days 1
scripts/bin/play-delivery release --envelope envelope.json --ack delivered-ack.json

The host scheduler owns recurrence, destination delivery, and storage. prepare emits an immutable envelope with a deterministic delivery ID; release emits the next checkpoint only for a matching successful acknowledgment and never persists it. Failed sends therefore leave the prior checkpoint unchanged. Play never installs or fabricates a scheduler as part of an on-demand digest request.

Search normalizes punctuation and repeated terms, runs both sources concurrently, deduplicates aliases and versions by canonical Play reference, and shows a URI, local availability, and the next read-only inspection command for every registry-addressable result.

The organization view shows active member, private Play, public Play, and total counts. The Play view groups private and public Plays under each authorized organization. An ambiguous $play list request presents both views as structured choices supported by the active harness.

For diagnostics or integrations, the same reusable building blocks are available directly:

scripts/bin/play-search recent emails --json
scripts/bin/play-inspect warsaw-rust/posthog-dau-report@0.0.3 --json
scripts/bin/play-inventory --json
scripts/bin/play-handoff prepare --stdin --json
scripts/bin/play-handoff verify --stdin --json
scripts/bin/play-birth show weekly-customer-report --json
scripts/bin/play-birth verify weekly-customer-report --json
scripts/bin/play-question approve_play_run --harness codex
scripts/bin/play-question approve_play_run --harness claude
scripts/bin/play-question approve_play_run --harness kimi

The question command maps the same prompt and event contract to Codex request_user_input, Claude and Kimi askquestion, or a numbered Markdown fallback. play-inspect normalizes the complete rote play inspect <reference> --json result into a stable disclosure. After approval, the controller performs exactly one rote play run <exact-reference> <approved-parameters> --yes. It uses rote flow or rote registry flow only where rote play has no equivalent capability and never decomposes a failed Play operation into a pull-plus-Flow-run fallback.

After a new Play is released, Play captures its owner-private birth object. After publication, it binds the object to the registry content hash, indexes the Play, reads the canonical registry entry back with JSON inspection, verifies its owner/version/visibility, and only then reports success.

Optional thinking-orbs UI

React-capable hosts can use the adapter in ui/thinking-orbs to render thinking-orbs from the authoritative Play machine state:

just ui-install
just ui-check
import { PlayActivity } from '@modiqo/play-thinking-orbs';

<PlayActivity playState="creator_search" />

The mapping uses all nine animations for distinct trajectories: listening for declared prompts, searching for discovery, solving for classification and verification, connecting for existing-Play inspection/execution, weaving for multimodal exploration, shaping for crystallization, composing for release/publication, working for result assembly, and breathing for paused terminal states. Every machine state has exactly one accessible status label, and tests fail if the machine and mapping drift.

The installed skill also teaches the agent to use the same presentation at meaningful milestones:

scripts/bin/play-presentation creator_search
# ◌ Peeking through the Play shelves…

scripts/bin/play-presentation use_run --json
# play.presentation/v1 payload for a capable host renderer

When a compatible MCP Apps/custom-UI host exposes a callable renderer, PlayActivity displays the animated orb and message. Installing the skills-only plugin does not create that renderer. Codex CLI and Claude Code text transcripts use the exact static glyph and message without claiming it is animated. During the blocking rote play run command, Rote retains ownership of its own progress display.

This is an optional host adapter, not a claim that a skill can replace native Codex, Claude Code, Cursor, or Kimi activity chrome. Hosts without a custom React surface continue to receive Play's milestone-only text updates. The adapter depends on thinking-orbs 0.2.0 from Jakub Antalik under the MIT license; no upstream source is copied into this repository.

Disable

Remove Play from every managed harness root and restore the exact original rote activation files:

just uninstall
just status

Restart running harnesses after disabling the profile. Uninstall fails closed if a managed Play link was replaced or a rote activation file changed after installation; it will not overwrite the newer content silently.

Development checks

just package
just package-check
just ui-check
just test

The tests exercise the declarative Play machine and the complete activation lifecycle in temporary harness roots, including installation, verification, idempotency, rollback, and conflict handling.

The foundation is Python-only. Commands under scripts/bin/ and harness entrypoints under scripts/harness/ are thin executables; reusable command, private-store, birth-certificate, registry, search, inventory, digest, elicitation, typed specialist handoff, and machine-validation logic lives in scripts/lib/play/. References and tests are grouped by controller, awareness, Explore, publication, integration, and harness use case.

For isolated testing, override the discovered roots or reversible state location:

PLAY_HARNESS_ROOTS=/path/one:/path/two just install
PLAY_PROFILE_STATE=/tmp/play-profile.json just install

About

Play-first discovery, safe execution, creation, sharing, and awareness for Rote.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages