🧬 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)
fieldin CC schema?drives CC selection?read by ork?
descriptionYES (req)YES — the only onegenerate-indexes (regex)
keywordsnono5 hooks ✓
taskTypes / examplePromptsnonodocs-gen ✓
model / tools / effortYESno (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