Architecture: Python module graph
tach.toml freezes
the Python module graph, and CI runs uv run python scripts/check_tach.py. An import between
modules that is not a declared depends_on edge fails the check. Modules cover
src/ (entrypoints, launch, headless, server.*, vibesys.*) and every libs/*/src.
Run it through the wrapper, not tach check directly: tach treats a library with
its own pyproject.toml (a uv workspace member) as a separate package and stops
checking its edges and interfaces, and the wrapper runs tach on a copy of the
source roots without those files.
The graph below is generated from tach.toml by tach show --mermaid. CI
fails when it is stale. To refresh after editing tach.toml:
uv run python scripts/check_tach_graph.py --write
A package-level overview with submodules collapsed to their top-level package.
The graph is acyclic and tach.toml forbids cycles.
Every direct subfolder of src/vibesys/orchestration/ is one orchestration
strategy: dynamic, evolve, issue_queue, multi, or single. Shared
policy and resources (domains, hypothesis, profile_focus,
prompts, and metrics) live beside orchestration under src/vibesys/ and
never import it. Domain templates live in their domain package; strategy
templates live in their strategy package. The shared prompts package owns
only shared rendering and templates. A strategy may exclusively own a run shell
when every source consumer of that shell belongs to the strategy. Shared run
composition and peer strategies cannot import such a shell. Its downward policy
interfaces and library dependencies remain declared in Tach. Top-level orchestration modules remain
shared policy helpers; they are not strategy packages.
A strategy written as a flow (today dynamic) has this layout inside its folder:
search/: the@searchroot flow, itsRoledeclarations beside it, its config, prompt contexts and the pure helpers its flows share.search/api.pyis the only public surface; other modules start with an underscore and nothing outside the package imports them. Code here reachesvs_coreonly throughvs_core.api.search.core_policy/: host-side policy that reads the widervs_core.api(evidence requirements, prompt variables, the projection of the publishedRunSnapshot), with the prompt templates.launch.pyandmodels.py: the options an operator writes (the search config plus the task's inputs) and the reply models.
What a task fixes (gates, metric space, stage limits) is vs_core.api.TaskTerms on the run's
facts, not search config. The old strategy/ state-machine package is gone for dynamic; the
other strategies keep it until they are ported. See orchestration-runtime.md
and flows.md; tests/architecture/test_search_packages.py enforces the rules.
src/vibesys/ holds strategies, domain resources, prompts, and thin wiring.
The generic pure lifecycle lives in vs_core, published only through
vs_core.api: immutable state, events, requests, strategy contracts and projections.
The vs_runtime async shell calls strategy decisions and core transitions
separately, persists intent before I/O, and returns typed observations.
vs_core has no I/O-library dependencies. Infrastructure mechanisms belong in
vs_runtime and its libraries. Placement
test: "Would another product built on vs_runtime need this mechanism, with no
VibeSys policy in it? Then it belongs in a library, not src/vibesys."
Design stateful systems as a pure core that emits requests and a thin async
shell that calls I/O interfaces and returns typed outcomes as events. Each
owning library declares its interface in its .api and holds interchangeable
implementations inside it or in smaller libraries it depends on. The shell
imports the core and owning libraries; the core imports no I/O library. Follow
the software-design rule
Functional core, interfaces and implementations
for durable intent, recovery, and tests.
Execution environments, command runners, and agent confinement have separate owners; see Sandboxing and confinement.
Long-running work writes its output to vs_logs, which keeps each stream under a typed
owner for humans to read. vs_logs sits in the tach layer logs, above every library, so
no library imports it; only the product writes logs and only server.host_helper reads
them (vs_logs.read). See Log streams.
The TypeScript clients under clients/ have their own package graph, layers, and
build model; see Clients architecture.
vibesys.orchestration owns built-in orchestration policy. Explicit plugins,
including the issue queue, live under the singular
vibesys.orchestration.<plugin> namespace; launch assembles their built-in
catalog from individual registrations exposed by vibesys.api.catalog.
vibesys.plugin_catalog retains the explicit registry contract.
Agent roles, plan and reply schemas, prompts, and pure strategy decisions
live with their owning orchestration; generic lifecycle and recovery decisions
belong in the pure core. For example, the
hypothesis planner's skill-selection and title rules are public through
vibesys.hypothesis, not a top-level schema catch-all. Generic
run roles live in vibesys.api: Runs starts and resumes independent
RunHandle tasks, which publish semantic events, accept stop requests, and
return results. launch.default_runs selects VibeSys implementations;
vs-runtime owns the generic task and event-stream mechanism. Only
entrypoints, tests and scripts import launch. Headless receives one started
handle; the server receives Runs. The transitional vibesys.api.session
contract retains queries, readiness and auxiliary-agent access;
workspace, persistence, and sandbox mechanisms live in the runtime libraries.
The vibesys.api package root is policy-neutral. Applications opt into a
built-in policy through a named facade such as vibesys.api.hypothesis or
vibesys.api.evolve; the generic facade does not re-export those contracts.
Tach records each dependency. vs_project owns generic project layout and
persistence operations. Hypothesis-search history and its serialization belong
to vibesys.hypothesis; they are policy contracts, not generic lifecycle types.
The v5 manifest separates policy-specific descriptor options from the generic
execution record. The latter is derived from RunRequest and resolved host
settings, including the concrete profiler. Resume checks it before setup.
Trusted evaluator execution and package mechanics live in the runtime and
sandbox libraries. Orchestration owns gate cadence and result interpretation.
Libraries are converted to uv workspace members bottom-up (a library converts
once every library it imports is a member). A member's pyproject.toml declares
exactly the third-party packages its src/ imports, and reaches other members
through [tool.uv.sources] <name> = { workspace = true }.
scripts/check_member_dependencies.py fails CI on an undeclared or unused
dependency, an undeclared member edge, or an import of a root-distribution
package. The vibesys wheel still bundles every library's source, so each
member dependency must also appear in the root dependencies with the same
version constraint.
The strategy and plugin contract is in orchestration-runtime.md.
A search is @search(name, config=Model) over a flow (straight-line async code that the
runtime journals and replays). It follows flows.md: the ctx vocabulary (agents,
workspaces, evaluation, groups), the rule for flow code, how core ends the run from the winner
the flow chose, and how to test it.
Architecture overview
Submodules such as vibesys.orchestration and server.api are collapsed into their top-level package.
graph TD
entrypoints --> headless
entrypoints --> launch
entrypoints --> server
entrypoints --> vibesys
entrypoints --> vs_agent
entrypoints --> vs_decode
entrypoints --> vs_github
entrypoints --> vs_issue_tracker
entrypoints --> vs_logs
entrypoints --> vs_mcp
entrypoints --> vs_project
entrypoints --> vs_runtime
entrypoints --> vs_sandbox
entrypoints --> vs_sim
entrypoints --> vs_slurm
headless --> vibesys
launch --> vibesys
launch --> vs_agent
launch --> vs_evaluation
launch --> vs_github
launch --> vs_mcp
launch --> vs_project
launch --> vs_runtime
launch --> vs_sandbox
launch --> vs_sim
launch --> vs_slurm
server --> vibesys
server --> vs_async_ops
server --> vs_decode
server --> vs_logs
server --> vs_project
server --> vs_prompts
server --> vs_redaction
server --> vs_sim
vibesys --> vs_agent
vibesys --> vs_core
vibesys --> vs_decode
vibesys --> vs_evaluation
vibesys --> vs_evaluator_protocol
vibesys --> vs_github
vibesys --> vs_issue_tracker
vibesys --> vs_logs
vibesys --> vs_mcp
vibesys --> vs_project
vibesys --> vs_prompts
vibesys --> vs_redaction
vibesys --> vs_runtime
vibesys --> vs_sandbox
vibesys --> vs_sim
vibesys --> vs_slurm
vs_agent --> vs_decode
vs_agent --> vs_faults
vs_agent --> vs_mcp
vs_agent --> vs_project
vs_agent --> vs_prompts
vs_agent --> vs_sandbox
vs_agent --> vs_sim
vs_async_ops --> vs_sim
vs_dst --> vs_core
vs_dst --> vs_project
vs_dst --> vs_runtime
vs_dst --> vs_sim
vs_evaluation --> vs_decode
vs_evaluation --> vs_evaluator_protocol
vs_evaluation --> vs_mcp
vs_evaluation --> vs_project
vs_evaluation --> vs_sim
vs_evaluator_protocol --> vs_decode
vs_faults --> vs_decode
vs_faults --> vs_sim
vs_github --> vs_decode
vs_github --> vs_sim
vs_issue_tracker --> vs_core
vs_issue_tracker --> vs_decode
vs_issue_tracker --> vs_github
vs_issue_tracker --> vs_sim
vs_logs --> vs_decode
vs_logs --> vs_sim
vs_project --> vs_decode
vs_project --> vs_sim
vs_runtime --> vs_agent
vs_runtime --> vs_async_ops
vs_runtime --> vs_core
vs_runtime --> vs_decode
vs_runtime --> vs_evaluation
vs_runtime --> vs_evaluator_protocol
vs_runtime --> vs_github
vs_runtime --> vs_issue_tracker
vs_runtime --> vs_mcp
vs_runtime --> vs_project
vs_runtime --> vs_prompts
vs_runtime --> vs_redaction
vs_runtime --> vs_sandbox
vs_runtime --> vs_sim
vs_runtime --> vs_slurm
vs_sandbox --> vs_decode
vs_sandbox --> vs_evaluation
vs_sandbox --> vs_project
vs_sandbox --> vs_redaction
vs_sandbox --> vs_sim
vs_sandbox --> vs_slurm
vs_slurm --> vs_decode
vs_slurm --> vs_sim