Hive Hive
Sign in

feat(agents): scaffold Condukt-backed agentic workflow infrastructure

GitHub issue · Closed

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

Motivation

I want Hive to be the place where Tuist’s agentic meadow workflows live, and right now there’s no agent infrastructure here at all. Atlas already runs a production Condukt setup that we like, so this PR stands up the equivalent scaffolding in Hive so adding an agent becomes a single-file change rather than a multi-day plumbing exercise.

While I was at it, I added a small AGENTS.md convention that documentation should describe behavior from the operator’s perspective and not leak internal module names or refactoring artifacts. That bullet is the original reason this branch existed; the Condukt scaffolding grew from a follow-up question in the same session.

What’s in the PR

Condukt scaffolding (feat(agents))

  • :condukt 1.7.0 from tuist/condukt, pinned the same way Atlas pins it.
  • HIVE_LLM_API_KEY / HIVE_LLM_MODEL / HIVE_LLM_BASE_URL read into :hive, :llm in config/runtime.exs. When the API key is unset, the LLM stays unconfigured and the rest of Hive boots normally, so self-hosters who do not want agentic features can deploy without an LLM.
  • Hive.Agents exposes config/0, enabled?/0, and client_opts/0. Hive.Agents.Sessions is the single call site wrapping Condukt.run/3 and Condukt.Operation.run/4 and merging in the LLM options, so individual agents stay free of LLM plumbing and so a future audit-trail layer has one place to hook in. Hive.Agents.StyleGuide.prose_rules/0 carries the cross-cutting style rules every agent appends to its system_prompt/0.
  • Hive.TestSupport.Agents.NoopAgent + NoopRuntime mirror Atlas’s pattern: a minimal use Condukt module backed by a runtime that returns {:ok, "handled: " <> prompt}, useful for code that needs a real agent module without an LLM round-trip. Mimic.copy(Condukt, type_check: true) is added in test/test_helper.exs so async tests can stub at the framework boundary.
  • AGENTS.md gets a Tech Stack mention, a Layout pointer to lib/hive/agents/ plus the lib/hive/<domain>/agents/<name>_agent.ex convention, and a new ## Agents section with the env vars and a five-step “Adding an agent” walkthrough.

Documentation guidance (docs(agents))

A new bullet in the Conventions section saying docs should describe behavior and outcomes from the user’s perspective, not internal module names, function signatures, code paths, or refactoring artifacts.

Approach and trade-offs considered

I weighed a couple of options before settling on this shape:

  • Full Atlas parity vs. minimum viable scaffolding. Atlas’s setup includes DB-backed Session/Event Ecto schemas, a TelemetryHandler that streams Condukt’s telemetry into those rows, a Sessions LiveView for the audit UI, and a Helm overlay (agent-sandboxes.yaml) that provisions a separate Kubernetes namespace for sandboxed pods. Porting all of that now would mean writing migrations, schemas, UI, and infra for a system with zero agents to feed it. I chose to defer those layers to the PR that lands the first concrete agent, where we will know what we actually need to record. Hive.Agents.Sessions is structured so audit hooks slot in without changing any agent’s call site.
  • HIVE_ prefix vs. raw LLM_*. Atlas uses LLM_API_KEY / LLM_MODEL / LLM_BASE_URL. Hive’s existing env-var convention is to namespace everything (HIVE_GOOGLE_CLIENT_ID, HIVE_OIDC_ISSUER, HIVE_S3_BUCKET, etc.) so I went with HIVE_LLM_* for consistency with the rest of runtime.exs.
  • Building a ReqLLM.Model struct vs. passing the model string through. Atlas’s Runner constructs a ReqLLM.Model and pattern-matches on the provider so unsupported providers raise at config time. Condukt accepts a raw "provider:model_id" string and forwards it to ReqLLM, so I pass the string directly and let ReqLLM handle the parsing. We can revisit if we ever need provider-level guardrails.
  • Single Hive.Agents module vs. Hive.LLMs + Hive.LLMs.Runner split. Atlas splits LLM config and runtime helpers across two modules because it has dozens of agents and multiple invocation sites. Hive has none, so I folded both into Hive.Agents and kept the surface small. If it gets unwieldy we can split later.

Verification

  • mix deps.get resolved the new tree (Condukt plus the transitives it pulls).
  • mix compile --warnings-as-errors clean in :dev and :test. The remaining warnings come from timex and puid (transitive deps) and are pre-existing.
  • mix format run on every touched file.
  • Manual audit of the diff against Atlas’s setup (lib/atlas/llms.ex, lib/atlas/llms/runner.ex, lib/atlas/agents/sessions.ex, lib/atlas/agents/style_guide.ex, test/support/agents/, test/test_helper.exs) to make sure the public shapes line up.
  • No tests added in this PR because the modules are thin delegations with no branching logic and no agent exists yet to exercise them end to end. The first agent PR will land tests that cover both the agent and the wrapper.

Follow-ups

  • First concrete agent (probably triage or summarization in the forage section) which will also drive the audit-DB schema and LiveView.
  • Update README.md once at least one agent is operator-visible (env vars users need to set, what the feature does); per the new convention bullet, that doc lands in the same PR as the feature.
Comments
GA
github-actions[bot] Jun 12, 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/27428396515

Commit: 8d54dca41c92eb61fb963cc01ddf4615cc4953a8