diff --git a/internal/cli/copy_catalog_test.go b/internal/cli/copy_catalog_test.go index cd6b50c..40405a9 100644 --- a/internal/cli/copy_catalog_test.go +++ b/internal/cli/copy_catalog_test.go @@ -532,6 +532,57 @@ func (c *catalogPrompter) Confirm(label string, def bool) (bool, error) { return def, nil } +// literalString folds a compile-time-constant string expression to its value. +// +// A message written across two source lines — +// +// fmt.Errorf("unknown backend environment %q — valid values are … "+ +// "set CLIENT_ENV or pass --env", env) +// +// — is an *ast.BinaryExpr, not a *ast.BasicLit. The harvest used to type-assert +// straight to *ast.BasicLit, so it skipped those arguments entirely: not the +// second half, the WHOLE message. That made this file's own header claim ("the +// completeness backstop") false for an entire syntactic class of copy, and it +// passed forever because nothing it could see had gone missing. The env-validation +// error above was absent from the golden while being plainly user-facing. +// +// Only an ALL-literal join folds. An operand that is a variable, a call, or a +// constant identifier makes the whole expression unfoldable and yields false — +// deliberately, because emitting the literal half of a part-computed message +// would put a sentence in the catalog that no user ever sees, and mark it +// inventoried while the real text drifts. Absent is honest; half is not. +func literalString(e ast.Expr) (string, bool) { + switch n := e.(type) { + case *ast.BasicLit: + if n.Kind != token.STRING { + return "", false + } + s, err := strconv.Unquote(n.Value) + if err != nil { + return "", false + } + return s, true + case *ast.ParenExpr: + return literalString(n.X) + case *ast.BinaryExpr: + // ADD only: any other operator on strings is not a concatenation, and + // arithmetic on non-strings is filtered by the BasicLit kind check above. + if n.Op != token.ADD { + return "", false + } + l, ok := literalString(n.X) + if !ok { + return "", false + } + r, ok := literalString(n.Y) + if !ok { + return "", false + } + return l + r, true + } + return "", false +} + // harvestMessages parses the user-facing packages and returns every string // literal that reaches a user: ALL arguments to a Printer method or an error / // format constructor (errors.New, fmt.Errorf, fmt.Sprintf), PLUS the string @@ -593,12 +644,8 @@ func harvestMessages(t *testing.T) []string { seen := map[string]struct{}{} collect := func(prefix string, exprs []ast.Expr) { for _, arg := range exprs { - lit, ok := arg.(*ast.BasicLit) - if !ok || lit.Kind != token.STRING { - continue - } - s, uerr := strconv.Unquote(lit.Value) - if uerr != nil { + s, ok := literalString(arg) + if !ok { continue } s = strings.TrimSpace(s) @@ -653,3 +700,66 @@ func harvestMessages(t *testing.T) []string { sort.Strings(out) return out } + +// TestLiteralString pins the fold itself. The inputs are written down here +// independently of the implementation — never derived from it — so a typo in the +// matcher cannot also plant the same typo in its own fixture (the "never test a +// list against itself" rule). +func TestLiteralString(t *testing.T) { + cases := []struct { + name string + src string // an expression + want string + ok bool + }{ + {"plain literal", `"hello there"`, "hello there", true}, + {"raw literal", "`raw string`", "raw string", true}, + {"two-part join", `"first half " + "second half"`, "first half second half", true}, + {"three-part join", `"a " + "b " + "c"`, "a b c", true}, + {"join across quote styles", "`raw ` + \"interpreted\"", "raw interpreted", true}, + {"parenthesised join", `("a " + "b")`, "a b", true}, + // The load-bearing refusals: a part-computed message must not be emitted + // half-harvested, or the catalog would claim to inventory a sentence no + // user ever sees. + {"literal + identifier", `"prefix " + name`, "", false}, + {"identifier + literal", `name + " suffix"`, "", false}, + {"literal + call", `"prefix " + fmt.Sprint(x)`, "", false}, + {"nested unfoldable operand", `"a " + ("b " + c)`, "", false}, + {"non-ADD operator", `"a" == "b"`, "", false}, + {"numeric literal", `42`, "", false}, + {"numeric addition", `1 + 2`, "", false}, + {"bare identifier", `msg`, "", false}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + expr, err := parser.ParseExpr(tc.src) + if err != nil { + t.Fatalf("ParseExpr(%q): %v", tc.src, err) + } + got, ok := literalString(expr) + if ok != tc.ok { + t.Fatalf("literalString(%q) ok = %v, want %v (got %q)", tc.src, ok, tc.ok, got) + } + if got != tc.want { + t.Errorf("literalString(%q) = %q, want %q", tc.src, got, tc.want) + } + }) + } +} + +// TestHarvestMessages_SeesConcatenatedCopy is the regression pin for the reported +// defect: a real, plainly user-facing error written as a two-line join was absent +// from the catalog entirely. Reverting the fold to a bare *ast.BasicLit assertion +// reddens this. +func TestHarvestMessages_SeesConcatenatedCopy(t *testing.T) { + msgs := harvestMessages(t) + // runLogin's env validation (auth.go) — split across source lines, so it was + // invisible to an operand-blind scan. + const needle = "unknown backend environment" + for _, m := range msgs { + if strings.Contains(m, needle) { + return + } + } + t.Fatalf("harvest is missing the concatenated message %q — the fold is not applied", needle) +} diff --git a/internal/cli/testdata/golden/zz-all-strings.golden b/internal/cli/testdata/golden/zz-all-strings.golden index 2116c6e..c5feb3b 100644 --- a/internal/cli/testdata/golden/zz-all-strings.golden +++ b/internal/cli/testdata/golden/zz-all-strings.golden @@ -17,22 +17,36 @@ screen. %s/%d are runtime placeholders. "%d files" "%d files (%s)" "%d image pull secret(s) present and well-formed" +"%d image(s) are smaller than the %dx%d minimum you set with --min-size: %s. Provide larger images, or lower the floor with --min-size, then re-run." +"%d image(s) can't be ingested: %s. The cluster rejects these after the upload — fix or remove them and re-run." +"%d image(s) don't match the %dx%d resolution: %s. The cluster validates the size, it does not resize — make them uniform, or pass --target-size to match your data." "%d image(s) without a mask (%s)" "%d image(s) without an annotation (%s)" +"%d labels.csv row(s) reference images that aren't in images/: %s. Those records would fail after the upload — fix the rows or add the files, then re-run." +"%d mask(s) are smaller than the %dx%d minimum you set with --min-size: %s. Provide larger masks, or lower the floor with --min-size, then re-run." +"%d mask(s) don't match the %dx%d resolution the images use: %s. Semantic-segmentation masks are pixel-wise label maps, so each mask must be exactly the image size — the cluster validates this after the upload. Resize the masks to match and re-run." +"%d mask(s) in masks/ can't be ingested: %s. The cluster reads every mask as a PNG and rejects these after the upload — fix or remove them and re-run." "%d mask(s) not named _mask.png (%s)" "%d mask(s) without an image (%s)" "%d minutes" "%d of %d" "%d pod(s), none crash-looping or stuck Pending" "%d pod(s), none restarted ≥%d times" +"%d row(s) in %s have an empty %q (e.g. %s). Every row must name its mask file — an empty value makes the training client derive a garbage filename and fail. Fill in %q or drop those rows, then re-run." +"%d sequence(s) grouped by %q change their %q value mid-sequence (first offending sequences start at data row(s) %v%s). Time-series classification assigns ONE label per sequence: every row of a sequence must repeat the same label value. The cluster rejects this after the upload — fix the labels and re-run." +"%d sequence(s) grouped by %q have out-of-order %q values (first offending data row(s) %v%s). Timestep rows must be sorted ascending by %q within each sequence — sort each sequence and re-run. Interleaving different sequences is fine; ordering is only checked within a sequence. The cluster rejects this after the upload." "%d system table(s) hidden — show with --all." "%dd ago" "%dh ago" "%dm ago" "%q exists but is not a directory" "%q is a directory, not a file" +"%q is a directory, not a file. labels.csv must be the CSV file holding the filename,label rows." +"%q is a symbolic link, which v0.1 does not allow in the dataset layout (security: a symlink could escape the dataset tree or bypass size caps). Materialize the link target (e.g. `cp -L`) and re-run, or wait for v0.2's cloud-source story if the data lives elsewhere." +"%q is not a .csv file. Tabular / time-series data is a single CSV — pass the .csv file itself, or a directory containing exactly one .csv." "%q is not a directory; pass the directory containing labels.csv + images/" "%q is not a directory; pass the directory containing labels.csv + the text files" +"%q won't work — no hyphens or spaces (use _); use letters, digits, and underscores, starting with a letter or underscore (e.g. churn_train)" "%s state=%s namespace=%s location=%s" "%s %q must be WxH (e.g. 512x512)" "%s %q: height is not an integer: %w" @@ -42,12 +56,16 @@ screen. %s/%d are runtime placeholders. "%s (%dx%d)" "%s (unreadable: %v)" "%s Bound, mounted at %s" +"%s contains a NUL byte — the file is corrupt or not really a CSV. The cluster rejects it after the upload; re-export the file and re-run." "%s has a header but no data rows (0 ingestable records). Add at least one data row and re-run." +"%s has duplicate column name(s): %s. Each column must be unique — the cluster rejects duplicates, and the schema would map onto the wrong column. Rename them and re-run." "%s is empty — add a header and at least one data row, then re-run" "%s is empty — no header row" +"%s isn't valid UTF-8 (likely a Latin-1/Windows-1252 export). The cluster rejects non-UTF-8 CSVs after the upload — re-save it as UTF-8 and re-run." "%s of %s GiB" "%s of %s cores" "%s requires CLIENT_WRITE permission" +"%s starts with a UTF-8 byte-order mark (Excel's \"CSV UTF-8\" export adds it), which the cluster's schema check can't read — the ingestion would fail after uploading everything. Re-save it without the mark (in Excel choose plain \"CSV\"; or: tail -c +4 %s > fixed.csv) and re-run." "%s still present after removal" "%s unreachable: %v" "%s · %d" @@ -64,6 +82,9 @@ screen. %s/%d are runtime placeholders. "%s: %w" "%s=%s,%s=%s" "%v (policy: %v)" +"%w in namespace %q, but tracebloc clients are running in: %s. Pass --namespace to pick one." +"%w in namespace %q. If your client runs in another namespace, pass --namespace; if this cluster has no tracebloc client yet, run the installer: %s. Diagnose with `tracebloc doctor`." +"%w on the cluster your kubeconfig points at — if this machine should have one, run the installer to provision it; otherwise point at the right cluster with --context/--namespace" "%w. Run `tracebloc login` to start a new one" "(%d CPU · %d GiB" "(+%d more)" @@ -79,11 +100,14 @@ screen. %s/%d are runtime placeholders. "-%02d" "--%s has no effect without --seal" "--label-column doesn't apply to task %q — it trains on the text itself, with no label column" +"--label-policy is regression-class tasks only (tabular_regression, time_series_forecasting, time_to_event_prediction); it doesn't apply to task %q" "--min-size is image tasks only; it doesn't apply to task %q" "--number-of-keypoints is keypoint_detection only; it doesn't apply to task %q" +"--number-of-keypoints must be a positive integer (got %d); it's the number of keypoints per sample (e.g. 17 for COCO pose)" "--overwrite can't be combined with --idempotency-key: a reused key makes the cluster replay the previous run instead of ingesting the new data — after --overwrite's removal that would report success while loading nothing. Drop one of the two (a fresh per-run key is the default)." "--schema is empty; expected col:TYPE,col:TYPE,..." "--schema is tabular/time-series tasks only; it doesn't apply to task %q" +"--schema names column(s) that aren't in %s: %s. The cluster rejects this after the upload — fix the schema or the CSV header, then re-run." "--target-size is image tasks only; it doesn't apply to task %q" "--time-column is time_to_event_prediction only; it doesn't apply to task %q" "--timeout has no effect without --wait or --seal" @@ -131,14 +155,18 @@ screen. %s/%d are runtime placeholders. "Copying %s" "Correlation id: %s" "Couldn't check for active training runs (%v) — continuing; the confirmation below still guards you." +"Couldn't clear the stored active-client pointer (%v) — the on-disk config still names the revoked client; run `tracebloc logout` or remove it by hand." "Couldn't connect to your secure environment — check your kubeconfig/context." +"Couldn't determine this client's namespace — skipped the Helm uninstall. If a release is still installed, re-run with --namespace ." "Couldn't locate the CLI binary to remove it (%v) — delete it by hand." "Couldn't read the target cluster's identity — provisioning without a cluster anchor, so re-running won't be idempotent. Point --kubeconfig/--context at the reachable cluster to enable that." "Couldn't read your tracebloc config — run `%s login` to recreate it." "Couldn't reclaim the temporary copy (%v). It's harmless — the next re-ingest of %q or a `tracebloc data delete %s` will clear it." +"Couldn't remove local data (%v) — cleared the active-client pointer; remove the data by hand: rm -rf %s" "Couldn't remove the CLI (%v) — remove it by hand: rm -f %s" "Couldn't remove the CLI (%v). It looks Homebrew-managed — finish with: brew uninstall tracebloc" "Couldn't remove the `tb` alias (%v) — remove it by hand: rm -f %s" +"Couldn't revoke the credential server-side (%v) — continuing with local teardown. The credential may still be live on tracebloc; revoke it from the dashboard if needed." "Couldn't save the active-client pointer (%v) — re-run `tracebloc client create` (it adopts this cluster's client) to set it." "Couldn't save the active-client pointer (%v) — re-run `tracebloc client create` (it adopts this cluster's client)." "Couldn't verify your session with the backend (%v)." @@ -175,6 +203,7 @@ screen. %s/%d are runtime placeholders. "Fix the failing checks above, then re-run `tracebloc client status --seal` to confirm the seal." "Follow it later with: kubectl logs -f -n %s job/%s" "For help: https://docs.tracebloc.io/create-use-case/prepare-dataset" +"Found a %q entry, but image and text data need a real lowercase folder like %q (not a symlink or a file). If that's your data folder, fix it and ingest again." "Found labels.csv and a %s folder — this looks like text data." "Free some up on this machine, or %s." "Free some up, give Docker more memory (WSL2 backend: `[wsl2] memory=…` in %%UserProfile%%\\.wslconfig then `wsl --shutdown`; Hyper-V backend: Docker Desktop → Settings → Resources → Advanced), or %s." @@ -238,10 +267,13 @@ screen. %s/%d are runtime placeholders. "Not signed in. Run `tracebloc login`." "Not yet in the CLI:" "Note: %d file(s) in images/ have no labels.csv row and won't be part of the dataset: %s" +"Note: %d sequence(s) grouped by %q — the platform counts this dataset in sequences, not rows" "Offboarded %q. This machine is no longer connected to tracebloc." +"Offboarded %q: the machine credential is revoked, so it can no longer connect to tracebloc — but some cleanup above didn't complete. Finish the flagged steps by hand." "Only tracebloc's small jobs-manager restarts — running training isn't interrupted." "Open" "POST %s%s: %w" +"PVC %s/%s is in phase %q, not Bound. The shared volume hasn't been provisioned — check that the cluster has a usable StorageClass (kubectl get sc) and that the PVC's storageClassName matches." "PVC is %v, not ReadWriteMany — the stage Pod will co-locate with the existing mounter" "Path:" "Pending > %s: %v" @@ -280,6 +312,7 @@ screen. %s/%d are runtime placeholders. "Revoked this machine's credential — your secure environment %q stays on tracebloc as a record." "Run '%s --help' for the available commands." "SELECT '%s',%s,COUNT(*),%s,%s FROM `%s`.`%s`" +"SELECT r.table_name, COALESCE(r.task,'') FROM `%s`.`%s` r JOIN (SELECT table_name, MAX(started_at) ms FROM `%s`.`%s` WHERE task IS NOT NULL GROUP BY table_name) m ON r.table_name = m.table_name AND r.started_at = m.ms WHERE r.task IS NOT NULL" "SELECT table_name FROM information_schema.tables WHERE table_schema='%s' ORDER BY table_name" "Seal check — secure environment %q" "Seal unknown — this chart ships no conformance checks, so this environment's protections can't be verified. Not claiming sealed." @@ -315,6 +348,7 @@ screen. %s/%d are runtime placeholders. "Text a folder with labels.csv + texts/ e.g. %s" "The column holding the duration / time-to-event. e.g. time, tenure_days" "The dataset's catalog metadata is kept as a record on tracebloc, marked unavailable — never removed." +"The ingestion hasn't started yet (usually a slow image pull or a busy cluster). It's queued to run once the cluster can schedule it — check on it with the command below." "The name you provided was only control characters — auto-naming this client instead." "The number of landmark points each sample is annotated with — dataset-specific. e.g. 17 for COCO human pose" "The size your images already are, as WxH — tracebloc checks every image matches and never resizes. Press Enter to read it from your first image. e.g. 224x224" @@ -334,6 +368,8 @@ screen. %s/%d are runtime placeholders. "Time column:" "To train or benchmark models on it, create a use case at https://ai.tracebloc.io/my-use-cases" "To update tracebloc on Windows, run this in a new PowerShell window:" +"Tore down %q on this machine, but some cleanup above didn't complete and the server-side revoke didn't complete — the credential may still be live on tracebloc (revoke it from the dashboard). Finish the flagged steps by hand." +"Tore down %q on this machine. The server-side revoke didn't complete, so the credential may still be live on tracebloc — revoke it from the dashboard if needed (the orphan reaper sweeps it otherwise)." "Training results can't reach tracebloc — experiments will stall." "Try again shortly; if it persists, email support@tracebloc.io with `%s doctor --diagnose`." "Type %q to offboard this machine" @@ -346,6 +382,7 @@ screen. %s/%d are runtime placeholders. "VARCHAR(%d)" "Validate and load" "Verify the requests-proxy is wired: kubectl set env deploy/-jobs-manager --list | grep PROXY" +"WARNING: %d orphan stage Pod%s detected in this namespace — likely leftover from a previously crashed `data ingest`:" "Waiting for the ingestion to start (scheduling + pulling the image)" "Waiting for tracebloc to confirm…" "Waiting for your browser…" @@ -371,6 +408,7 @@ screen. %s/%d are runtime placeholders. "Your secure environment is equipped with:" "Your session expired — run `%s login`." "Your use cases and the models trained here" +"`tracebloc ingest` doesn't stage datasets — did you mean:\n tracebloc data ingest %s" "a Ready node can schedule a training job (%s)" "a dataset path is required" "a tracebloc client is already running on this cluster, but listing your account to verify ownership failed (%w) — re-run once tracebloc is reachable, or resolve manually" @@ -383,7 +421,9 @@ screen. %s/%d are runtime placeholders. "active client %q runs on another machine — namespace %q isn't on the cluster your kubeconfig points at.\n\ntracebloc clients are running on this cluster, in namespaces: %s.\n Target one just this once: --namespace %s" "active client %q runs on another machine — namespace %q isn't on the cluster your kubeconfig points at; run this command there, or override with --namespace/--context" "active client %q runs on another machine — namespace %q isn't on the cluster your kubeconfig points at; run this command there, or override with --namespace/--context.\n\nNo tracebloc client is running on this cluster either — if this machine should have one, set one up: %s" +"active client %s isn't in your account — run `tracebloc client create` (or re-run the installer) to provision this machine" "annotations" +"another tracebloc client (%s) in your account is already live on this cluster — offboard it first with `tracebloc delete`, or provision on a separate machine (cluster_in_use)" "app version" "authorized — confirming the token with the backend …" "auto-detect" @@ -395,6 +435,7 @@ screen. %s/%d are runtime placeholders. "building rest config from kubeconfig: %w" "building submit request: %w" "building tar archive: %w" +"can't confirm %q exists on this client — refusing to delete without confirming the target first: %w" "can't read %q: %w" "cancelled by user" "chart version" @@ -403,16 +444,21 @@ screen. %s/%d are runtime placeholders. "client id" "closing tar writer: %w" "cluster" +"column %q isn't all %s: %d value(s) don't match its declared type (e.g. %s). The cluster's data-type check rejects these after the table is created — fix the values, or correct the column's type with --schema, then re-run." "columns" "command" "connected: %s — %s" "constructing kubernetes clientset: %w" "context" "could not check — cluster API unreachable (see 'Cluster reachable' above)" +"couldn't check whether a tracebloc client is already running on this cluster (%w) — provisioning now could mint a duplicate that never deploys and locks the cluster to it. Re-run (if this was transient); if it persists, ensure your kubeconfig/context can list deployments and secrets across namespaces. Diagnose with `tracebloc doctor`" +"couldn't determine the installed client chart version (the release is missing its helm.sh/chart version label), so the upgrade can't be pinned to it. Refusing to change resources with an unpinned upgrade — it would pull the latest chart and could silently change your client. Re-run the tracebloc installer to repair the release, then try again" +"couldn't reach the backend to choose a unique client name (%v) — retry, or pass --name explicitly" "couldn't reach the backend to finish signing in — %d attempts failed in a row (check your network / HTTPS_PROXY): %w" "couldn't read RESOURCE_REQUESTS from jobs-manager — skipping node-fit" "couldn't read capacity: %v" "couldn't read jobs-manager to resolve image pull secrets — skipping" +"couldn't read the account's client list to tell whether this cluster is new or already registered (%v) — retry when the backend is reachable (a re-run adopts an existing client), or pass --yes/--credential-file to provision now" "couldn't read the chart's conformance checks: %w" "couldn't read this machine's capacity: %w" "cpu=%s, memory=%s" @@ -428,6 +474,10 @@ screen. %s/%d are runtime placeholders. "data CSV" "data row %d" "dataset exceeded v0.1 total cap of %s after streaming %s (reached %s)" +"dataset exceeded v0.1 total cap of %s after streaming %s (reached %s; file growth between pre-flight and stream)" +"dataset exceeded v0.1 total cap of %s during stream (labels.csv alone is %s; pre-flight likely raced with a file growing on disk)" +"dataset is %s, exceeds v0.1 cap of %s. For larger datasets, the cloud-source path (S3/GCS/HTTPS) is on the v0.2 roadmap — see tracebloc/client#147 non-goals. Workaround for v0.1: split the push into multiple smaller tables, or stage directly via the existing helm flow." +"dataset is %s, exceeds v0.1 cap of %s. For larger datasets, the cloud-source path is on the v0.2 roadmap (tracebloc/client#147)." "dataset name is required (set --name)" "dataset name is required — pass it as an argument: tracebloc data delete " "decoding image header %q: %w" @@ -443,10 +493,15 @@ screen. %s/%d are runtime placeholders. "exactly %d" "exec stream against %s/%s: %w" "exit %d" +"expected %s %s-separated fields (%s), found %d. Separate each field with exactly one %s" +"expected a single %s record but the file spans multiple lines. Put one %s per .txt" "expires" "expires in" "field %d is empty — every field (%s) must be non-empty" +"file %q is %s, exceeds v0.1 single-file cap of %s. For larger files, see tracebloc/client#147's v0.2 cloud-source story." "file failures" +"found %d .csv files in %q (%s); the tabular layout expects exactly one. Put the dataset CSV in its own directory and re-run." +"found %d tracebloc clients in namespace %q (%s); this CLI doesn't yet support disambiguating between multiple. Pass --namespace to target a namespace with exactly one client." "full log: %s" "generating Pod-name random suffix: %w" "generating idempotency key: %w" @@ -458,6 +513,8 @@ screen. %s/%d are runtime placeholders. "http://localhost:%d" "image pull secret %q not found" "images" +"images/ and annotations/ don't pair up: %s. Every image needs a same-named .xml annotation (and vice versa) — the cluster rejects mismatches after the upload." +"images/ and masks/ don't pair up: %s. Every image needs a same-named \"_mask.png\" in masks/ (and vice versa) — the cluster rejects mismatches after the upload." "infer from CSV" "inferring schema from CSV: %w" "ingested %s of %s records (%.1f%%)" @@ -476,12 +533,17 @@ screen. %s/%d are runtime placeholders. "jobs-manager %s returned HTTP %d: %s" "jobs-manager has no literal REQUESTS_PROXY_URL (chart too old, or it's set via a configMap/secret ref)" "jobs-manager: %s" +"keypoint_detection requires --number-of-keypoints (e.g. --number-of-keypoints 17); it's dataset-specific and has no default" "keypoints" "kube-system namespace has no UID" "kubectl set env deploy/-jobs-manager --list | grep RESOURCE_REQUESTS" "label column" +"label column %q isn't in %s's header (columns: %s). Pass --label-column with one of the existing columns, or add a %q column to the CSV." "label policy" "labels.csv" +"labels.csv column %q must be lowercase \"filename\": the cluster reads each image's file with a case-sensitive record.get(\"filename\") at transfer, so %q would upload, then fail with \"No filename found in record\". Rename it to \"filename\" and re-run." +"labels.csv has no \"filename\" column (columns: %s) — image tasks match each row to its file by that column, and the cluster drops every row without it (the ingest uploads, then fails with \"No filename found in record\"). Rename your file column to \"filename\" and re-run." +"labels.csv has no \"filename\" column (columns: %s) — the ingestor matches each row to its text file by that column and rejects a manifest without it. Add a filename column and re-run." "letters, digits, and underscores — no hyphens or spaces, use _; start with a letter or underscore e.g. churn_train" "listing Pods for service %s/%s: %w" "listing chart-managed deployments in namespace %s: %w" @@ -490,6 +552,7 @@ screen. %s/%d are runtime placeholders. "listing stage Pods in %s: %w" "loading embedded schema: %w" "loading kubeconfig: %w" +"local dataset path is required — pass it as an argument, or run on a terminal without --no-input for guided prompts" "locating mysql pod: %w" "location" "love from tracebloc 💚" @@ -499,6 +562,9 @@ screen. %s/%d are runtime placeholders. "min size" "minting token for ServiceAccount %s/%s via TokenRequest: %w" "missing %s/ subdirectory in %q" +"missing images/ subdirectory in %q. The CLI expects /labels.csv + /images/*.{jpg,jpeg,png}." +"missing labels.csv in %q. Text categories expect /labels.csv + /%s/." +"missing labels.csv in %q. The CLI expects /labels.csv + /images/ for image_classification; see https://docs.tracebloc.io for the dataset layout." "must be a positive integer" "must be between %d and %d" "mysql -uroot -p\"$MYSQL_ROOT_PASSWORD\" -N -e \"%s\"" @@ -507,18 +573,28 @@ screen. %s/%d are runtime placeholders. "name" "namespace" "never (static-secret fallback)" +"no .csv file found in %q. Tabular / time-series categories expect a single CSV holding the dataset (one column per feature, plus the label column)." +"no .png mask files found in %q. semantic_segmentation expects /masks/*.png (one PNG mask per image, named _mask.png)." +"no .txt files found in %q. Text categories expect /%s/*.txt." +"no .xml annotation files found in %q. object_detection expects /annotations/*.xml (Pascal VOC)." "no CLI-supported tasks for %s data yet" +"no PersistentVolumeClaim named %q found in namespace %q. The chart's _helpers.tpl pins this name; if your install renamed it out-of-band, the CLI doesn't yet support that (read-name-from-jobs-manager is a v0.2 follow-up). Verify with: kubectl get pvc -n %s" "no Ready node can fit a training job (needs %s)" "no Ready node on this machine to size a training run against" +"no Running Pod backing service %s/%s (found %d Pod(s); check `kubectl get pods -n %s -l %s`)" "no Running pod with name containing %q in namespace %q" +"no Secret of type kubernetes.io/service-account-token bound to ServiceAccount %s found in namespace %s" "no active client on this machine — nothing to offboard" "no active client on this machine — run `tracebloc client create` (or re-run the installer) first" "no dataset named %q on this client%s" +"no image files found in %q. Expected .jpg, .jpeg, or .png; got %d non-image entries." "no image files to detect a type from" "no image pull secret in use (public/digest-pinned images)" "no single Ready node satisfies cpu+memory AND %s — GPU jobs rely on the CPU fallback (needs %s)" "no such file or directory: %q — check the path to your dataset" "no tracebloc client found" +"no usable image files in %q — found %s, but the ingestor accepts only .jpg, .jpeg, or .png. Convert the images and re-run." +"no usable ingestor token. TokenRequest failed: %v. Fallback to static secret also failed: %w. Remediation: either grant your user the `create` verb on `serviceaccounts/token` (RBAC), or have an admin create a long-lived Secret of type kubernetes.io/service-account-token that references the %s ServiceAccount in namespace %s." "none detected" "not signed in — run `tracebloc login` first" "outcome: early exit before the cluster was probed" @@ -536,6 +612,8 @@ screen. %s/%d are runtime placeholders. "port-forward to %s/%s failed during startup: %w" "prepare-host didn't complete (%w). You can run the installer directly:\n %s" "prepare-host readies a Linux server or HPC login node so a non-admin user can install tracebloc without root — it doesn't apply to Windows. Run it as an administrator on the Unix host the researcher will use." +"push.FinalDestPrefix: unsafe table name %q — caller must ValidateTableName before constructing a PVC path" +"push.StagedPrefix: unsafe table name %q — caller must ValidateTableName before constructing a PVC path" "pvc path" "querying datasets: %w%s" "reading %q: %w" @@ -560,6 +638,7 @@ screen. %s/%d are runtime placeholders. "refusing to change the ceiling without confirmation: pass --yes, or run on a terminal" "refusing to delete without confirmation: pass --yes or run on a terminal" "refusing to offboard without confirmation: pass --yes, or run on a terminal to type the client name" +"refusing to provision non-interactively without confirmation — pass --yes to confirm, and --credential-file to write the credential to a file instead of stdout" "refusing to stream symbolic link %q (defense-in-depth; should have been rejected by Discover)" "release" "release %q, chart %s, appVersion %s (namespace %s)" @@ -567,6 +646,7 @@ screen. %s/%d are runtime placeholders. "removed — runs will use CPU only" "removing PVC paths: %w%s" "removing staged copy %s: %w%s" +"replacing table %q failed partway — its removal may be incomplete, and a plain re-run would hit the leftovers after uploading everything. Run `tracebloc data delete %s` first, then re-run this ingest. Nothing new was staged. (%w)" "requests-proxy deployment not found" "requests-proxy is running, but egress to Service Bus is not actively verified — readiness only confirms the relay started, not that it can reach Service Bus" "requests-proxy not ready (%d/%d replicas)" @@ -582,13 +662,17 @@ screen. %s/%d are runtime placeholders. "scanning the cluster for tracebloc clients: %w" "schema" "schema entry %q must be col:TYPE (e.g. age:INT,price:FLOAT)" +"schema type %q for column %q isn't a supported SQL type — the ingestor would reject it in-cluster. Supported types: %s (with optional length/precision, e.g. VARCHAR(255), DECIMAL(10,2))" "secret %q has an empty or malformed %s" "secret %q is type %q, not %s" "see why: kubectl logs -n %s %s" +"semantic_segmentation needs a %q column in %s (columns: %s) linking each image to its mask file. Add it — the training client reads it to locate each mask." +"semantic_segmentation needs a %q column in %s, but found %q (wrong case). Rename it to exactly %q — the training client reads that column to locate each mask." "sent to API" "server" "service %s/%s has no selector — can't resolve to a Pod for port-forwarding" "session: %s" +"set -e\nrm -rf %q\nmkdir -p %q\n/bin/tar -xf - -C %q\nif [ -e %q ]; then mv %q %q; fi\nif ! mv %q %q; then\n if [ -e %q ]; then mv %q %q; fi\n exit 1\nfi\nrm -rf %q\nfind %q -maxdepth 1 \\( -name %q -o -name %q \\) -mmin +60 -exec rm -rf {} + 2>/dev/null || true" "setting up jobs-manager port-forward: %w" "sha256[:8]" "shared PVC" @@ -614,20 +698,35 @@ screen. %s/%d are runtime placeholders. "submit response missing job_name (got body %q)" "submit response missing namespace (got body %q)" "synthesized spec failed schema validation; check the flag values above" +"table %q already exists in this secure environment. Re-ingesting the same table doesn't merge or replace — the run would fail after uploading everything. Re-run with --overwrite to replace it, or pick a different --name. (`tracebloc data delete %s` also removes it.)" +"table name is %d characters; the max is %d (matches both the MySQL identifier limit and the Kubernetes label-value limit, which the stage Pod's tracebloc.io/table label is bound by). Use a shorter name." "tabular = a CSV table; image = labels.csv + images/; text = labels.csv + texts/" "task" "task %q isn't a recognized task. Supported tasks: %s." "task %q isn't supported by the CLI yet%s. Supported tasks: %s." "teardown failed: %w" +"teardown incomplete — the table %s.%s was dropped, but removing its files failed: %w; re-run `tracebloc data delete %s`, or delete the leftover staging dirs on the node" +"text category %q has no primary_subdir in the vendored layout contract — the Go registry has drifted from layout.v1.json; re-run scripts/sync-schema.sh" +"that doesn't fit. This machine has %s · %s, and tracebloc keeps about %s and %s for itself, so one run can use at most %d cores and %d GiB. Try --cores %d --memory %d." "the client running in this namespace is anchored to a different cluster (%s) than --kubeconfig/--context points at (%s) — check you're targeting the right cluster" "the cluster API server at %s isn't answering — is the cluster running?" "the duration/time column name" +"the label column %q has %d distinct value(s) — a classification dataset needs at least 2 classes. The cluster rejects this after the upload; check the labels and re-run." "the label/target column name" +"the sequence column %q has %d empty/null value(s) (first at data row %d). Every timestep row must carry the id of the sequence it belongs to — the cluster rejects this after the upload; fill in the ids and re-run." "the sign-in code expired before it was approved%s" "the sign-in code expired%s" "the size your images already are; tracebloc checks it, it never resizes" +"the time column %q has %d missing/invalid value(s) (first at data row(s) %v%s). Every timestep row needs a valid value to order it within its sequence — the cluster rejects this after the upload; fix the values and re-run." +"this backend (%s) doesn't support browser login yet — the device-grant endpoints land in backend#835: %w" +"this cluster is already registered to another tracebloc account (%s) — ask them to release it, or sign in as that account (cluster_conflict)" "this machine has %s, but you asked for %s." +"this machine is too small to choose an amount — after tracebloc's ~1 core and 3 GiB overhead it can offer a training run at most %d core(s) and %d GiB. Free up resources or use a larger machine." +"this task's data is sequence-grouped: the schema must declare %q (groups the timestep rows of one sequence — e.g. a patient/device/session id) and %q (orders the rows within each sequence). Missing: %s. The column names are fixed by the platform — rename your CSV columns to match and re-run." "time column" +"timed out after %s before tracebloc could confirm this client — retry, or run `tracebloc doctor`." +"timed out after %s waiting for tracebloc to report this client online (last state: %s). Run `tracebloc doctor` to diagnose, or re-run the installer." +"timed out after %s waiting for tracebloc to report this client online; the last status check failed: %v" "token saved to ~/.tracebloc (0600)" "total records" "total size" @@ -642,6 +741,7 @@ screen. %s/%d are runtime placeholders. "tracebloc-doctor-%s.txt" "tracebloc-stage-%s-%s" "unavailable" +"unknown backend environment %q — valid values are dev, stg, prod (default). Check --env / $CLIENT_ENV" "unknown command %q for %q" "upgrade didn't complete (%w). You can run the installer directly:\n %s" "values:" @@ -651,9 +751,12 @@ screen. %s/%d are runtime placeholders. "waiting for teardown pod: %w" "watching ingestor Job: %w" "which split this data is" +"which task is this data for? pass --task — one of: %s. (On a terminal without --no-input, tracebloc asks you to pick.)" "whole GPUs a single run may use" "would set each run to" "writing credential file %s: %w" +"your images mix file types (%s) — the ingestor requires one type per dataset. Convert them to a single type, or split them into separate tables." +"your secure environment %q has %d training run(s) active — offboarding would stop them. Wait for them to finish or cancel them, then run `%s delete` again — or `%s delete --force` to stop them and offboard now" "~%s (requested; server may cap shorter)" "· %d GPU" "· %d classes"