Code organization

The module map of the engine — the TypeScript conductor under src/conductor/ — plus what each layer owns, where execution enters, and which direction imports flow. For contributors changing engine code. For the operator-facing role model, see architecture.

Contribution license

AI Conductor is licensed under the Apache License, Version 2.0. Unless a contributor explicitly states otherwise, intentionally submitted contributions use the same license without additional terms. The root contribution guide records the complete submission notice and links the required branch, validation, documentation, and release-metadata workflow.

Repository layout

Path Contents
src/conductor/src/ Engine source. Five directories plus seven entry-point files.
src/conductor/test/ Vitest suite. See testing.
src/conductor/scripts/ publish-engine.mjs (the build), publish-guard.mjs, intake-label-sync-apply.mts.
bin/ Repo-root bash wrappers: ai-conductor, install, setup, update, migrate, generate-model-table, generate-docs-guard-hook, intake-backfill, quarantine-engineer-signals.
skills/ Skill catalog. See skills reference.
agents/, templates/, tech-context/ Prompt templates, scaffolding, stack knowledge.
hooks/claude/ Hook scripts. See settings and hooks.
test/ Repo-level bash scripts, including the integrity suite and lint_shell.sh. See validation.

Static-analysis configuration sits at two levels. Anything needing the TypeScript project lives inside src/conductor/; anything spanning the whole repository lives at the root.

Path Configures
src/conductor/eslint.config.mjs Type-aware ESLint over src/**/*.ts. Needs tsconfig.json and node_modules, so it lives with the package.
src/conductor/tsconfig.json npm run typecheck — src/ only; test/ is excluded.
src/conductor/tsconfig.test.json npm run typecheck:test — extends the above and adds test/.
test/lint_shell.sh The ShellCheck file set and severity. Single source of truth for both CI’s shellcheck job and integrity check 1b.
lychee.toml Documentation link checking. At the root because its inputs span docs/, the root READMEs, and src/conductor/README.md.

Layers

src/conductor/src/ has five directories. Counts are .ts files, including subdirectories.

Layer Files Owns
engine/ 238 The state machine, step catalogue, gate loop, daemon, composer-loop implementation, config resolution, self-host guardrails — essentially all domain logic.
execution/ 6 The third-party process boundary: LLM provider adapters and subprocess/session management.
types/ 6 The shared type surface: StepName, ConductState, ConductorEvent, HarnessConfig, plugin kinds.
ui/ 10 Event emitter, subscriber fan-out, terminal renderer, dashboard snapshot/text, notifications, prompt host.
tools/ 3 Build-time code generators invoked by bin/ wrappers. Nothing in the runtime imports them.

engine/

164 files sit flat in engine/; the remaining 74 are grouped into six subpackages.

Subpackage Files Owns
engine/engineer/ 25 The composer-loop implementation: authoring, routing, handoff, land-time spec and coherence gates, lesson store.
engine/engineer/intake/ 26 Intake queue, ledger, GitHub issue read/write-back, label sync, closed-issue reconciliation, filing-time evidence-path overlap preflight.
engine/self-host/ 16 Guardrails for the harness building itself: detector, write fence, sandbox build env, build auth, version gate, release gate.
engine/halt-issues/ 6 Halt-monitor issue reconciliation and its CLI.
engine/otel/ 15 OpenTelemetry config, metrics, visualizer, shared entry-point wiring, and the durable export spool (store, lease, drainer, delivery classifier).
engine/owner-gate/ 5 Multi-operator identity partitioning.

Flat engine/ files cluster by filename prefix. Use the prefix to find the subsystem:

Prefix Representative files
daemon-*, daemon.ts daemon.ts, daemon-runner.ts, daemon-backlog.ts, daemon-lock.ts, daemon-log.ts, daemon-tmux.ts, daemon-command.ts, daemon-fleet.ts, daemon-auto-park.ts
step/pipeline core steps.ts, step-runners.ts, conductor.ts, selector.ts, state.ts, artifacts.ts, skill-invocation.ts, skill-resolver.ts, complete-verifier.ts
gate* gates.ts, gate-verdicts.ts, gate-code-validity.ts, gate-invalidation.ts, gate-writeback.ts, gated-snapshot.ts
build-review-* build-review-prompt.ts, build-review-inputs.ts, build-review-disposition.ts
shipment-*, shipped-* shipment-evidence.ts, shipment-reconciliation.ts, shipped-record.ts, shipped-record-cli.ts
attribution-* attribution-audit.ts, attribution-telemetry.ts, attribution-verdict.ts, task-attribution.ts
provider-*, model policy provider-runtime.ts, provider-execution.ts, provider-selection.ts, provider-session.ts, provider-model-policy.ts, model-availability.ts, model-table-metadata.ts
config config.ts, resolved-config.ts, user-config.ts
full-suite-* full-suite-executor.ts, full-suite-verifier.ts, full-suite-evidence.ts, full-suite-fingerprint.ts
markers halt-marker.ts, park-marker.ts, pause-marker.ts, phase-marker.ts, restart-marker.ts, restart-intent.ts
plugins plugin-loader.ts, plugin-registry.ts, plugin-manifest.ts
memory memory-store.ts, memory-cli.ts, memory-migrate.ts, local-memory-provider.ts
worktree / git worktree.ts, worktree-prepare.ts, worktree-shared.ts, git-blob-batch.ts, git-hook-assets.ts

Two lookups that are easy to get wrong:

  • The build_review grader has no build-review.ts. The prompt and verdict shape live in engine/build-review-prompt.ts, the inputs in engine/build-review-inputs.ts, the routing decision in engine/build-review-disposition.ts, and verdict parsing plus the completeness rubric in engine/artifacts.ts:1080-1177.
  • The daemon’s ship-eligibility guard has no module of its own. It is inline in engine/daemon-runner.ts — isVerifiedShip at :219 and failureReasonForFalseShip at :228.
  • Backlog priority resolution is createPriorityResolver in engine/backlog-priority.ts:123.
  • The daemon pidfile lock is engine/daemon-lock.ts, not under engine/engineer/.

execution/

The only layer permitted to reach a third party in ordinary code paths.

File Owns
llm-provider.ts The port. LLMProvider exposes one invoke entry point; InvokeOptions.interactive selects operator-facing behavior and streamConsumer carries optional observation.
claude-provider.ts The claude host adapter plus its failure detectors.
codex-provider.ts The codex host adapter and JSONL parsing.
codex-self-host-auth.ts Codex credential handling for self-host builds.
pi-self-host-auth.ts Pi credential resolution into the isolated self-host home.
session.ts SessionManager.
subprocess.ts runCommand.

Tests must fake this seam rather than cross it. See testing.

types/

types/index.ts is a barrel over five modules: steps.ts (the StepName union at :1, Phase at :37, EnforcementLevel at :44, StepDefinition at :50), state.ts, events.ts, config.ts (HarnessConfig, the config schema of record), and plugin.ts (PluginKind at :8).

ui/

events.ts (ConductorEventEmitter), types.ts (UIRenderer, UISubscriber, StepSnapshot, DashboardSnapshot, ViewMode, UIPromptHost), terminal-renderer.ts, subscriber.ts, dispatch.ts, dashboard-snapshot.ts, dashboard-text.ts, live-region.ts, notifications.ts, and terminal/prompt-host.ts — the only file under ui/terminal/.

tools/

Each generator splits into pure logic plus a CLI shell, so importing the logic from a test never triggers process.exit or stdio side effects.

File Generates
generate-model-table.ts The ARCHITECTURE.md model-selection table, from engine/provider-model-policy.ts and engine/model-table-metadata.ts.
generate-docs-guard-hook.ts hooks/claude/docs-guard.sh, from engine/session-hook-assets.ts.
generate-docs-guard-hook-main.ts The direct-execution shell for the above.

Both are driven by bin/generate-model-table and bin/generate-docs-guard-hook, which run the source through src/conductor/node_modules/.bin/tsx and never touch dist/. Both use the same exit-code contract: 0 clean, 1 drift, 2 environment error.

Entry points

Seven files sit at the top level of src/conductor/src/.

File Role
index.ts The composition root and argv dispatcher. bin/ai-conductor execs dist/index.js, built from this file.
cli.ts The commander declaration surface. Builds the help text; most subcommands declared here are help-only.
daemon-cli.ts The daemon runtime. Registers zero commander commands; entered through runDaemonMode at :491, lazily imported from index.ts so non-daemon paths never load it.
intake-loop-cli.ts detectIntakeLoopCommand :49 / dispatchIntakeLoop :106, wired into index.ts.
intake-file-cli.ts Standalone main(); invoked by skills/intake/scripts/intake-file.
intake-backfill-cli.ts Standalone main(); invoked by bin/intake-backfill.
quarantine-engineer-signals-cli.ts Standalone main(); wraps engine/engineer/quarantine.ts.

main() in index.ts:390 dispatches in strict priority order, each branch calling process.exit. Subcommand detection runs first; the detectInline check at :670 is the last fallthrough, and a bare invocation with no subcommand is rejected with guidance rather than silently starting a run.

Static wiring reachability review is retired. Cover every independently invoked entry point with an appropriate CLI or integration test instead of adding a wiring.entry_points configuration block.

Dependency direction

Intended layering is types ← execution ← engine ← ui ← entry points. Measured by import grep over src/conductor/src/:

Edge Imports Notes
engine → types 74 The dominant edge.
engine → execution 17 Includes concrete ClaudeProvider / CodexProvider in engine/plugin-loader.ts.
ui → types 11  
engine → ui 14 11 are import type.
ui → engine 10 5 are import type.
types → execution 1 types/events.ts:3-9.
execution → anything internal 0 A clean leaf — execution/ imports nothing from engine/, types/, or ui/.
anything → entry points 0 No layer file imports index.js, cli.js, or daemon-cli.js.
tools → engine, types — Generators read engine metadata; the reverse never happens.

Known limitation. engine/ and ui/ import each other, so the layering above is not enforceable as a one-way rule. Engine-side value imports: engine/conductor.ts:61 and engine/event-persister.ts:4 (ConductorEventEmitter), engine/plugin-loader.ts:8-9 (TerminalSubscriber, TerminalRenderer). UI-side value imports: ui/terminal-renderer.ts:8,10 (getArtifactStatus, STEP_ARTIFACT_GLOBS, formatProgressDelta), ui/terminal/prompt-host.ts:14 (getRecoveryOptions). Moving a symbol between the two layers can therefore create a runtime initialization cycle that the type checker will not flag. Tracked in #1017.

Known limitation. types/events.ts:3-9 imports AuthenticationReadinessState, AuthenticationSource, CodexProbeFailureKind, CodexProbeParserRejection, and TokenUsage from ../execution/llm-provider.js — the only outbound edge from types/, and it inverts types ← execution. It is a type-only import, so it has no runtime effect, but types/ is not the dependency-free leaf the layering implies. Tracked in #1017.

Build output

npm run build in src/conductor runs scripts/publish-engine.mjs, which stages the bundle, finalizes it into an immutable dist-versions/<id>/, and atomically flips the dist symlink. Running npx tsup directly is refused: tsup.config.ts:8 calls assertPublishWrapperEnv(process.env), because raw tsup output would clobber the versioned layout. Node is pinned to 26.7.0 by src/conductor/.tool-versions.

Extending any of this

Adding a step, gate, skill, hook, or CLI command touches a fixed set of registration points. See extending.


This site uses Just the Docs, a documentation theme for Jekyll.