Reuse work that already works—without giving up visibility or control.
Play is a sidekick for Codex, Claude Code, Kimi, Cursor, Hermes, OpenCode, and DeepSeek Harness. Before your agent starts a task, Play checks whether a saved procedure already produces the result you want. If one fits, you can inspect it and approve the exact run. If none fits, Play gets out of the way and your agent works normally. When that work turns out to be useful again, Play can help save it for next time.
You do not need to learn a workflow language or replace your agent. Ask for outcomes in ordinary language.
- Play is this sidekick. It finds, explains, runs, and saves reusable procedures.
- A Play is one saved, inspectable procedure—for example, “retrieve recent emails” or “deploy staging and post a summary.”
- Rote is the local engine that runs Plays and connects them to tools and APIs. Your credentials stay on your machine.
The docs sometimes say harness. That simply means the agent app you use, such as Codex or Claude Code.
On macOS or Linux, run:
curl -fsSL https://getrote.dev/playoffs/install.sh | shThat is the whole setup. Play finds your agent apps, checks Rote and its skills, and shows what it
will install, update, or refresh. For Codex and Claude Code, it also refreshes the Play marketplace,
removes any stale cached plugin, and reinstalls the current plugin before you launch the app.
Nothing changes until you approve the plan, and installing Rote from getrote.dev has its own
confirmation.
When it finishes, Play verifies the setup and tells you where it saved the report. Restart your
agent app, then continue to step 3. The installer requires Python 3.10+ and
uv; advanced options are in the
installation reference.
Start a new conversation and invoke Play:
$play # Codex and Cursor
/play # Claude Code, Hermes, OpenCode, and DeepSeek Harness
/skill:play # Kimi Code
On first use, Play checks the local setup and guides you through Rote sign-in if needed. It then offers Run Hello, a low-risk example that uses public data, needs no account credentials, and declares no writes.
$play find a Play that retrieves recent emails
/play run the PostHog daily active users report
/skill:play whats new
Before anything runs, Play shows the exact version, required inputs, local setup, credentials by name, and declared effects. Choosing a search result is not approval to execute it; running is a separate confirmation.
If your agent worked something out and you expect to do it again:
$play settle deployed staging and posted the summary
Play checks the recorded work rather than guessing from the conversation. If the procedure is actually reusable, you can keep it for a team, share it with the community, or skip saving it.
For a one-off task, just ask normally. You can also be explicit:
Handle this normally without Play
| You do this | Play does this | You stay in control of |
|---|---|---|
| Ask for an outcome | Searches your local and authorized Play collections | Whether to inspect or ignore a match |
| Inspect a matching Play | Shows inputs, setup, credentials by name, and declared effects | Whether the exact version may run |
| No adequate Play exists | Steps aside and lets your agent work normally | The task, corrections, and approvals |
| Finish repeatable work | Checks whether the recorded steps are worth saving | Team, Community, or Skip |
| Ask “what’s new” | Shows new and revised Plays grouped by organization | Whether to inspect one |
Play is designed to be quiet. Conversation, creative work, and one-off tasks continue without a Play dialog. You can also teach it scoped preferences such as “no Plays while I’m prototyping” or “always offer Plays for deploy chores.”
- Nothing runs merely because search found a match.
- Remote installer code and an exact Play run have separate approval boundaries.
- Credentials stay in Rote’s local stores; Play reports credential names, never secret values.
- Play fails closed when a version, receipt, declared effect, or publication check does not match.
- Owner-private state lives under
~/.rote-play/; Rote’s execution state remains under~/.rote/. - The cross-harness bootstrap preserves unrelated hooks and creates backups before changing supported hook files.
$play # Guided introduction
$play find a Play that retrieves emails # Find by outcome
$play run the PostHog DAU report # Find, inspect, then approve a run
$play whats new # Open your Play inbox
$play settle <what just worked> # Consider saving repeatable work
$play birth weekly customer report # See how one of your Plays was made
$play list my organizations and Plays # Browse authorized collections
Handle this normally without Play # Explicitly skip Play
That is enough for everyday use. Jump to the section that matches what you need next:
- Installation reference—marketplaces, source checkouts, updates, and multi-harness bootstrap.
- Everyday Play commands—searching, running, saving, the inbox, and birth certificates.
- Architecture and internals—the state machine and typed runtime.
- Development checks—package, test, benchmark, and UI validation commands.
Play stays predictable by making each layer own one job:
| Layer | Responsibility |
|---|---|
SKILL.md |
Teaches an agent how to enter the runtime and handle its next boundary. |
play-machine |
Owns search, inspection, approval, execution control, verification, saving, and fail-closed behavior. |
| Rote skills | Own setup, tools, browsers, adapters, workspaces, authoring, and publication. |
| Rote CLI and registry | Run exact Plays locally and distribute authorized Plays. |
The following sections are primarily for maintainers and integrators.
Play is driven by one declarative machine,
references/controller/machine.yaml (play.machine/v1).
The typed runtime loads and validates the bundle once per invocation, executes eligible
deterministic actions until a model, human, specialist, or terminal boundary, and accepts only
events declared by actions.yaml and
prompts.yaml. It never jumps states from conversational
intuition. Initial state: invoke. Terminals: receipt, completed, exited, blocked.
stateDiagram-v2
direction TB
[*] --> invoke
%% ── Typed invocation ──
invoke --> onboarding : empty $play, /play, or canonical URI
invoke --> search : unambiguous outcome (fast lane)
invoke --> save_judge : $play settle (post-task re-entry)
invoke --> qualify : ordinary request
%% ── 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 orgs / plays
qualify --> birth_show : birth lookup
qualify --> standby_exit : explicitly excluded (ledger writes)
qualify --> exited : conversation
%% ── Search and adequacy ──
search --> classify : results complete
search --> search_offer : search-only request
search_offer --> use_inspect : result selected
classify --> use_inspect : full match (arguments do not dilute)
classify --> standby_exit : partial / uncertain / no match
%% ── Use (run a saved Play) ──
use_inspect --> use_decide : read-only inspection
use_decide --> use_parameter_offer : required parameter missing
use_parameter_offer --> use_decide : typed value supplied
use_decide --> use_prepare : exact local Play
use_decide --> use_offer : remote pull consent
use_offer --> use_prepare : approved
use_prepare --> use_run : run handoff bound
use_run --> use_verify : unchanged output
use_run --> use_auth_repair : recoverable adapter auth
use_auth_repair --> use_inspect : validated rote-adapter-config repair
use_run --> standby_exit : drifted / failed
use_verify --> use_receipt : outcome verified
use_verify --> standby_exit : not verified
use_receipt --> receipt
%% ── Stay out of the way + the save hook ──
standby_exit --> exited : hook armed, ledger updated, silence
save_judge --> crystallize : worth saving (trace evidence)
save_judge --> exited : one-off
%% ── Save lifecycle (delegated to rote specialists) ──
crystallize --> save_prepare : rote-flow-crystallization candidate
crystallize --> completed : not reusable
save_prepare --> save_offer : namespaces resolved
save_offer --> author_release : Team / Community
save_offer --> completed : Skip
author_release --> birth_capture : unpublished release
birth_capture --> publish : private org / public owner
publish --> birth_bind : publication matches captured birth
birth_bind --> index
index --> saved_inspect : canonical readback
saved_inspect --> publication_gate : public credentials + smoke
saved_inspect --> birth_present : private
publication_gate --> birth_present : verified
birth_present --> completed : certificate presented
%% ── Awareness, creator, management ──
awareness_collect --> awareness_offer : new items
awareness_collect --> completed : unchanged
awareness_offer --> use_inspect : play selected
creator_search --> creator_offer : related Play exists
creator_search --> standby_exit : no match — hook armed
creator_offer --> use_inspect : use existing
creator_offer --> standby_exit : adapt / create outside the machine
management --> completed
birth_show --> completed
receipt --> [*]
completed --> [*]
exited --> [*]
blocked --> [*]
(The diagram groups the onboarding, team-invite, auth-repair, and publication sub-chains for
readability; machine.yaml is the exact authority.)
There is deliberately no Explore lane. Earlier versions of this machine orchestrated
exploration — modality routing, adapter discovery, effect approvals — re-implementing what the
rote skills already own. Today, when no adequate Play exists, Play arms the save hook and steps
aside; the agent works normally through rote; and $play settle re-enters for the save judgment.
Fewer states, fewer bespoke entry paths, fewer places for bugs to hide.
State ownership is explicit: play owns invocation classification, prompts, evaluators,
deterministic verification, and the standby/ledger writes; rote-specialist states
(crystallize, author_release, publication, management_list, team/org actions) are delegated
through typed play.handoff/v1 packets and validated play.handoff-receipt/v1 receipts;
flow-runtime owns saved_inspect, publication_credentials, and publication_smoke via
first-class Rote surfaces. tests/controller/test_machine_conformance.py fails when the machine,
actions, prompts, or the thinking-orbs presentation mapping drift.
Three evaluator (model) boundaries remain in the whole machine: request qualification (ledger- aware), creator classification, and the save-worthiness judgment. An adequate local Play with bound parameters runs with zero model and zero human yields — one runtime call to the receipt.
The executable controller lives in
scripts/lib/play/controller.py. It compiles the authoritative
Play YAML into python-statemachine 3.2, while
retaining Play's machine, action, prompt, context, and handoff contracts as the source of truth.
The runtime provides typed cursors and events, context-schema validation, bundle-SHA binding,
derived guards, mutation semantics, checkpointed play.context/v1, terminal enforcement, and
per-step timing. runtime_actions.py executes safe
deterministic commands without shell interpolation and loops until the next evaluator, prompt,
specialist, or terminal boundary.
The automatic runner owns every deterministic action state. The harness sees only model judgments,
human prompts, exact Rote specialist handoffs, and terminal results. The complete context is
checkpointed in an owner-private, 24-hour continuation store under ~/.rote-play/continuations;
stateless CLI calls exchange only a random 24-character continuation ID.
Install the locked dependencies and inspect the compiled bundle:
uv sync
uv run scripts/bin/play-machine describe --json
printf '%s' '{"run_id":"demo","task_key":"demo","request":{"original":"Review this repository"}}' \
| uv run scripts/bin/play-machine run-until-yield --stdin --jsonMeasure controller-only latency with just benchmark-controller and just benchmark-runtime.
The 2026-08-07 baseline (Apple Silicon, Python 3.14.5, then 87 states) recorded one-time compile
at 76–82 ms, warm transitions at 0.58 ms median / 0.78 ms p95, and the full invoke-to-evaluator
loop at 54 ms; the machine has since shrunk to 70 states and the ~4 KB activation skill replaced a
34.9 KB model-owned controller manual. Treat these as development baselines, not cross-machine
guarantees.
Use the same command on a new machine or to bring an existing installation up to date:
curl -fsSL https://getrote.dev/playoffs/install.sh | shBefore making changes, the installer shows:
- the detected Codex, Claude Code, Kimi, Cursor, Hermes, OpenCode, and DeepSeek Harness installations;
- whether Rote is missing, current, or has an update available;
- whether Rote skills need to be installed or refreshed in each selected app;
- the Play installations and hooks it will configure.
After approval, it performs that plan, verifies each selected app, and saves a JSON and Markdown
report under ~/.local/state/play-bootstrap/runs/. The final status card gives each app's launch
command, exact Play invocation, any remaining action, and a few starter prompts. Full structured
command output stays in the saved JSON report; the terminal shows bounded human summaries unless
you explicitly pass --json.
Invocation differs by app:
| App | Start | Invoke Play |
|---|---|---|
| Codex | codex |
$play |
| Claude Code | claude |
/play |
| Kimi Code | kimi |
/skill:play |
| Cursor | Open Cursor | $play |
| Hermes Agent | hermes |
/play |
| OpenCode | opencode |
/play (installed as a managed command bridge) |
| DeepSeek Harness (developer preview) | dsh web |
/play |
By default, Play selects the top three detected apps. To choose explicitly:
curl -fsSL https://getrote.dev/playoffs/install.sh \
| sh -s -- --harness codex --harness claudeRepeat --harness with any of codex, claude, kimi, cursor, hermes, opencode, or
deepseek. DeepSeek Harness is still a developer preview upstream.
For automation, approve the displayed Play plan with PLAY_INSTALL_YES=1. If Rote may be missing,
approve its separate official installer explicitly as well:
curl -fsSL https://getrote.dev/playoffs/install.sh \
| env PLAY_INSTALL_YES=1 PLAY_APPROVE_REMOTE_INSTALLER=1 shOmit PLAY_APPROVE_REMOTE_INSTALLER=1 when Rote is known to be installed. Set
PLAY_INSTALL_TOP_K=<n> to change the default number of selected apps.
From a checkout, run the same guided bootstrap directly:
scripts/bin/play-bootstrap install --top-k 3To separate review from execution, first create a read-only plan:
scripts/bin/play-bootstrap plan --top-k 3
scripts/bin/play-bootstrap plan --top-k 3 --jsonThen apply its exact ID:
scripts/bin/play-bootstrap apply --top-k 3 --plan-id sha256:<plan-id>Add --approve-remote-installer only after approving the official Rote download. The bootstrap is
safe to retry: it updates Rote only when an update is available, refreshes existing Rote skills,
refreshes and reinstalls the Play marketplace plugin for selected Codex and Claude targets,
preserves unrelated hooks, and backs up every hook file it changes. An explicitly disabled Codex
Play skill remains a user choice: the report asks you to enable it in /skills before restarting.
Reports never contain credentials.
Pin both the script and downloaded archive to the same release:
curl -fsSL https://raw.githubusercontent.com/modiqo/play/v0.4.6/install.sh \
| env PLAY_INSTALL_REF=v0.4.6 shTo inspect the small bootstrap before running it:
curl -fsSLo /tmp/install-play.sh \
https://raw.githubusercontent.com/modiqo/play/main/install.sh
less /tmp/install-play.sh
sh /tmp/install-play.shjust package
just plan
just install
just verify-profilejust install links the checkout, so edits become live after a harness restart. just install-copy
exercises the durable-copy path used by the curl installer.
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.
The Rote skill provider is a prerequisite so Play can hand missing local installation to the
guided rote-setup specialist:
# 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-skillsAfter Play is installed, the play-machine launcher is on PATH; harnesses invoke it directly
without locating the skill directory or its Python environment. play-machine is a Python
entrypoint, not a compiled artifact: the installer writes a small executable launcher that uses the
pinned environment (bootstrapping through uv when needed). The preflight distinguishes a missing
launcher, an incomplete bundled runtime, an unavailable Python environment bootstrap (uv or an
already active pinned environment), a missing Rote CLI, missing Rote skills in the active harness,
authentication, and rote play capability; it also reports cross-harness coverage and
multi-select repair targets. An empty $play or /play probes
the local binary and identity. If either
is missing, Play invokes rote-setup; that specialist asks before downloaded installer code, login,
credentials, or optional onboarding. Ordinary requests are lexically classified and qualified
first; only Play-bound evaluator events run the full preflight, so excluded conversation and
repository work do not pay the identity/capability probe.
Public Play URIs can still show their read-only public card before the CLI exists.
Marketplace installs repair this launcher automatically from Play's session-start hook. If a
harness cached or enabled the plugin without completing activation, $play also runs the bundled
scripts/bin/play-activate and continues through the bundled runtime in the same turn. This works
before Rote skills are present; later sessions converge Rote skills discovered in Codex or Claude
plugin caches. In Codex, an explicitly disabled Play skill remains a user preference: open
/skills, enable Play, and restart the session.
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-skillsThis 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-skillsThe public marketplace source is modiqo/play, so . can be replaced with that GitHub
owner/repository from outside this checkout. The guided installer converges the separately trusted
Rote skill distribution before reinstalling Play; every harness still uses the same runtime
preflight because plugin metadata alone cannot prove CLI installation or login state.
Kimi, Hermes, OpenCode, and DeepSeek Harness have no Play plugin marketplace. The installer uses
their native personal skill roots (and shared ~/.config/agents/skills or ~/.agents/skills where supported), then installs
the invocation surface each app expects. For OpenCode, that includes a managed global /play
command because its standard skill surface is tool-driven rather than a direct slash command.
Install Play for them from this checkout with:
just plan
just installinstall discovers every supported local harness and 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-profileIn 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.
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 remove play@play-skills
codex plugin add play@play-skillsFor Claude Code:
claude plugin marketplace update play-skills
claude plugin uninstall play@play-skills --scope user
claude plugin install play@play-skills --scope userRestart 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-profileSkill-directory harnesses have no Play plugin cache to upgrade; rerun the installer to refresh their personal skill links and integrations. 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 updateThen 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.
Preview every harness root and canonical rote skill that will change:
just planActivate the Play-first profile and verify it:
just install
just verify-profileAfter editing the source skill, confirm that every source-linked installation is still valid:
just updateThe links make source edits live immediately; a running harness must still be restarted to reload the revised skill.
install detects Codex, Claude Code, Kimi, and Cursor, discovers their skill roots 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-rootsStart a supported interactive harness after verifying the profile:
just harness codex
just harness claude
just harness kimiStart a compact Codex session without changing global Codex configuration:
just harness-quietThis 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 search-and-step-aside boundary:
just smoke codex
just smoke claude
just smoke kimiRun every locally installed supported smoke-test harness with:
just smoke-allSmoke tests start new harness processes and may consume model credits.
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, save, and share without memorizing lifecycle commands:
$play create a reusable weekly customer report
$play settle built the weekly customer report end to end
Handle this normally without Play
Play always searches before creating. If an adequate Play exists it offers Inspect existing or
lets you adapt outside the machine. If none exists, Play arms the save hook and steps aside — the
agent does the work normally through the rote skills, with rote's own workspace capture recording
the evidence. When the work settles, $play settle <one-line summary> re-enters the machine and
the save-worthiness judge examines the trace, not the conversation: at least two effect-bearing
steps, at least one input that would vary on reuse, a stable output shape, and a recurrence prior.
A worth-saving verdict leads to one offer:
- Team — release and publish to an authorized private organization, then offer colleague
invites through
rote-org; - Community — release and publish under a selected public owner, verify associated adapter credential contracts, then run the exact public URI once from an isolated directory;
- Skip — keep the result without publishing or indexing a Play.
Execution ownership stays with the rote skill suite end to end: crystallization with
rote-flow-crystallization, release with rote-flow-authoring, publication with rote-registry.
Play never re-implements those flows; it validates each specialist's typed receipt and blocks on
mismatch — a specialist cannot claim success in prose.
One save-time caveat Play discloses honestly: browser-derived (DRIVE) work has a crystallization
limit. Typed browser steps carry navigation, waits, clicks, typing, and canonical extract slices
only; an outcome whose required facts exceed those slices can crystallize only as a legacy stepless
body, which rote play run rejects (play_run_eligible: false) — a publication gate requiring
explicit approval. See the
DRIVE crystallization limit 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. The final typed Python renderer frames the verified certificate with the Play URI, X and LinkedIn copy for Public Plays, and a redacted trace-learning summary showing explicit successes, errors, and unknown outcomes. It closes with a personalized thank-you to the human domain expert. Organization membership, invitations, and sharing use the organization/list surface rather than hidden local state.
Release and publication are deliberately separate specialist handoffs. rote-flow-authoring must
stop after an explicitly unpublished local release. Play then captures the immutable birth object,
and only a fresh rote-registry handoff may publish that exact artifact while echoing the captured
birth SHA. If a broad registry publication request publishes early, the machine emits
publication_boundary_violated and blocks instead of treating a registry summary as completion or
offering a retrospective certificate.
Public namespace resolution also happens before release. The typed
play-public-owner probe reads the claimed Rote profile handle and
authorized organizations, distinguishes those two identity kinds, and inserts a bounded summary
into the save prompt. Public owner selection is completed before author_release; a later generic
CLI hint to claim a handle cannot trigger a redundant rote profile set-handle attempt. If the
probe is unavailable, Private and Skip remain available while Public fails closed.
A successful registry push is not enough for a Public Play. After canonical readback, Play uses
scripts/bin/play-publication-gate to compare every associated
adapter across three Rote-owned views:
- the exact Play resolver's selected source, credential demand, and receipt status;
- the installed adapter's version, fingerprint, auth family, and credential binding names;
- the selected registry adapter's published version, fingerprint, and auth contract.
Source/provenance, version, fingerprint, auth family, and environment-variable names must agree.
This deliberately catches contracts such as local GITHUB_API_TOKEN versus published GH_TOKEN
even when the two adapters have the same fingerprint. The checker handles static credentials and
OAuth-family metadata, but never reads, hashes, prints, copies, or persists a token value.
Only after that metadata check passes does Play invoke exactly one
rote play run <registry-returned-versioned-uri> <verified-parameters> --yes from a fresh temporary
working directory under /tmp. The temporary directory is removed afterward. Controller context
retains only status, hashes, byte count, and elapsed nanoseconds—not the smoke run's primary output.
This proves that the canonical URI resolves and runs with the current host's Rote/credential setup;
it does not prove every consumer already has the required credentials.
A mismatch, missing credential, provenance failure, or unsuccessful run blocks Play-page links,
social copy, and congratulations. The gate does not silently pull or republish an adapter, change
token_env, authenticate, delete transaction backups, or retry. Remediation stays with the
appropriate Rote skill, after which the canonical gates run again. See the
GitHub token-env incident RCA.
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, explicit success/error/unknown outcomes, 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
scripts/bin/play-public-trends --play modiqo/hello@0.1.0 --json
scripts/bin/play-public-trends --org modiqo --workers 8 --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. Public JSON cards are fetched concurrently, grouped by their declared organization or user owner kind, and show both lifetime downloads and installs. Registry and public cards do not currently expose run counts or windowed counter changes, so the UI never calls cumulative totals runs or trending activity. The reusable report records per-card and batch fetch latency.
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.
The inbox also has a proactive, zero-token surface. A background refresh caches both tiers —
a precomputed one-line summary and the full digest with rendered markdown — plus the authorized
hub catalog, under ~/.rote-play/inbox-cache.json:
play-inbox refresh --if-older-than 6 # background-job body; skips when fresh
play-inbox line # instant; prints one line or nothing
play-inbox details # cached full inbox, no networkWire it stale-while-revalidate at session start (no daemon or cron): the hook serves the previous refresh instantly and detaches the next one. The line counts only unseen items — the interactive digest owns the acknowledgment checkpoint, so viewing "what's new" quiets the banner on its own.
The same hook surface carries the interception loop:
play-intercept prompt # UserPromptSubmit: local + hub-catalog match, one line or silence
play-intercept settle-nudge # Stop: one reminder per armed save hook per sessionHook state (index cache, cooldowns, nudge markers, preference ledger, standby hooks) lives in
shared ~/.rote-play/ stores, so the safeguards compose across harnesses: a Play saved from one
harness is an interception candidate in every other, and nudges never double-fire.
Cache lifecycle: just install kicks one detached warm-up refresh, and every session start
re-refreshes when the cache is older than six hours — no cron, no manual sync. Without an
installed, authenticated Rote every tier degrades to silence: the refresh fails quietly, the
catalog stays empty, and the interceptor's advice line becomes the onboarding funnel (play skill →
Rote probe → guided rote-setup). The first refresh after sign-in populates the catalog with
everything the new identity is authorized to see.
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.jsonThe 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-public-trends --play modiqo/hello@0.1.0 --json
scripts/bin/play-search recent emails --json
scripts/bin/play-inspect warsaw-rust/posthog-dau-report@0.0.3 --json
scripts/bin/play-run --stdin --json
scripts/bin/play-run-output --stdin --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 kimiThe 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 passes the bound inspection and approval packet to play-run. That universal runner
performs exactly one rote play run <canonical-uri-or-exact-reference> <approved-parameters> --yes
and emits the typed controller event. It does not delegate execution to a prose skill, rediscover
the Play, resolve a local path, replay the command to capture output, or ask the harness to construct
a receipt.
Unambiguous outcome verbs route directly from invocation to parallel Play search without a model
qualification or harness preflight round trip. Canonical URIs accept explicit key=value
parameters; inspection deterministically elicits any remaining required values before pull consent.
It uses rote play for local Play operations and rote registry play for registry distribution and
registry-scoped discovery. It never uses legacy Flow command aliases or decomposes a failed Play
operation into a manual pull-plus-run fallback.
Successful Use-mode execution passes the complete primary payload directly from use_run to
use_verify. Play retains the declared source, format, manifest, truncation flag, and full-output
reference but does not render or convert the payload. The harness owns presentation.
After verification, the receipt computes integrity and byte-count metadata over the unchanged primary value and returns that same value to the harness. Compact or summary-only output cannot be verified as a complete result; truncated output requires a full-output reference.
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 presents the typed
certificate and reports success. A play_published event alone is always intermediate.
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-checkimport { 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 rendererWhen 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.
Remove Play from every managed harness root and restore the exact original rote activation files:
just uninstall
just statusRestart 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.
uv sync
just package
just package-check
just ui-check
just test
just benchmark-controller
just benchmark-runtimeThe 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, templated elicitation, typed greeting/URI onboarding, typed
specialist handoff, typed controller runtime, public credential/smoke validation, 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