Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The customer-facing CLI for the tracebloc declarative ingestion path. Wraps the

**v0.3.0 is released** — the latest stable [release](https://github.com/tracebloc/cli/releases/latest), cut from `develop`. It builds on v0.2.0's guided `data ingest` and `dataset rm` with a new `dataset list` command plus home-screen / output polish (clearer copy, guided-first framing). The binary implements `version`, `completion`, `data validate`, `cluster info`, and the full `data ingest` / `dataset list` / `dataset rm` flow — local schema validation, cluster discovery, data staging, submission, and Job watching, end to end.

`data ingest` covers **9 of 10 task categories**: `image_classification`, `object_detection`, `keypoint_detection`, `text_classification`, `masked_language_modeling`, `tabular_classification`, `tabular_regression`, `time_series_forecasting`, and `time_to_event_prediction`. `semantic_segmentation` is pending mask-sidecar support upstream ([data-ingestors#136](https://github.com/tracebloc/data-ingestors/issues/136)); `instance_segmentation` is not yet implemented.
`data ingest` covers **9 of 10 task categories**: `image_classification`, `object_detection`, `keypoint_detection`, `text_classification`, `masked_language_modeling`, `tabular_classification`, `tabular_regression`, `time_series_forecasting`, and `time_to_event_prediction`. `semantic_segmentation` is pending mask-sidecar support upstream ([data-ingestors#136](https://github.com/tracebloc/data-ingestors/issues/136)).

The release pipeline ships [`v0.3.0`](https://github.com/tracebloc/cli/releases/latest) as **cosign-signed, multi-arch binaries** — Linux (`amd64`, `arm64`, `386`, `arm`), macOS (`amd64`, `arm64`), and Windows (`amd64`, `arm64`) — each with `SHA256SUMS` and the install scripts. Install via [Customer experience](#customer-experience) or [build from source](#building-from-source). (A Homebrew tap and the `install.tracebloc.io` vanity URL are later follow-ups; the GitHub release URL serves installs today.)

Expand Down Expand Up @@ -117,7 +117,7 @@ All v0.1 phases are merged:

Beyond the original phases, `data ingest` was widened from image-classification-only to 9 of 10 modalities, and the test suite gained unit-coverage wins plus a kind-based integration harness for the real-I/O seams.

**v0.2.0** added a friendlier guided `data ingest` and `dataset rm` on the home screen (#44, #47). **v0.3.0** added the `dataset list` command (#53) plus home-screen / output-spacing polish and feedback-copy refinements (#52, #56). **Next:** cloud-source ingestion (S3/GCS/HTTPS) for datasets above the 1 GiB local cap; `semantic_segmentation` ([data-ingestors#136](https://github.com/tracebloc/data-ingestors/issues/136)) and `instance_segmentation`. Smaller follow-ups are tracked as [open issues](https://github.com/tracebloc/cli/issues).
**v0.2.0** added a friendlier guided `data ingest` and `dataset rm` on the home screen (#44, #47). **v0.3.0** added the `dataset list` command (#53) plus home-screen / output-spacing polish and feedback-copy refinements (#52, #56). **Next:** cloud-source ingestion (S3/GCS/HTTPS) for datasets above the 1 GiB local cap; `semantic_segmentation` ([data-ingestors#136](https://github.com/tracebloc/data-ingestors/issues/136)). Smaller follow-ups are tracked as [open issues](https://github.com/tracebloc/cli/issues).

Epic: [tracebloc/client#147](https://github.com/tracebloc/client/issues/147).

Expand Down
4 changes: 2 additions & 2 deletions internal/cli/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,8 @@ other collaborators train against it without ever seeing the raw files.`))
// supported
case push.IsKnown(a.Spec.Category):
// A recognized category data ingest doesn't implement yet — image
// (semantic_segmentation / instance_segmentation) or text
// (causal_language_modeling). Routed here (not the default branch) so the
// (semantic_segmentation) or text (causal_language_modeling, seq2seq,
// ). Routed here (not the default branch) so the
// user gets the registry's per-category pending-support reason, not a
// misleading "unrecognized category". Supported categories were already
// caught above, so IsKnown here means known-but-unsupported.
Expand Down
4 changes: 2 additions & 2 deletions internal/cli/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ func execDataIngest(t *testing.T, args []string) (exitCode int, stdout, stderr s
func TestDataIngest_UnsupportedCategory_ExitsTwo(t *testing.T) {
root := imgcLayout(t)
for _, badCategory := range []string{
"semantic_segmentation", // blocked on the ingestor (data-ingestors#136)
"instance_segmentation", // not implemented
"semantic_segmentation", // known but blocked on the ingestor (data-ingestors#136)
"instance_segmentation", // dead — removed from the registry (#1005), now unrecognized
"definitely-not-a-category", // nonsense; gate catches this too
} {
t.Run(badCategory, func(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions internal/cli/interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
// category picker. It derives from the push registry's CLI-supported
// set — the exact categories runDataIngest's gate accepts — so the
// picker can't drift from what `data ingest` actually supports.
// semantic_/instance_segmentation are excluded (CLISupported=false)
// until they're implemented.
// semantic_segmentation is excluded (CLISupported=false) until it's
// implemented.
var promptCategories = push.SupportedCategoryIDs()

// prompter is the narrow seam over the interactive library. Production
Expand Down
20 changes: 11 additions & 9 deletions internal/push/category.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ type CategorySpec struct {
// never ships to the central backend by default.
RegressionClass bool
// CLISupported reports whether `dataset push` implements the category
// today. semantic_/instance_segmentation are known (the schema
// defines them) but not yet pushable.
// today. semantic_segmentation is known (the schema defines it) but
// not yet pushable.
CLISupported bool
// UnsupportedNote explains why a known-but-unimplemented category
// isn't available yet; surfaced by the push gate. Empty when supported.
Expand All @@ -49,11 +49,15 @@ const (
FamilyText
)

// categoryRegistry is the ordered, authoritative list of every category
// the ingest.v1 schema defines. Order is the display order for help text
// and the interactive picker (CLI-supported first, in workflow order;
// the not-yet-implemented ones last). Adding a category to the schema
// means adding it here — the parity test pins the set.
// categoryRegistry is the ordered list of every category the ingest.v1
// schema defines — nothing more, nothing less. Order is the display order for
// help text and the interactive picker (CLI-supported first, in workflow
// order; the not-yet-implemented ones last). Adding a category to the schema
// means adding it here; TestRegistryCoversSchemaCategories +
// TestRegistryWithinSchema pin the set equal to the schema enum both ways, so
// it can neither fall behind (a schema category rejected as "unrecognized")
// nor carry an extra the ingestor won't accept (the instance_segmentation
// half-ingest class — data-ingestors #240/#99, #1005).
var categoryRegistry = []CategorySpec{
{ID: "image_classification", Family: FamilyImage, Label: "Image classification", CLISupported: true},
{ID: "object_detection", Family: FamilyImage, Label: "Object detection", CLISupported: true},
Expand All @@ -66,8 +70,6 @@ var categoryRegistry = []CategorySpec{
{ID: "time_to_event_prediction", Family: FamilyTabular, Label: "Time-to-event prediction", RegressionClass: true, CLISupported: true},
{ID: "semantic_segmentation", Family: FamilyImage, Label: "Semantic segmentation", CLISupported: false,
UnsupportedNote: "blocked on the ingestor's mask-sidecar support (data-ingestors#136)"},
{ID: "instance_segmentation", Family: FamilyImage, Label: "Instance segmentation", CLISupported: false,
UnsupportedNote: "not implemented"},
{ID: "causal_language_modeling", Family: FamilyText, Label: "Causal language modeling", CLISupported: false,
UnsupportedNote: "schema-recognized (data-ingestors#805); `tracebloc ingest` discover/build for its raw-.txt / prompt\\tcompletion `texts` layout is pending"},
{ID: "seq2seq", Family: FamilyText, Label: "Sequence-to-sequence", CLISupported: false,
Expand Down
64 changes: 49 additions & 15 deletions internal/push/category_registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
func TestRegistryKnownCategories(t *testing.T) {
want := []string{
"image_classification", "object_detection", "keypoint_detection",
"semantic_segmentation", "instance_segmentation",
"semantic_segmentation",
"text_classification", "token_classification",
"masked_language_modeling", "causal_language_modeling", "seq2seq",
"sentence_pair_classification", "embeddings",
Expand Down Expand Up @@ -45,9 +45,9 @@ func TestSupportedCategories(t *testing.T) {
t.Errorf("SupportedCategoryIDs returned %q but IsCLISupported is false", id)
}
}
// segmentation + the self-supervised text categories (CLM, seq2seq) +
// token_classification are known but not yet pushable, and must explain why.
for _, id := range []string{"semantic_segmentation", "instance_segmentation", "causal_language_modeling", "seq2seq", "token_classification", "sentence_pair_classification", "embeddings"} {
// semantic_segmentation + the self-supervised text categories (CLM, seq2seq)
// + token_classification are known but not yet pushable, and must explain why.
for _, id := range []string{"semantic_segmentation", "causal_language_modeling", "seq2seq", "token_classification", "sentence_pair_classification", "embeddings"} {
if !IsKnown(id) {
t.Errorf("%s should be known", id)
}
Expand Down Expand Up @@ -87,16 +87,12 @@ func TestPredicatesDeriveFromRegistry(t *testing.T) {
}
}

// TestRegistryCoversSchemaCategories pins registry⇄schema parity: every
// category the ingest schema accepts must be known to the registry, or a
// schema-valid `dataset push --category=X` is wrongly rejected as
// "unrecognized" (the token_classification drift, Bugbot v0.4.0 RC). The
// existing tests only pin the registry against a hand-written list, which
// stays internally consistent while drifting from the schema — this closes
// that gap. The reverse direction isn't required: the registry may carry a
// known-but-unsupported alias the v1 schema doesn't list yet (e.g.
// instance_segmentation), which is gated out before schema validation.
func TestRegistryCoversSchemaCategories(t *testing.T) {
// schemaCategoryEnum returns the category enum from the embedded ingest.v1
// schema — the single source of truth the registry is pinned against (#1005).
// The schema is vendored + drift-checked against data-ingestors by
// scripts/sync-schema.sh, so this ties the registry transitively to upstream.
func schemaCategoryEnum(t *testing.T) []string {
t.Helper()
var doc struct {
Properties struct {
Category struct {
Expand All @@ -110,14 +106,52 @@ func TestRegistryCoversSchemaCategories(t *testing.T) {
if len(doc.Properties.Category.Enum) == 0 {
t.Fatal("no category enum found in the embedded schema (parse path wrong?)")
}
for _, id := range doc.Properties.Category.Enum {
return doc.Properties.Category.Enum
}

// registryAliases are registry category IDs deliberately NOT in the ingest.v1
// schema enum — declared placeholders. Empty today: instance_segmentation used
// to sit here unchecked, but it's dead (it half-ingested with no validators or
// file transfer — data-ingestors #240/#99) and was removed, not kept. A future
// known-but-unschema'd placeholder must be DECLARED here, so TestRegistryWithinSchema
// flags undeclared drift while allowing an intentional superset (#1005).
var registryAliases = map[string]bool{}

// TestRegistryCoversSchemaCategories pins schema ⊆ registry: every category the
// ingest schema accepts must be known to the registry, or a schema-valid
// `dataset push --category=X` is wrongly rejected as "unrecognized" (the
// token_classification drift, Bugbot v0.4.0 RC).
func TestRegistryCoversSchemaCategories(t *testing.T) {
for _, id := range schemaCategoryEnum(t) {
if !IsKnown(id) {
t.Errorf("schema category %q missing from the registry — `dataset push --category=%s` "+
"would be rejected as unrecognized despite passing schema validation", id, id)
}
}
}

// TestRegistryWithinSchema pins registry ⊆ schema (+ declared aliases): the
// registry must not carry a category the ingest schema — and therefore the
// ingestor — doesn't accept. An undeclared extra is exactly the
// instance_segmentation half-ingest class: the backend/CLI would accept a
// `--category` the pipeline can't handle, and the config half-ingests (DB rows
// + API records, zero files staged; #1005, data-ingestors #240/#99). Together
// with TestRegistryCoversSchemaCategories this pins registry == schema, modulo
// explicitly declared placeholders in registryAliases.
func TestRegistryWithinSchema(t *testing.T) {
inSchema := make(map[string]bool)
for _, id := range schemaCategoryEnum(t) {
inSchema[id] = true
}
for _, id := range AllCategoryIDs() {
if !inSchema[id] && !registryAliases[id] {
t.Errorf("registry category %q is not in the ingest.v1 schema enum and not a declared "+
"alias — add it to the schema (data-ingestors) if it's real, or declare it in "+
"registryAliases if it's an intentional placeholder", id)
}
}
}

func equalSet(a, b []string) bool {
if len(a) != len(b) {
return false
Expand Down
Loading