Tao Run Deft Object Detection
NVIDIAOfficialCreated by NVIDIA Corporation
- Skill
- Workflow
- Software development
- Apache-2.0
- Medium risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill executes a multi-stage, iterative workflow designed to improve object detection model performance through smart data augmentation. It manages a complete loop including zero-shot baseline inference, KPI analysis, per-class gap analysis, SigLIP-based embedding, unique-neighbor mining from a source pool, dataset staging, and retraining. The skill also provides a separate invocation for preparing the source pool, which involves pseudo-labeling raw images with Co-DETR, remapping classes, and converting data formats. It operates as a state-managed loop, ensuring consistency across iterations and providing automated reporting.
What it does
- Performs zero-shot baseline inference and KPI analysis on Grounding DINO models.
- Conducts per-class gap analysis to identify model weaknesses.
- Mines unique, high-value training samples from a prepared source pool using SigLIP embeddings.
- Stages mined data into the ODVG format for iterative model retraining.
- Prepares source pools by pseudo-labeling raw images and converting them to the required ODVG format.
- Maintains a persistent state machine to track progress and ensure auditability across training iterations.
When to use it
- When you need to improve the mAP of a Grounding DINO model through iterative data mining.
- When you have a large pool of unlabeled images that need to be pseudo-labeled and mined for training.
- When you want to automate the gap analysis, mining, and retraining cycle.
- When you need to prepare a source pool of images for use in a DEFT-based detection workflow.
Advantages
- Automates complex, multi-step data augmentation and training loops.
- Reduces manual effort in identifying and labeling high-value training data.
- Ensures consistent data processing and state management across iterations.
- Provides clear visibility into model performance trends through automated KPI reporting.
Usage examples
- “run the DEFT OD loop”
- The agent initiates the iterative mining and retraining process, starting with baseline inference and gap analysis.
- “prep the source pool”
- The agent runs the Co-DETR pseudo-labeling and embedding pipeline to prepare your raw images for future mining.
- “mine and retrain my detection model”
- The agent performs gap analysis, mines new samples from the source pool, stages the data, and triggers a retraining iteration.
- “improve OD mAP with gap analysis and mining”
- The agent executes the full DEFT loop to identify performance gaps and iteratively grow the training dataset to boost mAP.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/nvidia/tao-run-deft-object-detection/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip tao-run-deft-object-detection.zip -d .agents/skills
Documentation by NVIDIA Corporation
Skill: tao-run-deft-object-detection
Standalone install? If this session was not initialized by the TAO skill bank plugin, run the
tao-setupskill first (host preflight, credentials, cross-skill discovery).
Execution Contract
Treat this as a disk-backed state machine, not as a prose recipe.
Preserve every explicit user value.
epoch 1meanstrain.num_epochs=1; a spec value or documented default applies only when the user did not supply that parameter. Show the source of every run parameter (user,spec, ordefault) in the Pre-Flight Summary.After the user approves the Summary, initialize
deft_state.jsononce withscripts/init_deft_state.py. Never hand-author or reinitialize it on resume.Run every bundled or inline host-Python command through
scripts/deft_python.sh. On startup, after context compaction, before every stage, and before any completion claim, run:<skill_root>/scripts/deft_python.sh \ <skill_root>/scripts/audit_deft_run.py --results-dir "${RESULTS_DIR}"If it prints
DEFT_RUN_STATUS=INVALID, stop and repair the listed disk inconsistency; do not launch another stage. Read the path printed asread_before_actionbefore continuing.Invoke the mapped underlying skill after reading the DEFT overlay. Do not replace a missing or unread stage reference, or a failed skill call, with guessed shell commands, inline Python, a different output tree, or fabricated data.
Commit every stage with
scripts/commit_stage.py; it verifies artifacts, updatesdeft_state.json, appends exactly one orderedloop_log.jsonlevent, and rolls back if its audit fails.Claim the loop complete only when this exits zero:
<skill_root>/scripts/deft_python.sh \ <skill_root>/scripts/audit_deft_run.py \ --results-dir "${RESULTS_DIR}" --require-complete
Context Discipline
Load references just in time. Run the audit, read only its
read_before_actionfile and the current stage's named section, then act. Never preload all references.Redirect verbose train, inference, and Docker output to files. Inspect at most the final 40 lines or a one-line artifact check; never print a full spec, state file, or loop log into the conversation.
A Skill-tool call loads stage instructions; it does not start a background orchestrator. Continue the documented stage in the parent immediately after it returns.
When to Use This Skill
Use this skill when the user wants an agent to run the full smart-data-augmentation loop for a TAO Grounding DINO detection model: zero-shot baseline, gap analysis, mining, dataset growth, and retraining across N iterations.
"Run the DEFT OD loop"
"Run smart data augmentation for grounding dino"
"Mine more training data for my detection model and retrain"
"Improve detection mAP with gap analysis and unique-neighbor mining"
Also use it to prepare the source pool, which is a separate run that completes before the
loop launches (see ## Two Invocations: Prep, Then Loop):
"Prep the source pool"
"Pseudo-label my unlabeled images for mining"
"Build the mining pool from these raw images"
Do not use this skill for a single standalone TAO training run, one-off inference, or gap analysis alone. Invoke the relevant leaf skill directly instead.
Scope: Grounding DINO + ODVG
This loop targets Grounding DINO with ODVG training annotations (tmm_odvg.jsonl + labelmap.json), matching the reference pipeline. dataset.train_data_sources is a list; each iteration appends one new ODVG source rather than rewriting a combined CSV. DINO and RT-DETR (COCO) are not supported by this workflow — use the leaf skills directly for those.
The loop does not train at baseline. It evaluates the supplied zero-shot / pretrained checkpoint as iteration 0 and only trains from iteration 1 onward, once mining has produced data to add.
Two Invocations: Prep, Then Loop
The source pool is prepared by its own run, before the loop launches. They are separate because a pool is prepped once and then serves many loop runs — coupling them would re-label and re-embed the same images on every launch.
| Invocation | Does | Produces |
|---|---|---|
| "Prep the source pool" | Co-DETR pseudo-labels raw pool images, folds to the target classes, converts KITTI→COCO→ODVG, verifies, embeds | coco.json, odvg/, source_embeddings.parquet, pool_report.json |
| "Run the DEFT loop" | baseline → iterations | checkpoints, KPI, the mAP trend |
Follow references/prep-source-pool.md for the first. The loop then takes those four paths as
inputs; Pre-Flight validates them and init_deft_state.py pins them, so a run cannot reach
mine with no corpus to search.
Launch Intake
Do not ask which platform to run on. This workflow supports one: every stage launches
with docker run, and its compatibility line requires docker plus the
nvidia-container-toolkit. The bank's rule in AGENTS.md is to treat a single supported
platform as the selection and only ask when an application supports several — asking here
would offer a choice that nothing downstream can act on.
Read tao-run-on-docker's ## Credentials section.
Never ask for or read credential values. Check only whether the required environment variable is set; if unset, tell the user which variable to export.
Agent Behavior
There is exactly one user gate: pre-flight confirmation. Print the Pre-Flight Summary (see
references/preflight.md), then STOP and wait for explicit approval ("go", "yes", "looks good"). Do not launch any side-effecting step before that approval.After the gate, the skill is fully autonomous. Run the entire loop without asking for confirmation. Only stop if a step fails with an unrecoverable error or a hard-stop gate fires. Print a one-line status update at each stage milestone.
Auto-mode required. The post-gate loop fires constant side-effecting calls; without auto-accept mode it stalls on the first prompt. Remind the user at the Pre-Flight Summary to enable auto-mode (shift+tab) before approving.
Non-zero command rule. Never repeat an unchanged failed command. Read the final error block, map it to the loaded stage reference, make one evidence-based correction, and rerun its documented verification. If the reference does not cover the failure, commit
status=errorand halt.Revised plan. If any run parameter changes after the Summary was shown, re-run Pre-Flight and show an updated Summary before proceeding.
Workflow
Full detail in references/pipeline-and-state.md.
Pre-Flight. Run every check in
references/preflight.md. Resolve workspace, specs, annotations, the zero-shot checkpoint, the source-pool embedding parquet, and container images. Hard stop only on missing input you cannot resolve yourself.Prep (once, before baseline). If the source pool is not already labeled and embedded, pseudo-label it with Co-DETR, fold the predictions onto the user's target classes, convert KITTI→COCO→ODVG, and embed the pool. Skip any step whose output already exists — no script does this for you, so check before each step. See
references/prep-source-pool.md.Baseline (iter_0) — no training. Run
inferencewith the supplied zero-shot / pretrained checkpoint, thenkpi_analyze. Seedtrain_grounding_dino.yamlfrom the user's template for later iterations to extend.Iterate. For each iteration 1..
max_iterations, run the seven stages in order:gap_analysis→embed→mine→stage→train→inference→kpi_analyze. Each iteration'sgap_analysisconsumes the previous phase's inference labels. Between stages run the audit and follow its one-line disk-backed next action.Stop when
max_iterationsis reached or a hard-stop gate fires. mAP is reported, not gated — the loop does not early-exit on a metric target.The report renders itself.
results/DEFT_Loop_Report.mdis written byinit_deft_state.pyand re-rendered by every acceptedcommit_stage.pycall. Do not spawn a subagent for it and do not compose it by hand; to refresh it out of band, runscripts/render_report.py --results-dir "${RESULTS_DIR}".
All stages run inline in the parent context. Prefer invoking the underlying tao-skill-bank:* skills via the Skill tool, layering loop conventions on top via the matching references/*.md overlay.
Using Bundled Scripts
Run bundled scripts through <skill_root>/scripts/deft_python.sh. Resolve every path argument to an absolute host path first. Use commit_stage.py for all state and log writes. See references/scripts-and-agents.md.
Stage Reference Modules
Each stage maps to one underlying skill or to bundled glue. Read only the current stage's overlay, then invoke.
The overlays are written against two loop variables that Pre-Flight sets and every stage uses: N, the current iteration number, and PHASE, which is baseline or iter${N}. They are stated here because a stage overlay is read on its own — see references/preflight.md for the full set.
If an overlay is missing, stop and ask the user to reinstall the plugin — the loop cannot run a stage whose settings it does not have. If a mapped skill is unavailable, do not stop and do not improvise: fall back to the overlay's documented docker run as described in references/scripts-and-agents.md, and commit that stage with --execution-path direct-container instead of the documented skill. The overlay carries everything the invocation needs, so the fallback produces the same artifacts.
On a runtime without a Skill tool (Codex, for example), the mapped skills are still on disk under the plugin root: read skills/<layer>/<name>/SKILL.md there for the invocation contract, then run it through the direct-container fallback above, committing the stage with --execution-path direct-container. On such a runtime that fallback is the expected route for every stage, not an exception.
| Stage | Overlay | Underlying skill |
|---|---|---|
prep (once) | references/prep-source-pool.md | tao-skill-bank:tao-train-codetr + tao-generate-image-embeddings (+ bundled glue) |
gap_analysis | references/tao-analyze-gaps-od-map.md | tao-skill-bank:tao-analyze-gaps-od-map |
embed | references/tao-generate-image-embeddings.md | tao-skill-bank:tao-generate-image-embeddings |
mine | references/tao-mine-od-images.md | tao-skill-bank:tao-mine-od-images |
stage | references/stage-mined-data.md | (bundled glue — no leaf skill) |
train, inference | references/grounding-dino.md | tao-skill-bank:tao-train-grounding-dino |
kpi_analyze | references/tao-analyze-detection-kpi.md | tao-skill-bank:tao-analyze-detection-kpi |
Path rule (invariant). Record absolute host paths under ${RESULTS_DIR}. Mount "$WORKSPACE:$WORKSPACE" with identical host and container paths. TAO's update_results_dir appends the task name to results_dir, so passing results_dir=X to train writes X/train/ and to inference writes X/inference/. Never append the subdirectory yourself.
Data, Pre-Flight, Pipeline, and State References
| Topic | Reference |
|---|---|
| Data contract, ODVG layout, source pool, output tree | references/data-layout.md |
| One-time source-pool prep (pseudo-label, remap, convert, embed) | references/prep-source-pool.md |
| Pre-Flight checks, defaults, Summary template | references/preflight.md |
| Pipeline stages, state schema, loop-end sequence | references/pipeline-and-state.md |
| Bundled scripts, glue, the report renderer, stage table | references/scripts-and-agents.md |
max_iterations defaults to 1 — one mine, train and score pass, the smallest run that yields a comparison against the baseline. Confirm it with the user when they have not said how many iterations they want; an unattended run takes the default rather than stopping to ask.
Gating
Run the full Pre-Flight, print the Summary, then STOP at the one user gate. After approval, run the baseline and the seven-stage iteration pipeline.
Hard-stop and never auto-retry on: any stage status=error; a missing or zero-row source-pool embedding parquet; a zero-row mining result when weak images were present; a missing ODVG annotation source; an image/annotation mismatch after staging; or a train exit that emits no new iteration checkpoint. The loop stops when max_iterations is reached or an unrecoverable gate fires. Each terminal path commits loop_stop through commit_stage.py, then follows the loop-end sequence in references/pipeline-and-state.md.
Files
- ATTRIBUTION.mdAdded by AutoGineer957 B
- BENCHMARK.md8.0 KiB
- LICENSELicence file added by AutoGineer11.2 KiB
- SKILL.md13.7 KiB
- agents/reporter.md1.4 KiB
- assets/gap_analysis_object_detection.yaml1.9 KiB
- assets/image_embeddings.yaml790 B
- assets/overlays/coco_to_odvg.yaml334 B
- assets/overlays/codetr_inference.yaml5.0 KiB
- assets/overlays/grounding_dino_inference.yaml1.3 KiB
- assets/overlays/kitti_to_coco.yaml500 B
- assets/overlays/kpi_analyze.yaml2.0 KiB
- assets/tmm_unique_neighbor_matching.yaml1.7 KiB
- assets/train_grounding_dino.yaml2.6 KiB
- config/skillspector-baseline.yaml4.6 KiB
- evals/evals.json5.6 KiB
- references/data-layout.md7.7 KiB
- references/example_classes_its.yaml2.0 KiB
- references/grounding-dino.md16.7 KiB
- references/pipeline-and-state.md15.4 KiB
- references/preflight.md29.4 KiB
- references/prep-source-pool.md27.3 KiB
- references/scripts-and-agents.md15.2 KiB
- references/stage-mined-data.md6.3 KiB
- references/tao-analyze-detection-kpi.md11.4 KiB
- references/tao-analyze-gaps-od-map.md8.6 KiB
- references/tao-generate-image-embeddings.md4.0 KiB
- references/tao-mine-od-images.md8.2 KiB
- scripts/apply_spec_overrides.pyScript16.0 KiB
- scripts/audit_deft_run.pyScript39.2 KiB
- scripts/await_stage.pyScript7.5 KiB
- scripts/commit_stage.pyScript52.8 KiB
- scripts/deft_python.shScript2.5 KiB
- scripts/deft_stages.pyScript8.4 KiB
- scripts/emit_default_spec.pyScript9.1 KiB
- scripts/fetch_codetr_checkpoint.pyScript10.4 KiB
- scripts/fetch_gdino_checkpoint.pyScript7.4 KiB
- scripts/init_deft_state.pyScript44.8 KiB
- scripts/prepare_budget_for_mining.pyScript7.1 KiB
- scripts/prepare_class_mappings_for_mining_data_prep.pyScript8.9 KiB
- scripts/prepare_exclude_for_mining.pyScript4.7 KiB
- scripts/prepare_input_for_image_embeddings.pyScript4.3 KiB
- scripts/prepare_mapping_for_kpi_analyze.pyScript5.1 KiB
- scripts/prepare_spec_for_train.pyScript11.8 KiB
- scripts/prepare_thresholds_for_gap_analysis.pyScript5.9 KiB
- scripts/prepare_val_split_for_train.pyScript5.5 KiB
- scripts/render_report.pyScript33.7 KiB
- scripts/stage_mined_odvg.pyScript10.3 KiB
- scripts/summarize_kpi.pyScript9.1 KiB
- scripts/tests/test_state_machine.shScript178.2 KiB
- scripts/validate_odvg_images.pyScript4.6 KiB
- scripts/validate_pool_coco.pyScript13.0 KiB
- scripts/verify_class_contract.pyScript8.0 KiB
- scripts/verify_embed_encoder.pyScript3.8 KiB
- scripts/verify_pseudo_labels.pyScript7.5 KiB
- skill-card.md5.5 KiB
- skill.oms.sig16.6 KiB
scripts/audit_deft_run.py
#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Read-only audit of one DEFT OD run: state vs log vs what is actually on disk.
Why this exists: the loop's memory is disk, not context. An agent resuming after
compaction — or about to tell the user the loop finished — has no trustworthy
in-context record of what ran. This script rebuilds that record from
``deft_state.json`` and ``loop_log.jsonl``, re-checks every artifact path the run
recorded, and prints the single next action that is safe to take.
It never writes anything. Repairing an inconsistency is the agent's job, and the
only supported repair is re-running the stage through ``commit_stage.py`` — never
hand-editing JSON.
Three questions, in the order the loop asks them:
DEFT_RUN_STATUS Is the run internally consistent? Anything else is unsafe.
next_action Which stage may run now (or ``loop_stop`` / ``complete``).
read_before_action Which ``references/*.md`` overlay to load before doing it.
``--require-complete`` exists because "the report file is there" is not proof the
loop ran. Completion means a committed ``loop_stop``, a ``baseline`` that finished
``kpi_analyze`` — without it there is no baseline mAP and therefore no trend, the
loop's whole deliverable — plus either every configured iteration finishing all of
``ITER_ORDER``, or a ``gap_analysis`` that recorded zero weak images and stopped
there (the documented early stop). The presence of a checkpoint, a KPI csv, or
``DEFT_Loop_Report.md`` counts for nothing here.
``--require-terminal`` means one thing: ``loop_stop`` is committed. A run that
failed but never finalized is *not* terminal — ``next_action`` still says
``loop_stop``, and a report rendered before that has no authoritative outcome.
CLI:
python3 scripts/audit_deft_run.py --results-dir /abs/results/run_X
python3 scripts/audit_deft_run.py --results-dir /abs/results/run_X --json
python3 scripts/audit_deft_run.py --results-dir /abs/results/run_X --require-terminal
python3 scripts/audit_deft_run.py --results-dir /abs/results/run_X --require-complete
Exit codes:
0 VALID and every --require-* condition met
1 INVALID, a --require-* condition unmet, or invalid arguments (nothing audited)
2 deft_state.json is missing or unparseable (a special case of INVALID)
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
from deft_stages import (
ALL_STAGES,
BASELINE_ORDER,
ITER_ORDER,
PREP_ORDER,
SCHEMA_VERSION,
STAGE_ARTIFACTS,
TERMINAL_STAGE,
check_artifact,
iter_number,
log_path,
next_stage,
phase_order,
previous_phase,
read_state,
state_path,
validate_transition,
)
# next_action -> the overlay the agent must read before running it. Stages absent
# from this table (loop_stop, complete, and any INVALID repair description) have
# no overlay and report "none".
STAGE_OVERLAYS = {
"prep": "references/prep-source-pool.md",
"gap_analysis": "references/tao-analyze-gaps-od-map.md",
"embed": "references/tao-generate-image-embeddings.md",
"mine": "references/tao-mine-od-images.md",
"stage": "references/stage-mined-data.md",
"train": "references/grounding-dino.md",
"inference": "references/grounding-dino.md",
"kpi_analyze": "references/tao-analyze-detection-kpi.md",
}
VALID_LOG_STATUSES = {"ok", "error"}
# "stopped": loop_stop was committed on a run this audit does not call complete.
# commit_stage.py writes "complete" only when this audit agrees it is one.
VALID_RUN_STATUSES = {"running", "in_progress", "stopped", "complete", "failed"}
# Optional paths the overlays record alongside a stage's required artifacts.
# commit_stage.py declares one flag per entry and checks it at write time; this
# audit re-checks them on every run, because a recorded path that is no longer on
# disk is exactly as misleading here as a required one. Field -> (flag, kind).
EXTRA_ARTIFACT_FIELDS: dict[str, tuple[str, str]] = {
# kpi_analyze stdout tee; the only place mAP appears
"kpi_log": ("--kpi-log", "file"),
# prep: validate_pool_coco.py's verdict on the converted pool
"pool_report": ("--pool-report", "file"),
# stage: merged training manifest
"combined_manifest": ("--combined-manifest", "file"),
# stage: post-merge consistency report
"merge_validation_report": ("--merge-validation-report", "file"),
}
# init_deft_state.py pins the source pool in config; `prep` is the stage that
# produces it when --allow-missing-pool was passed. Same two artifacts as
# STAGE_ARTIFACTS["prep"], under the config names.
POOL_CONFIG_FIELDS = {
"source_pool_annotations": "dir",
"source_pool_embeddings": "file",
}
REPAIR_ACTION = "repair the listed disk inconsistencies before running another stage"
FAILED_ACTION = "report the run as FAILED; do not claim completion"
INCOMPLETE_ACTION = (
"report the run as INCOMPLETE; loop_stop was committed before the configured "
"iterations finished and before any documented early stop"
)
def _phase_sort_key(phase: str) -> tuple[int, int]:
"""Order phases the way the loop runs them: prep, baseline, iter1, iter2..."""
if phase == "prep":
return (0, 0)
if phase == "baseline":
return (1, 0)
number = iter_number(phase)
return (2, number) if number is not None else (3, 0)
def _is_int(value: Any) -> bool:
return isinstance(value, int) and not isinstance(value, bool)
def _label_problem(phase: str) -> str | None:
"""Reject iteration labels that are not exactly ``iter<n>``, n >= 1.
``deft_stages`` matches ``iter(\\d+)``, so ``iter01`` is a phase distinct from
``iter1`` that reports the same iteration number. Left alone it is an alias:
an extra full iteration that slips past the max_iterations bound, is counted
in iterations_completed, and desynchronizes ``previous_phase()`` (iter2 then
reads iter1's labels even though iter01 trained last). One spelling per
iteration is the only way those three stay consistent.
``iter0`` is rejected for a different reason: iterations are numbered from 1,
and ``previous_phase("iter0")`` answers ``"iter-1"`` — a label nothing can
resolve.
"""
number = iter_number(phase)
if number is None:
return None
if number < 1:
return (
f"iteration label {phase!r} is out of range; iterations are numbered from 1 "
"and the zeroth phase is called 'baseline'"
)
if phase != f"iter{number}":
return (
f"iteration label {phase!r} is not canonical; the only accepted spelling for "
f"iteration {number} is 'iter{number}'"
)
return None
def _load_log(results_dir: Path, errors: list[str]) -> list[dict[str, Any]]:
"""Parse loop_log.jsonl line by line.
deft_stages.read_log() raises on the first bad line, which would abandon the
whole audit. An auditor has to survive a corrupt log and report where it is
corrupt, so it parses per line and keeps going.
"""
path = log_path(results_dir)
if not path.is_file():
return []
entries: list[dict[str, Any]] = []
with path.open("r", encoding="utf-8") as handle:
for lineno, raw in enumerate(handle, 1):
if not raw.strip():
continue
try:
event = json.loads(raw)
except json.JSONDecodeError as exc:
errors.append(f"loop_log.jsonl:{lineno}: invalid JSON: {exc}")
continue
if not isinstance(event, dict):
errors.append(f"loop_log.jsonl:{lineno}: event must be a JSON object")
continue
entries.append(event)
return entries
def _check_event_schema(
entries: list[dict[str, Any]],
errors: list[str],
warnings: list[str],
) -> None:
"""seq must be strictly increasing from 1 with no gaps or duplicates."""
for index, event in enumerate(entries, 1):
seq = event.get("seq")
if not _is_int(seq):
errors.append(f"loop_log entry {index}: seq must be an integer, got {seq!r}")
elif seq != index:
errors.append(
f"loop_log entry {index}: seq={seq} breaks the strictly-increasing-from-1 "
f"sequence (expected {index}); the log has a gap, a duplicate, or a reorder"
)
label = f"seq={seq}" if _is_int(seq) else f"entry {index}"
if event.get("status") not in VALID_LOG_STATUSES:
errors.append(
f"loop_log {label}: status must be one of "
f"{sorted(VALID_LOG_STATUSES)}, got {event.get('status')!r}"
)
summary = event.get("summary")
if not isinstance(summary, str) or not summary.strip():
errors.append(f"loop_log {label}: summary must be a non-empty string")
duration = event.get("duration_sec")
if not _is_int(duration) or duration < 0:
errors.append(
f"loop_log {label}: duration_sec must be a non-negative integer, "
f"got {duration!r}"
)
if not isinstance(event.get("ts"), str) or not event.get("ts"):
errors.append(f"loop_log {label}: ts must be an ISO-8601 UTC string")
# context_tokens is a reserved placeholder; it is always 0.
# Absent is survivable; present-but-wrong means someone edited the log.
if "context_tokens" not in event:
warnings.append(f"loop_log {label}: context_tokens is missing")
elif not _is_int(event["context_tokens"]) or event["context_tokens"] < 0:
errors.append(
f"loop_log {label}: context_tokens must be a non-negative integer, "
f"got {event['context_tokens']!r}"
)
def _zero_weak_images(info: Any) -> bool:
"""True when this phase's gap_analysis recorded the documented early stop.
The count wins over the flag whenever both are present: a recorded positive
count is the measurement, and ``zero_weak_images`` next to it is a
contradiction, not a second opinion (``_contradictory_zero_weak`` reports it).
"""
if not isinstance(info, dict):
return False
count = info.get("weak_image_count")
if _is_int(count):
return count == 0
return info.get("zero_weak_images") is True
def _contradictory_zero_weak(info: Any) -> bool:
"""True when a phase entry claims zero weak images and a positive count."""
if not isinstance(info, dict):
return False
count = info.get("weak_image_count")
return info.get("zero_weak_images") is True and _is_int(count) and count > 0
def _pool_exhausted(iterations: dict[str, Any]) -> str | None:
"""The phase whose loop_stop recorded a spent source pool, if any.
The second documented terminal state. The miner raises rather than returning a
short result, so a pool that cannot fill the budget ends the run. ``--pool-exhausted``
on the loop_stop commit asserts it; ``pool_remaining`` is the count and may be
non-zero, since too few images is exhaustion just as much as none.
"""
for phase in sorted(iterations, key=_phase_sort_key):
# Only an iteration can run out of pool: baseline never mines, so a claim
# recorded there is not evidence of anything.
if iter_number(phase) is None:
continue
info = iterations.get(phase)
if not isinstance(info, dict):
continue
if info.get("pool_exhausted") is True or info.get("pool_remaining") == 0:
return phase
return None
def _max_iterations(state: dict[str, Any]) -> tuple[Any, str]:
"""init_deft_state.py freezes it under config; accept a top-level copy too."""
config = state.get("config")
if isinstance(config, dict) and "max_iterations" in config:
return config["max_iterations"], "state.config.max_iterations"
return state.get("max_iterations"), "state.max_iterations"
def _prep_produced_pool(iterations: dict[str, Any]) -> bool:
"""True when a prep phase already recorded both pool artifacts, still on disk."""
entry = iterations.get("prep")
if not isinstance(entry, dict):
return False
return all(
entry.get(field) and check_artifact(str(entry[field]), kind) is None
for field, (_flag, kind) in STAGE_ARTIFACTS["prep"].items()
)
def _pool_on_disk(state: dict[str, Any]) -> bool:
"""True when the pool init_deft_state.py pinned is already labeled and embedded."""
config = state.get("config")
config = config if isinstance(config, dict) else {}
return all(
config.get(field) and check_artifact(str(config[field]), kind) is None
for field, kind in POOL_CONFIG_FIELDS.items()
)
def _next_action(
*,
state: dict[str, Any],
iterations: dict[str, Any],
completed_by_phase: dict[str, str],
loop_stop_committed: bool,
run_failed: bool,
complete: bool,
max_iterations: int | None,
) -> str:
"""The one stage it is safe to run next, given only what is on disk.
This walks the run in the order the loop runs it — prep, baseline, iter1..N —
and answers with the first phase that still owes a stage. It deliberately does
not key off the last log event's phase: a commit to a lagging phase (the
baseline kpi_analyze that was skipped, a late prep) is a legal write that says
nothing about where the run is. Keying off it rewinds next_action to a stage
commit_stage.py will reject, and the loop's one instruction to the agent
becomes an instruction it cannot carry out.
"""
if loop_stop_committed:
# A finalized hard stop is over, but it is not "complete" — say so, so a
# resuming agent cannot read this line as permission to claim success.
if run_failed:
return FAILED_ACTION
# Same reasoning for a loop_stop committed early: --require-complete
# rejects this run, so next_action must not answer "complete" and hand
# the agent the completion claim that flag exists to withhold.
return "complete" if complete else INCOMPLETE_ACTION
if run_failed:
# A hard stop still has to be finalized: commit loop_stop, then report.
return TERMINAL_STAGE
# prep comes first and produces the mining corpus every iteration reads. It is
# re-checked on every audit, not only on an empty log: the pool can be absent
# at `mine` on a --allow-missing-pool run whose prep was never committed, and
# naming `mine` there hands the agent a stage that cannot succeed.
if not (_pool_on_disk(state) or _prep_produced_pool(iterations)):
return PREP_ORDER[0]
def _owed(phase: str) -> str | None:
try:
upcoming = next_stage(phase, completed_by_phase.get(phase))
except ValueError:
return REPAIR_ACTION
if upcoming is None:
return None
# Zero weak images short-circuits the remaining six stages of the phase.
if (
completed_by_phase.get(phase) == "gap_analysis"
and _zero_weak_images(iterations.get(phase))
):
return TERMINAL_STAGE
return upcoming
owed = _owed("baseline")
if owed is not None:
return owed
for number in range(1, (max_iterations or 0) + 1):
owed = _owed(f"iter{number}")
if owed is not None:
return owed
return TERMINAL_STAGE
def audit(results_dir: Path) -> dict[str, Any]:
"""Cross-check state, log, and disk. Returns the full report; writes nothing."""
results_dir = results_dir.expanduser().resolve()
errors: list[str] = []
warnings: list[str] = []
state: dict[str, Any] | None = None
load_failed = False
try:
state = read_state(results_dir)
except FileNotFoundError:
errors.append(f"deft_state.json not found: {state_path(results_dir)}")
load_failed = True
except json.JSONDecodeError as exc:
errors.append(f"deft_state.json is not valid JSON: {exc}")
load_failed = True
if state is not None and not isinstance(state, dict):
errors.append("deft_state.json root must be a JSON object")
load_failed = True
state = None
if state is None:
state = {}
entries = _load_log(results_dir, errors)
_check_event_schema(entries, errors, warnings)
if not load_failed and state.get("schema_version") != SCHEMA_VERSION:
errors.append(
f"state.schema_version={state.get('schema_version')!r} does not match "
f"the schema this audit understands ({SCHEMA_VERSION})"
)
recorded_results_dir = state.get("results_dir")
if recorded_results_dir:
recorded_path = Path(str(recorded_results_dir)).expanduser()
if not recorded_path.is_absolute():
errors.append(f"state.results_dir must be absolute: {recorded_results_dir}")
elif recorded_path.resolve() != results_dir:
errors.append(
f"state.results_dir={recorded_path.resolve()} does not match the audited "
f"directory {results_dir}"
)
elif not load_failed:
errors.append("state.results_dir is required")
max_iterations, max_iterations_key = _max_iterations(state)
if not _is_int(max_iterations) or max_iterations <= 0:
if not load_failed:
errors.append(
f"{max_iterations_key} must be an integer > 0, got {max_iterations!r}"
)
max_iterations = None
current_iteration = state.get("current_iteration")
if current_iteration is not None and (
not _is_int(current_iteration) or current_iteration < 0
):
errors.append(
f"state.current_iteration must be a non-negative integer, "
f"got {current_iteration!r}"
)
run_status = state.get("status")
if run_status is not None and run_status not in VALID_RUN_STATUSES:
errors.append(
f"state.status={run_status!r} is invalid; expected one of "
f"{sorted(VALID_RUN_STATUSES)}"
)
iterations = state.get("iterations")
if not isinstance(iterations, dict):
if not load_failed:
errors.append("state.iterations must be an object")
iterations = {}
# ── walk the log: legal ordering, and what each phase actually finished ──
completed_by_phase: dict[str, str] = {}
ok_stages_by_phase: dict[str, list[str]] = {}
seq_of: dict[tuple[str, str], int] = {}
flagged_phases: set[str] = set()
first_error_seq: Any = None
loop_stop_seq: Any = None
loop_stop_committed = False
for event in entries:
seq = event.get("seq")
phase = str(event.get("iter", ""))
stage = str(event.get("stage", ""))
status = event.get("status")
try:
phase_order(phase)
except ValueError as exc:
errors.append(f"loop_log seq={seq}: {exc}")
continue
label_problem = _label_problem(phase)
if label_problem:
errors.append(f"loop_log seq={seq}: {label_problem}")
continue
if stage not in ALL_STAGES:
errors.append(
f"loop_log seq={seq}: stage {stage!r} is not one of {ALL_STAGES}"
)
continue
if loop_stop_seq is not None:
errors.append(
f"loop_log seq={seq}: {phase}/{stage} was committed after loop_stop "
f"(seq={loop_stop_seq}); loop_stop must be the final event"
)
if first_error_seq is not None and stage != TERMINAL_STAGE:
errors.append(
f"loop_log seq={seq}: {phase}/{stage} follows the status=error commit at "
f"seq={first_error_seq}; only loop_stop may follow a hard stop"
)
if status == "error":
if first_error_seq is None:
first_error_seq = seq
continue # a failed stage does not advance the phase
if status != "ok":
continue # already reported by _check_event_schema
if stage == TERMINAL_STAGE:
loop_stop_seq = seq
loop_stop_committed = True
continue
if stage not in phase_order(phase):
errors.append(
f"loop_log seq={seq}: stage {stage!r} is not part of phase {phase!r} "
f"({' -> '.join(phase_order(phase))})"
)
continue
try:
validate_transition(phase, completed_by_phase.get(phase), stage)
except ValueError as exc:
if phase not in flagged_phases:
errors.append(f"loop_log seq={seq}: {exc}")
flagged_phases.add(phase)
completed_by_phase[phase] = stage
ok_stages_by_phase.setdefault(phase, []).append(stage)
seq_of[(phase, stage)] = seq if _is_int(seq) else 0
if loop_stop_seq is not None and entries and entries[-1].get("stage") != TERMINAL_STAGE:
warnings.append("loop_stop is committed but is not the last log event")
# ── cross-phase dependency: iterN/gap_analysis reads prev phase's inference ──
# An iteration may not start until the phase before it is *finished*, not just
# far enough along to have written labels. A phase abandoned at inference has
# no kpi_analyze, and its mAP is the number the next iteration is measured
# against; without it the run's one deliverable — the trend — cannot exist.
for phase in sorted(set(list(completed_by_phase) + list(iterations)), key=_phase_sort_key):
previous = previous_phase(phase)
if previous is None:
continue
gap_seq = seq_of.get((phase, "gap_analysis"))
if gap_seq is None:
continue
try:
# _label_problem already rejected the labels that make this unresolvable;
# an auditor still never crashes on the file it is auditing.
final_stage = phase_order(previous)[-1]
except ValueError as exc:
errors.append(f"state.iterations.{phase}: {exc}")
continue
previous_seq = seq_of.get((previous, "inference"))
final_seq = seq_of.get((previous, final_stage))
if previous_seq is None:
errors.append(
f"{phase}/gap_analysis was committed (seq={gap_seq}) but {previous}/inference "
f"never completed; gap_analysis consumes {previous}'s inference labels"
)
elif previous_seq > gap_seq:
errors.append(
f"{phase}/gap_analysis (seq={gap_seq}) was committed before "
f"{previous}/inference (seq={previous_seq}); it cannot have read those labels"
)
elif final_seq is None:
errors.append(
f"{phase}/gap_analysis was committed (seq={gap_seq}) but {previous} never "
f"finished {final_stage}; {previous} was abandoned mid-phase, so the run has "
f"no {previous} mAP to measure {phase} against"
)
elif final_seq > gap_seq:
errors.append(
f"{phase}/gap_analysis (seq={gap_seq}) was committed before {previous} "
f"finished {final_stage} (seq={final_seq}); {phase} started on an unfinished phase"
)
# ── state must agree with the log about what finished ──
for phase, stage in completed_by_phase.items():
info = iterations.get(phase)
if not isinstance(info, dict):
errors.append(
f"loop_log commits {phase}/{stage} but state.iterations.{phase} is missing"
)
continue
recorded = info.get("stage_completed")
if recorded != stage:
errors.append(
f"state.iterations.{phase}.stage_completed={recorded!r} disagrees with the "
f"log, whose last ok stage for {phase} is {stage!r}"
)
for phase in sorted(iterations, key=_phase_sort_key):
info = iterations[phase]
if not isinstance(info, dict):
errors.append(f"state.iterations.{phase} must be an object")
continue
try:
phase_order(phase)
except ValueError as exc:
errors.append(f"state.iterations: {exc}")
continue
label_problem = _label_problem(phase)
if label_problem:
errors.append(f"state.iterations: {label_problem}")
continue
if _contradictory_zero_weak(info):
errors.append(
f"state.iterations.{phase} records zero_weak_images=true alongside "
f"weak_image_count={info.get('weak_image_count')}; the early stop and the "
"measurement contradict each other, so neither can be trusted"
)
recorded = info.get("stage_completed")
if recorded is not None and phase not in completed_by_phase:
errors.append(
f"state.iterations.{phase}.stage_completed={recorded!r} but loop_log has no "
f"matching ok event for {phase}"
)
number = iter_number(phase)
if number is not None and max_iterations is not None and number > max_iterations:
errors.append(
f"state.iterations.{phase} exceeds state.max_iterations={max_iterations}"
)
# ── every recorded artifact must still be on disk, and be the right kind ──
recorded_artifacts: dict[str, tuple[str, str]] = dict(EXTRA_ARTIFACT_FIELDS)
for stage, fields in STAGE_ARTIFACTS.items():
recorded_artifacts.update(fields)
for phase in sorted(iterations, key=_phase_sort_key):
info = iterations[phase]
if not isinstance(info, dict):
continue
for field, (flag, kind) in recorded_artifacts.items():
value = info.get(field)
if value in (None, ""):
continue
if not Path(str(value)).is_absolute():
errors.append(
f"state.iterations.{phase}.{field} ({flag}) must be an absolute "
f"path: {value}"
)
continue
problem = check_artifact(str(value), kind)
if problem:
errors.append(f"state.iterations.{phase}.{field} ({flag}) {problem}")
# A stage the log says finished must have recorded its artifact paths.
for stage in ok_stages_by_phase.get(phase, []):
for field, (flag, _kind) in STAGE_ARTIFACTS[stage].items():
if info.get(field) in (None, ""):
errors.append(
f"loop_log commits {phase}/{stage} but state.iterations.{phase}."
f"{field} ({flag}) was never recorded"
)
# A kpi_analyze recorded ok must carry its mAP: the per-phase trend is the
# loop's only result. commit_stage.py refuses such a commit now; this catches
# a run recorded before it did, so the run stops at the hole rather than
# advancing past it and ending "complete".
map_value = info.get("map_value")
if "kpi_analyze" in ok_stages_by_phase.get(phase, []) and (
isinstance(map_value, bool) or not isinstance(map_value, (int, float))
):
errors.append(
f"loop_log commits {phase}/kpi_analyze ok but state.iterations.{phase}."
f"map_value (--map-value) was never recorded, so the trend has no {phase} "
f"point. Re-score the phase, or record it as --status error"
)
highest_iteration = max(
(
number
for phase in set(list(iterations) + list(completed_by_phase))
if (number := iter_number(phase)) is not None
),
default=0,
)
if _is_int(current_iteration) and current_iteration != highest_iteration:
warnings.append(
f"state.current_iteration={current_iteration} does not match the highest "
f"iteration on disk ({highest_iteration})"
)
# ── run-level status ──
error_events = [event for event in entries if event.get("status") == "error"]
run_failed = bool(error_events) or run_status == "failed"
if error_events and run_status is not None and run_status != "failed":
warnings.append(
f"loop_log records a status=error commit but state.status is {run_status!r}"
)
if run_status == "failed" and not error_events:
warnings.append(
"state.status is 'failed' but no loop_log event has status=error; the failure "
"was never recorded as a stage, so nothing on disk says what failed"
)
if entries and not (_pool_on_disk(state) or _prep_produced_pool(iterations)):
pool_config = state.get("config")
pool_config = pool_config if isinstance(pool_config, dict) else {}
missing = [
str(pool_config.get(field))
for field, kind in POOL_CONFIG_FIELDS.items()
if not (
pool_config.get(field)
and check_artifact(str(pool_config[field]), kind) is None
)
]
warnings.append(
"the source pool `mine` reads is not on disk and no prep stage produced it: "
f"{missing}; prep must run before any iteration reaches `mine`"
)
status = "INVALID" if errors else "VALID"
# Terminal means finalized, and only loop_stop finalizes. A failed run that
# never committed loop_stop still owes that commit — next_action says so — and
# a report rendered before it has no authoritative outcome to render.
terminal = loop_stop_committed
iterations_completed = sum(
1
for phase, stage in completed_by_phase.items()
if iter_number(phase) is not None and stage == ITER_ORDER[-1]
)
complete = False
completion_reason = ""
if not loop_stop_committed:
completion_reason = "no loop_stop event is committed"
elif error_events:
completion_reason = "the run committed a status=error hard stop"
elif run_status == "failed":
completion_reason = "state.status is 'failed'"
elif (
completed_by_phase.get("baseline") == BASELINE_ORDER[-1]
and not isinstance((iterations.get("baseline") or {}).get("map_value"), (int, float))
):
# --map-value is optional, because a log printing "mAP: nan" has none to
# record. But the baseline mAP is what every iteration is compared against,
# so a run without one has produced no trend and is not complete.
completion_reason = (
"baseline/kpi_analyze committed no map_value, so there is no baseline mAP to "
"compare iterations against"
)
elif completed_by_phase.get("baseline") != BASELINE_ORDER[-1]:
# The baseline mAP is the number every iteration is compared against.
# Without it there is no trend, which is the only thing the loop produces.
completion_reason = (
f"baseline never finished {BASELINE_ORDER[-1]}, so the run has no baseline mAP "
"and no measurable trend"
)
elif max_iterations is not None and all(
completed_by_phase.get(f"iter{n}") == ITER_ORDER[-1]
for n in range(1, max_iterations + 1)
):
complete = True
completion_reason = (
f"all {max_iterations} iterations completed every stage of "
f"{' -> '.join(ITER_ORDER)}"
)
else:
# The early stop only counts on a phase that actually stopped at
# gap_analysis. A phase that recorded zero weak images and then went on to
# embed, mine and train did not take the documented exit, and a zero
# recorded on any other stage is not gap_analysis's measurement at all.
early = next(
(
phase
for phase in sorted(iterations, key=_phase_sort_key)
if completed_by_phase.get(phase) == "gap_analysis"
and _zero_weak_images(iterations[phase])
),
None,
)
exhausted = _pool_exhausted(iterations)
if early is not None:
complete = True
completion_reason = (
f"documented early stop: {early}/gap_analysis recorded zero weak images"
)
elif exhausted is not None and iterations_completed >= 1:
# A spent pool ends a loop that produced something. With no iteration
# finished there is no trend, and the claim is unfalsifiable — it would
# let any abandoned run be relabelled as a documented stop.
complete = True
completion_reason = (
f"documented early stop: the source pool was exhausted at {exhausted} "
f"(pool_remaining={iterations[exhausted].get('pool_remaining', 'unrecorded')}), "
f"so no further iteration could mine"
)
else:
completion_reason = (
f"only {iterations_completed} of {max_iterations} iterations finished "
f"{ITER_ORDER[-1]}, and the loop was stopped by neither documented early "
f"stop (zero weak images, or a source pool exhausted after at least one "
f"completed iteration)"
)
if status == "INVALID":
complete = False
completion_reason = "the run is INVALID"
elif run_status == "complete" and not complete:
# commit_stage.py only writes "complete" once this audit has agreed it is
# one, so a disagreement means the file was written by something else.
warnings.append(
f"state.status is 'complete' but this audit withholds completion: "
f"{completion_reason}"
)
if status == "INVALID":
next_action = REPAIR_ACTION
else:
next_action = _next_action(
state=state,
iterations=iterations,
completed_by_phase=completed_by_phase,
loop_stop_committed=loop_stop_committed,
run_failed=run_failed,
complete=complete,
max_iterations=max_iterations,
)
# Stages that ran through the overlay's docker run rather than their mapped
# skill, as commit_stage.py --execution-path records them. Reported, not judged:
# the fallback is documented, and the point is that a run can be attributed to
# the code path that produced it.
direct_container_stages = [
f"{phase}/{stage}"
for phase in sorted(iterations, key=_phase_sort_key)
if isinstance(iterations[phase], dict)
and isinstance(iterations[phase].get("execution_paths"), dict)
for stage, path in iterations[phase]["execution_paths"].items()
if path == "direct-container"
]
last_event = entries[-1] if entries else None
last_committed = (
f"{last_event.get('iter')}/{last_event.get('stage')}" if last_event else "none"
)
return {
"status": status,
"results_dir": str(results_dir),
"schema_version": state.get("schema_version"),
"max_iterations": max_iterations,
"iterations_completed": iterations_completed,
"log_entries": len(entries),
"last_committed": last_committed,
"last_status": last_event.get("status") if last_event else None,
"stage_completed_by_phase": completed_by_phase,
"next_action": next_action,
"read_before_action": STAGE_OVERLAYS.get(next_action, "none"),
"terminal": terminal,
"loop_stop_committed": loop_stop_committed,
"run_failed": run_failed,
"complete": complete,
"completion_reason": completion_reason,
"direct_container_stages": direct_container_stages,
"load_failed": load_failed,
"errors": errors,
"warnings": warnings,
}
def _print_text(report: dict[str, Any]) -> None:
print(f"DEFT_RUN_STATUS={report['status']}")
print(f"results_dir={report['results_dir']}")
max_iterations = report["max_iterations"]
print(
f"iterations_completed={report['iterations_completed']}/"
f"{max_iterations if max_iterations is not None else 'unknown'}"
)
print(f"last_committed={report['last_committed']}")
print(f"next_action={report['next_action']}")
print(f"read_before_action={report['read_before_action']}")
print(f"terminal={str(report['terminal']).lower()}")
print(f"complete={str(report['complete']).lower()}")
# Printed on success as well as failure: "complete" reached by finishing every
# iteration and "complete" reached by a documented early stop are different
# runs, and the reason is the only thing that tells them apart.
print(f"completion_reason={report['completion_reason']}")
if report["direct_container_stages"]:
print("direct_container_stages=" + ",".join(report["direct_container_stages"]))
sys.stdout.flush() # keep the key=value block ahead of stderr when piped
for warning in report["warnings"]:
print(f"warning: {warning}", file=sys.stderr)
for error in report["errors"]:
print(f"error: {error}", file=sys.stderr)
class _AuditArgumentParser(argparse.ArgumentParser):
"""argparse with this script's exit codes.
argparse exits 2 on a usage error, and here 2 means deft_state.json is missing or
unparseable -- so a mistyped flag would read as a corrupt run. A usage error
audits nothing; it is reported as 1, "not valid, stop", rather than as a fault in
the state.
"""
def error(self, message: str): # type: ignore[override]
self.print_usage(sys.stderr)
self.exit(1, f"{self.prog}: error: {message}\n")
def main() -> int:
parser = _AuditArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument(
"--results-dir",
required=True,
help="Absolute path to the run directory holding deft_state.json.",
)
parser.add_argument(
"--json",
action="store_true",
help="Emit one JSON object instead of key=value lines.",
)
parser.add_argument(
"--require-terminal",
action="store_true",
help=(
"Also require the run to be finalized: loop_stop committed. A properly "
"finalized FAILED run passes; a run that failed and never committed "
"loop_stop does not."
),
)
parser.add_argument(
"--require-complete",
action="store_true",
help=(
"Also require genuine completion: a committed loop_stop plus either "
"max_iterations full iterations, or a documented early stop: zero weak\n"
"images, or an exhausted source pool."
),
)
args = parser.parse_args()
try:
report = audit(Path(args.results_dir))
except Exception as exc: # noqa: BLE001
print(f"ERROR: {exc}", file=sys.stderr)
return 2
if args.json:
print(json.dumps(report, indent=2))
else:
_print_text(report)
if report["load_failed"]:
return 2
if report["status"] == "INVALID":
return 1
if args.require_terminal and not report["terminal"]:
print(
"error: --require-terminal: the run is not finalized; no loop_stop is "
f"committed. next_action={report['next_action']}",
file=sys.stderr,
)
return 1
if args.require_complete and not report["complete"]:
print(
f"error: --require-complete: {report['completion_reason']}",
file=sys.stderr,
)
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Needs Python
The author says: Requires docker + nvidia-container-toolkit and one or more CUDA GPUs. Workflows declare additional requirements.
Security
What our scanner noticed· Medium risk
- Large base64-encoded blobs
- References to credential files
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- NVIDIA Corporation
- Publisher
- NVIDIA (Official)
- Source
- NVIDIA/skills · skills/tao-run-deft-object-detection · commit fce3cca
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE-APACHE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by NVIDIA Corporation or NVIDIA.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download tao-run-deft-object-detection.zip