Skip to main content

Architecture — OMA's Two-Layer Model

This page consolidates how oh-my-aidlcops lays itself out on the user's machine and what happens when a Claude Code or Kiro session starts. The goal is that an engineer can answer "which file do I edit to change behavior X" in one read.

Diagram labels are English, body prose is English, and every mermaid node and edge label is double-quoted.

Core model — three facts

OMA's architecture compresses into three statements. §1, §2, and §3 of this page each correspond to one statement.

  1. Assets are split into two layers. Assets in the OMA repo (~/.oma/ or a git checkout) are symlinked or merged once into a user-global directory (~/.claude/ or ~/.kiro/) at install time. Each project the user works in gets its own <project>/.omao/ containing only that project's policy and state. In short, capability is user-global; policy is project-local.

  2. The two harnesses pull policy in different ways.

    • Claude Code is active. Hooks registered in ~/.claude/settings.json run on every session start and every user prompt; they read the cwd's .omao/ and inject the result into the system context as additionalContext JSON.
    • Kiro is declarative. There are no hooks. Instead the Kiro engine reloads ~/.kiro/steering/, kiro.meta.yaml, and agents/*.json on every invocation, and SKILL bodies explicitly Read .omao/ when they need policy values.
  3. .omao/ is the shared surface across both harnesses. You can switch between Claude Code and Kiro on the same project without losing work — both read and write the same profile.yaml, ontology/, plans/, state/, and audit.jsonl under the same conventions. One asymmetry, caused by the absence of Kiro hooks, is documented in §2.6.

The safety property that holds across both harnesses is that the session is never mutated — only context is appended. In a project without .omao/, Claude Code hooks all no-op out, and Kiro simply runs without policy values, falling back to its static assets.

What this document covers — overview diagram

This diagram is the spine of the document. Each arrow is detailed in the per-harness sections — §1 (Claude Code) and §2 (Kiro) — and the shared piece is summarized in §3.

LayerRoleChange frequencyCreated by
~/.oma/ or ~/Dev/sample-oh-my-aidlcops/OMA source tree (plugins, skills, hook scripts, compiler)At each OMA releaseinstall.sh or git clone
~/.claude/User-global config consumed by Claude CodeOnce per OMA installscripts/install/claude.sh or /plugin marketplace add
~/.kiro/User-global assets consumed by KiroOnce per OMA installscripts/install/kiro.sh
<project>/.omao/That project's policy and stateContinuously, per taskoma setup, oma init, and individual skills

Document flow:

  1. §1 Claude Code harness — how hooks pinned in settings.json turn .omao/ into context on every call.
  2. §2 Kiro harness — how steering, sidecars, and agent profiles re-pull policy rules every invocation without hooks.
  3. §3 Shared — <project>/.omao/ — the policy surface both harnesses share. Producer/consumer mapping and edit points.

1. Claude Code harness

The Claude Code harness centers on an active model: hook scripts emit additionalContext JSON on stdout, and Claude Code appends it to the system prompt. Static assets (plugins, commands, MCP) are exposed via ~/.claude/settings.json. Dynamic policy (.omao/) is read by the hooks on every session and every prompt.

1.1 What each asset references

§1.2 through §1.6 detail what every arrow above corresponds to in code.

1.2 Install — how ~/.claude/ gets populated

scripts/install/claude.sh seats the user-global assets in four steps. The native marketplace path (/plugin marketplace add ...) yields the same result — it just additionally caches a copy under ~/.claude/plugins/cache/ and records it in ~/.claude/installed_plugins.json.

StepFunctionResultDiagram node
1install_plugins (claude.sh:108)4 symlinks under ~/.claude/plugins/<name>/ → SOURCE plugins/<name>/PLUG
2install_commands (claude.sh:130)~/.claude/commands/oma/ symlink → SOURCE steering/commands/oma/CMD
3install_mcp_servers (claude.sh:143)jq-merge each plugin's .mcp.json mcpServers into ~/.claude/settings.json (existing keys preserved)SET#mcpServers
4install_hooks (claude.sh:169)Register hook script paths under ~/.claude/settings.json#hooks.SessionStart and hooks.UserPromptSubmitSET#hooks

Resulting layout:

~/.claude/
plugins/<plugin>/ → SOURCE/plugins/<plugin> # PLUG (symlink)
commands/oma/ → SOURCE/steering/commands/oma # CMD (symlink)
settings.json
"mcpServers": { aws-documentation, aws-iac, aws-pricing, ... } # 11 entries (SET#mcp)
"hooks": {
"SessionStart": [{ "hooks": [{"command": "SOURCE/hooks/session-start.sh"}]}],
"UserPromptSubmit": [{ "hooks": [{"command": "SOURCE/hooks/user-prompt-submit.sh"}]}]
}

1.3 SessionStart hook — context injection at session start

Detail of the SET → SS → ST · ONT → AGENT arrows.

Context blocks emitted at every session start:

BlockSourceCode location
[OMA Session Context] Active Tier-0 Mode: ...cwd/.omao/state/active-modesession-start.sh:25
Project Memory: { ... }cwd/.omao/project-memory.jsonsession-start.sh:39
[OMA Ontology] (one line per Budget · Incident · Deployment)cwd/.omao/ontology/<type>/*.jsonsession-start.sh:53-87
Available OMA Tier-0 Commands: ... (static catalog)hardcodedsession-start.sh:92

Safety guarantees:

  • Honors CLAUDE_PROJECT_DIR over cwd — the hook reads the right .omao/ even when Claude Code spawns it from a different working directory (session-start.sh:20).
  • JSON is emitted via jq, python3, or python (in that order) — if none are present the hook exits 1. The hook never builds JSON via shell interpolation, so ontology files containing quotes, backslashes, or newlines remain safe (session-start.sh:118-150).
  • Kill switchesOMA_DISABLE_TRIGGERS=1 or OMA_DISABLE_ONTOLOGY=1 env vars (session-start.sh:12,53).

1.4 UserPromptSubmit hook — keyword and budget checks per prompt

Detail of the SET → UPS → TRIG · ONT → AGENT arrows.

OutputTrigger conditionCode location
[MAGIC KEYWORD: OMA_TRIGGER]A .omao/triggers.json keyword matches the prompt and (if present) every context_required token is also presentuser-prompt-submit.sh:55-124
[MAGIC KEYWORD: OMA_BUDGET_WARN]Any budget's spend_usd / limit_usd > 0.8user-prompt-submit.sh:130-155
(none)Nothing matches — prompt passes through normallyexit 0

Match rules:

  • Slash commands (/oma:agenticops) and multi-word phrases use substring matching.
  • Single tokens use grep -qw word-boundary matching (e.g., auto does not match inside automobile).
  • Explicit slash commands bypass context_required.

1.5 Tier-0 dispatch — where static assets meet dynamic policy

Once the session is up, the user invokes a slash command such as /oma:autopilot. From that point on, static assets (PLUG, CMD) and dynamic policy (PROF, ONT, ST) coexist in the same context.

The top-of-stack hierarchy is enforced by steering/oma-hub.md:9-30 and steering/workflows/ontology-harness-mandate.md:11-49 — the seven absolute rules there override every SKILL.md body.

1.6 Where to edit (Claude Code side)

Behavior to changeSingle edit pointFollow-up command
Add or version-bump an MCP servermcp: block in plugins/<plugin>/<plugin>.oma.yamlpython3 -m tools.oma_compile <file> then re-merge with bash scripts/install/claude.sh
New keyword triggertriggers: block in <plugin>.oma.yamloma compile, then copy .omao/triggers.json into the user's project
New Tier-0 slash commandAdd steering/commands/oma/<name>.md and update <plugin>.oma.yaml#triggersThe symlink already points at ~/.claude/commands/oma/, so a CLI restart is enough
New SKILLAdd plugins/<plugin>/skills/<skill>/SKILL.mdClaude Code picks it up immediately (symlink)
New session-start context blockUpdate the ADDITIONAL_CONTEXT accumulator in hooks/session-start.shVerify with bash hooks/session-start.sh
Change prompt match ruleshooks/user-prompt-submit.sh (e.g., word boundaries)echo '{"prompt":"..."}' | bash hooks/user-prompt-submit.sh

2. Kiro harness

Kiro has no hooks. Where Claude Code actively emits context via hook scripts, Kiro is declarative — its engine re-reads ~/.kiro/steering/, kiro.meta.yaml, and agents/*.json on every invocation, and SKILL bodies explicitly Read .omao/ when needed.

2.1 What each asset references

§2.2 through §2.5 detail every arrow above with code locations.

2.2 Install — how ~/.kiro/ gets populated

scripts/install/kiro.sh runs five steps. There is no hook-registration step — that is the decisive difference from Claude Code.

StepFunctionResultDiagram node
1install_skills (kiro.sh:91)Flattened symlinks under ~/.kiro/skills/<p>/<s>/. Two-level groups like aidlc/skills/inception/<s> get one extra descentSK
2install_steering (kiro.sh:145)~/.kiro/steering → SOURCE steering/ (manifest, workflows, oma-hub.md)ST
3install_guides (kiro.sh:157)Per-plugin stage-gated guide directoriesGD
4install_agents (kiro.sh:176)Kiro .agent.json profiles (MCP pins + autoApprove)AG
5install_settings (kiro.sh:199)Copy template into ~/.kiro/settings/cli.json (preserved if already present)CFG

Resulting layout:

~/.kiro/
skills/<plugin>/<skill>/ → SOURCE/plugins/<plugin>/skills/<skill> # SK (symlink)
steering/ → SOURCE/steering # ST (symlink)
guides/<plugin>/ → SOURCE/plugins/<plugin>/guides # GD (symlink)
agents/*.agent.json → SOURCE/plugins/<plugin>/kiro-agents/*.json # AG (symlink)
settings/cli.json (file copy from scripts/kiro-cli.template.json) # CFG

2.3 Steering auto-load — absolute rule injection

The ST → AGENT arrow.

The Kiro engine auto-loads everything under ~/.kiro/steering/ on every invocation. Consequently the following content is always present in every Kiro session:

This is equivalent to the rules side of Claude Code's SessionStart hook — the differences are summarized in §2.6.

2.4 SKILL matching — sidecar trigger_keywords

The SK → AGENT arrow.

A kiro.meta.yaml sidecar can sit next to each SKILL (see kiro-setup.md:98-134). The Kiro engine reads it to auto-match natural-language inputs to a SKILL.

# kiro.meta.yaml — example for vllm-serving-setup
kiro:
trigger_keywords:
- "vllm"
- "model serving"
- "PagedAttention"
context_files:
- SKILL.md
- reference/vllm-config.yaml
mcp_required:
- eks-mcp-server
- aws-pricing-mcp-server
phase: operations
approval_required: true
FieldEffect
trigger_keywordsBoost SKILL matching priority on natural-language input
context_filesAdditional files to load alongside the SKILL
mcp_requiredVerify required MCP servers are connected before invocation
phaseTag as Inception / Construction / Operations
approval_requiredWhether checkpoint approval is required

A SKILL without a sidecar still works fine — just on its SKILL.md frontmatter alone.

2.5 Agent profile — agents/*.agent.json

The AG → AGENT arrow.

Each Kiro agent profile is a compile output of tools/oma_compile reading SOURCE <plugin>.oma.yaml#agents. Where Claude Code consolidates 11 MCP servers in a single settings.json, Kiro pins MCPs per agent.

// ~/.kiro/agents/ai-infra.agent.json — symlink → SOURCE/plugins/ai-infra/kiro-agents/...
{
"name": "ai-infra",
"description": "AI runtime infrastructure architect ...",
"tools": ["*"],
"mcpServers": {
"awslabs.eks-mcp-server": { "command": "uvx", "args": ["awslabs.eks-mcp-server==0.1.28"], ... },
"awslabs.aws-documentation-mcp-server": { ... },
"awslabs.aws-pricing-mcp-server": { ... },
"awslabs.cloudwatch-mcp-server": { ... }
},
"autoApprove": { "readOnly": true, "fileWrites": false, "bashCommands": false },
"resources": ["file://.kiro/steering/oma-hub.md", "skill://.kiro/skills/ai-infra/**/*.md"]
}

At runtime the user activates a profile with @ai-infra deploy vllm 70b or similar.

2.6 Kiro asymmetry — the gap left by absent hooks

Some context that Claude Code's hooks actively push is not auto-pushed under Kiro. Operators must close the gap by Reading the relevant files explicitly inside SKILL procedures.

ContextClaude CodeKiro
Steering absolute rules (oma-hub.md, mandate.md)Auto-loaded by Claude Code without hooks~/.kiro/steering/ auto-load ✅
SKILL bodies~/.claude/plugins/<p>/skills/<s>/SKILL.md~/.kiro/skills/<p>/<s>/SKILL.md
MCP server catalogGlobal in settings.json#mcpServersPer-agent in agent profile ✅
Ontology current values snapshot (Budget remaining, open incidents, draft deployments)Pushed by session-start.sh at every session start ✅❌ No auto-push. SKILL body must Read explicitly
active-mode / project-memoryPushed by session-start.sh❌ No auto-push
Per-prompt budget threshold warning ([OMA_BUDGET_WARN])Checked by user-prompt-submit.sh on every prompt ✅❌ No per-prompt check
Keyword triggers (natural language → Tier-0 command)user-prompt-submit.sh + global .omao/triggers.json catalog ✅kiro.meta.yaml#trigger_keywords handles only SKILL matching (no Tier-0 catalog)

Implications:

  • When a Kiro user runs @ai-infra deploy ..., the agent starts unaware of whether the budget is at 80%, whether a draft deployment exists, etc. The SKILL procedure must explicitly Read .omao/ontology/budgets/*.json and .omao/ontology/deployments/*.json to surface that.
  • Absolute Rule #4 from steering/workflows/ontology-harness-mandate.md"on receiving [MAGIC KEYWORD: OMA_BUDGET_WARN], the agent must surface the warning in its first response" — is Claude-Code-specific. Under Kiro the magic keyword is never emitted, so the rule lapses. Operationally, work around this by invoking the cost-governance skill explicitly or registering the budget file under kiro.meta.yaml#context_files.

2.7 Where to edit (Kiro side)

Behavior to changeSingle edit pointFollow-up command
Kiro agent's MCP pinsagents: block in <plugin>.oma.yaml (runtime: kiro)python3 -m tools.oma_compile <file> then re-run bash scripts/install/kiro.sh (refreshes symlinks)
Kiro agent's autoApprove defaultsThe compiler hardcodes {readOnly: true, fileWrites: false, bashCommands: false} (compile.py:213-217); change there or hand-edit the emitted kiro-agents/<id>.agent.jsonRe-run install if you edited the compiler
New Kiro agent profileSame place — agent id, description, tools, mcp, resourcesSame — kiro-agents/<id>.agent.json is regenerated
SKILL trigger_keywordsThe SKILL directory's kiro.meta.yaml (create one if absent)Symlink already points; restart Kiro
Absolute rules / new workflow definitionsteering/oma-hub.md or steering/workflows/<name>.mdReflected in every Kiro session immediately (steering auto-load)
New SKILLplugins/<plugin>/skills/<skill>/ (SKILL.md plus optional kiro.meta.yaml)Re-run bash scripts/install/kiro.sh to add the skill symlink
Kiro default model / autoApprove~/.kiro/settings/cli.json (user-editable; one-time copy from template)Restart Kiro
Stage-gated guideplugins/<plugin>/guides/stages/<stage>.mdSymlink already points; effect is immediate

3. Shared — <project>/.omao/

.omao/ is the policy surface both harnesses share. The reason switching between Claude Code and Kiro on the same project does not lose work is that both read and write .omao/ under identical conventions.

3.1 Disk layout

<project>/.omao/
profile.yaml # 7-Q wizard output (oma setup)
ontology/{budgets,deployments,risks,incidents}/*.json
triggers.json # copy of repo's compile output
plans/ # AIDLC artifacts (spec, design, ADR, stories)
state/ # active-mode, sessions, gates, audit/...
audit.jsonl # schema-validated audit log
notepad.md
project-memory.json

A single oma setup (scripts/oma/setup.sh) configures both user-global (~/.claude/ or ~/.kiro/) and project-local (.omao/) at once. For subsequent projects, oma init alone creates a fresh .omao/ without re-running the wizard.

3.2 Producer / consumer mapping

Each artifact has a clearly separated producer and consumer. To make a change, locate the producer in this table.

.omao/ pathProducerConsumerSchema
profile.yamloma setup (setup.sh:152)every SKILL · oma doctor · enterprise-statusschemas/profile/profile.schema.json
ontology/budgets/*.jsonoma setup seed plus the FinOps team manuallycost-governance · Claude user-prompt-submit.sh · session-start.shbudget.schema.json
ontology/deployments/*.jsonaidlc.code-generation · autopilot-deployincident-response · strict-enterprise gatedeployment.schema.json
ontology/incidents/*.jsonagenticops.incident-responseClaude session-start.sh snapshot · human approverincident.schema.json
ontology/risks/*.jsonaidlc.risk-discovery · modernization.risk-discoveryquality-gates · strict-enterprise gaterisk.schema.json
triggers.jsonoma compile (compile.py:41)Claude user-prompt-submit.sh (Kiro uses sidecars instead)dsl.schema.json#triggers
plans/<slug>/*.mdaidlc.inception.* · aidlc.construction.*Human reviewers · downstream skills(free-form)
state/active-modeSet when entering a Tier-0; cleared by /oma:cancelClaude session-start.sh · other Tier-0 commands (collision avoidance)(single line)
state/sessions/<id>/checkpoint.jsonaidlc-full-loop workflowHuman approver · resume(free-form)
state/gates/<phase>.jsonaidlc.quality-gatesDownstream skills(free-form)
audit.jsonltools/oma_audit/append.py or audit-trail skillAuditors · oma enterprise-statusevent.schema.json

References

OMA internal docs

Core sources