Review 1: Infrastructure and rules

Which AGENTS.md rule is protected by a test, and which depends only on someone reading it? Updated on 24 October 2026.

Scope and method

Review of version 9.11.0 on the dev branch (commit 170ee03), written from scratch against the current code. Since the previous review (9.2.3, commit 924aef6) there are 238 commits and 39 closed milestones (M5 to M43). It follows the docs/audits/AuditFull.md procedure.

The criterion is the 9.0.0 lesson: a rule only counts as met if a test fails when it is broken. Each statement shows where it comes from:

  • [test] an automated test protects it;
  • [cmd] a reproducible command checks it today, but nothing stops it from breaking;
  • [reading] code review, with no test or command;
  • [no test] a written rule that no test watches.

1. Baseline

CommandResult
npm run test:all702 cases: 623 from node --test and 79 from unittest; all passing and 1 skipped (Job Object, Windows only)
npm run test:unit180/180
npm run test:integration273/273
npm run test:infrastructure28/28 in Node and 79 in Python across 12 files (1 skipped)
npm run test:architecture39/39 across 14 files
npm run test:browser103/103 in headless Chromium
npm run buildNo differences in zerochat.py, py/dd-managed-services.py or the js/vendor/ bundles

Size: 64 JavaScript modules in js/ (27,963 lines) and 12 built-in tools in js/tools/builtin/ (970 lines); 13 Python modules in py/ (4,909 lines); 14 style sheets (8,367 lines); 834 translation keys per language; 3 third-party bundles in js/vendor/. [cmd]

The suite has fewer cases than in 9.2.3 (864) while covering more features: milestones M24, M32 and M41 removed trivial, duplicate or absence tests and left each behavior tested once, at the lowest level that observes it.

2. Rules and tests

Each row is a rule from AGENTS.md (by section) or from a subsystem README, with the test that protects it continuously.

RuleProtectionState today
§2 Code quality
One source per decision, and tests that import that source[test] for the JS–Python contracts: tests/fixtures/mcp/ (service statuses, engines, browsers, public tool names) is checked by both the Node and the Python tests; elsewhere, [reading]Met
Dependencies through resolveDep or the UMD factory[test] test_code_hygiene.jsMet
Separate domain, persistence, adapters and presentation[test] test_persistence_boundary.js and test_state_contract.js; model, view and controller zones in the dialogs (M38), [reading]Met
Explicit errors[test] test_error_handling.js (no empty catch) and test_backend_hygiene.js (no silent catch-alls in py/)Met
No speculative or backward-compatibility code[no test] in general; partial [test]: test_code_hygiene.js catches functions the app never runsManual review of each diff
Remove what becomes orphaned after a removal[test] test_code_hygiene.js (functions and variables), test_i18n_literals.js (unused keys), test_icon_sprite.js (symbols and glyphs), test_style_hygiene.js (selectors and tokens)Met
No production code used only by tests[test] test_code_hygiene.jsMet
§3 Security
External data kept out of innerHTML, closed list of uses[test] test_html_sinks.js (93 uses in 24 files) and injection tests (browser_tool_cards.test.js)Met
Commands, sandbox and sensitive data with boundary and authorization tests[test] py/test_run_command.py, test_tool_security.js, browser_sandbox_security.test.js, py/test_login.pyMet
Mandatory session token and listening on 127.0.0.1[test] test_local_server.js, py/test_token.py, py/test_network.pyMet
§4 Tests
Each behavior tested once, at the lowest level[reading] reviews in M32 and M41Met after the M41 pruning
Every cap tested at N and N+1 with its notice[test] cap by cap (test_office_parser.js, test_local_server.js with the 1 MiB body and the 4096-byte body without a token, py/test_login.py with the attempts); [no test] that every new cap gets oneMet for the reviewed caps
§5 Git and versions
Versions only through npm run bump[test] test_versioning.jsMet
Promotion to master only from dev, with a bump and a green suite[no test]: it is a procedureFollowed for every version since 9.2.3
§6 Completion
Spanish and English in sync in the interface and help[test] test_i18n.js (same keys in both languages) and test_help_pages.js (translation, language and cross link of every page)Met: 834/834 keys
sitemap.xml regenerated when pages are added or removed[test] test_help_pages.jsMet: 50 pages
Backend rebuilt after changing py/[test] test_backend_hygiene.js (zerochat.py equals the concatenation of py/)Met
Project memory (.zerochat/) up to date[no test]; task.py updates it atomicallyMet in M42
Subsystem READMEs
Events bound once in mount() and ChatDialogs dialogs always awaited[test] test_ui_listeners.js and test_code_hygiene.jsMet
Messages injected for the model in English and visible text through ChatI18n[test] test_english_literals.js and test_i18n_literals.jsMet
Colors in tokens.css, no fallback in var(), hide with hidden[test] test_style_hygiene.jsMet
No emojis; icons from the sprite and ChatIcons[test] test_icon_sprite.jsMet
ChatState only through its mutators[test] test_state_contract.jsMet
Only startup libraries load with the page and go into the precache[test] test_vendor_loading.jsMet
System prompt order for the context cache[test] test_context_cache_architecture.jsMet
Providers extend BaseProviderAdapter[test] test_providers.jsMet
Public pages with a unique title and description[test] test_help_pages.jsMet
Backend: zz-main.py last and tool definitions paired with handlers[test] test_backend_hygiene.jsMet
The wheel only contains the executable[test] tests/packaging_smoke_test.py in the releaseMet in 9.11.0

Count: of 29 rules, 23 are protected by tests, 2 partly (one source per decision, caps at the boundary), 1 relies on reading the test reviews, and 3 have no automated test: the absence of speculative or compatibility code, promotion to master, and project memory. The last two are procedures; the first is only watched by reviewing diffs.

3. Local server security

  • Mandatory daily token on every request, in headers only. It expires at the day change even with the server running: the old one gets a 401 and a new one is generated. [test] test_local_server.js, py/test_token.py
  • Sign-in with the system account: without a token only sign-in is served, with a body of at most 4096 bytes. The password is checked with PAM (Linux and macOS) or LogonUserW (Windows) and never stored. After 5 failures in a row, attempts are rejected for 15 minutes without checking them. [test] py/test_login.py, test_local_server.js
  • No trust in the reverse proxy: the header the proxy routes by grants no access; behind it the token and sign-in are still required. [test] test_local_server.js and browser_connection.test.js
  • POST-only API over JSON-RPC, with CORS origins from a closed list; GET only serves the interface in development. [test] test_local_server.js
  • Each tool call signed with HMAC, a single-use nonce and a 60 s window that tolerates ±30 s of clock skew. [test] test_local_server.js
  • Bounded HTTP body of 1 MiB, except the --prompt --export export (64 MB). [test] test_local_server.js
  • Loopback listening by default, with a warning for any other host. [test] py/test_network.py
  • Server web transport with SSRF protection on the requested URL and on every redirect, and a bounded body. [test] py/test_network.py
  • Bounded commands: timeout, termination of the process group and cleanup of orphans from earlier sessions; on Windows, a Job Object. [test] py/test_run_command.py (the Job Object test only runs on Windows)
  • JavaScript sandbox in an opaque-origin iframe without network; the help warns that it is not a security barrier. [test] browser_sandbox_security.test.js
  • Tool permissions: ask, workspace_trust and allow_all policies, directory rules and traversal checks. [test] test_tool_security.js

4. Accepted risks

RiskSituation
Shared origin albalday.github.ioOther pages on the same origin could read the session cookie, localStorage and IndexedDB. Accepted while the origin only publishes the project's own pages.
Proxy key unencrypted in the browserStored in the local configuration (M42-D17): it only opens the proxy door, and the server still requires the daily token and sign-in. It is only sent to the configured URL.
System account lockoutIf the computer locks accounts after failed attempts (for example, pam_faillock) with a limit below 5, failures from the web could lock the account. The help explains how to avoid it.
Non-local --hostExposes command execution to the network without encryption; startup warns. For remote access, the SSH tunnel and the HTTPS reverse proxy are documented.
WebLLM from a CDNThe engine is imported from cdn.jsdelivr.net when this provider is used.
Web toolsDuckDuckGo search and page reading go through the local server or, without it, through Jina Reader.
Remote providersWith a cloud API, messages go to the configured provider; with a local LLM they do not.

5. Findings

IdFindingRisk and priorityProposal
F-01The Windows-only paths (LogonUserW for sign-in and the Job Object for commands) run in no test: CI only uses ubuntu-latest and the Job Object test is skipped.Stability; mediumA CI job on windows-latest for the Python infrastructure tests, with a LogonUserW case that rejects a wrong password.
F-02A successful PAM sign-in has no automated test: the tests mock the check and, by hand, only the rejection of a nonexistent user and the project owner's real use have been tried.Security; lowIn CI, a temporary user with a known password that runs the check on its own account.
F-03js/app.js (1,020 lines) and js/mcp.js (1,058) keep growing with coordination: the backend session (startup fragment, connection and sign-in) lives in app.js.Technical debt; lowMove the backend session to its own domain module the next time it changes.

Resolved in this version: a browser startup test assumed no real zerochat was listening on 127.0.0.1:6388; with one running, its 401 opened the sign-in dialog. The test now blocks that address.

6. Verdict

Solid infrastructure, with the network surface redesigned without losing protection. Since 9.2.3 the local server has moved to a POST-only transport, a daily token that expires while running, sign-in with the system account and remote access by tunnel or reverse proxy; each of those changes arrived with its tests. The suite is smaller and faster than in 9.2.3 and covers more features. What remains is Windows coverage (F-01), which today is only tested by hand.

Authorship and verification environment

  • Review and writing: Claude Opus 5.5 in Claude Code, from the project owner's instructions and the docs/audits/AuditFull.md procedure.
  • Environment: Linux x86_64, Node.js 24.21.0, Python 3.14.8, Playwright 1.62.1 with headless Chromium.
  • Reproduction: the baseline commands.