Skip to content

v3.6.0

Latest

Choose a tag to compare

@unitares-agents unitares-agents released this 26 Oct 22:09
ef8dc3b

v3.6.0

Breaking

  • Secrets file: public runtime helpers and the BEAM start scripts read a secrets file only when UNITARES_SECRETS_ENV names it, as an absolute path to a readable file. ~/.config/unitares/secrets.env and the legacy ~/.config/cirwel/secrets.env are no longer found by default. Credentials supplied directly through the service environment still work. Upgrade note: a service that relied on the default location starts without its credentials (the lease plane, for example, answers 503) until its job sets UNITARES_SECRETS_ENV. Set it before updating. A job that launches through a secrets wrapper must also name the wrapper in UNITARES_SECRETS_WRAPPER; sentinel-plist.py render refuses a wrapped Sentinel plist that does not, rather than writing the bearer into it. Parity tests cover explicit selection and the refusal to discover unselected files, and fleet-specific bridge deployment tooling now lives outside the public repository (#2925).
  • http route packs: the built-in reference-residents and automation-census pack names are gone. A pack now mounts only when UNITARES_ROUTE_PACKS selects it and the deployment-owned JSON manifest named by UNITARES_ROUTE_PACKS_FILE declares its paths, handlers and methods (docs/operations/resident-roster.md, "Route packs"). Upgrade note: a deployment that set UNITARES_ROUTE_PACKS=reference-residents or =automation-census mounts nothing after this change, and only a log line reports it. Write a manifest for the routes it uses before updating. docs/operations/automation-census-setup.md has the /api/automations entry. Extensions mount after every core route, and a pack whose path and method match a core route, or whose path falls under a core mount such as /mcp, is rejected whole and logged (#2925).
  • exposed deployments: a server configured to listen beyond loopback now refuses to start when an operator credential or continuity secret still has a published default value. Set deployment-owned secrets before updating; loopback-only development retains a warning instead of a refusal (#2957).
  • Interface contract 2.0.0 removes implicit vendor selection for omitted
    delegate_inference.host_id and thorough consult calls. Select a host
    explicitly, configure jobs.consult.thorough.prefer through
    UNITARES_INFERENCE_HOSTS_FILE, or provide one unambiguous eligible host.
    Ambiguous/unconfigured requests return actionable refusals; consultation
    retains caller-permitted local degradation. Explicit pins and councils keep
    their existing authorization and privacy rules. Updated SDKs omit an absent
    host instead of sending the old default. The document schema remains v1;
    delegate_inference’s input digest and the aggregate surface digest change (#2925).
  • get_workspace_health and admin(action='workspace_health') now report
    installation diagnostics without desktop client discovery or assumed
    checkout documents. Default client mappings and server lists are empty;
    configured counts are zero and do not measure liveness. Unperformed
    security, executable-permission, live-server and documentation checks return
    null, with not_assessed or not_requested status where applicable.
    Clients must accept these unknown/unrequested states instead of treating them
    as failure or success. Explicit client and workspace-file checks remain
    Python embedding hooks, not MCP file access; use health_check for running
    server subsystem health. Tool inputs are unchanged for workspace diagnostics (#2925).

Added

  • The launch profiles generator writes dialectic.reviewer: the dialectic reviewer takes only DIALECTIC_SESSION_ID and reads its thesis and evidence from the session, with the server's non-secret reviewer settings, its host list as the server resolves it (inference hosts file included; regenerate after editing that file), the CLIs found and the server's lifetime cap fixed in the profile. The server's reviewer host selection, lifetime cap and governance URL now live in src/dialectic_reviewer_config.py, shared by the server and the generator; the server's behaviour is unchanged. The generator takes server settings from --env-file or the new --launchd-plist, keeping only the names it uses and binding credentials (the MCP bearer, the external host's key) by name only; AGENT_ORCHESTRATOR_PROFILES_SERVER_PLIST passes the plist from the orchestrator's start.sh. No caller sends profile requests yet (inference-hosts-config-v0 step 4b) (#3099).
  • dialectic(action='get') now includes paused_agent_state, the pause evidence the server captured when the session opened, on its fast read as well (its other read path already did). With DIALECTIC_REVIEWER_FROM_SESSION=1, the orchestrated dialectic reviewer takes only DIALECTIC_SESSION_ID from its spawn and reads the thesis, the evidence, the situation and the paused agent from that record, exiting 4 without reviewing when the record does not support a review. Nothing sets the switch yet; it is for the reviewer's launch profile (inference-hosts-config-v0 step 4b) (#3096).
  • install: an opt-in core profile, docker-compose.core.yml, starts only the server and PostgreSQL (Redis and the lease plane off): docker compose -f docker-compose.yml -f docker-compose.core.yml up -d --wait, or COMPOSE_FILE=docker-compose.yml:docker-compose.core.yml in .env so ./scripts/unitares update keeps it. Check-ins, identity resumption by a keyed client_session_id, the knowledge graph and dialectic requests work; state Redis would hold falls back to process memory and is lost on restart, onboard pins do not work (clients pass client_session_id), and surface leases and agent messages are unavailable (install manual §2.4). The default stack is unchanged. The Minimal Core Matrix measures this file itself rather than a CI-only copy, and checks that the update script's stop of the lease plane by name does not bring it back (#3032).
  • dashboard: a new core Timeline tab draws one row per agent across the last 1, 6 or 24 hours from GET /v1/timeline. Marks: check-ins (shape by policy action; hook-recorded ones hollow, with a toggle to hide them), graded outcomes (✓/✕), knowledge writes, declared lineage (the child's row sits under its parent, labelled subagent, successor or continuation from its spawn_reason, as src/identity/lineage_semantics.py classifies it), lifecycle changes, and bands for pauses and dialectic reviews. Selecting a mark shows its record in UTC: for a check-in, its report excerpt when the server returns one (otherwise it says the text was not retained); for an outcome, the confidence it grades against the result. Rows are ordered by most recent event, capped at 25 until "show all", with a filter on label or id; kinds the server truncated are named. The 1h and 6h windows refresh on the live tick; the 24h window loads on demand (#3047).
  • Public surface inventory now includes the Homebrew preview and timestamped Markdown/JSON reports, with source and refresh paths, explicit unknown states for delegated checks, and opt-in deployment observations. The existing weekly Positioning Surfaces workflow retains the report and acceptance evidence (#3065).
  • Add unitares setup --homebrew: inspect native configuration and database readiness, or use --apply to create missing private configuration with a shared lease bearer and optional models disabled. Existing configuration and databases are preserved; an absent migration registry is never treated as proof that a database is empty (#3048).
  • install: the packaged Apple Silicon Homebrew CLI now gives native installs
    the coordinated unitares update lifecycle already expected from the Docker
    path: a read-only cached-tap --check, explicit package refresh, both writers
    stopped and verified, a private custom-format backup verified by pg_restore,
    four-formula pin management, full installed-formula inventory evidence around
    Homebrew dependency resolution, re-execution into the new keg, migrations,
    previous running/stopped-state restoration, identity-count verification,
    deep-health verification for restarted writers (schema verification when
    stopped), and a private journal resumed with --resume. Unfinished journals
    block unitares start; a safe pre-change --abort and a reviewed
    identity-count-mismatch acknowledgement provide explicit recovery paths.
    Verified backups are checksum-indexed, and --cleanup-backups provides a
    dry-run-by-default, journaled retention path that counts only existing,
    reverified dumps, preserves the newest set and every update-journal reference,
    and explicitly reconciles stale missing-file records. UNITARES never deletes
    Homebrew kegs.
    Direct brew services commands bypass the journal guard. Formula hooks still
    never migrate a database. Homebrew is recommended for supported Apple Silicon
    desktop Macs; Docker remains the cross-platform/server/CI path. Stable
    releases now have a protected deterministic archive-to-draft-tap-PR handoff;
    bottle build and publication remain separate operator-gated actions (#3086).
  • The launch profiles generator also writes host.codex and host.antigravity, so every host adapter lane has a profile. For host.codex, the Codex app-server client now runs the codex exec fallback itself (HA_CODEX_EXEC_FALLBACK=1) instead of the raw adapter's shell template, with the prompt on standard input and the lane's administrator-configuration and sandbox-probe checks repeated before exec. The raw adapter path is unchanged, and no caller sends profile requests yet (inference-hosts-config-v0 step 4b) (#3092).
  • The agent orchestrator can spawn named launch profiles (AGENT_ORCHESTRATOR_PROFILES_FILE): a request names a profile and fills only its declared variables, the child environment is built from the profile (non-inherited variables unset, secrets bound to the profile and redacted from output), and per-profile concurrency and hourly limits apply. AGENT_ORCHESTRATOR_RAW_SPAWN defaults to deny whenever a profiles file is configured. Without a profiles file nothing changes (inference-hosts-config-v0 step 4a) (#3090).
  • scripts/ops/orchestrator-profiles.py writes the agent orchestrator's launch profiles file, and the orchestrator's scripts/start.sh runs it before boot when AGENT_ORCHESTRATOR_PROFILES_GENERATE=1 hands it the file AGENT_ORCHESTRATOR_PROFILES_FILE names. Its first profile, host.claude, runs the new claude_cli_client.py: the prompt goes to claude -p on standard input, the flags are fixed, the caller chooses only the model, and the CLI gets an allowlisted environment in an empty directory. No caller sends profile requests yet, so nothing changes (inference-hosts-config-v0 step 4b, first part) (#3091).
  • Public site: a read-only demo page, demo.html, walks through one incident (a claim, the evidence against it, a review challenge, a correction, and a successor's reconstruction) with nothing to install. Its quoted facts, record ids and link names are filled at build time from the accountability-journey test fixture; the build fails on an unfilled placeholder or a fixture link the page never names, and the site redeploys when the fixture changes. The page says plainly that the same rehearsal tied with git plus a structured handoff. Linked from the landing hero and the site nav (#3000).
  • dashboard / REST: new read-only GET /v1/timeline returns every agent's events in one time window, merged and sorted oldest first, with a lookup of the agents they name: check-ins (marked authored_by agent or substrate), graded outcomes (with the reported_confidence they grade), pauses, resumes and archives, knowledge writes, declared lineage, and dialectic sessions. hours (default 6, up to 168), agent_id, kinds, limit per kind (default 500, up to 2000; truncated names each kind that reached it), include_substrate (default on) and include_trajectory (the server's own trajectory_validated re-scores, default off). An unknown parameter or kind is a 400, not a wider read. It is the data source for the core dashboard Timeline tab (#3045).
  • timeline: GET /v1/timeline returns each agent's tags. The live Timeline pane uses them to fold agents tagged autonomous into a collapsed background group. Each row now shows what its agent last said, a "needs your attention" box names agents paused with no later resume or whose latest check-in drew a pause verdict, and one light replaces the status lines (#3078).
  • live portal Timeline: the BEAM dialectic_live portal now has a live Timeline pane backed by the governance WebSocket, with bounded retained state, reconnect/snapshot recovery, attention summaries, and lane-based rendering for agents and their events (#3061, #3076).

Changed

  • check-ins: each sync_state check-in now keeps a bounded excerpt of its report text in its state row (state_json.report_excerpt), so the record keeps what the agent said beside the state it was given. Through v3.5.0 the report text was not kept at all, apart from the 500-character summary an auto-recorded outcome stores when the text matches its completion or failure pattern. The excerpt is the agent's own words, not a verified claim: durable claims still belong in a finding. It is at most 500 characters by default (UNITARES_CHECKIN_EXCERPT_CHARS, capped at 2000; 0 turns it off), whitespace is collapsed, and a longer text is cut at a word boundary with …, so an identifier is not split mid-token (a single word longer than the limit is cut at the limit). Common credential shapes are redacted before storage: provider tokens, Bearer values, name=value pairs whose name looks secret (at any value length; after a bare name: only values of 8 or more characters that carry a digit or run to 20), URL passwords and private-key blocks. This is best effort, not a guarantee. The server instructions, product definition, capabilities page, reviewer guide and architecture doc now say this instead of "not retained". Operator decision, 2026-10-23 (#3049).
  • release: the Promote Release pin job now regenerates COMPATIBILITY.md's plugin and SDK rows and the public site's SDK pins with scripts/ci/compat_component_rows.py, as it already did the published-server row. The SDK is the newest sdk-v* tag in the release, and the job fails if PyPI does not serve it. The plugin is the release entry's plugin-bundle-recut version, and the job fails if that plugin release does not exist. A test now requires a Compatibility policy bullet for VERSION. These were hand edits, and v3.5.0 shipped with all of them still naming plugin v0.4.21 and SDK 0.5.0 (#3044).
  • Drift doctor: the read-only deploy preflights (#3020) run only for services listed in the JSON file named by UNITARES_DEPLOY_PREFLIGHTS, like the drift surfaces in UNITARES_DEPLOY_DRIFT_SURFACES. No built-in service roster ships. governance_bearer.py recognizes a secrets wrapper only through the job's UNITARES_SECRETS_WRAPPER, and reads only the file its UNITARES_SECRETS_ENV names (#2925).
  • Interface contract 1.56.1: Automatic local model selection requires an explicitly configured endpoint; fresh installs stay model-free (#3043).
  • Interface contract 2.1.0: Knowledge details adds bounded response-chain depth, limit, and offset controls with deterministic pagination (#3058).
  • Interface contract 2.3.0: dialectic reads gain an opt-in compact projection, and duplicate-session conflicts return executable recovery guidance without exposing PostgreSQL message bodies (#3094).
  • Interface contract 2.2.0: onboard accepts an optional host_session declaration ({harness, session_id, workspace?}), stored once on a fresh identity; GET /v1/timeline reports each agent's host_session as {harness, session_id, resumable} without the workspace path (portal hub H0) (#3089).
  • The Docker quickstart runs the AGE knowledge backend. docker-compose.yml and .env.example now set UNITARES_KNOWLEDGE_BACKEND=age, so a new install stores the graph over its findings (AUTHORED, RELATED_TO, TAGGED, RESPONDS_TO, SUPERSEDES) and search results carry a superseded_by pointer. An existing quickstart install that ran postgres rebuilds the graph from its rows on the first knowledge-graph access after the switch, including the related links the postgres backend kept only in each finding's related_to list; set UNITARES_KNOWLEDGE_BACKEND=postgres in .env to keep the old behaviour. A native install with the variable unset still resolves to postgres. New docker-compose.full.yml adds semantic search (bge-m3, CPU torch) and hybrid ranking in a locally built image; the reranker and related-finding expansion stay off. The quickstart CI job now proves that stored findings get their graph edges (#2961).
  • release tooling: simplify server releases with scripts/ops/prepare_release.py (make release-prep PART=patch|minor|major), --after-merge operator/agent command cards, and --check-ghcr anonymous GHCR visibility verification; anchor changelog_coverage.py and release_series_drift.py at the release commit with nearest-ancestor tag discovery (git describe --tags --abbrev=0) so post-release commits on master before tagging do not fail Release Seams; run release-seams.yml on release/** branches; include next-command remediation hints in server_release_sync.py and sdk_release_sync.py; and rewrite docs/operations/RELEASE_PROCESS.md as an actor-grouped checklist with release cadence guidance (#3080).
  • server.json description: the MCP registry entry now reads "Accountability infrastructure for long-running AI agents. Shared memory is a provenance graph." It still opens with the product sentence verbatim; the second sentence describes what the AGE backend, now the quickstart default, stores: who wrote each finding, how findings relate, and what replaced what (#2988).
  • dashboard / REST: GET /v1/timeline check-in events now carry state_id and the stored report_excerpt (null where the row has none; agent-authored rows keep one since check-ins began storing it). A row that recorded the check-in's self-report also carries confidence_reported, the confidence the agent supplied (null when it supplied none); rows without that record omit the field. Read-only (#3060).
  • operations: credential resolution, secret-rotation preflights, Sentinel plist rendering, and scheduled deployment checks now share one explicit governance-bearer resolver and fail with the same actionable diagnostics (#3026).
  • provenance and review routing: tool responses expose the recorded caller model provenance, relayed agent PRs receive model-family labels, and automated review skips the PR author's model family without requiring an extra operator cycle grant (#3041, #3042, #3052, #3072, #3073, #3084).
  • runtime dependencies: the BEAM applications move to the current compatible Hex dependency set, including the Phoenix LiveView runtime pin used by dialectic_live (#3013).

Fixed

  • Installation: start, stop, and logs support configured macOS Homebrew services, selected automatically by the packaged CLI or explicitly with --homebrew from source. Startup verifies port ownership and the installed schema before starting writers, reuses existing services, and checks deep health. Stop preserves shared PostgreSQL/Redis services. Native package operations cannot accidentally fall through to Docker update/model commands. Package publication and first-time database/configuration setup remain separate (#3037).
  • Installation: the managed Docker launcher reports native/Homebrew installation metadata and requires an explicit --separate choice with alternate, persisted host ports before creating a Docker stack alongside configured native services. CLI-only packages and reuse of an existing validated managed stack remain supported. Foreign or unlabelled Docker containers are refused before cloning. unitares status identifies the endpoint whose health/version it reports, with URL credentials omitted. Installation-choice guidance distinguishes packaging from optional capabilities and records the remaining Homebrew publication/lifecycle gates (#3030).
  • Stored findings link to similar ones again. The parameter middleware hands the store handler auto_link_related=None when a caller leaves the flag out, and the handler read that None as "off", so validated stores skipped linking: in the week of 2026-10-19, 2 of 269 new findings on the reference deployment got related links. An unsent or null flag now means on, on single and batch stores. An explicit false still turns linking off; on the knowledge router the schema reads any string other than true, 1 or yes (case-insensitive) as false, and a batch item's string is read as false for false, 0 or no (#3035).
  • The lease plane warns at boot when its bearer token is the published compose default. It names LEASE_PLANE_BEARER_TOKEN, never the value, and adds a second warning when the listener binds beyond loopback outside a container, whether UNITARES_LEASE_PLANE_HTTP_IP or config :lease_plane, http_ip: set the address. It does not refuse: inside the compose container it binds every interface by design, so the bind address cannot tell an exposed install from the quickstart. The digest is pinned to docker-compose.yml and to the governance server's copy by tests (#2983).
  • Native installation now has real PostgreSQL acceptance coverage for fresh bootstrap, interruption after migration 62, migration recovery, checksum provenance and server readiness. The required CI test check and release promotion both depend on acceptance passing against the candidate archive (#3034).
  • release tooling: scripts/dev/changelog_assemble.py now appends the introducing PR number (#NNNN) from first-parent git history when folding docs/changelog.d/ fragments; scripts/ci/changelog_coverage.py classifies chore(deps) / chore(deps-dev) alongside build(deps) and includes Elixir mix.exs / mix.lock and re-stamped skills/.attestations/*.json in its inert-bump filter; scripts/ci/release_series_drift.py excludes skills/.attestations/ (matching the scripts/dev/skills_manifest.py bundle fingerprint) so attestation-only commits do not require a unitares-governance-plugin release recut; and scripts/ops/prepare_release.py snapshots and restores symlinks (including dangling tracked symlinks and symlinked fragments) on --dry-run or rollback (#3088).
  • merge queue: preserve a signed agent PR's exact self-reported model as a [model:…] token in the landed squash subject and carry its provenance signature into the squash body, refreshing an armed subject after title or signature edits so attribution survives branch deletion without replacing GitHub's commit body or co-author trailers (#3067, #3074).
  • dialectic_live & install docs: the live Timeline pane now follows TimelineFeed's claimed mark-to-row pairing when a re-read confirms rapid check-ins, preserves resumed_at pause closure across stale/same-snapshot reloads when the provisional cap evicted a resume, and closes a held pause at the retained resume before holding a subsequent pause (#3077); the root README.md and docs/manual/02-install.md now explain the one-stack-per-machine docker compose down requirement (stopped containers still hold fixed container_name values, and restart: unless-stopped revives un-stopped containers across daemon restarts), point to scripts/install/launch.sh for ownership checks, and note the harmless shallow-tag clone notice (#3031) (#3079).
  • identity recovery: refusal text no longer suggests that a continuity token alone can recover an identity; recovery requires the bound session proof expected by the active transport (#2996).
  • leases and queue automation: a crashed lease-plane request releases any uncommitted pre-admission file-write lease, and the PR babysitter no longer marks the same merge retry on every poll when GitHub never started the rerun (#3039, #3040).

Security

  • Session binding accepts ownership only from the fail-closed transport-source allowlist, rather than trusting an unexpected source label (#3038).
  • OAuth enrollment is closed by default outside its bounded flow, provider state is authenticated and expiry-bound, and callback persistence no longer accepts replayed or cross-provider state (#3056).
  • Authenticated HTTP writes are bound to the caller's session and request/stream bodies have explicit size limits; the dashboard no longer places operator credentials in URLs (#3057, #3069).
  • Lease-plane mutations require principal-bound proofs, including dialectic and effect-custody paths; compatibility fallbacks cannot turn an unproven mutation into an authorized one (#3059).

Documentation

  • Aligned docs/CAPABILITIES_AND_DEPLOYMENT.md, docs/REVIEWER_GUIDE.md, and docs/manual/03-running-the-server.md with the Compose default (UNITARES_KNOWLEDGE_BACKEND=age for replacement-link edges, full-text search until docker-compose.full.yml adds embeddings), clarified identity vs. transport auth in docs/manual/06-operating.md, refreshed docs/ROADMAP.md recently-shipped items, and surfaced ./scripts/unitares update, per-file edit leases, and hosted MCP connectors in README.md and docs/manual/04-integrating-agents.md (#3098).
  • docs: COMPATIBILITY.md now names the plugin and SDK that shipped with v3.5.0 (plugin v0.4.22, unitares-sdk 0.6.0; the rows still named v0.4.21 and 0.5.0) and gains a v3.5.0 compatibility entry: no callable removed, no migration, the UNITARES_MODEL_ID default removed, unproven knowledge writers refused (GHSA-4m7p-grgw-vgmh), the related-expansion flag renamed, and the two search changes without a setting. The public site's SDK link and install command move to 0.6.0 too. The README's install sentence and the docs index point at the core profile and the installation choice (#3044).
  • Clarified README.md install guidance by splitting post-install endpoint and single-stack rules into scannable bullets and distinguishing client-side agent/model compatibility (any MCP/HTTP client, unitares-governance-plugin for Claude Code and Codex, unitares-host-adapter, npx mcp-remote, Python SDK, resident runtime) from optional server-side models (./scripts/unitares model for Ollama, vLLM, LM Studio, and llama.cpp, plus the agent orchestrator for cross-family CLI review) (#3093).
  • Product and installation docs now lead with UNITARES' five operational questions and a concrete supersession example, distinguish interrupted bootstrap recovery from a fresh install, consolidate the Compose security reference and mechanism descriptions, trim duplicated workflow/install prose, and index previously orphaned analysis and operations records (#2997, #3046, #3062, #3064, #3070, #3071).

Compatibility

  • INTERFACE_CONTRACT_VERSION: 2.3.0
  • unitares-sdk: 0.6.1

Migrations

None.

Rollback

  • Return to v3.5.0 (docker compose image tags and git checkout at v3.5.0).

Model: gpt-daybreak-blue-latest
UNITARES agent: f5306a3f-4f2a-4a10-9a5f-3df3d3708ce3