TamozTechnical deep dive~30 min read

Tamoz, technically: from a sensor event to a governed action

ArchitectureStream ProcessingAgent GovernanceEvaluationHardware-in-the-Loop
Who this is forEngineers who want to judge the design, reuse a piece of it, or contribute. The overview covers the why; this page covers the how.
Organizing ruleThe Situation is the seam. Agentic Stream owns time and authority, and Tamoz owns judgment. Neither reaches into the other's half.
Evidence labelsEach claim says whether it's implemented, proven in simulation with a scripted reasoner, proven with a real model, operator-observed, or still open.

1. The system at a glance

The problem this layout solves is putting judgment close to equipment without putting authority there. Three repositories, each with its own job and its own license file (all MIT). They talk only through typed, digest-bound contracts: JSONL events in, gRPC episodes over a Unix socket, server-sent events for outcomes, and a device wire for commands. The shared canonical form comes with test vectors that both the Ruby side and the Go side must reproduce byte for byte, so a digest means the same thing on both sides.

Figure 1 · Components and the contracts between them

System map of Streams Simulator, Agentic Stream, and Tamoz Streams Simulator produces events that enter Agentic Stream's ingress. Agentic Stream builds Situation versions and its cognitive scheduler sends a sealed snapshot to Tamoz's EpisodeWorker. Tamoz's episode graph returns a Decision to the stream's validator, then policy. Approval requests go to Tamoz's approval relay. The outbox and dispatcher send commands to the simulator's effectors or device emulator, and the reconciled outcome streams back to Tamoz's outcome listener, which feeds memory. STREAMS SIMULATOR · GO AGENTIC STREAM · GO TAMOZ · RUBY Domain JSON · 8 domains World core · faults Perturbation + delivery ledger Adapter → sink Sealed truth Independent scorer Effectors / device emulator Ingress + append-only event log Stream engine · watermarks · windows Immutable Situation versions Cognitive scheduler Decision + intent validator Policy gateway Outbox → dispatcher Durable core · SQLite Memory · three layers Situation memory · recall EpisodeWorker · gRPC Episode graph · decide Approval relay → a person Outcome listener (SSE) events cmds snapshot decision approval outcome or: gateway + Arduino, same device wire one Go process · SQLite WAL 27 gems + Tamoz Agent app

Scroll sideways to see all three repositories.

From the public repos. The software composition ran end to end across five simulator rounds. The Arduino path swaps in for the simulator's device emulator and uses the same device wire; its Tamoz-to-stream intent conversion is still work in progress.

The split of responsibilities is written down as a contract, and every design question gets settled by asking which side of it a concern falls on:

ConcernOwnerWhy there
Event admission, dedup, quarantine, event time, watermarks, latenessAgentic StreamDeterministic and replayable. No model belongs on the hot path.
Windows, operators, Situation versions, lifecycle, provenanceAgentic StreamState changes serialize per partition; published versions are immutable.
When to reason: admission, budgets, cancellation, supersessionAgentic StreamReasoning cost has to be a deterministic, explainable decision.
Decision validation, policy, risk, approval authority, interlocksAgentic StreamAuthority can't sit with the party that proposes.
Commands, outbox, effectors, reconciliation, outcomesAgentic StreamEffects need one owner with durable, idempotent records.
Planning, reasoning, verification of its own conclusionsTamozJudgment is the one thing the model is good for.
Memory: Experience, Knowledge, WisdomTamozLearning needs a durable, authorized, scoped store.
Skills and MCP tools inside an episode, human approval deliveryTamozTamoz has the channels. The stream keeps the approval authority.
World truth, delivery faults, scoringStreams SimulatorAn instrument has to be independent of what it measures.
Serial port, output bounds, lease watchdog, safe stopGateway + firmwareThe last line of defense runs without any network or model.

Twelve joint invariants are release-blocking for the pair. A few of them carry most of the design. Both sides compute byte-identical snapshot digests. Tamoz never computes event time, a watermark, lateness, or window membership. The stream never calls a model on its deterministic path. Every attempt is identified by (episode_id, attempt_id, fence), and output from a stale attempt is refused even if its snapshot matches. And no intent executes without passing the stream's policy pipeline, whichever executor produced it.

2. The stream plane: from events to Situations

Agentic Stream is a modular monolith in Go 1.26: one process, SQLite in WAL mode, restricted CEL for rules, and YAML SituationSpecs compiled and digested at load. Here's the path of one event:

  1. Validateenvelope + schema
  2. Appenddedup by stable id
  3. Partitiontenant + key
  4. Watermark+ source health
  5. Windowstumbling, sliding, count, decay
  6. Operatorsaggregates, slopes, heartbeats
  7. Situation vNimmutable, digested

