Skip to content

Add examples, CI, release automation, badges and a LICENSE - #8

Merged
TonsOfFun merged 5 commits into
mainfrom
claude/solid-agent-docs-examples-mcm58b
Aug 17, 2026
Merged

Add examples, CI, release automation, badges and a LICENSE#8
TonsOfFun merged 5 commits into
mainfrom
claude/solid-agent-docs-examples-mcm58b

Conversation

@TonsOfFun

@TonsOfFun TonsOfFun commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

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:

  • The README shows fragments; nothing showed a concern in the shape it takes inside an app.
  • There were no workflows at all — no test run on a pull request, no release automation — and the gemspec pointed at a LICENSE file that was never committed. Releases were a local rake release against a suite nobody had run.

Examples

examples/ gets one worked example per concern, laid out in Rails paths so the files can be dropped into app/ as-is:

Example Concerns
persistent_conversation HasContext — agent, instructions view, controller, console walkthrough
memory_handoff HasMemory — two agent classes sharing a subject
tool_streaming HasTools, StreamsToolUpdates, ToolCache — JSON tool template, inline DSL, ActionCable channel
reasoning HasReasons, Reasonable
run_tracking AgentRun, RunFingerprint, ModelPricing — service, job, polling controller
manifests AgentManifest — a valid .agent.md plus the load/validate/convert API

test/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.

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 its main branch and its latest release tag, with SOLID_AGENT_STRICT=1 so nothing silently skips. This suite mocks ActiveSupport, ActiveRecord and ActionCable by 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 a v* tag via 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 pointing at this repo and workflow, or the push step fails with an authorization error.
  • LICENSE — MIT, matching what the gemspec has always declared.
  • README badges — version, downloads, CI, docs, Ruby, activeagent floor, license; plus how to run the cross-repo suite locally and how releases go out.

Bugs this turned up

  • append_event dropped 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 inside with_lock in both SolidAgent::Records::AgentRun and the install-generator template, add_log shares the same path, and three tests cover it.
  • Gemfile.lock was out of sync with the gemspec on main — it recorded activeagent (>= 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.
  • The README's HasContext example double-persisted the user turn — it called add_conversation_user_message and then let the auto_save after_prompt callback store the same message again.
  • Naming a context also names its models. has_context :conversation resolves Conversation/ConversationMessage/ConversationGeneration, not the AgentContext family solid_agent:install writes, so a fresh app raises NameError: uninitialized constant Conversation on the first request. Caught by the cross-repo suite. The README, the generator note and the example now pass class_name: and say why; rails generate solid_agent:context <name> remains the answer when separate tables are actually wanted.

Testing

  • Unit suite: 238 runs, 567 assertions, 0 failures
  • Record suite (real ActiveRecord): 195 runs, 494 assertions, 0 failures
  • Cross-repo suite against activeagent's branch, strict: 29 runs, 98 assertions, 0 failures, 0 skips
  • CI green on Ruby 3.2/3.3/3.4

Notes for review

  • Merge Document SolidAgent, and test the two repos together activeagent#364 first. The integration job checks out activeagent and runs test/integration/solid_agent/ with gemfiles/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 the latest-release entry will keep skipping until a release carries the harness.
  • The Ruby floor is a promise this repo can't keep. The gemspec says >= 3.0.0, but Gemfile.lock's BUNDLED WITH 2.7.2 cannot install below 3.2, so the matrix starts at 3.2. Either raise required_ruby_version, or pin an older bundler and add the earlier versions back.
  • The CI badge reads "no status" until ci.yml exists on main — expected for a first workflow.
  • No lint job. This repo has no RuboCop config or dependency; adding one would bury the diff in style offenses. Worth doing separately if you want it.
  • examples/ ships in the gem — the gemspec's file list excludes bin/, test/, spec/, assets/ but not examples/ or docs/. Text only, small; say the word if you'd rather exclude it.
  • The release workflow assumes this gem is published after activeagent and before actionagent, matching the dependency direction.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NGn2NzpzFZT4JGKBdFMpxh

@superconductor-for-github

Copy link
Copy Markdown

Superconductor is workingView implementation


I'll get back to you soon!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment thread test/examples_test.rb
Comment thread examples/tool_streaming/app/agents/browser_agent.rb
Comment thread .github/workflows/ci.yml
Comment on lines +56 to +59
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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

claude added 3 commits August 17, 2026 16:52
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
@TonsOfFun
TonsOfFun force-pushed the claude/solid-agent-docs-examples-mcm58b branch from 5cc8aa3 to 8d24b18 Compare August 17, 2026 16:55
claude added 2 commits August 17, 2026 16:57
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
TonsOfFun pushed a commit to activeagents/activeagent that referenced this pull request Aug 17, 2026
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

Copy link
Copy Markdown
Contributor Author

Copilot's three findings, and what changed (61e8da4):

1. append_event lost update — real bug, fixed beyond where it was flagged. The read-modify-write on the events column drops entries when two writers race, and 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 SolidAgent::Records::AgentRun and in the install generator's template alike, and add_log routes through the same helper instead of carrying its own copy of the race. Three tests added in test/records/agent_run_test.rb (stale-writer append, add_log against a concurrent event, unsaved run). activeagents/activeagent#364 mirrors it in the dummy model and corrects the same claim in the docs.

2. Net::HTTP in the browser agent example — accepted. require "net/http" added; the example is meant to survive being pasted into app/agents/.

3. Placeholder API keys in the cross-repo job — kept, now explained. They mirror activeagent's own CI, where config/active_agent.yml reads them at boot and provider clients raise when constructed without one. Every generation in that suite goes through the mock provider, so nothing reaches a network; a real call would fail loudly rather than bill anyone. Comment added saying so.

One correction on the examples_test.rb note: Module#const_defined? does accept a qualified name, so SolidAgent.const_defined?("Reasonable::Reason") resolves — the test passes today against exactly that constant. It would raise on a malformed capture (a trailing :: swept up by the regex), so that now rescues into a normal failure.

Since the first review, the branch was also rebased onto main and picked up three CI fixes worth knowing about: main's Gemfile.lock recorded activeagent (>= 1.1.0) while the gemspec asks >= 1.0.0, which no frozen install had ever exercised; Ruby 3.1 cannot install the lockfile's bundler 2.7.2, so the matrix floor is 3.2 (the gemspec still claims 3.0 — worth reconciling); and the cross-repo job now skips with a reason when the activeagent revision predates the harness, which is every release tag until one ships with it.


Generated by Claude Code

TonsOfFun added a commit to activeagents/activeagent that referenced this pull request Aug 17, 2026
* 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>
@TonsOfFun
TonsOfFun marked this pull request as ready for review August 17, 2026 22:54
@TonsOfFun
TonsOfFun merged commit bb53afc into main Aug 17, 2026
5 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