Skip to content

Cards — the communication protocol

A card is a small YAML document describing one shareable artifact, and it is the only thing you ever exchange with the platform. Loading a task, submitting a scaffold, pinning a model, publishing a result — all the same gesture: read a card, or write a card. (If you know the Hugging Face Hub, the analogy is exact.)

Both directions, one file

Load a task/scaffold card to use an artifact. Submit task and scaffold repos with galapagos submit. Model cards are local configuration only; discovery cards use the Hub API/browser flow.

Extensible by default

Defined in galapagos.cards.schema (pydantic v2, extra="allow"). A small required core, plus any method- or task-specific extras you like — no schema change needed. The tables below mark the core.

The name rule

card.name must equal its directory name. The submission gate enforces it.

There are four card types:

Card Describes Loaded with Lives at
Task Card an evaluation task (problem + metrics + evaluator) gx.load_task tasks/<name>/card.yaml
Scaffold Card a discovery method (controller + six components) gx.GalapagosScaffold.from_card scaffolds/<name>/card.yaml
Model Card a local model configuration (path + host) gx.GalapagosModel.from_card user-supplied; no Hub upload route
Verification Card a lightweight discovery claim Hub POST /api/verifications legacy Hub endpoint

Task Card

A task is a discovery problem. The Task Card states what to optimize (the problem and its metrics), how to score it (the deterministically-verifiable evaluator), and under what constraints (the software/hardware it needs). It is the single source of truth for a task; the Evaluator it points at is supplied to every scaffold that runs the task, so runnable scaffolds can be paired with runnable compatible tasks.

Field Type Required Meaning
name str yes (core) unique task slug. Must be URL/path-safe: lowercase letters, numbers, _, and -; no whitespace.
display_name str no (recommended) Human-readable task title.
organization str no (recommended) Hub organization/namespace. Together with name, this gives the Hub repo id: <organization>/<name>. Must not contain whitespace.
domain str no (recommended) Top-level domain (math, gpu_kernel, systems, ml, nlp, bio, …). Defaults to general.
family str no (recommended) The macro / sub-domain grouping (e.g. packing).
summary str no (recommended) A one-line description.
system_message str no (but the submission gate requires it) The task's LLM system prompt — persona, problem statement, objective. It fills the proposer's system slot (as task.context) and replaces the scaffold's generic default. The human-facing write-up belongs in the sibling README.md, not here.
metrics list[dict] no (recommended) The evaluation metrics — a list, since a task may score on several (see below).
components.initial_program path yes for runnable tasks The seed/candidate filename. Its extension must exactly match the canonical suffix for language (for example python.py, cpp.cpp). Metadata-only status: spec cards may omit it.
environment obj no What the evaluator needs to run and score the task — the timeout, the GPU, the image, the keys. One block: see the sub-key table below.
language str yes Canonical seed/solution language (python, cpp, rust, r, …). Abbreviations and aliases such as py and c++ are rejected; use python and cpp.
modality str no (recommended) The I/O modality (text, image, …).
references obj no (recommended) Provenance and the target to beat. Free-form, and the one field every bundled card sets. Common keys: source (where the task came from), url / paper / repository, best_known (the number to beat) and found_by / date, plus dataset coordinates (dataset, split, problem_id, level, contest).
assets list no The sibling files the task needs beyond its components — examples, fixtures, a statement.txt, a small data/** the evaluator reads. A file the evaluator opens at run time but the card does not list here is dropped from the Hub upload, and the Hub copy then scores a silent zero. Do not list README.md: collect_bundle() always ships a sibling README, so declaring it is a no-op.
external_resources list[dict] no Large data or artifacts that should not be uploaded to the Galapagos Hub; each entry stores a URI, checksum, and target path to materialize during load_task().
metadata obj no Free-form metadata.

name and language are required by the schema. A runnable card must also declare components.initial_program, whose filename extension is checked against language when the card is loaded. The schema is extra="allow", so a card may carry method- or task-specific keys beyond this table — they round-trip untouched.

environment — what the evaluator needs

Most cards need no environment block at all. Every key below is one the loop actually reads (the last two are prose, for humans); a key nothing reads is a key that drifts, so there are none.

This used to be two blocks — evaluation ("how the task is run") and constraint ("what it needs") — and they were describing the same thing. The split had started to cost: gpu and gpus were the same request, api_keys and env both ended up in the same container, packages/requirements/ library were three ways to name a dependency, and two keys called network meant different things (a bool "reaches the internet" and a docker --network mode). An un-migrated card that still writes evaluation/constraint keeps working — they fold into environment at load time.

Sub-key Meaning
est_runtime_s the expected evaluation time. It sets the evaluator's timeout (est*4+30) — the most load-bearing key here, and the only thing that stops a runaway candidate. The loop is sequential, so a hung candidate stalls the whole run for exactly that long: size it to the task's honest cost rather than reaching for a big number. The bundled cards span 303600; a card that declares nothing gets 60 (→ a 270 s timeout).
gpu requests --gpus all in Docker mode. A flag that carries prose — the 317 cards that set it say things like "CUDA GPU" or "8x AMD MI300X", naming the hardware a human needs; the value is never passed to Docker. A GPU task therefore fails loudly on a host with no NVIDIA Container Toolkit rather than silently scoring zero on CPU.
api_keys keys the evaluator needs (e.g. an LLM-judge task). The sealed container forwards exactly these and nothing else — a key the card does not declare cannot reach the evaluator.
env other environment variables forwarded into the container, by name: the framework copies the host's value. A container inherits nothing, so an evaluator that reads a variable must say so (188 FrontierCS cards name FRONTIERCS_JUDGE_URL this way).
network the container's network mode (e.g. host, when the evaluator must reach a judge service on the host's loopback).
docker_access grants the evaluator the host Docker socket, for a task whose evaluator is itself a Docker client. This is the one case where the whole scaffold loop runs inside the container — and where the container stops being a sandbox. See Task environments.
cascade_evaluation / cascade_thresholds staged scoring — cheap stages gate the expensive ones. Only meaningful if the task's evaluator.py defines evaluate_stage1; 4 do.
dockerfile the task's Dockerfile — and the only image key there is, because the Dockerfile is the image. It defaults to a sibling Dockerfile, so ordinary cards omit the field. A task that ships none is refused unless its review-visible submission metadata declares the documented host_only exception with a reason; mlx_metal_kernel_opt is the one bundled example and must run locally. Container-capable Dockerfiles must start FROM a Galapagos base — the submission gate checks this.
requires / toolchain prose, for a human reading the card: the stack a solver needs, the compiler the candidate is built with. Documentation, not enforcement — nothing branches on them.

Where the evaluator runs

docker is the default — the task is scored inside its own image. That is a run-level choice, it lives in the config, and it is the only input:

galapagos run ... --general.eval_mode local   # a config value like any other

The card has no say. It states facts about the task; where to score it is this run's decision, so a run is reproducible from its config alone — no card and no shell variable can move it. The sole bundled task without a Dockerfile is mlx_metal_kernel_opt, which requires Apple Metal and runs with --general.eval_mode local.

name and organization are identifiers, not display strings. Keep them stable and path-safe; use display_name for spaces, capitalization, and presentation.

The evaluation metrics is a list of dicts

A task is rarely single-objective: a kernel must be both correct and fast; a packing must be both valid and dense. So metrics is a list, one dict per objective (the legacy metric field holds a single {key, direction, type} dict):

Sub-field Meaning
metric_name the metric's key in the Evaluator's output dict (e.g. combined_score, latency_ms).
metric_direction maximize | minimize.
metric_description what the metric means, in prose.
metric_computation how it is computed — the deterministic rule the evaluator implements.
metric_function free-form extra — not part of the Metric schema and not read by the platform (kept via extra="allow"); names the evaluator function or stage that produces the metric (e.g. evaluate, evaluate_stage2).

The first metric is, by convention, the headline combined_score the search drives.

Task components

components bundles the files that make a task runnable. File references are resolved relative to the card.yaml directory, and submitted tasks may only reference files under that same task root. Absolute paths, .. escapes, and symlink escapes are rejected during submission/loading.

Component What it is
initial_program the seed file path (for example initial_program.py) — the search starts from this file, with # EVOLVE-BLOCK markers around the region the search may rewrite. Its suffix must match the canonical language.
evaluator the deterministically-verifiable scorerevaluate(program_path) -> dict returns iterative-search metrics. The same file may optionally expose evaluate_final(program_path) for one automatic frozen-winner evaluation after search; this requires no second component field. See the authoring guide. This is the Evaluator component.
config an optional config.yaml of task-specific knobs.
requirement a requirements.txt path, or an inline list of pip requirements.

A status: spec card may omit initial_program as a metadata-only entry. Every runnable card must declare it explicitly; cards never infer a seed filename or accept inline source in its place.

Assets and external resources

Use assets for the small supporting files that should live inside the Galapagos task repo — the ones components does not already name:

assets:
  - examples/**
  - data/small_fixture.json          # a fixture the evaluator opens at run time

assets is not decoration: collect_bundle() uploads the card, the components files, a sibling README.md and the task's Dockerfile (both always — never list either), and every assets glob, and nothing else. A data file the evaluator reads but the card does not declare simply does not travel, and the Hub copy of the task scores a silent zero.

Use external_resources for large data that should stay in external storage such as Hugging Face Datasets, S3, GCS, or an HTTPS object store. The Hub stores the declaration in the Task Card; the large bytes stay outside the Galapagos Hub and are downloaded by load_task() into the task root.

external_resources:
  - name: benchmark_data
    uri: https://huggingface.co/datasets/our_org/circle_packing/resolve/main/data.zip
    sha256: "<sha256>"
    size_bytes: 123456789
    unpack: true
    path: data/benchmark/

path is always a relative path under the materialized task root. After loading, the task object sees the same file layout whether it came from the default cache or a user-specified local path.

The full template

Every field the schema defines, in the order a bundled card writes them. Only name is required; everything else is optional, and most tasks use a fraction of this.

name: circle_packing                       # (core) the slug — MUST equal the directory name
display_name: Circle Packing (n=26)
organization: our_org                      # repo_id = <organization>/<name>
domain: math                               # top-level domain
family: packing                            # macro / sub-domain — drives the docs/Hub taxonomy
summary: "Pack 26 circles in the unit square; maximize the sum of radii."

system_message: |                          # the proposer's SYSTEM prompt for this task
  You are an expert mathematician specializing in circle packing and computational geometry.
  Find centers and radii for 26 non-overlapping circles inside the unit square [0,1]^2 that
  maximize the sum of radii. Only the code inside the EVOLVE-BLOCK is modified by the search.
  Validity and the score are recomputed independently from the returned geometry (anti reward-hacking).

metrics:                                   # a list — a task may report several metrics
  - metric_name: combined_score            # the first one is the headline the search drives
    metric_direction: maximize
    metric_description: "Fraction of the AlphaEvolve best (sum_radii / 2.635)."
    metric_computation: "Re-validate the geometry, then sum_radii / 2.635; 0.0 if invalid."
    metric_function: evaluate
  - metric_name: sum_radii
    metric_direction: maximize
    metric_description: "Total radius of the 26 packed circles."
    metric_computation: "Sum of the radii recomputed from the returned centers/radii."
    metric_function: evaluate

components:                                # what makes the task RUNNABLE
  initial_program: initial_program.py      # the seed file path; suffix must match language: python
  evaluator: evaluator.py                  # the deterministic verifiable scorer
  config: config.yaml                      # optional task-specific knobs
  requirement: [numpy]                     # inline list, or a requirements.txt path

environment:                               # what the EVALUATOR needs. Most cards need only the first
  est_runtime_s: 60                        # line. Sets the evaluator timeout (est*4+30) — and the
                                           # sequential loop's only brake on a hung candidate.
  # gpu: true                              # requests --gpus all; fails LOUDLY if unavailable
  # api_keys: [OPENAI_API_KEY]             # the ONLY secrets forwarded into the sealed container
  # env: [FRONTIERCS_JUDGE_URL]            # other vars forwarded BY NAME (a container inherits none)
  # network: host                          # e.g. to reach a judge on the host's loopback
  # docker_access: true                    # evaluator is itself a Docker client (NOT a sandbox)
  # cascade_thresholds: [0.5, 0.8]         # only if the evaluator defines evaluate_stage1
  # requires: "prose, for a human"         # documentation, not enforcement
                                           # There is no `mode` here: WHERE to run is the run's call
                                           # (general.eval_mode), not the task's.

language: python
modality: text
                                           # No `library`: what the task installs is in its Dockerfile,
                                           # which IS its environment. A card that also listed the deps
                                           # would be a second description, free to disagree with the first.

references:                                # provenance + the number to beat
  source: OpenEvolve
  best_known: 2.635
  note: AlphaEvolve sum-of-radii for n=26

assets:                                    # sibling files uploaded with the task repo, BEYOND
  - data/small/**                          # components. README.md is always shipped — never list it.

external_resources:                        # large data that stays OUT of the Hub
  - name: benchmark_data
    uri: https://huggingface.co/datasets/our_org/circle_packing/resolve/main/data.zip
    sha256: "<sha256>"
    unpack: true
    path: data/benchmark/
import galapagos as gx

task = gx.GalapagosTask.from_card(
    "our_org/circle_packing", download_external=True
)                                               # bundled/local -> cache -> Hub
task.root                                       # package, cache, or user-specified local_path
task.context                                    # the system prompt (from system_message)
seed = task.initial_genome()                    # the seed Genome (generation 0)
task.evaluator                                  # the deterministic verifiable scorer

Loading Hub tasks

GalapagosTask.from_card("org/name") first checks the bundled registry, then reuses the Galapagos cache or downloads the task repo from the Hub. Pass download_external=True to also fetch any declared external_resources. The returned task's root points at the materialized directory. The default cache can be changed with:

export GALAPAGOS_CACHE_DIR=/mnt/ssd/galapagos
To materialize a Hub task somewhere other than the cache, pass local_path:
task = gx.GalapagosTask.from_card(
    "our_org/circle_packing", local_path="./tasks/circle_packing"
)
To load an already-local task directory or card without using the Hub cache, pass path:
task = gx.GalapagosTask.from_card(path="./my_task/card.yaml")
task = gx.GalapagosTask.from_card(path="./my_task")
gx.load_task(...) remains a compatibility wrapper and enables download_external=True by default.

Deterministic, verifiable scoring (anti reward-hacking)

The evaluator must recompute the objective from the candidate's raw output — never trust a self-reported score. circle_packing's evaluator discards the program's own sum_radii and re-validates every constraint (count, bounds, non-overlap), returning combined_score = 0.0 on any violation. The catalog ships 1,274 bundled task cards (1,269 runnable); circle_packing and function_minimization are the canonical quickstart examples.


Scaffold Card

A scaffold is an evolutionary-search method. In Galapagos a method is not a monolithic loop — it is a composition of the six components driven by a controller. The Scaffold Card declares the controller class and records which implementation fills each of the six slots for cataloging and Hub display. The runnable behavior is supplied by the controller's Python build_components() method.

Field Type Meaning
name str (core) unique scaffold id (the slug).
display_name str human label.
organization str HF-Hub-style group; repo_id = <organization>/<name> (the slug).
type str method category (default test_time_search).
family str the method grouping the catalog and Hub facet on. Every bundled scaffold sets it; the four in use are evolutionary_method (adaevolve, evox, openevolve), search_baseline (ale_agent, beam_search, best_of_n, best_of_n_attempts, topk), autonomous_agent (algotune_agent), and single_agent_baseline (claude_code, meta_harness).
summary str one-liner.
source str the paper or repo the method comes from.
tags list free-form tags.
license str SPDX id or label.
controller str dotted path to the GalapagosScaffold subclass that orchestrates the loop. Omit it and the card is composed from its components instead — see below.
components obj the six slots: population, selection_policy, prompt_builder, proposer, evaluator, memory. A slot holds either a label ({kind: map_elites_islands} — what the leaderboard compares methods by; it names no code) or a spec (galapagos.components.IslandPopulation, or ./my_proposer.py relative to the card) — and a spec is loaded.
model obj {default, host} — the method's preferred model setup. Runtime callers usually pass the model explicitly, so most cards omit it.
requirements obj runtime prerequisites recorded by a card. claude_code declares its Claude CLI/subscription requirement; Meta-Harness's bundled default also uses Claude subscription authentication even though its card omits this optional field.

Two fields you do not write by hand:

Field Why
description Generated at load time from the scaffold README's ## Overview section — so the prose has one home, not two.
status Auto-derived: stable if a controller is wired, else spec. (experimental / adapter are also valid values.)

The schema also defines tier, assets, external_resources, defaults_config, and examples. No bundled scaffold uses them today; bundled defaults live in a sibling config.yaml loaded by GalapagosConfig.from_config(name), not through defaults_config.

The full template

The bundled OpenEvolve card (src/galapagos/scaffolds/openevolve/card.yaml), annotated:

name: openevolve                           # (core) MUST equal the directory name
display_name: OpenEvolve
organization: "OpenEvolve"
type: test_time_search
family: evolutionary_method                # the catalog / Hub facet
summary: "Island-model MAP-Elites evolutionary search with diff mutation (the open AlphaEvolve)."
source: "OpenEvolve (open implementation of AlphaEvolve, Google DeepMind)"
tags: [map-elites, islands, diff-evolution, quality-diversity]
license: Apache-2.0

controller: galapagos.scaffolds.openevolve.scaffold.OpenEvolveScaffold

components:                                # the six slots — the method IS this composition
  population: {kind: map_elites_islands}
  selection_policy: {kind: three_tier_explore_exploit}
  prompt_builder: {kind: openevolve_template}
  proposer: {kind: diff}
  evaluator: {kind: task}                  # always `task` — the Evaluator comes from the Task Card
  memory: {kind: none}

# model: {default: openai/gpt-5.5, host: openrouter}   # optional; the caller usually passes this
# requirements: {cli: "..."}                           # only if the loop needs an external binary
#
# NOT written by hand:
#   description  <- the README's `## Overview`
#   status       <- `stable` iff `controller` is set

Bundled scaffold defaults live in a sibling config.yaml. That file is loaded by GalapagosConfig.from_config(scaffold_name="openevolve"); it is not read through the card at runtime. Runtime choices such as the model are normally supplied by the caller or CLI.

seed: 42
general:
  max_iterations: 100
  max_solution_length: 10000

population:
  num_islands: 5
  archive_size: 100
  population_size: 1000
  feature_dimensions: [complexity, diversity]
  feature_bins: 10
  migration_interval: 50
  migration_rate: 0.1
  diversity_reference_size: 20

selection_policy:
  exploration_ratio: 0.2
  exploitation_ratio: 0.7
  elite_selection_ratio: 0.1
  num_inspirations: 3
  num_diverse: 2

proposer:
  top_p: 0.95
  max_tokens: 4096
  timeout: 60
import galapagos as gx

config   = gx.GalapagosConfig.from_config(scaffold_name="openevolve")
model    = gx.GalapagosModel.from_card(name="openai/gpt-5.5", host="openrouter")
scaffold = gx.GalapagosScaffold.from_card(name="openevolve", config=config, model=model)

A card with no controller IS the method

If a slot names code rather than a label, the card needs no controller class at all — six slots and a YAML file:

name: my_method
components:
  population: galapagos.components.InMemoryPopulation   # a dotted module.Class
  selection_policy: galapagos.components.ExploreExploitPolicy
  prompt_builder: galapagos.components.DefaultPromptBuilder
  proposer: ./my_proposer.py                            # …or a file, relative to THIS card
  evaluator: {kind: task}                               # always the task's — never a scaffold spec
  memory: {kind: none}
# no `controller:` — the components are the method

A .py path resolves against the card's own directory, so a submitted repo works wherever it is unpacked, and galapagos submit uploads the file with it. The submission gate loads every spec, so a typo fails CI instead of failing quietly at run time.

The fourteen bundled scaffolds are the other shape: their slots are {kind: …} labels, and their behaviour comes from the controller class's build_components(). A card whose slots are only labels is a spec — it documents a method before the code exists, and says so rather than pretending to run.

Build your own — no card file needed

The same six slots can be passed directly to from_card, each as a component instance, a "module.Class" path, or a .py file:

scaffold = gx.GalapagosScaffold.from_card(
    population="galapagos.components.IslandPopulation",
    selection_policy="galapagos.components.UCBBanditPolicy",
    prompt_builder="galapagos.components.DefaultPromptBuilder",
    proposer="./my_proposer.py",                 # a .py file with one Proposer subclass
    memory="galapagos.components.ScratchpadMemory",
    model=model,
)
Omitting a slot leaves that component unused (e.g. no memory= ⇒ a Memory-free loop). The catalog ships fourteen bundled scaffolds — adaevolve, ale_agent, algotune_agent, beam_search, best_of_n, best_of_n_attempts, claude_code, codex, evox, gepa, meta_harness, openevolve, random, and topk — all runnable.


Model Card

A Model Card pins a model so a run is reproducible from disk: a display name, the real model path, and the host that serves it.

Field Type Meaning
name str (core) the model's display name / id.
model_path str the real provider model name (e.g. openai/gpt-5.5).
host str where it is served — see the host list below. Default openrouter.
temperature float sampling temperature.
max_tokens int generation cap.

The host selects an OpenAI-compatible endpoint. The protocol's host vocabulary is:

openrouter (default) · openai · anthropic · gemini · azure · vllm · litellm — or any explicit OpenAI-compatible base URL (a string containing ://).

name: gpt-5.5
model_path: openai/gpt-5.5
host: openrouter
temperature: 0.7
max_tokens: 16384
model = gx.GalapagosModel.from_card(name="openai/gpt-5.5", host="openrouter")
model = gx.GalapagosModel.from_card(path="my_model_card.yaml")     # or pin it from a card file

See Models for the host-to-base_url resolution table, the three mandated load forms (HF / hosting platform / local vLLM), and which hosts are wired into the shipped loader.


Verification Card

A discovery is more than a number — it is a claim with provenance. A legacy Verification Card records the task, the scaffold (or agent) that produced it, the best solution, and the full discovery trajectory. It is a portable, reviewable artifact, but the endpoint does not implement an automatic verification workflow.

A verification card is a lightweight claim — it always lands unverified and carries no leaderboard linkage. The live leaderboard is fed by the heavier discovery repo flow instead: a discovery card plus its bundle is uploaded, lands pending, and a reviewer accepting it promotes it into a leaderboard row. See Submit to the Hub for both paths.

Field Type Meaning
task str (core) the task the discovery was made on.
scaffold str the scaffold that produced it (or…).
agent str …the agent that produced it.
submitter str who is submitting.
claimed_score float the claimed headline score.
best_solution str the discovered solution — inline, or a path.
trajectory str path / URI to the full discovery trajectory.
status str unverified | under_review | verified | rejected.
notes str reviewer / submitter notes.
task: circle_packing
scaffold: adaevolve
submitter: passing2961
claimed_score: 0.9997
best_solution: solutions/circle_packing_2.6342.py
trajectory: runs/2026-06-08_adaevolve_circle_packing/
status: unverified
notes: "26 circles, sum_radii = 2.6342 (99.97% of the AlphaEvolve best)."

A verification card is not submitted through the CLI — it is POSTed to a Hub instance as a JSON object at POST /api/verifications (see the full flow in Submit to the Hub):

curl -X POST https://open-galapagos.com/api/verifications \
     -H "authorization: Bearer $TOKEN" -H "content-type: application/json" \
     -d "$(python -c 'import json,yaml; print(json.dumps(yaml.safe_load(open("circle_packing_discovery.yaml"))))')"

Legacy status semantics

On submission the Hub stores this lightweight card with status: unverified, regardless of the submitted value. This endpoint has no review transition or leaderboard linkage. The separate discovery-repo review endpoint records pending / accepted / rejected, but does not automatically replay or re-score a claim.


Loading and submitting

Every card type follows the same two-verb protocol:

import galapagos as gx

task     = gx.load_task("our_org/circle_packing")
scaffold = gx.GalapagosScaffold.from_card(name="openevolve")
model    = gx.GalapagosModel.from_card(name="openai/gpt-5.5", host="openrouter")
Functional aliases: gx.load_task, gx.load_scaffold, gx.load_model, gx.load_config. gx.load_task("org/name") materializes a Hub task repo into the Galapagos cache (or local_path=...) before loading it and downloads any declared external_resources. Scaffold loading currently resolves bundled cards or explicit local path= values; Hub scaffold download is not wired into load_scaffold() yet. Pass path=.../card.yaml or path=.../repo_dir to load an already-local card without using the Hub cache.

galapagos submit --repo-type task --card-path my_task/card.yaml
galapagos submit --repo-type scaffold --card-path my_scaffold/card.yaml
The card is validated by the same galapagos.cards.schema that validates loaded cards, then published to the Hub as a repo bundle via POST /api/scaffolds/upload / POST /api/tasks/upload — so the library and the Hub never disagree about a card's shape. A discovery (verification card) is POSTed to POST /api/verifications; see Submit to the Hub.

The library ⊆ Hub invariant

The card is the same artifact locally and on the Hub. The cards bundled in the galapagos wheel are a subset of the Hub catalog — never a fork. One schema validates both directions, so a card that loads in the library publishes to the Hub, and vice versa.


See also

  • Core components — the six slots a Scaffold Card declares.
  • Genome — the unit a Task Card's evaluator scores.
  • Models — the hosts a Model Card's host selects.
  • The Hub — where cards are published and the leaderboard lives.