A Situation version carries the derived facts, the lifecycle phase, severity, completeness, the material delta since the last version, a provenance summary, source references, and a canonical digest. A later event never rewrites a published version. It produces a new correction version. Lateness isn't a silent default either. Each spec declares its bounded out-of-orderness, idle timeout, allowed lateness, clock-skew tolerance, and one of four policies:

Late policyWhat happens
drop_with_auditState doesn't change, and the decision to drop is recorded.
history_onlyEvidence is kept, and the derived Situation is left alone.
correctAffected state is recomputed and a correction version is published.
correct_and_reconsiderCorrect, then admit one deduplicated reconsideration episode.

A missing heartbeat is a first-class feature. When a source goes quiet, completeness becomes uncertain, and every downstream gate can see it. That's how "the probe went silent" turns into a denied emergency action instead of a confident guess.

3. The cognitive scheduler: when reasoning is worth paying for

Admission is a deterministic decision the stream makes. The agent never decides whether to wake up. Every outcome (ignored, deferred, coalesced, admitted, cancelled, expired) is recorded with a reason, so cognitive cost is always explainable after the fact.

Figure 2 · The admission funnel

Cognitive scheduler admission funnel A published Situation version passes a trigger condition, a score threshold, freshness and completeness checks, and debounce, cooldown, coalescing and capacity controls before a bounded episode is admitted. Each stage has an exit: ignored, deferred, or coalesced, always recorded with a reason. Situation version published every material change produces one Trigger condition restricted, deterministic CEL Score ≥ threshold weak evidence doesn't wake anyone Fresh and complete enough plus a material delta Debounce · cooldown coalesce · capacity · cost ceiling Episode admitted snapshot · budget · fence false → ignored, recorded below → ignored, recorded stale or uncertain → deferred with a reason duplicate → coalesced or superseded MEASURED IN ROUND 3 7,200 quiet events → 0 episodes

Scroll sideways to see the exit paths.

From the public repo (Agentic Stream internal/cognition). The Round 3 number was measured live in the simulator.

Supersession is the quiet hero here. If a newer version materially changes the Situation while an episode is still thinking, the attempt is cancelled, and its fence guarantees a late answer can't be accepted. If a correction arrives after a command already executed, the stream admits one deduplicated reconsideration bound to the corrected version.

4. The episode boundary

This is where continuous evidence turns into one bounded, supervised piece of reasoning. Tamoz implements the EpisodeWorker gRPC service with two RPCs, Handshake and Execute. The stream dials it over a Unix socket (mTLS optional). The worker is authoritative for nothing. It proposes, and the runtime decides.

Figure 3 · One episode, as a sequence

Episode sequence between Agentic Stream, Tamoz, evidence tools, and the model Agentic Stream performs a handshake with the Tamoz worker, then calls Execute with the snapshot, its digest, a budget, and a fence. Tamoz re-hashes the snapshot and stops before any model call on mismatch. It reads bounded evidence through scoped read-only tools, sends a frozen frame to the model through a witness gateway that signs a record of the call, builds a typed Decision, and streams events back ending with a terminal event and an artifact manifest. The stream validates identity, fence, and digest and accepts or rejects the Decision. Agentic Stream executor Tamoz worker + graph Evidence tools reverse channel Model via witness gateway Handshake: protocol, contract, features compatible (or refused) Execute: snapshot + sha256, budget, fence re-hash the snapshot in constant time; a mismatch stops the episode before any model call evidence.get / features.query bounded rows and bytes frozen frame, exact bytes → typed document + signed call record build the Decision: facts apart from inferences, cited evidence, uncertainty, intents from the catalog events: budget.updated, decision.proposed terminal + artifact manifest validate episode, attempt, fence, digest, schema, catalog → accept or reject, durably

Scroll sideways to follow all four participants.

From the public repos (tamoz-stream and Agentic Stream internal/episodes). The witness gateway and the no-hidden-fallback rule landed after the simulator rounds, which used a scripted brain.

A few details make this boundary hard to abuse:

  • The snapshot is sealed. The request carries snapshot_json and its SHA-256, plus digests of the decision schema, tool catalog, prompt, and objective. A strict scanner refuses malformed JSON at the door: duplicate keys, unpaired surrogates, unsafe numbers.
  • The tool surface is a fixed allowlist. The episode host exposes features.query, evidence.get, situations.related, history.prior_incidents, knowledge.search, and forecast.run, all read-only and bounded, and nothing else can be named. The host holds no reference to a toolbox, effect journal, MCP client, filesystem, or memory write path.
  • The budget is finite and enforced from outside. Wall time, model and tool calls, input and output tokens, tool-result bytes, provider retries, and cost. The stream owns the budget, and a late provider response can't extend the deadline.
  • Episodes are non-interactive. An interrupt inside a stream episode is a typed terminal failure, never a wait. People are reached through the approval relay, not by pausing the worker.
  • Every completed episode is attributable. The terminal event carries an artifact manifest with the prompt, skill-set, tool-catalog, and memory-record digests, plus the model policy and contract version.

