Hive Hive
Sign in

feat(specs): summarize spec revisions with a Condukt agent

GitHub issue · Closed

Metadata
Source
tuist/hive #37
Updated
Jun 24, 2026
Domains
Hive
Details

Summary

Spec revision summaries used to read “This revision updated the proposal body with X additions and Y removals,” which is true but uninformative. This PR replaces that line with a one or two sentence description of what actually changed, generated by a Condukt agent that compares the previous and current revision.

The agent runs in the background so saving a revision is not slowed down by the LLM round trip:

  • New agent. Hive.Specs.Agents.RevisionSummaryAgent defines a typed :summarize_revision operation that takes the previous and current {title, status, body} and returns a single summary string. The system prompt tells it to describe substance (which sections, claims, criteria, tradeoffs), skip preamble like “This revision”, and avoid restating line counts. The shared Hive.Agents.StyleGuide.prose_rules() is appended so it inherits the no-em-dash rule.
  • Async pipeline. Hive.Specs.RevisionSummaries.summarize/2 fetches the revision, looks up the previous one, calls the agent through Hive.Agents.Sessions, and persists the result via a new Revision.summary_changeset/2. Hive.Specs.RevisionSummaryWorker wraps that in an Oban worker on a new specs queue with per-revision uniqueness. Hive.Specs.create_revision/2 enqueues the worker after every non-first revision; the first revision keeps the static “Created the initial draft proposal.” copy.
  • Persistence. New nullable summary :text column on spec_revisions. Nil means “either still pending or no LLM configured,” which lets HiveWeb.SpecComponents.revision_summary/2 prefer the agent-written value when present and fall back to the existing counts heuristic otherwise. That fallback is intentional: self-hosters without an LLM keep the old experience.
  • Test infrastructure. test_helper.exs now starts a single Oban supervisor for the suite in :manual mode. Previously each Oban-using test called start_supervised!({Oban, ...}) and that worked while only the meadows worker test needed it; with a second worker test added here, two async tests would race on the named Oban process. Starting it once globally lets every test use Oban.Testing helpers under async: true without colliding, and the now-redundant start_supervised! was removed from evolution_worker_test.

Why this approach

The first instinct was to make the summary generation synchronous inside the spec update transaction, but that would block the UI on an LLM call and would couple spec edits to LLM availability. An Oban worker on its own queue keeps the request path fast, lets the worker retry on transient LLM failures, and (because the job is inserted inside the same DB transaction as the revision) is automatically rolled back when the spec update is. The enqueue/1 short-circuits to :skipped when agents are disabled, so the same code path works whether or not an LLM is configured.

The runner injection pattern in RevisionSummaries.summarize/2 mirrors Hive.Meadows.Evolution so tests can exercise the real flow without standing up Condukt or stubbing the agent module.

How it was verified

  • mix format
  • mix compile --warnings-as-errors
  • mix credo (no issues)
  • mix test: 368 tests, 0 failures, including new tests covering enqueue uniqueness and the disabled-agents skip, the worker’s two no-op perform/1 paths, the full RevisionSummaries.summarize/2 flow with the runner injection (success, string-keyed output, :llm_not_configured, first revision, unexpected error), and the LiveView preferring the agent summary over the heuristic when present.

Testing

  • mix format
  • mix compile --warnings-as-errors
  • mix credo
  • mix test
Comments
GA
github-actions[bot] Jun 16, 2026

Blick review didn’t run

The blick review step failed before producing a manifest, so there’s no review to post on this PR. This usually means the agent (opencode) couldn’t start — common causes are an expired or suspended model API key, a missing secret, or the workflow timing out.

See the workflow run for details: https://github.com/tuist/hive/actions/runs/27617462469

Commit: 9c766e1ab3d4588540b051aebedae57b8783f180