Architecture — Current State (v0.1.1, 2026-05-21)

Living snapshot. Update at every minor version cut. Last refresh: end of EXECUTION_MASTER_PLAN Phases 1-10.

Three-layer model

L3  INTERFACE       CLI · Cockpit (React + PWA) · GitHub PRs
        ↑
L2  SERVICES        agents · adapters · schedulers · WebSocket push
        ↑
L1  SUBSTRATE       markdown + git (one repo per company)

The substrate is the load-bearing decision. Every other layer is swappable, but only because the substrate is just files.

Repository topology

Two distinct repos at runtime:

  1. The Lycato source repo (this workspace) — code that produces the lycato CLI, @lycato/cockpit web bundle, adapters, agents, the public @lycato/sdk.
  2. The company repo — a separate folder per company, scaffolded by lycato init. Holds WHO/WHAT/WHEN, decisions/, journal/, money/, people/, inbox/, memory/, investors/, support/, content/, .founder/, Lycato/cache/.

The CLI and cockpit always operate against a company repo, never the source repo.

Source repo layout

packages/
  schemas/                @lycato/schemas             Zod schemas
  core/                   @lycato/core                parse/write, git, money, paths, search,
                                                     attention, memory, provenance, reconcile,
                                                     policy, keys, index-cache, approvals,
                                                     runs, migrations, modules, llm/
  sdk/                    @lycato/sdk                 PUBLIC. Adapter/Agent/Module types +
                                                     defineX helpers + LlmClient re-export
  templates/              @lycato/templates           renderKernel + renderDemoSeed
  adapters/
    mercury, github, plain, visible, stripe-atlas, stripe
  agents/
    daily-digest, finance-close, investor-update, content-queue,
    kpi-snapshot, decision-draft, notifier
apps/
  cli/                    @lycato/cli                 oclif CLI (`lycato` binary); topic dirs
  cockpit/                @lycato/cockpit             React 19 + Vite + Tailwind + PWA;
                                                     Fastify api/ with WebSocket
  hosted/                 (no package.json — not a workspace package)
                          web/ deployed hosted browser app + site shells
                          test/ vitest + Firebase-emulator suites

19 workspace packages. apps/hosted is not one of them: it is plain browser JS copied into the docs-site output by scripts/build-docs.mjs. Workspace via pnpm-workspace.yaml. ESM throughout (NodeNext, verbatimModuleSyntax, exactOptionalPropertyTypes).

The installable npm boundary is the coordinated CLI runtime closure documented in DISTRIBUTION.md; the CLI is not released independently of its Cockpit, core, schema, template, adapter, and worker packages.

Core module surface (post-Phase-10)

Module Purpose
parse / write gray-matter + Zod for typed markdown
repo / paths repo root resolution + canonical paths
git simple-git wrapper with prior-branch restore
money / reconcile ledger parsing + Stripe↔Mercury reconcile
search / index-cache on-disk index for cmd-K
attention / memory / provenance founder-facing read helpers
policy / approvals risk scoring + editable flag + approval markers
action-digest create → approve → consume gate for scheduled agents that write company truth with no branch and no diff; backs lycato operator approvals / approve / deny
agent-schedule / notify / backup cron slot matching + missed-minute catch-up · notification triggers and channels · portable lycato-backup-v1 archives
keys BYO key registry in .founder/keys.env
runs JSONL run-ledger in .founder/runs/YYYY-MM.jsonl
migrations forward-only schema migrations + CURRENT_REPO_VERSION
modules .founder/modules.yml + built-in saas/services/d2c
telemetry opt-in local event log + recordRun / recordUsage helpers
llm/{anthropic,openai,gemini,factory} provider-agnostic LLM client

Company repo topology

WHO.md  WHAT.md  WHEN.md          founder + product + obligations
decisions/                        accepted/proposed decisions
journal/daily/                    daily-digest output (agent-authored)
inbox/                            unprocessed signals (+ POST /api/inbox writes here)
people/                           pre-memory typed people
memory/people|companies|projects|topics/   file-per-entity memory
money/ledger/<yyyy-mm>.md         normalised transactions
money/reconcile-<yyyy-mm>.md      reconcile output
investors/<yyyy-mm>.md            investor-update agent output
support/tickets/<id>.md           Plain adapter output
content/queue/<date>-<channel>-<slug>.md  content-queue agent output
cap-table/                        equity pool + grants
.founder/config.yml               company metadata
.founder/adapters/<name>.yml      per-adapter config
.founder/keys.env                 BYO API keys (gitignored, chmod 600)
.founder/policy.yml               risk-aware approval policy + editable flag
.founder/reviews/<branch>.yml     approval markers
.founder/runs/<yyyy-mm>.jsonl     run-ledger (gitignored by default)
.founder/modules.yml              enabled modules registry
.co-md-version                    schema version stamp
Lycato/cache/index.json            derived search index (gitignored)

Data-flow diagram (text)

External SaaS (Mercury, Stripe, GitHub, Plain, Visible, Stripe Atlas)
      │  https (BYO key)
      ▼
adapter.client  ─── injectable fetch ─── transform ─── pull
      │  writes (idempotent by external id)
      │  appendRun() → .founder/runs/YYYY-MM.jsonl
      ▼
Company repo (markdown files)
      │  reads
      ▼
agent.run       ─── LlmClient (Anthropic | OpenAI | Gemini) ─── writes draft
      │  appendRun() → .founder/runs/YYYY-MM.jsonl
      │  writes to a "proposal" path or branch
      ▼
chokidar watcher  → /api/ws  → cockpit useLive(kinds, refresh)
      │  cockpit Review page expands inline diff (LCS)
      │  optional inline edit (policy.editable) → POST /api/file commits
      ▼
Git diff
      │  Founder approves via:
      │    a) Cockpit "Approve" button         →  .founder/reviews/<branch>.yml
      │    b) `lycato review approve <branch>`     →  .founder/reviews/<branch>.yml
      │  then manual `git merge`
      ▼
Company truth (main branch)

CLI surface (v0.1.1)

lycato init [--demo]
lycato doctor                                       # now reports schema version
lycato migrate [--dry-run] [--to <ver>] [--json]    # NEW Phase 3
lycato search <query>
lycato morning [--auto-sync]
lycato obligations [-d <substr>]
lycato sync [--dry-run] [--only <step>] [--json]
lycato logs [--limit] [--kind] [--name] [--since] [--json]   # NEW Phase 2
lycato audit [--provenance] [--json]
lycato reconcile [--month <n>] [--year <n>] [--dry-run]
lycato reindex
lycato close [--month <n>] [--year <n>] [--provider <p>]
lycato serve [--interval <sec>] [--skip-cockpit] [--skip-sync]   # NEW Phase 9
lycato cockpit
lycato module list                                  # NEW Phase 8
lycato module add <name>                            # NEW Phase 8
lycato module remove <name>                         # NEW Phase 8
lycato money summary | runway
lycato people add | list
lycato decide | decide list
lycato equity grant | pool | cap-table
lycato mem <key> | mem ls | mem find | mem write
lycato keys | keys set <name> <value>
lycato inbox | inbox view | inbox triage
lycato operator investor-update [--month] [--year] [--provider]
lycato operator content-queue [--provider]
lycato import notion | gmail-mbox | linear
lycato review [--base <branch>]
lycato review approve <branch> [--approver] [--note]

Cockpit API (v0.1.1)

Method Path Returns
GET /api/health { ok, repoRoot, version }
GET /api/attention AttentionSummary
GET /api/summary balance + monthly net
GET /api/decisions last 10 decisions
GET /api/people all people
GET /api/inbox sorted inbox
POST /api/inbox NEW capture: { title, body?, source? } → inbox file
GET /api/runs run-ledger entries (rewritten Phase 2)
GET /api/review proposal branches with risk + approval state
POST /api/review/:branch/approve record approval marker
GET /api/review/:branch/diff NEW per-file diff (Phase 6)
GET /api/adapters adapter YAML configs
GET /api/memory entity list
GET /api/memory/:kind/:id entity record
GET /api/file?path= repo file (traversal-guarded, 256KB cap)
POST /api/file NEW edit + commit on proposal branch (Phase 7)
GET /api/search?q= NEW classified hits (Phase 4)
POST /api/reindex NEW rebuild on-disk cache (Phase 4)
WS /api/ws NEW chokidar-driven { kind, path, at } (Phase 5)

Feature flag cockpit.live_updates: false in .founder/config.yml disables the watcher + WS route.

Cockpit frontend additions

Schema versions

Test posture

Build / deploy posture

Bottlenecks & risks today (refreshed)

Resolved since v0.1.0:

Still open:

  1. Cockpit chunk is one ~612KB JS bundle. Resolved post-v0.1.1: vite manualChunks splits charts / react-vendor / vendor / app, and the recharts money chart is React.lazy-loaded — first-paint critical JS dropped to ~86KB gzip.
  2. Markdown-as-DB scales linearly. Index cache at v0.1.1 is JSON rebuilt on demand. Past ~10k entities → SQLite + watcher (deferred until founder pain emerges).
  3. No multi-tenant story in the CLI. lycato serve is single-tenant. The deployed hosted slice (apps/hosted/web/) is separately multi-tenant — one uid-keyed tenant per signed-in founder, isolated by firestore.rules — but it shares no code with lycato serve and there is no server component behind it. The container-per-tenant design in docs/hosted/MULTI_TENANT.md is not built.
  4. Trust boundary is a convention for adapters, not a guard. Adapters could write to high-risk paths without a proposal/ branch. Policy is only enforced at review-time and at the cockpit /api/file write boundary. SDK consumers should follow the convention; we don't fence them in yet.
  5. Cockpit is unauthenticated. Single-tenant assumption. Hardening designed in docs/hosted/AUTH.md; runbook at docs/deployment/SELF_HOSTING.md calls out the reverse-proxy-or-Tailscale stop-gap.
  6. Run-ledger telemetry is local-only. No upstream aggregation yet — by design, see docs/security/TELEMETRY_OPT_IN.md.

Forward maturity (next 3 minor versions)

(See strategy/ROADMAP.md for the planning document; this is the architecture lens.)