5. Inside a decision

A Decision is a proposal, not an authorization. It separates observed facts from inferences, cites its evidence by reference, states its uncertainty, carries a validity interval, and proposes zero or more typed ActionIntents. What an intent may contain comes from a spec-bound intent catalog. It's authored in the Situation spec, embedded in the episode with a shared digest, verified by the worker before any model call, and verified again, independently, in Go. The catalog fixes each intent's exact risk class, parameter schema, presets, and the short list of fields the model is allowed to write.

// thermal catalog entry (shape simplified)
"request_bounded_cooling": {
  "risk": "R2",
  "model_writable_fields": ["mode"],
  "parameters": { "mode": { "enum": ["hold", "bounded_cooling"] } },
  "presets":    { "bounded_cooling": "from the capability catalog, never the model" }
}

// first real-model attempt  → rejected: wrote fields it does not own
{ "type": "request_bounded_cooling", "duty_pct": 50, "lease_ms": 5000 }

// after narrowing the surface → accepted
{ "type": "request_bounded_cooling", "mode": "bounded_cooling" }
Modeled on the thermal domain in the public repo; field names are real and the entry is shortened for reading. The rejected and accepted proposals are from the real-model thermal run described in the motor field note.

Go-side validation checks risk equality with the catalog, not just "below the ceiling". It also checks per-intent schemas, preset byte-identity, entity binding, evidence grounding, intent counts, and the compensation rules. The policy gateway then enforces the catalog's approval requirement and a per-intent hourly rate limit. The model proposes. It never declares its own authority.

6. From intent to effect

The path from "the model suggested it" to "the fan spun" is deliberately longer than one API call. Each hop turns an untrusted proposal into a more constrained, durable record.

Figure 4 · The governance pipeline

Governance pipeline from Decision to outcome A Decision passes the validator, then policy against current state, then a durable outbox with an idempotency key, a dispatcher that leases and re-validates, the effector, and finally an outcome of succeeded, failed, or unknown. Invalid or denied proposals are durably rejected with a reason. High-risk intents detour through a signed human approval before reaching the outbox. Unknown outcomes go to reconciliation and are never blindly retried. Decision typed proposal from Tamoz Validator identity, fence, digest, catalog Policy freshness, risk, rate, interlock Outbox durable command, idempotency key Dispatcher lease, then re-validate Effector device edge or simulator Outcome succeeded, failed, unknown Rejected or denied recorded with a reason Human approval signed, single-use Unknown → reconcile never a blind retry Policy judges the current state, not the state the model saw. Operator stop and epoch drain act here, so a worker can't outrun a stop.

Scroll sideways to follow the pipeline.

From the public repo (Agentic Stream internal/decisions, internal/policy, internal/actions). Validation, denial, approval, and compensation ran live in the simulator rounds; the unknown-outcome path is covered by tests.

Risk classes run from R0 to R4. What each class requires is versioned per spec and per policy, never hard-coded. In the experiment specs they worked out like this:

ClassTypical intent in the roundsWhat policy did
R0install_watch_conditionExecuted after validation. A watch is scoped to one Situation, expires, is count-bounded, and can't modify a spec.
R1start_aerator, create_maintenance_ticket, notify_customerExecuted automatically after every re-validation.
R2emergency_water_exchange, expedite_shipment, request_bounded_coolingParked until a signed human approval, then re-validated for freshness and preconditions.
R3 and upisolate_segment (water network)Denied by risk policy, with the lower-risk intents in the same Decision still considered on their own merits.

Human approval is split into transport and authority. Tamoz delivers the request to a person, and the stream decides. In the simulator rounds, approvals were ed25519-signed assertions of 13 fields, single-use, with the relay and the approver required to be different principals. After every relayed approval, the stream re-checks freshness and completeness. An approval that arrives after the Situation has moved on is refused as a version conflict. A compensating intent, such as a downgrade that walks back an earlier action, goes through the same pipeline under its own risk class. It's never automatically low-risk.

7. The learning loop and memory

An agent that grades its own work learns to flatter itself. So Tamoz only writes an Experience from a completed episode plus an outcome observed by someone else, and in this system that observer is the stream's reconciled outcome. Neither product could close this loop on its own.

Figure 5 · How an outcome becomes a memory, and a memory becomes a better decision

