Operating systems for agents
Twenty-one structures at the layer above a single agent: what an agent lives inside, what it retrieves through, what it is assembled from, the control plane that drives one without owning it, the instrument that watches it write code, the one directory that carries five layers of it at once — and one that this page is currently running.
The Cursor State Machine
An agent that is always inside one state, routed there by a persisted “you are here” pin, where the states are skills. It does that state’s leg, advances the cursor, journals why, and loops. Two agents on the same codebase produce the same structure — that invariance is the point.
( BOOT ) the first thing every session does, and every
| resumption after a context compaction
v
( read { THE CURSOR } ) a persisted "you are here" pin
|
v
[ the state it names ] each state IS a skill:
| init mirror an unmirrored codebase
| seework the dispatcher: pick the next gap
| change a module changed, re-derive its doc
| prompts find or author a scored procedure
v
( do that state's leg )
|
v
( advance { THE CURSOR } + journal the WHY )
|
'--> loop back to boot, or exit
the agent is never "deciding what to do next". the flow is scripted,
so it cannot improvise a new kind of artifact halfway through.
- The cursor is the whole control structure. Boot reads it; the leg advances it; nothing else routes. A compaction, a crash or a new session all resume identically.
- Every output is exactly one legal kind. No ad-hoc session notes, no topic documents — which is what makes two independent runs converge on the same tree.
- The paired commit closes the loop: the derived artifact and the code it describes land in the same commit, so drift is not a thing that can quietly happen.
Runs in doc-mirror
Brains Whose Neurons Are Brains
Retrieval as a recursive structure rather than a flat index: a node is either a document or another whole brain, and both answer the same two-stage protocol — so the hierarchy nests to arbitrary depth without a second mechanism.
{ BRAIN } its digest
/ | \
/ | \
{ BRAIN } [ doc ] [ doc ] a NEURON is either a
/ \ document or another
[ doc ] [ doc ] entire brain
every node -- leaf or brain -- answers the SAME two stages:
cognize a cheap relevance vote, read off the node's digest
instruct the full pass: cognize -> instruct -> synthesise,
recursively, through whatever is below it
digests build BOTTOM-UP: the level touching raw files gets a real
fold; every level above concatenates its children's digests verbatim,
so distinctive vocabulary survives all the way to the top.
- One protocol at both scales is what buys the nesting: a brain is a legal neuron because it answers what a neuron answers.
- The cheap stage is a gate on the expensive one. Relevance is voted from a digest before anything descends, so depth costs only where the query actually goes.
- Verbatim concatenation upward is a deliberate choice against summarising twice — a fold of a fold is where the distinguishing words go missing.
Runs in brain-agent
The Seven Primitives, and What Each One Actually Buys
Everything you can build in a modern coding-agent harness is a combination of seven component types. The design question is never “what do I put in the prompt” — it is which primitive carries each requirement, because they differ in one property: how much control they actually have.
PRIMITIVE WHAT IT IS CONTROL LEVEL
----------- ----------------------------- -----------------
CLAUDE.md system-prompt injection a TRIGGER -- it hopes
skill a capability, hot-reloaded a SUGGESTION
hook an event handler CONTROL -- it enforces
MCP an external tool with state EXTERNAL effect
subagent an isolated context DELEGATION
plugin a bundle PACKAGING
team coordinated sessions COORDINATION
the third column is the whole table. the SAME rule written into the
instructions file is a hope; written as a hook it is a law.
the combinations are the systems:
skill -> a workflow you invoke
skill + scoped hook -> behaviour enforced WHILE it runs
skill + forked subagent -> research in an isolated context
subagent + skills + MCPs -> a domain specialist with a loadout
hook + MCP -> an external system driven by an event
team + SHARED STATE -> a market world (see the arena above)
plugin + any of the above -> the whole thing, distributable
- Read the ladder, then place the requirement. Anything that must never happen belongs at the enforcing end; anything that should usually happen can live at the suggesting end.
- The decision is mechanical: is it something you invoke, or something that should fire on its own? does it need enforcement? does it need isolation? is it multi-agent, and if so is the coupling peer messaging or shared state?
- Every architecture above is one of these combinations, which is why they port between harnesses instead of being tricks.
Runs in the lab
The Ratchet-Navigation Funnel
A funnel is a series of ratcheted options such that the visitor cannot work out how to do anything except a sanctioned move. Every page’s visible exits are its sanctioned moves, and the entrance is one-way. You are inside one now.
{ THE ENTRANCE } one way. nothing routes back to it.
|
v
[ a page ] ------> [ one level deeper ] every artifact links into
| the system that generated it, with a receipt at
| each layer -- the descent IS the argument
v
[ the hub ] <-- reachable from the brand mark, from anywhere
|
v
[ the offer ]
the exit set of each page is DESIGNED, not inherited from a template.
nav is a grammar: what you can see is what you are allowed to do, and
the visitor sorts themselves by how deep they choose to go.
- The one-way entrance is structural. After the first pass there is no navigation route back to it — the browser’s back button is the only way, which is exactly the friction a ratchet is.
- The exit set is the design surface. Deciding what a page links to is deciding what a reader can do next; leaving it to a template is leaving the funnel to chance.
- It grades its reader rather than filtering them. Engineers who pull threads land on running code, operators land on running businesses, and nobody is told anything untrue on the way.
Runs in this site
The Attached Terminal Mirror
A control plane that does not own the agent process. It attaches to an already-running terminal session by name, drives it by typing, reads it by capturing the screen, and separately lifts the agent’s configuration off the filesystem — then serves both as one interface. Kill the control plane and the agent keeps working; restart it and it re-attaches.
over HTTP: read the screen, type into it, read the state, attach
|
v
( THE CONTROL PLANE ) restartable. owns NOTHING but the binding.
|
| attach: does a session with this name exist?
| yes --> bind to it
| no --> stay unattached, and SAY SO
|
+-- ( the terminal driver ) four verbs: exists, type,
| | capture, type-and-wait
| v
+-- ( the config reader ) reads FILES, read-only by
| | contract: settings, servers,
| | hooks, skills, rules
v v
{ THE SESSION } the real interactive agent.
NOT owned. NOT spawned. NOT restarted.
two independent sensing paths -- a screen scrape and a config read --
compose into ONE state document.
every live route is fronted by the attach check: no session means
"not attached", never a silent respawn.
- The only write is keystrokes into the pane. The runtime never edits the agent’s transcript, which is what makes the mirror a mirror.
- Attaching is an explicit act. A control plane that spawns what it failed to find is a control plane that quietly forks your agent.
- Configuration is sensed, not held. What the agent is equipped with is read off disk each time, so the control plane cannot report a loadout the agent does not have.
Runs in cave
Hooks Lifted Out, and an Active Set
Every behavioural hook is moved out of the agent’s own configuration and into a registry inside the runtime; what stays in the agent is a set of thin relays that post the event. Dispatch is then filtered by a mutable set of active names, so the same loaded registry produces completely different behaviour depending on which names are switched on — and the runtime, not the agent, holds the authority to refuse.
the agent's config keeps ONLY relays
before-tool, after-tool, stop --post the event-->
|
( THE RUNTIME ) v
1. normalise the envelope several harnesses, one shape
2. hooks = registry(event type) INTERSECT { ACTIVE NAMES } <-- GATE
3. run each; a BLOCK verdict short-circuits and is returned
4. accumulate the extra context each one contributes
|
{ continue } or { block, with a reason }
v
back to the agent process
the registry holds two populations behind one call signature:
class hooks discovered from a directory
script hooks a subprocess. one specific exit code means BLOCK;
ANY OTHER failure means APPROVE -- deliberately
fail-open, so a broken hook cannot brick the editor
a hook in the registry but NOT in the active set is loaded and
SILENT. whoever writes the active set writes the behaviour.
- Every hook shares one persistent state dict across calls. That dict is the memory an autonomous loop is built on — see the next entry.
- Two-stage gating — registered and active — is what lets one installed library of hooks express many different agent modes without reinstalling anything.
- Block authority sits with the runtime and is returned to the agent, so the thing being constrained is not also the thing deciding.
Runs in cave
A Loop Is Data: a prompt, a hook set, and an exit predicate
An autonomous work loop over an interactive agent, written as a plain record instead of a driver program. Activating it does two things at once: it installs the loop’s hook set and it types the loop’s prompt. The loop then closes through the hooks it just installed — they write into shared state, and the exit condition is a predicate over exactly that state.
A LOOP = { name, prompt, its hook set, exit(state), what's next }
activate: 1. install THIS loop's hook set <-- arm
2. type THIS loop's prompt <-- kick
|
v
the agent works --> fires events --> the relays post --> dispatch
|
only this loop's hooks are active |
v
the hooks mutate the shared state
|
exit(state) ?
no --> keep running
yes --> disarm, then pick the next:
by index, by name, by
running a transition
cycle round, or stop
|
'--> activate
there is NO loop thread. the hook dispatch IS the tick.
a schedule of autonomous work is then a LIST of these records.
an exit predicate that raises is read as "not yet" -- a broken
predicate stalls the loop rather than skipping it.
- The hooks that decide when the loop is done are the hooks the loop installed. Arming and measuring are one act, which is what removes the driver program.
- Autonomy becomes editable data: reorder the list, change one prompt, swap one hook set. Nothing is recompiled and nothing is subclassed.
- Failure directions are chosen: a missing named successor just advances the index, while a failed transition stops the whole schedule.
Runs in cave
Conversations, Not Transports
An agent’s input and output modelled as a named map of conversations rather than a list of pipes. Two conversations can ride the same transport and stay separate histories; one conversation can be both an input source and an output mirror. Each carries a mode set, so the same object graph gives you “stream me everything” or “only tell me the answer” from configuration.
{ THE CHANNEL MAP } main | heartbeat | journal | stream | ...
each entry: deliver(payload), receive(), a MODE SET
MIRROR its receive() is polled into the agent's inbox
BROADCAST every emitted event fans out to it, turn by turn
DELIVERABLE only the typed RESULT of a run lands here
in : receive() --> ( THE INBOX ) --> the agent runs
out: the result --> the main conversation, AND every deliverable one
the INBOX mediates across all conversations in priority order, so
"which conversation is talking" is not the agent's problem.
presets: mirror everything, only notify, per-conversation modes
and every agent silently gets a stream conversation for observers.
behind a conversation: a chat transport with a persisted cursor -
a terminal pane, a directory of files consumed by deletion, an
in-memory queue. THE SAME THREE OBLIGATIONS.
emitting is intercepted at registration, so no agent can emit
without the runtime seeing it.
- A conversation is not a transport. Separating the two is what lets one chat channel carry two independent histories, and one history span two transports.
- “What lands in the transcript” versus “what is the answer” is one flag, not a second code path — so a noisy debugging view and a quiet production view are the same wiring.
Runs in cave
One Clock, One Way Out
The whole runtime has a single background thread and a single place that touches the outside world. Every periodic behaviour is a tick on one clock, and the rule is written into the source: nothing polls an external service anywhere else.
( THE CLOCK ) ONE daemon thread, one-second cadence
every registered tick that is due, executes.
a tick that throws is caught -- one bad organ cannot stop it.
perception the outward poll, below
health check results ride in a shared carrier
schedule fire see the next entry
hot reload re-read the job definitions from disk
heartbeat prompt type into the agent -- UNLESS a human is
active (a fresh lock file wins over the
clock) or the live config says no
( PERCEPTION ) THE ONLY OUTWARD POLL IN THE SYSTEM
for every agent, receive from every conversation
a command? handle it
otherwise enqueue to THAT agent's inbox
then tick the world's own event sources
deterministic --> route it as a message
probabilistic --> write it into the injection point
and it RATE-LIMITS ITSELF: called more often than its own
interval, it returns nothing. so any tick, and any loop, may
call it safely.
- Self-rate-limiting inside the organ rather than at the call site is what makes “exactly one poller” enforceable instead of a convention everyone is asked to respect.
- The human wins over the clock. A heartbeat that types over somebody’s live session is precisely the failure this exists to prevent, so the lock file is checked at fire time, not at boot.
Runs in cave
Scheduled Work Judged by Artifacts, and Identity-Checked Reaping
Scheduled work whose success is decided by files on disk, not by exit status. Each job declares what it must leave behind and what must have succeeded first. When a run hands back a process id, the runtime re-reads that process’s start time before believing the id is still the same process — and only once it is genuinely gone does it check the declared files.
definitions on disk --hot reload--> { live job objects }
EVERY TICK -- PHASE 1, reap what is in flight
for each tracked run:
is the process alive?
yes --> re-read its START TIME. different? the id was
REUSED by the operating system: treat it as dead
no --> finished. reap it.
then, and only then:
every declared deliverable present?
yes --> success, and any standing block is resolved
no --> "deliverables missing", plus a block report
on an append-only ledger
PHASE 2 -- fire what is due
have all of its prerequisites succeeded? no --> skip this round
fire it.
returned a process id? track it; check deliverables LATER
otherwise check deliverables NOW
THREE independent gates in series: the prerequisite, the process
actually exiting, and the files actually existing.
- “It exited zero” is not evidence. The declared artifact is — which also means a job that lies about finishing is caught by the same code path that catches one that crashed.
- Checking process identity, not just liveness, is the difference between a reaper and a race condition on a busy box.
- A failure writes a block report a later success marks resolved, so the ledger is the standing state of what is stuck rather than a pile of historical errors.
Runs in cave
The Loadout Archive, Identified by Hash
An agent’s entire identity surface — its servers, its settings, its instructions file, its rules and its hooks — treated as one swappable unit. Injecting a named loadout always archives the current one first, so a swap cannot destroy what it replaces. And “which loadout is active” is answered by hashing the live files, with the bookkeeping record demoted to a labelled fallback.
THE LOADOUT = six things, moved as ONE
the server config, settings, local settings -
the instructions file, the rules directory, the hooks directory
archive(name) REFUSES if that name is taken. no silent overwrite.
inject(name)
1. archive the CURRENT one first, under a reserved backup name
this step failing ABORTS the whole injection
2. copy the files over the live ones
3. REPLACE the two directories -- replace, not merge
4. record what is now active
which one is active?
hash the live tree (its files, and everything under its dirs)
matches an archive --> { active: that one, matched by: HASH }
matches none --> fall back to the record, AND SAY SO:
{ matched by: HISTORY, note: the current
files may have changed }
nothing at all --> { active: none }
hand-edit the live files and the system immediately reports
"this matches no archive" rather than lying about what is loaded.
- Identity derived from content, with bookkeeping as an admitted fallback. A record that can be wrong is only safe when its answer says it might be.
- The forced backup is what makes the destructive directory replacement survivable — and it is gated on its own success, so a failed backup means nothing was touched.
Runs in cave
One Detector, Two Mounts, Asymmetric Authority
A single code-smell detector mounted twice on the identical event, with deliberately different powers: the copy that runs before a write can refuse it; the copy that runs after cannot block anything, so it speaks through the only channel that still reaches the model. Both fail open — every error path lets the edit through.
the agent is about to write a file
|
( GATE 1 -- BEFORE ) the ONLY veto in the system
the library isn't installed? --> allow
the naming lock is off? --> allow (the gate is inert)
lock on, non-canonical filename? --> REFUSE THE WRITE
| allowed
v
*** the write happens ***
|
( GATE 2 -- AFTER ) cannot block. its error channel is swallowed.
so it ALWAYS speaks, every time, through the one channel that
does reach the model: extra context attached to the event.
|
v
the findings land in the agent's context. the agent decides.
neither mount holds the logic. both call the same library, which is
equally reachable from a command or any other host.
the before-gate checks only the FILENAME, never the content -- it is
budgeted to sit in front of every single edit.
- Two mounts, two authorities, one detector. Advisory and blocking are a deployment decision rather than two codebases that drift.
- Fail-open is written at every exit, because a linter that holds a veto and has a bug is worse than no linter at all.
Runs in codenose
The Mode Latch Is a File
Two operating modes live in the filesystem rather than in configuration or a daemon: one is the existence of a file, the other is a word inside one. Three processes that never talk to each other each check the same path and independently escalate the same finding — advisory becomes critical, critical becomes a refused write. The dial is a touch; the read is a file check.
a slash command a library call any shell at all
touch / remove set on / off write a word into it
\ | /
'------> TWO SENTINEL FILES <-----------'
the naming lock (it EXISTS = on)
the strict mode (it CONTAINS a word)
|
+---------------------+----------------------+
| | |
( the before-gate ) ( the scanner ) ( the after-reporter )
becomes a HARD one finding findings become critical
blocker escalates to and the message is
critical prefixed
the latch DETECTS NOTHING. it only re-grades what the detectors
already found -- which is why the same rule set is advisory in one
repository and blocking in the next.
no reload. no message passing. no restart.
- The lock path is declared independently in three places, so a process running without the library installed still honours it.
- The strict mode is read inside the scan, not at construction, so it can change between two edits in one session.
- Detection and grading stay separate, which is the same split that lets one taxonomy serve several houses’ standards — see the next entry.
Runs in codenose
The Taxonomy Is Data
The smell taxonomy is not a pile of branches — it is a name-to-function registry plus layered configuration, with third-party detectors named as a module and a function and imported at scan time. And the detectors never decide how serious they are: the scanner stamps severity on afterwards.
the user's config the project's config project WINS
deep-merged; custom detectors APPEND rather than replace
|
v
{ per rule: enabled?, severity, thresholds }
|
( SCAN A FILE )
an ignore directive in the first few lines --> return NOTHING
for each built-in rule that is enabled:
run it, then STAMP the configured severity on every finding
the rule takes no options? call it without them, carry on
for each custom rule:
import the module, get the function, run it, stamp it
cannot import? warn and skip -- never abort the scan
a rule that raises? caught. the scan continues.
|
v
findings, rolled up into a per-directory cleanliness reading
adding a smell type is adding ONE ENTRY TO A DICTIONARY.
the SAME detector output is graded differently per project, and the
detector knows nothing about it.
- Separating detection from grading is what lets one taxonomy serve many standards without forking the detectors.
- Everything fails soft. An unloadable rule, a raising rule and an unparseable file each cost exactly one finding — never the run.
Runs in codenose
Severity Is Which Envelope It Arrives In
How serious a finding is, is expressed by which context envelope it is wrapped in. A normal result comes back in the tool’s own low-priority tag; a critical one is rendered in the harness’s highest-attention framing and carries an explicit obligation. The tag, the icons, the severity words and even the tool’s name are configuration, so the whole instrument re-skins without touching a detector.
findings + a theme { the tool's name, its tag, severity words,
an icon per smell type }
|
group by type; render a legend and a table
|
is anything critical?
|
no -----+----- yes
| |
v v
the tool's THE HARNESS'S HIGHEST-ATTENTION BLOCK
own low- "ACTION REQUIRED", the latch states, the table, and
priority tag an explicit "address these before proceeding"
| |
'------+------'
v
delivered as extra context to the agent
|
v
and the RECEIVING END is itself a loaded rule: comply by default,
deviate only when the finding is obviously pedantic, and then satisfy
the SPIRIT -- never silently drop the block.
escalation is not a louder message. it is a DIFFERENT ENVELOPE.
- No new authority is invented. The router borrows the framing the harness already treats as highest priority, rather than inventing a convention and hoping it is honoured.
- The envelope is only half the mechanism. The other half is a standing rule defining what the agent owes a critical finding — neither half works alone, and shipping only the first is the common mistake.
Runs in codenose
Layers From Filenames
Architectural layers derived from file names — not from a manifest, a package graph or annotations. A short whitelist of canonical names is enforced at write time, which makes every file’s depth computable from its basename alone, so a static check can flag an import that points outward or one that skips a layer with zero project configuration.
THE TABLE basename --> depth (higher = further in)
outer the entry points: server, api, command line, main
middle the core
inner the utilities
base anything under the base directory
FLOATING models, config, constants, types, exceptions
-- no constraint, importable from anywhere
unknown not in the table --> the check stays SILENT
( CHECK ) only RELATIVE imports. an absolute import is another
package's business, not this check's.
the target is shallower than me --> inner imports outer
the target is more than one deeper --> a skipped layer
otherwise --> fine: inward, one step
AND THE REASON IT CAN WORK AT ALL:
the write gate + the naming lock ==> filenames are canonical
==> a NAME IS A POSITION
==> the layer check has an
ontology without anyone
having written one down
- Two literals are the whole ontology, and anything unrecognised is passed over silently rather than guessed at — which is why it can be switched on in an unfamiliar codebase without a configuration pass.
- The invariant survives because it was made survivable: tests, migrations, scripts and the base directory are exempt by name, so nobody has to fight the lock to do ordinary work.
Runs in codenose
Five Dotdirs, One Directory
One directory carries up to five sibling configuration folders, each owned by a different layer of the stack — so a single folder is simultaneously an instruction set, an agent roster, a machine shop, a render manifest and a graph door, and no layer reads another layer’s slot.
ONE directory. up to FIVE sibling config folders, one per layer:
.claude the harness vendor's. READ-ONLY to us.
.heaven WHO IS HERE rules, skills, agents, hooks,
rename-sets
the runtime WHAT RUNS HERE hooks, automations, the ledger seat
the render WHAT IT LOOKS LIKE a render program. a raw object
list is FORBIDDEN in it -- that is
the doubling trap
the graph the graph's ONLY door
EXACTLY TWO VERBS. THERE IS NO THIRD.
REGISTER present in the tree ==> known, addressable, importable.
the home-level folder IS the registry: no second store,
no symlink duplicates, no index to fall out of date.
ACTIVATE local presence -- PRESENT IS ACTIVE, there is no
activation index for a local file -- or naming it by
reference in the slot's own json.
NAMING IS IMPORTING IS ACTIVATING: one move.
AND MEMBERSHIP IS DECIDED BY A TRANSPORT TEST:
if it does not MOVE when somebody downloads this directory --
tokens, machine-local application settings, runtime state --
IT IS NOT DEVDIR MATERIAL.
the test convicted and deleted a chat-integration file, a persona
pair, and an entire home-level hook folder that held a static file
impersonating the runtime's activation authority.
ONE CARVE-OUT, BY LAW: an automation being PRESENT is not ARMED.
- The stranger who downloads the folder is the whole design constraint. Membership is not taste; it is a mechanical question with a yes and a no, which is why it could convict three existing files and delete them.
- Two verbs and no third is what removes the class of bug where a thing is installed but not enabled, enabled but not installed, or listed in two registries that disagree.
- A slot may be forbidden content. The render folder holds a program, never a list of the objects it renders — the moment it holds both, the world exists twice and one copy starts losing.
Runs in the lab
The Connector Is a Hook File
Putting any agent system — a coding harness, an in-process agent, an opaque terminal session — under one reflective controller is done by dropping a hook file into that system’s own hooks folder. The connector is not a service, and it is not a slot of its own.
THREE CHANNELS, never conflated:
A the in-process LIFECYCLE hooks fired during an agent's own run
before / after run - iteration - tool call - system prompt -
block report - error
B THE REFLECTIVE CONTROLLER -- the layer that lifts hook events
into a stateful, programmable runtime with activation gating
and block / inject verdicts
C the event STREAM. observation. NOT a hook tier.
AND CHANNEL B IS REACHED BY A CHANNEL-A *FILE*:
one conventionally-named hook file into the coding harness's own
hooks folder --> that harness is now on the controller
the same-shaped file into the in-process agent's hooks folder
--> that agent is now on the controller
same name, same shape, one per harness. install it and that agent
system is hooked up. there is nothing else to run.
TRANSPORT DEGRADES EXPLICITLY:
the in-process seam FIRST -- an HTTP call to a controller sitting
on a BLOCKED loop can never complete
else HTTP to an external controller
else no-op
VERDICTS MAP BOTH WAYS, FIXED:
block --> the framework's tool veto
extra context --> the system-prompt inject
THE OBSERVER-CLASS VARIANT -- a tap on an agent the world cannot own
-- is the same install into a foreign harness's config, under three
clauses:
FAIL-OPEN every path exits zero. the world being down never
blocks somebody else's tool.
SILENT in that harness stdout INJECTS into the watched
agent's context, so a reporter writes none.
BOUNDED sub-second timeout, fire-and-forget.
it gives the world EYES on a foreign agent. never a mouth.
- The integration surface is a file, not an interface. Nothing registers, nothing is discovered, nothing negotiates a protocol — the file is in the folder, so the events arrive.
- The three channels being separate is the load-bearing part. Treating the event stream as a hook tier is how an observation surface quietly acquires the authority to block.
- The observer tier is deliberately mute. A watcher that can also speak into the watched agent’s context is not a watcher; and in that harness the difference is one accidental print statement, which is why silence is written as a law rather than a habit.
Runs in the lab
Activation by Rename
An agent’s loadout is not a configuration value; it is the state of the filesystem. Turning a skill, rule, hook or sub-agent off renames its file, and a saved rename-set is equipped like a talent build.
THE LOADOUT IS THE STATE OF THE FILESYSTEM.
PRESENT = ACTIVE
OFF = the same file with ".inactive" appended
which the resolver's slot grammar STRUCTURALLY EXCLUDES -- so the
toggle needs no index, no registry, and no cooperation from any
loader. an active file wins over its inactive sibling on a scan.
A SAVED RENAME-SET: { section: { item: on | off } }
four sections -- skills, rules, agents, hooks
applying it performs EXACTLY the renames needed, and reports
how many it did
the browser configurator and the library are BYTE-COMPATIBLE
implementations of one rename algebra (one was extracted from the
other), so a human clicking rings and an agent calling a function
are performing the identical operation.
AND IT CAN BE SCOPED IN TIME: the heartbeat applies its own set for
ONE beat and restores it in a finally block.
one more file promotes the configuration into an OWNER of the
directory -- see the write-block above.
a stack of these layers, faced as ONE agent, is the whole topology;
today's single agent is the degenerate one-layer case it grows from.
- No loader had to learn about this. The off-state is unreadable to the resolver because of the shape of its filename — which is the cheapest possible way to make a switch that every consumer already honours.
- Two implementations, one algebra, verified byte-compatible — so the interface a human uses and the interface an agent uses cannot drift into two different meanings of “equipped.”
- Reversible and time-boxed. Applying a set returns enough to undo it, which is what lets a background beat borrow a completely different loadout and give it back.
Runs in the lab
One Core, N Sinks
Terminal app, web daemon and desktop app are not three implementations of the agent. They are three sinks attached to one agent turn through a single callback argument.
THE WHOLE FACE LAYER IS ONE ARGUMENT:
agent.run( message, callback = composite([ capture, A SINK ]) )
and a sink is just a callable taking one raw message.
TERMINAL renders to a terminal IN-PROCESS. no HTTP, no port
-- deliberately the self-modification safety net:
the surface that still works while you are editing
the server
STREAM converts to { type, data } and hands off to the
event loop
WRAPPED another agent's turn, under ONE outer event type,
so a delegated sub-agent routes to its own panel
instead of colliding with the host's chat log
TWO STRUCTURAL RULES, BOTH LEARNED FROM LIVE FAILURES:
1. THE EMIT MUST HOP onto the event loop threadsafe, because the
callback fires from a worker thread. without the hop, every
event batches to turn-end: the interface sits on "working" and
then dumps everything at once.
2. THE STREAM IS A FAN-OUT BUS, ONE QUEUE PER SUBSCRIBER.
a single shared queue PARTITIONS events across clients: the
second browser tab steals the DONE, and the first tab hangs
forever.
and the desktop shell is a META process: adopt a healthy daemon on
the port or fork one, keep the app being built as a separate
crashable process, and cap its restart budget.
- Adding a face is adding a function, not a transport, an adapter or a second code path through the agent.
- The in-process terminal is a safety net on purpose. When the thing being modified is the server, the surface that needs no server is the one you can still watch it from.
- One queue per subscriber is not an optimisation. A shared queue does not slow the second client down; it silently splits the conversation between them, and both halves look like a hang.
Runs in the lab
The Agent’s Daemon Is the World Server
Instead of an app talking to a game server, the coding agent’s own daemon is the world server — rooted at the user’s own directory, with one event stream carrying chat, world events and agent activity, and the directory’s machines running on the heartbeat that was already ticking.
NOT an app talking to a game server.
( THE CODING AGENT'S OWN DAEMON )
+ ONE EXTENSION mounts the entire world surface onto it: the
loading walk, the graph events, the verb doors, the board
persisted under the place's own config folder
+ a MERGED event generator
so a chat message, a world event and an agent's activity all ride
ONE event system. the previously standalone world server collapses
into this one and survives only as a development harness.
ROOT RESOLUTION NEVER GUESSES:
the environment variable wins
else the working directory, ONLY IF it already carries the
game-seat marks
else THE MOUNT IS SKIPPED, with one honest log line
mounting a universe into an arbitrary working directory would write
seats into somebody's unrelated folder.
THE MACHINE LAYER OBEYS THE SAME DISCIPLINE:
per-directory automation programs register IN MEMORY, under a
place-prefixed key, on the daemon's OWN registry
they fire on the heart's existing minute tick -- ZERO new
schedulers. the calendar's registry IS the registry.
the hot-reload method is wrapped ON THE BOUND INSTANCE, so the
periodic reload cannot sweep them away
NOTHING is written to the flat host-shared registry folder: a
file there is loaded AND fired by every daemon on the box
THE MONEY LAW: a code-pointer machine registers live; a
model-calling machine lands DORMANT until it is armed
and a world failure never takes the agent's daemon down.
- The collapse is the point. Two servers means two event streams, two lifecycles and two places state can be; one server means the world is something the agent is already inside.
- Reusing the existing clock rather than adding one is the same discipline as the single-clock runtime two floors up: a second scheduler is a second answer to “what time is it.”
- Refusing to mount is a feature. A universe that guesses its own root writes seats into a stranger’s directory, and the one honest log line is cheaper than every recovery path that follows.
Runs in the lab