Autonomous issue-to-PR driver for Claude Code and OpenCode, structured per lair/architecture generic.md. Workspace: entities/core/data/agent library crates plus api, worker and cli binaries. Two pieces of real logic land with tests — lane routing (cc for judgement, oc for specification) and the limit governor. Constraints encoded as code rather than comments: - agents are spawned as vendor binaries; tireless never calls a provider API - ANTHROPIC_API_KEY is never set by tireless, only passed through - assert_not_anthropic refuses to start an OpenCode lane pointed at Anthropic - every run passes the governor; provider rate-limit signals win over our own accounting Deployment assets target bob.hanzalova.internal:23296 (registered in port-allocations.md), fronted by hanzalova at tireless.internal. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHhHtohxcdk1PL3tfnYJdH
102 lines
3.8 KiB
Rust
102 lines
3.8 KiB
Rust
//! The OpenCode lane.
|
|
//!
|
|
//! OpenCode is run the way vibe-kanban runs it: as a local server
|
|
//! (`opencode serve --hostname 127.0.0.1 --port 0`) driven over loopback HTTP
|
|
//! with a per-spawn basic-auth password. See
|
|
//! `crates/executors/src/executors/opencode.rs:92`.
|
|
//!
|
|
//! **This lane is never pointed at Anthropic.** OpenCode is a third-party
|
|
//! harness with its own provider clients; driving an Anthropic *subscription*
|
|
//! through it is the pattern Anthropic blocked in January 2026, and OpenCode was
|
|
//! named explicitly. Anthropic work goes through the Claude Code lane, which
|
|
//! uses the first-party binary. [`assert_not_anthropic`] enforces this at
|
|
//! startup so the rule survives a config edit.
|
|
|
|
use tireless_entities::Error;
|
|
|
|
/// Pinned for the same reason as the Claude Code package.
|
|
pub const OPENCODE_PACKAGE: &str = "opencode-ai@1.4.7";
|
|
|
|
pub struct OpencodeExecutor {
|
|
/// OpenCode provider id, e.g. `lair-helexa`.
|
|
pub provider: String,
|
|
/// Model id within that provider, e.g. `Qwen/Qwen3.6-27B`.
|
|
pub model: String,
|
|
/// OpenAI-compatible base URL. Defaults to helexa cortex on the office site,
|
|
/// which routes by model name across the neuron fleet.
|
|
pub base_url: String,
|
|
pub timeout_seconds: u64,
|
|
}
|
|
|
|
/// Provider identifiers that indicate an Anthropic backend.
|
|
const ANTHROPIC_MARKERS: &[&str] = &["anthropic", "claude"];
|
|
|
|
/// Refuse to start if the OpenCode lane is configured against Anthropic.
|
|
///
|
|
/// Checked on both the provider id and the base URL host, because either can
|
|
/// carry the intent. This is a terms-of-service constraint expressed as code:
|
|
/// the comment explaining it can rot, a failing startup assertion cannot.
|
|
pub fn assert_not_anthropic(provider: &str, base_url: &str) -> Result<(), Error> {
|
|
let provider_lc = provider.to_ascii_lowercase();
|
|
let url_lc = base_url.to_ascii_lowercase();
|
|
|
|
// Match provider ids on token boundaries so a self-hosted provider that
|
|
// merely *serves* a Claude-compatible surface is not caught by accident,
|
|
// while `anthropic`, `anthropic-oauth` and `claude-max` all are.
|
|
let provider_hits = ANTHROPIC_MARKERS.iter().any(|m| {
|
|
provider_lc
|
|
.split(|c: char| !c.is_ascii_alphanumeric())
|
|
.any(|part| part == *m)
|
|
});
|
|
|
|
// The URL check is host-based: `api.anthropic.com` is disqualifying wherever
|
|
// it appears, but a local gateway is not.
|
|
let url_hits = url_lc.contains("anthropic.com");
|
|
|
|
if provider_hits || url_hits {
|
|
return Err(Error::AnthropicViaOpencode(provider.to_string()));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn the_house_helexa_backend_is_accepted() {
|
|
assert!(assert_not_anthropic("lair-helexa", "http://hanzalova.internal:31313/v1").is_ok());
|
|
assert!(
|
|
assert_not_anthropic("lmstudio", "http://beast.hanzalova.internal:1234/v1").is_ok()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn anthropic_providers_are_refused() {
|
|
for provider in [
|
|
"anthropic",
|
|
"Anthropic",
|
|
"anthropic-oauth",
|
|
"claude",
|
|
"claude-max",
|
|
] {
|
|
assert!(
|
|
assert_not_anthropic(provider, "http://localhost/v1").is_err(),
|
|
"{provider} should be refused"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn the_anthropic_api_host_is_refused_whatever_the_provider_is_called() {
|
|
assert!(assert_not_anthropic("totally-fine", "https://api.anthropic.com/v1").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn a_local_gateway_serving_an_anthropic_compatible_surface_is_fine() {
|
|
// cortex presents both OpenAI and Anthropic compatible APIs, but it is
|
|
// local inference — no Anthropic subscription is involved.
|
|
assert!(assert_not_anthropic("lair-helexa", "http://hanzalova.internal:31313/v1").is_ok());
|
|
}
|
|
}
|