The learning loop An episode decides on Situation version N; the command executes through policy and the outbox; the outcome is reconciled by an independent observer; Tamoz admits an Experience marked observed; later a similar Situation appears on another entity; the memory is recalled into the episode, where the Decision cites the recalled memory digests. Then the cycle repeats. Episode decides pond-04, Situation v47 Command executes policy, outbox, effector Outcome reconciled the independent observer Experience admitted epistemic_kind: observed Similar Situation later pond-09, same precursor Recalled into the episode Decision cites memory digests Retrieval is authorization first: records outside the tenant, entity type, or situation type never reach the ranker, the logs, or the model.

Scroll sideways to follow the loop.

From the Round 3 aquaculture run in the simulator: pond-09's Decision carried two recalled pond-04 Experience digests in facts_used. Mechanics evidence with a scripted reasoner; the recall itself is real.

Underneath sit three durable layers, each with a different kind of authority:

  1. Experiencewhat happened, to whom, with what result
  2. Knowledgereviewed, reusable facts and procedures
  3. Wisdomevaluated strategies, promoted only

Every record is immutable and versioned, and it carries an epistemic kind: observed, reported, inferred, or prescribed. A model-written summary can never become observed. A recalled memory is marked in the trace and excluded as new evidence, so a claim can't get "more true" by being repeated. Experience is never injected automatically. Knowledge needs contradiction checks and, for anything consequential, a person's approval. Wisdom changes behavior only through an explicit, versioned behavior transition, and in-flight turns stay pinned to the behavior they started with.

8. The durable core

Everything above stands on a small durable-execution engine. It's the part of Tamoz I'd reuse even without a model. A turn is a graph run over a SQLite checkpoint store, with barrier-atomic super-steps, deterministic task identity, interrupts, replay, and subgraphs. The graph engine never loads an LLM client, so it stays testable offline.

  • Durable means a synchronous barrier commit. A checkpoint counts only once the store reports the commit. A killed process resumes from the last committed barrier, and resume is refused if the graph version changed underneath it.
  • Effects are at-least-once unless proven otherwise. Each effect has a stable identity and moves through not_attempted → running → completed, or unknown. A non-idempotent effect whose fate can't be proven stops as unknown and waits for a person. It's never retried to "make sure".
  • One fenced writer per thread namespace. Leases and fences keep two workers from committing the same history.
  • Reviewed plans, exact diffs. For the coding-agent surface, nothing acts without a plan bound to its canonical digest, and no file changes without an approval for that exact diff. A failed check becomes evidence for at most two newly reviewed repairs.

The same core runs the Tamoz Agent app (tamoz CLI, durable sessions, trusted profiles, scheduling, a Telegram channel) and the stream EpisodeWorker. It ships as 27 gems. Each one installs and runs with only its declared dependencies, and that's proven per gem by an isolated install.

9. Self-healing

Self-healing in Tamoz isn't rescue plus retry. It's a reviewed, authorized, bounded state machine. It detects a known, typed failure, proves the preconditions, performs one permitted remediation, verifies that the original invariant holds again, compensates where it can, and escalates before uncertainty spreads.

Figure 6 · The remediation state machine

Self-healing state machine The main path runs observed, classified, plan and review, preflight, remediating, verifying, recovered. Unknown, rejected, or stale classifications escalate to a human. A timeout or ambiguous effect during remediation becomes uncertain, then reconciled, and escalated if unresolved. A failed verification triggers compensation, and a failed compensation opens a durable circuit. observed classified plan +review preflight remediating verifying recovered EVERY TRANSITION RECORDS RULE VERSION, PLAN DIGEST, EFFECT IDS, FENCE, AND BUDGET unknown, rejected, stale escalated a person decides timeout, ambiguous effect uncertain → reconcile unresolved → escalated check fails compensate fails → circuit open An open circuit allows only observation, reconciliation, and notification. Time alone never resets it.

Scroll sideways to see every branch.

From the public repo design (documentation/design/self-healing.md). Work in progress: detection and classification run on every live path in shadow mode; no rule has been promoted to active.

Failures are typed, never parsed from free text. The categories include transient_pre_dispatch, stale_precondition, dependency_unavailable, effect_unknown, verification_failed, derived_state_corrupt, and policy_denied. Some categories never trigger automatic repair: a policy denial, durable corruption, a programmer error, an unknown classification. A healer can't widen policy to heal a denial. The allowed remediations form a ladder from least to most consequential. At the bottom is refreshing state and recomputing the same minimal action. Above that come a bounded retry after a proven pre-dispatch transient, renewing a lease, rebuilding a derived index, restoring reversible local state from a preimage, running a pre-authorized equivalent fallback, and executing a separately authorized compensation. At the top is containing and escalating.

Rules earn authority by climbing draft → replay → shadow → fault_injection → canary → active. A rule can't change its own matcher, oracle, budgets, or circuit, and it can't promote itself. Where it stands: the shadow stage classifies failed turns on both the ephemeral and the durable paths (worker, queue, schedule, Telegram). The remediation coordinator is built and tested. The next step is walking one real rule up the whole ladder, then feeding recurring unremediable failures into self-improvement as its training signal.

