From 4d85f0289c0b600958af3dad002ab14d9bcb1f4d Mon Sep 17 00:00:00 2001 From: Mike Rapadas Date: Sat, 13 Jun 2026 03:24:35 -0400 Subject: [PATCH 1/2] docs: scheduled jobs are hex workers, not LaunchAgents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AGENTS.md 'Automation' section instructed agents to write a launchd plist for new scheduled jobs — stale guidance that contradicts the no-LaunchAgents decision (persistent-processes-via-iii-exec-not-launchagents-2026-06-11) and caused a launchd reminder to be (wrongly) created for the reading-digest system. Rewrite the guidance to match the code: - Scheduled/recurring jobs = typed-Rust hex workers in the foundation registry (hex_modules::module_registry(), Worker::new().on_cron_named); authoring one is a foundation change, not a config edit. - Persistent processes ride the engine via iii-exec in the instance .hex/iii/engine-workers.yaml (which hosts daemons/factories, NOT cron jobs). - New per-job LaunchAgents are forbidden; the one sanctioned LaunchAgent is the harness bootstrap (com.hex.harness) that hosts the engine. Also: capability-table row, a hex-ops.md note clarifying its LaunchAgents section is the sanctioned-services exception (not a pattern to copy), and a code-intel.md forward-note (new daemons → iii-exec). Existing prohibitions (no runtime cron primitives / polling / sleep loops) preserved. Co-Authored-By: Claude Opus 4.8 (1M context) --- AGENTS.md | 6 ++++-- docs/code-intel.md | 9 +++++---- docs/hex-ops.md | 7 +++++++ templates/AGENTS.md | 6 ++++-- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3f35f2bd..b7cc90ee 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 | @@ -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. --- diff --git a/docs/code-intel.md b/docs/code-intel.md index 3bf432f2..dabeee5e 100644 --- a/docs/code-intel.md +++ b/docs/code-intel.md @@ -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 diff --git a/docs/hex-ops.md b/docs/hex-ops.md index 9c213f99..9cd0f0d4 100644 --- a/docs/hex-ops.md +++ b/docs/hex-ops.md @@ -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/`** domain, with **`SessionCreate=true`** and **no `UserName`**. Examples: `com.hex.harness` (the core diff --git a/templates/AGENTS.md b/templates/AGENTS.md index e16200e9..f5b46835 100644 --- a/templates/AGENTS.md +++ b/templates/AGENTS.md @@ -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 | @@ -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. --- From 19b3cea7aaec8d17b2953a9cb0b2207d8586284d Mon Sep 17 00:00:00 2001 From: Mike Rapadas Date: Sat, 13 Jun 2026 03:31:33 -0400 Subject: [PATCH 2/2] =?UTF-8?q?docs(iii-hex):=20correct=20stale=20worker?= =?UTF-8?q?=20CLI=20=E2=80=94=20hex=20module,=20not=20hex=20worker=20run?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The worker section documented a 'hex worker run ' CLI and a {worker_name, jobs:[{command, cron}]} YAML host. Neither exists: there is no 'hex worker' subcommand (verified vs the binary and develop main.rs), no iii_worker.rs, and no worker_name parsing anywhere in the harness. The doc even hedged 'verify once shipped' — it was written ahead of code and never reconciled. Correct it to the real, code-verified model: workers are typed Rust (Worker::new ().on_cron_named/.on_event) registered in hex_modules::module_registry() and run by 'hex harness serve'; lifecycle is 'hex module list|status|enable|disable'. This is the doc the AGENTS.md Automation rule points to, so the citation is now accurate instead of pointing at a nonexistent CLI. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/iii-hex.md | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/docs/iii-hex.md b/docs/iii-hex.md index fc8735b1..4c1913ff 100644 --- a/docs/iii-hex.md +++ b/docs/iii-hex.md @@ -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`. --- @@ -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 ` | +| **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:** @@ -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 | enable | disable `** — 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 [--data ] [--producer ]`** — 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 `. ---