README: infrastructure boundary and per-technology learning spine; commit architecture/runtime/schemas/style papers
This commit is contained in:
1 parent
529a411cb8
commit
f6aad467e2
9 files changed
+647
-167
No files matched your search
@@ -0,0 +1,118 @@
|
||||
# Gate Layer
|
||||
|
||||
Upstream layer between sensors and the lemniscate. Gates admit signals based on thresholds. Each gate is registered; every decision validates against the register.
|
||||
|
||||
## Gate state
|
||||
|
||||
Each gate has one registry row and one state row.
|
||||
|
||||
- impulses: count since last summary.
|
||||
- threshold: value shifted by the L1 decision.
|
||||
- last_opened: cycle index of last opening.
|
||||
- last_decision_cycle: cycle_uuid of the last decision that shifted this gate.
|
||||
- counters: per-gate counters (e.g. dropped_pings).
|
||||
- tier: T0-T3 declared importance.
|
||||
|
||||
## Injection loop
|
||||
|
||||
1. sensor emits impulse (event_uuid, gate, timestamp, payload).
|
||||
2. gate summary at L1:
|
||||
table columns: gate, impulses, threshold, last_opened, drives, last_decision_cycle, tier.
|
||||
3. L1 decision: open/hold gates; threshold delta; optional evidence request.
|
||||
4. opened gates compose content at L2 (frames, text, rows).
|
||||
5. L2 answer + action calls; answer is next summary's salience signal.
|
||||
|
||||
## Cycle closure guard
|
||||
|
||||
Injection is only allowed while the lemniscate is CLOSED: both crossings complete and gate state snapshotted.
|
||||
|
||||
Properties:
|
||||
1. Validation: the close is the validation point; a bad cycle is diagnosed from the summary table.
|
||||
2. Staleness: summary and composed input use the same frozen snapshot.
|
||||
3. Latency: crossing callbacks that block inference hold the cycle open; no desync.
|
||||
|
||||
Exceptions (re-run, not mid-cycle):
|
||||
- threshold breach during an open cycle: summary rebuilt, cycle re-runs before inference lands.
|
||||
- safety gate (T0): pierces the closed state; immediate cycle.
|
||||
- starvation: a gate unseen for K cycles is force-opened at the next cycle boundary.
|
||||
|
||||
## Identity and causality
|
||||
|
||||
| ID | Assigned | Carries |
|
||||
|---|---|---|
|
||||
| event_uuid | sensor, at impulse | (the origin) |
|
||||
| cycle_uuid | lemniscate, at open | admitted event_uuids |
|
||||
| decision_uuid | L1 decision | event_uuids opened |
|
||||
|
||||
- L2 answer carries cycle_uuid + decision_uuid; output traces to its admitted impulses.
|
||||
- UUID v7: 48-bit timestamp prefix; sort order = causal order.
|
||||
- No record enters Phoebe without its causal UUIDs.
|
||||
- Gate history keyed by cycle_uuid is replayable.
|
||||
|
||||
## Intake tiers
|
||||
|
||||
- Summary (poll): aggregate table each cycle.
|
||||
- Evidence (pull): raw impulse rows for one gate, since last cycle, on request. The request is part of the L1 decision; rows are validated against the registry; they arrive as L2 composed input in the same cycle.
|
||||
|
||||
Impulse row: `(event_uuid, gate, timestamp, payload)`.
|
||||
Created at the sensor; UUID v7; immutable. Failed ping is an emitted impulse, never inferred from absence.
|
||||
|
||||
## Policy layers
|
||||
|
||||
Higher layers cannot be overridden by lower ones; lower layers cannot close what a higher one opened.
|
||||
|
||||
| Layer | Owner | Decides |
|
||||
|---|---|---|
|
||||
| Hard floor | operator (dafit) | T0 gates; never closable |
|
||||
| Presence policy | harness/switchboard, rule-bound | all closed / task-relevant open |
|
||||
| Attention policy | runtime L1 | remainder |
|
||||
|
||||
Presence is a gate input above the L1 policy. It is rule-bound and has no LLM in the loop. Presence is the physical form of operator override.
|
||||
|
||||
## Gate tags (declared importance)
|
||||
|
||||
Importance is declared, never inferred.
|
||||
|
||||
| Tier | Meaning | Guarantee | Example |
|
||||
|---|---|---|---|
|
||||
| T0 critical | safety | pierces the closed cycle; hard floor; never closable | fire alarm, operator channel |
|
||||
| T1 relational | operator presence | presence-owned; guaranteed attention share; cannot be attention-closed while active | chat/text gate, sofa camera |
|
||||
| T2 standard | normal attention | tunable thresholds; starvation sampling | keepalive, host monitors |
|
||||
| T3 ambient | background | poll-only; summary trend, on-demand evidence | telemetry |
|
||||
|
||||
Tag row: `(gate, tier, why, mode_overrides)`.
|
||||
|
||||
- Registry-declared and signed. T0/T1 immutable at runtime.
|
||||
- T2/T3 may shift per mode (e.g. webcam T3 -> T1 in sofa mode); each shift is a UUID-logged decision.
|
||||
- `why` is the audit reason string.
|
||||
- Attention budget never starves a tier: T0 pierces, T1 holds a guaranteed share; L1 allocates the remainder.
|
||||
|
||||
## Adaptive timing
|
||||
|
||||
- L1 round-trip: decision capacity.
|
||||
- L2 round-trip: synthesis capacity.
|
||||
- Both derived from v7 timestamps; no new instrumentation.
|
||||
- Cycle interval = mean(L1_RTT) + mean(L2_RTT) + margin.
|
||||
- Animation duration >= L2_RTT; holds the gesture through synthesis.
|
||||
- Estimator: sliding mean with hysteresis.
|
||||
- RTT values feed schedule parameters, not gate thresholds.
|
||||
|
||||
## Long-running probes and heartbeat integrity
|
||||
|
||||
- Probes are async: issued at the crossing, not awaited. Result returns as an impulse into the next cycle's summary.
|
||||
- RTT measures decision capacity only; probe wall-clock never enters the cycle interval.
|
||||
- Probe latency is gate metadata; results accumulate losslessly into the next window.
|
||||
- Silence must be manufactured: a probe's absence cannot be inferred. The deadline emits a timeout impulse (event_uuid, tiered); escalation rides a real signal.
|
||||
- Pierce is for inspection, never effect-side arbitration: T0 forces the cycle to see; actions still resolve under mutex/priority.
|
||||
|
||||
## Persistence and restart
|
||||
|
||||
- Gate table in Phoebe, checkpointed after each cycle. The table is the resumable self; restart restores attention profile and thresholds.
|
||||
- Raw impulses: bounded in-RAM ring buffer per gate; flushed to Phoebe at checkpoint. Evidence reads the buffer; audit reads the store.
|
||||
- Truncation is explicit: an evidence request older than the buffer returns the buffer's lower bound; the gate table records the truncation.
|
||||
|
||||
## Attention semantics
|
||||
|
||||
Gates that close under sustained noise are the noise suppression: the gate stops mattering, rather than filtering smarter. Attention is a first-class resource with a budget and decay. The trait algebra at L2 tunes attention itself.
|
||||
|
||||
Version: 0.9 | Updated: 2026-11-04
|
||||
Whitespace-only changes.
@@ -0,0 +1,176 @@
|
||||
# Runtime Engine: Lemniscate
|
||||
|
||||
Scope: the runtime loop for the agent (Nyx). It is a server, not a UI and not a borrowed coding harness.
|
||||
|
||||
## Runtime decision
|
||||
|
||||
- Interactive harnesses (pi-agent-core, omp) are hosts: they wait for a human, end on session close, and do not run unattended.
|
||||
- Nimmerworld implements its own loop (this engine) and runs indefinitely.
|
||||
- Harnesses host nimmer-lib only. Clients never implement the agent brain; the runtime never borrows one.
|
||||
|
||||
## Topology
|
||||
|
||||
Figure-eight with two lobes and two crossings:
|
||||
|
||||
```
|
||||
Gate Summary In Action Result
|
||||
| |
|
||||
v Second Crossing v
|
||||
+--------------+ +--------------+
|
||||
| LEFT LOBE | | RIGHT LOBE |
|
||||
| (intake) | | (effect) |
|
||||
+--------------+ +--------------+
|
||||
^ First Crossing ^
|
||||
| |
|
||||
Gate Feedback Action Call (tool/actuator)
|
||||
```
|
||||
|
||||
### Bus transport (NATS / JetStream)
|
||||
|
||||
The engine is host-agnostic: it never speaks to a model or an actuator directly,
|
||||
everything crosses the bus as structured JSON.
|
||||
|
||||
```
|
||||
sensor/camera -> pub <sensor.subject> (raw events; gates subscribe via catalog)
|
||||
gate layer -> pub gate.summary (composer renders summary table, once per cycle)
|
||||
L1 (LLM) -> sub gate.summary / pub gate.decision (core NATS, ephemeral, cycle-scoped)
|
||||
L2 composer -> sub gate.decision
|
||||
validate targets vs drives (table lookup, once, upstream of fan-out)
|
||||
then pub action.call.<target> per validated action
|
||||
actuator:<t> -> own JetStream consumer on action.call.<t>.>
|
||||
client:<name> -> own durable consumer on zone.state.> / action.result.> (replay-on-wake)
|
||||
```
|
||||
|
||||
Placement rules:
|
||||
|
||||
- `gate.summary` / `gate.decision` stay on core NATS: ephemeral, regenerated
|
||||
every pulse; never block the pulse loop on storage I/O.
|
||||
- Consequence subjects (`action.call.>`, `action.result`, causal records) are
|
||||
JetStream streams: at-least-once, survive restart, replayable via
|
||||
`event_uuid -> cycle_uuid -> decision_uuid`.
|
||||
- Fan-out is the subject matcher, not code: the L2 composer publishes to
|
||||
`action.call.<target>` from the catalog; each actuator consumes its own
|
||||
subject and tracks its own position; a dead actuator stalls no one.
|
||||
- Validation happens once, in the composer, before the fan-out; consumers
|
||||
execute or nack, they never re-decide.
|
||||
- Idempotency rides the envelope: every action carries `decision_uuid` +
|
||||
`cycle_uuid`; actuators dedupe on them so redelivery cannot double-fire.
|
||||
- The LLM endpoint is a swappable bus client. vLLM today, harness tomorrow,
|
||||
zero engine changes.
|
||||
|
||||
- Left lobe: intake. Gate summaries, evidence pulls, thresholds.
|
||||
- Right lobe: effect. Synthesized answer, action calls, client output.
|
||||
- L1 (first crossing): admission decision. Open/hold gates; shift thresholds; request evidence.
|
||||
- L2 (second crossing): synthesis. Produce answer and action-call list from the admitted content.
|
||||
- The crossings are the only synchronous points. All state resolves atomically there.
|
||||
|
||||
## Loop semantics
|
||||
|
||||
- One traversal = one cycle. The lemniscate closes exactly once: L1 then L2.
|
||||
- Between crossings, traversal is local; only the crossing touches shared state.
|
||||
- The cycle is a transaction: no new input lands while it is open.
|
||||
- Feedback: the L2 answer is the salience signal for the next gate summary. It is not a gate.
|
||||
|
||||
## Data flow
|
||||
|
||||
L1 inputs:
|
||||
- gate summary table: gate, impulses, threshold, last_opened, drives, last_decision_cycle, tier.
|
||||
- request parameters (evidence pull, k frames, probe query).
|
||||
|
||||
L1 outputs:
|
||||
- gate decision: decision_uuid, opened gate list, parameters, threshold deltas.
|
||||
|
||||
L2 inputs:
|
||||
- composed content from the opened gates (evidence rows, frames, text).
|
||||
- catalog of valid output gates from each opened gate's `drives`.
|
||||
|
||||
L2 outputs:
|
||||
- answer text.
|
||||
- action-call list: action_uuid, target actuator/tool, parameters, tier.
|
||||
- gesture accumulator update.
|
||||
- salience signal for next summary.
|
||||
|
||||
Validation:
|
||||
- output gate must be in the union of opened input gates' `drives`.
|
||||
- invalid decision: cycle re-runs. Membership is a table lookup.
|
||||
|
||||
Gate summary envelope (what the model actually reads at L1):
|
||||
|
||||
```json
|
||||
{
|
||||
"cycle_uuid": "c-042",
|
||||
"gates": [
|
||||
{"gate": "k8s_alert", "impulses": 4, "threshold": 10,
|
||||
"last_opened": 12, "drives": ["k8s_action"], "tier": "T1",
|
||||
"last_decision_cycle": "c-041"}
|
||||
],
|
||||
"request_params": {"k_frames": 3, "probe": null}
|
||||
}
|
||||
```
|
||||
|
||||
L1 replies exactly:
|
||||
|
||||
```json
|
||||
{
|
||||
"decision_uuid": "d-117",
|
||||
"opened": ["k8s_alert"],
|
||||
"params": {"k_frames": 5},
|
||||
"threshold_deltas": {"k8s_alert": -2}
|
||||
}
|
||||
```
|
||||
|
||||
## Pulse clock
|
||||
|
||||
```
|
||||
elapsed += dt
|
||||
while elapsed >= interval:
|
||||
crossing()
|
||||
elapsed -= interval
|
||||
```
|
||||
|
||||
- Interval = mean(L1_RTT) + mean(L2_RTT) + safety_margin.
|
||||
- RTT is derived from v7 timestamps (impulse to decision), not from wall-clock probes.
|
||||
- Sliding-mean estimator with hysteresis. A single slow cycle must not stretch the interval.
|
||||
- Probe latencies are metadata on their gates; they never enter the cycle interval.
|
||||
- dt >= 1/pulse_rate would fire every tick once pulse_rate > 62; the accumulator prevents that.
|
||||
|
||||
## Map
|
||||
|
||||
| Lemniscate | Nimmerworld |
|
||||
|---|---|
|
||||
| Director | runtime |
|
||||
| Zone / EmergentZone | bounded event episode |
|
||||
| Crossing L1 | gate summary read; gate open/hold |
|
||||
| Crossing L2 | composed input synthesized; answer + action call |
|
||||
| Slot tokens | participants |
|
||||
| Gesture accumulator | nimmer-lib trait algebra |
|
||||
| update_all_zones | NATS event loop |
|
||||
| Gate / Sensor | threshold channel upstream |
|
||||
| UUID causality | event_uuid -> cycle_uuid -> decision_uuid |
|
||||
| Intake tiers | summary poll + evidence pull |
|
||||
| Policy layers | hard floor (operator) > presence policy (harness) > attention policy (runtime) |
|
||||
| Gate tags | T0-T3 declared importance |
|
||||
| Adaptive timing | round-trips regulate interval and animation duration |
|
||||
| Register | declared sensors/gates/actuators/clients and `drives` links |
|
||||
|
||||
## Engine corrections
|
||||
|
||||
1. `_should_crossing` must accumulate time:
|
||||
- `elapsed += dt; while elapsed >= interval: crossing`.
|
||||
- `dt >= 1/pulse_rate` fires every tick once pulse_rate > 62 and makes the pulse rate meaningless.
|
||||
2. Loops A/B must carry distinct semantics:
|
||||
- the crossing callback must know which loop it is on.
|
||||
- symmetric loops degrade the figure-eight into round-robin.
|
||||
- resolved by the L1/L2 split (intake vs effect).
|
||||
3. `Director.update_all_zones` must call `zone.execute_turn(dt)`, not `zone.run_crossing()`:
|
||||
- otherwise the state machine never ticks.
|
||||
- terminal reports must be collected, not dropped.
|
||||
|
||||
## Interfaces (v1)
|
||||
|
||||
- Core NATS (ephemeral): `gate.summary`, `gate.decision`.
|
||||
- JetStream (durable, at-least-once): `action.call.<target>`, `action.result`, causal records.
|
||||
- Register/catalog: sensors, gates, actuators, clients, `drives`. Catalog entry declares the class path; runtime imports an explicit registry and fails loud on a missing class.
|
||||
- Clients hold own durable consumers on `zone.state.>` and `action.result.>` (replay-on-wake).
|
||||
|
||||
Version: 0.11 | Updated: 2026-11-04
|
||||
@@ -0,0 +1,126 @@
|
||||
# Register
|
||||
|
||||
Central registry: sensors, gates, actuators, clients, and the links between them. Runtime decisions validate against it. Register changes are governed impulses.
|
||||
|
||||
## Registry scope
|
||||
|
||||
| Question | Registry |
|
||||
|---|---|
|
||||
| What sensors exist and what may they emit? | sensor registry |
|
||||
| What gates exist and what are they? | gate_registry (tier, why, mode_overrides) |
|
||||
| What may be emitted out? | actuator registry (animation catalog, output channels) |
|
||||
| What links exist? | gate links (`drives`) |
|
||||
| Who is listening? | client registry |
|
||||
|
||||
The register is a gate layer one level up: sensors, gates and clients are admitted by declaration.
|
||||
|
||||
## `drives` links
|
||||
|
||||
Input gates link to output gates an answer may target:
|
||||
|
||||
```
|
||||
room.cam T3 drives: body.anim (sofa: T1)
|
||||
dafit.chat T1 drives: body.anim, clients.chat
|
||||
fire.alarm T0 drives: body.alert
|
||||
```
|
||||
|
||||
Rules:
|
||||
1. Link visibility at L1: summary carries the link column; attention sees consequences before opening.
|
||||
2. Choice-list at L2: evidence return carries content plus valid output gate catalog. Answer target must be in union of opened gates' `drives`.
|
||||
3. Membership validation: valid = output gate in `drives`; invalid = decision rejected, cycle re-runs. Table lookup, not judgment.
|
||||
4. No orphan decisions: input opened with no route, or answer with no causal input, is a representation error.
|
||||
|
||||
## Effect-side arbitration
|
||||
|
||||
Cycle is the transaction boundary. Two policies:
|
||||
|
||||
| Policy | Example | Enforcement |
|
||||
|---|---|---|
|
||||
| mutex | body.anim: exclusive | one effect per actuator per cycle; second call = override or queue, never both |
|
||||
| priority | T0 safety > T1 reflex > T2 deliberate | higher tier supersedes; superseded reason logged |
|
||||
|
||||
Resolution:
|
||||
1. cycle closes;
|
||||
2. action set resolved (mutex/priority);
|
||||
3. surviving actions validated against `drives`;
|
||||
4. effects fire in one order, atomically, at the crossing;
|
||||
5. cycle closes with a complete effect record.
|
||||
|
||||
Open policy decisions:
|
||||
- per-cycle action cap;
|
||||
- supersede vs queue default;
|
||||
- whether T0 safety can interrupt in-flight animation.
|
||||
|
||||
## Debug workflows and skill store
|
||||
|
||||
`drives` may fan out:
|
||||
|
||||
```
|
||||
host.keep T2 drives: host.prom, clients.status
|
||||
```
|
||||
|
||||
- `host.keep -> host.prom` is a hypothesis: ping down -> metrics explain why.
|
||||
- A probe may sit behind a workflow: a registered state machine or debug command with parameters, answering on its own channel. Registered, tiered, why-stated, UUID-traced, reviewable.
|
||||
|
||||
Skill store:
|
||||
- register change events teach attention in real time; summary carries what each gate's `drives` can answer.
|
||||
- gesture accumulator + decision UUIDs make probe effectiveness queryable.
|
||||
- self-improvement is legible register change (tier shifts, link changes, new workflows), signed and reviewable.
|
||||
|
||||
Governance:
|
||||
- diagnostics (read-only probes) run on the cycle's authority.
|
||||
- remedial workflows carry the registered tier and sign.
|
||||
|
||||
## Pod spawning and escalation ladder
|
||||
|
||||
Spawn is an async probe with two transactions:
|
||||
|
||||
- Run 1 (spawn): decision calls `pod.spawn`, validated against `drives`. Action result carries spawn feedback gate link (`spawn_uuid`), registered at spawn time. Spawn is a register change: event_uuid + why.
|
||||
- Run 2 (result): pod result requested against feedback link; answer lands in next summary as impulse.
|
||||
- Silence must be manufactured: absence cannot be inferred. Deadline emits a timeout impulse (event_uuid, tiered) at budget edge; escalation flag rides a real signal.
|
||||
|
||||
Escalation:
|
||||
- ladder bounded and signed; `escalate: T2->T1` is registered workflow step.
|
||||
- importance declared, never inferred; attention never raises tiers itself.
|
||||
- ladder terminates outside runtime: at T0 the last step is escalation to operator via `clients.chat`.
|
||||
- silence escalates inspection, never arbitration.
|
||||
- escalations logged with UUIDs and superseded reasons.
|
||||
|
||||
## Late results and orphan rule
|
||||
|
||||
When a tool answer arrives after its cycle closed:
|
||||
|
||||
- verification checks causal UUIDs; missing decision cycle = orphan = rejected.
|
||||
- rejection is visible: it is an impulse into next summary.
|
||||
- rejection is not silent execution: tool execution may have happened; result is discarded or re-registered by the next decision. Ingress drops results; decision stays with the runtime.
|
||||
- late/orphan truth carries extra priority tier for inspection, not arbitration.
|
||||
|
||||
Invariant: lossless accumulation; rejection is an impulse; attention stays informed.
|
||||
|
||||
## Consistency mechanism
|
||||
|
||||
- register state in Phoebe (truth), materialized in runtime RAM (validator copy), pushed as change events on NATS.
|
||||
- register changes are impulses: event_uuid at creation, decision_uuid, why string; replayable v7 causality.
|
||||
- registry tier-locked T0/T1: signed by operator and immutable at runtime. Runtime observes and proposes; operator signs.
|
||||
|
||||
## Placement
|
||||
|
||||
- stateful = VM 123 worldserver: gate table in Phoebe, register materialized in RAM, pulse.
|
||||
- broker = VM 122 nats-dev: isolated, cubes VLAN, client port 30000.
|
||||
- ephemeral = k8s pods: daemons, python tools, probe workflows, spawned pods.
|
||||
- no canonical state in a pod: gate state, register copy, impulse buffers live in Phoebe/123.
|
||||
|
||||
## Control chain
|
||||
|
||||
```
|
||||
operator signature
|
||||
-> register (law)
|
||||
-> gate state (memory)
|
||||
-> attention policy (L1/L2)
|
||||
-> action call (actuator)
|
||||
-> body/client
|
||||
```
|
||||
|
||||
No component can rewrite the layer above it.
|
||||
|
||||
Version: 0.5 | Updated: 2026-11-04
|
||||
Reference in new issue
Block a user