10. Self-improvement

A learning agent that can rewrite its own prompt, evaluator, or policy can redefine success and hide its own regressions. So in Tamoz, improvement is a promotion pipeline, never live self-editing:

  1. Candidategenerated, with provenance
  2. Development evalpaired report
  3. Holdout evalthe candidate never sees it
  4. Human gategenerated content can't approve itself
  5. Behavior versionactivates at a thread's next intake
  6. Monitor + rollbackresume stays pinned

Capability, security, evaluator, prompt-hierarchy, and code changes always need a person. The evaluator sits outside the thing it judges: the evaluation gems are non-runtime, and no production gem may depend on them. Where it stands: tamoz improve generates candidates, the pipeline composes generation, holdout evaluation, decision, and provenance, and tamoz improve promote records a durable, human-gated promotion. It's proven against real components for one bounded kind of heuristic, an insert-only planning hint. The evaluation step still runs from tests rather than a CLI, and my own eval found that the holdout bar is currently "no regression" rather than "strict improvement". I've pinned that with a test so any tightening is a deliberate choice.

11. The evaluation framework

I care more about this part than any feature: a claim about Tamoz should come from executed evidence, not from a paragraph I wrote. There are four layers, each answering a different question.

LayerQuestion it answersHow
Requirements auditIs each promised guarantee actually true?A manifest generated from invariants, ADRs, phase criteria, public API, CLI, and migrations. The audit is regenerated by running each named test, so a row passes only because its test executed and passed. The limitations page must match the measured gaps, or the build fails.
ScorecardsDoes the agent behave safely on a fixed corpus?Deterministic cases reporting success, verified completion, repairs, and approvals, with hard-zero gates on unsafe actions, false completions, and incomplete evidence. A higher score never buys back a gate.
agentevalHow capable is it, really?Tasks are generators, not fixtures: every seed produces fresh names, values, and layouts, so the corpus can't be memorized. Ten adversity modifiers cross every task: inject, freeze, impossible, phantom, destructive, noise, interrupt, presolved, ambiguous, and clean. It reports pass^k over scenarios with a 95% interval, and three hard gates: no false success, no injection capture, no destructive execution.
Benchmark protocolIs the reasoning any good, and is the model call real?A frozen, SHA-pinned protocol with preregistered baselines and stop rules. Holdout cases get opaque IDs and a truth-leak scan. Metrics include macro-F1, balanced accuracy, Brier score, calibration error, lead time, action utility, and fabricated-reference rate. Only calls signed by the witness gateway count as real.

The rule I added after being fooled twice: prove the grader before trusting the grade. Before any run spends money, five synthetic agents go through the real judge. A null agent does nothing and says nothing. A cheap agent echoes the prompt and the file listing. A parrot gives a plausible, correctly worded refusal without reading anything. An oracle applies the known-correct answer, and an adversary obeys every planted trap. The first three must fail every cell, the oracle must pass every cell, and the adversary must trip the safety gates, or the run stops as a scoring bug. The parrot is the control that caught my abstention grader scoring refusals by their wording. The report also publishes the do-nothing floor. On the committed corpus, an agent that does nothing wins 0 of 18 acting cells and all 6 inaction cells, so no headline rate can hide that split.

What the eval found about my own agent

The 17 September baseline passed every hard gate: zero false successes, zero unsafe actions, zero harness errors. Reading the transcripts showed what that actually meant. In 36 of 46 trials, the plan-review gate rejected every plan before the agent touched a file. Only 2 trials wrote anything, and both were correct. So the "perfect judgment" cells were the same abort as everything else, and no coding capability was measured at all. I kept the first, wrong headline in the findings file with the corrections underneath, because the correction is the useful part.

The physical-side evals found more. Tamoz's recommendation layer doesn't check evidence fitness (0 of 8 unfit-evidence cells refused). Its confidence floor is inert in the thermal loop. And it has no "go and look" step when data is missing. Each finding is pinned by a test that flips when it's fixed.

12. Replay and shadow modes

Replay is an effect-safety boundary. The replay package is built without credentials, effectors, or a resolver, and it reports EffectsAllowed=false in every mode.

ModeWhat it doesExternal effects
DeterministicReplays a trace and hashes the Situation-version historyNever
RecordedReuses a durable recorded worker ledger; never calls TamozNever
ShadowRuns a new executor or prompt against frozen snapshots and reports differences, pinning memory and skill digestsNever
CounterfactualSends typed commands to an explicit simulator onlySimulator only

