DEV Community

Daniel
Daniel

Posted on Fully Autonomous

Your Agent Boots. Phoenix Still Doesn’t Know It Exists.

A MatrixOS agent can start, emit a beacon, and do useful work—while Phoenix’s swarm workspace still has no idea it exists. That is not a mysterious boot failure. It is usually a missing contract between two projects.

The agent source defines what the process does. Phoenix’s per-agent metadata file tells the workspace how to find, configure, constrain, and present that agent. Leave out either half and you get the software equivalent of an operator arriving at the gate with a working radio and no badge. The radio is excellent. The gate remains unimpressed.

This walkthrough follows the real layout: an agent package in MatrixOS, a JSON manifest in Phoenix, and optional configuration editors and custom panels. We’ll also look at how Phoenix treats signing and certificate requirements, so nobody puts a private key in a JSON file and calls it “automation.”

Two projects, two jobs

Keep the responsibilities separate:

MatrixOS repository
└── agents/python_core/shift_scribe/
    ├── __init__.py
    └── shift_scribe.py              # runtime behavior

Phoenix repository
├── agents_meta/shift_scribe.json    # workspace contract and defaults
└── matrix_gui/
    ├── swarm_workspace/cls_lib/agent/config_editors/
    │   └── shift_scribe.py          # optional configuration dialog
    └── core/panel/custom_panels/shift_scribe/
        └── status_panel.py          # optional custom controls
Enter fullscreen mode Exit fullscreen mode

The metadata file belongs to the agent type, not to each swarm instance. Phoenix reads the JSON files in agents_meta to populate the agent palette. When you add an agent to a workspace, its defaults become the starting configuration for that node; the workspace then saves the operator’s edits.

1. Start with the MatrixOS source skeleton

Create a package whose folder and Python module share the agent’s snake_case name. The environment path setup must run before imports from the MatrixOS tree. Guard each environment value: os.getenv() can return None, and sys.path.insert(0, None) is not a fun way to begin a mission.

Here is a small scaffold based on the supplied template. It includes the mixin import that the original snippet needs, and uses consistent spaces:

import os
import sys

for env_name in ("SITE_ROOT", "AGENT_PATH"):
    env_path = os.getenv(env_name)
    if env_path and env_path not in sys.path:
        sys.path.insert(0, env_path)

from core.python_core.boot_agent import BootAgent
from core.python_core.mixin.reflex_alert import ReflexAlertMixin
from core.python_core.utils.swarm_sleep import interruptible_sleep
from core.python_core.class_lib.packet_delivery.utility.encryption.utility import (
    identity,
)
IdentityObject = identity.IdentityObject


class Agent(BootAgent, ReflexAlertMixin):
    def __init__(self):
        super().__init__()
        self.AGENT_VERSION = "1.0.0"

        # BootAgent loads the directive in super().
        cfg = self.tree_node.get("config", {})
        self.report_role = cfg.get(
            "report_to_role", "hive.forensics.data_feed"
        )
        self.alert_role = cfg.get("alert_to_role", "hive.rpc")
        self.interval = int(cfg.get("interval", 30))

        # Monitor the worker thread.
        self._emit_beacon = self.check_for_thread_poke(
            "worker", timeout=60, emit_to_file_interval=10
        )

    def worker_pre(self):
        self.log("Shift Scribe on station.")

    def post_boot(self):
        self.log("Shift Scribe booted. Ready.")

    def worker(
        self,
        config: dict = None,
        identity: IdentityObject = None,
    ): 
        cfg = config or self.tree_node.get("config", {})
        self._emit_beacon()
        self.log("Worker cycle complete.")
        interval = int(cfg.get("interval", self.interval))
        interruptible_sleep(self, interval)


if __name__ == "__main__":
    agent = Agent()
    agent.boot()
Enter fullscreen mode Exit fullscreen mode

The bootstrap and imports are the load-bearing part of the source contract. Keep lifecycle methods and the worker(config, identity) signature in the shape expected by the MatrixOS runtime. BootAgent provides the loaded tree_node; read this agent’s settings from its config, then supply sensible fallbacks. Use interruptible_sleep for a recurring worker so the runtime can stop it cleanly. Emit a beacon for the actual worker thread you want monitored.

The sample you supplied also imports threading, datetime, and OrderedDict. Add those only when the implementation uses them. Python will not award extra professionalism points for unused imports. If a class inherits from ReflexAlertMixin, import it; otherwise the class definition will fail before the first log line gets its moment in the sun.

The example worker is intentionally boring in the productive way: it reads configuration, reports liveness, does one cycle, and sleeps. Replace the log line with the agent’s job. Add dedicated threads only when the work needs them, and give each monitored thread its own beacon.

2. Add the Phoenix meta-file

Create agents_meta/shift_scribe.json. The filename stem and the name should match exactly; Phoenix uses that name to find the manifest and the runtime package. Here is a starter manifest:

{
  "name": "shift_scribe",
  "universal_id": "shift-scribe",
  "constraints": [
    {
      "packet_signing": {
        "in": true,
        "out": true
      }
    }
  ],
  "config": {
    "ui": {
      "agent_tree": {
        "emoji": "🧭"
      }
    },
    "interval": 30,
    "report_to_role": "hive.forensics.data_feed",
    "alert_to_role": "hive.rpc"
  }
}
Enter fullscreen mode Exit fullscreen mode

The top-level fields have distinct jobs:

  • name is the stable agent-type name used by Phoenix’s palette and module lookup. Keep it aligned with the MatrixOS folder and module.
  • universal_id is useful metadata, but a workspace node gets its own runtime identity when it is added. Don’t hard-code one live swarm instance ID into a reusable agent type.
  • constraints declares requirements the workspace must resolve. Phoenix checks that a constraint has a registered editor or auto-generation handler; an unknown name is not a wish Phoenix can grant.
  • config holds defaults and Phoenix UI hints. The workspace copies these into a new node, and the agent reads the operational values from its directive.

