Skip to content

release: actor-bound application runtime v0.7.0-rc.30 - #348

Merged
Shooksie merged 107 commits into
mainfrom
feat/task-1014-application-chat-controls
Jul 26, 2026
Merged

release: actor-bound application runtime v0.7.0-rc.30#348
Shooksie merged 107 commits into
mainfrom
feat/task-1014-application-chat-controls

Conversation

@Shooksie

Copy link
Copy Markdown
Contributor

Summary

  • publish the actor-bound application chat surface used by LaunchApp Portal
  • add durable, idempotent, fenced shared-conversation operations backed by the v2 subject actor contract
  • consolidate canonical Protocol crates at immutable v0.7.0-rc.12 and bump the CLI to v0.7.0-rc.30
  • retain the accumulated v0.7 runtime, workflow, environment, daemon, OAuth, and plugin-host integration work on this release branch

Release contract

  • CLI version: 0.7.0-rc.30
  • Protocol source: v0.7.0-rc.12 at ad39f6aba986a2ccd0adcd26c829f6c8ff0e956c
  • Postgres application authority: v0.2.2
  • release tag will be created manually at the exact merge commit after post-merge CI passes

Verification

  • cargo fmt --all -- --check
  • cargo check --workspace --all-targets --locked --offline
  • cargo clippy --workspace --all-targets --locked --offline -- -D warnings
  • cargo test --workspace --locked --offline (passes with loopback access; sandbox-only OAuth callback binds were separately identified)

Tracks TASK-970, TASK-971, TASK-998, TASK-1000, TASK-1001, and TASK-1014.

Shooksie and others added 30 commits July 1, 2026 19:08
…light; git-dep animus-plugin-protocol; drop in-tree copy + protocol_drift test

Kernel-side consumption of the multi-kind protocol change. Protocol dep currently
points at the feat branch; will repoint to animus-protocol main on 0.7 integration.
…ce) — multi-kind on release/0.7

Repoint all animus-protocol deps that touch animus_actor to v0.7.0-rc.1 (= v0.1.26
+ additive multi-kind), unifying the actor rev (no duplicate-crate E0308). Keep the
pre-actor old-wire aliases (subject-protocol-v05 + queue-protocol) at v0.5.10 as a
distinct rev. In-tree animus-plugin-protocol dropped earlier; kernel now consumes
plugin_kinds/serves_kind. Workspace builds; 0.6.33 fixes + actor foundation intact.
…lities

InitializeResult/PluginInfo/PluginManifest test constructors gain the new
multi-kind fields. Workspace builds; plugin-host 190+ tests + preflight 23 green.
One plugin process can now serve multiple roles. Introduce a process-global
ResidentHostRegistry keyed by (canonical binary path, mtime, spawn-context
fingerprint) that unifies the previously per-role host caches so a binary
resolved by several resident-style roles is spawned + handshaked once and
shared. LRU-bounded with lease-pinned eviction (a host with an outstanding
lease is never reaped mid-RPC / mid-watch).

Rewire the three resident spawn sites onto the shared registry:
- subject_backend (subject_router LazyHosts): host lookup/spawn moved into the
  registry; per-router cache/LRU removed. Each subject spec fingerprints its
  own spawn context (manifest-only env + project-root cwd), keeping it distinct
  from the full-env config/journal slot (preserves the v0.4.x secret trust
  boundary).
- config_source (config_source_client): resident cache replaced by the
  registry; retry/backoff + handshake-error cleanup preserved; lease held
  across the RPC.
- workflow_journal (journal_client): same.

The spawn-context fingerprint is load-bearing: config_source and
workflow_journal (both forward the full parent env) share one process, while a
subject_backend spawn of the same binary stays separate. In-place upgrades
(new mtime) reap the stale-mtime process. Legacy ANIMUS_SUBJECT_HOST_CACHE_MAX
honored as a fallback to the new ANIMUS_RESIDENT_HOST_CACHE_MAX.