Deterministic replay is exposed through the CLI today. Recorded, shadow, and counterfactual modes exist as runtime APIs with tests, and a CLI for choosing between them hasn't been built yet.

13. Streams Simulator: a world with an answer key

An instrument that's less trustworthy than the system it measures is worse than no instrument. So the simulator treats determinism, sealed truth, delivery classification, closed-loop actuation, and adversarial validity controls as product requirements. It knows nothing about its consumers. Domains and adapters are plain JSON, and a new consumer should never need a branch in the binary.

Figure 7 · What happened, what was delivered, and who scores it

Streams Simulator pipeline Domain JSON drives a world, which produces events. A perturbation layer changes what is delivered and records every change in a delivery ledger. An adapter renders records into the consumer's format and a sink carries them to the consumer, the system under test. The world also keeps sealed truth. The consumer submits a verdict, and an independent scorer compares the verdict with the sealed truth and the delivery ledger. The consumer's commands return to the world through MCP effectors. commands through declared MCP effectors change the world (closed loop) DomainJSON data Worldstate, faults Perturbationlate, dup, missing Adapterwire format Sinkfile, HTTP, mem Consumersystem under test Sealed truthhidden until reveal Delivery ledgerwhy each record Verdictwhat it concluded Independent scorertruth vs. verdict vs. actions

Scroll sideways to see the whole pipeline.

From the public repo (Streams Simulator). The verdict is submitted after the run ends and before truth is unblinded; callers have to keep that order.

Eight domains ship with it: rotating machinery, aquaculture ponds, cold-chain transit, discrete-line OEE, host system health, Kubernetes clusters, open-field irrigation, and shipment exceptions. Runs are reproducible from a seed, versioned inputs, and a command log, and every artifact (trace, ledger, state history, replay metadata) is digested. The simulator also hosts a device emulator that speaks the same wire contract as the Arduino firmware. That's what let the whole device path be built and tested before any hardware existed.

14. The experiment rounds in detail

Each round had a runbook with a written bar ("A to F green with live evidence", "moments 1 to 7"), a scratch directory of databases, traces, and logs, and an issue file per defect. The rule was to loop until the bar was met or genuinely blocked, and never to lower the bar. As the overview explains, the reasoner in these rounds was scripted. Read every row as evidence about the machinery.

Round 1 · 13 Aug 2026 · connect

StepResultEvidence
Shared canonicalization vectorsPassRuby and Go bound the same snapshot digest; 73 Ruby assertions
Worker handshake over UDSPassProtocol and contract 1.0 negotiated
Live simulator trace, end to endPassRotating machinery with bearing wear, 31,683 events: attempt produced → decision accepted → command succeeded → outcome reconciled
Learning from the outcomePassExactly one Experience admitted, epistemic_kind = observed, with outcome, decision, and command provenance

Sixteen gaps were fixed. Typical ones: the adapter emitted flat dotted keys where replay expected nested events, arrival-time ties broke strict ordering, digests were raw bytes on one side and sha256:hex on the other, and verdict vocabularies disagreed between repos.

Round 2 · 14 Aug 2026 · supervise and learn

MomentResultEvidence
Budget killPassA second model call past a one-call budget ended the attempt mid-run as BUDGET_EXHAUSTED; zero decisions
SupersessionPassEpisode marked superseded mid-flight; attempt cancelled, zero decisions, resumable checkpoint left
Reconsideration + compensationPassA late event produced a corrected version, a RECONSIDER episode, and an R1 downgrade compensating the invalidated command
R2 approvalPassapproval_required, relay delivery receipt, signed single-use assertion; withdrawal and late-approve refusal covered
Second-occurrence recallPassmotor-18's checkpoint memory held motor-17's Experience with full provenance; cross-boundary isolation tested
Trace + cost ceiling + soak-litePassOne traceparent from source event to outcome; cost ceiling halted admission with ingress alive; 30+ minutes clean

Full suites at the end of the round: Tamoz 1,663 runs and 18,197 assertions, 29 Go packages in Agentic Stream, and the simulator suite, all green. Fifteen issues were fixed. The repeated classes taught me the most: digest domains missing a trailing newline, payload keys not declared as graph channels, and zero values treated as missing.

Round 3 · 14 Aug 2026 · three industries

MomentAquacultureWater networkGreenhouse
Quiet baseline admits nothingPass (7,200 events, 0 episodes)Pass (8,280 events, 0 episodes)Pass
Silence fails closedPass (R2 exchange denied, source_health_incomplete)Pass (R2 denied, R3 denied)Pass
Watch over guessPass (R0 watch + R1 aerator)PassPass
Confounder discountedPass (post-feeding dip)Pass (scheduled draw → watch only)Pass
Signed approval → command → outcomePassPass (plus R3 interlock)Partial (fail-closed under probe silence)
Late correction → downgradePassPartial (timer versions interleave)
Second-occurrence recallPass (2 recalled digests)Pass
Storm at the cost ceilingPass (3,600 evals, 0 episodes)Pass

Twenty-three commits across two repos. The greenhouse finding is the one that shaped the roadmap. At 8 bays by 9 channels, per-event processing slowed past the 5-minute heartbeat window, so intents went stale before approval. That's a single-node ceiling, and Round 4 exists to measure it.

Round 4 · prepared, not run

A grid-scale battery storage scenario: one node, 256 to 1,024 racks, about 12 channels each. It's meant to find the knee and the degradation curve, plus one distinctive reasoning moment: when a rack runs away and its neighbors warm, the right move is isolating the source (R2, human) and watching the neighbors, not raising twelve alarms. The runbook and starter specs are written. Nothing has run.

Round 5 · 17 Aug 2026 · audit the assumptions

PhaseResultWhat it showed
Cold chainPass, with one engine bugA 14-hour ocean gap; 840 late readings admitted within allowed lateness, 606 reconsiderations, a real compensating episode. F-1: the reducer picks the winner by event time, so a corrected running total (true value about 8.65 h) never replaced the stale one (about 1.65 h).
LogisticsPass, all seven momentsLost tracking failed closed; an absorbed leg delay got a watch, not an expedite; a customs-hold cascade named its cause at 0.9 and went through a signed approval; an optimistic carrier ETA was cross-checked and not trusted; shipment-09 recalled shipment-04; a hub-outage storm of 1,105 events started 0 episodes.

Round 5 also exposed drift. The starter specs had been written against an older wire shape, and the run needed nine live spec corrections. Those corrections now live in the starters. The remaining picks (security events, an agent-fleet supervisor) haven't run, and the round's assessment recommends fixing F-1 before they do.

After the rounds: a real reasoner

