Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ The behavioral contract is identical across agent runtimes — only the tool mod
|---|---|---|
| Skills / slash commands | Invoke the skill directly | Browse `.hex/skills/*/SKILL.md` and follow its instructions |
| Hooks (pre/post tool) | Use the runtime's hook config | Apply the behavior manually |
| Scheduling / automation | — | OS-level scheduling (launchd/cron) per job |
| Scheduling / automation | — | hex workers (foundation-registry cron/trigger workers); never new LaunchAgents |
| Sandbox model | Whatever the runtime enforces | Per-session isolation |
| Web access | Use the native fetch/search tool | `curl` + public APIs, or note the limitation |

Expand Down Expand Up @@ -392,7 +392,9 @@ Enforcement checkpoints with "teeth" — they activate automatically, not on req

## Automation

Recurring and scheduled work is handled by OS-level scheduling (launchd on macOS, cron on Linux) configured per job. There is no general event bus or policy engine in hex. For a new scheduled job, write a launchd plist or crontab entry targeting the specific script and wire it manually. Do not use polling loops or `sleep` loops.
Recurring and scheduled work runs as **hex workers** — never as new LaunchAgents. A hex worker is a typed Rust worker (`Worker::new("name").on_cron_named(...)`) registered in the foundation worker registry (`hex_modules::module_registry()`) and run in-process by the harness engine; cron triggers carry a 7-field cron expression, reactive triggers bind a `state`/`queue` event. Authoring a new scheduled job is therefore a **foundation change** (it ships to every instance), not a config edit. **Persistent local processes** (long-running daemons) ride the engine via an `iii-exec` entry in the instance `.hex/iii/engine-workers.yaml` (additive — survives `/hex-upgrade`); that file hosts engine worker factories and supervised processes, **not** new cron schedules. See `docs/iii-hex.md` ("Instance engine workers").

**Do not create new LaunchAgents.** The single sanctioned LaunchAgent is the harness bootstrap itself (`com.hex.harness`; see `docs/hex-ops.md`) — it hosts the engine that runs every worker. New per-job plists or crontab entries are forbidden (decision: `persistent-processes-via-iii-exec-not-launchagents-2026-06-11`). There is no general event bus or policy engine built into hex. Do not use runtime-built-in cron/schedule primitives, polling loops, or `sleep` loops.

---

Expand Down
9 changes: 5 additions & 4 deletions docs/code-intel.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,11 @@ No per-workspace plist installs: register workspaces with `cq register
`hex harness restart` after an upgrade (rebuild = install). Verify with
`hex module status hex-codeintel-indexer`.

Categorical distinction: scheduled **jobs** live in the harness as worker
modules, while long-running **daemons** like scipd stay on launchd
(`system/templates/launchd/com.hex.scipd.plist` and its install steps above
are unchanged).
Categorical distinction: scheduled **jobs** are hex workers (typed Rust, foundation
registry), not launchd. scipd is an **existing** long-running daemon documented here as-is; its launchd
plist (`system/templates/launchd/com.hex.scipd.plist`) and the install steps above are
unchanged. **New** long-running daemons should ride the engine via **iii-exec** (see the
AGENTS.md "Automation" rule), not a new plist.

## Known limitation: calls inside `macro_rules!` bodies

Expand Down
7 changes: 7 additions & 0 deletions docs/hex-ops.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ LaunchAgents, and **telemetry**.

## LaunchAgents (launchd)

> **Note:** This section documents hex's **sanctioned** supervised services (the harness
> bootstrap and the personal BOI daemon). It is **not** a pattern to copy for new scheduled
> jobs — new recurring/scheduled work is a **hex worker**, and new persistent processes ride
> the engine via **iii-exec** (see the AGENTS.md "Automation" rule and `docs/iii-hex.md`). Do
> not add new per-job LaunchAgents (decision:
> `persistent-processes-via-iii-exec-not-launchagents-2026-06-11`).

hex's supervised long-running services run as **per-user gui LaunchAgents** in
`~/Library/LaunchAgents/`, bootstrapped into the **`gui/<uid>`** domain, with
**`SessionCreate=true`** and **no `UserName`**. Examples: `com.hex.harness` (the core
Expand Down
48 changes: 27 additions & 21 deletions docs/iii-hex.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ skills — `iii-getting-started`, `iii-functions-and-triggers`, `iii-trigger-sch
`iii-state-reactions`, `iii-queue-processing`, `iii-state-management`. This doc only
covers the hex layer on top.

> Status (2026-06-04): the `hex worker` / `hex triggers` surfaces + the `ops` seam are
> being built (spec `Skt0r3dbg`). The **concepts and conventions** below are stable;
> verify exact CLI flags against `hex worker --help` / `hex triggers --help` once shipped.
> Status: the **concepts and conventions** below are stable. The CLI shipped as
> `hex module …` (worker lifecycle) + `hex triggers …` (event production) — NOT the
> `hex worker run` form an earlier draft assumed (that YAML worker host was never built).
> Verify exact flags against `hex module --help` / `hex triggers --help`.

---

Expand All @@ -21,7 +22,7 @@ iii is three nouns and a set of channels.
|---|---|---|
| **Function** | a unit of work — "do this" | `hex::landings::reconcile` |
| **Trigger** | a *subscription*: "fire function F when X happens." Registered **once**, when the consumer sets up. Has a **type**. | a `state` trigger watching scope `boi` |
| **Worker** | a process that hosts functions + triggers | `hex worker run <config>` |
| **Worker** | a typed-Rust unit binding a function to a trigger, run in-process by the harness engine | `Worker::new("hex-backup").on_cron_named(...)`; inspect via `hex module list` |

