tao-artifacts
Four typed artifacts flow through every TAO job. Their schemas live here and
nowhere else — producers (model/data skills) and consumers (platform skills)
both validate against this skill's references/.
| Artifact |
Schema |
Produced by → consumed by |
| spec-bundle |
references/spec_bundle.schema.json |
model/data skill → platform skill (at the submit seam) |
| job-record |
references/job_record.schema.json |
scripts/tao_job_record.py (the ONLY writer) → any re-attaching agent/poller |
| results_dir layout |
references/results_dir.contract.md |
platform skill at submit → whoever collects outputs |
| best_rec |
references/best_rec.schema.json |
tao-run-automl adapter → DEFT warm-start |
Quick Start — validate an artifact
python - <<'PY'
import json, yaml, jsonschema, pathlib
ref = pathlib.Path("${TAO_SKILL_BANK_PATH:?}/skills/core/tao-artifacts/references")
schema = json.loads((ref / "spec_bundle.schema.json").read_text())
bundle = yaml.safe_load(open("/path/to/bundle.yaml")) # or a dict built in-context
jsonschema.validate(bundle, schema) # raises on violation
print("bundle OK")
PY
Validate the bundle before the verify-before-launch gate; validate a
job-record only when debugging (the writer script already enforces the schema).
The two rules the schemas enforce structurally
- Nested, not dotted. A
spec is a nested dict mirroring the container's
config shape — {"train": {"num_epochs": 12}}. Any key containing . at
any depth is rejected ({"train.num_epochs": 12} is the #1 authoring
mistake). Note the distinction: declared_inputs[].spec_key and
gpu_spec_key are dotted/indexed pointers into the spec
(dataset.train_data_sources[0].image_dir) — dots are correct there.
- Mode discrimination.
mode: config requires spec + config_format
and a command containing {config_path}, and forbids args.
mode: args requires args and forbids spec. There is no other mode.
Optional action lifecycle
Use execution when an action needs more than its primary command. This is the
shared model-to-platform seam; do not add a model-specific Docker, Kubernetes,
or SLURM renderer merely to carry runtime environment, attestations,
post-processing, or helper dependencies.
- The producing model/data skill owns
environment, ordered pre_commands,
ordered post_commands, distributed-launch intent, and completion evidence.
- The platform owns container mounts, scheduler/container syntax, task/rank
binding, timeouts, log paths, and preservation of the real child exit code.
environment is non-secret. Credential values continue to use the selected
platform's secret/sidecar contract and never enter a spec-bundle.
- Commands, environment values, and string values in
spec may use
{config_path}, {job_id}, and {results_dir}. The platform binds them
only after the job record has been opened; the job record's results_dir is
authoritative over any pre-review display path. Persist hashes of both the
producer bundle and the bound runtime config.
supporting_files names checked-in orchestration helpers relative to the
producing skill root. The platform stages the closed set, verifies every
declared SHA256, and rejects traversal, undeclared siblings, or overwrite of
a different bundle. Supporting files orchestrate an action; they must never
shadow or patch code inside the selected image.
- A
torchrun declaration expresses process topology, not SLURM/Kubernetes
syntax. Each platform maps it to its native distributed launcher.
Fixed status vocabulary
Every job state anywhere in the pipeline is exactly one of:
PENDING · RUNNING · COMPLETE · ERROR · CANCELED · UNKNOWN
Platform-native sub-states (ImagePullBackOff, PENDING-because-resources,
Insufficient-GPU, slurm COMPLETING…) are never new states — they ride in
the transition's message field. Terminal = COMPLETE | ERROR | CANCELED.
This is what lets the in-turn poll loop and the detached poller share one code
path across docker/slurm/kubernetes/brev.
Ordering invariants (enforced at the seam, stated here)
- The verify-before-launch gate runs on the spec-bundle, before any job id
exists.
tao_job_record.py open writes PENDING + the resolved results_dir
first and returns the id — the only handle a launch can use. A submit
that skipped the gate has no id, so it cannot launch.
transitions is append-only; .tao/ lives outside every synced results
tree.
1---2name: tao-artifacts3description: The contract home for TAO's SDK-free execution pipeline — authoritative JSON Schemas for the four typed artifacts (spec-bundle, job-record, results_dir layout, best_rec) plus the fixed job-status vocabulary and the nested-not-dotted spec rule. Use when authoring or validating a spec-bundle before submit, writing or reading a .tao/jobs job-record, resolving where results land, or consuming AutoML's best_rec.json. Trigger phrases include "validate the spec bundle", "job record schema", "status vocabulary", "results_dir layout", "best_rec schema".4license: Apache-2.05---67# tao-artifacts89Four typed artifacts flow through every TAO job. Their schemas live **here and10nowhere else** — producers (model/data skills) and consumers (platform skills)11both validate against this skill's `references/`.1213| Artifact | Schema | Produced by → consumed by |14|---|---|---|15| **spec-bundle** | `references/spec_bundle.schema.json` | model/data skill → platform skill (at the submit seam) |16| **job-record** | `references/job_record.schema.json` | `scripts/tao_job_record.py` (the ONLY writer) → any re-attaching agent/poller |17| **results_dir layout** | `references/results_dir.contract.md` | platform skill at submit → whoever collects outputs |18| **best_rec** | `references/best_rec.schema.json` | tao-run-automl adapter → DEFT warm-start |1920## Quick Start — validate an artifact2122```bash23python - <<'PY'24import json, yaml, jsonschema, pathlib25ref = pathlib.Path("${TAO_SKILL_BANK_PATH:?}/skills/core/tao-artifacts/references")26schema = json.loads((ref / "spec_bundle.schema.json").read_text())27bundle = yaml.safe_load(open("/path/to/bundle.yaml")) # or a dict built in-context28jsonschema.validate(bundle, schema) # raises on violation29print("bundle OK")30PY31```3233Validate the bundle **before** the verify-before-launch gate; validate a34job-record only when debugging (the writer script already enforces the schema).3536## The two rules the schemas enforce structurally37381. **Nested, not dotted.** A `spec` is a nested dict mirroring the container's39 config shape — `{"train": {"num_epochs": 12}}`. Any key containing `.` at40 any depth is rejected (`{"train.num_epochs": 12}` is the #1 authoring41 mistake). Note the distinction: `declared_inputs[].spec_key` and42 `gpu_spec_key` are dotted/indexed **pointers** into the spec43 (`dataset.train_data_sources[0].image_dir`) — dots are correct there.442. **Mode discrimination.** `mode: config` requires `spec` + `config_format`45 and a `command` containing `{config_path}`, and forbids `args`.46 `mode: args` requires `args` and forbids `spec`. There is no other mode.4748## Optional action lifecycle4950Use `execution` when an action needs more than its primary command. This is the51shared model-to-platform seam; do not add a model-specific Docker, Kubernetes,52or SLURM renderer merely to carry runtime environment, attestations,53post-processing, or helper dependencies.5455- The producing model/data skill owns `environment`, ordered `pre_commands`,56 ordered `post_commands`, distributed-launch intent, and completion evidence.57- The platform owns container mounts, scheduler/container syntax, task/rank58 binding, timeouts, log paths, and preservation of the real child exit code.59- `environment` is non-secret. Credential values continue to use the selected60 platform's secret/sidecar contract and never enter a spec-bundle.61- Commands, environment values, and string values in `spec` may use62 `{config_path}`, `{job_id}`, and `{results_dir}`. The platform binds them63 only after the job record has been opened; the job record's `results_dir` is64 authoritative over any pre-review display path. Persist hashes of both the65 producer bundle and the bound runtime config.66- `supporting_files` names checked-in orchestration helpers relative to the67 producing skill root. The platform stages the closed set, verifies every68 declared SHA256, and rejects traversal, undeclared siblings, or overwrite of69 a different bundle. Supporting files orchestrate an action; they must never70 shadow or patch code inside the selected image.71- A `torchrun` declaration expresses process topology, not SLURM/Kubernetes72 syntax. Each platform maps it to its native distributed launcher.7374## Fixed status vocabulary7576Every job state anywhere in the pipeline is exactly one of:7778`PENDING · RUNNING · COMPLETE · ERROR · CANCELED · UNKNOWN`7980Platform-native sub-states (`ImagePullBackOff`, `PENDING`-because-resources,81`Insufficient-GPU`, slurm `COMPLETING`…) are never new states — they ride in82the transition's `message` field. Terminal = `COMPLETE | ERROR | CANCELED`.83This is what lets the in-turn poll loop and the detached poller share one code84path across docker/slurm/kubernetes/brev.8586## Ordering invariants (enforced at the seam, stated here)8788- The verify-before-launch gate runs on the **spec-bundle**, before any job id89 exists.90- `tao_job_record.py open` writes `PENDING` + the resolved `results_dir`91 **first** and returns the id — the only handle a launch can use. A submit92 that skipped the gate has no id, so it cannot launch.93- `transitions` is append-only; `.tao/` lives outside every synced results94 tree.