Skip to content

chore(release): activeagent and actionagent 1.6.2 - #455

Merged
TonsOfFun merged 26 commits into
mainfrom
release/1.6.2
Sep 16, 2026
Merged

TonsOfFun merged 26 commits into
mainfrom
release/1.6.2

Conversation

@TonsOfFun

@TonsOfFun TonsOfFun commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Preview of the 1.6.2 cut: release/1.6.0 (already carrying 1.6.0 and 1.6.1, which main does not yet have) plus every open PR that merges cleanly and passes, with the version bump and changelog on top. Merging this also brings main up to the release line, as #442 did for 1.5.2.

What is in it

PR Change
#453 Agent releases — digest, revision, pinning on traces/runs/evaluation runs, add_agent_releases migration, rake tasks
#452 Tools tab reads agent.tools for schema tools, and can change it
#454 Evaluations run on MySQL (nil config/criteria), judge class memoized once, sign_in_path/sign_out_path documented
#456 Dashboard API hardening — container-valued query params coerced instead of 500ing, compare validates providers, a recording's show scrubs cookies and web storage from the handoff state
#398 405 for unsupported MCP transport methods
#358, #357, #348 Actions bumps: action-gh-release v3, upload-artifact v7, setup-node v7

Plus its own commits: the merged PRs' changelog entries moved out of ## [1.6.0] (where a merge onto the release branch places them by context) into the new ## [1.6.2] section, the version bump, a telemetry test that restores the global configuration after swapping the tracer, the ## [1.6.2] entries for #456, and a test-helper fix for an order-dependent failure the suite already had (below).

Not in it, and why

Blockers before this stops being a preview

  1. feat(agents): releases — a digest of what the model is given, cut on deploy, pinned to every trace, run and evaluation #453's test isolation. Resolved: feat(agents): releases — a digest of what the model is given, cut on deploy, pinned to every trace, run and evaluation #453 landed on main carrying the fix (the two real-generation tests instrument their own agent class and clear what they stored), main is merged in here, and CI on the branch head is green.
  2. Version. By the reasoning in the 1.6.1 release note ("no new public surface … so a patch under semver"), feat(agents): releases — a digest of what the model is given, cut on deploy, pinned to every trace, run and evaluation #453 adds public surface — ActiveAgent::Release, a migration, rake tasks — which reads as 1.7.0 rather than 1.6.2. Named 1.6.2 here as requested; renaming is a one-commit change.
  3. chore(deps): bump softprops/action-gh-release from 2 to 3 #358 and chore(deps): bump actions/upload-artifact from 4 to 7 #357 change release.yml, which the gem publish depends on; worth watching the first tag that runs through them.
  4. Changelog date is today's; set at cut time.

Test isolation fixed on this branch

The rails-main job on 8ebff51 failed with 28 errors, every one Gem::LoadError: Error loading the 'redis' Action Cable pubsub adapter out of AgentRun#broadcast_update. Action Cable reads config/cable.yml for the current environment the first time its server class loads, and the dummy app does not eager load in test, so that first time was whichever test first broadcast; when the seed put engine_integration_test's staging case first, its stubbed Rails.env had no section in cable.yml, the config came back empty and the adapter fell back to redis for the rest of the process. Reproduced on Rails 8 with a forced order; test/test_helper.rb now loads the config under the real test environment before any test runs (f995cc9). Independent of #456; any seed could hit it on any Rails.

Worth watching

The rails-main job on f995cc9 errored once, on Integration::OpenAI::Responses::CommonFormat::ToolsTest "tool_choice_auto Request Building": NoMethodError: undefined method 'each_key' for nil inside WebMock's global-stub bookkeeping (DynamicResponse#evaluate), during the second, WebMock-stubbed replay of the cassette. Green on re-run; #456's own rails-main run saw the Anthropic sibling of the same test fail as Connection error. The 343 integration replay tests pass three times in a row locally on Rails 8, and no file either failure touches changes on this branch, so it is filed here rather than fixed.

Verified

🤖 Generated with Claude Code

dependabot Bot and others added 20 commits July 29, 2026 08:04
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6 to 7.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](actions/setup-node@v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@v4...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](softprops/action-gh-release@v2...v3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Releases both gems from one tag, cut from main at 51c7feb.

A minor rather than the 1.5.3 originally planned: the cycle adds
ActiveAgent::Base#current_user and the as(...) caller seam, SchemaTools
.define/.undefine with a per-model registry, the active_agent:schema_tools
generator, schema tools served over the MCP facade, caller inheritance
through delegate_to, and the ungrounded_answer evaluation fault. Seven new
public APIs across both gems is more surface than a patch number
advertises, even though 1.5.2 shipped a feature as a patch.

The engine's floor on the framework (activeagent >= 1.4) is unchanged and
still correct. activeagents-telemetry stays at ~> 0.1: nothing this cycle
calls a 0.3-only API, and ~> 0.1 already resolves the published 0.3.0.

Verified: CI green on the base commit across Ruby 3.2/3.3/3.4 and
rails7/rails8/railsmain (run 34736861631, 1978 runs, 0 failures, 0 errors);
bin/lint clean at 542 files; rake build_all produces activeagent-1.6.0.gem
(220 KB, 194 files) and actionagent-1.6.0.gem (512 KB, 111 files), both
archives carrying VERSION = "1.6.0" and their asserted entry points.

A local suite run reports 34 errors that CI does not. All are
environmental, and docs/work/release-1.6.0/validation.md records both
causes: placeholder keys in .env.test, and gemfiles/rails8.gemfile.lock
being untracked, so CI resolves ruby_llm fresh while a local checkout keeps
1.16.0 — whose Message dropped the tool_calls=/input_tokens= writers that
the provider test's StubProvider assigns. Worth its own branch; not a
release blocker.

Not published. Tag-driven release has never succeeded in this repository:
every release.yml run fails at "No trusted publisher configured for this
workflow found on rubygems.org", which is why 1.5.1 and 1.5.2 have no tags
yet are on RubyGems. The archives are built for the owner to push by hand.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CKFMuPp7hiQMutW9N8nKST
… that installs

Two defects with the same shape: each turns a broken run into a
plausible-looking success rather than an error, so neither showed up as a
test failure.

SchemaTools#permitted_filters! validated a filter's column against the
allowlist but passed its value through untouched. A range hash therefore
reached `where` unrecognized, and Rails compiled
`where(due_date: {"before" => x})` to `due_date = NULL` — a predicate no row
satisfies. The tool answered `{count: 0}` with no error and the model
reported that as fact: "0 overdue tickets" against a database holding four.
Equality filters were unaffected, which is why it survived. Comparisons now
build through Arel with the column's own type cast (before, after, lt, lte,
gt, gte, on_or_before, on_or_after; two bounds together express a window),
and an operator outside that roster raises UnpermittedAttribute rather than
returning zero — the same "reject rather than silently ignore" rule an
undeclared column already gets. Only date, datetime, time and numeric
columns are offered a range: a lexical `>` on a name column answers a
question nobody asked.

The range form was also undiscoverable. filter_properties described a date
column as a bare {type: "string", format: "date"}, so the surface could not
express "before today" and a model asking correctly still had no way to ask.
Comparable columns now advertise anyOf: [scalar, range object].

Separately, the railtie prepended GenerationInstrumentation only when
Telemetry.enabled? was already true as railties ran — before
config/initializers/*.rb. An app enabling telemetry from its own
initializer, which is what the docs show, was never instrumented: enabled?
answered true, local_storage was on, the trace model resolved, the store
lambda wrote a row when called by hand, and no generation ever produced a
span to store. Telemetry.configure now installs too when the resulting
config is enabled; instrument_telemetry! is idempotent, so the two paths
cannot double-prepend and initializer order stops mattering.

Both fixes carry tests that fail without them: stashing the source changes
takes schema_tools_test from 0 failures to 6 failures and 1 error, and
instrumentation_install_test from 0 to 2 failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
AgentRegistrar deduplicates within Agent.for_owner(owner), and a
single-tenant dashboard resolves no owner per trace — deliberately, since it
owns everything itself. But for_owner(nil) returns `none` whenever an owner
association is configured, which is the ordinary `owned_by :user` install. So
the dedupe lookup matched nothing and every ingest registered another copy of
the same agent, each taking a random slug suffix from the collision-breaker
in observed_slug. MAX_OBSERVED_PER_OWNER did not contain it either: the count
that cap reads was taken through the same empty relation, so it was always
zero.

Invisible until now because it takes real telemetry to see — an eighteen-call
evaluation left eighteen identical EvaluationJudgeAgent rows. The existing
"reuses the agent record across repeated ingests" test passes because this
dummy app configures no owner association at all, where for_owner(nil) is
`all`; the bug needs a host that configures one and runs single-tenant.

Registration now scopes to the whole table when there is no owner to resolve,
which is what for_owner already does for a model with no owner association.
Multi-tenant behaviour is untouched: the registrar still returns early when
an owner cannot be resolved and multi_tenant is on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Releases both gems from one tag, cut from release/1.6.0 at 877ca0f.

A patch, not a minor: no new public surface and no behaviour change for
anything that already worked. The cycle is three defects that share a failure
mode — a date filter that matched nothing reported zero instead of raising,
telemetry that was enabled but never instrumented wrote no traces while every
configuration signal read healthy, and an observed agent was re-registered on
every trace because the dedupe lookup ran through a relation that can never
match. All three produce a confident wrong answer or a silently wrong row
rather than an error, which is why none appeared in a suite.

Two upgrade notes. A suite filtering on a date column will report different
numbers after upgrading, because the filter now compares instead of matching
nothing; read the first run as a corrected baseline. And duplicate observed
agent rows already written are not cleaned up by the upgrade — delete them
once.

The engine's floor on the framework (activeagent >= 1.4) is unchanged and
still correct; activeagents-telemetry stays at ~> 0.1.

Verified: bin/lint clean at 543 files; bin/test 1994 runs, 6489 assertions,
2 failures and 34 errors, matching the pre-change baseline exactly (the
failures are the RubyLLM suite, whose optional dependency is absent locally,
plus one ordering-sensitive dashboard test that fails identically with and
without these changes). Each fix carries a test that fails without it. The
built archives were installed as real gems into the support-hub app and the
full telemetry and evaluation path exercised end to end against live
OpenRouter calls.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
MySQL cannot give a JSON column a default, so an evaluation saved there
without `config` reads it back as nil, and `compare_models` raised before
the runner did anything else. `criteria` had the same gap on the paths that
call `partition`, `select` or `reject` on it.

`Evaluation#config` and `#criteria` now read as the empty value their
column default supplies on other databases, which also covers rows that
already exist.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`judge_class` resolved the provider, model and credentials on every call
and then discarded them whenever `@judge_class` was already set. The
work now happens once, inside the memoized block, and the test pins that
the provider is resolved a single time per service instance.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Both options existed on `ActionAgent.configure` but neither the dashboard
guide nor the generated initializer mentioned them, so a host that wanted
a browser redirected to its own sign-in page had to read the engine's
controller to find the setting. The Authentication section and the
initializer template now show them beside `authentication_method`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Merged onto the release branch, the entries from the three pull requests
landed inside the 1.6.0 section by surrounding context. They belong to the
next release.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Releases both gems from one tag, cut from release/1.6.0.

Agents gain releases (a digest of what the model is given, cut on deploy and
pinned to every trace, run and evaluation run), the evaluation model reads
its JSON columns as empty rather than nil on MySQL, the Tools tab reads the
roster the runner reads, the MCP endpoint answers unsupported methods with
405, and sign_in_path / sign_out_path are documented. Three GitHub Actions
bumps ride along.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@TonsOfFun

Copy link
Copy Markdown
Contributor Author

CI reading for this preview: lint, API gems and both solid_agent integrations green; 3.4/rails8 green; 3.3/rails8, 3.2/rails7 and 3.4/railsmain red.

Everything red traces to #453's test isolation (details and the rails7 database is locked evidence on #453 (comment) and the follow-up there), except one error each on 3.3/rails8 and railsmain: an Anthropic Connection error in a live-network example test, unrelated to this branch. Once #453 is updated its merge here gets redone and CI re-run.

claude and others added 3 commits September 16, 2026 21:44
Three hardenings the hosted platform made to its own copy of the dashboard
controllers in September, ported to the engine so a host that mounts it
gets the same behaviour.

A query value can arrive as a container (`minutes[]=1&minutes[]=2`, or
`page[x]=1`), and neither Array nor ActionController::Parameters responds
to `to_i`. Every list the dashboard paginates or windows read those
params directly and turned a malformed query into a 500. Api::BaseController
gains integer_param and clamped_param: a multi-valued param means its
first value, a nested object floors to the default. Applied to agent runs,
agent analytics, the account analytics, interactions and session
recordings.

Sandboxes#compare read `providers` as a list. A bare string or a nested
object raised NoMethodError; it is now a 400.

A recording's show response returned the handoff state — a copy of the
visitor's cookies and web storage — unscrubbed, both as its own key and
nested inside metadata, while every other read path redacted. Both are
now stripped of cookies, session_storage and local_storage; only #handoff
returns them, to the owner, when they continue the session.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DkXcH6XVKyD5wYnpLuH9iE
…tracer

`swap_global_tracer` turned the global configuration's `enabled` and
`local_storage` on and its callers' `ensure` called it again to "restore",
so both tests left telemetry on with local storage for the rest of the
process. With generation instrumentation on `ActiveAgent::Base`, every later
generation in the suite was traced, delivered by the batching reporter's
thread on its own connection, and committed — surviving the test
transaction and surfacing as observed agents in whichever dashboard test
ran next.

The swap now returns the tracer and both flags, and `restore_global_tracer`
puts all three back. Same-seed full suite on a clean database: 0 failures,
no rows committed outside a transaction, none left on disk.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@TonsOfFun

Copy link
Copy Markdown
Contributor Author

A second, older source of the dashboard order-sensitivity is fixed on this branch in 49a57cbf — the one the 1.6.1 release note called "one ordering-sensitive dashboard test that fails identically with and without these changes".

telemetry_correlation_test's swap_global_tracer set the global configuration's enabled and local_storage, and both callers' ensure called the same helper to "restore" — so after either test, telemetry stayed on with local storage for the rest of the process. With GenerationInstrumentation on ActiveAgent::Base (installed, legitimately, by InstrumentationInstallTest), every later VCR generation was traced, delivered by the batching reporter's thread on its own connection, and committed — surviving the test transaction and showing up as observed agents in whichever dashboard test ran next (the Providers::OllamaProviderTest::EfficientAgent.ask and …ArrayInstructionsAgent.basic_request agents in this PR's last CI diff).

Measured with a probe on every ingest and on row counts after each test, same seed, before and after: before — background-thread ingests during dozens of unrelated tests, rows surviving rollback; after, on a clean database — 0 failures, 0 leaked rows, 0 background ingests, 0 rows left on disk. One thing to know for local runs: the leaked rows had been committed to test/dummy/storage/test.sqlite3, so a suite run after a leaky one starts dirty until the tables are cleared.

The merge of #456 brings the container-valued query parameter coercion,
the compare providers validation and the recording show scrub onto the
release line; the 1.6.2 section now lists them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DkXcH6XVKyD5wYnpLuH9iE
TonsOfFun pushed a commit to activeagents/activeagents that referenced this pull request Sep 16, 2026
Both agent gems now pin release/1.6.2 (activeagents/activeagent#455),
which resolves to activeagent 1.6.2 and actionagent 1.6.2 and carries
the dashboard API hardening this branch depended on from its own engine
branch. The engine's add_agent_releases migration names its prefixed
tables; this app mounts the engine with an empty table_name_prefix, so
the same columns go on the unprefixed tables here: release_digest on
agents, release_digest and revision on agent versions, and
agent_version_id on traces, runs and evaluation runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DkXcH6XVKyD5wYnpLuH9iE
Action Cable reads config/cable.yml for the current environment the
first time its server class loads, and keeps what it read. The dummy app
does not eager load in test, so that first time was whichever test first
broadcast or rendered the dashboard. When the seed put
engine_integration_test's staging case first, its stubbed Rails.env had
no section in cable.yml: the config came back empty, the adapter fell
back to redis, and every later broadcast in the process raised
Gem::LoadError for redis-client (28 errors and failures on the rails-main
job for 8ebff51, none on the same revision an hour earlier). The helper
now loads the config under the real test environment before any test
runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DkXcH6XVKyD5wYnpLuH9iE
@TonsOfFun
TonsOfFun marked this pull request as ready for review September 16, 2026 23:17
@TonsOfFun
TonsOfFun merged commit b8c741d into main Sep 16, 2026
15 of 16 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.

3 participants