Tests: registry cross-role sharing (one spawn/handshake, shared process +
distinct-context isolation), LRU skips leased hosts, stale-mtime reap on
upgrade; existing subject_router lazy tests ported to the shared registry.
…id (TASK-158)

resolve_auto now prefers ANIMUS_SECRET_KEY / key_file (user-key), then
ANIMUS_SECRET_PASSPHRASE (passphrase), and only falls back to device-id
on interactive local hosts (with a one-time warning). On server/headless
hosts (ANIMUS_SECRET=1 or no TTY on any std stream) the device-id fallback
is a hard error, since it is decryptable by any local user.

Also hardens the device store: temp files are created O_EXCL + 0600 with
collision retry (no world-readable window), and dir chmod failures propagate
instead of being swallowed.
…OFU (TASK-159)

Make the plugin signature policy context-aware and stop animus install from
silently downgrading trust on hostile repos.

- effective_policy_mode now resolves in precedence order: explicit per-call
  flag > ANIMUS_PLUGIN_SIGNATURE_POLICY env > plugins.signature_policy global
  config > Warn default. Local default is unchanged (Warn) so the fix is inert
  until a host opts into strict. resolve_cli_signature_policy returns Option so
  a flag-less plugin install also inherits env/config.
- Publisher TOFU fails closed off a TTY: enforce_org_trust no longer lets
  yes/force auto-trust an unknown org in a non-interactive/server context, and
  the release-source gate now runs BEFORE download + --manifest probe so no
  untrusted binary is fetched or executed before rejection. animus install and
  the MCP install tool gain --allow-org / allow_org escape hatches; locked
  installs thread the operator allowlist through.
- Cosign preflight: strict + missing cosign fails fast with an actionable
  error before any download/probe instead of a confusing per-plugin block.
- Pure, injectable resolver seams (resolve_signature_policy_layers,
  parse_signature_policy_layer, resolve_org_trust_gate, cosign_preflight_error,
  enforce_org_trust_with) with unit tests for precedence, env/config layers,
  fail-closed TOFU, and the cosign preflight message.
- Docs: ANIMUS_PLUGIN_SIGNATURE_POLICY + plugins.signature_policy +
  ANIMUS_SERVER and the TOFU fail-closed behavior in cli/index.md and
  configuration.md.
… slug-in-flavor

Fixes two codex findings on the discovery pre-probe scope gate:

P1 (project-registry binary: vector): under an unrestricted (`all`) scope
the effective admit set is everything, so the pre-probe scope gate never
fired and `<project>/.animus/plugins.yaml` `binary:` entries were probed
(EXECUTED) even on the server-safe path. A cloned hostile repo could commit
a plugins.yaml pointing at `./.animus/plugins/evil` and get it run during
discovery. The project registry is repo-shippable (UNTRUSTED), so it is now
gated behind `probe_project_local_plugins` exactly like the project-local
directory scan — the server-safe `discover_plugins` leaves the whole
project-local tier OFF and never executes a repo-shipped binary.

P2 (renamed installed plugins): the pre-probe gate skipped any candidate
whose filename slug was outside the flavor admit set, so a plugin installed
with `--name custom` under a flavor-required manifest name was dropped
before `PluginScope::admits`'s post-probe manifest-name fallback could match
it. The pre-probe skip now fires ONLY on UNTRUSTED (`DiscoverySource::
ProjectLocal`) candidates; TRUSTED installed candidates (global registry,
global install dir, $PATH) are always probed and filtered post-probe, so
renamed required plugins keep discovering.

Adds `DiscoverySource::is_untrusted()`; new regression tests: hostile
project registry not executed on the server-safe path under `all` scope,
and a renamed trusted install still probed+admitted under flavor-only scope.
…nal preflight role (v0.7 foundation)

- repoint animus-protocol crates v0.7.0-rc.1 -> rc.2 (old-wire v0.5.10/v0.5.5 aliases untouched)
- add animus-environment-protocol dep; re-export as orchestrator_core::environment (client-types seam for follow-on EnvironmentClient)
- surface workspaces / environment_routing + per-workflow/phase environment/workspace through WorkflowConfig compilation (auto via config-protocol re-export); YAML roundtrip test
- add pure resolve_environment() resolver in orchestrator-config with full precedence unit tests (phase > rule > workflow > default > none)
- register optional RequiredRole::Environment + is_environment() helper (reported by preflight/plugin status, never required)
- docs: workflow-yaml #workspaces/#environment_routing/#environment + configuration.md optional-role note
… + streaming buffer/starvation fixes (v0.7)

- ping_is_dead: interpret liveness by transport state, not classify() retry
  semantics — any RPC error (structured, method-not-supported) or a Timeout is
  ALIVE; only ConnectionLost/ProcessExited (closed transport) is dead. Prevents
  reaping a busy shared host out from under a concurrent in-flight exec.
- exec_stream: honor the manifest notification_buffer_size in both the spawn
  options and the resident-host fingerprint (environment is the streaming role);
  fair (non-biased) select so a concurrent handle's output flood cannot starve
  the request future; bound the post-response drain to the count buffered at
  response time so cleanup cannot spin on a neighbor's stream.
- Fix the two failing ping tests (liveness interpretation + dead-host fake reads
  the follow-up $/ping before closing so the handshake completes cleanly).
…to environment plugin

ensure_execution_cwd (BuiltinTaskSubjectAdapter) can delegate ONLY the
`git worktree add` step to an installed `environment` plugin, keeping
branch/path/base-ref selection, existing-worktree reuse, task-record
persistence, and .mcp.json sync in-tree. Gated behind
ANIMUS_ENVIRONMENT_DELEGATE so the DEFAULT stays the byte-for-byte
in-tree path; a missing/unresolvable/broken/wrong-path/wrong-branch
plugin falls back to the in-tree add. Cleanup on a delegation miss is
restricted to the kernel-owned managed target (never a plugin-supplied
path). Adds a both-paths test via an injectable prepare seam.
…-kind + host-sharing + security trio + environment kernel)

Cut from release/0.7 off v0.6.33 + actor foundation. Crates bumped to 0.7.0-rc.1;
animus-protocol pinned v0.7.0-rc.2. RC for staged portal verification before v0.7.0 final.
… plugins

When an agent run resolves to a NON-LOCAL environment (config
environment_routing or the ANIMUS_ENVIRONMENT_EXEC override), the harness
command that would have been spawned on the host is prepared + executed
inside that environment via EnvironmentClient (prepare -> exec_stream ->
teardown), with streamed stdout/stderr surfaced through the same
SessionEvent channel the local provider path uses. Default resolution is
None: the local spawn path is byte-for-byte unchanged.

- gate: env-var kill-switch/override + environment_routing (kind=None,
  harness=tool); 'local'/'worktree' ids stay on the host path
- launch: contract cli.launch wins, else the built-in launch table;
  machine-output flags stripped (plain-text exec transport), permission
  mode + codex reasoning effort re-applied, system prompt mapped per tool
  (claude flag / prompt merge / fail-closed), prompt_via_stdin honored
- spec: project root as primary repo pinned to the current checkout ref;
  routing-rule spec overrides (image/resources/env/opaque metadata) merged
- failure posture: no silent local fallback once an env is resolved;
  kernel-mediated approvals fail closed; missing exit code and timeouts
  are terminal errors; buffered-exec fallback only on METHOD_NOT_SUPPORTED
- teardown always runs once a handle exists (success or failure)

Workflow-phase exec stays a runner-side follow-up: phase execution
machinery is plugin-private in the out-of-tree workflow_runner.
- Repin animus-protocol git deps v0.7.0-rc.2 -> v0.7.0-rc.3 (adds
  WorkflowConfigSource::Plugin).
- config_source label fix: the plugin runtime load path
  (load_workflow_config_with_metadata) now stamps source=Plugin instead of
  the misleading source=Yaml, so team_config_get reports the real
  config_source (postgres) rather than 'yaml'. Cosmetic; no behavior change.
  The genuine YAML-library compile path keeps source=Yaml.
- Carries the TASK-166 Phase 2 env-routed agent exec seam already on
  release/0.7 (gated ANIMUS_ENVIRONMENT_EXEC, default off).
- orchestrator-cli 0.7.0-rc.1 -> 0.7.0-rc.2. Fixes TASK-177.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… health last_error clear (#298)

Three changes on the v0.7 plugin/discovery path.

1. DiscoverySource::DbRegistry tier (TASK-194)
Adds an opt-in discovery tier that resolves the desired plugin set from the
Postgres plugin_registry (served by the animus-postgres BaaS) against the
binaries present on the volume. New db_registry module defines the stable
in-kernel contract: DbRegistryEntry {name, version, source, sha256, target,
enabled, scope} plus a PluginRegistrySource trait (the read seam) and a
StaticRegistrySource stub for tests and pre-schema wiring. orchestrator-plugin-host
takes no Postgres dependency; the SQL lives behind the trait in a daemon-side
adapter to be added once the BaaS schema is finalized.

Bootstrap paradox is respected: the tier is off until a source is wired via
PluginDiscovery::with_db_registry, which the daemon only calls after the
bootstrap DB-backend plugin is up. The tier slots after the explicit/project
registry tiers (a hand-pinned config entry still wins) and before the
unconditional global-dir scan. Enabled rows whose binary is missing surface a
warning; a registry read error degrades to a single warning instead of sinking
discovery.

2. noarch / any install target (TASK-200 sub-item)
Release-asset resolution now falls back to a platform-independent
<name>-noarch.tar.gz (or -any) asset when no triple-specific asset exists, so a
JS-bundle plugin publishes ONE asset instead of duplicating bytes under every
triple. A triple-specific asset always wins; noarch is strictly a fallback.
target_triple_from_asset_name and the SHA256SUMS parser recognize the noarch
archive under a synthetic noarch target so the integrity claim keys consistently.

3. Health last_error clear (cosmetic)
PluginStatusRegistry::record_rpc now clears last_error on a successful RPC
round-trip. A transient connection-lost captured during the ~4s boot window (e.g.
subject_backend unroutable) latched forever and kept daemon health reading as
degraded long after the subject router resolved; a successful round-trip is
definitive proof the handshake completed and supersedes the stale error.

Unit tests added for all three. cargo build + test + clippy pass for
orchestrator-plugin-host, orchestrator-cli, orchestrator-daemon-runtime.
…ervability (#299)

Part of REQUIREMENT-024 (platform correctness). Lands the in-tree slices of
three linked tasks; cross-repo remainders (animus-protocol wire types and the
animus-workflow-runner-default plugin) are called out as deferred.

TASK-201 subjectless / ad-hoc runs:
- animus queue enqueue --adhoc dispatches a run with NO bound subject
  (requires --workflow-ref). The wire SubjectRef is non-optional, so a
  subjectless run is represented as a custom-kind subject with a unique
  adhoc:<nanos> id, keeping each run's queue subject_key distinct so a burst
  of subjectless runs (relate on repo events) is never deduped into one.
- Regression test proving the run-loop binds an ad-hoc subject via the
  built-in custom adapter instead of dying with no subject adapter registered.

TASK-202 de-privilege task/requirement (also closes TASK-185 mark-running):
- subject get/update/status/delete now resolve the kind from a kind-qualified
  id (transcript:TRANSCRIPT-001 to kind transcript) before any config default,
  so a qualified id never falls through to the task-favoring default.
- default_subject_repo_for_kind generalized to the uniform
  animus-subject-<kind> convention (sqlite/markdown/linear resolve too), no
  longer only task/requirement.
- Preflight auto-install for at_least_one_subject_backend now uses a
  kind-agnostic default_subject_backend_repo starter instead of the task
  backend, so no code path privileges task/requirement. Preflight already
  requires only AtLeastOneSubjectBackend, so a deployment with neither
  installed still boots.

TASK-203 command-phase observability:
- Journal wire mapping now surfaces phase_failed events in journal_events
  (previously dropped as unmapped). It folds into PhaseCompleted with an
  explicit failed status; the command exit-code + stderr snippet ride along
  in the event detail so failures are visible in the event stream, not only
  in the checkpoint snapshot decision history.

Deferred (cross-repo, out of this worktree):
- True Option-subject end to end and the command-phase fail-fast on an empty
  or unresolved template both live in animus-protocol wire types and the
  animus-workflow-runner-default plugin.
- Emitting phase_failed for a non-zero command exit (the runner currently
  emits PhaseCompleted+rework) is a runner-plugin change; the in-tree journal
  mapping is ready to surface it once emitted.

Tests, clippy, and fmt pass on orchestrator-core and orchestrator-cli.
#300)

Re-pin the animus-protocol git dep to v0.7.0-rc.4 (Cargo.toml + Cargo.lock),
which makes the wire subject optional (Option<SubjectRef>) on SubjectDispatch,
WorkflowRunInput, and OrchestratorWorkflow. With that, replace PR 299's
custom-kind adhoc:<nanos> sentinel with a genuinely absent subject.

- ops_queue: adhoc_subject_dispatch (built a unique custom sentinel) is
  replaced by subjectless_dispatch, which builds SubjectDispatch::subjectless
  (subject = None). Its PR 299 tests now assert true-None behavior: no subject,
  no subject id/kind, and no subject_key.
- Run loop: the None subject is threaded end-to-end. Bootstrap and phase-success
  advance bind a None subject context (no subject resolution, subject template
  vars simply absent) instead of a sentinel id. The runner command builder emits
  no subject selector for a subjectless dispatch; reattach builds a subjectless
  execute request.
- Adapt every Option<SubjectRef> call site across orchestrator-core /
  orchestrator-cli / orchestrator-daemon-runtime: subject_id / subject_kind /
  subject_key accessors now return Option; task_id / requirement_id /
  schedule_id thread through the Option; journal/index/reconciliation reads fall
  back cleanly when the subject is absent.
- Daemon within-batch queue dedup is now correlated by dispatch INDEX (via an
  ordered spawn-outcome vector) instead of subject_key, so subjectless entries
  (which have no key) reconcile correctly.

Legacy queue boundary: the queue plugin RPC stays pinned to the v0.5 line for
deployed-plugin wire compatibility, and that SubjectDispatch still requires a
subject. queue enqueue --adhoc therefore now fails fast with a clear message
(dispatch subjectless runs directly); completing subjectless enqueue needs the
queue protocol to take up the optional-subject 0.7 line. The direct dispatch /
control path already carries subjectless runs end-to-end.

Incidental base fix (release/0.7 did not compile before this change due to a
merge gap between PR 298 and PR 299): thread the plugin scope through
discover_db_registry into resolve_manifests, handle the resulting
SkippedOutOfScope outcome, and include the probe_project_local_plugins field in
the manual PluginDiscovery Debug impl.

cargo build -p orchestrator-core -p orchestrator-cli, workspace clippy, and fmt
are clean. Two pre-existing decision-gate test failures (unrelated to subjects;
present on the pristine base) remain.
…rm kinds), DbRegistry discovery tier, daemon-health last_error clear

Cut from release/0.7: TASK-201/202/203 (dispatch), TASK-204 (Option SubjectRef, protocol rc.4), TASK-194 (DbRegistry discovery), TASK-200 noarch, plus the daemon-health last_error clear. Protocol pinned at v0.7.0-rc.4.
…ble custom verdict keys (TASK-207) (#301)

* TASK-210: fix latent decision_gate resume-guard tests on release/0.7

Two decision_gate tests failed on a pristine release/0.7 (hidden until PR
300 made the crate compile):
- workflow_yaml_phase_definition_with_decision_contract_blocks
- workflow_on_verdict_routing_blocks_resume_advance

Root cause is incomplete test fixtures, not a gating-logic bug. After the
config_source plugin extraction the kernel sources config through the full
validate path:
- Test 1 omitted tools_allowlist, so load_agent_runtime_config failed
  validation (tools_allowlist must include at least one non-empty command)
  and _or_default fell back to the builtin config with no security-audit
  phase, hiding its decision_contract. The workflow config itself loaded
  fine with the phase_definition present.
- Test 2 referenced phase research without defining it, so workflow
  validation rejected gated-research (references unknown phase research)
  and the config fell back to the builtin base with empty routing.

Fix the fixtures: add tools_allowlist to both, and define the research
phase (gate-free) in test 2 so validation passes. The resume gate then
correctly honors the YAML-supplied decision_contract and on_verdict
routing. All 7 decision_gate tests pass.

* TASK-207: extensible decision keys + conditional on_verdict routing

Let an agent or command phase emit a custom (non-builtin) verdict key that
the workflow executor routes through the per-phase on_verdict map to any
target phase, forward or backward, with the existing loop/attempt guards
intact. Built-in advance/rework/skip/fail behavior is unchanged.

Carrier: PhaseDecision gains verdict_key (added in animus-protocol
v0.7.0-rc.5-dev.decision-keys, re-pinned here). A non-builtin verdict string
now parses to verdict = Unknown with the raw key on verdict_key instead of
being dropped, in both producers:
- agent structured output (payload_traversal::try_parse_decision)
- command / persisted decisions (phase_output::read_persisted_decision),
  which also persists the key verbatim so it round-trips through crash
  recovery rather than collapsing to unknown.

Routing (lifecycle_executor):
- new GateEvaluationResult::Route and build_route_effect. On an Unknown
  verdict carrying a key, resolve_verdict_target / resolve_agent_selected_target
  look the key up in the phase on_verdict map. A forward jump drives the
  advance-with-target machine path; a backward or self jump drives the retry
  path and is counted against the target rework budget.
- a custom key with no on_verdict mapping is an explicit failure (clear
  error naming the key), not a silent advance.
- a genuinely empty/absent key preserves the historical tolerant pass.

The on_verdict map is both the declaration of valid custom keys and their
wiring, so no separate config-protocol contract field was needed.

Tests: forward jump, backward jump, unmapped-key failure, and backward-jump
rework-budget guard at the executor level; custom-key preservation in the
agent parser; and a persisted round-trip for the command path.

* TASK-212: re-pin animus-protocol dev tag -> v0.7.0-rc.5

The verdict_key field is now landed on animus-protocol release/0.7 (PR #3)
and cut as the real tag v0.7.0-rc.5 (commit 7a5ef89). Re-pin all six
animus-protocol git deps across the workspace + the two per-crate manifests
(orchestrator-cli, orchestrator-daemon-runtime) from the interim dev tag
v0.7.0-rc.5-dev.decision-keys to v0.7.0-rc.5. Lock reconciled to the
canonical rc.5 commit; cargo check --workspace --locked clean.
…ugin backend (#302)

queue enqueue --task-id / --requirement-id resolved the subject through the
legacy file-backed in-tree task store (hub.tasks().get() -> load_task -> local
SQLite) before building the dispatch. On a deployment whose subjects live in a
subject_backend plugin (e.g. animus-postgres on the portal), that in-tree store
is empty, so enqueue returned "task not found: TASK-XXX" for subjects that
demonstrably exist and resolve fine through subject get/list/status and the
generic --subject-id enqueue path (all of which route through the SubjectRouter
plugin). The daemon and workflow run --task-id were unaffected because they
already fall back to the plugin resolver; only the enqueue path did a bare
in-tree lookup with no fallback.

Route the --task-id and --requirement-id existence check through the subject
router (RouterSubjectProbe), the same plugin store the rest of the subject
surface uses, and build a backend-qualified dispatch. This makes enqueue-by-id
consistent with the --subject-id path. The task default workflow_ref now comes
from the project's configured default_workflow_ref (matching --subject-id and
--title) instead of the tag-derived heuristic, which relied on in-tree task
metadata the plugin-backed subject does not carry.

Adds unit tests over a stub probe covering the resolved-task and not-found
cases. Removes the now-unused hub argument from resolve_enqueue_dispatch.
… conditional on_verdict) + enqueue-by-id store-routing fix

Cut from release/0.7 on top of rc.3. Adds: TASK-207/210 extensible custom
decision keys and conditional routing (protocol rc.5 verdict_key), and
TASK-215 fix routing queue enqueue --task-id/--requirement-id through the
subject router instead of the legacy in-tree store. Protocol pinned at
v0.7.0-rc.5.
…#303)

The Protocol Drift check has failed on every release/0.7 PR since before
rc.1. The real cause is not a stale STANDALONE_TAG: on release/0.7 the
in-tree animus-plugin-protocol crate and the protocol_drift integration
test were deleted when the crate moved to a git dependency (commit
7b8f46a, "drop in-tree copy + protocol_drift test"). ao-cli release/0.7
now consumes animus-plugin-protocol exclusively as a git dependency pinned
to a release tag, so there is no second in-tree source of truth to drift
against.

The workflow still runs cargo test -p orchestrator-plugin-host --test
protocol_drift, which errors with "no test target named protocol_drift"
and exits 101 before any comparison happens. STANDALONE_TAG v0.1.27 is
never even read. Bumping the tag would not change the outcome.

Removing the workflow on release/0.7 is the correct fix: with no in-tree
mirror, the in-tree-vs-published drift concept no longer applies to this
line. The main branch keeps its own copy of the workflow, where the
in-tree crate and the protocol_drift test still exist and the check passes
against v0.1.27.

TASK-120 is a duplicate of this and can be closed once this lands.
The animus.subject.update MCP tool schema advertised a title parameter
path but neither the MCP arg builder nor the animus subject update clap
command forwarded it, so any caller trying to rename a subject got a clap
UnknownArgument error at parse time and title edits could only be done via
admin SQL.

This adds --title to SubjectUpdateArgs (clap) and maps it into the update
patch in handle_subject_update, rejecting an empty title. It also adds a
title field to the MCP SubjectUpdateInput and forwards it as --title in
build_subject_update_args. The subject router forwards the patch verbatim
to the backend, so a backend that accepts a title in its update patch
(animus-postgres does) now applies the rename.

Docs updated: mcp-tools.md subject.update parameter list and the CLI
reference subject update entry. Tests added: a clap parse test that
subject update --title lands in SubjectUpdateArgs.title, and an MCP
arg-builder test that the title input forwards as --title.

Resolves TASK-192.
…305)

The CLI standalone queue-client discovered the queue plugin by exact
primary plugin_kind equality (find_plugin_for_kind used
manifest.plugin_kind == kind). On a consolidated-BaaS deployment the
queue role is served by animus-postgres, whose primary kind is
subject_backend and which advertises queue as one of its additional
plugin_kinds. Exact-match discovery returned None, so every manual
CLI/MCP queue op (enqueue, list, stats, hold, release, drop, reorder)
failed with no queue plugin installed even though the daemon resolved
the same role fine.

Switch find_plugin_for_kind and probe_active_plugin_roles to
PluginManifest::serves_kind, matching the primary kind OR any additional
plugin_kinds. This mirrors how the daemon resolves roles via
orchestrator_plugin_host::discover_by_kind, so a multi-role plugin now
satisfies the queue (and workflow_runner) role for standalone CLI calls.

Adds a unit test that registers a stub plugin whose primary kind is
subject_backend but which advertises the queue role, and asserts both
find_plugin_for_kind and probe_active_plugin_roles resolve it.
…fixes queue page + manual dispatch on consolidated BaaS)

Cut from release/0.7 on top of rc.4. Adds TASK-228: the CLI queue-client now
resolves the queue backend by serves_kind (primary OR additional kinds) like
the daemon, so animus-postgres (primary subject_backend, additional queue)
satisfies it. Fixes all manual queue ops (enqueue/list/stats/hold/release/
drop/reorder), the portal Queue page, and manual run_workflow dispatch.
The kernel OAuth broker caches HTTP-transport MCP bearer/refresh tokens
under the per-scope state dir. On an ephemeral host (e.g. a Railway
container whose HOME is wiped on redeploy) that cache is lost, forcing a
fresh re-auth on every deploy.

Add an ANIMUS_MCP_OAUTH_CACHE_DIR override so the cache can live on a
mounted persistent volume, surviving redeploys independent of HOME and
the per-scope state symlink. Also finalize a dead refresh grant (cached
rotation chain AND env-var seed both rejected with invalid_grant) with an
explicit re-authentication-required signal, so a connection-state reader
can classify needs-reauth versus a transient failure that stays retryable.

Tests cover the override path, the reauth signal on a dead grant, and the
absence of that signal on a transient 5xx.
@vercel

vercel Bot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
animus-docs Ready Ready Preview, Comment Jul 26, 2026 12:43pm

Request Review

@animus-launchapp-gitprovider animus-launchapp-gitprovider Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: CHANGES REQUESTED

  1. README.md:86 (also docs/getting-started/installation.md:31 and docs/guides/deployment.md:18) — the executable install examples now say ANIMUS_VERSION=v0.7.0-rc.30, but the PR body says the v0.7.0-rc.30 release tag will only be created after this PR merges, so those commands currently resolve to a nonexistent release and scripts/install.sh exits with Download failed. REQUIRED CHANGE: keep executable install/deployment examples pinned to the latest release that already has downloadable assets (currently v0.6.33) in this PR, then update them in a post-release docs change after v0.7.0-rc.30 and its assets exist; alternatively, change the examples to use the installer's unpinned latest-release path instead of naming an unpublished tag. Verify with curl -fsS https://api.github.com/repos/launchapp-dev/animus-cli/releases/tags/v0.7.0-rc.30 (expected HTTP 200 before any docs instruct users to pin it) and ANIMUS_VERSION=v0.7.0-rc.30 scripts/install.sh on macOS (expected archive download and successful install).

The release consolidation is otherwise well documented: the protocol pin is coherent across the workspace, the nine-crate inventory matches Cargo.toml, and the current rustfmt, cargo-check, docs-sync, clippy, and Rust-only guardrail jobs shown so far are passing. I also cannot authorize merge while the PR aggregate remains red: the Vercel deployment status is failing and the remaining synchronized CI jobs have not all completed.

@animus-launchapp-gitprovider animus-launchapp-gitprovider Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: CHANGES REQUESTED

  1. README.md:86 (also docs/getting-started/installation.md:31 and docs/guides/deployment.md:18) — the current head still publishes executable examples pinned to ANIMUS_VERSION=v0.7.0-rc.30 / ARG ANIMUS_VERSION=v0.7.0-rc.30, while the release contract says that tag will only be created after merge; the GitHub Releases API currently returns HTTP 404 for that tag, so these commands fail before installation. REQUIRED CHANGE: keep every executable installer and deployment example pinned to an already-published release with downloadable assets (currently v0.6.33), or use the installer's unpinned latest-release path; update the examples to v0.7.0-rc.30 only after that tag and its assets exist. Verify with curl -fsS https://api.github.com/repos/launchapp-dev/animus-cli/releases/tags/v0.7.0-rc.30 (expected HTTP 200 before any example pins it), then run ANIMUS_VERSION=v0.7.0-rc.30 scripts/install.sh and build the deployment Dockerfile (expected archive downloads and successful installs).

The synchronized commit cleanly repairs the CLI-reference Markdown code spans, and the completed rustfmt, cargo-check, docs-sync, clippy, and Vercel checks are green. The remaining checks are still pending, so this head is not mergeable yet even after the documentation blocker is resolved.

@Shooksie
Shooksie merged commit e3dbbc9 into main Jul 26, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant