Xbloom studio brew
Design bean-specific hot pour-over and manual-over-ice Americano-style flash-brew recipes for xBloom Studio, research cited roaster/cafe/xPod references, import/query/sync a private app-visible coffee/tea catalog, preview or explicitly add/delete local account recipes, import App brew history, keep a local brew journal for dial-in, run guarded Omni Tea Brewer recipes, dial in by taste, and operate bundled local BLE for diagnostics, settings, scale, grinder, temperature/volume water, recipe load, A/B/C presets, monitoring, cancel, persistent pause/resume, and explicitly gated physical starts. Use for xBloom Studio, Omni Dripper, xPod/NFC Recipe Cards, Omni Tea Brewer, official or saved coffee/tea recipes, iced coffee, C40 conversion, WAIT troubleshooting, electronic-scale readings, standalone grinding/water, brew history, or direct xBloom Bluetooth control.From its SKILL.md
npx -y skills add HomoLand/xbloom-studio-brew --skill xbloom-studio-brewAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
32.1 KB, ~7.2k tokens by cl100k_base, as published. Nobody here has run it
xBloom Studio Brew
Turn bean information into a concrete, validated xBloom Studio recipe and, when explicitly requested, load or run it through the bundled local BLE controller.
Resolve <skill-dir> to the absolute directory containing this file. Run only the scripts inside
that directory; never recreate protocol frames in the conversation.
Choose the workflow
Classify the request before acting:
- Recipe only: design, save, validate, and explain the recipe. Do not scan or connect.
- Research and compare: find credible public bean/recipe sources, distinguish native xBloom recipes from adapted manual brews, and let the user choose before creating an executable recipe.
- Private catalog: import authorized App/API or decoded-MMKV JSON; ephemerally sync official, created, Product/xPod, and shared account recipes; preview or explicitly add/delete local recipes; import App brew-history records into the local journal.
- xPod reference: preserve roaster intent but classify it
xPod-native; adapt explicitly before using loose beans with Omni, and never assume an NFC card contains the full recipe payload. - Dial-in: inspect the previous recipe, local brew journal, and tasted result, then change one variable.
- Brew journal: local load/start/cancel/complete telemetry plus optional App history import.
- Load to machine: validate, preflight firmware/state, and arm the recipe. Do not start it.
- Brew now: load first, then require current physical-readiness confirmation before
start. - Tea: use the dedicated tea schema and Omni Tea Brewer protocol; keep load and execute separate.
- Scale: account for the firmware's mandatory entry auto-zero; choose an empty-platform baseline for absolute object weight or a pre-positioned empty vessel for net contents.
- Standalone grinder/water: use their specific owner gate, readiness phrase, and cleanup flow.
- Persistent bridge: prefer the long-lived local bridge for device work so one process owns BLE and serializes coffee, tea, scale, grinder, water, presets, settings, and tuning writes.
- Preset slots: require explicit intent to overwrite all A/B/C presets.
- Diagnostics/settings: use read-only
doctor,scan,probe,settings,advanced, ormonitor; usecancelfor recovery. Persistent writes require their own owner gate.
Do not produce espresso recipes. xBloom Studio brews pour-over, not espresso. When the user asks for iced Americano, offer an Americano-style flash brew and state the distinction briefly.
Non-negotiable flash-brew invariant
xBloom Studio has no separate iced or flash coffee program. Hot and manual-over-ice servings load
the same coffee pour-over frames, and the machine dispenses only programmed water. Local
kind: flash-brew, ice_g, and final water_ml are serving/safety metadata that never reach the
machine. Require the measured ice in the receiving vessel before start; never claim the machine
recognizes or dispenses ice. If a cloud/catalog recipe has a concentrated hot program and mentions
ice only in its name, do not call it stored incorrectly: the App schema cannot preserve ice mass.
Explain that local serving metadata is incomplete, confirm ice/final water, then wrap the unchanged
hot stages as local flash-brew metadata. Never replace the vessel ice with machine bypass.
Design a recipe
Read references/recipe-design.md when creating or adjusting a recipe. Read
references/recipe-schema.md before writing YAML. For advanced community statistical
templates (optional), read references/recipe-baselines.md after the conservative guide.
Mandatory design checklist (before writing any recipe parameters)
Complete this reasoning before emitting grind/temp/pours YAML. Do not skip to a template.
- Prior art (catalog / history / beans first)
- Run
catalog list --kind coffee --query <bean-or-roaster>(and tea if relevant). - Run
beans list --query <bean-or-roaster>andhistory list --limit 20when dialing in. - Read
preferences listwhen taste targets exist. - If an executable catalog recipe matches the bean/lot, prefer exporting and adapting it over inventing a new baseline. State the catalog id in the rationale.
- Run
- Bean model
- Roast (very light → dark), process (washed/honey/natural/anaerobic/…), age, density clues (origin/altitude/variety).
- Map to a conservative
recipe-design.mdrow; only then consider baselines A–G.
- Special cases
- Geisha/Gesha: split Panama washed vs Ethiopia natural (see baselines); never one density myth.
- Flash brew: reconstruct hot water +
ice_gper flash rules; never treat ice as a machine mode. - Clog risk (cake filter): honey/natural + fine grind + many pours + heavy vibration → reduce mid-bed vibration and/or coarsen (baselines clog score).
- Agitation / temp / structure
- Default bloom
vibration: afteronly; no mid-pour vibration without a reason. - Prefer 3 pours for first cups; 4–5 only as advanced or catalog match.
- Justify each of grind, temperature, ratio, rpm, pattern in one short phrase each.
- Default bloom
- Only then write YAML and validate.
Never use “most recipes use X” as the only reason for a parameter.
Design steps
- Extract drink style, roast, roast date, process, origin/variety, tasting notes, water, and the user's flavor target. Prefer the user's target over the roaster's notes.
- Complete the mandatory design checklist above (catalog/history before invention).
- When an exact roaster, coffee, lot, or xPod is identifiable, or the user asks for expert/public
recipes, read
references/web-enrichment.mdand search first-party public sources if web tools are available. If credible recipes exist, show the Skill baseline and up to two cited adaptations for selection. Never load an external adaptation before the user chooses it. - If details are missing after research, use reasonable assumptions and state them. Default to 15 g coffee, 240 g final water, Omni Dripper 2, and filtered water; never assume unknown water is RO.
- Choose the smallest useful pour count and a conservative first-cup profile (or catalog export).
- For manual-over-ice flash brew, keep the unchanged coffee pour-over program separate from vessel
ice; label both ratios and require the ice before physical start.
For an intentional bypass recipe, keep extraction pours and final bypass explicit; bypass is
machine water, not display-only metadata. Use
RT/BPonly when that mode is intentional. - Copy
assets/hot-template.yamlorassets/flash-brew-template.yamlto a user/workspace path, or export from catalog. Never modify the installed template in place. - Fill every public field with a concrete value. Do not add undocumented protocol fields.
- Validate the finished local file:
python <skill-dir>/scripts/xbloom.py validate <recipe.yaml>
- Fix every validation error rather than bypassing the guard. Present the validated recipe, its assumptions, checklist highlights (especially catalog match or deliberate deviation), and exactly one first correction for the next cup.
Do not claim a generated recipe has been taste-validated. After the user tastes it, use the result and drawdown behavior to adjust one variable at a time.
Use the private recipe catalog
Read references/catalog.md before importing or syncing app-visible recipes. Catalog operations
are offline from BLE and require no machine connection:
python <skill-dir>/scripts/xbloom.py catalog status
python <skill-dir>/scripts/xbloom.py catalog import-json <authorized-export.json>
python <skill-dir>/scripts/xbloom.py catalog import-mmkv <decoded-mmkv.json>
python <skill-dir>/scripts/xbloom.py catalog list --kind coffee --executable
python <skill-dir>/scripts/xbloom.py catalog list --kind tea
python <skill-dir>/scripts/xbloom.py catalog show <id-or-name>
python <skill-dir>/scripts/xbloom.py catalog export <id> <workspace-recipe.yaml>
python <skill-dir>/scripts/xbloom.py catalog login-sync --region <china|international>
python <skill-dir>/scripts/xbloom.py catalog push <recipe.yaml> --region <china|international>
python <skill-dir>/scripts/xbloom.py catalog delete --region <china|international> --table-id <id>
python <skill-dir>/scripts/xbloom.py catalog history-sync --region <china|international>
python <skill-dir>/scripts/xbloom.py history status
python <skill-dir>/scripts/xbloom.py history list --limit 20
python <skill-dir>/scripts/xbloom.py history note <event_id> "tasting notes"
python <skill-dir>/scripts/xbloom.py beans list
python <skill-dir>/scripts/xbloom.py beans add "M2M 花魁 SELECT" --process natural --roast-level light
python <skill-dir>/scripts/xbloom.py preferences set taste "{\"sweetness\":4,\"acidity\":3}"
python <skill-dir>/scripts/xbloom.py preferences list
The shared local catalog runtime is state.db (recipes / recipe_revisions), the same store
used by Web recipe APIs. Legacy catalog/catalog.json is import-only after migration. Treat
“all” as all records in the supplied export or visible to the user's own account and region, not a
global xBloom database. Keep the normalized catalog private: redistribution rights default to
unknown. xPod and J20 entries are reference-only; tea exports use the dedicated tea path.
login-sync reads all five account categories by default. Those reads and two explicitly
owner-approved add-only writes were live-service verified against the China tenant on 2026-07-14,
including readback and no-write idempotent replay. Supply email/password only through
XBLOOM_ACCOUNT_EMAIL and XBLOOM_ACCOUNT_PASSWORD (or the hidden interactive password prompt);
never print or persist them. The older explicit-form sync remains available through
--config/XBLOOM_CLOUD_CONFIG.
catalog push is an offline preview by default. Remote use is add-only and idempotent: it refuses
same-name/different-parameter conflicts. Run --apply --confirm-write own-account-cloud-recipe
only after the user explicitly approves that exact recipe and account mutation in the current
interaction. Never use a live account merely to test the write path; tests must mock the endpoint.
catalog delete is also preview-first. It deletes only a member-created cloud recipe by remote
tableId, after verifying that id is present in the account created list. Apply only with
--apply --confirm-delete own-account-cloud-recipe-delete.
catalog history-sync imports App brew-history records into the local journal so phone-only
brews can still be reviewed. Confirmed terminal/cancel/stop history is written once by the
bridge into state.db (history_events); Skill load/cancel/monitor paths do not duplicate
that journal. Use history status|list|note for dial-in review. App history is coarser than
local telemetry.
Prepare local BLE
Read references/device-safety.md completely before the first BLE action in a task.
For a scale, grinder, or water request, also read references/standalone-tools.md. For tea, read
references/tea-brewing.md.
Bootstrap once per user/Agent environment. The virtual environment is stored in the writable
state directory, not inside the installed Skill, so upgrades and read-only package caches are safe.
Release bundles install the exact vendored core wheel offline, then non-core deps from the
universal hashed requirements-runtime.lock (pip --require-hashes); see
references/deployment.md.
python <skill-dir>/scripts/bootstrap.py
Check runtime and discover the machine:
python <skill-dir>/scripts/xbloom.py doctor --scan
python <skill-dir>/scripts/xbloom.py scan
If exactly one xBloom is nearby, commands can scan automatically. If several exist, put the selected identifier before the subcommand:
python <skill-dir>/scripts/xbloom.py --address <ble-address-or-uuid> probe
Treat addresses and serials as private local identifiers. Do not reproduce them in public output.
For pause/resume or control-grade telemetry, start the bundled loopback-only bridge. Starting the daemon does not scan, connect, grind, or dispense water:
python <skill-dir>/scripts/xbloom.py bridge start
python <skill-dir>/scripts/xbloom.py bridge status
python <skill-dir>/scripts/xbloom.py bridge connect
python <skill-dir>/scripts/xbloom.py bridge events --since 0
The bridge is the sole BLE owner. All active connection/machine commands (probe, load, start, tea,
scale, grind, water, settings, cancel, save-slots, …) use typed bridge RPC through the daemon.
Only passive scan / doctor --scan may call BLE discovery directly. bridge stop refuses during
an activity; bridge stop --force first sends that activity's guarded stop/cancel path. Read
references/deployment.md for daemon state, external runtime, and deployment details.
Load without starting
Use this as the normal device workflow:
python <skill-dir>/scripts/xbloom.py probe
python <skill-dir>/scripts/xbloom.py validate <recipe.yaml>
python <skill-dir>/scripts/xbloom.py load <recipe.yaml>
load performs its own read-only firmware/state preflight, transmits only guarded load frames,
waits for the machine's armed state, and returns a durable workflow_id with an immutable
snapshot stored in state.db. Source YAML paths are provenance only after load; the SQLite
snapshot is authoritative. Confirm that JSON reports "status": "armed", a non-empty
"workflow_id", and "remote_start_sent": false. Legacy armed-state.json is import-only
(via xbloom-state migrate) and is never written by runtime.
Tell the user the machine is armed and can be approved physically. Do not run probe again while
a durable coffee workflow is active. To exit instead, run:
python <skill-dir>/scripts/xbloom.py cancel
Start and monitor a coffee brew
Remote start is an available core capability, not an add-on, but it has owner and per-brew gates. Never set the owner opt-in yourself. Never infer physical readiness from BLE.
Only after the user explicitly confirms water, beans, filter, dripper, cup, and clear surroundings
in the current interaction—and measured vessel ice for flash-brew—and only when the deployment
owner has enabled remote start, run:
python <skill-dir>/scripts/xbloom.py start --workflow-id <id-from-load> --confirm-ready cup-filter-water-beans
start uses --workflow-id or the bridge active durable coffee workflow. The optional positional
recipe argument is ignored/deprecated CLI compatibility only and is never opened, validated, or
hashed. After load, deleting or editing the source YAML does not change the immutable snapshot.
Loaded recipes wait indefinitely for explicit start or cancel — there is no five-minute
loaded expiry. Hardware commands go through the local bridge daemon (sole BLE owner); monitor
only observes status/events and never connects, ensures, or mutates. Client exit does not release
the brew. For listen-only observation of a running daemon workflow, use:
python <skill-dir>/scripts/xbloom.py monitor --workflow-id <id> --duration 300
If monitoring reaches its duration without a terminal machine state, start exits with code 3,
reports completion_unconfirmed, and preserves durable workflow ownership for monitor or
cancel. Never interpret that timeout as a failed brew or a completed brew.
Interpret liquid telemetry as three separate measurements:
target_dispensed_water_ml: programmed recipe water, including machine bypass when present.dispensed_water_ml: the machine's cumulative output for this operation (report40523), not the amount remaining in a reservoir or direct-feed supply.cup_weight_g/cup_delta_g: raw cup-scale weight and its net increase from the observed operation baseline. Retained water, grounds, ice, evaporation, and timing can make this differ from the machine meter.
Do not claim the protocol exposes supply inventory; it only reports whether water is available and which source is selected.
If anything is uncertain, cancel. Never schedule an unattended start.
For a coffee brew that must support pause/resume, keep the whole load/start flow on the bridge:
python <skill-dir>/scripts/xbloom.py bridge coffee-load <recipe.yaml>
python <skill-dir>/scripts/xbloom.py bridge coffee-start \
--confirm-ready cup-filter-water-beans
python <skill-dir>/scripts/xbloom.py bridge pause
python <skill-dir>/scripts/xbloom.py bridge resume
python <skill-dir>/scripts/xbloom.py bridge cancel
Only send pause/resume when bridge status shows a compatible running/paused activity. Command
40518 is state-sensitive: it can confirm a stable awaiting start but pauses while running. Treat
the first awaiting_confirm as transitional; let the CLI observe auto-start and freshly recheck
state before fallback. Never retry 40518 after an unconfirmed start; inspect, monitor, or cancel.
Use the Omni Tea Brewer
Tea requires the dedicated siphon accessory and schema; never emulate it with a coffee no-grind
recipe. Copy the closest official template from assets/tea-*-official.yaml to a workspace path,
then read references/tea-brewing.md, adapt one variable at a time, and validate:
Treat each stage's 80/90 ml as programmed chamber-fill water. The app's ~120/240/360 ml selector
is approximate finished siphon output; firmware owns the post-soak finish and reports it as a
bypass phase. It is not the generic configurable coffee bypass, and 120 ml must never be encoded
as one stage merely to match the display.
python <skill-dir>/scripts/xbloom.py tea-validate <tea.yaml>
python <skill-dir>/scripts/xbloom.py tea-load <tea.yaml>
tea-load only uploads the recipe and returns a durable workflow_id with an immutable
snapshot in state.db. Source YAML paths are provenance only after load. It must report
"status": "tea_loaded", a non-empty "workflow_id", and "remote_start_sent": false.
Legacy tea-loaded-state.json is import-only and is never written by runtime. Execute only after
the owner hot-water gate is enabled and the user currently confirms the Omni Tea Brewer, leaves,
selected water supply, receiving vessel, and clear surroundings:
python <skill-dir>/scripts/xbloom.py tea-start --workflow-id <id-from-tea-load> \
--confirm-ready tea-brewer-water-cup-clear
tea-start uses --workflow-id or the bridge active durable tea workflow. The optional positional
recipe argument is ignored/deprecated and is never opened or hashed. Deleting or editing source
YAML after tea-load does not alter the stored snapshot.
When load and execution should remain on one BLE connection, the same gates and checklist apply to:
python <skill-dir>/scripts/xbloom.py tea-brew <tea.yaml> \
--confirm-ready tea-brewer-water-cup-clear
tea-brew still emits distinct tea_loaded and start_accepted states and preserves a recovery
record until terminal telemetry. Soaking, paused, restarted, and soak-time reports are surfaced
when firmware emits them.
When the bridge is running, keep tea on that owner instead:
python <skill-dir>/scripts/xbloom.py bridge tea-load <tea.yaml>
python <skill-dir>/scripts/xbloom.py bridge tea-start \
--confirm-ready tea-brewer-water-cup-clear
Never schedule it. Use cancel to exit a loaded tea recipe or recover from an unsafe operation.
Use the standalone tools
Read references/standalone-tools.md and keep recipe states separate from FreeSolo modes.
Electronic-scale reading is low risk and does not require a physical-action environment gate:
python <skill-dir>/scripts/xbloom.py scale --duration 30
The official 8003 scale-enter command automatically zeros the load already present. For an
object's absolute weight, require the platform to be empty before starting, wait until JSON reports
"status": "ready", then ask the user to place the object. For net contents, put the empty vessel
on the platform before starting and add contents only after ready. An object present at entry
will read zero; removing it will read negative. There is no decoded enter-without-zero command.
Add --tare only when the user explicitly requests an additional re-tare after entry. Do not
describe the default as "without tare" or imply that omitting --tare preserves the pre-entry
absolute load.
For an interactive absolute-weight request, keep the platform empty through entry, surface
"status": "ready" immediately, then tell the user to place the object. Use a 60-90 second session
when chat latency requires it and report only a stable, plausible positive reading. An all-zero
session did not measure the object's absolute weight; explain the baseline and retry from empty.
With a running bridge, the non-blocking equivalent keeps BLE ownership stable and permits an explicit re-tare or early exit:
python <skill-dir>/scripts/xbloom.py bridge scale-start --duration 90
python <skill-dir>/scripts/xbloom.py bridge scale-tare
python <skill-dir>/scripts/xbloom.py bridge cancel
The grinder is a motor action. Never set its owner opt-in yourself. After current confirmation of beans, receiving cup, clear chute, and clear hands, an enabled deployment may run at most 30 s:
python <skill-dir>/scripts/xbloom.py grind --size 62 --rpm 100 --seconds 10 \
--confirm-ready beans-cup-clear
Respect the durable 60-second grinder rest (grinder_guard in state.db); never invent or delete
a JSON rest file to retry. Standalone water is a hot-water action and uses the hot-water owner
gate. Require a suitable centered vessel, available water at the selected source, the correct
water path, and clear surroundings in the current interaction:
python <skill-dir>/scripts/xbloom.py water --volume 250 --temp 85 --flow 3.5 \
--pattern center --water-source auto --confirm-ready vessel-water-clear
Use --temp RT for the official room-temperature/pass-through setting. RT does not actively cool
the source water to an exact 20 C, and it remains a guarded physical water-dispense action.
--water-source auto follows the source reported by the machine; if that report is unavailable,
require the user to select tank or tap explicitly instead of guessing. Here tap is the
protocol/CLI compatibility name for Studio's direct-feed/auto-refill source, not a promise about
the installation's plumbing.
Use the bridge when interactive grinder or FreeSolo-water pause/resume is required:
python <skill-dir>/scripts/xbloom.py bridge grinder-start --size 62 --rpm 100 --seconds 10 \
--confirm-ready beans-cup-clear
python <skill-dir>/scripts/xbloom.py bridge water-start --volume 250 --temp 85 --flow 3.5 \
--pattern center --water-source auto --confirm-ready vessel-water-clear
python <skill-dir>/scripts/xbloom.py bridge pause
python <skill-dir>/scripts/xbloom.py bridge resume
For bridge grinder control, a missing start/pause/resume ACK triggers fail-closed STOP/QUIT and
must not be retried past the durable SQLite rest guard. Unconfirmed STOP retains recovery and the
BLE link; prompt release runs only after a confirmed durable terminal. For bridge water, claim
completion only when last_operation.result is complete. A result of completion_unconfirmed
or safety_timeout_stopped, or a phase of control_unconfirmed/stop_unconfirmed, requires
recovery and physical verification.
The decoded FreeSolo live-temperature and live-pattern commands are intentionally separate from
pause/resume. They change only the target for the remaining bounded water session: temperature has
physical heating lag, pattern changes the outlet motion, and neither changes total volume or flow.
They do not modify coffee recipe pours. A running center → spiral pattern change is physically
verified on firmware V12.0D.500; command 8016 may apply without an echo, while report 8107 is
optional and reported separately when observed. Live-temperature command encoding and its
completed BLE write are verified, but physical outlet response is unmeasured.
Keep both controls behind their owner gate and exact per-call confirmation, and report verification
per control and firmware exactly as documented in references/device-safety.md.
Never claim a grind or dispense completed unless its command reports success. On uncertainty, use the machine's physical stop/cancel after the wrapper's automatic STOP/QUIT cleanup.
Inspect or change Studio settings
Read persistent user settings and APK-defined mechanical tuning without a write gate:
python <skill-dir>/scripts/xbloom.py settings
python <skill-dir>/scripts/xbloom.py advanced
If the bridge is running, use bridge settings and bridge advanced instead.
Persistent changes are implemented with APK-exact commands, idle/firmware preflight, read-after-write comparison, and best-effort rollback. They have deterministic protocol coverage but have not been physically written by this project, so never set their owner opt-in on the user's behalf:
XBLOOM_ENABLE_SETTINGS_WRITE=I_ACCEPT_PERSISTENT_MACHINE_SETTINGS
python <skill-dir>/scripts/xbloom.py set-settings --weight-unit g \
--temperature-unit C --water-source tank --display medium \
--confirm-write persistent-machine-settings
python <skill-dir>/scripts/xbloom.py set-advanced --pour-radius-level 3 \
--vibration-level 3 --confirm-write mechanical-tuning
For a running bridge, restart it while idle after setting the owner environment variable, then use
the matching bridge set-settings or bridge set-advanced command. The daemon captures owner
gates at startup.
set-advanced derives five radius levels from the individual machine's reported baseline and uses
the APK's six amplitude levels. Do not substitute raw values, write during a loaded/running
workflow, or report success unless exact readback matches. These persistent controls are unrelated
to per-recipe vibration timing and live FreeSolo pattern changes.
Save A/B/C presets
Explain that this replaces all three on-machine presets, then require explicit user approval. Validate each file for both recipe safety and lossless slot representation, then write all three in A/B/C order:
python <skill-dir>/scripts/xbloom.py validate <A.yaml> --slot
python <skill-dir>/scripts/xbloom.py validate <B.yaml> --slot
python <skill-dir>/scripts/xbloom.py validate <C.yaml> --slot
python <skill-dir>/scripts/xbloom.py save-slots <A.yaml> <B.yaml> <C.yaml>
Add --scale on off on only when the user explicitly wants different on-brew scale behavior for
A, B, and C; the default is on on on.
With a running bridge, use bridge save-slots <A.yaml> <B.yaml> <C.yaml> so the daemon remains the
sole connection owner.
Confirm JSON reports "status": "saved" and "brew_started": false. Do not use preset writes
as a substitute for loading one temporary recipe. A/B/C stores pours, grind, ratio, and scale
behavior; the machine measures dose at use time. It cannot store coffee bypass, tea, xPod-native
geometry, J20 recipes, recipe names, notes, or citations. Every preset path rejects bypass_ml
instead of silently omitting it. Read references/catalog.md for the full representation boundary.
Handle failures
- Runtime missing: run
scripts/bootstrap.py; do not install packages globally or inside a read-only Skill cache. - No machine found: confirm Bluetooth is on, the Agent is executing locally, the phone app is not holding the connection, and the machine is nearby. Treat Studio BLE as single-controller in practice: have the user fully close/disconnect the phone app before Agent BLE work.
- Multiple machines: select one with
--addressorXBLOOM_ADDRESS. - Unknown firmware: stop. Only the deployment owner may use the explicit override documented in
references/device-safety.mdafter controlled validation. - Active durable coffee workflow (loaded/armed/unconfirmed):
bridge status/monitororcancel; do not probe or load over it. Authoritative state isstate.db, notarmed-state.json(import-only). - Completion is unconfirmed: leave the durable workflow intact and run
monitor --workflow-id …orcancel; do not retry start (retry protection is bridge-owned). - Active durable tea workflow (tea_loaded/unconfirmed): start with
--workflow-idwhile currently ready, or cancel. Authoritative state isstate.db, nottea-loaded-state.json(import-only). - Grinder cooldown/recovery active: wait for
status.grinder_guardremainder (or recover first). Authoritative state isstate.dbvia bridgegrinder_guard(ready/cooldown/recovery_required/unavailable); never invent or delete a JSON rest file. - Bridge activity uncertain: keep the vessel in place and use
cancel/bridge cancelor the machine's physical control beforebridge stop --force. Top-level hardware commands already use the daemon; they do not open a second direct BLE connection. - Bridge environment changed: restart the idle bridge only when no durable ownership/recovery; owner gates are captured by the daemon when it starts. Never force-stop an activity merely to reload configuration.
WAITor slow drawdown: follow the physical checks inreferences/recipe-design.md, then change one recipe variable.- Interrupted or unsafe operation: run
canceland use the machine's physical control if BLE fails.
Output contract
Match the user's language. Keep the response compact but include:
- Bean assumptions and flavor target.
- For coffee: dose, extraction water, bypass/final water where applicable, grind, RPM, and expected time.
- For tea: leaf mass, each steep's programmed water/temperature/pause, expected siphon output, and whether the recipe is official or adapted.
- Every pour: ml, temperature, pattern, vibration timing, pause, and flow.
- Validation result and file path.
- Cited public sources, source brewer, original device, match quality, and every adaptation when web enrichment was used. Keep citations outside executable YAML.
- Device state only if a BLE command was requested and actually run.
- For scale/grinder/water, report the requested values and confirmed completion/exit without publishing the machine identifier. Keep recipe target, cumulative machine output, and cup-scale increase separate; never describe any of them as water-supply inventory.
- One taste-based next adjustment.
Never say a brew started, completed, or was cancelled unless the corresponding command and machine
state support it. Distinguish loaded/armed from started/brewing.
References
- Read
references/recipe-design.mdfor bean logic, flash brew, C40 conversion, and dial-in. - Read
references/recipe-baselines.mdfor optional community statistical templates and clog/Geisha notes. - Read
references/web-enrichment.mdwhen researching bean metadata or public expert recipes. - Read
references/recipe-schema.mdfor the exact guarded file contract. - Read
references/device-safety.mdbefore BLE writes or remote start. - Read
references/standalone-tools.mdfor FreeSolo scale, grinder, and brewer commands. - Read
references/tea-brewing.mdfor tea requirements, official templates, schema, and workflow. - Read
references/catalog.mdfor authorized App/MMKV import, optional private sync, export, and A/B/C representation limits. - Read
references/deployment.mdfor Codex/Hermes installation, publication, and environment setup. - Read
references/apk-capability-matrix.mdbefore claiming app parity or adding decoded commands. - Read
references/hardware-validation.mdbefore planning or recording supervised machine tests. - Read
references/sources.mdwhen checking provenance or making hardware/protocol claims.
What ships with it: 52 files
976.7 KB alongside SKILL.md, 22 of them executable
agents/
- openai.yaml266 B
assets/
licenses/
references/
- apk-capability-matrix.md16.4 KB
- catalog.md14.9 KB
- deployment.md21.3 KB
- device-safety.md18.2 KB
- hardware-validation.md7.4 KB
- recipe-baselines.md6.5 KB
- recipe-design.md8.8 KB
- recipe-schema.md6.0 KB
- sources.md10.2 KB
- standalone-tools.md13.8 KB
- tea-brewing.md7.5 KB
- web-enrichment.md5.8 KB
scripts/
- bootstrap.pyruns28.5 KB
- xbloom.pyruns108.4 KB
tests/
- test_a10_transport_integration.pyruns28.0 KB
- test_bridge_client.pyruns22.8 KB
- test_bridge.pyruns189.4 KB
- test_catalog.pyruns47.3 KB
- test_client.pyruns31.7 KB
- test_cli_guard.pyruns15.6 KB
- test_daemon_lifecycle.pyruns50.1 KB
- test_freesolo_protocol.pyruns8.8 KB
- test_history.pyruns13.2 KB
- LICENSE1.1 KB
- pytest.ini48 B
- requirements-dev.txt34 B
- requirements-runtime.lock28.9 KB
- requirements.txt540 B
- .skillignore210 B
12 more files not listed here. See all 52 in the repository.