Tamoz, technically: from a sensor event to a governed action
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
Scroll sideways to see all three repositories.
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:
| Concern | Owner | Why there |
|---|---|---|
| Event admission, dedup, quarantine, event time, watermarks, lateness | Agentic Stream | Deterministic and replayable. No model belongs on the hot path. |
| Windows, operators, Situation versions, lifecycle, provenance | Agentic Stream | State changes serialize per partition; published versions are immutable. |
| When to reason: admission, budgets, cancellation, supersession | Agentic Stream | Reasoning cost has to be a deterministic, explainable decision. |
| Decision validation, policy, risk, approval authority, interlocks | Agentic Stream | Authority can't sit with the party that proposes. |
| Commands, outbox, effectors, reconciliation, outcomes | Agentic Stream | Effects need one owner with durable, idempotent records. |
| Planning, reasoning, verification of its own conclusions | Tamoz | Judgment is the one thing the model is good for. |
| Memory: Experience, Knowledge, Wisdom | Tamoz | Learning needs a durable, authorized, scoped store. |
| Skills and MCP tools inside an episode, human approval delivery | Tamoz | Tamoz has the channels. The stream keeps the approval authority. |
| World truth, delivery faults, scoring | Streams Simulator | An instrument has to be independent of what it measures. |
| Serial port, output bounds, lease watchdog, safe stop | Gateway + firmware | The 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:
- Validateenvelope + schema
- Appenddedup by stable id
- Partitiontenant + key
- Watermark+ source health
- Windowstumbling, sliding, count, decay
- Operatorsaggregates, slopes, heartbeats
- 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 policy | What happens |
|---|---|
drop_with_audit | State doesn't change, and the decision to drop is recorded. |
history_only | Evidence is kept, and the derived Situation is left alone. |
correct | Affected state is recomputed and a correction version is published. |
correct_and_reconsider | Correct, 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
Scroll sideways to see the exit paths.
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
Scroll sideways to follow all four participants.
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_jsonand 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, andforecast.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" }
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
Scroll sideways to follow the pipeline.
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:
| Class | Typical intent in the rounds | What policy did |
|---|---|---|
R0 | install_watch_condition | Executed after validation. A watch is scoped to one Situation, expires, is count-bounded, and can't modify a spec. |
R1 | start_aerator, create_maintenance_ticket, notify_customer | Executed automatically after every re-validation. |
R2 | emergency_water_exchange, expedite_shipment, request_bounded_cooling | Parked until a signed human approval, then re-validated for freshness and preconditions. |
R3 and up | isolate_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
Scroll sideways to follow the loop.
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:
- Experiencewhat happened, to whom, with what result
- Knowledgereviewed, reusable facts and procedures
- 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, orunknown. A non-idempotent effect whose fate can't be proven stops asunknownand 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
Scroll sideways to see every branch.
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:
- Candidategenerated, with provenance
- Development evalpaired report
- Holdout evalthe candidate never sees it
- Human gategenerated content can't approve itself
- Behavior versionactivates at a thread's next intake
- 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.
| Layer | Question it answers | How |
|---|---|---|
| Requirements audit | Is 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. |
| Scorecards | Does 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. |
| agenteval | How 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 protocol | Is 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.
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.
| Mode | What it does | External effects |
|---|---|---|
| Deterministic | Replays a trace and hashes the Situation-version history | Never |
| Recorded | Reuses a durable recorded worker ledger; never calls Tamoz | Never |
| Shadow | Runs a new executor or prompt against frozen snapshots and reports differences, pinning memory and skill digests | Never |
| Counterfactual | Sends typed commands to an explicit simulator only | Simulator 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
Scroll sideways to see the whole pipeline.
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
| Step | Result | Evidence |
|---|---|---|
| Shared canonicalization vectors | Pass | Ruby and Go bound the same snapshot digest; 73 Ruby assertions |
| Worker handshake over UDS | Pass | Protocol and contract 1.0 negotiated |
| Live simulator trace, end to end | Pass | Rotating machinery with bearing wear, 31,683 events: attempt produced → decision accepted → command succeeded → outcome reconciled |
| Learning from the outcome | Pass | Exactly 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
| Moment | Result | Evidence |
|---|---|---|
| Budget kill | Pass | A second model call past a one-call budget ended the attempt mid-run as BUDGET_EXHAUSTED; zero decisions |
| Supersession | Pass | Episode marked superseded mid-flight; attempt cancelled, zero decisions, resumable checkpoint left |
| Reconsideration + compensation | Pass | A late event produced a corrected version, a RECONSIDER episode, and an R1 downgrade compensating the invalidated command |
| R2 approval | Pass | approval_required, relay delivery receipt, signed single-use assertion; withdrawal and late-approve refusal covered |
| Second-occurrence recall | Pass | motor-18's checkpoint memory held motor-17's Experience with full provenance; cross-boundary isolation tested |
| Trace + cost ceiling + soak-lite | Pass | One 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
| Moment | Aquaculture | Water network | Greenhouse |
|---|---|---|---|
| Quiet baseline admits nothing | Pass (7,200 events, 0 episodes) | Pass (8,280 events, 0 episodes) | Pass |
| Silence fails closed | Pass (R2 exchange denied, source_health_incomplete) | Pass (R2 denied, R3 denied) | Pass |
| Watch over guess | Pass (R0 watch + R1 aerator) | Pass | Pass |
| Confounder discounted | Pass (post-feeding dip) | Pass (scheduled draw → watch only) | Pass |
| Signed approval → command → outcome | Pass | Pass (plus R3 interlock) | Partial (fail-closed under probe silence) |
| Late correction → downgrade | Pass | Partial (timer versions interleave) | — |
| Second-occurrence recall | Pass (2 recalled digests) | Pass | — |
| Storm at the cost ceiling | Pass (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
| Phase | Result | What it showed |
|---|---|---|
| Cold chain | Pass, with one engine bug | A 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). |
| Logistics | Pass, all seven moments | Lost 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
Scroll sideways to see both directions.
What ran, in order:
- 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.
- 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.
- LED probe.
set_ledreturned a receipt, a result, and a state ofenergizedat 1000, andsafe_stopreturned 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. - 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.
- 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.
- 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 returnedbounded_cooling. It doesn't produce a schema-valid Decision every time, and a bounded retry lands one within a couple of attempts.
| Gate | State |
|---|---|
| Protocol emulation (happy path) | Pass. The joined fault artifacts (rejection, lost ack, reboot, stuck output) remain open. |
| Physical telemetry | DHT11 subset passes. Disconnect and reconnect lifecycle still open. |
| Bounded physical effect | Operator-observed, direct serial. No independent instrument yet. |
| Authority, reconciliation, e-stop on hardware | Not green |
| Eight-hour physical fault soak | Not 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.
| Finding | Where | Why it matters |
|---|---|---|
| Intent conversion seam | Tamoz ↔ Agentic Stream | request_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 agent | Tamoz decision_builder | 0 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 loop | Tamoz thermal domain | The confidence floor is 0 and raw confidence is never set, so "unknown means stop" can't be enforced there yet. |
| O3 · neutral holdout still promotes | Tamoz improvement | The promotion bar is "no regression", not "strict improvement". |
| O6 · no active investigation | Tamoz stream episode | On insufficient data the outcome is abstain, never "ask for the missing reading, then decide". |
| F-1 · corrected totals | Agentic Stream reducers | A late correction can't revise a windowed running total. |
| F-5 · heartbeat seam | Agentic Stream timers | Wall-clock heartbeat timers don't propagate missing in every configuration. |
| Single-node ceiling | Round 3 greenhouse | Wide specs slow processing past the heartbeat window. Unmeasured until Round 4. |
| Environment-bound evidence | Tamoz release audit | Kill-signal and takeover evidence only reproduces on an unrestricted machine, so the audit marks it unproven elsewhere. |
| Scheduling and skills | Tamoz | No 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:
| Topic | Start here |
|---|---|
| The episode boundary | Tamoz gems/tamoz-stream: episode_worker.rb, situation_snapshot.rb, decision_builder.rb, reconsideration.rb |
| Durable execution | Tamoz gems/tamoz-graph and gems/tamoz-sqlite |
| Memory, healing, improvement | Tamoz gems/tamoz-agent-memory, tamoz-agent-healing, tamoz-agent-improvement |
| Evaluation | Tamoz gems/tamoz-evals, gems/tamoz-evals-runner, agenteval/, docs/REQUIREMENTS_AUDIT.md |
| Stream plane and scheduler | Agentic Stream internal/engine, internal/situations, internal/cognition |
| Governance and actions | Agentic Stream internal/decisions/validator.go, internal/policy, internal/actions/dispatcher.go |
| Simulator and device emulator | Streams Simulator internal/world, internal/perturb, internal/device, domains/ |
| Decisions behind the design | Tamoz 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