agentleFS
Sign inSign up

Vigla

Kilbex/Vigla/llms-full.txt

Canonical source: https://github.com/Kilbex/Vigla Five coding agents should not mean five terminals, five diff reviews, and five unread merges. Vigla gives the work one operations room and gives you one job: set the authority envelope, then judge the verdict. Vigla is local-first: a Rust orchestrator, Tauri shell, and SQLite event store, with no cloud control plane, product account, or product telemetry. It supervises the command-line tools you already use; it does not wrap model APIs or add another billing layer. Recovery…

llms.txt116 starsChanged 2 months ago
  • Reads credentials
  • Installs packages
<!-- Generated by scripts/build-site.mjs from README.md and ARCHITECTURE.md. -->
<!-- Edit the source documents, then run: node scripts/build-site.mjs --write-llms -->

# Vigla — full project context

Canonical source: https://github.com/Kilbex/Vigla

## README

<p align="center">
  <img src="docs/media/social-preview.svg" width="1280" alt="Vigla field dispatch: four isolated coding-agent work lines converge through an audit gate into one reversible merge" />
</p>

<p align="center">
  <strong>Open-source mission control for coding agents.</strong><br />
  Run cross-vendor workers in parallel worktrees. Audit every submission.
  Revert the whole mission when the result is wrong.
</p>

<p align="center">
  <a href="https://kilbex.github.io/Vigla/demo/"><strong>Open the 16-second replay</strong></a>
  &nbsp;·&nbsp; <a href="https://kilbex.github.io/Vigla/">Field notes</a>
  &nbsp;·&nbsp; <a href="#build-a-local-dmg">Build on macOS</a>
  &nbsp;·&nbsp; <a href="./ARCHITECTURE.md">Read the architecture</a>
  &nbsp;·&nbsp; <a href="./CONTRIBUTING.md">Take an open task</a>
</p>

---

Five coding agents should not mean five terminals, five diff reviews, and five
unread merges. Vigla gives the work one operations room and gives you one job:
set the authority envelope, then judge the verdict.

| WORK | WATCH | VERDICT | REVERT |
|---|---|---|---|
| Claude Code, Codex CLI, Antigravity, and profile-backed CLIs share one mission | Each worker gets an isolated git worktree and typed event stream | A supervisor checks scope, reversibility, risk, and quality before merge | The accepted mission can be undone with a normal Git revert commit |

Vigla is local-first: a Rust orchestrator, Tauri shell, and SQLite event store,
with no cloud control plane, product account, or product telemetry. It
supervises the command-line tools you already use; it does not wrap model APIs
or add another billing layer.

> **Recovery receipt — 27/27.** Every seeded failure trajectory escalated
> within the default retry bounds. Reproduce the credential-free case set with
> `cargo xtask receipt`, then inspect the public
> [method, data, and limitations](https://github.com/Kilbex/Vigla/blob/main/docs/evidence/recovery-receipt.md).

## Try the no-install replay

[Open the read-only browser replay](https://kilbex.github.io/Vigla/demo/) to
step through accepted, bound-tripped, and quota-paused missions in the real
Operations Room UI. The three event streams are hand-authored canonical
`event-schema` events committed in `app/src/demo/recordings.ts` — a scripted,
deterministic replay, not a capture of a real vendor session. The UI is the
production one; no account, vendor CLI, credential, or network-backed agent is
involved.

### Run the credential-free demo locally

Vigla ships a mock supervisor, so you can watch a whole mission —
decomposition, parallel workers, integration, audit, verdict — with no vendor
CLI, no credentials, and no token spend:

```sh
git clone https://github.com/Kilbex/Vigla.git && cd Vigla
pnpm install --frozen-lockfile
./scripts/dev.sh
```

When the Operations Room window opens:

1. In the **DEPLOY WORKERS** panel, describe the work under *What should the
   team do?*
2. Click **Choose folder…** and pick a Git repository.
3. Expand **Advanced**.
4. Set the **Supervisor** dropdown to **Mock (demo)** — the only other choice
   is **Claude**.
5. Click **Start mission**. A notice above the button confirms the mock is
   armed.

The choice is sticky per machine and defaults to **Claude**, so the demo is
opt-in and nobody gets it by accident.

**The mock mission is not a dry run.** It spends nothing and reads no
credential — no vendor CLI process is launched and no API key or login is
read — but it drives the real mission machinery against the folder you chose:
it creates a supervisor branch and worktree, a branch and worktree per worker,
writes files, and makes real `git commit`s, then integrates each worker's work
and stops at a pending-merge decision you **Merge** or **Discard** from the
mission overlay. (The review queue is a different surface: it triages
individual worker submissions, not the mission verdict.) Point it at a scratch
repository or a branch you don't mind.

It shows the happy-path arc only. Every task takes a clean first pass by
construction, so a mock mission can never produce a blocked, failed,
retry-exhausted, or quota-exhausted trajectory, and Plan mode *Review* has no
effect on it — the mock runtime never pauses for plan approval. The unhappy
paths live in two other places:

- the [browser replay](https://kilbex.github.io/Vigla/demo/), which scripts
  accepted, bound-tripped, and quota-paused missions;
- the seven bundled mock-harness scripts — `claude_happy`, `codex_blocked`,
  `gemini_happy`, `gemini_blocked`, `gemini_failed`, `gemini_terminal`,
  `claude_quota_exhausted` — each of which drives a single **worker**, not a
  mission. Run `mock-harness --help` for the trajectory each one takes, or
  spawn them from **Settings → Developer → Mock spawn** in a development build
  (`import.meta.env.DEV`, or `VITE_VIGLA_E2E=1`).

Prereqs: macOS 12+, [Rust 1.95](https://github.com/Kilbex/Vigla/blob/main/rust-toolchain.toml) via rustup,
Node 22, pnpm 10, Xcode Command Line Tools. See
[CONTRIBUTING.md](https://github.com/Kilbex/Vigla/blob/main/CONTRIBUTING.md#fast-start) for the full setup.

## How it works

**1 — Assign a mission inside an envelope.** Describe the goal, choose
the worker roster and models, and set the authority envelope. In review
mode the supervisor proposes a plan first — task graph, file scope, risk
fit — and waits for your approval:

<div align="center">

<img src="docs/media/plan-review.png" width="640" alt="Vigla plan review: the proposed task graph is checked against the Scope, Reversibility, Risk, and Quality bounds before any agent starts" />

</div>

**2 — The supervisor arbitrates; you stay out of the loop.** Workers
execute in parallel worktrees while the supervisor reviews each
submission and decides **Accept / Extend / Scrub / Escalate** — inside
your envelope, without pinging you. Live state, diffs, tests, cost, and
raw terminals are always one click away if you *want* to watch.

<div align="center">

<img src="docs/media/ops-room.png" width="900" alt="Vigla Operations Room: five coding-agent workers progress in parallel while one completed submission waits in the review queue" />

</div>

**3 — You judge results, not keystrokes.** Finished missions land in
your inbox with a structured verdict: audit score, test results, files
changed, residual-risk band, unresolved issues — and a revert button
that undoes the whole mission atomically:

<div align="center">

<img src="docs/media/mission-inbox.png" width="900" alt="Vigla mission inbox: a merged mission with audit breakdown, subtask status, low-risk verdict, and one-click revert" />

</div>

The vocabulary is small and precise — *mission*, *worker*, *envelope*,
*arbiter*, *verdict* — and defined in [docs/lexicon.md](https://github.com/Kilbex/Vigla/blob/main/docs/lexicon.md).

## How it compares

The table is positioning, not a feature checklist. Each cell is backed
by a primary source below; cells are re-verified before each release.

*Sources verified 2026-07-21.* “Not documented” means the linked product
documentation does not describe that capability; it is not a claim that an
internal implementation is impossible.

| | Vigla | Codex app (OpenAI) | Claude Code on desktop (Anthropic) |
|---|---|---|---|
| Cross-vendor worker roster | yes | no; Codex agents | no; Claude agents |
| Parallel local agent sessions | yes | yes | yes |
| Isolated git worktrees | one per worker | built in | automatic or manual |
| Bound-based supervisor audit across workers | yes | not documented | not documented |
| Atomic mission merge + revert | yes | not documented | not documented |
| Deterministic demo without a vendor account | yes | not documented | not documented |

<details>
<summary><b>Sources</b></summary>

- **Vigla.** See this README and [ARCHITECTURE.md](https://github.com/Kilbex/Vigla/blob/main/ARCHITECTURE.md) for positioning
  and goals; [LICENSE](https://github.com/Kilbex/Vigla/blob/main/LICENSE) (Apache 2.0).
- **Codex app (OpenAI).**
  [Introducing the Codex app](https://openai.com/index/introducing-the-codex-app/)
  documents parallel agents, local and cloud work, and built-in worktree
  support.
- **Claude Code on desktop (Anthropic).**
  [Claude Code on desktop](https://code.claude.com/docs/en/desktop)
  documents parallel sessions and automatic worktree isolation; the
  [worktrees guide](https://code.claude.com/docs/en/worktrees) covers the
  underlying isolation flow.

</details>

## Vendor support

Real workers are driven from the in-app Deploy panel; the mock harness
covers demos and CI. Two independent tiers of evidence back a vendor, and
they are not interchangeable:

- **Adapter goldens (CI).** Committed transcript-and-golden pairs run through
  the shared `vigla-adapter-conformance` harness on every pull request. They
  pin the byte-to-event contract. They prove nothing about the vendor binary.
- **Real-CLI gate (local, opt-in).** An `#[ignore]`d integration test that
  spawns the actual CLI against a failing fixture repository and asserts the
  agent fixed the defect. It needs a working binary and credentials, so **no
  CI workflow runs it** — a maintainer runs it by hand.

| Vendor | Binary | Role | Real-CLI gate (local, opt-in) | Adapter goldens (CI) |
|---|---|---|---|---|
| Claude Code | `claude` | supervisor + worker; session retry / continue | `real_claude_gate.rs`, `supervisor_live.rs` | 6 conformance cases + `from_fixture.rs` |
| Codex CLI | `codex` | worker | `real_codex_run.rs`, `supervisor_live.rs` | 4 conformance cases + `from_fixture.rs` |
| Antigravity | `agy` | profile-backed worker | `real_antigravity_run.rs` | 4 conformance cases |
| Gemini CLI | `gemini` | legacy / enterprise worker | `supervisor_live.rs` | 4 conformance cases + `from_fixture.rs` |
| Kiro | `kiro-cli` | profile-backed worker | none yet | 4 conformance cases |
| GitHub Copilot | `copilot` | profile-backed worker | none yet | 4 conformance cases |

Google ended consumer **Login with Google** access for Gemini CLI on
2026-06-18. Vigla retains the adapter for existing enterprise and legacy
configurations, but Gemini CLI is no longer a primary launch path. Google
directs affected consumer users to Antigravity in its
[official deprecation notice](https://developers.google.com/gemini-code-assist/docs/deprecations/code-assist-individuals).

**Mission supervision is Claude-only today.** The Supervisor dropdown offers
exactly two values — *Claude* (spawns the real `claude` CLI) and *Mock (demo)*
(the scripted runtime, no vendor process) — and `host_services` rejects any
other `supervisor_model` with `UnsupportedSupervisorModel`. There is no
experimental non-Claude supervisor to opt into; a second *real* supervisor is
[roadmap work](https://github.com/Kilbex/Vigla/blob/main/ROADMAP.md). Vigla does not pin vendor CLI versions — the
launch path verifies each configured binary, and the real-CLI gates track
adapter compatibility:

```sh
cargo test -p vigla-orchestrator --test real_claude_gate -- --ignored --nocapture
cargo test -p vigla-orchestrator --test real_codex_run   -- --ignored --nocapture
cargo test -p vigla-orchestrator --test real_antigravity_run -- --ignored --nocapture --test-threads=1
VIGLA_LIVE=1 cargo test -p vigla-orchestrator --test supervisor_live -- --ignored --nocapture
```

`VIGLA_LIVE=1` is not optional on the last line: without it every test in
`supervisor_live.rs` prints a skip line and passes, so the gate looks green
without having run.

The Claude and Codex gates use `tests/samples/sandbox/`, a workspace-excluded
crate with a deliberately wrong `multiply` function. The Antigravity gate
creates the same kind of isolated failing Rust fixture in a temporary
repository. Each of those three asserts that the agent fixed the defect. The
supervisor gate builds its own throwaway repositories instead — a wrong `add`,
a broken test, and a docs task — and asserts that a real `claude` supervisor
drives each mission to a completed, audited verdict.

## Feature tour

<details>
<summary><b>Mission launch and supervision</b></summary>

- **Deploy panel** — supervisor profile, worker vendor roster, worker
  count, model selections, plan mode, objective, folder, and optional
  scoped paths.
- **Plan governance** — direct mode, or review-first mode showing the
  generated plan, task graph, worker assignments, file scope, and
  envelope checks with approve / regenerate / abort.
- **Arbiter-driven supervision** — one supervisor per mission decides
  Accept / Extend / Scrub / Escalate without pausing inside the
  envelope.
- **Structured completion verdicts** — `CompletionVerdict` scores test
  pass, scope, regression, and lint into a residual-risk band
  (Low / Medium / High) with the full audit breakdown in the inbox.
- **Reversibility envelope** — task integrations and the final target merge
  receive distinct rollback anchors; `Revert mission` preserves later commits.
- **Inspectable aborts** — abort retains Vigla-owned branches and worktrees for
  diagnosis; the explicit `Clean up artifacts` action removes them later without
  changing the target branch.

</details>

<details>
<summary><b>Worker execution</b></summary>

- **Real CLI and mock workers** — Claude Code, Codex CLI, Antigravity,
  legacy Gemini CLI, profile-backed Kiro / Copilot, plus deterministic mock
  workers for development and demos.
- **Isolated worktrees** — each worker gets its own git worktree and
  branch; work is inspected, merged, discarded, or reverted without
  touching your main checkout.
- **Canonical event stream** — adapter-normalized status, cost, file,
  test, review, and terminal events (`event-schema`).
- **Session-aware recovery** — Claude workers support retry and
  follow-up continuation; quota windows can pause missions and resume
  them when the window reopens; failures are classified for retry,
  continuation, or escalation.

</details>

<details>
<summary><b>Operations room</b></summary>

- **Station canvas** — one tile per worker with dependency edges, live
  status, task, model, progress, ETA, cost, file and test counters.
- **Worker drawer** — result, feed, terminal, files, tests, cost, and
  plan tabs; stop, retry / continue, switch models, assign squads.
- **Review queue** — workers needing review surface as actionable cards
  with open, retry, continue, accept, and reject flows.
- **Live terminal capture** — raw stdout/stderr preserved alongside
  normalized events; a power-user feed with every event is one toggle
  away.
- **Squads** — group workers, designate leads, color-code the fleet.

</details>

<details>
<summary><b>Inbox, history, and replay</b></summary>

- **Mission inbox** — completions, escalations, side effects,
  unresolved issues, audit breakdowns, and revert eligibility in one
  right rail; macOS notifications when a bound trips while the app is
  unfocused.
- **Mission history** — browse audited missions with status, risk
  tier, and full drill-down.
- **Worker replay** — page through event history, play / pause, step,
  scrub, change speed, and return to live.

</details>

<details>
<summary><b>Memory and context</b></summary>

- **Local Memory Kernel** — repository-scoped memory written through a
  single-writer path and attached to future workers as context bundles.
- **Pinned notes + auto-promoted insights** — pin facts, decisions, and
  hazards; completed work proposes durable notes after validation.
- **Safety filters** — memory writes are size-limited, schema-checked,
  secret-scanned, and drift-checked.
- **Vendor-native files as render targets** — `CLAUDE.md`, `AGENTS.md`,
  and `GEMINI.md` are generated projections, never the source of truth.

</details>

## Built in public

| Now | Next | Later |
|---|---|---|
| Harden real-CLI gates, local packaging, first run, and regression coverage | Verify more adapters and supervisors; prove [Mac App Store sandbox feasibility](https://github.com/Kilbex/Vigla/blob/main/docs/roadmap/mac-app-store.md) | Add Linux and Windows parity, richer memory provenance, reusable missions, and a public fleet benchmark |

Priorities move with operator evidence and focused contributions. See the
[full roadmap](https://github.com/Kilbex/Vigla/blob/main/ROADMAP.md) for acceptance boundaries and the work Vigla
deliberately will not take on.

## Tech stack

| Layer | Choice |
|---|---|
| Desktop shell | Tauri 2 (Rust host) |
| Orchestrator | Rust 1.95, `tokio`, `sqlx` (SQLite), `tracing` |
| UI | React 19, Vite, TypeScript, Tailwind v4, Zustand |
| Canvas & terminal | React Flow (`@xyflow/react`), xterm.js |
| IPC | `tauri-specta` typed bindings (Rust → TS) |
| Tests | cargo test, Vitest, Playwright |

The only abstraction Vigla permits is the event boundary: vendor CLI
bytes → canonical events. Adapters live in `crates/adapters/{vendor}`, one
crate per vendor, pure translation — no I/O, no process spawning, no
git.

<details>
<summary><b>Storage paths</b></summary>

| Path | What | Override |
|---|---|---|
| `~/Library/Application Support/Vigla/vigla.sqlite` | Worker events, missions, workers, audit summaries, quota state | `VIGLA_DB_PATH` |
| `<repo>/.vigla/memory/memory.sqlite` | Memory index (notes, witnesses, bundles), one store per repo | rooted at the repo's canonical git root, not under `VIGLA_DB_PATH` |
| `<repo>/.vigla/memory/notes/` | Long-term memory note bodies (Markdown), alongside the index | same repo root |
| `~/Library/Logs/Vigla/vigla.log.YYYY-MM-DD` | Rolling daily structured logs | managed by `tracing-appender` |

The desktop app is macOS-only, so those are the paths you will actually
see. The orchestrator crate is portable and resolves the database
against the host platform's own convention, which is what the Linux CI
job exercises: `$XDG_DATA_HOME/vigla/vigla.sqlite` (default
`~/.local/share/vigla/vigla.sqlite`) on Linux, and
`%APPDATA%\Vigla\vigla.sqlite` on Windows. `VIGLA_DB_PATH` overrides
all of them.

</details>

<details>
<summary><b>Repo layout</b></summary>

```
vigla/
├── app/                  # Tauri 2 + React 19 + Vite + TS
│   ├── src/              # React UI, Zustand stores, hotkeys
│   └── src-tauri/        # Tauri host (Rust): IPC, event forwarding
├── crates/               # All Rust library/bin crates (Cargo workspace)
│   ├── orchestrator/     # Rust supervision crate (business logic)
│   │   ├── src/memory/               # Memory Kernel (event-sourced)
│   │   ├── src/mission_runtime/      # Mission state machine + replay
│   │   ├── src/mission_supervisor_run/ # Supervisor turns + review loop
│   │   ├── src/supervisor/           # Worker process lifecycle + resume
│   │   ├── src/arbiter/              # Bound-based escalation decisions
│   │   └── resources/                # Bundled at compile time:
│   │       ├── vendor_profiles/      #   per-vendor command-rendering policy
│   │       └── skills/               #   worker skill set (embedded)
│   ├── adapters/         # One pure crate per vendor CLI + supervisor
│   ├── event-schema/     # Canonical typed event contract
│   ├── mock-harness/     # Mock vendor CLI (credential-free demos)
│   └── xtask/            # Workspace task runner (cargo xtask)
├── tests/                # Playwright e2e specs + real-CLI sample targets
├── docs/                 # Lexicon, good-first-issues, media
└── scripts/              # dev.sh, build.sh, capture-readme-media.cjs
```

</details>

Deep dive: [ARCHITECTURE.md](https://github.com/Kilbex/Vigla/blob/main/ARCHITECTURE.md) covers the orchestrator,
memory kernel, mission lifecycle, arbiter / audit / recovery, event
schema, and per-vendor adapters.

## Requirements

- **macOS 12+.** Linux and Windows are on the [roadmap](https://github.com/Kilbex/Vigla/blob/main/ROADMAP.md) —
  the non-host Rust workspace is built, linted, and tested on Linux in CI;
  desktop packaging and platform UX are scoped on the roadmap.
- Development: Rust 1.95 (pinned via `rust-toolchain.toml`), Node 22.x,
  pnpm 10.x, Xcode Command Line Tools.
- Vendor CLIs are optional and only needed for real (non-mock) workers.

## Build a local DMG

Vigla publishes no maintainer-built binaries today: there is no signing
identity, notarization credential, or update channel to trust, and the only
supported artifact is the one your machine produces. (A Mac App Store release
would replace that trust model with Apple's; whether to make that trade is
[undecided and under investigation](https://github.com/Kilbex/Vigla/blob/main/docs/roadmap/mac-app-store.md).) On a
Mac, clone the source and run one command:

```sh
./scripts/build.sh
```

The script installs the locked frontend dependencies, builds the application,
ad-hoc signs it without an Apple account or personal signing identity, verifies
the app and disk image, and prints the DMG path and SHA-256 checksum. The local
artifact remains under `target/release/bundle/dmg/`; no workflow uploads it.

Keep the printed checksum with the artifact. [SECURITY.md](https://github.com/Kilbex/Vigla/blob/main/SECURITY.md#verifying-a-local-build)
documents the independent `shasum`, `hdiutil`, and `codesign` checks.

Prerequisites are the development tools listed in [Requirements](#requirements).
Set `EMBEDDINGS=1` when running the command to include the optional embeddings
feature. Its first use downloads the public FastEmbed model into the per-user
cache; if that download is unavailable, retrieval falls back to local BM25.

## Known limitations

Design trade-offs in the current build, not bugs:

- **Real supervisor execution is Claude-only.** `supervisor_model` accepts
  `claude` (the real CLI) or `auto` (the scripted mock); everything else is
  rejected. Cross-vendor applies to workers, not to the supervisor.
- **Memory retrieval is local and best-effort.** Alias-expanded BM25
  with optional embedding / hybrid re-ranking; degrades to lexical
  retrieval instead of blocking workers.
- **The supervisor sees typed mission events, not raw worker
  dialogue.** By design — escalation is bounded on outcomes, not
  chain-of-thought.
- **Session resume requires vendor session-ID support.** CLIs that
  don't expose a session ID can't be continued across app restarts.

## Why "Vigla"?

**Vigla** (Byzantine Greek *βίγλα*, "watchpost" — from Latin *vigilia*,
the root of *vigilance*) was the imperial guard regiment that kept the
night watch on campaign: it posted the sentries, held the watchword,
and ran the signal line so the emperor could actually sleep. That is
this app's entire job — a supervisor that keeps trained watch over a
fleet of powerful agents and wakes you only when something crosses a
bound.

In category terms: Vigla is an open-source control plane for supervised
agent operations — coordinating fleets of AI coding agents instead of
babysitting them one terminal at a time.

## Contributing

Start with [CONTRIBUTING.md](https://github.com/Kilbex/Vigla/blob/main/CONTRIBUTING.md) and
[ARCHITECTURE.md](https://github.com/Kilbex/Vigla/blob/main/ARCHITECTURE.md); project authority and maintainer
succession are explicit in [GOVERNANCE.md](https://github.com/Kilbex/Vigla/blob/main/GOVERNANCE.md).
Newcomer-friendly tasks live in
[docs/GOOD_FIRST_ISSUES.md](https://github.com/Kilbex/Vigla/blob/main/docs/GOOD_FIRST_ISSUES.md) — adapter
fixture work is the recommended first PR and is designed to land in
under two hours.

Questions and bug-report routing: [SUPPORT.md](https://github.com/Kilbex/Vigla/blob/main/SUPPORT.md). Security reports:
see [SECURITY.md](https://github.com/Kilbex/Vigla/blob/main/SECURITY.md). Reviewing or recording Vigla? The public
[creator kit](https://github.com/Kilbex/Vigla/blob/main/docs/operations/creator-kit.md) provides a 10-minute script,
credential-free inputs, media, evidence, and exact claim boundaries.
Operators coming from vibe-kanban can use the
[concept-by-concept migration guide](https://github.com/Kilbex/Vigla/blob/main/docs/migrations/from-vibe-kanban.md);
it does not claim a database importer or kanban-board parity.

## License

[Apache 2.0](https://github.com/Kilbex/Vigla/blob/main/LICENSE), with the project [NOTICE](https://github.com/Kilbex/Vigla/blob/main/NOTICE). Bundled component
attribution and retained licenses are in
[THIRD_PARTY_NOTICES.md](https://github.com/Kilbex/Vigla/blob/main/THIRD_PARTY_NOTICES.md); the generated,
self-contained [THIRD_PARTY_NOTICES.txt](https://github.com/Kilbex/Vigla/blob/main/THIRD_PARTY_NOTICES.txt) covers the
locked production Rust and JavaScript dependency graphs.

---

<div align="center">

If Vigla looks useful, **a ⭐ helps other agent operators find it.**

</div>

## Architecture reference

# Vigla Architecture

Vigla is split into a Tauri host, a React frontend, and a Rust
orchestrator. The core rule is simple: **UI hosts integrate; the
orchestrator owns business behavior.** Adapters are pure line-by-line
translators with no IO. Persistence and process management live in the
orchestrator. This document is the canonical design reference; the root
[`README.md`](https://github.com/Kilbex/Vigla/blob/main/README.md) covers *what* Vigla is, *how to install
and run it*, and *how to compare it* to alternatives — but defers all
design depth to this file.

File pointers below are paths from the repo root, verified on 2026-07-21.

## High-Level Flow

```mermaid
flowchart LR
    UI["React UI"] --> IPC["Tauri commands/events"]
    IPC --> Host["app/src-tauri host glue"]
    Host --> Services["orchestrator host_services"]
    Services --> Runtime["MissionRuntime"]
    Runtime --> Workspace["MissionWorkspace git worktrees"]
    Runtime --> SupervisorRun["Supervisor-driven mission loop"]
    SupervisorRun --> WorkerDispatch["Real/mock worker dispatch"]
    WorkerDispatch --> Adapters["Vendor adapters"]
    Adapters --> Events["event-schema canonical worker events"]
    Events --> Repo["Repository SQLite worker-event persistence"]
    Runtime --> MissionEvents["mission event bus + bounded replay"]
    MissionEvents --> AuditHistory["persisted audit summaries"]
    Events --> UI
    MissionEvents --> UI
```

## Crates and Responsibilities

| Area | Path | Responsibility |
| --- | --- | --- |
| Frontend | `app/src/` | React UI, keyboard handling, visual mission/worker state |
| Tauri host | `app/src-tauri/src/lib.rs` | IPC registration, app setup, typed event forwarding |
| Host services | `crates/orchestrator/src/host_services.rs` | Host-independent validation, mission lifecycle locking, backend routing |
| Mission runtime | `crates/orchestrator/src/mission_runtime/` | Mission state machine, mock timeline, event replay, merge/abort/resolve |
| Supervisor loop | `crates/orchestrator/src/mission_supervisor_run/` | Real/scripted supervisor turns, prompts, budget events, worker review loop |
| Worker supervisor | `crates/orchestrator/src/supervisor/` | Standalone worker process lifecycle, retry coordination, resume support |
| Git workspace | `crates/orchestrator/src/mission_workspace/mod.rs` | Mission branches, worktrees, integrations, final merge/discard |
| Event schema | `crates/event-schema/` | Canonical typed event contract shared by adapters and UI |
| Adapters | `crates/adapters/*` | Pure line-by-line translation from vendor CLI streams to canonical events |
| Vendor profiles | `crates/orchestrator/resources/vendor_profiles/` | Command rendering policy for supported CLIs |

## Adapter Boundary

Adapters are the main contribution surface.

An adapter:

- Receives one stdout/stderr line at a time.
- Maintains only local parser state such as sequence number, current
  session id, or accumulated assistant text.
- Emits zero or more canonical `event_schema::Event` values.
- Does not spawn processes, read or write files, call git, or persist
  data.

Process management belongs in `crates/orchestrator/src/supervisor/` and
`crates/orchestrator/src/mission_worker_dispatch.rs`. Persistence belongs in
`crates/orchestrator/src/repository/mod.rs`.

## Memory Kernel — `crates/orchestrator/src/memory/`

Local, event-sourced long-term memory. Six anchoring design choices:

| # | Design choice | Where |
|---|---|---|
| 1 | **Single-writer to project memory.** Vendor native files (`CLAUDE.md`/`AGENTS.md`/`GEMINI.md`) are render *targets*, never read as truth. | `memory/mod.rs` (module doc) |
| 2 | **Event-sourced.** Witnesses are append-only; confidence is *derived* by `scoring.rs`, not stored. Weight changes need no migration. | `memory/witnesses.rs` (module doc) |
| 3 | **Anchored block.** Kernel owns one delimited region per native file; everything outside is preserved byte-exact across writes. | `memory/coherence.rs` (module doc) |
| 4 | **Per-repo isolation.** A registry opens one kernel per canonical repo root at `<repo>/.vigla/memory/memory.sqlite`. | `memory/registry.rs` (module doc) |
| 5 | **Pre-event secret scanning.** Patterns + 20-char-window entropy detector run *before* `MemoryProposed` persists. | `memory/scanner.rs` (module doc) |
| 6 | **Fail-soft attach.** Errors from listing / composing / rendering are swallowed + logged; memory must never block mission dispatch. | `memory/attach.rs` (module doc) |

**Phase status** (`memory/mod.rs` phase table). P0/P1 shipped. P2 completed the
closed loop: *worker proposes → supervisor ratifies → mission accept promotes →
next mission's composer picks it up*. P3 is also complete: alias-expanded BM25,
optional local MiniLM embeddings, MMR diversity, retrieval-driven composition,
and BM25-only graceful degradation all ship behind stable interfaces.

**Note state machine** (states from `event_schema::memory::NoteState`):

```
Owned ──── (supervisor ratify + confidence ≥ τ_kind) ────► Promoted
   │                                                          │
   │ scrub barrier         conflict signal                    │ demote
   ▼                       ▼                                  ▼
Invalid                  Disputed                         (back to Owned)
```

**Confidence formula** (`scoring.rs::confidence`) — pure function over witness
rows:

```
raw = WIT_W · Σ(witness.weight)
    + AGE_W · recency_bonus(witnesses, now)      # half-life 90 days
    − CONF_W · conflict_penalty(witnesses)
confidence = sigmoid(raw)         # ∈ (0, 1)
```

Coefficients `WIT_W = 1.0`, `AGE_W = 0.2`, `CONF_W = 0.5`
(`scoring.rs`: `WIT_W`, `AGE_W`, `CONF_W`).

**Promotion thresholds** are kind-asymmetric (`policy.rs::fallback_threshold`):

| Kind | Threshold | Floor with user-authored |
|---|---|---|
| `hazard` | 0.55 | 0.50 |
| `fact` | 0.70 | 0.50 |
| `procedure` | 0.75 | 0.50 |
| `decision` | 0.85 | 0.50 |
| (unknown) | 0.90 | — |

The **user-oracle fast path** (`policy.rs` module doc;
`USER_AUTHORED_FAST_PATH_BAR = 0.5`) treats the effective bar as
`min(τ_kind, 0.5)` when a `UserAuthored` witness is present —
preserves the *"talking to Vigla teaches it"* promise.

**Submodule responsibilities:**

| File | Responsibility |
|---|---|
| `kernel/` | Facade. Sub-files: `types`, `ratify`, `barrier`, `proposal`, `pin`, `compose`, `sweep`, `query`. |
| `store.rs` | T3 long-term store. Atomic same-dir tmp+rename; `prepare_note` + `mint_note_in_tx` split for ratify atomicity. |
| `composer.rs` | Deterministic manual assembly plus the shared rendering path used after retrieval and MMR selection. |
| `attach.rs` | Mission-lifecycle bridge. Composes + renders into worker worktree. Fail-soft. |
| `coherence.rs` | Anchor span finder + writer + drift detection. |
| `adapter.rs` + `adapters/` | `MemoryAdapter` trait + Claude/Codex/legacy Gemini renderers. Pure transforms. |
| `witnesses.rs` | Append-only signal store. `(note_id, kind, source_event_id)` unique. |
| `scoring.rs` | Stateless confidence sigmoid. |
| `policy.rs` | Promotion thresholds + user-oracle fast path. |
| `reflection.rs` | Post-mission consolidation. `on_accept` / `on_scrub`. Idempotent per `(mission_id, kind)`. |
| `scanner.rs` | Pre-event secret detection (fixed patterns, ≥ 4.0 bits/char over a 20-char entropy window, and a 32-char contiguous-hex rule). |
| `intent_router.rs` + `intent_sink.rs` | Pure router from worker `MemoryIntent` → kernel `on_proposal`. |
| `registry.rs` | Per-repo kernel pool. |
| `handoff.rs` | Cross-worker structured notes for DAG-downstream tasks. |
| `archive.rs` | Tier-2G cold storage (zstd JSONL.zst). |
| `context_match.rs` | BM25/optional-embedding context matching with a substring compatibility fallback. |
| `retrieval/` | Tokenization, aliases, BM25, optional embeddings, hybrid scoring, vector storage, and MMR. |

**Storage layout:**

```
<repo>/.vigla/memory/
├── memory.sqlite            # index: memory_notes, memory_witnesses,
│                            # memory_links, memory_provenance,
│                            # memory_taxonomy, memory_events,
│                            # memory_bundles, memory_handoffs
├── notes/<note_id>.md       # full note bodies (frontmatter + body)
├── missions/<mission_id>/
│   ├── pending.jsonl.zst    # archived after mission barrier
│   └── bundles/<worker_id>/<turn>.md
└── events-archive/
    └── YYYY-MM.jsonl.zst    # monthly rollup past retention
```

## Context System

The supervisor's surface for getting the right memory in front of the
right worker at the right time.

| Piece | What it does | File |
|---|---|---|
| **Composer** | Manual or retrieval-selected bundle assembly. Same ordered note IDs ⇒ same `bundle_hash`; budget overflow drops the tail. | `memory/composer.rs` (module doc) |
| **Attach** | Post-worktree-create / pre-dispatch injection. Builds a retrieval brief from mission/task/handoff context, retrieves and renders promoted notes, then falls back to manual budgeted composition on failure. Fail-soft. | `memory/attach.rs` |
| **`MemoryAdapter` trait** | Pure transforms per vendor (`native_file_name`, `anchor_open/close`, `max_tokens`, `render_block_body`). | `memory/adapter.rs::MemoryAdapter` |
| **Context-request loop** | Worker emits `RequestContext { kind, detail }` → ranked promoted-note match (BM25 plus optional embeddings) → match supplied next turn; a miss escalates. | `memory/context_match.rs` |
| **Drift detection** | `find_anchor_span` + `detect_drift` at the start of each worker turn. Outcomes: `Drift` / `AnchorMissing` / `FileMissing`. | `memory/coherence.rs::find_anchor_span` / `::detect_drift` |

**Context-request flow:**

```
worker emits RequestContext
       │
       ▼
context_match::match_context (BM25 + optional embedding ranking;
                              substring compatibility fallback)
       │
       ├── Found ──► supplied via next-turn rework-directive channel
       │
       └── Missing ──► MissionEventKind::ContextRequestUnmet
                       └─► ArbiterDecided { bound: Some(Scope), evidence }
                           (user sees the gap in the inbox)
```

**Budgets** (`memory/hierarchy.rs` constants):

| Constant | Value | Meaning |
|---|---|---|
| `T1_MAX_TOKENS_DEFAULT` | 1200 | Per-worker T1 token budget |
| `FAULT_BUDGET_PER_MISSION` | 8 | `memory.fetch` requests before kernel denies |
| `NOTE_BODY_CAP_BYTES` | 4096 | Atomic notes; encourages splitting |

Budget events surface as `MissionEventKind::ContextBudgetExceeded` /
`ContextBudgetTruncated`.

## Skills — `crates/orchestrator/src/skills/`

Curated procedural playbooks injected into each worker before it starts — a
separate, simpler sibling of the Memory Kernel that reuses the same
native-file anchor-block injection *pattern* without the event-sourcing,
witnesses, or confidence machinery. Where memory is *learned*, skills are
*authored and enabled*.

| Piece | What it does | File |
|---|---|---|
| **Library** | Loads a bundled curated set (compiled via `include_str!` from the crate’s `resources/skills/`) plus user skills from `<repo>/.vigla/skills/<id>/SKILL.md`; a user `id` shadows the bundled one. File-based — no SQLite, no migration. | `skills/library.rs`, `skills/bundled.rs` |
| **Format** | `SKILL.md` = `---` frontmatter (`name`, `description`, `scope`, `enabled`, `priority`) + markdown body, parsed by a dependency-free single-line-scalar parser. An unrecognized `scope` falls back to repo scope (skill stays available, never silently vanishes). | `skills/library.rs::parse_skill` |
| **Selection** | Enabled skills whose `scope` is `repo` or the worker's vendor, ordered `priority` desc then `id` asc (deterministic). Mirrors memory's promoted-note Tier-2B selection. | `skills/library.rs::select_for_worker` |
| **Render** | Deterministic body into a **second** anchor region `<!-- vigla:skills:begin v1 -->` (distinct from `vigla:memory`), token-budgeted (`SKILLS_TOKEN_BUDGET = 4000`, tail-dropped, first skill always kept). | `skills/render.rs` |
| **Attach** | Fail-soft bridge: after memory attach, writes the skills region by reusing the pure, parameterized `memory::coherence::write_anchor_block`; logs and returns on any error — **never blocks dispatch**. | `skills/attach.rs` |

**Lifecycle & threading.** The library is resolved once per mission in
`host_services::start_mission` (`SkillLibrary::open_for_repo`, no registry —
loading a few files is cheap) and threaded as `Option<Arc<SkillLibrary>>`
parallel to the memory kernel, down to `TaskRunCtx`. `attach_skills_for_worker`
runs immediately **after** memory attach at all three worker-dispatch sites
(initial spawn, rework, vendor fallback) in `mission_supervisor_run/run_task.rs`;
the sequential awaits serialize the two writes to the same native file. The two
anchored regions coexist because the anchor writer is parameterized on its
delimiters and preserves every byte outside its span — so skills never touch
the memory region or user content. Success emits the telemetry-only
`MissionEventKind::SkillsAttached { worker_id, skill_ids, tokens, dropped }`
(routed `Internal`, like `ContextBundleComposed`) for the operator's trust trail.

**Out of scope (later layers, mirroring memory's roadmap):** per-mission manual
equip, relevance/retrieval selection, a skill-management UI, and native
`.claude/skills/` provisioning.

## Worker (Employee) Management

Six concerns. Each one is a separate file or directory.

### A. Process lifecycle — `crates/orchestrator/src/supervisor.rs`

`Supervisor` owns running children + cancellation handles + a
`session_ids` map captured once per worker and persisted to the
repository. `SupervisorError` enumerates the public failure surface
(`supervisor.rs::SupervisorError`): `UnknownScript`, `MockHarnessMissing`,
`WorkerNotFound`, `WorkerStillRunning`, `ResumeUnsupported(Vendor)`,
`SessionIdMissing`, `Io`, `Repository`.

### B. Per-worker supervision loop — `crates/orchestrator/src/supervisor/adapter_supervision.rs`

Single-threaded line-pump feeds **both stdout and stderr** into one
adapter instance (no `Mutex`). Lines capped at `MAX_LINE_BYTES`; a
`stderr_eof` flag prevents `select!` tight-loop on a closed stream;
`session_id_captured` ensures `set_session_id` runs exactly once with an
explicit warning logged on persist failure.

### C. Real-CLI dispatch — `crates/orchestrator/src/mission_worker_dispatch.rs`

Three things this module does that nothing else does:

1. **Spawns real profile-backed vendor CLIs** inside the worker worktree.
   Antigravity's production route is covered by the opt-in local real-CLI gate
   in `crates/orchestrator/tests/real_antigravity_run.rs`, which is
   `#[ignore]`d and runs in no CI workflow.
2. **Commits on the worker's behalf** — workers are forbidden from
   running `git`. Concentrating commits in the orchestrator gives
   atomicity (one commit per submission), boundary clarity ("done" =
   process exit), and safety (worker can't push / switch branches /
   commit partial state).
3. **Streams stdout/stderr** through the same adapter pipeline as the
   standalone supervisor — mission-spawned and standalone real-CLI
   workers are observable identically.

Routing (`mission_supervisor_run/worker_pass.rs`):

- `worker_model = None | "auto"` → task-role routing to a real CLI
- A registered vendor (`claude`, `codex`, `antigravity`, `kiro`, `copilot`,
  or legacy `gemini`) → that real CLI
- A comma-separated roster → one real CLI per task index, cycling as needed
- Invalid selections are rejected pre-spawn at the host IPC.

`DEFAULT_WORKER_TIMEOUT = 300s`; captured output capped at 64 KB with a
truncation marker; 250 ms post-exit drain.

### D. Session + resume — `crates/orchestrator/src/supervisor/resume.rs`

`continue_worker` requires (in order):

1. Worker exists.
2. Worker is not currently running (else `WorkerStillRunning`).
3. Vendor supports resume — **today only `Vendor::Claude`**.
   Every other registered vendor explicitly returns
   `ResumeUnsupported(...)`.
4. Worker has a saved `session_id` (else `SessionIdMissing`).

### E. Vendor profiles — `crates/orchestrator/src/vendor_profile.rs` + `crates/orchestrator/resources/vendor_profiles/*.json`

Single source of vendor-specific CLI launch flags + declared side
effects. JSON profiles bundled via `include_str!`. `CommandRole`
(worker/supervisor) + `CommandVars` + `render_command_args` keeps
vendor-specific template strings out of runtime code.

### F. Scope ACL — `crates/orchestrator/src/acl/`

`MissionSpec.scope_paths` declares the worker's permissible write
surface. Enforcement is two-tier: (1) sentinel written to
`.vigla/acl.json` inside the worktree, paired with
`.vigla/.gitignore` (`*`) so the worker's `git add -A` doesn't
sweep the sentinel into the mission commit; (2) post-commit diff check
trips `AuthorityBound::Scope` on any out-of-scope write.

## Mission Lifecycle

Mission startup is intentionally host-independent:

1. A host calls `MissionController::start_mission`.
2. `host_services` validates the working directory, enforces one
   active mission, creates a `MissionWorkspace`, and selects mock vs
   real supervisor/worker backends.
3. `MissionRuntime` owns state transitions and event replay.
4. The host subscribes to `MissionEventReceiver` and forwards events
   through its UI transport.

This prevents desktop-specific code from owning mission policy and keeps
additional platform hosts from duplicating business logic.

**States** (`crates/orchestrator/src/mission.rs::MissionState`):

```text
Created → Executing ⇄ PendingPlanApproval
             │            └─ reject_plan → Aborted
             ├─⇄ Reviewing
             ├─⇄ Paused { reason }       (PauseReason names the vendor; automatic quota resume)
             ├─→ Attention               (user chooses merge/discard)
             └─→ CompletePendingMerge → Merged | Discarded

Any non-terminal state ── abort ──→ Aborted
```

`Completed` is an event emitted before final disposition; it is not a
`MissionState`. `Extended` remains a historical wire shape only. Current review
controls expose Merge and Discard because supervisor re-entry is not yet a tested
runtime path.

**Event kinds** (`crates/orchestrator/src/mission_event/mod.rs::MissionEventKind`)
are grouped by concern (representative variants shown):

| Group | Variants |
|---|---|
| **Lifecycle** | `Created`, `ExecutionStarted`, `Decomposition`, `WorkerSpawned`, `WorkerResultSubmitted`, `Integrated`, `Completed`, `Aborted`, `WorkerProgress` |
| **Plan approval** | `PlanProposed`, `PlanConfirmed`, `PlanRegenerationRequested`, `PlanRejected`, `DecompositionRejected` |
| **Arbiter** | `ReviewStarted`, `AuditCompleted`, `ArbiterDecided`, `PostIntegrationAuditCompleted` |
| **User action** | `MissionReverted`, `MergeResolved`; `MissionExtended` is decode-only compatibility |
| **Recovery** | `RecoveryDecided`, `MissionPaused`, `MissionResumed`, `ContextBudgetExceeded`, `ContextBudgetTruncated`, `ContextRequestUnmet` |
| **Memory** | `HandoffNote`, `CompletionVerdictRendered`, `SubSupervisorRefused` |
| **Other** | `SideEffectLogged`, `TestResult` |

**Runtime** (`crates/orchestrator/src/mission_runtime/`): `mock.rs` is the
scripted task per MSV spec; the real path lives in
`crates/orchestrator/src/mission_supervisor_run/`. Event bus is a broadcast
channel with replay for late subscribers (`MAX_HISTORY = 2048`).

**Workspace** (`crates/orchestrator/src/mission_workspace/mod.rs`): one git
worktree per worker under `.vigla/worktrees/<mission-id>/`, with branches in the
`vigla/<mission-id>/...` namespace. Workers integrate serially into
`vigla/<mission-id>/supervisor`; an explicit final action merges that branch into
the mission's validated local `target_ref`. A pre-integration tag protects each
staged task merge. Final merge also records durable `before` and `merged` tags
for the target branch, then removes the mission worktrees and branches. The
user-facing rollback applies a normal Git revert to the recorded merge commit,
so commits added afterward remain intact — that is `revert_merged_mission`, the
only path the shipping Tauri command uses.

`revert_mission` additionally covers the not-yet-merged case: with no final
anchors it rewinds the supervisor branch to the mission's branch point, which
`create_supervisor_branch` records once as `refs/vigla/base/<mission-id>`. It
falls back to the lowest-index `vigla/pre-merge/<mission-id>/N` tag only for
missions branched before that ref existed. The `/0` snapshot is *not* reliably
the branch point: integration indexes are handed out when the task is spawned
onto the `JoinSet`, while integration itself is serialised first-come by a
mutex, so a higher-index task can tag the branch point first.

Ref namespaces the mission owns:

| Ref | What |
|---|---|
| `refs/heads/vigla/<mid>/...` | supervisor and per-worker branches |
| `refs/tags/vigla/pre-merge/<mid>/N` | pre-integration snapshot per staged task merge |
| `refs/tags/vigla/snap/...` | intermediate mission snapshots |
| `refs/tags/vigla/revert/...` | durable `before` / `merged` target-branch anchors |
| `refs/vigla/base/<mid>` | branch point of the supervisor branch |

`refs/vigla/base/<mid>` is deliberately a plain ref rather than a tag, so it
stays out of `git tag --list`, `git branch`, and the default push refspec.
`discard` deletes it with the pre-merge and snap tags; the durable
`vigla/revert/...` anchors survive. Abort intentionally retains mission
artifacts for diagnosis. A separate,
durably tracked cleanup action is authorized only by an `aborted` outcome with
the exact recorded repository identity; it removes the mission's worktrees,
branches, and intermediate tags without touching the target branch.

## Arbiter + Judgment + Audit + Recovery

**Arbiter** (`crates/orchestrator/src/arbiter/`) — pure policy function.
Consumes `AuditReport`, emits `ArbiterDecision`. No IO, no vendor calls
(`arbiter/mod.rs` module doc).

Four authority bounds (`arbiter/bound.rs::AuthorityBound`):

| Bound | Trips when |
|---|---|
| **Scope** | Worker touched files outside declared `scope_paths` |
| **Reversibility** | Snapshot creation failed or merge target unreachable |
| **Risk** | A risk detector tripped (schema migration, mass deletion, secret-touching change) |
| **Quality** | Audit composite below policy floor AND rework budget exhausted |

Priority order (`arbiter/mod.rs::decide`): Scope → Risk → Quality. Scope
and Risk always escalate; Quality is recoverable via rework budget.

Decisions (`arbiter/decision.rs::ArbiterDecision`): `Accept(payload)`,
`Extend { rework_kind, attempts_remaining }`, `Scrub { reason,
retained_artifacts, partial_audit }`, `Escalate { bound, evidence,
suggested_user_action }`.

**Judgment** (`crates/orchestrator/src/judgment/`) — mission-level
"is this done?" verdict. Pure module; emitted as
`MissionEventKind::CompletionVerdictRendered`.

Risk band boundaries (`judgment/risk_band.rs::score_risk`):

- `Low` ⇐ overall ≥ 0.85 AND zero security flags AND quiet recovery
  (total < 3 occurrences)
- `High` ⇐ overall < 0.7 OR > 1 security flag
- `Medium` ⇐ otherwise (residual)

Recovery activity **only pushes the band up** — busy history bumps
`Low` to `Medium` but cannot demote `High`.

**Audit** (`crates/orchestrator/src/audit/`) — entry point `audit_submission`.
Five sub-scorers blended by `composite::blend_overall` with a
`WeightProfile`:

| Scorer | Measures |
|---|---|
| `test_pass` | Configured or detected test-run outcome |
| `scope` | Diff stays within `scope_paths` |
| `regression` | Newly-failing tests vs. newly-passing |
| `lint` | Linter compliance |
| `security` | `SecurityFlagKind` (mass deletion, schema migration, secret-touching, …) |

`AuditTier` selects which scorers run, and `audit_submission` branches on
exactly one boundary: Smoke runs only the pure scope and security functions (no
subprocess); Standard and Deep both run the project's test runner and linter,
and add regression only when a baseline was captured, so a missing baseline
cannot inflate the composite. Standard and Deep are therefore identical in what
they execute today — the distinction exists in `AuditTier::auto_select`'s
thresholds (any risk hit, or >20 files / >1000 lines, selects Deep), which the
mission loop does not yet call: `run_task.rs` passes
`ArbiterPolicy::default_audit_tier`, a constant `Standard`. Every supervised
mission pass audits at Standard.

**Recovery** (`crates/orchestrator/src/recovery/`) — `quota.rs` owns
per-vendor rolling-window state, persisted to `vendor_quota_state`
(migration 0009). Default windows: Claude 5h, every other registered real
vendor 1h, Mock 100 ms. `QuotaSignalSource::AdapterParsed` vs. inferred — the
adapter parses a vendor-specific quota error and supplies an explicit
reset, or the tracker fills in `now + default_window_ms`. Host restart
reads `estimated_reset_at_ms` and either resumes immediately or
schedules the wake-up. Surfaces as `MissionEventKind::MissionPaused` →
`MissionResumed`.

## Persistence and Event Model

Worker events follow `event-schema`. The repository stores canonical event
payloads and worker/task metadata in SQLite. Unknown event types are tolerated
on replay so older builds can inspect newer logs without crashing. Mission
events are separate and broadcast through `MissionRuntime` with bounded replay
for late subscribers. A dedicated subscriber persists worker and mission audit
summaries into `audit_reports`, using source-event timestamps, so cross-mission
History does not depend on an open frontend listener.

**Repository** (`crates/orchestrator/src/repository/mod.rs`) — SQLite via sqlx;
pool `POOL_ACQUIRE_TIMEOUT = 5s`, per-connection
`SQLITE_BUSY_TIMEOUT = 3s`. File-pool max 5 connections. Migrations
0001–0020 in `crates/orchestrator/migrations/`, with an upgrade-path test
at `crates/orchestrator/tests/migration_upgrade.rs`. The same migration set is
applied to the application-support database and to each per-repo memory
database (`MemoryKernel::open_for_repo`); the migration range quoted here is
pinned to the highest-numbered file by `scripts/architecture-facts.test.mjs`.

## Cross-Cutting

**Event schema** (`crates/event-schema/`) — runtime-free crate (only `serde` +
`specta`). Closed `Vendor` set: `Claude`, `Codex`, `Gemini`,
`Antigravity`, `Kiro`, `Copilot`, `Opencode`, `Mock`. Aider removed in schema 2.0 (major bump; `aider_removed.rs`
test locks this). Envelope is `{schema_version, worker_id, task_id,
seq, ts, type, payload}`. The memory submodule re-exports the canonical
memory vocabulary (`MemoryEvent`, `MemoryNoteAuthored`,
`MemoryPromoted`, `MemoryProposed`, `MemoryRatified`, `MemoryBarrier`,
`MemoryWitnessRecorded`, `NoteKind`, `NoteState`, `Scope`,
`WitnessKind`, `BarrierKind`).

**Adapters** (per-vendor crates):

- `crates/adapters/core` — `Adapter` trait + `MemoryIntent` extraction
- `crates/adapters/claude` — `claude -p --output-format stream-json` parser
- `crates/adapters/codex` — `codex --json` parser
- `crates/adapters/antigravity` — production Antigravity raw-log adapter;
  its opt-in local real-CLI gate exercises the full spawn, event, submission,
  integration, and verification path
- `crates/adapters/kiro` — Kiro raw-log adapter with terminal synthesis
- `crates/adapters/copilot` — Copilot raw-log adapter with terminal synthesis
- `crates/adapters/gemini` — maintained legacy Gemini stream parser
- `crates/adapters/supervisor` — **different shape.** Parses
  Claude-running-the-playbook into `SupervisorIntent` envelopes
  (`decompose`, `spawn_worker`, `review`, `declare_complete`) instead
  of canonical events. Audit and test gates run automatically; the supervisor
  doesn't edit files; it semantically reviews a bounded committed-diff excerpt
  after every real worker pass and makes mission-level decisions which map to
  mission events on a separate channel
  (`crates/adapters/supervisor/src/lib.rs` module doc).

**Mock harness** (`crates/mock-harness/`) — bundled scripts for
credential-free demos: `claude_happy`, `codex_blocked`, `gemini_happy`,
`gemini_blocked`, `gemini_failed`, `gemini_terminal`,
`claude_quota_exhausted`.

## Testing Strategy

- Adapter crates are fixture-driven. All six shipping vendor adapters run
  through the shared `vigla-adapter-conformance` harness with committed
  transcript/golden pairs; `claude`, `codex`, and `gemini` add
  `from_fixture.rs` suites over `tests/fixtures/*.jsonl`.
- Orchestrator business logic should be testable without Tauri.
- Tauri host tests should focus on IPC-adjacent glue and platform
  probes.
- Real-CLI tests are `#[ignore]`d and run in no CI workflow: they require an
  installed binary and local credentials, so a maintainer opts in by hand.

Useful commands:

```sh
cargo xtask test                            # self-contained: builds the release
                                            # mock-harness, then cargo test --workspace
cargo test -p vigla-orchestrator --all-targets
cargo test -p vigla-host --lib
cd app && pnpm exec vitest run
```

`cargo xtask test` is the self-contained entry point — the Tauri host bundles
`target/release/mock-harness` as a resource that `tauri_build` validates on
every compile, so a bare `cargo test --workspace` fails from a clean tree until
that binary exists. `cargo xtask {build,clippy,ci}` cover the other gates.

## Adding a New Vendor Adapter

1. Add or copy a crate under `crates/adapters/<vendor>/`.
2. Implement `adapter_core::Adapter`.
3. Add transcript fixtures built only from line types the parser
   dispatches on. Synthetic is fine and is what every shipping adapter
   uses; a real capture must be redacted and must contain no token,
   account, or private path.
4. Add parser tests for normal completion, failure,
   cancellation/finalize behavior, and session id capture if the CLI
   supports resume.
5. Add a vendor profile under `crates/orchestrator/resources/vendor_profiles/` when the worker can
   be spawned by Vigla.
6. Wire the worker vendor routing only after parser tests are stable.

Discussion

Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.

Posts are public.Sign in to post

No one has posted yet. Be the first.