Business-OS Endgame — Module, Skill & Autonomy Architecture
Status: north-star design (the target all module/skill work points at). Scope: the long-horizon architecture. Not a migration plan — a compass.North star
Poindexter is an autonomous business OS: a workforce of modules (departments) that an architect composes per intent, run by a single operator from a phone. Content is module 1 of N. Future departments — customer support, finance, devops, compliance/legal, marketing, HR — are the rest of the org chart. The whole system is built so that, over time, it can extend itself — author new skills and stand up new departments against a stable contract — with progressively less human direction.The core model
Three layers, orthogonal, composed top-down:- Capabilities are deterministic tools behind a plugin contract (the existing ~20 entry-point groups + the declarative data-plane). Mechanism.
- Skills are procedures (instruction text + optional bundled scripts) in
the agentskills.io
SKILL.mdformat. Judgment. They tell an LLM when (thedescription, used for routing) and how (the body) to use capabilities. - Modules are departments: a manifest that binds the capabilities it needs, bundles the skills it runs, and adds business glue. (Module v1 already defines the manifest + migrations + routes + dashboards + probes shape.)
- The architect reads the skill/module catalog and composes a process for a given intent, rather than running one hard-coded pipeline.
Four governing decisions
1. Own your interfaces; rent your implementations
Keep full control at the seams — the skill format, the module/plugin contract, the MCP + CLI surface, the data schema — and at the moat — editorial DNA, adversarial QA, the business loop. Borrow everything behind those seams: orchestration (LangGraph), LLM routing (LiteLLM), retrieval (LlamaIndex), QA rails (DeepEval/Guardrails/Ragas), and commodity skills from the open ecosystems. Because the contract is owned, any rented implementation is swappable without a rewrite. This single rule buys simplicity (don’t build commodity), control (no lock-in), extendability (new function = new skill/plugin behind the same seam), and the eventual product surface (the seam is what others build on). Corollary — everything behind a seam is disposable. Implementations are temporary by design, including the ones we hand-rolled. If something out there does a job better, the right move is to adopt it and throw out the local version — the owned interface is exactly what makes that cheap and safe. Don’t defend hand-rolled code on sunk-cost grounds; defend the seam, and let the thing behind it be replaced whenever a better option appears. The goal is ultimate flexibility: a stable contract with maximally swappable internals.2. A module is hybrid by maturity
One module identity, two fill-ins behind it:- Thin tier (default): a manifest that binds capabilities + lists skills (mostly borrowed) + minimal glue. Standing up a department is largely declaring which skills it uses. This tier is the one the system can eventually author itself.
- Graduated tier: custom services replace skills only where a moat emerges. Promotion is earned, not assumed.
skills[] be progressively swapped for services/ without changing
its identity, routes, or data — graduation is a fill-in change, not a rewrite.
3. Earned autonomy
Self-development is governed by trust dials, not a fixed gate. Every self-dev capability (author a skill, stand up a thin module, graduate a module, raise a spend cap, …) has a DB-tunable autonomy level that starts at propose-only and auto-promotes when it accumulates a clean, measured track record — zero rollbacks, tests green, cost in bounds. Full autonomy is the asymptote these dials approach over years; it is never a single switch. This generalizes a pattern already proven in content: the auto-publish gate that withholds automation until a capability logs N near-zero-edit runs. Every self-dev capability needs three things, mirroring that gate:- an autonomy level (an app_setting — runtime-tunable, not hard-coded),
- track-record telemetry to earn promotion (measured outcomes), and
- uniform rollback / kill-switch rails so any autonomous action is reversible.
4. Tenancy via an OperatorScope seam
Design the scope seam now; defer the multi-tenant implementation.
- Now: single operator per deployment — instance-per-customer is the de-facto model (self-host the OSS, or run a managed isolated deployment). No shared-DB tenant plumbing.
- Seam: all data access flows through one injected
OperatorScopeobject (today seeded from per-deployment config + niche scoping). Running with one implicit operator costs nothing extra. - Later (only if justified): shared-DB multi-tenancy becomes an additive change at that one seam (populate a real tenant id + row-scope there), not a codebase-wide retrofit.
Two skill layers, one format
Skills come in two layers that share the agentskills.ioSKILL.md format,
namespaced by pack — <root>/<pack>/<skill>/SKILL.md. The two layers have
different consumers and runtimes, and that difference dictates where each tree
physically lives:
| Layer | Location | Consumed by | Purpose |
|---|---|---|---|
| Operator skills | repo-root skills/poindexter/<verb>/ | the operator agent (Claude) | Drive the business — wrap the CLI/MCP (approve-post, cost-report, …) |
| Pipeline skills | package src/cofounder_agent/skills/<pack>/ | the worker’s UnifiedPromptManager | Procedure text a module’s stages use (research, blog-generation, …) |
COPY . . from the package (src/cofounder_agent/ → /app).
The repo root is not in the build context, so a repo-root skills/ tree is
invisible inside the container — loading it crashed the worker (the prompt
manager resolved a repo-root path that only existed on the host). Pipeline skills
therefore live inside the package, as a sibling of the old prompts/ dir, and
resolve via a package-relative path (Path(__file__).parent.parent / "skills") —
identical on the host and at /app/skills in the container. Operator skills are
read by the Claude/agent runtime, which sees the whole repo, so they stay at the
repo root. The SKILL.md format (the owned seam) is the same in both trees.
Pack = module. The operator toolset is the poindexter pack; each business
module owns a pack of pipeline skills (content, later finance, support).
The prompt loader scans the package skills tree and registers only prompt-bearing
skills (those declaring metadata.prompts); operator action skills lack that
block and are a different layer (not prompt text) — and aren’t under the package
tree anyway.
How pipeline skills fit the existing prompt stack
AdoptingSKILL.md replaces only the default-text layer of prompt
resolution; it removes nothing.
- Storage + routing →
prompts/*.yamlmigrate toskills/<name>/SKILL.md. Skills gain adescription(the architect’s routing menu) and a home for bundled scripts — a superset of what flat YAML keys offered. - Runtime override + versioning → unchanged. The prompt manager still resolves override-surface → on-disk default; the override surface still wins. The skill body is just the default it falls back to.
- Tracing / observability → unchanged and orthogonal. Tracing hooks the dispatch layer (every LLM call), not prompt storage. Zero impact.
SKILL.md’s frontmatter declares the
prompt keys it provides; the resolver reads them exactly as before.
Build-vs-borrow stance
Content is the one moat department; almost every other is borrow-heavy.| Department | Stance |
|---|---|
| Content | Build the moat — voice DNA, adversarial QA, publish gate. Graduated. |
| Customer support | Borrow — triage / reply-drafting / helpdesk skills exist widely |
| Finance | Borrow — bookkeeping / reconciliation / reporting skills exist |
| DevOps | Borrow — deploy / monitor / incident / self-healing doctor skills exist |
| Compliance / legal | Borrow — contract-review / policy-QA / PII-scan skills exist |
SKILL.md format; a
self-healing doctor; an energy/cost eval harness; skill importers; and
specific permissive skills (web research, scrape, summarize, structured
extraction, TTS) from the open ecosystems. Vet each borrowed skill’s license.
Seams to preserve in all new work
Every change from here should keep these intact, because they are what make the endgame reachable incrementally:- Skill format — new procedures land as
SKILL.md, not inline prompts. - Module manifest — new departments declare capabilities + skills; custom code is a later, earned fill-in.
OperatorScope— data access goes through the scope, even with one operator.- Autonomy as an app_setting — anything the system might one day do on its
own is gated by a runtime-tunable trust level + track-record telemetry +
reversible rails, never a hard-coded
if. - CLI/MCP first — operator actions are CLI verbs with MCP parity, so both a human and the architect can drive them.