A **trigger's type is the channel it listens on:**

Expand Down Expand Up @@ -53,30 +54,35 @@ We do **not** scatter `iii_sdk::` calls across hex. One seam owns iii.
- **`system/harness/src/ops.rs`** — the *only* place (besides the worker host) that calls
`iii_sdk::`. Exposes hex-native `emit(...)`, state read/write, connect. If iii's API
changes or we swap substrate, only this file changes.
- **`hex worker run | list | status`** — worker lifecycle, hex-native (was `hex iii worker
run`; the `iii` is gone from the surface). A worker config is declarative YAML:
`{ worker_name, jobs: [{ id, command, cron | trigger }] }`, hosted by `hex worker run`.
The hex binary **is** the worker host — no node, no per-worker binary.
- **Workers are typed Rust, not YAML.** Each is a `Worker` (`system/harness/src/worker/mod.rs`)
binding a function to a trigger — e.g. `Worker::new("hex-backup").on_cron_named("daily",
CRON_DAILY, run_backup)` (`system/harness/src/modules/backup.worker.rs`). They are collected
in `hex_modules::module_registry()` (surfaced by `hex::workers::registry()`) and run
in-process by the harness engine (`hex harness serve`) — no node, no per-worker binary.
- **`hex module list | status <name> | enable <name> | disable <name>`** — worker (module)
lifecycle. There is **no `hex worker run`**; an earlier draft of this doc described a
`{ worker_name, jobs }` YAML host that was never built.
- **`hex triggers emit <event> [--data <json>] [--producer <name>]`** — the producer.
One `iii.trigger(...)` call under the hood; writes the event onto a channel so reactive
workers fire. Shell/hook callers use the CLI; Rust callers use `ops::emit(...)` directly
— same code path, the CLI is just `clap` + the lib.

### Trigger config in a worker YAML

```yaml
worker_name: hex-landings
jobs:
- id: hex::memory::index
command: [hex, memory, index]
cron: "0 */15 * * * * *" # bare cron = a cron trigger
- id: hex::landings::reconcile
command: [hex, landings, reconcile]
trigger:
state: { scope: "boi" } # state | queue (cron also valid here)
### Authoring a worker (real model)

A worker is typed Rust — bind a function to a trigger via the `Worker` builder, then register
it so `hex harness serve` runs it:

```rust
// system/harness/src/modules/backup.worker.rs (illustrative)
Worker::new("hex-backup")
.on_cron_named("daily", CRON_DAILY, run_backup) // cron trigger (7-field expr)
// .on_event(...) binds a state/queue trigger instead
```

The fired command receives the trigger event as the **`III_EVENT`** env var (JSON).
Add it to `hex_modules::module_registry()` so the engine schedules it. The bound function
receives the trigger event as a typed argument (not an env var). Authoring a new worker is a
**foundation change** (it compiles into the harness), not a config edit; inspect/pause running
workers with `hex module list` / `hex module disable <name>`.

---

Expand Down
6 changes: 4 additions & 2 deletions templates/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The behavioral contract is identical across agent runtimes — only the tool mod
|---|---|---|
| Skills / slash commands | Invoke the skill directly (e.g. `/hex-startup`) | Browse `.hex/skills/*/SKILL.md` and follow its instructions |
| Hooks (pre/post tool, settings.json) | Use the runtime's hook config | Apply the behavior manually each turn |
| Scheduling / automation | Use what the runtime offers; else OS-level scheduling (launchd/cron) per job | OS-level scheduling per job |
| Scheduling / automation | hex workers — typed Rust cron/trigger workers in the foundation registry (never new LaunchAgents) | hex workers; persistent procs via iii-exec |
| Sandbox model | Whatever the runtime enforces | Per-session isolation |
| Web access | Use the native fetch/search tool | `curl` + public APIs, or note the limitation |

Expand Down Expand Up @@ -391,7 +391,9 @@ Every status change gets a timestamped changelog entry at the bottom.

## Automation

Recurring and scheduled work is handled by OS-level scheduling (launchd on macOS, cron on Linux) configured per job. There is no general event bus or policy engine built into hex. For a new scheduled job, write a launchd plist or crontab entry targeting the specific script, then wire it manually. Do not use runtime-built-in cron/schedule primitives or polling loops.
Recurring and scheduled work runs as **hex workers** — never as new LaunchAgents. A hex worker is a typed Rust worker (`Worker::new("name").on_cron_named(...)`) registered in the foundation worker registry (`hex_modules::module_registry()`) and run in-process by the harness engine; cron triggers carry a 7-field cron expression, reactive triggers bind a `state`/`queue` event. Authoring a new scheduled job is therefore a **foundation change** (it ships to every instance), not a config edit. **Persistent local processes** (long-running daemons) ride the engine via an `iii-exec` entry in the instance `.hex/iii/engine-workers.yaml` (additive — survives `/hex-upgrade`); that file hosts engine worker factories and supervised processes, **not** new cron schedules. See `docs/iii-hex.md` ("Instance engine workers").

**Do not create new LaunchAgents.** The single sanctioned LaunchAgent is the harness bootstrap itself (`com.hex.harness`; see `docs/hex-ops.md`) — it hosts the engine that runs every worker. New per-job plists or crontab entries are forbidden (decision: `persistent-processes-via-iii-exec-not-launchagents-2026-06-11`). There is no general event bus or policy engine built into hex. Do not use runtime-built-in cron/schedule primitives, polling loops, or `sleep` loops.

---

Expand Down
Loading