WORKING NOTE — href.on() (observe) and ABM lifecycle hot-swap (Incident Cataloging)

**Note** from Bead: Incident Cataloging · [canonical source](https://redfish.acequia.io/guerin/.agents/c38c1239-bfd3-44dd-8d97-1a0aa39ac8da/2026-06-10/notes/href-on-and-abm-hotswap.md) · session 2026-06-10 · discussion: Talk: Incident Cataloging

> Stephen, 2026-06-10. Two pieces: (1) a developer-facing `href.on()` reactive primitive over the service-worker cache (the dual of `fetch`); (2) its killer worked example — hot-swapping an AgentScript ABM's `step` live from a URI, keeping world state.

## 1. `href.on()` = observe = the dual of fetch `GET`/`fetch` reads a snapshot; **`href.on('change', cb)` subscribes to its change.** This is the same primitive under three other names in this workspace: - the cataloging dual ([catalog-cataloging-duality](catalog-cataloging-duality.md)): `select ↦ connect`, read-edge `#` ↦ write-edge `@`. `href.on()` is fetch's dual. - the [#devops](https://redfish.acequia.io/guerin/.agents/64be6d29-d133-4ade-9dce-f62701003e37/about.md) Phase-2 "active field": *the SW owns the Cache API, revalidates on a signal, and `postMessage`s the watching agent — it "listens for cache changes."* - the WebSocket observe channel in [#sovereign-webdav/uploads-server.mjs](https://redfish.acequia.io/guerin/.agents/1ca761b0-cb21-4bd7-be94-12dcc5824851/2026-06-10/skills/uploads-server.mjs) — the **server-side** instance of the same contract. `href.on()` is the **in-browser, SW-cache** instance. They should share one protocol. **The win** (vs Firebase `.on()`): same realtime-reactive ergonomics, but over **any URI in an open WebDAV namespace** rather than a proprietary tree — self-sovereign auth, served from the mesh, no vendor, **no drop to the OS**. The namespace is the realtime database; the SW is the reactive engine; and the *same* SW is the [#devops] policy monitor (one SW, two roles: gate writes + broadcast changes). Decisions to pin (carried from the discussion): - **What is a change** — ETag/Last-Modified delta (cheapest; a revalidation 200-vs-304 *is* the event), content-hash, or an explicit invalidation signal. - **Push vs poll** — local PUTs fire instantly (the SW sees its own write); remote/peer change needs a push transport (SSE / WS / WebRTC / the Acequia Event Bus), degrading to conditional-GET polling. - **Scope** — single URI vs **subtree/prefix** (`dir/.on()` = path-as-group; `uploads-server` does this with recursive `fs.watch`). - **Event shape** — Firebase-style `value` / `child_added|removed|changed` (child = files appearing/changing under a prefix); `on()` returns an unsubscribe; fires cached-value-first then on change. - **Auth** — a subscription is a standing GET, gated by the same `acequia-access.json`/capability as any read.

## 2. The worked example: ABM lifecycle as URI-served, hot-swappable scripts NetLogo's lifecycle, and a sharp distinction worth preserving: - **`startup` — a real RESERVED primitive.** The engine invokes it automatically at model load. Engine-level. - **`setup` and `go`/`step` — SOCIAL NORMS.** They are modeler-defined procedures, conventionally named, wired to buttons. The engine doesn't reserve them; the *culture* does. Mapped onto **AgentScript-over-URIs**, each lifecycle hook is a **script at the model path** (`<model>/startup`, `<model>/setup`, `<model>/step`). The platform should **reserve only a small set of lifecycle verbs** (`startup`, maybe a teardown/apoptosis hook) and **discover the rest by path/name as convention** — which is exactly the agent-as-file ducktyping (project_agent-as-file-ducktyping): a few verbs reified, the rest negotiated. Reserved = engine contract; social = convention the harness binds. **Hot-swap (the payoff).** Because `step` is invoked every tick through a *mutable reference* (`model.step`), you can replace that reference while the model runs: ```js href.on(stepUri, 'change', async () => { const src = await (await fetch(stepUri)).text(); model.step = new Function('world', 'ticks', src); // capital-F Function: compile source → live fn }); ``` The running simulation **keeps its world, agents, and tick count**; only the rule swaps in on the next tick. That is live-coding an ABM — change the rules mid-run and watch the dynamics shift — which NetLogo can't easily do (it recompiles and restarts). The reactive SW cache makes it automatic: save `step.js` → cache entry changes → listener recompiles + rebinds. **Per-hook swap semantics differ** (the `href.on` callback decides what to do based on *which* script changed): - **`step`/`go`** — rebind `model.step` live, every tick. State preserved. The hot one. - **`setup`** — recompile, but it only takes effect on the next reset (it rebuilds state); offer "re-run setup with new code." - **`startup`** — load-time/reserved; re-running mid-session is odd. Swap takes effect on reload. **`Function` vs dynamic `import`.** Capital-F `new Function(...)` is lean and instant for a pure-logic `step` over the world API (no module graph, no closure over module scope). Cache-busted dynamic `import(stepUri + '?v=' + hash)` gives a real ES module (imports, helpers) at the cost of a new module instance each swap. Use `Function` for the tight step body; dynamic import when the hook needs its own imports.

## Why this matters (synthesis) A model becomes a **composed namespace of lifecycle scripts** at URIs, with the runtime binding them and `href.on()` re-binding on change — the same model-as-namespace / re-bindable-binding pattern as project_uri-bind-mount and bead-as-agent. Hot-swap = the active field ([observe]) applied to *behavior*, with state preserved. The reserved/social split keeps the engine contract minimal and the rest cultural, so the IDE stays open and convention-extensible. And it all stays in the browser: edit a hook at its URI, the running model swaps it live.

## Provenance Stephen 2026-06-10 (the AgentScript web-IDE + `href.on()` + ABM hot-swap thread). Extends [catalog-cataloging-duality](catalog-cataloging-duality.md), the [#devops] Phase-2 active field, and [#sovereign-webdav]'s `uploads-server.mjs` observe channel.

## References (bead cross-links) - Bead: Devops · [canonical](https://redfish.acequia.io/guerin/.agents/64be6d29-d133-4ade-9dce-f62701003e37/) - Bead: Sovereign Webdav · [canonical](https://redfish.acequia.io/guerin/.agents/1ca761b0-cb21-4bd7-be94-12dcc5824851/)