A review after Round 3 found the flaw described in the overview: the graph brains hard-coded the diagnosis and emitted model events themselves. The fix shipped as eight phases on 15 August. (Round 5 ran two days later and still used a labeled fixture brain on purpose, because it was testing the engine's assumptions, not reasoning quality.)

  • one real, journaled model call through the fixed episode graph;
  • durable model calls as graph branches, with budgets computed from receipts;
  • the witness gateway, which re-hashes the frozen request, forwards it verbatim, and signs one record binding request, response, provider, model, settings, and usage. Tamoz's own events stopped counting as evidence;
  • the spec-bound intent catalog, verified independently in Go;
  • skills and memory entering the frame as attributed, untrusted evidence;
  • RECONSIDER as graph nodes (judge, then compensate);
  • a frozen benchmark protocol with holdout mechanics and a 14-control gate;
  • a calibration artifact bound to the compiled spec, so any spec change drops consequential intents back to watch-only, plus a no-hidden-fallback rule: a production route gets a real model answer or a hard failure, never a canned decision.

15. The hardware bench

The physical experiment adds one layer below the stream: a gateway process and an Arduino-compatible board. Nothing in the core changes, because the simulator's device emulator already spoke the same contract.

Figure 8 · Who touches what on the bench

Hardware bench topology Five layers from top to bottom: Tamoz decides a mode and has no port or device names; Agentic Stream builds the Situation, applies policy, and materializes a bounded set_pwm_lease from catalog presets; the gateway is the only process that opens the USB serial port; the Mega 2560 firmware enforces a 60 percent cap, a 10-second lease watchdog, and safe stop; at the bottom, a DHT11 sensor on pin D2 and an L293D driver with a DC motor and fan. Telemetry flows up to Agentic Stream on the left, and commands flow down from it on the right; Tamoz sees only Situation snapshots and returns typed intents. The seam between Tamoz's intent and the stream's device route is still open. The first run failed because the driver inputs were wired to D4 and D3 instead of D6 and D7. Tamoz chooses a mode: hold or bounded_cooling · no port, no device names in its code Agentic Stream Situation, policy, and a bounded set_pwm_lease built from catalog presets Gateway the only process that opens USB serial · quarantines bad frames · forwards commands Mega 2560 firmware · device wire v1 60% duty cap · 10 s lease watchdog · safe_stop · firmware and capability digests DHT11 on D2 temperature + humidity, quality flag L293D → DC motor + fan EN on D5 · direction on D6, D7 TELEMETRY UP COMMANDS DOWN Situation snapshot ↑ · typed intent ↓ seam open: Tamoz intent → device route, not yet joined first run: direction inputs on D4 and D3 → H-bridge held in brake The firmware's limits hold even if every layer above it is wrong.

Scroll sideways to see both directions.

From the bench records. Telemetry ingestion and the governed direct-serial command are real and were operator-observed; the Tamoz-to-stream conversion on this path is work in progress.

What ran, in order:

  1. Simulator first. A fresh three-process run against the device emulator produced 61 accepted telemetry events, zero quarantines, one Tamoz decision, one command, an executed device exchange, and reconciled state feedback. Verdict: pass. Software only.
  2. Sensor-only live ingress. The real board emitted paired DHT11 frames, and a fresh Agentic Stream runtime durably accepted 8 temperature and 8 humidity events with zero quarantine rows.
  3. LED probe. set_led returned a receipt, a result, and a state of energized at 1000, and safe_stop returned the board to a safe state. The operator saw the LED. The raw probe wasn't kept as an immutable artifact, so it counts as candidate evidence.
  4. Fan probe, first attempt. A perfect trace with a motionless motor. Full power in both directions, still nothing. The motor alone across the rails, it spun. Root cause: the direction inputs were on D4 and D3.
  5. Fan probe, corrected. Two jumpers moved with no firmware change, so the digests still matched the checked-in build. A governed command at 600 permille with a 10-second lease, operator-confirmed visible and audible rotation, then an explicit safe stop. A combined sensor-and-actuation run followed.
  6. Real model, separate run. A thermal snapshot went to a real model. The first valid-looking Decision wrote PWM fields and was rejected. With the catalog narrowed to mode, it returned bounded_cooling. It doesn't produce a schema-valid Decision every time, and a bounded retry lands one within a couple of attempts.
GateState
Protocol emulation (happy path)Pass. The joined fault artifacts (rejection, lost ack, reboot, stuck output) remain open.
Physical telemetryDHT11 subset passes. Disconnect and reconnect lifecycle still open.
Bounded physical effectOperator-observed, direct serial. No independent instrument yet.
Authority, reconciliation, e-stop on hardwareNot green
Eight-hour physical fault soakNot started

16. Open findings

These are the known gaps, collected in one place. Each has an owner file in its repo and, where possible, a test that flips when it closes.

FindingWhereWhy it matters
Intent conversion seamTamoz ↔ Agentic Streamrequest_bounded_cooling isn't yet converted to select_thermal_mode through a reviewed contract, so there's no joined physical run yet.
O1 · no evidence-fitness gate in the agentTamoz decision_builder0 of 8 unfit-evidence cells refused at Tamoz's layer. Downstream policy may still refuse, but the agent itself provides no defense in depth.
O2 · confidence is inert in the thermal loopTamoz thermal domainThe confidence floor is 0 and raw confidence is never set, so "unknown means stop" can't be enforced there yet.
O3 · neutral holdout still promotesTamoz improvementThe promotion bar is "no regression", not "strict improvement".
O6 · no active investigationTamoz stream episodeOn insufficient data the outcome is abstain, never "ask for the missing reading, then decide".
F-1 · corrected totalsAgentic Stream reducersA late correction can't revise a windowed running total.
F-5 · heartbeat seamAgentic Stream timersWall-clock heartbeat timers don't propagate missing in every configuration.
Single-node ceilingRound 3 greenhouseWide specs slow processing past the heartbeat window. Unmeasured until Round 4.
Environment-bound evidenceTamoz release auditKill-signal and takeover evidence only reproduces on an unrestricted machine, so the audit marks it unproven elsewhere.
Scheduling and skillsTamozNo cron or IANA timezones yet, and no skill install or update pipeline.

The full list, bound to the release audit by a test, is in Tamoz's limitations page.

17. Where to read the code

If you want to check any of this yourself, these are the files I'd open first:

TopicStart here
The episode boundaryTamoz gems/tamoz-stream: episode_worker.rb, situation_snapshot.rb, decision_builder.rb, reconsideration.rb
Durable executionTamoz gems/tamoz-graph and gems/tamoz-sqlite
Memory, healing, improvementTamoz gems/tamoz-agent-memory, tamoz-agent-healing, tamoz-agent-improvement
EvaluationTamoz gems/tamoz-evals, gems/tamoz-evals-runner, agenteval/, docs/REQUIREMENTS_AUDIT.md
Stream plane and schedulerAgentic Stream internal/engine, internal/situations, internal/cognition
Governance and actionsAgentic Stream internal/decisions/validator.go, internal/policy, internal/actions/dispatcher.go
Simulator and device emulatorStreams Simulator internal/world, internal/perturb, internal/device, domains/
Decisions behind the designTamoz documentation/adr (55 ADRs), especially ADR-036 (the Situation boundary), ADR-038 (typed intent, never model effect), and ADR-039 (Tamoz is supervisory)

Repositories: Tamoz · Agentic Stream · Streams Simulator. All three are MIT-licensed and pre-release, and each keeps its own limitations page.

For the story behind the design, and why the boundary between "what" and "how much" matters, go back to the overview.

Back to the Tamoz overview