The little compass under config.ui.agent_tree.emoji is the return icon: it labels the agent in the palette/tree. Pick an emoji that survives your terminal font and looks different from the other thirty-seven shields.

3. Let constraints provision security material

Phoenix’s constraints are more than badges. They tell the workspace and deployment path what resources an agent needs. For example, the real matrix_https metadata declares packet signing in both directions, a connection certificate, and HTTPS transport:

"constraints": [
  { "packet_signing": { "in": true, "out": true } },
  { "connection_cert": {} },
  { "https": {} }
]
Enter fullscreen mode Exit fullscreen mode

If the agent needs to phone home to Phoenix, give it a signing key pair. In this metadata, packet_signing in and out are both enabled: the agent needs its private signing key for outbound packets and the appropriate public key to verify inbound traffic. Phoenix’s registered packet_signing auto-generation handler creates the key material during deployment and injects only the fields required by the directive. The connection-certificate handler can likewise generate certificate bundles. For connection certificates, the transport matters: the current injection map uses them for HTTPS and WSS connections.

That is the point of declaring the requirement in the type metadata: Phoenix can ask for or generate the matching resource and block deployment when a required item is unresolved. It does not mean you should paste a private key or certificate into the public meta-file. Keep secrets in Phoenix’s protected vault/provisioning path, and let the deployment flow inject only what the agent needs.

Use a constraint name the Phoenix version actually registers. If you invent a new one, you must also implement and register the editor or auto-generation handler. Otherwise the workspace can reject the agent before deployment—with the digital equivalent of “your paperwork is in a language we do not speak.”

4. Defaults, configuration editors, and panels

Put ordinary operational defaults under config: polling intervals, paths, role names, safe-mode flags, or feature toggles. The MatrixOS agent should still validate values and apply defensive fallbacks; a default is not input validation wearing a nicer jacket.

Phoenix has a useful fallback for configuration. When an operator double-clicks an agent node, it looks for a module named after the agent under config_editors and a class named in CamelCase. For shift_scribe, that means:

matrix_gui/swarm_workspace/cls_lib/agent/config_editors/shift_scribe.py
class ShiftScribe(...)
Enter fullscreen mode Exit fullscreen mode

If no specialized editor exists, Phoenix uses its generic editor for simple fields. A custom editor is worthwhile when the config needs typed controls, validation, grouped sections, or role pickers. Its save action should update the node’s config and mark the workspace dirty.

A custom operations panel is a separate, optional module. Add its path to config.ui.panel, for example:

"ui": {
  "agent_tree": { "emoji": "🧭" },
  "panel": ["shift_scribe.status_panel"]
}
Enter fullscreen mode Exit fullscreen mode

Then create matrix_gui/core/panel/custom_panels/shift_scribe/status_panel.py. Phoenix resolves that dotted path beneath custom_panels, imports the module, and looks for a class named StatusPanel. Custom panels implement the Phoenix panel interface and can contribute buttons or views for agent-specific operations. If you declare a panel path, ship the matching module and class; otherwise Phoenix will politely log that the panel is missing and continue without it.

5. Service roles are the command contract

A service-manager entry advertises service roles and handlers that can be routed to the agent. It is not the custom panel itself. A simplified entry might look like:

"service-manager": [
  {
    "role": ["shift_scribe.status@cmd_status"],
    "scope": ["parent", "any"]
  }
]
Enter fullscreen mode Exit fullscreen mode

The agent must implement the matching handler, and any custom panel button that calls it must send a compatible service request. Think of the role string as the radio frequency: listing it in Phoenix does not install a radio in the agent. Keep the declared role, handler name, payload shape, and panel request aligned.

The existing matrix_https manifest shows this pattern with perimeter toggle and status roles. Its config.ui.panel points to matrix_https.matrix_https; that path leads to a real custom panel, while service-manager lists the services it can request. One is the cockpit; the other is the set of commands it can call. They work together, but they are not interchangeable.

6. Put the two halves together and verify

Before adding the new type to a real swarm, check the small integration contract:

  1. In MatrixOS, confirm the folder, init.py, module name, Agent class, imports, and lifecycle methods follow the existing agents under agents/python_core.
  2. In Phoenix, confirm agents_meta/shift_scribe.json parses and its filename, name, and MatrixOS package agree.
  3. Add the type in the Swarm Workspace palette. Confirm the icon appears, drag it into a test workspace, and inspect the constraints Phoenix loaded.
  4. Open its configuration. Check defaults, save an edit, and reopen it. If you declared a custom editor or panel, verify its module and CamelCase class load.
  5. Resolve the required constraints and deploy to a test environment. Verify the directive contains the intended config and provisioned fields, then confirm the worker logs, beacons, and service handlers behave as expected.

If the agent is missing from the palette, begin with the JSON path and filename. If it appears but cannot be added, inspect the constraint registry. If the worker boots but behaves as though every setting is None, compare the editor’s saved config keys with the names read by the agent. If the panel button appears to salute and then do nothing, compare its service request with the role and handler that the agent actually registered.

That is the whole handoff: MatrixOS owns behavior; Phoenix metadata describes how to assemble and operate the type; optional Phoenix modules provide richer editing and controls. The meta-file is not decorative paperwork. It is the map that gets the agent from “Python class exists” to “operator can add it, configure it, provision it, and use it.”

Victory Always.

🌐 Links & Resources:

Top comments (0)