Dashecorp Rig — Brain¶
Fresh-agent entry point. Read this first. One fetch (~57 KB) gives you the repo manifest, deployed surfaces (rig-conductor's 77 endpoints and its built-in Dashboard included), agent instances, primary flows, the frontmatter schema, 45 event types (summary; full schemas at /events.md), a 20-whitepaper catalog, and the current backlog with prior_art links. Every claim traces to its source file in
facts/.Compiled from
facts/*.yaml+ live GitHub state (gh api /orgs/dashecorp/reposfor the repo list; manifest validation for agents). Do not hand-edit BRAIN.md. Regenerate withnpm run brain. CI runs--checkand fails on drift.
What this is¶
The Dashecorp rig is an autonomous coding-agent system. A human posts a user
story; agents research, propose, code, review, and ship. Canonical docs live
in dashecorp/rig-docs (Astro Starlight); operational memory lives in a
Postgres + pgvector Memory MCP; deployments are Flux-managed on a 3-node k3s
cluster on GCE (invotek-k3s in invotek-github-infra: control plane +
2 STANDARD workers, europe-north1-b).
Published surfaces¶
Rig landing — discoverable index of all surfaces¶
- URL: https://rig.dashecorp.com/
- Type: html
Canonical brain entry point (this file, rendered)¶
- URL: https://docs.rig.dashecorp.com/brain/
- Raw: https://research.rig.dashecorp.com/BRAIN.md
- Type: markdown
Brain map — visual architecture + doc-linkage graph¶
- URL: https://research.rig.dashecorp.com/map/
- Type: astro-starlight
- Note: Two auto-derived diagrams (architecture from facts/, linkage from doc frontmatter) — the shape of what the rig knows, before fetching pages.
LLM site map (research, proposals, user-stories)¶
- URL: https://research.rig.dashecorp.com/llms.txt
- Type: llms-txt
Full content dump (single-shot ingestion)¶
- URL: https://research.rig.dashecorp.com/llms-full.txt
- Type: llms-full-txt
Research, proposals, user-stories (rendered Starlight site)¶
- URL: https://research.rig.dashecorp.com/
- Type: astro-starlight
- Source: dashecorp/rig-docs
Aggregated engineering docs (architecture, guides, whitepapers, per-repo docs)¶
- URL: https://docs.rig.dashecorp.com/
- Type: mkdocs-material
- Source: dashecorp/rig-gitops (docs-site/)
- Note: Built by rig-gitops/scripts/build-docs.sh on push + hourly cron, pulling each repo's docs/ via gh api. Engineering reference, not research (that is research.rig.dashecorp.com).
Sitemap (XML)¶
- URL: https://research.rig.dashecorp.com/sitemap-index.xml
- Type: sitemap-xml
rig-conductor API (/api/*)¶
- Type: rest-api
- Visibility: access-gated
- Endpoints:
POST /api/events— Submit any of the 40+ event types — see /events.mdGET /api/assignments/next— Claim next issue assignment. Query: agentId=dev-e-nodeGET /api/pr-reviews/next— Claim direct-PR review (no issue) for infra/tooling PRsGET /api/pr-reviews/item— Inspect a single PR review item. Query: repo, prNumberPOST /api/pr-reviews/merge— Server-side merge gate for direct PR reviews (rc#1028)GET /api/issues— List tracked issues. Query: state=open|done|stuckGET /api/issues/item— Fetch a single issue projection by (repo, issueNumber)GET /api/issues/trace— Per-issue event trace + state transitions for debuggingGET /api/stuck-issues— List issues in a non-terminal state for too long (stuck-watcher candidate set)GET /api/releases— Release-note feed (rc#1439) — most-recent 30 posted release notesGET /api/queue— Current dispatch queue stateGET /api/usage— Token / cost usage by agent and/or repo. Query: agentId, repoGET /api/costs/issue— Cost for a specific issue. Query: repo, issueNumberGET /api/costs/summary— Aggregate cost. Query: days (default 7)GET /api/costs/daily— Daily cost time series. Query: daysGET /api/events/live— SSE stream of live events (for Dashboard.html)GET /api/streams/status— Stream consumer statusGET /api/streams/{agentId}— Per-agent stream tail (recent assignment messages). Query: countGET /api/agents— List registered agents (heartbeat + status). Query: archived=trueDELETE /api/agents/{agentId}— Forcibly archive a specific agent (admin)DELETE /api/agents/offline— Bulk-archive all agents that are offline (no recent heartbeat)GET /api/agent-capacity— Per-agent capacity / quota / dispatch eligibility snapshotGET /api/agents/heartbeat-softdrops— Heartbeat soft-drop counter, total + per agent (rc#1870, process-local)POST /api/webhook/github— GitHub webhook intake — normalizes GH events into rig-conductor streamPOST /api/webhook/flux— Flux deploy confirmation webhook (rc#413 in_deploy → deployed)POST /api/merge— Server-side merge gatePOST /api/execution-logs— Create execution log envelopePOST /api/execution-logs/{id}/logs— Append log entriesPOST /api/execution-logs/{id}/steps— Append structured stepPOST /api/execution-logs/{id}/complete— Mark log completeGET /api/execution-logs/{id}— Fetch log by idGET /api/execution-logs/issue— Logs per issue. Query: repo, issueNumberGET /api/execution-logs— List logs. Query: limit, statusGET /api/execution-logs/stats— Turn/cost/token stats per agent. Query: agentId, days, statusPOST /api/execution-logs/cleanup— Prune old logsGET /api/repo-learnings— Fetch learnings. Query: repoPOST /api/repo-learnings— Upsert learningDELETE /api/repo-learnings— Delete learning. Query: repo, keyGET /api/guard-blocked— Guard-block counts per agent. Query: agentIdGET /healthz— Liveness probe (rc#1522) — rig_control reachability only. 200 / 503GET /health— Liveness probe — always 200 if the process is aliveGET /healthz/deep— Deep readiness probe — Marten + Valkey + dependency checks (rc#1188)GET /api/health— Detailed health snapshot for the dashboard (component-level)GET /api/version— Build version + git SHAGET /dashboard— Built-in single-page dashboard (HTML) — Engineering Rig control planeGET /api/events/stream— Single event-stream tail by stream id. Query: idGET /api/events/recent— Recent events across all streams. Query: hoursGET /api/main-ci— Main-branch CI status snapshot. Query: repoGET /api/ci-failures— List CI failures across repos. Query: repo, includeAckedPOST /api/ci-failures/{repo}/{workflowName}/{runId:long}/ack— Ack a CI failure so it stops showing as activeGET /api/main-guard/incidents— Main-guard incidents (rc#1226 + rc#1234). Query: repo, statusGET /api/a11y— Accessibility scan results per repo. Query: repoGET /api/stuck-watch— Live stuck-watch snapshot (proxies upstream cluster check)GET /api/stuck-patterns— List active stuck patterns. Query: includeResolved=true for allPOST /api/stuck-patterns/{fingerprint}/resolve— Mark a stuck pattern as resolved (writes memory)GET /api/stuck-patterns/brain-section— Generate the## Known stuck patternsmarkdown for BRAIN.mdGET /api/agent-logs— List recent agent log entries across all agents. Query: countGET /api/agent-logs/{agentId}— Tail recent log entries for one agent. Query: countPOST /api/agent-logs— Append a batch of log entries from an agent (push from pod)GET /api/agent-logs/live— SSE stream of live agent log entriesGET /api/self-improvement/signatures— Watcher signature states (rc#947): occurrences, OpenIssue, clean-tick counterGET /api/tenancy— Tenancy census (rc#1523) — tenantCount, isMultiTenant, bare tenant idsGET /api/admin/tenants— Tenant roster from the rig_control allowlist (id/name/status/type DTO)POST /api/admin/tenants— Onboard a tenant (B2C grace needs a verified X-Operator-Discord-Id)DELETE /api/admin/tenants/{tenantId}— Tenant erasure — operator-allowlisted, fail-closed, confirmation headerPOST /api/admin/issues/force-done— Operator force-close an issue's read-model state to Done (admin)POST /api/admin/overrides— Record an operator override event (audit trail)GET /api/admin/overrides— List recent operator overrides for auditPOST /api/planner/trigger— Dispatch a planner task (planner agent stream)- Note: In-cluster, AND at the edge on rig-conductor.dashecorp.com — that hostname is behind a catch-all Cloudflare Access app (admin email + the cockpit service token) and the ONLY bypass is /api/webhook/github (infra cloudflare/dashecorp.com/access.tf). Exact in-cluster host/port is intentionally not surfaced. There is NO in-process authorization on any /api/ route (zero RequireAuthorization calls in Program.cs): the whole boundary is Access at the edge plus rig-cockpit-worker's operator-session check. Since 2026-09-08 that worker proxies only a small explicit allowlist (src/config/api-allowlist.js), NOT /api/ generically. GPU pod control and /public/* are separate surfaces below.
rig-conductor RunPod GPU pod control (start / stop / status)¶
- Type: rest-api
- Source: dashecorp/rig-conductor (src/ConductorE.Api/Endpoints/RunPodPodEndpoints.cs)
- Visibility: access-gated
- Endpoints:
POST /api/pods/{name}/start— Volume-first wake onto any acceptable GPU. 200 AlreadyRunning|Started|Created · 202 Waiting (poll again) · 409 Failed · 503 unconfigured · 404 unknown name. Body {name,outcome,podId,gpuId,pricePerHour,proxyUrl,message}POST /api/pods/{name}/stop— Stops EVERY pod on the volume (a stale pod and its replacement can both bill). 200 {name,ok,pods[]} · 404 no pod · 503 unreadable/unconfiguredGET /api/pods/{name}— Always 200 except 404 unknown name. {name,configured,status,isRunning,podId,proxyUrl,podsOnVolume,datacenter,lookup,readySince}- Note: infra#434 / rig-conductor#2008. Registered by MapRunPodPodEndpoints() outside Program.cs, so the endpoint drift-check misses them. {name} is the friendly pod name (RUNPOD_POD_NAME, default
qwen), NEVER a RunPod pod id — the pod is resolved through the network volume, and an unknown name 404s before any RunPod call. isRunning = RUNNING with >= 1 GPU, the only state that serves; proxyUrl is set whenever a pod is named, so check isRunning too. readySince (#2056) = the idle monitor's first vLLM /metrics answer since boot, so isRunning:true + readySince:null is "waking, not usable yet" (in-process — a restart forgets it). Unconfigured (RUNPOD_API_KEY unset OR RUNPOD_VOLUME_ID blank) → 503 on start/stop, 200 configured:false on GET, idle monitor exits at startup. Auth is Cloudflare Access at the edge only; an X-Operator-Discord-Id gate on these cost-incurring writes is proposed, not shipped. rig-cockpit-worker allowlists GET /api/pods/{name} (rcw#86, 2026-09-09) so a client can read pod status; the writes stay refused. The cockpit status tile (rcw#87) and Stop button (rcw#85) are still open, and the iOS app has no chat and no pod surface at all. Config: RUNPOD_VOLUME_ID is the ONLY pod identity (RUNPOD_POD_ID is read nowhere; REST v1 deleted in #2043), plus RUNPOD_TEMPLATE_ID, RUNPOD_DATACENTER_ID=EU-RO-1, RUNPOD_POD_NAME=qwen, IDLE_MINUTES=15, MAX_RUNTIME_MINUTES=360, RUNPOD_BOOT_GRACE_MINUTES=20; code defaults CHECK_SECONDS=60, RUNPOD_POD_PORT=8000, RUNPOD_MIN_VRAM_GB=32, RUNPOD_MAX_PRICE_PER_HOUR=1.00 USD/h, no GPU allowlist. The idle window starts at model-READY, not RUNNING: a pod not ready within RUNPOD_BOOT_GRACE_MINUTES (20) is stopped as a dead model, and MAX_RUNTIME_MINUTES (360) hard-stops any pod up that long regardless of the streak. Full tables: rig-conductor docs/runpod-pod-control.md.
rig-conductor sanitized read-only API (/public/*)¶
- Type: rest-api
- Source: dashecorp/rig-conductor
- Visibility: access-gated
- Endpoints:
GET /public/agents— id, status, lastHeartbeat, currentRepo onlyGET /public/issues— Active issues — title, state, GitHub URL onlyGET /public/streams/status— Per-stream xlen + aggregate pendingGET /public/cost-summary— Aggregate daily + weekly cost, no per-agent breakdown. Query: daysGET /public/events— SSE feed of sanitized events- Note: Designed for a future public rig-dashboard (PublicSanitizer — no secrets, tokens, env vars or internal paths; 60 req/min per IP) but NOT publicly reachable today: rig-conductor.dashecorp.com is fully Access-gated and nothing bypasses Access for /public/ (only /api/webhook/github does). The name is the intent, not the exposure. A separate surface for the sanitization contract — not for a visibility difference from /api/.
rig-conductor Dashboard (the built-in cost/activity UI)¶
- Type: html-dashboard
- Source: dashecorp/rig-conductor (src/ConductorE.Api/Dashboard.html)
- Visibility: access-gated
- Note: Served at the root of the Access-gated rig-conductor.dashecorp.com. ~102 KB single-page HTML dashboard, "Engineering Rig — Control Plane" (104,822 bytes on 2026-09-09; the long-quoted "42 KB" is stale). Costs, Issues, Agents, Streams tabs on /api/costs/, /api/usage, /api/issues, /api/streams/. No Grafana/Starlight dashboard is needed — this already renders per-agent / per-issue / per-day cost.
Memory MCP (Postgres + pgvector)¶
- Type: mcp-server
- Package: @dashecorp/rig-memory-mcp
- Tools:
read_memories— Query prior memory by topic/repo/scope with vector similaritywrite_memory— Persist a new memory with scope/kind/importance/tagsmark_used— Increment hit_count on a memory that informed a decision
Discord agent channels (notifications)¶
- Type: discord
- Channels: #dev-e, #review-e, #ibuild-e, #admin
- Note: Agents post thread updates here; humans watch for stuck / pending state.
Repos¶
Live from gh api /orgs/dashecorp/repos merged with facts/repos.yaml annotations. Archived repos are dropped automatically.
| Repo | Purpose | Language | Depends on | AGENTS.md |
|---|---|---|---|---|
rig-gitops |
GitOps manifests (Flux HelmReleases, Kustomize bases) and the canonical AGENTS.md every rig repo imports. Also hosts the aggregated MkDocs b | shell | — | compiled |
rig-agent-runtime |
The AI agent runtime (Node) — one image that deploys as Dev-E, Review-E, rig-planner or Rig-E depending on character + environment (prompt/l | javascript | rig-memory-mcp, rig-conductor | imports-rig-gitops |
rig-memory-mcp |
MCP server backing persistent agent memory with Postgres + pgvector. Exposes read_memories / write_memory / mark_used tools consumed b |
javascript | postgres-pgvector | claude-md |
rig-conductor |
Event store + dispatch service (C# + Marten + Postgres). Receives PR/issue events, assigns work, tracks turns/cost/stuck state, serves the ` | csharp | postgres, pgvector | imports-rig-gitops |
rig-manager |
Agent runtime, caged executor, verb schemas and the reproducible container build. Its deterministic SHA-256 digest IS the KMS attestation me | dockerfile | — | none |
rig-docs |
Research, proposals, user-stories, and rig-wide reference (Astro Starlight). This repo — you're reading its BRAIN.md. Deploys to research.ri | astro | — | hand |
rig-tools |
Shell scripts, Git hooks, and workflow sync for AI-assisted development. Developer tooling, not deployed. The one repo without an AGENTS.md | shell | — | none |
infra |
OpenTofu for GitHub org settings, Cloudflare (DNS, Pages, tunnels), GCP (the 3-node k3s cluster invotek-k3s — control plane + 2 STANDARD wor | hcl | — | imports-rig-gitops |
Per-repo doc index (token-efficient discovery)¶
Before cloning a repo to find docs, consult this list to decide which docs are relevant to your issue. Then fetch raw markdown for only the relevant ones:
Auto-derived per compile via gh api /repos/<r>/contents/docs. Repos without a docs/ dir are omitted.
rig-gitops— architecture-current.md, architecture-proposed-v2.md, architecture-proposed.md, documentation-standard.md, onboarding.md, research-multi-agent-platforms.md, review-e-bootstrap.md, sops.mdrig-agent-runtime— architecture.md, configuration.md, dashboard.md, deployment.md, discord-setup.md, heartbeat.md, index.md, memory.md, messaging.md, observability.md, quickstart.md, usage-tracking.mdrig-memory-mcp— api.mdrig-conductor— api.md, architecture.md, deployment.md, event-store.md, index.md, principles.mdrig-manager— container.md, reproducible-build.mdrig-tools— agent-workflow.md
Agents (deployment instances)¶
Dev-E — writes code¶
- Runtime: dashecorp/rig-agent-runtime
- Deployed in: k3s on GCE — invotek-k3s 3-node cluster (control plane + 2 STANDARD workers, europe-north1-b, invotek-github-infra)
- Manifest:
dashecorp/rig-gitops/apps/dev-e/ - Variants:
- node:
apps/dev-e/rig-agent-helmrelease.yaml - python:
apps/dev-e/python-helmrelease.yaml - dotnet:
apps/dev-e/dotnet-helmrelease.yaml - node-codex:
apps/dev-e/rig-agent-helmrelease-codex.yaml - python-codex:
apps/dev-e/python-helmrelease-codex.yaml - dotnet-codex:
apps/dev-e/dotnet-helmrelease-codex.yaml - Character: baked into HelmRelease values
- Triggers: signal:dev-e-node/-python/-dotnet (+ -codex) LIST + assignments:dev-e STREAM
- Notes: SIX instances in namespace dev-e, not three: a claude lane (node = claude-tmux, dotnet + python = claude-cli, all claude-opus-4-7[1m], maxTurns 300) and a codex lane (codex-cli + gpt-5.5, maxTurns 150). Stream-consumed via Valkey, NOT REST polling: each variant's ScaledObject watches its signal:
LIST at valkey-primary.rig-conductor.svc.cluster.local:6379 to scale 0→1, then the agent drains assignments:dev-e. KEDA is NOT uniform — dev-e-dotnet-codex is PINNED ON (min = max = 1), dev-e-python-codex is OFF (replicaCount 0 + paused-replicas "0"), the other four scale 0→1. Trust the ScaledObject spec, not its comments: dev-e-node-codex's still claims a paused annotation it no longer has.
Review-E — reviews PRs¶
- Runtime: dashecorp/rig-agent-runtime
- Deployed in: k3s on GCE — invotek-k3s 3-node cluster (namespace review-e)
- Manifest:
dashecorp/rig-gitops/apps/review-e/rig-agent-helmrelease.yaml - Triggers: signal:review-e LIST + assignments:review-e STREAM
- Discord: #review-e
- Notes: TWO live instances, both in namespace review-e, both replicaCount 1, both with AGENT_ID review-e, both KEDA-scaled on the SAME signal:review-e LIST (an atomic LPOP splits the work):
review-e(claude-tmux + claude-sonnet-4-6, maxTurns 150) andreview-e-codex(codex-cli + gpt-5.5, maxTurns 75) — parallel since gitops#476 because the codex quota saturates under burst. DISPATCH is Valkey + KEDA, not cron/REST polling (the cron + search_filter are dead code). Because both pods register asreview-e, the conductor's quota-split router can never selectreview-e-codex(it only matches registered agent ids) — and if it ever did, the XADD/LPUSH would land on assignments/signal keys no pod or ScaledObject watches, since both ScaledObjects watch signal:review-e. ROUTING — auto-routed only for dev-e-bot / ibuild-e-bot / dependabot authors, or an orchestrator PR on a rig-internal repo; everything else needs the needs-review label, honoured by the 5-minute ReviewScanService.
Rig-E — read-only tenant chat agent (dashecorp) behind Rig Cockpit¶
- Runtime: dashecorp/rig-agent-runtime
- Deployed in: k3s on GCE — invotek-k3s 3-node cluster (namespace dashecorp, tenantId dashecorp)
- Manifest:
dashecorp/rig-gitops/apps/dashecorp/rig-e-helmrelease.yaml - Character: baked into HelmRelease values
- Triggers: synchronous POST /api/chat — no KEDA ScaledObject, no assignment stream
- Discord: none — chat-only (messagingPlatform none, rar#659/#660)
- Notes: LIVE since rig-gitops#712 at replicaCount 1 — chat is synchronous, so no ScaledObject and no scale-to-zero. Surface: Rig Cockpit → rig-cockpit-worker → rig-e.dashecorp.com → Cloudflare tunnel → the pod's POST /api/chat, gated by CHAT_API_TOKEN (infra#442). readOnly true, strict MCP, and its WHOLE tool grant is six conductor GET endpoints (health, agents, streams/status, stuck-issues, issues, events/recent) served as MCP tools by the in-image httptools bridge. Brain = the operator's own GPU: openai-api + Qwen/Qwen2.5-32B-Instruct-AWQ on the on-demand RunPod pod
qwen, with NO literal pod URL — llm.baseUrlFrom { conductorPod: qwen, conductorUrl: http://rig-conductor-api.rig-conductor.svc.cluster.local:8080 } (rig-gitops#737, rar#674/#676/#677). llm.timeoutMs 300000 OVERRIDES the runtime's 120 s openai-api default. NO fallbackProviders, deliberately: a fallback would inherit llm.model, so a stopped pod fails loud instead of masking the pod path. Image pinned BY HAND (not one of the 10 auto-pinned files) — tag and baseUrlFrom must move together.
iBuild-E — macOS / iOS builds¶
- Runtime: Stig-Johnny/claude-3
- Deployed in: Mac Mini M4 (Ålesund, Møre og Romsdal — the operator's parents' house, remote-only; on the operator's Tailnet)
- Manifest:
not-in-cluster - Discord: #ibuild-e
- Notes: Does NOT run dashecorp/rig-agent-runtime: it runs the
agent-runnerbridge from Stig-Johnny/claude-3 as the launchd LaunchAgent ai.invotek.ibuild-e (agent-runner/agents/ibuild-e.json), as the ibuild-e-bot GitHub App. Xcode + App Store Connect; off-cluster because iOS builds need macOS. The site is Ålesund, never Oslo, with NO hands-on access — assume remote-only recovery.
Planner-E — plans sprints, manages backlog, assigns issues to agents¶
- Runtime: dashecorp/rig-agent-runtime
- Deployed in: k3s on GCE — invotek-k3s 3-node cluster (namespace rig-planner)
- Manifest:
dashecorp/rig-gitops/apps/rig-planner/ - Triggers: signal:rig-planner LIST + assignments:rig-planner STREAM
- Discord: #planner
- Notes: GitHub App rig-planner-bot (App ID 3546083) handles issue intake. NOT scaled 0→1: its ScaledObject carries paused-replicas "1" (operator pin 2026-05-14) and the HelmRelease sets replicaCount 1, so it is always-on. It reads signal:rig-planner (LIST) + assignments:rig-planner (STREAM). Provider claude-cli + claude-opus-4-7[1m] (NOT sonnet), maxTurns 50. A second instance
rig-planner-codex(codex-cli + gpt-5.5) is OFF (replicaCount 0 + paused-replicas "0"). Persona: /whitepaper/planner/.
Primary flows¶
PR lifecycle in dashecorp (orchestrator-owned — DO NOT copy legacy personal-org workflow files)¶
Trigger: A PR in a dashecorp repo authored by dev-e-bot / ibuild-e-bot / dependabot, or an orchestrator (Stig-Johnny) PR on one of the six rig-internal repos. Every other PR is opt-in via the needs-review label, picked up by the 5-minute ReviewScanService backstop, not the webhook.
- GitHub — POSTs the webhook to rig-conductor /api/webhook/github (HMAC fail-closed — GITHUB_WEBHOOK_SECRET unset ⇒ 503; every delivery must carry a matching X-Hub-Signature-256)
- rig-conductor — Normalizes the PR event, XADDs assignments:{agentId} (group
agents) then LPUSHes signal:{agentId}. agentId is ALWAYSreview-etoday: ReviewDispatchRouter only considers candidates registered asreview-e/review-e-codex, and BOTH reviewer pods run AGENT_ID=review-e, so nothing ever registers as codex and the quota-split router falls back to ClaudeReviewerId every time — it stays inert until the codex pod gets its own AGENT_ID. The issue-link rule is ADVISORY: a non-bot PR with noCloses|Fixes|Resolves #Ngets a warning comment, then processing continues — nothing blocks dispatch or merge on it - Review-E — Consumes its review assignment from the assignments:review-e Valkey stream (woken by KEDA scaling 0→1 on the signal:review-e list), reviews, posts approval or CHANGES_REQUESTED
- rig-conductor (POST /api/merge → MergeGate) — /api/merge does NOT merge — it sets MergeGate's
review-approvedcondition. MergeGate merges onreview-approved+ci-passed+ NOTblocked, after a fail-closed re-check that every non-skip-listed check_run on the PR's CURRENT head SHA completed success/skipped/neutral. Squash merge, then emits MERGED
Rules:
- Do NOT copy the operator's per-repo request-review.yml / auto-merge.yml from legacy personal-org repos into dashecorp repos — that is the pre-conductor pattern, and the conductor owns this lifecycle. If a dashecorp repo isn't reviewed or merged, the fix is configure the GitHub webhook, not add a workflow file.
- The operator's personal-org repos keep the per-repo workflow pattern (they predate rig-conductor) until they are archived post-migration.
- MergeGate checks three Valkey hash conditions on merge-gate:{repo}#{prNumber} — review-approved, ci-passed, NOT blocked. It does not check thread resolution, and the label is do-not-merge, never manual-merge — and that label is INERT (Block()/Unblock() are its only writers and have zero callers). It can merge WITHOUT ci-passed: it merges at once when branch protection reports no required checks, else waits 30 s (MERGE_GATE_WAITING → MERGE_GATE_TIMEOUT) and merges anyway — both paths still pass the head-SHA check_runs guard.
Complete when: conductor emits the MERGED event (there is no PR_MERGED type) and downstream consumers (CF Pages, iBuild-E, etc.) react
Epic to merged work¶
Trigger: Human opens a user-story issue in a serviceable-tenant repo that is NOT rig-internal. Issues in the rig-internal repos (rig-docs, rig-conductor, rig-agent-runtime, rig-gitops, rig-tools, infra, terraform) are SKIPPED as rig_internal_repo — orchestrator work.
- rig-conductor — Push-based, not a scan:
issues.openedwithout the agent-ready label LPUSHes signal:rig-planner;issues.labeledwith label exactlyagent-readydispatches to an agent. IssueScanService is only a 10-minute backstop for missed webhooks - Dev-E — Reads issue + relevant research; authors research / proposal / code PR
- Review-E (stream-consumed via assignments:review-e, KEDA-scaled) — Finds PR, reviews against AGENTS.md + memory, requests changes or approves
- Human — Merges (or Review-E's approval satisfies branch protection; auto-merge fires)
- Cloudflare Pages — Redeploys research.rig.dashecorp.com and docs.rig.dashecorp.com Complete when: issue closed via `Closes
rig-conductor self-deploy (post-merge image rollout via Flux)¶
Trigger: A PR merges to dashecorp/rig-conductor main (the MERGED step in the PR lifecycle flow)
- GitHub Actions (.github/workflows/publish-image.yml — job
publish) — Builds the container and pushes:latest+:sha-<commit>to GHCR and Artifact Registry (europe-north1-docker.pkg.dev/invotek-github-infra/dashecorp/rig-conductor); 3-attempt retry on transient registry / WIF / BuildKit errors - GitHub Actions (job
update-gitops, needs publish) — sed-bumps the image tag in deploy/k8s/deployment.yaml to:sha-<commit>, opens achore: pin rig-conductor image to sha-<sha>PR, validates the diff is exactly the one-line image bump, then admin-merges it - Flux source-controller (GitRepository
rig-conductor) — Polls dashecorp/rig-conductor main every 5m and picks up the new deployment.yaml - Flux kustomize-controller (Kustomization
rig-conductor-api) — Reconciles every 10m frompath: deploy(prune true); the new image (imagePullPolicy Always) rolls the pod
Rules:
- paths-ignore: deploy/k8s/deployment.yaml on the workflow stops the pin commit from re-triggering Publish Image (loop guard). The publish build+push is the real success signal — a pin PR that fails because a newer pin already landed is benign supersession (rc#1532), not a failure.
- Worst-case propagation is ~15m (5m GitRepository interval + 10m Kustomization interval), not 5m.
- The sibling flow for AGENT images is automatic and different: after a successful Build & Push on rig-agent-runtime, rig-conductor's ImagePinDispatchService opens one atomic chore: pin rig-agent-runtime images to <sha> PR on rig-gitops as dev-e-bot, covering EXACTLY 10 HelmReleases — dev-e (node/dotnet/python × claude+codex), review-e (×2), rig-planner (×2). Rig-E, the dashecorp tenant fleet, the stigjohnny fleet and dev-e-staging are NOT covered and are pinned by hand, which is why they drift behind.
- Verify a deploy from inside the cluster: kubectl -n rig-conductor exec deploy/rig-conductor-api -- curl -s localhost:8080/api/version (returns the git SHA) and the same for /healthz/deep (200 = Marten + Valkey + deps healthy). Both endpoints are cluster-internal — already in surfaces.yaml.
Complete when: the rig-conductor-api pod runs the new sha-
GPU brain wake-on-chat (Rig-E → rig-conductor → RunPod pod qwen)¶
Trigger: An operator chats with Rig-E in Rig Cockpit while the on-demand vLLM pod is stopped (it is OFF by default and bills only while up).
- Rig-E (openai-api provider) — Resolves its endpoint from GET /api/pods/qwen (llm.baseUrlFrom). Not running ⇒ providerNotReady — retryable, no fallback; a stopped pod never crashes the boot. The conductor's proxyUrl is accepted only as a bare https origin on an allowed host suffix (default
.proxy.runpod.net, widened via llm.baseUrlFrom.allowedHostSuffixes) - Rig-E (chat.js, decideWakeOnNotReady) — POSTs /api/pods/qwen/start fire-and-forget (3 s cap, never throws) and answers HTTP 200 in the normal {reply,sessionId} shape: "Waking the GPU brain — that takes about 9 minutes." Asking again inside the 15-min window reports elapsed minutes and does NOT re-wake; past it, it wakes again; lookup UNREADABLE wakes nothing
- rig-conductor (RunPodWakeService) — Volume-first wake on the application lifetime (a 524 at the edge never aborts it; a retry joins the same run): start the pod on the volume, else create a replacement on the cheapest acceptable GPU in the volume's datacenter, terminating the stale sibling only once the replacement is RUNNING — proven live 2026-09-09, and it needs no gitops change
- RunPod → RunPodIdleMonitor — Measured 2026-09-09: start 09:37:51Z → RUNNING 09:40:41Z (≈3 min) → model loaded 09:46:57Z (≈6-7 min more, ≈9 min total). The first /metrics success is READY and publishes readySince. The completion runs against {proxyUrl origin}/v1/chat/completions, capped by llm.timeoutMs (Rig-E 300 s, default 120 s), re-resolving ONCE per turn on 404/502/503/504/network
- rig-conductor (RunPodIdleMonitor) — Stops the pod 15 min (IDLE_MINUTES) after READY, not after RUNNING (rc#2055/#2056) — the earlier bug billed the model load as idle (usable 8.7 of ~16 billed min). Config + defaults: the pod-control surface above
Rules:
- KNOWN GAPS as of 2026-09-09. rar#685 (RUNNING-but-still-loading answered a raw 502 in the very window the user was told to retry) is FIXED and live: rig-agent-runtime#689, shipped to Rig-E as node-sha-418a948 by rig-gitops#749 on 2026-09-09 — that window now answers "The GPU is up and the model is still loading…" and gives up after 20 minutes. Still open: rar#684 — a timed-out start POST logs requested=false although the wake proceeds; rig-conductor#2058 — a conductor rollout mid-wake loses the tail; rig-cockpit-worker#87 — the cockpit GPU status tile, and #85 — the Stop button (the read route is allowlisted, rcw#86).
Complete when: GET /api/pods/qwen reports isRunning true with a non-null readySince and the turn answers; the pod stops itself 15 min after the last request
Research and proposal authoring¶
Trigger: An Epic needs investigation before implementation
- author dated research/YYYY-MM-DD-slug.md with user_story frontmatter
- author proposals/YYYY-MM-DD-slug.md with source_research frontmatter
- user_story file gets research_docs and proposal fields pointing back
- RelatedDocs component auto-renders the graph; no manual cross-linking
Rules: - bidirectional links required - schema enforced in src/content.config.ts - CI rejects PRs missing required fields
Cold-start agent session¶
Trigger: Fresh agent with blank memory receives an Epic or task
- WebFetch https://research.rig.dashecorp.com/brain/ (or raw BRAIN.md)
- Parse facts/repos.yaml equivalent in BRAIN.md — learn repo manifest
- Parse facts/surfaces.yaml equivalent — learn URLs and endpoints
- WebFetch https://research.rig.dashecorp.com/llms.txt for topic index
- WebFetch relevant research/proposal docs directly via raw URL
- For the target repo, fetch its AGENTS.md (compiled or imports-rig-gitops)
- read_memories scoped to repo + topic via Memory MCP
- Begin work with full context in ~15 KB total Token budget: ~15 KB read, leaves 200K+ for actual work on Opus
Docs-memory promotion (weekly Lint)¶
Trigger: Weekly scheduled Lint job
- Scan Memory MCP for rows with importance >= 4 AND hit_count >= 5
- For each candidate, check if docs already cover the topic (BM25 sim)
- If not covered, propose a docs PR with the memory content promoted
- Human approves PR, merge triggers redeploy Status: not-yet-built (design in research/2026-04-18-docs-memory-drift-lint)
Diagram-as-code authoring¶
Trigger: A research / proposal / user-story needs a diagram
Rule: Mermaid source inline in fenced code block. No PNG or SVG ever committed.
Rendering: remark-mermaid plugin wraps in <figure> with <pre class=mermaid> and <details> source; mermaid.js renders client-side; source preserved post-render for agent readers.
Frontmatter schema (for authoring rig-docs content)¶
- type (optional): one of
research|proposal|decision|postmortem|reference|user-story|runbook - audience (optional): one of
human|agent|both— not a free-form array - Required:
title,description - Optional linkage fields (paths are relative to src/content/docs/, no leading slash, no .md or .mdx extension):
type— See type enum above.subtype— See subtype enum above (whitepapers only).audience— See audience enum above.created— ISO date string YYYY-MM-DD.updated— ISO date string YYYY-MM-DD.topic— Short slug grouping related docs.source_refs— Array of external source URLs.supersedes— Path to the doc this replaces (no leading slash, no extension).superseded_by— Path to the newer doc replacing this.user_story— (research/proposal) Path to the user story this supports.research_docs— (user-story) Research doc paths this story spawned.proposal— (user-story) Path to the proposal answering this story.source_research— (proposal) Research paths this proposal synthesises.github_issue— (user-story only) Full GitHub issue URL. Omit entirely if none — never an empty string.whitepaper— (user-story) Whitepaper slug this story supports — filename without extension. Rolls up story counts in the sidebar.whitepapers— (user-story) List form ofwhitepaper:— inline[a, b]or block list; counts on each paper's badge. Both keys together is tolerated (merged, deduped).
Path examples: user-stories/2026-04-18-docs-memory-strategy, research/2026-04-18-docs-tools-evaluation, proposals/2026-04-18-docs-tooling-decision, decisions/2026-04-18-docs-tooling-decision.
Omit a field entirely when it has no value — do not use empty string.
Whitepapers (private — catalog only)¶
These whitepapers live at dashecorp/rig-gitops/docs/whitepaper/*.md (private repo — requires gh auth to fetch). BRAIN.md surfaces their titles + 1-line summaries so agents know what exists. Full content must be fetched with: gh api /repos/dashecorp/rig-gitops/contents/docs/whitepaper/<file> --jq .download_url | xargs curl -sL.
- Whitepaper index (
index.md) — Entry point listing every whitepaper section and companion doc. - MVP scope (
mvp-scope.md) — What the rig does in the MVP. Gatekeeper for "is this in scope?" - Design principles (
principles.md) — First principles — measurement precedes trust, honest gaps, portability. - Trust model (
trust-model.md) — Who can approve what, which gates exist, human-in-the-loop rules. - Safety (
safety.md) — Dangerous-command guards, sandboxing, blast-radius containment. - Security (
security.md) — Secrets handling, attestation, audit trail, SOPS+age. - Agent secrets broker (
agent-secrets-broker.md) — Capability-based secret lifecycle broker: agents hold opaque refs while the broker handles plaintext across Bitwarden, GitHub, SOPS, k8s and Cloudflare, so it never enters a prompt, tool argument or log. Covers the tool surface, the ref grammar, the policy model and the audit trail. - Provider portability (
provider-portability.md) — Multi-runtime (Claude Code, Codex CLI, Gemini CLI) via OTel GenAI conventions. Swap runtime without changing backend. - Observability — OTel, Langfuse, Prometheus, SLOs (
observability.md) — Self-hosted Langfuse (agent traces) + Grafana Cloud (infra) + local Prometheus (SLO gates); native OTel via CLAUDE_CODE_ENABLE_TELEMETRY=1, one Collector per cluster. Per implementation-status: OTel Collector Partial (deployed for rig-conductor, agents not yet emitting), Langfuse Planned, cost dashboard Partial (TokenUsageProjection exists, no LiteLLM proxy). - Cost framework (
cost-framework.md) — Budget policy, per-model rate tables, cost attribution strategy. Companion to observability. - Self-healing (
self-healing.md) — Automatic recovery loops, StaleHeartbeatService, escalation severity routing. - Memory architecture (
memory.md) — Memory MCP scope, importance/hit_count model, promotion-to-docs threshold design. - Quality and evaluation (
quality-and-evaluation.md) — How the rig evaluates its own output. Judge-agent pattern, fixed rubrics. - Drift detection (
drift-detection.md) — Schema drift, docs drift, infra drift — detection thresholds and response. - Development process (
development-process.md) — Issue → Epic → research → proposal → PR lifecycle, agent-human gates. - Example first story (
example-first-story.md) — Worked walkthrough of one Epic end-to-end. - Glossary (
glossary.md) — Rig-specific terminology (Epic, proposal, rig-conductor, Review-E, etc). - Known limitations (
limitations.md) — Honest catalog of what the rig can't do today. - Implementation status (
implementation-status.md) — Single source of truth for deployed vs planned per capability. 78 tracked across 11 domains; 21 deployed/partial (27%), 44 planned/deferred (56%). Every capability named in the whitepapers gets a row with status + whitepaper section + ticket/evidence. - Tool choices (ADRs) (
tool-choices.md) — Decision records for tooling. Includes rejection list with rationale.
Most agents should start with: the /implementation/ dashboard (structured per-capability status — see summary below) and whichever domain-specific whitepaper matches the Epic.
Capability status (40 in registry · full dashboard)¶
shipped:16 · partial:8 · planned:15 · deferred:0 (registry seed — full migration tracked in rig-docs#124)
Top blockers: default-deny-egress (dashecorp/rig-docs#57), gpu-brain-wake-on-chat (dashecorp/rig-agent-runtime#684)
Multi-tenancy¶
Shared control plane + per-tenant siloed data plane. tenant_id is resolved ONCE at a trusted edge (GitHub installation_id for webhooks, Cloudflare Access identity for the dashboard, the conductor-issued session token for agents) and threaded immutably as a Marten event header — never asserted by the LLM, never read from a body or tool argument ("the LLM is the threat model, not the guard").
Boundary: The database IS the tenant boundary. Marten master-table tenancy (the rig_control registry) routes each tenant to its own Postgres database (rig_t_
Active tenants: invotek (type: B2B) — Tenant-0, on its legacy database (renamed rig_t_invotek_evt at the PR-5 cutover). Post-handover (rig-docs#324) githubOrg=null — invotek is verifiably not a GitHub org or user, so it owns no namespace. It keeps pinned ownership of the 6 rig-internal repos via RepoTenant rows, plus all pre-cutover dashe- history (drain-don't-migrate).; dashecorp (type: B2B) — Org-default tenant for the dashecorp GitHub org. First-party / Model B (operator-owned, no external DPA counterparty). Owns the 8 dashe- app / site repos, pinned via RepoTenant during the suspended provisioning window then caught by the org-default after activation (pins stay as belt-and-braces). New data-plane DB rig_t_dashecorp_evt; dashe-* starts FRESH (pre-cutover history stays in invotek's DB). Activation gated on rig-conductor#1926/#1476; the pin-first / activate-last sequence means the interim window can only fail-to-null, never mis-route.
Target (gated, 4-tenant lock):
- dashecorp — type: B2B, model: B (hosted), first-party — org-default host — see Active tenants above.
- invotek — type: B2B, model: B (hosted), first-party — tenant-0 — see Active tenants above.
- stigjohnny — type: B2C, model: A (BYO), first-party — consumer-facing persona; the B2C gate (consent + age-gate + rc#1496 subject-level erasure) blocks Status=active until rc#1496 ships.
- run-the-docs — type: B2B, model: A (BYO), first-party — docs/content pipeline; subjects = contributors.
Type axis status: ratified-design / not-yet-enforced-in-code (operator-ratified 2026-06-20, /reference/tenant-types/, rig-docs#314). The type values below are policy metadata only; the code-enforced Tenant.Type field is rig-conductor#1476.
Human gates: - Review — ReviewRoutingPolicy: a PR routes to Review-E iff authored by a known bot (dev-e-bot/ibuild-e-bot) or dependabot, OR it carries the needs-review operator opt-in label. - Merge — MergeGate (event-driven): merges only on review-approved + CI-passed + not-blocked; agents never self-merge (fallback is a manual operator squash). - Fail-closed write — RequireTenant throws on a blank/invalid tenant, so an unattributed write lands in no tenant's DB; a CI scope-guard blocks new unattributed write paths. Dashboard READS stay lenient (coalesce to invotek). - Onboarding — TenantOnboardingGate refuses activating an external tenant until its data-plane DB + erasure prerequisites exist (invotek exempt; preserves the GDPR no-window invariant). - Schema fence — env-gated (MARTEN_TENANT_SCHEMA_LOCKED), armed at the PR-5 cutover: the boot assert then checks every registered tenant's schema and fails closed on drift. Default-off until armed. - GDPR — rig-conductor#1486 is the launch blocker. The DPA SIGNATURE is EXTERNAL-tenant-only: all four targets are first-party, so none needs an operator signature to launch. They are gated instead on controller duties toward third-party contributors — Art.17 erasure (shipped), sub-processor disclosure, EU residency — plus the DPO minimal-field-set opinion (#1486 §3.4).
Built (merged + deployed): DB-per-tenant (rc#1515), the fail-closed write boundary (rc#1608), tenant resolution (TenantMatch), per-tenant dispatch (rc#1481) + per-installation GitHub tokens (rc#1665), the cross-tenant isolation gate (rc#1617), the onboarding gate (rc#1614), per-tenant Discord routing (rc#1643/#1661/#1668), the PR-5 schema fence (rc#1685) and per-tenant pgvector memory (#1478, rig-memory-mcp#24).
Pending (gated): secrets-broker tenant-prefixed refs (#1479), the PR-5 cutover, the dashecorp org-default activation (rig-docs#324, gated on rc#1926/#1476) and the #1486 GDPR pack. invotek is the only tenant ACTIVATED in the registry, but the DEPLOYED state is ahead of it: Rig-E (tenantId dashecorp) runs at replicaCount 1 in namespace dashecorp with its own ServiceAccount, secret and memory DB, beside a dormant dashecorp dev-e/review-e/rig-planner fleet, and the stigjohnny namespace is provisioned (review-e replicaCount 1, older image, secrets not yet in its kustomization). Only invotek is registered statically at boot — every other tenant lives in the Marten master registry table, so the live tenant list is NOT derivable from any repo.
Launch blocker: dashecorp/rig-conductor#1486. Canonical operating guide: https://research.rig.dashecorp.com/proposals/multi-tenancy/.
rig-conductor event types (POST /api/events)¶
All events from dashecorp/rig-conductor/src/ConductorE.Core/UseCases/SubmitEvent.cs MapToEvent switch. Names only here — fetch /events.md for full field schemas (no auth required).
Pipeline (issue → PR → merge → deploy): ISSUE_APPROVED, ISSUE_ASSIGNED, ISSUE_UNASSIGNED, WORK_STARTED, BRANCH_CREATED, PR_CREATED, CI_PASSED, CI_FAILED, REVIEW_ASSIGNED, REVIEW_PASSED, REVIEW_DISPUTED, HUMAN_GATE_TRIGGERED, HUMAN_GATE_REMINDER, MERGED, MERGE_GATE_WAITING, MERGE_GATE_MERGED, MERGE_GATE_TIMEOUT, MAIN_CI_STARTED, MAIN_CI_PASSED, MAIN_CI_FAILED, DEPLOYED_STAGING, DEPLOYED_PRODUCTION, SMOKE_PASSED, SMOKE_FAILED, BUILD_FAILED, VERIFIED, ISSUE_DONE, ESCALATED, MILESTONE_COMPLETE, DUPLICATE_PR_CLOSED
Direct PR path (no issue): PR_OPENED, PR_REVIEW_ASSIGNED, PR_REVIEW_APPROVED, PR_REVIEW_REJECTED
Agent lifecycle: AGENT_STARTED, HEARTBEAT, AGENT_STUCK
CLI sessions: CLI_STARTED, CLI_PROGRESS, CLI_COMPLETED
Observability (cost + tooling): TOKEN_USAGE, TOOL_USED
Memory MCP: MEMORY_WRITE, MEMORY_READ, MEMORY_HIT_USED
Known gaps (rig backlog)¶
Cold-start agents should see these so they don't re-discover what's already identified. Each gap links to prior_art — existing stubs, research, or PRs that have already touched it. When a gap is being worked, linked_user_story points to the user story; when closed, the entry is removed from facts/backlog.yaml.
[observability] Cost tracking mostly deployed — LiteLLM proxy + external access are the remaining gaps¶
DO NOT propose "build a cost pipeline" — most is shipped: (1) TokenUsageProjection + CostProjection consume TOKEN_USAGE + CLI_COMPLETED into Marten read models; (2) GET /api/usage + /api/costs/{issue,summary,daily}; (3) Dashboard.html Costs tab at / and /dashboard. Remaining: (a) LiteLLM proxy not deployed (blocks hard budget enforcement / kill-switch); (b) /dashboard is cluster-internal — no external read-only view; (c) no Discord alert on threshold breach. Rough spend ~$5-15/day fleet-wide.
Prior art: - rig-conductor src/ConductorE.Api/ — cost endpoints, Dashboard.html, Adapters/MartenProjections.cs (TokenUsage + Cost projections) - TOKEN_USAGE + CLI_COMPLETED events — see /events.md - rig-gitops/docs/whitepaper/{cost-framework,observability}.md (private)
Status: mostly-deployed
[observability] OTel collector deployed for rig-conductor only — agents not yet emitting¶
OTel Collector is Partial — deployed for rig-conductor; agent pods have not
enabled native OTel (CLAUDE_CODE_ENABLE_TELEMETRY=1). Langfuse and Grafana
Cloud ingest are Planned. Design: observability whitepaper.
Prior art: - rig-gitops/docs/whitepaper/observability.md + implementation-status.md (OTel 'Partial', Langfuse 'Planned') - Enable native OTel: CLAUDE_CODE_ENABLE_TELEMETRY=1 + OTEL_EXPORTER_OTLP_ENDPOINT at the in-cluster collector
Status: partial
[docs-memory] Docs-memory drift lint not implemented¶
Weekly LLM-as-judge pass promoting memory→docs (importance≥4 AND hit_count≥5), flagging stale research and orphan docs. Designed, not built.
Prior art: - Design: research/2026-04-18-docs-memory-drift-lint; story: user-stories/2026-04-18-docs-memory-strategy; principles: research/2026-04-18-docs-vs-memory-principles
Linked user story: user-stories/2026-04-18-docs-memory-strategy
Status: open
[docs-surfaces] Two docs surfaces with overlapping scope¶
docs.rig.dashecorp.com (MkDocs from rig-gitops/docs-site/) and research.rig.dashecorp.com (Starlight from rig-docs) both host rig docs; the boundary is not formalised, so agents learn it empirically.
Prior art: - MkDocs built by rig-gitops/scripts/build-docs.sh; Starlight in rig-docs - decisions/2026-04-18-docs-tooling-decision — Starlight for research, MkDocs for aggregation
Status: open
[agents] ATL-E retired, no active coordinator agent¶
ATL-E (legacy personal-org atl-agent) was a k3s CronJob handling
handoff-stall Discord notifications; undeployed since ~2026-03-26, repo
dormant. If an Epic needs a coordinator role, decide redeploy vs replace.
Prior art:
- Dormant personal-org atl-agent repo (last push 2026-03-26); no ArgoCD manifest in the personal-org GitOps repo
Status: open
[cleanup] Plane residue — uninstall GitHub App + archive workspace¶
Plane was retired 2026-04-18 but the makeplane GitHub App is still installed on the dashecorp org and the workspace is alive (token revoked). Manual UI.
Prior art: - Retraction decision: decisions/2026-04-18-docs-tooling-decision (What retires section) - Retirement commit: dashecorp/infra PR #74
Status: open
Architecture at a glance¶
flowchart LR
H[Human]
subgraph Code["Code repos"]
RD[rig-docs]
RG[rig-gitops]
RAR[rig-agent-runtime]
CE_R[rig-conductor]
RMM_R[rig-memory-mcp]
RT[rig-tools]
INF[infra]
end
subgraph Deployed["Deployed services + agents"]
direction TB
CE[rig-conductor svc]
RMM[rig-memory-mcp svc]
DE[Dev-E pod]
RE[Review-E cron]
IB[iBuild-E — Mac Mini]
end
subgraph Publish["Published surfaces"]
direction TB
S1[research.rig.dashecorp.com<br/>Astro Starlight]
S2[docs.rig.dashecorp.com<br/>MkDocs aggregator]
CFP[Cloudflare Pages]
end
%% Authoring + dispatch
H -->|user-story issue| RD
RD -->|dispatch| CE
CE -->|assign issue| DE
CE -->|assign PR review| RE
CE -->|assign iOS build| IB
DE -->|author PR| RD
RD -->|PR opens| RE
RE -->|approve / request changes| RD
RD -->|merge| CFP
CFP -->|publish| S1
RG -->|docs aggregation| S2
%% MCP + memory
DE -->|tool use| RMM
RE -->|tool use| RMM
IB -->|tool use| RMM
RMM_R -.implements.-> RMM
%% Flux GitOps
RG -->|Flux deploys| CE
RG -->|Flux deploys| RMM
RG -->|Flux deploys| DE
RG -->|Flux deploys| RE
%% Runtime image used by all agent deployments
RAR -.image.-> DE
RAR -.image.-> RE
RAR -.image.-> IB
CE_R -.image.-> CE
%% Per-repo docs/ feeding into the MkDocs aggregator
RG -.docs/.-> S2
RAR -.docs/.-> S2
CE_R -.docs/.-> S2
RMM_R -.docs/.-> S2
RT -.docs/.-> S2
%% Infra — outside the loop but manages everything above
INF -.OpenTofu.-> CFP
Legend: solid arrows are runtime flows (dispatch, tool calls, deploys). Dashed arrows are source-of relationships — "this repo's image powers that pod" or "this repo's docs/ feeds that site". Every rig repo from facts/repos.yaml is represented.
Conventions (rig-wide)¶
- Docs are markdown with YAML frontmatter. Required fields:
title,description,type,audience,created/updated,topic. See AGENTS.md in this repo. - Bidirectional linkage. User story ↔ research ↔ proposal → decision via
research_docs,proposal,user_story,source_research,supersedes/superseded_by. RelatedDocs component renders the graph. - Diagrams as code. Mermaid source inline in markdown. No PNG or SVG committed. Source preserved post-render via
<details>blocks. - Per-repo CLAUDE.md auto-loads when Claude Code starts a session in that repo's cwd (Claude Code reads
CLAUDE.md, notAGENTS.md— cross-vendor standard is AGENTS.md but the loader is CLAUDE.md). Same-repo local@AGENTS.mdimports work; cross-repo@owner/repo/filedoes not fetch from GitHub (filesystem-only, max 5 hops). - Rig-wide agent instructions live in TWO places: (1) each running agent's HelmRelease
character.personalityprompt (authoritative for Dev-E, Review-E in-cluster), (2) each repo's rootCLAUDE.md(authoritative for interactive sessions). Both include the BRAIN.md fetch at session start. - Closes #N required in PR bodies. Review-E blocks on this.
- Memory MCP scope: operational / ephemeral state only. Durable knowledge goes to rig-docs.
- Default to a two-PR split for feature work >500 LOC.
large-pr-okis reserved for migrations, codemods, dependency bumps, and generated code — not feature work that decomposes into policy + adapter. A/B-validated 2026-05-18: same code shipped as a labelled single PR got zero code-level feedback; the disciplined split caught 3 real bugs. Rig-side enforcement in rar#492; full decision tree in research/2026-05-18-pr-size-and-large-pr-ok-semantics. - Behavior PRs ship their doc updates in the same PR. Per-file convention: when
src/<X>.{cs,js,ts,go,py,...}changes,docs/<X>.md(if it exists) updates alongside. Rig-side enforcement in rar#497 (detectDocMismatchessurfaces a warning in the size-gate review body). - Three-layer drift-prevention playbook. When the operator catches the orchestrator drifting on a discipline recurringly + structurally observable + measurable cost: ship L1 memory rule + L2 rig-side enforcement at the trigger point + L3 durable artifact. Three instances codified the week of 2026-05-18 (PR-split shortcut, doc-staleness, main-guard rig-internal dispatch). Meta-playbook in research/2026-05-18-three-layer-drift-prevention-playbook.
Token-efficient cold start¶
When you pick up a new Epic with blank memory, the cheapest order of operations:
- Fetch this file (
https://research.rig.dashecorp.com/BRAIN.md, public, no auth) — ~57 KB. - Fetch
/llms.txtfor the research hub topic index — ~2 KB. - Identify 1-3 relevant research / proposal docs, fetch raw — ~5-15 KB.
- Fetch target repo's
AGENTS.md(each repo's is ≤8 KB) — ~5 KB. read_memoriesfrom Memory MCP scoped to repo + topic — ~2 KB.
Total cold-start context: this file (~57 KB) + ~15-25 KB of targeted fetches. Leaves the rest of the budget for actual work.
When this file needs updating¶
Manual fields that live in facts/*.yaml — update when the matching reality changes:
facts/repos.yaml— annotations only (purpose, depends_on, used_by, agents_md, docs_surface). The repo list itself is auto-derived fromgh apion every compile. Adding a new annotation, or updating an existing one, happens here.facts/surfaces.yaml— URLs, API endpoints, MCP tools. Update when an endpoint changes or a new surface is published.facts/agents.yaml— agent deployment instances. Compile validates eachmanifest:path exists on GitHub and warns on drift (how ATL-E retirement was caught).facts/flows.yaml— documented rig processes. Update after retrospectives.facts/schema.yaml— mirrors the Zod schema insrc/content.config.ts. Keep in sync manually when the schema changes.facts/events.yaml— rig-conductor event types. Keep in sync withMapToEventin the C# source.facts/backlog.yaml— known gaps. Add when identified; remove when closed.
Then run npm run brain. CI (build workflow) runs brain:check and fails on drift.