I gave the ops Agent its first version number, v0.1.0-rc.1, and first laid out this map
The ops Agent project ops-agent has released its first release candidate, v0.1.0-rc.1 (commit d897d2c95e70), with a runtime of the Python 3.9+ standard library and target platforms macOS/Linux. This version includes built-in self-checks and independent regression tests totaling 309 + 223 items, and the release verification matrix passed across four combinations of Ubuntu/macOS × Python 3.9/3.12, with W8 local drills at 10/10. External acceptance for real SSH, real model services, notification channels, and 7 consecutive days of observation after fixes has not yet been completed.
Body · AI translation
Overview of the "ops-agent Build Notes" series. Code baseline: v0.1.0-rc.1, commit d897d2c95e70. Updated 2026-10-25; this is a released release candidate, and real-environment and 7-day continuous acceptance testing for this version have not yet been completed.
--- 0. The first version number: first, let me pin down what I am explaining
When I tagged this project v0.1.0-rc.1, I looked back at these twelve articles. Taken individually, each one answers a specific question: who approves a command, whether a retry is possible after an SSH disconnect, how to detect that an inspection did not run, and whether an old approval is still valid after a backup restore. Put together, what needs explaining first is how these mechanisms connect.
This version can already be downloaded as a source release candidate; the runtime is the Python 3.9+ standard library, and the target platforms are macOS/Linux. External SSH, Docker, systemd, kubectl, or model services are prepared separately according to the features actually used. My goal has always been concrete: make the troubleshooting process leave evidence, make write operations require explicit human authorization, and make execution stop and verify when the result is uncertain.
W1–W8 below are work package numbers in the roadmap; W8 corresponds to recovery drills and release acceptance. 1. I connect "seeing" and "changing" into two chains
The first chain collects state, organizes evidence, explains anomalies, and sends notifications. The second chain executes changes after obtaining specific authorization, then records command results, verifies business state, and prepares for rollback when necessary. The two chains share the gatekeeper and state directory, but permissions and failure semantics each have their own constraints.
Laid out by responsibility, it is roughly this diagram:
core.py in the diagram is the execution constraint shared by all entry points. The standalone Agent's bash tool calls it, and the CLI and MCP also call it. The MCP host is responsible for the model conversation itself, and mcpserver.py provides controlled tools; it does not run another copy of this project's Agent loop.
Deterministic inspections, meanwhile, are performed by agent/checks.py, which runs fixed probes and determines status. The model can explain sampling results and can continue calling read-only tools to gather evidence, but the rule checks themselves do not need an API key. Preset checks in the scheduler run before the model; if the model is unavailable from the first round, rule anomalies that have already been saved can still enter the run records and notification handling.
This diagram shows responsibility relationships. Different entry points pass through different nodes; in this version, MCP supports only local execution and does not share an automatically recovered session with the REPL. Entry point differences are covered in article 10. 2. Looking back from a single inspection: what evidence must be kept
I can first use a separate state directory and check only the local machine:
The last command writes the report into this temporary state directory. A new directory has no service list, so the check result will usually include "services not configured", with a conclusion of "incomplete" and exit code 3. I want to preserve this distinction: being able to connect to the host does not mean the business has been checked.
An inspection always checks three groups: connectivity, resources, and services. Each item has only five states: ok / warn / critical / unknown / error. The report must retain the target, observed values, thresholds, sampling time, and probe command. When something cannot be sampled, leave a gap; only when both coverage and collection are complete and there are no anomalies can a normal conclusion possibly be given. Article 2 is about this boundary.
Once connected to scheduled tasks, another layer of questions appears: did this check actually start, when did it end, and did it time out or get interrupted? runs/ records an inspection task, reports/ holds reports, and signals/ stores rule results for the scheduler and notifications to retrieve. running, timeout, interrupted, and missed each have their own meaning and cannot be substituted for one another with a single phrase like "inspection complete". Article 6 follows this record further.
Notifications also have independent state. notify.json is the channel configuration, notify-state.json stores state such as anomaly fingerprints, notifications/ is the outbox, and events.jsonl records state changes. Deduplication, silencing, and resending after send failures need to be judged separately; an inspection finding an anomaly does not mean the message has been delivered. Article 3 separates these pieces of state.
This review also added a program constraint: if a tool's rule collection, complete report, or summary saving fails, the scheduler reads ToolContext.evidenceerrors and downgrades the result to incomplete. A final sentence from the model saying everything is normal cannot override this internal error state. 3. Before a suggestion becomes a command, I still need to confirm another set of facts
When the model proposes a restart, there is still a gap before actual execution: the specific command, the actual target, and confirmation of impact and recovery plan. The approval stamp is bound to the command and execution environment, including target connection information, working directory, and the relevant Kubernetes scope; if the target or environment is changed after approval, re-approval is required.
Actual write operations go through opsexec or opsx exec into the target write lock, execution records, snapshot plan, and approval count validation. One authorization by default allows only one execution, and snapshots are collected according to the parameters provided this time; the system has no ability to conjure business backups out of thin air. R0's static judgment, sensitive read restrictions, and the boundaries of manual confirmation are covered in Part 1.
Execution records retain five states: running / succeeded / failed / unknown / aborted. Among these, unknown means the command may have taken effect and cannot be determined. After the SSH connection drops, the local SSH process stops, and this cannot be used to infer that the remote command did not execute. Unverified unknown records will block replay of the same command against the same target; later, a human observes the actual state and then records the verification result. Part 4 explains why I am willing to leave an extra manual step here.
A command exiting 0 does not equal business recovery either. scenario.py connects the pre-check, execution, and post-check of a fixed scenario; execution.status and the scenario verdict describe the command and the verification result respectively. File rollback must be confirmed separately, and after restoring configuration it may still be necessary to re-approve reload/restart and then check the service. This part is in Part 5, including exactly which configuration copy the snapshot saved before nginx reload.
This version continues to narrow the shell boundary: dangerous options are checked after quote and escape decoding; dynamic parameters, expansions that can assemble write options, and dynamic Kubernetes resource names are restricted. Local content globs re-check sensitive paths and symlink targets item by item, while remote content globs and content brace expansion are conservatively rejected. This will increase the cases that require explicit paths or manual confirmation; I have written these compatibility changes into the release notes. 4. The state directory holds several different kinds of "remembering"
Session memory remembers what the model and tools have said, long-term memory remembers confirmed facts, and skill files remember reusable troubleshooting steps. These three are not merged into a single knowledge base. This is especially true for subagents: they use an independent prompt and a read-only tool set, and the main agent's long-term memory is not automatically injected. Part 8 discusses these differences.
To make things traceable after the process exits, some state must be persisted to disk: run records, rule summaries, outbox, approvals, execution records, and snapshots. The shared directory saves an extra synchronization mechanism, and it also means that backups must face the different meanings of these states.
opsx state backup saves raw bytes, including credentials and original snapshots. Restoration must go into a brand-new directory, old approvals are isolated, scheduling, notifications, and active targets are paused, and running records are converted to pending verification or interrupted. In this way, what is restored is a state that requires manual inspection before being enabled, rather than automatically continuing to do things under old processes and old authorizations. Part 9 will also explain why --quiesced is still a manual write-stop declaration, and why hashes cannot authenticate the source of a backup.
Cost state is also on this map. usage.py first reserves the number of calls, then records actual or estimated tokens; failed requests are counted as well. The provider's limited retries and the outer retries of scheduled tasks each have their own boundaries, and requests that have already delivered part of a streaming output will not automatically start over. Part 7 calculates these two layers of retries and the budget separately. 5. What this candidate version has already proven
I write "pass" separately so that I can return to specific evidence rather than adding them up into a very large number.
| Evidence | Result for this version | Coverage | | --- | --- | --- | | Built-in self-checks and independent regressions | 309 items + 223 items; of which 52 new independent regressions were added this round | Guarding, state machine, protocol, error handling, and reproduced issues | | Release verification matrix | Four groups passed: Ubuntu/macOS × Python 3.9/3.12 | Compilation, self-checks, regressions, and local drills of the same tag's corresponding commit across four combinations | | W8 local drill | 10/10; 24 items participate in code/policy digest binding | Real local processes, files, and loopback HTTP; models and notifications use local stubs | | Source release artifacts | Source package for the exact tag, SHA256SUMS | Post-release download verification, and checking that source content matches the tagged commit | | This version's W8 external acceptance | Not completed | Real SSH, real model services, notification channels, and continuous 7-day observation after fixes |
State backup and restoration also has regressions such as tests/testw8statebackup.py, which cannot be counted as part of those 10 scenario restoration drills. Passing tests also do not turn static command classification into a complete shell sandbox, nor do they prove that all actual hosts and services are adapted. Part 11 continues to discuss how these pieces of evidence are organized, and how review turns known defects into regressions.
The version itself also has a traceable chain: update the release notes, CHANGELOG, README, and VERSION, first go through develop checks, then enter main and tag; the Release workflow verifies the tag, commit, and documentation, and only after the four-group matrix succeeds does it generate the source package and release page. The candidate marker of v0.1.0-rc.1 is retained together with the pending status of external acceptance. 6. Next, I will continue walking along the gaps
There are quite a few intentionally left blanks among the twelve topic articles: no general DAG, no vector memory, no Web/IM chat shell, and no approval-free automatic self-healing. Each has its own implementation cost, and I first need to verify the existing chain clearly in the test environment.
At this stage, what remains at the front is still the real-environment acceptance and continuous observation of this version's W8. How to observe heartbeats from outside when the host is completely down, when static thresholds need to be redone, and how unknown states can be reliably verified all still need to be answered by the next round of evidence. Part 12 puts these decisions and outstanding debts together.
That is the overview; the specific mechanisms are left to each article. Every source link in the table of contents is pinned to the same version, and I hope that when I go back to check, what the article talks about and the code I open still match.
---
Code and evidence: opsagent, mcpserver.py and agent/tools.py: entry points and tool boundaries agent/checks.py: deterministic checks and rule summaries agent/schedule.py: run records and conclusion wrap-up core.py / execution.py: approvals, snapshots, and execution records Correspondence between this review and the 52 new regressions Release notes and release pipeline results