Platform Dispatch — Tier Detection & Tier 2 Inline Execution
Tier Detection Protocol
Detection heuristics run in order — first match wins. Override: SUPERPIPELINES_FORCE_TIER env var, when set to a known tier id (tier_1 | tier_1b | tier_1c | tier_1d | tier_2), bypasses all heuristics. Use when detection picks the wrong tier in a multi-platform workspace.
- Tier 1 (Claude Code):
Tasktool present ANDsubagent_typeparameter accepted. Secondary:CLAUDE_CODEenv var set OR.claude-plugin/plugin.jsonresolvable via${CLAUDE_PLUGIN_ROOT}. (Runtime capability is the primary signal; the secondary check is acceptable here because DETECT() runs in the live runtime where the Task primitive can be probed directly.) - Tier 1b (OpenCode):
$OPENCODE_CONFIG_DIRenv var set OR agent files usingmode: subagentfrontmatter present under the active scope root. - Tier 1c (Antigravity):
agybinary on PATH. - Tier 1d (Codex):
codexbinary on PATH OR.codex-plugin/plugin.jsonresolvable. - Tier 2 (fallback): None of the above. Safe default — sequential inline execution always works.
After resolving tier_id:
READ(skills/sk-platform-dispatch/profiles/{tier_id}.json) → profile object
Return the full profile object. Caller caches it in pipeline-state.json as metadata.platform_profile and sets metadata.runtime_tier = profile.tier.
Platform-specific skill-load tool names
When calling skills from within a running-a-pipeline orchestration, use the correct tool name for the current platform:
| Tier | Skill-load tool | Note |
|---|---|---|
| Tier 1 (Claude Code) | Skill |
Skill(superpipelines:sk-platform-dispatch) |
| Tier 1c (Antigravity CLI, plugin installed) | activate_skill |
activate_skill(sk-platform-dispatch) |
| Tier 1b (OpenCode) | skill (lowercase) |
OC tool naming convention |
| Tier 1d (Codex) / Tier 2 | N/A — no skill tool | Use running-a-pipeline INLINE-DETECT() fallback |
Antigravity CLI (Tier 1c) — installation requirement: The superpipelines plugin must be installed in AGY's extension registry for activate_skill to resolve it. If superpipelines is only installed in Claude Code, activate_skill will fail to resolve the skill even though skill_tool: true in the profile. In that case, running-a-pipeline Phase 0.25 INLINE-DETECT() handles the fallback automatically. This is expected behavior for cross-platform handoff scenarios where not all platforms share a unified plugin registry.
Profile capability fields
| Field | Purpose |
|---|---|
dynamic_subagents |
If true, user picks orchestrator tier only; per-step assignment skipped |
model_field_format |
shorthand (CC) | provider_prefixed (OC) | toml_split (Codex) | omit (Tier 2, Antigravity) |
effort_field_name |
Name of the reasoning-effort key in agent files (null = platform has no effort field) |
effort_field_applies_to_providers |
List of provider prefixes for which effort emits (null = all) |
effort_emit_map |
Translation table for effort values (identity on current profiles; live Codex 0.142.5 accepts low verbatim and fails minimal when tools are exposed) |
subagent_env_override |
Env var that forces all subagents to one model (CC CLAUDE_CODE_SUBAGENT_MODEL) |
subagent_inherit_target |
What inherit resolves to natively (session, primary, orchestrator) |
provider_families |
Provider prefixes this platform accepts |
model_tiers_version |
ISO date stamp for drift detection |
model_tiers[*] |
4-tier table: triage, fast, medium, deep — each with model, effort, optional free_tier, quota_class |
Model Resolution Hook
Before DISPATCH executes any step, callers MUST load sk-model-resolver and call RESOLVE(agent, profile, prefs) for each topology step. The resolved object is persisted to pipeline-state.json metadata.resolved_models[step_id] and consulted by DISPATCH (native_task model override; native_subagent payload; toml_split agent file rewrite; omit = no-op).
running-a-pipeline Phase 0.45 handles this for run-time execution. creating-a-pipeline Phase 4 uses resolution only for the preview table in Phase 5 approval; architect output writes model_tier: and never model:.
DISPATCH Contract
Returns:
{ status: "DONE" | "DONE_WITH_CONCERNS" | "NEEDS_CONTEXT" | "BLOCKED", outputs: [path...], concerns?: string, missing_context?: string, blocker?: string }
Option A — Materialize-at-Runtime (CAD → native)
Data-only pipelines store each agent as a tool-neutral canonical agent def (CAD) under
DATA_ROOT/pipelines/{P}/agents/{agent}.md (frontmatter + inline protocol body). Before a
subagent-capable tier dispatches a step, the orchestrator MATERIALIZES that CAD into the
tool's native agent dir as an ephemeral file, dispatches natively, then treats the file as
disposable cache. The CAD is the single source of truth; the native file is regenerated every
run and never read as source. Schema + translation table: pipeline-auditor-references/references/canonical-agent-def.md.
MATERIALIZE is tier-neutral: it selects the native-agent dir and the translator from the
profile — never from a hardcoded tier string or path (per DEPENDENCY_INVERSION: PROFILE_DRIVEN).
Adding a new subagent-capable tier = adding its extensions.native_agent_dir + a TRANSLATE_CAD_TO_*
branch; no other MATERIALIZE edit.
MATERIALIZE(agent_def_path, resolved, profile, P, scope) → subagent_name:
cad = READ(RESOLVE_DATA_ROOT(scope) + "/" + agent_def_path) // tool-neutral frontmatter + inline body
assert cad.schema_version supported // else BLOCKED: unsupported CAD schema
// native agent dir + file extension are per-tier facts, sourced from the profile:
target = RESOLVE_SCOPE_ROOT(scope, profile.tier) + "/" + profile.extensions.native_agent_dir
+ "/" + P + "/" + cad.name + profile.extensions.native_agent_ext // .md (CC/OC) | .toml (Codex)
ensure_dir(dirname(target))
// translator is selected by dispatch_mechanism — NOT by tier string. Each translator returns
// { content, body_inlined }: YAML tiers return frontmatter (body appended below); the TOML tier
// returns the complete file with the protocol embedded as an `instructions` string.
SWITCH profile.capabilities.dispatch_mechanism:
"native_task" → t = { content: TRANSLATE_CAD_TO_CC(cad, resolved), body_inlined: false }
"native_subagent" → t = { content: TRANSLATE_CAD_TO_OC(cad, resolved, profile), body_inlined: false }
"model_driven" → IF profile.capabilities.model_field_format == "toml_split": // Codex
t = { content: TRANSLATE_CAD_TO_CODEX(cad, resolved, profile), body_inlined: true }
ELSE: BLOCKED — no CAD materializer for this model_driven tier
(Antigravity dynamic subagents are dispatched without a materialized file)
// Filesystem isolation degrades on tiers without a worktree primitive (reviewer isolation is
// unaffected — it is enforced structurally in the translated agent file, not by the worktree):
IF cad.isolation_required == true AND profile.capabilities.worktrees == false
AND profile.extensions.isolation_unavailable_warning present:
w = profile.extensions.isolation_unavailable_warning with "{name}" → cad.name
surface w to the user; append w to metadata.isolation_warning (atomic)
file = t.body_inlined ? t.content : (t.content + "\n\n" + cad.body)
WRITE(target, file) // native agent = native config + inline protocol
return cad.name // subagent_type (CC) / subagent name (OC) / agent name (Codex)
Runtime registration assumption (load-bearing for Option A). Task(subagent_type=cad.name)
resolves the agent file MATERIALIZE just wrote under the workspace/user .claude/agents/
directory. Claude Code resolves agent files present on disk under the active scope root at
dispatch time, so a file written earlier in the same session is dispatchable. IF a materialized
subagent_type fails to resolve at Task() time on a given host, DISPATCH MUST return
BLOCKED with: "Materialized agent '{name}' did not resolve as a subagent_type on this host —
Option A requires same-session agent-file discovery. Verify CC picks up agent files written
under .claude/agents/superpipelines/{P}/ at dispatch time." NEVER silently fall back to a
generic subagent (that would drop the structural isolation the CAD encodes).
TRANSLATE_CAD_TO_CC(cad, resolved) emits CC YAML per the canonical-agent-def §3 table:
name: cad.name
description: cad.description // required by CC agent-file discovery
model_tier: cad.model_tier // resolved.model is passed at the Task() call, not the file
// effort is profile-gated, never hardcoded (CC accepts low|medium|high|xhigh|max — our
// vocabulary maps identity, so no emit map is configured for tier_1):
IF resolved.effort != null AND profile.capabilities.effort_field_name != null:
{profile.capabilities.effort_field_name}: resolved.effort // e.g. effort: medium
maxTurns: cad.turn_budget
// capability intent → CC enforcement primitive (structural):
IF cad.capabilities.write_files == false:
tools: (Read/Glob/Grep + cad.tool_hints.allow ∩ read-class) // excludes Write/Edit/Bash
disallowedTools: Write, Edit, Bash
permissionMode: plan
ELSE:
tools: Read, Write, Edit[, Bash if run_shell][, web tools if network] (+ tool_hints.allow)
permissionMode: acceptEdits
IF cad.capabilities.run_shell == false AND write_files == true: omit Bash from tools
IF cad.isolation_required == true: isolation: worktree
skills: cad.protocol_skills // bundle skills, still tier-discovered; omit if empty
Reviewer isolation stays STRUCTURAL. capabilities.write_files: false is the single
canonical source for reviewer write-deny; MATERIALIZE emits real permissionMode: plan +
disallowedTools: Write, Edit, Bash (and isolation: worktree for writers) — enforcement is
structural on native_task, not convention. This preserves WRITE_REVIEW_ISOLATION: STRUCTURAL_ON_TIER1_1B_1D under the data-only model.
OpenCode (Tier 1b) materialization
TRANSLATE_CAD_TO_OC(cad, resolved, profile) emits OpenCode mode: subagent YAML per the
canonical-agent-def §3 table. Provider-prefixed model + provider-gated reasoningEffort come
from the resolved object; the effort key name and the providers it applies to are read from the
profile (never hardcoded):
mode: subagent
name: cad.name
description: cad.description
model: resolved.model // provider-prefixed, e.g. opencode-go/qwen3.6-plus
maxTurns: cad.turn_budget
// effort is provider-gated — emit only for providers the profile lists:
IF resolved.effort != null
AND provider_prefix(resolved.model) ∈ profile.capabilities.effort_field_applies_to_providers:
{profile.capabilities.effort_field_name}: resolved.effort // e.g. reasoningEffort: high
// capability intent → OC enforcement primitive (structural); emit only the DENY keys:
permission: // omit the whole block ONLY if no key denies below;
// a writer (write_files:true) still emits permission
// when run_shell/network deny (e.g. webfetch: deny)
IF cad.capabilities.write_files == false: edit: deny // reviewer write-deny (structural)
IF cad.capabilities.run_shell == false: bash: deny
IF cad.capabilities.network == false: webfetch: deny
tools: concrete OC tool list from cad.tool_hints.allow (capability-consistent); omit if empty
// isolation_required has NO OC worktree primitive → handled by MATERIALIZE degradation guard
Reviewer isolation stays STRUCTURAL on OC. capabilities.write_files: false emits a real
permission: { edit: deny } — the writer cannot mutate tracked source. This is the
extensions.reviewer_isolation_recipe for tier_1b and upholds WRITE_REVIEW_ISOLATION: STRUCTURAL_ON_TIER1_1B_1D. OC has no worktree primitive, so a writer's isolation_required
degrades to a surfaced warning (filesystem isolation only) — the write/review boundary itself
never degrades to convention on OC.
Runtime registration assumption (load-bearing for Option A on OC). A subagent dispatched by
name resolves the mode: subagent agent file MATERIALIZE wrote under the active scope root's
agent/superpipelines/{P}/ dir. OpenCode discovers agent files present on disk under the active
scope root at dispatch time. IF a materialized OC subagent fails to resolve by name at dispatch,
DISPATCH MUST return BLOCKED with: "Materialized OC agent '{name}' did not resolve as a
subagent on this host — Option A requires same-session agent-file discovery. Verify OpenCode
picks up agent files written under .opencode/agent/superpipelines/{P}/ at dispatch time."
NEVER silently fall back to a generic subagent (that would drop the structural isolation the CAD
encodes).
Codex (Tier 1d) materialization
TRANSLATE_CAD_TO_CODEX(cad, resolved, profile) emits a complete Codex agent TOML document
per the canonical-agent-def §3 table. Unlike the YAML tiers, the protocol body is embedded as a
triple-quoted instructions string (TOML has no frontmatter/body split), so the translator
returns the whole file (body_inlined: true):
name = cad.name
description = cad.description # REQUIRED by the live parser ("must define a description")
model = resolved.model # e.g. "gpt-5.4"
IF resolved.effort != null
AND (profile.capabilities.effort_field_applies_to_providers == null # null = all providers
OR provider_prefix(resolved.model) ∈ profile.capabilities.effort_field_applies_to_providers):
model_reasoning_effort = resolved.effort # mapped by sk-model-resolver via effort_emit_map (identity); emit verbatim
# capability intent → Codex sandbox primitive (host-conditional structural, see below):
sandbox_mode = cad.capabilities.write_files ? "workspace-write" : "read-only"
# cad.turn_budget has NO Codex primitive — the live parser rejects `turn_limit` as an unknown
# field (verified 2026-07); do not emit it. The turn budget degrades to prompt-level guidance.
developer_instructions = """<cad.body verbatim>""" # `instructions` is rejected by the live parser
# read-only ALSO denies shell-exec writes + network, so a reviewer (run_shell:false,
# network:false) is fully covered by sandbox_mode alone. For a WRITER that denies network, emit a
# [sandbox_workspace_write] table — and ONLY after all top-level keys (TOML table-ordering rule):
IF cad.capabilities.write_files == true AND cad.capabilities.network == false:
[sandbox_workspace_write]
network_access = false
Reviewer isolation on Codex is STRUCTURAL, host-conditionally. capabilities.write_files: false emits sandbox_mode = "read-only" per extensions.reviewer_isolation_recipe — but
enforcement depends on the live host's sandbox helpers. Live verification (2026-07,
docs/agents/verification/codex-discovery-2026-07.md Probe D) showed a read-only agent writing
to the workspace when the parent session ran danger-full-access on a host without sandbox
support. Runtime guard (MANDATORY): before dispatching any write_files: false step on a
profile that defines extensions.isolation_unsandboxed_warning, determine whether the current
session's sandbox is disabled (danger-full-access) or host sandbox helpers are unavailable;
if so, surface that warning at dispatch, append it to metadata.isolation_warning (atomic),
and treat the step's review verdicts as ADVISORY for this run — never report them as
structurally enforced. On sandbox-capable hosts the read-only deny is structural and
WRITE_REVIEW_ISOLATION: STRUCTURAL_ON_TIER1_1B_1D holds. Codex has no per-subagent worktree
primitive (worktrees: false); it isolates per-thread at the app level, so a writer's
isolation_required carries no additional materialized primitive.
Concurrency. Codex fans subagents out per topology.json, capped at
profile.extensions.max_concurrent_subagents (6). The orchestrator must not dispatch more than
that many parallel branch workers at once; excess steps queue.
Runtime registration assumption (load-bearing for Option A on Codex). A subagent named
cad.name resolves the TOML agent file MATERIALIZE wrote under the active scope root's
agents/superpipelines/{P}/ dir. IF a materialized Codex agent fails to resolve by name at
dispatch, DISPATCH MUST return BLOCKED with: "Materialized Codex agent '{name}' did not
resolve on this host — Option A requires same-session agent-file discovery. Verify Codex picks up
agent TOML written under .codex/agents/superpipelines/{P}/ at dispatch time." NEVER
silently fall back to a generic subagent (that would drop the structural isolation the CAD encodes).
Materialized-Cache Cleanup
The materialized native agent dir is disposable cache, owned by DISPATCH:
CLEANUP_MATERIALIZED(P, scope): // called by orchestrator at Phase 4 on `completed`
profile = metadata.platform_profile // cached in pipeline-state.json by DETECT()
// native agent dir + scope-root tier are profile-sourced — same dir MATERIALIZE wrote to:
delete_dir(RESOLVE_SCOPE_ROOT(scope, profile.tier) + "/" + profile.extensions.native_agent_dir
+ "/" + P + "/") // scoped to {P} ONLY
- Delete ONLY the
{P}subdir — never the parent{native_agent_dir}/(other pipelines' caches). - The signature stays 2-arg (
P,scope): every architect-emittedentry.mdcalls it that way; the profile is read from cached run state, keeping native dirs portable across tiers. - On
escalated/failed/blocked: leave the dir for debugging; it is regenerated next run regardless. - DISPATCH ALWAYS re-reads the CAD and re-materializes each run — the cache is never authoritative.
Tier-Specific DISPATCH Behavior
Skills branch on profile.capabilities flags — NOT on profile.tier string. This ensures unknown future platforms with familiar capabilities route correctly without skill edits.
mechanism = profile.capabilities.dispatch_mechanism
SWITCH mechanism:
"native_task" → IF step.agent_def present: // data-only pipeline
name = MATERIALIZE(step.agent_def, resolved_models[step.id], profile, P, scope)
Task(subagent_type=name, model=resolved_models[step.id].model,
prompt=build_prompt(step, inputs))
ELSE: // BC3: legacy old-root pipeline
Task(subagent_type=step.agent, model=resolved_models[step.id].model,
prompt=build_prompt(step, inputs)) // agent already registered; no materialize
"native_subagent" → IF step.agent_def present: // data-only pipeline
name = MATERIALIZE(step.agent_def, resolved_models[step.id], profile, P, scope)
dispatch OC native subagent (mode: subagent) by `name`, passing
resolved_models[step.id].model + reasoningEffort (when set) in the payload,
prompt=build_prompt(step, inputs)
if it fails to resolve by name → BLOCKED per OC registration assumption
ELSE: OC native mode:subagent dispatch via step.agent file (legacy)
"model_driven" → IF step.agent_def present: // data-only pipeline
IF profile.capabilities.model_field_format == "toml_split": // Codex
name = MATERIALIZE(step.agent_def, resolved_models[step.id], profile, P, scope)
dispatch Codex native subagent by `name` (model + model_reasoning_effort
+ sandbox_mode already in the materialized TOML); Codex fans out per
topology.json, capped at extensions.max_concurrent_subagents
if it fails to resolve by name → BLOCKED per Codex registration assumption
ELSE: // Antigravity dynamic subagents
// No materialized file: AGY auto-manages dynamic subagents at the
// orchestrator tier (dynamic_subagents:true, model_field_format:"omit").
surface profile.degradation_warnings // orchestrator-tier-only model + convention isolation
body = READ(RESOLVE_DATA_ROOT(scope) + "/" + step.agent_def).body
dispatch dynamic subagent with task = build_prompt(step, inputs) + body;
OMIT per-step model (host orchestrator owns subagent model selection)
// reviewer isolation is CONVENTION here — review output is a self-check
// (the surfaced degradation says so); no structural write-deny exists
ELSE: Emit orchestration prompt; Codex model fans out per topology.json (legacy)
"inline" → Tier 2 inline loop (see Tier 2 Inline Loop below) // data-only handled: reads CAD body
DEFAULT (unknown) → fallback to "inline" + emit:
"⚠️ Unknown dispatch_mechanism '{mechanism}'. Falling back to inline execution."
Tier 2 Inline Loop
For each step in topology.json (dependency order):
- Load protocol: data-only pipeline →
Read(RESOLVE_DATA_ROOT(scope) + "/" + step.agent_def)and execute the CAD's inline protocol body as data (no subagent boundary on Tier 2). Legacy old-root pipeline (noagent_def) →Skill(step.protocol_skill). Either way the agent's full protocol enters the orchestrator's context. - Resolve inputs: read upstream step outputs from disk using paths recorded in
pipeline-state.json[phases][upstream].outputs. - Execute inline: the orchestrator performs the protocol's actions using
Read,Write,Edit,Bash,Glob,Grep. NoTask()call. - Persist outputs: write all output files to the paths declared in
step.output_paths. - Self-verify: run the protocol's stated verification steps inline. Capture pass/fail.
- Update state: append phase entry to
pipeline-state.jsonwithstatus,outputs,errorvia the atomic write pattern fromsk-pipeline-state. - Status check: emit one terminal status per the contract above.
- Branch on status:
DONE→ proceed to next step.DONE_WITH_CONCERNS→ read concerns; proceed if observational; address inline if correctness/scope.NEEDS_CONTEXT→ re-execute the step with added context loaded from the named files. Bounded retry: maximum 2 attempts.BLOCKED→ setstate.status = "escalated", preserve temp dir, surface to user with the blocker text, stop the loop.
Parallel patterns (Pattern 2 / 2b) degrade to sequential on Tier 2. The dispatch loop processes branch workers one at a time in declared order. A merger step receives all branch outputs only after each branch finishes sequentially.
Iterative patterns (Pattern 3) execute inline. The orchestrator runs tester → analyzer → fixer in the same session; cycle limit (3) and architectural-escalation gate from dispatch-protocols.md Pattern 3 still apply.
Spec-Driven (Pattern 5) on Tier 2: Tasks execute sequentially. Each task's two-stage review runs inline — the orchestrator reads spec.md, applies the spec-reviewer protocol, then applies the quality-reviewer protocol. Reviewer isolation is convention-only. The orchestrator runs both writer and reviewer protocols with its own full toolset. There is no structural barrier preventing a reviewer from writing. Document this degradation in any user-facing report and treat Tier 2 reviews as advisory, not structurally enforced.
Per-Tier Scope-Root Resolution
sk-pipeline-paths resolves scope-root by reading metadata.runtime_tier from the pipeline state and walking the chain above. For Tier 2, if a pipeline was scaffolded on CC (paths reference .claude/), the dispatch layer rewrites .claude/ → .superpipelines/ at read/write time so portable artifacts continue to resolve. This rewrite is invertible: state files stamp the original scope-root string for auditability.
Degradation Surfacing (Profile-Driven)
Degradation warnings are owned by the profile — not hardcoded in skills. When a profile has non-empty degradation_warnings:
warnings = profile.degradation_warnings
IF warnings is non-empty:
1. Emit each warning at run START with "⚠️" prefix (running-a-pipeline Phase 0.25)
2. Emit each warning at run END in entry skill completion summary
3. Write join(warnings, "\n") to pipeline-state.json as metadata.isolation_warning
Adding or changing a degradation message for any platform requires editing only that tier's JSON profile. No skill edits required.
Worktree Behavior
On Tier 2, the orchestrator MUST verify the workspace is clean (no uncommitted changes) before starting a destructive step, and MUST commit between steps to enable rollback. If the workspace is dirty, surface to user and stop.
Cross-Tier Resume Protocol
Invoked by running-a-pipeline Phase 0.25 when resuming an existing run:
new_profile = DETECT()
new_tier = new_profile.tier
prev_tier = metadata.runtime_tier ?? metadata.tier // backward compat
IF new_tier != prev_tier:
append { from: prev_tier, to: new_tier, at: iso8601_now() }
to metadata.tier_changes (atomic write)
metadata.runtime_tier = new_tier
metadata.platform_profile = new_profile
metadata.isolation_warning = join(new_profile.degradation_warnings)
emit: "⚠️ Cross-tier resume: scaffolded on {metadata.source_tier},
now running on {new_tier}.
Dispatch adapts to {new_tier} capabilities."
emit each new_profile.degradation_warning
ELSE:
proceed silently (no tier change, no log entry)
metadata.source_tier is NEVER updated on resume. It records where the pipeline was originally scaffolded, permanently.
Status Protocol Reference
| Worker status | Orchestrator action (any tier) |
|---|---|
DONE |
Proceed to next phase. |
DONE_WITH_CONCERNS |
Read concerns. If correctness/scope: address before review. If observational: proceed. |
NEEDS_CONTEXT |
Identify missing context; re-dispatch with same model + added context. Max 2 retries on Tier 2. |
BLOCKED |
(1) provide more context; (2) higher effort/model (Tier 1 only); (3) decompose; (4) escalate. NEVER retry same approach. |
Red Flags — STOP
- "I'll skip tier detection since I know this is Claude Code." → STOP. Detection is cheap; profile caching enables resume, portability validation, and cross-tier advisory from any checkpoint.
- "I'll branch on
metadata.tier == 'tier_2'instead of reading the profile." → STOP. Tier string branching breaks when new platforms arrive. Always branch onprofile.capabilitiesflags. - "I'll call
Task()from the Tier 2 inline loop." → STOP. Tier 2 has noTask()primitive; the call fails. Use inlineSkill+ own tools. - "Reviewer ran clean on Tier 2, so the code is verified." → STOP. Tier 2 reviewer isolation is convention-only. Surface the degradation to the user; do not promote advisory reviews to structural guarantees.
- "I'll re-detect tier after a tool failure to see if something changed." → STOP. Tier is immutable per run. A tool failure is a tool failure, not a tier change.
Reference Files
pipeline-runner-references/references/dispatch-protocols.md— Tier-specific dispatch shapes.sk-pipeline-state/SKILL.md— State schema (includingmetadata.source_tierandmetadata.runtime_tier).sk-pipeline-patterns/SKILL.md— Pattern definitions referenced by Tier 2 inline loop.running-a-pipeline/SKILL.md— Loads this skill in Phase 0.25.