Skip to content

openhome-cli deploy creates abilities that the cloud router silently ignores (template:null) #14

@realdecimalist

Description

@realdecimalist

Summary

Abilities deployed via openhome-cli deploy land on the server with template: null, are NOT registered in the cloud capability router, and therefore never fire on voice or openhome-cli trigger. The same code uploaded via the portal (which forks from a template → template: <n>) does route correctly.

This blocks every CLI-deployed user ability — skills, brain skills, background daemons — from actually running in voice sessions. End-to-end verified 2026-04-24 with a minimal repro (below) + a 4-hour diagnostic session that produced a documented workaround.


Reproduction (5 min, zero edits to code)

Setup

# A minimal skill that speaks a unique, unguessable phrase when triggered
mkdir -p /tmp/trigger-test && cd /tmp/trigger-test
cat > __init__.py <<'PY'
PY
cat > main.py <<'PY'
from src.agent.capability import MatchingCapability
from src.agent.capability_worker import CapabilityWorker
from src.main import AgentWorker

class TriggerTestCapability(MatchingCapability):
    worker: AgentWorker = None
    capability_worker: CapabilityWorker = None
    # {{register_capability}}
    async def _run(self):
        await self.capability_worker.speak("Banana hovercraft confirmed.")
        self.capability_worker.resume_normal_flow()
    def call(self, worker: AgentWorker):
        self.worker = worker
        self.capability_worker = CapabilityWorker(self.worker)
        self.worker.session_tasks.create(self._run())
PY
cat > config.json <<'JSON'
{"unique_name":"trigger_test","matching_hotwords":["banana hovercraft"]}
JSON
echo "# Trigger Test" > README.md
cd .. && zip -rq trigger-test.zip trigger-test

Deploy via CLI + attempt to trigger

openhome-cli deploy trigger-test.zip --name "TriggerTest" --category skill \
    --triggers "banana hovercraft" --personality <AGENT_ID>
openhome-cli trigger --agent <AGENT_ID> "banana hovercraft"

Observed

  • Deploy returns {"ok":true,"ability_id":<id>,...} — success
  • GET /api/capabilities/get-installed-capabilities/ shows the ability enabled:true
  • PUT /api/personalities/edit-personality/ returns success after --personality attaches it
  • openhome-cli trigger's WebSocket session opens, transcribed phrase "banana hovercraft" arrives
  • Router does NOT fire the ability: no Capability Worker initialized by: triggertest, no chat_details:{"name":"triggertest"} event
  • Base LLM answers generically: "That sounds quite the whimsical image!"
  • Ability never speaks its expected "Banana hovercraft confirmed."

Expected

  • Router matches the trigger phrase, fires the ability, logs Capability Worker initialized by: triggertest, user hears "Banana hovercraft confirmed."

Control (same account, same agent, same day)

Create an ability in the portal from any template — sets template: <n> on the record — it routes correctly on the exact same trigger flow. I verified this by portal-creating test email (basic-advisor template, trigger "send email") and OrynqBackgroundDaemon (basic template, no trigger). Both fired.

Proof the ONLY difference is template

GET /api/capabilities/get-capability/<id>/:

ability deploy path template routes on voice
TriggerTest openhome-cli deploy null
test email portal 92
OrynqBackgroundDaemon portal 3 ✅ (BD auto-starts, no hotword)

Deep-dived every other field (is_installed, is_user_enabled, trigger_words, categories, name, unique_name, version ids, capability_versions shape). template is the only meaningful differentiator. Setting template=92 on a CLI-uploaded ability via PUT /api/capabilities/edit-capability/<id>/ makes triggers dispatch — but the dispatch runs the TEMPLATE's code, not the uploaded zip, confirming the router indexes by template id.


Documented workaround (end-to-end verified)

Until the CLI path registers with the router:

  1. Portal → create ability from a template matching the intended category.
  2. Resolve release_id of the user-enabled version:
    GET /api/capabilities/get/installed-capability/by-capability/{capability_id}/
    # response.release_id is v1's id
    
  3. Upload code to v1 via:
    POST /api/capabilities/validate/release-code/{release_id}/
    Content-Type: multipart/form-data
    Fields:
      zip_file = FLAT zip (files at root, NO top-level directory)
      committed = false
      commit_message = <any>
    
    Server returns {"detail":"Release files validated successfully and saved"}. The user's zip becomes the active v1 — actual uploaded code runs (not the template's code).
  4. Assign via PUT /api/personalities/edit-personality/ with matching_capabilities=<CSV> in a single form field.

Verified with a non-trivial ability (900-line background.py, multi-file, real HTTP calls): session start → BD auto-load → heartbeat file written → conversation captured → Materios gateway upload → on-chain receipt landed + certified within ~4s. The full pipeline runs when code is uploaded via this portal-fork + validate/release-code path.

Receipt proof of the working path: content 0xf7790eb5a5fb89be4f279bdf7adf083c165072c2d18f15da70bd26bb86cbe0eb, receipt 0x50dfa9dea3f9144bedea0759ce2a1d3b56e56635cce30f702fa1e3cdfa79e603, cert 0x5c34048cd8141524843eb4b21394f438a0de286f929e8ed7d33e0b6d63a78e06, block ~21:42:06 UTC on Materios preprod.


Secondary bug in this same flow

openhome-cli assign --capabilities X,Y,Z sends multiple matching_capabilities multipart fields. The server drops all but the last value. Correct encoding: single form field with CSV: matching_capabilities=X,Y,Z via --data-urlencode to PUT /api/personalities/edit-personality/. The CLI path effectively wipes the agent's capability list to only the last ID on every assign.


Suggested fix direction

The CLI's POST /api/capabilities/add-capability/ path needs to either:

  • Set template to a default / synthetic value that the router accepts, OR
  • Make the router index by capability_id directly (not by template), OR
  • Expose a --template <id> flag on deploy so the CLI call can mimic the portal-fork behaviour

Happy to share the full WS trace (transcribed → no capability event → base LLM response) and the server responses I collected during the investigation if helpful. Filed separately from any ability-specific issue because this affects every user ability deployed through the CLI for the entire account.

Environment: account 90403 (decimalist), agent 578906 (Penny), openhome-cli 0.1.40, 2026-04-24.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions