Add examples, CI, release automation, badges and a LICENSE - #8
Conversation
|
⏳ Superconductor is working — View implementation I'll get back to you soon! |
There was a problem hiding this comment.
Pull request overview
This PR adds end-to-end documentation-as-code examples for SolidAgent concerns, plus CI and release automation to ensure the gem and its examples stay valid as the ecosystem evolves.
Changes:
- Add a comprehensive
examples/directory (Rails-like layout) covering core concerns (context, memory, tools/streaming/cache, reasoning, runs, manifests), with a top-level examples index. - Add automated validation for examples/manifests via
test/examples_test.rb. - Introduce GitHub Actions workflows for CI (unit + cross-repo integration) and tag-based releases, plus README badges/updates and an MIT
LICENSE.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
test/examples_test.rb |
Adds tests to parse Ruby examples, validate manifests, and ensure examples are linked and reference existing APIs. |
README.md |
Adds badges, links to docs/examples, fixes/clarifies HasContext guidance, and documents CI/cross-repo testing and release flow. |
LICENSE |
Adds MIT license text referenced by gem metadata. |
examples/README.md |
Adds examples index and explains assumptions/how to run examples. |
examples/persistent_conversation/usage.rb |
Console walkthrough for persisted conversations. |
examples/persistent_conversation/app/views/agents/support/instructions.md.erb |
Example instructions template for a support agent. |
examples/persistent_conversation/app/controllers/support_conversations_controller.rb |
Example controller showing persisted conversation usage. |
examples/persistent_conversation/app/agents/support_agent.rb |
Example agent demonstrating HasContext with persistence and message replay. |
examples/memory_handoff/usage.rb |
Console walkthrough for long-term memory handoff. |
examples/memory_handoff/app/agents/researcher_agent.rb |
Example agent writing to memory (HasMemory). |
examples/memory_handoff/app/agents/writer_agent.rb |
Example agent reading/priming memory (HasMemory). |
examples/tool_streaming/usage.rb |
Console walkthrough for tools + streaming + cache usage. |
examples/tool_streaming/app/views/browser_agent/tools/fetch_url.json.erb |
JSON tool schema template example. |
examples/tool_streaming/app/channels/tool_status_channel.rb |
Example ActionCable channel for tool status streaming. |
examples/tool_streaming/app/agents/browser_agent.rb |
Example agent demonstrating HasTools, streaming updates, and tool caching. |
examples/reasoning/usage.rb |
Console walkthrough for reasoning capture/persistence. |
examples/reasoning/app/agents/analysis_agent.rb |
Example agent demonstrating HasReasons + persisted reasoning. |
examples/run_tracking/usage.rb |
Console walkthrough for run tracking, cohorts, and cost estimation. |
examples/run_tracking/app/services/document_analysis_run.rb |
Example service driving an AgentRun lifecycle with events. |
examples/run_tracking/app/jobs/document_analysis_job.rb |
Example job executing a run asynchronously. |
examples/run_tracking/app/controllers/agent_runs_controller.rb |
Example controller for creating/polling/cancelling runs. |
examples/run_tracking/app/agents/report_agent.rb |
Example agent whose executions are tracked via AgentRun + trace IDs. |
examples/manifests/usage.rb |
Console walkthrough for .agent.md manifest parse/validate/load/convert. |
examples/manifests/changelog_writer.agent.md |
Example .agent.md manifest. |
.github/workflows/ci.yml |
Adds unit test matrix + cross-repo integration workflow. |
.github/workflows/release.yml |
Adds tag-driven release workflow (build + attach + RubyGems trusted publishing). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| SOLID_AGENT_STRICT: "1" | ||
| ANTHROPIC_API_KEY: ANTHROPIC_API_KEY | ||
| OPEN_AI_API_KEY: OPEN_AI_API_KEY | ||
| OPEN_ROUTER_API_KEY: OPEN_ROUTER_API_KEY |
There was a problem hiding this comment.
Kept, but documented — leaving this thread open in case you'd rather it went the other way.
They're not decorative: this job runs activeagent's bin/test, whose config/active_agent.yml reads these at boot, and several provider clients raise when constructed without a key. activeagent's own CI sets the identical placeholders for that reason, so dropping them here would break the job rather than harden it.
On the risk you're pointing at — every generation in the cross-repo suite goes through the mock provider, which never opens a socket, and the one dashboard test uses generate_with :mock too. A code path that did reach a provider with ANTHROPIC_API_KEY=ANTHROPIC_API_KEY would get a 401 immediately: loud, free, and arguably a better failure than a mysteriously-skipped test. Wiring real secrets in would be the change that makes a live call possible.
61e8da4 adds a comment stating that, so the next reader doesn't have to re-derive it. If you'd prefer the job assert no HTTP escapes (WebMock is already in that bundle), say the word.
Generated by Claude Code
The gem's README shows fragments; nothing showed a concern in the shape it takes inside an app. examples/ now has one worked example per concern — agent classes, views, channels, controllers, jobs and console walkthroughs laid out in Rails paths — and examples_test.rb keeps them honest: every Ruby file parses, every .agent.md validates against the real validator, every SolidAgent constant they name exists, and every example is linked from the index. Also fixes the README's HasContext example, which added the user message by hand and then let auto_save persist the same turn a second time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh
This repository had no workflows at all: no test run on a pull request, no release automation, and a gemspec pointing at a LICENSE file that was never committed. Releases were a local `rake release` against a gem whose suite nobody had run. - ci.yml runs the unit suite on Ruby 3.1/3.3/3.4, plus a cross-repo job that runs this working tree through activeagent's integration suite — against its main branch and its latest release tag, with SOLID_AGENT_STRICT=1 so nothing silently skips. This suite mocks everything by design, so it can pass while the concerns no longer compose with the framework they extend; that job is what notices. - release.yml publishes from a v* tag through RubyGems trusted publishing (OIDC, no stored key), gated on CI, skipping versions already published. Needs a trusted publisher configured for solid_agent on rubygems.org. - README gains version, downloads, CI, docs, Ruby and license badges, plus how to run the cross-repo suite and how releases go out. Also documents the has_context naming trap the cross-repo suite surfaced: a named context resolves models named after it, not the AgentContext family the installer writes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh
Two things the new CI surfaced on its first run, both pre-existing: Gemfile.lock recorded activeagent (>= 1.1.0) for the path gem while the gemspec asks for >= 1.0.0, so any frozen install — which is what setup-ruby's bundler-cache does — refused to proceed. Nothing had run a deployment-mode install before, so the drift was invisible. Bundler 2.7.2, which the lockfile pins, requires Ruby >= 3.2, so the 3.1 job could not get as far as installing gems. The gemspec still claims >= 3.0.0; 3.2 is the floor CI can actually prove, and the matrix now says so rather than failing on a promise the repo cannot keep. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh
5cc8aa3 to
8d24b18
Compare
gemfiles/solid_agent_main.gemfile and test/integration/solid_agent live in activeagent, so any revision without them has nothing to run — every release tag until one ships with the harness, and main until activeagents/activeagent#364 merges. Both were hard-failing on a missing BUNDLE_GEMFILE, which reads as "this gem is broken" rather than "the other side is too old". The job now checks for the harness first and skips with that reason in the step summary. The latest-release entry will keep skipping until a release carries the suite, which is the honest state of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh
Copilot's review caught a real lost update: append_event reads the events column, adds an entry, and writes the result, so two writers that read before either writes leave only one entry behind. A tool loop appending progress while the run's own thread logs is exactly that shape. The docstring claimed concurrent appends "interleave safely", which is what re-reading buys on its own — it narrows the window, it does not close it. The read and the write now happen inside with_lock, in Records::AgentRun and in the install generator's template alike, and add_log goes through the same path rather than carrying its own copy of the race. An unsaved run has no row to lock and keeps the in-memory behaviour. Also from the same review: require net/http in the browser agent example, since Rails does not guarantee it is loaded; make the examples' constant check fail rather than error on a malformed capture; and record why the cross-repo job sets placeholder API keys. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh
Mirrors activeagents/solid_agent#8: append_event's read-modify-write on a JSON column loses entries when two writers race, so the re-read and the write are serialized by a row lock. The dummy model tracks the install generator's template, so it moves with it. The runs page repeated the old claim that concurrent appends "interleave safely" — true of the lock, not of the re-read that was there. The context page said "turned auto_save: false off", which reads as the opposite of what it means. Both from Copilot's review. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh
|
Copilot's three findings, and what changed (61e8da4): 1. 2. 3. Placeholder API keys in the cross-repo job — kept, now explained. They mirror activeagent's own CI, where One correction on the Since the first review, the branch was also rebased onto main and picked up three CI fixes worth knowing about: main's Generated by Claude Code |
* Document SolidAgent on docs.activeagents.ai solid_agent's gemspec has pointed its homepage and documentation_uri at docs.activeagents.ai/solid_agent since it was published, and that page did not exist — the docs mentioned the gem only in passing, as something the dashboard happens to depend on. Adds a Persistence section: an overview at /solid_agent plus a page per concern (context, memory, tools/streaming/caching, reasoning, runs and cost, manifests) and a worked-examples page mirroring examples/ in the solid_agent repo. Linked from the sidebar, the home page features, the framework overview, getting started, the agents and tools pages, and the dashboard page that already depended on the gem. These pages carry their code inline rather than importing it, because the code they document lives in another repository; contributing/documentation now says so, and points at the tested examples they mirror. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh * Test activeagent, actionagent and solid_agent together Three gems, two repositories, one dependency direction — and nothing ran them together, so all three suites were green while the combination a user installs was broken. test/integration/solid_agent runs both repos in the dummy app against the models solid_agent:install writes, through the mock provider: conversation persistence, memory hand-offs, run records, the tool cache, and the version constraints themselves. Two configurations. Source (solid_agent main, SOLID_AGENT_STRICT=1) is what the repos develop toward and must be green. Released (whatever Bundler resolves) is what users get; tests declare what they need and skip when the gem can't do it, printing the reason — that skip list is the report of how far the gem trails its source, not a failure. gemfiles/solid_agent_main takes SOLID_AGENT_PATH or SOLID_AGENT_REF so either repo can drive it. Runs on every PR, nightly, and as a gate before publishing; solid_agent's CI runs the same suite from its side. Two breakages it found immediately: - has_context's auto-context keyword was renamed contextable: -> contextual: in solid_agent 0.2. AgentExecutionService still passed the old one, so every dashboard run against current solid_agent died with ArgumentError. Resolved from the installed method now, since the gemspec floor admits both versions. - AgentToolbox's fallback cache key hashed arguments differently from SolidAgent::ToolCache, so upgrading the gem silently invalidated every cached tool result. Both paths now normalize identically. Also fixes the docs and examples that named a context without class_name: has_context :conversation resolves Conversation/ConversationMessage/ ConversationGeneration, not the installed AgentContext family, and raises NameError on the first request. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh * Keep the dummy schema dump on Rails 8.0 Regenerating the dump after adding migration 005 stamped it ActiveRecord::Schema[8.1], the Rails the dump ran under. The API-gem CI jobs pin rails ~> 8.0.0, whose Active Record rejects that version outright — db:migrate died on load with "Unknown migration version" before running a single test. The header tracks whoever last dumped it, so anyone regenerating on 8.1 will reintroduce this; the matrix's floor is what it has to satisfy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh * Lock the dummy AgentRun's event append, and fix two doc claims Mirrors activeagents/solid_agent#8: append_event's read-modify-write on a JSON column loses entries when two writers race, so the re-read and the write are serialized by a row lock. The dummy model tracks the install generator's template, so it moves with it. The runs page repeated the old claim that concurrent appends "interleave safely" — true of the lock, not of the re-read that was there. The context page said "turned auto_save: false off", which reads as the opposite of what it means. Both from Copilot's review. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh --------- Co-authored-by: Claude <noreply@anthropic.com>
Draft — pairs with activeagents/activeagent#364, which publishes the docs these examples are linked from and holds the cross-repo suite this repo's CI runs. Merge that one first (see Notes).
Why
Two gaps, both invisible from inside the repo:
LICENSEfile that was never committed. Releases were a localrake releaseagainst a suite nobody had run.Examples
examples/gets one worked example per concern, laid out in Rails paths so the files can be dropped intoapp/as-is:persistent_conversationHasContext— agent, instructions view, controller, console walkthroughmemory_handoffHasMemory— two agent classes sharing a subjecttool_streamingHasTools,StreamsToolUpdates,ToolCache— JSON tool template, inline DSL, ActionCable channelreasoningHasReasons,Reasonablerun_trackingAgentRun,RunFingerprint,ModelPricing— service, job, polling controllermanifestsAgentManifest— a valid.agent.mdplus the load/validate/convert APItest/examples_test.rbkeeps them honest: every Ruby file parses, every.agent.mdvalidates against the real validator, everySolidAgent::constant they name exists, and every example is linked from the index.CI and releases
ci.yml— unit suite on Ruby 3.2/3.3/3.4, plus a cross-repo job that runs this working tree through activeagent's integration suite, against itsmainbranch and its latest release tag, withSOLID_AGENT_STRICT=1so nothing silently skips. This suite mocksActiveSupport,ActiveRecordandActionCableby design, so it can pass while the concerns no longer compose with the framework they extend — that job is what notices. Also runs nightly.release.yml— publishes from av*tag via RubyGems trusted publishing (OIDC, no stored key), gated on CI, skipping versions already published. Needs a trusted publisher configured forsolid_agenton rubygems.org pointing at this repo and workflow, or the push step fails with an authorization error.LICENSE— MIT, matching what the gemspec has always declared.Bugs this turned up
append_eventdropped events under concurrency (found by Copilot's review). The read-modify-write on the events column loses an entry when two writers read before either writes — a tool loop appending progress while the run's own thread logs is exactly that. The docstring claimed concurrent appends "interleave safely", which is what re-reading buys on its own: it narrows the window, it does not close it. The read and the write now happen insidewith_lockin bothSolidAgent::Records::AgentRunand the install-generator template,add_logshares the same path, and three tests cover it.Gemfile.lockwas out of sync with the gemspec on main — it recordedactiveagent (>= 1.1.0)against a gemspec asking>= 1.0.0, so any frozen install refused to proceed. Nothing had ever run a deployment-mode install, so this was invisible until CI existed.HasContextexample double-persisted the user turn — it calledadd_conversation_user_messageand then let theauto_saveafter_promptcallback store the same message again.has_context :conversationresolvesConversation/ConversationMessage/ConversationGeneration, not theAgentContextfamilysolid_agent:installwrites, so a fresh app raisesNameError: uninitialized constant Conversationon the first request. Caught by the cross-repo suite. The README, the generator note and the example now passclass_name:and say why;rails generate solid_agent:context <name>remains the answer when separate tables are actually wanted.Testing
Notes for review
integrationjob checks outactiveagentand runstest/integration/solid_agent/withgemfiles/solid_agent_main.gemfile; neither exists on that repo's main until #364 lands. The job skips with that reason rather than failing, so it is green here — and thelatest-releaseentry will keep skipping until a release carries the harness.>= 3.0.0, butGemfile.lock'sBUNDLED WITH 2.7.2cannot install below 3.2, so the matrix starts at 3.2. Either raiserequired_ruby_version, or pin an older bundler and add the earlier versions back.ci.ymlexists onmain— expected for a first workflow.examples/ships in the gem — the gemspec's file list excludesbin/,test/,spec/,assets/but notexamples/ordocs/. Text only, small; say the word if you'd rather exclude it.activeagentand beforeactionagent, matching the dependency direction.🤖 Generated with Claude Code
https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh