🧬 Activation & frontmatter — the two layers
corrected with hard data · CC docs (claude-code-guide, fetched live) + ork source grep · tabs + mermaid
Correction: frontmatter fields DO exist (CC documents 16; plugin agents get 11). And ork's custom keywords: is NOT dead — 5 ork hooks read it. The real split: CC's native selector uses only description; ork's hook layer uses keywords. Two layers, not one.
The frontmatter schema — who recognizes what
flowchart TB
classDef cc fill:#0e2742,stroke:#60a5fa,color:#bfdbfe;
classDef pl fill:#0f2e22,stroke:#34d399,color:#d1fae5;
classDef ork fill:#241b33,stroke:#a78bfa,color:#e9d5ff;
CC["CC documented schema — 16 fields
name* · description* · tools · disallowedTools · model
permissionMode · maxTurns · skills · mcpServers · hooks
memory · background · effort · isolation · color · initialPrompt"]:::cc
PL["Plugin agents get 11 of them
(hooks · mcpServers · permissionMode NOT allowed)"]:::pl
ORK["ork-custom — NOT in CC schema
keywords · taskTypes · examplePrompts
critical_system_reminder · category"]:::ork
CC --> PL
CC -. CC ignores unrecognized fields
(warnings, not errors) .-> ORK
CC schema (16): name* description* tools disallowedTools model permissionMode
maxTurns skills mcpServers hooks memory background effort isolation color initialPrompt
→ plugin agents get 11 (no hooks/mcpServers/permissionMode)
ork-custom (NOT in CC schema): keywords · taskTypes · examplePrompts · critical_system_reminder · category
→ CC ignores unrecognized fields (warnings not errors)
| field | in CC schema? | drives CC selection? | read by ork? |
| description | YES (req) | YES — the only one | generate-indexes (regex) |
| keywords | no | no | 5 hooks ✓ |
| taskTypes / examplePrompts | no | no | docs-gen ✓ |
| model / tools / effort | YES | no (configures AFTER) | — |
Layer 1 — CC native selection (description only)
flowchart LR
classDef det fill:#0f2e22,stroke:#34d399,color:#d1fae5;
classDef prob fill:#3a2a0a,stroke:#f59e0b,color:#fde68a;
T([task needs a subagent]) --> Q{how is it chosen?}
Q -->|"@agent-mention"| M["deterministic
'guarantees it runs'"]:::det
Q -->|"subagent_type= / Agent(type)"| S["deterministic ★ WIRE"]:::det
Q -->|automatic| D["reads description
+ task + context"]:::prob
D --> P["probabilistic — loses to the
generic gravity well"]:::prob
CFG["model · tools · effort · memory ..."] -. configure the agent AFTER it's picked
(do NOT drive selection) .-> D
task → how chosen?
@agent-mention ........ DETERMINISTIC ('guarantees it runs')
subagent_type= ........ DETERMINISTIC ★ WIRE
automatic ............. reads DESCRIPTION + task + context → PROBABILISTIC
(model/tools/effort configure the agent AFTER selection, don't drive it)
CC docs, verbatim: "Claude uses each subagent's description to decide when to delegate." No keyword index, no routing table. The reliable paths are @mention and explicit subagent_type=.
Layer 2 — ork's OWN layer (this is where keywords lives)
flowchart LR
classDef ork fill:#241b33,stroke:#a78bfa,color:#e9d5ff;
classDef hook fill:#0e2742,stroke:#60a5fa,color:#bfdbfe;
KW["keywords: field
(37 agents)"]:::ork
KW --> H1[decision-processor]:::hook
KW --> H2[pre-compact-task-done]:::hook
KW --> H3[output-validator]:::hook
KW --> H4[realtime-sync]:::hook
KW --> H5[issue-subtask-updater]:::hook
DESC["description"]:::ork --> GI["generate-indexes.js
regex: 'Activates for…'
→ routing-index prose in CLAUDE.md"]:::hook
ADV["task-agent-advisor.ts
matchSpecialistDomain()"]:::hook
keywords: field (37 agents) → read by 5 ork hooks:
decision-processor · pre-compact-task-done · output-validator · realtime-sync · issue-subtask-updater
description → generate-indexes.js (regex 'Activates for…') → routing-index prose in CLAUDE.md
advisor: task-agent-advisor.ts matchSpecialistDomain()
So keywords is NOT dead — it feeds ork's hook layer. BUT note: the generated routing-index prose in CLAUDE.md is built from the description (regex), and CC ignores that prose for native selection. Two different things I'd wrongly merged.
How we improve — across BOTH layers
flowchart TB
classDef wire fill:#0f2e22,stroke:#34d399,color:#d1fae5;
classDef ground fill:#3a2a0a,stroke:#f59e0b,color:#fde68a;
subgraph CCN["Layer 1 · CC native"]
W["① WIRE — subagent_type= / @agent
(the deterministic path CC documents)"]:::wire
end
subgraph ORKL["Layer 2 · ork hooks"]
A["advisor + keyword-driven hooks
nudge generic → specialist"]:::wire
end
subgraph MERIT["Merit"]
G["② GROUND — knowledge in agent BODY
recall 2/4 → 4/4 (control-validated)"]:::ground
end
W --> FIRE([specialist FIRES])
A --> FIRE
FIRE --> G --> CATCH([catches what a generic misses])
Layer 1 (CC native): ① WIRE — subagent_type= / @agent (deterministic, documented)
Layer 2 (ork hooks): advisor + keyword-driven hooks nudge generic → specialist
→ specialist FIRES
Merit: ② GROUND — knowledge in agent BODY (recall 2/4 → 4/4, control-validated)
→ catches what a generic misses
Net: wire on the layer that's deterministic (CC native subagent_type=), keep ork's keyword hooks doing their job, and ground the body for merit. Don't tune the description for selection (A/B Δ0) and don't delete keywords (5 hooks need it).
Plan: before → after
flowchart LR
classDef bad fill:#3a1414,stroke:#ef4444,color:#fecaca;
classDef good fill:#0f2e22,stroke:#34d399,color:#d1fae5;
subgraph B["BEFORE — today"]
B1["ork 12.5% · ratio 0.79"]:::bad
B2["generic catch-all > catalog"]:::bad
B3["specialists ungrounded"]:::bad
B4["advisor advisory-only"]:::bad
B5["pilot uncommitted in src/"]:::bad
end
subgraph A["AFTER — the plan"]
A1["ratio → past 1.0"]:::good
A2["wired: subagent_type= fires"]:::good
A3["grounded body: recall 2/4→4/4"]:::good
A4["advisor redirects generic→specialist"]:::good
A5["built · committed · extended"]:::good
end
B1 --> A1
B2 --> A2
B3 --> A3
B4 --> A4
B5 --> A5
BEFORE today → AFTER the plan
ork 12.5% · ratio 0.79 ratio → past 1.0
generic > catalog wired: subagent_type= fires
specialists ungrounded grounded body: recall 2/4→4/4
advisor advisory-only advisor redirects generic→specialist
pilot uncommitted built · committed · extended
How we measure it — we lean on OLD session data
flowchart LR
classDef d fill:#0e2742,stroke:#60a5fa,color:#bfdbfe;
classDef g fill:#0f2e22,stroke:#34d399,color:#d1fae5;
PAST["past sessions"]:::d --> J["subagent-spawns.jsonl
1040 / 31 days"]:::d
J --> AU["run-activation-audit.sh"]:::d --> BASE["BEFORE: 12.5% · 0.79"]:::d
BASE --> FIX{{"apply WIRE + GROUND"}}:::g
FIX --> NEW["new sessions"]:::g --> J2["fresh spawns"]:::g --> AU2["re-audit"]:::g --> AFT["AFTER: ratio ↑"]:::g
AFT -. compare delta .-> BASE
past sessions → subagent-spawns.jsonl (1040 / 31 days)
→ run-activation-audit.sh → BEFORE baseline (12.5% · 0.79)
→ apply WIRE + GROUND → new sessions → fresh spawns
→ re-audit → AFTER (ratio ↑) → compare delta vs BEFORE
The whole before/after IS old-vs-new session telemetry. Reliance caveats: rolling 31-day window — old data ages out; "never fired = absent from the window, a strong signal NOT proof of zero." The RTK-discover 0.2% scare was a trailing window dominated by a pre-fix broken period — wait for fresh post-fix sessions to accumulate before claiming lift. And it's single-user / single-machine telemetry — not representative of all ork users.
sources: code.claude.com/docs/en/sub-agents.md (frontmatter table L265–287; delegation L25,651) · plugins-reference.md (plugin agent fields L71–72; unrecognized-field handling L457–472) · ork grep: keywords→5 hooks, generate-indexes.js:91 reads description