Documentation · zos-core

zos-core — library API.

The brain engine, derived from the source of zos_core 0.1.0. Working code, June 2026.

zos‑core is the brain engine: a Python library that defines what your AI knows, obeys, and remembers — contexts, firewall, canon, lints, memory, routing, and the zos CLI. Python 3.11+ · PyYAML only · BUSL-1.1

Package zos_core 0.1.0. Top-level exports are lazy (importlib on attribute access) — the optional memory/routing subpackages' absence never breaks the brain modules; accessing them when not installed raises AttributeError (chained from ModuleNotFoundError).

from zos_core import (
    Context, load_context,        # zos_core.contexts
    Violation, check_outbound,    # zos_core.firewall
    check_target,                 # zos_core.firewall
    assemble,                     # zos_core.canon
    zos_home,                     # zos_core.home
)
zos_core.__version__              # "0.1.0"

Contexts & the data root

ZOS_HOME resolution — zos_core.home

zos_home(root: Path | str | None = None) -> Path

Resolve the zOS data root. Precedence: explicit root argument > $ZOS_HOME > ~/.zos. The result is ~-expanded and absolute; it is not required to exist — callers decide whether a missing root is an error. Every path in the library derives from this one function or an explicit root argument; nothing hardcodes a machine-specific path.

$ZOS_HOME/
  manifest.json        boot manifest (hierarchy-ordered file list)
  brain/               canonical layer files (hierarchy.md, safety-floor.md, ...)
  contexts/            <name>.yml (config) + <name>.md (prose overlay) per context
  memory/              memory-store root (optional subpackage)
  routing/fleet.yml    optional fleet override for the router

Contexts — zos_core.contexts

A context is config, not code: a <ZOS_HOME>/contexts/<name>.yml record plus an optional prose overlay <name>.md beside it. Adding a context is dropping two files — never an engine change.

@dataclass class Context: name: str isolation: str # "open" (default) | "hard" | custom outbound_deny: list[str] read_allow: list[str] allowed_tools: list[str] register: str # default "professional" role_default: str # default "assistant" extras: dict # every unconsumed yml key, untouched

isolation semantics are advisory — the enforcing layer decides policy: open = findings are reported, not blocked; hard = violations should fail closed. Unknown strings pass through (lowercased) for deployment-defined levels — lenient enforcers should treat them as open, strict ones as hard. Module constants: DEFAULT_ISOLATION = "open", DEFAULT_REGISTER = "professional", DEFAULT_ROLE = "assistant".

load_context(name: str, root: Path | None = None) -> Context
context_path(name: str, root: Path | str | None = None) -> Path list_contexts(root: Path | str | None = None) -> list[str]

context_path returns the config path (.yml, falling back to .yaml when only that exists; the canonical .yml path when neither does — useful as the create-here target). list_contexts returns the sorted, de-duplicated *.yml/*.yaml stems under <root>/contexts (skipping _* files); it never raises and returns [] when the directory is absent.

Context YAML schema

Keys marked extras are preserved verbatim in Context.extras for enforcing layers (the gateway reads budget.daily_tokens from there). Any other key is legal and survives the round trip in extras.

KeyTypeDefaultLands inMeaning
contextstrconsumed, unusedinformational name; the loader uses the file stem
display_namestrextrashuman-readable name
role_defaultstrassistantrole_defaultdefault role for the context
registerstrprofessionalregistervoice/tone: professional | warm (free-form token)
transparency_groupstrextrascontexts in the same group share freely; a group-of-one is a hard firewall boundary
firewall.isolationstropenisolationopen = report, don't block; hard = fail closed
firewall.bridge_policystrextras["firewall"]free | abstractions_only | none — what may carry OUT of this context
firewall.read_allowlist[str][]read_allowsurfaces this context may read (declared; advisory)
firewall.outbound_denylist[str][]outbound_denysurfaces this context may NOT write into — paths, scheme://token entries, or plain terms
external_sendstrextrasdraft_only — outbound to a human is drafted, never auto-sent
allowed_toolslist[str][]allowed_toolsdefault-deny allowlist; empty = the enforcing layer's default
budget.daily_tokensintunset = unlimitedextrasdaily token cap, enforced by zos-gateway (not by zos-core itself)

Onboarding — init · init --org · join

Three wizards seed a brain, and all three share the same spine. The safety floor is stated first and is non-negotiable: brain/safety-floor.md installs unchanged, and nothing chosen later can weaken it. Everything above the floor suggests, never forces: every seeded file stays editable, every suggested default carries a one-sentence "why", and skipping is always a legitimate answer. Interactive by default; every prompt has a flag twin, so scripts get full parity (--non-interactive never prompts and falls back to the defaults).

Personal — zos init

Wizard steps (each skipped when its flag is given): 1. safety floor (stated first, in both modes) · 2. values — four curated starting points, the balanced default pre-selected, pick one / edit inline / skip · 3. balance — a tendencies self-assessment (below) · 4. optimization criteria — the ordered, non-invertible priority stack, default health > relationships > work, presented as a default most people endorse on reflection; reorder freely.

FlagMeaning
--principal NAMEthe principal's name (substituted into the templates as {{PRINCIPAL}})
--context NAMEcontext name to create — repeatable (default: work)
--values-template NAMEvalues starting point for brain/values.md: balanced (default) | builder | caregiver | researcher | custom (= the blank scaffold, i.e. skip)
--values "a,b,c"seed brain/values.md from your own comma-separated value names instead (overrides --values-template; practice sentences left to fill in)
--priorities "a,b,c"ordered optimization priorities, highest first, written into brain/priority-stack.md (default: health,relationships,work). Exit 1 if the list parses empty
--tendencies "a,b"balance self-assessment as comma-separated stable slugs (table below; the wizard's 1-based numbers also accepted). Unknown slugs exit 1 before any write; an empty list means none (deselect-all is legitimate)
--no-balanceskip the balance step entirely (no ## Your balance section). Contradicts --tendencies — passing both exits 1
--forceoverwrite existing files (default: skip files that exist)
--non-interactivenever prompt; use flags/defaults (balanced values, health,relationships,work priorities, no balance emphases)

Installs: brain/hierarchy.md, brain/safety-floor.md, brain/priority-stack.md, brain/values.md, brain/behavior-rules.md, brain/observed-patterns.md; a <name>.yml + <name>.md pair per requested context; and a hierarchy-ordered manifest.json with relative paths (the tree is relocatable). Manifest entries hierarchy, laws, priority-stack, values, behavior-rules, and context-overlay are required: true; observed-patterns is optional.

The balance step — tendencies → counterweight emphases

Premise: everyone has strengths whose shadows need a counterweight, and the suggested emphases balance the specific person — good for them, for the people around them, and for the wider world (the three-ring framing, presented in one warm line). Each selected tendency maps to an EMPHASIS on one of the seeded values, written into brain/values.md as a ## Your balance section (tendency → emphasized value → one-sentence why). Suggest, never force: deselecting everything is a fine answer and leaves no section. The balance step is personal-mode only--tendencies/--no-balance exit 1 with --org (balance is per-person; individuals in an org get it via zos join).

SlugSelf-assessmentEmphasized value
fast-moverI move fast and sometimes break thingsVerify before claiming
over-thinkerI overthink before actingShip the thing in front of you
conflict-avoiderI avoid hard conversationsTell the truth even when it costs
over-committerI say yes to too muchGuard your priorities
lone-wolfI go it aloneBuild capability in others
novelty-chaserI chase the new thingFinish what you started
self-runner-downI run myself down before I restHealth before work

Organization — zos init --org

Onboards an organization instead of a person: same wizard shape, same safety-floor-first framing, same full flag parity. Wizard steps: safety floor, org identity (name + principal role), org values, units, team overlays (department/team units only), compliance posture, seats, priority stack. The org-only flags error without --org, and --context errors with it — org contexts come from --units.

FlagMeaning
--org-name NAMEthe organization's name (default: organization)
--principal-role ROLEwho speaks for this org's policy — a role the system escalates to, e.g. operator, CISO, founder (default: operator)
--values-template NAMEorg values starting point: trust-first (default) | care-org (healthcare-flavored) | counsel-org (legal/consulting) | custom (the blank scaffold). Personal templates are rejected with --org and vice versa
--values "a,b,c"seed brain/values.md from the org's own value names (overrides --values-template)
--units "type:a,b,…"org structure → contexts, with type one of departments | clients | matters | teams (default: departments:general). Exit 1 — before any write — on a missing/unknown type, empty or duplicate names, or the reserved name org
--compliance Pposture none (default) | regulated-light | regulated-strict, written into brain/compliance-posture.md (an optional manifest entry, so it rides in every boot). Under regulated-strict, every unit context seeds isolation: hard
--roles "a,b,c"initial seat names (free text) for the brain/roles.md scaffold (default: operator,reviewer,member). Exit 1 if the list parses empty
--priorities "a,b,c"same flag as personal mode; org default: trust,people,delivery
--team-overlays yes|nofor departments/teams units: seed a thin per-unit values overlay at brain/overlays/<unit>.md — "what this team emphasizes on top of the org canon" (a scaffold; default: yes). Client/matter units never get one (an outside party's material is not a team identity)

Units → contexts. Every unit becomes a contexts/<unit>.yml + <unit>.md pair, plus a shared org context (the commons — coordination and policy, never client/matter material):

Unit typeisolationtransparency_groupdeny scaffold
clients / mattershard (fail closed)the unit's own name (group-of-one boundary)yes — a commented outbound_deny example for walling off other units
departments / teamsopen (hard under regulated-strict)org (shares freely inside the org)no
shared org contextopen alwaysorgno

Installs everything personal mode installs plus brain/roles.md and brain/compliance-posture.md, both optional manifest entries (required: false — their absence degrades, never breaks a boot), plus (by default) one brain/overlays/<unit>.md values-overlay scaffold per department/team unit. The overlays are not in the org manifest — they ride in a joined individual's boot, layered between the org canon and the personal overlay. All flag validation happens before the first write: a bad spec exits 1 with the tree untouched. The seeded tree boots (zos boot --context org) and lints clean.

Joining an org — zos join

Seeds an individual brain inside an org root — the hierarchical-values story: org canon → team overlay → individual overlay. The org canon is inherited by reference: the individual's manifest.json entries point at the org's files (relative paths into the org root — relocatable as long as the trees move together), never copies — so an org-side edit to values or policy is live in every member's next boot, with no copies to chase. The wizard states up front that the org floor and the safety floor are non-negotiable — the individual's own values and priorities layer WITHIN the org's bounds, never above them — then runs the personal values / balance / priorities steps for the individual's own overlay.

zos join --org-root PATH [--name PERSON] [--team UNIT] [--root PATH] ...
FlagMeaning
--org-root PATH(required) the org's ZOS_HOME, seeded by zos init --org. Validated before any write: must be a directory holding manifest.json, brain/safety-floor.md, and brain/roles.md (the org marker) — else exit 1
--name PERSONthe joining person's name (their principal). Prompted when interactive; required (exit 1) with --non-interactive
--team UNITorg unit to join. The unit context contexts/<unit>.yml must exist in the org root (exit 1 before writes otherwise). Layers the org's brain/overlays/<unit>.md between the org canon and the personal overlay; a missing overlay file (org seeded with --team-overlays no) is scaffolded at join time so the boot never carries a missing-file marker
--root PATHwhere the individual tree goes (default: <org-root>/people/<name>)
--context NAMEpersonal context to create — repeatable (default: work)
--values-template / --values / --priorities / --tendencies / --no-balance / --force / --non-interactivethe personal-mode flags, applied to the individual's overlay (personal values templates only; balance included — same slugs, same validation-before-writes)

Installs under the individual root: brain/values.md (own starting point + balance section), brain/priority-stack.md, brain/observed-patterns.md (per-person — the assistant's observations about this principal), a context pair per --context, and a manifest.json whose files[] is, in hierarchy order:

  1. org by reference — hierarchy, laws, org-priority-stack, org-values, org-behavior-rules (required), roles, compliance-posture (optional);
  2. team-overlay (with --team only; points at the org's brain/overlays/<unit>.md);
  3. the individual's own priority-stack, values (required), observed-patterns (optional), and the context-overlay slot.

So zos --home <individual-root> boot --context work assembles org canon → team overlay → personal overlay → context overlay.

Firewall — zos_core.firewall

Scans outbound text (a file body about to be written, a message about to be sent) against the active context's outbound_deny list. The module reports; it never decides policy — the enforcing layer (gateway, hook, CI) chooses what to do with a finding. And scanning never raises: a scan bug degrades to "no findings", never a crash.

@dataclass class Violation: pattern: str # the deny-list entry that matched excerpt: str # matched snippet, capped at 160 chars severity: str # "block" (isolation=hard) | "warn" (anything else) check_outbound(text: str, context: Context) -> list[Violation] check_target(target: str, context: Context) -> list[Violation] severity_for(context: Context) -> str

check_outbound returns one violation per deny entry the text references (first match per entry — one finding per surface); an empty list means clean. check_target scans a write destination the same way — the destination is the stronger signal: a benign body written into a forbidden surface is still a violation. severity_for maps isolation to severity: "block" iff isolation == "hard", else "warn".

Matching semantics

Three entry shapes, decided by the entry's own form — never naive substring on structured entries:

from zos_core import load_context, check_outbound, check_target

ctx = load_context("work")
for v in check_outbound(draft_text, ctx):
    print(v.severity, v.pattern, "—", v.excerpt)
if check_target("/srv/clients/acme/notes.md", ctx):
    ...  # the destination itself is a denied surface

Canon assembly — zos_core.canon

assemble(manifest_path: Path, context: Context, byte_budget: int = 60000) -> str

Assembles ONE injectable boot payload from an ordered JSON manifest, in hierarchy order (LAWS > PRIORITY STACK > VALUES > DECLARED RULES > OBSERVED PATTERNS > CONTEXT OVERLAY). The manifest order IS the hierarchy order — the module never reorders, never writes, and never raises: an unreadable/invalid manifest (or empty files[], or all files missing) returns a !! ZOS BOOT DEGRADED !! banner with safety-floor fallback instructions; an individual missing file becomes a loud !! BOOT FILE MISSING !! marker in the payload; any unexpected exception degrades to a banner.

{
  "byte_budget": 60000,            // optional; the assemble() arg wins
  "overlay_dir": "contexts",       // optional; default <manifest dir>/contexts
  "files": [
    {"key": "laws", "path": "brain/safety-floor.md", "required": true},
    {"key": "context-overlay", "path": "__OVERLAY__", "required": true}
  ]
}

The same assembled canon also ships into Claude chat, Cowork, and Claude Code as part of the zOS bundle — see zOS inside Claude.

Drift lints — zos_core.lint

"Lint what you declare." Four classes of declared-vs-reality drift. Pure reporters: never write, never block, never raise (a lint bug must not break a boot); unreadable inputs are skipped. Each returns loud !! ... !! lines.

scan_refs(text, root=None) -> list[str] find_broken_refs(file_paths, root=None) -> list[str] find_contradictions(file_paths) -> list[str] find_stale_supersedes(file_paths) -> list[str] find_missing_enforcers(file_paths, root=None) -> list[str] check_all(file_paths, root=None) -> list[str]

check_all runs all four with each independently guarded (a crashed check contributes a !! LINT DEGRADED !! line instead of suppressing the others). Report-only — callers must never block on these.

Canon attestation — zos_core.attestation

Drift detection for long-running sessions: a session that booted the canon can silently lose it (compaction, overflow, plain drift). Attestation periodically probes the session with a verbatim line from one canon layer and verifies the reply echoes it; a turns-since-verified gauge thresholds the result. Telemetry-only by design — nothing here blocks a turn, and a missed probe expires instead of wedging so the loop always rotates. Layers are caller-supplied; the module hardcodes no layer names or paths.

ConstantValueMeaning
MIN_LINE_LEN / MAX_LINE_LEN25 / 90probe-line length window (distinctive yet recitable)
MIN_LINE_WORDS4minimum words in a probe line
DEFAULT_EXPIRE_TURNS8turns before an unanswered probe is dropped
DEFAULT_YELLOW_AFTER / DEFAULT_RED_AFTER8 / 16default status() thresholds
@dataclass(frozen=True) class Probe: layer: str line: str # the verbatim line owed back issued_turn: int = 0 issued_at: float = 0.0 # wall-clock candidate_lines(text: str) -> list[str] make_probe(layers, rng_seed=None, *, turn=0, staleness=None) -> Probe | None verify_echo(reply_text: str, probe: Probe) -> bool
AttestationState(path, *, expire_turns=8, yellow_after=8, red_after=16) issue(probe: Probe) -> Probe record_turn(reply_text: str) -> str status() -> str # "green" | "yellow" | "red"
from zos_core.attestation import AttestationState, make_probe

state = AttestationState(home / "attestation.json")
if state.pending is None and time_to_probe():        # caller-chosen cadence
    probe = make_probe(home / "manifest.json")        # or {name: path} mapping
    if probe:
        issued = state.issue(probe)
        surface(f'Include this line verbatim in your reply: "{issued.line}"')
gauge = state.record_turn(reply_text)                 # "green" | "yellow" | "red"

Memory store — zos_core.memory optional subpackage

One fact per markdown file with flat YAML frontmatter; per-scope hot indexes (MEMORY-<scope>.md, one bullet per fact); a shared bus; byte budgets that graduate overflow to a warm tier instead of deleting it. Exports: MemoryStore, Fact, BudgetReport, ScopeBudget, WriteGateError, VALID_TYPES.

MemoryStore(root: Path | None = None) # default $ZOS_HOME/memory write(name, description, type, body, scope="default", sensitivity="normal", pinned=False) -> Path index(scope="default") -> str facts() -> list[Fact] recall(query: str) -> list[Fact] enforce_budget(soft=20000, archive=22000, hard=24000) -> BudgetReport configure_scope(scope, isolated=False) -> None is_isolated(scope) -> bool scopes() -> list[str] sync_shared(scope=None) -> dict
from zos_core.memory import MemoryStore

store = MemoryStore()                       # $ZOS_HOME/memory
store.configure_scope("client-acme", isolated=True)
store.write("Deploy ritual", "Deploys go out Tuesdays after standup.",
            type="reference", body="...full detail...", scope="shared")
store.sync_shared()                         # client-acme is excluded
hits = store.recall("deploy tuesday")
report = store.enforce_budget()

Task router — zos_core.routing optional subpackage

Five pure steps; route() never raises on any task text (a broken fleet file is the only error that propagates — caller config, not task input). Suggestion-only by default. Exports: route, RouteDecision, load_fleet, detect_shape, CONF_FLOOR (0.5), MAX_FANOUT.

route(task_text: str, context_name: str = "default", fleet: Path | None = None) -> RouteDecision @dataclass(frozen=True) class RouteDecision: shape: str # design|financial|legal|technical|strategic|mechanical|general role: str # designer|analyst|counsel|engineer|strategist|operator|generalist orchestration_mode: str # single | panel | fan-out model: str # model id from the chosen fleet tier confidence: float rationale: str # human-readable trace of every step mode: str = "suggest" # suggest | active tier: str = "" fanout: int = 0 irreversible: bool = False needs_escalation: bool = False # confidence below CONF_FLOOR
  1. shape-detect — word-boundary regex signal groups (substring matching is banned); ties resolve to the higher-stakes shape (legal > financial > strategic > technical > design > mechanical); no signal → ("general", 0.0). Exposed as detect_shape(text) -> (shape, confidence, scores).
  2. role-map — shape → role token; general/unknown → generalist.
  3. orchestration — scored on stakes / ambiguity / reversibility / user-marker / independence. fan-out requires ≥ 2 genuinely independent sub-tasks (an imperative verb + its own object per segment) AND a stakes signal; a user-marker or ambiguity → panel; otherwise single.
  4. agent-shape — fan-out size = min(independence, MAX_FANOUT); the ceiling is min(8, max(2, cpus−2)), overridable via ZOS_MAX_FANOUT.
  5. safety + model — destructive-signal detection (delete/drop/force-push/…) plus the cheapest capable tier from the fleet config; if no tier claims the shape, the most expensive tier takes it (fail toward care).

mode == "active" only when ZOS_ROUTING_ACTIVE=1 AND the shape is mechanical AND confidence ≥ 0.5 AND no irreversible signal; ZOS_ROUTING_DISABLED=1 kill-switches to suggest.

Fleet config — load_fleet

load_fleet(path: Path | None = None) -> dict[str, dict]

Resolution: explicit path → $ZOS_HOME/routing/fleet.yml → the packaged default. Raises ValueError when the config has no tiers mapping. Each tier declares model, cost_per_mtok, and capable_shapes ([all] = every shape) — quality floors are config, not code: a shape listed only under the top tier can never route cheaper without an explicit config change.

tiers:
  light:
    model: example/light-1      # model id forwarded into RouteDecision.model
    cost_per_mtok: 0.80         # blended cost; any consistent unit
    capable_shapes: [mechanical]
  top:
    model: example/top-1
    cost_per_mtok: 30.00
    capable_shapes: [all]

CLI — zos

zos [--home PATH] <command>     # --home: data root (default $ZOS_HOME or ~/.zos)
CommandFlagsDoesExit codes
zos init--principal, --context (repeatable), --values-template, --values, --priorities, --tendencies, --no-balance, --force, --non-interactivecreates a personal ZOS_HOME tree (brain/ + contexts/ + manifest.json) from the neutral templates; interactive wizard by default, full flag parity for scripts; existing files are skipped unless --force — see Onboarding0; 1 if the template set is missing, --priorities parses empty, or a tendency slug is unknown (before any write)
zos init --org--org-name, --principal-role, --values-template, --values, --units, --compliance, --roles, --priorities, --team-overlaysonboards an organization: org values, units → contexts (with isolation rules), compliance posture, seats, team overlays — see Onboarding · org0; 1 on a bad unit spec or an empty list — validated before any write
zos join--org-root (required), --name, --team, --root, --context + the personal-mode flagsseeds an individual brain inside an org root; the org canon is inherited by reference — see Onboarding · join0; 1 on an invalid org root or a missing unit context (before any write), or a missing --name with --non-interactive
zos boot--context (required), --budget (default 60000)assembles and prints the canon for a context0; 1 if the context can't load (assembly itself degrades in-band, never fails)
zos check--context, --file (both required)firewall-checks a file: the path via check_target, the content via check_outbound0 clean · 1 warn-only findings · 2 any block-severity finding
zos lintruns all four drift lints over every .md/.yml under brain/ and contexts/0 always when files were linted (reports, never gates); 1 only when nothing to lint

Templates

templates/ (symlinked at src/zos_core/templates so it resolves in both source and installed runs) ships the neutral set installed by zos init. Substituted tokens: {{PRINCIPAL}} (the principal's name; in org mode, <org name> (<principal role>)), {{ZOS_HOME}} (the resolved home path), {{PRIORITY_STACK}} (in priority-stack.md.template only — the chosen ordered tiers), and — in templates/values-org/ only — {{ORG_NAME}} and {{PRINCIPAL_ROLE}}.

TemplateInstalled asSubstitution
hierarchy.mdbrain/hierarchy.mdno
safety-floor.mdbrain/safety-floor.mdno
priority-stack.md.templatebrain/priority-stack.mdyes (incl. {{PRIORITY_STACK}})
values/balanced.md · builder.md · caregiver.md · researcher.mdbrain/values.md (chosen starting point)yes
values-org/trust-first.md · care-org.md · counsel-org.mdbrain/values.md (org starting point, --org only)yes (incl. {{ORG_NAME}}, {{PRINCIPAL_ROLE}})
values.md.templatebrain/values.md (only for --values-template custom / wizard skip)yes
behavior-rules.md.templatebrain/behavior-rules.mdyes
observed-patterns.mdbrain/observed-patterns.mdno
contexts/work.yml + contexts/work.mdcontexts/<name>.yml + .md per context (org mode derives unit isolation/groups from these too)yes

Org mode also generates brain/roles.md, brain/compliance-posture.md, and the per-unit brain/overlays/<unit>.md team values overlays in code (no template file), as are the ## Your balance section of a personal brain/values.md and a joined individual's reference manifest. The templates/values/ and templates/values-org/ starting points are deliberately generic prosocial content: a defensible default for someone seeding a brain for the first time, suggested with a one-sentence why and fully editable after install.

Environment variables

VariableDefaultUsed byMeaning
ZOS_HOME~/.zoseverythingthe data root (overridden by an explicit root/--home)
ZOS_MAX_FANOUTmin(8, max(2, cpus−2))routingparallel-agent ceiling (read at import time)
ZOS_ROUTING_ACTIVEunsetrouting1 arms active mode (mechanical-only, confidence-gated, never destructive)
ZOS_ROUTING_DISABLEDunsetrouting1 kill-switches routing to suggest-only

This page mirrors docs/API.md in the zos-core repository, derived from the source at 0.1.0. Companion: zos-gateway REST API · platform overview. Questions? Request early access.