← index2026-05-03 06:40 (Beirut)(backfill from DOCUMENTATION/)

02 — Architecture

02 — Architecture

File layout

/root/.claude/system/
├── README.md                 ← short overview (regenerated; canonical detail in this folder)
├── bin/
│   └── arg                   ← Python CLI (symlinked to /usr/local/bin/arg)
├── resources/                ← Layer 1: atoms (things in the world)
│   ├── accounts.json         ← LinkedIn / Meta / Gmail / Stripe / etc.
│   ├── keys.json             ← INDEX only, never values
│   ├── hosts.json            ← Hetzner / contabo / Mac / Chrome CDP / etc.
│   ├── data_stores.json      ← Postgres / Redis / sqlite / Drive / etc.
│   ├── products.json         ← jonahtebaa.com / brianserves.me / 20CRM / etc.
│   ├── channels.json         ← TG COMMS / TG LOGS / WA / Voice / Blog / etc.
│   ├── subsystems.json       ← External AI brains (codex/hermes/jules/manus/...)
│   └── agents.json           ← Claude Code sub-agents (gsd-*, code-reviewer, ...)
├── access/
│   └── grants.json           ← OAuth / MCP / ad-account permissions (from→to)
├── tools/
│   └── skills.json           ← curated /-callable skills index
├── capabilities/
│   └── capabilities.json     ← composed actions (52 rows at ship)
├── policies/
│   └── boundaries.json       ← hard (hook-enforced) + soft (advisory) rules
├── routines/
│   └── routines.json         ← scheduled cadence (GEO hour, daily publish, etc.)
├── observability/
│   ├── events/<YYYY-MM-DD>.ndjson   ← append-only journal, gzip>7d, delete>90d
│   ├── inbox/                       ← sub-agent proposals (single-writer invariant)
│   ├── probe_status.json            ← latest probe result per id (cache)
│   └── autofix_state.json           ← flap-detection history per (rid, action)
├── schemas/                         ← jsonschema files (Draft 2020-12)
│   ├── _common.schema.json
│   ├── _generic_atom.schema.json
│   ├── accounts.schema.json
│   ├── keys.schema.json
│   ├── capabilities.schema.json
│   └── boundaries.schema.json
└── views/                           ← generated markdown — DO NOT edit; `arg render-views` regenerates
    └── {accounts,keys,hosts,...}.md

JSON schema

Every category file has the same envelope:

{
  "schema_version": "1.0",
  "category": "<name>",
  "description": "...",
  "columns": ["id", "name", ...],   // ordered, used for view rendering
  "rows": [
    {
      "id": "...",
      "name": "...",
      "...": "...",
      "probe": { "command": "...", "timeout_sec": 10, "min_interval_seconds": 3600 },
      "last_probe": { "ok": true, "layer": "auth", "evidence": "...", "checked_at": "..." },
      "freshness_budget_hours": 24,
      "critical": true
    }
  ]
}

Capability rows additionally carry

Field Type Purpose
requires {resources, access, tools, capabilities} dep IDs (resolved against other rows)
side_effects array of strings writes-external, costs-money, irreversible-without-deletion, ...
risk_level low \| medium \| high \| critical shapes warning surface
cost_class free \| metered \| paid money policy gating
idempotency safe-retry \| at-least-once \| non-idempotent \| exactly-once-required \| depends-on-command autofix uses this
approval_required bool pre-policy default-ask flag
freshness_budget_hours int or null when does last_probe go stale
rate_limit {per_day, per_hour} optional
rollback {type, note} optional

Boundary rows additionally carry

Field Type Purpose
severity hard \| soft hard = hook-enforced; soft = advisory
match {side_effects_any, cost_class, risk_at_least, capability_id_regex, extra} AND across clauses within a rule, OR across rules
decision deny \| require_approval \| require_account_check \| deny_unless_brian_account \| deny_unless_inbox \| code_enforced \| audit \| warn \| advisory translation to resolver verdict
reason string shown in policy_block events + stderr
exceptions array of cap-ids exact-match exemption list

The four states

Probes return one of four states at the atom level:

State Meaning
verified-fresh last probe ok=true, within freshness_budget_hours
verified-stale last probe ok=true, but past budget — treat as suspect
red last probe ok=false
unknown never probed

arg status returns counts across all atoms.

The five resolver verdicts

Capability resolution composes:
1. State of every dep (atoms via probe, sub-caps recursively)
2. Freshness against freshness_budget_hours
3. Policy match against boundaries
4. cost_class and risk_level
5. Context (env hints, time of day for routines)

Output:

Verdict Meaning Caller action
yes act now proceed
yes-after-probe deps stale or unknown run probes first
yes-after-approval needs Jonah's go-ahead use /ask skill
no a dep is red or missing escalate or repair
blocked-by-policy a hard boundary blocks do not attempt

Boundary match semantics (post-2026-05-02 fix)

Within one rule: every match clause that is specified must fire. If a rule lists both side_effects_any: [costs-money] and cost_class: paid, BOTH must match. (Pre-fix: the rule fired on either — wrong.)

Across rules: independent. Each rule that matches contributes its decision. The most restrictive wins (deny > require_approval > advisory).

Rule writers should specify the most specific clauses they can. Over-broad regexes (^cap\.mac\..*) historically caused false-positive blocks. The smoke-test invariants checker (_check_invariants in arg validate) flags caps that match a deny_unless_brian_account rule but have no brian-* dep — preventing future drift.

Single-writer invariant

Discovery (belt + suspenders)

Three independent paths surface ARG to a fresh session:

  1. MEMORY.md CORE pointer line — always loaded.
  2. arg_sessionstart.sh — SessionStart hook injects current status counts + recent events.
  3. /ARG slash skill at /root/.claude/skills/ARG/SKILL.md — load on demand.

If any one is missing, the other two still ensure ARG is reachable.