zos-core — library API.
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
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.
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".
- Loads
<root>/contexts/<name>.yml— or<name>.yamlwhen only that exists (.ymlwins when both are present);rootdefaults to$ZOS_HOME. - Raises
FileNotFoundError— no yml/yaml exists fornameunderroot. - Raises
ContextError(ValueError)— blank name, invalid YAML, or a non-mapping document. - Consumed top-level keys:
context,register,role_default,allowed_tools,firewall; consumedfirewall:sub-keys:isolation,outbound_deny,read_allow. Everything else lands inextrasuntouched (unconsumed firewall sub-keys inextras["firewall"]). - List fields coerce scalars to one-element lists, stringify, strip, and drop empties;
isolationandregisterare lowercased.Context.nameis always thenameargument (the file stem), not the yml'scontext:key.
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.
| Key | Type | Default | Lands in | Meaning |
|---|---|---|---|---|
context | str | — | consumed, unused | informational name; the loader uses the file stem |
display_name | str | — | extras | human-readable name |
role_default | str | assistant | role_default | default role for the context |
register | str | professional | register | voice/tone: professional | warm (free-form token) |
transparency_group | str | — | extras | contexts in the same group share freely; a group-of-one is a hard firewall boundary |
firewall.isolation | str | open | isolation | open = report, don't block; hard = fail closed |
firewall.bridge_policy | str | — | extras["firewall"] | free | abstractions_only | none — what may carry OUT of this context |
firewall.read_allow | list[str] | [] | read_allow | surfaces this context may read (declared; advisory) |
firewall.outbound_deny | list[str] | [] | outbound_deny | surfaces this context may NOT write into — paths, scheme://token entries, or plain terms |
external_send | str | — | extras | draft_only — outbound to a human is drafted, never auto-sent |
allowed_tools | list[str] | [] | allowed_tools | default-deny allowlist; empty = the enforcing layer's default |
budget.daily_tokens | int | unset = unlimited | extras | daily 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.
| Flag | Meaning |
|---|---|
--principal NAME | the principal's name (substituted into the templates as {{PRINCIPAL}}) |
--context NAME | context name to create — repeatable (default: work) |
--values-template NAME | values 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-balance | skip the balance step entirely (no ## Your balance section). Contradicts --tendencies — passing both exits 1 |
--force | overwrite existing files (default: skip files that exist) |
--non-interactive | never 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).
| Slug | Self-assessment | Emphasized value |
|---|---|---|
fast-mover | I move fast and sometimes break things | Verify before claiming |
over-thinker | I overthink before acting | Ship the thing in front of you |
conflict-avoider | I avoid hard conversations | Tell the truth even when it costs |
over-committer | I say yes to too much | Guard your priorities |
lone-wolf | I go it alone | Build capability in others |
novelty-chaser | I chase the new thing | Finish what you started |
self-runner-down | I run myself down before I rest | Health 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.
| Flag | Meaning |
|---|---|
--org-name NAME | the organization's name (default: organization) |
--principal-role ROLE | who speaks for this org's policy — a role the system escalates to, e.g. operator, CISO, founder (default: operator) |
--values-template NAME | org 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 P | posture 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|no | for 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 type | isolation | transparency_group | deny scaffold |
|---|---|---|---|
clients / matters | hard (fail closed) | the unit's own name (group-of-one boundary) | yes — a commented outbound_deny example for walling off other units |
departments / teams | open (hard under regulated-strict) | org (shares freely inside the org) | no |
shared org context | open always | org | no |
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] ...
| Flag | Meaning |
|---|---|
--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 PERSON | the joining person's name (their principal). Prompted when interactive; required (exit 1) with --non-interactive |
--team UNIT | org 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 PATH | where the individual tree goes (default: <org-root>/people/<name>) |
--context NAME | personal context to create — repeatable (default: work) |
--values-template / --values / --priorities / --tendencies / --no-balance / --force / --non-interactive | the 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:
- org by reference —
hierarchy,laws,org-priority-stack,org-values,org-behavior-rules(required),roles,compliance-posture(optional); team-overlay(with--teamonly; points at the org'sbrain/overlays/<unit>.md);- the individual's own
priority-stack,values(required),observed-patterns(optional), and thecontext-overlayslot.
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.
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:
scheme://token(e.g.vault://client-secrets,tracker://12345) — matches only as a whole token bounded by non-token characters.- Filesystem path (contains
/or\) — matches only as a path prefix aligned on a segment boundary:/a/clientgmatches/a/clientg/notes.mdbut never/a/clientg-notes. - Plain term (a client or project codename) — case-insensitive substring.
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
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}
]
}
- Relative paths resolve against the manifest's directory — a whole
ZOS_HOMEtree is relocatable. The__OVERLAY__slot resolves to<overlay_dir>/<context.name>.mdat assemble time. Non-dict entries infiles[]are skipped silently. - Byte budget:
required: true(aliasalways_full) entries are never truncated, and missing-markers are always included. Optional present files fit in manifest order; the first that does not fit is truncated with a loud...[TRUNCATED]note (only if ≥ 200 spare bytes remain — else omitted), the rest are omitted with...[OMITTED]markers naming the full file path. - The payload opens with a
ZOS BOOT MANIFESTheader naming the context and budget, and states the first-reply requirement: line 1 of the session's first reply must be aLoaded:line naming the booted artifacts. Each file body follows under a===== KEY :: /path =====section header.
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.
- BROKEN REF — an absolute root-prefixed path referenced in a file that does not exist on disk (
scan_refsextracts the de-duplicated, order-preserving reference list). - CONTRADICTION — structured
rule:/polarity:/claim:declarations with the same rule id (or near-identical claim, Jaccard ≥ 0.8) and opposite polarity; plus a narrow free-text pass for the same predicate both mandated (must/always/shall) and forbidden (never/must not/don't). - STALE SUPERSEDE — a
supersedes:/superseded by:pointer whose target doesn't resolve, or resolves to a target that is still live (not archived, not itself marked superseded). - MISSING ENFORCER — an
enforcement:/enforced_by:declaration naming a.sh/.pyhook file that does not exist on disk; prose declarations likenativeare ignored.
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.
| Constant | Value | Meaning |
|---|---|---|
MIN_LINE_LEN / MAX_LINE_LEN | 25 / 90 | probe-line length window (distinctive yet recitable) |
MIN_LINE_WORDS | 4 | minimum words in a probe line |
DEFAULT_EXPIRE_TURNS | 8 | turns before an unanswered probe is dropped |
DEFAULT_YELLOW_AFTER / DEFAULT_RED_AFTER | 8 / 16 | default status() thresholds |
candidate_lines— the probe-worthy lines of a text, in file order. A candidate is a distinctive, recitable prose line: leading list markers stripped; blank lines, headers, table rows, block quotes and fenced-code content skipped; lines carrying backticks or URLs skipped (hard to recite exactly); length withinMIN_LINE_LEN..MAX_LINE_LENand at leastMIN_LINE_WORDSwords.make_probe— pick a layer and a verbatim probe-worthy line from it.layersis either a{name: path}mapping or a canon-manifest JSON path (the samefiles: [{key, path}, …]shapecanon.assemblereads; relative paths resolve against the manifest's directory, overlay-slot entries are skipped). Selection is random and seedable (rng_seed); whenstalenessis given ({layer: turns since last verified}) the stalest layers are tried first so probes rotate toward what has gone longest unattested. A layer with no candidate line falls through to the next. ReturnsNonewhen no layer qualifies or the manifest is unreadable/invalid — never raises.verify_echo— verbatim containment check:Trueiffprobe.lineappears inreply_textexactly. A blank probe line never passes.
- Turns-since-verified state, persisted as JSON at
path(atomic tmp-then-replace; parent directory created on save). Fields:turn,turns_since_verified(0 at construction — boot counts as attested),last_layer: str | None,pending: Probe | None. A missing or corrupt state file loads as fresh state; persistence failures are swallowed — telemetry must never break a turn. issue— setprobepending (re-stamped to the current turn) and persist. At most one probe is outstanding — issuing replaces any pending one. Surface the returned probe'slineto the session so it can echo it.record_turn— advance one turn against the session's reply; persist; return the new status. The turn and the gauge advance; a pending probe verified by the reply resets the gauge to 0 and recordslast_layer; an unverified probe older thanexpire_turnsis dropped (expire-and-rotate — expiry is not verification, the gauge keeps climbing).status—green(turns_since_verified < yellow_after) |yellow(< red_after) |red(>= red_after).
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.
- Write-gate —
write()raisesWriteGateError(ValueError)on a missing name/description/type/scope, a description over 150 chars (the description IS the retrieval surface), a type outsideuser | feedback | project | reference, a sensitivity outsidenormal | confidential, or a confidential fact aimed at thesharedscope (confidential never rides the shared bus).pinned=Trueexempts the bullet from FIFO graduation. recall(query)— keyword match over name + description: every whitespace token must appear, case-insensitive. No embeddings, on purpose. Empty query →[].enforce_budget()— bytes-only. An index at/over the archive cap FIFO-graduates its oldest non-pinned bullets (by trailing date, then file position) toMEMORY-<scope>-warm.mduntil back under the soft cap. Pinned bullets are exempt; fact files are never touched — graduation moves index bullets only.sync_shared()— copies shared-bus bullets into scope views, excluding scopes configuredisolated=True(the client-firewall pattern) and skipping confidential facts (defense in depth). Adds-only, de-duped by slug. Returns{"synced": {scope: [slugs]}, "excluded": [...], "skipped_confidential": [...]}.Fact(frozen dataclass) —name, description, type, created, last_used, scope, sensitivity, pinned, path, body+ aslugproperty.BudgetReportcarries the thresholds, per-scopeScopeBudgetslices (bytes/status before & after, graduated bullets), and agraduated_totalproperty.
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.
- 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 asdetect_shape(text) -> (shape, confidence, scores). - role-map — shape → role token;
general/unknown →generalist. - orchestration — scored on stakes / ambiguity / reversibility / user-marker / independence.
fan-outrequires ≥ 2 genuinely independent sub-tasks (an imperative verb + its own object per segment) AND a stakes signal; a user-marker or ambiguity →panel; otherwisesingle. - agent-shape — fan-out size =
min(independence, MAX_FANOUT); the ceiling ismin(8, max(2, cpus−2)), overridable viaZOS_MAX_FANOUT. - 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
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)
| Command | Flags | Does | Exit codes |
|---|---|---|---|
zos init | --principal, --context (repeatable), --values-template, --values, --priorities, --tendencies, --no-balance, --force, --non-interactive | creates 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 Onboarding | 0; 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-overlays | onboards an organization: org values, units → contexts (with isolation rules), compliance posture, seats, team overlays — see Onboarding · org | 0; 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 flags | seeds an individual brain inside an org root; the org canon is inherited by reference — see Onboarding · join | 0; 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 context | 0; 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_outbound | 0 clean · 1 warn-only findings · 2 any block-severity finding |
zos lint | — | runs 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}}.
| Template | Installed as | Substitution |
|---|---|---|
hierarchy.md | brain/hierarchy.md | no |
safety-floor.md | brain/safety-floor.md | no |
priority-stack.md.template | brain/priority-stack.md | yes (incl. {{PRIORITY_STACK}}) |
values/balanced.md · builder.md · caregiver.md · researcher.md | brain/values.md (chosen starting point) | yes |
values-org/trust-first.md · care-org.md · counsel-org.md | brain/values.md (org starting point, --org only) | yes (incl. {{ORG_NAME}}, {{PRINCIPAL_ROLE}}) |
values.md.template | brain/values.md (only for --values-template custom / wizard skip) | yes |
behavior-rules.md.template | brain/behavior-rules.md | yes |
observed-patterns.md | brain/observed-patterns.md | no |
contexts/work.yml + contexts/work.md | contexts/<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
| Variable | Default | Used by | Meaning |
|---|---|---|---|
ZOS_HOME | ~/.zos | everything | the data root (overridden by an explicit root/--home) |
ZOS_MAX_FANOUT | min(8, max(2, cpus−2)) | routing | parallel-agent ceiling (read at import time) |
ZOS_ROUTING_ACTIVE | unset | routing | 1 arms active mode (mechanical-only, confidence-gated, never destructive) |
ZOS_ROUTING_DISABLED | unset | routing | 1 kill-switches routing to suggest-only |